Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
0612ffef12
commit
cb36ae7dd5
|
|
@ -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/**/*'
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@ export default class Autosave {
|
|||
}
|
||||
|
||||
dispose() {
|
||||
// eslint-disable-next-line @gitlab/no-global-event-off
|
||||
this.field.off('input');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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) => {
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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', () => {
|
||||
|
|
|
|||
|
|
@ -154,6 +154,7 @@ export default {
|
|||
});
|
||||
},
|
||||
beforeDestroy() {
|
||||
// eslint-disable-next-line @gitlab/no-global-event-off
|
||||
$(this.$refs.dropdown).off();
|
||||
},
|
||||
methods: {
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -215,6 +215,7 @@ export default {
|
|||
this.fetchIssuables();
|
||||
},
|
||||
beforeDestroy() {
|
||||
// eslint-disable-next-line @gitlab/no-global-event-off
|
||||
issueableEventHub.$off('issuables:toggleBulkEdit');
|
||||
},
|
||||
methods: {
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"]'));
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -367,6 +367,7 @@ export default {
|
|||
},
|
||||
);
|
||||
|
||||
// eslint-disable-next-line @gitlab/no-global-event-off
|
||||
eChart.off('datazoom');
|
||||
eChart.on('datazoom', this.throttledDatazoom);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
#import "~/pipelines/graphql/queries/pipeline_stages.fragment.graphql"
|
||||
|
||||
query getCiConfigData($content: String!) {
|
||||
ciConfig(content: $content) {
|
||||
errors
|
||||
status
|
||||
stages {
|
||||
...PipelineStagesData
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
failureType: null,
|
||||
failureReasons: [],
|
||||
|
||||
isSaving: false,
|
||||
editorIsReady: false,
|
||||
ciConfigData: {},
|
||||
content: '',
|
||||
contentModel: '',
|
||||
currentTabIndex: 0,
|
||||
editorIsReady: false,
|
||||
failureType: null,
|
||||
failureReasons: [],
|
||||
isSaving: false,
|
||||
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>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
fragment PipelineStagesData on CiConfigStage {
|
||||
name
|
||||
groups {
|
||||
name
|
||||
jobs {
|
||||
name
|
||||
needs {
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add oncall rotations and participants tables
|
||||
merge_request: 49058
|
||||
author:
|
||||
type: added
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add member_events column to web_hooks table
|
||||
merge_request: 49273
|
||||
author:
|
||||
type: added
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix division by error when upload max size is set to 0
|
||||
merge_request: 49482
|
||||
author:
|
||||
type: fixed
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1 @@
|
|||
2929b74d9b9d6e205c0e1fb2aaaffe323394058f6e583c56035a2c83b4d4ff03
|
||||
|
|
@ -0,0 +1 @@
|
|||
451d7f29392f965467f364c7b119d269551e2dc1485e8cb15ebd14753fdb6e6a
|
||||
|
|
@ -0,0 +1 @@
|
|||
8178b8a9acf7d2d8990bb6f7d984eb9e3b77d45cb2a8b54b56500ef6f93772ad
|
||||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
[`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
|
||||
```
|
||||
```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)
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
- [MR25314](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/25314)
|
||||
- [MR25122](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/25122#note_289087459)
|
||||
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:
|
||||
|
||||
- [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`
|
||||
|
||||
An example can be found [in this commit](https://gitlab.com/gitlab-org/gitlab/commit/717824144f8181bef524592eab882dd7525a60ef).
|
||||
#### Go
|
||||
|
||||
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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -25,3 +25,6 @@ rules:
|
|||
- 'testAction'
|
||||
jest/no-test-callback:
|
||||
- off
|
||||
"@gitlab/no-global-event-off":
|
||||
- off
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -12,6 +12,16 @@ job1:
|
|||
- echo 'test'
|
||||
`;
|
||||
|
||||
export const mockCiConfigQueryResponse = {
|
||||
data: {
|
||||
ciConfig: {
|
||||
errors: [],
|
||||
stages: [],
|
||||
status: '',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const mockLintResponse = {
|
||||
valid: true,
|
||||
errors: [],
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue