Commit 6b8d671d authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent 163a7046
......@@ -47,6 +47,7 @@ const Api = {
adminStatisticsPath: '/api/:version/application/statistics',
pipelineSinglePath: '/api/:version/projects/:id/pipelines/:pipeline_id',
lsifPath: '/api/:version/projects/:id/commits/:commit_id/lsif/info',
environmentsPath: '/api/:version/projects/:id/environments',
group(groupId, callback) {
const url = Api.buildUrl(Api.groupPath).replace(':id', groupId);
......@@ -483,6 +484,11 @@ const Api = {
return axios.get(url, { params: { path } });
},
environments(id) {
const url = Api.buildUrl(this.environmentsPath).replace(':id', encodeURIComponent(id));
return axios.get(url);
},
buildUrl(url) {
return joinPaths(gon.relative_url_root || '', url.replace(':version', gon.api_version));
},
......
import * as types from './mutation_types';
import axios from '~/lib/utils/axios_utils';
import Api from '~/api';
import createFlash from '~/flash';
import { __ } from '~/locale';
import { prepareDataForApi, prepareDataForDisplay, prepareEnvironments } from './utils';
export const toggleValues = ({ commit }, valueState) => {
commit(types.TOGGLE_VALUES, valueState);
};
export const clearModal = ({ commit }) => {
commit(types.CLEAR_MODAL);
};
export const resetEditing = ({ commit, dispatch }) => {
// fetch variables again if modal is being edited and then hidden
// without saving changes, to cover use case of reactivity in the table
dispatch('fetchVariables');
commit(types.RESET_EDITING);
};
export const requestAddVariable = ({ commit }) => {
commit(types.REQUEST_ADD_VARIABLE);
};
export const receiveAddVariableSuccess = ({ commit }) => {
commit(types.RECEIVE_ADD_VARIABLE_SUCCESS);
};
export const receiveAddVariableError = ({ commit }, error) => {
commit(types.RECEIVE_ADD_VARIABLE_ERROR, error);
};
export const addVariable = ({ state, dispatch }) => {
dispatch('requestAddVariable');
return axios
.patch(state.endpoint, {
variables_attributes: [prepareDataForApi(state.variable)],
})
.then(() => {
dispatch('receiveAddVariableSuccess');
dispatch('fetchVariables');
})
.catch(error => {
createFlash(error.response.data[0]);
dispatch('receiveAddVariableError', error);
});
};
export const requestUpdateVariable = ({ commit }) => {
commit(types.REQUEST_UPDATE_VARIABLE);
};
export const receiveUpdateVariableSuccess = ({ commit }) => {
commit(types.RECEIVE_UPDATE_VARIABLE_SUCCESS);
};
export const receiveUpdateVariableError = ({ commit }, error) => {
commit(types.RECEIVE_UPDATE_VARIABLE_ERROR, error);
};
export const updateVariable = ({ state, dispatch }, variable) => {
dispatch('requestUpdateVariable');
const updatedVariable = prepareDataForApi(variable);
updatedVariable.secrect_value = updateVariable.value;
return axios
.patch(state.endpoint, { variables_attributes: [updatedVariable] })
.then(() => {
dispatch('receiveUpdateVariableSuccess');
dispatch('fetchVariables');
})
.catch(error => {
createFlash(error.response.data[0]);
dispatch('receiveUpdateVariableError', error);
});
};
export const editVariable = ({ commit }, variable) => {
const variableToEdit = variable;
variableToEdit.secret_value = variableToEdit.value;
commit(types.VARIABLE_BEING_EDITED, variableToEdit);
};
export const requestVariables = ({ commit }) => {
commit(types.REQUEST_VARIABLES);
};
export const receiveVariablesSuccess = ({ commit }, variables) => {
commit(types.RECEIVE_VARIABLES_SUCCESS, variables);
};
export const fetchVariables = ({ dispatch, state }) => {
dispatch('requestVariables');
return axios
.get(state.endpoint)
.then(({ data }) => {
dispatch('receiveVariablesSuccess', prepareDataForDisplay(data.variables));
})
.catch(() => {
createFlash(__('There was an error fetching the variables.'));
});
};
export const requestDeleteVariable = ({ commit }) => {
commit(types.REQUEST_DELETE_VARIABLE);
};
export const receiveDeleteVariableSuccess = ({ commit }) => {
commit(types.RECEIVE_DELETE_VARIABLE_SUCCESS);
};
export const receiveDeleteVariableError = ({ commit }, error) => {
commit(types.RECEIVE_DELETE_VARIABLE_ERROR, error);
};
export const deleteVariable = ({ dispatch, state }, variable) => {
dispatch('requestDeleteVariable');
const destroy = true;
return axios
.patch(state.endpoint, { variables_attributes: [prepareDataForApi(variable, destroy)] })
.then(() => {
dispatch('receiveDeleteVariableSuccess');
dispatch('fetchVariables');
})
.catch(error => {
createFlash(error.response.data[0]);
dispatch('receiveDeleteVariableError', error);
});
};
export const requestEnvironments = ({ commit }) => {
commit(types.REQUEST_ENVIRONMENTS);
};
export const receiveEnvironmentsSuccess = ({ commit }, environments) => {
commit(types.RECEIVE_ENVIRONMENTS_SUCCESS, environments);
};
export const fetchEnvironments = ({ dispatch, state }) => {
dispatch('requestEnvironments');
return Api.environments(state.projectId)
.then(res => {
dispatch('receiveEnvironmentsSuccess', prepareEnvironments(res.data));
})
.catch(() => {
createFlash(__('There was an error fetching the environments information.'));
});
};
import Vue from 'vue';
import Vuex from 'vuex';
import * as actions from './actions';
import mutations from './mutations';
import state from './state';
Vue.use(Vuex);
export default (initialState = {}) =>
new Vuex.Store({
actions,
mutations,
state: {
...state(),
...initialState,
},
});
export const TOGGLE_VALUES = 'TOGGLE_VALUES';
export const VARIABLE_BEING_EDITED = 'VARIABLE_BEING_EDITED';
export const RESET_EDITING = 'RESET_EDITING';
export const CLEAR_MODAL = 'CLEAR_MODAL';
export const REQUEST_VARIABLES = 'REQUEST_VARIABLES';
export const RECEIVE_VARIABLES_SUCCESS = 'RECEIVE_VARIABLES_SUCCESS';
export const REQUEST_DELETE_VARIABLE = 'REQUEST_DELETE_VARIABLE';
export const RECEIVE_DELETE_VARIABLE_SUCCESS = 'RECEIVE_DELETE_VARIABLE_SUCCESS';
export const RECEIVE_DELETE_VARIABLE_ERROR = 'RECEIVE_DELETE_VARIABLE_ERROR';
export const REQUEST_ADD_VARIABLE = 'REQUEST_ADD_VARIABLE';
export const RECEIVE_ADD_VARIABLE_SUCCESS = 'RECEIVE_ADD_VARIABLE_SUCCESS';
export const RECEIVE_ADD_VARIABLE_ERROR = 'RECEIVE_ADD_VARIABLE_ERROR';
export const REQUEST_UPDATE_VARIABLE = 'REQUEST_UPDATE_VARIABLE';
export const RECEIVE_UPDATE_VARIABLE_SUCCESS = 'RECEIVE_UPDATE_VARIABLE_SUCCESS';
export const RECEIVE_UPDATE_VARIABLE_ERROR = 'RECEIVE_UPDATE_VARIABLE_ERROR';
export const REQUEST_ENVIRONMENTS = 'REQUEST_ENVIRONMENTS';
export const RECEIVE_ENVIRONMENTS_SUCCESS = 'RECEIVE_ENVIRONMENTS_SUCCESS';
import * as types from './mutation_types';
import { __ } from '~/locale';
export default {
[types.REQUEST_VARIABLES](state) {
state.isLoading = true;
},
[types.RECEIVE_VARIABLES_SUCCESS](state, variables) {
state.isLoading = false;
state.variables = variables;
},
[types.REQUEST_DELETE_VARIABLE](state) {
state.isDeleting = true;
},
[types.RECEIVE_DELETE_VARIABLE_SUCCESS](state) {
state.isDeleting = false;
},
[types.RECEIVE_DELETE_VARIABLE_ERROR](state, error) {
state.isDeleting = false;
state.error = error;
},
[types.REQUEST_ADD_VARIABLE](state) {
state.isLoading = true;
},
[types.RECEIVE_ADD_VARIABLE_SUCCESS](state) {
state.isLoading = false;
},
[types.RECEIVE_ADD_VARIABLE_ERROR](state, error) {
state.isLoading = false;
state.error = error;
},
[types.REQUEST_UPDATE_VARIABLE](state) {
state.isLoading = true;
},
[types.RECEIVE_UPDATE_VARIABLE_SUCCESS](state) {
state.isLoading = false;
},
[types.RECEIVE_UPDATE_VARIABLE_ERROR](state, error) {
state.isLoading = false;
state.error = error;
},
[types.TOGGLE_VALUES](state, valueState) {
state.valuesHidden = valueState;
},
[types.REQUEST_ENVIRONMENTS](state) {
state.isLoading = true;
},
[types.RECEIVE_ENVIRONMENTS_SUCCESS](state, environments) {
state.isLoading = false;
state.environments = environments;
state.environments.unshift(__('All environments'));
},
[types.VARIABLE_BEING_EDITED](state, variable) {
state.variableBeingEdited = variable;
},
[types.CLEAR_MODAL](state) {
state.variable = {
variable_type: __('Variable'),
key: '',
secret_value: '',
protected: false,
masked: false,
environment_scope: __('All environments'),
};
},
[types.RESET_EDITING](state) {
state.variableBeingEdited = null;
state.showInputValue = false;
},
};
import { __ } from '~/locale';
export default () => ({
endpoint: null,
projectId: null,
isGroup: null,
maskableRegex: null,
isLoading: false,
isDeleting: false,
variable: {
variable_type: __('Variable'),
key: '',
secret_value: '',
protected: false,
masked: false,
environment_scope: __('All environments'),
},
variables: null,
valuesHidden: true,
error: null,
environments: [],
typeOptions: [__('Variable'), __('File')],
variableBeingEdited: null,
});
import { __ } from '~/locale';
const variableType = 'env_var';
const fileType = 'file';
const variableTypeHandler = type => (type === 'Variable' ? variableType : fileType);
export const prepareDataForDisplay = variables => {
const variablesToDisplay = [];
variables.forEach(variable => {
const variableCopy = variable;
if (variableCopy.variable_type === variableType) {
variableCopy.variable_type = __('Variable');
} else {
variableCopy.variable_type = __('File');
}
if (variableCopy.environment_scope === '*') {
variableCopy.environment_scope = __('All environments');
}
variablesToDisplay.push(variableCopy);
});
return variablesToDisplay;
};
export const prepareDataForApi = (variable, destroy = false) => {
const variableCopy = variable;
variableCopy.protected.toString();
variableCopy.masked.toString();
variableCopy.variable_type = variableTypeHandler(variableCopy.variable_type);
if (variableCopy.environment_scope === __('All environments')) {
variableCopy.environment_scope = __('*');
}
if (destroy) {
// eslint-disable-next-line
variableCopy._destroy = destroy;
}
return variableCopy;
};
export const prepareEnvironments = environments => environments.map(e => e.name);
......@@ -102,7 +102,6 @@
padding-bottom: 0.3em;
border-bottom: 1px solid $white-dark;
color: $gl-text-color;
overflow: hidden;
&:first-child {
margin-top: 0;
......@@ -116,7 +115,6 @@
padding-bottom: 0.3em;
border-bottom: 1px solid $white-dark;
color: $gl-text-color;
overflow: hidden;
}
h3 {
......
......@@ -7,7 +7,7 @@ class AdminEmailWorker
include CronjobQueue
# rubocop:enable Scalability/CronWorkerContext
feature_category_not_owned!
feature_category :source_code_management
def perform
send_repository_check_mail if Gitlab::CurrentSettings.repository_checks_enabled
......
......@@ -58,7 +58,7 @@
:resource_boundary: :unknown
:weight: 1
- :name: cronjob:admin_email
:feature_category: :not_owned
:feature_category: :source_code_management
:has_external_dependencies:
:latency_sensitive:
:resource_boundary: :unknown
......@@ -88,7 +88,7 @@
:resource_boundary: :unknown
:weight: 1
- :name: cronjob:gitlab_usage_ping
:feature_category: :not_owned
:feature_category: :collection
:has_external_dependencies:
:latency_sensitive:
:resource_boundary: :unknown
......@@ -142,7 +142,7 @@
:resource_boundary: :cpu
:weight: 1
- :name: cronjob:prune_old_events
:feature_category: :not_owned
:feature_category: :users
:has_external_dependencies:
:latency_sensitive:
:resource_boundary: :unknown
......
......@@ -9,7 +9,7 @@ class GitlabUsagePingWorker
include CronjobQueue
# rubocop:enable Scalability/CronWorkerContext
feature_category_not_owned!
feature_category :collection
# Retry for up to approximately three hours then give up.
sidekiq_options retry: 10, dead: false
......
......@@ -7,7 +7,7 @@ class PruneOldEventsWorker
include CronjobQueue
# rubocop:enable Scalability/CronWorkerContext
feature_category_not_owned!
feature_category :users
DELETE_LIMIT = 10_000
......
---
title: Do not draw heading borders over floated images in markdown
merge_request:
author: Gwen_
type: fixed
......@@ -619,7 +619,7 @@ provided by `gitlab-ctl`.
Consider the following example, where you first build the image:
```bash
```shell
# This builds a image with content of sha256:111111
docker build -t my.registry.com/my.group/my.project:latest .
docker push my.registry.com/my.group/my.project:latest
......@@ -627,7 +627,7 @@ docker push my.registry.com/my.group/my.project:latest
Now, you do overwrite `:latest` with a new version:
```bash
```shell
# This builds a image with content of sha256:222222
docker build -t my.registry.com/my.group/my.project:latest .
docker push my.registry.com/my.group/my.project:latest
......@@ -774,7 +774,7 @@ once a week.
Create a file under `/etc/cron.d/registry-garbage-collect`:
```bash
```shell
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
......
......@@ -88,11 +88,10 @@ pattern (`*~`).
The hooks are searched and executed in this order:
1. `gitlab-shell/hooks` directory as known to Gitaly.
1. `<project>.git/hooks/<hook_name>` - executed by `git` itself, this is symlinked to `gitlab-shell/hooks/<hook_name>`.
1. Built-in GitLab server hooks (not user-customizable).
1. `<project>.git/custom_hooks/<hook_name>` - per-project hook (this was kept as the already existing behavior).
1. `<project>.git/custom_hooks/<hook_name>.d/*` - per-project hooks.
1. `<project>.git/hooks/<hook_name>.d/*` OR `<custom_hooks_dir>/<hook_name.d>/*` - global hooks: all executable files (except editor backup files).
1. `<custom_hooks_dir>/<hook_name>.d/*` - global hooks: all executable files (except editor backup files).
The hooks of the same type are executed in order and execution stops on the
first script exiting with a non-zero value.
......
......@@ -74,7 +74,7 @@ and they will assist you with any issues you are having.
- How to get cronjobs configured on a cluster
```bash
```shell
kubectl get cronjobs
```
......
......@@ -17,7 +17,7 @@ Currently, these levels are recognized:
Gets a list of protected environments from a project:
```bash
```shell
GET /projects/:id/protected_environments
```
......@@ -25,7 +25,7 @@ GET /projects/:id/protected_environments
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. |
```bash
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" 'https://gitlab.example.com/api/v4/projects/5/protected_environments/'
```
......@@ -51,7 +51,7 @@ Example response:
Gets a single protected environment:
```bash
```shell
GET /projects/:id/protected_environments/:name
```
......@@ -60,7 +60,7 @@ GET /projects/:id/protected_environments/:name
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `name` | string | yes | The name of the protected environment |
```bash
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" 'https://gitlab.example.com/api/v4/projects/5/protected_environments/production'
```
......@@ -84,11 +84,11 @@ Example response:
Protects a single environment:
```bash
```shell
POST /projects/:id/protected_environments
```
```bash
```shell
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" 'https://gitlab.example.com/api/v4/projects/5/protected_environments?name=staging&deploy_access_levels%5B%5D%5Buser_id%5D=1'
```
......@@ -122,11 +122,11 @@ Example response:
Unprotects the given protected environment:
```bash
```shell
DELETE /projects/:id/protected_environments/:name
```
```bash
```shell
curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" 'https://gitlab.example.com/api/v4/projects/5/protected_environments/staging'
```
......
......@@ -50,6 +50,7 @@ To get started with GitLab CI/CD, we recommend you read through
the following documents:
- [How GitLab CI/CD works](introduction/index.md#how-gitlab-cicd-works).
- [Fundamental pipeline architectures](pipelines/pipeline_architectures.md).
- [GitLab CI/CD basic workflow](introduction/index.md#basic-cicd-workflow).
- [Step-by-step guide for writing `.gitlab-ci.yml` for the first time](../user/project/pages/getting_started_part_four.md).
......
......@@ -397,7 +397,7 @@ Before the new extended Docker configuration options, you would need to create
your own image based on the `super/sql:latest` image, add the default command,
and then use it in job's configuration, like:
```Dockerfile
```dockerfile
# my-super-sql:latest image's Dockerfile
FROM super/sql:latest
......
......@@ -63,7 +63,7 @@ Next, we'll create a small subset of tests that exemplify most of the states I e
this `Weapon` class to go through. To get started, create a folder called `lib/tests`
and add the following code to a new file `weaponTests.ts`:
```ts
```typescript
import { expect } from 'chai';
import { Weapon, BulletFactory } from '../lib/weapon';
......@@ -114,7 +114,7 @@ describe('Weapon', () => {
To build and run these tests using gulp, let's also add the following gulp functions
to the existing `gulpfile.js` file:
```ts
```typescript
gulp.task('build-test', function () {
return gulp.src('src/tests/**/*.ts', { read: false })
.pipe(tap(function (file) {
......@@ -140,7 +140,7 @@ to trigger the weapon. In the `src/lib` folder create a `weapon.ts` file. We'll
to it: `Weapon` and `BulletFactory` which will encapsulate Phaser's **sprite** and
**group** objects, and the logic specific to our game.
```ts
```typescript
export class Weapon {
private isTriggered: boolean = false;
private currentTimer: number = 0;
......@@ -210,7 +210,7 @@ export class BulletFactory {
Lastly, we'll redo our entry point, `game.ts`, to tie together both `Player` and `Weapon` objects
as well as add them to the update loop. Here is what the updated `game.ts` file looks like:
```ts
```typescript
import { Player } from "./player";
import { Weapon, BulletFactory } from "./weapon";
......
---
type: reference
---
# Pipeline Architecture
Pipelines are the fundamental building blocks for CI/CD in GitLab. This page documents
some of the important concepts related to them.
There are three main ways to structure your pipelines, each with their
own advantages. These methods can be mixed and matched if needed:
- [Basic](#basic-pipelines): Good for straightforward projects where all the configuration is in one easy to find place.
- [Directed Acylic Graph](#directed-acyclic-graph-pipelines): Good for large, complex projects that need efficient execution.
- [Child/Parent Pipelines](#child--parent-pipelines): Good for monorepos and projects with lots of independently defined components.
For more details about
any of the keywords used below, check out our [CI YAML reference](../yaml/) for details.
## Basic Pipelines
This is the simplest pipeline in GitLab. It will run everything in the build stage concurrently,
and once all of those finish, it will run everything in the test stage the same way, and so on.
It's not the most efficient, and if you have lots of steps it can grow quite complex, but it's
easier to maintain:
```mermaid
graph LR
subgraph deploy stage
deploy --> deploy_a
deploy --> deploy_b
end
subgraph test stage
test --> test_a
test --> test_b
end
subgraph build stage
build --> build_a
build --> build_b
end
build_a -.-> test
build_b -.-> test
test_a -.-> deploy
test_b -.-> deploy
```
Example basic `/.gitlab-ci.yml` pipeline configuration matching the diagram:
```yaml
stages:
- build
- test
- deploy
image: alpine
build_a:
stage: build
script:
- echo "This job builds something."
build_b:
stage: build
script:
- echo "This job builds something else."
test_a:
stage: test
script:
- echo "This job tests something. It will only run when all jobs in the"
- echo "build stage are complete."
test_b:
stage: test
script:
- echo "This job tests something else. It will only run when all jobs in the"
- echo "build stage are complete too. It will start at about the same time as test_a."
deploy_a:
stage: deploy
script:
- echo "This job deploys something. It will only run when all jobs in the"
- echo "test stage complete."
deploy_b:
stage: deploy
script:
- echo "This job deploys something else. It will only run when all jobs in the"
- echo "test stage complete. It will start at about the same time as deploy_a."
```
## Directed Acyclic Graph Pipelines
If efficiency is important to you and you want everything to run as quickly as possible,
you can use [Directed Acylic Graphs (DAG)](../directed_acyclic_graph/index.md). Use the
[`needs` keyword](../yaml/README.md#needs) to define dependency relationships between
your jobs. When GitLab knows the relationships between your jobs, it can run everything
as fast as possible, and even skips into subsequent stages when possible.
In the example below, if `build_a` and `test_a` are much faster than `build_b` and
`test_b`, GitLab will start `deploy_a` even if `build_b` is still running.
```mermaid
graph LR
subgraph Pipeline using DAG
build_a --> test_a --> deploy_a
build_b --> test_b --> deploy_b
end
```
Example DAG `/.gitlab-ci.yml` configuration matching the diagram:
```yaml
stages:
- build
- test
- deploy
image: alpine
build_a:
stage: build
script:
- echo "This job builds something quickly."
build_b:
stage: build
script:
- echo "This job builds something else slowly."
test_a:
stage: test
needs: build_a
script:
- echo "This test job will start as soon as build_a finishes."
- echo "It will not wait for build_b, or other jobs in the build stage, to finish."
test_b:
stage: test
needs: build_b
script:
- echo "This test job will start as soon as build_b finishes."
- echo "It will not wait for other jobs in the build stage to finish."
deploy_a:
stage: deploy
needs: test_a
script:
- echo "Since build_a and test_a run quickly, this deploy job can run much earlier."
- echo "It does not need to wait for build_b or test_b."
deploy_b:
stage: deploy
needs: test_b
script:
- echo "Since build_b and test_b run slowly, this deploy job will run much later."
```
## Child / Parent Pipelines
In the examples above, it's clear we've got two types of things that could be built independently.
This is an ideal case for using [Child / Parent Pipelines](../parent_child_pipelines.md)) via
the [`trigger` keyword](../yaml/README.md#trigger). It will separate out the configuration
into multiple files, keeping things very simple. You can also combine this with:
- The [`rules` keyword](../yaml/README.md#rules): For example, have the child pipelines triggered only
when there are changes to that area.
- The [`include` keyword](../yaml/README.md#include): Bring in common behaviors, ensuring
you are not repeating yourself.
- [DAG pipelines](#directed-acyclic-graph-pipelines) inside of child pipelines, achieving the benefits of both.
```mermaid
graph LR
subgraph Parent pipeline
trigger_a -.-> build_a
trigger_b -.-> build_b
subgraph child pipeline B
build_b --> test_b --> deploy_b
end
subgraph child pipeline A
build_a --> test_a --> deploy_a
end
end
```
Example `/.gitlab-ci.yml` configuration for the parent pipeline matching the diagram:
```yaml
stages:
- triggers
trigger_a:
stage: triggers
trigger:
include: a/.gitlab-ci.yml
rules:
- changes:
- a/*
trigger_b:
stage: triggers
trigger:
include: b/.gitlab-ci.yml
rules:
- changes:
- b/*
```
Example child `a` pipeline configuration, located in `/a/.gitlab-ci.yml`, making
use of the DAG `needs:` keyword:
```yaml
stages:
- build
- test
- deploy
image: alpine
build_a:
stage: build
script:
- echo "This job builds something."
test_a:
stage: test
needs: build_a
script:
- echo "This job tests something."
deploy_a:
stage: deploy
needs: test_a
script:
- echo "This job deploys something."
```
Example child `b` pipeline configuration, located in `/b/.gitlab-ci.yml`, making
use of the DAG `needs:` keyword:
```yaml
stages:
- build
- test
- deploy
image: alpine
build_b:
stage: build
script:
- echo "This job builds something else."
test_b:
stage: test
needs: build_b
script:
- echo "This job tests something else."
deploy_b:
stage: deploy
needs: test_b
script:
- echo "This job deploys something else."
```
It's also possible to set jobs to run before or after triggering child pipelines,
for example if you have common setup steps or a unified deployment at the end.
......@@ -17,6 +17,11 @@ NOTE: **Note:**
Coming over to GitLab from Jenkins? Check out our [reference](../jenkins/index.md)
for converting your pre-existing pipelines over to our format.
NOTE: **Note:**
There are a few different [basic pipeline architectures](../pipelines/pipeline_architectures.md)
that you can consider for use in your project. You may want to familiarize
yourself with these prior to getting started.
GitLab offers a [continuous integration](https://about.gitlab.com/stages-devops-lifecycle/continuous-integration/) service. For each commit or push to trigger your CI
[pipeline](../pipelines.md), you must:
......
......@@ -17,12 +17,12 @@ First, in your `.gitlab-ci.yml` add:
```yaml
services:
- postgres:latest
- postgres:12.2-alpine
variables:
POSTGRES_DB: nice_marmot
POSTGRES_USER: runner
POSTGRES_PASSWORD: ""
POSTGRES_PASSWORD: "runner-password"
```
NOTE: **Note:**
......@@ -37,7 +37,7 @@ And then configure your application to use the database, for example:
```yaml
Host: postgres
User: runner
Password:
Password: runner-password
Database: nice_marmot
```
......
......@@ -7,7 +7,7 @@ check if a comment is still relevant and what needs to be done to address it.
Examples:
```rb
```ruby
# Deprecated scope until code_owner column has been migrated to rule_type.
# To be removed with https://gitlab.com/gitlab-org/gitlab/issues/11834.
scope :code_owner, -> { where(code_owner: true).or(where(rule_type: :code_owner)) }
......
......@@ -8,7 +8,7 @@ To use this type, add `limit: 2` to the migration that creates the column.
Example:
```rb
```ruby
def change
add_column :ci_job_artifacts, :file_format, :integer, limit: 2
end
......
......@@ -783,33 +783,64 @@ nicely on different mobile devices.
- When providing a shell command and its output, prefix the shell command with `$` and
leave a blank line between the command and the output.
- When providing a command without output, don't prefix the shell command with `$`.
- If you need to include triple backticks inside a code block, use four backticks
for the codeblock fences instead of three.
- For regular code blocks, always use a highlighting class corresponding to the
language for better readability. Examples:
~~~md
````markdown
```ruby
Ruby code
```
```js
```javascript
JavaScript code
```
```md
```markdown
[Markdown code example](example.md)
```
```text
```plaintext
Code or text for which no specific highlighting class is available.
```
~~~
- To display raw Markdown instead of rendered Markdown, you can use triple backticks
with `md`, like the `Markdown code` example above, unless you want to include triple
backticks in the code block as well. In that case, use triple tildes (`~~~`) instead.
- [Syntax highlighting for code blocks](https://github.com/rouge-ruby/rouge/wiki/List-of-supported-languages-and-lexers)
is available for many languages. Use `shell` instead of `bash` or `sh` for shell output.
- For a complete reference on code blocks, check the [Kramdown guide](https://about.gitlab.com/handbook/product/technical-writing/markdown-guide/#code-blocks).
````
Syntax highlighting is required for code blocks added to the GitLab documentation.
Refer to the table below for the most common language classes, or check the
[complete list](https://github.com/rouge-ruby/rouge/wiki/List-of-supported-languages-and-lexers)
of language classes available.
| Preferred language tags | Language aliases and notes |
|-------------------------|------------------------------------------------------------------------------|
| `asciidoc` | |
| `dockerfile` | Alias: `docker`. |
| `elixir` | |
| `erb` | |
| `golang` | Alias: `go`. |
| `graphql` | |
| `haml` | |
| `html` | |
| `ini` | For some simple config files that are not in TOML format. |
| `javascript` | Alias `js`. |
| `json` | |
| `markdown` | Alias: `md`. |
| `mermaid` | |
| `nginx` | |
| `perl` | |
| `php` | |
| `plaintext` | Examples with no defined language, such as output from shell commands or API calls. If a codeblock has no language, it defaults to `plaintext`. Alias: `text`. |
| `prometheus` | Prometheus configuration examples. |
| `python` | |
| `ruby` | Alias: `rb`. |
| `shell` | Aliases: `bash` or `sh`. |
| `sql` | |
| `toml` | Runner configuration examples, and other toml formatted configuration files. |
| `typescript` | Alias: `ts`. |
| `xml` | |
| `yaml` | Alias: `yml`. |
For a complete reference on code blocks, check the [Kramdown guide](https://about.gitlab.com/handbook/product/technical-writing/markdown-guide/#code-blocks).
## GitLab SVG icons
......
......@@ -240,13 +240,13 @@ Here are the steps to gate a new feature in Gitaly behind a feature flag.
1. Create a package scoped flag name:
```go
```golang
var findAllTagsFeatureFlag = "go-find-all-tags"
```
1. Create a switch in the code using the `featureflag` package:
```go
```golang
if featureflag.IsEnabled(ctx, findAllTagsFeatureFlag) {
// go implementation
} else {
......@@ -256,7 +256,7 @@ Here are the steps to gate a new feature in Gitaly behind a feature flag.
1. Create Prometheus metrics:
```go
```golang
var findAllTagsRequests = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "gitaly_find_all_tags_requests_total",
......@@ -280,7 +280,7 @@ Here are the steps to gate a new feature in Gitaly behind a feature flag.
1. Set headers in tests:
```go
```golang
import (
"google.golang.org/grpc/metadata"
......
......@@ -195,7 +195,7 @@ When comparing expected and actual values in tests, use
and others to improve readability when comparing structs, errors,
large portions of text, or JSON documents:
```go
```golang
type TestData struct {
// ...
}
......
......@@ -918,12 +918,12 @@ instead of the default `ruby:latest`:
1. Set `AUTO_DEVOPS_BUILD_IMAGE_EXTRA_ARGS` to `--build-arg=RUBY_VERSION=alpine`.
1. Add the following to a custom `Dockerfile`:
```docker
ARG RUBY_VERSION=latest
FROM ruby:$RUBY_VERSION
```dockerfile
ARG RUBY_VERSION=latest
FROM ruby:$RUBY_VERSION
# ... put your stuff here
```
# ... put your stuff here
```
NOTE: **Note:**
Passing in complex values (newlines and spaces, for example) will likely
......@@ -955,14 +955,14 @@ In projects:
1. Activate the experimental `Dockerfile` syntax by adding the following
to the top of the file:
```docker
```dockerfile
# syntax = docker/dockerfile:experimental
```
1. To make secrets available in any `RUN $COMMAND` in the `Dockerfile`, mount
the secret file and source it prior to running `$COMMAND`:
```docker
```dockerfile
RUN --mount=type=secret,id=auto-devops-build-secrets . /run/secrets/auto-devops-build-secrets && $COMMAND
```
......
......@@ -80,11 +80,11 @@ under which this application will be deployed.
![Google auth](../autodevops/img/guide_google_auth_v12_3.png)
1. The last step is to provide the cluster details.
1. Give it a name, leave the environment scope as is, and choose the GCP project under which the cluster
will be created (per the instructions to [configure your Google account](#configuring-your-google-account), a project should have already been created for you).
1. Choose the [region/zone](https://cloud.google.com/compute/docs/regions-zones/) under which the cluster will be created.
1. Enter the number of nodes you want it to have.
1. Choose the [machine type](https://cloud.google.com/compute/docs/machine-types).
1. Give it a name, leave the environment scope as is, and choose the GCP project under which the cluster
will be created (per the instructions to [configure your Google account](#configuring-your-google-account), a project should have already been created for you).
1. Choose the [region/zone](https://cloud.google.com/compute/docs/regions-zones/) under which the cluster will be created.
1. Enter the number of nodes you want it to have.
1. Choose the [machine type](https://cloud.google.com/compute/docs/machine-types).
![GitLab GKE cluster details](../autodevops/img/guide_gitlab_gke_details_v12_3.png)
......@@ -180,40 +180,40 @@ your cluster either using [Cloud Shell](https://cloud.google.com/shell/) or the
1. After connecting to your cluster, check if the Ingress-NGINX controller is running and ModSecurity is enabled.
This is done by running the following commands:
This is done by running the following commands:
```bash
$ kubectl get pods -n gitlab-managed-apps | grep 'ingress-controller'
ingress-nginx-ingress-controller-55f9cf6584-dxljn 2/2 Running
```shell
$ kubectl get pods -n gitlab-managed-apps | grep 'ingress-controller'
ingress-nginx-ingress-controller-55f9cf6584-dxljn 2/2 Running
$ kubectl -n gitlab-managed-apps exec -it $(kubectl get pods -n gitlab-managed-apps | grep 'ingress-controller' | awk '{print $1}') -- cat /etc/nginx/nginx.conf | grep 'modsecurity on;'
modsecurity on;
```
$ kubectl -n gitlab-managed-apps exec -it $(kubectl get pods -n gitlab-managed-apps | grep 'ingress-controller' | awk '{print $1}') -- cat /etc/nginx/nginx.conf | grep 'modsecurity on;'
modsecurity on;
```
1. Verify the Rails application has been installed properly.
```bash
$ kubectl get ns
auto-devv-2-16730183-production Active
```shell
$ kubectl get ns
auto-devv-2-16730183-production Active
$ kubectl get pods -n auto-devv-2-16730183-production
NAME READY STATUS RESTARTS
production-5778cfcfcd-nqjcm 1/1 Running 0
production-postgres-6449f8cc98-r7xgg 1/1 Running 0
```
$ kubectl get pods -n auto-devv-2-16730183-production
NAME READY STATUS RESTARTS
production-5778cfcfcd-nqjcm 1/1 Running 0
production-postgres-6449f8cc98-r7xgg 1/1 Running 0
```
1. To make sure the Rails application is responding, send a request to it by running:
```bash
$ kubectl get ing -n auto-devv-2-16730183-production
NAME HOSTS PORTS
production-auto-deploy fjdiaz-auto-devv-2.34.68.60.207.nip.io,le-16730183.34.68.60.207.nip.io 80, 443
```shell
$ kubectl get ing -n auto-devv-2-16730183-production
NAME HOSTS PORTS
production-auto-deploy fjdiaz-auto-devv-2.34.68.60.207.nip.io,le-16730183.34.68.60.207.nip.io 80, 443
$ curl --location --insecure fjdiaz-auto-devv-2.34.68.60.207.nip.io | grep 'Rails!' --after 2 --before 2
<body>
<p>You're on Rails!</p>
</body>
```
$ curl --location --insecure fjdiaz-auto-devv-2.34.68.60.207.nip.io | grep 'Rails!' --after 2 --before 2
<body>
<p>You're on Rails!</p>
</body>
```
Now that we have confirmed our system is properly setup, we can go ahead and test
the WAF with OWASP CRS!
......@@ -223,7 +223,7 @@ the WAF with OWASP CRS!
Now let's send a potentially malicious request, as if we were a scanner,
checking for vulnerabilities within our application and examine the modsecurity logs:
```bash
```shell
$ curl --location --insecure fjdiaz-auto-devv-2.34.68.60.207.nip.io --header "User-Agent: absinthe" | grep 'Rails!' --after 2 --before 2
<body>
<p>You're on Rails!</p>
......
......@@ -55,6 +55,17 @@ will help you to quickly create a deployment:
1. Navigate to your project's **CI/CD > Pipelines** page, and run a pipeline on any branch.
1. When the pipeline has run successfully, graphs will be available on the **Operations > Metrics** page.
![Monitoring Dashboard](img/prometheus_monitoring_dashboard_v12_8.png)
#### Using the Metrics Dashboard
##### Select an environment
The **Environment** dropdown box above the dashboard displays the list of all [environments](#monitoring-cicd-environments).
It enables you to search as you type through all environments and select the one you're looking for.
![Monitoring Dashboard Environments](img/prometheus_dashboard_environments_v12_8.png)
#### About managed Prometheus deployments
Prometheus is deployed into the `gitlab-managed-apps` namespace, using the [official Helm chart](https://github.com/helm/charts/tree/master/stable/prometheus). Prometheus is only accessible within the cluster, with GitLab communicating through the [Kubernetes API](https://kubernetes.io/docs/concepts/overview/kubernetes-api/).
......@@ -428,6 +439,29 @@ Note the following properties:
![single stat panel type](img/prometheus_dashboard_single_stat_panel_type.png)
###### Percentile based results
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/201946) in GitLab 12.8.
Query results sometimes need to be represented as a percentage value out of 100. You can use the `max_value` property at the root of the panel definition:
```yaml
dashboard: 'Dashboard Title'
panel_groups:
- group: 'Group Title'
panels:
- title: "Single Stat"
type: "single-stat"
max_value: 100
metrics:
- id: 10
query: 'max(go_memstats_alloc_bytes{job="prometheus"})'
unit: '%'
label: "Total"
```
For example, if you have a query value of `53.6`, adding `%` as the unit results in a single stat value of `53.6%`, but if the maximum expected value of the query is `120`, the value would be `44.6%`. Adding the `max_value` causes the correct percentage value to display.
##### Heatmaps
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/30581) in GitLab 12.5.
......
performance:
stage: performance
# pin to a version matching the dind service, just to be safe
image: docker:19.03.5
allow_failure: true
variables:
DOCKER_TLS_CERTDIR: ""
services:
# pin to a known working version until https://gitlab.com/gitlab-org/gitlab-runner/issues/6697 is fixed
- docker:19.03.5-dind
script:
- |
......
......@@ -4,7 +4,6 @@ build:
variables:
DOCKER_TLS_CERTDIR: ""
services:
# pin to a known working version until https://gitlab.com/gitlab-org/gitlab-runner/issues/6697 is fixed
- docker:19.03.5-dind
script:
- |
......
code_quality:
stage: test
# pin to a version matching the dind service, just to be safe
image: docker:19.03.5
allow_failure: true
services:
# pin to a known working version until https://gitlab.com/gitlab-org/gitlab-runner/issues/6697 is fixed
- docker:19.03.5-dind
variables:
DOCKER_DRIVER: overlay2
......
......@@ -48,7 +48,8 @@ module Quality
resource_names = raw_resource_names
command = [
'delete',
%(--namespace "#{namespace}")
%(--namespace "#{namespace}"),
'--ignore-not-found'
]
Array(release_name).each do |release|
......
......@@ -534,6 +534,9 @@ msgstr ""
msgid "(removed)"
msgstr ""
msgid "*"
msgstr ""
msgid "+ %{amount} more"
msgstr ""
......@@ -1544,6 +1547,9 @@ msgstr ""
msgid "All email addresses will be used to identify your commits."
msgstr ""
msgid "All environments"
msgstr ""
msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
msgstr ""
......@@ -8385,6 +8391,9 @@ msgstr ""
msgid "Fetching licenses failed. You are not permitted to perform this action."
msgstr ""
msgid "File"
msgstr ""
msgid "File Hooks"
msgstr ""
......@@ -19425,6 +19434,12 @@ msgstr ""
msgid "There was an error fetching the Designs"
msgstr ""
msgid "There was an error fetching the environments information."
msgstr ""
msgid "There was an error fetching the variables."
msgstr ""
msgid "There was an error fetching value stream analytics stages."
msgstr ""
......@@ -21367,6 +21382,9 @@ msgstr ""
msgid "Value Stream Analytics gives an overview of how much time it takes to go from idea to production in your project."
msgstr ""
msgid "Variable"
msgstr ""
msgid "Variables"
msgstr ""
......
export default {
mockVariables: [
{
environment_scope: 'All environments',
id: 113,
key: 'test_var',
masked: false,
protected: false,
value: 'test_val',
variable_type: 'Variable',
},
{
environment_scope: 'All environments',
id: 114,
key: 'test_var_2',
masked: false,
protected: false,
value: 'test_val_2',
variable_type: 'Variable',
},
{
environment_scope: 'All environments',
id: 115,
key: 'test_var_3',
masked: false,
protected: false,
value: 'test_val_3',
variable_type: 'Variable',
},
],
mockVariablesApi: [
{
environment_scope: '*',
id: 113,
key: 'test_var',
masked: false,
protected: false,
value: 'test_val',
variable_type: 'env_var',
},
{
environment_scope: '*',
id: 114,
key: 'test_var_2',
masked: false,
protected: false,
value: 'test_val_2',
variable_type: 'file',
},
],
mockVariablesDisplay: [
{
environment_scope: 'All environments',
id: 113,
key: 'test_var',
masked: false,
protected: false,
value: 'test_val',
variable_type: 'Variable',
},
{
environment_scope: 'All environments',
id: 114,
key: 'test_var_2',
masked: false,
protected: false,
value: 'test_val_2',
variable_type: 'File',
},
],
mockEnvironments: [
{
id: 28,
name: 'staging',
slug: 'staging',
external_url: 'https://staging.example.com',
state: 'available',
},
{
id: 29,
name: 'production',
slug: 'production',
external_url: 'https://production.example.com',
state: 'available',
},
],
};
import Api from '~/api';
import MockAdapter from 'axios-mock-adapter';
import testAction from 'helpers/vuex_action_helper';
import axios from '~/lib/utils/axios_utils';
import createFlash from '~/flash';
import getInitialState from '~/ci_variable_list/store/state';
import * as actions from '~/ci_variable_list/store/actions';
import * as types from '~/ci_variable_list/store/mutation_types';
import mockData from '../services/mock_data';
import { prepareDataForDisplay, prepareEnvironments } from '~/ci_variable_list/store/utils';
jest.mock('~/api.js');
jest.mock('~/flash.js');
describe('CI variable list store actions', () => {
let mock;
let state;
const mockVariable = {
environment_scope: '*',
id: 63,
key: 'test_var',
masked: false,
protected: false,
value: 'test_val',
variable_type: 'env_var',
_destory: true,
};
const payloadError = new Error('Request failed with status code 500');
beforeEach(() => {
mock = new MockAdapter(axios);
state = getInitialState();
state.endpoint = '/variables';
});
afterEach(() => {
mock.restore();
});
describe('toggleValues', () => {
const valuesHidden = false;
it('commits TOGGLE_VALUES mutation', () => {
testAction(actions.toggleValues, valuesHidden, {}, [
{
type: types.TOGGLE_VALUES,
payload: valuesHidden,
},
]);
});
});
describe('clearModal', () => {
it('commits CLEAR_MODAL mutation', () => {
testAction(actions.clearModal, {}, {}, [
{
type: types.CLEAR_MODAL,
},
]);
});
});
describe('resetEditing', () => {
it('commits RESET_EDITING mutation', () => {
testAction(
actions.resetEditing,
{},
{},
[
{
type: types.RESET_EDITING,
},
],
[{ type: 'fetchVariables' }],
);
});
});
describe('deleteVariable', () => {
it('dispatch correct actions on successful deleted variable', done => {
mock.onPatch(state.endpoint).reply(200);
testAction(
actions.deleteVariable,
mockVariable,
state,
[],
[
{ type: 'requestDeleteVariable' },
{ type: 'receiveDeleteVariableSuccess' },
{ type: 'fetchVariables' },
],
() => {
done();
},
);
});
it('should show flash error and set error in state on delete failure', done => {
mock.onPatch(state.endpoint).reply(500, '');
testAction(
actions.deleteVariable,
mockVariable,
state,
[],
[
{ type: 'requestDeleteVariable' },
{
type: 'receiveDeleteVariableError',
payload: payloadError,
},
],
() => {
expect(createFlash).toHaveBeenCalled();
done();
},
);
});
});
describe('updateVariable', () => {
it('dispatch correct actions on successful updated variable', done => {
mock.onPatch(state.endpoint).reply(200);
testAction(
actions.updateVariable,
mockVariable,
state,
[],
[
{ type: 'requestUpdateVariable' },
{ type: 'receiveUpdateVariableSuccess' },
{ type: 'fetchVariables' },
],
() => {
done();
},
);
});
it('should show flash error and set error in state on update failure', done => {
mock.onPatch(state.endpoint).reply(500, '');
testAction(
actions.updateVariable,
mockVariable,
state,
[],
[
{ type: 'requestUpdateVariable' },
{
type: 'receiveUpdateVariableError',
payload: payloadError,
},
],
() => {
expect(createFlash).toHaveBeenCalled();
done();
},
);
});
});
describe('addVariable', () => {
it('dispatch correct actions on successful added variable', done => {
mock.onPatch(state.endpoint).reply(200);
testAction(
actions.addVariable,
{},
state,
[],
[
{ type: 'requestAddVariable' },
{ type: 'receiveAddVariableSuccess' },
{ type: 'fetchVariables' },
],
() => {
done();
},
);
});
it('should show flash error and set error in state on add failure', done => {
mock.onPatch(state.endpoint).reply(500, '');
testAction(
actions.addVariable,
{},
state,
[],
[
{ type: 'requestAddVariable' },
{
type: 'receiveAddVariableError',
payload: payloadError,
},
],
() => {
expect(createFlash).toHaveBeenCalled();
done();
},
);
});
});
describe('fetchVariables', () => {
it('dispatch correct actions on fetchVariables', done => {
mock.onGet(state.endpoint).reply(200, { variables: mockData.mockVariables });
testAction(
actions.fetchVariables,
{},
state,
[],
[
{ type: 'requestVariables' },
{
type: 'receiveVariablesSuccess',
payload: prepareDataForDisplay(mockData.mockVariables),
},
],
() => {
done();
},
);
});
it('should show flash error and set error in state on fetch variables failure', done => {
mock.onGet(state.endpoint).reply(500);
testAction(actions.fetchVariables, {}, state, [], [{ type: 'requestVariables' }], () => {
expect(createFlash).toHaveBeenCalledWith('There was an error fetching the variables.');
done();
});
});
});
describe('fetchEnvironments', () => {
it('dispatch correct actions on fetchEnvironments', done => {
Api.environments = jest.fn().mockResolvedValue({ data: mockData.mockEnvironments });
testAction(
actions.fetchEnvironments,
{},
state,
[],
[
{ type: 'requestEnvironments' },
{
type: 'receiveEnvironmentsSuccess',
payload: prepareEnvironments(mockData.mockEnvironments),
},
],
() => {
done();
},
);
});
it('should show flash error and set error in state on fetch environments failure', done => {
Api.environments = jest.fn().mockRejectedValue();
testAction(
actions.fetchEnvironments,
{},
state,
[],
[{ type: 'requestEnvironments' }],
() => {
expect(createFlash).toHaveBeenCalledWith(
'There was an error fetching the environments information.',
);
done();
},
);
});
});
});
import state from '~/ci_variable_list/store/state';
import mutations from '~/ci_variable_list/store/mutations';
import * as types from '~/ci_variable_list/store/mutation_types';
describe('CI variable list mutations', () => {
let stateCopy;
beforeEach(() => {
stateCopy = state();
});
describe('TOGGLE_VALUES', () => {
it('should toggle state', () => {
const valuesHidden = false;
mutations[types.TOGGLE_VALUES](stateCopy, valuesHidden);
expect(stateCopy.valuesHidden).toEqual(valuesHidden);
});
});
describe('VARIABLE_BEING_EDITED', () => {
it('should set variable that is being edited', () => {
const variableBeingEdited = {
environment_scope: '*',
id: 63,
key: 'test_var',
masked: false,
protected: false,
value: 'test_val',
variable_type: 'env_var',
};
mutations[types.VARIABLE_BEING_EDITED](stateCopy, variableBeingEdited);
expect(stateCopy.variableBeingEdited).toEqual(variableBeingEdited);
});
});
describe('RESET_EDITING', () => {
it('should reset variableBeingEdited to null', () => {
mutations[types.RESET_EDITING](stateCopy);
expect(stateCopy.variableBeingEdited).toEqual(null);
});
});
describe('CLEAR_MODAL', () => {
it('should clear modal state ', () => {
const modalState = {
variable_type: 'Variable',
key: '',
secret_value: '',
protected: false,
masked: false,
environment_scope: 'All environments',
};
mutations[types.CLEAR_MODAL](stateCopy);
expect(stateCopy.variable).toEqual(modalState);
});
});
});
import {
prepareDataForDisplay,
prepareEnvironments,
prepareDataForApi,
} from '~/ci_variable_list/store/utils';
import mockData from '../services/mock_data';
describe('CI variables store utils', () => {
it('prepares ci variables for display', () => {
expect(prepareDataForDisplay(mockData.mockVariablesApi)).toStrictEqual(
mockData.mockVariablesDisplay,
);
});
it('prepares single ci variable for api', () => {
expect(prepareDataForApi(mockData.mockVariablesDisplay[0])).toStrictEqual({
environment_scope: '*',
id: 113,
key: 'test_var',
masked: false,
protected: false,
value: 'test_val',
variable_type: 'env_var',
});
expect(prepareDataForApi(mockData.mockVariablesDisplay[1])).toStrictEqual({
environment_scope: '*',
id: 114,
key: 'test_var_2',
masked: false,
protected: false,
value: 'test_val_2',
variable_type: 'file',
});
});
it('prepares single ci variable for delete', () => {
expect(prepareDataForApi(mockData.mockVariablesDisplay[0], true)).toHaveProperty(
'_destroy',
true,
);
});
it('prepares environments for display', () => {
expect(prepareEnvironments(mockData.mockEnvironments)).toStrictEqual(['staging', 'production']);
});
});
......@@ -38,7 +38,7 @@ RSpec.describe Quality::KubernetesClient do
.and_return(Gitlab::Popen::Result.new([], '', '', double(success?: true)))
expect(Gitlab::Popen).to receive(:popen_with_detail)
.with([%(kubectl delete --namespace "#{namespace}" #{pod_for_release})])
.with([%(kubectl delete --namespace "#{namespace}" --ignore-not-found #{pod_for_release})])
.and_return(Gitlab::Popen::Result.new([], '', '', double(success?: true)))
# We're not verifying the output here, just silencing it
......@@ -64,7 +64,7 @@ RSpec.describe Quality::KubernetesClient do
.and_return(Gitlab::Popen::Result.new([], '', '', double(success?: true)))
expect(Gitlab::Popen).to receive(:popen_with_detail)
.with([%(kubectl delete --namespace "#{namespace}" #{pod_for_release})])
.with([%(kubectl delete --namespace "#{namespace}" --ignore-not-found #{pod_for_release})])
.and_return(Gitlab::Popen::Result.new([], '', '', double(success?: true)))
# We're not verifying the output here, just silencing it
......@@ -89,7 +89,7 @@ RSpec.describe Quality::KubernetesClient do
.and_return(Gitlab::Popen::Result.new([], '', '', double(success?: true)))
expect(Gitlab::Popen).to receive(:popen_with_detail)
.with([%(kubectl delete --namespace "#{namespace}" #{pod_for_release})])
.with([%(kubectl delete --namespace "#{namespace}" --ignore-not-found #{pod_for_release})])
.and_return(Gitlab::Popen::Result.new([], '', '', double(success?: true)))
# We're not verifying the output here, just silencing it
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment