Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
9bbcab8301
commit
b3cd77e904
|
@ -0,0 +1,3 @@
|
|||
<template>
|
||||
<router-view />
|
||||
</template>
|
|
@ -1,10 +1,11 @@
|
|||
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: {
|
|
@ -0,0 +1,2 @@
|
|||
// eslint-disable-next-line import/prefer-default-export
|
||||
export const HOME_ROUTE_NAME = 'home';
|
|
@ -0,0 +1,15 @@
|
|||
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;
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
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' }
|
||||
|
|
|
@ -1 +1,4 @@
|
|||
#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 }
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Remove deprecated Release Evidence endpoints documentation
|
||||
merge_request: 30978
|
||||
author:
|
||||
type: removed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fixed alignment of Snippet Clone copy buttons
|
||||
merge_request: 30897
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
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.
|
||||
the components outlined above and the pre-loaded demo runbook.
|
||||
|
||||
### 1. Add a Kubernetes cluster
|
||||
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.
|
||||
|
||||
Follow the steps outlined in [Create new cluster](../add_remove_clusters.md#create-new-cluster)
|
||||
to add a Kubernetes cluster to your project.
|
||||
![install helm](img/helm-install.png)
|
||||
|
||||
### 2. Install Helm Tiller, Ingress, and JupyterHub
|
||||
1. After Helm Tiller has been installed successfully, click the **Install** button next
|
||||
to the **Ingress** application.
|
||||
|
||||
Once the cluster has been provisioned in GKE, click the **Install** button next to the **Helm Tiller** app.
|
||||
![install ingress](img/ingress-install.png)
|
||||
|
||||
![install helm](img/helm-install.png)
|
||||
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 Tiller has been installed successfully, click the **Install** button next to the **Ingress** app.
|
||||
![install JupyterHub](img/jupyterhub-install.png)
|
||||
|
||||
![install ingress](img/ingress-install.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 Ingress has been installed successfully, click the **Install** button next to the **JupyterHub** app.
|
||||
![authorize Jupyter](img/authorize-jupyter.png)
|
||||
|
||||
![install jupyterhub](img/jupyterhub-install.png)
|
||||
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:
|
||||
|
||||
### 3. Login to JupyterHub and start the server
|
||||
1. Double-click the **DevOps-Runbook-Demo** folder located on the left panel.
|
||||
|
||||
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**.
|
||||
![demo runbook](img/demo-runbook.png)
|
||||
|
||||
![authorize jupyter](img/authorize-jupyter.png)
|
||||
1. Double-click the `Nurtch-DevOps-Demo.ipynb` runbook.
|
||||
|
||||
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.
|
||||
![sample runbook](img/sample-runbook.png)
|
||||
|
||||
### 4. Configure access
|
||||
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:
|
||||
|
||||
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.
|
||||
```sql
|
||||
PRIVATE_TOKEN = 'n671WNGecHugsdEDPsyo'
|
||||
PROJECT_ID = '1234567'
|
||||
```
|
||||
|
||||
Double-click the **DevOps-Runbook-Demo** folder located on the left panel.
|
||||
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`.
|
||||
|
||||
![demo runbook](img/demo-runbook.png)
|
||||
```sql
|
||||
VARIABLE_VALUE = project.variables.get('PRIVATE_TOKEN').value
|
||||
```
|
||||
|
||||
Double-click the "Nurtch-DevOps-Demo.ipynb" runbook.
|
||||
1. To configure the operation of a runbook, create and configure variables:
|
||||
|
||||
![sample runbook](img/sample-runbook.png)
|
||||
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:
|
||||
|
||||
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
|
||||
%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
|
||||
PRIVATE_TOKEN = 'n671WNGecHugsdEDPsyo'
|
||||
PROJECT_ID = '1234567'
|
||||
```
|
||||
1. Navigate to **{settings}** **Settings >> CI/CD >> Variables** to create
|
||||
the variables in your project.
|
||||
|
||||
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`.
|
||||
![GitLab variables](img/gitlab-variables.png)
|
||||
|
||||
```sql
|
||||
VARIABLE_VALUE = project.variables.get('PRIVATE_TOKEN').value
|
||||
```
|
||||
1. Click **Save variables**.
|
||||
|
||||
### 5. Configure an operation
|
||||
1. In Jupyter, click the **Run SQL queries in Notebook** heading, and then click
|
||||
**Run**. The results are displayed inline as follows:
|
||||
|
||||
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.
|
||||
![PostgreSQL query](img/postgres-query.png)
|
||||
|
||||
```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}
|
||||
```
|
||||
|
||||
Create the matching variables in your project's **Settings >> CI/CD >> Variables**
|
||||
|
||||
![gitlab variables](img/gitlab-variables.png)
|
||||
|
||||
Back in Jupyter, click the "Run SQL queries in Notebook" heading and the click *Run*. The results will be
|
||||
displayed in-line as follows:
|
||||
|
||||
![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}'")
|
||||
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'airborne'
|
||||
|
||||
module QA
|
||||
module Runtime
|
||||
module API
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'airborne'
|
||||
|
||||
module QA
|
||||
context 'Manage with IP rate limits', :requires_admin do
|
||||
describe 'Users API' do
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'airborne'
|
||||
|
||||
module QA
|
||||
context 'Manage' do
|
||||
describe 'Users API' do
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'airborne'
|
||||
|
||||
module QA
|
||||
context 'Plan' do
|
||||
include Support::Api
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'airborne'
|
||||
require 'securerandom'
|
||||
|
||||
module QA
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# 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)
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rest-client'
|
||||
|
||||
module QA
|
||||
module Support
|
||||
module Api
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
# 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
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
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
|
||||
|
|
Loading…
Reference in New Issue