From 2abb1b54c0305b359b178d6660810e865f619c22 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Fri, 27 Sep 2019 18:06:20 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- .../javascripts/lib/utils/axios_utils.js | 15 ++ .../javascripts/lib/utils/common_utils.js | 2 + .../suppress_ajax_errors_during_navigation.js | 16 ++ .../notes/components/notes_app.vue | 7 +- .../pages/groups/shared/group_details.js | 7 +- app/controllers/boards/lists_controller.rb | 7 +- app/models/ci/build.rb | 1 + app/models/ci/persistent_ref.rb | 54 ++++++ app/models/ci/pipeline.rb | 6 + app/models/list.rb | 36 ++-- app/models/repository.rb | 3 +- app/presenters/ci/build_runner_presenter.rb | 17 +- app/services/boards/lists/list_service.rb | 2 +- .../unreleased/53041-groups-named-shared.yml | 5 + .../unreleased/dedicated-pipeline-ref.yml | 6 + ...2885-add-trigram-index-snippet-content.yml | 5 + ...increase-limit-for-recursively-include.yml | 5 + .../nfriend-suppress-ajax-errors-again.yml | 5 + config/webpack.config.js | 4 +- ...0927074328_add_index_on_snippet_content.rb | 18 ++ db/schema.rb | 3 +- .../pipelines_for_merged_results/index.md | 11 ++ doc/ci/pipelines.md | 41 +++++ doc/ci/yaml/README.md | 16 +- doc/development/contributing/index.md | 3 +- doc/user/project/pipelines/schedules.md | 2 +- doc/user/project/pipelines/settings.md | 4 +- lib/gitlab/ci/config/external/mapper.rb | 2 +- lib/gitlab/gon_helper.rb | 4 + package.json | 2 +- .../boards/lists_controller_spec.rb | 25 ++- spec/features/issues/issues_tabs_spec.rb | 56 ++++++ ...ress_ajax_errors_during_navigation_spec.js | 37 ++++ .../frequent_items/components/app_spec.js | 7 + .../lib/utils/common_utils_spec.js | 10 ++ spec/models/ci/build_spec.rb | 6 + spec/models/ci/persistent_ref_spec.rb | 108 ++++++++++++ spec/models/ci/pipeline_spec.rb | 10 ++ spec/models/list_spec.rb | 12 -- .../ci/build_runner_presenter_spec.rb | 17 ++ spec/requests/boards/lists_controller_spec.rb | 25 +++ .../ci/process_pipeline_service_spec.rb | 16 ++ spec/services/ci/register_job_service_spec.rb | 16 ++ yarn.lock | 163 ++++++++++++------ 44 files changed, 685 insertions(+), 132 deletions(-) create mode 100644 app/assets/javascripts/lib/utils/suppress_ajax_errors_during_navigation.js create mode 100644 app/models/ci/persistent_ref.rb create mode 100644 changelogs/unreleased/53041-groups-named-shared.yml create mode 100644 changelogs/unreleased/dedicated-pipeline-ref.yml create mode 100644 changelogs/unreleased/fj-32885-add-trigram-index-snippet-content.yml create mode 100644 changelogs/unreleased/increase-limit-for-recursively-include.yml create mode 100644 changelogs/unreleased/nfriend-suppress-ajax-errors-again.yml create mode 100644 db/migrate/20190927074328_add_index_on_snippet_content.rb create mode 100644 spec/features/issues/issues_tabs_spec.rb create mode 100644 spec/frontend/lib/utils/suppress_ajax_errors_during_navigation_spec.js create mode 100644 spec/models/ci/persistent_ref_spec.rb create mode 100644 spec/requests/boards/lists_controller_spec.rb 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//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/`, + 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" -- GitLab