diff --git a/.stylelintrc b/.stylelintrc index 241d2c94a8899022ed1a362d8904f42f3da14eea..59ee0e879e6d87aa89cf4613b02c722322e4b0fd 100644 --- a/.stylelintrc +++ b/.stylelintrc @@ -64,7 +64,7 @@ "number-leading-zero":"always", "number-no-trailing-zeros":true, "property-no-unknown":true, - "property-no-vendor-prefix":true, + "property-no-vendor-prefix": [true, { "ignoreProperties": ["user-select"] }], "rule-empty-line-before":[ "always-multi-line", { diff --git a/GITLAB_WORKHORSE_VERSION b/GITLAB_WORKHORSE_VERSION index f9c71a52e2fd865dbf40db779b5bf8157d5c5a6a..85e2cd530929dd50759ff567d66966b1778b9db7 100644 --- a/GITLAB_WORKHORSE_VERSION +++ b/GITLAB_WORKHORSE_VERSION @@ -1 +1 @@ -8.5.1 +8.5.2 diff --git a/app/assets/javascripts/monitoring/components/dashboard.vue b/app/assets/javascripts/monitoring/components/dashboard.vue index f2bd4150b6dee4855fbfb4d7f1c4347e4785d144..00547abd7bc4648a1157dccd7614b5eaa2f0a048 100644 --- a/app/assets/javascripts/monitoring/components/dashboard.vue +++ b/app/assets/javascripts/monitoring/components/dashboard.vue @@ -221,6 +221,7 @@ export default { {{ environment.name }} { const el = document.getElementById('prometheus-graphs'); diff --git a/app/assets/javascripts/mr_popover/index.js b/app/assets/javascripts/mr_popover/index.js index 9a97e98f9db9019658280e4a39292e614a80323c..18c0e2013001815da85e76677065c7adbf520c86 100644 --- a/app/assets/javascripts/mr_popover/index.js +++ b/app/assets/javascripts/mr_popover/index.js @@ -22,13 +22,10 @@ const handleUserPopoverMouseOut = ({ target }) => { * Adds a MergeRequestPopover component to the body, hands over as much data as the target element has in data attributes. * loads based on data-project-path and data-iid more data about an MR from the API and sets it on the popover */ -const handleMRPopoverMount = apolloProvider => ({ target }) => { +const handleMRPopoverMount = ({ apolloProvider, projectPath, mrTitle, iid }) => ({ target }) => { // Add listener to actually remove it again target.addEventListener('mouseleave', handleUserPopoverMouseOut); - const { projectPath, mrTitle, iid } = target.dataset; - const mergeRequest = {}; - renderFn = setTimeout(() => { const MRPopoverComponent = Vue.extend(MRPopover); renderedPopover = new MRPopoverComponent({ @@ -36,7 +33,6 @@ const handleMRPopoverMount = apolloProvider => ({ target }) => { target, projectPath, mergeRequestIID: iid, - mergeRequest, mergeRequestTitle: mrTitle, }, apolloProvider, @@ -57,8 +53,13 @@ export default elements => { const listenerAddedAttr = 'data-mr-listener-added'; mrLinks.forEach(el => { - if (!el.getAttribute(listenerAddedAttr)) { - el.addEventListener('mouseenter', handleMRPopoverMount(apolloProvider)); + const { projectPath, mrTitle, iid } = el.dataset; + + if (!el.getAttribute(listenerAddedAttr) && projectPath && mrTitle && iid) { + el.addEventListener( + 'mouseenter', + handleMRPopoverMount({ apolloProvider, projectPath, mrTitle, iid }), + ); el.setAttribute(listenerAddedAttr, true); } }); diff --git a/app/assets/javascripts/pipelines/pipeline_details_bundle.js b/app/assets/javascripts/pipelines/pipeline_details_bundle.js index 6660f8120f88e8dc79287f3d6088ef5fc4ffb8d6..b8976f77bac6c12014d2ef23f11400913672c581 100644 --- a/app/assets/javascripts/pipelines/pipeline_details_bundle.js +++ b/app/assets/javascripts/pipelines/pipeline_details_bundle.js @@ -34,6 +34,7 @@ export default () => { props: { isLoading: this.mediator.state.isLoading, pipeline: this.mediator.store.state.pipeline, + mediator: this.mediator, }, on: { refreshPipelineGraph: this.requestRefreshPipelineGraph, diff --git a/app/assets/javascripts/vue_shared/components/notes/system_note.vue b/app/assets/javascripts/vue_shared/components/notes/system_note.vue index acc179b38347161f388736683995753d587fbfd9..3c86b7e4c614e7b29ef04920f80f52084f68b41d 100644 --- a/app/assets/javascripts/vue_shared/components/notes/system_note.vue +++ b/app/assets/javascripts/vue_shared/components/notes/system_note.vue @@ -22,6 +22,7 @@ import noteHeader from '~/notes/components/note_header.vue'; import Icon from '~/vue_shared/components/icon.vue'; import TimelineEntryItem from './timeline_entry_item.vue'; import { spriteIcon } from '../../../lib/utils/common_utils'; +import initMRPopovers from '~/mr_popover/'; const MAX_VISIBLE_COMMIT_LIST_COUNT = 3; @@ -71,6 +72,9 @@ export default { ); }, }, + mounted() { + initMRPopovers(this.$el.querySelectorAll('.gfm-merge_request')); + }, }; diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss index fc1c1bd99621503a28234ceec1b06062f51aeaea..037ca9f954a642f73351c8f698c365ed47fad7ec 100644 --- a/app/assets/stylesheets/pages/boards.scss +++ b/app/assets/stylesheets/pages/boards.scss @@ -11,6 +11,9 @@ opacity: 1 !important; * { + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; user-select: none; // !important to make sure no style can override this when dragging cursor: grabbing !important; diff --git a/app/assets/stylesheets/pages/search.scss b/app/assets/stylesheets/pages/search.scss index 20bdc6596e9d1ed5b9a757847ed43a7ba9daa9e3..0c99ff5341c6c31ae323b1797804248e7a8eb112 100644 --- a/app/assets/stylesheets/pages/search.scss +++ b/app/assets/stylesheets/pages/search.scss @@ -84,6 +84,9 @@ input[type='checkbox']:hover { .search-icon { transition: color $default-transition-duration; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; user-select: none; } diff --git a/app/controllers/projects/settings/ci_cd_controller.rb b/app/controllers/projects/settings/ci_cd_controller.rb index d1c5cef76fa5dcbc9ea7acbe89df24af6d0f0170..c4dff95a4b970ba421148a57955742c4aa67f10d 100644 --- a/app/controllers/projects/settings/ci_cd_controller.rb +++ b/app/controllers/projects/settings/ci_cd_controller.rb @@ -19,7 +19,7 @@ module Projects redirect_to project_settings_ci_cd_path(@project) else - render 'show' + redirect_to project_settings_ci_cd_path(@project), alert: result[:message] end end end diff --git a/app/helpers/markup_helper.rb b/app/helpers/markup_helper.rb index d83c69603a91b13c0e27902ce64cb294734ba3e5..8aa96759ce52a2a636f23962eaf09d5bc8042b21 100644 --- a/app/helpers/markup_helper.rb +++ b/app/helpers/markup_helper.rb @@ -4,7 +4,7 @@ require 'nokogiri' module MarkupHelper include ActionView::Helpers::TagHelper - include ActionView::Context + include ::Gitlab::ActionViewOutput::Context def plain?(filename) Gitlab::MarkupHelper.plain?(filename) @@ -83,7 +83,8 @@ module MarkupHelper text = sanitize( text, tags: tags, - attributes: Rails::Html::WhiteListSanitizer.allowed_attributes + ['style', 'data-src', 'data-name', 'data-unicode-version'] + attributes: Rails::Html::WhiteListSanitizer.allowed_attributes + + %w(style data-src data-name data-unicode-version data-iid data-project-path data-mr-title) ) # since tags are stripped, this can leave empty tags hanging around diff --git a/app/models/ci/bridge.rb b/app/models/ci/bridge.rb index 0d8d7d957915995f246b5f21705e6a3e37142a9b..644716ba8e75e07541ca239714db5c5225c5ea85 100644 --- a/app/models/ci/bridge.rb +++ b/app/models/ci/bridge.rb @@ -4,6 +4,7 @@ module Ci class Bridge < CommitStatus include Ci::Processable include Ci::Contextable + include Ci::PipelineDelegator include Importable include AfterCommitQueue include HasRef @@ -13,8 +14,6 @@ module Ci belongs_to :trigger_request validates :ref, presence: true - delegate :merge_request_event?, to: :pipeline - def self.retry(bridge, current_user) raise NotImplementedError end diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 5cf9bb4979a96ec67c81c071a079acba70ecef6a..fc743475187ed4601ebc12887f86dcef47276e2e 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -6,6 +6,7 @@ module Ci include Ci::Processable include Ci::Metadatable include Ci::Contextable + include Ci::PipelineDelegator include TokenAuthenticatable include AfterCommitQueue include ObjectStorage::BackgroundMove @@ -48,8 +49,6 @@ module Ci delegate :terminal_specification, to: :runner_session, allow_nil: true delegate :gitlab_deploy_token, to: :project delegate :trigger_short_token, to: :trigger_request, allow_nil: true - delegate :merge_request_event?, :merge_request_ref?, - :legacy_detached_merge_request_pipeline?, to: :pipeline ## # Since Gitlab 11.5, deployments records started being created right after diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index fd65b2c733a3c0b8446973147a1a276d471c4984..4534ba49c0dc9e2d578b301c68392fa286012be1 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -754,6 +754,22 @@ module Ci self.sha == sha || self.source_sha == sha end + def triggered_by?(current_user) + user == current_user + end + + def source_ref + if triggered_by_merge_request? + merge_request.source_branch + else + ref + end + end + + def source_ref_slug + Gitlab::Utils.slugify(source_ref.to_s) + end + private def ci_yaml_from_repo diff --git a/app/models/concerns/ci/contextable.rb b/app/models/concerns/ci/contextable.rb index 4986a42dbd22d0de2b929e7b1fdb4abfaf80dce8..e1d5ce7f7d4371f64e51336bc916d02c1428f8af 100644 --- a/app/models/concerns/ci/contextable.rb +++ b/app/models/concerns/ci/contextable.rb @@ -70,8 +70,8 @@ module Ci variables.append(key: 'CI_COMMIT_SHA', value: sha) variables.append(key: 'CI_COMMIT_SHORT_SHA', value: short_sha) variables.append(key: 'CI_COMMIT_BEFORE_SHA', value: before_sha) - variables.append(key: 'CI_COMMIT_REF_NAME', value: ref) - variables.append(key: 'CI_COMMIT_REF_SLUG', value: ref_slug) + variables.append(key: 'CI_COMMIT_REF_NAME', value: source_ref) + variables.append(key: 'CI_COMMIT_REF_SLUG', value: source_ref_slug) variables.append(key: "CI_COMMIT_TAG", value: ref) if tag? variables.append(key: "CI_PIPELINE_TRIGGERED", value: 'true') if trigger_request variables.append(key: "CI_JOB_MANUAL", value: 'true') if action? @@ -85,8 +85,8 @@ module Ci Gitlab::Ci::Variables::Collection.new.tap do |variables| variables.append(key: 'CI_BUILD_REF', value: sha) variables.append(key: 'CI_BUILD_BEFORE_SHA', value: before_sha) - variables.append(key: 'CI_BUILD_REF_NAME', value: ref) - variables.append(key: 'CI_BUILD_REF_SLUG', value: ref_slug) + variables.append(key: 'CI_BUILD_REF_NAME', value: source_ref) + variables.append(key: 'CI_BUILD_REF_SLUG', value: source_ref_slug) variables.append(key: 'CI_BUILD_NAME', value: name) variables.append(key: 'CI_BUILD_STAGE', value: stage) variables.append(key: "CI_BUILD_TAG", value: ref) if tag? diff --git a/app/models/concerns/ci/pipeline_delegator.rb b/app/models/concerns/ci/pipeline_delegator.rb new file mode 100644 index 0000000000000000000000000000000000000000..dbc5ed1bc9a6519a88bbd0480c61c7f61973d456 --- /dev/null +++ b/app/models/concerns/ci/pipeline_delegator.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +## +# This module is mainly used by child associations of `Ci::Pipeline` that needs to look up +# single source of truth. For example, `Ci::Build` has `git_ref` method, which behaves +# slightly different from `Ci::Pipeline`'s `git_ref`. This is very confusing as +# the system could behave differently time to time. +# We should have a single interface in `Ci::Pipeline` and access the method always. +module Ci + module PipelineDelegator + extend ActiveSupport::Concern + + included do + delegate :merge_request_event?, + :merge_request_ref?, + :source_ref, + :source_ref_slug, + :legacy_detached_merge_request_pipeline?, to: :pipeline + end + end +end diff --git a/app/models/concerns/has_ref.rb b/app/models/concerns/has_ref.rb index 413cd36dcaaacced2992a66507e574ce77bea7f3..fa0cf5ddfd27b622a982191b4fb0dd997869b64d 100644 --- a/app/models/concerns/has_ref.rb +++ b/app/models/concerns/has_ref.rb @@ -1,5 +1,8 @@ # frozen_string_literal: true +## +# We will disable `ref` and `sha` attributes in `Ci::Build` in the future +# and remove this module in favor of Ci::PipelineDelegator. module HasRef extend ActiveSupport::Concern diff --git a/app/models/deployment.rb b/app/models/deployment.rb index d847a0a11e4d0472042f2f5c8bc8f141c9cd9ef2..332cd87c97a9a30dcbf222e9b0061fda99ea9d01 100644 --- a/app/models/deployment.rb +++ b/app/models/deployment.rb @@ -79,7 +79,11 @@ class Deployment < ApplicationRecord end def cluster - project.deployment_platform(environment: environment.name)&.cluster + platform = project.deployment_platform(environment: environment.name) + + if platform.present? && platform.respond_to?(:cluster) + platform.cluster + end end def last? diff --git a/app/serializers/concerns/user_status_tooltip.rb b/app/serializers/concerns/user_status_tooltip.rb index 633b117d392edbcc13bcf8d951aa36ca27a6ad45..a81e377691ec2255b0b684c54de39c2626d4cf51 100644 --- a/app/serializers/concerns/user_status_tooltip.rb +++ b/app/serializers/concerns/user_status_tooltip.rb @@ -3,7 +3,7 @@ module UserStatusTooltip extend ActiveSupport::Concern include ActionView::Helpers::TagHelper - include ActionView::Context + include ::Gitlab::ActionViewOutput::Context include EmojiHelper include UsersHelper diff --git a/app/views/search/_results.html.haml b/app/views/search/_results.html.haml index 5b25a67bc87b4f5ca98fa94c7360e9716136b469..8ae2807729b66c18597078ede7a7c4d604e3aad5 100644 --- a/app/views/search/_results.html.haml +++ b/app/views/search/_results.html.haml @@ -21,10 +21,9 @@ - if @scope == 'projects' .term = render 'shared/projects/list', projects: @search_objects, pipeline_status: false - - elsif %w[blobs wiki_blobs].include?(@scope) - = render partial: 'search/results/blob', collection: @search_objects, locals: { projects: blob_projects(@search_objects) } - else - = render partial: "search/results/#{@scope.singularize}", collection: @search_objects + - locals = { projects: blob_projects(@search_objects) } if %w[blobs wiki_blobs].include?(@scope) + = render partial: "search/results/#{@scope.singularize}", collection: @search_objects, locals: locals - if @scope != 'projects' = paginate_collection(@search_objects) diff --git a/app/workers/pipeline_schedule_worker.rb b/app/workers/pipeline_schedule_worker.rb index 02a69ea3b5445a0203bdf360c240e04f557fd11c..8a9ee7808e4f208260746bbc577b15aee70004e2 100644 --- a/app/workers/pipeline_schedule_worker.rb +++ b/app/workers/pipeline_schedule_worker.rb @@ -3,20 +3,26 @@ class PipelineScheduleWorker include ApplicationWorker include CronjobQueue + include ::Gitlab::ExclusiveLeaseHelpers + + EXCLUSIVE_LOCK_KEY = 'pipeline_schedules:run:lock' + LOCK_TIMEOUT = 50.minutes # rubocop: disable CodeReuse/ActiveRecord def perform - Ci::PipelineSchedule.active.where("next_run_at < ?", Time.now) - .preload(:owner, :project).find_each do |schedule| - - Ci::CreatePipelineService.new(schedule.project, - schedule.owner, - ref: schedule.ref) - .execute!(:schedule, ignore_skip_ci: true, save_on_errors: true, schedule: schedule) - rescue => e - error(schedule, e) - ensure - schedule.schedule_next_run! + in_lock(EXCLUSIVE_LOCK_KEY, ttl: LOCK_TIMEOUT, retries: 1) do + Ci::PipelineSchedule.active.where("next_run_at < ?", Time.now) + .preload(:owner, :project).find_each do |schedule| + + schedule.schedule_next_run! + + Ci::CreatePipelineService.new(schedule.project, + schedule.owner, + ref: schedule.ref) + .execute!(:schedule, ignore_skip_ci: true, save_on_errors: true, schedule: schedule) + rescue => e + error(schedule, e) + end end end # rubocop: enable CodeReuse/ActiveRecord diff --git a/changelogs/unreleased/54656-500-error-on-save-of-general-pipeline-settings-timeout.yml b/changelogs/unreleased/54656-500-error-on-save-of-general-pipeline-settings-timeout.yml new file mode 100644 index 0000000000000000000000000000000000000000..8b4f4894048776c9e71cd6c07e8a972f6e08e77e --- /dev/null +++ b/changelogs/unreleased/54656-500-error-on-save-of-general-pipeline-settings-timeout.yml @@ -0,0 +1,5 @@ +--- +title: Fix 500 in general pipeline settings when passing an invalid build timeout. +merge_request: 27416 +author: +type: fixed diff --git a/changelogs/unreleased/60540-merge-request-popover-is-not-working-on-the-to-do-page.yml b/changelogs/unreleased/60540-merge-request-popover-is-not-working-on-the-to-do-page.yml new file mode 100644 index 0000000000000000000000000000000000000000..7b5fae016bb98f73216d016367d00f512640d15e --- /dev/null +++ b/changelogs/unreleased/60540-merge-request-popover-is-not-working-on-the-to-do-page.yml @@ -0,0 +1,5 @@ +--- +title: Fix MR popover on ToDos page +merge_request: 27382 +author: +type: fixed diff --git a/changelogs/unreleased/60687-enviro-dropdown.yml b/changelogs/unreleased/60687-enviro-dropdown.yml new file mode 100644 index 0000000000000000000000000000000000000000..1fc5a7dd6f579d9b70802309109541408a989dd2 --- /dev/null +++ b/changelogs/unreleased/60687-enviro-dropdown.yml @@ -0,0 +1,5 @@ +--- +title: Fix Metrics Environments dropdown +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/60821-deployment-jobs-broken-as-of-11-10-0.yml b/changelogs/unreleased/60821-deployment-jobs-broken-as-of-11-10-0.yml new file mode 100644 index 0000000000000000000000000000000000000000..88584352d426293fa6ef6d887140932d09805a43 --- /dev/null +++ b/changelogs/unreleased/60821-deployment-jobs-broken-as-of-11-10-0.yml @@ -0,0 +1,5 @@ +--- +title: Fix Kubernetes service template deployment jobs broken as of 11.10.0 +merge_request: 27687 +author: +type: fixed diff --git a/changelogs/unreleased/60855-mr-popover-is-not-attached-in-system-notes.yml b/changelogs/unreleased/60855-mr-popover-is-not-attached-in-system-notes.yml new file mode 100644 index 0000000000000000000000000000000000000000..f7017ddf3dd772819195ae84b9623b1d2244b0ae --- /dev/null +++ b/changelogs/unreleased/60855-mr-popover-is-not-attached-in-system-notes.yml @@ -0,0 +1,5 @@ +--- +title: Fix bug where system note MR has no popover +merge_request: 27589 +author: +type: fixed diff --git a/changelogs/unreleased/60906-fix-wiki-links.yml b/changelogs/unreleased/60906-fix-wiki-links.yml new file mode 100644 index 0000000000000000000000000000000000000000..cc65a1382bf9f3ce7b0129d0c9eda17feba051ef --- /dev/null +++ b/changelogs/unreleased/60906-fix-wiki-links.yml @@ -0,0 +1,5 @@ +--- +title: Show proper wiki links in search results +merge_request: 27634 +author: +type: fixed diff --git a/changelogs/unreleased/fix-ci-commit-ref-name-and-slug.yml b/changelogs/unreleased/fix-ci-commit-ref-name-and-slug.yml new file mode 100644 index 0000000000000000000000000000000000000000..c34bc6d8b52e180e2abef7a47edf319d7ef2b6f1 --- /dev/null +++ b/changelogs/unreleased/fix-ci-commit-ref-name-and-slug.yml @@ -0,0 +1,5 @@ +--- +title: Make `CI_COMMIT_REF_NAME` and `SLUG` variable idempotent +merge_request: 27663 +author: +type: fixed diff --git a/changelogs/unreleased/fj-60827-fix-web-strategy-error.yml b/changelogs/unreleased/fj-60827-fix-web-strategy-error.yml new file mode 100644 index 0000000000000000000000000000000000000000..ad707ca0225b8262f1e3374fb4093be8b9a48f1b --- /dev/null +++ b/changelogs/unreleased/fj-60827-fix-web-strategy-error.yml @@ -0,0 +1,5 @@ +--- +title: Fix bug when project export to remote url fails +merge_request: 27614 +author: +type: fixed diff --git a/changelogs/unreleased/lock-pipeline-schedule-worker.yml b/changelogs/unreleased/lock-pipeline-schedule-worker.yml new file mode 100644 index 0000000000000000000000000000000000000000..1b889f0162047effe9cf3998f38199925ddb0483 --- /dev/null +++ b/changelogs/unreleased/lock-pipeline-schedule-worker.yml @@ -0,0 +1,5 @@ +--- +title: Prevent concurrent execution of PipelineScheduleWorker +merge_request: 27781 +author: +type: performance diff --git a/changelogs/unreleased/sh-fix-slow-partial-rendering.yml b/changelogs/unreleased/sh-fix-slow-partial-rendering.yml new file mode 100644 index 0000000000000000000000000000000000000000..0f65a6f8d698e3b5fa8798599bcd83782b02a453 --- /dev/null +++ b/changelogs/unreleased/sh-fix-slow-partial-rendering.yml @@ -0,0 +1,5 @@ +--- +title: Fix slow performance with compiling HAML templates +merge_request: 27782 +author: +type: performance diff --git a/changelogs/unreleased/update-workhorse-11-10.yml b/changelogs/unreleased/update-workhorse-11-10.yml new file mode 100644 index 0000000000000000000000000000000000000000..5172a2860f3f409974eacf5d09fa309f81d3fb73 --- /dev/null +++ b/changelogs/unreleased/update-workhorse-11-10.yml @@ -0,0 +1,5 @@ +--- +title: Update Workhorse to v8.5.2 +merge_request: 27631 +author: +type: fixed diff --git a/changelogs/unreleased/winh-boards-drag-selection.yml b/changelogs/unreleased/winh-boards-drag-selection.yml new file mode 100644 index 0000000000000000000000000000000000000000..84b23ab664b336c902ddda2941aa0e2413f1247a --- /dev/null +++ b/changelogs/unreleased/winh-boards-drag-selection.yml @@ -0,0 +1,5 @@ +--- +title: Prevent text selection when dragging in issue boards +merge_request: 27724 +author: +type: fixed diff --git a/doc/administration/high_availability/nfs.md b/doc/administration/high_availability/nfs.md index f406163aea051670b3d34a632ba53b2f7b4276e1..9d956b8442637d6b85c99b87bf839ce4ab11bd5b 100644 --- a/doc/administration/high_availability/nfs.md +++ b/doc/administration/high_availability/nfs.md @@ -39,7 +39,14 @@ options: ### Improving NFS performance with GitLab -NOTE: **Note:** This is only available with GitLab 11.9 and up. +NOTE: **Note:** +This is only available starting in certain versions of GitLab: + + * 11.5.11 + * 11.6.11 + * 11.7.12 + * 11.8.8 + * 11.9.0 and up (e.g. 11.10, 11.11, etc.) If you are using NFS to share Git data, we recommend that you enable a number of feature flags that will allow GitLab application processes to diff --git a/lib/gitlab/action_view_output/context.rb b/lib/gitlab/action_view_output/context.rb new file mode 100644 index 0000000000000000000000000000000000000000..9fbc9811636e4c935fba2f6f7e8e8937047654b6 --- /dev/null +++ b/lib/gitlab/action_view_output/context.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +# This file was simplified from https://raw.githubusercontent.com/rails/rails/195f39804a7a4a0034f25e8704220e03d95a752a/actionview/lib/action_view/context.rb. +# +# It is only needed by modules that need to call ActionView helper +# methods (e.g. those in +# https://github.com/rails/rails/tree/c4d3e202e10ae627b3b9c34498afb45450652421/actionview/lib/action_view/helpers) +# to generate tags outside of a Rails controller (e.g. API, Sidekiq, +# etc.). +# +# In Rails 5, ActionView::Context automatically includes CompiledTemplates. +# This means that any module that includes ActionView::Context is now a descendant +# of CompiledTemplates. +# +# When a partial is rendered for the first time, it runs +# Module#module_eval, which will evaluate a string source that defines a +# new method. For example: +# +# def _app_views_profiles_show_html_haml___1285955918103175884_70307801785400(local_assigns, output_buffer) +# "hello world" +# end +# +# When a new method is defined, the Ruby interpreter clears the method +# cache for all descendants, and all methods for those modules will have +# to be redefined. This can lead to a significant performance penalty. +# +# Rails 6 fixes this behavior by moving out the `include +# CompiledTemplates` into ActionView::Base so that including `ActionView::Context` +# doesn't quietly affect other modules in this way. + +if Rails::VERSION::STRING.start_with?('6') + raise 'This module is no longer needed in Rails 6. Use ActionView::Context instead.' +end + +module Gitlab + module ActionViewOutput + module Context + attr_accessor :output_buffer, :view_flow + end + end +end diff --git a/lib/gitlab/checks/lfs_check.rb b/lib/gitlab/checks/lfs_check.rb index cc6a14d2d9a42ddebb307abdc6c30596f5ed9e6d..67a65d61441563d004447e657e0d5c46fbedf0da 100644 --- a/lib/gitlab/checks/lfs_check.rb +++ b/lib/gitlab/checks/lfs_check.rb @@ -7,6 +7,7 @@ module Gitlab ERROR_MESSAGE = 'LFS objects are missing. Ensure LFS is properly set up or try a manual "git lfs push --all".'.freeze def validate! + return unless Feature.enabled?(:lfs_check, default_enabled: true) return unless project.lfs_enabled? return if skip_lfs_integrity_check diff --git a/lib/gitlab/ci/variables/collection/item.rb b/lib/gitlab/ci/variables/collection/item.rb index 833aa75adb5beb548630ced4b0d4c1eb1c8f880d..aab10aef3987e2acb87ea30a38ab501dd16071e5 100644 --- a/lib/gitlab/ci/variables/collection/item.rb +++ b/lib/gitlab/ci/variables/collection/item.rb @@ -27,13 +27,9 @@ module Gitlab # don't expose `file` attribute at all (stems from what the runner # expects). # - # If the `variable_masking` feature is enabled we expose the `masked` - # attribute, otherwise it's not exposed. - # def to_runner_variable @variable.reject do |hash_key, hash_value| - (hash_key == :file && hash_value == false) || - (hash_key == :masked && !Feature.enabled?(:variable_masking)) + hash_key == :file && hash_value == false end end diff --git a/lib/gitlab/import_export/after_export_strategies/web_upload_strategy.rb b/lib/gitlab/import_export/after_export_strategies/web_upload_strategy.rb index b30900f7c61de14e89ffccd0488fa452c15e4254..7a057d79921db3d329e5f401fb7c499dee17a758 100644 --- a/lib/gitlab/import_export/after_export_strategies/web_upload_strategy.rb +++ b/lib/gitlab/import_export/after_export_strategies/web_upload_strategy.rb @@ -30,10 +30,7 @@ module Gitlab def handle_response_error(response) unless response.success? - error_code = response.dig('Error', 'Code') || response.code - error_message = response.dig('Error', 'Message') || response.message - - raise StrategyError.new("Error uploading the project. Code #{error_code}: #{error_message}") + raise StrategyError.new("Error uploading the project. Code #{response.code}: #{response.message}") end end diff --git a/package.json b/package.json index 6d3e6764a4623922f58655b473d7e6274aac1eee..888faa829a8c795b17083c675503ac0f3b1ce094 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "@babel/preset-env": "^7.3.1", "@gitlab/csslab": "^1.9.0", "@gitlab/svgs": "^1.58.0", - "@gitlab/ui": "^3.2.0", + "@gitlab/ui": "3.2.0-hotfix.1", "apollo-cache-inmemory": "^1.5.1", "apollo-client": "^2.5.1", "apollo-upload-client": "^10.0.0", diff --git a/rubocop/cop/include_action_view_context.rb b/rubocop/cop/include_action_view_context.rb new file mode 100644 index 0000000000000000000000000000000000000000..14662a33e95f97f5d55fe8425f83427d83d95942 --- /dev/null +++ b/rubocop/cop/include_action_view_context.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +require_relative '../spec_helpers' + +module RuboCop + module Cop + # Cop that makes sure workers include `::Gitlab::ActionViewOutput::Context`, not `ActionView::Context`. + class IncludeActionViewContext < RuboCop::Cop::Cop + include SpecHelpers + + MSG = 'Include `::Gitlab::ActionViewOutput::Context`, not `ActionView::Context`, for Rails 5.'.freeze + + def_node_matcher :includes_action_view_context?, <<~PATTERN + (send nil? :include (const (const nil? :ActionView) :Context)) + PATTERN + + def on_send(node) + return if in_spec?(node) + return unless includes_action_view_context?(node) + + add_offense(node.arguments.first, location: :expression) + end + + def autocorrect(node) + lambda do |corrector| + corrector.replace(node.source_range, '::Gitlab::ActionViewOutput::Context') + end + end + end + end +end diff --git a/rubocop/rubocop.rb b/rubocop/rubocop.rb index 3e33419eb2edfe0fff41dd3f6941931bc9cac9dc..f36d88d4a40821c52c7ba5dd39cfa651af82865f 100644 --- a/rubocop/rubocop.rb +++ b/rubocop/rubocop.rb @@ -4,6 +4,7 @@ require_relative 'cop/gitlab/predicate_memoization' require_relative 'cop/gitlab/httparty' require_relative 'cop/gitlab/finder_with_find_by' require_relative 'cop/gitlab/union' +require_relative 'cop/include_action_view_context' require_relative 'cop/include_sidekiq_worker' require_relative 'cop/safe_params' require_relative 'cop/avoid_return_from_blocks' diff --git a/spec/controllers/projects/settings/ci_cd_controller_spec.rb b/spec/controllers/projects/settings/ci_cd_controller_spec.rb index 41cc0607cee20716a07c6216decadaf3593feefa..1b5dcc6ff522bc37b5b00edbf69227850ec01ee2 100644 --- a/spec/controllers/projects/settings/ci_cd_controller_spec.rb +++ b/spec/controllers/projects/settings/ci_cd_controller_spec.rb @@ -189,6 +189,15 @@ describe Projects::Settings::CiCdController do expect(project.build_timeout).to eq(5400) end end + + context 'when build_timeout_human_readable is invalid' do + let(:params) { { build_timeout_human_readable: '5m' } } + + it 'set specified timeout' do + expect(subject).to set_flash[:alert] + expect(response).to redirect_to(namespace_project_settings_ci_cd_path) + end + end end end end diff --git a/spec/controllers/search_controller_spec.rb b/spec/controllers/search_controller_spec.rb index 752d6ae55cc51b7b6a4e4102d53623c1aadfaa68..5d59a0aeae9ac8d394b4e248d1b7e82172ca52cf 100644 --- a/spec/controllers/search_controller_spec.rb +++ b/spec/controllers/search_controller_spec.rb @@ -9,6 +9,30 @@ describe SearchController do sign_in(user) end + context 'uses the right partials depending on scope' do + using RSpec::Parameterized::TableSyntax + render_views + + set(:project) { create(:project, :public, :repository, :wiki_repo) } + + subject { get(:show, params: { project_id: project.id, scope: scope, search: 'merge' }) } + + where(:partial, :scope) do + '_blob' | :blobs + '_wiki_blob' | :wiki_blobs + '_commit' | :commits + end + + with_them do + it do + project_wiki = create(:project_wiki, project: project, user: user) + create(:wiki_page, wiki: project_wiki, attrs: { title: 'merge', content: 'merge' }) + + expect(subject).to render_template("search/results/#{partial}") + end + end + end + it 'finds issue comments' do project = create(:project, :public) note = create(:note_on_issue, project: project) diff --git a/spec/features/dashboard/todos/todos_spec.rb b/spec/features/dashboard/todos/todos_spec.rb index fd8677feab553f5a226d881b18f29405715025b0..d58e3b2841ec30bd7f89c8c589e26d3ae5825c62 100644 --- a/spec/features/dashboard/todos/todos_spec.rb +++ b/spec/features/dashboard/todos/todos_spec.rb @@ -17,6 +17,26 @@ describe 'Dashboard Todos' do end end + context 'when the todo references a merge request' do + let(:referenced_mr) { create(:merge_request, source_project: project) } + let(:note) { create(:note, project: project, note: "Check out #{referenced_mr.to_reference}") } + let!(:todo) { create(:todo, :mentioned, user: user, project: project, author: author, note: note) } + + before do + sign_in(user) + visit dashboard_todos_path + end + + it 'renders the mr link with the extra attributes' do + link = page.find_link(referenced_mr.to_reference) + + expect(link).not_to be_nil + expect(link['data-iid']).to eq(referenced_mr.iid.to_s) + expect(link['data-project-path']).to eq(referenced_mr.project.full_path) + expect(link['data-mr-title']).to eq(referenced_mr.title) + end + end + context 'User has a todo', :js do before do create(:todo, :mentioned, user: user, project: project, target: issue, author: author) diff --git a/spec/frontend/mr_popover/index_spec.js b/spec/frontend/mr_popover/index_spec.js index 8c33e52a04bdc079bff89cefabc76e0ed755e1b3..b9db2342687358540a82c5809d905a1170d844b4 100644 --- a/spec/frontend/mr_popover/index_spec.js +++ b/spec/frontend/mr_popover/index_spec.js @@ -7,18 +7,28 @@ createDefaultClient.default = jest.fn(); describe('initMRPopovers', () => { let mr1; let mr2; + let mr3; beforeEach(() => { setHTMLFixture(` -
MR1
-
MR2
+
+ MR1 +
+
+ MR2 +
+
+ MR3 +
`); mr1 = document.querySelector('#one'); mr2 = document.querySelector('#two'); + mr3 = document.querySelector('#three'); mr1.addEventListener = jest.fn(); mr2.addEventListener = jest.fn(); + mr3.addEventListener = jest.fn(); }); it('does not add the same event listener twice', () => { @@ -27,4 +37,10 @@ describe('initMRPopovers', () => { expect(mr1.addEventListener).toHaveBeenCalledTimes(1); expect(mr2.addEventListener).toHaveBeenCalledTimes(1); }); + + it('does not add listener if it does not have the necessary data attributes', () => { + initMRPopovers([mr1, mr2, mr3]); + + expect(mr3.addEventListener).not.toHaveBeenCalled(); + }); }); diff --git a/spec/javascripts/monitoring/dashboard_spec.js b/spec/javascripts/monitoring/dashboard_spec.js index ce2c6c43c0f780b3062963265661a106c35c88d6..16dc0084a1018a83335f5b6539c7bccff8a63221 100644 --- a/spec/javascripts/monitoring/dashboard_spec.js +++ b/spec/javascripts/monitoring/dashboard_spec.js @@ -175,7 +175,7 @@ describe('Dashboard', () => { setTimeout(() => { const dropdownItems = component.$el.querySelectorAll( - '.js-environments-dropdown .dropdown-item[active="true"]', + '.js-environments-dropdown .dropdown-item.is-active', ); expect(dropdownItems.length).toEqual(1); diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_alert_message_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_alert_message_spec.js index f115cb457e5fc4732a15c9dbae75d1a4571a2471..8ec17efffb99399a1e00df93e5d3b0b346709e58 100644 --- a/spec/javascripts/vue_mr_widget/components/mr_widget_alert_message_spec.js +++ b/spec/javascripts/vue_mr_widget/components/mr_widget_alert_message_spec.js @@ -11,6 +11,7 @@ describe('MrWidgetAlertMessage', () => { wrapper = shallowMount(localVue.extend(MrWidgetAlertMessage), { propsData: {}, localVue, + sync: false, }); }); @@ -19,45 +20,58 @@ describe('MrWidgetAlertMessage', () => { }); describe('when type is not provided', () => { - it('should render a red message', () => { - expect(wrapper.classes()).toContain('danger_message'); - expect(wrapper.classes()).not.toContain('warning_message'); + it('should render a red message', done => { + wrapper.vm.$nextTick(() => { + expect(wrapper.classes()).toContain('danger_message'); + expect(wrapper.classes()).not.toContain('warning_message'); + done(); + }); }); }); describe('when type === "danger"', () => { - it('should render a red message', () => { + it('should render a red message', done => { wrapper.setProps({ type: 'danger' }); - - expect(wrapper.classes()).toContain('danger_message'); - expect(wrapper.classes()).not.toContain('warning_message'); + wrapper.vm.$nextTick(() => { + expect(wrapper.classes()).toContain('danger_message'); + expect(wrapper.classes()).not.toContain('warning_message'); + done(); + }); }); }); describe('when type === "warning"', () => { - it('should render a red message', () => { + it('should render a red message', done => { wrapper.setProps({ type: 'warning' }); - - expect(wrapper.classes()).toContain('warning_message'); - expect(wrapper.classes()).not.toContain('danger_message'); + wrapper.vm.$nextTick(() => { + expect(wrapper.classes()).toContain('warning_message'); + expect(wrapper.classes()).not.toContain('danger_message'); + done(); + }); }); }); describe('when helpPath is not provided', () => { - it('should not render a help icon/link', () => { - const link = wrapper.find(GlLink); + it('should not render a help icon/link', done => { + wrapper.vm.$nextTick(() => { + const link = wrapper.find(GlLink); - expect(link.exists()).toBe(false); + expect(link.exists()).toBe(false); + done(); + }); }); }); describe('when helpPath is provided', () => { - it('should render a help icon/link', () => { + it('should render a help icon/link', done => { wrapper.setProps({ helpPath: '/path/to/help/docs' }); - const link = wrapper.find(GlLink); + wrapper.vm.$nextTick(() => { + const link = wrapper.find(GlLink); - expect(link.exists()).toBe(true); - expect(link.attributes().href).toBe('/path/to/help/docs'); + expect(link.exists()).toBe(true); + expect(link.attributes().href).toBe('/path/to/help/docs'); + done(); + }); }); }); }); diff --git a/spec/javascripts/vue_shared/components/notes/system_note_spec.js b/spec/javascripts/vue_shared/components/notes/system_note_spec.js index adcb1c858aa17e87939d948673648497301018ae..5b4ca20940aeb89091421801b3365c0a6a539d61 100644 --- a/spec/javascripts/vue_shared/components/notes/system_note_spec.js +++ b/spec/javascripts/vue_shared/components/notes/system_note_spec.js @@ -5,8 +5,10 @@ import createStore from '~/notes/stores'; describe('system note component', () => { let vm; let props; + let initMRPopoversSpy; beforeEach(() => { + initMRPopoversSpy = spyOnDependency(issueSystemNote, 'initMRPopovers'); props = { note: { id: '1424', @@ -56,4 +58,8 @@ describe('system note component', () => { it('removes wrapping paragraph from note HTML', () => { expect(vm.$el.querySelector('.system-note-message').innerHTML).toEqual('closed'); }); + + it('should initMRPopovers onMount', () => { + expect(initMRPopoversSpy).toHaveBeenCalled(); + }); }); diff --git a/spec/lib/gitlab/checks/lfs_check_spec.rb b/spec/lib/gitlab/checks/lfs_check_spec.rb index 35f8069c8a4882c1df973e31abd682dc6a3afc57..dad14e100a7e5057a3575cb5ff274ea5097cec01 100644 --- a/spec/lib/gitlab/checks/lfs_check_spec.rb +++ b/spec/lib/gitlab/checks/lfs_check_spec.rb @@ -27,6 +27,18 @@ describe Gitlab::Checks::LfsCheck do allow(project).to receive(:lfs_enabled?).and_return(true) end + context 'with lfs_check feature disabled' do + before do + stub_feature_flags(lfs_check: false) + end + + it 'skips integrity check' do + expect_any_instance_of(Gitlab::Git::LfsChanges).not_to receive(:new_pointers) + + subject.validate! + end + end + context 'deletion' do let(:changes) { { oldrev: oldrev, ref: ref } } diff --git a/spec/lib/gitlab/ci/variables/collection/item_spec.rb b/spec/lib/gitlab/ci/variables/collection/item_spec.rb index 3ff2fe18c157c48a1a8308c8262771d0295b4584..613814df23feaf22b6b0a2926c4f12fa17718d60 100644 --- a/spec/lib/gitlab/ci/variables/collection/item_spec.rb +++ b/spec/lib/gitlab/ci/variables/collection/item_spec.rb @@ -137,19 +137,5 @@ describe Gitlab::Ci::Variables::Collection::Item do .to eq(key: 'VAR', value: 'value', public: true, file: true, masked: false) end end - - context 'when variable masking is disabled' do - before do - stub_feature_flags(variable_masking: false) - end - - it 'does not expose the masked field to the runner' do - runner_variable = described_class - .new(key: 'VAR', value: 'value', masked: true) - .to_runner_variable - - expect(runner_variable).to eq(key: 'VAR', value: 'value', public: true) - end - end end end diff --git a/spec/lib/gitlab/import_export/after_export_strategies/web_upload_strategy_spec.rb b/spec/lib/gitlab/import_export/after_export_strategies/web_upload_strategy_spec.rb index ec17ad8541fda4069438460f5fea963713a60a0b..7c4ac62790e0945d7326c2b4f3132dfc2e720ce1 100644 --- a/spec/lib/gitlab/import_export/after_export_strategies/web_upload_strategy_spec.rb +++ b/spec/lib/gitlab/import_export/after_export_strategies/web_upload_strategy_spec.rb @@ -32,5 +32,17 @@ describe Gitlab::ImportExport::AfterExportStrategies::WebUploadStrategy do strategy.execute(user, project) end + + context 'when upload fails' do + it 'stores the export error' do + stub_request(:post, example_url).to_return(status: [404, 'Page not found']) + + strategy.execute(user, project) + + errors = project.import_export_shared.errors + expect(errors).not_to be_empty + expect(errors.first).to eq "Error uploading the project. Code 404: Page not found" + end + end end end diff --git a/spec/models/ci/bridge_spec.rb b/spec/models/ci/bridge_spec.rb index aacfbe3f180b4ccb1d77e426d0774eccc35070d4..913695f72a7024cebfbc0c208d81c16500cbfe17 100644 --- a/spec/models/ci/bridge_spec.rb +++ b/spec/models/ci/bridge_spec.rb @@ -10,6 +10,8 @@ describe Ci::Bridge do create(:ci_bridge, pipeline: pipeline) end + it { is_expected.to include_module(Ci::PipelineDelegator) } + describe '#tags' do it 'only has a bridge tag' do expect(bridge.tags).to eq [:bridge] diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index 66be192ab2127924f6f87495caf39389fa6fb566..0e1be8809cb556c781a54c9c08fdc4f0c4d38314 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -28,6 +28,7 @@ describe Ci::Build do it { is_expected.to delegate_method(:merge_request_event?).to(:pipeline) } it { is_expected.to delegate_method(:merge_request_ref?).to(:pipeline) } it { is_expected.to delegate_method(:legacy_detached_merge_request_pipeline?).to(:pipeline) } + it { is_expected.to include_module(Ci::PipelineDelegator) } it { is_expected.to be_a(ArtifactMigratable) } @@ -2312,6 +2313,19 @@ describe Ci::Build do it { user_variables.each { |v| is_expected.to include(v) } } end + context 'when build belongs to a pipeline for merge request' do + let(:merge_request) { create(:merge_request, :with_detached_merge_request_pipeline, source_branch: 'improve/awesome') } + let(:pipeline) { merge_request.all_pipelines.first } + let(:build) { create(:ci_build, ref: pipeline.ref, pipeline: pipeline) } + + it 'returns values based on source ref' do + is_expected.to include( + { key: 'CI_COMMIT_REF_NAME', value: 'improve/awesome', public: true, masked: false }, + { key: 'CI_COMMIT_REF_SLUG', value: 'improve-awesome', public: true, masked: false } + ) + end + end + context 'when build has an environment' do let(:environment_variables) do [ @@ -2703,6 +2717,8 @@ describe Ci::Build do ) end + let(:pipeline) { create(:ci_pipeline, project: project, ref: 'feature') } + it 'returns static predefined variables' do expect(build.variables.size).to be >= 28 expect(build.variables) @@ -2752,6 +2768,8 @@ describe Ci::Build do ) end + let(:pipeline) { create(:ci_pipeline, project: project, ref: 'feature') } + it 'does not persist the build' do expect(build).to be_valid expect(build).not_to be_persisted diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index f34aa0d9b0312aee7c47cfe88555ad0dd6fa2e49..9b80e45d9de8e836ac4cded61164a0ed08c45bbe 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -382,6 +382,54 @@ describe Ci::Pipeline, :mailer do end end + describe '#source_ref' do + subject { pipeline.source_ref } + + let(:pipeline) { create(:ci_pipeline, ref: 'feature') } + + it 'returns source ref' do + is_expected.to eq('feature') + end + + context 'when the pipeline is a detached merge request pipeline' do + let(:merge_request) { create(:merge_request) } + + let(:pipeline) do + create(:ci_pipeline, source: :merge_request_event, merge_request: merge_request, ref: merge_request.ref_path) + end + + it 'returns source ref' do + is_expected.to eq(merge_request.source_branch) + end + end + end + + describe '#source_ref_slug' do + subject { pipeline.source_ref_slug } + + let(:pipeline) { create(:ci_pipeline, ref: 'feature') } + + it 'slugifies with the source ref' do + expect(Gitlab::Utils).to receive(:slugify).with('feature') + + subject + end + + context 'when the pipeline is a detached merge request pipeline' do + let(:merge_request) { create(:merge_request) } + + let(:pipeline) do + create(:ci_pipeline, source: :merge_request_event, merge_request: merge_request, ref: merge_request.ref_path) + end + + it 'slugifies with the source ref of the merge request' do + expect(Gitlab::Utils).to receive(:slugify).with(merge_request.source_branch) + + subject + end + end + end + describe '.triggered_for_branch' do subject { described_class.triggered_for_branch(ref) } diff --git a/spec/models/deployment_spec.rb b/spec/models/deployment_spec.rb index d9170d5fa07dee172ccd38cd405c2a050c8c27fa..be0cc58b35a67c44f9bdf47a11aa367679bb6684 100644 --- a/spec/models/deployment_spec.rb +++ b/spec/models/deployment_spec.rb @@ -379,6 +379,12 @@ describe Deployment do it { is_expected.to be_nil } end + context 'project uses the kubernetes service for deployments' do + let!(:service) { create(:kubernetes_service, project: project) } + + it { is_expected.to be_nil } + end + context 'project has a deployment platform' do let!(:cluster) { create(:cluster, projects: [project]) } let!(:platform) { create(:cluster_platform_kubernetes, cluster: cluster) } diff --git a/spec/rubocop/cop/include_action_view_context_spec.rb b/spec/rubocop/cop/include_action_view_context_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..c888555b54f7bc5dfa73a61b6b86112d0f8ec606 --- /dev/null +++ b/spec/rubocop/cop/include_action_view_context_spec.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +require 'spec_helper' + +require 'rubocop' +require 'rubocop/rspec/support' + +require_relative '../../../rubocop/cop/include_action_view_context' + +describe RuboCop::Cop::IncludeActionViewContext do + include CopHelper + + subject(:cop) { described_class.new } + + context 'when `ActionView::Context` is included' do + let(:source) { 'include ActionView::Context' } + let(:correct_source) { 'include ::Gitlab::ActionViewOutput::Context' } + + it 'registers an offense' do + inspect_source(source) + + aggregate_failures do + expect(cop.offenses.size).to eq(1) + expect(cop.offenses.map(&:line)).to eq([1]) + expect(cop.highlights).to eq(['ActionView::Context']) + end + end + + it 'autocorrects to the right version' do + autocorrected = autocorrect_source(source) + + expect(autocorrected).to eq(correct_source) + end + end + + context 'when `ActionView::Context` is not included' do + it 'registers no offense' do + inspect_source('include Context') + + aggregate_failures do + expect(cop.offenses.size).to eq(0) + end + end + end +end diff --git a/spec/workers/pipeline_schedule_worker_spec.rb b/spec/workers/pipeline_schedule_worker_spec.rb index f23910d23bead252f0977d22948d837b0185ff63..8c604b132976f10780d3177a83945bcab1aec72c 100644 --- a/spec/workers/pipeline_schedule_worker_spec.rb +++ b/spec/workers/pipeline_schedule_worker_spec.rb @@ -3,6 +3,8 @@ require 'spec_helper' describe PipelineScheduleWorker do + include ExclusiveLeaseHelpers + subject { described_class.new.perform } set(:project) { create(:project, :repository) } @@ -39,6 +41,16 @@ describe PipelineScheduleWorker do it_behaves_like 'successful scheduling' + context 'when exclusive lease has already been taken by the other instance' do + before do + stub_exclusive_lease_taken(described_class::EXCLUSIVE_LOCK_KEY, timeout: described_class::LOCK_TIMEOUT) + end + + it 'raises an error and does not start creating pipelines' do + expect { subject }.to raise_error(Gitlab::ExclusiveLeaseHelpers::FailedToObtainLockError) + end + end + context 'when the latest commit contains [ci skip]' do before do allow_any_instance_of(Ci::Pipeline) diff --git a/yarn.lock b/yarn.lock index 571ac952e90ed7c5533e466167ce82dd3e134ff5..3e7e4275e45aeba2db93a3af0cc4af2ed3a02de8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -663,10 +663,10 @@ resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.58.0.tgz#bb05263ff2eb7ca09a25cd14d0b1a932d2ea9c2f" integrity sha512-RlWSjjBT4lMIFuNC1ziCO1nws9zqZtxCjhrqK2DxDDTgp2W0At9M/BFkHp8RHyMCrO3g1fHTrLPUgzr5oR3Epg== -"@gitlab/ui@^3.2.0": - version "3.2.0" - resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-3.2.0.tgz#3a44ac806a22b87fe45e6edfa410cb9355164f04" - integrity sha512-If2ngMIw0jWAdQ1q3PfB8sDhCXz1r3DsRm1X5Vy767kZ2TeFd7SGBp5KP5ceMGGpQ4TTYU/V8IqYnQbTXfPKRw== +"@gitlab/ui@3.2.0-hotfix.1": + version "3.2.0-hotfix.1" + resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-3.2.0-hotfix.1.tgz#80d3bab994b2a84fd9a61c8fdd06e5ca10547aa1" + integrity sha512-3og6WuXb5JDk+btAS3UuC8PGv/cdu2bisCQ/tpkD0QRNptit2Or96bGEH0hzKxAlSSEzFA3sZlhbxvg/vPLCRg== dependencies: "@babel/standalone" "^7.0.0" bootstrap-vue "^2.0.0-rc.11"