summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-08-10 06:08:47 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2021-08-10 06:08:47 +0000
commit0adc81d8e0c7b291fd7fdef33a4ea9c01b4852ce (patch)
treea5ef7b7c4109427b748d239066435151a43fb499
parentf2c0afdcb5597b226d75cdb043e57f79034b04bb (diff)
downloadgitlab-ce-0adc81d8e0c7b291fd7fdef33a4ea9c01b4852ce.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/assets/javascripts/commit/pipelines/pipelines_table.vue3
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_list/pipeline_url.vue6
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_list/pipelines.vue48
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_list/pipelines_filtered_search.vue14
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table.vue131
-rw-r--r--app/assets/javascripts/pipelines/constants.js14
-rw-r--r--app/controllers/projects_controller.rb2
-rw-r--r--app/helpers/nav/top_nav_helper.rb6
-rw-r--r--app/models/ci/build.rb2
-rw-r--r--doc/ci/chatops/index.md14
-rw-r--r--doc/ci/ci_cd_for_external_repos/bitbucket_integration.md11
-rw-r--r--doc/ci/ci_cd_for_external_repos/github_integration.md74
-rw-r--r--doc/ci/pipelines/index.md6
-rw-r--r--doc/ci/pipelines/settings.md9
-rw-r--r--doc/ci/yaml/index.md17
-rw-r--r--doc/development/database/multiple_databases.md50
-rw-r--r--lib/gitlab/background_migration/recalculate_vulnerabilities_occurrences_uuid.rb5
-rw-r--r--locale/gitlab.pot60
-rw-r--r--spec/controllers/projects_controller_spec.rb26
-rw-r--r--spec/features/projects/pipelines/pipelines_spec.rb20
-rw-r--r--spec/frontend/pipelines/pipeline_url_spec.js1
-rw-r--r--spec/frontend/pipelines/pipelines_spec.js9
-rw-r--r--spec/frontend/pipelines/pipelines_table_spec.js4
-rw-r--r--spec/lib/gitlab/background_migration/recalculate_vulnerabilities_occurrences_uuid_spec.rb11
-rw-r--r--spec/models/ci/build_spec.rb8
-rw-r--r--spec/support/database/prevent_cross_joins.rb26
26 files changed, 370 insertions, 207 deletions
diff --git a/app/assets/javascripts/commit/pipelines/pipelines_table.vue b/app/assets/javascripts/commit/pipelines/pipelines_table.vue
index 42d46dc3d5d..b92f3d5a97b 100644
--- a/app/assets/javascripts/commit/pipelines/pipelines_table.vue
+++ b/app/assets/javascripts/commit/pipelines/pipelines_table.vue
@@ -2,6 +2,7 @@
import { GlButton, GlEmptyState, GlLoadingIcon, GlModal, GlLink } from '@gitlab/ui';
import { getParameterByName } from '~/lib/utils/url_utility';
import PipelinesTableComponent from '~/pipelines/components/pipelines_list/pipelines_table.vue';
+import { PipelineKeyOptions } from '~/pipelines/constants';
import eventHub from '~/pipelines/event_hub';
import PipelinesMixin from '~/pipelines/mixins/pipelines_mixin';
import PipelinesService from '~/pipelines/services/pipelines_service';
@@ -10,6 +11,7 @@ import TablePagination from '~/vue_shared/components/pagination/table_pagination
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
export default {
+ PipelineKeyOptions,
components: {
GlButton,
GlEmptyState,
@@ -205,6 +207,7 @@ export default {
:pipelines="state.pipelines"
:update-graph-dropdown="updateGraphDropdown"
:view-type="viewType"
+ :pipeline-key-option="$options.PipelineKeyOptions[0]"
>
<template #table-header-actions>
<div v-if="canRenderPipelineButton" class="gl-text-right">
diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_url.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_url.vue
index fc8f31c5b7e..e2f30d5a8e6 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_url.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_url.vue
@@ -29,6 +29,10 @@ export default {
type: String,
required: true,
},
+ pipelineKey: {
+ type: String,
+ required: true,
+ },
},
computed: {
user() {
@@ -60,7 +64,7 @@ export default {
data-testid="pipeline-url-link"
data-qa-selector="pipeline_url_link"
>
- #{{ pipeline.id }}
+ #{{ pipeline[pipelineKey] }}
</gl-link>
<div class="label-container">
<gl-badge
diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines.vue
index e3373178239..e7ff5449331 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines.vue
@@ -1,12 +1,17 @@
<script>
-import { GlEmptyState, GlIcon, GlLoadingIcon } from '@gitlab/ui';
+import { GlDropdown, GlDropdownItem, GlEmptyState, GlIcon, GlLoadingIcon } from '@gitlab/ui';
import { isEqual } from 'lodash';
import createFlash from '~/flash';
import { getParameterByName } from '~/lib/utils/url_utility';
import { __, s__ } from '~/locale';
import NavigationTabs from '~/vue_shared/components/navigation_tabs.vue';
import TablePagination from '~/vue_shared/components/pagination/table_pagination.vue';
-import { ANY_TRIGGER_AUTHOR, RAW_TEXT_WARNING, FILTER_TAG_IDENTIFIER } from '../../constants';
+import {
+ ANY_TRIGGER_AUTHOR,
+ RAW_TEXT_WARNING,
+ FILTER_TAG_IDENTIFIER,
+ PipelineKeyOptions,
+} from '../../constants';
import PipelinesMixin from '../../mixins/pipelines_mixin';
import PipelinesService from '../../services/pipelines_service';
import { validateParams } from '../../utils';
@@ -16,8 +21,11 @@ import PipelinesFilteredSearch from './pipelines_filtered_search.vue';
import PipelinesTableComponent from './pipelines_table.vue';
export default {
+ PipelineKeyOptions,
components: {
EmptyState,
+ GlDropdown,
+ GlDropdownItem,
GlEmptyState,
GlIcon,
GlLoadingIcon,
@@ -114,6 +122,7 @@ export default {
page: getParameterByName('page') || '1',
requestData: {},
isResetCacheButtonLoading: false,
+ selectedPipelineKeyOption: this.$options.PipelineKeyOptions[0],
};
},
stateMap: {
@@ -301,6 +310,9 @@ export default {
this.updateContent(this.requestData);
},
+ changeVisibilityPipelineID(val) {
+ this.selectedPipelineKeyOption = val;
+ },
},
};
</script>
@@ -330,12 +342,31 @@ export default {
/>
</div>
- <pipelines-filtered-search
- v-if="stateToRender !== $options.stateMap.emptyState"
- :project-id="projectId"
- :params="validatedParams"
- @filterPipelines="filterPipelines"
- />
+ <div v-if="stateToRender !== $options.stateMap.emptyState" class="gl-display-flex">
+ <div class="row-content-block gl-display-flex gl-flex-grow-1">
+ <pipelines-filtered-search
+ class="gl-display-flex gl-flex-grow-1 gl-mr-4"
+ :project-id="projectId"
+ :params="validatedParams"
+ @filterPipelines="filterPipelines"
+ />
+ <gl-dropdown
+ class="gl-display-flex"
+ :text="selectedPipelineKeyOption.text"
+ data-testid="pipeline-key-dropdown"
+ >
+ <gl-dropdown-item
+ v-for="(val, index) in $options.PipelineKeyOptions"
+ :key="index"
+ :is-checked="selectedPipelineKeyOption.key === val.key"
+ is-check-item
+ @click="changeVisibilityPipelineID(val)"
+ >
+ {{ val.text }}
+ </gl-dropdown-item>
+ </gl-dropdown>
+ </div>
+ </div>
<div class="content-list pipelines">
<gl-loading-icon
@@ -374,6 +405,7 @@ export default {
:pipeline-schedule-url="pipelineScheduleUrl"
:update-graph-dropdown="updateGraphDropdown"
:view-type="viewType"
+ :pipeline-key-option="selectedPipelineKeyOption"
/>
</div>
diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_filtered_search.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_filtered_search.vue
index de3f783ac84..a7e1443df30 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_filtered_search.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_filtered_search.vue
@@ -101,12 +101,10 @@ export default {
</script>
<template>
- <div class="row-content-block">
- <gl-filtered-search
- v-model="value"
- :placeholder="__('Filter pipelines')"
- :available-tokens="tokens"
- @submit="onSubmit"
- />
- </div>
+ <gl-filtered-search
+ v-model="value"
+ :placeholder="__('Filter pipelines')"
+ :available-tokens="tokens"
+ @submit="onSubmit"
+ />
</template>
diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table.vue
index 949a58f80fd..2475d958e3c 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table.vue
@@ -17,63 +17,6 @@ const DEFAULT_TH_CLASSES =
'gl-bg-transparent! gl-border-b-solid! gl-border-b-gray-100! gl-p-5! gl-border-b-1! gl-font-sm!';
export default {
- fields: [
- {
- key: 'status',
- label: s__('Pipeline|Status'),
- thClass: DEFAULT_TH_CLASSES,
- columnClass: 'gl-w-10p',
- tdClass: DEFAULT_TD_CLASS,
- thAttr: { 'data-testid': 'status-th' },
- },
- {
- key: 'pipeline',
- label: s__('Pipeline|Pipeline'),
- thClass: DEFAULT_TH_CLASSES,
- tdClass: `${DEFAULT_TD_CLASS} ${HIDE_TD_ON_MOBILE}`,
- columnClass: 'gl-w-10p',
- thAttr: { 'data-testid': 'pipeline-th' },
- },
- {
- key: 'triggerer',
- label: s__('Pipeline|Triggerer'),
- thClass: DEFAULT_TH_CLASSES,
- tdClass: `${DEFAULT_TD_CLASS} ${HIDE_TD_ON_MOBILE}`,
- columnClass: 'gl-w-10p',
- thAttr: { 'data-testid': 'triggerer-th' },
- },
- {
- key: 'commit',
- label: s__('Pipeline|Commit'),
- thClass: DEFAULT_TH_CLASSES,
- tdClass: DEFAULT_TD_CLASS,
- columnClass: 'gl-w-20p',
- thAttr: { 'data-testid': 'commit-th' },
- },
- {
- key: 'stages',
- label: s__('Pipeline|Stages'),
- thClass: DEFAULT_TH_CLASSES,
- tdClass: DEFAULT_TD_CLASS,
- columnClass: 'gl-w-quarter',
- thAttr: { 'data-testid': 'stages-th' },
- },
- {
- key: 'timeago',
- label: s__('Pipeline|Duration'),
- thClass: DEFAULT_TH_CLASSES,
- tdClass: DEFAULT_TD_CLASS,
- columnClass: 'gl-w-15p',
- thAttr: { 'data-testid': 'timeago-th' },
- },
- {
- key: 'actions',
- thClass: DEFAULT_TH_CLASSES,
- tdClass: DEFAULT_TD_CLASS,
- columnClass: 'gl-w-15p',
- thAttr: { 'data-testid': 'actions-th' },
- },
- ],
components: {
GlTable,
LinkedPipelinesMiniList: () =>
@@ -109,6 +52,10 @@ export default {
type: String,
required: true,
},
+ pipelineKeyOption: {
+ type: Object,
+ required: true,
+ },
},
data() {
return {
@@ -118,6 +65,68 @@ export default {
cancelingPipeline: null,
};
},
+ computed: {
+ tableFields() {
+ const fields = [
+ {
+ key: 'status',
+ label: s__('Pipeline|Status'),
+ thClass: DEFAULT_TH_CLASSES,
+ columnClass: 'gl-w-10p',
+ tdClass: DEFAULT_TD_CLASS,
+ thAttr: { 'data-testid': 'status-th' },
+ },
+ {
+ key: 'pipeline',
+ label: this.pipelineKeyOption.label,
+ thClass: DEFAULT_TH_CLASSES,
+ tdClass: `${DEFAULT_TD_CLASS} ${HIDE_TD_ON_MOBILE}`,
+ columnClass: 'gl-w-10p',
+ thAttr: { 'data-testid': 'pipeline-th' },
+ },
+ {
+ key: 'triggerer',
+ label: s__('Pipeline|Triggerer'),
+ thClass: DEFAULT_TH_CLASSES,
+ tdClass: `${DEFAULT_TD_CLASS} ${HIDE_TD_ON_MOBILE}`,
+ columnClass: 'gl-w-10p',
+ thAttr: { 'data-testid': 'triggerer-th' },
+ },
+ {
+ key: 'commit',
+ label: s__('Pipeline|Commit'),
+ thClass: DEFAULT_TH_CLASSES,
+ tdClass: DEFAULT_TD_CLASS,
+ columnClass: 'gl-w-20p',
+ thAttr: { 'data-testid': 'commit-th' },
+ },
+ {
+ key: 'stages',
+ label: s__('Pipeline|Stages'),
+ thClass: DEFAULT_TH_CLASSES,
+ tdClass: DEFAULT_TD_CLASS,
+ columnClass: 'gl-w-quarter',
+ thAttr: { 'data-testid': 'stages-th' },
+ },
+ {
+ key: 'timeago',
+ label: s__('Pipeline|Duration'),
+ thClass: DEFAULT_TH_CLASSES,
+ tdClass: DEFAULT_TD_CLASS,
+ columnClass: 'gl-w-15p',
+ thAttr: { 'data-testid': 'timeago-th' },
+ },
+ {
+ key: 'actions',
+ thClass: DEFAULT_TH_CLASSES,
+ tdClass: DEFAULT_TD_CLASS,
+ columnClass: 'gl-w-15p',
+ thAttr: { 'data-testid': 'actions-th' },
+ },
+ ];
+ return fields;
+ },
+ },
watch: {
pipelines() {
this.cancelingPipeline = null;
@@ -148,7 +157,7 @@ export default {
<template>
<div class="ci-table">
<gl-table
- :fields="$options.fields"
+ :fields="tableFields"
:items="pipelines"
tbody-tr-class="commit"
:tbody-tr-attr="{ 'data-testid': 'pipeline-table-row' }"
@@ -169,7 +178,11 @@ export default {
</template>
<template #cell(pipeline)="{ item }">
- <pipeline-url :pipeline="item" :pipeline-schedule-url="pipelineScheduleUrl" />
+ <pipeline-url
+ :pipeline="item"
+ :pipeline-schedule-url="pipelineScheduleUrl"
+ :pipeline-key="pipelineKeyOption.key"
+ />
</template>
<template #cell(triggerer)="{ item }">
diff --git a/app/assets/javascripts/pipelines/constants.js b/app/assets/javascripts/pipelines/constants.js
index 21b114825a6..a2c7eaec113 100644
--- a/app/assets/javascripts/pipelines/constants.js
+++ b/app/assets/javascripts/pipelines/constants.js
@@ -35,3 +35,17 @@ export const POST_FAILURE = 'post_failure';
export const UNSUPPORTED_DATA = 'unsupported_data';
export const CHILD_VIEW = 'child';
+
+// Constants for the ID and IID selection dropdown
+export const PipelineKeyOptions = [
+ {
+ text: __('Show Pipeline ID'),
+ label: __('Pipeline ID'),
+ key: 'id',
+ },
+ {
+ text: __('Show Pipeline IID'),
+ label: __('Pipeline IID'),
+ key: 'iid',
+ },
+];
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 9aa1ed65fa5..bdb645e1934 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -54,8 +54,6 @@ class ProjectsController < Projects::ApplicationController
# rubocop: disable CodeReuse/ActiveRecord
def new
- return access_denied! unless current_user.can_create_project?
-
@namespace = Namespace.find_by(id: params[:namespace_id]) if params[:namespace_id]
return access_denied! if @namespace && !can?(current_user, :create_projects, @namespace)
diff --git a/app/helpers/nav/top_nav_helper.rb b/app/helpers/nav/top_nav_helper.rb
index 7e200ebc8a8..052b8339ebd 100644
--- a/app/helpers/nav/top_nav_helper.rb
+++ b/app/helpers/nav/top_nav_helper.rb
@@ -267,11 +267,7 @@ module Nav
builder.add_primary_menu_item(id: 'your', title: _('Your projects'), href: dashboard_projects_path)
builder.add_primary_menu_item(id: 'starred', title: _('Starred projects'), href: starred_dashboard_projects_path)
builder.add_primary_menu_item(id: 'explore', title: _('Explore projects'), href: explore_root_path)
-
- if current_user.can_create_project?
- builder.add_secondary_menu_item(id: 'create', title: _('Create new project'), href: new_project_path)
- end
-
+ builder.add_secondary_menu_item(id: 'create', title: _('Create new project'), href: new_project_path)
builder.build
end
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 537d18646c5..5cc8474b71d 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -151,7 +151,7 @@ module Ci
scope :with_project_and_metadata, -> do
if Feature.enabled?(:non_public_artifacts, type: :development)
- joins(:metadata).includes(:project, :metadata)
+ joins(:metadata).includes(:metadata).preload(:project)
end
end
diff --git a/doc/ci/chatops/index.md b/doc/ci/chatops/index.md
index 03a1005c9bc..a461147661c 100644
--- a/doc/ci/chatops/index.md
+++ b/doc/ci/chatops/index.md
@@ -49,7 +49,7 @@ If a job shouldn't be able to be triggered from chat, you can set the job to `ex
Since ChatOps is built upon GitLab CI/CD, the job has all the same features and
functions available. Consider these best practices when creating ChatOps jobs:
-- GitLab strongly recommends you set `only: [chat]` so the job does not run as part
+- GitLab strongly recommends you set [`rules`](../yaml/index.md#rules) so the job does not run as part
of the standard CI pipeline.
- If the job is set to `when: manual`, ChatOps creates the pipeline, but the job waits to be started.
- ChatOps provides limited support for access control. If the user triggering the
@@ -65,9 +65,13 @@ The output for jobs with a single command is sent to the channel as a reply. For
example, the chat reply of the following job is `Hello World` in the channel:
```yaml
+stages:
+- chatops
+
hello-world:
stage: chatops
- only: [chat]
+ rules:
+ - if: '$CI_PIPELINE_SOURCE == "chat"'
script:
- echo "Hello World"
```
@@ -81,9 +85,13 @@ the `chat_reply` section. For example, the following job lists the files in the
current directory:
```yaml
+stages:
+- chatops
+
ls:
stage: chatops
- only: [chat]
+ rules:
+ - if: '$CI_PIPELINE_SOURCE == "chat"'
script:
- echo "This command will not be shown."
- echo -e "section_start:$( date +%s ):chat_reply\r\033[0K\n$( ls -la )\nsection_end:$( date +%s ):chat_reply\r\033[0K"
diff --git a/doc/ci/ci_cd_for_external_repos/bitbucket_integration.md b/doc/ci/ci_cd_for_external_repos/bitbucket_integration.md
index 7eaafcf21bb..e69daf6651a 100644
--- a/doc/ci/ci_cd_for_external_repos/bitbucket_integration.md
+++ b/doc/ci/ci_cd_for_external_repos/bitbucket_integration.md
@@ -14,15 +14,16 @@ GitLab CI/CD can be used with Bitbucket Cloud by:
To use GitLab CI/CD with a Bitbucket Cloud repository:
-1. <!-- vale gitlab.Spelling = NO --> In GitLab create a **CI/CD for external repository**, select
- **Repo by URL** and create the project.
- <!-- vale gitlab.Spelling = YES -->
+1. In GitLab, create a project:
+ 1. On the top menu, select **Projects > Create new project**.
+ 1. Select **Run CI/CD for external repository**.
+ 1. Select **Repo by URL**.
![Create project](img/external_repository.png)
GitLab imports the repository and enables [Pull Mirroring](../../user/project/repository/repository_mirroring.md#pull-from-a-remote-repository).
-1. In GitLab create a
+1. In GitLab, create a
[Personal Access Token](../../user/profile/personal_access_tokens.md)
with `api` scope. This is used to authenticate requests from the web
hook that is created in Bitbucket to notify GitLab of new commits.
@@ -120,7 +121,7 @@ To use GitLab CI/CD with a Bitbucket Cloud repository:
\"$BITBUCKET_DESCRIPTION\",\"url\": \"$CI_PROJECT_URL/-/jobs/$CI_JOB_ID\" }"
```
-1. Still in Bitbucket, create a `.gitlab-ci.yml` file to use the script to push
+1. In Bitbucket, create a `.gitlab-ci.yml` file to use the script to push
pipeline success and failures to Bitbucket.
```yaml
diff --git a/doc/ci/ci_cd_for_external_repos/github_integration.md b/doc/ci/ci_cd_for_external_repos/github_integration.md
index 7a79794f322..60a939496d6 100644
--- a/doc/ci/ci_cd_for_external_repos/github_integration.md
+++ b/doc/ci/ci_cd_for_external_repos/github_integration.md
@@ -27,27 +27,26 @@ repositories to GitLab, and the GitHub user must have the [owner role](https://d
To perform a one-off authorization with GitHub to grant GitLab access your
repositories:
-1. Open <https://github.com/settings/tokens/new> to create a **Personal Access
- Token**. This token is used to access your repository and push commit
- statuses to GitHub.
-
- The `repo` and `admin:repo_hook` should be enable to allow GitLab access to
- your project, update commit statuses, and create a web hook to notify
- GitLab of new commits.
-
-1. In GitLab, go to the [new project page](../../user/project/working_with_projects.md#create-a-project), select the **CI/CD for external repository** tab, and then click
- **GitHub**.
-
-1. Paste the token into the **Personal access token** field and click **List
- Repositories**. Click **Connect** to select the repository.
-
+1. In GitHub, create a token:
+ 1. Open <https://github.com/settings/tokens/new>.
+ 1. Create a **Personal Access Token**.
+ 1. Enter a **Token description** and update the scope to allow
+ `repo` and `admin:repo_hook` so that GitLab can access your project,
+ update commit statuses, and create a web hook to notify GitLab of new commits.
+1. In GitLab, create a project:
+ 1. On the top menu, select **Projects > Create new project**.
+ 1. Select **Run CI/CD for external repository**.
+ 1. Select **GitHub**.
+ 1. For **Personal access token**, paste the token.
+ 1. Select **List Repositories**.
+ 1. Select **Connect** to select the repository.
1. In GitHub, add a `.gitlab-ci.yml` to [configure GitLab CI/CD](../quick_start/index.md).
GitLab:
1. Imports the project.
-1. Enables [Pull Mirroring](../../user/project/repository/repository_mirroring.md#pull-from-a-remote-repository)
-1. Enables [GitHub project integration](../../user/project/integrations/github.md)
+1. Enables [Pull Mirroring](../../user/project/repository/repository_mirroring.md#pull-from-a-remote-repository).
+1. Enables [GitHub project integration](../../user/project/integrations/github.md).
1. Creates a web hook on GitHub to notify GitLab of new commits.
## Connect manually
@@ -56,30 +55,25 @@ To use **GitHub Enterprise** with **GitLab.com**, use this method.
To manually enable GitLab CI/CD for your repository:
-1. In GitHub open <https://github.com/settings/tokens/new> create a **Personal
- Access Token.** GitLab uses this token to access your repository and
- push commit statuses.
-
- Enter a **Token description** and update the scope to allow:
-
- `repo` so that GitLab can access your project and update commit statuses
-
-1. In GitLab create a **CI/CD project** using the Git URL option and the HTTPS
- URL for your GitHub repository. If your project is private, use the personal
- access token you just created for authentication.
-
- GitLab automatically configures polling-based pull mirroring.
-
-1. Still in GitLab, enable the [GitHub project integration](../../user/project/integrations/github.md)
- from **Settings > Integrations.**
-
- Check the **Active** checkbox to enable the integration, paste your
- personal access token and HTTPS repository URL into the form, and **Save.**
-
-1. Still in GitLab create a **Personal Access Token** with `API` scope to
+1. In GitHub, create a token:
+ 1. Open <https://github.com/settings/tokens/new>.
+ 1. Create a **Personal Access Token**.
+ 1. Enter a **Token description** and update the scope to allow
+ `repo` so that GitLab can access your project and update commit statuses.
+1. In GitLab, create a project:
+ 1. On the top menu, select **Projects > Create new project**.
+ 1. Select **Run CI/CD for external repository** and **Repo by URL**.
+ 1. In the **Git repository URL** field, enter the HTTPS URL for your GitHub repository.
+ If your project is private, use the personal access token you just created for authentication.
+ 1. Fill in all the other fields and select **Create project**.
+ GitLab automatically configures polling-based pull mirroring.
+1. In GitLab, enable [GitHub project integration](../../user/project/integrations/github.md):
+ 1. On the left sidebar, select **Settings > Integrations**.
+ 1. Select the **Active** checkbox.
+ 1. Paste your personal access token and HTTPS repository URL into the form and select **Save**.
+1. In GitLab, create a **Personal Access Token** with `API` scope to
authenticate the GitHub web hook notifying GitLab of new commits.
-
-1. In GitHub from **Settings > Webhooks** create a web hook to notify GitLab of
+1. In GitHub, from **Settings > Webhooks**, create a web hook to notify GitLab of
new commits.
The web hook URL should be set to the GitLab API to
@@ -92,7 +86,7 @@ To manually enable GitLab CI/CD for your repository:
Select the **Let me select individual events** option, then check the **Pull requests** and **Pushes** checkboxes. These settings are needed for [pipelines for external pull requests](index.md#pipelines-for-external-pull-requests).
-1. In GitHub add a `.gitlab-ci.yml` to configure GitLab CI/CD.
+1. In GitHub, add a `.gitlab-ci.yml` to configure GitLab CI/CD.
<!-- ## Troubleshooting
diff --git a/doc/ci/pipelines/index.md b/doc/ci/pipelines/index.md
index ce0220d4c2a..f1730eb31d5 100644
--- a/doc/ci/pipelines/index.md
+++ b/doc/ci/pipelines/index.md
@@ -123,6 +123,9 @@ you can filter the pipeline list by:
- Status ([GitLab 13.1 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/217617))
- Tag ([GitLab 13.1 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/217617))
+[Starting in GitLab 14.2](https://gitlab.com/gitlab-org/gitlab/-/issues/26621), you can change the
+pipeline column to display the pipeline ID or the pipeline IID.
+
### Run a pipeline manually
Pipelines can be manually executed, with predefined or manually-specified [variables](../variables/index.md).
@@ -167,6 +170,9 @@ variables:
You cannot set job-level variables to be pre-filled when you run a pipeline manually.
+Pre-filled variables do not show up when the CI/CD configuration is [external to the project](settings.md#specify-a-custom-cicd-configuration-file).
+See the [related issue](https://gitlab.com/gitlab-org/gitlab/-/issues/336184) for more details.
+
### Run a pipeline by using a URL query string
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/24146) in GitLab 12.5.
diff --git a/doc/ci/pipelines/settings.md b/doc/ci/pipelines/settings.md
index dc24b913fe7..3c2d30596a6 100644
--- a/doc/ci/pipelines/settings.md
+++ b/doc/ci/pipelines/settings.md
@@ -122,8 +122,13 @@ If the CI/CD configuration file is on an external site, the URL must end with `.
- `http://example.com/generate/ci/config.yml`
-If the CI/CD configuration file is in a different project, the path must be relative
-to the root directory in the other project. Include the group and project name at the end:
+If the CI/CD configuration file is in a different project:
+
+- The file must exist on its default branch.
+- The path must be relative to the root directory in the other project.
+- The path must include the group and project name at the end.
+
+For example:
- `.gitlab-ci.yml@mygroup/another-project`
- `my/path/.my-custom-file.yml@mygroup/another-project`
diff --git a/doc/ci/yaml/index.md b/doc/ci/yaml/index.md
index 56bcbbd3642..6ff24918bf1 100644
--- a/doc/ci/yaml/index.md
+++ b/doc/ci/yaml/index.md
@@ -1530,13 +1530,16 @@ production:
#### Requirements and limitations
-- In [GitLab 14.1 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/30632)
- you can refer to jobs in the same stage as the job you are configuring. This feature
- is [Deployed behind a feature flag](../../user/feature_flags.md), disabled by default.
-- Disabled on GitLab.com.
-- Not recommended for production use.
-- For GitLab self-managed instances, GitLab adminsitrators
- can choose to [disable it](#enable-or-disable-needs-for-jobs-in-the-same-stage)
+- In [GitLab 14.1 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/30632) you
+ can refer to jobs in the same stage as the job you are configuring. This feature is:
+
+ - [Deployed behind a feature flag](../../user/feature_flags.md), disabled by default.
+ - Disabled on GitLab.com.
+ - Not recommended for production use.
+
+ For GitLab self-managed instances, GitLab administrators can choose to
+ [enable it](#enable-or-disable-needs-for-jobs-in-the-same-stage).
+
- In GitLab 14.0 and older, you can only refer to jobs in earlier stages.
- In GitLab 13.9 and older, if `needs:` refers to a job that might not be added to
a pipeline because of `only`, `except`, or `rules`, the pipeline might fail to create.
diff --git a/doc/development/database/multiple_databases.md b/doc/development/database/multiple_databases.md
index a44807a1b05..71dcc5bb866 100644
--- a/doc/development/database/multiple_databases.md
+++ b/doc/development/database/multiple_databases.md
@@ -113,7 +113,7 @@ patterns may apply to future cases.
The simplest solution we've seen several times now has been an existing scope
that is unused. This is the easiest example to fix. So the first step is to
-investigate if the code is unused and then simply remove it. These are some
+investigate if the code is unused and then remove it. These are some
real examples:
- <https://gitlab.com/gitlab-org/gitlab/-/merge_requests/67162>
@@ -131,6 +131,20 @@ to evaluate, because `UsageData` is not critical to users and it may be possible
to get a similarly useful metric with a simpler approach. Alternatively we may
find that nobody is using these metrics, so we can remove them.
+#### Use `preload` instead of `includes`
+
+The `includes` and `preload` methods in Rails are both ways to avoid an N+1
+query. The `includes` method in Rails uses a heuristic approach to determine
+if it needs to join to the table, or if it can load all of the
+records in a separate query. This method assumes it needs to join if it thinks
+you need to query the columns from the other table, but sometimes
+this method gets it wrong and executes a join even when not needed. In
+this case using `preload` to explicitly load the data in a separate query
+allows you to avoid the join, while still avoiding the N+1 query.
+
+You can see a real example of this solution being used in
+<https://gitlab.com/gitlab-org/gitlab/-/merge_requests/67655>.
+
#### De-normalize some foreign key to the table
De-normalization refers to adding redundant precomputed (duplicated) data to
@@ -243,3 +257,37 @@ A quick checklist for fixing a specific join query would be:
adding a new column
1. Can we remove the join by adding a new table in the correct database that
replicates the minimum data needed to do the join
+
+#### How to validate you have correctly removed a cross-join
+
+Using RSpec tests, you can validate all SQL queries within a code block to
+ensure that none of them are joining across the two databases. This is a useful
+tool to confirm you have correctly fixed an existing cross-join.
+
+At some point in the future we will have fixed all cross-joins and this tool
+will run by default in all tests. For now, the tool needs to be explicitly enabled
+for your test.
+
+You can use this method like so:
+
+```ruby
+it 'does not join across databases' do
+ with_cross_joins_prevented do
+ ::Ci::Build.joins(:project).to_a
+ end
+end
+```
+
+This will raise an exception if the query joins across the two databases. The
+previous example is fixed by removing the join, like so:
+
+```ruby
+it 'does not join across databases' do
+ with_cross_joins_prevented do
+ ::Ci::Build.preload(:project).to_a
+ end
+end
+```
+
+You can see a real example of using this method for fixing a cross-join in
+<https://gitlab.com/gitlab-org/gitlab/-/merge_requests/67655>.
diff --git a/lib/gitlab/background_migration/recalculate_vulnerabilities_occurrences_uuid.rb b/lib/gitlab/background_migration/recalculate_vulnerabilities_occurrences_uuid.rb
index a00d291245c..84ff7423254 100644
--- a/lib/gitlab/background_migration/recalculate_vulnerabilities_occurrences_uuid.rb
+++ b/lib/gitlab/background_migration/recalculate_vulnerabilities_occurrences_uuid.rb
@@ -9,6 +9,8 @@ class Gitlab::BackgroundMigration::RecalculateVulnerabilitiesOccurrencesUuid
end
class VulnerabilitiesFinding < ActiveRecord::Base
+ include ShaAttribute
+
self.table_name = "vulnerability_occurrences"
belongs_to :primary_identifier, class_name: 'VulnerabilitiesIdentifier', inverse_of: :primary_findings, foreign_key: 'primary_identifier_id'
REPORT_TYPES = {
@@ -21,6 +23,9 @@ class Gitlab::BackgroundMigration::RecalculateVulnerabilitiesOccurrencesUuid
api_fuzzing: 6
}.with_indifferent_access.freeze
enum report_type: REPORT_TYPES
+
+ sha_attribute :fingerprint
+ sha_attribute :location_fingerprint
end
class CalculateFindingUUID
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index ecfdd166f66..0296f743e37 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -8209,9 +8209,6 @@ msgstr ""
msgid "Company"
msgstr ""
-msgid "Company name"
-msgstr ""
-
msgid "Compare"
msgstr ""
@@ -9289,9 +9286,6 @@ msgstr ""
msgid "Couldn't assign policy to project"
msgstr ""
-msgid "Country"
-msgstr ""
-
msgid "Coverage"
msgstr ""
@@ -16370,9 +16364,6 @@ msgstr ""
msgid "Hi %{username}!"
msgstr ""
-msgid "Hi%{salutation}, your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information about %{company} to activate your trial."
-msgstr ""
-
msgid "Hide"
msgstr ""
@@ -16509,9 +16500,6 @@ msgstr ""
msgid "How many seconds an IP will be counted towards the limit"
msgstr ""
-msgid "How many users will be evaluating the trial?"
-msgstr ""
-
msgid "I accept the %{terms_link}"
msgstr ""
@@ -22918,9 +22906,6 @@ msgstr ""
msgid "Number of commits per MR"
msgstr ""
-msgid "Number of employees"
-msgstr ""
-
msgid "Number of events"
msgstr ""
@@ -24189,6 +24174,12 @@ msgstr ""
msgid "Pipeline %{label} for \"%{dataTitle}\""
msgstr ""
+msgid "Pipeline ID"
+msgstr ""
+
+msgid "Pipeline IID"
+msgstr ""
+
msgid "Pipeline Schedule"
msgstr ""
@@ -30450,6 +30441,12 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{boldStart}will%{boldEnd} lose access to your account."
msgstr ""
+msgid "Show Pipeline ID"
+msgstr ""
+
+msgid "Show Pipeline IID"
+msgstr ""
+
msgid "Show all activity"
msgstr ""
@@ -32449,9 +32446,6 @@ msgstr ""
msgid "TeamcityIntegration|Trigger TeamCity CI after every push to the repository, except branch delete"
msgstr ""
-msgid "Telephone number"
-msgstr ""
-
msgid "Tell us your experiences with the new Markdown editor %{linkStart}in this feedback issue%{linkEnd}."
msgstr ""
@@ -35011,6 +35005,9 @@ msgstr ""
msgid "Trial|Company name"
msgstr ""
+msgid "Trial|Continue"
+msgstr ""
+
msgid "Trial|Continue using the basic features of GitLab for free."
msgstr ""
@@ -35020,15 +35017,30 @@ msgstr ""
msgid "Trial|Dismiss"
msgstr ""
+msgid "Trial|First name"
+msgstr ""
+
msgid "Trial|GitLab Ultimate trial (optional)"
msgstr ""
+msgid "Trial|Hi%{salutation}, your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information about %{company} to activate your trial."
+msgstr ""
+
msgid "Trial|How many employees will use Gitlab?"
msgstr ""
+msgid "Trial|How many users will be evaluating the trial?"
+msgstr ""
+
+msgid "Trial|Last name"
+msgstr ""
+
msgid "Trial|Number of employees"
msgstr ""
+msgid "Trial|Please select a country"
+msgstr ""
+
msgid "Trial|Successful trial activation image"
msgstr ""
@@ -35041,6 +35053,12 @@ msgstr ""
msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
msgstr ""
+msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
+msgstr ""
+
+msgid "Trial|your company"
+msgstr ""
+
msgid "Trigger"
msgstr ""
@@ -38233,9 +38251,6 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
-msgid "Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
-msgstr ""
-
msgid "Your GitLab account has been locked due to an excessive amount of unsuccessful sign in attempts. Your account will automatically unlock in %{duration} or you may click the link below to unlock now."
msgstr ""
@@ -40180,8 +40195,5 @@ msgstr ""
msgid "yaml invalid"
msgstr ""
-msgid "your company"
-msgstr ""
-
msgid "your settings"
msgstr ""
diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb
index 92df35f9d10..8afb80d9cc5 100644
--- a/spec/controllers/projects_controller_spec.rb
+++ b/spec/controllers/projects_controller_spec.rb
@@ -42,32 +42,6 @@ RSpec.describe ProjectsController do
expect(response).not_to render_template('new')
end
end
-
- context 'when user is an external user' do
- let_it_be(:user) { create(:user, external: true) }
-
- it 'responds with status 404' do
- group.add_owner(user)
-
- get :new, params: { namespace_id: group.id }
-
- expect(response).to have_gitlab_http_status(:not_found)
- expect(response).not_to render_template('new')
- end
- end
-
- context 'when user is a group guest' do
- let_it_be(:user) { create(:user) }
-
- it 'responds with status 404' do
- group.add_guest(user)
-
- get :new, params: { namespace_id: group.id }
-
- expect(response).to have_gitlab_http_status(:not_found)
- expect(response).not_to render_template('new')
- end
- end
end
end
end
diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb
index 1de0eea4657..b531d677870 100644
--- a/spec/features/projects/pipelines/pipelines_spec.rb
+++ b/spec/features/projects/pipelines/pipelines_spec.rb
@@ -585,6 +585,26 @@ RSpec.describe 'Pipelines', :js do
expect(page).to have_selector('.gl-pagination .page-link', count: 4)
end
end
+
+ context 'with pipeline key selection' do
+ before do
+ visit project_pipelines_path(project)
+ wait_for_requests
+ end
+
+ it 'changes the Pipeline ID column for Pipeline IID' do
+ page.find('[data-testid="pipeline-key-dropdown"]').click
+
+ within '.gl-new-dropdown-contents' do
+ dropdown_options = page.find_all '.gl-new-dropdown-item'
+
+ dropdown_options[1].click
+ end
+
+ expect(page.find('[data-testid="pipeline-th"]')).to have_content 'Pipeline IID'
+ expect(page.find('[data-testid="pipeline-url-link"]')).to have_content "##{pipeline.iid}"
+ end
+ end
end
describe 'GET /:project/-/pipelines/show' do
diff --git a/spec/frontend/pipelines/pipeline_url_spec.js b/spec/frontend/pipelines/pipeline_url_spec.js
index 367c7f2b2f6..912b5afe0e1 100644
--- a/spec/frontend/pipelines/pipeline_url_spec.js
+++ b/spec/frontend/pipelines/pipeline_url_spec.js
@@ -28,6 +28,7 @@ describe('Pipeline Url Component', () => {
flags: {},
},
pipelineScheduleUrl: 'foo',
+ pipelineKey: 'id',
};
const createComponent = (props) => {
diff --git a/spec/frontend/pipelines/pipelines_spec.js b/spec/frontend/pipelines/pipelines_spec.js
index dd2cb1e8643..aa30062c987 100644
--- a/spec/frontend/pipelines/pipelines_spec.js
+++ b/spec/frontend/pipelines/pipelines_spec.js
@@ -74,6 +74,7 @@ describe('Pipelines', () => {
const findTablePagination = () => wrapper.findComponent(TablePagination);
const findTab = (tab) => wrapper.findByTestId(`pipelines-tab-${tab}`);
+ const findPipelineKeyDropdown = () => wrapper.findByTestId('pipeline-key-dropdown');
const findRunPipelineButton = () => wrapper.findByTestId('run-pipeline-button');
const findCiLintButton = () => wrapper.findByTestId('ci-lint-button');
const findCleanCacheButton = () => wrapper.findByTestId('clear-cache-button');
@@ -528,6 +529,10 @@ describe('Pipelines', () => {
expect(findFilteredSearch().exists()).toBe(true);
});
+ it('renders the pipeline key dropdown', () => {
+ expect(findPipelineKeyDropdown().exists()).toBe(true);
+ });
+
it('renders tab empty state finished scope', async () => {
mock.onGet(mockPipelinesEndpoint, { params: { scope: 'finished', page: '1' } }).reply(200, {
pipelines: [],
@@ -623,6 +628,10 @@ describe('Pipelines', () => {
expect(findFilteredSearch().exists()).toBe(false);
});
+ it('does not render the pipeline key dropdown', () => {
+ expect(findPipelineKeyDropdown().exists()).toBe(false);
+ });
+
it('does not render tabs nor buttons', () => {
expect(findNavigationTabs().exists()).toBe(false);
expect(findTab('all').exists()).toBe(false);
diff --git a/spec/frontend/pipelines/pipelines_table_spec.js b/spec/frontend/pipelines/pipelines_table_spec.js
index 68b0dfc018e..4472a5ae70d 100644
--- a/spec/frontend/pipelines/pipelines_table_spec.js
+++ b/spec/frontend/pipelines/pipelines_table_spec.js
@@ -8,6 +8,7 @@ import PipelineTriggerer from '~/pipelines/components/pipelines_list/pipeline_tr
import PipelineUrl from '~/pipelines/components/pipelines_list/pipeline_url.vue';
import PipelinesTable from '~/pipelines/components/pipelines_list/pipelines_table.vue';
import PipelinesTimeago from '~/pipelines/components/pipelines_list/time_ago.vue';
+import { PipelineKeyOptions } from '~/pipelines/constants';
import eventHub from '~/pipelines/event_hub';
import CiBadge from '~/vue_shared/components/ci_badge_link.vue';
@@ -24,6 +25,7 @@ describe('Pipelines Table', () => {
const defaultProps = {
pipelines: [],
viewType: 'root',
+ pipelineKeyOption: PipelineKeyOptions[0],
};
const createMockPipeline = () => {
@@ -80,7 +82,7 @@ describe('Pipelines Table', () => {
it('should render table head with correct columns', () => {
expect(findStatusTh().text()).toBe('Status');
- expect(findPipelineTh().text()).toBe('Pipeline');
+ expect(findPipelineTh().text()).toBe('Pipeline ID');
expect(findTriggererTh().text()).toBe('Triggerer');
expect(findCommitTh().text()).toBe('Commit');
expect(findStagesTh().text()).toBe('Stages');
diff --git a/spec/lib/gitlab/background_migration/recalculate_vulnerabilities_occurrences_uuid_spec.rb b/spec/lib/gitlab/background_migration/recalculate_vulnerabilities_occurrences_uuid_spec.rb
index 70906961641..30908145782 100644
--- a/spec/lib/gitlab/background_migration/recalculate_vulnerabilities_occurrences_uuid_spec.rb
+++ b/spec/lib/gitlab/background_migration/recalculate_vulnerabilities_occurrences_uuid_spec.rb
@@ -13,12 +13,13 @@ RSpec.describe Gitlab::BackgroundMigration::RecalculateVulnerabilitiesOccurrence
let(:vulnerabilities) { table(:vulnerabilities) }
let(:vulnerabilities_findings) { table(:vulnerability_occurrences) }
let(:vulnerability_identifiers) { table(:vulnerability_identifiers) }
+
let(:vulnerability_identifier) do
vulnerability_identifiers.create!(
project_id: project.id,
external_type: 'uuid-v5',
external_id: 'uuid-v5',
- fingerprint: '7e394d1b1eb461a7406d7b1e08f057a1cf11287a',
+ fingerprint: Gitlab::Database::ShaAttribute.serialize('7e394d1b1eb461a7406d7b1e08f057a1cf11287a'),
name: 'Identifier for UUIDv5')
end
@@ -27,7 +28,7 @@ RSpec.describe Gitlab::BackgroundMigration::RecalculateVulnerabilitiesOccurrence
project_id: project.id,
external_type: 'uuid-v4',
external_id: 'uuid-v4',
- fingerprint: '772da93d34a1ba010bcb5efa9fb6f8e01bafcc89',
+ fingerprint: Gitlab::Database::ShaAttribute.serialize('772da93d34a1ba010bcb5efa9fb6f8e01bafcc89'),
name: 'Identifier for UUIDv4')
end
@@ -59,7 +60,7 @@ RSpec.describe Gitlab::BackgroundMigration::RecalculateVulnerabilitiesOccurrence
scanner_id: different_scanner.id,
primary_identifier_id: different_vulnerability_identifier.id,
report_type: 0, # "sast"
- location_fingerprint: "fa18f432f1d56675f4098d318739c3cd5b14eb3e",
+ location_fingerprint: Gitlab::Database::ShaAttribute.serialize("fa18f432f1d56675f4098d318739c3cd5b14eb3e"),
uuid: known_uuid_v4
)
end
@@ -91,7 +92,7 @@ RSpec.describe Gitlab::BackgroundMigration::RecalculateVulnerabilitiesOccurrence
scanner_id: scanner.id,
primary_identifier_id: vulnerability_identifier.id,
report_type: 0, # "sast"
- location_fingerprint: "838574be0210968bf6b9f569df9c2576242cbf0a",
+ location_fingerprint: Gitlab::Database::ShaAttribute.serialize("838574be0210968bf6b9f569df9c2576242cbf0a"),
uuid: known_uuid_v5
)
end
@@ -115,7 +116,7 @@ RSpec.describe Gitlab::BackgroundMigration::RecalculateVulnerabilitiesOccurrence
scanner_id: different_scanner.id,
primary_identifier_id: different_vulnerability_identifier.id,
report_type: 0, # "sast"
- location_fingerprint: "fa18f432f1d56675f4098d318739c3cd5b14eb3e",
+ location_fingerprint: Gitlab::Database::ShaAttribute.serialize("fa18f432f1d56675f4098d318739c3cd5b14eb3e"),
uuid: known_uuid_v4
)
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index ab2b6950e19..3e16de44cea 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -5188,6 +5188,14 @@ RSpec.describe Ci::Build do
end
end
+ describe '.with_project_and_metadata' do
+ it 'does not join across databases' do
+ with_cross_joins_prevented do
+ ::Ci::Build.with_project_and_metadata.to_a
+ end
+ end
+ end
+
describe '.without_coverage' do
let!(:build_with_coverage) { create(:ci_build, pipeline: pipeline, coverage: 100.0) }
diff --git a/spec/support/database/prevent_cross_joins.rb b/spec/support/database/prevent_cross_joins.rb
index 9fb697da722..c49b165c6c3 100644
--- a/spec/support/database/prevent_cross_joins.rb
+++ b/spec/support/database/prevent_cross_joins.rb
@@ -56,6 +56,20 @@ module Database
end
end
+ module SpecHelpers
+ def with_cross_joins_prevented
+ subscriber = ActiveSupport::Notifications.subscribe('sql.active_record') do |event|
+ ::Database::PreventCrossJoins.validate_cross_joins!(event.payload[:sql])
+ end
+
+ Thread.current[:allow_cross_joins_across_databases] = false
+
+ yield
+ ensure
+ ActiveSupport::Notifications.unsubscribe(subscriber) if subscriber
+ end
+ end
+
module GitlabDatabaseMixin
def allow_cross_joins_across_databases(url:)
Thread.current[:allow_cross_joins_across_databases] = true
@@ -69,16 +83,10 @@ Gitlab::Database.singleton_class.prepend(
Database::PreventCrossJoins::GitlabDatabaseMixin)
RSpec.configure do |config|
+ config.include(::Database::PreventCrossJoins::SpecHelpers)
+
# TODO: remove `:prevent_cross_joins` to enable the check by default
config.around(:each, :prevent_cross_joins) do |example|
- subscriber = ActiveSupport::Notifications.subscribe('sql.active_record') do |event|
- ::Database::PreventCrossJoins.validate_cross_joins!(event.payload[:sql])
- end
-
- Thread.current[:allow_cross_joins_across_databases] = false
-
- example.run
- ensure
- ActiveSupport::Notifications.unsubscribe(subscriber) if subscriber
+ with_cross_joins_prevented { example.run }
end
end