Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
22622fab4a
commit
b563a5209a
|
@ -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>
|
|
@ -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"
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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}');
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1 @@
|
|||
52625ff0a6117724cc1d7c6417ef95fe8dbcbb394486bb4734e28d3b41d23fd2
|
|
@ -0,0 +1 @@
|
|||
7724e5a2c52be99b1b40c449f25abdc23f279f5b0bdaebcfd897c39d295fda41
|
|
@ -0,0 +1 @@
|
|||
dab6123f19fb44a1566a8de9c760dedec5548dd64e472a180e7748cd7c93eea9
|
|
@ -0,0 +1 @@
|
|||
f5e69502e582c5f30ba686f8b668d8f0ce5cf8078b0833d2eda67f5ed97ac074
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
- Due date
|
||||
- Labels
|
||||
- [Weight](issues/issue_weight.md)
|
||||
- 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
|
||||
- Due date
|
||||
- [Epic](../group/epics/index.md)
|
||||
- [Iteration](../group/iterations/index.md)
|
||||
- Labels
|
||||
- Milestone
|
||||
- Notifications setting
|
||||
- Title
|
||||
- [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)
|
||||
```
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
`;
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -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();
|
||||
});
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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|
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue