diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-04-27 21:10:10 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-04-27 21:10:10 +0000 |
commit | 579e85eb029c4ee66e8b8cd537a94b9e6cb0e58b (patch) | |
tree | 26c28e966b7f2e4fabf06bfaa1d650ac4579f9e4 | |
parent | f569792df8a25caa1bed9c448c8c4c3f837f5164 (diff) | |
download | gitlab-ce-579e85eb029c4ee66e8b8cd537a94b9e6cb0e58b.tar.gz |
Add latest changes from gitlab-org/gitlab@master
30 files changed, 238 insertions, 61 deletions
diff --git a/.gitlab/CODEOWNERS b/.gitlab/CODEOWNERS index a9c23764908..dc09d0499ff 100644 --- a/.gitlab/CODEOWNERS +++ b/.gitlab/CODEOWNERS @@ -10,7 +10,7 @@ *.md @gl-docsteam /doc/ @gl-docsteam # Dev and Doc guidelines -/doc/development/ @marcia +/doc/development/ @marcia @mjang1 /doc/development/documentation/ @mikelewis # Frontend maintainers should see everything in `app/assets/` @@ -166,7 +166,7 @@ gem 'diff_match_patch', '~> 0.1.0' gem 'rack', '~> 2.0.9' group :unicorn do - gem 'unicorn', '~> 5.4.1' + gem 'unicorn', '~> 5.5' gem 'unicorn-worker-killer', '~> 0.4.4' end diff --git a/Gemfile.lock b/Gemfile.lock index 9f60cdcd4ad..3d6472a284d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -589,7 +589,7 @@ GEM activerecord kaminari-core (= 1.0.1) kaminari-core (1.0.1) - kgio (2.11.2) + kgio (2.11.3) knapsack (1.17.0) rake kramdown (2.1.0) @@ -1108,7 +1108,7 @@ GEM unicode_plot (0.0.4) enumerable-statistics (>= 2.0.1) unicode_utils (1.4.0) - unicorn (5.4.1) + unicorn (5.5.5) kgio (~> 2.6) raindrops (~> 0.7) unicorn-worker-killer (0.4.4) @@ -1410,7 +1410,7 @@ DEPENDENCIES u2f (~> 0.2.1) uglifier (~> 2.7.2) unf (~> 0.1.4) - unicorn (~> 5.4.1) + unicorn (~> 5.5) unicorn-worker-killer (~> 0.4.4) unleash (~> 0.1.5) validates_hostname (~> 1.0.6) diff --git a/app/assets/javascripts/diffs/components/app.vue b/app/assets/javascripts/diffs/components/app.vue index 072bcaaad97..98c660077bb 100644 --- a/app/assets/javascripts/diffs/components/app.vue +++ b/app/assets/javascripts/diffs/components/app.vue @@ -7,6 +7,7 @@ import createFlash from '~/flash'; import PanelResizer from '~/vue_shared/components/panel_resizer.vue'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import { isSingleViewStyle } from '~/helpers/diffs_helper'; +import { updateHistory } from '~/lib/utils/url_utility'; import eventHub from '../../notes/event_hub'; import CompareVersions from './compare_versions.vue'; import DiffFile from './diff_file.vue'; @@ -140,6 +141,20 @@ export default { }, }, watch: { + commit(newCommit, oldCommit) { + const commitChangedAfterRender = newCommit && !this.isLoading; + const commitIsDifferent = oldCommit && newCommit.id !== oldCommit.id; + const url = window?.location ? String(window.location) : ''; + + if (commitChangedAfterRender && commitIsDifferent) { + updateHistory({ + title: document.title, + url: url.replace(oldCommit.id, newCommit.id), + }); + this.refetchDiffData(); + this.adjustView(); + } + }, diffViewType() { if (this.needsReload() || this.needsFirstLoad()) { this.refetchDiffData(); diff --git a/app/models/ci/job_artifact.rb b/app/models/ci/job_artifact.rb index 96bfe7f93b7..3e06fa0957c 100644 --- a/app/models/ci/job_artifact.rb +++ b/app/models/ci/job_artifact.rb @@ -70,6 +70,10 @@ module Ci TYPE_AND_FORMAT_PAIRS = INTERNAL_TYPES.merge(REPORT_TYPES).freeze + # This is required since we cannot add a default to the database + # https://gitlab.com/gitlab-org/gitlab/-/issues/215418 + attribute :locked, :boolean, default: false + belongs_to :project belongs_to :job, class_name: "Ci::Build", foreign_key: :job_id @@ -86,6 +90,7 @@ module Ci scope :with_files_stored_locally, -> { where(file_store: [nil, ::JobArtifactUploader::Store::LOCAL]) } scope :with_files_stored_remotely, -> { where(file_store: ::JobArtifactUploader::Store::REMOTE) } scope :for_sha, ->(sha, project_id) { joins(job: :pipeline).where(ci_pipelines: { sha: sha, project_id: project_id }) } + scope :for_ref, ->(ref, project_id) { joins(job: :pipeline).where(ci_pipelines: { ref: ref, project_id: project_id }) } scope :for_job_name, ->(name) { joins(:job).where(ci_builds: { name: name }) } scope :with_file_types, -> (file_types) do @@ -121,6 +126,8 @@ module Ci end scope :expired, -> (limit) { where('expire_at < ?', Time.now).limit(limit) } + scope :locked, -> { where(locked: true) } + scope :unlocked, -> { where(locked: [false, nil]) } scope :scoped_project, -> { where('ci_job_artifacts.project_id = projects.id') } diff --git a/app/services/ci/create_job_artifacts_service.rb b/app/services/ci/create_job_artifacts_service.rb index 5d7d552dc5a..350751610e6 100644 --- a/app/services/ci/create_job_artifacts_service.rb +++ b/app/services/ci/create_job_artifacts_service.rb @@ -33,7 +33,8 @@ module Ci file_type: params['artifact_type'], file_format: params['artifact_format'], file_sha256: artifacts_file.sha256, - expire_in: expire_in) + expire_in: expire_in, + locked: true) artifact_metadata = if metadata_file Ci::JobArtifact.new( @@ -43,7 +44,8 @@ module Ci file_type: :metadata, file_format: :gzip, file_sha256: metadata_file.sha256, - expire_in: expire_in) + expire_in: expire_in, + locked: true) end [artifact, artifact_metadata] @@ -64,6 +66,7 @@ module Ci Ci::JobArtifact.transaction do artifact.save! artifact_metadata&.save! + unlock_previous_artifacts!(artifact) # NOTE: The `artifacts_expire_at` column is already deprecated and to be removed in the near future. job.update_column(:artifacts_expire_at, artifact.expire_at) @@ -81,6 +84,10 @@ module Ci error(error.message, :bad_request) end + def unlock_previous_artifacts!(artifact) + Ci::JobArtifact.for_ref(artifact.job.ref, artifact.project_id).locked.update_all(locked: false) + end + def sha256_matches_existing_artifact?(job, artifact_type, artifacts_file) existing_artifact = job.job_artifacts.find_by_file_type(artifact_type) return false unless existing_artifact diff --git a/app/services/ci/destroy_expired_job_artifacts_service.rb b/app/services/ci/destroy_expired_job_artifacts_service.rb index 7d2f5d33fed..181133ba63c 100644 --- a/app/services/ci/destroy_expired_job_artifacts_service.rb +++ b/app/services/ci/destroy_expired_job_artifacts_service.rb @@ -28,7 +28,7 @@ module Ci private def destroy_batch - artifacts = Ci::JobArtifact.expired(BATCH_SIZE).to_a + artifacts = Ci::JobArtifact.expired(BATCH_SIZE).unlocked.to_a return false if artifacts.empty? diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml index 80a14412968..49345b7b215 100644 --- a/app/views/layouts/_page.html.haml +++ b/app/views/layouts/_page.html.haml @@ -5,7 +5,7 @@ .mobile-overlay .alert-wrapper = render 'shared/outdated_browser' - - if Feature.enabled?(:subscribable_banner_license) + - if Feature.enabled?(:subscribable_banner_license, default_enabled: true) = render_if_exists "layouts/header/ee_subscribable_banner" = render "layouts/broadcast" = render "layouts/header/read_only_banner" diff --git a/app/views/layouts/devise.html.haml b/app/views/layouts/devise.html.haml index 6a261bbbc46..bbcb525ea4f 100644 --- a/app/views/layouts/devise.html.haml +++ b/app/views/layouts/devise.html.haml @@ -22,10 +22,10 @@ = brand_text - else %h3.mt-sm-0 - = _('Open source software to collaborate on code') + = _('A complete DevOps platform') %p - = _('Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki.') + = _('GitLab is a single application for the entire software development lifecycle. From project planning and source code management to CI/CD, monitoring, and security.') - if Gitlab::CurrentSettings.sign_in_text.present? = markdown_field(Gitlab::CurrentSettings.current_application_settings, :sign_in_text) diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml index 27e69f5d7d5..37b47897949 100644 --- a/app/workers/all_queues.yml +++ b/app/workers/all_queues.yml @@ -1234,7 +1234,7 @@ :urgency: :high :resource_boundary: :unknown :weight: 3 - :idempotent: + :idempotent: true - :name: project_cache :feature_category: :source_code_management :has_external_dependencies: diff --git a/app/workers/process_commit_worker.rb b/app/workers/process_commit_worker.rb index 9960e812a2f..bdfabea8938 100644 --- a/app/workers/process_commit_worker.rb +++ b/app/workers/process_commit_worker.rb @@ -7,13 +7,15 @@ # result of this the workload of this worker should be kept to a bare minimum. # Consider using an extra worker if you need to add any extra (and potentially # slow) processing of commits. -class ProcessCommitWorker # rubocop:disable Scalability/IdempotentWorker +class ProcessCommitWorker include ApplicationWorker feature_category :source_code_management urgency :high weight 3 + idempotent! + # project_id - The ID of the project this commit belongs to. # user_id - The ID of the user that pushed the commit. # commit_hash - Hash containing commit details to use for constructing a diff --git a/changelogs/unreleased/212470-change-text-on-self-managed-sign-on-page.yml b/changelogs/unreleased/212470-change-text-on-self-managed-sign-on-page.yml new file mode 100644 index 00000000000..ec1742da241 --- /dev/null +++ b/changelogs/unreleased/212470-change-text-on-self-managed-sign-on-page.yml @@ -0,0 +1,5 @@ +--- +title: Update text on self-managed sign in page +merge_request: 30135 +author: +type: other diff --git a/changelogs/unreleased/mc-feature-keep-latest-artifact-for-ref.yml b/changelogs/unreleased/mc-feature-keep-latest-artifact-for-ref.yml new file mode 100644 index 00000000000..4e4bf350298 --- /dev/null +++ b/changelogs/unreleased/mc-feature-keep-latest-artifact-for-ref.yml @@ -0,0 +1,5 @@ +--- +title: Keep latest artifact for each ref. +merge_request: 29802 +author: +type: changed diff --git a/changelogs/unreleased/sh-upgrade-unicorn.yml b/changelogs/unreleased/sh-upgrade-unicorn.yml new file mode 100644 index 00000000000..5cbcc2f2800 --- /dev/null +++ b/changelogs/unreleased/sh-upgrade-unicorn.yml @@ -0,0 +1,5 @@ +--- +title: Upgrade Unicorn to v5.5.1 +merge_request: 30541 +author: +type: fixed diff --git a/db/migrate/20200417145946_add_locked_to_ci_job_artifact.rb b/db/migrate/20200417145946_add_locked_to_ci_job_artifact.rb new file mode 100644 index 00000000000..3bcc733f3b0 --- /dev/null +++ b/db/migrate/20200417145946_add_locked_to_ci_job_artifact.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class AddLockedToCiJobArtifact < ActiveRecord::Migration[6.0] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + def up + with_lock_retries do + add_column :ci_job_artifacts, :locked, :boolean + end + end + + def down + with_lock_retries do + remove_column :ci_job_artifacts, :locked + end + end +end diff --git a/db/structure.sql b/db/structure.sql index 2ba55f2338e..f2b2d5e0f35 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -1079,7 +1079,8 @@ CREATE TABLE public.ci_job_artifacts ( file_store integer, file_sha256 bytea, file_format smallint, - file_location smallint + file_location smallint, + locked boolean ); CREATE SEQUENCE public.ci_job_artifacts_id_seq @@ -13509,6 +13510,7 @@ COPY "schema_migrations" (version) FROM STDIN; 20200416120128 20200416120354 20200417044453 +20200417145946 20200420104303 20200420104323 20200420162730 diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index 4dfe174ea18..25544124f18 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -2939,6 +2939,10 @@ job: expire_in: 1 week ``` +NOTE: **Note:** +For artifacts created in [GitLab 13.0](https://gitlab.com/gitlab-org/gitlab/-/issues/16267) +and later, the latest artifact for a ref is always kept, regardless of the expiry time. + #### `artifacts:reports` > [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/20390) in GitLab 11.2. Requires GitLab Runner 11.2 and above. diff --git a/locale/gitlab.pot b/locale/gitlab.pot index edf43da4b0c..fc12d0098f3 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -836,6 +836,9 @@ msgstr "" msgid "A basic page and serverless function that uses AWS Lambda, AWS API Gateway, and GitLab Pages" msgstr "" +msgid "A complete DevOps platform" +msgstr "" + msgid "A default branch cannot be chosen for an empty project." msgstr "" @@ -12542,9 +12545,6 @@ msgstr "" msgid "Manage" msgstr "" -msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki." -msgstr "" - msgid "Manage Web IDE features" msgstr "" @@ -14284,9 +14284,6 @@ msgstr "" msgid "Open sidebar" msgstr "" -msgid "Open source software to collaborate on code" -msgstr "" - msgid "Open: %{openIssuesCount}" msgstr "" diff --git a/spec/frontend/custom_metrics/components/custom_metrics_form_fields_spec.js b/spec/frontend/custom_metrics/components/custom_metrics_form_fields_spec.js index 61cbef0c557..79c37293fe5 100644 --- a/spec/frontend/custom_metrics/components/custom_metrics_form_fields_spec.js +++ b/spec/frontend/custom_metrics/components/custom_metrics_form_fields_spec.js @@ -152,7 +152,6 @@ describe('custom metrics form fields component', () => { describe('when query validation is in flight', () => { beforeEach(() => { - jest.useFakeTimers(); mountComponent( { metricPersisted: true, ...makeFormData({ query: 'validQuery' }) }, { diff --git a/spec/frontend/diffs/components/app_spec.js b/spec/frontend/diffs/components/app_spec.js index 3a0354205f8..1a95f586344 100644 --- a/spec/frontend/diffs/components/app_spec.js +++ b/spec/frontend/diffs/components/app_spec.js @@ -14,10 +14,13 @@ import TreeList from '~/diffs/components/tree_list.vue'; import { INLINE_DIFF_VIEW_TYPE, PARALLEL_DIFF_VIEW_TYPE } from '~/diffs/constants'; import createDiffsStore from '../create_diffs_store'; import axios from '~/lib/utils/axios_utils'; +import * as urlUtils from '~/lib/utils/url_utility'; import diffsMockData from '../mock_data/merge_request_diffs'; const mergeRequestDiff = { version_index: 1 }; const TEST_ENDPOINT = `${TEST_HOST}/diff/endpoint`; +const COMMIT_URL = '[BASE URL]/OLD'; +const UPDATED_COMMIT_URL = '[BASE URL]/NEW'; describe('diffs/components/app', () => { const oldMrTabs = window.mrTabs; @@ -602,6 +605,70 @@ describe('diffs/components/app', () => { }); }); + describe('commit watcher', () => { + const spy = () => { + jest.spyOn(wrapper.vm, 'refetchDiffData').mockImplementation(() => {}); + jest.spyOn(wrapper.vm, 'adjustView').mockImplementation(() => {}); + }; + let location; + + beforeAll(() => { + location = window.location; + delete window.location; + window.location = COMMIT_URL; + document.title = 'My Title'; + }); + + beforeEach(() => { + jest.spyOn(urlUtils, 'updateHistory'); + }); + + afterAll(() => { + window.location = location; + }); + + it('when the commit changes and the app is not loading it should update the history, refetch the diff data, and update the view', () => { + createComponent({}, ({ state }) => { + state.diffs.commit = { ...state.diffs.commit, id: 'OLD' }; + }); + spy(); + + store.state.diffs.commit = { id: 'NEW' }; + + return wrapper.vm.$nextTick().then(() => { + expect(urlUtils.updateHistory).toHaveBeenCalledWith({ + title: document.title, + url: UPDATED_COMMIT_URL, + }); + expect(wrapper.vm.refetchDiffData).toHaveBeenCalled(); + expect(wrapper.vm.adjustView).toHaveBeenCalled(); + }); + }); + + it.each` + isLoading | oldSha | newSha + ${true} | ${'OLD'} | ${'NEW'} + ${false} | ${'NEW'} | ${'NEW'} + `( + 'given `{ "isLoading": $isLoading, "oldSha": "$oldSha", "newSha": "$newSha" }`, nothing should happen', + ({ isLoading, oldSha, newSha }) => { + createComponent({}, ({ state }) => { + state.diffs.isLoading = isLoading; + state.diffs.commit = { ...state.diffs.commit, id: oldSha }; + }); + spy(); + + store.state.diffs.commit = { id: newSha }; + + return wrapper.vm.$nextTick().then(() => { + expect(urlUtils.updateHistory).not.toHaveBeenCalled(); + expect(wrapper.vm.refetchDiffData).not.toHaveBeenCalled(); + expect(wrapper.vm.adjustView).not.toHaveBeenCalled(); + }); + }, + ); + }); + describe('diffs', () => { it('should render compare versions component', () => { createComponent({}, ({ state }) => { diff --git a/spec/frontend/ide/components/preview/clientside_spec.js b/spec/frontend/ide/components/preview/clientside_spec.js index 0cde6fb6060..7b2025f5e9f 100644 --- a/spec/frontend/ide/components/preview/clientside_spec.js +++ b/spec/frontend/ide/components/preview/clientside_spec.js @@ -70,14 +70,6 @@ describe('IDE clientside preview', () => { }); }; - beforeAll(() => { - jest.useFakeTimers(); - }); - - afterAll(() => { - jest.useRealTimers(); - }); - afterEach(() => { wrapper.destroy(); }); diff --git a/spec/frontend/monitoring/store/actions_spec.js b/spec/frontend/monitoring/store/actions_spec.js index f312aa1fd34..37bd270a1c0 100644 --- a/spec/frontend/monitoring/store/actions_spec.js +++ b/spec/frontend/monitoring/store/actions_spec.js @@ -62,9 +62,6 @@ describe('Monitoring store actions', () => { beforeEach(() => { mock = new MockAdapter(axios); - // Mock `backOff` function to remove exponential algorithm delay. - jest.useFakeTimers(); - jest.spyOn(commonUtils, 'backOff').mockImplementation(callback => { const q = new Promise((resolve, reject) => { const stop = arg => (arg instanceof Error ? reject(arg) : resolve(arg)); diff --git a/spec/frontend/notes/old_notes_spec.js b/spec/frontend/notes/old_notes_spec.js index 49b887b21b4..3f7c94c3a2b 100644 --- a/spec/frontend/notes/old_notes_spec.js +++ b/spec/frontend/notes/old_notes_spec.js @@ -33,7 +33,6 @@ gl.utils.disableButtonIfEmptyField = () => {}; // eslint-disable-next-line jest/no-disabled-tests describe.skip('Old Notes (~/notes.js)', () => { beforeEach(() => { - jest.useFakeTimers(); loadFixtures(fixture); // Re-declare this here so that test_setup.js#beforeEach() doesn't diff --git a/spec/frontend/notes/stores/actions_spec.js b/spec/frontend/notes/stores/actions_spec.js index e0c5441b9d3..cbfb9597159 100644 --- a/spec/frontend/notes/stores/actions_spec.js +++ b/spec/frontend/notes/stores/actions_spec.js @@ -272,8 +272,6 @@ describe('Actions Notes Store', () => { }); describe('poll', () => { - jest.useFakeTimers(); - beforeEach(done => { jest.spyOn(axios, 'get'); diff --git a/spec/frontend/smart_interval_spec.js b/spec/frontend/smart_interval_spec.js index b32ac99e4e4..1a2fd7ff8f1 100644 --- a/spec/frontend/smart_interval_spec.js +++ b/spec/frontend/smart_interval_spec.js @@ -3,8 +3,6 @@ import { assignIn } from 'lodash'; import waitForPromises from 'helpers/wait_for_promises'; import SmartInterval from '~/smart_interval'; -jest.useFakeTimers(); - let interval; describe('SmartInterval', () => { diff --git a/spec/models/ci/job_artifact_spec.rb b/spec/models/ci/job_artifact_spec.rb index 0701f088837..dd33d55d954 100644 --- a/spec/models/ci/job_artifact_spec.rb +++ b/spec/models/ci/job_artifact_spec.rb @@ -160,15 +160,26 @@ describe Ci::JobArtifact do end describe '.for_sha' do + let(:first_pipeline) { create(:ci_pipeline) } + let(:second_pipeline) { create(:ci_pipeline, project: first_pipeline.project, sha: Digest::SHA1.hexdigest(SecureRandom.hex)) } + let!(:first_artifact) { create(:ci_job_artifact, job: create(:ci_build, pipeline: first_pipeline)) } + let!(:second_artifact) { create(:ci_job_artifact, job: create(:ci_build, pipeline: second_pipeline)) } + it 'returns job artifacts for a given pipeline sha' do - project = create(:project) - first_pipeline = create(:ci_pipeline, project: project) - second_pipeline = create(:ci_pipeline, project: project, sha: Digest::SHA1.hexdigest(SecureRandom.hex)) - first_artifact = create(:ci_job_artifact, job: create(:ci_build, pipeline: first_pipeline)) - second_artifact = create(:ci_job_artifact, job: create(:ci_build, pipeline: second_pipeline)) + expect(described_class.for_sha(first_pipeline.sha, first_pipeline.project.id)).to eq([first_artifact]) + expect(described_class.for_sha(second_pipeline.sha, first_pipeline.project.id)).to eq([second_artifact]) + end + end + + describe '.for_ref' do + let(:first_pipeline) { create(:ci_pipeline, ref: 'first_ref') } + let(:second_pipeline) { create(:ci_pipeline, ref: 'second_ref', project: first_pipeline.project) } + let!(:first_artifact) { create(:ci_job_artifact, job: create(:ci_build, pipeline: first_pipeline)) } + let!(:second_artifact) { create(:ci_job_artifact, job: create(:ci_build, pipeline: second_pipeline)) } - expect(described_class.for_sha(first_pipeline.sha, project.id)).to eq([first_artifact]) - expect(described_class.for_sha(second_pipeline.sha, project.id)).to eq([second_artifact]) + it 'returns job artifacts for a given pipeline ref' do + expect(described_class.for_ref(first_pipeline.ref, first_pipeline.project.id)).to eq([first_artifact]) + expect(described_class.for_ref(second_pipeline.ref, first_pipeline.project.id)).to eq([second_artifact]) end end @@ -185,9 +196,9 @@ describe Ci::JobArtifact do end describe 'callbacks' do - subject { create(:ci_job_artifact, :archive) } - describe '#schedule_background_upload' do + subject { create(:ci_job_artifact, :archive) } + context 'when object storage is disabled' do before do stub_artifacts_object_storage(enabled: false) diff --git a/spec/services/ci/create_job_artifacts_service_spec.rb b/spec/services/ci/create_job_artifacts_service_spec.rb index fe64a66f322..2d869575d2a 100644 --- a/spec/services/ci/create_job_artifacts_service_spec.rb +++ b/spec/services/ci/create_job_artifacts_service_spec.rb @@ -30,6 +30,26 @@ describe Ci::CreateJobArtifactsService do describe '#execute' do subject { service.execute(job, artifacts_file, params, metadata_file: metadata_file) } + context 'locking' do + let(:old_job) { create(:ci_build, pipeline: create(:ci_pipeline, project: job.project, ref: job.ref)) } + let!(:latest_artifact) { create(:ci_job_artifact, job: old_job, locked: true) } + let!(:other_artifact) { create(:ci_job_artifact, locked: true) } + + it 'locks the new artifact' do + subject + + expect(Ci::JobArtifact.last).to have_attributes(locked: true) + end + + it 'unlocks all other artifacts for the same ref' do + expect { subject }.to change { latest_artifact.reload.locked }.from(true).to(false) + end + + it 'does not unlock artifacts for other refs' do + expect { subject }.not_to change { other_artifact.reload.locked }.from(true) + end + end + context 'when artifacts file is uploaded' do it 'saves artifact for the given type' do expect { subject }.to change { Ci::JobArtifact.count }.by(1) diff --git a/spec/services/ci/destroy_expired_job_artifacts_service_spec.rb b/spec/services/ci/destroy_expired_job_artifacts_service_spec.rb index fc5450ab33d..4b9f12d8fdf 100644 --- a/spec/services/ci/destroy_expired_job_artifacts_service_spec.rb +++ b/spec/services/ci/destroy_expired_job_artifacts_service_spec.rb @@ -11,8 +11,26 @@ describe Ci::DestroyExpiredJobArtifactsService, :clean_gitlab_redis_shared_state let(:service) { described_class.new } let!(:artifact) { create(:ci_job_artifact, expire_at: 1.day.ago) } - it 'destroys expired job artifacts' do - expect { subject }.to change { Ci::JobArtifact.count }.by(-1) + context 'when artifact is expired' do + context 'when artifact is not locked' do + before do + artifact.update!(locked: false) + end + + it 'destroys job artifact' do + expect { subject }.to change { Ci::JobArtifact.count }.by(-1) + end + end + + context 'when artifact is locked' do + before do + artifact.update!(locked: true) + end + + it 'does not destroy job artifact' do + expect { subject }.not_to change { Ci::JobArtifact.count } + end + end end context 'when artifact is not expired' do @@ -72,7 +90,7 @@ describe Ci::DestroyExpiredJobArtifactsService, :clean_gitlab_redis_shared_state stub_const('Ci::DestroyExpiredJobArtifactsService::BATCH_SIZE', 1) end - let!(:artifact) { create_list(:ci_job_artifact, 2, expire_at: 1.day.ago) } + let!(:second_artifact) { create(:ci_job_artifact, expire_at: 1.day.ago) } it 'raises an error and does not continue destroying' do is_expected.to be_falsy @@ -96,7 +114,7 @@ describe Ci::DestroyExpiredJobArtifactsService, :clean_gitlab_redis_shared_state stub_const('Ci::DestroyExpiredJobArtifactsService::BATCH_SIZE', 1) end - let!(:artifact) { create_list(:ci_job_artifact, 2, expire_at: 1.day.ago) } + let!(:second_artifact) { create(:ci_job_artifact, expire_at: 1.day.ago) } it 'destroys all expired artifacts' do expect { subject }.to change { Ci::JobArtifact.count }.by(-2) diff --git a/spec/services/git/branch_push_service_spec.rb b/spec/services/git/branch_push_service_spec.rb index acd14005c69..d5ffe4d4536 100644 --- a/spec/services/git/branch_push_service_spec.rb +++ b/spec/services/git/branch_push_service_spec.rb @@ -291,7 +291,7 @@ describe Git::BranchPushService, services: true do execute_service(project, user, oldrev: oldrev, newrev: newrev, ref: ref) end - it "defaults to the pushing user if the commit's author is not known", :sidekiq_might_not_need_inline do + it "defaults to the pushing user if the commit's author is not known", :sidekiq_inline, :use_clean_rails_redis_caching do allow(commit).to receive_messages( author_name: 'unknown name', author_email: 'unknown@email.com' @@ -336,7 +336,7 @@ describe Git::BranchPushService, services: true do end context "while saving the 'first_mentioned_in_commit_at' metric for an issue" do - it 'sets the metric for referenced issues', :sidekiq_might_not_need_inline do + it 'sets the metric for referenced issues', :sidekiq_inline, :use_clean_rails_redis_caching do execute_service(project, user, oldrev: oldrev, newrev: newrev, ref: ref) expect(issue.reload.metrics.first_mentioned_in_commit_at).to be_like_time(commit_time) @@ -397,7 +397,7 @@ describe Git::BranchPushService, services: true do allow(project).to receive(:default_branch).and_return('not-master') end - it "creates cross-reference notes", :sidekiq_might_not_need_inline do + it "creates cross-reference notes", :sidekiq_inline, :use_clean_rails_redis_caching do expect(SystemNoteService).to receive(:cross_reference).with(issue, closing_commit, commit_author) execute_service(project, user, oldrev: oldrev, newrev: newrev, ref: ref) end @@ -438,7 +438,7 @@ describe Git::BranchPushService, services: true do context "mentioning an issue" do let(:message) { "this is some work.\n\nrelated to JIRA-1" } - it "initiates one api call to jira server to mention the issue", :sidekiq_might_not_need_inline do + it "initiates one api call to jira server to mention the issue", :sidekiq_inline, :use_clean_rails_redis_caching do execute_service(project, user, oldrev: oldrev, newrev: newrev, ref: ref) expect(WebMock).to have_requested(:post, jira_api_comment_url('JIRA-1')).with( diff --git a/spec/workers/process_commit_worker_spec.rb b/spec/workers/process_commit_worker_spec.rb index 21c300af7ac..d247668ac76 100644 --- a/spec/workers/process_commit_worker_spec.rb +++ b/spec/workers/process_commit_worker_spec.rb @@ -22,16 +22,26 @@ describe ProcessCommitWorker do worker.perform(project.id, -1, commit.to_hash) end - it 'processes the commit message' do - expect(worker).to receive(:process_commit_message).and_call_original + include_examples 'an idempotent worker' do + subject do + perform_multiple([project.id, user.id, commit.to_hash], worker: worker) + end - worker.perform(project.id, user.id, commit.to_hash) - end + it 'processes the commit message' do + expect(worker).to receive(:process_commit_message) + .exactly(IdempotentWorkerHelper::WORKER_EXEC_TIMES) + .and_call_original - it 'updates the issue metrics' do - expect(worker).to receive(:update_issue_metrics).and_call_original + subject + end - worker.perform(project.id, user.id, commit.to_hash) + it 'updates the issue metrics' do + expect(worker).to receive(:update_issue_metrics) + .exactly(IdempotentWorkerHelper::WORKER_EXEC_TIMES) + .and_call_original + + subject + end end end |