Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/assets/javascripts/lib/utils/axios_utils.js15
-rw-r--r--app/assets/javascripts/lib/utils/common_utils.js2
-rw-r--r--app/assets/javascripts/lib/utils/suppress_ajax_errors_during_navigation.js16
-rw-r--r--app/assets/javascripts/notes/components/notes_app.vue7
-rw-r--r--app/assets/javascripts/pages/groups/shared/group_details.js7
-rw-r--r--app/controllers/boards/lists_controller.rb7
-rw-r--r--app/models/ci/build.rb1
-rw-r--r--app/models/ci/persistent_ref.rb54
-rw-r--r--app/models/ci/pipeline.rb6
-rw-r--r--app/models/list.rb36
-rw-r--r--app/models/repository.rb3
-rw-r--r--app/presenters/ci/build_runner_presenter.rb17
-rw-r--r--app/services/boards/lists/list_service.rb2
-rw-r--r--changelogs/unreleased/53041-groups-named-shared.yml5
-rw-r--r--changelogs/unreleased/dedicated-pipeline-ref.yml6
-rw-r--r--changelogs/unreleased/fj-32885-add-trigram-index-snippet-content.yml5
-rw-r--r--changelogs/unreleased/increase-limit-for-recursively-include.yml5
-rw-r--r--changelogs/unreleased/nfriend-suppress-ajax-errors-again.yml5
-rw-r--r--config/webpack.config.js4
-rw-r--r--db/migrate/20190927074328_add_index_on_snippet_content.rb18
-rw-r--r--db/schema.rb3
-rw-r--r--doc/ci/merge_request_pipelines/pipelines_for_merged_results/index.md11
-rw-r--r--doc/ci/pipelines.md41
-rw-r--r--doc/ci/yaml/README.md16
-rw-r--r--doc/development/contributing/index.md3
-rw-r--r--doc/user/project/pipelines/schedules.md2
-rw-r--r--doc/user/project/pipelines/settings.md4
-rw-r--r--lib/gitlab/ci/config/external/mapper.rb2
-rw-r--r--lib/gitlab/gon_helper.rb4
-rw-r--r--package.json2
-rw-r--r--spec/controllers/boards/lists_controller_spec.rb25
-rw-r--r--spec/features/issues/issues_tabs_spec.rb56
-rw-r--r--spec/frontend/lib/utils/suppress_ajax_errors_during_navigation_spec.js37
-rw-r--r--spec/javascripts/frequent_items/components/app_spec.js7
-rw-r--r--spec/javascripts/lib/utils/common_utils_spec.js10
-rw-r--r--spec/models/ci/build_spec.rb6
-rw-r--r--spec/models/ci/persistent_ref_spec.rb108
-rw-r--r--spec/models/ci/pipeline_spec.rb10
-rw-r--r--spec/models/list_spec.rb12
-rw-r--r--spec/presenters/ci/build_runner_presenter_spec.rb17
-rw-r--r--spec/requests/boards/lists_controller_spec.rb25
-rw-r--r--spec/services/ci/process_pipeline_service_spec.rb16
-rw-r--r--spec/services/ci/register_job_service_spec.rb16
-rw-r--r--yarn.lock163
44 files changed, 685 insertions, 132 deletions
diff --git a/app/assets/javascripts/lib/utils/axios_utils.js b/app/assets/javascripts/lib/utils/axios_utils.js
index c17f62c671c..a04fe609015 100644
--- a/app/assets/javascripts/lib/utils/axios_utils.js
+++ b/app/assets/javascripts/lib/utils/axios_utils.js
@@ -1,5 +1,6 @@
import axios from 'axios';
import csrf from './csrf';
+import suppressAjaxErrorsDuringNavigation from './suppress_ajax_errors_during_navigation';
axios.defaults.headers.common[csrf.headerKey] = csrf.token;
// Used by Rails to check if it is a valid XHR request
@@ -25,6 +26,20 @@ axios.interceptors.response.use(
},
);
+let isUserNavigating = false;
+window.addEventListener('beforeunload', () => {
+ isUserNavigating = true;
+});
+
+// Ignore AJAX errors caused by requests
+// being cancelled due to browser navigation
+const { gon } = window;
+const featureFlagEnabled = gon && gon.features && gon.features.suppressAjaxNavigationErrors;
+axios.interceptors.response.use(
+ response => response,
+ err => suppressAjaxErrorsDuringNavigation(err, isUserNavigating, featureFlagEnabled),
+);
+
export default axios;
/**
diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js
index 6e8f63a10a4..7c4373ba517 100644
--- a/app/assets/javascripts/lib/utils/common_utils.js
+++ b/app/assets/javascripts/lib/utils/common_utils.js
@@ -15,6 +15,8 @@ export const getPagePath = (index = 0) => {
return page.split(':')[index];
};
+export const getDashPath = (path = window.location.pathname) => path.split('/-/')[1] || null;
+
export const isInGroupsPage = () => getPagePath() === 'groups';
export const isInProjectPage = () => getPagePath() === 'projects';
diff --git a/app/assets/javascripts/lib/utils/suppress_ajax_errors_during_navigation.js b/app/assets/javascripts/lib/utils/suppress_ajax_errors_during_navigation.js
new file mode 100644
index 00000000000..4c61da9b862
--- /dev/null
+++ b/app/assets/javascripts/lib/utils/suppress_ajax_errors_during_navigation.js
@@ -0,0 +1,16 @@
+/**
+ * An Axios error interceptor that suppresses AJAX errors caused
+ * by the request being cancelled when the user navigates to a new page
+ */
+export default (err, isUserNavigating, featureFlagEnabled) => {
+ if (featureFlagEnabled && isUserNavigating && err.code === 'ECONNABORTED') {
+ // If the user is navigating away from the current page,
+ // prevent .then() and .catch() handlers from being
+ // called by returning a Promise that never resolves
+ return new Promise(() => {});
+ }
+
+ // The error is not related to browser navigation,
+ // so propagate the error
+ return Promise.reject(err);
+};
diff --git a/app/assets/javascripts/notes/components/notes_app.vue b/app/assets/javascripts/notes/components/notes_app.vue
index 16a0fb3f33a..fd592e965bf 100644
--- a/app/assets/javascripts/notes/components/notes_app.vue
+++ b/app/assets/javascripts/notes/components/notes_app.vue
@@ -69,6 +69,7 @@ export default {
'commentsDisabled',
'getNoteableData',
'userCanReply',
+ 'discussionTabCounter',
]),
noteableType() {
return this.noteableData.noteableType;
@@ -95,13 +96,13 @@ export default {
}
},
allDiscussions() {
- if (this.discussonsCount) {
- this.discussonsCount.textContent = this.allDiscussions.length;
+ if (this.discussionsCount && !this.isLoading) {
+ this.discussionsCount.textContent = this.discussionTabCounter;
}
},
},
created() {
- this.discussonsCount = document.querySelector('.js-discussions-count');
+ this.discussionsCount = document.querySelector('.js-discussions-count');
this.setNotesData(this.notesData);
this.setNoteableData(this.noteableData);
diff --git a/app/assets/javascripts/pages/groups/shared/group_details.js b/app/assets/javascripts/pages/groups/shared/group_details.js
index 01ef3f1db2b..37b253d7c48 100644
--- a/app/assets/javascripts/pages/groups/shared/group_details.js
+++ b/app/assets/javascripts/pages/groups/shared/group_details.js
@@ -1,6 +1,6 @@
/* eslint-disable no-new */
-import { getPagePath } from '~/lib/utils/common_utils';
+import { getPagePath, getDashPath } from '~/lib/utils/common_utils';
import { ACTIVE_TAB_SHARED, ACTIVE_TAB_ARCHIVED } from '~/groups/constants';
import NewGroupChild from '~/groups/new_group_child';
import notificationsDropdown from '~/notifications_dropdown';
@@ -12,9 +12,8 @@ import GroupTabs from './group_tabs';
export default function initGroupDetails(actionName = 'show') {
const newGroupChildWrapper = document.querySelector('.js-new-project-subgroup');
const loadableActions = [ACTIVE_TAB_SHARED, ACTIVE_TAB_ARCHIVED];
- const paths = window.location.pathname.split('/');
- const subpath = paths[paths.length - 1];
- let action = loadableActions.includes(subpath) ? subpath : getPagePath(1);
+ const dashPath = getDashPath();
+ let action = loadableActions.includes(dashPath) ? dashPath : getPagePath(1);
if (actionName && action === actionName) {
action = 'show'; // 'show' resets GroupTabs to default action through base class
}
diff --git a/app/controllers/boards/lists_controller.rb b/app/controllers/boards/lists_controller.rb
index b64b18505b6..90e04414d8d 100644
--- a/app/controllers/boards/lists_controller.rb
+++ b/app/controllers/boards/lists_controller.rb
@@ -11,6 +11,8 @@ module Boards
def index
lists = Boards::Lists::ListService.new(board.parent, current_user).execute(board)
+ List.preload_preferences_for_user(lists, current_user)
+
render json: serialize_as_json(lists)
end
@@ -51,7 +53,10 @@ module Boards
service = Boards::Lists::GenerateService.new(board_parent, current_user)
if service.execute(board)
- lists = board.lists.movable.preload_associations(current_user)
+ lists = board.lists.movable.preload_associations
+
+ List.preload_preferences_for_user(lists, current_user)
+
render json: serialize_as_json(lists)
else
head :unprocessable_entity
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 3ef0ed0ef49..5cca18024c1 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -234,6 +234,7 @@ module Ci
end
after_transition pending: :running do |build|
+ build.pipeline.persistent_ref.create
build.deployment&.run
build.run_after_commit do
diff --git a/app/models/ci/persistent_ref.rb b/app/models/ci/persistent_ref.rb
new file mode 100644
index 00000000000..9bb67c88577
--- /dev/null
+++ b/app/models/ci/persistent_ref.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+module Ci
+ ##
+ # The persistent pipeline ref to ensure runners can safely fetch source code
+ # even if force-push/source-branch-deletion happens.
+ class PersistentRef
+ include ActiveModel::Model
+
+ attr_accessor :pipeline
+
+ delegate :project, :sha, to: :pipeline
+ delegate :repository, to: :project
+ delegate :ref_exists?, :create_ref, :delete_refs, to: :repository
+
+ def exist?
+ return unless enabled?
+
+ ref_exists?(path)
+ rescue
+ false
+ end
+
+ def create
+ return unless enabled? && !exist?
+
+ create_ref(sha, path)
+ rescue => e
+ Gitlab::Sentry
+ .track_acceptable_exception(e, extra: { pipeline_id: pipeline.id })
+ end
+
+ def delete
+ return unless enabled?
+
+ delete_refs(path)
+ rescue Gitlab::Git::Repository::NoRepository
+ # no-op
+ rescue => e
+ Gitlab::Sentry
+ .track_acceptable_exception(e, extra: { pipeline_id: pipeline.id })
+ end
+
+ def path
+ "refs/#{Repository::REF_PIPELINES}/#{pipeline.id}"
+ end
+
+ private
+
+ def enabled?
+ Feature.enabled?(:depend_on_persistent_pipeline_ref, project)
+ end
+ end
+end
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index 12295c0457d..b22a87e7cef 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -174,6 +174,8 @@ module Ci
after_transition any => ::Ci::Pipeline.completed_statuses do |pipeline|
pipeline.run_after_commit do
+ pipeline.persistent_ref.delete
+
pipeline.all_merge_requests.each do |merge_request|
next unless merge_request.auto_merge_enabled?
@@ -853,6 +855,10 @@ module Ci
end
end
+ def persistent_ref
+ @persistent_ref ||= PersistentRef.new(pipeline: self)
+ end
+
private
def ci_yaml_from_repo
diff --git a/app/models/list.rb b/app/models/list.rb
index b4a4631b397..b53e2fb7c33 100644
--- a/app/models/list.rb
+++ b/app/models/list.rb
@@ -21,20 +21,10 @@ class List < ApplicationRecord
scope :destroyable, -> { where(list_type: list_types.slice(*destroyable_types).values) }
scope :movable, -> { where(list_type: list_types.slice(*movable_types).values) }
- scope :preload_associations, -> (user) do
- preload(:board, label: :priorities)
- end
+ scope :preload_associations, -> { preload(:board, label: :priorities) }
scope :ordered, -> { order(:list_type, :position) }
- # Loads list with preferences for given user
- # if preferences exists for user or not
- scope :with_preferences_for, -> (user) do
- return unless user
-
- includes(:list_user_preferences).where(list_user_preferences: { user_id: [user.id, nil] })
- end
-
alias_method :preferences, :list_user_preferences
class << self
@@ -45,25 +35,25 @@ class List < ApplicationRecord
def movable_types
[:label]
end
+
+ def preload_preferences_for_user(lists, user)
+ return unless user
+
+ lists.each { |list| list.preferences_for(user) }
+ end
end
def preferences_for(user)
return preferences.build unless user
- if preferences.loaded?
- preloaded_preferences_for(user)
- else
- preferences.find_or_initialize_by(user: user)
- end
- end
+ BatchLoader.for(list_id: id, user_id: user.id).batch(default_value: preferences.build(user: user)) do |items, loader|
+ list_ids = items.map { |i| i[:list_id] }
+ user_ids = items.map { |i| i[:user_id] }
- def preloaded_preferences_for(user)
- user_preferences =
- preferences.find do |preference|
- preference.user_id == user.id
+ ListUserPreference.where(list_id: list_ids, user_id: user_ids).find_each do |preference|
+ loader.call({ list_id: preference.list_id, user_id: preference.user_id }, preference)
end
-
- user_preferences || preferences.build(user: user)
+ end
end
def update_preferences_for(user, preferences = {})
diff --git a/app/models/repository.rb b/app/models/repository.rb
index fb84bbaca56..6c6023a8709 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -6,6 +6,7 @@ class Repository
REF_MERGE_REQUEST = 'merge-requests'
REF_KEEP_AROUND = 'keep-around'
REF_ENVIRONMENTS = 'environments'
+ REF_PIPELINES = 'pipelines'
ARCHIVE_CACHE_TIME = 60 # Cache archives referred to by a (mutable) ref for 1 minute
ARCHIVE_CACHE_TIME_IMMUTABLE = 3600 # Cache archives referred to by an immutable reference for 1 hour
@@ -16,7 +17,7 @@ class Repository
replace
#{REF_ENVIRONMENTS}
#{REF_KEEP_AROUND}
- #{REF_ENVIRONMENTS}
+ #{REF_PIPELINES}
].freeze
include Gitlab::RepositoryCacheAdapter
diff --git a/app/presenters/ci/build_runner_presenter.rb b/app/presenters/ci/build_runner_presenter.rb
index 5231a8efa55..8e469795581 100644
--- a/app/presenters/ci/build_runner_presenter.rb
+++ b/app/presenters/ci/build_runner_presenter.rb
@@ -34,7 +34,8 @@ module Ci
def refspecs
specs = []
- specs << refspec_for_merge_request_ref if merge_request_ref?
+ specs << refspec_for_pipeline_ref if merge_request_ref?
+ specs << refspec_for_persistent_ref if persistent_ref_exist?
if git_depth > 0
specs << refspec_for_branch(ref) if branch? || legacy_detached_merge_request_pipeline?
@@ -86,10 +87,22 @@ module Ci
"+#{Gitlab::Git::TAG_REF_PREFIX}#{ref}:#{RUNNER_REMOTE_TAG_PREFIX}#{ref}"
end
- def refspec_for_merge_request_ref
+ def refspec_for_pipeline_ref
"+#{ref}:#{ref}"
end
+ def refspec_for_persistent_ref
+ "+#{persistent_ref_path}:#{persistent_ref_path}"
+ end
+
+ def persistent_ref_exist?
+ pipeline.persistent_ref.exist?
+ end
+
+ def persistent_ref_path
+ pipeline.persistent_ref.path
+ end
+
def git_depth_variable
strong_memoize(:git_depth_variable) do
variables&.find { |variable| variable[:key] == 'GIT_DEPTH' }
diff --git a/app/services/boards/lists/list_service.rb b/app/services/boards/lists/list_service.rb
index 3609d9c6283..82cba1b68c4 100644
--- a/app/services/boards/lists/list_service.rb
+++ b/app/services/boards/lists/list_service.rb
@@ -6,7 +6,7 @@ module Boards
def execute(board)
board.lists.create(list_type: :backlog) unless board.lists.backlog.exists?
- board.lists.preload_associations(current_user)
+ board.lists.preload_associations
end
end
end
diff --git a/changelogs/unreleased/53041-groups-named-shared.yml b/changelogs/unreleased/53041-groups-named-shared.yml
new file mode 100644
index 00000000000..165b6727fe9
--- /dev/null
+++ b/changelogs/unreleased/53041-groups-named-shared.yml
@@ -0,0 +1,5 @@
+---
+title: Naming a project "shared" will no longer automatically open the "Shared Projects" tab.
+merge_request: 16847
+author: Jesse Hall @jessehall3
+type: fixed
diff --git a/changelogs/unreleased/dedicated-pipeline-ref.yml b/changelogs/unreleased/dedicated-pipeline-ref.yml
new file mode 100644
index 00000000000..6484bab1da4
--- /dev/null
+++ b/changelogs/unreleased/dedicated-pipeline-ref.yml
@@ -0,0 +1,6 @@
+---
+title: Create a persistent ref per pipeline for keeping pipelines run from force-push
+ and merged results
+merge_request: 17043
+author:
+type: fixed
diff --git a/changelogs/unreleased/fj-32885-add-trigram-index-snippet-content.yml b/changelogs/unreleased/fj-32885-add-trigram-index-snippet-content.yml
new file mode 100644
index 00000000000..695098d41ff
--- /dev/null
+++ b/changelogs/unreleased/fj-32885-add-trigram-index-snippet-content.yml
@@ -0,0 +1,5 @@
+---
+title: Add trigram index on snippet content
+merge_request: 17806
+author:
+type: performance
diff --git a/changelogs/unreleased/increase-limit-for-recursively-include.yml b/changelogs/unreleased/increase-limit-for-recursively-include.yml
new file mode 100644
index 00000000000..5fed53e8f24
--- /dev/null
+++ b/changelogs/unreleased/increase-limit-for-recursively-include.yml
@@ -0,0 +1,5 @@
+---
+title: Increase the limit of includes in CI file to 100
+merge_request: 17807
+author:
+type: fixed
diff --git a/changelogs/unreleased/nfriend-suppress-ajax-errors-again.yml b/changelogs/unreleased/nfriend-suppress-ajax-errors-again.yml
new file mode 100644
index 00000000000..04909152aa8
--- /dev/null
+++ b/changelogs/unreleased/nfriend-suppress-ajax-errors-again.yml
@@ -0,0 +1,5 @@
+---
+title: Suppress error messages shown when navigating to a new page
+merge_request: 17706
+author:
+type: fixed
diff --git a/config/webpack.config.js b/config/webpack.config.js
index e9f9fe4c402..b5656040da2 100644
--- a/config/webpack.config.js
+++ b/config/webpack.config.js
@@ -209,9 +209,7 @@ module.exports = {
{
loader: 'css-loader',
options: {
- modules: {
- localIdentName: '[name]__[local].[hash:8].[ext]',
- },
+ name: '[name].[hash:8].[ext]',
},
},
],
diff --git a/db/migrate/20190927074328_add_index_on_snippet_content.rb b/db/migrate/20190927074328_add_index_on_snippet_content.rb
new file mode 100644
index 00000000000..ef7583508f2
--- /dev/null
+++ b/db/migrate/20190927074328_add_index_on_snippet_content.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class AddIndexOnSnippetContent < ActiveRecord::Migration[5.2]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+ INDEX_NAME = 'index_snippets_on_content_trigram'
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :snippets, :content, name: INDEX_NAME, using: :gin, opclass: { content: :gin_trgm_ops }
+ end
+
+ def down
+ remove_concurrent_index_by_name(:snippets, INDEX_NAME)
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index e66e3fa6881..be774169517 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 2019_09_26_041216) do
+ActiveRecord::Schema.define(version: 2019_09_27_074328) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_trgm"
@@ -3319,6 +3319,7 @@ ActiveRecord::Schema.define(version: 2019_09_26_041216) do
t.text "description"
t.text "description_html"
t.index ["author_id"], name: "index_snippets_on_author_id"
+ t.index ["content"], name: "index_snippets_on_content_trigram", opclass: :gin_trgm_ops, using: :gin
t.index ["file_name"], name: "index_snippets_on_file_name_trigram", opclass: :gin_trgm_ops, using: :gin
t.index ["project_id"], name: "index_snippets_on_project_id"
t.index ["title"], name: "index_snippets_on_title_trigram", opclass: :gin_trgm_ops, using: :gin
diff --git a/doc/ci/merge_request_pipelines/pipelines_for_merged_results/index.md b/doc/ci/merge_request_pipelines/pipelines_for_merged_results/index.md
index e1c5848af03..ced6efc415b 100644
--- a/doc/ci/merge_request_pipelines/pipelines_for_merged_results/index.md
+++ b/doc/ci/merge_request_pipelines/pipelines_for_merged_results/index.md
@@ -93,6 +93,17 @@ To check these feature flag values, please ask administrator to execute the foll
> Feature.enable(:ci_use_merge_request_ref) # Enable the feature flag.
```
+### Intermittently pipelines fail by `fatal: reference is not a tree:` error
+
+Since pipelines for merged results are a run on a merge ref of a merge request
+(`refs/merge-requests/<iid>/merge`), the git-reference could be overwritten at an
+unexpected timing, for example, when a source or target branch is advanced.
+In this case, the pipeline fails because of `fatal: reference is not a tree:` error,
+which indicates that the checkout-SHA is not found in the merge ref.
+
+This behavior was improved at GitLab 12.4 by introducing [Persistent pipeline refs](../../pipelines.md#persistent-pipeline-refs).
+You should be able to create pipelines at any timings without concerning the error.
+
## Using Merge Trains **(PREMIUM)**
By enabling [Pipelines for merged results](#pipelines-for-merged-results-premium),
diff --git a/doc/ci/pipelines.md b/doc/ci/pipelines.md
index 2a077e66ec0..bb40feea10e 100644
--- a/doc/ci/pipelines.md
+++ b/doc/ci/pipelines.md
@@ -405,3 +405,44 @@ branches, avoiding untrusted code to be executed on the protected runner and
preserving deployment keys and other credentials from being unintentionally
accessed. In order to ensure that jobs intended to be executed on protected
runners will not use regular runners, they must be tagged accordingly.
+
+## Persistent pipeline refs
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/merge_requests/17043) in GitLab 12.4.
+
+Previously, you'd have encountered unexpected pipeline failures when you force-pushed
+a branch to its remote repository. To illustrate the problem, suppose you've had the current workflow:
+
+1. A user creates a feature branch named `example` and pushes it to a remote repository.
+1. A new pipeline starts running on the `example` branch.
+1. A user rebases the `example` branch on the latest `master` branch and force-pushes it to its remote repository.
+1. A new pipeline starts running on the `example` branch again, however,
+ the previous pipeline (2) fails because of `fatal: reference is not a tree:` error.
+
+This is because the previous pipeline cannot find a checkout-SHA (which associated with the pipeline record)
+from the `example` branch that the commit history has already been overwritten by the force-push.
+Similarly, [Pipelines for merged results](merge_request_pipelines/pipelines_for_merged_results/index.md)
+might have failed intermittently due to [the same reason](merge_request_pipelines/pipelines_for_merged_results/index.md#intermittently-pipelines-fail-by-fatal-reference-is-not-a-tree-error).
+
+As of GitLab 12.4, we've improved this behavior by persisting pipeline refs exclusively.
+To illustrate its life cycle:
+
+1. A pipeline is created on a feature branch named `example`.
+1. A persistent pipeline ref is created at `refs/pipelines/<pipeline-id>`,
+ which retains the checkout-SHA of the associated pipeline record.
+ This persistent ref stays intact during the pipeline execution,
+ even if the commit history of the `example` branch has been overwritten by force-push.
+1. GitLab Runner fetches the persistent pipeline ref and gets source code from the checkout-SHA.
+1. When the pipeline finished, its persistent ref is cleaned up in a background process.
+
+NOTE: **NOTE**: At this moment, this feature is off dy default and can be manually enabled
+by enabling `depend_on_persistent_pipeline_ref` feature flag, however, we'd remove this
+feature flag and make it enabled by deafult by the day we release 12.4 _if we don't find any issues_.
+If you'd be interested in manually turning on this behavior, please ask the administrator
+to execute the following commands in rails console.
+
+```shell
+> sudo gitlab-rails console # Login to Rails console of GitLab instance.
+> project = Project.find_by_full_path('namespace/project-name') # Get the project instance.
+> Feature.enable(:depend_on_persistent_pipeline_ref, project) # Enable the feature flag.
+```
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index e6d83d31e36..f022012e2bc 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -1246,8 +1246,8 @@ Read how caching works and find out some good practices in the
[caching dependencies documentation](../caching/index.md).
`cache` is used to specify a list of files and directories which should be
-cached between jobs. You can only use paths that are within the project
-workspace.
+cached between jobs. You can only use paths that are within the local working
+copy.
If `cache` is defined outside the scope of jobs, it means it is set
globally and all jobs will use that definition.
@@ -1417,7 +1417,7 @@ be available for download in the GitLab UI.
#### `artifacts:paths`
-You can only use paths that are within the project workspace.
+You can only use paths that are within the local working copy.
Wildcards can be used that follow the [glob](https://en.wikipedia.org/wiki/Glob_(programming)) patterns and [filepath.Match](https://golang.org/pkg/path/filepath/#Match).
To pass artifacts between different jobs, see [dependencies](#dependencies).
@@ -2296,7 +2296,7 @@ or public project, or template is allowed.
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/issues/56836) in GitLab 11.9.
Nested includes allow you to compose a set of includes.
-A total of 50 includes is allowed.
+A total of 100 includes is allowed.
Duplicate includes are considered a configuration error.
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/28212) in GitLab 12.4.
@@ -2738,14 +2738,14 @@ unspecified, the default from project settings will be used.
There are three possible values: `clone`, `fetch`, and `none`.
`clone` is the slowest option. It clones the repository from scratch for every
-job, ensuring that the project workspace is always pristine.
+job, ensuring that the local working copy is always pristine.
```yaml
variables:
GIT_STRATEGY: clone
```
-`fetch` is faster as it re-uses the project workspace (falling back to `clone`
+`fetch` is faster as it re-uses the local working copy (falling back to `clone`
if it doesn't exist). `git clean` is used to undo any changes made by the last
job, and `git fetch` is used to retrieve commits made since the last job ran.
@@ -2754,11 +2754,11 @@ variables:
GIT_STRATEGY: fetch
```
-`none` also re-uses the project workspace, but skips all Git operations
+`none` also re-uses the local working copy, but skips all Git operations
(including GitLab Runner's pre-clone script, if present). It is mostly useful
for jobs that operate exclusively on artifacts (e.g., `deploy`). Git repository
data may be present, but it is certain to be out of date, so you should only
-rely on files brought into the project workspace from cache or artifacts.
+rely on files brought into the local working copy from cache or artifacts.
```yaml
variables:
diff --git a/doc/development/contributing/index.md b/doc/development/contributing/index.md
index 82eef431269..3e3683e0cab 100644
--- a/doc/development/contributing/index.md
+++ b/doc/development/contributing/index.md
@@ -62,8 +62,7 @@ Please help other GitLab users when you can.
The methods people will use to seek help can be found on the [getting help page](https://about.gitlab.com/get-help/).
Sign up for the mailing list, answer GitLab questions on StackOverflow or
-respond in the IRC channel. You can also sign up on [CodeTriage](https://www.codetriage.com/gitlabhq/gitlabhq) to help with
-the remaining issues on the GitHub issue tracker.
+respond in the IRC channel.
## I want to contribute
diff --git a/doc/user/project/pipelines/schedules.md b/doc/user/project/pipelines/schedules.md
index bb3e37ef2bf..bffb0b58230 100644
--- a/doc/user/project/pipelines/schedules.md
+++ b/doc/user/project/pipelines/schedules.md
@@ -84,7 +84,7 @@ For example, only two pipelines will be created per day if:
To change the Sidekiq worker's frequency:
1. Edit the `gitlab_rails['pipeline_schedule_worker_cron']` value in your instance's `gitlab.rb` file.
-1. [Restart GitLab](../../../administration/restart_gitlab.md).
+1. [Reconfigure GitLab](../../../administration/restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
For GitLab.com, refer to the [dedicated settings page](../../gitlab_com/index.md#cron-jobs).
diff --git a/doc/user/project/pipelines/settings.md b/doc/user/project/pipelines/settings.md
index 26ea328c730..67051b801f0 100644
--- a/doc/user/project/pipelines/settings.md
+++ b/doc/user/project/pipelines/settings.md
@@ -21,8 +21,8 @@ from GitLab in a job.
There are two options. Using:
- `git clone`, which is slower since it clones the repository from scratch
- for every job, ensuring that the project workspace is always pristine.
-- `git fetch`, which is faster as it re-uses the project workspace (falling
+ for every job, ensuring that the local working copy is always pristine.
+- `git fetch`, which is faster as it re-uses the local working copy (falling
back to clone if it doesn't exist).
The default Git strategy can be overridden by the [GIT_STRATEGY variable](../../../ci/yaml/README.md#git-strategy)
diff --git a/lib/gitlab/ci/config/external/mapper.rb b/lib/gitlab/ci/config/external/mapper.rb
index 5f7694e2cc6..0143d7784fa 100644
--- a/lib/gitlab/ci/config/external/mapper.rb
+++ b/lib/gitlab/ci/config/external/mapper.rb
@@ -7,7 +7,7 @@ module Gitlab
class Mapper
include Gitlab::Utils::StrongMemoize
- MAX_INCLUDES = 50
+ MAX_INCLUDES = 100
FILE_CLASSES = [
External::File::Remote,
diff --git a/lib/gitlab/gon_helper.rb b/lib/gitlab/gon_helper.rb
index 92917028851..2616a19fdaa 100644
--- a/lib/gitlab/gon_helper.rb
+++ b/lib/gitlab/gon_helper.rb
@@ -38,6 +38,10 @@ module Gitlab
gon.current_user_fullname = current_user.name
gon.current_user_avatar_url = current_user.avatar_url
end
+
+ # Initialize gon.features with any flags that should be
+ # made globally available to the frontend
+ push_frontend_feature_flag(:suppress_ajax_navigation_errors, default_enabled: true)
end
# Exposes the state of a feature flag to the frontend code.
diff --git a/package.json b/package.json
index 06d3ec255ee..82a4944f13e 100644
--- a/package.json
+++ b/package.json
@@ -61,7 +61,7 @@
"copy-webpack-plugin": "^5.0.4",
"core-js": "^3.2.1",
"cropper": "^2.3.0",
- "css-loader": "^3.2.0",
+ "css-loader": "^1.0.0",
"d3": "^4.13.0",
"d3-array": "^1.2.1",
"d3-axis": "^1.0.8",
diff --git a/spec/controllers/boards/lists_controller_spec.rb b/spec/controllers/boards/lists_controller_spec.rb
index 77b5872fcb3..bc46d02556b 100644
--- a/spec/controllers/boards/lists_controller_spec.rb
+++ b/spec/controllers/boards/lists_controller_spec.rb
@@ -14,6 +14,10 @@ describe Boards::ListsController do
end
describe 'GET index' do
+ before do
+ create(:list, board: board)
+ end
+
it 'returns a successful 200 response' do
read_board_list user: user, board: board
@@ -22,27 +26,22 @@ describe Boards::ListsController do
end
it 'returns a list of board lists' do
- create(:list, board: board)
-
read_board_list user: user, board: board
expect(response).to match_response_schema('lists')
expect(json_response.length).to eq 3
end
- it 'avoids n+1 queries when serializing lists' do
- list_1 = create(:list, board: board)
- list_1.update_preferences_for(user, { collapsed: true })
-
- control_count = ActiveRecord::QueryRecorder.new { read_board_list user: user, board: board }.count
-
- list_2 = create(:list, board: board)
- list_2.update_preferences_for(user, { collapsed: true })
+ context 'when another user has list preferences' do
+ before do
+ board.lists.first.update_preferences_for(guest, collapsed: true)
+ end
- list_3 = create(:list, board: board)
- list_3.update_preferences_for(user, { collapsed: true })
+ it 'returns the complete list of board lists' do
+ read_board_list user: user, board: board
- expect { read_board_list user: user, board: board }.not_to exceed_query_limit(control_count)
+ expect(json_response.length).to eq 3
+ end
end
context 'with unauthorized user' do
diff --git a/spec/features/issues/issues_tabs_spec.rb b/spec/features/issues/issues_tabs_spec.rb
new file mode 100644
index 00000000000..66d55dd9021
--- /dev/null
+++ b/spec/features/issues/issues_tabs_spec.rb
@@ -0,0 +1,56 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Issue page tabs', :js do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, :public) }
+ let(:issue) { create(:issue, author: user, assignees: [user], project: project) }
+
+ describe 'discussions tab counter' do
+ before do
+ stub_licensed_features(design_management: true)
+ stub_feature_flags(design_management_flag: true)
+ allow(Ability).to receive(:allowed?) { true }
+ end
+
+ subject do
+ sign_in(user)
+
+ visit project_issue_path(project, issue)
+
+ wait_for_requests
+
+ find('#discussion')
+ end
+
+ context 'new issue' do
+ it 'displays count of 0' do
+ is_expected.to have_content('Discussion 0')
+ end
+ end
+
+ context 'issue with 2 system notes and 1 discussion' do
+ let!(:discussion) { create(:discussion_note_on_issue, noteable: issue, project: project, note: "This is good") }
+
+ before do
+ create(:system_note, noteable: issue, project: project, author: user, note: 'description updated')
+ create(:system_note, noteable: issue, project: project, author: user, note: 'description updated')
+ end
+
+ it 'displays count of 1' do
+ is_expected.to have_content('Discussion 1')
+ end
+
+ context 'with 1 reply' do
+ before do
+ create(:note, noteable: issue, in_reply_to: discussion, discussion_id: discussion.discussion_id, note: 'I also think this is good')
+ end
+
+ it 'displays count of 2' do
+ is_expected.to have_content('Discussion 2')
+ end
+ end
+ end
+ end
+end
diff --git a/spec/frontend/lib/utils/suppress_ajax_errors_during_navigation_spec.js b/spec/frontend/lib/utils/suppress_ajax_errors_during_navigation_spec.js
new file mode 100644
index 00000000000..89e8459d594
--- /dev/null
+++ b/spec/frontend/lib/utils/suppress_ajax_errors_during_navigation_spec.js
@@ -0,0 +1,37 @@
+import suppressAjaxErrorsDuringNavigation from '~/lib/utils/suppress_ajax_errors_during_navigation';
+import waitForPromises from 'helpers/wait_for_promises';
+
+describe('suppressAjaxErrorsDuringNavigation', () => {
+ const OTHER_ERR_CODE = 'foo';
+ const NAV_ERR_CODE = 'ECONNABORTED';
+
+ it.each`
+ isFeatureFlagEnabled | isUserNavigating | code
+ ${false} | ${false} | ${OTHER_ERR_CODE}
+ ${false} | ${false} | ${NAV_ERR_CODE}
+ ${false} | ${true} | ${OTHER_ERR_CODE}
+ ${false} | ${true} | ${NAV_ERR_CODE}
+ ${true} | ${false} | ${OTHER_ERR_CODE}
+ ${true} | ${false} | ${NAV_ERR_CODE}
+ ${true} | ${true} | ${OTHER_ERR_CODE}
+ `('should return a rejected Promise', ({ isFeatureFlagEnabled, isUserNavigating, code }) => {
+ const err = { code };
+ const actual = suppressAjaxErrorsDuringNavigation(err, isUserNavigating, isFeatureFlagEnabled);
+
+ return expect(actual).rejects.toBe(err);
+ });
+
+ it('should return a Promise that never resolves', () => {
+ const err = { code: NAV_ERR_CODE };
+ const actual = suppressAjaxErrorsDuringNavigation(err, true, true);
+
+ const thenCallback = jest.fn();
+ const catchCallback = jest.fn();
+ actual.then(thenCallback).catch(catchCallback);
+
+ return waitForPromises().then(() => {
+ expect(thenCallback).not.toHaveBeenCalled();
+ expect(catchCallback).not.toHaveBeenCalled();
+ });
+ });
+});
diff --git a/spec/javascripts/frequent_items/components/app_spec.js b/spec/javascripts/frequent_items/components/app_spec.js
index 6814f656f5d..36dd8604d08 100644
--- a/spec/javascripts/frequent_items/components/app_spec.js
+++ b/spec/javascripts/frequent_items/components/app_spec.js
@@ -236,8 +236,15 @@ describe('Frequent Items App Component', () => {
.then(() => {
expect(vm.$el.querySelector('.loading-animation')).toBeDefined();
})
+
+ // This test waits for multiple ticks in order to allow the responses to
+ // propagate through each interceptor installed on the Axios instance.
+ // This shouldn't be necessary; this test should be refactored to avoid this.
+ // https://gitlab.com/gitlab-org/gitlab/issues/32479
+ .then(vm.$nextTick)
.then(vm.$nextTick)
.then(vm.$nextTick)
+
.then(() => {
expect(vm.$el.querySelectorAll('.frequent-items-list-container li').length).toBe(
mockSearchedProjects.length,
diff --git a/spec/javascripts/lib/utils/common_utils_spec.js b/spec/javascripts/lib/utils/common_utils_spec.js
index 85949f2ae86..8956bc92e6b 100644
--- a/spec/javascripts/lib/utils/common_utils_spec.js
+++ b/spec/javascripts/lib/utils/common_utils_spec.js
@@ -943,4 +943,14 @@ describe('common_utils', () => {
expect(commonUtils.isScopedLabel({ title: 'foobar' })).toBe(false);
});
});
+
+ describe('getDashPath', () => {
+ it('returns the path following /-/', () => {
+ expect(commonUtils.getDashPath('/some/-/url-with-dashes-/')).toEqual('url-with-dashes-/');
+ });
+
+ it('returns null when no path follows /-/', () => {
+ expect(commonUtils.getDashPath('/some/url')).toEqual(null);
+ });
+ });
});
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 5c0f8bd392a..8ae56ae2d4b 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -3079,6 +3079,12 @@ describe Ci::Build do
rescue StateMachines::InvalidTransition
end
+ it 'ensures pipeline ref existence' do
+ expect(job.pipeline.persistent_ref).to receive(:create).once
+
+ run_job_without_exception
+ end
+
shared_examples 'saves data on transition' do
it 'saves timeout' do
expect { job.run! }.to change { job.reload.ensure_metadata.timeout }.from(nil).to(expected_timeout)
diff --git a/spec/models/ci/persistent_ref_spec.rb b/spec/models/ci/persistent_ref_spec.rb
new file mode 100644
index 00000000000..71e0b03dff9
--- /dev/null
+++ b/spec/models/ci/persistent_ref_spec.rb
@@ -0,0 +1,108 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Ci::PersistentRef do
+ it 'cleans up persistent refs after pipeline finished' do
+ pipeline = create(:ci_pipeline, :running)
+
+ expect(pipeline.persistent_ref).to receive(:delete).once
+
+ pipeline.succeed!
+ end
+
+ context '#exist?' do
+ subject { pipeline.persistent_ref.exist? }
+
+ let(:pipeline) { create(:ci_pipeline, sha: sha, project: project) }
+ let(:project) { create(:project, :repository) }
+ let(:sha) { project.repository.commit.sha }
+
+ context 'when a persistent ref does not exist' do
+ it { is_expected.to eq(false) }
+ end
+
+ context 'when a persistent ref exists' do
+ before do
+ pipeline.persistent_ref.create
+ end
+
+ it { is_expected.to eq(true) }
+ end
+ end
+
+ context '#create' do
+ subject { pipeline.persistent_ref.create }
+
+ let(:pipeline) { create(:ci_pipeline, sha: sha, project: project) }
+ let(:project) { create(:project, :repository) }
+ let(:sha) { project.repository.commit.sha }
+
+ context 'when a persistent ref does not exist' do
+ it 'creates a persistent ref' do
+ subject
+
+ expect(pipeline.persistent_ref).to be_exist
+ end
+
+ context 'when depend_on_persistent_pipeline_ref feature flag is disabled' do
+ before do
+ stub_feature_flags(depend_on_persistent_pipeline_ref: false)
+ end
+
+ it 'does not create a persistent ref' do
+ expect(project.repository).not_to receive(:create_ref)
+
+ subject
+ end
+ end
+
+ context 'when sha does not exist in the repository' do
+ let(:sha) { 'not-exist' }
+
+ it 'fails to create a persistent ref' do
+ subject
+
+ expect(pipeline.persistent_ref).not_to be_exist
+ end
+ end
+ end
+
+ context 'when a persistent ref already exists' do
+ before do
+ pipeline.persistent_ref.create
+ end
+
+ it 'does not create a persistent ref' do
+ expect(project.repository).not_to receive(:create_ref)
+
+ subject
+ end
+ end
+ end
+
+ context '#delete' do
+ subject { pipeline.persistent_ref.delete }
+
+ let(:pipeline) { create(:ci_pipeline, sha: sha, project: project) }
+ let(:project) { create(:project, :repository) }
+ let(:sha) { project.repository.commit.sha }
+
+ context 'when a persistent ref exists' do
+ before do
+ pipeline.persistent_ref.create
+ end
+
+ it 'deletes the ref' do
+ expect { subject }.to change { pipeline.persistent_ref.exist? }
+ .from(true).to(false)
+ end
+ end
+
+ context 'when a persistent ref does not exist' do
+ it 'does not raise an error' do
+ expect { subject }.not_to raise_error
+ end
+ end
+ end
+end
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index fdd0cd8a3b7..a00d93ac4ba 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -1318,6 +1318,16 @@ describe Ci::Pipeline, :mailer do
let(:build_b) { create_build('build2', queued_at: 0) }
let(:build_c) { create_build('build3', queued_at: 0) }
+ %w[succeed! drop! cancel! skip!].each do |action|
+ context "when the pipeline recieved #{action} event" do
+ it 'deletes a persistent ref' do
+ expect(pipeline.persistent_ref).to receive(:delete).once
+
+ pipeline.public_send(action)
+ end
+ end
+ end
+
describe '#duration' do
context 'when multiple builds are finished' do
before do
diff --git a/spec/models/list_spec.rb b/spec/models/list_spec.rb
index dc28204d7aa..bc9124e73af 100644
--- a/spec/models/list_spec.rb
+++ b/spec/models/list_spec.rb
@@ -136,18 +136,6 @@ describe List do
expect(preferences).to be_persisted
expect(preferences.collapsed).to eq(true)
end
-
- context 'when preferences are already loaded for user' do
- it 'gets preloaded user preferences' do
- fetched_list = described_class.where(id: list.id).with_preferences_for(user).first
-
- expect(fetched_list).to receive(:preloaded_preferences_for).with(user).and_call_original
-
- preferences = fetched_list.preferences_for(user)
-
- expect(preferences.collapsed).to eq(true)
- end
- end
end
context 'when preferences for user does not exist' do
diff --git a/spec/presenters/ci/build_runner_presenter_spec.rb b/spec/presenters/ci/build_runner_presenter_spec.rb
index a4234d14255..fa8791f2257 100644
--- a/spec/presenters/ci/build_runner_presenter_spec.rb
+++ b/spec/presenters/ci/build_runner_presenter_spec.rb
@@ -207,5 +207,22 @@ describe Ci::BuildRunnerPresenter do
end
end
end
+
+ context 'when persistent pipeline ref exists' do
+ let(:project) { create(:project, :repository) }
+ let(:sha) { project.repository.commit.sha }
+ let(:pipeline) { create(:ci_pipeline, sha: sha, project: project) }
+ let(:build) { create(:ci_build, pipeline: pipeline) }
+
+ before do
+ pipeline.persistent_ref.create
+ end
+
+ it 'exposes the persistent pipeline ref' do
+ is_expected
+ .to contain_exactly("+refs/pipelines/#{pipeline.id}:refs/pipelines/#{pipeline.id}",
+ "+refs/heads/#{build.ref}:refs/remotes/origin/#{build.ref}")
+ end
+ end
end
end
diff --git a/spec/requests/boards/lists_controller_spec.rb b/spec/requests/boards/lists_controller_spec.rb
new file mode 100644
index 00000000000..7451ad93efd
--- /dev/null
+++ b/spec/requests/boards/lists_controller_spec.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Boards::ListsController do
+ describe '#index' do
+ let(:board) { create(:board) }
+ let(:user) { board.project.owner }
+
+ it 'does not have N+1 queries' do
+ login_as(user)
+
+ # First request has more queries because we create the default `backlog` list
+ get board_lists_path(board)
+
+ create(:list, board: board)
+
+ control_count = ActiveRecord::QueryRecorder.new { get board_lists_path(board) }.count
+
+ create_list(:list, 5, board: board)
+
+ expect { get board_lists_path(board) }.not_to exceed_query_limit(control_count)
+ end
+ end
+end
diff --git a/spec/services/ci/process_pipeline_service_spec.rb b/spec/services/ci/process_pipeline_service_spec.rb
index 1b28d2d4d02..05adec8b745 100644
--- a/spec/services/ci/process_pipeline_service_spec.rb
+++ b/spec/services/ci/process_pipeline_service_spec.rb
@@ -422,6 +422,18 @@ describe Ci::ProcessPipelineService, '#execute' do
end
end
+ context 'when an exception is raised during a persistent ref creation' do
+ before do
+ successful_build('test', stage_idx: 0)
+
+ allow_any_instance_of(Ci::PersistentRef).to receive(:delete_refs) { raise ArgumentError }
+ end
+
+ it 'process the pipeline' do
+ expect { process_pipeline }.not_to raise_error
+ end
+ end
+
context 'when there are manual action in earlier stages' do
context 'when first stage has only optional manual actions' do
before do
@@ -907,6 +919,10 @@ describe Ci::ProcessPipelineService, '#execute' do
create(:ci_build, :created, pipeline: pipeline, name: name, **opts)
end
+ def successful_build(name, **opts)
+ create(:ci_build, :success, pipeline: pipeline, name: name, **opts)
+ end
+
def delayed_options
{ when: 'delayed', options: { script: %w(echo), start_in: '1 minute' } }
end
diff --git a/spec/services/ci/register_job_service_spec.rb b/spec/services/ci/register_job_service_spec.rb
index 874945c8585..2f2c525ccc4 100644
--- a/spec/services/ci/register_job_service_spec.rb
+++ b/spec/services/ci/register_job_service_spec.rb
@@ -501,6 +501,22 @@ module Ci
expect(pending_job).to be_archived_failure
end
end
+
+ context 'when an exception is raised during a persistent ref creation' do
+ before do
+ allow_any_instance_of(Ci::PersistentRef).to receive(:exist?) { false }
+ allow_any_instance_of(Ci::PersistentRef).to receive(:create_ref) { raise ArgumentError }
+ end
+
+ subject { execute(specific_runner, {}) }
+
+ it 'picks the build' do
+ expect(subject).to eq(pending_job)
+
+ pending_job.reload
+ expect(pending_job).to be_running
+ end
+ end
end
describe '#register_success' do
diff --git a/yarn.lock b/yarn.lock
index ea91bad1970..9822733ac13 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2029,6 +2029,15 @@ axios@^0.19.0:
follow-redirects "1.5.10"
is-buffer "^2.0.2"
+babel-code-frame@^6.26.0:
+ version "6.26.0"
+ resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b"
+ integrity sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=
+ dependencies:
+ chalk "^1.1.3"
+ esutils "^2.0.2"
+ js-tokens "^3.0.2"
+
babel-eslint@^10.0.1:
version "10.0.1"
resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.0.1.tgz#919681dc099614cd7d31d45c8908695092a1faed"
@@ -2638,7 +2647,7 @@ camelcase@^4.0.0, camelcase@^4.1.0:
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd"
integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=
-camelcase@^5.0.0, camelcase@^5.3.1:
+camelcase@^5.0.0:
version "5.3.1"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
@@ -2686,7 +2695,7 @@ chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.4.
escape-string-regexp "^1.0.5"
supports-color "^5.3.0"
-chalk@^1.1.1:
+chalk@^1.1.1, chalk@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=
@@ -3401,29 +3410,38 @@ css-b64-images@~0.2.5:
resolved "https://registry.yarnpkg.com/css-b64-images/-/css-b64-images-0.2.5.tgz#42005d83204b2b4a5d93b6b1a5644133b5927a02"
integrity sha1-QgBdgyBLK0pdk7axpWRBM7WSegI=
-css-loader@^3.2.0:
- version "3.2.0"
- resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-3.2.0.tgz#bb570d89c194f763627fcf1f80059c6832d009b2"
- integrity sha512-QTF3Ud5H7DaZotgdcJjGMvyDj5F3Pn1j/sC6VBEOVp94cbwqyIBdcs/quzj4MC1BKQSrTpQznegH/5giYbhnCQ==
+css-loader@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-1.0.1.tgz#6885bb5233b35ec47b006057da01cc640b6b79fe"
+ integrity sha512-+ZHAZm/yqvJ2kDtPne3uX0C+Vr3Zn5jFn2N4HywtS5ujwvsVkyg0VArEXpl3BgczDA8anieki1FIzhchX4yrDw==
dependencies:
- camelcase "^5.3.1"
- cssesc "^3.0.0"
- icss-utils "^4.1.1"
- loader-utils "^1.2.3"
- normalize-path "^3.0.0"
- postcss "^7.0.17"
- postcss-modules-extract-imports "^2.0.0"
- postcss-modules-local-by-default "^3.0.2"
- postcss-modules-scope "^2.1.0"
- postcss-modules-values "^3.0.0"
- postcss-value-parser "^4.0.0"
- schema-utils "^2.0.0"
+ babel-code-frame "^6.26.0"
+ css-selector-tokenizer "^0.7.0"
+ icss-utils "^2.1.0"
+ loader-utils "^1.0.2"
+ lodash "^4.17.11"
+ postcss "^6.0.23"
+ postcss-modules-extract-imports "^1.2.0"
+ postcss-modules-local-by-default "^1.2.0"
+ postcss-modules-scope "^1.1.0"
+ postcss-modules-values "^1.3.0"
+ postcss-value-parser "^3.3.0"
+ source-list-map "^2.0.0"
css-selector-parser@^1.3:
version "1.3.0"
resolved "https://registry.yarnpkg.com/css-selector-parser/-/css-selector-parser-1.3.0.tgz#5f1ad43e2d8eefbfdc304fcd39a521664943e3eb"
integrity sha1-XxrUPi2O77/cME/NOaUhZklD4+s=
+css-selector-tokenizer@^0.7.0:
+ version "0.7.1"
+ resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.7.1.tgz#a177271a8bca5019172f4f891fc6eed9cbf68d5d"
+ integrity sha512-xYL0AMZJ4gFzJQsHUKa5jiWWi2vH77WVNg7JYRyewwj6oPh4yb/y6Y9ZCw9dsj/9UauMhtuxR+ogQd//EdEVNA==
+ dependencies:
+ cssesc "^0.1.0"
+ fastparse "^1.1.1"
+ regexpu-core "^1.0.0"
+
css@^2.1.0:
version "2.2.4"
resolved "https://registry.yarnpkg.com/css/-/css-2.2.4.tgz#c646755c73971f2bba6a601e2cf2fd71b1298929"
@@ -3434,6 +3452,11 @@ css@^2.1.0:
source-map-resolve "^0.5.2"
urix "^0.1.0"
+cssesc@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-0.1.0.tgz#c814903e45623371a0477b40109aaafbeeaddbb4"
+ integrity sha1-yBSQPkViM3GgR3tAEJqq++6t27Q=
+
cssesc@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-2.0.0.tgz#3b13bd1bb1cb36e1bcb5a4dcd27f54c5dcb35703"
@@ -5041,6 +5064,11 @@ fast-levenshtein@~2.0.4:
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
+fastparse@^1.1.1:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.2.tgz#91728c5a5942eced8531283c79441ee4122c35a9"
+ integrity sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==
+
fault@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/fault/-/fault-1.0.2.tgz#c3d0fec202f172a3a4d414042ad2bb5e2a3ffbaa"
@@ -6092,12 +6120,17 @@ iconv-lite@0.4, iconv-lite@0.4.24, iconv-lite@^0.4.22, iconv-lite@^0.4.4:
dependencies:
safer-buffer ">= 2.1.2 < 3"
-icss-utils@^4.0.0, icss-utils@^4.1.1:
- version "4.1.1"
- resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-4.1.1.tgz#21170b53789ee27447c2f47dd683081403f9a467"
- integrity sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA==
+icss-replace-symbols@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded"
+ integrity sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=
+
+icss-utils@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-2.1.0.tgz#83f0a0ec378bf3246178b6c2ad9136f135b1c962"
+ integrity sha1-g/Cg7DeL8yRheLbCrZE28TWxyWI=
dependencies:
- postcss "^7.0.14"
+ postcss "^6.0.1"
ieee754@1.1.8, ieee754@^1.1.4:
version "1.1.8"
@@ -7268,6 +7301,11 @@ js-levenshtein@^1.1.3:
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
+js-tokens@^3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
+ integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls=
+
js-yaml@^3.10.0, js-yaml@^3.12.0, js-yaml@^3.13.1:
version "3.13.1"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847"
@@ -9445,38 +9483,36 @@ postcss-media-query-parser@^0.2.3:
resolved "https://registry.yarnpkg.com/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz#27b39c6f4d94f81b1a73b8f76351c609e5cef244"
integrity sha1-J7Ocb02U+Bsac7j3Y1HGCeXO8kQ=
-postcss-modules-extract-imports@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz#818719a1ae1da325f9832446b01136eeb493cd7e"
- integrity sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ==
+postcss-modules-extract-imports@^1.2.0:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.1.tgz#dc87e34148ec7eab5f791f7cd5849833375b741a"
+ integrity sha512-6jt9XZwUhwmRUhb/CkyJY020PYaPJsCyt3UjbaWo6XEbH/94Hmv6MP7fG2C5NDU/BcHzyGYxNtHvM+LTf9HrYw==
dependencies:
- postcss "^7.0.5"
+ postcss "^6.0.1"
-postcss-modules-local-by-default@^3.0.2:
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.2.tgz#e8a6561be914aaf3c052876377524ca90dbb7915"
- integrity sha512-jM/V8eqM4oJ/22j0gx4jrp63GSvDH6v86OqyTHHUvk4/k1vceipZsaymiZ5PvocqZOl5SFHiFJqjs3la0wnfIQ==
+postcss-modules-local-by-default@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz#f7d80c398c5a393fa7964466bd19500a7d61c069"
+ integrity sha1-99gMOYxaOT+nlkRmvRlQCn1hwGk=
dependencies:
- icss-utils "^4.1.1"
- postcss "^7.0.16"
- postcss-selector-parser "^6.0.2"
- postcss-value-parser "^4.0.0"
+ css-selector-tokenizer "^0.7.0"
+ postcss "^6.0.1"
-postcss-modules-scope@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-2.1.0.tgz#ad3f5bf7856114f6fcab901b0502e2a2bc39d4eb"
- integrity sha512-91Rjps0JnmtUB0cujlc8KIKCsJXWjzuxGeT/+Q2i2HXKZ7nBUeF9YQTZZTNvHVoNYj1AthsjnGLtqDUE0Op79A==
+postcss-modules-scope@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz#d6ea64994c79f97b62a72b426fbe6056a194bb90"
+ integrity sha1-1upkmUx5+XtipytCb75gVqGUu5A=
dependencies:
- postcss "^7.0.6"
- postcss-selector-parser "^6.0.0"
+ css-selector-tokenizer "^0.7.0"
+ postcss "^6.0.1"
-postcss-modules-values@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-3.0.0.tgz#5b5000d6ebae29b4255301b4a3a54574423e7f10"
- integrity sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg==
+postcss-modules-values@^1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz#ecffa9d7e192518389f42ad0e83f72aec456ea20"
+ integrity sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA=
dependencies:
- icss-utils "^4.0.0"
- postcss "^7.0.6"
+ icss-replace-symbols "^1.1.0"
+ postcss "^6.0.1"
postcss-reporter@^6.0.1:
version "6.0.1"
@@ -9533,7 +9569,7 @@ postcss-selector-parser@^5.0.0:
indexes-of "^1.0.1"
uniq "^1.0.1"
-postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2:
+postcss-selector-parser@^6.0.2:
version "6.0.2"
resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz#934cf799d016c83411859e09dcecade01286ec5c"
integrity sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==
@@ -9547,7 +9583,7 @@ postcss-syntax@^0.36.2:
resolved "https://registry.yarnpkg.com/postcss-syntax/-/postcss-syntax-0.36.2.tgz#f08578c7d95834574e5593a82dfbfa8afae3b51c"
integrity sha512-nBRg/i7E3SOHWxF3PpF5WnJM/jQ1YpY9000OaVXlAQj6Zp/kIqJxEDWIZ67tAd7NLuk7zqN4yqe9nc0oNAOs1w==
-postcss-value-parser@^3.3.1:
+postcss-value-parser@^3.3.0, postcss-value-parser@^3.3.1:
version "3.3.1"
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281"
integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==
@@ -9557,7 +9593,7 @@ postcss-value-parser@^4.0.0:
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.0.0.tgz#99a983d365f7b2ad8d0f9b8c3094926eab4b936d"
integrity sha512-ESPktioptiSUchCKgggAkzdmkgzKfmp0EU8jXH+5kbIUB+unr0Y4CY9SRMvibuvYUBjNh1ACLbxqYNpdTQOteQ==
-postcss@^6.0.14:
+postcss@^6.0.1, postcss@^6.0.14, postcss@^6.0.23:
version "6.0.23"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.23.tgz#61c82cc328ac60e677645f979054eb98bc0e3324"
integrity sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==
@@ -9566,7 +9602,7 @@ postcss@^6.0.14:
source-map "^0.6.1"
supports-color "^5.4.0"
-postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.16, postcss@^7.0.17, postcss@^7.0.2, postcss@^7.0.5, postcss@^7.0.6, postcss@^7.0.7:
+postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.17, postcss@^7.0.2, postcss@^7.0.7:
version "7.0.18"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.18.tgz#4b9cda95ae6c069c67a4d933029eddd4838ac233"
integrity sha512-/7g1QXXgegpF+9GJj4iN7ChGF40sYuGYJ8WZu8DZWnmhQ/G36hfdk3q9LBJmoK+lZ+yzZ5KYpOoxq7LF1BxE8g==
@@ -10138,7 +10174,7 @@ regenerate-unicode-properties@^8.1.0:
dependencies:
regenerate "^1.4.0"
-regenerate@^1.4.0:
+regenerate@^1.2.1, regenerate@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11"
integrity sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==
@@ -10163,6 +10199,15 @@ regexpp@^2.0.1:
resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f"
integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==
+regexpu-core@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-1.0.0.tgz#86a763f58ee4d7c2f6b102e4764050de7ed90c6b"
+ integrity sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=
+ dependencies:
+ regenerate "^1.2.1"
+ regjsgen "^0.2.0"
+ regjsparser "^0.1.4"
+
regexpu-core@^4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.6.0.tgz#2037c18b327cfce8a6fea2a4ec441f2432afb8b6"
@@ -10190,11 +10235,23 @@ registry-url@^3.0.3:
dependencies:
rc "^1.0.1"
+regjsgen@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.2.0.tgz#6c016adeac554f75823fe37ac05b92d5a4edb1f7"
+ integrity sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=
+
regjsgen@^0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.0.tgz#a7634dc08f89209c2049adda3525711fb97265dd"
integrity sha512-RnIrLhrXCX5ow/E5/Mh2O4e/oa1/jW0eaBKTSy3LaCj+M3Bqvm97GWDp2yUtzIs4LEn65zR2yiYGFqb2ApnzDA==
+regjsparser@^0.1.4:
+ version "0.1.5"
+ resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.1.5.tgz#7ee8f84dc6fa792d3fd0ae228d24bd949ead205c"
+ integrity sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=
+ dependencies:
+ jsesc "~0.5.0"
+
regjsparser@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.0.tgz#f1e6ae8b7da2bae96c99399b868cd6c933a2ba9c"