summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-04-27 21:10:10 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-04-27 21:10:10 +0000
commit579e85eb029c4ee66e8b8cd537a94b9e6cb0e58b (patch)
tree26c28e966b7f2e4fabf06bfaa1d650ac4579f9e4
parentf569792df8a25caa1bed9c448c8c4c3f837f5164 (diff)
downloadgitlab-ce-579e85eb029c4ee66e8b8cd537a94b9e6cb0e58b.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.gitlab/CODEOWNERS2
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.lock6
-rw-r--r--app/assets/javascripts/diffs/components/app.vue15
-rw-r--r--app/models/ci/job_artifact.rb7
-rw-r--r--app/services/ci/create_job_artifacts_service.rb11
-rw-r--r--app/services/ci/destroy_expired_job_artifacts_service.rb2
-rw-r--r--app/views/layouts/_page.html.haml2
-rw-r--r--app/views/layouts/devise.html.haml4
-rw-r--r--app/workers/all_queues.yml2
-rw-r--r--app/workers/process_commit_worker.rb4
-rw-r--r--changelogs/unreleased/212470-change-text-on-self-managed-sign-on-page.yml5
-rw-r--r--changelogs/unreleased/mc-feature-keep-latest-artifact-for-ref.yml5
-rw-r--r--changelogs/unreleased/sh-upgrade-unicorn.yml5
-rw-r--r--db/migrate/20200417145946_add_locked_to_ci_job_artifact.rb19
-rw-r--r--db/structure.sql4
-rw-r--r--doc/ci/yaml/README.md4
-rw-r--r--locale/gitlab.pot9
-rw-r--r--spec/frontend/custom_metrics/components/custom_metrics_form_fields_spec.js1
-rw-r--r--spec/frontend/diffs/components/app_spec.js67
-rw-r--r--spec/frontend/ide/components/preview/clientside_spec.js8
-rw-r--r--spec/frontend/monitoring/store/actions_spec.js3
-rw-r--r--spec/frontend/notes/old_notes_spec.js1
-rw-r--r--spec/frontend/notes/stores/actions_spec.js2
-rw-r--r--spec/frontend/smart_interval_spec.js2
-rw-r--r--spec/models/ci/job_artifact_spec.rb29
-rw-r--r--spec/services/ci/create_job_artifacts_service_spec.rb20
-rw-r--r--spec/services/ci/destroy_expired_job_artifacts_service_spec.rb26
-rw-r--r--spec/services/git/branch_push_service_spec.rb8
-rw-r--r--spec/workers/process_commit_worker_spec.rb24
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/`
diff --git a/Gemfile b/Gemfile
index 88c1ed19a30..dbde17da603 100644
--- a/Gemfile
+++ b/Gemfile
@@ -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