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:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-12-09 00:10:06 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-12-09 00:10:06 +0300
commitcb36ae7dd5fde175f8a24bfa97b20e9b2e2058bf (patch)
tree691c6406d0eea6c8417ecf99a47c77ca6b90cc18
parent0612ffef1200ffdcc9443c0469f7874b37271275 (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.eslintrc.yml1
-rw-r--r--app/assets/javascripts/autosave.js1
-rw-r--r--app/assets/javascripts/awards_handler.js1
-rw-r--r--app/assets/javascripts/behaviors/shortcuts/shortcuts.js1
-rw-r--r--app/assets/javascripts/boards/components/new_list_dropdown.js1
-rw-r--r--app/assets/javascripts/clusters/clusters_bundle.js8
-rw-r--r--app/assets/javascripts/commit/image_file.js2
-rw-r--r--app/assets/javascripts/confirm_danger_modal.js2
-rw-r--r--app/assets/javascripts/create_cluster/components/cluster_form_dropdown.vue1
-rw-r--r--app/assets/javascripts/create_label.js6
-rw-r--r--app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js1
-rw-r--r--app/assets/javascripts/deprecated_jquery_dropdown/gl_dropdown.js1
-rw-r--r--app/assets/javascripts/environments/components/environments_app.vue2
-rw-r--r--app/assets/javascripts/gfm_auto_complete.js1
-rw-r--r--app/assets/javascripts/gl_field_error.js2
-rw-r--r--app/assets/javascripts/gl_form.js4
-rw-r--r--app/assets/javascripts/ide/components/nav_dropdown.vue1
-rw-r--r--app/assets/javascripts/issuable_bulk_update_actions.js1
-rw-r--r--app/assets/javascripts/issue_show/components/app.vue3
-rw-r--r--app/assets/javascripts/issue_show/utils/parse_data.js6
-rw-r--r--app/assets/javascripts/issues_list/components/issuables_list_app.vue1
-rw-r--r--app/assets/javascripts/lib/utils/text_markdown.js3
-rw-r--r--app/assets/javascripts/main.js1
-rw-r--r--app/assets/javascripts/members.js2
-rw-r--r--app/assets/javascripts/mirrors/mirror_repos.js1
-rw-r--r--app/assets/javascripts/mirrors/ssh_mirror.js5
-rw-r--r--app/assets/javascripts/monitoring/components/charts/time_series.vue1
-rw-r--r--app/assets/javascripts/notes.js1
-rw-r--r--app/assets/javascripts/pager.js1
-rw-r--r--app/assets/javascripts/pages/projects/issues/show.js3
-rw-r--r--app/assets/javascripts/pipeline_editor/graphql/queries/ci_config.graphql11
-rw-r--r--app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue57
-rw-r--r--app/assets/javascripts/pipelines/graphql/queries/pipeline_stages.fragment.graphql12
-rw-r--r--app/assets/javascripts/project_find_file.js1
-rw-r--r--app/assets/javascripts/projects/project_new.js3
-rw-r--r--app/assets/javascripts/related_issues/components/related_issuable_input.vue2
-rw-r--r--app/assets/javascripts/right_sidebar.js3
-rw-r--r--app/assets/javascripts/settings_panels.js1
-rw-r--r--app/assets/javascripts/smart_interval.js1
-rw-r--r--app/assets/javascripts/terminal/terminal.js2
-rw-r--r--app/assets/javascripts/version_check_image.js1
-rw-r--r--app/models/concerns/enums/data_visualization_palette.rb33
-rw-r--r--changelogs/unreleased/262857-creation-rotation-table-models.yml5
-rw-r--r--changelogs/unreleased/group-member-webhook-column.yml5
-rw-r--r--changelogs/unreleased/sh-handle-zero-maximum-upload-size.yml5
-rw-r--r--db/migrate/20201124030537_create_incident_management_on_call_rotations.rb35
-rw-r--r--db/migrate/20201125233219_add_incident_management_on_call_participants.rb33
-rw-r--r--db/migrate/20201204205814_add_member_events_to_web_hooks.rb9
-rw-r--r--db/schema_migrations/202011240305371
-rw-r--r--db/schema_migrations/202011252332191
-rw-r--r--db/schema_migrations/202012042058141
-rw-r--r--db/structure.sql70
-rw-r--r--doc/administration/pages/index.md11
-rw-r--r--doc/development/agent/local.md60
-rw-r--r--doc/development/secure_coding_guidelines.md62
-rw-r--r--doc/user/clusters/agent/index.md6
-rw-r--r--doc/user/project/clusters/index.md10
-rw-r--r--lib/gitlab.rb6
-rw-r--r--lib/object_storage/direct_upload.rb9
-rw-r--r--spec/frontend/.eslintrc.yml3
-rw-r--r--spec/frontend/issue_show/issue_spec.js3
-rw-r--r--spec/frontend/pipeline_editor/mock_data.js10
-rw-r--r--spec/frontend/pipeline_editor/pipeline_editor_app_spec.js26
-rw-r--r--spec/lib/gitlab_spec.rb20
-rw-r--r--spec/lib/object_storage/direct_upload_spec.rb28
65 files changed, 516 insertions, 95 deletions
diff --git a/.eslintrc.yml b/.eslintrc.yml
index 48294e00844..1e6df6f5a77 100644
--- a/.eslintrc.yml
+++ b/.eslintrc.yml
@@ -42,6 +42,7 @@ rules:
no-jquery/no-serialize: error
promise/always-return: off
promise/no-callback-in-promise: off
+ "@gitlab/no-global-event-off": error
overrides:
- files:
- '**/spec/**/*'
diff --git a/app/assets/javascripts/autosave.js b/app/assets/javascripts/autosave.js
index 5f50fcc112e..0a05e0d44ce 100644
--- a/app/assets/javascripts/autosave.js
+++ b/app/assets/javascripts/autosave.js
@@ -74,6 +74,7 @@ export default class Autosave {
}
dispose() {
+ // eslint-disable-next-line @gitlab/no-global-event-off
this.field.off('input');
}
}
diff --git a/app/assets/javascripts/awards_handler.js b/app/assets/javascripts/awards_handler.js
index 17e6255700a..d937060536a 100644
--- a/app/assets/javascripts/awards_handler.js
+++ b/app/assets/javascripts/awards_handler.js
@@ -596,6 +596,7 @@ export class AwardsHandler {
hideMenuElement($emojiMenu) {
$emojiMenu.on(transitionEndEventString, e => {
if (e.currentTarget === e.target) {
+ // eslint-disable-next-line @gitlab/no-global-event-off
$emojiMenu.removeClass(IS_RENDERED).off(transitionEndEventString);
}
});
diff --git a/app/assets/javascripts/behaviors/shortcuts/shortcuts.js b/app/assets/javascripts/behaviors/shortcuts/shortcuts.js
index a53150f8d61..c0f67923191 100644
--- a/app/assets/javascripts/behaviors/shortcuts/shortcuts.js
+++ b/app/assets/javascripts/behaviors/shortcuts/shortcuts.js
@@ -97,6 +97,7 @@ export default class Shortcuts {
e.preventDefault();
});
+ // eslint-disable-next-line @gitlab/no-global-event-off
$('.js-shortcuts-modal-trigger')
.off('click')
.on('click', this.onToggleHelp);
diff --git a/app/assets/javascripts/boards/components/new_list_dropdown.js b/app/assets/javascripts/boards/components/new_list_dropdown.js
index 47eee5306da..d1011c24977 100644
--- a/app/assets/javascripts/boards/components/new_list_dropdown.js
+++ b/app/assets/javascripts/boards/components/new_list_dropdown.js
@@ -15,6 +15,7 @@ function shouldCreateListGraphQL(label) {
return store.getters.shouldUseGraphQL && !store.getters.getListByLabelId(fullLabelId(label));
}
+// eslint-disable-next-line @gitlab/no-global-event-off
$(document)
.off('created.label')
.on('created.label', (e, label, addNewList) => {
diff --git a/app/assets/javascripts/clusters/clusters_bundle.js b/app/assets/javascripts/clusters/clusters_bundle.js
index ba005e98d53..a533a1a78e8 100644
--- a/app/assets/javascripts/clusters/clusters_bundle.js
+++ b/app/assets/javascripts/clusters/clusters_bundle.js
@@ -265,13 +265,21 @@ export default class Clusters {
removeListeners() {
eventHub.$off('installApplication', this.installApplication);
eventHub.$off('updateApplication', this.updateApplication);
+ // eslint-disable-next-line @gitlab/no-global-event-off
eventHub.$off('saveKnativeDomain');
+ // eslint-disable-next-line @gitlab/no-global-event-off
eventHub.$off('setKnativeDomain');
+ // eslint-disable-next-line @gitlab/no-global-event-off
eventHub.$off('setCrossplaneProviderStack');
+ // eslint-disable-next-line @gitlab/no-global-event-off
eventHub.$off('uninstallApplication');
+ // eslint-disable-next-line @gitlab/no-global-event-off
eventHub.$off('setIngressModSecurityEnabled');
+ // eslint-disable-next-line @gitlab/no-global-event-off
eventHub.$off('setIngressModSecurityMode');
+ // eslint-disable-next-line @gitlab/no-global-event-off
eventHub.$off('resetIngressModSecurityChanges');
+ // eslint-disable-next-line @gitlab/no-global-event-off
eventHub.$off('setFluentdSettings');
}
diff --git a/app/assets/javascripts/commit/image_file.js b/app/assets/javascripts/commit/image_file.js
index 28abe558f53..b70f8d6e736 100644
--- a/app/assets/javascripts/commit/image_file.js
+++ b/app/assets/javascripts/commit/image_file.js
@@ -72,12 +72,14 @@ export default class ImageFile {
callback(e, left);
};
+ // eslint-disable-next-line @gitlab/no-global-event-off
$el
.off('mousedown')
.off('touchstart')
.on('mousedown', dragStart)
.on('touchstart', dragStart);
+ // eslint-disable-next-line @gitlab/no-global-event-off
$body
.off('mouseup')
.off('mousemove')
diff --git a/app/assets/javascripts/confirm_danger_modal.js b/app/assets/javascripts/confirm_danger_modal.js
index 7321e4d18cc..4f7bc829b0c 100644
--- a/app/assets/javascripts/confirm_danger_modal.js
+++ b/app/assets/javascripts/confirm_danger_modal.js
@@ -14,6 +14,7 @@ function openConfirmDangerModal($form, $modal, text) {
$submit.disable();
$input.focus();
+ // eslint-disable-next-line @gitlab/no-global-event-off
$input.off('input').on('input', function handleInput() {
const confirmText = rstrip($(this).val());
if (confirmText === confirmTextMatch) {
@@ -23,6 +24,7 @@ function openConfirmDangerModal($form, $modal, text) {
}
});
+ // eslint-disable-next-line @gitlab/no-global-event-off
$('.js-confirm-danger-submit', $modal)
.off('click')
.on('click', () => {
diff --git a/app/assets/javascripts/create_cluster/components/cluster_form_dropdown.vue b/app/assets/javascripts/create_cluster/components/cluster_form_dropdown.vue
index e9d484bdd94..1e3a19b9da1 100644
--- a/app/assets/javascripts/create_cluster/components/cluster_form_dropdown.vue
+++ b/app/assets/javascripts/create_cluster/components/cluster_form_dropdown.vue
@@ -154,6 +154,7 @@ export default {
});
},
beforeDestroy() {
+ // eslint-disable-next-line @gitlab/no-global-event-off
$(this.$refs.dropdown).off();
},
methods: {
diff --git a/app/assets/javascripts/create_label.js b/app/assets/javascripts/create_label.js
index 9c0ed7f79d4..0d53efe8689 100644
--- a/app/assets/javascripts/create_label.js
+++ b/app/assets/javascripts/create_label.js
@@ -29,11 +29,17 @@ export default class CreateLabelDropdown {
}
cleanBinding() {
+ // eslint-disable-next-line @gitlab/no-global-event-off
this.$colorSuggestions.off('click');
+ // eslint-disable-next-line @gitlab/no-global-event-off
this.$newLabelField.off('keyup change');
+ // eslint-disable-next-line @gitlab/no-global-event-off
this.$newColorField.off('keyup change');
+ // eslint-disable-next-line @gitlab/no-global-event-off
this.$dropdownBack.off('click');
+ // eslint-disable-next-line @gitlab/no-global-event-off
this.$cancelButton.off('click');
+ // eslint-disable-next-line @gitlab/no-global-event-off
this.$newLabelCreateButton.off('click');
}
diff --git a/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js b/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js
index 4cccabca28b..70ebe91a3b2 100644
--- a/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js
+++ b/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js
@@ -74,6 +74,7 @@ export default () => {
const $dropdown = $('.js-ca-dropdown');
const $label = $dropdown.find('.dropdown-label');
+ // eslint-disable-next-line @gitlab/no-global-event-off
$dropdown
.find('li a')
.off('click')
diff --git a/app/assets/javascripts/deprecated_jquery_dropdown/gl_dropdown.js b/app/assets/javascripts/deprecated_jquery_dropdown/gl_dropdown.js
index c17f2d2efe4..fe57dd2dc8f 100644
--- a/app/assets/javascripts/deprecated_jquery_dropdown/gl_dropdown.js
+++ b/app/assets/javascripts/deprecated_jquery_dropdown/gl_dropdown.js
@@ -622,6 +622,7 @@ export class GitLabDropdown {
// eslint-disable-next-line class-methods-use-this
removeArrowKeyEvent() {
+ // eslint-disable-next-line @gitlab/no-global-event-off
return $('body').off('keydown');
}
diff --git a/app/assets/javascripts/environments/components/environments_app.vue b/app/assets/javascripts/environments/components/environments_app.vue
index c1b9ba755a6..ce84caeb28f 100644
--- a/app/assets/javascripts/environments/components/environments_app.vue
+++ b/app/assets/javascripts/environments/components/environments_app.vue
@@ -93,7 +93,9 @@ export default {
},
beforeDestroy() {
+ // eslint-disable-next-line @gitlab/no-global-event-off
eventHub.$off('toggleFolder');
+ // eslint-disable-next-line @gitlab/no-global-event-off
eventHub.$off('toggleDeployBoard');
},
diff --git a/app/assets/javascripts/gfm_auto_complete.js b/app/assets/javascripts/gfm_auto_complete.js
index 14538ad7237..dcb27434a07 100644
--- a/app/assets/javascripts/gfm_auto_complete.js
+++ b/app/assets/javascripts/gfm_auto_complete.js
@@ -78,6 +78,7 @@ class GfmAutoComplete {
this.input.each((i, input) => {
const $input = $(input);
if (!$input.hasClass('js-gfm-input-initialized')) {
+ // eslint-disable-next-line @gitlab/no-global-event-off
$input.off('focus.setupAtWho').on('focus.setupAtWho', this.setupAtWho.bind(this, $input));
$input.on('change.atwho', () => input.dispatchEvent(new Event('input')));
// This triggers at.js again
diff --git a/app/assets/javascripts/gl_field_error.js b/app/assets/javascripts/gl_field_error.js
index ac4c8d28ee4..60f1b7f5aa4 100644
--- a/app/assets/javascripts/gl_field_error.js
+++ b/app/assets/javascripts/gl_field_error.js
@@ -80,6 +80,7 @@ export default class GlFieldError {
// hidden when injected into DOM
errorAnchor.after(this.fieldErrorElement);
+ // eslint-disable-next-line @gitlab/no-global-event-off
this.inputElement.off('invalid').on('invalid', this.handleInvalidSubmit.bind(this));
this.scopedSiblings = this.safelySelectSiblings();
}
@@ -117,6 +118,7 @@ export default class GlFieldError {
this.form.focusInvalid.apply(this.form);
// For UX, wait til after first invalid submission to check each keyup
+ // eslint-disable-next-line @gitlab/no-global-event-off
this.inputElement
.off('keyup.fieldValidator')
.on('keyup.fieldValidator', this.updateValidity.bind(this));
diff --git a/app/assets/javascripts/gl_form.js b/app/assets/javascripts/gl_form.js
index 6958cf4c173..4a3755f39cc 100644
--- a/app/assets/javascripts/gl_form.js
+++ b/app/assets/javascripts/gl_form.js
@@ -70,8 +70,10 @@ export default class GLForm {
}
setupAutosize() {
+ // eslint-disable-next-line @gitlab/no-global-event-off
this.textarea.off('autosize:resized').on('autosize:resized', this.setHeightData.bind(this));
+ // eslint-disable-next-line @gitlab/no-global-event-off
this.textarea.off('mouseup.autosize').on('mouseup.autosize', this.destroyAutosize.bind(this));
setTimeout(() => {
@@ -97,7 +99,9 @@ export default class GLForm {
}
clearEventListeners() {
+ // eslint-disable-next-line @gitlab/no-global-event-off
this.textarea.off('focus');
+ // eslint-disable-next-line @gitlab/no-global-event-off
this.textarea.off('blur');
removeMarkdownListeners(this.form);
}
diff --git a/app/assets/javascripts/ide/components/nav_dropdown.vue b/app/assets/javascripts/ide/components/nav_dropdown.vue
index a2338c6dec5..8cea8655461 100644
--- a/app/assets/javascripts/ide/components/nav_dropdown.vue
+++ b/app/assets/javascripts/ide/components/nav_dropdown.vue
@@ -30,6 +30,7 @@ export default {
.on('hide.bs.dropdown', () => this.hideDropdown());
},
removeDropdownListeners() {
+ // eslint-disable-next-line @gitlab/no-global-event-off
$(this.$refs.dropdown)
.off('show.bs.dropdown')
.off('hide.bs.dropdown');
diff --git a/app/assets/javascripts/issuable_bulk_update_actions.js b/app/assets/javascripts/issuable_bulk_update_actions.js
index c7806fc17fc..6ba21cd7869 100644
--- a/app/assets/javascripts/issuable_bulk_update_actions.js
+++ b/app/assets/javascripts/issuable_bulk_update_actions.js
@@ -15,6 +15,7 @@ export default {
},
bindEvents() {
+ // eslint-disable-next-line @gitlab/no-global-event-off
return this.form.off('submit').on('submit', this.onFormSubmit.bind(this));
},
diff --git a/app/assets/javascripts/issue_show/components/app.vue b/app/assets/javascripts/issue_show/components/app.vue
index 8acda4ada16..61e5db0970a 100644
--- a/app/assets/javascripts/issue_show/components/app.vue
+++ b/app/assets/javascripts/issue_show/components/app.vue
@@ -3,7 +3,6 @@ import { GlIcon, GlIntersectionObserver } from '@gitlab/ui';
import Visibility from 'visibilityjs';
import { __, s__, sprintf } from '~/locale';
import { deprecatedCreateFlash as createFlash } from '~/flash';
-import { sanitize } from '~/lib/dompurify';
import { visitUrl } from '~/lib/utils/url_utility';
import Poll from '~/lib/utils/poll';
import eventHub from '../event_hub';
@@ -179,7 +178,7 @@ export default {
const store = new Store({
titleHtml: this.initialTitleHtml,
titleText: this.initialTitleText,
- descriptionHtml: sanitize(this.initialDescriptionHtml),
+ descriptionHtml: this.initialDescriptionHtml,
descriptionText: this.initialDescriptionText,
updatedAt: this.updatedAt,
updatedByName: this.updatedByName,
diff --git a/app/assets/javascripts/issue_show/utils/parse_data.js b/app/assets/javascripts/issue_show/utils/parse_data.js
index aacbb6a9c6f..12f38005366 100644
--- a/app/assets/javascripts/issue_show/utils/parse_data.js
+++ b/app/assets/javascripts/issue_show/utils/parse_data.js
@@ -4,13 +4,11 @@ import { sanitize } from '~/lib/dompurify';
// We currently load + parse the data from the issue app and related merge request
let cachedParsedData;
-export const parseIssuableData = () => {
+export const parseIssuableData = el => {
try {
if (cachedParsedData) return cachedParsedData;
- const initialDataEl = document.getElementById('js-issuable-app');
-
- const parsedData = JSON.parse(initialDataEl.dataset.initial);
+ const parsedData = JSON.parse(el.dataset.initial);
parsedData.initialTitleHtml = sanitize(parsedData.initialTitleHtml);
parsedData.initialDescriptionHtml = sanitize(parsedData.initialDescriptionHtml);
diff --git a/app/assets/javascripts/issues_list/components/issuables_list_app.vue b/app/assets/javascripts/issues_list/components/issuables_list_app.vue
index 0d4f5bce965..0ce2bcc1cce 100644
--- a/app/assets/javascripts/issues_list/components/issuables_list_app.vue
+++ b/app/assets/javascripts/issues_list/components/issuables_list_app.vue
@@ -215,6 +215,7 @@ export default {
this.fetchIssuables();
},
beforeDestroy() {
+ // eslint-disable-next-line @gitlab/no-global-event-off
issueableEventHub.$off('issuables:toggleBulkEdit');
},
methods: {
diff --git a/app/assets/javascripts/lib/utils/text_markdown.js b/app/assets/javascripts/lib/utils/text_markdown.js
index dfb86787788..c711c0bd163 100644
--- a/app/assets/javascripts/lib/utils/text_markdown.js
+++ b/app/assets/javascripts/lib/utils/text_markdown.js
@@ -339,6 +339,7 @@ export function addMarkdownListeners(form) {
Shortcuts.initMarkdownEditorShortcuts($(this), updateTextForToolbarBtn);
});
+ // eslint-disable-next-line @gitlab/no-global-event-off
const $allToolbarBtns = $('.js-md', form)
.off('click')
.on('click', function() {
@@ -351,6 +352,7 @@ export function addMarkdownListeners(form) {
}
export function addEditorMarkdownListeners(editor) {
+ // eslint-disable-next-line @gitlab/no-global-event-off
$('.js-md')
.off('click')
.on('click', e => {
@@ -376,5 +378,6 @@ export function removeMarkdownListeners(form) {
Shortcuts.removeMarkdownEditorShortcuts($(this));
});
+ // eslint-disable-next-line @gitlab/no-global-event-off
return $('.js-md', form).off('click');
}
diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js
index f5ba933d012..de7648c31b1 100644
--- a/app/assets/javascripts/main.js
+++ b/app/assets/javascripts/main.js
@@ -77,6 +77,7 @@ if (process.env.NODE_ENV !== 'production' && gon?.test_env) {
document.addEventListener('beforeunload', () => {
// Unbind scroll events
+ // eslint-disable-next-line @gitlab/no-global-event-off
$(document).off('scroll');
// Close any open tooltips
tooltips.dispose(document.querySelectorAll('.has-tooltip, [data-toggle="tooltip"]'));
diff --git a/app/assets/javascripts/members.js b/app/assets/javascripts/members.js
index 6dd4018f87a..5bd228496da 100644
--- a/app/assets/javascripts/members.js
+++ b/app/assets/javascripts/members.js
@@ -11,9 +11,11 @@ export default class Members {
}
addListeners() {
+ // eslint-disable-next-line @gitlab/no-global-event-off
$('.js-member-update-control')
.off('change')
.on('change', this.formSubmit.bind(this));
+ // eslint-disable-next-line @gitlab/no-global-event-off
$('.js-edit-member-form')
.off('ajax:success')
.on('ajax:success', this.formSuccess.bind(this));
diff --git a/app/assets/javascripts/mirrors/mirror_repos.js b/app/assets/javascripts/mirrors/mirror_repos.js
index 818ca8aa847..18ea27e9a34 100644
--- a/app/assets/javascripts/mirrors/mirror_repos.js
+++ b/app/assets/javascripts/mirrors/mirror_repos.js
@@ -39,6 +39,7 @@ export default class MirrorRepos {
initMirrorSSH() {
if (this.$password) {
+ // eslint-disable-next-line @gitlab/no-global-event-off
this.$password.off('input.updateUrl');
}
this.$password = undefined;
diff --git a/app/assets/javascripts/mirrors/ssh_mirror.js b/app/assets/javascripts/mirrors/ssh_mirror.js
index eecfaa76168..c6486350f3b 100644
--- a/app/assets/javascripts/mirrors/ssh_mirror.js
+++ b/app/assets/javascripts/mirrors/ssh_mirror.js
@@ -185,10 +185,15 @@ export default class SSHMirror {
}
destroy() {
+ // eslint-disable-next-line @gitlab/no-global-event-off
this.$repositoryUrl.off('keyup');
+ // eslint-disable-next-line @gitlab/no-global-event-off
this.$form.find('.js-known-hosts').off('keyup');
+ // eslint-disable-next-line @gitlab/no-global-event-off
this.$dropdownAuthType.off('change');
+ // eslint-disable-next-line @gitlab/no-global-event-off
this.$btnDetectHostKeys.off('click');
+ // eslint-disable-next-line @gitlab/no-global-event-off
this.$btnSSHHostsShowAdvanced.off('click');
}
}
diff --git a/app/assets/javascripts/monitoring/components/charts/time_series.vue b/app/assets/javascripts/monitoring/components/charts/time_series.vue
index bda2adeb62a..170c5ff7695 100644
--- a/app/assets/javascripts/monitoring/components/charts/time_series.vue
+++ b/app/assets/javascripts/monitoring/components/charts/time_series.vue
@@ -367,6 +367,7 @@ export default {
},
);
+ // eslint-disable-next-line @gitlab/no-global-event-off
eChart.off('datazoom');
eChart.on('datazoom', this.throttledDatazoom);
},
diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js
index 37bb79defd1..9a887021e5d 100644
--- a/app/assets/javascripts/notes.js
+++ b/app/assets/javascripts/notes.js
@@ -187,6 +187,7 @@ export default class Notes {
this.$wrapperEl.off('click', '.js-discussion-reply-button');
this.$wrapperEl.off('click', '.js-add-diff-note-button');
this.$wrapperEl.off('click', '.js-add-image-diff-note-button');
+ // eslint-disable-next-line @gitlab/no-global-event-off
this.$wrapperEl.off('visibilitychange');
this.$wrapperEl.off('keyup input', '.js-note-text');
this.$wrapperEl.off('click', '.js-note-target-reopen');
diff --git a/app/assets/javascripts/pager.js b/app/assets/javascripts/pager.js
index 2aa37842707..f9a91ec322b 100644
--- a/app/assets/javascripts/pager.js
+++ b/app/assets/javascripts/pager.js
@@ -72,6 +72,7 @@ export default {
},
initLoadMore() {
+ // eslint-disable-next-line @gitlab/no-global-event-off
$(document).off('scroll');
$(document).endlessScroll({
bottomPixels: ENDLESS_SCROLL_BOTTOM_PX,
diff --git a/app/assets/javascripts/pages/projects/issues/show.js b/app/assets/javascripts/pages/projects/issues/show.js
index 4b15e435f60..614f8262e5b 100644
--- a/app/assets/javascripts/pages/projects/issues/show.js
+++ b/app/assets/javascripts/pages/projects/issues/show.js
@@ -17,7 +17,8 @@ import initInviteMemberModal from '~/invite_member/init_invite_member_modal';
import { IssuableType } from '~/issuable_show/constants';
export default function() {
- const { issueType, ...issuableData } = parseIssuableData();
+ const initialDataEl = document.getElementById('js-issuable-app');
+ const { issueType, ...issuableData } = parseIssuableData(initialDataEl);
switch (issueType) {
case IssuableType.Incident:
diff --git a/app/assets/javascripts/pipeline_editor/graphql/queries/ci_config.graphql b/app/assets/javascripts/pipeline_editor/graphql/queries/ci_config.graphql
new file mode 100644
index 00000000000..149cb256ced
--- /dev/null
+++ b/app/assets/javascripts/pipeline_editor/graphql/queries/ci_config.graphql
@@ -0,0 +1,11 @@
+#import "~/pipelines/graphql/queries/pipeline_stages.fragment.graphql"
+
+query getCiConfigData($content: String!) {
+ ciConfig(content: $content) {
+ errors
+ status
+ stages {
+ ...PipelineStagesData
+ }
+ }
+}
diff --git a/app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue b/app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue
index a4bdc27d1a0..b1c52ffa920 100644
--- a/app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue
+++ b/app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue
@@ -1,7 +1,7 @@
<script>
import { GlAlert, GlLoadingIcon, GlTab, GlTabs } from '@gitlab/ui';
import { __, s__, sprintf } from '~/locale';
-import { redirectTo, mergeUrlParams, refreshCurrentPage } from '~/lib/utils/url_utility';
+import { mergeUrlParams, redirectTo, refreshCurrentPage } from '~/lib/utils/url_utility';
import PipelineGraph from '~/pipelines/components/pipeline_graph/pipeline_graph.vue';
import CommitForm from './components/commit/commit_form.vue';
@@ -9,24 +9,25 @@ import TextEditor from './components/text_editor.vue';
import commitCiFileMutation from './graphql/mutations/commit_ci_file.mutation.graphql';
import getBlobContent from './graphql/queries/blob_content.graphql';
+import getCiConfigData from './graphql/queries/ci_config.graphql';
const MR_SOURCE_BRANCH = 'merge_request[source_branch]';
const MR_TARGET_BRANCH = 'merge_request[target_branch]';
-const LOAD_FAILURE_NO_REF = 'LOAD_FAILURE_NO_REF';
-const LOAD_FAILURE_NO_FILE = 'LOAD_FAILURE_NO_FILE';
-const LOAD_FAILURE_UNKNOWN = 'LOAD_FAILURE_UNKNOWN';
const COMMIT_FAILURE = 'COMMIT_FAILURE';
const DEFAULT_FAILURE = 'DEFAULT_FAILURE';
+const LOAD_FAILURE_NO_FILE = 'LOAD_FAILURE_NO_FILE';
+const LOAD_FAILURE_NO_REF = 'LOAD_FAILURE_NO_REF';
+const LOAD_FAILURE_UNKNOWN = 'LOAD_FAILURE_UNKNOWN';
export default {
components: {
+ CommitForm,
GlAlert,
GlLoadingIcon,
GlTab,
GlTabs,
PipelineGraph,
- CommitForm,
TextEditor,
},
props: {
@@ -55,14 +56,15 @@ export default {
},
data() {
return {
- showFailureAlert: false,
+ ciConfigData: {},
+ content: '',
+ contentModel: '',
+ currentTabIndex: 0,
+ editorIsReady: false,
failureType: null,
failureReasons: [],
-
isSaving: false,
- editorIsReady: false,
- content: '',
- contentModel: '',
+ showFailureAlert: false,
};
},
apollo: {
@@ -85,18 +87,35 @@ export default {
this.handleBlobContentError(error);
},
},
+ ciConfigData: {
+ query: getCiConfigData,
+ // If content is not loaded, we can't lint the data
+ skip: ({ contentModel }) => {
+ return !contentModel;
+ },
+ variables() {
+ return {
+ content: this.contentModel,
+ };
+ },
+ update(data) {
+ return data?.ciConfig ?? {};
+ },
+ error() {
+ this.reportFailure(LOAD_FAILURE_UNKNOWN);
+ },
+ },
},
computed: {
- isLoading() {
+ isBlobContentLoading() {
return this.$apollo.queries.content.loading;
},
+ isVisualizeTabActive() {
+ return this.currentTabIndex === 1;
+ },
defaultCommitMessage() {
return sprintf(this.$options.i18n.defaultCommitMessage, { sourcePath: this.ciConfigPath });
},
- pipelineData() {
- // Note data will loaded as part of https://gitlab.com/gitlab-org/gitlab/-/issues/263141
- return {};
- },
failure() {
switch (this.failureType) {
case LOAD_FAILURE_NO_REF:
@@ -233,17 +252,17 @@ export default {
</ul>
</gl-alert>
<div class="gl-mt-4">
- <gl-loading-icon v-if="isLoading" size="lg" class="gl-m-3" />
+ <gl-loading-icon v-if="isBlobContentLoading" size="lg" class="gl-m-3" />
<div v-else class="file-editor gl-mb-3">
- <gl-tabs>
+ <gl-tabs v-model="currentTabIndex">
<!-- editor should be mounted when its tab is visible, so the container has a size -->
<gl-tab :title="$options.i18n.tabEdit" :lazy="!editorIsReady">
<!-- editor should be mounted only once, when the tab is displayed -->
<text-editor v-model="contentModel" @editor-ready="editorIsReady = true" />
</gl-tab>
- <gl-tab :title="$options.i18n.tabGraph">
- <pipeline-graph :pipeline-data="pipelineData" />
+ <gl-tab :title="$options.i18n.tabGraph" :lazy="!isVisualizeTabActive">
+ <pipeline-graph :pipeline-data="ciConfigData" />
</gl-tab>
</gl-tabs>
</div>
diff --git a/app/assets/javascripts/pipelines/graphql/queries/pipeline_stages.fragment.graphql b/app/assets/javascripts/pipelines/graphql/queries/pipeline_stages.fragment.graphql
new file mode 100644
index 00000000000..0aef2fdfd7f
--- /dev/null
+++ b/app/assets/javascripts/pipelines/graphql/queries/pipeline_stages.fragment.graphql
@@ -0,0 +1,12 @@
+fragment PipelineStagesData on CiConfigStage {
+ name
+ groups {
+ name
+ jobs {
+ name
+ needs {
+ name
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/project_find_file.js b/app/assets/javascripts/project_find_file.js
index 2f35c4485f9..0e12c219e45 100644
--- a/app/assets/javascripts/project_find_file.js
+++ b/app/assets/javascripts/project_find_file.js
@@ -55,6 +55,7 @@ export default class ProjectFindFile {
}
initEvent() {
+ // eslint-disable-next-line @gitlab/no-global-event-off
this.inputElement.off('keyup');
this.inputElement.on('keyup', event => {
const target = $(event.target);
diff --git a/app/assets/javascripts/projects/project_new.js b/app/assets/javascripts/projects/project_new.js
index d74a2d06786..e0f8740e5b7 100644
--- a/app/assets/javascripts/projects/project_new.js
+++ b/app/assets/javascripts/projects/project_new.js
@@ -26,12 +26,14 @@ const onProjectPathChange = ($projectNameInput, $projectPathInput, hasExistingPr
};
const setProjectNamePathHandlers = ($projectNameInput, $projectPathInput) => {
+ // eslint-disable-next-line @gitlab/no-global-event-off
$projectNameInput.off('keyup change').on('keyup change', () => {
onProjectNameChange($projectNameInput, $projectPathInput);
hasUserDefinedProjectName = $projectNameInput.val().trim().length > 0;
hasUserDefinedProjectPath = $projectPathInput.val().trim().length > 0;
});
+ // eslint-disable-next-line @gitlab/no-global-event-off
$projectPathInput.off('keyup change').on('keyup change', () => {
onProjectPathChange($projectNameInput, $projectPathInput, hasUserDefinedProjectName);
hasUserDefinedProjectPath = $projectPathInput.val().trim().length > 0;
@@ -137,6 +139,7 @@ const bindEvents = () => {
target.focus();
})
.on('hide.bs.popover', () => {
+ // eslint-disable-next-line @gitlab/no-global-event-off
$(document).off('click.popover touchstart.popover');
});
}
diff --git a/app/assets/javascripts/related_issues/components/related_issuable_input.vue b/app/assets/javascripts/related_issues/components/related_issuable_input.vue
index 9809b228308..b05a873e939 100644
--- a/app/assets/javascripts/related_issues/components/related_issuable_input.vue
+++ b/app/assets/javascripts/related_issues/components/related_issuable_input.vue
@@ -97,7 +97,9 @@ export default {
},
beforeDestroy() {
const $input = $(this.$refs.input);
+ // eslint-disable-next-line @gitlab/no-global-event-off
$input.off('shown-issues.atwho');
+ // eslint-disable-next-line @gitlab/no-global-event-off
$input.off('hidden-issues.atwho');
$input.off('inserted-issues.atwho', this.onInput);
},
diff --git a/app/assets/javascripts/right_sidebar.js b/app/assets/javascripts/right_sidebar.js
index 87c8aa541d8..6f43f837374 100644
--- a/app/assets/javascripts/right_sidebar.js
+++ b/app/assets/javascripts/right_sidebar.js
@@ -23,8 +23,11 @@ Sidebar.initialize = function() {
Sidebar.prototype.removeListeners = function() {
this.sidebar.off('click', '.sidebar-collapsed-icon');
+ // eslint-disable-next-line @gitlab/no-global-event-off
this.sidebar.off('hidden.gl.dropdown');
+ // eslint-disable-next-line @gitlab/no-global-event-off
$('.dropdown').off('loading.gl.dropdown');
+ // eslint-disable-next-line @gitlab/no-global-event-off
$('.dropdown').off('loaded.gl.dropdown');
$(document).off('click', '.js-sidebar-toggle');
};
diff --git a/app/assets/javascripts/settings_panels.js b/app/assets/javascripts/settings_panels.js
index d22aca35e09..18160421136 100644
--- a/app/assets/javascripts/settings_panels.js
+++ b/app/assets/javascripts/settings_panels.js
@@ -3,6 +3,7 @@ import { __ } from './locale';
function expandSection($section) {
$section.find('.js-settings-toggle:not(.js-settings-toggle-trigger-only)').text(__('Collapse'));
+ // eslint-disable-next-line @gitlab/no-global-event-off
$section
.find('.settings-content')
.off('scroll.expandSection')
diff --git a/app/assets/javascripts/smart_interval.js b/app/assets/javascripts/smart_interval.js
index 0e52d2d8010..c4655d35cf0 100644
--- a/app/assets/javascripts/smart_interval.js
+++ b/app/assets/javascripts/smart_interval.js
@@ -95,6 +95,7 @@ export default class SmartInterval {
window.removeEventListener('blur', this.onWindowVisibilityChange);
window.removeEventListener('focus', this.onWindowVisibilityChange);
this.cancel();
+ // eslint-disable-next-line @gitlab/no-global-event-off
$(document)
.off('visibilitychange')
.off('beforeunload');
diff --git a/app/assets/javascripts/terminal/terminal.js b/app/assets/javascripts/terminal/terminal.js
index cf9064aba57..bae320cb705 100644
--- a/app/assets/javascripts/terminal/terminal.js
+++ b/app/assets/javascripts/terminal/terminal.js
@@ -25,6 +25,7 @@ export default class GLTerminal {
this.setSocketUrl();
this.createTerminal();
+ // eslint-disable-next-line @gitlab/no-global-event-off
$(window)
.off('resize.terminal')
.on('resize.terminal', () => {
@@ -104,6 +105,7 @@ export default class GLTerminal {
}
dispose() {
+ // eslint-disable-next-line @gitlab/no-global-event-off
this.terminal.off('data');
this.terminal.dispose();
this.socket.close();
diff --git a/app/assets/javascripts/version_check_image.js b/app/assets/javascripts/version_check_image.js
index ec515e892c6..4e00e0f11f7 100644
--- a/app/assets/javascripts/version_check_image.js
+++ b/app/assets/javascripts/version_check_image.js
@@ -1,5 +1,6 @@
export default class VersionCheckImage {
static bindErrorEvent(imageElement) {
+ // eslint-disable-next-line @gitlab/no-global-event-off
imageElement.off('error').on('error', () => imageElement.hide());
}
}
diff --git a/app/models/concerns/enums/data_visualization_palette.rb b/app/models/concerns/enums/data_visualization_palette.rb
new file mode 100644
index 00000000000..25002e64ba6
--- /dev/null
+++ b/app/models/concerns/enums/data_visualization_palette.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+module Enums
+ # These color palettes are part of the Pajamas Design System.
+ # See https://design.gitlab.com/data-visualization/color/#categorical-data
+ module DataVisualizationPalette
+ def self.colors
+ {
+ blue: 0,
+ orange: 1,
+ aqua: 2,
+ green: 3,
+ magenta: 4
+ }
+ end
+
+ def self.weights
+ {
+ '50' => 0,
+ '100' => 1,
+ '200' => 2,
+ '300' => 3,
+ '400' => 4,
+ '500' => 5,
+ '600' => 6,
+ '700' => 7,
+ '800' => 8,
+ '900' => 9,
+ '950' => 10
+ }
+ end
+ end
+end
diff --git a/changelogs/unreleased/262857-creation-rotation-table-models.yml b/changelogs/unreleased/262857-creation-rotation-table-models.yml
new file mode 100644
index 00000000000..d45c60ca289
--- /dev/null
+++ b/changelogs/unreleased/262857-creation-rotation-table-models.yml
@@ -0,0 +1,5 @@
+---
+title: Add oncall rotations and participants tables
+merge_request: 49058
+author:
+type: added
diff --git a/changelogs/unreleased/group-member-webhook-column.yml b/changelogs/unreleased/group-member-webhook-column.yml
new file mode 100644
index 00000000000..fef9556be23
--- /dev/null
+++ b/changelogs/unreleased/group-member-webhook-column.yml
@@ -0,0 +1,5 @@
+---
+title: Add member_events column to web_hooks table
+merge_request: 49273
+author:
+type: added
diff --git a/changelogs/unreleased/sh-handle-zero-maximum-upload-size.yml b/changelogs/unreleased/sh-handle-zero-maximum-upload-size.yml
new file mode 100644
index 00000000000..7781f994641
--- /dev/null
+++ b/changelogs/unreleased/sh-handle-zero-maximum-upload-size.yml
@@ -0,0 +1,5 @@
+---
+title: Fix division by error when upload max size is set to 0
+merge_request: 49482
+author:
+type: fixed
diff --git a/db/migrate/20201124030537_create_incident_management_on_call_rotations.rb b/db/migrate/20201124030537_create_incident_management_on_call_rotations.rb
new file mode 100644
index 00000000000..18546d97fd5
--- /dev/null
+++ b/db/migrate/20201124030537_create_incident_management_on_call_rotations.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+class CreateIncidentManagementOnCallRotations < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ unless table_exists?(:incident_management_oncall_rotations)
+ with_lock_retries do
+ create_table :incident_management_oncall_rotations do |t|
+ t.timestamps_with_timezone
+ t.references :oncall_schedule, index: false, null: false, foreign_key: { to_table: :incident_management_oncall_schedules, on_delete: :cascade }
+ t.integer :length, null: false
+ t.integer :length_unit, limit: 2, null: false
+ t.datetime_with_timezone :starts_at, null: false
+ t.text :name, null: false
+
+ t.index %w(oncall_schedule_id id), name: 'index_inc_mgmnt_oncall_rotations_on_oncall_schedule_id_and_id', unique: true, using: :btree
+ t.index %w(oncall_schedule_id name), name: 'index_inc_mgmnt_oncall_rotations_on_oncall_schedule_id_and_name', unique: true, using: :btree
+ end
+ end
+ end
+
+ add_text_limit :incident_management_oncall_rotations, :name, 200
+ end
+
+ def down
+ with_lock_retries do
+ drop_table :incident_management_oncall_rotations
+ end
+ end
+end
diff --git a/db/migrate/20201125233219_add_incident_management_on_call_participants.rb b/db/migrate/20201125233219_add_incident_management_on_call_participants.rb
new file mode 100644
index 00000000000..2a9b1d8b276
--- /dev/null
+++ b/db/migrate/20201125233219_add_incident_management_on_call_participants.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+class AddIncidentManagementOnCallParticipants < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ PARTICIPANT_ROTATION_INDEX_NAME = 'index_inc_mgmnt_oncall_participants_on_oncall_rotation_id'
+ PARTICIPANT_USER_INDEX_NAME = 'index_inc_mgmnt_oncall_participants_on_oncall_user_id'
+ UNIQUE_INDEX_NAME = 'index_inc_mgmnt_oncall_participants_on_user_id_and_rotation_id'
+
+ disable_ddl_transaction!
+
+ def up
+ unless table_exists?(:incident_management_oncall_participants)
+ with_lock_retries do
+ create_table :incident_management_oncall_participants do |t|
+ t.references :oncall_rotation, index: false, null: false, foreign_key: { to_table: :incident_management_oncall_rotations, on_delete: :cascade }
+ t.references :user, index: false, null: false, foreign_key: { on_delete: :cascade }
+ t.integer :color_palette, limit: 2, null: false
+ t.integer :color_weight, limit: 2, null: false
+ t.index :user_id, name: PARTICIPANT_USER_INDEX_NAME
+ t.index :oncall_rotation_id, name: PARTICIPANT_ROTATION_INDEX_NAME
+ t.index [:user_id, :oncall_rotation_id], unique: true, name: UNIQUE_INDEX_NAME
+ end
+ end
+ end
+ end
+
+ def down
+ drop_table :incident_management_oncall_participants
+ end
+end
diff --git a/db/migrate/20201204205814_add_member_events_to_web_hooks.rb b/db/migrate/20201204205814_add_member_events_to_web_hooks.rb
new file mode 100644
index 00000000000..edb374f1bdd
--- /dev/null
+++ b/db/migrate/20201204205814_add_member_events_to_web_hooks.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddMemberEventsToWebHooks < ActiveRecord::Migration[6.0]
+ DOWNTIME = false
+
+ def change
+ add_column :web_hooks, :member_events, :boolean, null: false, default: false
+ end
+end
diff --git a/db/schema_migrations/20201124030537 b/db/schema_migrations/20201124030537
new file mode 100644
index 00000000000..6754e179ae3
--- /dev/null
+++ b/db/schema_migrations/20201124030537
@@ -0,0 +1 @@
+2929b74d9b9d6e205c0e1fb2aaaffe323394058f6e583c56035a2c83b4d4ff03 \ No newline at end of file
diff --git a/db/schema_migrations/20201125233219 b/db/schema_migrations/20201125233219
new file mode 100644
index 00000000000..54728b704e9
--- /dev/null
+++ b/db/schema_migrations/20201125233219
@@ -0,0 +1 @@
+451d7f29392f965467f364c7b119d269551e2dc1485e8cb15ebd14753fdb6e6a \ No newline at end of file
diff --git a/db/schema_migrations/20201204205814 b/db/schema_migrations/20201204205814
new file mode 100644
index 00000000000..2308ba1245c
--- /dev/null
+++ b/db/schema_migrations/20201204205814
@@ -0,0 +1 @@
+8178b8a9acf7d2d8990bb6f7d984eb9e3b77d45cb2a8b54b56500ef6f93772ad \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index c84f5eaec80..0acceb8c9be 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -12989,6 +12989,44 @@ CREATE SEQUENCE import_failures_id_seq
ALTER SEQUENCE import_failures_id_seq OWNED BY import_failures.id;
+CREATE TABLE incident_management_oncall_participants (
+ id bigint NOT NULL,
+ oncall_rotation_id bigint NOT NULL,
+ user_id bigint NOT NULL,
+ color_palette smallint NOT NULL,
+ color_weight smallint NOT NULL
+);
+
+CREATE SEQUENCE incident_management_oncall_participants_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE incident_management_oncall_participants_id_seq OWNED BY incident_management_oncall_participants.id;
+
+CREATE TABLE incident_management_oncall_rotations (
+ id bigint NOT NULL,
+ created_at timestamp with time zone NOT NULL,
+ updated_at timestamp with time zone NOT NULL,
+ oncall_schedule_id bigint NOT NULL,
+ length integer NOT NULL,
+ length_unit smallint NOT NULL,
+ starts_at timestamp with time zone NOT NULL,
+ name text NOT NULL,
+ CONSTRAINT check_5209fb5d02 CHECK ((char_length(name) <= 200))
+);
+
+CREATE SEQUENCE incident_management_oncall_rotations_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE incident_management_oncall_rotations_id_seq OWNED BY incident_management_oncall_rotations.id;
+
CREATE TABLE incident_management_oncall_schedules (
id bigint NOT NULL,
created_at timestamp with time zone NOT NULL,
@@ -17713,7 +17751,8 @@ CREATE TABLE web_hooks (
encrypted_url_iv character varying,
deployment_events boolean DEFAULT false NOT NULL,
releases_events boolean DEFAULT false NOT NULL,
- feature_flag_events boolean DEFAULT false NOT NULL
+ feature_flag_events boolean DEFAULT false NOT NULL,
+ member_events boolean DEFAULT false NOT NULL
);
CREATE SEQUENCE web_hooks_id_seq
@@ -18230,6 +18269,10 @@ ALTER TABLE ONLY import_export_uploads ALTER COLUMN id SET DEFAULT nextval('impo
ALTER TABLE ONLY import_failures ALTER COLUMN id SET DEFAULT nextval('import_failures_id_seq'::regclass);
+ALTER TABLE ONLY incident_management_oncall_participants ALTER COLUMN id SET DEFAULT nextval('incident_management_oncall_participants_id_seq'::regclass);
+
+ALTER TABLE ONLY incident_management_oncall_rotations ALTER COLUMN id SET DEFAULT nextval('incident_management_oncall_rotations_id_seq'::regclass);
+
ALTER TABLE ONLY incident_management_oncall_schedules ALTER COLUMN id SET DEFAULT nextval('incident_management_oncall_schedules_id_seq'::regclass);
ALTER TABLE ONLY index_statuses ALTER COLUMN id SET DEFAULT nextval('index_statuses_id_seq'::regclass);
@@ -19439,6 +19482,12 @@ ALTER TABLE ONLY import_export_uploads
ALTER TABLE ONLY import_failures
ADD CONSTRAINT import_failures_pkey PRIMARY KEY (id);
+ALTER TABLE ONLY incident_management_oncall_participants
+ ADD CONSTRAINT incident_management_oncall_participants_pkey PRIMARY KEY (id);
+
+ALTER TABLE ONLY incident_management_oncall_rotations
+ ADD CONSTRAINT incident_management_oncall_rotations_pkey PRIMARY KEY (id);
+
ALTER TABLE ONLY incident_management_oncall_schedules
ADD CONSTRAINT incident_management_oncall_schedules_pkey PRIMARY KEY (id);
@@ -21372,6 +21421,16 @@ CREATE INDEX index_import_failures_on_project_id_not_null ON import_failures USI
CREATE INDEX index_imported_projects_on_import_type_creator_id_created_at ON projects USING btree (import_type, creator_id, created_at) WHERE (import_type IS NOT NULL);
+CREATE INDEX index_inc_mgmnt_oncall_participants_on_oncall_rotation_id ON incident_management_oncall_participants USING btree (oncall_rotation_id);
+
+CREATE INDEX index_inc_mgmnt_oncall_participants_on_oncall_user_id ON incident_management_oncall_participants USING btree (user_id);
+
+CREATE UNIQUE INDEX index_inc_mgmnt_oncall_participants_on_user_id_and_rotation_id ON incident_management_oncall_participants USING btree (user_id, oncall_rotation_id);
+
+CREATE UNIQUE INDEX index_inc_mgmnt_oncall_rotations_on_oncall_schedule_id_and_id ON incident_management_oncall_rotations USING btree (oncall_schedule_id, id);
+
+CREATE UNIQUE INDEX index_inc_mgmnt_oncall_rotations_on_oncall_schedule_id_and_name ON incident_management_oncall_rotations USING btree (oncall_schedule_id, name);
+
CREATE INDEX index_incident_management_oncall_schedules_on_project_id ON incident_management_oncall_schedules USING btree (project_id);
CREATE UNIQUE INDEX index_index_statuses_on_project_id ON index_statuses USING btree (project_id);
@@ -23744,6 +23803,9 @@ ALTER TABLE ONLY namespace_statistics
ALTER TABLE ONLY clusters_applications_elastic_stacks
ADD CONSTRAINT fk_rails_026f219f46 FOREIGN KEY (cluster_id) REFERENCES clusters(id) ON DELETE CASCADE;
+ALTER TABLE ONLY incident_management_oncall_participants
+ ADD CONSTRAINT fk_rails_032b12996a FOREIGN KEY (oncall_rotation_id) REFERENCES incident_management_oncall_rotations(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY events
ADD CONSTRAINT fk_rails_0434b48643 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
@@ -23921,6 +23983,9 @@ ALTER TABLE ONLY saml_group_links
ALTER TABLE ONLY group_custom_attributes
ADD CONSTRAINT fk_rails_246e0db83a FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
+ALTER TABLE ONLY incident_management_oncall_rotations
+ ADD CONSTRAINT fk_rails_256e0bc604 FOREIGN KEY (oncall_schedule_id) REFERENCES incident_management_oncall_schedules(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY analytics_devops_adoption_snapshots
ADD CONSTRAINT fk_rails_25da9a92c0 FOREIGN KEY (segment_id) REFERENCES analytics_devops_adoption_segments(id) ON DELETE CASCADE;
@@ -24251,6 +24316,9 @@ ALTER TABLE ONLY resource_weight_events
ALTER TABLE ONLY approval_project_rules
ADD CONSTRAINT fk_rails_5fb4dd100b FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
+ALTER TABLE ONLY incident_management_oncall_participants
+ ADD CONSTRAINT fk_rails_5fe86ea341 FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY user_highest_roles
ADD CONSTRAINT fk_rails_60f6c325a6 FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
diff --git a/doc/administration/pages/index.md b/doc/administration/pages/index.md
index 13dd742b53c..87ccbe6747c 100644
--- a/doc/administration/pages/index.md
+++ b/doc/administration/pages/index.md
@@ -210,7 +210,7 @@ control over how the Pages daemon runs and serves content in your environment.
| `external_https` | Configure Pages to bind to one or more secondary IP addresses, serving HTTPS requests. Multiple addresses can be given as an array, along with exact ports, for example `['1.2.3.4', '1.2.3.5:8063']`. Sets value for `listen_https`.
| `gitlab_client_http_timeout` | GitLab API HTTP client connection timeout in seconds (default: 10s).
| `gitlab_client_jwt_expiry` | JWT Token expiry time in seconds (default: 30s).
-| `domain_config_source` | Domain configuration source (default: `disk`)
+| `domain_config_source` | Domain configuration source (default: `auto`)
| `gitlab_id` | The OAuth application public ID. Leave blank to automatically fill when Pages authenticates with GitLab.
| `gitlab_secret` | The OAuth application secret. Leave blank to automatically fill when Pages authenticates with GitLab.
| `gitlab_server` | Server to use for authentication when access control is enabled; defaults to GitLab `external_url`.
@@ -668,13 +668,14 @@ Pages server.
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/217912) in GitLab 13.3.
GitLab Pages can use different sources to get domain configuration.
-The default value is `nil`; however, GitLab Pages will default to `disk`.
+The default value is `nil`; however, GitLab Pages will default to `auto`.
```ruby
gitlab_pages['domain_config_source'] = nil
```
-You can specify `gitlab` to enable [API-based configuration](#gitlab-api-based-configuration).
+If left unchanged, GitLab Pages tries to use any available source (either `gitlab` or `disk`). The
+preferred source is `gitlab`, which uses [API-based configuration](#gitlab-api-based-configuration).
For more details see this [blog post](https://about.gitlab.com/blog/2020/08/03/how-gitlab-pages-uses-the-gitlab-api-to-serve-content/).
@@ -691,10 +692,10 @@ was used prior to GitLab 13.0. Follow these steps to enable it:
1. [Reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
-If you encounter an issue, you can disable it by choosing `disk` or `nil`:
+If you encounter an issue, you can disable it by choosing `disk`:
```ruby
-gitlab_pages['domain_config_source'] = nil
+gitlab_pages['domain_config_source'] = "disk"
```
For other common issues, see the [troubleshooting section](#failed-to-connect-to-the-internal-gitlab-api)
diff --git a/doc/development/agent/local.md b/doc/development/agent/local.md
index baa9cc5696b..75d45366238 100644
--- a/doc/development/agent/local.md
+++ b/doc/development/agent/local.md
@@ -9,45 +9,45 @@ info: To determine the technical writer assigned to the Stage/Group associated w
You can run `kas` and `agentk` locally to test the [Kubernetes Agent](index.md) yourself.
1. Create a `cfg.yaml` file from the contents of
- [`config_example.yaml`](https://gitlab.com/gitlab-org/cluster-integration/gitlab-agent/-/blob/master/pkg/kascfg/config_example.yaml), or this example:
-
- ```yaml
- agent:
- listen:
- network: tcp
- address: 127.0.0.1:8150
- websocket: false
- gitops:
- poll_period: "10s"
- gitlab:
- address: http://localhost:3000
- authentication_secret_file: /Users/tkuah/code/ee-gdk/gitlab/.gitlab_kas_secret
- ```
+ [`config_example.yaml`](https://gitlab.com/gitlab-org/cluster-integration/gitlab-agent/-/blob/master/pkg/kascfg/config_example.yaml), or this example:
+
+ ```yaml
+ agent:
+ listen:
+ network: tcp
+ address: 127.0.0.1:8150
+ websocket: false
+ gitops:
+ poll_period: "10s"
+ gitlab:
+ address: http://localhost:3000
+ authentication_secret_file: /Users/tkuah/code/ee-gdk/gitlab/.gitlab_kas_secret
+ ```
1. Create a `token.txt`. This is the token for
- [the agent you created](../../user/clusters/agent/index.md#create-an-agent-record-in-gitlab). This file must not contain a newline character. You can create the file with this command:
+ [the agent you created](../../user/clusters/agent/index.md#create-an-agent-record-in-gitlab). This file must not contain a newline character. You can create the file with this command:
- ```shell
- echo -n "<TOKEN>" > token.txt
- ```
+ ```shell
+ echo -n "<TOKEN>" > token.txt
+ ```
1. Start the binaries with the following commands:
- ```shell
- # Need GitLab to start
- gdk start
- # Stop GDK's version of kas
- gdk stop gitlab-k8s-agent
+ ```shell
+ # Need GitLab to start
+ gdk start
+ # Stop GDK's version of kas
+ gdk stop gitlab-k8s-agent
- # Start kas
- bazel run //cmd/kas -- --configuration-file="$(pwd)/cfg.yaml"
- ```
+ # Start kas
+ bazel run //cmd/kas -- --configuration-file="$(pwd)/cfg.yaml"
+ ```
-1. In a new terminal window, run this command to start agentk:
+1. In a new terminal window, run this command to start `agentk`:
- ```shell
- bazel run //cmd/agentk -- --kas-address=grpc://127.0.0.1:8150 --token-file="$(pwd)/token.txt"
- ```
+ ```shell
+ bazel run //cmd/agentk -- --kas-address=grpc://127.0.0.1:8150 --token-file="$(pwd)/token.txt"
+ ```
You can also inspect the
[Makefile](https://gitlab.com/gitlab-org/cluster-integration/gitlab-agent/-/blob/master/Makefile)
diff --git a/doc/development/secure_coding_guidelines.md b/doc/development/secure_coding_guidelines.md
index ebab0e59cc3..95145a75f1f 100644
--- a/doc/development/secure_coding_guidelines.md
+++ b/doc/development/secure_coding_guidelines.md
@@ -112,21 +112,43 @@ Here `params[:ip]` should not contain anything else but numbers and dots. Howeve
In most cases the anchors `\A` for beginning of text and `\z` for end of text should be used instead of `^` and `$`.
-## Denial of Service (ReDoS)
+## Denial of Service (ReDoS) / Catastrophic Backtracking
-[ReDoS](https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS) is a possible attack if the attacker knows
-or controls the regular expression (regex) used, and is able to enter user input to match against the bad regular expression.
+When a regular expression (regex) is used to search for a string and can't find a match,
+it may then backtrack to try other possibilities.
+
+For example when the regex `.*!$` matches the string `hello!`, the `.*` first matches
+the entire string but then the `!` from the regex is unable to match because the
+character has already been used. In that case, the Ruby regex engine _backtracks_
+one character to allow the `!` to match.
+
+[ReDoS](https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS)
+is an attack in which the attacker knows or controls the regular expression used.
+The attacker may be able to enter user input that triggers this backtracking behavior in a
+way that increases execution time by several orders of magnitude.
### Impact
-The resource, for example Unicorn, Puma, or Sidekiq, can be made to hang as it takes a long time to evaluate the bad regex match.
+The resource, for example Unicorn, Puma, or Sidekiq, can be made to hang as it takes
+a long time to evaluate the bad regex match. The evaluation time may require manual
+termination of the resource.
### Examples
-GitLab-specific examples can be found in the following merge requests:
+Here are some GitLab-specific examples.
+
+User inputs used to create regular expressions:
+
+- [User-controlled filename](https://gitlab.com/gitlab-org/gitlab/-/issues/257497)
+- [User-controlled domain name](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/25314)
+- [User-controlled email address](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/25122#note_289087459)
+
+Hardcoded regular expressions with backtracking issues:
-- [MR25314](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/25314)
-- [MR25122](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/25122#note_289087459)
+- [Repository name validation](https://gitlab.com/gitlab-org/gitlab/-/issues/220019)
+- [Link validation](https://gitlab.com/gitlab-org/gitlab/-/issues/218753), and [a bypass](https://gitlab.com/gitlab-org/gitlab/-/issues/273771)
+- [Entity name validation](https://gitlab.com/gitlab-org/gitlab/-/issues/289934)
+- [Validating color codes](https://gitlab.com/gitlab-org/gitlab/commit/717824144f8181bef524592eab882dd7525a60ef)
Consider the following example application, which defines a check using a regular expression. A user entering `user@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!.com` as the email on a form will hang the web server.
@@ -141,22 +163,32 @@ class Email < ApplicationRecord
def domain_matches
errors.add(:email, 'does not match') if email =~ DOMAIN_MATCH
end
+end
```
### Mitigation
-GitLab has `Gitlab::UntrustedRegexp` which internally uses the [`re2`](https://github.com/google/re2/wiki/Syntax) library.
-By utilizing `re2`, we get a strict limit on total execution time, and a smaller subset of available regex features.
+#### Ruby
+
+GitLab has [`Gitlab::UntrustedRegexp`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/untrusted_regexp.rb)
+ which internally uses the [`re2`](https://github.com/google/re2/wiki/Syntax) library.
+`re2` does not support backtracking so we get constant execution time, and a smaller subset of available regex features.
All user-provided regular expressions should use `Gitlab::UntrustedRegexp`.
For other regular expressions, here are a few guidelines:
-- Remove unnecessary backtracking.
-- Avoid nested quantifiers if possible.
-- Try to be as precise as possible in your regex and avoid the `.` if something else can be used (e.g.: Use `_[^_]+_` instead of `_.*_` to match `_text here_`).
+- If there's a clean non-regex solution, such as `String#start_with?`, consider using it
+- Ruby supports some advanced regex features like [atomic groups](https://www.regular-expressions.info/atomic.html)
+and [possessive quantifiers](https://www.regular-expressions.info/possessive.html) that eleminate backtracking
+- Avoid nested quantifiers if possible (for example `(a+)+`)
+- Try to be as precise as possible in your regex and avoid the `.` if there's an alternative
+ - For example, Use `_[^_]+_` instead of `_.*_` to match `_text here_`
+- If in doubt, don't hesitate to ping `@gitlab-com/gl-security/appsec`
+
+#### Go
-An example can be found [in this commit](https://gitlab.com/gitlab-org/gitlab/commit/717824144f8181bef524592eab882dd7525a60ef).
+Go's [`regexp`](https://golang.org/pkg/regexp/) package uses `re2` and isn't vulnerable to backtracking issues.
## Further Links
@@ -466,7 +498,7 @@ where you can't avoid this:
characters, for example).
- Always use `--` to separate options from arguments.
-### Ruby
+#### Ruby
Consider using `system("command", "arg0", "arg1", ...)` whenever you can. This prevents an attacker
from concatenating commands.
@@ -475,7 +507,7 @@ For more examples on how to use shell commands securely, consult
[Guidelines for shell commands in the GitLab codebase](shell_commands.md).
It contains various examples on how to securely call OS commands.
-### Go
+#### Go
Go has built-in protections that usually prevent an attacker from successfully injecting OS commands.
diff --git a/doc/user/clusters/agent/index.md b/doc/user/clusters/agent/index.md
index 5c34e854d1a..0ce8888272a 100644
--- a/doc/user/clusters/agent/index.md
+++ b/doc/user/clusters/agent/index.md
@@ -56,6 +56,12 @@ There are several components that work in concert for the Agent to accomplish Gi
These repositories might be the same GitLab project or separate projects.
+NOTE:
+GitLab recommends you use the same GitLab project for the agent configuration
+and manifest repositories. Our backlog contains issues for adding support for
+[private manifest repositories outside of the configuration project](https://gitlab.com/gitlab-org/gitlab/-/issues/220912) and
+[group level agents](https://gitlab.com/gitlab-org/gitlab/-/issues/283885).
+
For more details, please refer to our [full architecture documentation](https://gitlab.com/gitlab-org/cluster-integration/gitlab-agent/-/blob/master/doc/architecture.md#high-level-architecture) in the Agent project.
## Get started with GitOps and the GitLab Agent
diff --git a/doc/user/project/clusters/index.md b/doc/user/project/clusters/index.md
index 03abf769392..c0c7eaf6ca1 100644
--- a/doc/user/project/clusters/index.md
+++ b/doc/user/project/clusters/index.md
@@ -45,17 +45,17 @@ versions at any given time. We regularly review the versions we support, and
provide a three-month deprecation period before we remove support of a specific
version. The range of supported versions is based on the evaluation of:
-- Our own needs.
- The versions supported by major managed Kubernetes providers.
- The versions [supported by the Kubernetes community](https://kubernetes.io/docs/setup/release/version-skew-policy/#supported-versions).
GitLab supports the following Kubernetes versions, and you can upgrade your
Kubernetes version to any supported version at any time:
-- 1.18
-- 1.17
-- 1.16
-- 1.15
+- 1.19 (support ends on February 22, 2022)
+- 1.18 (support ends on November 22, 2021)
+- 1.17 (support ends on September 22, 2021)
+- 1.16 (support ends on July 22, 2021)
+- 1.15 (support ends on May 22, 2021)
- 1.14 (deprecated, support ends on December 22, 2020)
Some GitLab features may support versions outside the range provided here.
diff --git a/lib/gitlab.rb b/lib/gitlab.rb
index 43785d165fb..0f2fd01e3c7 100644
--- a/lib/gitlab.rb
+++ b/lib/gitlab.rb
@@ -115,4 +115,10 @@ module Gitlab
'web'
end
+
+ def self.maintenance_mode?
+ return false unless ::Feature.enabled?(:maintenance_mode)
+
+ ::Gitlab::CurrentSettings.maintenance_mode
+ end
end
diff --git a/lib/object_storage/direct_upload.rb b/lib/object_storage/direct_upload.rb
index b5864382299..3a8fa51e198 100644
--- a/lib/object_storage/direct_upload.rb
+++ b/lib/object_storage/direct_upload.rb
@@ -184,15 +184,20 @@ module ObjectStorage
private
def rounded_multipart_part_size
- # round multipart_part_size up to minimum_mulitpart_size
+ # round multipart_part_size up to minimum_multipart_size
(multipart_part_size + MINIMUM_MULTIPART_SIZE - 1) / MINIMUM_MULTIPART_SIZE * MINIMUM_MULTIPART_SIZE
end
def multipart_part_size
+ return MINIMUM_MULTIPART_SIZE if maximum_size == 0
+
maximum_size / number_of_multipart_parts
end
def number_of_multipart_parts
+ # If we don't have max length, we can only assume the file is as large as possible.
+ return MAXIMUM_MULTIPART_PARTS if maximum_size == 0
+
[
# round maximum_size up to minimum_mulitpart_size
(maximum_size + MINIMUM_MULTIPART_SIZE - 1) / MINIMUM_MULTIPART_SIZE,
@@ -201,7 +206,7 @@ module ObjectStorage
end
def requires_multipart_upload?
- config.aws? && !has_length
+ config.aws? && !has_length && !use_workhorse_s3_client?
end
def upload_id
diff --git a/spec/frontend/.eslintrc.yml b/spec/frontend/.eslintrc.yml
index 8e6faa90c58..d0e585e844a 100644
--- a/spec/frontend/.eslintrc.yml
+++ b/spec/frontend/.eslintrc.yml
@@ -25,3 +25,6 @@ rules:
- 'testAction'
jest/no-test-callback:
- off
+ "@gitlab/no-global-event-off":
+ - off
+
diff --git a/spec/frontend/issue_show/issue_spec.js b/spec/frontend/issue_show/issue_spec.js
index 7a48353af94..cee9969d26a 100644
--- a/spec/frontend/issue_show/issue_spec.js
+++ b/spec/frontend/issue_show/issue_spec.js
@@ -30,7 +30,8 @@ describe('Issue show index', () => {
initialDescriptionHtml: '<svg onload=window.alert(1)>',
});
- const issuableData = parseData.parseIssuableData();
+ const initialDataEl = document.getElementById('js-issuable-app');
+ const issuableData = parseData.parseIssuableData(initialDataEl);
initIssuableApp(issuableData, createStore());
await waitForPromises();
diff --git a/spec/frontend/pipeline_editor/mock_data.js b/spec/frontend/pipeline_editor/mock_data.js
index 8f6ae2b5af7..d882490c272 100644
--- a/spec/frontend/pipeline_editor/mock_data.js
+++ b/spec/frontend/pipeline_editor/mock_data.js
@@ -12,6 +12,16 @@ job1:
- echo 'test'
`;
+export const mockCiConfigQueryResponse = {
+ data: {
+ ciConfig: {
+ errors: [],
+ stages: [],
+ status: '',
+ },
+ },
+};
+
export const mockLintResponse = {
valid: true,
errors: [],
diff --git a/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js b/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js
index c6dafc6e258..ca54c97d2bb 100644
--- a/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js
+++ b/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js
@@ -13,9 +13,10 @@ import waitForPromises from 'helpers/wait_for_promises';
import VueApollo from 'vue-apollo';
import createMockApollo from 'jest/helpers/mock_apollo_helper';
-import { redirectTo, refreshCurrentPage, objectToQuery } from '~/lib/utils/url_utility';
+import { objectToQuery, redirectTo, refreshCurrentPage } from '~/lib/utils/url_utility';
import {
mockCiConfigPath,
+ mockCiConfigQueryResponse,
mockCiYml,
mockCommitId,
mockCommitMessage,
@@ -24,10 +25,11 @@ import {
mockNewMergeRequestPath,
} from './mock_data';
-import TextEditor from '~/pipeline_editor/components/text_editor.vue';
+import CommitForm from '~/pipeline_editor/components/commit/commit_form.vue';
+import getCiConfig from '~/pipeline_editor/graphql/queries/ci_config.graphql';
import PipelineGraph from '~/pipelines/components/pipeline_graph/pipeline_graph.vue';
import PipelineEditorApp from '~/pipeline_editor/pipeline_editor_app.vue';
-import CommitForm from '~/pipeline_editor/components/commit/commit_form.vue';
+import TextEditor from '~/pipeline_editor/components/text_editor.vue';
const localVue = createLocalVue();
localVue.use(VueApollo);
@@ -42,9 +44,10 @@ jest.mock('~/lib/utils/url_utility', () => ({
describe('~/pipeline_editor/pipeline_editor_app.vue', () => {
let wrapper;
- let mockMutate;
let mockApollo;
let mockBlobContentData;
+ let mockCiConfigData;
+ let mockMutate;
const createComponent = ({
props = {},
@@ -96,7 +99,8 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => {
};
const createComponentWithApollo = ({ props = {}, mountFn = shallowMount } = {}) => {
- mockApollo = createMockApollo([], {
+ const handlers = [[getCiConfig, mockCiConfigData]];
+ const resolvers = {
Query: {
blobContent() {
return {
@@ -105,7 +109,9 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => {
};
},
},
- });
+ };
+
+ mockApollo = createMockApollo(handlers, resolvers);
const options = {
localVue,
@@ -125,10 +131,12 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => {
beforeEach(() => {
mockBlobContentData = jest.fn();
+ mockCiConfigData = jest.fn().mockResolvedValue(mockCiConfigQueryResponse);
});
afterEach(() => {
mockBlobContentData.mockReset();
+ mockCiConfigData.mockReset();
refreshCurrentPage.mockReset();
redirectTo.mockReset();
mockMutate.mockReset();
@@ -177,12 +185,10 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => {
beforeEach(async () => {
createComponent({ mountFn: mount });
- wrapper.setData({
+ await wrapper.setData({
content: mockCiYml,
contentModel: mockCiYml,
});
-
- await nextTick();
});
it('displays content after the query loads', () => {
@@ -347,7 +353,7 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => {
});
describe('displays fetch content errors', () => {
- it('no error is show when data is set', async () => {
+ it('no error is shown when data is set', async () => {
mockBlobContentData.mockResolvedValue(mockCiYml);
createComponentWithApollo();
diff --git a/spec/lib/gitlab_spec.rb b/spec/lib/gitlab_spec.rb
index eaab39291b2..e1b8323eb8e 100644
--- a/spec/lib/gitlab_spec.rb
+++ b/spec/lib/gitlab_spec.rb
@@ -329,4 +329,24 @@ RSpec.describe Gitlab do
expect(described_class.http_proxy_env?).to eq(false)
end
end
+
+ describe '.maintenance_mode?' do
+ it 'returns true when maintenance mode is enabled' do
+ stub_application_setting(maintenance_mode: true)
+
+ expect(described_class.maintenance_mode?).to eq(true)
+ end
+
+ it 'returns false when maintenance mode is disabled' do
+ stub_application_setting(maintenance_mode: false)
+
+ expect(described_class.maintenance_mode?).to eq(false)
+ end
+
+ it 'returns false when maintenance mode feature flag is disabled' do
+ stub_feature_flags(maintenance_mode: false)
+
+ expect(described_class.maintenance_mode?).to eq(false)
+ end
+ end
end
diff --git a/spec/lib/object_storage/direct_upload_spec.rb b/spec/lib/object_storage/direct_upload_spec.rb
index 2af10f9cfe9..bd9d197afa0 100644
--- a/spec/lib/object_storage/direct_upload_spec.rb
+++ b/spec/lib/object_storage/direct_upload_spec.rb
@@ -162,6 +162,10 @@ RSpec.describe ObjectStorage::DirectUpload do
it 'enables the Workhorse client' do
expect(subject[:UseWorkhorseClient]).to be true
end
+
+ it 'omits the multipart upload URLs' do
+ expect(subject).not_to include(:MultipartUpload)
+ end
end
context 'when only server side encryption is used' do
@@ -340,6 +344,30 @@ RSpec.describe ObjectStorage::DirectUpload do
stub_object_storage_multipart_init(storage_url, "myUpload")
end
+ context 'when maximum upload size is 0' do
+ let(:maximum_size) { 0 }
+
+ it 'returns maximum number of parts' do
+ expect(subject[:MultipartUpload][:PartURLs].length).to eq(100)
+ end
+
+ it 'part size is minimum, 5MB' do
+ expect(subject[:MultipartUpload][:PartSize]).to eq(5.megabyte)
+ end
+ end
+
+ context 'when maximum upload size is < 5 MB' do
+ let(:maximum_size) { 1024 }
+
+ it 'returns only 1 part' do
+ expect(subject[:MultipartUpload][:PartURLs].length).to eq(1)
+ end
+
+ it 'part size is minimum, 5MB' do
+ expect(subject[:MultipartUpload][:PartSize]).to eq(5.megabyte)
+ end
+ end
+
context 'when maximum upload size is 10MB' do
let(:maximum_size) { 10.megabyte }