diff --git a/app/assets/javascripts/lib/utils/axios_utils.js b/app/assets/javascripts/lib/utils/axios_utils.js index c17f62c671cdefa52ef69c2c28084c189f7b5b5f..a04fe609015407fbd01adc79211391d5301980c1 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 6e8f63a10a465761977c8f16b04a7bf11ce3ae03..7c4373ba51791c3360d785a3e4bd9f7de233cc5a 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 0000000000000000000000000000000000000000..4c61da9b86278595ae56b08c4c64b7aa3ef624dd --- /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 16a0fb3f33a7a2a9affa575cbd82df9c9d1ca615..fd592e965bfe8349bfc9fe0d16a8749f2cc7aa4b 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 01ef3f1db2bdc0819587f1e4abf127bf56d27b50..37b253d7c48d64587777aba4d94d810f181b30bf 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 b64b18505b672b9fa40b82e4c34b22dd6ec017d7..90e04414d8d4828b987601258146861154aebb63 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 3ef0ed0ef4972f7d3e8c9b1a244c81bc57bf7d48..5cca18024c10bd69f01d7ec2c7e0f77454f78ef2 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 0000000000000000000000000000000000000000..9bb67c88577a90e04d02bce71caeebefc37d12b2 --- /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 12295c0457d01009051be670b1914e0cd416736d..b22a87e7cefef4312bdf3d77e3b1a43fc5ea6b90 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 b4a4631b397153aad8f2d1b90d898e02cf431755..b53e2fb7c33ce1340b6984e9665b95e1acb6b093 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 fb84bbaca569d7228a542fff1ff245efba452410..6c6023a8709e42d4c4553b55f792b07a6e9612a0 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 5231a8efa551f879462a6b0eb275bdfe8ae52b06..8e46979558137b9d5191298d2e64a8606067f52a 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 3609d9c6283e85f47b9df7a1cc241653f7513b6a..82cba1b68c4fed43c52cc05fd79bf21d8af57329 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 0000000000000000000000000000000000000000..165b6727fe9fa4b567d7699b9ac3d5d983ff4636 --- /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 0000000000000000000000000000000000000000..6484bab1da4db31ec37c63d7498c3711c2b77fdf --- /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 0000000000000000000000000000000000000000..695098d41ff3b419edfebe9f864d26ff461706db --- /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 0000000000000000000000000000000000000000..5fed53e8f24d50a9838e5e80b59c3990487fa4d1 --- /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 0000000000000000000000000000000000000000..04909152aa88a49242076d1a21fb2c2b7651c6d5 --- /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 e9f9fe4c40208bed9f7294c3540b16866cc5e9aa..b5656040da2b55e94edbee0ae3ac8af66f6dec80 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 0000000000000000000000000000000000000000..ef7583508f214c71fbb2643deedcd28aac4bf576 --- /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 e66e3fa6881db7b4ba2cf4d9922c17658c1270f0..be7741695172f9ebc1a7ddcfbf9968bde0c3bb90 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 e1c5848af03e3d24b2fe969bfeb0e383f56496c6..ced6efc415b3ab48ed42583680766d14f90e1f25 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 2a077e66ec05364aabeec9ea0738f53c87574778..bb40feea10ea79e2763aa5adfaeaf53210005c32 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 e6d83d31e36b8c19d8327c85a83e58bf93422eea..f022012e2bcf38ab201eecdd302036208ef45045 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 82eef431269a844031faab50e160e7ab6963583b..3e3683e0cab84a816dba94d024bd2f259586e540 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 bb3e37ef2bf5499f9b19ce10980427f144315354..bffb0b5823031644703390f135c4ddb0e2162b53 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 26ea328c730a99af17e93d94a43313ba1aeaeddd..67051b801f0d7c0057c0d73182364ce56c3e1aa4 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 5f7694e2cc64d36e8c72f2f6a7132d042601819b..0143d7784fa6419a24ba2626793a6361903b1a07 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 92917028851eaa52398f1b820ed34153099f2e1b..2616a19fdaad54a4d52609681364e5bcfe2154bc 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 06d3ec255eed44077632f95a1d781d1c7dcade4b..82a4944f13e2d136c24772af17abe4bd35163401 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 77b5872fcb3069ad3eeaaed2875ad53792340231..bc46d02556b31ad337680fa58ee4741dba3b4b23 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 0000000000000000000000000000000000000000..66d55dd9021731a554c30588ea8af70d520bce36 --- /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 0000000000000000000000000000000000000000..89e8459d5942d40d868f72c765399265a2b2783f --- /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 6814f656f5d49c750981fedd9c28eebb5df95a44..36dd8604d0885d209335e7d4bf3f59237c387c98 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 85949f2ae864a660d4d74978c62d963e022e2d93..8956bc92e6b0b3f3d2bf1e7c0d4bfde59bde52f6 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 5c0f8bd392ab705f8871af2beee1153931bac571..8ae56ae2d4b87ad6d329e210e514af2a28d02763 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 0000000000000000000000000000000000000000..71e0b03dff96ae86da18f1f6c711c0c394ab2f7a --- /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 fdd0cd8a3b79735e014e74e65e96f320c6bb1d0d..a00d93ac4ba0e6d1b46a5989883ecd13988be927 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 dc28204d7aaaee2c062a055a181a8e0a1471033e..bc9124e73af84275ad4ae3a1664a64ef794a1004 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 a4234d14255585f3113edbe42c8a0f45f833c658..fa8791f225741b776b4e99784eb92154355f5a6c 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 0000000000000000000000000000000000000000..7451ad93efd5075647bee3aa46b22a440bd41ed1 --- /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 1b28d2d4d02b095341b963b3def14227fb62883f..05adec8b7454bb371a6f6cef89a6ac1529dd8565 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 874945c85852055471826a13416788e1ef4b6a89..2f2c525ccc45fbc9a939d6c54913cdc7a9e2ee3e 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 ea91bad1970a73f10f59f8e6100cdc049d78c8cb..9822733ac13c03d592358c45c90edb2c531998fe 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"