Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
7ea5ca0bb5
commit
ae0889b396
|
@ -1,6 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import $ from 'jquery';
|
import $ from 'jquery';
|
||||||
import 'select2';
|
import 'select2';
|
||||||
|
import { loadCSSFile } from '~/lib/utils/css_utils';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
// False positive i18n lint: https://gitlab.com/gitlab-org/frontend/eslint-plugin-i18n/issues/26
|
// False positive i18n lint: https://gitlab.com/gitlab-org/frontend/eslint-plugin-i18n/issues/26
|
||||||
|
@ -20,10 +21,14 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
$(this.$refs.dropdownInput)
|
loadCSSFile(gon.select2_css_path)
|
||||||
.val(this.value)
|
.then(() => {
|
||||||
.select2(this.options)
|
$(this.$refs.dropdownInput)
|
||||||
.on('change', event => this.$emit('input', event.target.value));
|
.val(this.value)
|
||||||
|
.select2(this.options)
|
||||||
|
.on('change', event => this.$emit('input', event.target.value));
|
||||||
|
})
|
||||||
|
.catch(() => {});
|
||||||
},
|
},
|
||||||
|
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Releases
|
||||||
|
class EvidencePipelineFinder
|
||||||
|
include Gitlab::Utils::StrongMemoize
|
||||||
|
|
||||||
|
attr_reader :project, :params
|
||||||
|
|
||||||
|
def initialize(project, params = {})
|
||||||
|
@project = project
|
||||||
|
@params = params
|
||||||
|
end
|
||||||
|
|
||||||
|
def execute
|
||||||
|
# TODO: remove this with the release creation moved to it's own form https://gitlab.com/gitlab-org/gitlab/-/issues/214245
|
||||||
|
return params[:evidence_pipeline] if params[:evidence_pipeline]
|
||||||
|
|
||||||
|
sha = existing_tag&.dereferenced_target&.sha
|
||||||
|
sha ||= repository&.commit(ref)&.sha
|
||||||
|
|
||||||
|
return unless sha
|
||||||
|
|
||||||
|
project.ci_pipelines.for_sha(sha).last
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def repository
|
||||||
|
strong_memoize(:repository) do
|
||||||
|
project.repository
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def existing_tag
|
||||||
|
repository.find_tag(tag_name)
|
||||||
|
end
|
||||||
|
|
||||||
|
def tag_name
|
||||||
|
params[:tag]
|
||||||
|
end
|
||||||
|
|
||||||
|
def ref
|
||||||
|
params[:ref]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -29,6 +29,8 @@ class Release < ApplicationRecord
|
||||||
scope :preloaded, -> { includes(:evidences, :milestones, project: [:project_feature, :route, { namespace: :route }]) }
|
scope :preloaded, -> { includes(:evidences, :milestones, project: [:project_feature, :route, { namespace: :route }]) }
|
||||||
scope :with_project_and_namespace, -> { includes(project: :namespace) }
|
scope :with_project_and_namespace, -> { includes(project: :namespace) }
|
||||||
scope :recent, -> { sorted.limit(MAX_NUMBER_TO_DISPLAY) }
|
scope :recent, -> { sorted.limit(MAX_NUMBER_TO_DISPLAY) }
|
||||||
|
scope :without_evidence, -> { left_joins(:evidences).where(::Releases::Evidence.arel_table[:id].eq(nil)) }
|
||||||
|
scope :released_within_2hrs, -> { where(released_at: Time.zone.now - 1.hour..Time.zone.now + 1.hour) }
|
||||||
|
|
||||||
# Sorting
|
# Sorting
|
||||||
scope :order_created, -> { reorder('created_at ASC') }
|
scope :order_created, -> { reorder('created_at ASC') }
|
||||||
|
|
|
@ -11,8 +11,6 @@ module Releases
|
||||||
@project, @current_user, @params = project, user, params.dup
|
@project, @current_user, @params = project, user, params.dup
|
||||||
end
|
end
|
||||||
|
|
||||||
delegate :repository, to: :project
|
|
||||||
|
|
||||||
def tag_name
|
def tag_name
|
||||||
params[:tag]
|
params[:tag]
|
||||||
end
|
end
|
||||||
|
@ -39,22 +37,18 @@ module Releases
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def existing_tag
|
|
||||||
strong_memoize(:existing_tag) do
|
|
||||||
repository.find_tag(tag_name)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def tag_exist?
|
|
||||||
existing_tag.present?
|
|
||||||
end
|
|
||||||
|
|
||||||
def repository
|
def repository
|
||||||
strong_memoize(:repository) do
|
strong_memoize(:repository) do
|
||||||
project.repository
|
project.repository
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def existing_tag
|
||||||
|
strong_memoize(:existing_tag) do
|
||||||
|
repository.find_tag(tag_name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def milestones
|
def milestones
|
||||||
return [] unless param_for_milestone_titles_provided?
|
return [] unless param_for_milestone_titles_provided?
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ module Releases
|
||||||
# should be found before the creation of new tag
|
# should be found before the creation of new tag
|
||||||
# because tag creation can spawn new pipeline
|
# because tag creation can spawn new pipeline
|
||||||
# which won't have any data for evidence yet
|
# which won't have any data for evidence yet
|
||||||
evidence_pipeline = find_evidence_pipeline
|
evidence_pipeline = Releases::EvidencePipelineFinder.new(project, params).execute
|
||||||
|
|
||||||
tag = ensure_tag
|
tag = ensure_tag
|
||||||
|
|
||||||
|
@ -78,26 +78,10 @@ module Releases
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_evidence_pipeline
|
|
||||||
# TODO: remove this with the release creation moved to it's own form https://gitlab.com/gitlab-org/gitlab/-/issues/214245
|
|
||||||
return params[:evidence_pipeline] if params[:evidence_pipeline]
|
|
||||||
|
|
||||||
sha = existing_tag&.dereferenced_target&.sha
|
|
||||||
sha ||= repository.commit(ref)&.sha
|
|
||||||
|
|
||||||
return unless sha
|
|
||||||
|
|
||||||
project.ci_pipelines.for_sha(sha).last
|
|
||||||
end
|
|
||||||
|
|
||||||
def create_evidence!(release, pipeline)
|
def create_evidence!(release, pipeline)
|
||||||
return if release.historical_release?
|
return if release.historical_release? || release.upcoming_release?
|
||||||
|
|
||||||
if release.upcoming_release?
|
::Releases::CreateEvidenceWorker.perform_async(release.id, pipeline&.id)
|
||||||
CreateEvidenceWorker.perform_at(release.released_at, release.id, pipeline&.id)
|
|
||||||
else
|
|
||||||
CreateEvidenceWorker.perform_async(release.id, pipeline&.id)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -18,28 +18,28 @@
|
||||||
|
|
||||||
.gl-mt-3
|
.gl-mt-3
|
||||||
= form.check_box :repository_update_events, class: 'float-left'
|
= form.check_box :repository_update_events, class: 'float-left'
|
||||||
.prepend-left-20
|
.gl-ml-6
|
||||||
= form.label :repository_update_events, class: 'list-label' do
|
= form.label :repository_update_events, class: 'list-label' do
|
||||||
%strong Repository update events
|
%strong Repository update events
|
||||||
%p.light
|
%p.light
|
||||||
This URL will be triggered when repository is updated
|
This URL will be triggered when repository is updated
|
||||||
%li
|
%li
|
||||||
= form.check_box :push_events, class: 'float-left'
|
= form.check_box :push_events, class: 'float-left'
|
||||||
.prepend-left-20
|
.gl-ml-6
|
||||||
= form.label :push_events, class: 'list-label' do
|
= form.label :push_events, class: 'list-label' do
|
||||||
%strong Push events
|
%strong Push events
|
||||||
%p.light
|
%p.light
|
||||||
This URL will be triggered for each branch updated to the repository
|
This URL will be triggered for each branch updated to the repository
|
||||||
%li
|
%li
|
||||||
= form.check_box :tag_push_events, class: 'float-left'
|
= form.check_box :tag_push_events, class: 'float-left'
|
||||||
.prepend-left-20
|
.gl-ml-6
|
||||||
= form.label :tag_push_events, class: 'list-label' do
|
= form.label :tag_push_events, class: 'list-label' do
|
||||||
%strong Tag push events
|
%strong Tag push events
|
||||||
%p.light
|
%p.light
|
||||||
This URL will be triggered when a new tag is pushed to the repository
|
This URL will be triggered when a new tag is pushed to the repository
|
||||||
%li
|
%li
|
||||||
= form.check_box :merge_requests_events, class: 'float-left'
|
= form.check_box :merge_requests_events, class: 'float-left'
|
||||||
.prepend-left-20
|
.gl-ml-6
|
||||||
= form.label :merge_requests_events, class: 'list-label' do
|
= form.label :merge_requests_events, class: 'list-label' do
|
||||||
%strong Merge request events
|
%strong Merge request events
|
||||||
%p.light
|
%p.light
|
||||||
|
|
|
@ -323,6 +323,22 @@
|
||||||
:weight: 1
|
:weight: 1
|
||||||
:idempotent:
|
:idempotent:
|
||||||
:tags: []
|
:tags: []
|
||||||
|
- :name: cronjob:releases_create_evidence
|
||||||
|
:feature_category: :release_evidence
|
||||||
|
:has_external_dependencies:
|
||||||
|
:urgency: :low
|
||||||
|
:resource_boundary: :unknown
|
||||||
|
:weight: 1
|
||||||
|
:idempotent:
|
||||||
|
:tags: []
|
||||||
|
- :name: cronjob:releases_manage_evidence
|
||||||
|
:feature_category: :release_evidence
|
||||||
|
:has_external_dependencies:
|
||||||
|
:urgency: :low
|
||||||
|
:resource_boundary: :unknown
|
||||||
|
:weight: 1
|
||||||
|
:idempotent:
|
||||||
|
:tags: []
|
||||||
- :name: cronjob:remove_expired_group_links
|
- :name: cronjob:remove_expired_group_links
|
||||||
:feature_category: :authentication_and_authorization
|
:feature_category: :authentication_and_authorization
|
||||||
:has_external_dependencies:
|
:has_external_dependencies:
|
||||||
|
@ -1377,14 +1393,6 @@
|
||||||
:weight: 2
|
:weight: 2
|
||||||
:idempotent: true
|
:idempotent: true
|
||||||
:tags: []
|
:tags: []
|
||||||
- :name: create_evidence
|
|
||||||
:feature_category: :release_evidence
|
|
||||||
:has_external_dependencies:
|
|
||||||
:urgency: :low
|
|
||||||
:resource_boundary: :unknown
|
|
||||||
:weight: 2
|
|
||||||
:idempotent:
|
|
||||||
:tags: []
|
|
||||||
- :name: create_note_diff_file
|
- :name: create_note_diff_file
|
||||||
:feature_category: :source_code_management
|
:feature_category: :source_code_management
|
||||||
:has_external_dependencies:
|
:has_external_dependencies:
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class CreateEvidenceWorker # rubocop:disable Scalability/IdempotentWorker
|
|
||||||
include ApplicationWorker
|
|
||||||
|
|
||||||
feature_category :release_evidence
|
|
||||||
weight 2
|
|
||||||
|
|
||||||
# pipeline_id is optional for backward compatibility with existing jobs
|
|
||||||
# caller should always try to provide the pipeline and pass nil only
|
|
||||||
# if pipeline is absent
|
|
||||||
def perform(release_id, pipeline_id = nil)
|
|
||||||
release = Release.find_by_id(release_id)
|
|
||||||
return unless release
|
|
||||||
|
|
||||||
pipeline = Ci::Pipeline.find_by_id(pipeline_id)
|
|
||||||
|
|
||||||
::Releases::CreateEvidenceService.new(release, pipeline: pipeline).execute
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Releases
|
||||||
|
class CreateEvidenceWorker # rubocop:disable Scalability/IdempotentWorker
|
||||||
|
include ApplicationWorker
|
||||||
|
include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
|
||||||
|
|
||||||
|
feature_category :release_evidence
|
||||||
|
|
||||||
|
# pipeline_id is optional for backward compatibility with existing jobs
|
||||||
|
# caller should always try to provide the pipeline and pass nil only
|
||||||
|
# if pipeline is absent
|
||||||
|
def perform(release_id, pipeline_id = nil)
|
||||||
|
release = Release.find_by_id(release_id)
|
||||||
|
|
||||||
|
return unless release
|
||||||
|
|
||||||
|
pipeline = Ci::Pipeline.find_by_id(pipeline_id)
|
||||||
|
|
||||||
|
::Releases::CreateEvidenceService.new(release, pipeline: pipeline).execute
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,24 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Releases
|
||||||
|
class ManageEvidenceWorker # rubocop:disable Scalability/IdempotentWorker
|
||||||
|
include ApplicationWorker
|
||||||
|
include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
|
||||||
|
|
||||||
|
feature_category :release_evidence
|
||||||
|
|
||||||
|
def perform
|
||||||
|
releases = Release.without_evidence.released_within_2hrs
|
||||||
|
|
||||||
|
releases.each do |release|
|
||||||
|
project = release.project
|
||||||
|
params = { tag: release.tag }
|
||||||
|
|
||||||
|
evidence_pipeline = Releases::EvidencePipelineFinder.new(project, params).execute
|
||||||
|
|
||||||
|
# perform_at released_at
|
||||||
|
::Releases::CreateEvidenceWorker.perform_async(release.id, evidence_pipeline&.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -2,10 +2,6 @@
|
||||||
|
|
||||||
class TrendingProjectsWorker # rubocop:disable Scalability/IdempotentWorker
|
class TrendingProjectsWorker # rubocop:disable Scalability/IdempotentWorker
|
||||||
include ApplicationWorker
|
include ApplicationWorker
|
||||||
# rubocop:disable Scalability/CronWorkerContext
|
|
||||||
# This worker does not perform work scoped to a context
|
|
||||||
include CronjobQueue
|
|
||||||
# rubocop:enable Scalability/CronWorkerContext
|
|
||||||
include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
|
include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
|
||||||
|
|
||||||
feature_category :source_code_management
|
feature_category :source_code_management
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
title: Create `vulnerability_findings_remediations` and `vulnerability_remediations`
|
||||||
|
tables
|
||||||
|
merge_request: 47166
|
||||||
|
author:
|
||||||
|
type: added
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Add cloud_license_enabled column to application_settings
|
||||||
|
merge_request: 47882
|
||||||
|
author:
|
||||||
|
type: added
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Schedule CreateEvidenceWorker jobs in a sliding window
|
||||||
|
merge_request: 47638
|
||||||
|
author:
|
||||||
|
type: added
|
|
@ -532,6 +532,9 @@ Settings.cron_jobs['member_invitation_reminder_emails_worker']['job_class'] = 'M
|
||||||
Settings.cron_jobs['schedule_merge_request_cleanup_refs_worker'] ||= Settingslogic.new({})
|
Settings.cron_jobs['schedule_merge_request_cleanup_refs_worker'] ||= Settingslogic.new({})
|
||||||
Settings.cron_jobs['schedule_merge_request_cleanup_refs_worker']['cron'] ||= '* * * * *'
|
Settings.cron_jobs['schedule_merge_request_cleanup_refs_worker']['cron'] ||= '* * * * *'
|
||||||
Settings.cron_jobs['schedule_merge_request_cleanup_refs_worker']['job_class'] = 'ScheduleMergeRequestCleanupRefsWorker'
|
Settings.cron_jobs['schedule_merge_request_cleanup_refs_worker']['job_class'] = 'ScheduleMergeRequestCleanupRefsWorker'
|
||||||
|
Settings.cron_jobs['manage_evidence_worker'] ||= Settingslogic.new({})
|
||||||
|
Settings.cron_jobs['manage_evidence_worker']['cron'] ||= '0 * * * *'
|
||||||
|
Settings.cron_jobs['manage_evidence_worker']['job_class'] = 'Releases::ManageEvidenceWorker'
|
||||||
|
|
||||||
Gitlab.ee do
|
Gitlab.ee do
|
||||||
Settings.cron_jobs['active_user_count_threshold_worker'] ||= Settingslogic.new({})
|
Settings.cron_jobs['active_user_count_threshold_worker'] ||= Settingslogic.new({})
|
||||||
|
|
|
@ -58,8 +58,6 @@
|
||||||
- 1
|
- 1
|
||||||
- - create_commit_signature
|
- - create_commit_signature
|
||||||
- 2
|
- 2
|
||||||
- - create_evidence
|
|
||||||
- 2
|
|
||||||
- - create_github_webhook
|
- - create_github_webhook
|
||||||
- 2
|
- 2
|
||||||
- - create_note_diff_file
|
- - create_note_diff_file
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class AddIndexToReleases < ActiveRecord::Migration[6.0]
|
||||||
|
include Gitlab::Database::MigrationHelpers
|
||||||
|
|
||||||
|
DOWNTIME = false
|
||||||
|
INDEX_NAME = 'index_releases_on_released_at'
|
||||||
|
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
|
def up
|
||||||
|
add_concurrent_index :releases, :released_at, name: INDEX_NAME
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
remove_concurrent_index_by_name :releases, INDEX_NAME
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,26 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class CreateVulnerabilityRemediationsTable < ActiveRecord::Migration[6.0]
|
||||||
|
include Gitlab::Database::MigrationHelpers
|
||||||
|
|
||||||
|
DOWNTIME = false
|
||||||
|
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
|
def up
|
||||||
|
create_table :vulnerability_remediations, if_not_exists: true do |t|
|
||||||
|
t.timestamps_with_timezone
|
||||||
|
|
||||||
|
t.integer :file_store, limit: 2
|
||||||
|
t.text :summary, null: false
|
||||||
|
t.text :file, null: false
|
||||||
|
end
|
||||||
|
|
||||||
|
add_text_limit :vulnerability_remediations, :summary, 200
|
||||||
|
add_text_limit :vulnerability_remediations, :file, 255
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
drop_table :vulnerability_remediations
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,16 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class CreateVulnerabilityFindingsRemediationsJoinTable < ActiveRecord::Migration[6.0]
|
||||||
|
DOWNTIME = false
|
||||||
|
|
||||||
|
def change
|
||||||
|
create_table :vulnerability_findings_remediations do |t|
|
||||||
|
t.references :vulnerability_occurrence, index: false, foreign_key: { on_delete: :cascade }
|
||||||
|
t.references :vulnerability_remediation, index: { name: 'index_vulnerability_findings_remediations_on_remediation_id' }, foreign_key: { on_delete: :cascade }
|
||||||
|
|
||||||
|
t.timestamps_with_timezone
|
||||||
|
|
||||||
|
t.index [:vulnerability_occurrence_id, :vulnerability_remediation_id], unique: true, name: 'index_vulnerability_findings_remediations_on_unique_keys'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,9 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class AddCloudLicenseEnabledToSettings < ActiveRecord::Migration[6.0]
|
||||||
|
DOWNTIME = false
|
||||||
|
|
||||||
|
def change
|
||||||
|
add_column :application_settings, :cloud_license_enabled, :boolean, null: false, default: false
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1 @@
|
||||||
|
764f08e3083985bb8e206bd25fb27209702110bb4848c8bbfc6546a2777d9157
|
|
@ -0,0 +1 @@
|
||||||
|
27ee3c5429dba139e6c300961172c4f90d25397e3d1e13d0654e049b63ac3325
|
|
@ -0,0 +1 @@
|
||||||
|
bdbf3cf39228c9b65b02391a9aa030bdeb06aa3fc9955e2fd53bd784bea37b66
|
|
@ -0,0 +1 @@
|
||||||
|
119afd73a58c247522446bc9693ece5c83a25c279e4dd7dfb942f7febd5b7a82
|
|
@ -9345,6 +9345,7 @@ CREATE TABLE application_settings (
|
||||||
encrypted_cloud_license_auth_token text,
|
encrypted_cloud_license_auth_token text,
|
||||||
encrypted_cloud_license_auth_token_iv text,
|
encrypted_cloud_license_auth_token_iv text,
|
||||||
secret_detection_revocation_token_types_url text,
|
secret_detection_revocation_token_types_url text,
|
||||||
|
cloud_license_enabled boolean DEFAULT false NOT NULL,
|
||||||
CONSTRAINT app_settings_registry_exp_policies_worker_capacity_positive CHECK ((container_registry_expiration_policies_worker_capacity >= 0)),
|
CONSTRAINT app_settings_registry_exp_policies_worker_capacity_positive CHECK ((container_registry_expiration_policies_worker_capacity >= 0)),
|
||||||
CONSTRAINT check_2dba05b802 CHECK ((char_length(gitpod_url) <= 255)),
|
CONSTRAINT check_2dba05b802 CHECK ((char_length(gitpod_url) <= 255)),
|
||||||
CONSTRAINT check_51700b31b5 CHECK ((char_length(default_branch_name) <= 255)),
|
CONSTRAINT check_51700b31b5 CHECK ((char_length(default_branch_name) <= 255)),
|
||||||
|
@ -17190,6 +17191,23 @@ CREATE SEQUENCE vulnerability_finding_links_id_seq
|
||||||
|
|
||||||
ALTER SEQUENCE vulnerability_finding_links_id_seq OWNED BY vulnerability_finding_links.id;
|
ALTER SEQUENCE vulnerability_finding_links_id_seq OWNED BY vulnerability_finding_links.id;
|
||||||
|
|
||||||
|
CREATE TABLE vulnerability_findings_remediations (
|
||||||
|
id bigint NOT NULL,
|
||||||
|
vulnerability_occurrence_id bigint,
|
||||||
|
vulnerability_remediation_id bigint,
|
||||||
|
created_at timestamp with time zone NOT NULL,
|
||||||
|
updated_at timestamp with time zone NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE SEQUENCE vulnerability_findings_remediations_id_seq
|
||||||
|
START WITH 1
|
||||||
|
INCREMENT BY 1
|
||||||
|
NO MINVALUE
|
||||||
|
NO MAXVALUE
|
||||||
|
CACHE 1;
|
||||||
|
|
||||||
|
ALTER SEQUENCE vulnerability_findings_remediations_id_seq OWNED BY vulnerability_findings_remediations.id;
|
||||||
|
|
||||||
CREATE TABLE vulnerability_historical_statistics (
|
CREATE TABLE vulnerability_historical_statistics (
|
||||||
id bigint NOT NULL,
|
id bigint NOT NULL,
|
||||||
created_at timestamp with time zone NOT NULL,
|
created_at timestamp with time zone NOT NULL,
|
||||||
|
@ -17316,6 +17334,26 @@ CREATE SEQUENCE vulnerability_occurrences_id_seq
|
||||||
|
|
||||||
ALTER SEQUENCE vulnerability_occurrences_id_seq OWNED BY vulnerability_occurrences.id;
|
ALTER SEQUENCE vulnerability_occurrences_id_seq OWNED BY vulnerability_occurrences.id;
|
||||||
|
|
||||||
|
CREATE TABLE vulnerability_remediations (
|
||||||
|
id bigint NOT NULL,
|
||||||
|
created_at timestamp with time zone NOT NULL,
|
||||||
|
updated_at timestamp with time zone NOT NULL,
|
||||||
|
file_store smallint,
|
||||||
|
summary text NOT NULL,
|
||||||
|
file text NOT NULL,
|
||||||
|
CONSTRAINT check_ac0ccabff3 CHECK ((char_length(summary) <= 200)),
|
||||||
|
CONSTRAINT check_fe3325e3ba CHECK ((char_length(file) <= 255))
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE SEQUENCE vulnerability_remediations_id_seq
|
||||||
|
START WITH 1
|
||||||
|
INCREMENT BY 1
|
||||||
|
NO MINVALUE
|
||||||
|
NO MAXVALUE
|
||||||
|
CACHE 1;
|
||||||
|
|
||||||
|
ALTER SEQUENCE vulnerability_remediations_id_seq OWNED BY vulnerability_remediations.id;
|
||||||
|
|
||||||
CREATE TABLE vulnerability_scanners (
|
CREATE TABLE vulnerability_scanners (
|
||||||
id bigint NOT NULL,
|
id bigint NOT NULL,
|
||||||
created_at timestamp with time zone NOT NULL,
|
created_at timestamp with time zone NOT NULL,
|
||||||
|
@ -18298,6 +18336,8 @@ ALTER TABLE ONLY vulnerability_feedback ALTER COLUMN id SET DEFAULT nextval('vul
|
||||||
|
|
||||||
ALTER TABLE ONLY vulnerability_finding_links ALTER COLUMN id SET DEFAULT nextval('vulnerability_finding_links_id_seq'::regclass);
|
ALTER TABLE ONLY vulnerability_finding_links ALTER COLUMN id SET DEFAULT nextval('vulnerability_finding_links_id_seq'::regclass);
|
||||||
|
|
||||||
|
ALTER TABLE ONLY vulnerability_findings_remediations ALTER COLUMN id SET DEFAULT nextval('vulnerability_findings_remediations_id_seq'::regclass);
|
||||||
|
|
||||||
ALTER TABLE ONLY vulnerability_historical_statistics ALTER COLUMN id SET DEFAULT nextval('vulnerability_historical_statistics_id_seq'::regclass);
|
ALTER TABLE ONLY vulnerability_historical_statistics ALTER COLUMN id SET DEFAULT nextval('vulnerability_historical_statistics_id_seq'::regclass);
|
||||||
|
|
||||||
ALTER TABLE ONLY vulnerability_identifiers ALTER COLUMN id SET DEFAULT nextval('vulnerability_identifiers_id_seq'::regclass);
|
ALTER TABLE ONLY vulnerability_identifiers ALTER COLUMN id SET DEFAULT nextval('vulnerability_identifiers_id_seq'::regclass);
|
||||||
|
@ -18310,6 +18350,8 @@ ALTER TABLE ONLY vulnerability_occurrence_pipelines ALTER COLUMN id SET DEFAULT
|
||||||
|
|
||||||
ALTER TABLE ONLY vulnerability_occurrences ALTER COLUMN id SET DEFAULT nextval('vulnerability_occurrences_id_seq'::regclass);
|
ALTER TABLE ONLY vulnerability_occurrences ALTER COLUMN id SET DEFAULT nextval('vulnerability_occurrences_id_seq'::regclass);
|
||||||
|
|
||||||
|
ALTER TABLE ONLY vulnerability_remediations ALTER COLUMN id SET DEFAULT nextval('vulnerability_remediations_id_seq'::regclass);
|
||||||
|
|
||||||
ALTER TABLE ONLY vulnerability_scanners ALTER COLUMN id SET DEFAULT nextval('vulnerability_scanners_id_seq'::regclass);
|
ALTER TABLE ONLY vulnerability_scanners ALTER COLUMN id SET DEFAULT nextval('vulnerability_scanners_id_seq'::regclass);
|
||||||
|
|
||||||
ALTER TABLE ONLY vulnerability_statistics ALTER COLUMN id SET DEFAULT nextval('vulnerability_statistics_id_seq'::regclass);
|
ALTER TABLE ONLY vulnerability_statistics ALTER COLUMN id SET DEFAULT nextval('vulnerability_statistics_id_seq'::regclass);
|
||||||
|
@ -19753,6 +19795,9 @@ ALTER TABLE ONLY vulnerability_feedback
|
||||||
ALTER TABLE ONLY vulnerability_finding_links
|
ALTER TABLE ONLY vulnerability_finding_links
|
||||||
ADD CONSTRAINT vulnerability_finding_links_pkey PRIMARY KEY (id);
|
ADD CONSTRAINT vulnerability_finding_links_pkey PRIMARY KEY (id);
|
||||||
|
|
||||||
|
ALTER TABLE ONLY vulnerability_findings_remediations
|
||||||
|
ADD CONSTRAINT vulnerability_findings_remediations_pkey PRIMARY KEY (id);
|
||||||
|
|
||||||
ALTER TABLE ONLY vulnerability_historical_statistics
|
ALTER TABLE ONLY vulnerability_historical_statistics
|
||||||
ADD CONSTRAINT vulnerability_historical_statistics_pkey PRIMARY KEY (id);
|
ADD CONSTRAINT vulnerability_historical_statistics_pkey PRIMARY KEY (id);
|
||||||
|
|
||||||
|
@ -19771,6 +19816,9 @@ ALTER TABLE ONLY vulnerability_occurrence_pipelines
|
||||||
ALTER TABLE ONLY vulnerability_occurrences
|
ALTER TABLE ONLY vulnerability_occurrences
|
||||||
ADD CONSTRAINT vulnerability_occurrences_pkey PRIMARY KEY (id);
|
ADD CONSTRAINT vulnerability_occurrences_pkey PRIMARY KEY (id);
|
||||||
|
|
||||||
|
ALTER TABLE ONLY vulnerability_remediations
|
||||||
|
ADD CONSTRAINT vulnerability_remediations_pkey PRIMARY KEY (id);
|
||||||
|
|
||||||
ALTER TABLE ONLY vulnerability_scanners
|
ALTER TABLE ONLY vulnerability_scanners
|
||||||
ADD CONSTRAINT vulnerability_scanners_pkey PRIMARY KEY (id);
|
ADD CONSTRAINT vulnerability_scanners_pkey PRIMARY KEY (id);
|
||||||
|
|
||||||
|
@ -21793,6 +21841,8 @@ CREATE INDEX index_releases_on_author_id ON releases USING btree (author_id);
|
||||||
|
|
||||||
CREATE INDEX index_releases_on_project_id_and_tag ON releases USING btree (project_id, tag);
|
CREATE INDEX index_releases_on_project_id_and_tag ON releases USING btree (project_id, tag);
|
||||||
|
|
||||||
|
CREATE INDEX index_releases_on_released_at ON releases USING btree (released_at);
|
||||||
|
|
||||||
CREATE INDEX index_remote_mirrors_on_last_successful_update_at ON remote_mirrors USING btree (last_successful_update_at);
|
CREATE INDEX index_remote_mirrors_on_last_successful_update_at ON remote_mirrors USING btree (last_successful_update_at);
|
||||||
|
|
||||||
CREATE INDEX index_remote_mirrors_on_project_id ON remote_mirrors USING btree (project_id);
|
CREATE INDEX index_remote_mirrors_on_project_id ON remote_mirrors USING btree (project_id);
|
||||||
|
@ -22229,6 +22279,10 @@ CREATE INDEX index_vulnerability_feedback_on_merge_request_id ON vulnerability_f
|
||||||
|
|
||||||
CREATE INDEX index_vulnerability_feedback_on_pipeline_id ON vulnerability_feedback USING btree (pipeline_id);
|
CREATE INDEX index_vulnerability_feedback_on_pipeline_id ON vulnerability_feedback USING btree (pipeline_id);
|
||||||
|
|
||||||
|
CREATE INDEX index_vulnerability_findings_remediations_on_remediation_id ON vulnerability_findings_remediations USING btree (vulnerability_remediation_id);
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX index_vulnerability_findings_remediations_on_unique_keys ON vulnerability_findings_remediations USING btree (vulnerability_occurrence_id, vulnerability_remediation_id);
|
||||||
|
|
||||||
CREATE INDEX index_vulnerability_historical_statistics_on_date_and_id ON vulnerability_historical_statistics USING btree (date, id);
|
CREATE INDEX index_vulnerability_historical_statistics_on_date_and_id ON vulnerability_historical_statistics USING btree (date, id);
|
||||||
|
|
||||||
CREATE UNIQUE INDEX index_vulnerability_identifiers_on_project_id_and_fingerprint ON vulnerability_identifiers USING btree (project_id, fingerprint);
|
CREATE UNIQUE INDEX index_vulnerability_identifiers_on_project_id_and_fingerprint ON vulnerability_identifiers USING btree (project_id, fingerprint);
|
||||||
|
@ -23508,6 +23562,9 @@ ALTER TABLE ONLY project_alerting_settings
|
||||||
ALTER TABLE ONLY dast_site_validations
|
ALTER TABLE ONLY dast_site_validations
|
||||||
ADD CONSTRAINT fk_rails_285c617324 FOREIGN KEY (dast_site_token_id) REFERENCES dast_site_tokens(id) ON DELETE CASCADE;
|
ADD CONSTRAINT fk_rails_285c617324 FOREIGN KEY (dast_site_token_id) REFERENCES dast_site_tokens(id) ON DELETE CASCADE;
|
||||||
|
|
||||||
|
ALTER TABLE ONLY vulnerability_findings_remediations
|
||||||
|
ADD CONSTRAINT fk_rails_28a8d0cf93 FOREIGN KEY (vulnerability_occurrence_id) REFERENCES vulnerability_occurrences(id) ON DELETE CASCADE;
|
||||||
|
|
||||||
ALTER TABLE ONLY resource_state_events
|
ALTER TABLE ONLY resource_state_events
|
||||||
ADD CONSTRAINT fk_rails_29af06892a FOREIGN KEY (issue_id) REFERENCES issues(id) ON DELETE CASCADE;
|
ADD CONSTRAINT fk_rails_29af06892a FOREIGN KEY (issue_id) REFERENCES issues(id) ON DELETE CASCADE;
|
||||||
|
|
||||||
|
@ -23868,6 +23925,9 @@ ALTER TABLE ONLY web_hook_logs
|
||||||
ALTER TABLE ONLY jira_imports
|
ALTER TABLE ONLY jira_imports
|
||||||
ADD CONSTRAINT fk_rails_675d38c03b FOREIGN KEY (label_id) REFERENCES labels(id) ON DELETE SET NULL;
|
ADD CONSTRAINT fk_rails_675d38c03b FOREIGN KEY (label_id) REFERENCES labels(id) ON DELETE SET NULL;
|
||||||
|
|
||||||
|
ALTER TABLE ONLY vulnerability_findings_remediations
|
||||||
|
ADD CONSTRAINT fk_rails_681c85ae0f FOREIGN KEY (vulnerability_remediation_id) REFERENCES vulnerability_remediations(id) ON DELETE CASCADE;
|
||||||
|
|
||||||
ALTER TABLE ONLY resource_iteration_events
|
ALTER TABLE ONLY resource_iteration_events
|
||||||
ADD CONSTRAINT fk_rails_6830c13ac1 FOREIGN KEY (merge_request_id) REFERENCES merge_requests(id) ON DELETE CASCADE;
|
ADD CONSTRAINT fk_rails_6830c13ac1 FOREIGN KEY (merge_request_id) REFERENCES merge_requests(id) ON DELETE CASCADE;
|
||||||
|
|
||||||
|
|
|
@ -24,19 +24,19 @@ The keywords available for jobs are:
|
||||||
|
|
||||||
| Keyword | Description |
|
| Keyword | Description |
|
||||||
|:---------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|:---------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| [`script`](#script) | Shell script that is executed by a runner. |
|
| [`script`](#script) | Shell script that is executed by a runner. |
|
||||||
| [`after_script`](#after_script) | Override a set of commands that are executed after job. |
|
| [`after_script`](#after_script) | Override a set of commands that are executed after job. |
|
||||||
| [`allow_failure`](#allow_failure) | Allow job to fail. Failed job does not contribute to commit status. |
|
| [`allow_failure`](#allow_failure) | Allow job to fail. A failed job does not cause the pipeline to fail. |
|
||||||
| [`artifacts`](#artifacts) | List of files and directories to attach to a job on success. Also available: `artifacts:paths`, `artifacts:exclude`, `artifacts:expose_as`, `artifacts:name`, `artifacts:untracked`, `artifacts:when`, `artifacts:expire_in`, and `artifacts:reports`. |
|
| [`artifacts`](#artifacts) | List of files and directories to attach to a job on success. Also available: `artifacts:paths`, `artifacts:exclude`, `artifacts:expose_as`, `artifacts:name`, `artifacts:untracked`, `artifacts:when`, `artifacts:expire_in`, and `artifacts:reports`. |
|
||||||
| [`before_script`](#before_script) | Override a set of commands that are executed before job. |
|
| [`before_script`](#before_script) | Override a set of commands that are executed before job. |
|
||||||
| [`cache`](#cache) | List of files that should be cached between subsequent runs. Also available: `cache:paths`, `cache:key`, `cache:untracked`, `cache:when`, and `cache:policy`. |
|
| [`cache`](#cache) | List of files that should be cached between subsequent runs. Also available: `cache:paths`, `cache:key`, `cache:untracked`, `cache:when`, and `cache:policy`. |
|
||||||
| [`coverage`](#coverage) | Code coverage settings for a given job. |
|
| [`coverage`](#coverage) | Code coverage settings for a given job. |
|
||||||
| [`dependencies`](#dependencies) | Restrict which artifacts are passed to a specific job by providing a list of jobs to fetch artifacts from. |
|
| [`dependencies`](#dependencies) | Restrict which artifacts are passed to a specific job by providing a list of jobs to fetch artifacts from. |
|
||||||
| [`environment`](#environment) | Name of an environment to which the job deploys. Also available: `environment:name`, `environment:url`, `environment:on_stop`, `environment:auto_stop_in`, and `environment:action`. |
|
| [`environment`](#environment) | Name of an environment to which the job deploys. Also available: `environment:name`, `environment:url`, `environment:on_stop`, `environment:auto_stop_in`, and `environment:action`. |
|
||||||
| [`except`](#onlyexcept-basic) | Limit when jobs are not created. Also available: [`except:refs`, `except:kubernetes`, `except:variables`, and `except:changes`](#onlyexcept-advanced). |
|
| [`except`](#onlyexcept-basic) | Limit when jobs are not created. Also available: [`except:refs`, `except:kubernetes`, `except:variables`, and `except:changes`](#onlyexcept-advanced). |
|
||||||
| [`extends`](#extends) | Configuration entries that this job inherits from. |
|
| [`extends`](#extends) | Configuration entries that this job inherits from. |
|
||||||
| [`image`](#image) | Use Docker images. Also available: `image:name` and `image:entrypoint`. |
|
| [`image`](#image) | Use Docker images. Also available: `image:name` and `image:entrypoint`. |
|
||||||
| [`include`](#include) | Allows this job to include external YAML files. Also available: `include:local`, `include:file`, `include:template`, and `include:remote`. |
|
| [`include`](#include) | Include external YAML files. Also available: `include:local`, `include:file`, `include:template`, and `include:remote`. |
|
||||||
| [`interruptible`](#interruptible) | Defines if a job can be canceled when made redundant by a newer run. |
|
| [`interruptible`](#interruptible) | Defines if a job can be canceled when made redundant by a newer run. |
|
||||||
| [`only`](#onlyexcept-basic) | Limit when jobs are created. Also available: [`only:refs`, `only:kubernetes`, `only:variables`, and `only:changes`](#onlyexcept-advanced). |
|
| [`only`](#onlyexcept-basic) | Limit when jobs are created. Also available: [`only:refs`, `only:kubernetes`, `only:variables`, and `only:changes`](#onlyexcept-advanced). |
|
||||||
| [`pages`](#pages) | Upload the result of a job to use with GitLab Pages. |
|
| [`pages`](#pages) | Upload the result of a job to use with GitLab Pages. |
|
||||||
|
@ -47,7 +47,7 @@ The keywords available for jobs are:
|
||||||
| [`rules`](#rules) | List of conditions to evaluate and determine selected attributes of a job, and whether or not it's created. May not be used alongside `only`/`except`. |
|
| [`rules`](#rules) | List of conditions to evaluate and determine selected attributes of a job, and whether or not it's created. May not be used alongside `only`/`except`. |
|
||||||
| [`services`](#services) | Use Docker services images. Also available: `services:name`, `services:alias`, `services:entrypoint`, and `services:command`. |
|
| [`services`](#services) | Use Docker services images. Also available: `services:name`, `services:alias`, `services:entrypoint`, and `services:command`. |
|
||||||
| [`stage`](#stage) | Defines a job stage (default: `test`). |
|
| [`stage`](#stage) | Defines a job stage (default: `test`). |
|
||||||
| [`tags`](#tags) | List of tags that are used to select a runner. |
|
| [`tags`](#tags) | List of tags that are used to select a runner. |
|
||||||
| [`timeout`](#timeout) | Define a custom job-level timeout that takes precedence over the project-wide setting. |
|
| [`timeout`](#timeout) | Define a custom job-level timeout that takes precedence over the project-wide setting. |
|
||||||
| [`trigger`](#trigger) | Defines a downstream pipeline trigger. |
|
| [`trigger`](#trigger) | Defines a downstream pipeline trigger. |
|
||||||
| [`variables`](#variables) | Define job variables on a job level. |
|
| [`variables`](#variables) | Define job variables on a job level. |
|
||||||
|
@ -225,7 +225,7 @@ stages:
|
||||||
There are also two edge cases worth mentioning:
|
There are also two edge cases worth mentioning:
|
||||||
|
|
||||||
1. If no `stages` are defined in `.gitlab-ci.yml`, then the `build`,
|
1. If no `stages` are defined in `.gitlab-ci.yml`, then the `build`,
|
||||||
`test` and `deploy` are allowed to be used as job's stage by default.
|
`test` and `deploy` can be used as job's stage by default.
|
||||||
1. If a job does not specify a `stage`, the job is assigned the `test` stage.
|
1. If a job does not specify a `stage`, the job is assigned the `test` stage.
|
||||||
|
|
||||||
### `workflow:rules`
|
### `workflow:rules`
|
||||||
|
@ -488,8 +488,8 @@ include:
|
||||||
- remote: 'https://gitlab.com/awesome-project/raw/master/.gitlab-ci-template.yml'
|
- remote: 'https://gitlab.com/awesome-project/raw/master/.gitlab-ci-template.yml'
|
||||||
```
|
```
|
||||||
|
|
||||||
All [nested includes](#nested-includes) are executed without context as public user, so only another remote
|
All [nested includes](#nested-includes) are executed without context as a public user,
|
||||||
or public project, or template, is allowed.
|
so you can only `include` public projects or templates.
|
||||||
|
|
||||||
#### `include:template`
|
#### `include:template`
|
||||||
|
|
||||||
|
@ -521,9 +521,9 @@ so it's possible to use project, remote or template includes.
|
||||||
|
|
||||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/56836) in GitLab 11.9.
|
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/56836) in GitLab 11.9.
|
||||||
|
|
||||||
Nested includes allow you to compose a set of includes.
|
Use nested includes to compose a set of includes.
|
||||||
|
|
||||||
A total of 100 includes is allowed, but duplicate includes are considered a configuration error.
|
You can have up to 100 includes, but you can't have duplicate includes.
|
||||||
|
|
||||||
In [GitLab 12.4](https://gitlab.com/gitlab-org/gitlab/-/issues/28212) and later, the time limit
|
In [GitLab 12.4](https://gitlab.com/gitlab-org/gitlab/-/issues/28212) and later, the time limit
|
||||||
to resolve all files is 30 seconds.
|
to resolve all files is 30 seconds.
|
||||||
|
@ -713,7 +713,7 @@ You can use special syntax in [`script`](README.md#script) sections to:
|
||||||
### `stage`
|
### `stage`
|
||||||
|
|
||||||
`stage` is defined per-job and relies on [`stages`](#stages), which is defined
|
`stage` is defined per-job and relies on [`stages`](#stages), which is defined
|
||||||
globally. It allows to group jobs into different stages, and jobs of the same
|
globally. Use `stage` to define which stage a job runs in, and jobs of the same
|
||||||
`stage` are executed in parallel (subject to [certain conditions](#using-your-own-runners)). For example:
|
`stage` are executed in parallel (subject to [certain conditions](#using-your-own-runners)). For example:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
@ -989,7 +989,7 @@ If you attempt to use both keywords in the same job, the linter returns a
|
||||||
|
|
||||||
#### Rules attributes
|
#### Rules attributes
|
||||||
|
|
||||||
The job attributes allowed by `rules` are:
|
The job attributes you can use with `rules` are:
|
||||||
|
|
||||||
- [`when`](#when): If not defined, defaults to `when: on_success`.
|
- [`when`](#when): If not defined, defaults to `when: on_success`.
|
||||||
- If used as `when: delayed`, `start_in` is also required.
|
- If used as `when: delayed`, `start_in` is also required.
|
||||||
|
@ -1059,7 +1059,7 @@ In this example:
|
||||||
is added to the [merge request pipeline](../merge_request_pipelines/index.md)
|
is added to the [merge request pipeline](../merge_request_pipelines/index.md)
|
||||||
with attributes of:
|
with attributes of:
|
||||||
- `when: manual` (manual job)
|
- `when: manual` (manual job)
|
||||||
- `allow_failure: true` (allows the pipeline to continue running even if the manual job is not run)
|
- `allow_failure: true` (the pipeline continues running even if the manual job is not run)
|
||||||
- If the pipeline is **not** for a merge request, the first rule doesn't match, and the
|
- If the pipeline is **not** for a merge request, the first rule doesn't match, and the
|
||||||
second rule is evaluated.
|
second rule is evaluated.
|
||||||
- If the pipeline is a scheduled pipeline, the second rule matches, and the job
|
- If the pipeline is a scheduled pipeline, the second rule matches, and the job
|
||||||
|
@ -1098,8 +1098,8 @@ for more details.
|
||||||
|
|
||||||
Jobs defined with `rules` can trigger multiple pipelines with the same action. You
|
Jobs defined with `rules` can trigger multiple pipelines with the same action. You
|
||||||
don't have to explicitly configure rules for each type of pipeline to trigger them
|
don't have to explicitly configure rules for each type of pipeline to trigger them
|
||||||
accidentally. Rules that are too loose (allowing too many types of pipelines) could
|
accidentally. Rules that are too broad could cause simultaneous pipelines of a different
|
||||||
cause a second pipeline to run unexpectedly.
|
type to run unexpectedly.
|
||||||
|
|
||||||
Some configurations that have the potential to cause duplicate pipelines cause a
|
Some configurations that have the potential to cause duplicate pipelines cause a
|
||||||
[pipeline warning](../troubleshooting.md#pipeline-warnings) to be displayed.
|
[pipeline warning](../troubleshooting.md#pipeline-warnings) to be displayed.
|
||||||
|
@ -1124,8 +1124,8 @@ causes duplicated pipelines.
|
||||||
There are multiple ways to avoid this:
|
There are multiple ways to avoid this:
|
||||||
|
|
||||||
- Use [`workflow: rules`](#workflowrules) to specify which types of pipelines
|
- Use [`workflow: rules`](#workflowrules) to specify which types of pipelines
|
||||||
can run. To eliminate duplicate pipelines, allow only merge request pipelines
|
can run. To eliminate duplicate pipelines, use merge request pipelines only
|
||||||
or push (branch) pipelines.
|
or push (branch) pipelines only.
|
||||||
|
|
||||||
- Rewrite the rules to run the job only in very specific cases,
|
- Rewrite the rules to run the job only in very specific cases,
|
||||||
and avoid using a final `when:` rule:
|
and avoid using a final `when:` rule:
|
||||||
|
@ -1323,8 +1323,8 @@ docker build:
|
||||||
In this example:
|
In this example:
|
||||||
|
|
||||||
- If the pipeline is a merge request pipeline, check `Dockerfile` for changes.
|
- If the pipeline is a merge request pipeline, check `Dockerfile` for changes.
|
||||||
- If `Dockerfile` has changed, add the job to the pipeline as a manual job, and allow the pipeline
|
- If `Dockerfile` has changed, add the job to the pipeline as a manual job, and the pipeline
|
||||||
to continue running even if the job is not triggered (`allow_failure: true`).
|
continues running even if the job is not triggered (`allow_failure: true`).
|
||||||
- If `Dockerfile` has not changed, do not add job to any pipeline (same as `when: never`).
|
- If `Dockerfile` has not changed, do not add job to any pipeline (same as `when: never`).
|
||||||
|
|
||||||
To use `rules: changes` with branch pipelines instead of merge request pipelines,
|
To use `rules: changes` with branch pipelines instead of merge request pipelines,
|
||||||
|
@ -1511,11 +1511,10 @@ There are a few rules that apply to the usage of job policy:
|
||||||
|
|
||||||
- `only` and `except` are inclusive. If both `only` and `except` are defined
|
- `only` and `except` are inclusive. If both `only` and `except` are defined
|
||||||
in a job specification, the ref is filtered by `only` and `except`.
|
in a job specification, the ref is filtered by `only` and `except`.
|
||||||
- `only` and `except` allow the use of regular expressions ([supported regexp syntax](#supported-onlyexcept-regexp-syntax)).
|
- `only` and `except` can use regular expressions ([supported regexp syntax](#supported-onlyexcept-regexp-syntax)).
|
||||||
- `only` and `except` allow to specify a repository path to filter jobs for
|
- `only` and `except` can specify a repository path to filter jobs for forks.
|
||||||
forks.
|
|
||||||
|
|
||||||
In addition, `only` and `except` allow the use of special keywords:
|
In addition, `only` and `except` can use special keywords:
|
||||||
|
|
||||||
| **Value** | **Description** |
|
| **Value** | **Description** |
|
||||||
|--------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|--------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
|
@ -1636,8 +1635,8 @@ Only a subset of features provided by [Ruby Regexp](https://ruby-doc.org/core/Re
|
||||||
are now supported.
|
are now supported.
|
||||||
|
|
||||||
From GitLab 11.9.7 to GitLab 12.0, GitLab provided a feature flag to
|
From GitLab 11.9.7 to GitLab 12.0, GitLab provided a feature flag to
|
||||||
let you use the unsafe regexp syntax. This flag allowed
|
let you use unsafe regexp syntax. After migrating to safe syntax, you should disable
|
||||||
compatibility with the previous syntax version so you could gracefully migrate to the new syntax.
|
this feature flag again:
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
Feature.enable(:allow_unsafe_ruby_regexp)
|
Feature.enable(:allow_unsafe_ruby_regexp)
|
||||||
|
@ -1935,8 +1934,8 @@ runs.
|
||||||
> - In GitLab 12.3, maximum number of jobs in `needs` array raised from five to 50.
|
> - In GitLab 12.3, maximum number of jobs in `needs` array raised from five to 50.
|
||||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/30631) in GitLab 12.8, `needs: []` lets jobs start immediately.
|
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/30631) in GitLab 12.8, `needs: []` lets jobs start immediately.
|
||||||
|
|
||||||
The `needs:` keyword enables executing jobs out-of-order, allowing you to implement
|
Use the `needs:` keyword to execute jobs out-of-order. Relationships between jobs
|
||||||
a [directed acyclic graph](../directed_acyclic_graph/index.md) in your `.gitlab-ci.yml`.
|
that use `needs` can be visualized as a [directed acyclic graph](../directed_acyclic_graph/index.md).
|
||||||
|
|
||||||
This lets you run some jobs without waiting for other ones, disregarding stage ordering
|
This lets you run some jobs without waiting for other ones, disregarding stage ordering
|
||||||
so you can have multiple stages running concurrently.
|
so you can have multiple stages running concurrently.
|
||||||
|
@ -2179,7 +2178,7 @@ The default value is `false`, except for [manual](#whenmanual) jobs using the
|
||||||
`when: manual` syntax, unless using [`rules:`](#rules) syntax, where all jobs
|
`when: manual` syntax, unless using [`rules:`](#rules) syntax, where all jobs
|
||||||
default to false, *including* `when: manual` jobs.
|
default to false, *including* `when: manual` jobs.
|
||||||
|
|
||||||
When `allow_failure` is enabled and the job fails, the job shows an orange warning in the UI.
|
When `allow_failure` is set to `true` and the job fails, the job shows an orange warning in the UI.
|
||||||
However, the logical flow of the pipeline considers the job a
|
However, the logical flow of the pipeline considers the job a
|
||||||
success/passed, and is not blocked.
|
success/passed, and is not blocked.
|
||||||
|
|
||||||
|
@ -2320,13 +2319,12 @@ can opt to disable it.
|
||||||
|
|
||||||
##### Protecting manual jobs **(PREMIUM)**
|
##### Protecting manual jobs **(PREMIUM)**
|
||||||
|
|
||||||
It's possible to use [protected environments](../environments/protected_environments.md)
|
Use [protected environments](../environments/protected_environments.md)
|
||||||
to define a precise list of users authorized to run a manual job. By allowing only
|
to define a list of users authorized to run a manual job. You can authorize only
|
||||||
users associated with a protected environment to trigger manual jobs, it's possible
|
the users associated with a protected environment to trigger manual jobs, which can:
|
||||||
to implement some special use cases, such as:
|
|
||||||
|
|
||||||
- More precisely limiting who can deploy to an environment.
|
- More precisely limit who can deploy to an environment.
|
||||||
- Enabling a pipeline to be blocked until an approved user "approves" it.
|
- Block a pipeline until an approved user "approves" it.
|
||||||
|
|
||||||
To do this, you must:
|
To do this, you must:
|
||||||
|
|
||||||
|
@ -2808,7 +2806,7 @@ If neither file was changed in any commits, the prefix is added to `default`, so
|
||||||
key in the example would be `test-default`.
|
key in the example would be `test-default`.
|
||||||
|
|
||||||
Like `cache:key`, `prefix` can use any of the [predefined variables](../variables/README.md),
|
Like `cache:key`, `prefix` can use any of the [predefined variables](../variables/README.md),
|
||||||
but the following are not allowed:
|
but cannot include:
|
||||||
|
|
||||||
- the `/` character (or the equivalent URI-encoded `%2F`)
|
- the `/` character (or the equivalent URI-encoded `%2F`)
|
||||||
- a value made only of `.` (or the equivalent URI-encoded `%2E`)
|
- a value made only of `.` (or the equivalent URI-encoded `%2E`)
|
||||||
|
@ -4353,8 +4351,8 @@ job_name:
|
||||||
|
|
||||||
#### YAML anchors for variables
|
#### YAML anchors for variables
|
||||||
|
|
||||||
[YAML anchors](#anchors) can be used with `variables`, to easily repeat assignment
|
[YAML anchors](#anchors) can be used with `variables`, to repeat assignment
|
||||||
of variables across multiple jobs. It can also enable more flexibility when a job
|
of variables across multiple jobs. Use can also use YAML anchors when a job
|
||||||
requires a specific `variables` block that would otherwise override the global variables.
|
requires a specific `variables` block that would otherwise override the global variables.
|
||||||
|
|
||||||
In the example below, we override the `GIT_STRATEGY` variable without affecting
|
In the example below, we override the `GIT_STRATEGY` variable without affecting
|
||||||
|
|
|
@ -833,7 +833,7 @@ If your application contains `@client` queries, most probably you will have an A
|
||||||
```javascript
|
```javascript
|
||||||
import createMockApollo from 'jest/helpers/mock_apollo_helper';
|
import createMockApollo from 'jest/helpers/mock_apollo_helper';
|
||||||
...
|
...
|
||||||
fakeApollo = createMockApollo(requestHandlers, {});
|
mockApollo = createMockApollo(requestHandlers, resolvers);
|
||||||
```
|
```
|
||||||
|
|
||||||
Sometimes we want to test a `result` hook of the local query. In order to have it triggered, we need to populate a cache with correct data to be fetched with this query:
|
Sometimes we want to test a `result` hook of the local query. In order to have it triggered, we need to populate a cache with correct data to be fetched with this query:
|
||||||
|
@ -849,14 +849,14 @@ query fetchLocalUser {
|
||||||
```javascript
|
```javascript
|
||||||
import fetchLocalUserQuery from '~/design_management/graphql/queries/fetch_local_user.query.graphql';
|
import fetchLocalUserQuery from '~/design_management/graphql/queries/fetch_local_user.query.graphql';
|
||||||
|
|
||||||
function createComponentWithApollo() {
|
function createMockApolloProvider() {
|
||||||
const requestHandlers = [
|
const requestHandlers = [
|
||||||
[getDesignListQuery, jest.fn().mockResolvedValue(designListQueryResponse)],
|
[getDesignListQuery, jest.fn().mockResolvedValue(designListQueryResponse)],
|
||||||
[permissionsQuery, jest.fn().mockResolvedValue(permissionsQueryResponse)],
|
[permissionsQuery, jest.fn().mockResolvedValue(permissionsQueryResponse)],
|
||||||
];
|
];
|
||||||
|
|
||||||
fakeApollo = createMockApollo(requestHandlers, {});
|
mockApollo = createMockApollo(requestHandlers, {});
|
||||||
fakeApollo.clients.defaultClient.cache.writeQuery({
|
mockApollo.clients.defaultClient.cache.writeQuery({
|
||||||
query: fetchLocalUserQuery,
|
query: fetchLocalUserQuery,
|
||||||
data: {
|
data: {
|
||||||
fetchLocalUser: {
|
fetchLocalUser: {
|
||||||
|
@ -864,15 +864,107 @@ function createComponentWithApollo() {
|
||||||
name: 'Test',
|
name: 'Test',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
wrapper = shallowMount(Index, {
|
return mockApollo;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createComponent(options = {}) {
|
||||||
|
const { mockApollo } = options;
|
||||||
|
|
||||||
|
return shallowMount(Index, {
|
||||||
localVue,
|
localVue,
|
||||||
apolloProvider: fakeApollo,
|
apolloProvider: mockApollo,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Sometimes it is necessary to control what the local resolver returns and inspect how it is called by the component. This can be done by mocking your local resolver:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import fetchLocalUserQuery from '~/design_management/graphql/queries/fetch_local_user.query.graphql';
|
||||||
|
|
||||||
|
function createMockApolloProvider(options = {}) {
|
||||||
|
const { fetchLocalUserSpy } = options;
|
||||||
|
|
||||||
|
mockApollo = createMockApollo([], {
|
||||||
|
Query: {
|
||||||
|
fetchLocalUser: fetchLocalUserSpy,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Necessary for local resolvers to be activated
|
||||||
|
mockApollo.clients.defaultClient.cache.writeQuery({
|
||||||
|
query: fetchLocalUserQuery,
|
||||||
|
data: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
return mockApollo;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In the test you can then control what the spy is supposed to do and inspect the component after the request have returned:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
describe('My Index test with `createMockApollo`', () => {
|
||||||
|
let wrapper;
|
||||||
|
let fetchLocalUserSpy;
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
wrapper.destroy();
|
||||||
|
wrapper = null;
|
||||||
|
fetchLocalUserSpy = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when loading', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
const mockApollo = createMockApolloProvider();
|
||||||
|
wrapper = createComponent({ mockApollo });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('displays the loader', () => {
|
||||||
|
// Assess that the loader is present
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with data', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
fetchLocalUserSpy = jest.fn().mockResolvedValue(localUserQueryResponse);
|
||||||
|
const mockApollo = createMockApolloProvider(fetchLocalUserSpy);
|
||||||
|
wrapper = createComponent({ mockApollo });
|
||||||
|
await waitForPromises();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fetch data once', () => {
|
||||||
|
expect(fetchLocalUserSpy).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('displays data', () => {
|
||||||
|
// Assess that data is present
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with error', () => {
|
||||||
|
const error = 'Error!';
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
fetchLocalUserSpy = jest.fn().mockRejectedValueOnce(error);
|
||||||
|
const mockApollo = createMockApolloProvider(fetchLocalUserSpy);
|
||||||
|
wrapper = createComponent({ mockApollo });
|
||||||
|
await waitForPromises();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fetch data once', () => {
|
||||||
|
expect(fetchLocalUserSpy).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('displays the error', () => {
|
||||||
|
// Assess that the error is displayed
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
## Handling errors
|
## Handling errors
|
||||||
|
|
||||||
GitLab's GraphQL mutations currently have two distinct error modes: [Top-level](#top-level-errors) and [errors-as-data](#errors-as-data).
|
GitLab's GraphQL mutations currently have two distinct error modes: [Top-level](#top-level-errors) and [errors-as-data](#errors-as-data).
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
stage: none
|
stage: Manage
|
||||||
group: unassigned
|
group: Access
|
||||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
|
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
|
||||||
type: reference, howto
|
type: reference, howto
|
||||||
---
|
---
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
stage: none
|
stage: Manage
|
||||||
group: unassigned
|
group: Access
|
||||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
|
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
|
||||||
type: reference
|
type: reference
|
||||||
---
|
---
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
stage: none
|
stage: Manage
|
||||||
group: unassigned
|
group: Access
|
||||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
|
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
|
||||||
type: howto
|
type: howto
|
||||||
---
|
---
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
stage: none
|
stage: Manage
|
||||||
group: unassigned
|
group: Access
|
||||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
|
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ Merge Request Analytics could be used when:
|
||||||
|
|
||||||
## Visualizations and data
|
## Visualizations and data
|
||||||
|
|
||||||
The following visualizations and data are available, representing all merge requests that were merged in the past 12 months.
|
The following visualizations and data are available, representing all merge requests that were merged in the given date range.
|
||||||
|
|
||||||
### Throughput chart
|
### Throughput chart
|
||||||
|
|
||||||
|
@ -46,7 +46,25 @@ The throughput chart shows the number of merge requests merged per month.
|
||||||
|
|
||||||
### Throughput table
|
### Throughput table
|
||||||
|
|
||||||
Data table displaying a maximum of the 100 most recent merge requests merged for the time period.
|
[Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/232651) in GitLab 13.3.
|
||||||
|
|
||||||
|
The Throughput table displays the most recent merge requests merged in the date range. The
|
||||||
|
table displays up to 20 merge requests at a time. If there are more than 20 merge requests,
|
||||||
|
you can paginate to them. For each merge request, you can review the following data:
|
||||||
|
|
||||||
|
- Title (as a link to the merge request itself)
|
||||||
|
- ID
|
||||||
|
- Pipeline status
|
||||||
|
- Label count
|
||||||
|
- Comment count
|
||||||
|
- Approval count (if approved)
|
||||||
|
- Date merged
|
||||||
|
- Time to merge
|
||||||
|
- Milestone
|
||||||
|
- Commit count
|
||||||
|
- Pipeline count
|
||||||
|
- Line change counts
|
||||||
|
- Assignees
|
||||||
|
|
||||||
![Throughput table](img/mr_throughput_table_v13_3.png "Merge Request Analytics - Throughput table listing the 100 merge requests most recently merged")
|
![Throughput table](img/mr_throughput_table_v13_3.png "Merge Request Analytics - Throughput table listing the 100 merge requests most recently merged")
|
||||||
|
|
||||||
|
@ -68,6 +86,17 @@ To filter results:
|
||||||
1. Click on the filter bar.
|
1. Click on the filter bar.
|
||||||
1. Select a parameter to filter by.
|
1. Select a parameter to filter by.
|
||||||
1. Select a value from the autocompleted results, or enter search text to refine the results.
|
1. Select a value from the autocompleted results, or enter search text to refine the results.
|
||||||
|
1. Hit the "Return" key.
|
||||||
|
|
||||||
|
## Date range
|
||||||
|
|
||||||
|
The date range is set to the past 12 months by default. You can modify the date range by changing the "From" and/or "To" values that appear alongside the filter bar. After changing either value, the data displayed on the page will update automatically.
|
||||||
|
|
||||||
|
## Tip: Bookmark preferred settings
|
||||||
|
|
||||||
|
You can bookmark preferred filters and date ranges. After you have applied a change to the
|
||||||
|
filter bar or the date range, you'll see that information in the URL. You can create a
|
||||||
|
bookmark for those preferred settings in your browser.
|
||||||
|
|
||||||
## Permissions
|
## Permissions
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,8 @@ module Gitlab
|
||||||
bulk_migrate_async(jobs) unless jobs.empty?
|
bulk_migrate_async(jobs) unless jobs.empty?
|
||||||
end
|
end
|
||||||
|
|
||||||
# Queues background migration jobs for an entire table, batched by ID range.
|
# Queues background migration jobs for an entire table in batches.
|
||||||
|
# The default batching column used is the standard primary key `id`.
|
||||||
# Each job is scheduled with a `delay_interval` in between.
|
# Each job is scheduled with a `delay_interval` in between.
|
||||||
# If you use a small interval, then some jobs may run at the same time.
|
# If you use a small interval, then some jobs may run at the same time.
|
||||||
#
|
#
|
||||||
|
@ -68,6 +69,7 @@ module Gitlab
|
||||||
# is scheduled to be run. These records can be used to trace execution of the background job, but there is no
|
# is scheduled to be run. These records can be used to trace execution of the background job, but there is no
|
||||||
# builtin support to manage that automatically at this time. You should only set this flag if you are aware of
|
# builtin support to manage that automatically at this time. You should only set this flag if you are aware of
|
||||||
# how it works, and intend to manually cleanup the database records in your background job.
|
# how it works, and intend to manually cleanup the database records in your background job.
|
||||||
|
# primary_column_name - The name of the primary key column if the primary key is not `id`
|
||||||
#
|
#
|
||||||
# *Returns the final migration delay*
|
# *Returns the final migration delay*
|
||||||
#
|
#
|
||||||
|
@ -87,8 +89,9 @@ module Gitlab
|
||||||
# # do something
|
# # do something
|
||||||
# end
|
# end
|
||||||
# end
|
# end
|
||||||
def queue_background_migration_jobs_by_range_at_intervals(model_class, job_class_name, delay_interval, batch_size: BACKGROUND_MIGRATION_BATCH_SIZE, other_job_arguments: [], initial_delay: 0, track_jobs: false)
|
def queue_background_migration_jobs_by_range_at_intervals(model_class, job_class_name, delay_interval, batch_size: BACKGROUND_MIGRATION_BATCH_SIZE, other_job_arguments: [], initial_delay: 0, track_jobs: false, primary_column_name: :id)
|
||||||
raise "#{model_class} does not have an ID to use for batch ranges" unless model_class.column_names.include?('id')
|
raise "#{model_class} does not have an ID column of #{primary_column_name} to use for batch ranges" unless model_class.column_names.include?(primary_column_name.to_s)
|
||||||
|
raise "#{primary_column_name} is not an integer column" unless model_class.columns_hash[primary_column_name.to_s].type == :integer
|
||||||
|
|
||||||
# To not overload the worker too much we enforce a minimum interval both
|
# To not overload the worker too much we enforce a minimum interval both
|
||||||
# when scheduling and performing jobs.
|
# when scheduling and performing jobs.
|
||||||
|
@ -99,7 +102,7 @@ module Gitlab
|
||||||
final_delay = 0
|
final_delay = 0
|
||||||
|
|
||||||
model_class.each_batch(of: batch_size) do |relation, index|
|
model_class.each_batch(of: batch_size) do |relation, index|
|
||||||
start_id, end_id = relation.pluck(Arel.sql('MIN(id), MAX(id)')).first
|
start_id, end_id = relation.pluck(Arel.sql("MIN(#{primary_column_name}), MAX(#{primary_column_name})")).first
|
||||||
|
|
||||||
# `BackgroundMigrationWorker.bulk_perform_in` schedules all jobs for
|
# `BackgroundMigrationWorker.bulk_perform_in` schedules all jobs for
|
||||||
# the same time, which is not helpful in most cases where we wish to
|
# the same time, which is not helpful in most cases where we wish to
|
||||||
|
|
|
@ -65,6 +65,9 @@ module QA
|
||||||
deleted_pipeline = pipeline
|
deleted_pipeline = pipeline
|
||||||
!pipeline.empty?
|
!pipeline.empty?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
raise "Pipeline response does not have a 'message' key: #{deleted_pipeline}" unless deleted_pipeline&.key?('message')
|
||||||
|
|
||||||
expect(deleted_pipeline['message'].downcase).to have_content('404 not found')
|
expect(deleted_pipeline['message'].downcase).to have_content('404 not found')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
RSpec.describe Releases::EvidencePipelineFinder, '#execute' do
|
||||||
|
let(:params) { {} }
|
||||||
|
let(:project) { create(:project, :repository) }
|
||||||
|
let(:tag_name) { project.repository.tag_names.first }
|
||||||
|
let(:sha) { project.repository.find_tag(tag_name).dereferenced_target.sha }
|
||||||
|
let!(:pipeline) { create(:ci_empty_pipeline, sha: sha, project: project) }
|
||||||
|
|
||||||
|
subject { described_class.new(project, params).execute }
|
||||||
|
|
||||||
|
context 'when the tag is passed' do
|
||||||
|
let(:params) { { tag: tag_name } }
|
||||||
|
|
||||||
|
it 'returns the evidence pipeline' do
|
||||||
|
expect(subject).to eq(pipeline)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the ref is passed' do
|
||||||
|
let(:params) { { ref: sha } }
|
||||||
|
|
||||||
|
it 'returns the evidence pipeline' do
|
||||||
|
expect(subject).to eq(pipeline)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'empty params' do
|
||||||
|
it 'returns nil' do
|
||||||
|
expect(subject).to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# TODO: remove this with the release creation moved to it's own form https://gitlab.com/gitlab-org/gitlab/-/issues/214245
|
||||||
|
context 'params[:evidence_pipeline] is present' do
|
||||||
|
let(:params) { { evidence_pipeline: pipeline } }
|
||||||
|
|
||||||
|
it 'returns the passed evidence pipeline' do
|
||||||
|
expect(subject).to eq(pipeline)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -189,7 +189,51 @@ RSpec.describe Gitlab::Database::Migrations::BackgroundMigrationHelpers do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when the model doesn't have an ID column" do
|
context 'when the model specifies a primary_column_name' do
|
||||||
|
let!(:id1) { create(:container_expiration_policy).id }
|
||||||
|
let!(:id2) { create(:container_expiration_policy).id }
|
||||||
|
let!(:id3) { create(:container_expiration_policy).id }
|
||||||
|
|
||||||
|
around do |example|
|
||||||
|
freeze_time { example.run }
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
ContainerExpirationPolicy.class_eval do
|
||||||
|
include EachBatch
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns the final expected delay', :aggregate_failures do
|
||||||
|
Sidekiq::Testing.fake! do
|
||||||
|
final_delay = model.queue_background_migration_jobs_by_range_at_intervals(ContainerExpirationPolicy, 'FooJob', 10.minutes, batch_size: 2, primary_column_name: :project_id)
|
||||||
|
|
||||||
|
expect(final_delay.to_f).to eq(20.minutes.to_f)
|
||||||
|
expect(BackgroundMigrationWorker.jobs[0]['args']).to eq(['FooJob', [id1, id2]])
|
||||||
|
expect(BackgroundMigrationWorker.jobs[0]['at']).to eq(10.minutes.from_now.to_f)
|
||||||
|
expect(BackgroundMigrationWorker.jobs[1]['args']).to eq(['FooJob', [id3, id3]])
|
||||||
|
expect(BackgroundMigrationWorker.jobs[1]['at']).to eq(20.minutes.from_now.to_f)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when the primary_column_name is not an integer" do
|
||||||
|
it 'raises error' do
|
||||||
|
expect do
|
||||||
|
model.queue_background_migration_jobs_by_range_at_intervals(ContainerExpirationPolicy, 'FooJob', 10.minutes, primary_column_name: :enabled)
|
||||||
|
end.to raise_error(StandardError, /is not an integer column/)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when the primary_column_name does not exist" do
|
||||||
|
it 'raises error' do
|
||||||
|
expect do
|
||||||
|
model.queue_background_migration_jobs_by_range_at_intervals(ContainerExpirationPolicy, 'FooJob', 10.minutes, primary_column_name: :foo)
|
||||||
|
end.to raise_error(StandardError, /does not have an ID column of foo/)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when the model doesn't have an ID or primary_column_name column" do
|
||||||
it 'raises error (for now)' do
|
it 'raises error (for now)' do
|
||||||
expect do
|
expect do
|
||||||
model.queue_background_migration_jobs_by_range_at_intervals(ProjectAuthorization, 'FooJob', 10.seconds)
|
model.queue_background_migration_jobs_by_range_at_intervals(ProjectAuthorization, 'FooJob', 10.seconds)
|
||||||
|
|
|
@ -236,7 +236,7 @@ RSpec.describe Releases::CreateService do
|
||||||
let(:released_at) { 3.weeks.ago }
|
let(:released_at) { 3.weeks.ago }
|
||||||
|
|
||||||
it 'does not execute CreateEvidenceWorker' do
|
it 'does not execute CreateEvidenceWorker' do
|
||||||
expect { subject }.not_to change(CreateEvidenceWorker.jobs, :size)
|
expect { subject }.not_to change(Releases::CreateEvidenceWorker.jobs, :size)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does not create an Evidence object', :sidekiq_inline do
|
it 'does not create an Evidence object', :sidekiq_inline do
|
||||||
|
@ -335,7 +335,7 @@ RSpec.describe Releases::CreateService do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'queues CreateEvidenceWorker' do
|
it 'queues CreateEvidenceWorker' do
|
||||||
expect { subject }.to change(CreateEvidenceWorker.jobs, :size).by(1)
|
expect { subject }.to change(Releases::CreateEvidenceWorker.jobs, :size).by(1)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'creates Evidence', :sidekiq_inline do
|
it 'creates Evidence', :sidekiq_inline do
|
||||||
|
@ -360,18 +360,12 @@ RSpec.describe Releases::CreateService do
|
||||||
context 'upcoming release' do
|
context 'upcoming release' do
|
||||||
let(:released_at) { 1.day.from_now }
|
let(:released_at) { 1.day.from_now }
|
||||||
|
|
||||||
it 'queues CreateEvidenceWorker' do
|
it 'does not execute CreateEvidenceWorker' do
|
||||||
expect { subject }.to change(CreateEvidenceWorker.jobs, :size).by(1)
|
expect { subject }.not_to change(Releases::CreateEvidenceWorker.jobs, :size)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'queues CreateEvidenceWorker at the released_at timestamp' do
|
it 'does not create an Evidence object', :sidekiq_inline do
|
||||||
subject
|
expect { subject }.not_to change(Releases::Evidence, :count)
|
||||||
|
|
||||||
expect(CreateEvidenceWorker.jobs.last['at'].to_i).to eq(released_at.to_i)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'creates Evidence', :sidekiq_inline do
|
|
||||||
expect { subject }.to change(Releases::Evidence, :count).by(1)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'is not a historical release' do
|
it 'is not a historical release' do
|
||||||
|
@ -385,8 +379,6 @@ RSpec.describe Releases::CreateService do
|
||||||
|
|
||||||
expect(last_release.upcoming_release?).to be_truthy
|
expect(last_release.upcoming_release?).to be_truthy
|
||||||
end
|
end
|
||||||
|
|
||||||
include_examples 'uses the right pipeline for evidence'
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
RSpec.describe CreateEvidenceWorker do
|
RSpec.describe Releases::CreateEvidenceWorker do
|
||||||
let(:project) { create(:project, :repository) }
|
let(:project) { create(:project, :repository) }
|
||||||
let(:release) { create(:release, project: project) }
|
let(:release) { create(:release, project: project) }
|
||||||
let(:pipeline) { create(:ci_empty_pipeline, sha: release.sha, project: project) }
|
let(:pipeline) { create(:ci_empty_pipeline, sha: release.sha, project: project) }
|
|
@ -0,0 +1,43 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
RSpec.describe Releases::ManageEvidenceWorker do
|
||||||
|
let(:project) { create(:project, :repository) }
|
||||||
|
|
||||||
|
shared_examples_for 'does not create a new Evidence record' do
|
||||||
|
specify :sidekiq_inline do
|
||||||
|
aggregate_failures do
|
||||||
|
expect(::Releases::CreateEvidenceService).not_to receive(:execute)
|
||||||
|
expect { described_class.new.perform }.to change(Releases::Evidence, :count).by(0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when `released_at` in inside the window' do
|
||||||
|
context 'when Evidence has not been created' do
|
||||||
|
let(:release) { create(:release, project: project, released_at: 1.hour.since) }
|
||||||
|
|
||||||
|
it 'creates a new Evidence record', :sidekiq_inline do
|
||||||
|
expect_next_instance_of(::Releases::CreateEvidenceService, release, { pipeline: nil }) do |service|
|
||||||
|
expect(service).to receive(:execute).and_call_original
|
||||||
|
end
|
||||||
|
|
||||||
|
expect { described_class.new.perform }.to change(Releases::Evidence, :count).by(1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when evidence has already been created' do
|
||||||
|
let(:release) { create(:release, project: project, released_at: 1.hour.since) }
|
||||||
|
let!(:evidence) { create(:evidence, release: release )}
|
||||||
|
|
||||||
|
it_behaves_like 'does not create a new Evidence record'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when `released_at` is outside the window' do
|
||||||
|
let(:release) { create(:release, project: project, released_at: 300.minutes.since) }
|
||||||
|
|
||||||
|
it_behaves_like 'does not create a new Evidence record'
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue