diff --git a/.gitlab/ci/docs.gitlab-ci.yml b/.gitlab/ci/docs.gitlab-ci.yml index f4d8698f22d..3e3d994c70b 100644 --- a/.gitlab/ci/docs.gitlab-ci.yml +++ b/.gitlab/ci/docs.gitlab-ci.yml @@ -75,3 +75,16 @@ ui-docs-links lint: needs: [] script: - bundle exec haml-lint -i DocumentationLinks + +deprecations-doc check: + variables: + SETUP_DB: "false" + extends: + - .default-retry + - .rails-cache + - .default-before_script + - .docs:rules:deprecations + stage: test + needs: [] + script: + - bundle exec rake gitlab:docs:check_deprecations diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml index a1ec3ea2be4..1f552d1dace 100644 --- a/.gitlab/ci/rules.gitlab-ci.yml +++ b/.gitlab/ci/rules.gitlab-ci.yml @@ -147,6 +147,13 @@ - ".markdownlint.yml" - "scripts/lint-doc.sh" +.docs-deprecations-patterns: &docs-deprecations-patterns + - "doc/deprecations/index.md" + - "data/deprecations/*.yml" + - "data/deprecations/templates/_deprecation_template.md.erb" + - "lib/tasks/gitlab/docs/compile_deprecations.rake" + - "tooling/deprecations/docs.rb" + .bundler-patterns: &bundler-patterns - '{Gemfile.lock,*/Gemfile.lock,*/*/Gemfile.lock}' @@ -453,6 +460,12 @@ changes: *docs-patterns when: on_success +.docs:rules:deprecations: + rules: + - <<: *if-default-refs + changes: *docs-deprecations-patterns + when: on_success + ################## # GraphQL rules # ################## diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index 71cec48aa4e..ab2b40b4115 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -308,6 +308,7 @@ module Ci scope :ci_and_parent_sources, -> { where(source: Enums::Ci::Pipeline.ci_and_parent_sources.values) } scope :for_user, -> (user) { where(user: user) } scope :for_sha, -> (sha) { where(sha: sha) } + scope :where_not_sha, -> (sha) { where.not(sha: sha) } scope :for_source_sha, -> (source_sha) { where(source_sha: source_sha) } scope :for_sha_or_source_sha, -> (sha) { for_sha(sha).or(for_source_sha(sha)) } scope :for_ref, -> (ref) { where(ref: ref) } diff --git a/app/models/ci/sources/pipeline.rb b/app/models/ci/sources/pipeline.rb index f78caf710a6..95842d944f9 100644 --- a/app/models/ci/sources/pipeline.rb +++ b/app/models/ci/sources/pipeline.rb @@ -4,6 +4,9 @@ module Ci module Sources class Pipeline < Ci::ApplicationRecord include Ci::NamespacedModelName + include IgnorableColumns + + ignore_columns 'source_job_id_convert_to_bigint', remove_with: '14.5', remove_after: '2021-11-22' self.table_name = "ci_sources_pipelines" diff --git a/app/services/feature_flags/base_service.rb b/app/services/feature_flags/base_service.rb index f1d19ff469d..9ae9ab4de63 100644 --- a/app/services/feature_flags/base_service.rb +++ b/app/services/feature_flags/base_service.rb @@ -52,8 +52,7 @@ module FeatureFlags scopes = strategy.scopes .map { |scope| %Q("#{scope.environment_scope}") } .join(', ') - %Q(Created strategy \"#{strategy.name}\" )\ - "with scopes #{scopes}." + %Q(Created strategy "#{strategy.name}" with scopes #{scopes}.) end def feature_flag_by_name diff --git a/data/deprecations/templates/_deprecation_template.md.erb b/data/deprecations/templates/_deprecation_template.md.erb index 20bccb88ce0..1a77689080e 100644 --- a/data/deprecations/templates/_deprecation_template.md.erb +++ b/data/deprecations/templates/_deprecation_template.md.erb @@ -2,10 +2,9 @@ stage: none group: none info: "See the Technical Writers assigned to Development Guidelines: https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments-to-development-guidelines" -description: "View features that are currently deprecated" --- -# Deprecated features by planned removal milestone +# Deprecated feature removal schedule +<% if milestones.any? %> + <%- milestones.each do |milestone| %> +## <%= milestone %> + <%- deprecations.select{|d| d["removal_milestone"] == milestone}.each do |deprecation| %> +### <%= deprecation["name"]%> -<% if milestones.any? -%> - <% milestones.each do |milestone| %> -### <%= milestone %> - <% deprecations.select{|d| d["removal_milestone"] == milestone}.each do |deprecation| %> -#### <%= deprecation["name"] %> -<%= deprecation["body"]%> - <% end %> - <% end %> -<% else -%> -## There are no deprecated features for this version of GitLab +<%= deprecation["body"] -%> + <%- end -%> + <%- end -%> +<%- else -%> + +Deprecated features scheduled for removal will be listed here, sorted by GitLab milestone. <% end -%> diff --git a/data/deprecations/templates/example.yml b/data/deprecations/templates/example.yml index 2ae52cad568..2bd92cc4b28 100644 --- a/data/deprecations/templates/example.yml +++ b/data/deprecations/templates/example.yml @@ -10,8 +10,8 @@ # # Please delete this line and above before submitting your merge request. -- name: # The name of the feature to be deprecated - removal_milestone: # XX.YY format - the milestone when this feature is planned to be removed +- name: "Feature name" # The name of the feature to be deprecated + removal_milestone: "XX.YY" # the milestone when this feature is planned to be removed body: | # Do not modify this line, instead modify the lines below. +``` + ### Emphasis - Use double asterisks (`**`) to mark a word or text in bold (`**bold**`). diff --git a/doc/topics/gitlab_flow.md b/doc/topics/gitlab_flow.md index e1d4f59aa81..79ea172f194 100644 --- a/doc/topics/gitlab_flow.md +++ b/doc/topics/gitlab_flow.md @@ -16,9 +16,16 @@ It combines [feature-driven development](https://en.wikipedia.org/wiki/Feature-d Organizations coming to Git from other version control systems frequently find it hard to develop a productive workflow. This article describes GitLab flow, which integrates the Git workflow with an issue tracking system. -It offers a transparent and effective way to work with Git. +It offers a transparent and effective way to work with Git: -![Four stages (working copy, index, local repository, remote repository) and three steps between them](img/gitlab_flow_four_stages.png) +```mermaid +graph LR + subgraph Git workflow + A[Working copy] --> |git add| B[Index] + B --> |git commit| C[Local repository] + C --> |git push| D[Remote repository] + end +``` When converting to Git, you have to get used to the fact that it takes three steps to share a commit with colleagues. Most version control systems have only one step: committing from the working copy to a shared server. diff --git a/doc/topics/img/gitlab_flow_four_stages.png b/doc/topics/img/gitlab_flow_four_stages.png deleted file mode 100644 index 3ef6a33d2d4..00000000000 Binary files a/doc/topics/img/gitlab_flow_four_stages.png and /dev/null differ diff --git a/doc/deprecations/index.md b/doc/update/deprecations.md similarity index 76% rename from doc/deprecations/index.md rename to doc/update/deprecations.md index e35d92a8feb..ecfc6e16071 100644 --- a/doc/deprecations/index.md +++ b/doc/update/deprecations.md @@ -2,10 +2,9 @@ stage: none group: none info: "See the Technical Writers assigned to Development Guidelines: https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments-to-development-guidelines" -description: "View features that are currently deprecated" --- -# Deprecated features by planned removal milestone +# Deprecated feature removal schedule -## There are no deprecated features for this version of GitLab +Deprecated features scheduled for removal will be listed here, sorted by GitLab milestone. diff --git a/lib/gitlab/background_migration/extract_project_topics_into_separate_table.rb b/lib/gitlab/background_migration/extract_project_topics_into_separate_table.rb index 15ce038321c..c5beb5a6865 100644 --- a/lib/gitlab/background_migration/extract_project_topics_into_separate_table.rb +++ b/lib/gitlab/background_migration/extract_project_topics_into_separate_table.rb @@ -26,12 +26,21 @@ module Gitlab belongs_to :topic end + # Temporary AR table for projects + class Project < ActiveRecord::Base + self.table_name = 'projects' + end + def perform(start_id, stop_id) Tagging.includes(:tag).where(taggable_type: 'Project', id: start_id..stop_id).each do |tagging| - topic = Topic.find_or_create_by(name: tagging.tag.name) - project_topic = ProjectTopic.find_or_create_by(project_id: tagging.taggable_id, topic: topic) + if Project.exists?(id: tagging.taggable_id) + topic = Topic.find_or_create_by(name: tagging.tag.name) + project_topic = ProjectTopic.find_or_create_by(project_id: tagging.taggable_id, topic: topic) - tagging.delete if project_topic.persisted? + tagging.delete if project_topic.persisted? + else + tagging.delete + end end mark_job_as_succeeded(start_id, stop_id) diff --git a/lib/gitlab/background_migration/migrate_merge_request_diff_commit_users.rb b/lib/gitlab/background_migration/migrate_merge_request_diff_commit_users.rb index b51fc07f352..7d150b9cd83 100644 --- a/lib/gitlab/background_migration/migrate_merge_request_diff_commit_users.rb +++ b/lib/gitlab/background_migration/migrate_merge_request_diff_commit_users.rb @@ -14,7 +14,7 @@ module Gitlab # The number of rows in merge_request_diff_commits to get in a single # query. - COMMIT_ROWS_PER_QUERY = 10_000 + COMMIT_ROWS_PER_QUERY = 1_000 # The number of rows in merge_request_diff_commits to update in a single # query. diff --git a/lib/gitlab/background_migration/steal_migrate_merge_request_diff_commit_users.rb b/lib/gitlab/background_migration/steal_migrate_merge_request_diff_commit_users.rb index f7eaf01bdc4..43a7032e682 100644 --- a/lib/gitlab/background_migration/steal_migrate_merge_request_diff_commit_users.rb +++ b/lib/gitlab/background_migration/steal_migrate_merge_request_diff_commit_users.rb @@ -15,13 +15,10 @@ module Gitlab end def schedule_next_job - # We process jobs in reverse order, so that (hopefully) we are less - # likely to process jobs that the regular background migration job is - # also processing. next_job = Database::BackgroundMigrationJob .for_migration_class('MigrateMergeRequestDiffCommitUsers') .pending - .last + .first return unless next_job diff --git a/lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines.rb b/lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines.rb index 1c0dfbdbee3..f637001f9f8 100644 --- a/lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines.rb +++ b/lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines.rb @@ -7,15 +7,19 @@ module Gitlab class CancelPendingPipelines < Chain::Base include Chain::Helpers + BATCH_SIZE = 25 + + # rubocop: disable CodeReuse/ActiveRecord def perform! return unless project.auto_cancel_pending_pipelines? Gitlab::OptimisticLocking.retry_lock(auto_cancelable_pipelines, name: 'cancel_pending_pipelines') do |cancelables| - cancelables.find_each do |cancelable| - cancelable.auto_cancel_running(pipeline) + cancelables.select(:id).each_batch(of: BATCH_SIZE) do |cancelables_batch| + auto_cancel_interruptible_pipelines(cancelables_batch.ids) end end end + # rubocop: enable CodeReuse/ActiveRecord def break? false @@ -23,16 +27,21 @@ module Gitlab private - # rubocop: disable CodeReuse/ActiveRecord def auto_cancelable_pipelines - project.all_pipelines.ci_and_parent_sources - .where(ref: pipeline.ref) - .where.not(id: pipeline.same_family_pipeline_ids) - .where.not(sha: project.commit(pipeline.ref).try(:id)) + project.all_pipelines.created_after(1.week.ago) + .ci_and_parent_sources + .for_ref(pipeline.ref) + .id_not_in(pipeline.same_family_pipeline_ids) + .where_not_sha(project.commit(pipeline.ref).try(:id)) .alive_or_scheduled - .with_only_interruptible_builds end - # rubocop: enable CodeReuse/ActiveRecord + + def auto_cancel_interruptible_pipelines(pipeline_ids) + ::Ci::Pipeline + .id_in(pipeline_ids) + .with_only_interruptible_builds + .each { |cancelable| cancelable.auto_cancel_running(pipeline) } + end end end end diff --git a/lib/tasks/gitlab/docs/compile_deprecations.rake b/lib/tasks/gitlab/docs/compile_deprecations.rake index 6d6602c9074..0fd43775015 100644 --- a/lib/tasks/gitlab/docs/compile_deprecations.rake +++ b/lib/tasks/gitlab/docs/compile_deprecations.rake @@ -4,28 +4,26 @@ namespace :gitlab do namespace :docs do desc "Generate deprecation list from individual files" task :compile_deprecations do - require_relative '../../../../tooling/deprecations/docs/renderer' + require_relative '../../../../tooling/deprecations/docs' - source_files = Rake::FileList.new("data/deprecations/**/*.yml") do |fl| - fl.exclude(/example\.yml/) + File.write(Deprecations::Docs.path, Deprecations::Docs.render) + + puts "Deprecations compiled to #{Deprecations::Docs.path}" + end + + desc "Check that the deprecation doc is up to date" + task :check_deprecations do + require_relative '../../../../tooling/deprecations/docs' + + contents = Deprecations::Docs.render + doc = File.read(Deprecations::Docs.path) + + if doc == contents + puts "Deprecations doc is up to date." + else + format_output('Deprecations doc is outdated! Please update it by running `bundle exec rake gitlab:docs:compile_deprecations`.') + abort end - - deprecations = source_files.map do |file| - YAML.load_file(file) - end - - deprecations.sort_by! { |d| -d["removal_milestone"].to_f } - - milestones = deprecations.map { |d| d["removal_milestone"].to_f }.uniq - - contents = Deprecations::Docs::Renderer - .render(deprecations: deprecations, milestones: milestones) - - File.write( - File.expand_path("doc/deprecations/index.md", "#{__dir__}/../../../.."), - contents) - - puts "Deprecations compiled to doc/deprecations/index.md" end end end diff --git a/qa/qa/page/dashboard/snippet/index.rb b/qa/qa/page/dashboard/snippet/index.rb index d8314509b1f..088fff17578 100644 --- a/qa/qa/page/dashboard/snippet/index.rb +++ b/qa/qa/page/dashboard/snippet/index.rb @@ -35,8 +35,10 @@ module QA end def has_number_of_files?(snippet_title, number) - within_element(:snippet_link, snippet_title: snippet_title) do - has_element?(:snippet_file_count_content, snippet_files: number) + retry_until(max_attempts: 5, reload: true, sleep_interval: 1) do # snippet statistics computation can take a few moments + within_element(:snippet_link, snippet_title: snippet_title) do + has_element?(:snippet_file_count_content, snippet_files: number, wait: 5) + end end end end diff --git a/qa/qa/page/merge_request/show.rb b/qa/qa/page/merge_request/show.rb index afe88fc0cdc..baf9d774bda 100644 --- a/qa/qa/page/merge_request/show.rb +++ b/qa/qa/page/merge_request/show.rb @@ -287,6 +287,17 @@ module QA raise "Rebase did not appear to be successful" unless success end + def merge_immediately! + merge_moment_dropdown_found = has_element?(:merge_moment_dropdown, wait: 0) + + if merge_moment_dropdown_found + click_element(:merge_moment_dropdown) + click_element(:merge_immediately_menu_item) + else + click_element(:merge_button) + end + end + def try_to_merge! # Revisit after merge page re-architect is done https://gitlab.com/gitlab-org/gitlab/-/issues/300042 # To remove page refresh logic if possible diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/protected_tags_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/protected_tags_spec.rb index 714c4a2da67..f688136ce3c 100644 --- a/qa/qa/specs/features/browser_ui/1_manage/project/protected_tags_spec.rb +++ b/qa/qa/specs/features/browser_ui/1_manage/project/protected_tags_spec.rb @@ -58,7 +58,7 @@ module QA it_behaves_like 'successful tag creation', :maintainer_user end - context 'when protected' do + context 'when protected', quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/339727', type: :bug } do before do add_members_to_project(project) diff --git a/spec/lib/gitlab/background_migration/extract_project_topics_into_separate_table_spec.rb b/spec/lib/gitlab/background_migration/extract_project_topics_into_separate_table_spec.rb index d8f8fda3e57..05f40ee2501 100644 --- a/spec/lib/gitlab/background_migration/extract_project_topics_into_separate_table_spec.rb +++ b/spec/lib/gitlab/background_migration/extract_project_topics_into_separate_table_spec.rb @@ -21,14 +21,16 @@ RSpec.describe Gitlab::BackgroundMigration::ExtractProjectTopicsIntoSeparateTabl tagging_2 = taggings.create!(taggable_type: 'Project', taggable_id: project.id, context: 'topics', tag_id: tag_2.id) other_tagging = taggings.create!(taggable_type: 'Other', taggable_id: project.id, context: 'topics', tag_id: tag_1.id) tagging_3 = taggings.create!(taggable_type: 'Project', taggable_id: project.id, context: 'topics', tag_id: tag_3.id) + tagging_4 = taggings.create!(taggable_type: 'Project', taggable_id: -1, context: 'topics', tag_id: tag_1.id) - subject.perform(tagging_1.id, tagging_3.id) + subject.perform(tagging_1.id, tagging_4.id) # Tagging records expect { tagging_1.reload }.to raise_error(ActiveRecord::RecordNotFound) expect { tagging_2.reload }.to raise_error(ActiveRecord::RecordNotFound) expect { other_tagging.reload }.not_to raise_error(ActiveRecord::RecordNotFound) expect { tagging_3.reload }.to raise_error(ActiveRecord::RecordNotFound) + expect { tagging_4.reload }.to raise_error(ActiveRecord::RecordNotFound) # Topic records topic_1 = topics.find_by(name: 'Topic1') diff --git a/spec/lib/gitlab/background_migration/steal_migrate_merge_request_diff_commit_users_spec.rb b/spec/lib/gitlab/background_migration/steal_migrate_merge_request_diff_commit_users_spec.rb index baa02a7f318..f2fb2ab6b6e 100644 --- a/spec/lib/gitlab/background_migration/steal_migrate_merge_request_diff_commit_users_spec.rb +++ b/spec/lib/gitlab/background_migration/steal_migrate_merge_request_diff_commit_users_spec.rb @@ -23,7 +23,7 @@ RSpec.describe Gitlab::BackgroundMigration::StealMigrateMergeRequestDiffCommitUs end describe '#schedule_next_job' do - it 'schedules the next job in reverse order' do + it 'schedules the next job in ascending order' do Gitlab::Database::BackgroundMigrationJob.create!( class_name: 'MigrateMergeRequestDiffCommitUsers', arguments: [10, 20] @@ -36,7 +36,7 @@ RSpec.describe Gitlab::BackgroundMigration::StealMigrateMergeRequestDiffCommitUs expect(BackgroundMigrationWorker) .to receive(:perform_in) - .with(5.minutes, 'StealMigrateMergeRequestDiffCommitUsers', [40, 50]) + .with(5.minutes, 'StealMigrateMergeRequestDiffCommitUsers', [10, 20]) migration.schedule_next_job end diff --git a/spec/lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines_spec.rb index 2727f2603cd..27a5abf988c 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines_spec.rb @@ -44,6 +44,14 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::CancelPendingPipelines do expect(build_statuses(pipeline)).to contain_exactly('pending') end + it 'cancels the builds with 2 queries to avoid query timeout' do + second_query_regex = /WHERE "ci_pipelines"\."id" = \d+ AND \(NOT EXISTS/ + recorder = ActiveRecord::QueryRecorder.new { perform } + second_query = recorder.occurrences.keys.filter { |occ| occ =~ second_query_regex } + + expect(second_query).to be_one + end + context 'when the previous pipeline has a child pipeline' do let(:child_pipeline) { create(:ci_pipeline, child_of: prev_pipeline) } diff --git a/spec/migrations/remove_duplicate_dast_site_tokens_with_same_token_spec.rb b/spec/migrations/remove_duplicate_dast_site_tokens_with_same_token_spec.rb new file mode 100644 index 00000000000..57d677af5cf --- /dev/null +++ b/spec/migrations/remove_duplicate_dast_site_tokens_with_same_token_spec.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +require 'spec_helper' + +require_migration! + +RSpec.describe RemoveDuplicateDastSiteTokensWithSameToken do + let(:namespaces) { table(:namespaces) } + let(:projects) { table(:projects) } + let(:dast_site_tokens) { table(:dast_site_tokens) } + let!(:namespace) { namespaces.create!(id: 1, name: 'group', path: 'group') } + let!(:project1) { projects.create!(id: 1, namespace_id: namespace.id, path: 'project1') } + # create non duplicate dast site token + let!(:dast_site_token1) { dast_site_tokens.create!(project_id: project1.id, url: 'https://gitlab.com', token: SecureRandom.uuid) } + + context 'when duplicate dast site tokens exists' do + # create duplicate dast site token + let_it_be(:duplicate_token) { 'duplicate_token' } + let_it_be(:other_duplicate_token) { 'other_duplicate_token' } + + let!(:project2) { projects.create!(id: 2, namespace_id: namespace.id, path: 'project2') } + let!(:dast_site_token2) { dast_site_tokens.create!(project_id: project2.id, url: 'https://gitlab2.com', token: duplicate_token) } + let!(:dast_site_token3) { dast_site_tokens.create!(project_id: project2.id, url: 'https://gitlab3.com', token: duplicate_token) } + let!(:dast_site_token4) { dast_site_tokens.create!(project_id: project2.id, url: 'https://gitlab4.com', token: duplicate_token) } + + let!(:project3) { projects.create!(id: 3, namespace_id: namespace.id, path: 'project3') } + let!(:dast_site_token5) { dast_site_tokens.create!(project_id: project3.id, url: 'https://gitlab2.com', token: other_duplicate_token) } + let!(:dast_site_token6) { dast_site_tokens.create!(project_id: project3.id, url: 'https://gitlab3.com', token: other_duplicate_token) } + let!(:dast_site_token7) { dast_site_tokens.create!(project_id: project3.id, url: 'https://gitlab4.com', token: other_duplicate_token) } + + describe 'migration up' do + it 'does remove duplicated dast site tokens with the same token' do + expect(dast_site_tokens.count).to eq(7) + expect(dast_site_tokens.where(token: duplicate_token).size).to eq(3) + + migrate! + + expect(dast_site_tokens.count).to eq(3) + expect(dast_site_tokens.where(token: duplicate_token).size).to eq(1) + end + end + end + + context 'when duplicate dast site tokens do not exist' do + let!(:dast_site_token5) { dast_site_tokens.create!(project_id: 1, url: 'https://gitlab5.com', token: SecureRandom.uuid) } + + describe 'migration up' do + it 'does not remove any dast site tokens' do + expect { migrate! }.not_to change(dast_site_tokens, :count) + end + end + end +end diff --git a/spec/migrations/slice_merge_request_diff_commit_migrations_spec.rb b/spec/migrations/slice_merge_request_diff_commit_migrations_spec.rb new file mode 100644 index 00000000000..1fd19ee42b4 --- /dev/null +++ b/spec/migrations/slice_merge_request_diff_commit_migrations_spec.rb @@ -0,0 +1,69 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_migration! 'slice_merge_request_diff_commit_migrations' + +RSpec.describe SliceMergeRequestDiffCommitMigrations, :migration do + let(:migration) { described_class.new } + + describe '#up' do + context 'when there are no jobs to process' do + it 'does nothing' do + expect(migration).not_to receive(:migrate_in) + expect(Gitlab::Database::BackgroundMigrationJob).not_to receive(:create!) + + migration.up + end + end + + context 'when there are pending jobs' do + let!(:job1) do + Gitlab::Database::BackgroundMigrationJob.create!( + class_name: described_class::MIGRATION_CLASS, + arguments: [1, 10_001] + ) + end + + let!(:job2) do + Gitlab::Database::BackgroundMigrationJob.create!( + class_name: described_class::MIGRATION_CLASS, + arguments: [10_001, 20_001] + ) + end + + it 'marks the old jobs as finished' do + migration.up + + job1.reload + job2.reload + + expect(job1).to be_succeeded + expect(job2).to be_succeeded + end + + it 'the jobs are slices into smaller ranges' do + migration.up + + new_jobs = Gitlab::Database::BackgroundMigrationJob + .for_migration_class(described_class::MIGRATION_CLASS) + .pending + .to_a + + expect(new_jobs.map(&:arguments)).to eq([ + [1, 5_001], + [5_001, 10_001], + [10_001, 15_001], + [15_001, 20_001] + ]) + end + + it 'schedules a background migration for the first job' do + expect(migration) + .to receive(:migrate_in) + .with(1.hour, described_class::STEAL_MIGRATION_CLASS, [1, 5_001]) + + migration.up + end + end + end +end diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index 597d532f8f1..b3d0b6af926 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -183,6 +183,28 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do end end + describe '.where_not_sha' do + let_it_be(:pipeline) { create(:ci_pipeline, sha: 'abcx') } + let_it_be(:pipeline_2) { create(:ci_pipeline, sha: 'abc') } + + let(:sha) { 'abc' } + + subject { described_class.where_not_sha(sha) } + + it 'returns the pipeline without the specified sha' do + is_expected.to contain_exactly(pipeline) + end + + context 'when argument is array' do + let(:sha) { %w[abc abcx] } + + it 'returns the pipelines without the specified shas' do + pipeline_3 = create(:ci_pipeline, sha: 'abcy') + is_expected.to contain_exactly(pipeline_3) + end + end + end + describe '.for_source_sha' do subject { described_class.for_source_sha(source_sha) } diff --git a/spec/services/feature_flags/create_service_spec.rb b/spec/services/feature_flags/create_service_spec.rb index f239f60b385..5a517ce6a64 100644 --- a/spec/services/feature_flags/create_service_spec.rb +++ b/spec/services/feature_flags/create_service_spec.rb @@ -69,15 +69,10 @@ RSpec.describe FeatureFlags::CreateService do end it 'creates audit event' do - expected_message = 'Created feature flag feature_flag '\ - 'with description "description". '\ - 'Created strategy "default" with scopes '\ - '"*". '\ - 'Created strategy "default" with scopes '\ - '"production".' - expect { subject }.to change { AuditEvent.count }.by(1) - expect(AuditEvent.last.details[:custom_message]).to eq(expected_message) + expect(AuditEvent.last.details[:custom_message]).to start_with('Created feature flag feature_flag with description "description".') + expect(AuditEvent.last.details[:custom_message]).to include('Created strategy "default" with scopes "*".') + expect(AuditEvent.last.details[:custom_message]).to include('Created strategy "default" with scopes "production".') end context 'when user is reporter' do diff --git a/tooling/deprecations/docs.rb b/tooling/deprecations/docs.rb new file mode 100644 index 00000000000..adc3e0edb10 --- /dev/null +++ b/tooling/deprecations/docs.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true +require 'erb' + +module Deprecations + module Docs + module_function + + def path + Rails.root.join("doc/update/deprecations.md") + end + + def render + deprecations_yaml_glob = Rails.root.join("data/deprecations/**/*.yml") + + source_files = Rake::FileList.new(deprecations_yaml_glob) do |fl| + fl.exclude(/example\.yml$/) + end + + deprecations = source_files.flat_map do |file| + YAML.load_file(file) + end + + deprecations = VersionSorter.rsort(deprecations) { |d| d["removal_milestone"] } + + milestones = deprecations.map { |d| d["removal_milestone"] }.uniq + + template = Rails.root.join("data/deprecations/templates/_deprecation_template.md.erb") + + load_template(template) + .result_with_hash(deprecations: deprecations, milestones: milestones) + end + + def load_template(filename) + ERB.new(File.read(filename), trim_mode: '-') + end + end +end diff --git a/tooling/deprecations/docs/renderer.rb b/tooling/deprecations/docs/renderer.rb deleted file mode 100644 index 832c4ce99d1..00000000000 --- a/tooling/deprecations/docs/renderer.rb +++ /dev/null @@ -1,20 +0,0 @@ -# frozen_string_literal: true -require 'erb' - -module Deprecations - module Docs - module Renderer - module_function - - def render(**variables) - template = File.expand_path("data/deprecations/templates/_deprecation_template.md.erb", "#{__dir__}/../../..") - - load_template(template).result_with_hash(variables) - end - - def load_template(filename) - ERB.new(File.read(filename), trim_mode: '-') - end - end - end -end