提交 b3cd77e9 编写于 作者: G GitLab Bot

Add latest changes from gitlab-org/gitlab@master

上级 9bbcab83
import Vue from 'vue';
import { parseBoolean } from '~/lib/utils/common_utils';
import StaticSiteEditor from './components/static_site_editor.vue';
import App from './components/app.vue';
import createStore from './store';
import createRouter from './router';
const initStaticSiteEditor = el => {
const { isSupportedContent, projectId, path: sourcePath, returnUrl } = el.dataset;
const { isSupportedContent, projectId, path: sourcePath, returnUrl, baseUrl } = el.dataset;
const store = createStore({
initialState: {
......@@ -15,15 +16,17 @@ const initStaticSiteEditor = el => {
username: window.gon.current_username,
},
});
const router = createRouter(baseUrl);
return new Vue({
el,
store,
router,
components: {
StaticSiteEditor,
App,
},
render(createElement) {
return createElement('static-site-editor', StaticSiteEditor);
return createElement('app');
},
});
};
......
......@@ -2,12 +2,12 @@
import { mapState, mapGetters, mapActions } from 'vuex';
import { GlSkeletonLoader } from '@gitlab/ui';
import EditArea from './edit_area.vue';
import EditHeader from './edit_header.vue';
import SavedChangesMessage from './saved_changes_message.vue';
import PublishToolbar from './publish_toolbar.vue';
import InvalidContentMessage from './invalid_content_message.vue';
import SubmitChangesError from './submit_changes_error.vue';
import EditArea from '../components/edit_area.vue';
import EditHeader from '../components/edit_header.vue';
import SavedChangesMessage from '../components/saved_changes_message.vue';
import PublishToolbar from '../components/publish_toolbar.vue';
import InvalidContentMessage from '../components/invalid_content_message.vue';
import SubmitChangesError from '../components/submit_changes_error.vue';
export default {
components: {
......
// eslint-disable-next-line import/prefer-default-export
export const HOME_ROUTE_NAME = 'home';
import Vue from 'vue';
import VueRouter from 'vue-router';
import routes from './routes';
Vue.use(VueRouter);
export default function createRouter(base) {
const router = new VueRouter({
base,
mode: 'history',
routes,
});
return router;
}
import Home from '../pages/home.vue';
import { HOME_ROUTE_NAME } from './constants';
export default [
{
name: HOME_ROUTE_NAME,
path: '/',
component: Home,
},
];
......@@ -4,7 +4,6 @@ import {
GlNewDropdownHeader,
GlFormInputGroup,
GlButton,
GlIcon,
GlTooltipDirective,
} from '@gitlab/ui';
import { __, sprintf } from '~/locale';
......@@ -16,7 +15,6 @@ export default {
GlNewDropdownHeader,
GlFormInputGroup,
GlButton,
GlIcon,
},
directives: {
GlTooltip: GlTooltipDirective,
......@@ -59,9 +57,9 @@ export default {
v-gl-tooltip.hover
:title="$options.copyURLTooltip"
:data-clipboard-text="sshLink"
>
<gl-icon name="copy-to-clipboard" :title="$options.copyURLTooltip" />
</gl-button>
icon="copy-to-clipboard"
class="d-inline-flex"
/>
</template>
</gl-form-input-group>
</div>
......@@ -77,9 +75,9 @@ export default {
v-gl-tooltip.hover
:title="$options.copyURLTooltip"
:data-clipboard-text="httpLink"
>
<gl-icon name="copy-to-clipboard" :title="$options.copyURLTooltip" />
</gl-button>
icon="copy-to-clipboard"
class="d-inline-flex"
/>
</template>
</gl-form-input-group>
</div>
......
......@@ -10,10 +10,11 @@ module HasUserType
visual_review_bot: 3,
service_user: 4,
ghost: 5,
project_bot: 6
project_bot: 6,
migration_bot: 7
}.with_indifferent_access.freeze
BOT_USER_TYPES = %w[alert_bot project_bot support_bot visual_review_bot].freeze
BOT_USER_TYPES = %w[alert_bot project_bot support_bot visual_review_bot migration_bot].freeze
NON_INTERNAL_USER_TYPES = %w[human project_bot service_user].freeze
INTERNAL_USER_TYPES = (USER_TYPES.keys - NON_INTERNAL_USER_TYPES).freeze
......
......@@ -636,6 +636,16 @@ class User < ApplicationRecord
end
end
def migration_bot
email_pattern = "noreply+gitlab-migration-bot%s@#{Settings.gitlab.host}"
unique_internal(where(user_type: :migration_bot), 'migration-bot', email_pattern) do |u|
u.bio = 'The GitLab migration bot'
u.name = 'GitLab Migration Bot'
u.confirmed_at = Time.zone.now
end
end
# Return true if there is only single non-internal user in the deployment,
# ghost user is ignored.
def single_user?
......
......@@ -18,6 +18,7 @@ class GlobalPolicy < BasePolicy
condition(:private_instance_statistics, score: 0) { Gitlab::CurrentSettings.instance_statistics_visibility_private? }
condition(:project_bot, scope: :user) { @user&.project_bot? }
condition(:migration_bot, scope: :user) { @user&.migration_bot? }
rule { admin | (~private_instance_statistics & ~anonymous) }
.enable :read_instance_statistics
......@@ -48,11 +49,14 @@ class GlobalPolicy < BasePolicy
rule { blocked | internal }.policy do
prevent :log_in
prevent :access_api
prevent :access_git
prevent :receive_notifications
prevent :use_slash_commands
end
rule { blocked | (internal & ~migration_bot) }.policy do
prevent :access_git
end
rule { project_bot }.policy do
prevent :log_in
prevent :receive_notifications
......
......@@ -14,7 +14,7 @@
.col.form-group
= f.label :expires_at, s_('Profiles|Expires at'), class: 'label-bold'
= f.date_field :expires_at, class: "form-control input-lg qa-key-expiry-field", min: Date.tomorrow
= f.date_field :expires_at, class: "form-control input-lg", min: Date.tomorrow, data: { qa_selector: 'key_expiry_date_field' }
.js-add-ssh-key-validation-warning.hide
.bs-callout.bs-callout-warning{ role: 'alert', aria_live: 'assertive' }
......
#static-site-editor{ data: @config.payload }
-# TODO: Remove after base URL is included in the model !30735
- data = @config.payload.merge({ base_url: namespace_project_show_sse_path })
#static-site-editor{ data: data }
---
title: Remove deprecated Release Evidence endpoints documentation
merge_request: 30978
author:
type: removed
---
title: Fixed alignment of Snippet Clone copy buttons
merge_request: 30897
author:
type: fixed
---
title: Add migration bot user
merge_request: 30738
author:
type: added
......@@ -201,6 +201,7 @@ namespaced
namespaces
Nanoc
NGINX
Nurtch
OAuth
Okta
offboarded
......@@ -277,6 +278,7 @@ resync
reverified
reverifies
reverify
Rubix
runbook
runbooks
runit
......
......@@ -286,7 +286,6 @@ Example response:
],
"commit_path":"/root/awesome-app/commit/588440f66559714280628a4f9799f0c4eb880a4a",
"tag_path":"/root/awesome-app/-/tags/v0.11.1",
"evidence_sha":"760d6cdfb0879c3ffedec13af470e0f71cf52c6cde4d",
"assets":{
"count":5,
"sources":[
......@@ -314,9 +313,15 @@ Example response:
"url":"https://gitlab.example.com/root/awesome-app/-/tags/v0.11.1/binaries/linux-amd64",
"external":true
}
],
"evidence_url":"https://gitlab.example.com/root/awesome-app/-/releases/v0.1/evidence.json"
]
},
"evidences":[
{
sha: "760d6cdfb0879c3ffedec13af470e0f71cf52c6cde4d",
filepath: "https://gitlab.example.com/root/awesome-app/-/releases/v0.1/evidence.json",
collected_at: "2019-07-16T14:00:12.256Z"
}
]
}
```
......
......@@ -366,7 +366,7 @@ will also see ready-to-use DevOps Runbooks built with Nurtch's [Rubix library](h
More information on
creating executable runbooks can be found in [our Runbooks
documentation](../project/clusters/runbooks/index.md#executable-runbooks). Note that
documentation](../project/clusters/runbooks/index.md#configure-an-executable-runbook-with-gitlab). Note that
Ingress must be installed and have an IP address assigned before
JupyterHub can be installed.
......
......@@ -4,11 +4,10 @@ Runbooks are a collection of documented procedures that explain how to
carry out a particular process, be it starting, stopping, debugging,
or troubleshooting a particular system.
Using [Jupyter Notebooks](https://jupyter.org/) and the [Rubix library](https://github.com/Nurtch/rubix),
Using [Jupyter Notebooks](https://jupyter.org/) and the
[Rubix library](https://github.com/Nurtch/rubix),
users can get started writing their own executable runbooks.
## Overview
Historically, runbooks took the form of a decision tree or a detailed
step-by-step guide depending on the condition or system.
......@@ -22,121 +21,128 @@ pre-written code blocks or database queries against a given environment.
The JupyterHub app offered via GitLab’s Kubernetes integration now ships
with Nurtch’s Rubix library, providing a simple way to create DevOps
runbooks. A sample runbook is provided, showcasing common operations. While Rubix makes it
simple to create common Kubernetes and AWS workflows, you can also create them manually without
Rubix.
runbooks. A sample runbook is provided, showcasing common operations. While
Rubix makes it simple to create common Kubernetes and AWS workflows, you can
also create them manually without Rubix.
**<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
Watch this [video](https://www.youtube.com/watch?v=Q_OqHIIUPjE)
for an overview of how this is accomplished in GitLab!**
for an overview of how this is accomplished in GitLab!
## Requirements
To create an executable runbook, you will need:
1. **Kubernetes** - A Kubernetes cluster is required to deploy the rest of the applications.
The simplest way to get started is to add a cluster using one of [GitLab's integrations](../add_remove_clusters.md#create-new-cluster).
1. **Helm Tiller** - Helm is a package manager for Kubernetes and is required to install
all the other applications. It is installed in its own pod inside the cluster which
can run the Helm CLI in a safe environment.
1. **Ingress** - Ingress can provide load balancing, SSL termination, and name-based
virtual hosting. It acts as a web proxy for your applications.
1. **JupyterHub** - [JupyterHub](https://jupyterhub.readthedocs.io/) is a multi-user service for managing notebooks across
a team. Jupyter Notebooks provide a web-based interactive programming environment
used for data analysis, visualization, and machine learning.
- **Kubernetes** - A Kubernetes cluster is required to deploy the rest of the
applications. The simplest way to get started is to add a cluster using one
of [GitLab's integrations](../add_remove_clusters.md#create-new-cluster).
- **Helm Tiller** - Helm is a package manager for Kubernetes and is required to
install all the other applications. It's installed in its own pod inside the
cluster which can run the Helm CLI in a safe environment.
- **Ingress** - Ingress can provide load balancing, SSL termination, and name-based
virtual hosting. It acts as a web proxy for your applications.
- **JupyterHub** - [JupyterHub](https://jupyterhub.readthedocs.io/) is a multi-user
service for managing notebooks across a team. Jupyter Notebooks provide a
web-based interactive programming environment used for data analysis,
visualization, and machine learning.
## Nurtch
Nurtch is the company behind the [Rubix library](https://github.com/Nurtch/rubix). Rubix is
an open-source Python library that makes it easy to perform common DevOps tasks inside Jupyter Notebooks.
Tasks such as plotting Cloudwatch metrics and rolling your ECS/Kubernetes app are simplified
down to a couple of lines of code. See the [Nurtch Documentation](http://docs.nurtch.com/en/latest/)
for more information.
Nurtch is the company behind the [Rubix library](https://github.com/Nurtch/rubix).
Rubix is an open-source Python library that makes it easy to perform common
DevOps tasks inside Jupyter Notebooks. Tasks such as plotting Cloudwatch metrics
and rolling your ECS/Kubernetes app are simplified down to a couple of lines of
code. See the [Nurtch Documentation](http://docs.nurtch.com/en/latest/) for more
information.
## Configure an executable runbook with GitLab
Follow this step-by-step guide to configure an executable runbook in GitLab using
the components outlined above and the preloaded demo runbook.
### 1. Add a Kubernetes cluster
Follow the steps outlined in [Create new cluster](../add_remove_clusters.md#create-new-cluster)
to add a Kubernetes cluster to your project.
### 2. Install Helm Tiller, Ingress, and JupyterHub
Once the cluster has been provisioned in GKE, click the **Install** button next to the **Helm Tiller** app.
![install helm](img/helm-install.png)
the components outlined above and the pre-loaded demo runbook.
Once Tiller has been installed successfully, click the **Install** button next to the **Ingress** app.
1. Add a Kubernetes cluster to your project by following the steps outlined in
[Create new cluster](../add_remove_clusters.md#create-new-cluster).
1. After the cluster has been provisioned in GKE, click the **Install** button
next to the **Helm Tiller** application to install Helm Tiller.
![install ingress](img/ingress-install.png)
![install helm](img/helm-install.png)
Once Ingress has been installed successfully, click the **Install** button next to the **JupyterHub** app.
1. After Helm Tiller has been installed successfully, click the **Install** button next
to the **Ingress** application.
![install jupyterhub](img/jupyterhub-install.png)
![install ingress](img/ingress-install.png)
### 3. Login to JupyterHub and start the server
1. After Ingress has been installed successfully, click the **Install** button next
to the **JupyterHub** application. You will need the **Jupyter Hostname** provided
here in the next step.
Once JupyterHub has been installed successfully, navigate to the displayed **Jupyter Hostname** URL and click
**Sign in with GitLab**. Authentication is automatically enabled for any user of the GitLab instance via OAuth2. This
will redirect to GitLab in order to authorize JupyterHub to use your GitLab account. Click **Authorize**.
![install JupyterHub](img/jupyterhub-install.png)
![authorize jupyter](img/authorize-jupyter.png)
1. After JupyterHub has been installed successfully, open the **Jupyter Hostname**
in your browser. Click the **Sign in with GitLab** button to log in to
JupyterHub and start the server. Authentication is enabled for any user of the
GitLab instance with OAuth2. This button redirects you to a page at GitLab
requesting authorization for JupyterHub to use your GitLab account.
Once the application has been authorized you will taken back to the JupyterHub application. Click **Start My Server**.
The server will take a couple of seconds to start.
![authorize Jupyter](img/authorize-jupyter.png)
### 4. Configure access
1. Click **Authorize**, and you will be redirected to the JupyterHub application.
1. Click **Start My Server**, and the server will start in a few seconds.
1. To configure the runbook's access to your GitLab project, you must enter your
[GitLab Access Token](../../../profile/personal_access_tokens.md)
and your Project ID in the **Setup** section of the demo runbook:
In order for the runbook to access your GitLab project, you will need to enter a
[GitLab Access Token](../../../profile/personal_access_tokens.md)
as well as your Project ID in the **Setup** section of the demo runbook.
1. Double-click the **DevOps-Runbook-Demo** folder located on the left panel.
Double-click the **DevOps-Runbook-Demo** folder located on the left panel.
![demo runbook](img/demo-runbook.png)
![demo runbook](img/demo-runbook.png)
1. Double-click the `Nurtch-DevOps-Demo.ipynb` runbook.
Double-click the "Nurtch-DevOps-Demo.ipynb" runbook.
![sample runbook](img/sample-runbook.png)
![sample runbook](img/sample-runbook.png)
Jupyter displays the runbook's contents in the right-hand side of the screen.
The **Setup** section displays your `PRIVATE_TOKEN` and your `PROJECT_ID`.
Enter these values, maintaining the single quotes as follows:
The contents on the runbook will be displayed on the right side of the screen. Under the "Setup" section, you will find
entries for both your `PRIVATE_TOKEN` and your `PROJECT_ID`. Enter both these values, conserving the single quotes as follows:
```sql
PRIVATE_TOKEN = 'n671WNGecHugsdEDPsyo'
PROJECT_ID = '1234567'
```
```sql
PRIVATE_TOKEN = 'n671WNGecHugsdEDPsyo'
PROJECT_ID = '1234567'
```
1. Update the `VARIABLE_NAME` on the last line of this section to match the name of
the variable you're using for your access token. In this example, our variable
name is `PRIVATE_TOKEN`.
Update the `VARIABLE_NAME` on the last line of this section to match the name of the variable you are using for your
access token. In this example our variable name is `PRIVATE_TOKEN`.
```sql
VARIABLE_VALUE = project.variables.get('PRIVATE_TOKEN').value
```
```sql
VARIABLE_VALUE = project.variables.get('PRIVATE_TOKEN').value
```
1. To configure the operation of a runbook, create and configure variables:
### 5. Configure an operation
NOTE: **Note:**
For this example, we are using the **Run SQL queries in Notebook** section in the
sample runbook to query a PostgreSQL database. The first four lines of the following
code block define the variables that are required for this query to function:
For this example we'll use the "**Run SQL queries in Notebook**" section in the sample runbook to query
a PostgreSQL database. The first 4 lines of the section define the variables that are required for this query to function.
```sql
%env DB_USER={project.variables.get('DB_USER').value}
%env DB_PASSWORD={project.variables.get('DB_PASSWORD').value}
%env DB_ENDPOINT={project.variables.get('DB_ENDPOINT').value}
%env DB_NAME={project.variables.get('DB_NAME').value}
```
```sql
%env DB_USER={project.variables.get('DB_USER').value}
%env DB_PASSWORD={project.variables.get('DB_PASSWORD').value}
%env DB_ENDPOINT={project.variables.get('DB_ENDPOINT').value}
%env DB_NAME={project.variables.get('DB_NAME').value}
```
1. Navigate to **{settings}** **Settings >> CI/CD >> Variables** to create
the variables in your project.
Create the matching variables in your project's **Settings >> CI/CD >> Variables**
![GitLab variables](img/gitlab-variables.png)
![gitlab variables](img/gitlab-variables.png)
1. Click **Save variables**.
Back in Jupyter, click the "Run SQL queries in Notebook" heading and the click *Run*. The results will be
displayed in-line as follows:
1. In Jupyter, click the **Run SQL queries in Notebook** heading, and then click
**Run**. The results are displayed inline as follows:
![PostgreSQL query](img/postgres-query.png)
![PostgreSQL query](img/postgres-query.png)
You can try other operations such as running shell scripts or interacting with a Kubernetes cluster. Visit the
You can try other operations, such as running shell scripts or interacting with a
Kubernetes cluster. Visit the
[Nurtch Documentation](http://docs.nurtch.com/) for more information.
......@@ -931,6 +931,14 @@ Prerequisites for embedding from a Grafana instance:
1. In GitLab, paste the URL into a Markdown field and save. The chart will take a few moments to render.
![GitLab Rendered Grafana Panel](img/rendered_grafana_embed_v12_5.png)
## Metrics dashboard visibility
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/201924) in GitLab 13.0.
You can set the visibility of the metrics dashboard to **Only Project Members**
or **Everyone With Access**. When set to **Everyone with Access**, the metrics
dashboard, and all of the custom dashboard, YAML files, are visible to authenticated and non-authenticated users.
## Troubleshooting
When troubleshooting issues with a managed Prometheus app, it is often useful to
......
......@@ -43,10 +43,10 @@ To add a new feature flag:
1. Click on the **New Feature Flag** button.
1. Give it a name.
NOTE: **Note:**
A name can contain only lowercase letters, digits, underscores (`_`)
and dashes (`-`), must start with a letter, and cannot end with a dash (`-`)
or an underscore (`_`).
NOTE: **Note:**
A name can contain only lowercase letters, digits, underscores (`_`)
and dashes (`-`), must start with a letter, and cannot end with a dash (`-`)
or an underscore (`_`).
1. Give it a description (optional, 255 characters max).
1. Define environment [specs](#define-environment-specs). If you want the flag on by default, enable the catch-all [wildcard spec (`*`)](#define-environment-specs)
......@@ -91,6 +91,41 @@ NOTE: **NOTE**
We'd highly recommend you to use the [Environment](../../../ci/environments.md)
feature in order to quickly assess which flag is enabled per environment.
## Feature flag behavior change in 13.0
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/35555) in GitLab 13.0.
Starting in GitLab 13.0, you can apply a feature flag strategy across multiple environment specs,
without defining the strategy multiple times.
This feature is under development and not ready for production use. It is
deployed behind a feature flag that is **disabled by default**.
[GitLab administrators with access to the GitLab Rails console](../../../administration/feature_flags.md)
can enable it for your instance.
To enable it:
```ruby
Feature.enable(:feature_flags_new_version)
```
To disable it:
```ruby
Feature.disable(:feature_flags_new_version)
```
### Applying a strategy to environments
After a strategy is defined, it applies to **All Environments** by default. To
make a strategy apply to a specific environment spec:
1. Click the **Add Environment** button.
1. Create a new
[spec](../../../ci/environments.md#scoping-environments-with-specs).
To apply the strategy to multiple environment specs, repeat these steps.
## Feature Flag strategies
GitLab Feature Flag adopts [Unleash](https://unleash.github.io)
......@@ -155,12 +190,12 @@ To get the access credentials that your application will need to talk to GitLab:
1. Navigate to your project's **Operations > Feature Flags**.
1. Click on the **Configure** button to see the values:
- **API URL**: URL where the client (application) connects to get a list of feature flags.
- **Instance ID**: Unique token that authorizes the retrieval of the feature flags.
- **Application name**: The name of the running environment. For instance,
if the application runs for production server, application name would be
`production` or similar. This value is used for
[the environment spec evaluation](#define-environment-specs).
- **API URL**: URL where the client (application) connects to get a list of feature flags.
- **Instance ID**: Unique token that authorizes the retrieval of the feature flags.
- **Application name**: The name of the running environment. For instance,
if the application runs for a production server, application name would be
`production` or similar. This value is used for
[the environment spec evaluation](#define-environment-specs).
NOTE: **Note:**
The meaning of these fields might change over time. For example, we are not sure
......
......@@ -7,7 +7,8 @@ gem 'capybara-screenshot', '~> 1.0.23'
gem 'rake', '~> 12.3.0'
gem 'rspec', '~> 3.7'
gem 'selenium-webdriver', '~> 3.12'
gem 'airborne', '~> 0.2.13'
gem 'airborne', '~> 0.3.4'
gem 'rest-client', '~> 2.1.0'
gem 'nokogiri', '~> 1.10.9'
gem 'rspec-retry', '~> 0.6.1'
gem 'rspec_junit_formatter', '~> 0.4.1'
......
......@@ -9,12 +9,12 @@ GEM
zeitwerk (~> 2.2)
addressable (2.7.0)
public_suffix (>= 2.0.2, < 5.0)
airborne (0.2.13)
airborne (0.3.4)
activesupport
rack
rack-test (~> 0.6, >= 0.6.2)
rest-client (>= 1.7.3, < 3.0)
rspec (~> 3.1)
rack-test (>= 1.1.0, < 2.0)
rest-client (>= 2.0.2, < 3.0)
rspec (~> 3.8)
byebug (9.1.0)
capybara (3.29.0)
addressable
......@@ -34,11 +34,12 @@ GEM
debase-ruby_core_source (>= 0.10.2)
debase-ruby_core_source (0.10.6)
diff-lcs (1.3)
domain_name (0.5.20170404)
domain_name (0.5.20190701)
unf (>= 0.0.5, < 1.0.0)
faker (1.9.3)
i18n (>= 0.7)
gitlab-qa (4.0.0)
http-accept (1.7.0)
http-cookie (1.0.3)
domain_name (~> 0.5)
i18n (1.8.2)
......@@ -48,9 +49,9 @@ GEM
launchy (2.4.3)
addressable (~> 2.3)
method_source (0.9.0)
mime-types (3.1)
mime-types (3.3.1)
mime-types-data (~> 3.2015)
mime-types-data (3.2016.0521)
mime-types-data (3.2020.0425)
mini_mime (1.0.2)
mini_portile2 (2.4.0)
minitest (5.14.0)
......@@ -67,30 +68,31 @@ GEM
byebug (~> 9.1)
pry (~> 0.10)
public_suffix (4.0.1)
rack (2.0.7)
rack-test (0.8.3)
rack (2.2.2)
rack-test (1.1.0)
rack (>= 1.0, < 3)
rake (12.3.0)
regexp_parser (1.6.0)
rest-client (2.0.2)
rest-client (2.1.0)
http-accept (>= 1.7.0, < 2.0)
http-cookie (>= 1.0.2, < 2.0)
mime-types (>= 1.16, < 4.0)
netrc (~> 0.8)
rspec (3.7.0)
rspec-core (~> 3.7.0)
rspec-expectations (~> 3.7.0)
rspec-mocks (~> 3.7.0)
rspec-core (3.7.1)
rspec-support (~> 3.7.0)
rspec-expectations (3.7.0)
rspec (3.9.0)
rspec-core (~> 3.9.0)
rspec-expectations (~> 3.9.0)
rspec-mocks (~> 3.9.0)
rspec-core (3.9.2)
rspec-support (~> 3.9.3)
rspec-expectations (3.9.1)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.7.0)
rspec-mocks (3.7.0)
rspec-support (~> 3.9.0)
rspec-mocks (3.9.1)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.7.0)
rspec-support (~> 3.9.0)
rspec-retry (0.6.1)
rspec-core (> 3.3)
rspec-support (3.7.0)
rspec-support (3.9.3)
rspec_junit_formatter (0.4.1)
rspec-core (>= 2, < 4, != 2.12.0)
ruby-debug-ide (0.7.2)
......@@ -101,11 +103,11 @@ GEM
rubyzip (>= 1.2.2)
thread_safe (0.3.6)
timecop (0.9.1)
tzinfo (1.2.6)
tzinfo (1.2.7)
thread_safe (~> 0.1)
unf (0.1.4)
unf_ext
unf_ext (0.0.7.4)
unf_ext (0.0.7.7)
xpath (3.2.0)
nokogiri (~> 1.8)
zeitwerk (2.3.0)
......@@ -115,7 +117,7 @@ PLATFORMS
DEPENDENCIES
activesupport (~> 6.0.2.2)
airborne (~> 0.2.13)
airborne (~> 0.3.4)
capybara (~> 3.29.0)
capybara-screenshot (~> 1.0.23)
debase (~> 0.2.4.1)
......@@ -126,6 +128,7 @@ DEPENDENCIES
parallel_tests (~> 2.29)
pry-byebug (~> 3.5.1)
rake (~> 12.3.0)
rest-client (~> 2.1.0)
rspec (~> 3.7)
rspec-retry (~> 0.6.1)
rspec_junit_formatter (~> 0.4.1)
......
......@@ -5,6 +5,7 @@ module QA
module Profile
class SSHKeys < Page::Base
view 'app/views/profiles/keys/_form.html.haml' do
element :key_expiry_date_field
element :key_title_field
element :key_public_key_field
element :add_key_button
......@@ -19,17 +20,26 @@ module QA
end
def add_key(public_key, title)
fill_element :key_public_key_field, public_key
fill_element :key_title_field, title
fill_element(:key_public_key_field, public_key)
fill_element(:key_title_field, title)
# Expire in 2 days just in case the key is created just before midnight
fill_expiry_date(Date.today + 2)
click_element :add_key_button
click_element(:add_key_button)
end
def fill_expiry_date(date)
date = date.strftime('%m/%d/%Y') if date.is_a?(Date)
Date.strptime(date, '%m/%d/%Y') rescue ArgumentError raise "Expiry date must be in mm/dd/yyyy format"
fill_element(:key_expiry_date_field, date)
end
def remove_key(title)
click_link(title)
accept_alert do
click_element :delete_key_button
click_element(:delete_key_button)
end
end
......
......@@ -5,12 +5,16 @@ module QA
class SSHKey < Base
extend Forwardable
attr_accessor :title
attr_reader :title
attribute :id
def_delegators :key, :private_key, :public_key, :md5_fingerprint
def initialize
self.title = Time.now.to_f
end
def key
@key ||= Runtime::Key::RSA.new
end
......@@ -28,6 +32,10 @@ module QA
api_post
end
def title=(title)
@title = "E2E test key: #{title}"
end
def api_delete
QA::Runtime::Logger.debug("Deleting SSH key with title '#{title}' and fingerprint '#{md5_fingerprint}'")
......
# frozen_string_literal: true
require 'airborne'
module QA
module Runtime
module API
......
# frozen_string_literal: true
require 'airborne'
module QA
context 'Manage with IP rate limits', :requires_admin do
describe 'Users API' do
......
# frozen_string_literal: true
require 'airborne'
module QA
context 'Manage' do
describe 'Users API' do
......
# frozen_string_literal: true
require 'airborne'
module QA
context 'Plan' do
include Support::Api
......
# frozen_string_literal: true
require 'airborne'
require 'securerandom'
module QA
......
# frozen_string_literal: true
require 'airborne'
require 'securerandom'
require 'digest'
......
......@@ -3,6 +3,8 @@
module QA
context 'Plan', :orchestrated, :smtp do
describe 'Email Notification' do
include Support::Api
let(:user) do
Resource::User.fabricate_or_use(Runtime::Env.gitlab_qa_username_1, Runtime::Env.gitlab_qa_password_1)
end
......
......@@ -12,7 +12,7 @@ module QA
resource.title = key_title
end
expect(page).to have_content("Title: #{key_title}")
expect(page).to have_content(key.title)
expect(page).to have_content(key.md5_fingerprint)
Page::Main::Menu.perform(&:click_settings_link)
......
......@@ -3,6 +3,8 @@
module QA
context 'Create', :requires_admin do
describe 'push after setting the file size limit via admin/application_settings' do
include Support::Api
before(:context) do
@project = Resource::Project.fabricate_via_api! do |p|
p.name = 'project-test-push-limit'
......@@ -39,12 +41,10 @@ module QA
def set_file_size_limit(limit)
request = Runtime::API::Request.new(@api_client, '/application/settings')
put request.url, receive_max_input_size: limit
response = put request.url, receive_max_input_size: limit
expect_status(200)
expect(json_body).to match(
a_hash_including(receive_max_input_size: limit)
)
expect(response.code).to eq(200)
expect(parse_body(response)[:receive_max_input_size]).to eq(limit)
end
def push_new_file(file_name, wait_for_push: true)
......
# frozen_string_literal: true
require 'rest-client'
module QA
module Support
module Api
......
# frozen_string_literal: true
describe QA::Resource::SSHKey do
describe '#key' do
it 'generates a default key' do
expect(subject.key).to be_a(QA::Runtime::Key::RSA)
end
end
describe '#title' do
it 'generates a default title' do
expect(subject.title).to match(/E2E test key: \d+/)
end
it 'is possible to set the title' do
subject.title = 'I am in a title'
expect(subject.title).to eq('E2E test key: I am in a title')
end
end
end
......@@ -31,14 +31,6 @@ RSpec.configure do |c|
config.color_mode = :off
# Load airborne again to avoid "undefined method `match_expected_default?'" errors
# that happen because a hook calls a method added via a custom RSpec setting
# that is removed when the RSpec configuration is sandboxed.
# If this needs to be changed (e.g., to load other libraries as well), see
# this discussion for alternative solutions:
# https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/25223#note_143392053
load 'airborne.rb'
ex.run
end
end
......
......@@ -31,6 +31,10 @@ FactoryBot.define do
user_type { :project_bot }
end
trait :migration_bot do
user_type { :migration_bot }
end
trait :external do
external { true }
end
......
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import PipelineMediator from '~/pipelines/pipeline_details_mediator';
import waitForPromises from 'helpers/wait_for_promises';
describe('PipelineMdediator', () => {
let mediator;
......@@ -23,14 +24,13 @@ describe('PipelineMdediator', () => {
});
describe('request and store data', () => {
it('should store received data', done => {
it('should store received data', () => {
mock.onGet('foo.json').reply(200, { id: '121123' });
mediator.fetchPipeline();
setTimeout(() => {
return waitForPromises().then(() => {
expect(mediator.store.state.pipeline).toEqual({ id: '121123' });
done();
}, 0);
});
});
});
});
......@@ -4,7 +4,7 @@ import { GlSkeletonLoader } from '@gitlab/ui';
import createState from '~/static_site_editor/store/state';
import StaticSiteEditor from '~/static_site_editor/components/static_site_editor.vue';
import Home from '~/static_site_editor/pages/home.vue';
import EditArea from '~/static_site_editor/components/edit_area.vue';
import EditHeader from '~/static_site_editor/components/edit_header.vue';
import InvalidContentMessage from '~/static_site_editor/components/invalid_content_message.vue';
......@@ -24,7 +24,7 @@ const localVue = createLocalVue();
localVue.use(Vuex);
describe('StaticSiteEditor', () => {
describe('static_site_editor/pages/home', () => {
let wrapper;
let store;
let loadContentActionMock;
......@@ -68,7 +68,7 @@ describe('StaticSiteEditor', () => {
};
const buildWrapper = () => {
wrapper = shallowMount(StaticSiteEditor, {
wrapper = shallowMount(Home, {
localVue,
store,
});
......
......@@ -44,18 +44,13 @@ exports[`Clone Dropdown Button rendering matches the snapshot 1`] = `
>
<gl-button-stub
category="tertiary"
class="d-inline-flex"
data-clipboard-text="ssh://foo.bar"
icon=""
icon="copy-to-clipboard"
size="medium"
title="Copy URL"
variant="default"
>
<gl-icon-stub
name="copy-to-clipboard"
size="16"
title="Copy URL"
/>
</gl-button-stub>
/>
</b-input-group-append-stub>
</b-input-group-stub>
</div>
......@@ -94,18 +89,13 @@ exports[`Clone Dropdown Button rendering matches the snapshot 1`] = `
>
<gl-button-stub
category="tertiary"
class="d-inline-flex"
data-clipboard-text="http://foo.bar"
icon=""
icon="copy-to-clipboard"
size="medium"
title="Copy URL"
variant="default"
>
<gl-icon-stub
name="copy-to-clipboard"
size="16"
title="Copy URL"
/>
</gl-button-stub>
/>
</b-input-group-append-stub>
</b-input-group-stub>
</div>
......
......@@ -4,8 +4,8 @@ require 'spec_helper'
describe User do
specify 'types consistency checks', :aggregate_failures do
expect(described_class::USER_TYPES)
.to include(*%i[human ghost alert_bot project_bot support_bot service_user visual_review_bot])
expect(described_class::USER_TYPES.keys)
.to match_array(%w[human ghost alert_bot project_bot support_bot service_user visual_review_bot migration_bot])
expect(described_class::USER_TYPES).to include(*described_class::BOT_USER_TYPES)
expect(described_class::USER_TYPES).to include(*described_class::NON_INTERNAL_USER_TYPES)
expect(described_class::USER_TYPES).to include(*described_class::INTERNAL_USER_TYPES)
......
......@@ -4584,4 +4584,20 @@ describe User, :do_not_mock_admin_mode do
it_behaves_like 'does not require password to be present'
end
end
describe '#migration_bot' do
it 'creates the user if it does not exist' do
expect do
described_class.migration_bot
end.to change { User.where(user_type: :migration_bot).count }.by(1)
end
it 'does not create a new user if it already exists' do
described_class.migration_bot
expect do
described_class.migration_bot
end.not_to change { User.count }
end
end
end
......@@ -6,6 +6,7 @@ describe GlobalPolicy do
include TermsHelper
let_it_be(:project_bot) { create(:user, :project_bot) }
let_it_be(:migration_bot) { create(:user, :migration_bot) }
let(:current_user) { create(:user) }
let(:user) { create(:user) }
......@@ -155,6 +156,12 @@ describe GlobalPolicy do
it { is_expected.to be_allowed(:access_api) }
end
context 'migration bot' do
let(:current_user) { migration_bot }
it { is_expected.not_to be_allowed(:access_api) }
end
context 'when terms are enforced' do
before do
enforce_terms
......@@ -244,6 +251,12 @@ describe GlobalPolicy do
it { is_expected.not_to be_allowed(:receive_notifications) }
end
context 'migration bot' do
let(:current_user) { migration_bot }
it { is_expected.not_to be_allowed(:receive_notifications) }
end
end
describe 'git access' do
......@@ -263,6 +276,12 @@ describe GlobalPolicy do
it { is_expected.to be_allowed(:access_git) }
end
context 'migration bot' do
let(:current_user) { migration_bot }
it { is_expected.to be_allowed(:access_git) }
end
describe 'deactivated user' do
before do
current_user.deactivate
......@@ -414,6 +433,12 @@ describe GlobalPolicy do
it { is_expected.to be_allowed(:use_slash_commands) }
end
context 'migration bot' do
let(:current_user) { migration_bot }
it { is_expected.not_to be_allowed(:use_slash_commands) }
end
end
describe 'create_snippet' do
......@@ -440,5 +465,11 @@ describe GlobalPolicy do
it { is_expected.not_to be_allowed(:log_in) }
end
context 'migration bot' do
let(:current_user) { migration_bot }
it { is_expected.not_to be_allowed(:log_in) }
end
end
end
......@@ -27,6 +27,16 @@ describe 'admin/users/_user.html.haml' do
expect(rendered).not_to have_selector('.table-action-buttons')
end
end
context 'when showing a `Migration User`' do
let(:user) { create(:user, user_type: :migration_bot) }
it 'does not render action buttons' do
render
expect(rendered).not_to have_selector('.table-action-buttons')
end
end
end
context 'when showing an external user' do
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册