Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-11-23 12:10:20 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-11-23 12:10:20 +0300
commitb563a5209a13da8da32688a5d503a7c0e2bc3ec3 (patch)
tree67114fae70174dff02f5c95089fca0d0e0b7d4d4
parent22622fab4a943df0988c86d175f28bb004ff8663 (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/assets/javascripts/ide/components/pipelines/empty_state.vue35
-rw-r--r--app/assets/javascripts/ide/components/pipelines/list.vue28
-rw-r--r--app/helpers/auth_helper.rb23
-rw-r--r--app/views/layouts/_google_tag_manager_body.html.haml2
-rw-r--r--app/views/layouts/_google_tag_manager_head.html.haml15
-rw-r--r--config/feature_flags/ops/gtm_nonce.yml8
-rw-r--r--db/post_migrate/20211104165220_remove_vulnerability_finding_links.rb21
-rw-r--r--db/post_migrate/20211110143306_add_not_null_constraint_to_security_findings_uuid.rb20
-rw-r--r--db/post_migrate/20211110151320_add_temporary_index_on_security_findings_uuid.rb23
-rw-r--r--db/post_migrate/20211110151350_schedule_drop_invalid_security_findings.rb25
-rw-r--r--db/schema_migrations/202111041652201
-rw-r--r--db/schema_migrations/202111101433061
-rw-r--r--db/schema_migrations/202111101513201
-rw-r--r--db/schema_migrations/202111101513501
-rw-r--r--db/structure.sql5
-rw-r--r--doc/user/project/issue_board.md100
-rw-r--r--lib/gitlab/background_migration/drop_invalid_security_findings.rb47
-rw-r--r--lib/gitlab/background_migration/remove_vulnerability_finding_links.rb17
-rw-r--r--lib/gitlab/content_security_policy/directives.rb2
-rw-r--r--spec/frontend/ide/components/pipelines/__snapshots__/list_spec.js.snap10
-rw-r--r--spec/frontend/ide/components/pipelines/empty_state_spec.js44
-rw-r--r--spec/frontend/ide/components/pipelines/list_spec.js8
-rw-r--r--spec/helpers/auth_helper_spec.rb73
-rw-r--r--spec/lib/gitlab/background_migration/drop_invalid_security_findings_spec.rb56
-rw-r--r--spec/lib/gitlab/background_migration/remove_vulnerability_finding_links_spec.rb66
-rw-r--r--spec/lib/gitlab/database/load_balancing/load_balancer_spec.rb18
-rw-r--r--spec/lib/gitlab/database/query_analyzers/gitlab_schemas_metrics_spec.rb2
-rw-r--r--spec/migrations/20211110143306_add_not_null_constraint_to_security_findings_uuid_spec.rb23
-rw-r--r--spec/migrations/20211110151350_schedule_drop_invalid_security_findings_spec.rb71
-rw-r--r--spec/support/database/multiple_databases.rb37
-rw-r--r--spec/support_specs/database/multiple_databases_spec.rb39
31 files changed, 670 insertions, 152 deletions
diff --git a/app/assets/javascripts/ide/components/pipelines/empty_state.vue b/app/assets/javascripts/ide/components/pipelines/empty_state.vue
new file mode 100644
index 00000000000..194deb2ece0
--- /dev/null
+++ b/app/assets/javascripts/ide/components/pipelines/empty_state.vue
@@ -0,0 +1,35 @@
+<script>
+import { GlEmptyState } from '@gitlab/ui';
+import { mapState } from 'vuex';
+import { s__ } from '~/locale';
+import { helpPagePath } from '~/helpers/help_page_helper';
+
+export default {
+ components: {
+ GlEmptyState,
+ },
+ computed: {
+ ...mapState(['pipelinesEmptyStateSvgPath']),
+ ciHelpPagePath() {
+ return helpPagePath('ci/quick_start/index.md');
+ },
+ },
+ i18n: {
+ title: s__('Pipelines|Build with confidence'),
+ description: s__(`Pipelines|GitLab CI/CD can automatically build,
+ test, and deploy your code. Let GitLab take care of time
+ consuming tasks, so you can spend more time creating.`),
+ primaryButtonText: s__('Pipelines|Get started with GitLab CI/CD'),
+ },
+};
+</script>
+
+<template>
+ <gl-empty-state
+ :title="$options.i18n.title"
+ :svg-path="pipelinesEmptyStateSvgPath"
+ :description="$options.i18n.description"
+ :primary-button-text="$options.i18n.primaryButtonText"
+ :primary-button-link="ciHelpPagePath"
+ />
+</template>
diff --git a/app/assets/javascripts/ide/components/pipelines/list.vue b/app/assets/javascripts/ide/components/pipelines/list.vue
index e1caf1ba44a..7f513afe82e 100644
--- a/app/assets/javascripts/ide/components/pipelines/list.vue
+++ b/app/assets/javascripts/ide/components/pipelines/list.vue
@@ -11,10 +11,17 @@ import {
import { escape } from 'lodash';
import { mapActions, mapGetters, mapState } from 'vuex';
import IDEServices from '~/ide/services';
-import { sprintf, __ } from '../../../locale';
-import EmptyState from '../../../pipelines/components/pipelines_list/empty_state.vue';
-import CiIcon from '../../../vue_shared/components/ci_icon.vue';
+import { sprintf, __ } from '~/locale';
+import CiIcon from '~/vue_shared/components/ci_icon.vue';
import JobsList from '../jobs/list.vue';
+import EmptyState from './empty_state.vue';
+
+const CLASSES_FLEX_VERTICAL_CENTER = [
+ 'gl-h-full',
+ 'gl-display-flex',
+ 'gl-flex-direction-column',
+ 'gl-justify-content-center',
+];
export default {
components: {
@@ -32,7 +39,6 @@ export default {
SafeHtml,
},
computed: {
- ...mapState(['pipelinesEmptyStateSvgPath']),
...mapGetters(['currentProject']),
...mapGetters('pipelines', ['jobsCount', 'failedJobsCount', 'failedStages', 'pipelineFailed']),
...mapState('pipelines', [
@@ -63,12 +69,15 @@ export default {
methods: {
...mapActions('pipelines', ['fetchLatestPipeline']),
},
+ CLASSES_FLEX_VERTICAL_CENTER,
};
</script>
<template>
<div class="ide-pipeline">
- <gl-loading-icon v-if="showLoadingIcon" size="lg" class="gl-mt-3" />
+ <div v-if="showLoadingIcon" :class="$options.CLASSES_FLEX_VERTICAL_CENTER">
+ <gl-loading-icon size="lg" />
+ </div>
<template v-else-if="hasLoadedPipeline">
<header v-if="latestPipeline" class="ide-tree-header ide-pipeline-header">
<ci-icon :status="latestPipeline.details.status" :size="24" class="d-flex" />
@@ -83,12 +92,9 @@ export default {
</a>
</span>
</header>
- <empty-state
- v-if="!latestPipeline"
- :empty-state-svg-path="pipelinesEmptyStateSvgPath"
- :can-set-ci="true"
- class="gl-p-5"
- />
+ <div v-if="!latestPipeline" :class="$options.CLASSES_FLEX_VERTICAL_CENTER">
+ <empty-state />
+ </div>
<gl-alert
v-else-if="latestPipeline.yamlError"
variant="danger"
diff --git a/app/helpers/auth_helper.rb b/app/helpers/auth_helper.rb
index 6fe92a5a978..2032b7e8bb7 100644
--- a/app/helpers/auth_helper.rb
+++ b/app/helpers/auth_helper.rb
@@ -164,10 +164,25 @@ module AuthHelper
end
def google_tag_manager_enabled?
- Gitlab.com? &&
- extra_config.has_key?('google_tag_manager_id') &&
- extra_config.google_tag_manager_id.present? &&
- !current_user
+ return false unless Gitlab.dev_env_or_com?
+
+ has_config_key = if Feature.enabled?(:gtm_nonce, type: :ops)
+ extra_config.has_key?('google_tag_manager_nonce_id') &&
+ extra_config.google_tag_manager_nonce_id.present?
+ else
+ extra_config.has_key?('google_tag_manager_id') &&
+ extra_config.google_tag_manager_id.present?
+ end
+
+ has_config_key && !current_user
+ end
+
+ def google_tag_manager_id
+ return unless google_tag_manager_enabled?
+
+ return extra_config.google_tag_manager_nonce_id if Feature.enabled?(:gtm_nonce, type: :ops)
+
+ extra_config.google_tag_manager_id
end
def auth_app_owner_text(owner)
diff --git a/app/views/layouts/_google_tag_manager_body.html.haml b/app/views/layouts/_google_tag_manager_body.html.haml
index d62e52dc91b..98d7bf5d138 100644
--- a/app/views/layouts/_google_tag_manager_body.html.haml
+++ b/app/views/layouts/_google_tag_manager_body.html.haml
@@ -1,4 +1,4 @@
- return unless google_tag_manager_enabled?
-<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=#{extra_config.google_tag_manager_id}"
+<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=#{google_tag_manager_id}"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
diff --git a/app/views/layouts/_google_tag_manager_head.html.haml b/app/views/layouts/_google_tag_manager_head.html.haml
index 48eb9e40cc4..d0700f8bc8e 100644
--- a/app/views/layouts/_google_tag_manager_head.html.haml
+++ b/app/views/layouts/_google_tag_manager_head.html.haml
@@ -1,8 +1,19 @@
-- if google_tag_manager_enabled?
+- return unless google_tag_manager_enabled?
+
+- if Feature.enabled?(:gtm_nonce, type: :ops)
+ = javascript_tag do
+ :plain
+ (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
+ new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
+ j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
+ 'https://www.googletagmanager.com/gtm.js?id='+i+dl;var n=d.querySelector('[nonce]');
+ n&&j.setAttribute('nonce',n.nonce||n.getAttribute('nonce'));f.parentNode.insertBefore(j,f);
+ })(window,document,'script','dataLayer','#{google_tag_manager_id}');
+- else
= javascript_tag do
:plain
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
- })(window,document,'script','dataLayer','#{extra_config.google_tag_manager_id}');
+ })(window,document,'script','dataLayer','#{google_tag_manager_id}');
diff --git a/config/feature_flags/ops/gtm_nonce.yml b/config/feature_flags/ops/gtm_nonce.yml
new file mode 100644
index 00000000000..b4007732aa2
--- /dev/null
+++ b/config/feature_flags/ops/gtm_nonce.yml
@@ -0,0 +1,8 @@
+---
+name: gtm_nonce
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/58494
+rollout_issue_url:
+milestone: '14.6'
+type: ops
+group: group::product intelligence
+default_enabled: false
diff --git a/db/post_migrate/20211104165220_remove_vulnerability_finding_links.rb b/db/post_migrate/20211104165220_remove_vulnerability_finding_links.rb
new file mode 100644
index 00000000000..fc50aa812a7
--- /dev/null
+++ b/db/post_migrate/20211104165220_remove_vulnerability_finding_links.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+class RemoveVulnerabilityFindingLinks < Gitlab::Database::Migration[1.0]
+ BATCH_SIZE = 50_000
+ MIGRATION = 'RemoveVulnerabilityFindingLinks'
+
+ disable_ddl_transaction!
+
+ def up
+ queue_background_migration_jobs_by_range_at_intervals(
+ define_batchable_model('vulnerability_finding_links'),
+ MIGRATION,
+ 2.minutes,
+ batch_size: BATCH_SIZE
+ )
+ end
+
+ def down
+ # no ops
+ end
+end
diff --git a/db/post_migrate/20211110143306_add_not_null_constraint_to_security_findings_uuid.rb b/db/post_migrate/20211110143306_add_not_null_constraint_to_security_findings_uuid.rb
new file mode 100644
index 00000000000..bdb8f5cd120
--- /dev/null
+++ b/db/post_migrate/20211110143306_add_not_null_constraint_to_security_findings_uuid.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+class AddNotNullConstraintToSecurityFindingsUuid < Gitlab::Database::Migration[1.0]
+ disable_ddl_transaction!
+
+ def up
+ add_not_null_constraint(
+ :security_findings,
+ :uuid,
+ validate: false
+ )
+ end
+
+ def down
+ remove_not_null_constraint(
+ :security_findings,
+ :uuid
+ )
+ end
+end
diff --git a/db/post_migrate/20211110151320_add_temporary_index_on_security_findings_uuid.rb b/db/post_migrate/20211110151320_add_temporary_index_on_security_findings_uuid.rb
new file mode 100644
index 00000000000..7bc4af0ec4d
--- /dev/null
+++ b/db/post_migrate/20211110151320_add_temporary_index_on_security_findings_uuid.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+class AddTemporaryIndexOnSecurityFindingsUuid < Gitlab::Database::Migration[1.0]
+ disable_ddl_transaction!
+
+ INDEX_NAME = "tmp_index_uuid_is_null"
+
+ def up
+ add_concurrent_index(
+ :security_findings,
+ :id,
+ where: "uuid IS NULL",
+ name: INDEX_NAME
+ )
+ end
+
+ def down
+ remove_concurrent_index_by_name(
+ :security_findings,
+ INDEX_NAME
+ )
+ end
+end
diff --git a/db/post_migrate/20211110151350_schedule_drop_invalid_security_findings.rb b/db/post_migrate/20211110151350_schedule_drop_invalid_security_findings.rb
new file mode 100644
index 00000000000..98e7b2a8a15
--- /dev/null
+++ b/db/post_migrate/20211110151350_schedule_drop_invalid_security_findings.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+class ScheduleDropInvalidSecurityFindings < Gitlab::Database::Migration[1.0]
+ disable_ddl_transaction!
+
+ MIGRATION = "DropInvalidSecurityFindings"
+ DELAY_INTERVAL = 2.minutes.to_i
+ BATCH_SIZE = 100_000
+ SUB_BATCH_SIZE = 10_000
+
+ def up
+ queue_background_migration_jobs_by_range_at_intervals(
+ define_batchable_model('security_findings').where(uuid: nil),
+ MIGRATION,
+ DELAY_INTERVAL,
+ batch_size: BATCH_SIZE,
+ other_job_arguments: [SUB_BATCH_SIZE],
+ track_jobs: true
+ )
+ end
+
+ def down
+ # no-op
+ end
+end
diff --git a/db/schema_migrations/20211104165220 b/db/schema_migrations/20211104165220
new file mode 100644
index 00000000000..abfa37a45a4
--- /dev/null
+++ b/db/schema_migrations/20211104165220
@@ -0,0 +1 @@
+52625ff0a6117724cc1d7c6417ef95fe8dbcbb394486bb4734e28d3b41d23fd2 \ No newline at end of file
diff --git a/db/schema_migrations/20211110143306 b/db/schema_migrations/20211110143306
new file mode 100644
index 00000000000..e1618c07f75
--- /dev/null
+++ b/db/schema_migrations/20211110143306
@@ -0,0 +1 @@
+7724e5a2c52be99b1b40c449f25abdc23f279f5b0bdaebcfd897c39d295fda41 \ No newline at end of file
diff --git a/db/schema_migrations/20211110151320 b/db/schema_migrations/20211110151320
new file mode 100644
index 00000000000..91f780811c3
--- /dev/null
+++ b/db/schema_migrations/20211110151320
@@ -0,0 +1 @@
+dab6123f19fb44a1566a8de9c760dedec5548dd64e472a180e7748cd7c93eea9 \ No newline at end of file
diff --git a/db/schema_migrations/20211110151350 b/db/schema_migrations/20211110151350
new file mode 100644
index 00000000000..98d590c26e9
--- /dev/null
+++ b/db/schema_migrations/20211110151350
@@ -0,0 +1 @@
+f5e69502e582c5f30ba686f8b668d8f0ce5cf8078b0833d2eda67f5ed97ac074 \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index b708e48a111..caa1f603df3 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -22613,6 +22613,9 @@ ALTER TABLE ONLY chat_teams
ALTER TABLE vulnerability_scanners
ADD CONSTRAINT check_37608c9db5 CHECK ((char_length(vendor) <= 255)) NOT VALID;
+ALTER TABLE security_findings
+ ADD CONSTRAINT check_6c2851a8c9 CHECK ((uuid IS NOT NULL)) NOT VALID;
+
ALTER TABLE sprints
ADD CONSTRAINT check_ccd8a1eae0 CHECK ((start_date IS NOT NULL)) NOT VALID;
@@ -27722,6 +27725,8 @@ CREATE UNIQUE INDEX tmp_index_on_tmp_project_id_on_namespaces ON namespaces USIN
CREATE INDEX tmp_index_on_vulnerabilities_non_dismissed ON vulnerabilities USING btree (id) WHERE (state <> 2);
+CREATE INDEX tmp_index_uuid_is_null ON security_findings USING btree (id) WHERE (uuid IS NULL);
+
CREATE UNIQUE INDEX uniq_pkgs_deb_grp_architectures_on_distribution_id_and_name ON packages_debian_group_architectures USING btree (distribution_id, name);
CREATE UNIQUE INDEX uniq_pkgs_deb_grp_components_on_distribution_id_and_name ON packages_debian_group_components USING btree (distribution_id, name);
diff --git a/doc/user/project/issue_board.md b/doc/user/project/issue_board.md
index 4c35f007fc7..316fd0dce16 100644
--- a/doc/user/project/issue_board.md
+++ b/doc/user/project/issue_board.md
@@ -219,27 +219,6 @@ This ordering also affects [issue lists](issues/sorting_issue_lists.md).
Changing the order in an issue board changes the ordering in an issue list,
and vice versa.
-### GraphQL-based issue boards
-
-<!-- This anchor is linked from #blocked-issues as well. -->
-
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/285074) in GitLab 13.9.
-> - [Deployed behind a feature flag](../feature_flags.md), enabled by default.
-> - [Enabled by default](https://gitlab.com/gitlab-org/gitlab/-/issues/248908) in GitLab 14.1
-> - [Feature flag `graphql_board_lists`](https://gitlab.com/gitlab-org/gitlab/-/issues/248908) removed in GitLab 14.3
-
-There can be
-[risks when disabling released features](../../administration/feature_flags.md#risks-when-disabling-released-features).
-Refer to this feature's version history for more details.
-
-Using GraphQL-based boards gives you these
-additional features:
-
-- [Edit more issue attributes](#edit-an-issue)
-- [View blocked issues](#blocked-issues)
-
-Learn more about the known issues in [epic 5596](https://gitlab.com/groups/gitlab-org/-/epics/5596).
-
## GitLab Enterprise features for issue boards
GitLab issue boards are available on the GitLab Free tier, but some
@@ -334,10 +313,7 @@ As in other list types, click the trash icon to remove a list.
### Iteration lists **(PREMIUM)**
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/250479) in GitLab 13.11.
-> - Enabled on GitLab.com and is ready for production use.
-> - Enabled with `iteration_board_lists` flag for self-managed GitLab and is ready for production use.
-> GitLab administrators can opt to [disable the feature flag](#enable-or-disable-iteration-lists-in-boards).
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/250479) in GitLab 13.11 [with a flag](../../administration/feature_flags.md) named `iteration_board_lists`. Enabled by default.
FLAG:
On self-managed GitLab, by default this feature is available. To hide the feature, ask an
@@ -345,8 +321,9 @@ administrator to [disable the `iteration_board_lists` flag](../../administration
On GitLab.com, this feature is available.
You're also able to create lists of an iteration.
-These are lists that filter issues by the assigned
-iteration. To add an iteration list:
+These lists filter issues by the assigned iteration.
+
+To add an iteration list:
1. Select **Create list**.
1. Select **Iteration**.
@@ -434,8 +411,6 @@ status.
When you hover over the blocked icon (**{issue-block}**), a detailed information popover is displayed.
-This feature is only supported when using the [GraphQL-based boards](#graphql-based-issue-boards). The feature is enabled by default regardless when you use group issue boards in epic swimlanes mode.
-
![Blocked issues](img/issue_boards_blocked_icon_v13_10.png)
## Actions you can take on an issue board
@@ -457,25 +432,25 @@ If you're not able to do some of the things above, make sure you have the right
### Edit an issue
+> Editing title, iteration, and confidentiality [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/248908) in GitLab 14.1.
+
You can edit an issue without leaving the board view.
To open the right sidebar, select an issue card (not its title).
You can edit the following issue attributes in the right sidebar:
- Assignees
-- [Epic](../group/epics/index.md)
-- Milestone
-- Time tracking value (view only)
+- Confidentiality
- Due date
+- [Epic](../group/epics/index.md)
+- [Iteration](../group/iterations/index.md)
- Labels
-- [Weight](issues/issue_weight.md)
+- Milestone
- Notifications setting
-
-When you use [GraphQL-based boards](#graphql-based-issue-boards), you can also edit the following issue attributes:
-
- Title
-- [Iteration](../group/iterations/index.md)
-- Confidentiality
+- [Weight](issues/issue_weight.md)
+
+Additionally, you can also see the time tracking value.
### Create a new list
@@ -620,13 +595,12 @@ and the target list.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/18954) in GitLab 12.4.
> - [Placed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/61955) behind a [feature flag](../feature_flags.md), disabled by default in GitLab 14.0.
-> - Disabled on GitLab.com.
-> - Not recommended for production use.
-> - To use in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-multi-selecting-issue-cards). **(FREE SELF)**
-This in-development feature might not be available for your use. There can be
-[risks when enabling features still in development](../../administration/feature_flags.md#risks-when-enabling-features-still-in-development).
-Refer to this feature's version history for more details.
+FLAG:
+On self-managed GitLab, by default this feature is not available. To make it available, ask an
+administrator to [enable the feature flag](../../administration/feature_flags.md) named `board_multi_select`.
+On GitLab.com, this feature is not available.
+The feature is not ready for production use.
You can select multiple issue cards, then drag the group to another position within the list, or to
another list. This makes it faster to reorder many issues at once.
@@ -668,41 +642,3 @@ A few things to remember:
- For performance and visibility reasons, each list shows the first 20 issues
by default. If you have more than 20 issues, start scrolling down and the next
20 appear.
-
-### Enable or disable iteration lists in boards **(PREMIUM SELF)**
-
-The iteration list is under development but ready for production use. 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 disable it.
-
-To enable it:
-
-```ruby
-Feature.enable(:iteration_board_lists)
-```
-
-To disable it:
-
-```ruby
-Feature.disable(:iteration_board_lists)
-```
-
-### Enable or disable multi-selecting issue cards **(FREE SELF)**
-
-Multi-selecting issue cards is under development and not ready for production use. It is
-deployed behind a feature flag that is **disabled by default**.
-[GitLab administrators with access to the GitLab Rails console](../../administration/feature_flags.md)
-can enable it.
-
-To enable it:
-
-```ruby
-Feature.enable(:board_multi_select)
-```
-
-To disable it:
-
-```ruby
-Feature.disable(:board_multi_select)
-```
diff --git a/lib/gitlab/background_migration/drop_invalid_security_findings.rb b/lib/gitlab/background_migration/drop_invalid_security_findings.rb
new file mode 100644
index 00000000000..87551bb1b1e
--- /dev/null
+++ b/lib/gitlab/background_migration/drop_invalid_security_findings.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+module Gitlab
+ module BackgroundMigration
+ # Drop rows from security_findings where the uuid is NULL
+ class DropInvalidSecurityFindings
+ # rubocop:disable Style/Documentation
+ class SecurityFinding < ActiveRecord::Base
+ include ::EachBatch
+ self.table_name = 'security_findings'
+ scope :no_uuid, -> { where(uuid: nil) }
+ end
+ # rubocop:enable Style/Documentation
+
+ PAUSE_SECONDS = 0.1
+
+ def perform(start_id, end_id, sub_batch_size)
+ ranged_query = SecurityFinding
+ .where(id: start_id..end_id)
+ .no_uuid
+
+ ranged_query.each_batch(of: sub_batch_size) do |sub_batch|
+ first, last = sub_batch.pluck(Arel.sql('min(id), max(id)')).first
+
+ # The query need to be reconstructed because .each_batch modifies the default scope
+ # See: https://gitlab.com/gitlab-org/gitlab/-/issues/330510
+ SecurityFinding.unscoped
+ .where(id: first..last)
+ .no_uuid
+ .delete_all
+
+ sleep PAUSE_SECONDS
+ end
+
+ mark_job_as_succeeded(start_id, end_id, sub_batch_size)
+ end
+
+ private
+
+ def mark_job_as_succeeded(*arguments)
+ Gitlab::Database::BackgroundMigrationJob.mark_all_as_succeeded(
+ self.class.name.demodulize,
+ arguments
+ )
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/background_migration/remove_vulnerability_finding_links.rb b/lib/gitlab/background_migration/remove_vulnerability_finding_links.rb
new file mode 100644
index 00000000000..31fb5e97c5d
--- /dev/null
+++ b/lib/gitlab/background_migration/remove_vulnerability_finding_links.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module BackgroundMigration
+ # Remove vulnerability finding link records
+ # The records will be repopulated from the `raw_metadata`
+ # column of `vulnerability_occurrences` once the unique
+ # index is in place.
+ class RemoveVulnerabilityFindingLinks
+ include Gitlab::Database::DynamicModelHelpers
+
+ def perform(start_id, stop_id)
+ define_batchable_model('vulnerability_finding_links').where(id: start_id..stop_id).delete_all
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/content_security_policy/directives.rb b/lib/gitlab/content_security_policy/directives.rb
index 30f3c16247d..3b958f8c92e 100644
--- a/lib/gitlab/content_security_policy/directives.rb
+++ b/lib/gitlab/content_security_policy/directives.rb
@@ -8,7 +8,7 @@ module Gitlab
module ContentSecurityPolicy
module Directives
def self.frame_src
- "https://www.google.com/recaptcha/ https://www.recaptcha.net/ https://content.googleapis.com https://content-compute.googleapis.com https://content-cloudbilling.googleapis.com https://content-cloudresourcemanager.googleapis.com"
+ "https://www.google.com/recaptcha/ https://www.recaptcha.net/ https://content.googleapis.com https://content-compute.googleapis.com https://content-cloudbilling.googleapis.com https://content-cloudresourcemanager.googleapis.com https://www.googletagmanager.com/ns.html"
end
def self.script_src
diff --git a/spec/frontend/ide/components/pipelines/__snapshots__/list_spec.js.snap b/spec/frontend/ide/components/pipelines/__snapshots__/list_spec.js.snap
index 47e3a56e83d..069b6927bac 100644
--- a/spec/frontend/ide/components/pipelines/__snapshots__/list_spec.js.snap
+++ b/spec/frontend/ide/components/pipelines/__snapshots__/list_spec.js.snap
@@ -6,10 +6,10 @@ exports[`IDE pipelines list when loaded renders empty state when no latestPipeli
>
<!---->
- <empty-state-stub
- cansetci="true"
- class="gl-p-5"
- emptystatesvgpath="http://test.host"
- />
+ <div
+ class="gl-h-full gl-display-flex gl-flex-direction-column gl-justify-content-center"
+ >
+ <empty-state-stub />
+ </div>
</div>
`;
diff --git a/spec/frontend/ide/components/pipelines/empty_state_spec.js b/spec/frontend/ide/components/pipelines/empty_state_spec.js
new file mode 100644
index 00000000000..f7409fc36be
--- /dev/null
+++ b/spec/frontend/ide/components/pipelines/empty_state_spec.js
@@ -0,0 +1,44 @@
+import { GlEmptyState } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import EmptyState from '~/ide/components/pipelines/empty_state.vue';
+import { createStore } from '~/ide/stores';
+
+const TEST_PIPELINES_EMPTY_STATE_SVG_PATH = 'illustrations/test/pipelines.svg';
+
+describe('~/ide/components/pipelines/empty_state.vue', () => {
+ let store;
+ let wrapper;
+
+ const createComponent = () => {
+ wrapper = shallowMount(EmptyState, {
+ store,
+ });
+ };
+
+ beforeEach(() => {
+ store = createStore();
+ store.dispatch('setEmptyStateSvgs', {
+ pipelinesEmptyStateSvgPath: TEST_PIPELINES_EMPTY_STATE_SVG_PATH,
+ });
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('default', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('renders empty state', () => {
+ expect(wrapper.find(GlEmptyState).props()).toMatchObject({
+ title: EmptyState.i18n.title,
+ description: EmptyState.i18n.description,
+ primaryButtonText: EmptyState.i18n.primaryButtonText,
+ primaryButtonLink: '/help/ci/quick_start/index.md',
+ svgPath: TEST_PIPELINES_EMPTY_STATE_SVG_PATH,
+ });
+ });
+ });
+});
diff --git a/spec/frontend/ide/components/pipelines/list_spec.js b/spec/frontend/ide/components/pipelines/list_spec.js
index a917f4c0230..8a3606e27eb 100644
--- a/spec/frontend/ide/components/pipelines/list_spec.js
+++ b/spec/frontend/ide/components/pipelines/list_spec.js
@@ -2,10 +2,10 @@ import { GlLoadingIcon, GlTab } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import Vue from 'vue';
import Vuex from 'vuex';
-import { TEST_HOST } from 'helpers/test_constants';
import { pipelines } from 'jest/ide/mock_data';
import JobsList from '~/ide/components/jobs/list.vue';
import List from '~/ide/components/pipelines/list.vue';
+import EmptyState from '~/ide/components/pipelines/empty_state.vue';
import IDEServices from '~/ide/services';
import CiIcon from '~/vue_shared/components/ci_icon.vue';
@@ -18,9 +18,6 @@ jest.mock('~/ide/services', () => ({
describe('IDE pipelines list', () => {
let wrapper;
- const defaultState = {
- pipelinesEmptyStateSvgPath: TEST_HOST,
- };
const defaultPipelinesState = {
stages: [],
failedStages: [],
@@ -38,7 +35,6 @@ describe('IDE pipelines list', () => {
currentProject: () => ({ web_url: 'some/url ', path_with_namespace: fakeProjectPath }),
},
state: {
- ...defaultState,
...rootState,
},
modules: {
@@ -131,6 +127,8 @@ describe('IDE pipelines list', () => {
it('renders empty state when no latestPipeline', () => {
createComponent({}, { ...defaultPipelinesLoadedState, latestPipeline: null });
+
+ expect(wrapper.find(EmptyState).exists()).toBe(true);
expect(wrapper.element).toMatchSnapshot();
});
diff --git a/spec/helpers/auth_helper_spec.rb b/spec/helpers/auth_helper_spec.rb
index c1c961c5cbb..5a596526a7e 100644
--- a/spec/helpers/auth_helper_spec.rb
+++ b/spec/helpers/auth_helper_spec.rb
@@ -283,35 +283,84 @@ RSpec.describe AuthHelper do
before do
allow(Gitlab).to receive(:com?).and_return(is_gitlab_com)
- stub_config(extra: { google_tag_manager_id: 'key' })
allow(helper).to receive(:current_user).and_return(user)
end
- subject(:google_tag_manager_enabled?) { helper.google_tag_manager_enabled? }
-
- context 'on gitlab.com and a key set without a current user' do
- it { is_expected.to be_truthy }
- end
+ subject(:google_tag_manager_enabled) { helper.google_tag_manager_enabled? }
context 'when not on gitlab.com' do
let(:is_gitlab_com) { false }
- it { is_expected.to be_falsey }
+ it { is_expected.to eq(false) }
end
- context 'when current user is set' do
- let(:user) { instance_double('User') }
+ context 'regular and nonce versions' do
+ using RSpec::Parameterized::TableSyntax
- it { is_expected.to be_falsey }
+ where(:gtm_nonce_enabled, :gtm_key) do
+ false | 'google_tag_manager_id'
+ true | 'google_tag_manager_nonce_id'
+ end
+
+ with_them do
+ before do
+ stub_feature_flags(gtm_nonce: gtm_nonce_enabled)
+ stub_config(extra: { gtm_key => 'key' })
+ end
+
+ context 'on gitlab.com and a key set without a current user' do
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when current user is set' do
+ let(:user) { instance_double('User') }
+
+ it { is_expected.to eq(false) }
+ end
+
+ context 'when no key is set' do
+ before do
+ stub_config(extra: {})
+ end
+
+ it { is_expected.to eq(false) }
+ end
+ end
+ end
+ end
+
+ describe '#google_tag_manager_id' do
+ subject(:google_tag_manager_id) { helper.google_tag_manager_id }
+
+ before do
+ stub_config(extra: { 'google_tag_manager_nonce_id': 'nonce', 'google_tag_manager_id': 'gtm' })
end
- context 'when no key is set' do
+ context 'when google tag manager is disabled' do
before do
- stub_config(extra: {})
+ allow(helper).to receive(:google_tag_manager_enabled?).and_return(false)
end
it { is_expected.to be_falsey }
end
+
+ context 'when google tag manager is enabled' do
+ before do
+ allow(helper).to receive(:google_tag_manager_enabled?).and_return(true)
+ end
+
+ context 'when nonce feature flag is enabled' do
+ it { is_expected.to eq('nonce') }
+ end
+
+ context 'when nonce feature flag is disabled' do
+ before do
+ stub_feature_flags(gtm_nonce: false)
+ end
+
+ it { is_expected.to eq('gtm') }
+ end
+ end
end
describe '#auth_app_owner_text' do
diff --git a/spec/lib/gitlab/background_migration/drop_invalid_security_findings_spec.rb b/spec/lib/gitlab/background_migration/drop_invalid_security_findings_spec.rb
new file mode 100644
index 00000000000..95fdfd752f6
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/drop_invalid_security_findings_spec.rb
@@ -0,0 +1,56 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe Gitlab::BackgroundMigration::DropInvalidSecurityFindings, schema: 20211108211434 do
+ let(:namespace) { table(:namespaces).create!(name: 'user', path: 'user') }
+ let(:project) { table(:projects).create!(namespace_id: namespace.id) }
+
+ let(:pipelines) { table(:ci_pipelines) }
+ let!(:pipeline) { pipelines.create!(project_id: project.id) }
+
+ let(:ci_builds) { table(:ci_builds) }
+ let!(:ci_build) { ci_builds.create! }
+
+ let(:security_scans) { table(:security_scans) }
+ let!(:security_scan) do
+ security_scans.create!(
+ scan_type: 1,
+ status: 1,
+ build_id: ci_build.id,
+ project_id: project.id,
+ pipeline_id: pipeline.id
+ )
+ end
+
+ let(:vulnerability_scanners) { table(:vulnerability_scanners) }
+ let!(:vulnerability_scanner) { vulnerability_scanners.create!(project_id: project.id, external_id: 'test 1', name: 'test scanner 1') }
+
+ let(:security_findings) { table(:security_findings) }
+ let!(:security_finding_without_uuid) do
+ security_findings.create!(
+ severity: 1,
+ confidence: 1,
+ scan_id: security_scan.id,
+ scanner_id: vulnerability_scanner.id,
+ uuid: nil
+ )
+ end
+
+ let!(:security_finding_with_uuid) do
+ security_findings.create!(
+ severity: 1,
+ confidence: 1,
+ scan_id: security_scan.id,
+ scanner_id: vulnerability_scanner.id,
+ uuid: 'bd95c085-71aa-51d7-9bb6-08ae669c262e'
+ )
+ end
+
+ let(:sub_batch_size) { 10_000 }
+
+ subject { described_class.new.perform(security_finding_without_uuid.id, security_finding_with_uuid.id, sub_batch_size) }
+
+ it 'drops Security::Finding objects with no UUID' do
+ expect { subject }.to change(security_findings, :count).from(2).to(1)
+ end
+end
diff --git a/spec/lib/gitlab/background_migration/remove_vulnerability_finding_links_spec.rb b/spec/lib/gitlab/background_migration/remove_vulnerability_finding_links_spec.rb
new file mode 100644
index 00000000000..68a2254b785
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/remove_vulnerability_finding_links_spec.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::BackgroundMigration::RemoveVulnerabilityFindingLinks, :migration, schema: 20211104165220 do
+ let(:vulnerability_findings) { table(:vulnerability_occurrences) }
+ let(:finding_links) { table(:vulnerability_finding_links) }
+
+ let(:namespace) { table(:namespaces).create!(name: 'user', path: 'user') }
+ let(:project) { table(:projects).create!(namespace_id: namespace.id) }
+ let(:scanner) { table(:vulnerability_scanners).create!(project_id: project.id, external_id: 'scanner', name: 'scanner') }
+ let(:vulnerability_identifier) do
+ table(:vulnerability_identifiers).create!(
+ project_id: project.id,
+ external_type: 'vulnerability-identifier',
+ external_id: 'vulnerability-identifier',
+ fingerprint: '7e394d1b1eb461a7406d7b1e08f057a1cf11287a',
+ name: 'vulnerability identifier')
+ end
+
+ # vulnerability findings
+ let!(:findings) do
+ Array.new(2) do |id|
+ vulnerability_findings.create!(
+ project_id: project.id,
+ name: 'Vulnerability Name',
+ severity: 7,
+ confidence: 7,
+ report_type: 0,
+ project_fingerprint: '123qweasdzxc',
+ scanner_id: scanner.id,
+ primary_identifier_id: vulnerability_identifier.id,
+ location_fingerprint: "location_fingerprint_#{id}",
+ metadata_version: 'metadata_version',
+ raw_metadata: 'raw_metadata',
+ uuid: "uuid_#{id}"
+ )
+ end
+ end
+
+ # vulnerability finding links
+ let!(:links) do
+ {
+ findings.first => Array.new(5) { |id| finding_links.create!(vulnerability_occurrence_id: findings.first.id, name: "Link Name 1", url: "link_url1.example") },
+ findings.second => Array.new(5) { |id| finding_links.create!(vulnerability_occurrence_id: findings.second.id, name: "Link Name 2", url: "link_url2.example") }
+ }
+ end
+
+ it 'removes vulnerability links' do
+ expect do
+ subject.perform(links[findings.first].first.id, links[findings.second].last.id)
+ end.to change { finding_links.count }.from(10).to(0)
+
+ expect(finding_links.all).to be_empty
+ end
+
+ it 'only deletes vulnerability links for the current batch' do
+ expected_links = [finding_links.where(vulnerability_occurrence_id: findings.second.id)].flatten
+
+ expect do
+ subject.perform(links[findings.first].first.id, links[findings.first].last.id)
+ end.to change { finding_links.count }.from(10).to(5)
+
+ expect(finding_links.all).to match_array(expected_links)
+ end
+end
diff --git a/spec/lib/gitlab/database/load_balancing/load_balancer_spec.rb b/spec/lib/gitlab/database/load_balancing/load_balancer_spec.rb
index 37b83729125..3c7819c04b6 100644
--- a/spec/lib/gitlab/database/load_balancing/load_balancer_spec.rb
+++ b/spec/lib/gitlab/database/load_balancing/load_balancer_spec.rb
@@ -487,25 +487,9 @@ RSpec.describe Gitlab::Database::LoadBalancing::LoadBalancer, :request_store do
end
end
- describe 'primary connection re-use', :reestablished_active_record_base do
+ describe 'primary connection re-use', :reestablished_active_record_base, :add_ci_connection do
let(:model) { Ci::ApplicationRecord }
- around do |example|
- if Gitlab::Database.has_config?(:ci)
- example.run
- else
- # fake additional Database
- model.establish_connection(
- ActiveRecord::DatabaseConfigurations::HashConfig.new(Rails.env, 'ci', ActiveRecord::Base.connection_db_config.configuration_hash)
- )
-
- example.run
-
- # Cleanup connection_specification_name for Ci::ApplicationRecord
- model.remove_connection
- end
- end
-
describe '#read' do
it 'returns ci replica connection' do
expect { |b| lb.read(&b) }.to yield_with_args do |args|
diff --git a/spec/lib/gitlab/database/query_analyzers/gitlab_schemas_metrics_spec.rb b/spec/lib/gitlab/database/query_analyzers/gitlab_schemas_metrics_spec.rb
index ab5f05e3ec4..86e74cf5177 100644
--- a/spec/lib/gitlab/database/query_analyzers/gitlab_schemas_metrics_spec.rb
+++ b/spec/lib/gitlab/database/query_analyzers/gitlab_schemas_metrics_spec.rb
@@ -17,7 +17,7 @@ RSpec.describe Gitlab::Database::QueryAnalyzers::GitlabSchemasMetrics, query_ana
process_sql(ActiveRecord::Base, "SELECT 1 FROM projects")
end
- context 'properly observes all queries', :mocked_ci_connection do
+ context 'properly observes all queries', :add_ci_connection do
using RSpec::Parameterized::TableSyntax
where do
diff --git a/spec/migrations/20211110143306_add_not_null_constraint_to_security_findings_uuid_spec.rb b/spec/migrations/20211110143306_add_not_null_constraint_to_security_findings_uuid_spec.rb
new file mode 100644
index 00000000000..946fbf7f568
--- /dev/null
+++ b/spec/migrations/20211110143306_add_not_null_constraint_to_security_findings_uuid_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+require 'spec_helper'
+require_migration!
+
+RSpec.describe AddNotNullConstraintToSecurityFindingsUuid do
+ let_it_be(:security_findings) { table(:security_findings) }
+ let_it_be(:migration) { described_class.new }
+
+ before do
+ allow(migration).to receive(:transaction_open?).and_return(false)
+ allow(migration).to receive(:with_lock_retries).and_yield
+ end
+
+ it 'adds a check constraint' do
+ constraint = security_findings.connection.check_constraints(:security_findings).find { |constraint| constraint.expression == "uuid IS NOT NULL" }
+ expect(constraint).to be_nil
+
+ migration.up
+
+ constraint = security_findings.connection.check_constraints(:security_findings).find { |constraint| constraint.expression == "uuid IS NOT NULL" }
+ expect(constraint).to be_a(ActiveRecord::ConnectionAdapters::CheckConstraintDefinition)
+ end
+end
diff --git a/spec/migrations/20211110151350_schedule_drop_invalid_security_findings_spec.rb b/spec/migrations/20211110151350_schedule_drop_invalid_security_findings_spec.rb
new file mode 100644
index 00000000000..90debca13e1
--- /dev/null
+++ b/spec/migrations/20211110151350_schedule_drop_invalid_security_findings_spec.rb
@@ -0,0 +1,71 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe ScheduleDropInvalidSecurityFindings, :migration, schema: 20211108211434 do
+ let_it_be(:background_migration_jobs) { table(:background_migration_jobs) }
+
+ let_it_be(:namespace) { table(:namespaces).create!(name: 'user', path: 'user') }
+ let_it_be(:project) { table(:projects).create!(namespace_id: namespace.id) }
+
+ let_it_be(:pipelines) { table(:ci_pipelines) }
+ let_it_be(:pipeline) { pipelines.create!(project_id: project.id) }
+
+ let_it_be(:ci_builds) { table(:ci_builds) }
+ let_it_be(:ci_build) { ci_builds.create! }
+
+ let_it_be(:security_scans) { table(:security_scans) }
+ let_it_be(:security_scan) do
+ security_scans.create!(
+ scan_type: 1,
+ status: 1,
+ build_id: ci_build.id,
+ project_id: project.id,
+ pipeline_id: pipeline.id
+ )
+ end
+
+ let_it_be(:vulnerability_scanners) { table(:vulnerability_scanners) }
+ let_it_be(:vulnerability_scanner) { vulnerability_scanners.create!(project_id: project.id, external_id: 'test 1', name: 'test scanner 1') }
+
+ let_it_be(:security_findings) { table(:security_findings) }
+ let_it_be(:security_finding_without_uuid) do
+ security_findings.create!(
+ severity: 1,
+ confidence: 1,
+ scan_id: security_scan.id,
+ scanner_id: vulnerability_scanner.id,
+ uuid: nil
+ )
+ end
+
+ let_it_be(:security_finding_with_uuid) do
+ security_findings.create!(
+ severity: 1,
+ confidence: 1,
+ scan_id: security_scan.id,
+ scanner_id: vulnerability_scanner.id,
+ uuid: 'bd95c085-71aa-51d7-9bb6-08ae669c262e'
+ )
+ end
+
+ before do
+ stub_const("#{described_class}::BATCH_SIZE", 1)
+ stub_const("#{described_class}::SUB_BATCH_SIZE", 1)
+ end
+
+ around do |example|
+ freeze_time { Sidekiq::Testing.fake! { example.run } }
+ end
+
+ it 'schedules background migrations' do
+ migrate!
+
+ expect(background_migration_jobs.count).to eq(1)
+ expect(background_migration_jobs.first.arguments).to match_array([security_finding_without_uuid.id, security_finding_without_uuid.id, described_class::SUB_BATCH_SIZE])
+
+ expect(BackgroundMigrationWorker.jobs.size).to eq(1)
+ expect(described_class::MIGRATION).to be_scheduled_delayed_migration(2.minutes, security_finding_without_uuid.id, security_finding_without_uuid.id, described_class::SUB_BATCH_SIZE)
+ end
+end
diff --git a/spec/support/database/multiple_databases.rb b/spec/support/database/multiple_databases.rb
index 9e72ea589e3..94857b47127 100644
--- a/spec/support/database/multiple_databases.rb
+++ b/spec/support/database/multiple_databases.rb
@@ -6,6 +6,10 @@ module Database
skip 'Skipping because multiple databases not set up' unless Gitlab::Database.has_config?(:ci)
end
+ def skip_if_multiple_databases_are_setup
+ skip 'Skipping because multiple databases are set up' if Gitlab::Database.has_config?(:ci)
+ end
+
def reconfigure_db_connection(name: nil, config_hash: {}, model: ActiveRecord::Base, config_model: nil)
db_config = (config_model || model).connection_db_config
@@ -46,6 +50,26 @@ module Database
new_handler&.clear_all_connections!
end
# rubocop:enable Database/MultipleDatabases
+
+ def with_added_ci_connection
+ if Gitlab::Database.has_config?(:ci)
+ # No need to add a ci: connection if we already have one
+ yield
+ else
+ with_reestablished_active_record_base(reconnect: true) do
+ reconfigure_db_connection(
+ name: :ci,
+ model: Ci::ApplicationRecord,
+ config_model: ActiveRecord::Base
+ )
+
+ yield
+
+ # Cleanup connection_specification_name for Ci::ApplicationRecord
+ Ci::ApplicationRecord.remove_connection
+ end
+ end
+ end
end
module ActiveRecordBaseEstablishConnection
@@ -69,18 +93,9 @@ RSpec.configure do |config|
end
end
- config.around(:each, :mocked_ci_connection) do |example|
- with_reestablished_active_record_base(reconnect: true) do
- reconfigure_db_connection(
- name: :ci,
- model: Ci::ApplicationRecord,
- config_model: ActiveRecord::Base
- )
-
+ config.around(:each, :add_ci_connection) do |example|
+ with_added_ci_connection do
example.run
-
- # Cleanup connection_specification_name for Ci::ApplicationRecord
- Ci::ApplicationRecord.remove_connection
end
end
end
diff --git a/spec/support_specs/database/multiple_databases_spec.rb b/spec/support_specs/database/multiple_databases_spec.rb
index 10d1a8277c6..a8692e315fe 100644
--- a/spec/support_specs/database/multiple_databases_spec.rb
+++ b/spec/support_specs/database/multiple_databases_spec.rb
@@ -56,4 +56,43 @@ RSpec.describe 'Database::MultipleDatabases' do
end
end
end
+
+ describe '.with_added_ci_connection' do
+ context 'when only a single database is setup' do
+ before do
+ skip_if_multiple_databases_are_setup
+ end
+
+ it 'connects Ci::ApplicationRecord to the main database for the duration of the block', :aggregate_failures do
+ main_database = current_database(ActiveRecord::Base)
+ original_database = current_database(Ci::ApplicationRecord)
+
+ with_added_ci_connection do
+ expect(current_database(Ci::ApplicationRecord)).to eq(main_database)
+ end
+
+ expect(current_database(Ci::ApplicationRecord)).to eq(original_database)
+ end
+ end
+
+ context 'when multiple databases are setup' do
+ before do
+ skip_if_multiple_databases_not_setup
+ end
+
+ it 'does not mock the original Ci::ApplicationRecord connection', :aggregate_failures do
+ original_database = current_database(Ci::ApplicationRecord)
+
+ with_added_ci_connection do
+ expect(current_database(Ci::ApplicationRecord)).to eq(original_database)
+ end
+
+ expect(current_database(Ci::ApplicationRecord)).to eq(original_database)
+ end
+ end
+
+ def current_database(connection_class)
+ connection_class.retrieve_connection.execute('select current_database()').first
+ end
+ end
end