Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
63b3a14f15
commit
b3930fc34f
48 changed files with 690 additions and 1483 deletions
2
Gemfile
2
Gemfile
|
@ -160,7 +160,7 @@ gem 'wikicloth', '0.8.1'
|
|||
gem 'asciidoctor', '~> 2.0.10'
|
||||
gem 'asciidoctor-include-ext', '~> 0.3.1', require: false
|
||||
gem 'asciidoctor-plantuml', '~> 0.0.12'
|
||||
gem 'asciidoctor-kroki', '~> 0.3.0', require: false
|
||||
gem 'asciidoctor-kroki', '~> 0.4.0', require: false
|
||||
gem 'rouge', '~> 3.26.0'
|
||||
gem 'truncato', '~> 0.7.11'
|
||||
gem 'bootstrap_form', '~> 4.2.0'
|
||||
|
|
|
@ -84,7 +84,7 @@ GEM
|
|||
asciidoctor (2.0.12)
|
||||
asciidoctor-include-ext (0.3.1)
|
||||
asciidoctor (>= 1.5.6, < 3.0.0)
|
||||
asciidoctor-kroki (0.3.0)
|
||||
asciidoctor-kroki (0.4.0)
|
||||
asciidoctor (~> 2.0)
|
||||
asciidoctor-plantuml (0.0.12)
|
||||
asciidoctor (>= 1.5.6, < 3.0.0)
|
||||
|
@ -1334,7 +1334,7 @@ DEPENDENCIES
|
|||
asana (~> 0.10.3)
|
||||
asciidoctor (~> 2.0.10)
|
||||
asciidoctor-include-ext (~> 0.3.1)
|
||||
asciidoctor-kroki (~> 0.3.0)
|
||||
asciidoctor-kroki (~> 0.4.0)
|
||||
asciidoctor-plantuml (~> 0.0.12)
|
||||
atlassian-jwt (~> 0.2.0)
|
||||
attr_encrypted (~> 3.1.0)
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
import {
|
||||
GlDropdown,
|
||||
GlDropdownDivider,
|
||||
GlDropdownSectionHeader,
|
||||
GlSearchBoxByType,
|
||||
GlSprintf,
|
||||
GlIcon,
|
||||
|
@ -27,7 +26,6 @@ export default {
|
|||
components: {
|
||||
GlDropdown,
|
||||
GlDropdownDivider,
|
||||
GlDropdownSectionHeader,
|
||||
GlSearchBoxByType,
|
||||
GlSprintf,
|
||||
GlIcon,
|
||||
|
@ -161,8 +159,13 @@ export default {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<gl-dropdown v-bind="$attrs" class="ref-selector" @shown="focusSearchBox">
|
||||
<template slot="button-content">
|
||||
<gl-dropdown
|
||||
v-bind="$attrs"
|
||||
:header-text="i18n.dropdownHeader"
|
||||
class="ref-selector"
|
||||
@shown="focusSearchBox"
|
||||
>
|
||||
<template #button-content>
|
||||
<span class="gl-flex-grow-1 gl-ml-2 gl-text-gray-400" data-testid="button-content">
|
||||
<span v-if="selectedRef" class="gl-font-monospace">{{ selectedRef }}</span>
|
||||
<span v-else>{{ i18n.noRefSelected }}</span>
|
||||
|
@ -170,13 +173,7 @@ export default {
|
|||
<gl-icon name="chevron-down" />
|
||||
</template>
|
||||
|
||||
<div class="gl-display-flex gl-flex-direction-column ref-selector-dropdown-content">
|
||||
<gl-dropdown-section-header>
|
||||
<span class="gl-text-center gl-display-block">{{ i18n.dropdownHeader }}</span>
|
||||
</gl-dropdown-section-header>
|
||||
|
||||
<gl-dropdown-divider />
|
||||
|
||||
<template #header>
|
||||
<gl-search-box-by-type
|
||||
ref="searchBox"
|
||||
v-model.trim="query"
|
||||
|
@ -184,72 +181,66 @@ export default {
|
|||
@input="onSearchBoxInput"
|
||||
@keydown.enter.prevent="onSearchBoxEnter"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<div class="gl-flex-grow-1 gl-overflow-y-auto">
|
||||
<gl-loading-icon v-if="isLoading" size="lg" class="gl-my-3" />
|
||||
<gl-loading-icon v-if="isLoading" size="lg" class="gl-my-3" />
|
||||
|
||||
<div
|
||||
v-else-if="showNoResults"
|
||||
class="gl-text-center gl-mx-3 gl-py-3"
|
||||
data-testid="no-results"
|
||||
>
|
||||
<gl-sprintf v-if="lastQuery" :message="i18n.noResultsWithQuery">
|
||||
<template #query>
|
||||
<b class="gl-word-break-all">{{ lastQuery }}</b>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
|
||||
<span v-else>{{ i18n.noResults }}</span>
|
||||
</div>
|
||||
|
||||
<template v-else>
|
||||
<template v-if="showBranchesSection">
|
||||
<ref-results-section
|
||||
:section-title="i18n.branches"
|
||||
:total-count="matches.branches.totalCount"
|
||||
:items="matches.branches.list"
|
||||
:selected-ref="selectedRef"
|
||||
:error="matches.branches.error"
|
||||
:error-message="i18n.branchesErrorMessage"
|
||||
:show-header="showSectionHeaders"
|
||||
data-testid="branches-section"
|
||||
@selected="selectRef($event)"
|
||||
/>
|
||||
|
||||
<gl-dropdown-divider v-if="showTagsSection || showCommitsSection" />
|
||||
</template>
|
||||
|
||||
<template v-if="showTagsSection">
|
||||
<ref-results-section
|
||||
:section-title="i18n.tags"
|
||||
:total-count="matches.tags.totalCount"
|
||||
:items="matches.tags.list"
|
||||
:selected-ref="selectedRef"
|
||||
:error="matches.tags.error"
|
||||
:error-message="i18n.tagsErrorMessage"
|
||||
:show-header="showSectionHeaders"
|
||||
data-testid="tags-section"
|
||||
@selected="selectRef($event)"
|
||||
/>
|
||||
|
||||
<gl-dropdown-divider v-if="showCommitsSection" />
|
||||
</template>
|
||||
|
||||
<template v-if="showCommitsSection">
|
||||
<ref-results-section
|
||||
:section-title="i18n.commits"
|
||||
:total-count="matches.commits.totalCount"
|
||||
:items="matches.commits.list"
|
||||
:selected-ref="selectedRef"
|
||||
:error="matches.commits.error"
|
||||
:error-message="i18n.commitsErrorMessage"
|
||||
:show-header="showSectionHeaders"
|
||||
data-testid="commits-section"
|
||||
@selected="selectRef($event)"
|
||||
/>
|
||||
</template>
|
||||
<div v-else-if="showNoResults" class="gl-text-center gl-mx-3 gl-py-3" data-testid="no-results">
|
||||
<gl-sprintf v-if="lastQuery" :message="i18n.noResultsWithQuery">
|
||||
<template #query>
|
||||
<b class="gl-word-break-all">{{ lastQuery }}</b>
|
||||
</template>
|
||||
</div>
|
||||
</gl-sprintf>
|
||||
|
||||
<span v-else>{{ i18n.noResults }}</span>
|
||||
</div>
|
||||
|
||||
<template v-else>
|
||||
<template v-if="showBranchesSection">
|
||||
<ref-results-section
|
||||
:section-title="i18n.branches"
|
||||
:total-count="matches.branches.totalCount"
|
||||
:items="matches.branches.list"
|
||||
:selected-ref="selectedRef"
|
||||
:error="matches.branches.error"
|
||||
:error-message="i18n.branchesErrorMessage"
|
||||
:show-header="showSectionHeaders"
|
||||
data-testid="branches-section"
|
||||
@selected="selectRef($event)"
|
||||
/>
|
||||
|
||||
<gl-dropdown-divider v-if="showTagsSection || showCommitsSection" />
|
||||
</template>
|
||||
|
||||
<template v-if="showTagsSection">
|
||||
<ref-results-section
|
||||
:section-title="i18n.tags"
|
||||
:total-count="matches.tags.totalCount"
|
||||
:items="matches.tags.list"
|
||||
:selected-ref="selectedRef"
|
||||
:error="matches.tags.error"
|
||||
:error-message="i18n.tagsErrorMessage"
|
||||
:show-header="showSectionHeaders"
|
||||
data-testid="tags-section"
|
||||
@selected="selectRef($event)"
|
||||
/>
|
||||
|
||||
<gl-dropdown-divider v-if="showCommitsSection" />
|
||||
</template>
|
||||
|
||||
<template v-if="showCommitsSection">
|
||||
<ref-results-section
|
||||
:section-title="i18n.commits"
|
||||
:total-count="matches.commits.totalCount"
|
||||
:items="matches.commits.list"
|
||||
:selected-ref="selectedRef"
|
||||
:error="matches.commits.error"
|
||||
:error-message="i18n.commitsErrorMessage"
|
||||
:show-header="showSectionHeaders"
|
||||
data-testid="commits-section"
|
||||
@selected="selectRef($event)"
|
||||
/>
|
||||
</template>
|
||||
</template>
|
||||
</gl-dropdown>
|
||||
</template>
|
||||
|
|
|
@ -1,13 +1,4 @@
|
|||
.ref-selector {
|
||||
& &-dropdown-content {
|
||||
// Setting a max height is necessary to allow the dropdown's content
|
||||
// to control where and how scrollbars appear.
|
||||
// This content is limited to the max-height of the dropdown
|
||||
// ($dropdown-max-height-lg) minus the additional padding
|
||||
// on the top and bottom (2 * $gl-padding-8)
|
||||
max-height: $dropdown-max-height-lg - 2 * $gl-padding-8;
|
||||
}
|
||||
|
||||
.dropdown-menu.show {
|
||||
// Make the dropdown a little wider and longer than usual
|
||||
// since it contains quite a bit of content.
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
module Security
|
||||
class LicenseComplianceJobsFinder < JobsFinder
|
||||
def self.allowed_job_types
|
||||
[:license_management, :license_scanning]
|
||||
[:license_scanning]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -17,15 +17,21 @@ module Resolvers
|
|||
default_value: nil,
|
||||
description: 'Sort projects by this criteria.'
|
||||
|
||||
argument :ids, [GraphQL::ID_TYPE],
|
||||
required: false,
|
||||
default_value: nil,
|
||||
description: 'Filter projects by IDs.'
|
||||
|
||||
type Types::ProjectType, null: true
|
||||
|
||||
def resolve(include_subgroups:, sort:, search:)
|
||||
def resolve(include_subgroups:, sort:, search:, ids:)
|
||||
# The namespace could have been loaded in batch by `BatchLoader`.
|
||||
# At this point we need the `id` or the `full_path` of the namespace
|
||||
# to query for projects, so make sure it's loaded and not `nil` before continuing.
|
||||
return Project.none if namespace.nil?
|
||||
|
||||
query = include_subgroups ? namespace.all_projects.with_route : namespace.projects.with_route
|
||||
query = ids ? query.merge(Project.where(id: parse_gids(ids))) : query # rubocop: disable CodeReuse/ActiveRecord
|
||||
|
||||
return query unless search.present?
|
||||
|
||||
|
@ -48,6 +54,10 @@ module Resolvers
|
|||
object.respond_to?(:sync) ? object.sync : object
|
||||
end
|
||||
end
|
||||
|
||||
def parse_gids(gids)
|
||||
gids&.map { |gid| GitlabSchema.parse_gid(gid, expected_type: ::Project).model_id }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -92,7 +92,7 @@ module Users
|
|||
# remove - The IDs of the authorization rows to remove.
|
||||
# add - Rows to insert in the form `[user id, project id, access level]`
|
||||
def update_authorizations(remove = [], add = [])
|
||||
log_refresh_details(remove.length, add.length)
|
||||
log_refresh_details(remove, add)
|
||||
|
||||
User.transaction do
|
||||
user.remove_project_authorizations(remove) unless remove.empty?
|
||||
|
@ -104,11 +104,16 @@ module Users
|
|||
user.reset
|
||||
end
|
||||
|
||||
def log_refresh_details(rows_deleted, rows_added)
|
||||
def log_refresh_details(remove, add)
|
||||
Gitlab::AppJsonLogger.info(event: 'authorized_projects_refresh',
|
||||
user_id: user.id,
|
||||
'authorized_projects_refresh.source': source,
|
||||
'authorized_projects_refresh.rows_deleted': rows_deleted,
|
||||
'authorized_projects_refresh.rows_added': rows_added)
|
||||
'authorized_projects_refresh.rows_deleted_count': remove.length,
|
||||
'authorized_projects_refresh.rows_added_count': add.length,
|
||||
# most often there's only a few entries in remove and add, but limit it to the first 5
|
||||
# entries to avoid flooding the logs
|
||||
'authorized_projects_refresh.rows_deleted_slice': remove.first(5),
|
||||
'authorized_projects_refresh.rows_added_slice': add.first(5))
|
||||
end
|
||||
|
||||
def fresh_access_levels_per_project
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Update Kroki to fix Wavedrom graphs
|
||||
merge_request: 55659
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Query group projects by ids with GraphQL
|
||||
merge_request: 55383
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Small visual updates to Git ref selector dropdown on New/Edit Release page
|
||||
merge_request: 55121
|
||||
author:
|
||||
type: changed
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
title: Add rake task to cleanup description templates cache in batches
|
||||
merge_request: 54706
|
||||
author:
|
||||
type: added
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
title: Do not expose user name if user is project bot
|
||||
merge_request: 54022
|
||||
author:
|
||||
type: changed
|
|
@ -13,13 +13,13 @@ GitLab provides Rake tasks for general maintenance.
|
|||
This command gathers information about your GitLab installation and the system it runs on.
|
||||
These may be useful when asking for help or reporting issues.
|
||||
|
||||
**For Omnibus installations**
|
||||
**Omnibus Installation**
|
||||
|
||||
```shell
|
||||
sudo gitlab-rake gitlab:env:info
|
||||
```
|
||||
|
||||
**For installations from source**
|
||||
**Source Installation**
|
||||
|
||||
```shell
|
||||
bundle exec rake gitlab:env:info RAILS_ENV=production
|
||||
|
@ -76,13 +76,13 @@ installations: a license cannot be installed into GitLab Community Edition.
|
|||
These may be useful when raising tickets with Support, or for programmatically
|
||||
checking your license parameters.
|
||||
|
||||
**For Omnibus installations**
|
||||
**Omnibus Installation**
|
||||
|
||||
```shell
|
||||
sudo gitlab-rake gitlab:license:info
|
||||
```
|
||||
|
||||
**For installations from source**
|
||||
**Source Installation**
|
||||
|
||||
```shell
|
||||
bundle exec rake gitlab:license:info RAILS_ENV=production
|
||||
|
@ -119,13 +119,13 @@ You may also have a look at our troubleshooting guides for:
|
|||
|
||||
To run `gitlab:check`, run:
|
||||
|
||||
**For Omnibus installations**
|
||||
**Omnibus Installation**
|
||||
|
||||
```shell
|
||||
sudo gitlab-rake gitlab:check
|
||||
```
|
||||
|
||||
**For installations from source**
|
||||
**Source Installation**
|
||||
|
||||
```shell
|
||||
bundle exec rake gitlab:check RAILS_ENV=production
|
||||
|
@ -182,13 +182,13 @@ Checking GitLab ... Finished
|
|||
|
||||
In some case it is necessary to rebuild the `authorized_keys` file. To do this, run:
|
||||
|
||||
**For Omnibus installations**
|
||||
**Omnibus Installation**
|
||||
|
||||
```shell
|
||||
sudo gitlab-rake gitlab:shell:setup
|
||||
```
|
||||
|
||||
**For installations from source**
|
||||
**Source Installation**
|
||||
|
||||
```shell
|
||||
cd /home/git/gitlab
|
||||
|
@ -203,64 +203,18 @@ You will lose any data stored in authorized_keys file.
|
|||
Do you want to continue (yes/no)? yes
|
||||
```
|
||||
|
||||
## Clear issue and merge request description template names cache
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/54706) in GitLab 13.10.
|
||||
|
||||
If the issue or merge request description template names in the dropdown
|
||||
do not reflect the actual description template names in the repository, consider clearing
|
||||
the Redis cache that stores the template names information.
|
||||
You can clear the cache of
|
||||
[all issues and merge request templates in the installation](#clear-cache-for-all-issue-and-merge-request-template-names)
|
||||
or [in a specific project](#clear-cache-for-issue-and-merge-request-template-names-in-specific-projects).
|
||||
|
||||
### Clear cache for all issue and merge request template names
|
||||
|
||||
If you want to refresh issue and merge request templates for all projects:
|
||||
|
||||
**For Omnibus installations**
|
||||
|
||||
```shell
|
||||
sudo gitlab-rake cache:clear:description_templates
|
||||
```
|
||||
|
||||
**For installations from source**
|
||||
|
||||
```shell
|
||||
cd /home/git/gitlab
|
||||
sudo -u git -H bundle exec rake cache:clear:description_templates RAILS_ENV=production
|
||||
```
|
||||
|
||||
### Clear cache for issue and merge request template names in specific projects
|
||||
|
||||
If you want to refresh issue and merge request templates for specific projects,
|
||||
provide a comma-separated list of IDs as the `project_ids` parameter to the Rake task.
|
||||
|
||||
**For Omnibus installations**
|
||||
|
||||
```shell
|
||||
sudo gitlab-rake cache:clear:description_templates project_ids=10,25,35
|
||||
```
|
||||
|
||||
**For installations from source**
|
||||
|
||||
```shell
|
||||
cd /home/git/gitlab
|
||||
sudo -u git -H bundle exec rake cache:clear:description_templates project_ids=10,25,35 RAILS_ENV=production
|
||||
```
|
||||
|
||||
## Clear Redis cache
|
||||
|
||||
If for some reason the dashboard displays the wrong information, you might want to
|
||||
clear Redis' cache. To do this, run:
|
||||
|
||||
**For Omnibus installations**
|
||||
**Omnibus Installation**
|
||||
|
||||
```shell
|
||||
sudo gitlab-rake cache:clear
|
||||
```
|
||||
|
||||
**For installations from source**
|
||||
**Source Installation**
|
||||
|
||||
```shell
|
||||
cd /home/git/gitlab
|
||||
|
@ -275,7 +229,7 @@ missing some icons. In that case, try to precompile the assets again.
|
|||
This only applies to source installations and does NOT apply to
|
||||
Omnibus packages.
|
||||
|
||||
**For installations from source**
|
||||
**Source Installation**
|
||||
|
||||
```shell
|
||||
cd /home/git/gitlab
|
||||
|
@ -295,13 +249,13 @@ Sometimes you need to know if your GitLab installation can connect to a TCP
|
|||
service on another machine - perhaps a PostgreSQL or HTTPS server. A Rake task
|
||||
is included to help you with this:
|
||||
|
||||
**For Omnibus installations**
|
||||
**Omnibus Installation**
|
||||
|
||||
```shell
|
||||
sudo gitlab-rake gitlab:tcp_check[example.com,80]
|
||||
```
|
||||
|
||||
**For installations from source**
|
||||
**Source Installation**
|
||||
|
||||
```shell
|
||||
cd /home/git/gitlab
|
||||
|
|
|
@ -22,7 +22,7 @@ GET /projects/:id/deployments
|
|||
| `sort` | string | no | Return deployments sorted in `asc` or `desc` order. Default is `asc` |
|
||||
| `updated_after` | datetime | no | Return deployments updated after the specified date. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`) |
|
||||
| `updated_before` | datetime | no | Return deployments updated before the specified date. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`) |
|
||||
| `environment` | string | no | The [name of the environment](../ci/environments/index.md#defining-environments) to filter deployments by |
|
||||
| `environment` | string | no | The [name of the environment](../ci/environments/index.md) to filter deployments by |
|
||||
| `status` | string | no | The status to filter deployments by |
|
||||
|
||||
The status attribute can be one of the following values:
|
||||
|
@ -281,7 +281,7 @@ POST /projects/:id/deployments
|
|||
| Attribute | Type | Required | Description |
|
||||
|---------------|----------------|----------|-----------------------------------------------------------------------------------------------------------------|
|
||||
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
|
||||
| `environment` | string | yes | The [name of the environment](../ci/environments/index.md#defining-environments) to create the deployment for |
|
||||
| `environment` | string | yes | The [name of the environment](../ci/environments/index.md) to create the deployment for |
|
||||
| `sha` | string | yes | The SHA of the commit that is deployed |
|
||||
| `ref` | string | yes | The name of the branch or tag that is deployed |
|
||||
| `tag` | boolean | yes | A boolean that indicates if the deployed ref is a tag (true) or not (false) |
|
||||
|
|
BIN
doc/ci/environments/img/deployments_list.png
Normal file
BIN
doc/ci/environments/img/deployments_list.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 58 KiB |
BIN
doc/ci/environments/img/environments_list.png
Normal file
BIN
doc/ci/environments/img/environments_list.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 58 KiB |
|
@ -25,295 +25,73 @@ If you have a deployment service like [Kubernetes](../../user/project/clusters/i
|
|||
associated with your project, you can use it to assist with your deployments.
|
||||
You can even access a [web terminal](#web-terminals) for your environment from within GitLab.
|
||||
|
||||
## Configuring environments
|
||||
## View environments and deployments
|
||||
|
||||
Configuring environments involves:
|
||||
Prerequisites:
|
||||
|
||||
1. Understanding how [pipelines](../pipelines/index.md) work.
|
||||
1. Defining environments in your project's [`.gitlab-ci.yml`](../yaml/README.md) file.
|
||||
1. Creating a job configured to deploy your application. For example, a deploy job configured with [`environment`](../yaml/README.md#environment) to deploy your application to a [Kubernetes cluster](../../user/project/clusters/index.md).
|
||||
- You must have a minimum of [Reporter permission](../../user/permissions.md#project-members-permissions).
|
||||
|
||||
The rest of this section illustrates how to configure environments and deployments using
|
||||
an example scenario. It assumes you have already:
|
||||
To view a list of environments and deployments:
|
||||
|
||||
- Created a [project](../../user/project/working_with_projects.md#create-a-project) in GitLab.
|
||||
- Set up [a runner](../runners/README.md).
|
||||
1. Go to the project's **Operations > Environments** page.
|
||||
The environments are displayed.
|
||||
|
||||
In the scenario:
|
||||
![Environments list](img/environments_list.png)
|
||||
|
||||
- We are developing an application.
|
||||
- We want to run tests and build our app on all branches.
|
||||
- Our default branch is `master`.
|
||||
- We deploy the app only when a pipeline on `master` branch is run.
|
||||
1. To view a list of deployments for an environment, select the environment name,
|
||||
for example, `staging`.
|
||||
|
||||
### Defining environments
|
||||
![Deployments list](img/deployments_list.png)
|
||||
|
||||
You can create environments manually in the web interface, but we recommend
|
||||
that you define your environments in the `.gitlab-ci.yml` file. After the first
|
||||
deploy, the environments are automatically created.
|
||||
Deployments show up in this list only after a deployment job has created them.
|
||||
|
||||
Let's consider the following `.gitlab-ci.yml` example:
|
||||
## Types of environments
|
||||
|
||||
```yaml
|
||||
stages:
|
||||
- test
|
||||
- build
|
||||
- deploy
|
||||
There are two types of environments:
|
||||
|
||||
test:
|
||||
stage: test
|
||||
script: echo "Running tests"
|
||||
- Static environments have static names, like `staging` or `production`.
|
||||
- Dynamic environments have dynamic names. Dynamic environments
|
||||
are a fundamental part of [Review apps](../review_apps/index.md).
|
||||
|
||||
build:
|
||||
stage: build
|
||||
script: echo "Building the app"
|
||||
### Create a static environment
|
||||
|
||||
deploy_staging:
|
||||
stage: deploy
|
||||
script:
|
||||
- echo "Deploy to staging server"
|
||||
environment:
|
||||
name: staging
|
||||
url: https://staging.example.com
|
||||
only:
|
||||
- master
|
||||
```
|
||||
You can create an environment and deployment in the UI or in your `.gitlab-ci.yml` file.
|
||||
|
||||
We have defined three [stages](../yaml/README.md#stages):
|
||||
In the UI:
|
||||
|
||||
- `test`
|
||||
- `build`
|
||||
- `deploy`
|
||||
1. Go to the project's **Operations > Environments** page.
|
||||
1. Select **New environment**.
|
||||
1. Enter a name and external URL.
|
||||
1. Select **Save**.
|
||||
|
||||
The jobs assigned to these stages run in this order. If any job fails, then
|
||||
the pipeline fails and jobs that are assigned to the next stage don't run.
|
||||
In your `.gitlab-ci.yml` file:
|
||||
|
||||
In our case:
|
||||
1. Specify a name for the environment and optionally, a URL, which determines the deployment URL.
|
||||
For example:
|
||||
|
||||
- The `test` job runs first.
|
||||
- Then the `build` job.
|
||||
- Lastly the `deploy_staging` job.
|
||||
```yaml
|
||||
deploy_staging:
|
||||
stage: deploy
|
||||
script:
|
||||
- echo "Deploy to staging server"
|
||||
environment:
|
||||
name: staging
|
||||
url: https://staging.example.com
|
||||
```
|
||||
|
||||
With this configuration, we:
|
||||
1. Trigger a deployment. (For example, by creating and pushing a commit.)
|
||||
|
||||
- Check that the tests pass.
|
||||
- Ensure that our app is able to be built successfully.
|
||||
- Lastly we deploy to the staging server.
|
||||
When the job runs, the environment and deployment are created.
|
||||
|
||||
Note that the `environment` keyword defines where the app is deployed. The environment `name` and
|
||||
`url` is exposed in various places within GitLab. Each time a job that has an environment specified
|
||||
succeeds, a deployment is recorded along with the Git SHA and environment name.
|
||||
NOTE:
|
||||
Some characters cannot be used in environment names.
|
||||
For more information about the `environment` keywords, see
|
||||
[the `.gitlab-ci.yml` keyword reference](../yaml/README.md#environment).
|
||||
|
||||
WARNING:
|
||||
Some characters are not allowed in environment names. Use only letters,
|
||||
numbers, spaces, and `-`, `_`, `/`, `{`, `}`, or `.`. Also, it must not start nor end with `/`.
|
||||
### Create a dynamic environment
|
||||
|
||||
In summary, with the above `.gitlab-ci.yml` we have achieved the following:
|
||||
|
||||
- All branches run the `test` and `build` jobs.
|
||||
- The `deploy_staging` job runs [only](../yaml/README.md#onlyexcept-basic) on the `master`
|
||||
branch, which means all merge requests that are created from branches don't
|
||||
get deployed to the staging server.
|
||||
- When a merge request is merged, all jobs run and the `deploy_staging`
|
||||
job deploys our code to a staging server while the deployment
|
||||
is recorded in an environment named `staging`.
|
||||
|
||||
#### CI/CD variables and runners
|
||||
|
||||
Starting with GitLab 8.15, the environment name is exposed to the runner in
|
||||
two forms:
|
||||
|
||||
- `$CI_ENVIRONMENT_NAME`. The name given in `.gitlab-ci.yml` (with any CI/CD variables
|
||||
expanded).
|
||||
- `$CI_ENVIRONMENT_SLUG`. A "cleaned-up" version of the name, suitable for use in URLs,
|
||||
DNS, etc.
|
||||
|
||||
If you change the name of an existing environment, the:
|
||||
|
||||
- `$CI_ENVIRONMENT_NAME` variable is updated with the new environment name.
|
||||
- `$CI_ENVIRONMENT_SLUG` variable remains unchanged to prevent unintended side
|
||||
effects.
|
||||
|
||||
Starting with GitLab 9.3, the environment URL is exposed to the runner via
|
||||
`$CI_ENVIRONMENT_URL`. The URL is expanded from either:
|
||||
|
||||
- `.gitlab-ci.yml`.
|
||||
- The external URL from the environment if not defined in `.gitlab-ci.yml`.
|
||||
|
||||
#### Set dynamic environment URLs after a job finishes
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/17066) in GitLab 12.9.
|
||||
|
||||
In a job script, you can specify a static [environment URL](#using-the-environment-url).
|
||||
However, there may be times when you want a dynamic URL. For example,
|
||||
if you deploy a Review App to an external hosting
|
||||
service that generates a random URL per deployment, like `https://94dd65b.amazonaws.com/qa-lambda-1234567`,
|
||||
you don't know the URL before the deployment script finishes.
|
||||
If you want to use the environment URL in GitLab, you would have to update it manually.
|
||||
|
||||
To address this problem, you can configure a deployment job to report back a set of
|
||||
variables, including the URL that was dynamically-generated by the external service.
|
||||
GitLab supports the [dotenv (`.env`)](https://github.com/bkeepers/dotenv) file format,
|
||||
and expands the `environment:url` value with variables defined in the `.env` file.
|
||||
|
||||
To use this feature, specify the
|
||||
[`artifacts:reports:dotenv`](../pipelines/job_artifacts.md#artifactsreportsdotenv) keyword in `.gitlab-ci.yml`.
|
||||
|
||||
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
|
||||
For an overview, see [Set dynamic URLs after a job finished](https://youtu.be/70jDXtOf4Ig).
|
||||
|
||||
##### Example of setting dynamic environment URLs
|
||||
|
||||
The following example shows a Review App that creates a new environment
|
||||
per merge request. The `review` job is triggered by every push, and
|
||||
creates or updates an environment named `review/your-branch-name`.
|
||||
The environment URL is set to `$DYNAMIC_ENVIRONMENT_URL`:
|
||||
|
||||
```yaml
|
||||
review:
|
||||
script:
|
||||
- DYNAMIC_ENVIRONMENT_URL=$(deploy-script) # In script, get the environment URL.
|
||||
- echo "DYNAMIC_ENVIRONMENT_URL=$DYNAMIC_ENVIRONMENT_URL" >> deploy.env # Add the value to a dotenv file.
|
||||
artifacts:
|
||||
reports:
|
||||
dotenv: deploy.env # Report back dotenv file to rails.
|
||||
environment:
|
||||
name: review/$CI_COMMIT_REF_SLUG
|
||||
url: $DYNAMIC_ENVIRONMENT_URL # and set the variable produced in script to `environment:url`
|
||||
on_stop: stop_review
|
||||
|
||||
stop_review:
|
||||
script:
|
||||
- ./teardown-environment
|
||||
when: manual
|
||||
environment:
|
||||
name: review/$CI_COMMIT_REF_SLUG
|
||||
action: stop
|
||||
```
|
||||
|
||||
As soon as the `review` job finishes, GitLab updates the `review/your-branch-name`
|
||||
environment's URL.
|
||||
It parses the `deploy.env` report artifact, registers a list of variables as runtime-created,
|
||||
uses it for expanding `environment:url: $DYNAMIC_ENVIRONMENT_URL` and sets it to the environment URL.
|
||||
You can also specify a static part of the URL at `environment:url:`, such as
|
||||
`https://$DYNAMIC_ENVIRONMENT_URL`. If the value of `DYNAMIC_ENVIRONMENT_URL` is
|
||||
`example.com`, the final result is `https://example.com`.
|
||||
|
||||
The assigned URL for the `review/your-branch-name` environment is [visible in the UI](#using-the-environment-url).
|
||||
|
||||
Note the following:
|
||||
|
||||
- `stop_review` doesn't generate a dotenv report artifact, so it doesn't recognize the
|
||||
`DYNAMIC_ENVIRONMENT_URL` environment variable. Therefore you shouldn't set `environment:url:` in the
|
||||
`stop_review` job.
|
||||
- If the environment URL isn't valid (for example, the URL is malformed), the system doesn't update
|
||||
the environment URL.
|
||||
- If the script that runs in `stop_review` exists only in your repository and therefore can't use
|
||||
`GIT_STRATEGY: none`, configure [pipelines for merge requests](../../ci/merge_request_pipelines/index.md)
|
||||
for these jobs. This ensures that runners can fetch the repository even after a feature branch is
|
||||
deleted. For more information, see [Ref Specs for Runners](../pipelines/index.md#ref-specs-for-runners).
|
||||
|
||||
### Configuring manual deployments
|
||||
|
||||
Adding `when: manual` to an automatically executed job's configuration converts it to
|
||||
a job requiring manual action.
|
||||
|
||||
To expand on the [previous example](#defining-environments), the following includes
|
||||
another job that deploys our app to a production server and is
|
||||
tracked by a `production` environment.
|
||||
|
||||
The `.gitlab-ci.yml` file for this is as follows:
|
||||
|
||||
```yaml
|
||||
stages:
|
||||
- test
|
||||
- build
|
||||
- deploy
|
||||
|
||||
test:
|
||||
stage: test
|
||||
script: echo "Running tests"
|
||||
|
||||
build:
|
||||
stage: build
|
||||
script: echo "Building the app"
|
||||
|
||||
deploy_staging:
|
||||
stage: deploy
|
||||
script:
|
||||
- echo "Deploy to staging server"
|
||||
environment:
|
||||
name: staging
|
||||
url: https://staging.example.com
|
||||
only:
|
||||
- master
|
||||
|
||||
deploy_prod:
|
||||
stage: deploy
|
||||
script:
|
||||
- echo "Deploy to production server"
|
||||
environment:
|
||||
name: production
|
||||
url: https://example.com
|
||||
when: manual
|
||||
only:
|
||||
- master
|
||||
```
|
||||
|
||||
The `when: manual` action:
|
||||
|
||||
- Exposes a "play" button in the GitLab UI for that job.
|
||||
- Means the `deploy_prod` job is only triggered when the "play" button is clicked.
|
||||
|
||||
You can find the "play" button in the pipelines, environments, deployments, and jobs views.
|
||||
|
||||
| View | Screenshot |
|
||||
|:----------------|:-------------------------------------------------------------------------------|
|
||||
| Pipelines | ![Pipelines manual action](../img/environments_manual_action_pipelines.png) |
|
||||
| Single pipeline | ![Pipelines manual action](../img/environments_manual_action_single_pipeline.png) |
|
||||
| Environments | ![Environments manual action](../img/environments_manual_action_environments.png) |
|
||||
| Deployments | ![Deployments manual action](../img/environments_manual_action_deployments.png) |
|
||||
| Jobs | ![Builds manual action](../img/environments_manual_action_jobs.png) |
|
||||
|
||||
Clicking the play button in any view triggers the `deploy_prod` job. The deployment is recorded as a
|
||||
new environment named `production`.
|
||||
|
||||
If your environment's name is `production` (all lowercase), it's recorded in
|
||||
[Value Stream Analytics](../../user/analytics/value_stream_analytics.md).
|
||||
|
||||
### Configuring dynamic environments
|
||||
|
||||
Regular environments are good when deploying to "stable" environments like staging or production.
|
||||
|
||||
However, for environments for branches other than `master`, dynamic environments
|
||||
can be used. Dynamic environments make it possible to create environments on the fly by
|
||||
declaring their names dynamically in `.gitlab-ci.yml`.
|
||||
|
||||
Dynamic environments are a fundamental part of [Review apps](../review_apps/index.md).
|
||||
|
||||
#### Allowed variables
|
||||
|
||||
The `name` and `url` keywords for dynamic environments can use most available CI/CD variables,
|
||||
including:
|
||||
|
||||
- [Predefined CI/CD variables](../variables/README.md#predefined-cicd-variables)
|
||||
- [Project and group CI/CD variables](../variables/README.md)
|
||||
- [`.gitlab-ci.yml` CI/CD variables](../yaml/README.md#variables)
|
||||
|
||||
However, you cannot use variables defined:
|
||||
|
||||
- Under `script`.
|
||||
- On the runner's side.
|
||||
|
||||
There are also other variables that are unsupported in the context of `environment:name`.
|
||||
For more information, see [Where variables can be used](../variables/where_variables_can_be_used.md).
|
||||
|
||||
#### Example configuration
|
||||
|
||||
Runners expose various [predefined CI/CD variables](../variables/predefined_variables.md) when a job runs, so
|
||||
you can use them as environment names.
|
||||
|
||||
In the following example, the job deploys to all branches except `master`:
|
||||
To create a dynamic name and URL for an environment, you can use
|
||||
[predefined CI/CD variables](../variables/predefined_variables.md). For example:
|
||||
|
||||
```yaml
|
||||
deploy_review:
|
||||
|
@ -331,37 +109,47 @@ deploy_review:
|
|||
|
||||
In this example:
|
||||
|
||||
- The job's name is `deploy_review` and it runs on the `deploy` stage.
|
||||
- We set the `environment` with the `environment:name` as `review/$CI_COMMIT_REF_NAME`.
|
||||
Since the [environment name](../yaml/README.md#environmentname) can contain slashes (`/`), we can
|
||||
use this pattern to distinguish between dynamic and regular environments.
|
||||
- We tell the job to run [`only`](../yaml/README.md#onlyexcept-basic) on branches,
|
||||
[`except`](../yaml/README.md#onlyexcept-basic) `master`.
|
||||
- The `name` is `review/$CI_COMMIT_REF_NAME`. Because the [environment name](../yaml/README.md#environmentname)
|
||||
can contain slashes (`/`), you can use this pattern to distinguish between dynamic and static environments.
|
||||
- For the `url`, you could use `$CI_COMMIT_REF_NAME`, but because this value
|
||||
may contain a `/` or other characters that would not be valid in a domain name or URL,
|
||||
use `$CI_ENVIRONMENT_SLUG` instead. The `$CI_ENVIRONMENT_SLUG` variable is guaranteed to be unique.
|
||||
|
||||
For the value of:
|
||||
|
||||
- `environment:name`, the first part is `review`, followed by a `/` and then `$CI_COMMIT_REF_NAME`,
|
||||
which receives the value of the branch name.
|
||||
- `environment:url`, we want a specific and distinct URL for each branch. `$CI_COMMIT_REF_NAME`
|
||||
may contain a `/` or other characters that would be invalid in a domain name or URL,
|
||||
so we use `$CI_ENVIRONMENT_SLUG` to guarantee that we get a valid URL.
|
||||
|
||||
For example, given a `$CI_COMMIT_REF_NAME` of `100-Do-The-Thing`, the URL is something
|
||||
like `https://100-do-the-4f99a2.example.com`. Again, the way you set up
|
||||
the web server to serve these requests is based on your setup.
|
||||
|
||||
We have used `$CI_ENVIRONMENT_SLUG` here because it is guaranteed to be unique. If
|
||||
you're using a workflow like [GitLab Flow](../../topics/gitlab_flow.md), collisions
|
||||
are unlikely and you may prefer environment names to be more closely based on the
|
||||
branch name. In that case, you could use `$CI_COMMIT_REF_NAME` in `environment:url` in
|
||||
the example above: `https://$CI_COMMIT_REF_NAME.example.com`, which would give a URL
|
||||
of `https://100-do-the-thing.example.com`.
|
||||
|
||||
You aren't required to use the same prefix or only slashes (`/`) in the dynamic environments' names.
|
||||
However, using this format enables the [grouping similar environments](#grouping-similar-environments)
|
||||
You do not have to use the same prefix or only slashes (`/`) in the dynamic environment name.
|
||||
However, when you use this format, you can use the [grouping similar environments](#grouping-similar-environments)
|
||||
feature.
|
||||
|
||||
### Configuring Kubernetes deployments
|
||||
NOTE:
|
||||
Some variables cannot be used as environment names or URLs.
|
||||
For more information about the `environment` keywords, see
|
||||
[the `.gitlab-ci.yml` keyword reference](../yaml/README.md#environment).
|
||||
|
||||
## Configure manual deployments
|
||||
|
||||
You can create a job that requires someone to manually start the deployment.
|
||||
For example:
|
||||
|
||||
```yaml
|
||||
deploy_prod:
|
||||
stage: deploy
|
||||
script:
|
||||
- echo "Deploy to production server"
|
||||
environment:
|
||||
name: production
|
||||
url: https://example.com
|
||||
when: manual
|
||||
only:
|
||||
- master
|
||||
```
|
||||
|
||||
The `when: manual` action:
|
||||
|
||||
- Exposes a play button for the job in the GitLab UI.
|
||||
- Means the `deploy_prod` job is only triggered when the play button is clicked.
|
||||
|
||||
You can find the play button in the pipelines, environments, deployments, and jobs views.
|
||||
|
||||
## Configure Kubernetes deployments
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/27630) in GitLab 12.6.
|
||||
|
||||
|
@ -402,132 +190,114 @@ trace on the deployment job page:
|
|||
|
||||
![Deployment cluster information](../img/environments_deployment_cluster_v12_8.png)
|
||||
|
||||
#### Configuring incremental rollouts
|
||||
### Configure incremental rollouts
|
||||
|
||||
Learn how to release production changes to only a portion of your Kubernetes pods with
|
||||
[incremental rollouts](../environments/incremental_rollouts.md).
|
||||
|
||||
### Deployment safety
|
||||
## CI/CD variables for environments and deployments
|
||||
|
||||
When you create an environment, you specify the name and URL.
|
||||
|
||||
If you want to use the name or URL in another job, you can use:
|
||||
|
||||
- `$CI_ENVIRONMENT_NAME`. The name defined in the `.gitlab-ci.yml` file.
|
||||
- `$CI_ENVIRONMENT_SLUG`. A "cleaned-up" version of the name, suitable for use in URLs,
|
||||
DNS, etc. This variable is guaranteed to be unique.
|
||||
- `$CI_ENVIRONMENT_URL`. The environment's URL, which was specified in the
|
||||
`.gitlab-ci.yml` file or automatically assigned.
|
||||
|
||||
If you change the name of an existing environment, the:
|
||||
|
||||
- `$CI_ENVIRONMENT_NAME` variable is updated with the new environment name.
|
||||
- `$CI_ENVIRONMENT_SLUG` variable remains unchanged to prevent unintended side
|
||||
effects.
|
||||
|
||||
## Set dynamic environment URLs after a job finishes
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/17066) in GitLab 12.9.
|
||||
|
||||
In a job script, you can specify a static environment URL.
|
||||
However, there may be times when you want a dynamic URL. For example,
|
||||
if you deploy a Review App to an external hosting
|
||||
service that generates a random URL per deployment, like `https://94dd65b.amazonaws.com/qa-lambda-1234567`,
|
||||
you don't know the URL before the deployment script finishes.
|
||||
If you want to use the environment URL in GitLab, you would have to update it manually.
|
||||
|
||||
To address this problem, you can configure a deployment job to report back a set of
|
||||
variables, including the URL that was dynamically-generated by the external service.
|
||||
GitLab supports the [dotenv (`.env`)](https://github.com/bkeepers/dotenv) file format,
|
||||
and expands the `environment:url` value with variables defined in the `.env` file.
|
||||
|
||||
To use this feature, specify the
|
||||
[`artifacts:reports:dotenv`](../pipelines/job_artifacts.md#artifactsreportsdotenv) keyword in `.gitlab-ci.yml`.
|
||||
|
||||
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
|
||||
For an overview, see [Set dynamic URLs after a job finished](https://youtu.be/70jDXtOf4Ig).
|
||||
|
||||
### Example of setting dynamic environment URLs
|
||||
|
||||
The following example shows a Review App that creates a new environment
|
||||
per merge request. The `review` job is triggered by every push, and
|
||||
creates or updates an environment named `review/your-branch-name`.
|
||||
The environment URL is set to `$DYNAMIC_ENVIRONMENT_URL`:
|
||||
|
||||
```yaml
|
||||
review:
|
||||
script:
|
||||
- DYNAMIC_ENVIRONMENT_URL=$(deploy-script) # In script, get the environment URL.
|
||||
- echo "DYNAMIC_ENVIRONMENT_URL=$DYNAMIC_ENVIRONMENT_URL" >> deploy.env # Add the value to a dotenv file.
|
||||
artifacts:
|
||||
reports:
|
||||
dotenv: deploy.env # Report back dotenv file to rails.
|
||||
environment:
|
||||
name: review/$CI_COMMIT_REF_SLUG
|
||||
url: $DYNAMIC_ENVIRONMENT_URL # and set the variable produced in script to `environment:url`
|
||||
on_stop: stop_review
|
||||
|
||||
stop_review:
|
||||
script:
|
||||
- ./teardown-environment
|
||||
when: manual
|
||||
environment:
|
||||
name: review/$CI_COMMIT_REF_SLUG
|
||||
action: stop
|
||||
```
|
||||
|
||||
As soon as the `review` job finishes, GitLab updates the `review/your-branch-name`
|
||||
environment's URL.
|
||||
It parses the `deploy.env` report artifact, registers a list of variables as runtime-created,
|
||||
uses it for expanding `environment:url: $DYNAMIC_ENVIRONMENT_URL` and sets it to the environment URL.
|
||||
You can also specify a static part of the URL at `environment:url:`, such as
|
||||
`https://$DYNAMIC_ENVIRONMENT_URL`. If the value of `DYNAMIC_ENVIRONMENT_URL` is
|
||||
`example.com`, the final result is `https://example.com`.
|
||||
|
||||
The assigned URL for the `review/your-branch-name` environment is visible in the UI.
|
||||
|
||||
Note the following:
|
||||
|
||||
- `stop_review` doesn't generate a dotenv report artifact, so it doesn't recognize the
|
||||
`DYNAMIC_ENVIRONMENT_URL` environment variable. Therefore you shouldn't set `environment:url:` in the
|
||||
`stop_review` job.
|
||||
- If the environment URL isn't valid (for example, the URL is malformed), the system doesn't update
|
||||
the environment URL.
|
||||
- If the script that runs in `stop_review` exists only in your repository and therefore can't use
|
||||
`GIT_STRATEGY: none`, configure [pipelines for merge requests](../../ci/merge_request_pipelines/index.md)
|
||||
for these jobs. This ensures that runners can fetch the repository even after a feature branch is
|
||||
deleted. For more information, see [Ref Specs for Runners](../pipelines/index.md#ref-specs-for-runners).
|
||||
|
||||
## Deployment safety
|
||||
|
||||
Deployment jobs can be more sensitive than other jobs in a pipeline,
|
||||
and might need to be treated with an extra care. There are multiple features
|
||||
in GitLab that helps maintain deployment security and stability.
|
||||
in GitLab that help maintain deployment security and stability.
|
||||
|
||||
- [Restrict write-access to a critical environment](deployment_safety.md#restrict-write-access-to-a-critical-environment)
|
||||
- [Limit the job-concurrency for deployment jobs](deployment_safety.md#ensure-only-one-deployment-job-runs-at-a-time)
|
||||
- [Skip outdated deployment jobs](deployment_safety.md#skip-outdated-deployment-jobs)
|
||||
- [Prevent deployments during deploy freeze windows](deployment_safety.md#prevent-deployments-during-deploy-freeze-windows)
|
||||
|
||||
### Complete example
|
||||
|
||||
The configuration in this section provides a full development workflow where your app is:
|
||||
|
||||
- Tested.
|
||||
- Built.
|
||||
- Deployed as a Review App.
|
||||
- Deployed to a staging server after the merge request is merged.
|
||||
- Finally, able to be manually deployed to the production server.
|
||||
|
||||
The following combines the previous configuration examples, including:
|
||||
|
||||
- Defining [simple environments](#defining-environments) for testing, building, and deployment to staging.
|
||||
- Adding [manual actions](#configuring-manual-deployments) for deployment to production.
|
||||
- Creating [dynamic environments](#configuring-dynamic-environments) for deployments for reviewing.
|
||||
|
||||
```yaml
|
||||
stages:
|
||||
- test
|
||||
- build
|
||||
- deploy
|
||||
|
||||
test:
|
||||
stage: test
|
||||
script: echo "Running tests"
|
||||
|
||||
build:
|
||||
stage: build
|
||||
script: echo "Building the app"
|
||||
|
||||
deploy_review:
|
||||
stage: deploy
|
||||
script:
|
||||
- echo "Deploy a review app"
|
||||
environment:
|
||||
name: review/$CI_COMMIT_REF_NAME
|
||||
url: https://$CI_ENVIRONMENT_SLUG.example.com
|
||||
only:
|
||||
- branches
|
||||
except:
|
||||
- master
|
||||
|
||||
deploy_staging:
|
||||
stage: deploy
|
||||
script:
|
||||
- echo "Deploy to staging server"
|
||||
environment:
|
||||
name: staging
|
||||
url: https://staging.example.com
|
||||
only:
|
||||
- master
|
||||
|
||||
deploy_prod:
|
||||
stage: deploy
|
||||
script:
|
||||
- echo "Deploy to production server"
|
||||
environment:
|
||||
name: production
|
||||
url: https://example.com
|
||||
when: manual
|
||||
only:
|
||||
- master
|
||||
```
|
||||
|
||||
A more realistic example would also include copying files to a location where a
|
||||
webserver (for example, NGINX) could then access and serve them.
|
||||
|
||||
The example below copies the `public` directory to `/srv/nginx/$CI_COMMIT_REF_SLUG/public`:
|
||||
|
||||
```yaml
|
||||
review_app:
|
||||
stage: deploy
|
||||
script:
|
||||
- rsync -av --delete public /srv/nginx/$CI_COMMIT_REF_SLUG
|
||||
environment:
|
||||
name: review/$CI_COMMIT_REF_NAME
|
||||
url: https://$CI_COMMIT_REF_SLUG.example.com
|
||||
```
|
||||
|
||||
This example requires that NGINX and GitLab Runner are set up on the server this job runs on.
|
||||
|
||||
See the [limitations](#limitations) section for some edge cases regarding the naming of your
|
||||
branches and Review Apps.
|
||||
|
||||
The complete example provides the following workflow to developers:
|
||||
|
||||
- Create a branch locally.
|
||||
- Make changes and commit them.
|
||||
- Push the branch to GitLab.
|
||||
- Create a merge request.
|
||||
|
||||
Behind the scenes, the runner:
|
||||
|
||||
- Picks up the changes and starts running the jobs.
|
||||
- Runs the jobs sequentially as defined in `stages`:
|
||||
- First, run the tests.
|
||||
- If the tests succeed, build the app.
|
||||
- If the build succeeds, the app is deployed to an environment with a name specific to the
|
||||
branch.
|
||||
|
||||
So now, every branch:
|
||||
|
||||
- Gets its own environment.
|
||||
- Is deployed to its own unique location, with the added benefit of:
|
||||
- Having a [history of deployments](#view-the-deployment-history).
|
||||
- Being able to [roll back changes](#retry-or-roll-back-a-deployment) if needed.
|
||||
|
||||
For more information, see [Using the environment URL](#using-the-environment-url).
|
||||
|
||||
### Protected environments
|
||||
## Protected environments
|
||||
|
||||
Environments can be "protected", restricting access to them.
|
||||
|
||||
|
@ -538,36 +308,16 @@ For more information, see [Protected environments](protected_environments.md).
|
|||
Once environments are configured, GitLab provides many features for working with them,
|
||||
as documented below.
|
||||
|
||||
### View environments and deployments
|
||||
### Environment rollback
|
||||
|
||||
Prerequisites:
|
||||
When you roll back a deployment on a specific commit,
|
||||
a _new_ deployment is created. This deployment has its own unique job ID.
|
||||
It points to the commit you're rolling back to.
|
||||
|
||||
- You must have a minimum of [Reporter permission](../../user/permissions.md#project-members-permissions).
|
||||
For the rollback to succeed, the deployment process must be defined in
|
||||
the job's `script`.
|
||||
|
||||
To view a list of environments and deployment statuses:
|
||||
|
||||
- Go to the project's **Operations > Environments** page.
|
||||
|
||||
The **Environments** page shows the latest deployments.
|
||||
|
||||
- An environment can have multiple deployments. Some deployments may not be listed on the page.
|
||||
- Only deploys that happen after your `.gitlab-ci.yml` is properly configured
|
||||
show up in the **Environment** and **Last deployment** lists.
|
||||
|
||||
### View the deployment history
|
||||
|
||||
GitLab tracks your deployments, so you:
|
||||
|
||||
- Always know what is currently deployed on your servers.
|
||||
- Have the full history of your deployments for every environment.
|
||||
|
||||
- Go to the project's **Operations > Environments** page.
|
||||
|
||||
![Deployments](../img/deployments_view.png)
|
||||
|
||||
This view is similar to the **Environments** page, but all deployments are shown.
|
||||
|
||||
### Retry or roll back a deployment
|
||||
#### Retry or roll back a deployment
|
||||
|
||||
If there is a problem with a deployment, you can retry it or roll it back.
|
||||
|
||||
|
@ -575,32 +325,23 @@ To retry or rollback a deployment:
|
|||
|
||||
1. Go to the project's **Operations > Environments**.
|
||||
1. Select the environment.
|
||||
1. In the deployment history list for the environment:
|
||||
- To retry a deployment, select **Retry**.
|
||||
- to roll back to a deployment, next to a previously successful deployment, select **Rollback**.
|
||||
1. To the right of the deployment name:
|
||||
- To retry a deployment, select **Re-deploy to environment**.
|
||||
- To roll back to a deployment, next to a previously successful deployment, select **Rollback environment**.
|
||||
|
||||
#### What to expect with a rollback
|
||||
|
||||
Pressing the **Rollback** button on a specific commit triggers a _new_ deployment with its own
|
||||
unique job ID. This new deployment points to the commit you're
|
||||
rolling back to.
|
||||
|
||||
Note that the defined deployment process in the job's `script` determines whether the rollback
|
||||
succeeds.
|
||||
|
||||
### Using the environment URL
|
||||
### Environment URL
|
||||
|
||||
The [environment URL](../yaml/README.md#environmenturl) is exposed in a few
|
||||
places within GitLab:
|
||||
|
||||
- In a merge request widget as a link:
|
||||
- In a merge request as a link:
|
||||
![Environment URL in merge request](../img/environments_mr_review_app.png)
|
||||
- In the Environments view as a button:
|
||||
![Environment URL in environments](../img/environments_available_13_7.png)
|
||||
![Environment URL in environments](../img/environments_available_13_10.png)
|
||||
- In the Deployments view as a button:
|
||||
![Environment URL in deployments](../img/deployments_view.png)
|
||||
|
||||
You can see this information in a merge request itself if:
|
||||
You can see this information in a merge request if:
|
||||
|
||||
- The merge request is eventually merged to the default branch (usually `master`).
|
||||
- That branch also deploys to an environment (for example, `staging` or `production`).
|
||||
|
@ -616,19 +357,16 @@ from source files to public pages in the environment set for Review Apps.
|
|||
|
||||
### Stopping an environment
|
||||
|
||||
Stopping an environment:
|
||||
When you stop an environment:
|
||||
|
||||
- Moves it from the list of **Available** environments to the list of **Stopped**
|
||||
- It moves from the list of **Available** environments to the list of **Stopped**
|
||||
environments on the [**Environments** page](#view-environments-and-deployments).
|
||||
- Executes an [`on_stop` action](../yaml/README.md#environmenton_stop), if defined.
|
||||
- An [`on_stop` action](../yaml/README.md#environmenton_stop), if defined, is executed.
|
||||
|
||||
This is often used when multiple developers are working on a project at the same time,
|
||||
each of them pushing to their own branches, causing many dynamic environments to be created.
|
||||
|
||||
Starting with GitLab 8.14, dynamic environments stop automatically when their associated branch is
|
||||
Dynamic environments stop automatically when their associated branch is
|
||||
deleted.
|
||||
|
||||
#### Automatically stopping an environment
|
||||
#### Stop an environment when a branch is deleted
|
||||
|
||||
Environments can be stopped automatically using special configuration.
|
||||
|
||||
|
@ -678,7 +416,7 @@ possible to trigger `action: stop` to stop the environment automatically.
|
|||
|
||||
You can read more in the [`.gitlab-ci.yml` reference](../yaml/README.md#environmenton_stop).
|
||||
|
||||
#### Environments auto-stop
|
||||
#### Stop an environment after a certain time period
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/20956) in GitLab 12.8.
|
||||
|
||||
|
@ -763,7 +501,7 @@ environment name to see its details and **Delete** it from there.
|
|||
You can also delete environments by viewing the details for a
|
||||
stopped environment:
|
||||
|
||||
1. Navigate to **Operations > Environments**.
|
||||
1. Go to the project's **Operations > Environments** page.
|
||||
1. Click on the name of an environment within the **Stopped** environments list.
|
||||
1. Click on the **Delete** button that appears at the top for all stopped environments.
|
||||
1. Finally, confirm your chosen environment in the modal that appears to delete it.
|
||||
|
@ -776,7 +514,7 @@ Environments can also be deleted by using the [Environments API](../../api/envir
|
|||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/208655) in GitLab 13.2.
|
||||
|
||||
By default, GitLab creates a [deployment](#view-the-deployment-history) every time a
|
||||
By default, GitLab creates a deployment every time a
|
||||
build with the specified environment runs. Newer deployments can also
|
||||
[cancel older ones](deployment_safety.md#skip-outdated-deployment-jobs).
|
||||
|
||||
|
@ -801,15 +539,15 @@ build:
|
|||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/7015) in GitLab 8.14.
|
||||
|
||||
As documented in [Configuring dynamic environments](#configuring-dynamic-environments), you can
|
||||
As documented in [Create a dynamic environment](#create-a-dynamic-environment), you can
|
||||
prepend environment name with a word, followed by a `/`, and finally the branch
|
||||
name, which is automatically defined by the `CI_COMMIT_REF_NAME` predefined CI/CD variable.
|
||||
|
||||
In short, environments that are named like `type/foo` are all presented under the same
|
||||
group, named `type`.
|
||||
|
||||
In our [minimal example](#example-configuration), we named the environments `review/$CI_COMMIT_REF_NAME`
|
||||
where `$CI_COMMIT_REF_NAME` is the branch name. Here is a snippet of the example:
|
||||
If you name the environments `review/$CI_COMMIT_REF_NAME`
|
||||
where `$CI_COMMIT_REF_NAME` is the branch name:
|
||||
|
||||
```yaml
|
||||
deploy_review:
|
||||
|
@ -820,7 +558,7 @@ deploy_review:
|
|||
name: review/$CI_COMMIT_REF_NAME
|
||||
```
|
||||
|
||||
In this case, if you visit the **Environments** page and the branches
|
||||
If you visit the **Environments** page and the branches
|
||||
exist, you should see something like:
|
||||
|
||||
![Environment groups](../img/environments_dynamic_groups.png)
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 39 KiB |
BIN
doc/ci/img/environments_available_13_10.png
Normal file
BIN
doc/ci/img/environments_available_13_10.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 43 KiB |
Binary file not shown.
Before Width: | Height: | Size: 28 KiB |
|
@ -103,7 +103,7 @@ Feature.enable(:ci_config_visualization_tab)
|
|||
## View expanded configuration
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/246801) in GitLab 13.9.
|
||||
> - It is [deployed behind a feature flag](../../user/feature_flags.md), disabled by default.
|
||||
> - It is [deployed behind a feature flag](../../user/feature_flags.md), enabled by default.
|
||||
> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-expanded-configuration). **(FREE SELF)**
|
||||
|
||||
To view the fully expanded CI/CD configuration as one combined file, go to the
|
||||
|
@ -118,20 +118,20 @@ where:
|
|||
### Enable or disable expanded configuration **(FREE SELF)**
|
||||
|
||||
Expanded CI/CD configuration is under development and not ready for production use.
|
||||
It is deployed behind a feature flag that is **disabled by default**.
|
||||
It is deployed behind a feature flag that is **enabled by default**.
|
||||
[GitLab administrators with access to the GitLab Rails console](../../administration/feature_flags.md)
|
||||
can opt to enable it.
|
||||
|
||||
To enable it:
|
||||
|
||||
```ruby
|
||||
Feature.enable(:ci_config_visualization_tab)
|
||||
```
|
||||
|
||||
To disable it:
|
||||
|
||||
```ruby
|
||||
Feature.disable(:ci_config_visualization_tab)
|
||||
Feature.disable(:ci_config_merged_tab)
|
||||
```
|
||||
|
||||
To enable it:
|
||||
|
||||
```ruby
|
||||
Feature.enable(:ci_config_merged_tab)
|
||||
```
|
||||
|
||||
## Commit changes to CI configuration
|
||||
|
|
|
@ -96,7 +96,7 @@ This table lists the refspecs injected for each pipeline type:
|
|||
The refs `refs/heads/<name>` and `refs/tags/<name>` exist in your
|
||||
project repository. GitLab generates the special ref `refs/pipelines/<id>` during a
|
||||
running pipeline job. This ref can be created even after the associated branch or tag has been
|
||||
deleted. It's therefore useful in some features such as [automatically stopping an environment](../environments/index.md#automatically-stopping-an-environment),
|
||||
deleted. It's therefore useful in some features such as [automatically stopping an environment](../environments/index.md#stopping-an-environment),
|
||||
and [merge trains](../merge_request_pipelines/pipelines_for_merged_results/merge_trains/index.md)
|
||||
that might run pipelines after branch deletion.
|
||||
|
||||
|
@ -208,7 +208,7 @@ You can do this straight from the pipeline graph. Just click the play button
|
|||
to execute that particular job.
|
||||
|
||||
For example, your pipeline might start automatically, but it requires manual action to
|
||||
[deploy to production](../environments/index.md#configuring-manual-deployments). In the example below, the `production`
|
||||
[deploy to production](../environments/index.md#configure-manual-deployments). In the example below, the `production`
|
||||
stage has a job with a manual action.
|
||||
|
||||
![Pipelines example](img/pipelines.png)
|
||||
|
|
|
@ -56,7 +56,7 @@ After adding Review Apps to your workflow, you follow the branched Git flow. Tha
|
|||
|
||||
## Configuring Review Apps
|
||||
|
||||
Review Apps are built on [dynamic environments](../environments/index.md#configuring-dynamic-environments), which allow you to dynamically create a new environment for each branch.
|
||||
Review Apps are built on [dynamic environments](../environments/index.md#create-a-dynamic-environment), which allow you to dynamically create a new environment for each branch.
|
||||
|
||||
The process of configuring Review Apps is as follows:
|
||||
|
||||
|
@ -89,7 +89,7 @@ you can copy and paste into `.gitlab-ci.yml` as a starting point. To do so:
|
|||
|
||||
## Review Apps auto-stop
|
||||
|
||||
See how to [configure Review Apps environments to expire and auto-stop](../environments/index.md#environments-auto-stop)
|
||||
See how to [configure Review Apps environments to expire and auto-stop](../environments/index.md#stop-an-environment-after-a-certain-time-period)
|
||||
after a given period of time.
|
||||
|
||||
## Review Apps examples
|
||||
|
|
|
@ -2322,7 +2322,7 @@ started by a user. You might want to use manual jobs for things like deploying t
|
|||
|
||||
To make a job manual, add `when: manual` to its configuration.
|
||||
|
||||
Manual jobs can be started from the pipeline, job, [environment](../environments/index.md#configuring-manual-deployments),
|
||||
Manual jobs can be started from the pipeline, job, [environment](../environments/index.md#configure-manual-deployments),
|
||||
and deployment views.
|
||||
|
||||
Manual jobs can be either optional or blocking:
|
||||
|
@ -2562,7 +2562,7 @@ it is set to `manual`, so it needs a [manual action](#whenmanual) from
|
|||
the GitLab UI to run.
|
||||
|
||||
Also in the example, `GIT_STRATEGY` is set to `none`. If the
|
||||
`stop_review_app` job is [automatically triggered](../environments/index.md#automatically-stopping-an-environment),
|
||||
`stop_review_app` job is [automatically triggered](../environments/index.md#stopping-an-environment),
|
||||
the runner won’t try to check out the code after the branch is deleted.
|
||||
|
||||
The example also overwrites global variables. If your `stop` `environment` job depends
|
||||
|
@ -2608,7 +2608,7 @@ When the environment for `review_app` is created, the environment's lifetime is
|
|||
Every time the review app is deployed, that lifetime is also reset to `1 day`.
|
||||
|
||||
For more information, see
|
||||
[the environments auto-stop documentation](../environments/index.md#environments-auto-stop)
|
||||
[the environments auto-stop documentation](../environments/index.md#stop-an-environment-after-a-certain-time-period)
|
||||
|
||||
#### `environment:kubernetes`
|
||||
|
||||
|
@ -2634,7 +2634,7 @@ environment, using the `production`
|
|||
[Kubernetes namespace](https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/).
|
||||
|
||||
For more information, see
|
||||
[Available settings for `kubernetes`](../environments/index.md#configuring-kubernetes-deployments).
|
||||
[Available settings for `kubernetes`](../environments/index.md#configure-kubernetes-deployments).
|
||||
|
||||
NOTE:
|
||||
Kubernetes configuration is not supported for Kubernetes clusters
|
||||
|
|
|
@ -109,7 +109,7 @@ subgraph "CNG-mirror pipeline"
|
|||
### Auto-stopping of Review Apps
|
||||
|
||||
Review Apps are automatically stopped 2 days after the last deployment thanks to
|
||||
the [Environment auto-stop](../../ci/environments/index.md#environments-auto-stop) feature.
|
||||
the [Environment auto-stop](../../ci/environments/index.md#stop-an-environment-after-a-certain-time-period) feature.
|
||||
|
||||
If you need your Review App to stay up for a longer time, you can
|
||||
[pin its environment](../../ci/environments/index.md#auto-stop-example) or retry the
|
||||
|
|
|
@ -204,7 +204,7 @@ into your project and edit it as needed.
|
|||
|
||||
For clusters not managed by GitLab, you can customize the namespace in
|
||||
`.gitlab-ci.yml` by specifying
|
||||
[`environment:kubernetes:namespace`](../../ci/environments/index.md#configuring-kubernetes-deployments).
|
||||
[`environment:kubernetes:namespace`](../../ci/environments/index.md#configure-kubernetes-deployments).
|
||||
For example, the following configuration overrides the namespace used for
|
||||
`production` deployments:
|
||||
|
||||
|
|
|
@ -312,7 +312,7 @@ following command in your deployment job script, for Kubernetes to access the re
|
|||
The Kubernetes cluster integration exposes these
|
||||
[deployment variables](../../../ci/variables/README.md#deployment-variables) in the
|
||||
GitLab CI/CD build environment to deployment jobs. Deployment jobs have
|
||||
[defined a target environment](../../../ci/environments/index.md#defining-environments).
|
||||
[defined a target environment](../../../ci/environments/index.md).
|
||||
|
||||
| Deployment Variable | Description |
|
||||
|----------------------------|-------------|
|
||||
|
@ -345,7 +345,7 @@ You can customize the deployment namespace in a few ways:
|
|||
- For **non-managed** clusters, the auto-generated namespace is set in the `KUBECONFIG`,
|
||||
but the user is responsible for ensuring its existence. You can fully customize
|
||||
this value using
|
||||
[`environment:kubernetes:namespace`](../../../ci/environments/index.md#configuring-kubernetes-deployments)
|
||||
[`environment:kubernetes:namespace`](../../../ci/environments/index.md#configure-kubernetes-deployments)
|
||||
in `.gitlab-ci.yml`.
|
||||
|
||||
When you customize the namespace, existing environments remain linked to their current
|
||||
|
@ -432,7 +432,7 @@ Reasons for failure include:
|
|||
- The token you gave GitLab does not have [`cluster-admin`](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#user-facing-roles)
|
||||
privileges required by GitLab.
|
||||
- Missing `KUBECONFIG` or `KUBE_TOKEN` deployment variables. To be passed to your job, they must have a matching
|
||||
[`environment:name`](../../../ci/environments/index.md#defining-environments). If your job has no
|
||||
[`environment:name`](../../../ci/environments/index.md). If your job has no
|
||||
`environment:name` set, the Kubernetes credentials are not passed to it.
|
||||
|
||||
NOTE:
|
||||
|
|
|
@ -75,7 +75,7 @@ specific environment, there are a lot of use cases. To name a few:
|
|||
|
||||
To display the Deploy Boards for a specific [environment](../../ci/environments/index.md) you should:
|
||||
|
||||
1. Have [defined an environment](../../ci/environments/index.md#defining-environments) with a deploy stage.
|
||||
1. Have [defined an environment](../../ci/environments/index.md) with a deploy stage.
|
||||
|
||||
1. Have a Kubernetes cluster up and running.
|
||||
|
||||
|
|
|
@ -3,8 +3,7 @@
|
|||
module API
|
||||
module Entities
|
||||
class UserSafe < Grape::Entity
|
||||
expose :id, :username
|
||||
expose :name, unless: ->(user) { user.project_bot? && !options[:current_user].admin?}
|
||||
expose :id, :name, :username
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -9,8 +9,6 @@ module Gitlab
|
|||
class ProjectPipelineStatus
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
|
||||
ALL_PIPELINES_STATUS_PATTERN = 'projects/*/pipeline_status'
|
||||
|
||||
attr_accessor :sha, :status, :ref, :project, :loaded
|
||||
|
||||
def self.load_for_project(project)
|
||||
|
|
|
@ -7,11 +7,14 @@ module Gitlab
|
|||
class Item
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
|
||||
def initialize(key:, value:, public: true, file: false, masked: false)
|
||||
attr_reader :raw
|
||||
|
||||
def initialize(key:, value:, public: true, file: false, masked: false, raw: false)
|
||||
raise ArgumentError, "`#{key}` must be of type String or nil value, while it was: #{value.class}" unless
|
||||
value.is_a?(String) || value.nil?
|
||||
|
||||
@variable = { key: key, value: value, public: public, file: file, masked: masked }
|
||||
@raw = raw
|
||||
end
|
||||
|
||||
def value
|
||||
|
@ -28,6 +31,8 @@ module Gitlab
|
|||
|
||||
def depends_on
|
||||
strong_memoize(:depends_on) do
|
||||
next if raw
|
||||
|
||||
next unless ExpandVariables.possible_var_reference?(value)
|
||||
|
||||
value.scan(ExpandVariables::VARIABLES_REGEXP).map(&:first)
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Cleanup
|
||||
module Redis
|
||||
class BatchDeleteByPattern
|
||||
REDIS_CLEAR_BATCH_SIZE = 1000 # There seems to be no speedup when pushing beyond 1,000
|
||||
REDIS_SCAN_START_STOP = '0'.freeze # Magic value, see http://redis.io/commands/scan
|
||||
|
||||
attr_reader :patterns
|
||||
|
||||
def initialize(patterns)
|
||||
raise ArgumentError.new('Argument should be an Array of patterns') unless patterns.is_a?(Array)
|
||||
|
||||
@patterns = patterns
|
||||
end
|
||||
|
||||
def execute
|
||||
return if patterns.blank?
|
||||
|
||||
batch_delete_cache_keys
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def batch_delete_cache_keys
|
||||
Gitlab::Redis::Cache.with do |redis|
|
||||
patterns.each do |match|
|
||||
cursor = REDIS_SCAN_START_STOP
|
||||
loop do
|
||||
cursor, keys = redis.scan(
|
||||
cursor,
|
||||
match: match,
|
||||
count: REDIS_CLEAR_BATCH_SIZE
|
||||
)
|
||||
|
||||
Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do
|
||||
redis.del(*keys) if keys.any?
|
||||
end
|
||||
|
||||
break if cursor == REDIS_SCAN_START_STOP
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,88 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Cleanup
|
||||
module Redis
|
||||
class DescriptionTemplatesCacheKeysPatternBuilder
|
||||
# project_ids - a list of project_ids for which to compute description templates cache keys or `:all` to compute
|
||||
# a pattern that cover all description templates cache keys.
|
||||
#
|
||||
# Example
|
||||
# * ::Gitlab::Cleanup::Redis::BatchDeleteDescriptionTemplates.new(:all).execute - to get 2
|
||||
# patterns for all issue and merge request description templates cache keys.
|
||||
#
|
||||
# * ::Gitlab::Cleanup::Redis::BatchDeleteDescriptionTemplates.new([1,2,3,4]).execute - to get an array of
|
||||
# patterns for each project's issue and merge request description templates cache keys.
|
||||
def initialize(project_ids)
|
||||
raise ArgumentError.new('project_ids can either be an array of project IDs or :all') if project_ids != :all && !project_ids.is_a?(Array)
|
||||
|
||||
@project_ids = parse_project_ids(project_ids)
|
||||
end
|
||||
|
||||
def execute
|
||||
case project_ids
|
||||
when :all
|
||||
all_instance_patterns
|
||||
else
|
||||
project_patterns
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :project_ids
|
||||
|
||||
def parse_project_ids(project_ids)
|
||||
return project_ids if project_ids == :all
|
||||
|
||||
project_ids.map { |id| Integer(id) }
|
||||
rescue ArgumentError
|
||||
raise ArgumentError.new('Invalid Project ID. Please ensure all passed in project ids values are valid integer project ids.')
|
||||
end
|
||||
|
||||
def project_patterns
|
||||
cache_key_patterns = []
|
||||
Project.id_in(project_ids).each_batch do |batch|
|
||||
cache_key_patterns << batch.map do |pr|
|
||||
next unless pr.repository.exists?
|
||||
|
||||
cache = Gitlab::RepositoryCache.new(pr.repository)
|
||||
|
||||
[repo_issue_templates_cache_key(cache), repo_merge_request_templates_cache_key(cache)]
|
||||
end
|
||||
end
|
||||
|
||||
cache_key_patterns.flatten.compact
|
||||
end
|
||||
|
||||
def all_instance_patterns
|
||||
[all_issue_templates_cache_key, all_merge_request_templates_cache_key]
|
||||
end
|
||||
|
||||
def issue_templates_cache_key
|
||||
Repository::METHOD_CACHES_FOR_FILE_TYPES[:issue_template]
|
||||
end
|
||||
|
||||
def merge_request_templates_cache_key
|
||||
Repository::METHOD_CACHES_FOR_FILE_TYPES[:merge_request_template]
|
||||
end
|
||||
|
||||
def all_issue_templates_cache_key
|
||||
"#{Gitlab::Redis::Cache::CACHE_NAMESPACE}:#{issue_templates_cache_key}:*"
|
||||
end
|
||||
|
||||
def all_merge_request_templates_cache_key
|
||||
"#{Gitlab::Redis::Cache::CACHE_NAMESPACE}:#{merge_request_templates_cache_key}:*"
|
||||
end
|
||||
|
||||
def repo_issue_templates_cache_key(cache)
|
||||
"#{Gitlab::Redis::Cache::CACHE_NAMESPACE}:#{cache.cache_key(issue_templates_cache_key)}"
|
||||
end
|
||||
|
||||
def repo_merge_request_templates_cache_key(cache)
|
||||
"#{Gitlab::Redis::Cache::CACHE_NAMESPACE}:#{cache.cache_key(merge_request_templates_cache_key)}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -2,22 +2,32 @@
|
|||
|
||||
namespace :cache do
|
||||
namespace :clear do
|
||||
REDIS_CLEAR_BATCH_SIZE = 1000 # There seems to be no speedup when pushing beyond 1,000
|
||||
REDIS_SCAN_START_STOP = '0'.freeze # Magic value, see http://redis.io/commands/scan
|
||||
|
||||
desc "GitLab | Cache | Clear redis cache"
|
||||
task redis: :environment do
|
||||
cache_key_patterns = %W[
|
||||
#{Gitlab::Redis::Cache::CACHE_NAMESPACE}*
|
||||
#{Gitlab::Cache::Ci::ProjectPipelineStatus::ALL_PIPELINES_STATUS_PATTERN}
|
||||
]
|
||||
Gitlab::Redis::Cache.with do |redis|
|
||||
cache_key_pattern = %W[#{Gitlab::Redis::Cache::CACHE_NAMESPACE}*
|
||||
projects/*/pipeline_status]
|
||||
|
||||
::Gitlab::Cleanup::Redis::BatchDeleteByPattern.new(cache_key_patterns).execute
|
||||
end
|
||||
cache_key_pattern.each do |match|
|
||||
cursor = REDIS_SCAN_START_STOP
|
||||
loop do
|
||||
cursor, keys = redis.scan(
|
||||
cursor,
|
||||
match: match,
|
||||
count: REDIS_CLEAR_BATCH_SIZE
|
||||
)
|
||||
|
||||
desc "GitLab | Cache | Clear description templates redis cache"
|
||||
task description_templates: :environment do
|
||||
project_ids = Array(ENV['project_ids']&.split(',')).map!(&:squish)
|
||||
Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do
|
||||
redis.del(*keys) if keys.any?
|
||||
end
|
||||
|
||||
cache_key_patterns = ::Gitlab::Cleanup::Redis::DescriptionTemplatesCacheKeysPatternBuilder.new(project_ids).execute
|
||||
::Gitlab::Cleanup::Redis::BatchDeleteByPattern.new(cache_key_patterns).execute
|
||||
break if cursor == REDIS_SCAN_START_STOP
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
task all: [:redis]
|
||||
|
|
|
@ -62,6 +62,7 @@ module DeprecationToolkitEnv
|
|||
carrierwave-1.3.1/lib/carrierwave/sanitized_file.rb
|
||||
activerecord-6.0.3.4/lib/active_record/relation.rb
|
||||
selenium-webdriver-3.142.7/lib/selenium/webdriver/firefox/driver.rb
|
||||
asciidoctor-2.0.12/lib/asciidoctor/extensions.rb
|
||||
]
|
||||
end
|
||||
|
||||
|
|
|
@ -475,7 +475,7 @@ FactoryBot.define do
|
|||
trait :license_scanning do
|
||||
options do
|
||||
{
|
||||
artifacts: { reports: { license_management: 'gl-license-scanning-report.json' } }
|
||||
artifacts: { reports: { license_scanning: 'gl-license-scanning-report.json' } }
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -127,7 +127,7 @@ RSpec.describe 'Project members list' do
|
|||
it 'does not show form used to change roles and "Expiration date" or the remove user button' do
|
||||
visit_members_page
|
||||
|
||||
page.within find_username_row(project_bot) do
|
||||
page.within find_member_row(project_bot) do
|
||||
expect(page).not_to have_button('Maintainer')
|
||||
expect(page).to have_field('Expiration date', disabled: true)
|
||||
expect(page).not_to have_button('Remove member')
|
||||
|
|
|
@ -15,10 +15,9 @@ RSpec.describe Security::LicenseComplianceJobsFinder do
|
|||
let!(:container_scanning_build) { create(:ci_build, :container_scanning, pipeline: pipeline) }
|
||||
let!(:dast_build) { create(:ci_build, :dast, pipeline: pipeline) }
|
||||
let!(:license_scanning_build) { create(:ci_build, :license_scanning, pipeline: pipeline) }
|
||||
let!(:license_management_build) { create(:ci_build, :license_management, pipeline: pipeline) }
|
||||
|
||||
it 'returns only the license_scanning jobs' do
|
||||
is_expected.to contain_exactly(license_scanning_build, license_management_build)
|
||||
it 'returns only the license_scanning job' do
|
||||
is_expected.to contain_exactly(license_scanning_build)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,6 +6,18 @@ RSpec.describe Resolvers::NamespaceProjectsResolver do
|
|||
include GraphqlHelpers
|
||||
|
||||
let(:current_user) { create(:user) }
|
||||
let(:include_subgroups) { true }
|
||||
let(:sort) { nil }
|
||||
let(:search) { nil }
|
||||
let(:ids) { nil }
|
||||
let(:args) do
|
||||
{
|
||||
include_subgroups: include_subgroups,
|
||||
sort: sort,
|
||||
search: search,
|
||||
ids: ids
|
||||
}
|
||||
end
|
||||
|
||||
context "with a group" do
|
||||
let(:group) { create(:group) }
|
||||
|
@ -27,7 +39,7 @@ RSpec.describe Resolvers::NamespaceProjectsResolver do
|
|||
end
|
||||
|
||||
it 'finds all projects including the subgroups' do
|
||||
expect(resolve_projects(include_subgroups: true, sort: nil, search: nil)).to contain_exactly(project1, project2, nested_project)
|
||||
expect(resolve_projects(args)).to contain_exactly(project1, project2, nested_project)
|
||||
end
|
||||
|
||||
context 'with an user namespace' do
|
||||
|
@ -38,7 +50,7 @@ RSpec.describe Resolvers::NamespaceProjectsResolver do
|
|||
end
|
||||
|
||||
it 'finds all projects including the subgroups' do
|
||||
expect(resolve_projects(include_subgroups: true, sort: nil, search: nil)).to contain_exactly(project1, project2)
|
||||
expect(resolve_projects(args)).to contain_exactly(project1, project2)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -48,6 +60,9 @@ RSpec.describe Resolvers::NamespaceProjectsResolver do
|
|||
let(:project_2) { create(:project, name: 'Test Project', path: 'test-project', namespace: namespace) }
|
||||
let(:project_3) { create(:project, name: 'Test', path: 'test', namespace: namespace) }
|
||||
|
||||
let(:sort) { :similarity }
|
||||
let(:search) { 'test' }
|
||||
|
||||
before do
|
||||
project_1.add_developer(current_user)
|
||||
project_2.add_developer(current_user)
|
||||
|
@ -55,7 +70,7 @@ RSpec.describe Resolvers::NamespaceProjectsResolver do
|
|||
end
|
||||
|
||||
it 'returns projects ordered by similarity to the search input' do
|
||||
projects = resolve_projects(include_subgroups: true, sort: :similarity, search: 'test')
|
||||
projects = resolve_projects(args)
|
||||
|
||||
project_names = projects.map { |proj| proj['name'] }
|
||||
expect(project_names.first).to eq('Test')
|
||||
|
@ -63,15 +78,17 @@ RSpec.describe Resolvers::NamespaceProjectsResolver do
|
|||
end
|
||||
|
||||
it 'filters out result that do not match the search input' do
|
||||
projects = resolve_projects(include_subgroups: true, sort: :similarity, search: 'test')
|
||||
projects = resolve_projects(args)
|
||||
|
||||
project_names = projects.map { |proj| proj['name'] }
|
||||
expect(project_names).not_to include('Project')
|
||||
end
|
||||
|
||||
context 'when `search` parameter is not given' do
|
||||
let(:search) { nil }
|
||||
|
||||
it 'returns projects not ordered by similarity' do
|
||||
projects = resolve_projects(include_subgroups: true, sort: :similarity, search: nil)
|
||||
projects = resolve_projects(args)
|
||||
|
||||
project_names = projects.map { |proj| proj['name'] }
|
||||
expect(project_names.first).not_to eq('Test')
|
||||
|
@ -79,14 +96,40 @@ RSpec.describe Resolvers::NamespaceProjectsResolver do
|
|||
end
|
||||
|
||||
context 'when only search term is given' do
|
||||
let(:sort) { nil }
|
||||
let(:search) { 'test' }
|
||||
|
||||
it 'filters out result that do not match the search input, but does not sort them' do
|
||||
projects = resolve_projects(include_subgroups: true, sort: :nil, search: 'test')
|
||||
projects = resolve_projects(args)
|
||||
|
||||
project_names = projects.map { |proj| proj['name'] }
|
||||
expect(project_names).to contain_exactly('Test', 'Test Project')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'ids filtering' do
|
||||
subject(:projects) { resolve_projects(args) }
|
||||
|
||||
let(:include_subgroups) { false }
|
||||
let(:project_3) { create(:project, name: 'Project', path: 'project', namespace: namespace) }
|
||||
|
||||
context 'when ids is provided' do
|
||||
let(:ids) { [project_3.to_global_id.to_s] }
|
||||
|
||||
it 'returns matching project' do
|
||||
expect(projects).to contain_exactly(project_3)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when ids is nil' do
|
||||
let(:ids) { nil }
|
||||
|
||||
it 'returns all projects' do
|
||||
expect(projects).to contain_exactly(project1, project2, project_3)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when passing a non existent, batch loaded namespace" do
|
||||
|
@ -108,7 +151,7 @@ RSpec.describe Resolvers::NamespaceProjectsResolver do
|
|||
expect(field.to_graphql.complexity.call({}, { include_subgroups: true }, 1)).to eq 24
|
||||
end
|
||||
|
||||
def resolve_projects(args = { include_subgroups: false, sort: nil, search: nil }, context = { current_user: current_user })
|
||||
def resolve_projects(args = { include_subgroups: false, sort: nil, search: nil, ids: nil }, context = { current_user: current_user })
|
||||
resolve(described_class, obj: namespace, args: args, ctx: context)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -35,22 +35,4 @@ RSpec.describe API::Entities::User do
|
|||
expect(subject[:bot]).to eq(true)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with project bot user' do
|
||||
let(:user) { create(:user, :project_bot) }
|
||||
|
||||
context 'when the requester is not an admin' do
|
||||
it 'does not expose project bot user name' do
|
||||
expect(subject).not_to include(:name)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the requester is an admin' do
|
||||
let(:current_user) { create(:user, :admin) }
|
||||
|
||||
it 'exposes project bot user name' do
|
||||
expect(subject).to include(:name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -92,6 +92,10 @@ RSpec.describe Gitlab::Ci::Variables::Collection::Item do
|
|||
variable: { key: 'VAR', value: 'something_${VAR2}_$VAR3' },
|
||||
expected_depends_on: %w(VAR2 VAR3)
|
||||
},
|
||||
"complex expansion in raw variable": {
|
||||
variable: { key: 'VAR', value: 'something_${VAR2}_$VAR3', raw: true },
|
||||
expected_depends_on: nil
|
||||
},
|
||||
"complex expansions for Windows": {
|
||||
variable: { key: 'variable3', value: 'key%variable%%variable2%' },
|
||||
expected_depends_on: %w(variable variable2)
|
||||
|
@ -156,6 +160,26 @@ RSpec.describe Gitlab::Ci::Variables::Collection::Item do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#raw' do
|
||||
it 'returns false when :raw is not specified' do
|
||||
item = described_class.new(**variable)
|
||||
|
||||
expect(item.raw).to eq false
|
||||
end
|
||||
|
||||
context 'when :raw is specified as true' do
|
||||
let(:variable) do
|
||||
{ key: variable_key, value: variable_value, public: true, masked: false, raw: true }
|
||||
end
|
||||
|
||||
it 'returns true' do
|
||||
item = described_class.new(**variable)
|
||||
|
||||
expect(item.raw).to eq true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#to_runner_variable' do
|
||||
context 'when variable is not a file-related' do
|
||||
it 'returns a runner-compatible hash representation' do
|
||||
|
@ -185,5 +209,19 @@ RSpec.describe Gitlab::Ci::Variables::Collection::Item do
|
|||
expect(runner_variable.depends_on).to eq(%w(CI_VAR_2 CI_VAR_3))
|
||||
end
|
||||
end
|
||||
|
||||
context 'when assigned the raw attribute' do
|
||||
it 'retains a true raw attribute' do
|
||||
runner_variable = described_class.new(key: 'CI_VAR', value: '123', raw: true)
|
||||
|
||||
expect(runner_variable).to eq(key: 'CI_VAR', value: '123', public: true, masked: false, raw: true)
|
||||
end
|
||||
|
||||
it 'does not retain a false raw attribute' do
|
||||
runner_variable = described_class.new(key: 'CI_VAR', value: '123', raw: false)
|
||||
|
||||
expect(runner_variable).to eq(key: 'CI_VAR', value: '123', public: true, masked: false)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -76,6 +76,13 @@ RSpec.describe Gitlab::Ci::Variables::Collection::Sort do
|
|||
{ key: 'variable2', value: '$variable3' },
|
||||
{ key: 'variable3', value: 'key$variable$variable2' }
|
||||
]
|
||||
},
|
||||
"array with raw variable": {
|
||||
variables: [
|
||||
{ key: 'variable', value: '$variable2' },
|
||||
{ key: 'variable2', value: '$variable3' },
|
||||
{ key: 'variable3', value: 'key$variable$variable2', raw: true }
|
||||
]
|
||||
}
|
||||
}
|
||||
end
|
||||
|
@ -128,6 +135,14 @@ RSpec.describe Gitlab::Ci::Variables::Collection::Sort do
|
|||
{ key: 'variable3', value: 'key$variable$variable2' }
|
||||
],
|
||||
validation_result: 'circular variable reference detected: ["variable", "variable2", "variable3"]'
|
||||
},
|
||||
"array with raw variable": {
|
||||
variables: [
|
||||
{ key: 'variable', value: '$variable2' },
|
||||
{ key: 'variable2', value: '$variable3' },
|
||||
{ key: 'variable3', value: 'key$variable$variable2', raw: true }
|
||||
],
|
||||
validation_result: nil
|
||||
}
|
||||
}
|
||||
end
|
||||
|
@ -271,6 +286,22 @@ RSpec.describe Gitlab::Ci::Variables::Collection::Sort do
|
|||
{ key: 'variable', value: '$variable2' }
|
||||
],
|
||||
result: %w[variable2 variable3 variable]
|
||||
},
|
||||
"raw variable does not get resolved": {
|
||||
variables: [
|
||||
{ key: 'variable', value: '$variable2' },
|
||||
{ key: 'variable2', value: '$variable3' },
|
||||
{ key: 'variable3', value: 'key$variable$variable2', raw: true }
|
||||
],
|
||||
result: %w[variable3 variable2 variable]
|
||||
},
|
||||
"variable containing escaped variable reference": {
|
||||
variables: [
|
||||
{ key: 'variable_c', value: '$variable_b' },
|
||||
{ key: 'variable_b', value: '$$variable_a' },
|
||||
{ key: 'variable_a', value: 'value' }
|
||||
],
|
||||
result: %w[variable_a variable_b variable_c]
|
||||
}
|
||||
}
|
||||
end
|
||||
|
|
|
@ -1,91 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::Cleanup::Redis::BatchDeleteByPattern, :clean_gitlab_redis_cache do
|
||||
subject { described_class.new(patterns) }
|
||||
|
||||
describe 'execute' do
|
||||
context 'when no patterns are passed' do
|
||||
before do
|
||||
expect(Gitlab::Redis::Cache).not_to receive(:with)
|
||||
end
|
||||
|
||||
context 'with nil patterns' do
|
||||
let(:patterns) { nil }
|
||||
|
||||
specify { expect { subject }.to raise_error(ArgumentError, 'Argument should be an Array of patterns') }
|
||||
end
|
||||
|
||||
context 'with empty array patterns' do
|
||||
let(:patterns) { [] }
|
||||
|
||||
specify { subject.execute }
|
||||
end
|
||||
end
|
||||
|
||||
context 'with patterns' do
|
||||
context 'when key is not found' do
|
||||
let(:patterns) { ['key'] }
|
||||
|
||||
before do
|
||||
expect_any_instance_of(Redis).not_to receive(:del) # rubocop:disable RSpec/AnyInstanceOf
|
||||
end
|
||||
|
||||
specify { subject.execute }
|
||||
end
|
||||
|
||||
context 'with cache data' do
|
||||
let(:cache_keys) { %w[key-test1 key-test2 key-test3 key-test4] }
|
||||
|
||||
before do
|
||||
stub_const("#{described_class}::REDIS_CLEAR_BATCH_SIZE", 2)
|
||||
|
||||
write_to_cache
|
||||
end
|
||||
|
||||
context 'with one key' do
|
||||
let(:patterns) { ['key-test1'] }
|
||||
|
||||
it 'deletes the key' do
|
||||
expect_any_instance_of(Redis).to receive(:del).with(patterns.first).once # rubocop:disable RSpec/AnyInstanceOf
|
||||
|
||||
subject.execute
|
||||
end
|
||||
end
|
||||
|
||||
context 'with many keys' do
|
||||
let(:patterns) { %w[key-test1 key-test2] }
|
||||
|
||||
it 'deletes keys for each pattern separatelly' do
|
||||
expect_any_instance_of(Redis).to receive(:del).with(patterns.first).once # rubocop:disable RSpec/AnyInstanceOf
|
||||
expect_any_instance_of(Redis).to receive(:del).with(patterns.last).once # rubocop:disable RSpec/AnyInstanceOf
|
||||
|
||||
subject.execute
|
||||
end
|
||||
end
|
||||
|
||||
context 'with cache_keys over batch size' do
|
||||
let(:patterns) { %w[key-test*] }
|
||||
|
||||
it 'deletes matched keys in batches' do
|
||||
# redis scan returns the values in random order so just checking it is being called twice meaning
|
||||
# scan returned results in 2 batches, which is what we expect
|
||||
key_like = start_with('key-test')
|
||||
expect_any_instance_of(Redis).to receive(:del).with(key_like, key_like).twice # rubocop:disable RSpec/AnyInstanceOf
|
||||
|
||||
subject.execute
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def write_to_cache
|
||||
Gitlab::Redis::Cache.with do |redis|
|
||||
cache_keys.each_with_index do |cache_key, index|
|
||||
redis.set(cache_key, index)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,94 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::Cleanup::Redis::DescriptionTemplatesCacheKeysPatternBuilder, :clean_gitlab_redis_cache do
|
||||
subject { described_class.new(project_ids).execute }
|
||||
|
||||
describe 'execute' do
|
||||
context 'when build pattern for all description templates' do
|
||||
RSpec.shared_examples 'all issue and merge request templates pattern' do
|
||||
it 'builds pattern to remove all issue and merge request templates keys' do
|
||||
expect(subject.count).to eq(2)
|
||||
expect(subject).to match_array(%W[
|
||||
#{Gitlab::Redis::Cache::CACHE_NAMESPACE}:issue_template_names_hash:*
|
||||
#{Gitlab::Redis::Cache::CACHE_NAMESPACE}:merge_request_template_names_hash:*
|
||||
])
|
||||
end
|
||||
end
|
||||
|
||||
context 'with project_ids == :all' do
|
||||
let(:project_ids) { :all }
|
||||
|
||||
it_behaves_like 'all issue and merge request templates pattern'
|
||||
end
|
||||
end
|
||||
|
||||
context 'with project_ids' do
|
||||
let_it_be(:project1) { create(:project, :repository) }
|
||||
let_it_be(:project2) { create(:project, :repository) }
|
||||
|
||||
context 'with nil project_ids' do
|
||||
let(:project_ids) { nil }
|
||||
|
||||
specify { expect { subject }.to raise_error(ArgumentError, 'project_ids can either be an array of project IDs or :all') }
|
||||
end
|
||||
|
||||
context 'with project_ids as string' do
|
||||
let(:project_ids) { '1' }
|
||||
|
||||
specify { expect { subject }.to raise_error(ArgumentError, 'project_ids can either be an array of project IDs or :all') }
|
||||
end
|
||||
|
||||
context 'with invalid project_ids as array of strings' do
|
||||
let(:project_ids) { %w[a b] }
|
||||
|
||||
specify { expect { subject }.to raise_error(ArgumentError, 'Invalid Project ID. Please ensure all passed in project ids values are valid integer project ids.') }
|
||||
end
|
||||
|
||||
context 'with non existent project id' do
|
||||
let(:project_ids) { [non_existing_record_id] }
|
||||
|
||||
it 'no patterns are built' do
|
||||
expect(subject.count).to eq(0)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with one project_id' do
|
||||
let(:project_ids) { [project1.id] }
|
||||
|
||||
it 'builds patterns for the project' do
|
||||
expect(subject.count).to eq(2)
|
||||
expect(subject).to match_array(%W[
|
||||
#{Gitlab::Redis::Cache::CACHE_NAMESPACE}:issue_template_names_hash:#{project1.full_path}:#{project1.id}
|
||||
#{Gitlab::Redis::Cache::CACHE_NAMESPACE}:merge_request_template_names_hash:#{project1.full_path}:#{project1.id}
|
||||
])
|
||||
end
|
||||
end
|
||||
|
||||
context 'with many project_ids' do
|
||||
let(:project_ids) { [project1.id, project2.id] }
|
||||
|
||||
RSpec.shared_examples 'builds patterns for the given projects' do
|
||||
it 'builds patterns for the given projects' do
|
||||
expect(subject.count).to eq(4)
|
||||
expect(subject).to match_array(%W[
|
||||
#{Gitlab::Redis::Cache::CACHE_NAMESPACE}:issue_template_names_hash:#{project1.full_path}:#{project1.id}
|
||||
#{Gitlab::Redis::Cache::CACHE_NAMESPACE}:merge_request_template_names_hash:#{project1.full_path}:#{project1.id}
|
||||
#{Gitlab::Redis::Cache::CACHE_NAMESPACE}:issue_template_names_hash:#{project2.full_path}:#{project2.id}
|
||||
#{Gitlab::Redis::Cache::CACHE_NAMESPACE}:merge_request_template_names_hash:#{project2.full_path}:#{project2.id}
|
||||
])
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'builds patterns for the given projects'
|
||||
|
||||
context 'with project_ids as string' do
|
||||
let(:project_ids) { [project1.id.to_s, project2.id.to_s] }
|
||||
|
||||
it_behaves_like 'builds patterns for the given projects'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -152,9 +152,13 @@ RSpec.describe Users::RefreshAuthorizedProjectsService do
|
|||
expect(Gitlab::AppJsonLogger).to(
|
||||
receive(:info)
|
||||
.with(event: 'authorized_projects_refresh',
|
||||
user_id: user.id,
|
||||
'authorized_projects_refresh.source': source,
|
||||
'authorized_projects_refresh.rows_deleted': 0,
|
||||
'authorized_projects_refresh.rows_added': 1))
|
||||
'authorized_projects_refresh.rows_deleted_count': 0,
|
||||
'authorized_projects_refresh.rows_added_count': 1,
|
||||
'authorized_projects_refresh.rows_deleted_slice': [],
|
||||
'authorized_projects_refresh.rows_added_slice': [[user.id, project.id, Gitlab::Access::MAINTAINER]])
|
||||
)
|
||||
|
||||
service.update_authorizations([], [[user.id, project.id, Gitlab::Access::MAINTAINER]])
|
||||
end
|
||||
|
|
|
@ -41,10 +41,6 @@ module Spec
|
|||
find_row(user.name)
|
||||
end
|
||||
|
||||
def find_username_row(user)
|
||||
find_row(user.username)
|
||||
end
|
||||
|
||||
def find_invited_member_row(email)
|
||||
find_row(email)
|
||||
end
|
||||
|
|
25
spec/tasks/cache/clear/redis_spec.rb
vendored
25
spec/tasks/cache/clear/redis_spec.rb
vendored
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'rake_helper'
|
||||
|
||||
RSpec.describe 'clearing redis cache', :clean_gitlab_redis_cache do
|
||||
RSpec.describe 'clearing redis cache' do
|
||||
before do
|
||||
Rake.application.rake_require 'tasks/cache'
|
||||
end
|
||||
|
@ -21,27 +21,4 @@ RSpec.describe 'clearing redis cache', :clean_gitlab_redis_cache do
|
|||
expect { run_rake_task('cache:clear:redis') }.to change { pipeline_status.has_cache? }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'invoking clear description templates cache rake task' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
before do
|
||||
stub_env('project_ids', project_ids) if project_ids
|
||||
service = double(:service, execute: true)
|
||||
|
||||
expect(Gitlab::Cleanup::Redis::DescriptionTemplatesCacheKeysPatternBuilder).to receive(:new).with(expected_project_ids).and_return(service)
|
||||
expect(Gitlab::Cleanup::Redis::BatchDeleteByPattern).to receive(:new).and_return(service)
|
||||
end
|
||||
|
||||
where(:project_ids, :expected_project_ids) do
|
||||
nil | [] # this acts as no argument is being passed
|
||||
'1' | %w[1]
|
||||
'1, 2, 3' | %w[1 2 3]
|
||||
'1, 2, some-string, 3' | %w[1 2 some-string 3]
|
||||
end
|
||||
|
||||
with_them do
|
||||
specify { run_rake_task('cache:clear:description_templates') }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue