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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/assets/javascripts/content_editor/components/bubble_menus/code_block.vue20
-rw-r--r--app/assets/javascripts/content_editor/services/code_block_language_loader.js9
-rw-r--r--app/assets/javascripts/content_editor/services/highlight_js_language_loader.js248
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/field.vue13
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/header.vue13
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/toolbar.vue7
-rw-r--r--app/assets/javascripts/vue_shared/components/source_viewer/source_viewer.vue3
-rw-r--r--app/controllers/admin/deploy_keys_controller.rb1
-rw-r--r--app/controllers/autocomplete_controller.rb2
-rw-r--r--app/controllers/groups/deploy_tokens_controller.rb1
-rw-r--r--app/controllers/groups/releases_controller.rb1
-rw-r--r--app/controllers/groups/settings/repository_controller.rb1
-rw-r--r--app/controllers/projects/deploy_keys_controller.rb1
-rw-r--r--app/controllers/projects/deploy_tokens_controller.rb1
-rw-r--r--app/controllers/projects/deployments_controller.rb1
-rw-r--r--app/controllers/projects/environments_controller.rb1
-rw-r--r--app/controllers/projects/feature_flags_clients_controller.rb1
-rw-r--r--app/controllers/projects/feature_flags_controller.rb1
-rw-r--r--app/controllers/projects/feature_flags_user_lists_controller.rb1
-rw-r--r--app/controllers/projects/releases/evidences_controller.rb1
-rw-r--r--app/controllers/projects/releases_controller.rb1
-rw-r--r--app/controllers/projects/settings/repository_controller.rb2
-rw-r--r--app/controllers/projects/tags/releases_controller.rb1
-rw-r--r--app/views/projects/merge_requests/_code_dropdown.html.haml10
-rw-r--r--doc/api/plan_limits.md24
-rw-r--r--doc/development/testing_guide/testing_migrations_guide.md12
-rw-r--r--lib/api/admin/plan_limits.rb8
-rw-r--r--lib/api/ci/resource_groups.rb1
-rw-r--r--lib/api/deploy_keys.rb1
-rw-r--r--lib/api/deploy_tokens.rb1
-rw-r--r--lib/api/deployments.rb1
-rw-r--r--lib/api/entities/plan_limit.rb8
-rw-r--r--lib/api/environments.rb1
-rw-r--r--lib/api/feature_flags.rb1
-rw-r--r--lib/api/feature_flags_user_lists.rb1
-rw-r--r--lib/api/features.rb1
-rw-r--r--lib/api/freeze_periods.rb1
-rw-r--r--lib/api/release/links.rb1
-rw-r--r--lib/api/releases.rb1
-rw-r--r--lib/api/unleash.rb2
-rw-r--r--qa/qa/page/merge_request/show.rb7
-rw-r--r--spec/frontend/snippets/components/__snapshots__/snippet_description_edit_spec.js.snap3
-rw-r--r--spec/frontend/vue_shared/components/markdown/field_spec.js26
-rw-r--r--spec/frontend/vue_shared/components/markdown/header_spec.js22
-rw-r--r--spec/frontend/vue_shared/components/markdown/toolbar_spec.js14
-rw-r--r--spec/lib/api/entities/plan_limit_spec.rb8
-rw-r--r--spec/requests/api/admin/plan_limits_spec.rb48
-rw-r--r--spec/support/matchers/background_migrations_matchers.rb8
48 files changed, 506 insertions, 36 deletions
diff --git a/app/assets/javascripts/content_editor/components/bubble_menus/code_block.vue b/app/assets/javascripts/content_editor/components/bubble_menus/code_block.vue
index e467053cd73..c42f06664cc 100644
--- a/app/assets/javascripts/content_editor/components/bubble_menus/code_block.vue
+++ b/app/assets/javascripts/content_editor/components/bubble_menus/code_block.vue
@@ -77,19 +77,11 @@ export default {
this.tiptapEditor.commands.setCodeBlock({ language: this.selectedLanguage.syntax });
},
- tippyOnBeforeUpdate(tippy, props) {
- if (props.getReferenceClientRect) {
- // eslint-disable-next-line no-param-reassign
- props.getReferenceClientRect = () => {
- const { view } = this.tiptapEditor;
- const { from } = this.tiptapEditor.state.selection;
-
- const node = getParentByTagName(view.domAtPos(from).node, 'pre');
- if (node) return node.getBoundingClientRect();
-
- return new DOMRect(-1000, -1000, 0, 0);
- };
- }
+ getReferenceClientRect() {
+ const { view } = this.tiptapEditor;
+ const { from } = this.tiptapEditor.state.selection;
+ const node = getParentByTagName(view.domAtPos(from).node, 'pre');
+ return node?.getBoundingClientRect() || new DOMRect(-1000, -1000, 0, 0);
},
deleteCodeBlock() {
@@ -105,7 +97,7 @@ export default {
:editor="tiptapEditor"
plugin-key="bubbleMenuCodeBlock"
:should-show="shouldShow"
- :tippy-options="{ onBeforeUpdate: tippyOnBeforeUpdate }"
+ :tippy-options="{ getReferenceClientRect }"
>
<editor-state-observer @transaction="updateSelectedLanguage">
<gl-button-group>
diff --git a/app/assets/javascripts/content_editor/services/code_block_language_loader.js b/app/assets/javascripts/content_editor/services/code_block_language_loader.js
index 74018d7e1e3..0d5a8c91907 100644
--- a/app/assets/javascripts/content_editor/services/code_block_language_loader.js
+++ b/app/assets/javascripts/content_editor/services/code_block_language_loader.js
@@ -1,6 +1,7 @@
import { lowlight } from 'lowlight/lib/core';
import { __, sprintf } from '~/locale';
import CODE_BLOCK_LANGUAGES from '../constants/code_block_languages';
+import languageLoader from './highlight_js_language_loader';
const codeBlockLanguageLoader = {
lowlight,
@@ -46,11 +47,11 @@ const codeBlockLanguageLoader = {
loadLanguages(languageList = []) {
const loaders = languageList
- .filter((languageName) => !this.isLanguageLoaded(languageName))
+ .filter(
+ (languageName) => !this.isLanguageLoaded(languageName) && languageName in languageLoader,
+ )
.map((languageName) => {
- return import(
- /* webpackChunkName: 'highlight.language.js' */ `highlight.js/lib/languages/${languageName}`
- )
+ return languageLoader[languageName]()
.then(({ default: language }) => {
this.lowlight.registerLanguage(languageName, language);
})
diff --git a/app/assets/javascripts/content_editor/services/highlight_js_language_loader.js b/app/assets/javascripts/content_editor/services/highlight_js_language_loader.js
new file mode 100644
index 00000000000..a0ebbebed4e
--- /dev/null
+++ b/app/assets/javascripts/content_editor/services/highlight_js_language_loader.js
@@ -0,0 +1,248 @@
+/**
+ * This file is generated based on the contents of highlight.js/lib/languages to avoid
+ * utilizing dynamic expressions within `import()` which were the source of some
+ * confusion when attempting to produce deterministic webpack compilations across
+ * multiple build environments.
+ *
+ * This list of highlight-able languages will need to be updated as new options are
+ * introduced within the highlight.js dependency.
+ */
+
+export default {
+ '1c': () => import(/* webpackChunkName: 'hl-1c' */ 'highlight.js/lib/languages/1c'),
+ abnf: () => import(/* webpackChunkName: 'hl-abnf' */ 'highlight.js/lib/languages/abnf'),
+ accesslog: () =>
+ import(/* webpackChunkName: 'hl-accesslog' */ 'highlight.js/lib/languages/accesslog'),
+ actionscript: () =>
+ import(/* webpackChunkName: 'hl-actionscript' */ 'highlight.js/lib/languages/actionscript'),
+ ada: () => import(/* webpackChunkName: 'hl-ada' */ 'highlight.js/lib/languages/ada'),
+ angelscript: () =>
+ import(/* webpackChunkName: 'hl-angelscript' */ 'highlight.js/lib/languages/angelscript'),
+ apache: () => import(/* webpackChunkName: 'hl-apache' */ 'highlight.js/lib/languages/apache'),
+ applescript: () =>
+ import(/* webpackChunkName: 'hl-applescript' */ 'highlight.js/lib/languages/applescript'),
+ arcade: () => import(/* webpackChunkName: 'hl-arcade' */ 'highlight.js/lib/languages/arcade'),
+ arduino: () => import(/* webpackChunkName: 'hl-arduino' */ 'highlight.js/lib/languages/arduino'),
+ armasm: () => import(/* webpackChunkName: 'hl-armasm' */ 'highlight.js/lib/languages/armasm'),
+ asciidoc: () =>
+ import(/* webpackChunkName: 'hl-asciidoc' */ 'highlight.js/lib/languages/asciidoc'),
+ aspectj: () => import(/* webpackChunkName: 'hl-aspectj' */ 'highlight.js/lib/languages/aspectj'),
+ autohotkey: () =>
+ import(/* webpackChunkName: 'hl-autohotkey' */ 'highlight.js/lib/languages/autohotkey'),
+ autoit: () => import(/* webpackChunkName: 'hl-autoit' */ 'highlight.js/lib/languages/autoit'),
+ avrasm: () => import(/* webpackChunkName: 'hl-avrasm' */ 'highlight.js/lib/languages/avrasm'),
+ awk: () => import(/* webpackChunkName: 'hl-awk' */ 'highlight.js/lib/languages/awk'),
+ axapta: () => import(/* webpackChunkName: 'hl-axapta' */ 'highlight.js/lib/languages/axapta'),
+ bash: () => import(/* webpackChunkName: 'hl-bash' */ 'highlight.js/lib/languages/bash'),
+ basic: () => import(/* webpackChunkName: 'hl-basic' */ 'highlight.js/lib/languages/basic'),
+ bnf: () => import(/* webpackChunkName: 'hl-bnf' */ 'highlight.js/lib/languages/bnf'),
+ brainfuck: () =>
+ import(/* webpackChunkName: 'hl-brainfuck' */ 'highlight.js/lib/languages/brainfuck'),
+ c: () => import(/* webpackChunkName: 'hl-c' */ 'highlight.js/lib/languages/c'),
+ cal: () => import(/* webpackChunkName: 'hl-cal' */ 'highlight.js/lib/languages/cal'),
+ capnproto: () =>
+ import(/* webpackChunkName: 'hl-capnproto' */ 'highlight.js/lib/languages/capnproto'),
+ ceylon: () => import(/* webpackChunkName: 'hl-ceylon' */ 'highlight.js/lib/languages/ceylon'),
+ clean: () => import(/* webpackChunkName: 'hl-clean' */ 'highlight.js/lib/languages/clean'),
+ 'clojure-repl': () =>
+ import(/* webpackChunkName: 'hl-clojure-repl' */ 'highlight.js/lib/languages/clojure-repl'),
+ clojure: () => import(/* webpackChunkName: 'hl-clojure' */ 'highlight.js/lib/languages/clojure'),
+ cmake: () => import(/* webpackChunkName: 'hl-cmake' */ 'highlight.js/lib/languages/cmake'),
+ coffeescript: () =>
+ import(/* webpackChunkName: 'hl-coffeescript' */ 'highlight.js/lib/languages/coffeescript'),
+ coq: () => import(/* webpackChunkName: 'hl-coq' */ 'highlight.js/lib/languages/coq'),
+ cos: () => import(/* webpackChunkName: 'hl-cos' */ 'highlight.js/lib/languages/cos'),
+ cpp: () => import(/* webpackChunkName: 'hl-cpp' */ 'highlight.js/lib/languages/cpp'),
+ crmsh: () => import(/* webpackChunkName: 'hl-crmsh' */ 'highlight.js/lib/languages/crmsh'),
+ crystal: () => import(/* webpackChunkName: 'hl-crystal' */ 'highlight.js/lib/languages/crystal'),
+ csharp: () => import(/* webpackChunkName: 'hl-csharp' */ 'highlight.js/lib/languages/csharp'),
+ csp: () => import(/* webpackChunkName: 'hl-csp' */ 'highlight.js/lib/languages/csp'),
+ css: () => import(/* webpackChunkName: 'hl-css' */ 'highlight.js/lib/languages/css'),
+ d: () => import(/* webpackChunkName: 'hl-d' */ 'highlight.js/lib/languages/d'),
+ dart: () => import(/* webpackChunkName: 'hl-dart' */ 'highlight.js/lib/languages/dart'),
+ delphi: () => import(/* webpackChunkName: 'hl-delphi' */ 'highlight.js/lib/languages/delphi'),
+ diff: () => import(/* webpackChunkName: 'hl-diff' */ 'highlight.js/lib/languages/diff'),
+ django: () => import(/* webpackChunkName: 'hl-django' */ 'highlight.js/lib/languages/django'),
+ dns: () => import(/* webpackChunkName: 'hl-dns' */ 'highlight.js/lib/languages/dns'),
+ dockerfile: () =>
+ import(/* webpackChunkName: 'hl-dockerfile' */ 'highlight.js/lib/languages/dockerfile'),
+ dos: () => import(/* webpackChunkName: 'hl-dos' */ 'highlight.js/lib/languages/dos'),
+ dsconfig: () =>
+ import(/* webpackChunkName: 'hl-dsconfig' */ 'highlight.js/lib/languages/dsconfig'),
+ dts: () => import(/* webpackChunkName: 'hl-dts' */ 'highlight.js/lib/languages/dts'),
+ dust: () => import(/* webpackChunkName: 'hl-dust' */ 'highlight.js/lib/languages/dust'),
+ ebnf: () => import(/* webpackChunkName: 'hl-ebnf' */ 'highlight.js/lib/languages/ebnf'),
+ elixir: () => import(/* webpackChunkName: 'hl-elixir' */ 'highlight.js/lib/languages/elixir'),
+ elm: () => import(/* webpackChunkName: 'hl-elm' */ 'highlight.js/lib/languages/elm'),
+ erb: () => import(/* webpackChunkName: 'hl-erb' */ 'highlight.js/lib/languages/erb'),
+ 'erlang-repl': () =>
+ import(/* webpackChunkName: 'hl-erlang-repl' */ 'highlight.js/lib/languages/erlang-repl'),
+ erlang: () => import(/* webpackChunkName: 'hl-erlang' */ 'highlight.js/lib/languages/erlang'),
+ excel: () => import(/* webpackChunkName: 'hl-excel' */ 'highlight.js/lib/languages/excel'),
+ fix: () => import(/* webpackChunkName: 'hl-fix' */ 'highlight.js/lib/languages/fix'),
+ flix: () => import(/* webpackChunkName: 'hl-flix' */ 'highlight.js/lib/languages/flix'),
+ fortran: () => import(/* webpackChunkName: 'hl-fortran' */ 'highlight.js/lib/languages/fortran'),
+ fsharp: () => import(/* webpackChunkName: 'hl-fsharp' */ 'highlight.js/lib/languages/fsharp'),
+ gams: () => import(/* webpackChunkName: 'hl-gams' */ 'highlight.js/lib/languages/gams'),
+ gauss: () => import(/* webpackChunkName: 'hl-gauss' */ 'highlight.js/lib/languages/gauss'),
+ gcode: () => import(/* webpackChunkName: 'hl-gcode' */ 'highlight.js/lib/languages/gcode'),
+ gherkin: () => import(/* webpackChunkName: 'hl-gherkin' */ 'highlight.js/lib/languages/gherkin'),
+ glsl: () => import(/* webpackChunkName: 'hl-glsl' */ 'highlight.js/lib/languages/glsl'),
+ gml: () => import(/* webpackChunkName: 'hl-gml' */ 'highlight.js/lib/languages/gml'),
+ go: () => import(/* webpackChunkName: 'hl-go' */ 'highlight.js/lib/languages/go'),
+ golo: () => import(/* webpackChunkName: 'hl-golo' */ 'highlight.js/lib/languages/golo'),
+ gradle: () => import(/* webpackChunkName: 'hl-gradle' */ 'highlight.js/lib/languages/gradle'),
+ groovy: () => import(/* webpackChunkName: 'hl-groovy' */ 'highlight.js/lib/languages/groovy'),
+ haml: () => import(/* webpackChunkName: 'hl-haml' */ 'highlight.js/lib/languages/haml'),
+ handlebars: () =>
+ import(/* webpackChunkName: 'hl-handlebars' */ 'highlight.js/lib/languages/handlebars'),
+ haskell: () => import(/* webpackChunkName: 'hl-haskell' */ 'highlight.js/lib/languages/haskell'),
+ haxe: () => import(/* webpackChunkName: 'hl-haxe' */ 'highlight.js/lib/languages/haxe'),
+ hsp: () => import(/* webpackChunkName: 'hl-hsp' */ 'highlight.js/lib/languages/hsp'),
+ http: () => import(/* webpackChunkName: 'hl-http' */ 'highlight.js/lib/languages/http'),
+ hy: () => import(/* webpackChunkName: 'hl-hy' */ 'highlight.js/lib/languages/hy'),
+ inform7: () => import(/* webpackChunkName: 'hl-inform7' */ 'highlight.js/lib/languages/inform7'),
+ ini: () => import(/* webpackChunkName: 'hl-ini' */ 'highlight.js/lib/languages/ini'),
+ irpf90: () => import(/* webpackChunkName: 'hl-irpf90' */ 'highlight.js/lib/languages/irpf90'),
+ isbl: () => import(/* webpackChunkName: 'hl-isbl' */ 'highlight.js/lib/languages/isbl'),
+ java: () => import(/* webpackChunkName: 'hl-java' */ 'highlight.js/lib/languages/java'),
+ javascript: () =>
+ import(/* webpackChunkName: 'hl-javascript' */ 'highlight.js/lib/languages/javascript'),
+ 'jboss-cli': () =>
+ import(/* webpackChunkName: 'hl-jboss-cli' */ 'highlight.js/lib/languages/jboss-cli'),
+ json: () => import(/* webpackChunkName: 'hl-json' */ 'highlight.js/lib/languages/json'),
+ 'julia-repl': () =>
+ import(/* webpackChunkName: 'hl-julia-repl' */ 'highlight.js/lib/languages/julia-repl'),
+ julia: () => import(/* webpackChunkName: 'hl-julia' */ 'highlight.js/lib/languages/julia'),
+ kotlin: () => import(/* webpackChunkName: 'hl-kotlin' */ 'highlight.js/lib/languages/kotlin'),
+ lasso: () => import(/* webpackChunkName: 'hl-lasso' */ 'highlight.js/lib/languages/lasso'),
+ latex: () => import(/* webpackChunkName: 'hl-latex' */ 'highlight.js/lib/languages/latex'),
+ ldif: () => import(/* webpackChunkName: 'hl-ldif' */ 'highlight.js/lib/languages/ldif'),
+ leaf: () => import(/* webpackChunkName: 'hl-leaf' */ 'highlight.js/lib/languages/leaf'),
+ less: () => import(/* webpackChunkName: 'hl-less' */ 'highlight.js/lib/languages/less'),
+ lisp: () => import(/* webpackChunkName: 'hl-lisp' */ 'highlight.js/lib/languages/lisp'),
+ livecodeserver: () =>
+ import(/* webpackChunkName: 'hl-livecodeserver' */ 'highlight.js/lib/languages/livecodeserver'),
+ livescript: () =>
+ import(/* webpackChunkName: 'hl-livescript' */ 'highlight.js/lib/languages/livescript'),
+ llvm: () => import(/* webpackChunkName: 'hl-llvm' */ 'highlight.js/lib/languages/llvm'),
+ lsl: () => import(/* webpackChunkName: 'hl-lsl' */ 'highlight.js/lib/languages/lsl'),
+ lua: () => import(/* webpackChunkName: 'hl-lua' */ 'highlight.js/lib/languages/lua'),
+ makefile: () =>
+ import(/* webpackChunkName: 'hl-makefile' */ 'highlight.js/lib/languages/makefile'),
+ markdown: () =>
+ import(/* webpackChunkName: 'hl-markdown' */ 'highlight.js/lib/languages/markdown'),
+ mathematica: () =>
+ import(/* webpackChunkName: 'hl-mathematica' */ 'highlight.js/lib/languages/mathematica'),
+ matlab: () => import(/* webpackChunkName: 'hl-matlab' */ 'highlight.js/lib/languages/matlab'),
+ maxima: () => import(/* webpackChunkName: 'hl-maxima' */ 'highlight.js/lib/languages/maxima'),
+ mel: () => import(/* webpackChunkName: 'hl-mel' */ 'highlight.js/lib/languages/mel'),
+ mercury: () => import(/* webpackChunkName: 'hl-mercury' */ 'highlight.js/lib/languages/mercury'),
+ mipsasm: () => import(/* webpackChunkName: 'hl-mipsasm' */ 'highlight.js/lib/languages/mipsasm'),
+ mizar: () => import(/* webpackChunkName: 'hl-mizar' */ 'highlight.js/lib/languages/mizar'),
+ mojolicious: () =>
+ import(/* webpackChunkName: 'hl-mojolicious' */ 'highlight.js/lib/languages/mojolicious'),
+ monkey: () => import(/* webpackChunkName: 'hl-monkey' */ 'highlight.js/lib/languages/monkey'),
+ moonscript: () =>
+ import(/* webpackChunkName: 'hl-moonscript' */ 'highlight.js/lib/languages/moonscript'),
+ n1ql: () => import(/* webpackChunkName: 'hl-n1ql' */ 'highlight.js/lib/languages/n1ql'),
+ nestedtext: () =>
+ import(/* webpackChunkName: 'hl-nestedtext' */ 'highlight.js/lib/languages/nestedtext'),
+ nginx: () => import(/* webpackChunkName: 'hl-nginx' */ 'highlight.js/lib/languages/nginx'),
+ nim: () => import(/* webpackChunkName: 'hl-nim' */ 'highlight.js/lib/languages/nim'),
+ nix: () => import(/* webpackChunkName: 'hl-nix' */ 'highlight.js/lib/languages/nix'),
+ 'node-repl': () =>
+ import(/* webpackChunkName: 'hl-node-repl' */ 'highlight.js/lib/languages/node-repl'),
+ nsis: () => import(/* webpackChunkName: 'hl-nsis' */ 'highlight.js/lib/languages/nsis'),
+ objectivec: () =>
+ import(/* webpackChunkName: 'hl-objectivec' */ 'highlight.js/lib/languages/objectivec'),
+ ocaml: () => import(/* webpackChunkName: 'hl-ocaml' */ 'highlight.js/lib/languages/ocaml'),
+ openscad: () =>
+ import(/* webpackChunkName: 'hl-openscad' */ 'highlight.js/lib/languages/openscad'),
+ oxygene: () => import(/* webpackChunkName: 'hl-oxygene' */ 'highlight.js/lib/languages/oxygene'),
+ parser3: () => import(/* webpackChunkName: 'hl-parser3' */ 'highlight.js/lib/languages/parser3'),
+ perl: () => import(/* webpackChunkName: 'hl-perl' */ 'highlight.js/lib/languages/perl'),
+ pf: () => import(/* webpackChunkName: 'hl-pf' */ 'highlight.js/lib/languages/pf'),
+ pgsql: () => import(/* webpackChunkName: 'hl-pgsql' */ 'highlight.js/lib/languages/pgsql'),
+ 'php-template': () =>
+ import(/* webpackChunkName: 'hl-php-template' */ 'highlight.js/lib/languages/php-template'),
+ php: () => import(/* webpackChunkName: 'hl-php' */ 'highlight.js/lib/languages/php'),
+ plaintext: () =>
+ import(/* webpackChunkName: 'hl-plaintext' */ 'highlight.js/lib/languages/plaintext'),
+ pony: () => import(/* webpackChunkName: 'hl-pony' */ 'highlight.js/lib/languages/pony'),
+ powershell: () =>
+ import(/* webpackChunkName: 'hl-powershell' */ 'highlight.js/lib/languages/powershell'),
+ processing: () =>
+ import(/* webpackChunkName: 'hl-processing' */ 'highlight.js/lib/languages/processing'),
+ profile: () => import(/* webpackChunkName: 'hl-profile' */ 'highlight.js/lib/languages/profile'),
+ prolog: () => import(/* webpackChunkName: 'hl-prolog' */ 'highlight.js/lib/languages/prolog'),
+ properties: () =>
+ import(/* webpackChunkName: 'hl-properties' */ 'highlight.js/lib/languages/properties'),
+ protobuf: () =>
+ import(/* webpackChunkName: 'hl-protobuf' */ 'highlight.js/lib/languages/protobuf'),
+ puppet: () => import(/* webpackChunkName: 'hl-puppet' */ 'highlight.js/lib/languages/puppet'),
+ purebasic: () =>
+ import(/* webpackChunkName: 'hl-purebasic' */ 'highlight.js/lib/languages/purebasic'),
+ 'python-repl': () =>
+ import(/* webpackChunkName: 'hl-python-repl' */ 'highlight.js/lib/languages/python-repl'),
+ python: () => import(/* webpackChunkName: 'hl-python' */ 'highlight.js/lib/languages/python'),
+ q: () => import(/* webpackChunkName: 'hl-q' */ 'highlight.js/lib/languages/q'),
+ qml: () => import(/* webpackChunkName: 'hl-qml' */ 'highlight.js/lib/languages/qml'),
+ r: () => import(/* webpackChunkName: 'hl-r' */ 'highlight.js/lib/languages/r'),
+ reasonml: () =>
+ import(/* webpackChunkName: 'hl-reasonml' */ 'highlight.js/lib/languages/reasonml'),
+ rib: () => import(/* webpackChunkName: 'hl-rib' */ 'highlight.js/lib/languages/rib'),
+ roboconf: () =>
+ import(/* webpackChunkName: 'hl-roboconf' */ 'highlight.js/lib/languages/roboconf'),
+ routeros: () =>
+ import(/* webpackChunkName: 'hl-routeros' */ 'highlight.js/lib/languages/routeros'),
+ rsl: () => import(/* webpackChunkName: 'hl-rsl' */ 'highlight.js/lib/languages/rsl'),
+ ruby: () => import(/* webpackChunkName: 'hl-ruby' */ 'highlight.js/lib/languages/ruby'),
+ ruleslanguage: () =>
+ import(/* webpackChunkName: 'hl-ruleslanguage' */ 'highlight.js/lib/languages/ruleslanguage'),
+ rust: () => import(/* webpackChunkName: 'hl-rust' */ 'highlight.js/lib/languages/rust'),
+ sas: () => import(/* webpackChunkName: 'hl-sas' */ 'highlight.js/lib/languages/sas'),
+ scala: () => import(/* webpackChunkName: 'hl-scala' */ 'highlight.js/lib/languages/scala'),
+ scheme: () => import(/* webpackChunkName: 'hl-scheme' */ 'highlight.js/lib/languages/scheme'),
+ scilab: () => import(/* webpackChunkName: 'hl-scilab' */ 'highlight.js/lib/languages/scilab'),
+ scss: () => import(/* webpackChunkName: 'hl-scss' */ 'highlight.js/lib/languages/scss'),
+ shell: () => import(/* webpackChunkName: 'hl-shell' */ 'highlight.js/lib/languages/shell'),
+ smali: () => import(/* webpackChunkName: 'hl-smali' */ 'highlight.js/lib/languages/smali'),
+ smalltalk: () =>
+ import(/* webpackChunkName: 'hl-smalltalk' */ 'highlight.js/lib/languages/smalltalk'),
+ sml: () => import(/* webpackChunkName: 'hl-sml' */ 'highlight.js/lib/languages/sml'),
+ sqf: () => import(/* webpackChunkName: 'hl-sqf' */ 'highlight.js/lib/languages/sqf'),
+ sql: () => import(/* webpackChunkName: 'hl-sql' */ 'highlight.js/lib/languages/sql'),
+ stan: () => import(/* webpackChunkName: 'hl-stan' */ 'highlight.js/lib/languages/stan'),
+ stata: () => import(/* webpackChunkName: 'hl-stata' */ 'highlight.js/lib/languages/stata'),
+ step21: () => import(/* webpackChunkName: 'hl-step21' */ 'highlight.js/lib/languages/step21'),
+ stylus: () => import(/* webpackChunkName: 'hl-stylus' */ 'highlight.js/lib/languages/stylus'),
+ subunit: () => import(/* webpackChunkName: 'hl-subunit' */ 'highlight.js/lib/languages/subunit'),
+ swift: () => import(/* webpackChunkName: 'hl-swift' */ 'highlight.js/lib/languages/swift'),
+ taggerscript: () =>
+ import(/* webpackChunkName: 'hl-taggerscript' */ 'highlight.js/lib/languages/taggerscript'),
+ tap: () => import(/* webpackChunkName: 'hl-tap' */ 'highlight.js/lib/languages/tap'),
+ tcl: () => import(/* webpackChunkName: 'hl-tcl' */ 'highlight.js/lib/languages/tcl'),
+ thrift: () => import(/* webpackChunkName: 'hl-thrift' */ 'highlight.js/lib/languages/thrift'),
+ tp: () => import(/* webpackChunkName: 'hl-tp' */ 'highlight.js/lib/languages/tp'),
+ twig: () => import(/* webpackChunkName: 'hl-twig' */ 'highlight.js/lib/languages/twig'),
+ typescript: () =>
+ import(/* webpackChunkName: 'hl-typescript' */ 'highlight.js/lib/languages/typescript'),
+ vala: () => import(/* webpackChunkName: 'hl-vala' */ 'highlight.js/lib/languages/vala'),
+ vbnet: () => import(/* webpackChunkName: 'hl-vbnet' */ 'highlight.js/lib/languages/vbnet'),
+ 'vbscript-html': () =>
+ import(/* webpackChunkName: 'hl-vbscript-html' */ 'highlight.js/lib/languages/vbscript-html'),
+ vbscript: () =>
+ import(/* webpackChunkName: 'hl-vbscript' */ 'highlight.js/lib/languages/vbscript'),
+ verilog: () => import(/* webpackChunkName: 'hl-verilog' */ 'highlight.js/lib/languages/verilog'),
+ vhdl: () => import(/* webpackChunkName: 'hl-vhdl' */ 'highlight.js/lib/languages/vhdl'),
+ vim: () => import(/* webpackChunkName: 'hl-vim' */ 'highlight.js/lib/languages/vim'),
+ wasm: () => import(/* webpackChunkName: 'hl-wasm' */ 'highlight.js/lib/languages/wasm'),
+ wren: () => import(/* webpackChunkName: 'hl-wren' */ 'highlight.js/lib/languages/wren'),
+ x86asm: () => import(/* webpackChunkName: 'hl-x86asm' */ 'highlight.js/lib/languages/x86asm'),
+ xl: () => import(/* webpackChunkName: 'hl-xl' */ 'highlight.js/lib/languages/xl'),
+ xml: () => import(/* webpackChunkName: 'hl-xml' */ 'highlight.js/lib/languages/xml'),
+ xquery: () => import(/* webpackChunkName: 'hl-xquery' */ 'highlight.js/lib/languages/xquery'),
+ yaml: () => import(/* webpackChunkName: 'hl-yaml' */ 'highlight.js/lib/languages/yaml'),
+ zephir: () => import(/* webpackChunkName: 'hl-zephir' */ 'highlight.js/lib/languages/zephir'),
+};
diff --git a/app/assets/javascripts/vue_shared/components/markdown/field.vue b/app/assets/javascripts/vue_shared/components/markdown/field.vue
index 722df3cc58b..765bd146a03 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/field.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/field.vue
@@ -111,6 +111,16 @@ export default {
required: false,
default: false,
},
+ showCommentToolBar: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
+ restrictedToolBarItems: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
},
data() {
return {
@@ -331,7 +341,7 @@ export default {
:enable-preview="enablePreview"
:show-suggest-popover="showSuggestPopover"
:suggestion-start-index="suggestionsStartIndex"
- data-testid="markdownHeader"
+ :restricted-tool-bar-items="restrictedToolBarItems"
@preview-markdown="showPreviewTab"
@write-markdown="showWriteTab"
@handleSuggestDismissed="() => $emit('handleSuggestDismissed')"
@@ -350,6 +360,7 @@ export default {
:markdown-docs-path="markdownDocsPath"
:quick-actions-docs-path="quickActionsDocsPath"
:can-attach-file="canAttachFile"
+ :show-comment-tool-bar="showCommentToolBar"
/>
</div>
</div>
diff --git a/app/assets/javascripts/vue_shared/components/markdown/header.vue b/app/assets/javascripts/vue_shared/components/markdown/header.vue
index d0bd5046bf0..7993910ec12 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/header.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/header.vue
@@ -54,6 +54,11 @@ export default {
required: false,
default: true,
},
+ restrictedToolBarItems: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
},
data() {
return {
@@ -207,6 +212,7 @@ export default {
icon="italic"
/>
<toolbar-button
+ v-if="!restrictedToolBarItems.includes('strikethrough')"
tag="~~"
:button-title="
sprintf(s__('MarkdownEditor|Add strikethrough text (%{modifierKey}⇧X)'), {
@@ -217,6 +223,7 @@ export default {
icon="strikethrough"
/>
<toolbar-button
+ v-if="!restrictedToolBarItems.includes('quote')"
:prepend="true"
:tag="tag"
:button-title="__('Insert a quote')"
@@ -272,24 +279,28 @@ export default {
icon="link"
/>
<toolbar-button
+ v-if="!restrictedToolBarItems.includes('bullet-list')"
:prepend="true"
tag="- "
:button-title="__('Add a bullet list')"
icon="list-bulleted"
/>
<toolbar-button
+ v-if="!restrictedToolBarItems.includes('numbered-list')"
:prepend="true"
tag="1. "
:button-title="__('Add a numbered list')"
icon="list-numbered"
/>
<toolbar-button
+ v-if="!restrictedToolBarItems.includes('task-list')"
:prepend="true"
tag="- [ ] "
:button-title="__('Add a task list')"
icon="list-task"
/>
<toolbar-button
+ v-if="!restrictedToolBarItems.includes('collapsible-section')"
:tag="mdCollapsibleSection"
:prepend="true"
tag-select="Click to expand"
@@ -297,12 +308,14 @@ export default {
icon="details-block"
/>
<toolbar-button
+ v-if="!restrictedToolBarItems.includes('table')"
:tag="mdTable"
:prepend="true"
:button-title="__('Add a table')"
icon="table"
/>
<toolbar-button
+ v-if="!restrictedToolBarItems.includes('full-screen')"
class="js-zen-enter"
:prepend="true"
:button-title="__('Go full screen')"
diff --git a/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue b/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue
index f1c293c87f4..1d83a61f2ae 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue
@@ -24,6 +24,11 @@ export default {
required: false,
default: true,
},
+ showCommentToolBar: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
},
computed: {
hasQuickActionsDocsPath() {
@@ -34,7 +39,7 @@ export default {
</script>
<template>
- <div class="comment-toolbar clearfix">
+ <div v-if="showCommentToolBar" class="comment-toolbar clearfix">
<div class="toolbar-text">
<template v-if="!hasQuickActionsDocsPath && markdownDocsPath">
<gl-link :href="markdownDocsPath" target="_blank">
diff --git a/app/assets/javascripts/vue_shared/components/source_viewer/source_viewer.vue b/app/assets/javascripts/vue_shared/components/source_viewer/source_viewer.vue
index edf2229a9a1..ed87a202b15 100644
--- a/app/assets/javascripts/vue_shared/components/source_viewer/source_viewer.vue
+++ b/app/assets/javascripts/vue_shared/components/source_viewer/source_viewer.vue
@@ -2,6 +2,7 @@
import { GlSafeHtmlDirective, GlLoadingIcon } from '@gitlab/ui';
import LineHighlighter from '~/blob/line_highlighter';
import eventHub from '~/notes/event_hub';
+import languageLoader from '~/content_editor/services/highlight_js_language_loader';
import { ROUGE_TO_HLJS_LANGUAGE_MAP, LINES_PER_CHUNK } from './constants';
import Chunk from './components/chunk.vue';
@@ -129,7 +130,7 @@ export default {
let languageDefinition;
try {
- languageDefinition = await import(`highlight.js/lib/languages/${this.language}`);
+ languageDefinition = await languageLoader[this.language]();
this.hljs.registerLanguage(this.language, languageDefinition.default);
} catch (message) {
this.$emit('error', message);
diff --git a/app/controllers/admin/deploy_keys_controller.rb b/app/controllers/admin/deploy_keys_controller.rb
index ed63e65d4df..b24b25446b0 100644
--- a/app/controllers/admin/deploy_keys_controller.rb
+++ b/app/controllers/admin/deploy_keys_controller.rb
@@ -5,6 +5,7 @@ class Admin::DeployKeysController < Admin::ApplicationController
before_action :deploy_key, only: [:destroy, :edit, :update]
feature_category :continuous_delivery
+ urgency :low
def index
end
diff --git a/app/controllers/autocomplete_controller.rb b/app/controllers/autocomplete_controller.rb
index eb6cedb5d4a..db793106aad 100644
--- a/app/controllers/autocomplete_controller.rb
+++ b/app/controllers/autocomplete_controller.rb
@@ -12,7 +12,7 @@ class AutocompleteController < ApplicationController
feature_category :code_review, [:merge_request_target_branches]
feature_category :continuous_delivery, [:deploy_keys_with_owners]
- urgency :low, [:merge_request_target_branches]
+ urgency :low, [:merge_request_target_branches, :deploy_keys_with_owners]
urgency :default, [:users]
urgency :medium, [:projects]
diff --git a/app/controllers/groups/deploy_tokens_controller.rb b/app/controllers/groups/deploy_tokens_controller.rb
index 9ef22aa33dc..5bab6f59a42 100644
--- a/app/controllers/groups/deploy_tokens_controller.rb
+++ b/app/controllers/groups/deploy_tokens_controller.rb
@@ -4,6 +4,7 @@ class Groups::DeployTokensController < Groups::ApplicationController
before_action :authorize_destroy_deploy_token!
feature_category :continuous_delivery
+ urgency :low
def revoke
Groups::DeployTokens::RevokeService.new(@group, current_user, params).execute
diff --git a/app/controllers/groups/releases_controller.rb b/app/controllers/groups/releases_controller.rb
index e87135cc104..3fa6023a78c 100644
--- a/app/controllers/groups/releases_controller.rb
+++ b/app/controllers/groups/releases_controller.rb
@@ -3,6 +3,7 @@
module Groups
class ReleasesController < Groups::ApplicationController
feature_category :release_evidence
+ urgency :low
def index
respond_to do |format|
diff --git a/app/controllers/groups/settings/repository_controller.rb b/app/controllers/groups/settings/repository_controller.rb
index 7404075985b..b0431c31179 100644
--- a/app/controllers/groups/settings/repository_controller.rb
+++ b/app/controllers/groups/settings/repository_controller.rb
@@ -12,6 +12,7 @@ module Groups
end
feature_category :continuous_delivery
+ urgency :low
def create_deploy_token
result = Groups::DeployTokens::CreateService.new(@group, current_user, deploy_token_params).execute
diff --git a/app/controllers/projects/deploy_keys_controller.rb b/app/controllers/projects/deploy_keys_controller.rb
index ce25f86d692..96afe9dbb9f 100644
--- a/app/controllers/projects/deploy_keys_controller.rb
+++ b/app/controllers/projects/deploy_keys_controller.rb
@@ -11,6 +11,7 @@ class Projects::DeployKeysController < Projects::ApplicationController
layout 'project_settings'
feature_category :continuous_delivery
+ urgency :low
def index
respond_to do |format|
diff --git a/app/controllers/projects/deploy_tokens_controller.rb b/app/controllers/projects/deploy_tokens_controller.rb
index 42c2d8b17f1..ed77fa2fee6 100644
--- a/app/controllers/projects/deploy_tokens_controller.rb
+++ b/app/controllers/projects/deploy_tokens_controller.rb
@@ -4,6 +4,7 @@ class Projects::DeployTokensController < Projects::ApplicationController
before_action :authorize_admin_project!
feature_category :continuous_delivery
+ urgency :low
def revoke
@token = @project.deploy_tokens.find(params[:id])
diff --git a/app/controllers/projects/deployments_controller.rb b/app/controllers/projects/deployments_controller.rb
index 231684427fb..bebade1b21b 100644
--- a/app/controllers/projects/deployments_controller.rb
+++ b/app/controllers/projects/deployments_controller.rb
@@ -4,6 +4,7 @@ class Projects::DeploymentsController < Projects::ApplicationController
before_action :authorize_read_deployment!
feature_category :continuous_delivery
+ urgency :low
# rubocop: disable CodeReuse/ActiveRecord
def index
diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb
index 92db849015a..a100afd3488 100644
--- a/app/controllers/projects/environments_controller.rb
+++ b/app/controllers/projects/environments_controller.rb
@@ -27,6 +27,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController
after_action :expire_etag_cache, only: [:cancel_auto_stop]
feature_category :continuous_delivery
+ urgency :low
def index
@project = ProjectPresenter.new(project, current_user: current_user)
diff --git a/app/controllers/projects/feature_flags_clients_controller.rb b/app/controllers/projects/feature_flags_clients_controller.rb
index 9a1f8932a27..2652345fc5a 100644
--- a/app/controllers/projects/feature_flags_clients_controller.rb
+++ b/app/controllers/projects/feature_flags_clients_controller.rb
@@ -5,6 +5,7 @@ class Projects::FeatureFlagsClientsController < Projects::ApplicationController
before_action :feature_flags_client
feature_category :feature_flags
+ urgency :low
def reset_token
feature_flags_client.reset_token!
diff --git a/app/controllers/projects/feature_flags_controller.rb b/app/controllers/projects/feature_flags_controller.rb
index 7c0da8f8a24..1d1fe91ad70 100644
--- a/app/controllers/projects/feature_flags_controller.rb
+++ b/app/controllers/projects/feature_flags_controller.rb
@@ -11,6 +11,7 @@ class Projects::FeatureFlagsController < Projects::ApplicationController
before_action :feature_flag, only: [:edit, :update, :destroy]
feature_category :feature_flags
+ urgency :low
def index
@feature_flags = FeatureFlagsFinder
diff --git a/app/controllers/projects/feature_flags_user_lists_controller.rb b/app/controllers/projects/feature_flags_user_lists_controller.rb
index fd81321924a..023eb51cc94 100644
--- a/app/controllers/projects/feature_flags_user_lists_controller.rb
+++ b/app/controllers/projects/feature_flags_user_lists_controller.rb
@@ -5,6 +5,7 @@ class Projects::FeatureFlagsUserListsController < Projects::ApplicationControlle
before_action :user_list, only: [:edit, :show]
feature_category :feature_flags
+ urgency :low
def index
end
diff --git a/app/controllers/projects/releases/evidences_controller.rb b/app/controllers/projects/releases/evidences_controller.rb
index 41e2ce81eb8..9f59898878c 100644
--- a/app/controllers/projects/releases/evidences_controller.rb
+++ b/app/controllers/projects/releases/evidences_controller.rb
@@ -8,6 +8,7 @@ module Projects
before_action :authorize_read_release_evidence!
feature_category :release_evidence
+ urgency :low
def show
respond_to do |format|
diff --git a/app/controllers/projects/releases_controller.rb b/app/controllers/projects/releases_controller.rb
index 19413d97d9d..84c763088e5 100644
--- a/app/controllers/projects/releases_controller.rb
+++ b/app/controllers/projects/releases_controller.rb
@@ -10,6 +10,7 @@ class Projects::ReleasesController < Projects::ApplicationController
before_action :validate_suffix_path, :fetch_latest_tag, only: :latest_permalink
feature_category :release_orchestration
+ urgency :low
def index
respond_to do |format|
diff --git a/app/controllers/projects/settings/repository_controller.rb b/app/controllers/projects/settings/repository_controller.rb
index a28c08e87cb..0fd2d56229a 100644
--- a/app/controllers/projects/settings/repository_controller.rb
+++ b/app/controllers/projects/settings/repository_controller.rb
@@ -12,7 +12,7 @@ module Projects
feature_category :source_code_management, [:show, :cleanup]
feature_category :continuous_delivery, [:create_deploy_token]
- urgency :low, [:show]
+ urgency :low, [:show, :create_deploy_token]
def show
render_show
diff --git a/app/controllers/projects/tags/releases_controller.rb b/app/controllers/projects/tags/releases_controller.rb
index 8e5539f546b..b852673d82a 100644
--- a/app/controllers/projects/tags/releases_controller.rb
+++ b/app/controllers/projects/tags/releases_controller.rb
@@ -9,6 +9,7 @@ class Projects::Tags::ReleasesController < Projects::ApplicationController
before_action :release
feature_category :release_evidence
+ urgency :low
def edit
end
diff --git a/app/views/projects/merge_requests/_code_dropdown.html.haml b/app/views/projects/merge_requests/_code_dropdown.html.haml
index ab5716d5485..0bd28e315d9 100644
--- a/app/views/projects/merge_requests/_code_dropdown.html.haml
+++ b/app/views/projects/merge_requests/_code_dropdown.html.haml
@@ -1,6 +1,6 @@
.float-left.gl-md-ml-3.dropdown.gl-new-dropdown{ class: "gl-display-none! gl-md-display-flex!" }
#js-check-out-modal{ data: how_merge_modal_data(@merge_request) }
- = button_tag type: 'button', class: "btn dropdown-toggle btn-confirm gl-button gl-dropdown-toggle", data: { toggle: 'dropdown', qa_selector: 'mr_code_drodpown' } do
+ = button_tag type: 'button', class: "btn dropdown-toggle btn-confirm gl-button gl-dropdown-toggle", data: { toggle: 'dropdown', qa_selector: 'mr_code_dropdown' } do
%span.gl-new-dropdown-button-text= _('Code')
= sprite_icon "chevron-down", size: 16, css_class: "dropdown-icon gl-icon gl-ml-2 gl-mr-0!"
.dropdown-menu.dropdown-menu-right
@@ -29,11 +29,11 @@
%li.gl-new-dropdown-section-header
%header.dropdown-header
= _('Download')
- %li.gl-new-dropdown-item{ data: { qa_selector: 'download_email_patches_menu_item' } }
- = link_to merge_request_path(@merge_request, format: :patch), class: 'dropdown-item', download: '' do
+ %li.gl-new-dropdown-item
+ = link_to merge_request_path(@merge_request, format: :patch), class: 'dropdown-item', download: '', data: { qa_selector: 'download_email_patches_menu_item' } do
.gl-new-dropdown-item-text-wrapper
= _('Email patches')
- %li.gl-new-dropdown-item{ data: { qa_selector: 'download_plain_diff_menu_item' } }
- = link_to merge_request_path(@merge_request, format: :diff), class: 'dropdown-item' do
+ %li.gl-new-dropdown-item
+ = link_to merge_request_path(@merge_request, format: :diff), class: 'dropdown-item', data: { qa_selector: 'download_plain_diff_menu_item' } do
.gl-new-dropdown-item-text-wrapper
= _('Plain diff')
diff --git a/doc/api/plan_limits.md b/doc/api/plan_limits.md
index 75c8d241513..760f64894bb 100644
--- a/doc/api/plan_limits.md
+++ b/doc/api/plan_limits.md
@@ -35,6 +35,14 @@ Example response:
```json
{
+ "ci_pipeline_size": 0,
+ "ci_active_jobs": 0,
+ "ci_active_pipelines": 0,
+ "ci_project_subscriptions": 2,
+ "ci_pipeline_schedules": 10,
+ "ci_needs_size_limit": 50,
+ "ci_registered_group_runners": 1000,
+ "ci_registered_project_runners": 1000,
"conan_max_file_size": 3221225472,
"generic_packages_max_file_size": 5368709120,
"helm_max_file_size": 5242880,
@@ -57,6 +65,14 @@ PUT /application/plan_limits
| Attribute | Type | Required | Description |
| --------------------------------- | ------- | -------- | ----------- |
| `plan_name` | string | yes | Name of the plan to update. |
+| `ci_pipeline_size` | integer | no | Maximum number of jobs in a single pipeline. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85895) in GitLab 15.0. |
+| `ci_active_jobs` | integer | no | Total number of jobs in currently active pipelines. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85895) in GitLab 15.0. |
+| `ci_active_pipelines` | integer | no | Maximum number of active pipelines per project. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85895) in GitLab 15.0. |
+| `ci_project_subscriptions` | integer | no | Maximum number of pipeline subscriptions to and from a project. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85895) in GitLab 15.0. |
+| `ci_pipeline_schedules` | integer | no | Maximum number of pipeline schedules. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85895) in GitLab 15.0. |
+| `ci_needs_size_limit` | integer | no | Maximum number of [DAG](../ci/directed_acyclic_graph/index.md) dependencies that a job can have. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85895) in GitLab 15.0. |
+| `ci_registered_group_runners` | integer | no | Maximum number of runners registered per group. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85895) in GitLab 15.0. |
+| `ci_registered_project_runners` | integer | no | Maximum number of runners registered per project. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85895) in GitLab 15.0. |
| `conan_max_file_size` | integer | no | Maximum Conan package file size in bytes. |
| `generic_packages_max_file_size` | integer | no | Maximum generic package file size in bytes. |
| `helm_max_file_size` | integer | no | Maximum Helm chart file size in bytes. |
@@ -74,6 +90,14 @@ Example response:
```json
{
+ "ci_pipeline_size": 0,
+ "ci_active_jobs": 0,
+ "ci_active_pipelines": 0,
+ "ci_project_subscriptions": 2,
+ "ci_pipeline_schedules": 10,
+ "ci_needs_size_limit": 50,
+ "ci_registered_group_runners": 1000,
+ "ci_registered_project_runners": 1000,
"conan_max_file_size": 3221225472,
"generic_packages_max_file_size": 5368709120,
"helm_max_file_size": 5242880,
diff --git a/doc/development/testing_guide/testing_migrations_guide.md b/doc/development/testing_guide/testing_migrations_guide.md
index 4092c1a2f6d..d71788e21f3 100644
--- a/doc/development/testing_guide/testing_migrations_guide.md
+++ b/doc/development/testing_guide/testing_migrations_guide.md
@@ -227,6 +227,18 @@ expect('MigrationClass').to have_scheduled_batched_migration(
)
```
+#### `be_finalize_background_migration_of`
+
+Verifies that a migration calls `finalize_background_migration` with the expected background migration class.
+
+```ruby
+# Migration
+finalize_background_migration('MigrationClass')
+
+# Spec
+expect(described_class).to be_finalize_background_migration_of('MigrationClass')
+```
+
### Examples of migration tests
Migration tests depend on what the migration does exactly, the most common types are data migrations and scheduling background migrations.
diff --git a/lib/api/admin/plan_limits.rb b/lib/api/admin/plan_limits.rb
index 99be30809d2..1dbcf812085 100644
--- a/lib/api/admin/plan_limits.rb
+++ b/lib/api/admin/plan_limits.rb
@@ -35,6 +35,14 @@ module API
params do
requires :plan_name, type: String, values: Plan.all_plans, desc: 'Name of the plan'
+ optional :ci_pipeline_size, type: Integer, desc: 'Maximum number of jobs in a single pipeline'
+ optional :ci_active_jobs, type: Integer, desc: 'Total number of jobs in currently active pipelines'
+ optional :ci_active_pipelines, type: Integer, desc: 'Maximum number of active pipelines per project'
+ optional :ci_project_subscriptions, type: Integer, desc: 'Maximum number of pipeline subscriptions to and from a project'
+ optional :ci_pipeline_schedules, type: Integer, desc: 'Maximum number of pipeline schedules'
+ optional :ci_needs_size_limit, type: Integer, desc: 'Maximum number of DAG dependencies that a job can have'
+ optional :ci_registered_group_runners, type: Integer, desc: 'Maximum number of runners registered per group'
+ optional :ci_registered_project_runners, type: Integer, desc: 'Maximum number of runners registered per project'
optional :conan_max_file_size, type: Integer, desc: 'Maximum Conan package file size in bytes'
optional :generic_packages_max_file_size, type: Integer, desc: 'Maximum generic package file size in bytes'
optional :helm_max_file_size, type: Integer, desc: 'Maximum Helm chart file size in bytes'
diff --git a/lib/api/ci/resource_groups.rb b/lib/api/ci/resource_groups.rb
index 616bec499d4..3c8c68caeae 100644
--- a/lib/api/ci/resource_groups.rb
+++ b/lib/api/ci/resource_groups.rb
@@ -6,6 +6,7 @@ module API
before { authenticate! }
feature_category :continuous_delivery
+ urgency :low
params do
requires :id, type: String, desc: 'The ID of a project'
diff --git a/lib/api/deploy_keys.rb b/lib/api/deploy_keys.rb
index 0ab9fe6644c..ca13db8701e 100644
--- a/lib/api/deploy_keys.rb
+++ b/lib/api/deploy_keys.rb
@@ -7,6 +7,7 @@ module API
before { authenticate! }
feature_category :continuous_delivery
+ urgency :low
helpers do
def add_deploy_keys_project(project, attrs = {})
diff --git a/lib/api/deploy_tokens.rb b/lib/api/deploy_tokens.rb
index 074c307e881..3e0411d2e91 100644
--- a/lib/api/deploy_tokens.rb
+++ b/lib/api/deploy_tokens.rb
@@ -5,6 +5,7 @@ module API
include PaginationParams
feature_category :continuous_delivery
+ urgency :low
helpers do
def scope_params
diff --git a/lib/api/deployments.rb b/lib/api/deployments.rb
index 6939853c06b..8db5f54b45a 100644
--- a/lib/api/deployments.rb
+++ b/lib/api/deployments.rb
@@ -8,6 +8,7 @@ module API
before { authenticate! }
feature_category :continuous_delivery
+ urgency :low
params do
requires :id, type: String, desc: 'The project ID'
diff --git a/lib/api/entities/plan_limit.rb b/lib/api/entities/plan_limit.rb
index 9f4d1635998..33d59a4207c 100644
--- a/lib/api/entities/plan_limit.rb
+++ b/lib/api/entities/plan_limit.rb
@@ -3,6 +3,14 @@
module API
module Entities
class PlanLimit < Grape::Entity
+ expose :ci_pipeline_size
+ expose :ci_active_jobs
+ expose :ci_active_pipelines
+ expose :ci_project_subscriptions
+ expose :ci_pipeline_schedules
+ expose :ci_needs_size_limit
+ expose :ci_registered_group_runners
+ expose :ci_registered_project_runners
expose :conan_max_file_size
expose :generic_packages_max_file_size
expose :helm_max_file_size
diff --git a/lib/api/environments.rb b/lib/api/environments.rb
index 19b48c1e3cf..646b7e5eedb 100644
--- a/lib/api/environments.rb
+++ b/lib/api/environments.rb
@@ -8,6 +8,7 @@ module API
before { authenticate! }
feature_category :continuous_delivery
+ urgency :low
params do
requires :id, type: String, desc: 'The project ID'
diff --git a/lib/api/feature_flags.rb b/lib/api/feature_flags.rb
index c1f958ac007..42050888c14 100644
--- a/lib/api/feature_flags.rb
+++ b/lib/api/feature_flags.rb
@@ -8,6 +8,7 @@ module API
.merge(name: API::NO_SLASH_URL_PART_REGEX)
feature_category :feature_flags
+ urgency :low
before do
authorize_read_feature_flags!
diff --git a/lib/api/feature_flags_user_lists.rb b/lib/api/feature_flags_user_lists.rb
index 8577da173b1..854719db4a1 100644
--- a/lib/api/feature_flags_user_lists.rb
+++ b/lib/api/feature_flags_user_lists.rb
@@ -9,6 +9,7 @@ module API
}
feature_category :feature_flags
+ urgency :low
before do
authorize_admin_feature_flags_user_lists!
diff --git a/lib/api/features.rb b/lib/api/features.rb
index 398e57794c8..51d1e5b4628 100644
--- a/lib/api/features.rb
+++ b/lib/api/features.rb
@@ -5,6 +5,7 @@ module API
before { authenticated_as_admin! }
feature_category :feature_flags
+ urgency :low
helpers do
def gate_value(params)
diff --git a/lib/api/freeze_periods.rb b/lib/api/freeze_periods.rb
index d001ced8581..e69baeee97f 100644
--- a/lib/api/freeze_periods.rb
+++ b/lib/api/freeze_periods.rb
@@ -7,6 +7,7 @@ module API
before { authenticate! }
feature_category :continuous_delivery
+ urgency :low
params do
requires :id, type: String, desc: 'The ID of a project'
diff --git a/lib/api/release/links.rb b/lib/api/release/links.rb
index 52c73104bb4..bc5ffe5b21f 100644
--- a/lib/api/release/links.rb
+++ b/lib/api/release/links.rb
@@ -11,6 +11,7 @@ module API
before { authorize! :read_release, user_project }
feature_category :release_orchestration
+ urgency :low
params do
requires :id, type: String, desc: 'The ID of a project'
diff --git a/lib/api/releases.rb b/lib/api/releases.rb
index 9e085a91a7c..048cbf3ad83 100644
--- a/lib/api/releases.rb
+++ b/lib/api/releases.rb
@@ -9,6 +9,7 @@ module API
RELEASE_CLI_USER_AGENT = 'GitLab-release-cli'
feature_category :release_orchestration
+ urgency :low
params do
requires :id, type: String, desc: 'The ID of a group'
diff --git a/lib/api/unleash.rb b/lib/api/unleash.rb
index 6dadaf4fc54..37fe540cde1 100644
--- a/lib/api/unleash.rb
+++ b/lib/api/unleash.rb
@@ -30,7 +30,7 @@ module API
end
desc 'Get a list of features'
- get 'client/features', urgency: :medium do
+ get 'client/features' do
present :version, 1
present :features, feature_flags, with: ::API::Entities::UnleashFeature
end
diff --git a/qa/qa/page/merge_request/show.rb b/qa/qa/page/merge_request/show.rb
index 6aa2d4b407a..8f5ac62d127 100644
--- a/qa/qa/page/merge_request/show.rb
+++ b/qa/qa/page/merge_request/show.rb
@@ -50,8 +50,9 @@ module QA
end
view 'app/views/projects/merge_requests/_code_dropdown.html.haml' do
- element :mr_code_drodpown
+ element :mr_code_dropdown
element :download_email_patches_menu_item
+ element :download_plain_diff_menu_item
element :open_in_web_ide_button
end
@@ -342,12 +343,12 @@ module QA
end
def view_email_patches
- click_element(:mr_code_drodpown)
+ click_element(:mr_code_dropdown)
visit_link_in_element(:download_email_patches_menu_item)
end
def view_plain_diff
- click_element(:mr_code_drodpown)
+ click_element(:mr_code_dropdown)
visit_link_in_element(:download_plain_diff_menu_item)
end
diff --git a/spec/frontend/snippets/components/__snapshots__/snippet_description_edit_spec.js.snap b/spec/frontend/snippets/components/__snapshots__/snippet_description_edit_spec.js.snap
index 2b26c306c68..fec300ddd7e 100644
--- a/spec/frontend/snippets/components/__snapshots__/snippet_description_edit_spec.js.snap
+++ b/spec/frontend/snippets/components/__snapshots__/snippet_description_edit_spec.js.snap
@@ -28,9 +28,9 @@ exports[`Snippet Description Edit component rendering matches the snapshot 1`] =
data-uploads-path=""
>
<markdown-header-stub
- data-testid="markdownHeader"
enablepreview="true"
linecontent=""
+ restrictedtoolbaritems=""
suggestionstartindex="0"
/>
@@ -81,6 +81,7 @@ exports[`Snippet Description Edit component rendering matches the snapshot 1`] =
canattachfile="true"
markdowndocspath="help/"
quickactionsdocspath=""
+ showcommenttoolbar="true"
/>
</div>
</div>
diff --git a/spec/frontend/vue_shared/components/markdown/field_spec.js b/spec/frontend/vue_shared/components/markdown/field_spec.js
index ab74ea868a2..b3376f26a25 100644
--- a/spec/frontend/vue_shared/components/markdown/field_spec.js
+++ b/spec/frontend/vue_shared/components/markdown/field_spec.js
@@ -5,12 +5,14 @@ import { TEST_HOST, FIXTURES_PATH } from 'spec/test_constants';
import axios from '~/lib/utils/axios_utils';
import MarkdownField from '~/vue_shared/components/markdown/field.vue';
import MarkdownFieldHeader from '~/vue_shared/components/markdown/header.vue';
+import MarkdownToolbar from '~/vue_shared/components/markdown/toolbar.vue';
import { mountExtended } from 'helpers/vue_test_utils_helper';
const markdownPreviewPath = `${TEST_HOST}/preview`;
const markdownDocsPath = `${TEST_HOST}/docs`;
const textareaValue = 'testing\n123';
const uploadsPath = 'test/uploads';
+const restrictedToolBarItems = ['quote'];
function assertMarkdownTabs(isWrite, writeLink, previewLink, wrapper) {
expect(writeLink.element.children[0].classList.contains('active')).toBe(isWrite);
@@ -63,6 +65,7 @@ describe('Markdown field component', () => {
textareaValue,
lines,
enablePreview,
+ restrictedToolBarItems,
},
provide: {
glFeatures: {
@@ -81,6 +84,8 @@ describe('Markdown field component', () => {
const getAttachButton = () => subject.find('.button-attach-file');
const clickAttachButton = () => getAttachButton().trigger('click');
const findDropzone = () => subject.find('.div-dropzone');
+ const findMarkdownHeader = () => subject.findComponent(MarkdownFieldHeader);
+ const findMarkdownToolbar = () => subject.findComponent(MarkdownToolbar);
describe('mounted', () => {
const previewHTML = `
@@ -184,6 +189,15 @@ describe('Markdown field component', () => {
assertMarkdownTabs(false, writeLink, previewLink, subject);
});
+
+ it('passes correct props to MarkdownToolbar', () => {
+ expect(findMarkdownToolbar().props()).toEqual({
+ canAttachFile: true,
+ markdownDocsPath,
+ quickActionsDocsPath: '',
+ showCommentToolBar: true,
+ });
+ });
});
describe('markdown buttons', () => {
@@ -314,9 +328,7 @@ describe('Markdown field component', () => {
it('escapes new line characters', () => {
createSubject({ lines: [{ rich_text: 'hello world\\n' }] });
- expect(subject.find('[data-testid="markdownHeader"]').props('lineContent')).toBe(
- 'hello world%br',
- );
+ expect(findMarkdownHeader().props('lineContent')).toBe('hello world%br');
});
});
@@ -330,4 +342,12 @@ describe('Markdown field component', () => {
expect(subject.findComponent(MarkdownFieldHeader).props('enablePreview')).toBe(true);
});
+
+ it('passess restricted tool bar items', () => {
+ createSubject();
+
+ expect(subject.findComponent(MarkdownFieldHeader).props('restrictedToolBarItems')).toBe(
+ restrictedToolBarItems,
+ );
+ });
});
diff --git a/spec/frontend/vue_shared/components/markdown/header_spec.js b/spec/frontend/vue_shared/components/markdown/header_spec.js
index fa4ca63f910..67222cab247 100644
--- a/spec/frontend/vue_shared/components/markdown/header_spec.js
+++ b/spec/frontend/vue_shared/components/markdown/header_spec.js
@@ -166,4 +166,26 @@ describe('Markdown field header component', () => {
expect(wrapper.findByTestId('preview-tab').exists()).toBe(false);
});
+
+ describe('restricted tool bar items', () => {
+ let defaultCount;
+
+ beforeEach(() => {
+ defaultCount = findToolbarButtons().length;
+ });
+
+ it('restricts items as per input', () => {
+ createWrapper({
+ restrictedToolBarItems: ['quote'],
+ });
+
+ expect(findToolbarButtons().length).toBe(defaultCount - 1);
+ });
+
+ it('shows all items by default', () => {
+ createWrapper();
+
+ expect(findToolbarButtons().length).toBe(defaultCount);
+ });
+ });
});
diff --git a/spec/frontend/vue_shared/components/markdown/toolbar_spec.js b/spec/frontend/vue_shared/components/markdown/toolbar_spec.js
index 8bff85b0bda..f698794b951 100644
--- a/spec/frontend/vue_shared/components/markdown/toolbar_spec.js
+++ b/spec/frontend/vue_shared/components/markdown/toolbar_spec.js
@@ -33,4 +33,18 @@ describe('toolbar', () => {
expect(wrapper.vm.$el.querySelector('.uploading-container')).toBeNull();
});
});
+
+ describe('comment tool bar settings', () => {
+ it('does not show comment tool bar div', () => {
+ createMountedWrapper({ showCommentToolBar: false });
+
+ expect(wrapper.find('.comment-toolbar').exists()).toBe(false);
+ });
+
+ it('shows comment tool bar by default', () => {
+ createMountedWrapper();
+
+ expect(wrapper.find('.comment-toolbar').exists()).toBe(true);
+ });
+ });
});
diff --git a/spec/lib/api/entities/plan_limit_spec.rb b/spec/lib/api/entities/plan_limit_spec.rb
index 1b8b21d47f3..0f38d119088 100644
--- a/spec/lib/api/entities/plan_limit_spec.rb
+++ b/spec/lib/api/entities/plan_limit_spec.rb
@@ -9,6 +9,14 @@ RSpec.describe API::Entities::PlanLimit do
it 'exposes correct attributes' do
expect(subject).to include(
+ :ci_pipeline_size,
+ :ci_active_jobs,
+ :ci_active_pipelines,
+ :ci_project_subscriptions,
+ :ci_pipeline_schedules,
+ :ci_needs_size_limit,
+ :ci_registered_group_runners,
+ :ci_registered_project_runners,
:conan_max_file_size,
:generic_packages_max_file_size,
:helm_max_file_size,
diff --git a/spec/requests/api/admin/plan_limits_spec.rb b/spec/requests/api/admin/plan_limits_spec.rb
index 03642ad617e..855a0ae0c0a 100644
--- a/spec/requests/api/admin/plan_limits_spec.rb
+++ b/spec/requests/api/admin/plan_limits_spec.rb
@@ -23,6 +23,14 @@ RSpec.describe API::Admin::PlanLimits, 'PlanLimits' do
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to be_an Hash
+ expect(json_response['ci_pipeline_size']).to eq(Plan.default.actual_limits.ci_pipeline_size)
+ expect(json_response['ci_active_jobs']).to eq(Plan.default.actual_limits.ci_active_jobs)
+ expect(json_response['ci_active_pipelines']).to eq(Plan.default.actual_limits.ci_active_pipelines)
+ expect(json_response['ci_project_subscriptions']).to eq(Plan.default.actual_limits.ci_project_subscriptions)
+ expect(json_response['ci_pipeline_schedules']).to eq(Plan.default.actual_limits.ci_pipeline_schedules)
+ expect(json_response['ci_needs_size_limit']).to eq(Plan.default.actual_limits.ci_needs_size_limit)
+ expect(json_response['ci_registered_group_runners']).to eq(Plan.default.actual_limits.ci_registered_group_runners)
+ expect(json_response['ci_registered_project_runners']).to eq(Plan.default.actual_limits.ci_registered_project_runners)
expect(json_response['conan_max_file_size']).to eq(Plan.default.actual_limits.conan_max_file_size)
expect(json_response['generic_packages_max_file_size']).to eq(Plan.default.actual_limits.generic_packages_max_file_size)
expect(json_response['helm_max_file_size']).to eq(Plan.default.actual_limits.helm_max_file_size)
@@ -44,6 +52,14 @@ RSpec.describe API::Admin::PlanLimits, 'PlanLimits' do
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to be_an Hash
+ expect(json_response['ci_pipeline_size']).to eq(Plan.default.actual_limits.ci_pipeline_size)
+ expect(json_response['ci_active_jobs']).to eq(Plan.default.actual_limits.ci_active_jobs)
+ expect(json_response['ci_active_pipelines']).to eq(Plan.default.actual_limits.ci_active_pipelines)
+ expect(json_response['ci_project_subscriptions']).to eq(Plan.default.actual_limits.ci_project_subscriptions)
+ expect(json_response['ci_pipeline_schedules']).to eq(Plan.default.actual_limits.ci_pipeline_schedules)
+ expect(json_response['ci_needs_size_limit']).to eq(Plan.default.actual_limits.ci_needs_size_limit)
+ expect(json_response['ci_registered_group_runners']).to eq(Plan.default.actual_limits.ci_registered_group_runners)
+ expect(json_response['ci_registered_project_runners']).to eq(Plan.default.actual_limits.ci_registered_project_runners)
expect(json_response['conan_max_file_size']).to eq(Plan.default.actual_limits.conan_max_file_size)
expect(json_response['generic_packages_max_file_size']).to eq(Plan.default.actual_limits.generic_packages_max_file_size)
expect(json_response['helm_max_file_size']).to eq(Plan.default.actual_limits.helm_max_file_size)
@@ -84,6 +100,14 @@ RSpec.describe API::Admin::PlanLimits, 'PlanLimits' do
it 'updates multiple plan limits' do
put api('/application/plan_limits', admin), params: {
'plan_name': 'default',
+ 'ci_pipeline_size': 101,
+ 'ci_active_jobs': 102,
+ 'ci_active_pipelines': 103,
+ 'ci_project_subscriptions': 104,
+ 'ci_pipeline_schedules': 105,
+ 'ci_needs_size_limit': 106,
+ 'ci_registered_group_runners': 107,
+ 'ci_registered_project_runners': 108,
'conan_max_file_size': 10,
'generic_packages_max_file_size': 20,
'helm_max_file_size': 25,
@@ -96,6 +120,14 @@ RSpec.describe API::Admin::PlanLimits, 'PlanLimits' do
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to be_an Hash
+ expect(json_response['ci_pipeline_size']).to eq(101)
+ expect(json_response['ci_active_jobs']).to eq(102)
+ expect(json_response['ci_active_pipelines']).to eq(103)
+ expect(json_response['ci_project_subscriptions']).to eq(104)
+ expect(json_response['ci_pipeline_schedules']).to eq(105)
+ expect(json_response['ci_needs_size_limit']).to eq(106)
+ expect(json_response['ci_registered_group_runners']).to eq(107)
+ expect(json_response['ci_registered_project_runners']).to eq(108)
expect(json_response['conan_max_file_size']).to eq(10)
expect(json_response['generic_packages_max_file_size']).to eq(20)
expect(json_response['helm_max_file_size']).to eq(25)
@@ -131,6 +163,14 @@ RSpec.describe API::Admin::PlanLimits, 'PlanLimits' do
it 'fails to update plan limits' do
put api('/application/plan_limits', admin), params: {
'plan_name': 'default',
+ 'ci_pipeline_size': 'z',
+ 'ci_active_jobs': 'y',
+ 'ci_active_pipelines': 'x',
+ 'ci_project_subscriptions': 'w',
+ 'ci_pipeline_schedules': 'v',
+ 'ci_needs_size_limit': 'u',
+ 'ci_registered_group_runners': 't',
+ 'ci_registered_project_runners': 's',
'conan_max_file_size': 'a',
'generic_packages_max_file_size': 'b',
'helm_max_file_size': 'h',
@@ -143,6 +183,14 @@ RSpec.describe API::Admin::PlanLimits, 'PlanLimits' do
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['error']).to include(
+ 'ci_pipeline_size is invalid',
+ 'ci_active_jobs is invalid',
+ 'ci_active_pipelines is invalid',
+ 'ci_project_subscriptions is invalid',
+ 'ci_pipeline_schedules is invalid',
+ 'ci_needs_size_limit is invalid',
+ 'ci_registered_group_runners is invalid',
+ 'ci_registered_project_runners is invalid',
'conan_max_file_size is invalid',
'generic_packages_max_file_size is invalid',
'helm_max_file_size is invalid',
diff --git a/spec/support/matchers/background_migrations_matchers.rb b/spec/support/matchers/background_migrations_matchers.rb
index b20bc1f1329..b471323dd72 100644
--- a/spec/support/matchers/background_migrations_matchers.rb
+++ b/spec/support/matchers/background_migrations_matchers.rb
@@ -83,3 +83,11 @@ RSpec::Matchers.define :have_scheduled_batched_migration do |table_name: nil, co
expect(batched_migrations.count).to be(0)
end
end
+
+RSpec::Matchers.define :be_finalize_background_migration_of do |migration|
+ define_method :matches? do |klass|
+ expect_next_instance_of(klass) do |instance|
+ expect(instance).to receive(:finalize_background_migration).with(migration)
+ end
+ end
+end