Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
15714832c8
commit
fca2dd4d55
|
@ -1,11 +1,11 @@
|
|||
<script>
|
||||
import { GlAlert } from '@gitlab/ui';
|
||||
import { GlLineChart } from '@gitlab/ui/dist/charts';
|
||||
import * as Sentry from '@sentry/browser';
|
||||
import produce from 'immer';
|
||||
import { sortBy } from 'lodash';
|
||||
import { formatDateAsMonth } from '~/lib/utils/datetime_utility';
|
||||
import { s__, __ } from '~/locale';
|
||||
import * as Sentry from '~/sentry/wrapper';
|
||||
import ChartSkeletonLoader from '~/vue_shared/components/resizable_chart/skeleton_loader.vue';
|
||||
import latestGroupsQuery from '../graphql/queries/groups.query.graphql';
|
||||
import latestProjectsQuery from '../graphql/queries/projects.query.graphql';
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<script>
|
||||
import * as Sentry from '@sentry/browser';
|
||||
import MetricCard from '~/analytics/shared/components/metric_card.vue';
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import { SUPPORTED_FORMATS, getFormatter } from '~/lib/utils/unit_format';
|
||||
import { s__ } from '~/locale';
|
||||
import * as Sentry from '~/sentry/wrapper';
|
||||
import usageTrendsCountQuery from '../graphql/queries/usage_trends_count.query.graphql';
|
||||
|
||||
const defaultPrecision = 0;
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
<script>
|
||||
import { GlAlert } from '@gitlab/ui';
|
||||
import { GlLineChart } from '@gitlab/ui/dist/charts';
|
||||
import * as Sentry from '@sentry/browser';
|
||||
import { some, every } from 'lodash';
|
||||
import {
|
||||
differenceInMonths,
|
||||
formatDateAsMonth,
|
||||
getDayDifference,
|
||||
} from '~/lib/utils/datetime_utility';
|
||||
import * as Sentry from '~/sentry/wrapper';
|
||||
import ChartSkeletonLoader from '~/vue_shared/components/resizable_chart/skeleton_loader.vue';
|
||||
import { TODAY, START_DATE } from '../constants';
|
||||
import { getAverageByMonth, getEarliestDate, generateDataKeys } from '../utils';
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
<script>
|
||||
import { GlAlert } from '@gitlab/ui';
|
||||
import { GlAreaChart } from '@gitlab/ui/dist/charts';
|
||||
import * as Sentry from '@sentry/browser';
|
||||
import produce from 'immer';
|
||||
import { sortBy } from 'lodash';
|
||||
import { formatDateAsMonth } from '~/lib/utils/datetime_utility';
|
||||
import { __ } from '~/locale';
|
||||
import * as Sentry from '~/sentry/wrapper';
|
||||
import ChartSkeletonLoader from '~/vue_shared/components/resizable_chart/skeleton_loader.vue';
|
||||
import usersQuery from '../graphql/queries/users.query.graphql';
|
||||
import { getAverageByMonth } from '../utils';
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import * as Sentry from '@sentry/browser';
|
||||
import { deprecatedCreateFlash as flash } from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { parseIntPagination, normalizeHeaders } from '~/lib/utils/common_utils';
|
||||
import Poll from '~/lib/utils/poll';
|
||||
import { __ } from '~/locale';
|
||||
import * as Sentry from '~/sentry/wrapper';
|
||||
import { MAX_REQUESTS } from '../constants';
|
||||
import * as types from './mutation_types';
|
||||
|
||||
|
|
|
@ -283,7 +283,7 @@ export function addContextLines(options) {
|
|||
* Trims the first char of the `richText` property when it's either a space or a diff symbol.
|
||||
* @param {Object} line
|
||||
* @returns {Object}
|
||||
* @deprecated
|
||||
* @deprecated Use `line.rich_text = line.rich_text ? line.rich_text.replace(/^[+ -]/, '') : undefined;` instead!. For more information, see https://gitlab.com/gitlab-org/gitlab/-/issues/299329
|
||||
*/
|
||||
export function trimFirstCharOfLineContent(line = {}) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
|
|
|
@ -16,6 +16,9 @@ export const EDITOR_READY_EVENT = 'editor-ready';
|
|||
export const EDITOR_TYPE_CODE = 'vs.editor.ICodeEditor';
|
||||
export const EDITOR_TYPE_DIFF = 'vs.editor.IDiffEditor';
|
||||
|
||||
export const EDITOR_CODE_INSTANCE_FN = 'createInstance';
|
||||
export const EDITOR_DIFF_INSTANCE_FN = 'createDiffInstance';
|
||||
|
||||
//
|
||||
// EXTENSIONS' CONSTANTS
|
||||
//
|
||||
|
|
|
@ -0,0 +1,164 @@
|
|||
import { debounce } from 'lodash';
|
||||
import { KeyCode, KeyMod, Range } from 'monaco-editor';
|
||||
import { EDITOR_TYPE_DIFF } from '~/editor/constants';
|
||||
import { EditorLiteExtension } from '~/editor/extensions/editor_lite_extension_base';
|
||||
import Disposable from '~/ide/lib/common/disposable';
|
||||
import { editorOptions } from '~/ide/lib/editor_options';
|
||||
import keymap from '~/ide/lib/keymap.json';
|
||||
|
||||
const isDiffEditorType = (instance) => {
|
||||
return instance.getEditorType() === EDITOR_TYPE_DIFF;
|
||||
};
|
||||
|
||||
export const UPDATE_DIMENSIONS_DELAY = 200;
|
||||
|
||||
export class EditorWebIdeExtension extends EditorLiteExtension {
|
||||
constructor({ instance, modelManager, ...options } = {}) {
|
||||
super({
|
||||
instance,
|
||||
...options,
|
||||
modelManager,
|
||||
disposable: new Disposable(),
|
||||
debouncedUpdate: debounce(() => {
|
||||
instance.updateDimensions();
|
||||
}, UPDATE_DIMENSIONS_DELAY),
|
||||
});
|
||||
|
||||
window.addEventListener('resize', instance.debouncedUpdate, false);
|
||||
|
||||
instance.onDidDispose(() => {
|
||||
window.removeEventListener('resize', instance.debouncedUpdate);
|
||||
|
||||
// catch any potential errors with disposing the error
|
||||
// this is mainly for tests caused by elements not existing
|
||||
try {
|
||||
instance.disposable.dispose();
|
||||
} catch (e) {
|
||||
if (process.env.NODE_ENV !== 'test') {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
EditorWebIdeExtension.addActions(instance);
|
||||
}
|
||||
|
||||
static addActions(instance) {
|
||||
const { store } = instance;
|
||||
const getKeyCode = (key) => {
|
||||
const monacoKeyMod = key.indexOf('KEY_') === 0;
|
||||
|
||||
return monacoKeyMod ? KeyCode[key] : KeyMod[key];
|
||||
};
|
||||
|
||||
keymap.forEach((command) => {
|
||||
const { bindings, id, label, action } = command;
|
||||
|
||||
const keybindings = bindings.map((binding) => {
|
||||
const keys = binding.split('+');
|
||||
|
||||
// eslint-disable-next-line no-bitwise
|
||||
return keys.length > 1 ? getKeyCode(keys[0]) | getKeyCode(keys[1]) : getKeyCode(keys[0]);
|
||||
});
|
||||
|
||||
instance.addAction({
|
||||
id,
|
||||
label,
|
||||
keybindings,
|
||||
run() {
|
||||
store.dispatch(action.name, action.params);
|
||||
return null;
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
createModel(file, head = null) {
|
||||
return this.modelManager.addModel(file, head);
|
||||
}
|
||||
|
||||
attachModel(model) {
|
||||
if (isDiffEditorType(this)) {
|
||||
this.setModel({
|
||||
original: model.getOriginalModel(),
|
||||
modified: model.getModel(),
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.setModel(model.getModel());
|
||||
|
||||
this.updateOptions(
|
||||
editorOptions.reduce((acc, obj) => {
|
||||
Object.keys(obj).forEach((key) => {
|
||||
Object.assign(acc, {
|
||||
[key]: obj[key](model),
|
||||
});
|
||||
});
|
||||
return acc;
|
||||
}, {}),
|
||||
);
|
||||
}
|
||||
|
||||
attachMergeRequestModel(model) {
|
||||
this.setModel({
|
||||
original: model.getBaseModel(),
|
||||
modified: model.getModel(),
|
||||
});
|
||||
}
|
||||
|
||||
updateDimensions() {
|
||||
this.layout();
|
||||
this.updateDiffView();
|
||||
}
|
||||
|
||||
setPos({ lineNumber, column }) {
|
||||
this.revealPositionInCenter({
|
||||
lineNumber,
|
||||
column,
|
||||
});
|
||||
this.setPosition({
|
||||
lineNumber,
|
||||
column,
|
||||
});
|
||||
}
|
||||
|
||||
onPositionChange(cb) {
|
||||
if (!this.onDidChangeCursorPosition) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.disposable.add(this.onDidChangeCursorPosition((e) => cb(this, e)));
|
||||
}
|
||||
|
||||
updateDiffView() {
|
||||
if (!isDiffEditorType(this)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.updateOptions({
|
||||
renderSideBySide: EditorWebIdeExtension.renderSideBySide(this.getDomNode()),
|
||||
});
|
||||
}
|
||||
|
||||
replaceSelectedText(text) {
|
||||
let selection = this.getSelection();
|
||||
const range = new Range(
|
||||
selection.startLineNumber,
|
||||
selection.startColumn,
|
||||
selection.endLineNumber,
|
||||
selection.endColumn,
|
||||
);
|
||||
|
||||
this.executeEdits('', [{ range, text }]);
|
||||
|
||||
selection = this.getSelection();
|
||||
this.setPosition({ lineNumber: selection.endLineNumber, column: selection.endColumn });
|
||||
}
|
||||
|
||||
static renderSideBySide(domElement) {
|
||||
return domElement.offsetWidth >= 700;
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import * as Sentry from '@sentry/browser';
|
||||
import { escape } from 'lodash';
|
||||
import * as Sentry from '~/sentry/wrapper';
|
||||
import { spriteIcon } from './lib/utils/common_utils';
|
||||
|
||||
const FLASH_TYPES = {
|
||||
|
|
|
@ -49,7 +49,9 @@ export default {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div class="d-flex align-items-center ide-file-templates qa-file-templates-bar">
|
||||
<div
|
||||
class="d-flex align-items-center ide-file-templates qa-file-templates-bar gl-relative gl-z-index-1"
|
||||
>
|
||||
<strong class="gl-mr-3"> {{ __('File templates') }} </strong>
|
||||
<dropdown
|
||||
:data="templateTypes"
|
||||
|
|
|
@ -1,6 +1,15 @@
|
|||
<script>
|
||||
import { mapState, mapGetters, mapActions } from 'vuex';
|
||||
import {
|
||||
EDITOR_TYPE_DIFF,
|
||||
EDITOR_CODE_INSTANCE_FN,
|
||||
EDITOR_DIFF_INSTANCE_FN,
|
||||
} from '~/editor/constants';
|
||||
import EditorLite from '~/editor/editor_lite';
|
||||
import { EditorWebIdeExtension } from '~/editor/extensions/editor_lite_webide_ext';
|
||||
import { deprecatedCreateFlash as flash } from '~/flash';
|
||||
import ModelManager from '~/ide/lib/common/model_manager';
|
||||
import { defaultDiffEditorOptions, defaultEditorOptions } from '~/ide/lib/editor_options';
|
||||
import { __ } from '~/locale';
|
||||
import {
|
||||
WEBIDE_MARK_FILE_CLICKED,
|
||||
|
@ -20,7 +29,6 @@ import {
|
|||
FILE_VIEW_MODE_PREVIEW,
|
||||
} from '../constants';
|
||||
import eventHub from '../eventhub';
|
||||
import Editor from '../lib/editor';
|
||||
import { getRulesWithTraversal } from '../lib/editorconfig/parser';
|
||||
import mapRulesToMonaco from '../lib/editorconfig/rules_mapper';
|
||||
import { getFileEditorOrDefault } from '../stores/modules/editor/utils';
|
||||
|
@ -46,6 +54,9 @@ export default {
|
|||
content: '',
|
||||
images: {},
|
||||
rules: {},
|
||||
globalEditor: null,
|
||||
modelManager: new ModelManager(),
|
||||
isEditorLoading: true,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
@ -132,6 +143,7 @@ export default {
|
|||
|
||||
// Compare key to allow for files opened in review mode to be cached differently
|
||||
if (oldVal.key !== this.file.key) {
|
||||
this.isEditorLoading = true;
|
||||
this.initEditor();
|
||||
|
||||
if (this.currentActivityView !== leftSidebarViews.edit.name) {
|
||||
|
@ -149,6 +161,7 @@ export default {
|
|||
}
|
||||
},
|
||||
viewer() {
|
||||
this.isEditorLoading = false;
|
||||
if (!this.file.pending) {
|
||||
this.createEditorInstance();
|
||||
}
|
||||
|
@ -181,11 +194,11 @@ export default {
|
|||
},
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.editor.dispose();
|
||||
this.globalEditor.dispose();
|
||||
},
|
||||
mounted() {
|
||||
if (!this.editor) {
|
||||
this.editor = Editor.create(this.$store, this.editorOptions);
|
||||
if (!this.globalEditor) {
|
||||
this.globalEditor = new EditorLite();
|
||||
}
|
||||
this.initEditor();
|
||||
|
||||
|
@ -211,8 +224,6 @@ export default {
|
|||
return;
|
||||
}
|
||||
|
||||
this.editor.clearEditor();
|
||||
|
||||
this.registerSchemaForFile();
|
||||
|
||||
Promise.all([this.fetchFileData(), this.fetchEditorconfigRules()])
|
||||
|
@ -251,20 +262,45 @@ export default {
|
|||
return;
|
||||
}
|
||||
|
||||
this.editor.dispose();
|
||||
|
||||
this.$nextTick(() => {
|
||||
if (this.viewer === viewerTypes.edit) {
|
||||
this.editor.createInstance(this.$refs.editor);
|
||||
} else {
|
||||
this.editor.createDiffInstance(this.$refs.editor);
|
||||
}
|
||||
const isDiff = this.viewer !== viewerTypes.edit;
|
||||
const shouldDisposeEditor = isDiff !== (this.editor?.getEditorType() === EDITOR_TYPE_DIFF);
|
||||
|
||||
if (this.editor && !shouldDisposeEditor) {
|
||||
this.setupEditor();
|
||||
});
|
||||
} else {
|
||||
if (this.editor && shouldDisposeEditor) {
|
||||
this.editor.dispose();
|
||||
}
|
||||
const instanceOptions = isDiff ? defaultDiffEditorOptions : defaultEditorOptions;
|
||||
const method = isDiff ? EDITOR_DIFF_INSTANCE_FN : EDITOR_CODE_INSTANCE_FN;
|
||||
|
||||
this.editor = this.globalEditor[method]({
|
||||
el: this.$refs.editor,
|
||||
blobPath: this.file.path,
|
||||
blobGlobalId: this.file.key,
|
||||
blobContent: this.content || this.file.content,
|
||||
...instanceOptions,
|
||||
...this.editorOptions,
|
||||
});
|
||||
|
||||
this.editor.use(
|
||||
new EditorWebIdeExtension({
|
||||
instance: this.editor,
|
||||
modelManager: this.modelManager,
|
||||
store: this.$store,
|
||||
file: this.file,
|
||||
options: this.editorOptions,
|
||||
}),
|
||||
);
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.setupEditor();
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
setupEditor() {
|
||||
if (!this.file || !this.editor.instance || this.file.loading) return;
|
||||
if (!this.file || !this.editor || this.file.loading) return;
|
||||
|
||||
const head = this.getStagedFile(this.file.path);
|
||||
|
||||
|
@ -279,6 +315,8 @@ export default {
|
|||
this.editor.attachModel(this.model);
|
||||
}
|
||||
|
||||
this.isEditorLoading = false;
|
||||
|
||||
this.model.updateOptions(this.rules);
|
||||
|
||||
this.model.onChange((model) => {
|
||||
|
@ -298,7 +336,7 @@ export default {
|
|||
});
|
||||
});
|
||||
|
||||
this.editor.setPosition({
|
||||
this.editor.setPos({
|
||||
lineNumber: this.fileEditor.editorRow,
|
||||
column: this.fileEditor.editorColumn,
|
||||
});
|
||||
|
@ -308,6 +346,10 @@ export default {
|
|||
fileLanguage: this.model.language,
|
||||
});
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.editor.updateDimensions();
|
||||
});
|
||||
|
||||
this.$emit('editorSetup');
|
||||
if (performance.getEntriesByName(WEBIDE_MARK_FILE_CLICKED).length) {
|
||||
eventHub.$emit(WEBIDE_MEASURE_FILE_AFTER_INTERACTION);
|
||||
|
@ -344,7 +386,7 @@ export default {
|
|||
});
|
||||
},
|
||||
onPaste(event) {
|
||||
const editor = this.editor.instance;
|
||||
const { editor } = this;
|
||||
const reImage = /^image\/(png|jpg|jpeg|gif)$/;
|
||||
const file = event.clipboardData.files[0];
|
||||
|
||||
|
@ -395,6 +437,7 @@ export default {
|
|||
<a
|
||||
href="javascript:void(0);"
|
||||
role="button"
|
||||
data-testid="edit-tab"
|
||||
@click.prevent="updateEditor({ viewMode: $options.FILE_VIEW_MODE_EDITOR })"
|
||||
>
|
||||
{{ __('Edit') }}
|
||||
|
@ -404,6 +447,7 @@ export default {
|
|||
<a
|
||||
href="javascript:void(0);"
|
||||
role="button"
|
||||
data-testid="preview-tab"
|
||||
@click.prevent="updateEditor({ viewMode: $options.FILE_VIEW_MODE_PREVIEW })"
|
||||
>{{ previewMode.previewTitle }}</a
|
||||
>
|
||||
|
@ -414,6 +458,7 @@ export default {
|
|||
<div
|
||||
v-show="showEditor"
|
||||
ref="editor"
|
||||
:key="`content-editor`"
|
||||
:class="{
|
||||
'is-readonly': isCommitModeActive,
|
||||
'is-deleted': file.deleted,
|
||||
|
@ -421,6 +466,8 @@ export default {
|
|||
}"
|
||||
class="multi-file-editor-holder"
|
||||
data-qa-selector="editor_container"
|
||||
data-testid="editor-container"
|
||||
:data-editor-loading="isEditorLoading"
|
||||
@focusout="triggerFilesChange"
|
||||
></div>
|
||||
<content-viewer
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { GlIcon } from '@gitlab/ui';
|
||||
import { GlIcon, GlTab } from '@gitlab/ui';
|
||||
import { mapActions, mapGetters } from 'vuex';
|
||||
import { __, sprintf } from '~/locale';
|
||||
|
||||
|
@ -13,6 +13,7 @@ export default {
|
|||
FileIcon,
|
||||
GlIcon,
|
||||
ChangedFileIcon,
|
||||
GlTab,
|
||||
},
|
||||
props: {
|
||||
tab: {
|
||||
|
@ -71,29 +72,30 @@ export default {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<li
|
||||
:class="{
|
||||
active: tab.active,
|
||||
disabled: tab.pending,
|
||||
}"
|
||||
<gl-tab
|
||||
:active="tab.active"
|
||||
:disabled="tab.pending"
|
||||
:title="tab.name"
|
||||
@click="clickFile(tab)"
|
||||
@mouseover="mouseOverTab"
|
||||
@mouseout="mouseOutTab"
|
||||
>
|
||||
<div :title="getUrlForPath(tab.path)" class="multi-file-tab">
|
||||
<file-icon :file-name="tab.name" :size="16" />
|
||||
{{ tab.name }}
|
||||
<file-status-icon :file="tab" />
|
||||
</div>
|
||||
<button
|
||||
:aria-label="closeLabel"
|
||||
:disabled="tab.pending"
|
||||
type="button"
|
||||
class="multi-file-tab-close"
|
||||
@click.stop.prevent="closeFile(tab)"
|
||||
>
|
||||
<gl-icon v-if="!showChangedIcon" :size="12" name="close" />
|
||||
<changed-file-icon v-else :file="tab" />
|
||||
</button>
|
||||
</li>
|
||||
<template #title>
|
||||
<div :title="getUrlForPath(tab.path)" class="multi-file-tab">
|
||||
<file-icon :file-name="tab.name" :size="16" />
|
||||
{{ tab.name }}
|
||||
<file-status-icon :file="tab" />
|
||||
</div>
|
||||
<button
|
||||
:aria-label="closeLabel"
|
||||
:disabled="tab.pending"
|
||||
type="button"
|
||||
class="multi-file-tab-close"
|
||||
@click.stop.prevent="closeFile(tab)"
|
||||
>
|
||||
<gl-icon v-if="!showChangedIcon" :size="12" name="close" />
|
||||
<changed-file-icon v-else :file="tab" />
|
||||
</button>
|
||||
</template>
|
||||
</gl-tab>
|
||||
</template>
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
<script>
|
||||
import { GlTabs } from '@gitlab/ui';
|
||||
import { mapActions, mapGetters } from 'vuex';
|
||||
import RepoTab from './repo_tab.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
RepoTab,
|
||||
GlTabs,
|
||||
},
|
||||
props: {
|
||||
activeFile: {
|
||||
|
@ -42,8 +44,8 @@ export default {
|
|||
|
||||
<template>
|
||||
<div class="multi-file-tabs">
|
||||
<ul ref="tabsScroller" class="list-unstyled gl-mb-0">
|
||||
<gl-tabs>
|
||||
<repo-tab v-for="tab in files" :key="tab.key" :tab="tab" />
|
||||
</ul>
|
||||
</gl-tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import * as Sentry from '@sentry/browser';
|
||||
import { sanitize } from '~/lib/dompurify';
|
||||
import * as Sentry from '~/sentry/wrapper';
|
||||
|
||||
// We currently load + parse the data from the issue app and related merge request
|
||||
let cachedParsedData;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import * as Sentry from '@sentry/browser';
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { convertToFixedRange } from '~/lib/utils/datetime_range';
|
||||
import * as Sentry from '~/sentry/wrapper';
|
||||
import { convertObjectPropsToCamelCase } from '../../lib/utils/common_utils';
|
||||
import { s__, sprintf } from '../../locale';
|
||||
import { ENVIRONMENT_AVAILABLE_STATE, OVERVIEW_DASHBOARD_PATH, VARIABLE_TYPES } from '../constants';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { trimFirstCharOfLineContent } from '~/diffs/store/utils';
|
||||
import { trimFirstCharOfLineContent } from '~/diffs/store/utils'; // eslint-disable-line import/no-deprecated
|
||||
import createGqClient, { fetchPolicies } from '~/lib/graphql';
|
||||
import AjaxCache from '~/lib/utils/ajax_cache';
|
||||
import { sprintf, __ } from '~/locale';
|
||||
|
@ -34,7 +34,7 @@ export const hasQuickActions = (note) => createQuickActionsRegex().test(note);
|
|||
export const stripQuickActions = (note) => note.replace(createQuickActionsRegex(), '').trim();
|
||||
|
||||
export const prepareDiffLines = (diffLines) =>
|
||||
diffLines.map((line) => ({ ...trimFirstCharOfLineContent(line) }));
|
||||
diffLines.map((line) => ({ ...trimFirstCharOfLineContent(line) })); // eslint-disable-line import/no-deprecated
|
||||
|
||||
export const gqClient = createGqClient(
|
||||
{},
|
||||
|
|
|
@ -54,3 +54,24 @@ export const MR_DIFFS_MARK_DIFF_FILES_END = 'mr-diffs-mark-diff-files-end';
|
|||
// Measures
|
||||
export const MR_DIFFS_MEASURE_FILE_TREE_DONE = 'mr-diffs-measure-file-tree-done';
|
||||
export const MR_DIFFS_MEASURE_DIFF_FILES_DONE = 'mr-diffs-measure-diff-files-done';
|
||||
|
||||
//
|
||||
// Pipelines Detail namespace
|
||||
//
|
||||
|
||||
// Marks
|
||||
export const PIPELINES_DETAIL_LINKS_MARK_CALCULATE_START =
|
||||
'pipelines-detail-links-mark-calculate-start';
|
||||
export const PIPELINES_DETAIL_LINKS_MARK_CALCULATE_END =
|
||||
'pipelines-detail-links-mark-calculate-end';
|
||||
|
||||
// Measures
|
||||
export const PIPELINES_DETAIL_LINKS_MEASURE_CALCULATION =
|
||||
'Pipelines Detail Graph: Links Calculation';
|
||||
|
||||
// Metrics
|
||||
// Note: These strings must match the backend
|
||||
// (defined in: app/services/ci/prometheus_metrics/observe_histograms_service.rb)
|
||||
export const PIPELINES_DETAIL_LINK_DURATION = 'pipeline_graph_link_calculation_duration_seconds';
|
||||
export const PIPELINES_DETAIL_LINKS_TOTAL = 'pipeline_graph_links_total';
|
||||
export const PIPELINES_DETAIL_LINKS_JOB_RATIO = 'pipeline_graph_link_per_job_ratio';
|
||||
|
|
|
@ -17,6 +17,7 @@ import {
|
|||
GlLoadingIcon,
|
||||
GlSafeHtmlDirective as SafeHtml,
|
||||
} from '@gitlab/ui';
|
||||
import * as Sentry from '@sentry/browser';
|
||||
import { uniqueId } from 'lodash';
|
||||
import Vue from 'vue';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
|
@ -24,7 +25,6 @@ import { backOff } from '~/lib/utils/common_utils';
|
|||
import httpStatusCodes from '~/lib/utils/http_status';
|
||||
import { redirectTo } from '~/lib/utils/url_utility';
|
||||
import { s__, __, n__ } from '~/locale';
|
||||
import * as Sentry from '~/sentry/wrapper';
|
||||
import { VARIABLE_TYPE, FILE_TYPE, CONFIG_VARIABLES_TIMEOUT } from '../constants';
|
||||
|
||||
export default {
|
||||
|
|
|
@ -15,14 +15,19 @@ export default {
|
|||
StageColumnComponent,
|
||||
},
|
||||
props: {
|
||||
pipeline: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
isLinkedPipeline: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
pipeline: {
|
||||
type: Object,
|
||||
required: true,
|
||||
metricsPath: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
|
@ -66,6 +71,12 @@ export default {
|
|||
hasUpstreamPipelines() {
|
||||
return Boolean(this.pipeline?.upstream?.length > 0);
|
||||
},
|
||||
metricsConfig() {
|
||||
return {
|
||||
path: this.metricsPath,
|
||||
collectMetrics: true,
|
||||
};
|
||||
},
|
||||
// The show downstream check prevents showing redundant linked columns
|
||||
showDownstreamPipelines() {
|
||||
return (
|
||||
|
@ -145,6 +156,7 @@ export default {
|
|||
:container-id="containerId"
|
||||
:container-measurements="measurements"
|
||||
:highlighted-job="hoveredJobName"
|
||||
:metrics-config="metricsConfig"
|
||||
default-link-color="gl-stroke-transparent"
|
||||
@error="onError"
|
||||
@highlightedJobsChange="updateHighlightedJobs"
|
||||
|
|
|
@ -14,6 +14,9 @@ export default {
|
|||
PipelineGraph,
|
||||
},
|
||||
inject: {
|
||||
metricsPath: {
|
||||
default: '',
|
||||
},
|
||||
pipelineIid: {
|
||||
default: '',
|
||||
},
|
||||
|
@ -108,6 +111,7 @@ export default {
|
|||
<gl-loading-icon v-if="showLoadingIcon" class="gl-mx-auto gl-my-4" size="lg" />
|
||||
<pipeline-graph
|
||||
v-if="pipeline"
|
||||
:metrics-path="metricsPath"
|
||||
:pipeline="pipeline"
|
||||
@error="reportFailure"
|
||||
@refreshPipelineGraph="refreshPipelineGraph"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import * as Sentry from '@sentry/browser';
|
||||
import Visibility from 'visibilityjs';
|
||||
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
|
||||
import * as Sentry from '~/sentry/wrapper';
|
||||
import { unwrapStagesWithNeeds } from '../unwrapping_utils';
|
||||
|
||||
const addMulti = (mainPipelineProjectPath, linkedPipeline) => {
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
import axios from '~/lib/utils/axios_utils';
|
||||
import { reportToSentry } from '../graph/utils';
|
||||
|
||||
export const reportPerformance = (path, stats) => {
|
||||
axios.post(path, stats).catch((err) => {
|
||||
reportToSentry('links_inner_perf', `error: ${err}`);
|
||||
});
|
||||
};
|
|
@ -1,9 +1,19 @@
|
|||
<script>
|
||||
import { isEmpty } from 'lodash';
|
||||
import {
|
||||
PIPELINES_DETAIL_LINKS_MARK_CALCULATE_START,
|
||||
PIPELINES_DETAIL_LINKS_MARK_CALCULATE_END,
|
||||
PIPELINES_DETAIL_LINKS_MEASURE_CALCULATION,
|
||||
PIPELINES_DETAIL_LINK_DURATION,
|
||||
PIPELINES_DETAIL_LINKS_TOTAL,
|
||||
PIPELINES_DETAIL_LINKS_JOB_RATIO,
|
||||
} from '~/performance/constants';
|
||||
import { performanceMarkAndMeasure } from '~/performance/utils';
|
||||
import { DRAW_FAILURE } from '../../constants';
|
||||
import { createJobsHash, generateJobNeedsDict } from '../../utils';
|
||||
import { reportToSentry } from '../graph/utils';
|
||||
import { parseData } from '../parsing_utils';
|
||||
import { reportPerformance } from './api';
|
||||
import { generateLinksData } from './drawing_utils';
|
||||
|
||||
export default {
|
||||
|
@ -26,6 +36,15 @@ export default {
|
|||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
totalGroups: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
metricsConfig: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: () => ({}),
|
||||
},
|
||||
defaultLinkColor: {
|
||||
type: String,
|
||||
required: false,
|
||||
|
@ -44,6 +63,9 @@ export default {
|
|||
};
|
||||
},
|
||||
computed: {
|
||||
shouldCollectMetrics() {
|
||||
return this.metricsConfig.collectMetrics && this.metricsConfig.path;
|
||||
},
|
||||
hasHighlightedJob() {
|
||||
return Boolean(this.highlightedJob);
|
||||
},
|
||||
|
@ -97,10 +119,52 @@ export default {
|
|||
}
|
||||
},
|
||||
methods: {
|
||||
beginPerfMeasure() {
|
||||
if (this.shouldCollectMetrics) {
|
||||
performanceMarkAndMeasure({ mark: PIPELINES_DETAIL_LINKS_MARK_CALCULATE_START });
|
||||
}
|
||||
},
|
||||
finishPerfMeasureAndSend() {
|
||||
if (this.shouldCollectMetrics) {
|
||||
performanceMarkAndMeasure({
|
||||
mark: PIPELINES_DETAIL_LINKS_MARK_CALCULATE_END,
|
||||
measures: [
|
||||
{
|
||||
name: PIPELINES_DETAIL_LINKS_MEASURE_CALCULATION,
|
||||
start: PIPELINES_DETAIL_LINKS_MARK_CALCULATE_START,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
window.requestAnimationFrame(() => {
|
||||
const duration = window.performance.getEntriesByName(
|
||||
PIPELINES_DETAIL_LINKS_MEASURE_CALCULATION,
|
||||
)[0]?.duration;
|
||||
|
||||
if (!duration) {
|
||||
return;
|
||||
}
|
||||
|
||||
const data = {
|
||||
histograms: [
|
||||
{ name: PIPELINES_DETAIL_LINK_DURATION, value: duration },
|
||||
{ name: PIPELINES_DETAIL_LINKS_TOTAL, value: this.links.length },
|
||||
{
|
||||
name: PIPELINES_DETAIL_LINKS_JOB_RATIO,
|
||||
value: this.links.length / this.totalGroups,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
reportPerformance(this.metricsConfig.path, data);
|
||||
});
|
||||
},
|
||||
isLinkHighlighted(linkRef) {
|
||||
return this.highlightedLinks.includes(linkRef);
|
||||
},
|
||||
prepareLinkData() {
|
||||
this.beginPerfMeasure();
|
||||
try {
|
||||
const arrayOfJobs = this.pipelineData.flatMap(({ groups }) => groups);
|
||||
const parsedData = parseData(arrayOfJobs);
|
||||
|
@ -109,6 +173,7 @@ export default {
|
|||
this.$emit('error', DRAW_FAILURE);
|
||||
reportToSentry(this.$options.name, err);
|
||||
}
|
||||
this.finishPerfMeasureAndSend();
|
||||
},
|
||||
getLinkClasses(link) {
|
||||
return [
|
||||
|
|
|
@ -70,6 +70,7 @@ export default {
|
|||
v-if="showLinkedLayers"
|
||||
:container-measurements="containerMeasurements"
|
||||
:pipeline-data="pipelineData"
|
||||
:total-groups="numGroups"
|
||||
v-bind="$attrs"
|
||||
v-on="$listeners"
|
||||
>
|
||||
|
|
|
@ -93,8 +93,13 @@ export default async function initPipelineDetailsBundle() {
|
|||
/* webpackChunkName: 'createPipelinesDetailApp' */ './pipeline_details_graph'
|
||||
);
|
||||
|
||||
const { pipelineProjectPath, pipelineIid } = dataset;
|
||||
createPipelinesDetailApp(SELECTORS.PIPELINE_GRAPH, pipelineProjectPath, pipelineIid);
|
||||
const { metricsPath, pipelineProjectPath, pipelineIid } = dataset;
|
||||
createPipelinesDetailApp(
|
||||
SELECTORS.PIPELINE_GRAPH,
|
||||
pipelineProjectPath,
|
||||
pipelineIid,
|
||||
metricsPath,
|
||||
);
|
||||
} catch {
|
||||
Flash(__('An error occurred while loading the pipeline.'));
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ const apolloProvider = new VueApollo({
|
|||
),
|
||||
});
|
||||
|
||||
const createPipelinesDetailApp = (selector, pipelineProjectPath, pipelineIid) => {
|
||||
const createPipelinesDetailApp = (selector, pipelineProjectPath, pipelineIid, metricsPath) => {
|
||||
// eslint-disable-next-line no-new
|
||||
new Vue({
|
||||
el: selector,
|
||||
|
@ -25,6 +25,7 @@ const createPipelinesDetailApp = (selector, pipelineProjectPath, pipelineIid) =>
|
|||
},
|
||||
apolloProvider,
|
||||
provide: {
|
||||
metricsPath,
|
||||
pipelineProjectPath,
|
||||
pipelineIid,
|
||||
dataMethod: GRAPHQL,
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import * as Sentry from '@sentry/browser';
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { joinPaths } from '~/lib/utils/url_utility';
|
||||
import { __ } from '~/locale';
|
||||
import * as Sentry from '~/sentry/wrapper';
|
||||
import * as types from './mutation_types';
|
||||
|
||||
export default {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import * as Sentry from '@sentry/browser';
|
||||
import $ from 'jquery';
|
||||
import { __ } from '~/locale';
|
||||
import * as Sentry from '~/sentry/wrapper';
|
||||
|
||||
const IGNORE_ERRORS = [
|
||||
// Random plugins/extensions
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
// Temporarily commented out to investigate performance: https://gitlab.com/gitlab-org/gitlab/-/issues/251179
|
||||
// export * from '@sentry/browser';
|
||||
|
||||
export function init(...args) {
|
||||
return args;
|
||||
}
|
||||
|
||||
export function setUser(...args) {
|
||||
return args;
|
||||
}
|
||||
|
||||
export function captureException(...args) {
|
||||
return args;
|
||||
}
|
||||
|
||||
export function captureMessage(...args) {
|
||||
return args;
|
||||
}
|
||||
|
||||
export function withScope(fn) {
|
||||
fn({
|
||||
setTag(...args) {
|
||||
return args;
|
||||
},
|
||||
});
|
||||
}
|
|
@ -122,6 +122,8 @@ export default {
|
|||
:value="subscribed"
|
||||
class="hide-collapsed"
|
||||
data-testid="subscription-toggle"
|
||||
:label="__('Notifications')"
|
||||
label-position="hidden"
|
||||
@change="toggleSubscription"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -11,12 +11,12 @@ import {
|
|||
GlButton,
|
||||
GlSafeHtmlDirective,
|
||||
} from '@gitlab/ui';
|
||||
import * as Sentry from '@sentry/browser';
|
||||
import highlightCurrentUser from '~/behaviors/markdown/highlight_current_user';
|
||||
import { fetchPolicies } from '~/lib/graphql';
|
||||
import { toggleContainerClasses } from '~/lib/utils/dom_utils';
|
||||
import { visitUrl, joinPaths } from '~/lib/utils/url_utility';
|
||||
import { s__ } from '~/locale';
|
||||
import * as Sentry from '~/sentry/wrapper';
|
||||
import Tracking from '~/tracking';
|
||||
import initUserPopovers from '~/user_popovers';
|
||||
import AlertDetailsTable from '~/vue_shared/components/alert_details_table.vue';
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import * as Sentry from '@sentry/browser';
|
||||
import Vue from 'vue';
|
||||
import Vuex from 'vuex';
|
||||
import * as Sentry from '~/sentry/wrapper';
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
||||
|
|
|
@ -36,6 +36,11 @@ export default {
|
|||
required: false,
|
||||
default: () => [VALID_IMAGE_FILE_MIMETYPE.mimetype],
|
||||
},
|
||||
singleFileSelection: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -79,7 +84,7 @@ export default {
|
|||
return;
|
||||
}
|
||||
|
||||
this.$emit('change', files);
|
||||
this.$emit('change', this.singleFileSelection ? files[0] : files);
|
||||
},
|
||||
ondragenter(e) {
|
||||
this.dragCounter += 1;
|
||||
|
@ -92,7 +97,7 @@ export default {
|
|||
this.$refs.fileUpload.click();
|
||||
},
|
||||
onFileInputChange(e) {
|
||||
this.$emit('change', e.target.files);
|
||||
this.$emit('change', this.singleFileSelection ? e.target.files[0] : e.target.files);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -119,9 +124,15 @@ export default {
|
|||
data-testid="dropzone-area"
|
||||
>
|
||||
<gl-icon name="upload" :size="iconStyles.size" :class="iconStyles.class" />
|
||||
<p class="gl-mb-0">
|
||||
<p class="gl-mb-0" data-testid="upload-text">
|
||||
<slot name="upload-text" :openFileUpload="openFileUpload">
|
||||
<gl-sprintf :message="__('Drop or %{linkStart}upload%{linkEnd} files to attach')">
|
||||
<gl-sprintf
|
||||
:message="
|
||||
singleFileSelection
|
||||
? __('Drop or %{linkStart}upload%{linkEnd} file to attach')
|
||||
: __('Drop or %{linkStart}upload%{linkEnd} files to attach')
|
||||
"
|
||||
>
|
||||
<template #link="{ content }">
|
||||
<gl-link @click.stop="openFileUpload">
|
||||
{{ content }}
|
||||
|
@ -139,7 +150,7 @@ export default {
|
|||
name="upload_file"
|
||||
:accept="validFileMimetypes"
|
||||
class="hide"
|
||||
multiple
|
||||
:multiple="!singleFileSelection"
|
||||
@change="onFileInputChange"
|
||||
/>
|
||||
</slot>
|
||||
|
|
|
@ -3,6 +3,11 @@
|
|||
@include gl-display-flex;
|
||||
@include gl-justify-content-center;
|
||||
@include gl-align-items-center;
|
||||
@include gl-z-index-0;
|
||||
|
||||
> * {
|
||||
filter: blur(5px);
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
|
|
|
@ -313,8 +313,9 @@ table {
|
|||
resize: none;
|
||||
padding: $gl-padding-8 $gl-padding-12;
|
||||
line-height: 1;
|
||||
border-color: $border-color;
|
||||
border: 1px solid $border-color;
|
||||
background-color: $white;
|
||||
overflow: hidden;
|
||||
|
||||
@include media-breakpoint-down(xs) {
|
||||
margin-bottom: $gl-padding-8;
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module SecurityAndCompliancePermissions
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
before_action :ensure_security_and_compliance_enabled!
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def ensure_security_and_compliance_enabled!
|
||||
render_404 unless can?(current_user, :access_security_and_compliance, project)
|
||||
end
|
||||
end
|
|
@ -3,6 +3,8 @@
|
|||
module Projects
|
||||
module Security
|
||||
class ConfigurationController < Projects::ApplicationController
|
||||
include SecurityAndCompliancePermissions
|
||||
|
||||
feature_category :static_application_security_testing
|
||||
|
||||
def show
|
||||
|
|
|
@ -395,6 +395,7 @@ class ProjectsController < Projects::ApplicationController
|
|||
metrics_dashboard_access_level
|
||||
analytics_access_level
|
||||
operations_access_level
|
||||
security_and_compliance_access_level
|
||||
]
|
||||
end
|
||||
|
||||
|
|
|
@ -69,7 +69,7 @@ class RootController < Dashboard::ProjectsController
|
|||
end
|
||||
|
||||
def customize_homepage
|
||||
@customize_homepage = experiment_enabled?(:customize_homepage)
|
||||
@customize_homepage = Feature.enabled?(:customize_homepage, default_enabled: :yaml)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
module Types
|
||||
class BoardType < BaseObject
|
||||
graphql_name 'Board'
|
||||
description 'Represents a project or group board'
|
||||
description 'Represents a project or group issue board'
|
||||
accepts ::Board
|
||||
authorize :read_board
|
||||
|
||||
|
|
|
@ -7,9 +7,9 @@ module Types
|
|||
graphql_name 'SastUiComponentSize'
|
||||
description 'Size of UI component in SAST configuration page'
|
||||
|
||||
value 'SMALL'
|
||||
value 'MEDIUM'
|
||||
value 'LARGE'
|
||||
value 'SMALL', description: "The size of UI component in SAST configuration page is small."
|
||||
value 'MEDIUM', description: "The size of UI component in SAST configuration page is medium."
|
||||
value 'LARGE', description: "The size of UI component in SAST configuration page is large."
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -379,10 +379,15 @@ module ProjectsHelper
|
|||
private
|
||||
|
||||
def can_read_security_configuration?(project, current_user)
|
||||
::Feature.enabled?(:secure_security_and_compliance_configuration_page_on_ce, @subject, default_enabled: :yaml) &&
|
||||
show_security_and_compliance_config? &&
|
||||
can?(current_user, :access_security_and_compliance, project) &&
|
||||
can?(current_user, :read_security_configuration, project)
|
||||
end
|
||||
|
||||
def show_security_and_compliance_config?
|
||||
::Feature.enabled?(:secure_security_and_compliance_configuration_page_on_ce, @subject, default_enabled: :yaml)
|
||||
end
|
||||
|
||||
def get_project_security_nav_tabs(project, current_user)
|
||||
if can_read_security_configuration?(project, current_user)
|
||||
[:security_and_compliance, :security_configuration]
|
||||
|
@ -646,7 +651,8 @@ module ProjectsHelper
|
|||
metricsDashboardAccessLevel: feature.metrics_dashboard_access_level,
|
||||
operationsAccessLevel: feature.operations_access_level,
|
||||
showDefaultAwardEmojis: project.show_default_award_emojis?,
|
||||
allowEditingCommitMessages: project.allow_editing_commit_messages?
|
||||
allowEditingCommitMessages: project.allow_editing_commit_messages?,
|
||||
securityAndComplianceAccessLevel: project.security_and_compliance_access_level
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -668,10 +674,13 @@ module ProjectsHelper
|
|||
pagesAvailable: Gitlab.config.pages.enabled,
|
||||
pagesAccessControlEnabled: Gitlab.config.pages.access_control,
|
||||
pagesAccessControlForced: ::Gitlab::Pages.access_control_is_forced?,
|
||||
pagesHelpPath: help_page_path('user/project/pages/introduction', anchor: 'gitlab-pages-access-control')
|
||||
pagesHelpPath: help_page_path('user/project/pages/introduction', anchor: 'gitlab-pages-access-control'),
|
||||
securityAndComplianceAvailable: show_security_and_compliance_toggle?
|
||||
}
|
||||
end
|
||||
|
||||
alias_method :show_security_and_compliance_toggle?, :show_security_and_compliance_config?
|
||||
|
||||
def project_permissions_panel_data_json(project)
|
||||
project_permissions_panel_data(project).to_json.html_safe
|
||||
end
|
||||
|
|
|
@ -34,6 +34,10 @@ module ProjectFeaturesCompatibility
|
|||
write_feature_attribute_boolean(:snippets_access_level, value)
|
||||
end
|
||||
|
||||
def security_and_compliance_enabled=(value)
|
||||
write_feature_attribute_boolean(:security_and_compliance_access_level, value)
|
||||
end
|
||||
|
||||
def repository_access_level=(value)
|
||||
write_feature_attribute_string(:repository_access_level, value)
|
||||
end
|
||||
|
@ -78,6 +82,10 @@ module ProjectFeaturesCompatibility
|
|||
write_feature_attribute_string(:operations_access_level, value)
|
||||
end
|
||||
|
||||
def security_and_compliance_access_level=(value)
|
||||
write_feature_attribute_string(:security_and_compliance_access_level, value)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def write_feature_attribute_boolean(field, value)
|
||||
|
|
|
@ -164,6 +164,10 @@ class Namespace < ApplicationRecord
|
|||
name = host.delete_suffix(gitlab_host)
|
||||
Namespace.where(parent_id: nil).by_path(name)
|
||||
end
|
||||
|
||||
def top_most
|
||||
where(parent_id: nil)
|
||||
end
|
||||
end
|
||||
|
||||
def package_settings
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
# frozen_string_literal: true
|
||||
module Packages
|
||||
module Nuget
|
||||
TEMPORARY_PACKAGE_NAME = 'NuGet.Temporary.Package'
|
||||
|
||||
def self.table_name_prefix
|
||||
'packages_nuget_'
|
||||
end
|
||||
|
|
|
@ -98,12 +98,12 @@ class Packages::Package < ApplicationRecord
|
|||
end
|
||||
scope :preload_composer, -> { preload(:composer_metadatum) }
|
||||
|
||||
scope :without_nuget_temporary_name, -> { where.not(name: Packages::Nuget::CreatePackageService::TEMPORARY_PACKAGE_NAME) }
|
||||
scope :without_nuget_temporary_name, -> { where.not(name: Packages::Nuget::TEMPORARY_PACKAGE_NAME) }
|
||||
|
||||
scope :has_version, -> { where.not(version: nil) }
|
||||
scope :processed, -> do
|
||||
where.not(package_type: :nuget).or(
|
||||
where.not(name: Packages::Nuget::CreatePackageService::TEMPORARY_PACKAGE_NAME)
|
||||
where.not(name: Packages::Nuget::TEMPORARY_PACKAGE_NAME)
|
||||
)
|
||||
end
|
||||
scope :preload_files, -> { preload(:package_files) }
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
module Packages
|
||||
module Rubygems
|
||||
TEMPORARY_PACKAGE_NAME = 'Gem.Temporary.Package'
|
||||
|
||||
def self.table_name_prefix
|
||||
'packages_rubygems_'
|
||||
end
|
||||
end
|
||||
end
|
|
@ -3,7 +3,6 @@
|
|||
module Packages
|
||||
module Rubygems
|
||||
class Metadatum < ApplicationRecord
|
||||
self.table_name = 'packages_rubygems_metadata'
|
||||
self.primary_key = :package_id
|
||||
|
||||
belongs_to :package, -> { where(package_type: :rubygems) }, inverse_of: :rubygems_metadatum
|
||||
|
|
|
@ -392,7 +392,8 @@ class Project < ApplicationRecord
|
|||
:merge_requests_access_level, :forking_access_level, :issues_access_level,
|
||||
:wiki_access_level, :snippets_access_level, :builds_access_level,
|
||||
:repository_access_level, :pages_access_level, :metrics_dashboard_access_level, :analytics_access_level,
|
||||
:operations_enabled?, :operations_access_level, to: :project_feature, allow_nil: true
|
||||
:operations_enabled?, :operations_access_level, :security_and_compliance_access_level,
|
||||
to: :project_feature, allow_nil: true
|
||||
delegate :show_default_award_emojis, :show_default_award_emojis=,
|
||||
:show_default_award_emojis?,
|
||||
to: :project_setting, allow_nil: true
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
class ProjectFeature < ApplicationRecord
|
||||
include Featurable
|
||||
|
||||
FEATURES = %i(issues forking merge_requests wiki snippets builds repository pages metrics_dashboard analytics operations).freeze
|
||||
FEATURES = %i(issues forking merge_requests wiki snippets builds repository pages metrics_dashboard analytics operations security_and_compliance).freeze
|
||||
EXPORTABLE_FEATURES = (FEATURES - [:security_and_compliance]).freeze
|
||||
|
||||
set_available_features(FEATURES)
|
||||
|
||||
|
@ -37,16 +38,17 @@ class ProjectFeature < ApplicationRecord
|
|||
validate :repository_children_level
|
||||
validate :allowed_access_levels
|
||||
|
||||
default_value_for :builds_access_level, value: ENABLED, allows_nil: false
|
||||
default_value_for :issues_access_level, value: ENABLED, allows_nil: false
|
||||
default_value_for :forking_access_level, value: ENABLED, allows_nil: false
|
||||
default_value_for :merge_requests_access_level, value: ENABLED, allows_nil: false
|
||||
default_value_for :snippets_access_level, value: ENABLED, allows_nil: false
|
||||
default_value_for :wiki_access_level, value: ENABLED, allows_nil: false
|
||||
default_value_for :repository_access_level, value: ENABLED, allows_nil: false
|
||||
default_value_for :analytics_access_level, value: ENABLED, allows_nil: false
|
||||
default_value_for :builds_access_level, value: ENABLED, allows_nil: false
|
||||
default_value_for :issues_access_level, value: ENABLED, allows_nil: false
|
||||
default_value_for :forking_access_level, value: ENABLED, allows_nil: false
|
||||
default_value_for :merge_requests_access_level, value: ENABLED, allows_nil: false
|
||||
default_value_for :snippets_access_level, value: ENABLED, allows_nil: false
|
||||
default_value_for :wiki_access_level, value: ENABLED, allows_nil: false
|
||||
default_value_for :repository_access_level, value: ENABLED, allows_nil: false
|
||||
default_value_for :analytics_access_level, value: ENABLED, allows_nil: false
|
||||
default_value_for :metrics_dashboard_access_level, value: PRIVATE, allows_nil: false
|
||||
default_value_for :operations_access_level, value: ENABLED, allows_nil: false
|
||||
default_value_for :operations_access_level, value: ENABLED, allows_nil: false
|
||||
default_value_for :security_and_compliance_access_level, value: PRIVATE, allows_nil: false
|
||||
|
||||
default_value_for(:pages_access_level, allows_nil: false) do |feature|
|
||||
if ::Gitlab::Pages.access_control_is_forced?
|
||||
|
|
|
@ -156,6 +156,7 @@ class ProjectPolicy < BasePolicy
|
|||
metrics_dashboard
|
||||
analytics
|
||||
operations
|
||||
security_and_compliance
|
||||
]
|
||||
|
||||
features.each do |f|
|
||||
|
@ -640,6 +641,10 @@ class ProjectPolicy < BasePolicy
|
|||
enable :set_pipeline_variables
|
||||
end
|
||||
|
||||
rule { ~security_and_compliance_disabled & can?(:developer_access) }.policy do
|
||||
enable :access_security_and_compliance
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def user_is_user?
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Packages
|
||||
class CreateTemporaryPackageService < ::Packages::CreatePackageService
|
||||
PACKAGE_VERSION = '0.0.0'
|
||||
|
||||
def execute(package_type, name: 'Temporary.Package')
|
||||
create_package!(package_type,
|
||||
name: name,
|
||||
version: "#{PACKAGE_VERSION}-#{uuid}",
|
||||
status: 'processing'
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def uuid
|
||||
SecureRandom.uuid
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,23 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Packages
|
||||
module Nuget
|
||||
class CreatePackageService < ::Packages::CreatePackageService
|
||||
TEMPORARY_PACKAGE_NAME = 'NuGet.Temporary.Package'
|
||||
PACKAGE_VERSION = '0.0.0'
|
||||
|
||||
def execute
|
||||
create_package!(:nuget,
|
||||
name: TEMPORARY_PACKAGE_NAME,
|
||||
version: "#{PACKAGE_VERSION}-#{uuid}"
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def uuid
|
||||
SecureRandom.uuid
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -68,7 +68,8 @@ module Packages
|
|||
def update_linked_package
|
||||
@package_file.package.update!(
|
||||
name: package_name,
|
||||
version: package_version
|
||||
version: package_version,
|
||||
status: :default
|
||||
)
|
||||
|
||||
::Packages::Nuget::CreateDependencyService.new(@package_file.package, package_dependencies)
|
||||
|
|
|
@ -6,60 +6,67 @@
|
|||
"^.*$": {
|
||||
"allOf": [
|
||||
{ "$ref": "#/definitions/named_field" },
|
||||
{ "$ref": "#/definitions/type_list" }
|
||||
{ "$ref": "#/definitions/detail_type" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"type_list": {
|
||||
"detail_type": {
|
||||
"oneOf": [
|
||||
{ "$ref": "#/definitions/named_list" },
|
||||
{ "$ref": "#/definitions/list" },
|
||||
{ "$ref": "#/definitions/table" },
|
||||
|
||||
{ "$ref": "#/definitions/text" },
|
||||
{ "$ref": "#/definitions/url" },
|
||||
{ "$ref": "#/definitions/code" },
|
||||
{ "$ref": "#/definitions/int" },
|
||||
|
||||
{ "$ref": "#/definitions/value" },
|
||||
{ "$ref": "#/definitions/diff" },
|
||||
{ "$ref": "#/definitions/markdown" },
|
||||
{ "$ref": "#/definitions/commit" },
|
||||
{ "$ref": "#/definitions/file_location" },
|
||||
{ "$ref": "#/definitions/module_location" }
|
||||
]
|
||||
},
|
||||
"lang_text": {
|
||||
"type": "object",
|
||||
"required": [ "value", "lang" ],
|
||||
"properties": {
|
||||
"lang": { "type": "string" },
|
||||
"value": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"lang_text_list": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/definitions/lang_text" }
|
||||
"text_value": {
|
||||
"type": "string"
|
||||
},
|
||||
"named_field": {
|
||||
"type": "object",
|
||||
"required": [ "name" ],
|
||||
"required": [
|
||||
"name"
|
||||
],
|
||||
"properties": {
|
||||
"name": { "$ref": "#/definitions/lang_text_list" },
|
||||
"description": { "$ref": "#/definitions/lang_text_list" }
|
||||
"name": {
|
||||
"$ref": "#/definitions/text_value",
|
||||
"minLength": 1
|
||||
},
|
||||
"description": {
|
||||
"$ref": "#/definitions/text_value"
|
||||
}
|
||||
}
|
||||
},
|
||||
"named_list": {
|
||||
"type": "object",
|
||||
"description": "An object with named and typed fields",
|
||||
"required": [ "type", "items" ],
|
||||
"required": [
|
||||
"type",
|
||||
"items"
|
||||
],
|
||||
"properties": {
|
||||
"type": { "const": "named-list" },
|
||||
"type": {
|
||||
"const": "named-list"
|
||||
},
|
||||
"items": {
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
"^.*$": {
|
||||
"allOf": [
|
||||
{ "$ref": "#/definitions/named_field" },
|
||||
{ "$ref": "#/definitions/type_list" }
|
||||
{
|
||||
"$ref": "#/definitions/named_field"
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/detail_type"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -69,38 +76,45 @@
|
|||
"list": {
|
||||
"type": "object",
|
||||
"description": "A list of typed fields",
|
||||
"required": [ "type", "items" ],
|
||||
"required": [
|
||||
"type",
|
||||
"items"
|
||||
],
|
||||
"properties": {
|
||||
"type": { "const": "list" },
|
||||
"type": {
|
||||
"const": "list"
|
||||
},
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/definitions/type_list" }
|
||||
"items": {
|
||||
"$ref": "#/definitions/detail_type"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"table": {
|
||||
"type": "object",
|
||||
"description": "A table of typed fields",
|
||||
"required": [],
|
||||
"required": [
|
||||
"type",
|
||||
"rows"
|
||||
],
|
||||
"properties": {
|
||||
"type": { "const": "table" },
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"header": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/type_list"
|
||||
}
|
||||
},
|
||||
"rows": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/type_list"
|
||||
}
|
||||
}
|
||||
"type": {
|
||||
"const": "table"
|
||||
},
|
||||
"header": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/detail_type"
|
||||
}
|
||||
},
|
||||
"rows": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/detail_type"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -109,73 +123,171 @@
|
|||
"text": {
|
||||
"type": "object",
|
||||
"description": "Raw text",
|
||||
"required": [ "type", "value" ],
|
||||
"required": [
|
||||
"type",
|
||||
"value"
|
||||
],
|
||||
"properties": {
|
||||
"type": { "const": "text" },
|
||||
"value": { "$ref": "#/definitions/lang_text_list" }
|
||||
"type": {
|
||||
"const": "text"
|
||||
},
|
||||
"value": {
|
||||
"$ref": "#/definitions/text_value"
|
||||
}
|
||||
}
|
||||
},
|
||||
"url": {
|
||||
"type": "object",
|
||||
"description": "A single URL",
|
||||
"required": [ "type", "href" ],
|
||||
"required": [
|
||||
"type",
|
||||
"href"
|
||||
],
|
||||
"properties": {
|
||||
"type": { "const": "url" },
|
||||
"text": { "$ref": "#/definitions/lang_text_list" },
|
||||
"href": { "type": "string" }
|
||||
"type": {
|
||||
"const": "url"
|
||||
},
|
||||
"text": {
|
||||
"$ref": "#/definitions/text_value"
|
||||
},
|
||||
"href": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"examples": ["http://mysite.com"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"code": {
|
||||
"type": "object",
|
||||
"description": "A codeblock",
|
||||
"required": [ "type", "value" ],
|
||||
"required": [
|
||||
"type",
|
||||
"value"
|
||||
],
|
||||
"properties": {
|
||||
"type": { "const": "code" },
|
||||
"value": { "type": "string" },
|
||||
"lang": { "type": "string" }
|
||||
"type": {
|
||||
"const": "code"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
},
|
||||
"lang": {
|
||||
"type": "string",
|
||||
"description": "A programming language"
|
||||
}
|
||||
}
|
||||
},
|
||||
"int": {
|
||||
"value": {
|
||||
"type": "object",
|
||||
"description": "An integer",
|
||||
"required": [ "type", "value" ],
|
||||
"description": "A field that can store a range of types of value",
|
||||
"required": ["type", "value"],
|
||||
"properties": {
|
||||
"type": { "const": "int" },
|
||||
"value": { "type": "integer" },
|
||||
"format": {
|
||||
"type": "string",
|
||||
"enum": [ "default", "hex" ]
|
||||
"type": { "const": "value" },
|
||||
"value": {
|
||||
"type": ["number", "string", "boolean"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"diff": {
|
||||
"type": "object",
|
||||
"description": "A diff",
|
||||
"required": [
|
||||
"type",
|
||||
"before",
|
||||
"after"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"const": "diff"
|
||||
},
|
||||
"before": {
|
||||
"type": "string"
|
||||
},
|
||||
"after": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"markdown": {
|
||||
"type": "object",
|
||||
"description": "GitLab flavoured markdown, see https://docs.gitlab.com/ee/user/markdown.html",
|
||||
"required": [
|
||||
"type",
|
||||
"value"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"const": "markdown"
|
||||
},
|
||||
"value": {
|
||||
"$ref": "#/definitions/text_value",
|
||||
"examples": ["Here is markdown `inline code` #1 [test](gitlab.com)\n\n![GitLab Logo](https://about.gitlab.com/images/press/logo/preview/gitlab-logo-white-preview.png)"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"commit": {
|
||||
"type": "object",
|
||||
"description": "A specific commit within the project",
|
||||
"required": [ "type", "value" ],
|
||||
"description": "A commit/tag/branch within the GitLab project",
|
||||
"required": [
|
||||
"type",
|
||||
"value"
|
||||
],
|
||||
"properties": {
|
||||
"type": { "const": "commit" },
|
||||
"value": { "type": "string", "description": "The commit SHA" }
|
||||
"type": {
|
||||
"const": "commit"
|
||||
},
|
||||
"value": {
|
||||
"type": "string",
|
||||
"description": "The commit SHA",
|
||||
"minLength": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
"file_location": {
|
||||
"type": "object",
|
||||
"description": "A location within a file in the project",
|
||||
"required": [ "type", "file_name", "line_start" ],
|
||||
"required": [
|
||||
"type",
|
||||
"file_name",
|
||||
"line_start"
|
||||
],
|
||||
"properties": {
|
||||
"type": { "const": "file-location" },
|
||||
"file_name": { "type": "string" },
|
||||
"line_start": { "type": "integer" },
|
||||
"line_end": { "type": "integer" }
|
||||
"type": {
|
||||
"const": "file-location"
|
||||
},
|
||||
"file_name": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"line_start": {
|
||||
"type": "integer"
|
||||
},
|
||||
"line_end": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"module_location": {
|
||||
"type": "object",
|
||||
"description": "A location within a binary module of the form module+relative_offset",
|
||||
"required": [ "type", "module_name", "offset" ],
|
||||
"required": [
|
||||
"type",
|
||||
"module_name",
|
||||
"offset"
|
||||
],
|
||||
"properties": {
|
||||
"type": { "const": "module-location" },
|
||||
"module_name": { "type": "string" },
|
||||
"offset": { "type": "integer" }
|
||||
"type": {
|
||||
"const": "module-location"
|
||||
},
|
||||
"module_name": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"examples": ["compiled_binary"]
|
||||
},
|
||||
"offset": {
|
||||
"type": "integer",
|
||||
"examples": [100]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,8 +54,7 @@
|
|||
= Gon::Base.render_data(nonce: content_security_policy_nonce)
|
||||
|
||||
= javascript_include_tag locale_path unless I18n.locale == :en
|
||||
-# Temporarily commented out to investigate performance: https://gitlab.com/gitlab-org/gitlab/-/issues/251179
|
||||
-# = webpack_bundle_tag "sentry" if Gitlab.config.sentry.enabled
|
||||
= webpack_bundle_tag "sentry" if Gitlab.config.sentry.enabled
|
||||
= webpack_bundle_tag 'performance_bar' if performance_bar_enabled?
|
||||
|
||||
= yield :page_specific_javascripts
|
||||
|
|
|
@ -26,4 +26,4 @@
|
|||
|
||||
= render "projects/pipelines/with_tabs", pipeline: @pipeline, pipeline_has_errors: pipeline_has_errors
|
||||
|
||||
.js-pipeline-details-vue{ data: { endpoint: project_pipeline_path(@project, @pipeline, format: :json), pipeline_project_path: @project.full_path, pipeline_iid: @pipeline.iid } }
|
||||
.js-pipeline-details-vue{ data: { endpoint: project_pipeline_path(@project, @pipeline, format: :json), metrics_path: namespace_project_ci_prometheus_metrics_histograms_path(namespace_id: @project.namespace, project_id: @project, format: :json), pipeline_project_path: @project.full_path, pipeline_iid: @pipeline.iid } }
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Introduce WebIDE as an extension for Editor Lite
|
||||
merge_request: 51527
|
||||
author:
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Enable customize homepage banner by default
|
||||
merge_request: 54357
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add GlToggle label in sidebar subscription toggle
|
||||
merge_request: 54548
|
||||
author: Yogi (@yo)
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix the npm instance level API to exclude subgroups
|
||||
merge_request: 54554
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: convert to GlTabs in app/assets/javascripts/ide/components/repo_tabs.vue
|
||||
merge_request: 42162
|
||||
author: Brandon Everett
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix style issue with "reply" placeholder textarea in firefox
|
||||
merge_request: 54592
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Restore Sentry functionaly to the frontend
|
||||
merge_request: 54441
|
||||
author:
|
||||
type: changed
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: customize_homepage
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/54357
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/299895
|
||||
milestone: '13.10'
|
||||
type: development
|
||||
group: group::expansion
|
||||
default_enabled: true
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
name: customize_homepage_experiment_percentage
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/39348
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/growth/team-tasks/-/issues/187
|
||||
milestone: '13.4'
|
||||
type: experiment
|
||||
group: group::expansion
|
||||
default_enabled: false
|
|
@ -101,7 +101,7 @@ function generateEntries() {
|
|||
|
||||
const manualEntries = {
|
||||
default: defaultEntries,
|
||||
// sentry: './sentry/index.js', Temporarily commented out to investigate performance: https://gitlab.com/gitlab-org/gitlab/-/issues/251179
|
||||
sentry: './sentry/index.js',
|
||||
performance_bar: './performance_bar/index.js',
|
||||
chrome_84_icon_fix: './lib/chrome_84_icon_fix.js',
|
||||
jira_connect_app: './jira_connect/index.js',
|
||||
|
|
|
@ -103,8 +103,10 @@ class Gitlab::Seeder::Packages
|
|||
name = "MyNugetApp.Package#{i}"
|
||||
version = "4.2.#{i}"
|
||||
|
||||
pkg = ::Packages::Nuget::CreatePackageService.new(project, project.creator, {}).execute
|
||||
# when using ::Packages::Nuget::CreatePackageService, packages have a fixed name and a fixed version.
|
||||
pkg = ::Packages::CreateTemporaryPackageService.new(
|
||||
project, project.creator, {}
|
||||
).execute(:nuget, name: Packages::Nuget::TEMPORARY_PACKAGE_NAME)
|
||||
# when using ::Packages::CreateTemporaryPackageService, packages have a fixed name and a fixed version.
|
||||
pkg.update!(name: name, version: version)
|
||||
|
||||
filename = 'package.nupkg'
|
||||
|
|
|
@ -5,160 +5,21 @@ info: "To determine the technical writer assigned to the Stage/Group associated
|
|||
type: reference, howto
|
||||
---
|
||||
|
||||
# PlantUML & GitLab **(FREE)**
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/8537) in GitLab 8.16.
|
||||
# PlantUML and GitLab **(FREE)**
|
||||
|
||||
When [PlantUML](https://plantuml.com) integration is enabled and configured in
|
||||
GitLab you can create diagrams in AsciiDoc and Markdown documents
|
||||
created in snippets, wikis, and repositories.
|
||||
GitLab, you can create diagrams in snippets, wikis, and repositories. To set up
|
||||
the integration, you must:
|
||||
|
||||
## PlantUML Server
|
||||
1. [Configure your PlantUML server](#configure-your-plantuml-server).
|
||||
1. [Configure local PlantUML access](#configure-local-plantuml-access).
|
||||
1. [Configure PlantUML security](#configure-plantuml-security).
|
||||
1. [Enable the integration](#enable-plantuml-integration).
|
||||
|
||||
Before you can enable PlantUML in GitLab; set up your own PlantUML
|
||||
server to generate the diagrams.
|
||||
|
||||
### Docker
|
||||
|
||||
With Docker, you can just run a container like this:
|
||||
|
||||
```shell
|
||||
docker run -d --name plantuml -p 8080:8080 plantuml/plantuml-server:tomcat
|
||||
```
|
||||
|
||||
The **PlantUML URL** is the hostname of the server running the container.
|
||||
|
||||
When running GitLab in Docker, it must have access to the PlantUML container.
|
||||
You can achieve that by using [Docker Compose](https://docs.docker.com/compose/).
|
||||
A basic `docker-compose.yml` file could contain:
|
||||
|
||||
```yaml
|
||||
version: "3"
|
||||
services:
|
||||
gitlab:
|
||||
image: 'gitlab/gitlab-ee:12.2.5-ee.0'
|
||||
environment:
|
||||
GITLAB_OMNIBUS_CONFIG: |
|
||||
nginx['custom_gitlab_server_config'] = "location /-/plantuml/ { \n proxy_cache off; \n proxy_pass http://plantuml:8080/; \n}\n"
|
||||
|
||||
plantuml:
|
||||
image: 'plantuml/plantuml-server:tomcat'
|
||||
container_name: plantuml
|
||||
```
|
||||
|
||||
In this scenario, PlantUML is accessible to GitLab at the URL
|
||||
`http://plantuml:8080/`.
|
||||
|
||||
### Debian/Ubuntu
|
||||
|
||||
You can also install and configure a PlantUML server in Debian/Ubuntu distributions using Tomcat.
|
||||
|
||||
First you need to create a `plantuml.war` file from the source code:
|
||||
|
||||
```shell
|
||||
sudo apt-get install graphviz openjdk-8-jdk git-core maven
|
||||
git clone https://github.com/plantuml/plantuml-server.git
|
||||
cd plantuml-server
|
||||
mvn package
|
||||
```
|
||||
|
||||
The above sequence of commands generates a `.war` file you can deploy with Tomcat:
|
||||
|
||||
```shell
|
||||
sudo apt-get install tomcat8
|
||||
sudo cp target/plantuml.war /var/lib/tomcat8/webapps/plantuml.war
|
||||
sudo chown tomcat8:tomcat8 /var/lib/tomcat8/webapps/plantuml.war
|
||||
sudo service tomcat8 restart
|
||||
```
|
||||
|
||||
After the Tomcat service restarts, the PlantUML service is ready and
|
||||
listening for requests on port 8080:
|
||||
|
||||
```plaintext
|
||||
http://localhost:8080/plantuml
|
||||
```
|
||||
|
||||
To change these defaults, edit the `/etc/tomcat8/server.xml` file.
|
||||
|
||||
NOTE:
|
||||
The default URL is different when using this approach. The Docker-based image
|
||||
makes the service available at the root URL, with no relative path. Adjust
|
||||
the configuration below accordingly.
|
||||
|
||||
### Making local PlantUML accessible using custom GitLab setup
|
||||
|
||||
The PlantUML server runs locally on your server, so it is not accessible
|
||||
externally by default. As such, it is necessary to catch external PlantUML
|
||||
calls and redirect them to the local server.
|
||||
|
||||
The idea is to redirect each call to `https://gitlab.example.com/-/plantuml/`
|
||||
to the local PlantUML server `http://plantuml:8080/` or `http://localhost:8080/plantuml/`, depending on your setup.
|
||||
|
||||
To enable the redirection, add the following line in `/etc/gitlab/gitlab.rb`:
|
||||
|
||||
```ruby
|
||||
# Docker deployment
|
||||
nginx['custom_gitlab_server_config'] = "location /-/plantuml/ { \n proxy_cache off; \n proxy_pass http://plantuml:8080/; \n}\n"
|
||||
|
||||
# Built from source
|
||||
nginx['custom_gitlab_server_config'] = "location /-/plantuml { \n rewrite ^/-/(plantuml.*) /$1 break;\n proxy_cache off; \n proxy_pass http://localhost:8080/plantuml; \n}\n"
|
||||
```
|
||||
|
||||
To activate the changes, run the following command:
|
||||
|
||||
```shell
|
||||
sudo gitlab-ctl reconfigure
|
||||
```
|
||||
|
||||
Note that the redirection through GitLab must be configured
|
||||
when running [GitLab with TLS](https://docs.gitlab.com/omnibus/settings/ssl.html)
|
||||
due to PlantUML's use of the insecure HTTP protocol. Newer browsers such
|
||||
as [Google Chrome 86+](https://www.chromestatus.com/feature/4926989725073408)
|
||||
do not load insecure HTTP resources on a page served over HTTPS.
|
||||
|
||||
### Security
|
||||
|
||||
PlantUML has features that allow fetching network resources.
|
||||
|
||||
```plaintext
|
||||
@startuml
|
||||
start
|
||||
' ...
|
||||
!include http://localhost/
|
||||
stop;
|
||||
@enduml
|
||||
```
|
||||
|
||||
**If you self-host the PlantUML server, network controls should be put in place to isolate it.**
|
||||
|
||||
## GitLab
|
||||
|
||||
You need to enable PlantUML integration from Settings under Admin Area. To do
|
||||
that, sign in with an Administrator account, and then do following:
|
||||
|
||||
1. In GitLab, go to **Admin Area > Settings > General**.
|
||||
1. Expand the **PlantUML** section.
|
||||
1. Select the **Enable PlantUML** check box.
|
||||
1. Set the PlantUML instance as `https://gitlab.example.com/-/plantuml/`.
|
||||
|
||||
NOTE:
|
||||
If you are using a PlantUML server running v1.2020.9 and
|
||||
above (for example, [plantuml.com](https://plantuml.com)), set the `PLANTUML_ENCODING`
|
||||
environment variable to enable the `deflate` compression. On Omnibus GitLab,
|
||||
this can be set in `/etc/gitlab.rb`:
|
||||
|
||||
```ruby
|
||||
gitlab_rails['env'] = { 'PLANTUML_ENCODING' => 'deflate' }
|
||||
```
|
||||
|
||||
From GitLab 13.1 and later, PlantUML integration now
|
||||
[requires a header prefix in the URL](https://github.com/plantuml/plantuml/issues/117#issuecomment-6235450160)
|
||||
to distinguish different encoding types.
|
||||
|
||||
## Creating Diagrams
|
||||
|
||||
With PlantUML integration enabled and configured, we can start adding diagrams to
|
||||
our AsciiDoc snippets, wikis, and repositories using delimited blocks:
|
||||
After completing the integration, PlantUML converts `plantuml`
|
||||
blocks to an HTML image tag, with the source pointing to the PlantUML instance. The PlantUML
|
||||
diagram delimiters `@startuml`/`@enduml` aren't required, as these are replaced
|
||||
by the `plantuml` block:
|
||||
|
||||
- **Markdown**
|
||||
|
||||
|
@ -189,13 +50,12 @@ our AsciiDoc snippets, wikis, and repositories using delimited blocks:
|
|||
Alice -> Bob: hi
|
||||
```
|
||||
|
||||
You can also use the `uml::` directive for compatibility with
|
||||
Although you can use the `uml::` directive for compatibility with
|
||||
[`sphinxcontrib-plantuml`](https://pypi.org/project/sphinxcontrib-plantuml/),
|
||||
but GitLab only supports the `caption` option.
|
||||
GitLab supports only the `caption` option.
|
||||
|
||||
The above blocks are converted to an HTML image tag with source pointing to the
|
||||
PlantUML instance. If the PlantUML server is correctly configured, this should
|
||||
render a nice diagram instead of the block:
|
||||
If the PlantUML server is correctly configured, these examples should render a
|
||||
diagram instead of the code block:
|
||||
|
||||
```plantuml
|
||||
Bob -> Alice : hello
|
||||
|
@ -204,23 +64,166 @@ Alice -> Bob : hi
|
|||
|
||||
Inside the block you can add any of the diagrams PlantUML supports, such as:
|
||||
|
||||
- [Sequence](https://plantuml.com/sequence-diagram)
|
||||
- [Use Case](https://plantuml.com/use-case-diagram)
|
||||
- [Class](https://plantuml.com/class-diagram)
|
||||
- [Activity](https://plantuml.com/activity-diagram-legacy)
|
||||
- [Class](https://plantuml.com/class-diagram)
|
||||
- [Component](https://plantuml.com/component-diagram)
|
||||
- [State](https://plantuml.com/state-diagram),
|
||||
- [Object](https://plantuml.com/object-diagram)
|
||||
- [Sequence](https://plantuml.com/sequence-diagram)
|
||||
- [State](https://plantuml.com/state-diagram)
|
||||
- [Use Case](https://plantuml.com/use-case-diagram)
|
||||
|
||||
You do not need to use the PlantUML
|
||||
diagram delimiters `@startuml`/`@enduml`, as these are replaced by the AsciiDoc `plantuml` block.
|
||||
You can add parameters to block definitions:
|
||||
|
||||
Some parameters can be added to the AsciiDoc block definition:
|
||||
|
||||
- `format`: Can be either `png` or `svg`. Note that `svg` is not supported by
|
||||
all browsers so use with care. The default is `png`.
|
||||
- `format`: Can be either `png` (default) or `svg`. Use `svg` with care, as it's
|
||||
not supported by all browsers, and isn't supported by Markdown.
|
||||
- `id`: A CSS ID added to the diagram HTML tag.
|
||||
- `width`: Width attribute added to the image tag.
|
||||
- `height`: Height attribute added to the image tag.
|
||||
|
||||
Markdown does not support any parameters and always uses PNG format.
|
||||
Markdown does not support any parameters, and always uses PNG format.
|
||||
|
||||
## Configure your PlantUML server
|
||||
|
||||
Before you can enable PlantUML in GitLab, set up your own PlantUML
|
||||
server to generate the diagrams:
|
||||
|
||||
- [In Docker](#docker).
|
||||
- [In Debian/Ubuntu](#debianubuntu).
|
||||
|
||||
### Docker
|
||||
|
||||
To run a PlantUML container in Docker, run this command:
|
||||
|
||||
```shell
|
||||
docker run -d --name plantuml -p 8080:8080 plantuml/plantuml-server:tomcat
|
||||
```
|
||||
|
||||
The **PlantUML URL** is the hostname of the server running the container.
|
||||
|
||||
When running GitLab in Docker, it must have access to the PlantUML container.
|
||||
To achieve that, use [Docker Compose](https://docs.docker.com/compose/).
|
||||
In this basic `docker-compose.yml` file, PlantUML is accessible to GitLab at the URL
|
||||
`http://plantuml:8080/`:
|
||||
|
||||
```yaml
|
||||
version: "3"
|
||||
services:
|
||||
gitlab:
|
||||
image: 'gitlab/gitlab-ee:12.2.5-ee.0'
|
||||
environment:
|
||||
GITLAB_OMNIBUS_CONFIG: |
|
||||
nginx['custom_gitlab_server_config'] = "location /-/plantuml/ { \n proxy_cache off; \n proxy_pass http://plantuml:8080/; \n}\n"
|
||||
|
||||
plantuml:
|
||||
image: 'plantuml/plantuml-server:tomcat'
|
||||
container_name: plantuml
|
||||
```
|
||||
|
||||
### Debian/Ubuntu
|
||||
|
||||
You can install and configure a PlantUML server in Debian/Ubuntu distributions
|
||||
using Tomcat:
|
||||
|
||||
1. Run these commands to create a `plantuml.war` file from the source code:
|
||||
|
||||
```shell
|
||||
sudo apt-get install graphviz openjdk-8-jdk git-core maven
|
||||
git clone https://github.com/plantuml/plantuml-server.git
|
||||
cd plantuml-server
|
||||
mvn package
|
||||
```
|
||||
|
||||
1. Deploy the `.war` file from the previous step with these commands:
|
||||
|
||||
```shell
|
||||
sudo apt-get install tomcat8
|
||||
sudo cp target/plantuml.war /var/lib/tomcat8/webapps/plantuml.war
|
||||
sudo chown tomcat8:tomcat8 /var/lib/tomcat8/webapps/plantuml.war
|
||||
sudo service tomcat8 restart
|
||||
```
|
||||
|
||||
The Tomcat service should restart. After the restart is complete, the
|
||||
PlantUML service is ready and listening for requests on port 8080:
|
||||
`http://localhost:8080/plantuml`
|
||||
|
||||
To change these defaults, edit the `/etc/tomcat8/server.xml` file.
|
||||
|
||||
NOTE:
|
||||
The default URL is different when using this approach. The Docker-based image
|
||||
makes the service available at the root URL, with no relative path. Adjust
|
||||
the configuration below accordingly.
|
||||
|
||||
## Configure local PlantUML access
|
||||
|
||||
The PlantUML server runs locally on your server, so it can't be accessed
|
||||
externally by default. Your server must catch external PlantUML
|
||||
calls to `https://gitlab.example.com/-/plantuml/` and redirect them to the
|
||||
local PlantUML server. Depending on your setup, the URL is either of the
|
||||
following:
|
||||
|
||||
- `http://plantuml:8080/`
|
||||
- `http://localhost:8080/plantuml/`
|
||||
|
||||
If you're running [GitLab with TLS](https://docs.gitlab.com/omnibus/settings/ssl.html)
|
||||
you must configure this redirection, because PlantUML uses the insecure HTTP protocol.
|
||||
Newer browsers such as [Google Chrome 86+](https://www.chromestatus.com/feature/4926989725073408)
|
||||
don't load insecure HTTP resources on pages served over HTTPS.
|
||||
|
||||
To enable this redirection:
|
||||
|
||||
1. Add the following line in `/etc/gitlab/gitlab.rb`, depending on your setup method:
|
||||
|
||||
```ruby
|
||||
# Docker deployment
|
||||
nginx['custom_gitlab_server_config'] = "location /-/plantuml/ { \n proxy_cache off; \n proxy_pass http://plantuml:8080/; \n}\n"
|
||||
|
||||
# Built from source
|
||||
nginx['custom_gitlab_server_config'] = "location /-/plantuml { \n rewrite ^/-/(plantuml.*) /$1 break;\n proxy_cache off; \n proxy_pass http://localhost:8080/plantuml; \n}\n"
|
||||
```
|
||||
|
||||
1. To activate the changes, run the following command:
|
||||
|
||||
```shell
|
||||
sudo gitlab-ctl reconfigure
|
||||
```
|
||||
|
||||
### Configure PlantUML security
|
||||
|
||||
PlantUML has features that allow fetching network resources. If you self-host the
|
||||
PlantUML server, put network controls in place to isolate it.
|
||||
|
||||
```plaintext
|
||||
@startuml
|
||||
start
|
||||
' ...
|
||||
!include http://localhost/
|
||||
stop;
|
||||
@enduml
|
||||
```
|
||||
|
||||
## Enable PlantUML integration
|
||||
|
||||
After configuring your local PlantUML server, you're ready to enable the PlantUML integration:
|
||||
|
||||
1. Sign in to GitLab as an [Administrator](../../user/permissions.md) user.
|
||||
1. In the top menu, click **{admin}** **Admin Area**.
|
||||
1. In the left sidebar, go to **Settings > General** and expand the **PlantUML** section.
|
||||
1. Select the **Enable PlantUML** check box.
|
||||
1. Set the PlantUML instance as `https://gitlab.example.com/-/plantuml/`,
|
||||
and click **Save changes**.
|
||||
|
||||
Depending on your PlantUML and GitLab version numbers, you may also need to take
|
||||
these steps:
|
||||
|
||||
- For PlantUML servers running v1.2020.9 and above, such as [plantuml.com](https://plantuml.com),
|
||||
you must set the `PLANTUML_ENCODING` environment variable to enable the `deflate`
|
||||
compression. In Omnibus GitLab, you can set this value in `/etc/gitlab.rb` with
|
||||
this command:
|
||||
|
||||
```ruby
|
||||
gitlab_rails['env'] = { 'PLANTUML_ENCODING' => 'deflate' }
|
||||
```
|
||||
|
||||
- For GitLab versions 13.1 and later, PlantUML integration now
|
||||
[requires a header prefix in the URL](https://github.com/plantuml/plantuml/issues/117#issuecomment-6235450160)
|
||||
to distinguish different encoding types.
|
||||
|
|
|
@ -1603,7 +1603,7 @@ enum BlobViewersType {
|
|||
}
|
||||
|
||||
"""
|
||||
Represents a project or group board
|
||||
Represents a project or group issue board
|
||||
"""
|
||||
type Board {
|
||||
"""
|
||||
|
@ -9490,7 +9490,7 @@ type EpicBoard {
|
|||
hideClosedList: Boolean
|
||||
|
||||
"""
|
||||
Global ID of the board.
|
||||
Global ID of the epic board.
|
||||
"""
|
||||
id: BoardsEpicBoardID!
|
||||
|
||||
|
@ -9525,9 +9525,19 @@ type EpicBoard {
|
|||
): EpicListConnection
|
||||
|
||||
"""
|
||||
Name of the board.
|
||||
Name of the epic board.
|
||||
"""
|
||||
name: String
|
||||
|
||||
"""
|
||||
Web path of the epic board.
|
||||
"""
|
||||
webPath: String!
|
||||
|
||||
"""
|
||||
Web URL of the epic board.
|
||||
"""
|
||||
webUrl: String!
|
||||
}
|
||||
|
||||
"""
|
||||
|
@ -23855,8 +23865,19 @@ type SastCiConfigurationOptionsEntityEdge {
|
|||
Size of UI component in SAST configuration page
|
||||
"""
|
||||
enum SastUiComponentSize {
|
||||
"""
|
||||
The size of UI component in SAST configuration page is large.
|
||||
"""
|
||||
LARGE
|
||||
|
||||
"""
|
||||
The size of UI component in SAST configuration page is medium.
|
||||
"""
|
||||
MEDIUM
|
||||
|
||||
"""
|
||||
The size of UI component in SAST configuration page is small.
|
||||
"""
|
||||
SMALL
|
||||
}
|
||||
|
||||
|
@ -28452,7 +28473,7 @@ type VulnerabilityDetailBase {
|
|||
"""
|
||||
Description of the field.
|
||||
"""
|
||||
description: String!
|
||||
description: String
|
||||
|
||||
"""
|
||||
Name of the field.
|
||||
|
@ -28462,7 +28483,7 @@ type VulnerabilityDetailBase {
|
|||
"""
|
||||
Name of the field.
|
||||
"""
|
||||
name: String!
|
||||
name: String
|
||||
}
|
||||
|
||||
"""
|
||||
|
@ -28472,7 +28493,7 @@ type VulnerabilityDetailBoolean {
|
|||
"""
|
||||
Description of the field.
|
||||
"""
|
||||
description: String!
|
||||
description: String
|
||||
|
||||
"""
|
||||
Name of the field.
|
||||
|
@ -28482,7 +28503,7 @@ type VulnerabilityDetailBoolean {
|
|||
"""
|
||||
Name of the field.
|
||||
"""
|
||||
name: String!
|
||||
name: String
|
||||
|
||||
"""
|
||||
Value of the field.
|
||||
|
@ -28497,7 +28518,7 @@ type VulnerabilityDetailCode {
|
|||
"""
|
||||
Description of the field.
|
||||
"""
|
||||
description: String!
|
||||
description: String
|
||||
|
||||
"""
|
||||
Name of the field.
|
||||
|
@ -28512,7 +28533,7 @@ type VulnerabilityDetailCode {
|
|||
"""
|
||||
Name of the field.
|
||||
"""
|
||||
name: String!
|
||||
name: String
|
||||
|
||||
"""
|
||||
Source code.
|
||||
|
@ -28527,7 +28548,7 @@ type VulnerabilityDetailCommit {
|
|||
"""
|
||||
Description of the field.
|
||||
"""
|
||||
description: String!
|
||||
description: String
|
||||
|
||||
"""
|
||||
Name of the field.
|
||||
|
@ -28537,7 +28558,7 @@ type VulnerabilityDetailCommit {
|
|||
"""
|
||||
Name of the field.
|
||||
"""
|
||||
name: String!
|
||||
name: String
|
||||
|
||||
"""
|
||||
The commit SHA value.
|
||||
|
@ -28562,7 +28583,7 @@ type VulnerabilityDetailDiff {
|
|||
"""
|
||||
Description of the field.
|
||||
"""
|
||||
description: String!
|
||||
description: String
|
||||
|
||||
"""
|
||||
Name of the field.
|
||||
|
@ -28572,7 +28593,7 @@ type VulnerabilityDetailDiff {
|
|||
"""
|
||||
Name of the field.
|
||||
"""
|
||||
name: String!
|
||||
name: String
|
||||
}
|
||||
|
||||
"""
|
||||
|
@ -28582,7 +28603,7 @@ type VulnerabilityDetailFileLocation {
|
|||
"""
|
||||
Description of the field.
|
||||
"""
|
||||
description: String!
|
||||
description: String
|
||||
|
||||
"""
|
||||
Name of the field.
|
||||
|
@ -28607,7 +28628,7 @@ type VulnerabilityDetailFileLocation {
|
|||
"""
|
||||
Name of the field.
|
||||
"""
|
||||
name: String!
|
||||
name: String
|
||||
}
|
||||
|
||||
"""
|
||||
|
@ -28617,7 +28638,7 @@ type VulnerabilityDetailInt {
|
|||
"""
|
||||
Description of the field.
|
||||
"""
|
||||
description: String!
|
||||
description: String
|
||||
|
||||
"""
|
||||
Name of the field.
|
||||
|
@ -28627,7 +28648,7 @@ type VulnerabilityDetailInt {
|
|||
"""
|
||||
Name of the field.
|
||||
"""
|
||||
name: String!
|
||||
name: String
|
||||
|
||||
"""
|
||||
Value of the field.
|
||||
|
@ -28642,7 +28663,7 @@ type VulnerabilityDetailList {
|
|||
"""
|
||||
Description of the field.
|
||||
"""
|
||||
description: String!
|
||||
description: String
|
||||
|
||||
"""
|
||||
Name of the field.
|
||||
|
@ -28657,7 +28678,7 @@ type VulnerabilityDetailList {
|
|||
"""
|
||||
Name of the field.
|
||||
"""
|
||||
name: String!
|
||||
name: String
|
||||
}
|
||||
|
||||
"""
|
||||
|
@ -28667,7 +28688,7 @@ type VulnerabilityDetailMarkdown {
|
|||
"""
|
||||
Description of the field.
|
||||
"""
|
||||
description: String!
|
||||
description: String
|
||||
|
||||
"""
|
||||
Name of the field.
|
||||
|
@ -28677,7 +28698,7 @@ type VulnerabilityDetailMarkdown {
|
|||
"""
|
||||
Name of the field.
|
||||
"""
|
||||
name: String!
|
||||
name: String
|
||||
|
||||
"""
|
||||
Value of the Markdown field.
|
||||
|
@ -28692,7 +28713,7 @@ type VulnerabilityDetailModuleLocation {
|
|||
"""
|
||||
Description of the field.
|
||||
"""
|
||||
description: String!
|
||||
description: String
|
||||
|
||||
"""
|
||||
Name of the field.
|
||||
|
@ -28707,7 +28728,7 @@ type VulnerabilityDetailModuleLocation {
|
|||
"""
|
||||
Name of the field.
|
||||
"""
|
||||
name: String!
|
||||
name: String
|
||||
|
||||
"""
|
||||
Offset of the module location.
|
||||
|
@ -28722,7 +28743,7 @@ type VulnerabilityDetailTable {
|
|||
"""
|
||||
Description of the field.
|
||||
"""
|
||||
description: String!
|
||||
description: String
|
||||
|
||||
"""
|
||||
Name of the field.
|
||||
|
@ -28737,7 +28758,7 @@ type VulnerabilityDetailTable {
|
|||
"""
|
||||
Name of the field.
|
||||
"""
|
||||
name: String!
|
||||
name: String
|
||||
|
||||
"""
|
||||
Table rows.
|
||||
|
@ -28752,7 +28773,7 @@ type VulnerabilityDetailText {
|
|||
"""
|
||||
Description of the field.
|
||||
"""
|
||||
description: String!
|
||||
description: String
|
||||
|
||||
"""
|
||||
Name of the field.
|
||||
|
@ -28762,7 +28783,7 @@ type VulnerabilityDetailText {
|
|||
"""
|
||||
Name of the field.
|
||||
"""
|
||||
name: String!
|
||||
name: String
|
||||
|
||||
"""
|
||||
Value of the text field.
|
||||
|
@ -28777,7 +28798,7 @@ type VulnerabilityDetailUrl {
|
|||
"""
|
||||
Description of the field.
|
||||
"""
|
||||
description: String!
|
||||
description: String
|
||||
|
||||
"""
|
||||
Name of the field.
|
||||
|
@ -28792,7 +28813,7 @@ type VulnerabilityDetailUrl {
|
|||
"""
|
||||
Name of the field.
|
||||
"""
|
||||
name: String!
|
||||
name: String
|
||||
|
||||
"""
|
||||
Text of the URL.
|
||||
|
|
|
@ -4260,7 +4260,7 @@
|
|||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "Board",
|
||||
"description": "Represents a project or group board",
|
||||
"description": "Represents a project or group issue board",
|
||||
"fields": [
|
||||
{
|
||||
"name": "assignee",
|
||||
|
@ -26126,7 +26126,7 @@
|
|||
},
|
||||
{
|
||||
"name": "id",
|
||||
"description": "Global ID of the board.",
|
||||
"description": "Global ID of the epic board.",
|
||||
"args": [
|
||||
|
||||
],
|
||||
|
@ -26207,7 +26207,7 @@
|
|||
},
|
||||
{
|
||||
"name": "name",
|
||||
"description": "Name of the board.",
|
||||
"description": "Name of the epic board.",
|
||||
"args": [
|
||||
|
||||
],
|
||||
|
@ -26218,6 +26218,42 @@
|
|||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "webPath",
|
||||
"description": "Web path of the epic board.",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "webUrl",
|
||||
"description": "Web URL of the epic board.",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
}
|
||||
],
|
||||
"inputFields": null,
|
||||
|
@ -68666,19 +68702,19 @@
|
|||
"enumValues": [
|
||||
{
|
||||
"name": "SMALL",
|
||||
"description": null,
|
||||
"description": "The size of UI component in SAST configuration page is small.",
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "MEDIUM",
|
||||
"description": null,
|
||||
"description": "The size of UI component in SAST configuration page is medium.",
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "LARGE",
|
||||
"description": null,
|
||||
"description": "The size of UI component in SAST configuration page is large.",
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
}
|
||||
|
@ -81695,13 +81731,9 @@
|
|||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
|
@ -81727,13 +81759,9 @@
|
|||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
|
@ -81758,13 +81786,9 @@
|
|||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
|
@ -81790,13 +81814,9 @@
|
|||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
|
@ -81839,13 +81859,9 @@
|
|||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
|
@ -81885,13 +81901,9 @@
|
|||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
|
@ -81903,13 +81915,9 @@
|
|||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
|
@ -81934,13 +81942,9 @@
|
|||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
|
@ -81966,13 +81970,9 @@
|
|||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
|
@ -81984,13 +81984,9 @@
|
|||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
|
@ -82015,13 +82011,9 @@
|
|||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
|
@ -82033,13 +82025,9 @@
|
|||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
|
@ -82051,13 +82039,9 @@
|
|||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
|
@ -82083,13 +82067,9 @@
|
|||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
|
@ -82114,13 +82094,9 @@
|
|||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
|
@ -82146,13 +82122,9 @@
|
|||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
|
@ -82200,13 +82172,9 @@
|
|||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
|
@ -82231,13 +82199,9 @@
|
|||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
|
@ -82263,13 +82227,9 @@
|
|||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
|
@ -82312,13 +82272,9 @@
|
|||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
|
@ -82370,13 +82326,9 @@
|
|||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
|
@ -82401,13 +82353,9 @@
|
|||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
|
@ -82433,13 +82381,9 @@
|
|||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
|
@ -82451,13 +82395,9 @@
|
|||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
|
@ -82482,13 +82422,9 @@
|
|||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
|
@ -82514,13 +82450,9 @@
|
|||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
|
|
|
@ -438,7 +438,7 @@ Autogenerated return type of AwardEmojiToggle.
|
|||
|
||||
### Board
|
||||
|
||||
Represents a project or group board.
|
||||
Represents a project or group issue board.
|
||||
|
||||
| Field | Type | Description |
|
||||
| ----- | ---- | ----------- |
|
||||
|
@ -1699,9 +1699,11 @@ Represents an epic board.
|
|||
| ----- | ---- | ----------- |
|
||||
| `hideBacklogList` | Boolean | Whether or not backlog list is hidden. |
|
||||
| `hideClosedList` | Boolean | Whether or not closed list is hidden. |
|
||||
| `id` | BoardsEpicBoardID! | Global ID of the board. |
|
||||
| `id` | BoardsEpicBoardID! | Global ID of the epic board. |
|
||||
| `lists` | EpicListConnection | Epic board lists. |
|
||||
| `name` | String | Name of the board. |
|
||||
| `name` | String | Name of the epic board. |
|
||||
| `webPath` | String! | Web path of the epic board. |
|
||||
| `webUrl` | String! | Web URL of the epic board. |
|
||||
|
||||
### EpicBoardCreatePayload
|
||||
|
||||
|
@ -4333,9 +4335,9 @@ Represents the vulnerability details base.
|
|||
|
||||
| Field | Type | Description |
|
||||
| ----- | ---- | ----------- |
|
||||
| `description` | String! | Description of the field. |
|
||||
| `description` | String | Description of the field. |
|
||||
| `fieldName` | String | Name of the field. |
|
||||
| `name` | String! | Name of the field. |
|
||||
| `name` | String | Name of the field. |
|
||||
|
||||
### VulnerabilityDetailBoolean
|
||||
|
||||
|
@ -4343,9 +4345,9 @@ Represents the vulnerability details boolean value.
|
|||
|
||||
| Field | Type | Description |
|
||||
| ----- | ---- | ----------- |
|
||||
| `description` | String! | Description of the field. |
|
||||
| `description` | String | Description of the field. |
|
||||
| `fieldName` | String | Name of the field. |
|
||||
| `name` | String! | Name of the field. |
|
||||
| `name` | String | Name of the field. |
|
||||
| `value` | Boolean! | Value of the field. |
|
||||
|
||||
### VulnerabilityDetailCode
|
||||
|
@ -4354,10 +4356,10 @@ Represents the vulnerability details code field.
|
|||
|
||||
| Field | Type | Description |
|
||||
| ----- | ---- | ----------- |
|
||||
| `description` | String! | Description of the field. |
|
||||
| `description` | String | Description of the field. |
|
||||
| `fieldName` | String | Name of the field. |
|
||||
| `lang` | String | Language of the code. |
|
||||
| `name` | String! | Name of the field. |
|
||||
| `name` | String | Name of the field. |
|
||||
| `value` | String! | Source code. |
|
||||
|
||||
### VulnerabilityDetailCommit
|
||||
|
@ -4366,9 +4368,9 @@ Represents the vulnerability details commit field.
|
|||
|
||||
| Field | Type | Description |
|
||||
| ----- | ---- | ----------- |
|
||||
| `description` | String! | Description of the field. |
|
||||
| `description` | String | Description of the field. |
|
||||
| `fieldName` | String | Name of the field. |
|
||||
| `name` | String! | Name of the field. |
|
||||
| `name` | String | Name of the field. |
|
||||
| `value` | String! | The commit SHA value. |
|
||||
|
||||
### VulnerabilityDetailDiff
|
||||
|
@ -4379,9 +4381,9 @@ Represents the vulnerability details diff field.
|
|||
| ----- | ---- | ----------- |
|
||||
| `after` | String! | Value of the field after the change. |
|
||||
| `before` | String! | Value of the field before the change. |
|
||||
| `description` | String! | Description of the field. |
|
||||
| `description` | String | Description of the field. |
|
||||
| `fieldName` | String | Name of the field. |
|
||||
| `name` | String! | Name of the field. |
|
||||
| `name` | String | Name of the field. |
|
||||
|
||||
### VulnerabilityDetailFileLocation
|
||||
|
||||
|
@ -4389,12 +4391,12 @@ Represents the vulnerability details location within a file in the project.
|
|||
|
||||
| Field | Type | Description |
|
||||
| ----- | ---- | ----------- |
|
||||
| `description` | String! | Description of the field. |
|
||||
| `description` | String | Description of the field. |
|
||||
| `fieldName` | String | Name of the field. |
|
||||
| `fileName` | String! | File name. |
|
||||
| `lineEnd` | Int! | End line number of the file location. |
|
||||
| `lineStart` | Int! | Start line number of the file location. |
|
||||
| `name` | String! | Name of the field. |
|
||||
| `name` | String | Name of the field. |
|
||||
|
||||
### VulnerabilityDetailInt
|
||||
|
||||
|
@ -4402,9 +4404,9 @@ Represents the vulnerability details integer value.
|
|||
|
||||
| Field | Type | Description |
|
||||
| ----- | ---- | ----------- |
|
||||
| `description` | String! | Description of the field. |
|
||||
| `description` | String | Description of the field. |
|
||||
| `fieldName` | String | Name of the field. |
|
||||
| `name` | String! | Name of the field. |
|
||||
| `name` | String | Name of the field. |
|
||||
| `value` | Int! | Value of the field. |
|
||||
|
||||
### VulnerabilityDetailList
|
||||
|
@ -4413,10 +4415,10 @@ Represents the vulnerability details list value.
|
|||
|
||||
| Field | Type | Description |
|
||||
| ----- | ---- | ----------- |
|
||||
| `description` | String! | Description of the field. |
|
||||
| `description` | String | Description of the field. |
|
||||
| `fieldName` | String | Name of the field. |
|
||||
| `items` | VulnerabilityDetail! => Array | List of details. |
|
||||
| `name` | String! | Name of the field. |
|
||||
| `name` | String | Name of the field. |
|
||||
|
||||
### VulnerabilityDetailMarkdown
|
||||
|
||||
|
@ -4424,9 +4426,9 @@ Represents the vulnerability details Markdown field.
|
|||
|
||||
| Field | Type | Description |
|
||||
| ----- | ---- | ----------- |
|
||||
| `description` | String! | Description of the field. |
|
||||
| `description` | String | Description of the field. |
|
||||
| `fieldName` | String | Name of the field. |
|
||||
| `name` | String! | Name of the field. |
|
||||
| `name` | String | Name of the field. |
|
||||
| `value` | String! | Value of the Markdown field. |
|
||||
|
||||
### VulnerabilityDetailModuleLocation
|
||||
|
@ -4435,10 +4437,10 @@ Represents the vulnerability details location within a file in the project.
|
|||
|
||||
| Field | Type | Description |
|
||||
| ----- | ---- | ----------- |
|
||||
| `description` | String! | Description of the field. |
|
||||
| `description` | String | Description of the field. |
|
||||
| `fieldName` | String | Name of the field. |
|
||||
| `moduleName` | String! | Module name. |
|
||||
| `name` | String! | Name of the field. |
|
||||
| `name` | String | Name of the field. |
|
||||
| `offset` | Int! | Offset of the module location. |
|
||||
|
||||
### VulnerabilityDetailTable
|
||||
|
@ -4447,10 +4449,10 @@ Represents the vulnerability details table value.
|
|||
|
||||
| Field | Type | Description |
|
||||
| ----- | ---- | ----------- |
|
||||
| `description` | String! | Description of the field. |
|
||||
| `description` | String | Description of the field. |
|
||||
| `fieldName` | String | Name of the field. |
|
||||
| `headers` | VulnerabilityDetail! => Array | Table headers. |
|
||||
| `name` | String! | Name of the field. |
|
||||
| `name` | String | Name of the field. |
|
||||
| `rows` | VulnerabilityDetail! => Array | Table rows. |
|
||||
|
||||
### VulnerabilityDetailText
|
||||
|
@ -4459,9 +4461,9 @@ Represents the vulnerability details text field.
|
|||
|
||||
| Field | Type | Description |
|
||||
| ----- | ---- | ----------- |
|
||||
| `description` | String! | Description of the field. |
|
||||
| `description` | String | Description of the field. |
|
||||
| `fieldName` | String | Name of the field. |
|
||||
| `name` | String! | Name of the field. |
|
||||
| `name` | String | Name of the field. |
|
||||
| `value` | String! | Value of the text field. |
|
||||
|
||||
### VulnerabilityDetailUrl
|
||||
|
@ -4470,10 +4472,10 @@ Represents the vulnerability details URL field.
|
|||
|
||||
| Field | Type | Description |
|
||||
| ----- | ---- | ----------- |
|
||||
| `description` | String! | Description of the field. |
|
||||
| `description` | String | Description of the field. |
|
||||
| `fieldName` | String | Name of the field. |
|
||||
| `href` | String! | Href of the URL. |
|
||||
| `name` | String! | Name of the field. |
|
||||
| `name` | String | Name of the field. |
|
||||
| `text` | String | Text of the URL. |
|
||||
|
||||
### VulnerabilityDismissPayload
|
||||
|
@ -5470,9 +5472,9 @@ Size of UI component in SAST configuration page.
|
|||
|
||||
| Value | Description |
|
||||
| ----- | ----------- |
|
||||
| `LARGE` | |
|
||||
| `MEDIUM` | |
|
||||
| `SMALL` | |
|
||||
| `LARGE` | The size of UI component in SAST configuration page is large. |
|
||||
| `MEDIUM` | The size of UI component in SAST configuration page is medium. |
|
||||
| `SMALL` | The size of UI component in SAST configuration page is small. |
|
||||
|
||||
### SecurityReportTypeEnum
|
||||
|
||||
|
|
|
@ -1061,7 +1061,7 @@ POST /projects
|
|||
| `avatar` | mixed | **{dotted-circle}** No | Image file for avatar of the project. |
|
||||
| `build_coverage_regex` | string | **{dotted-circle}** No | Test coverage parsing. |
|
||||
| `build_git_strategy` | string | **{dotted-circle}** No | The Git strategy. Defaults to `fetch`. |
|
||||
| `build_timeout` | integer | **{dotted-circle}** No | The maximum amount of time in minutes that a job is able run (in seconds). |
|
||||
| `build_timeout` | integer | **{dotted-circle}** No | The maximum amount of time, in seconds, that a job can run. |
|
||||
| `builds_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, or `enabled`. |
|
||||
| `ci_config_path` | string | **{dotted-circle}** No | The path to CI configuration file. |
|
||||
| `container_expiration_policy_attributes` | hash | **{dotted-circle}** No | Update the image cleanup policy for this project. Accepts: `cadence` (string), `keep_n` (integer), `older_than` (string), `name_regex` (string), `name_regex_delete` (string), `name_regex_keep` (string), `enabled` (boolean). |
|
||||
|
|
|
@ -242,6 +242,27 @@ class BatchedMigrationName < Elastic::Migration
|
|||
end
|
||||
```
|
||||
|
||||
### Multi-version compatibility
|
||||
|
||||
These Elasticsearch migrations, like any other GitLab changes, need to support the case where
|
||||
[multiple versions of the application are running at the same time](multi_version_compatibility.md).
|
||||
|
||||
Depending on the order of deployment, it's possible that the migration
|
||||
has started or finished and there's still a server running the application code from before the
|
||||
migration. We need to take this into consideration until we can [ensure all Elasticsearch migrations
|
||||
start after the deployment has finished](https://gitlab.com/gitlab-org/gitlab/-/issues/321619).
|
||||
|
||||
### Reverting a migration
|
||||
|
||||
Because Elasticsearch does not support transactions, we always need to design our
|
||||
migrations to accommodate a situation where the application
|
||||
code is reverted after the migration has started or after it is finished.
|
||||
|
||||
For this reason we generally defer destructive actions (for example, deletions after
|
||||
some data is moved) to a later merge request after the migrations have
|
||||
completed successfully. To be safe, for self-managed customers we should also
|
||||
defer it to another release if there is risk of important data loss.
|
||||
|
||||
## Performance Monitoring
|
||||
|
||||
### Prometheus
|
||||
|
|
|
@ -98,6 +98,57 @@ When declaring multiple globals, always use one `/* global [name] */` line per v
|
|||
/* global jQuery */
|
||||
```
|
||||
|
||||
### Deprecating functions with `import/no-deprecated`
|
||||
|
||||
Our `@gitlab/eslint-plugin` Node module contains the [`eslint-plugin-import`](https://gitlab.com/gitlab-org/frontend/eslint-plugin) package.
|
||||
|
||||
We can use the [`import/no-deprecated`](https://github.com/benmosher/eslint-plugin-import/blob/HEAD/docs/rules/no-deprecated.md) rule to deprecate functions using a JSDoc block with a `@deprecated` tag:
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Convert search query into an object
|
||||
*
|
||||
* @param {String} query from "document.location.search"
|
||||
* @param {Object} options
|
||||
* @param {Boolean} options.gatherArrays - gather array values into an Array
|
||||
* @returns {Object}
|
||||
*
|
||||
* ex: "?one=1&two=2" into {one: 1, two: 2}
|
||||
* @deprecated Please use `queryToObject` instead. See https://gitlab.com/gitlab-org/gitlab/-/issues/283982 for more information
|
||||
*/
|
||||
export function queryToObject(query, options = {}) {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
It is strongly encouraged that you:
|
||||
|
||||
- Put in an **alternative path for developers** looking to use this function.
|
||||
- **Provide a link to the issue** that tracks the migration process.
|
||||
|
||||
NOTE:
|
||||
Uses are detected if you import the deprecated function into another file. They are not detected when the function is used in the same file.
|
||||
|
||||
Running `$ yarn eslint` after this will give us the list of deprecated usages:
|
||||
|
||||
```shell
|
||||
$ yarn eslint
|
||||
|
||||
./app/assets/javascripts/issuable_form.js
|
||||
9:10 error Deprecated: Please use `queryToObject` instead. See https://gitlab.com/gitlab-org/gitlab/-/issues/283982 for more information import/no-deprecated
|
||||
33:23 error Deprecated: Please use `queryToObject` instead. See https://gitlab.com/gitlab-org/gitlab/-/issues/283982 for more information import/no-deprecated
|
||||
...
|
||||
```
|
||||
|
||||
Grep for disabled cases of this rule to generate a working list to create issues from, so you can track the effort of removing deprecated uses:
|
||||
|
||||
```shell
|
||||
$ grep "eslint-disable.*import/no-deprecated" -r .
|
||||
|
||||
./app/assets/javascripts/issuable_form.js:import { queryToObject, objectToQuery } from './lib/utils/url_utility'; // eslint-disable-line import/no-deprecate
|
||||
./app/assets/javascripts/issuable_form.js: // eslint-disable-next-line import/no-deprecated
|
||||
```
|
||||
|
||||
## Formatting with Prettier
|
||||
|
||||
> Support for `.graphql` [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/227280) in GitLab 13.2.
|
||||
|
|
|
@ -35,7 +35,7 @@ Access the default page for admin area settings by navigating to **Admin Area >
|
|||
| ------ | ----------- |
|
||||
| [Elasticsearch](../../../integration/elasticsearch.md#enabling-advanced-search) | Elasticsearch integration. Elasticsearch AWS IAM. |
|
||||
| [Kroki](../../../administration/integration/kroki.md#enable-kroki-in-gitlab) | Allow rendering of diagrams in AsciiDoc and Markdown documents using [kroki.io](https://kroki.io). |
|
||||
| [PlantUML](../../../administration/integration/plantuml.md#gitlab) | Allow rendering of PlantUML diagrams in AsciiDoc and Markdown documents. |
|
||||
| [PlantUML](../../../administration/integration/plantuml.md) | Allow rendering of PlantUML diagrams in documents. |
|
||||
| [Slack application](../../../user/project/integrations/gitlab_slack_application.md#configuration) **(FREE SAAS)** | Slack integration allows you to interact with GitLab via slash commands in a chat window. This option is only available on GitLab.com, though it may be [available for self-managed instances in the future](https://gitlab.com/gitlab-org/gitlab/-/issues/28164). |
|
||||
| [Third party offers](third_party_offers.md) | Control the display of third party offers. |
|
||||
| [Snowplow](../../../development/snowplow.md) | Configure the Snowplow integration. |
|
||||
|
|
|
@ -52,7 +52,8 @@ module API
|
|||
namespace_path = namespace_path_from_package_name
|
||||
next unless namespace_path
|
||||
|
||||
namespace = namespace_from_path(namespace_path)
|
||||
namespace = Namespace.top_most
|
||||
.by_path(namespace_path)
|
||||
next unless namespace
|
||||
|
||||
finder = ::Packages::Npm::PackageFinder.new(params[:package_name], namespace: namespace)
|
||||
|
@ -70,13 +71,6 @@ module API
|
|||
|
||||
package_name.match(Gitlab::Regex.npm_package_name_regex)&.captures&.first
|
||||
end
|
||||
|
||||
def namespace_from_path(path)
|
||||
group = Group.by_path(path)
|
||||
return group if group
|
||||
|
||||
Namespace.for_user.by_path(path)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -62,8 +62,9 @@ module API
|
|||
file_name: PACKAGE_FILENAME
|
||||
)
|
||||
|
||||
package = ::Packages::Nuget::CreatePackageService.new(project_or_group, current_user, declared_params.merge(build: current_authenticated_job))
|
||||
.execute
|
||||
package = ::Packages::CreateTemporaryPackageService.new(
|
||||
project_or_group, current_user, declared_params.merge(build: current_authenticated_job)
|
||||
).execute(:nuget, name: ::Packages::Nuget::TEMPORARY_PACKAGE_NAME)
|
||||
|
||||
package_file = ::Packages::CreatePackageFileService.new(package, file_params.merge(build: current_authenticated_job))
|
||||
.execute
|
||||
|
|
|
@ -12,7 +12,7 @@ module API
|
|||
# The Marshal version can be found by "#{Marshal::MAJOR_VERSION}.#{Marshal::MINOR_VERSION}"
|
||||
# Updating the version should require a GitLab API version change.
|
||||
MARSHAL_VERSION = '4.8'
|
||||
|
||||
PACKAGE_FILENAME = 'package.gem'
|
||||
FILE_NAME_REQUIREMENTS = {
|
||||
file_name: API::NO_SLASH_URL_PART_REGEX
|
||||
}.freeze
|
||||
|
@ -73,16 +73,45 @@ module API
|
|||
detail 'This feature was introduced in GitLab 13.9'
|
||||
end
|
||||
post 'gems/authorize' do
|
||||
# To be implemented in https://gitlab.com/gitlab-org/gitlab/-/issues/299263
|
||||
not_found!
|
||||
authorize_workhorse!(
|
||||
subject: user_project,
|
||||
has_length: false,
|
||||
maximum_size: user_project.actual_limits.rubygems_max_file_size
|
||||
)
|
||||
end
|
||||
|
||||
desc 'Upload a gem' do
|
||||
detail 'This feature was introduced in GitLab 13.9'
|
||||
end
|
||||
params do
|
||||
requires :file, type: ::API::Validations::Types::WorkhorseFile, desc: 'The package file to be published (generated by Multipart middleware)'
|
||||
end
|
||||
post 'gems' do
|
||||
# To be implemented in https://gitlab.com/gitlab-org/gitlab/-/issues/299263
|
||||
not_found!
|
||||
authorize_upload!(user_project)
|
||||
bad_request!('File is too large') if user_project.actual_limits.exceeded?(:rubygems_max_file_size, params[:file].size)
|
||||
|
||||
track_package_event('push_package', :rubygems)
|
||||
|
||||
ActiveRecord::Base.transaction do
|
||||
package = ::Packages::CreateTemporaryPackageService.new(
|
||||
user_project, current_user, declared_params.merge(build: current_authenticated_job)
|
||||
).execute(:rubygems, name: ::Packages::Rubygems::TEMPORARY_PACKAGE_NAME)
|
||||
|
||||
file_params = {
|
||||
file: params[:file],
|
||||
file_name: PACKAGE_FILENAME
|
||||
}
|
||||
|
||||
::Packages::CreatePackageFileService.new(
|
||||
package, file_params.merge(build: current_authenticated_job)
|
||||
).execute
|
||||
end
|
||||
|
||||
created!
|
||||
rescue ObjectStorage::RemoteStoreError => e
|
||||
Gitlab::ErrorTracking.track_exception(e, extra: { file_name: params[:file_name], project_id: user_project.id })
|
||||
|
||||
forbidden!
|
||||
end
|
||||
|
||||
desc 'Fetch a list of dependencies' do
|
||||
|
|
|
@ -172,6 +172,7 @@ apifuzzer_fuzz_dnd:
|
|||
-e FUZZAPI_HAR \
|
||||
-e FUZZAPI_OPENAPI \
|
||||
-e FUZZAPI_POSTMAN_COLLECTION \
|
||||
-e FUZZAPI_POSTMAN_COLLECTION_VARIABLES \
|
||||
-e FUZZAPI_TARGET_URL \
|
||||
-e FUZZAPI_OVERRIDES_FILE \
|
||||
-e FUZZAPI_OVERRIDES_ENV \
|
||||
|
@ -214,6 +215,7 @@ apifuzzer_fuzz_dnd:
|
|||
-e FUZZAPI_HAR \
|
||||
-e FUZZAPI_OPENAPI \
|
||||
-e FUZZAPI_POSTMAN_COLLECTION \
|
||||
-e FUZZAPI_POSTMAN_COLLECTION_VARIABLES \
|
||||
-e FUZZAPI_TARGET_URL \
|
||||
-e FUZZAPI_OVERRIDES_FILE \
|
||||
-e FUZZAPI_OVERRIDES_ENV \
|
||||
|
|
|
@ -58,10 +58,6 @@ module Gitlab
|
|||
tracking_category: 'Growth::Conversion::Experiment::ContactSalesInApp',
|
||||
use_backwards_compatible_subject_index: true
|
||||
},
|
||||
customize_homepage: {
|
||||
tracking_category: 'Growth::Expansion::Experiment::CustomizeHomepage',
|
||||
use_backwards_compatible_subject_index: true
|
||||
},
|
||||
group_only_trials: {
|
||||
tracking_category: 'Growth::Conversion::Experiment::GroupOnlyTrials',
|
||||
use_backwards_compatible_subject_index: true
|
||||
|
|
|
@ -41,6 +41,9 @@
|
|||
- i_package_pypi_delete_package
|
||||
- i_package_pypi_pull_package
|
||||
- i_package_pypi_push_package
|
||||
- i_package_rubygems_delete_package
|
||||
- i_package_rubygems_pull_package
|
||||
- i_package_rubygems_push_package
|
||||
- i_package_tag_delete_package
|
||||
- i_package_tag_pull_package
|
||||
- i_package_tag_push_package
|
||||
|
|
|
@ -99,6 +99,16 @@
|
|||
aggregation: weekly
|
||||
redis_slot: package
|
||||
feature_flag: collect_package_events_redis
|
||||
- name: i_package_rubygems_deploy_token
|
||||
category: deploy_token_packages
|
||||
aggregation: weekly
|
||||
redis_slot: package
|
||||
feature_flag: collect_package_events_redis
|
||||
- name: i_package_rubygems_user
|
||||
category: user_packages
|
||||
aggregation: weekly
|
||||
redis_slot: package
|
||||
feature_flag: collect_package_events_redis
|
||||
- name: i_package_tag_deploy_token
|
||||
category: deploy_token_packages
|
||||
aggregation: weekly
|
||||
|
|
|
@ -10772,6 +10772,9 @@ msgstr ""
|
|||
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
|
||||
msgstr ""
|
||||
|
||||
msgid "Drop or %{linkStart}upload%{linkEnd} file to attach"
|
||||
msgstr ""
|
||||
|
||||
msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -58,6 +58,7 @@
|
|||
"@gitlab/visual-review-tools": "1.6.1",
|
||||
"@rails/actioncable": "^6.0.3-4",
|
||||
"@rails/ujs": "^6.0.3-4",
|
||||
"@sentry/browser": "^5.22.3",
|
||||
"@sourcegraph/code-host-integration": "0.0.52",
|
||||
"@toast-ui/editor": "^2.5.1",
|
||||
"@toast-ui/vue-editor": "^2.5.1",
|
||||
|
@ -164,7 +165,7 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@babel/plugin-transform-modules-commonjs": "^7.10.1",
|
||||
"@gitlab/eslint-plugin": "8.0.0",
|
||||
"@gitlab/eslint-plugin": "8.1.0",
|
||||
"@gitlab/stylelint-config": "^2.2.0",
|
||||
"@testing-library/dom": "^7.16.2",
|
||||
"@vue/test-utils": "1.1.2",
|
||||
|
@ -177,17 +178,17 @@
|
|||
"commander": "^2.18.0",
|
||||
"custom-jquery-matchers": "^2.1.0",
|
||||
"docdash": "^1.0.2",
|
||||
"eslint": "7.19.0",
|
||||
"eslint": "7.20.0",
|
||||
"eslint-import-resolver-jest": "3.0.0",
|
||||
"eslint-import-resolver-webpack": "0.13.0",
|
||||
"eslint-plugin-jasmine": "4.1.2",
|
||||
"eslint-plugin-no-jquery": "2.5.0",
|
||||
"gettext-extractor": "^3.5.3",
|
||||
"gettext-extractor-vue": "^5.0.0",
|
||||
"glob": "^7.1.6",
|
||||
"istanbul-lib-coverage": "^3.0.0",
|
||||
"istanbul-lib-report": "^3.0.0",
|
||||
"istanbul-reports": "^3.0.0",
|
||||
"glob": "^7.1.6",
|
||||
"jasmine-core": "^2.9.0",
|
||||
"jasmine-diff": "^0.1.3",
|
||||
"jasmine-jquery": "^2.1.1",
|
||||
|
|
|
@ -54,7 +54,7 @@ module QA
|
|||
{
|
||||
file_path: '.gitlab-ci.yml',
|
||||
content: <<~YAML
|
||||
image: mcr.microsoft.com/dotnet/core/sdk:3.1
|
||||
image: mcr.microsoft.com/dotnet/sdk:5.0
|
||||
|
||||
stages:
|
||||
- deploy
|
||||
|
@ -102,7 +102,7 @@ module QA
|
|||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
@ -115,7 +115,7 @@ module QA
|
|||
{
|
||||
file_path: '.gitlab-ci.yml',
|
||||
content: <<~YAML
|
||||
image: mcr.microsoft.com/dotnet/core/sdk:3.1
|
||||
image: mcr.microsoft.com/dotnet/sdk:5.0
|
||||
|
||||
stages:
|
||||
- install
|
||||
|
|
|
@ -123,11 +123,7 @@ RSpec.describe RootController do
|
|||
expect(response).to render_template 'dashboard/projects/index'
|
||||
end
|
||||
|
||||
context 'when experiment is enabled' do
|
||||
before do
|
||||
stub_experiment_for_subject(customize_homepage: true)
|
||||
end
|
||||
|
||||
context 'when customize_homepage is enabled' do
|
||||
it 'renders the default dashboard' do
|
||||
get :index
|
||||
|
||||
|
@ -135,9 +131,9 @@ RSpec.describe RootController do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when experiment not enabled' do
|
||||
context 'when customize_homepage is not enabled' do
|
||||
before do
|
||||
stub_experiment(customize_homepage: false)
|
||||
stub_feature_flags(customize_homepage: false)
|
||||
end
|
||||
|
||||
it 'renders the default dashboard' do
|
||||
|
|
|
@ -12,7 +12,7 @@ RSpec.describe 'Sentry' do
|
|||
expect(has_requested_sentry).to eq(false)
|
||||
end
|
||||
|
||||
xit 'loads sentry if sentry is enabled' do
|
||||
it 'loads sentry if sentry is enabled' do
|
||||
stub_sentry_settings
|
||||
|
||||
visit new_user_session_path
|
||||
|
|
|
@ -122,7 +122,7 @@ RSpec.describe Packages::GroupPackagesFinder do
|
|||
end
|
||||
|
||||
context 'when there are processing packages' do
|
||||
let_it_be(:package4) { create(:nuget_package, project: project, name: Packages::Nuget::CreatePackageService::TEMPORARY_PACKAGE_NAME) }
|
||||
let_it_be(:package4) { create(:nuget_package, project: project, name: Packages::Nuget::TEMPORARY_PACKAGE_NAME) }
|
||||
|
||||
it { is_expected.to match_array([package1, package2]) }
|
||||
end
|
||||
|
|
|
@ -14,7 +14,7 @@ RSpec.describe ::Packages::PackageFinder do
|
|||
it { is_expected.to eq(maven_package) }
|
||||
|
||||
context 'processing packages' do
|
||||
let_it_be(:nuget_package) { create(:nuget_package, project: project, name: Packages::Nuget::CreatePackageService::TEMPORARY_PACKAGE_NAME) }
|
||||
let_it_be(:nuget_package) { create(:nuget_package, project: project, name: Packages::Nuget::TEMPORARY_PACKAGE_NAME) }
|
||||
let(:package_id) { nuget_package.id }
|
||||
|
||||
it 'are not returned' do
|
||||
|
|
|
@ -76,7 +76,7 @@ RSpec.describe ::Packages::PackagesFinder do
|
|||
end
|
||||
|
||||
context 'with processing packages' do
|
||||
let_it_be(:nuget_package) { create(:nuget_package, project: project, name: Packages::Nuget::CreatePackageService::TEMPORARY_PACKAGE_NAME) }
|
||||
let_it_be(:nuget_package) { create(:nuget_package, project: project, name: Packages::Nuget::TEMPORARY_PACKAGE_NAME) }
|
||||
|
||||
it { is_expected.to match_array([conan_package, maven_package]) }
|
||||
end
|
||||
|
|
|
@ -4,12 +4,12 @@ import {
|
|||
GlDeprecatedSkeletonLoading as GlSkeletonLoading,
|
||||
GlTable,
|
||||
} from '@gitlab/ui';
|
||||
import * as Sentry from '@sentry/browser';
|
||||
import { mount } from '@vue/test-utils';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import Clusters from '~/clusters_list/components/clusters.vue';
|
||||
import ClusterStore from '~/clusters_list/store';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import * as Sentry from '~/sentry/wrapper';
|
||||
import { apiData } from '../mock_data';
|
||||
|
||||
describe('Clusters', () => {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import * as Sentry from '@sentry/browser';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import testAction from 'helpers/vuex_action_helper';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
|
@ -7,7 +8,6 @@ import * as types from '~/clusters_list/store/mutation_types';
|
|||
import { deprecatedCreateFlash as flashError } from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import Poll from '~/lib/utils/poll';
|
||||
import * as Sentry from '~/sentry/wrapper';
|
||||
import { apiData } from '../mock_data';
|
||||
|
||||
jest.mock('~/flash.js');
|
||||
|
|
|
@ -275,24 +275,28 @@ describe('DiffsStoreUtils', () => {
|
|||
|
||||
describe('trimFirstCharOfLineContent', () => {
|
||||
it('trims the line when it starts with a space', () => {
|
||||
// eslint-disable-next-line import/no-deprecated
|
||||
expect(utils.trimFirstCharOfLineContent({ rich_text: ' diff' })).toEqual({
|
||||
rich_text: 'diff',
|
||||
});
|
||||
});
|
||||
|
||||
it('trims the line when it starts with a +', () => {
|
||||
// eslint-disable-next-line import/no-deprecated
|
||||
expect(utils.trimFirstCharOfLineContent({ rich_text: '+diff' })).toEqual({
|
||||
rich_text: 'diff',
|
||||
});
|
||||
});
|
||||
|
||||
it('trims the line when it starts with a -', () => {
|
||||
// eslint-disable-next-line import/no-deprecated
|
||||
expect(utils.trimFirstCharOfLineContent({ rich_text: '-diff' })).toEqual({
|
||||
rich_text: 'diff',
|
||||
});
|
||||
});
|
||||
|
||||
it('does not trims the line when it starts with a letter', () => {
|
||||
// eslint-disable-next-line import/no-deprecated
|
||||
expect(utils.trimFirstCharOfLineContent({ rich_text: 'diff' })).toEqual({
|
||||
rich_text: 'diff',
|
||||
});
|
||||
|
@ -303,12 +307,14 @@ describe('DiffsStoreUtils', () => {
|
|||
rich_text: ' diff',
|
||||
};
|
||||
|
||||
// eslint-disable-next-line import/no-deprecated
|
||||
utils.trimFirstCharOfLineContent(lineObj);
|
||||
|
||||
expect(lineObj).toEqual({ rich_text: ' diff' });
|
||||
});
|
||||
|
||||
it('handles a undefined or null parameter', () => {
|
||||
// eslint-disable-next-line import/no-deprecated
|
||||
expect(utils.trimFirstCharOfLineContent()).toEqual({});
|
||||
});
|
||||
});
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,5 +1,7 @@
|
|||
import { GlTab } from '@gitlab/ui';
|
||||
import { mount, createLocalVue } from '@vue/test-utils';
|
||||
import Vuex from 'vuex';
|
||||
import { stubComponent } from 'helpers/stub_component';
|
||||
import RepoTab from '~/ide/components/repo_tab.vue';
|
||||
import { createRouter } from '~/ide/ide_router';
|
||||
import { createStore } from '~/ide/stores';
|
||||
|
@ -8,16 +10,25 @@ import { file } from '../helpers';
|
|||
const localVue = createLocalVue();
|
||||
localVue.use(Vuex);
|
||||
|
||||
const GlTabStub = stubComponent(GlTab, {
|
||||
template: '<li><slot name="title" /></li>',
|
||||
});
|
||||
|
||||
describe('RepoTab', () => {
|
||||
let wrapper;
|
||||
let store;
|
||||
let router;
|
||||
|
||||
const findTab = () => wrapper.find(GlTabStub);
|
||||
|
||||
function createComponent(propsData) {
|
||||
wrapper = mount(RepoTab, {
|
||||
localVue,
|
||||
store,
|
||||
propsData,
|
||||
stubs: {
|
||||
GlTab: GlTabStub,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -55,7 +66,7 @@ describe('RepoTab', () => {
|
|||
|
||||
jest.spyOn(wrapper.vm, 'openPendingTab').mockImplementation(() => {});
|
||||
|
||||
await wrapper.trigger('click');
|
||||
await findTab().vm.$emit('click');
|
||||
|
||||
expect(wrapper.vm.openPendingTab).not.toHaveBeenCalled();
|
||||
});
|
||||
|
@ -67,7 +78,7 @@ describe('RepoTab', () => {
|
|||
|
||||
jest.spyOn(wrapper.vm, 'clickFile').mockImplementation(() => {});
|
||||
|
||||
wrapper.trigger('click');
|
||||
findTab().vm.$emit('click');
|
||||
|
||||
expect(wrapper.vm.clickFile).toHaveBeenCalledWith(wrapper.vm.tab);
|
||||
});
|
||||
|
@ -91,11 +102,11 @@ describe('RepoTab', () => {
|
|||
tab,
|
||||
});
|
||||
|
||||
await wrapper.trigger('mouseover');
|
||||
await findTab().vm.$emit('mouseover');
|
||||
|
||||
expect(wrapper.find('.file-modified').exists()).toBe(false);
|
||||
|
||||
await wrapper.trigger('mouseout');
|
||||
await findTab().vm.$emit('mouseout');
|
||||
|
||||
expect(wrapper.find('.file-modified').exists()).toBe(true);
|
||||
});
|
||||
|
|
|
@ -1,5 +1,15 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import { setHTMLFixture } from 'helpers/fixtures';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import {
|
||||
PIPELINES_DETAIL_LINK_DURATION,
|
||||
PIPELINES_DETAIL_LINKS_TOTAL,
|
||||
PIPELINES_DETAIL_LINKS_JOB_RATIO,
|
||||
} from '~/performance/constants';
|
||||
import * as perfUtils from '~/performance/utils';
|
||||
import * as sentryUtils from '~/pipelines/components/graph/utils';
|
||||
import * as Api from '~/pipelines/components/graph_shared/api';
|
||||
import LinksInner from '~/pipelines/components/graph_shared/links_inner.vue';
|
||||
import { createJobsHash } from '~/pipelines/utils';
|
||||
import {
|
||||
|
@ -18,7 +28,9 @@ describe('Links Inner component', () => {
|
|||
containerMeasurements: { width: 1019, height: 445 },
|
||||
pipelineId: 1,
|
||||
pipelineData: [],
|
||||
totalGroups: 10,
|
||||
};
|
||||
|
||||
let wrapper;
|
||||
|
||||
const createComponent = (props) => {
|
||||
|
@ -194,4 +206,141 @@ describe('Links Inner component', () => {
|
|||
expect(firstLink.classes(hoverColorClass)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('performance metrics', () => {
|
||||
let markAndMeasure;
|
||||
let reportToSentry;
|
||||
let reportPerformance;
|
||||
let mock;
|
||||
|
||||
beforeEach(() => {
|
||||
mock = new MockAdapter(axios);
|
||||
jest.spyOn(window, 'requestAnimationFrame').mockImplementation((cb) => cb());
|
||||
markAndMeasure = jest.spyOn(perfUtils, 'performanceMarkAndMeasure');
|
||||
reportToSentry = jest.spyOn(sentryUtils, 'reportToSentry');
|
||||
reportPerformance = jest.spyOn(Api, 'reportPerformance');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mock.restore();
|
||||
});
|
||||
|
||||
describe('with no metrics config object', () => {
|
||||
beforeEach(() => {
|
||||
setFixtures(pipelineData);
|
||||
createComponent({
|
||||
pipelineData: pipelineData.stages,
|
||||
});
|
||||
});
|
||||
|
||||
it('is not called', () => {
|
||||
expect(markAndMeasure).not.toHaveBeenCalled();
|
||||
expect(reportToSentry).not.toHaveBeenCalled();
|
||||
expect(reportPerformance).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('with metrics config set to false', () => {
|
||||
beforeEach(() => {
|
||||
setFixtures(pipelineData);
|
||||
createComponent({
|
||||
pipelineData: pipelineData.stages,
|
||||
metricsConfig: {
|
||||
collectMetrics: false,
|
||||
metricsPath: '/path/to/metrics',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('is not called', () => {
|
||||
expect(markAndMeasure).not.toHaveBeenCalled();
|
||||
expect(reportToSentry).not.toHaveBeenCalled();
|
||||
expect(reportPerformance).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('with no metrics path', () => {
|
||||
beforeEach(() => {
|
||||
setFixtures(pipelineData);
|
||||
createComponent({
|
||||
pipelineData: pipelineData.stages,
|
||||
metricsConfig: {
|
||||
collectMetrics: true,
|
||||
metricsPath: '',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('is not called', () => {
|
||||
expect(markAndMeasure).not.toHaveBeenCalled();
|
||||
expect(reportToSentry).not.toHaveBeenCalled();
|
||||
expect(reportPerformance).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('with metrics path and collect set to true', () => {
|
||||
const metricsPath = '/root/project/-/ci/prometheus_metrics/histograms.json';
|
||||
const duration = 0.0478;
|
||||
const numLinks = 1;
|
||||
const metricsData = {
|
||||
histograms: [
|
||||
{ name: PIPELINES_DETAIL_LINK_DURATION, value: duration },
|
||||
{ name: PIPELINES_DETAIL_LINKS_TOTAL, value: numLinks },
|
||||
{
|
||||
name: PIPELINES_DETAIL_LINKS_JOB_RATIO,
|
||||
value: numLinks / defaultProps.totalGroups,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
describe('when no duration is obtained', () => {
|
||||
beforeEach(() => {
|
||||
jest.spyOn(window.performance, 'getEntriesByName').mockImplementation(() => {
|
||||
return [];
|
||||
});
|
||||
|
||||
setFixtures(pipelineData);
|
||||
|
||||
createComponent({
|
||||
pipelineData: pipelineData.stages,
|
||||
metricsConfig: {
|
||||
collectMetrics: true,
|
||||
path: metricsPath,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('attempts to collect metrics', () => {
|
||||
expect(markAndMeasure).toHaveBeenCalled();
|
||||
expect(reportPerformance).not.toHaveBeenCalled();
|
||||
expect(reportToSentry).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('with duration and no error', () => {
|
||||
beforeEach(() => {
|
||||
jest.spyOn(window.performance, 'getEntriesByName').mockImplementation(() => {
|
||||
return [{ duration }];
|
||||
});
|
||||
|
||||
setFixtures(pipelineData);
|
||||
|
||||
createComponent({
|
||||
pipelineData: pipelineData.stages,
|
||||
metricsConfig: {
|
||||
collectMetrics: true,
|
||||
path: metricsPath,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('it calls reportPerformance with expected arguments', () => {
|
||||
expect(markAndMeasure).toHaveBeenCalled();
|
||||
expect(reportPerformance).toHaveBeenCalled();
|
||||
expect(reportPerformance).toHaveBeenCalledWith(metricsPath, metricsData);
|
||||
expect(reportToSentry).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import * as Sentry from '@sentry/browser';
|
||||
import SentryConfig from '~/sentry/sentry_config';
|
||||
import * as Sentry from '~/sentry/wrapper';
|
||||
|
||||
describe('SentryConfig', () => {
|
||||
describe('IGNORE_ERRORS', () => {
|
||||
|
|
|
@ -84,6 +84,15 @@ describe('Subscriptions', () => {
|
|||
spy.mockRestore();
|
||||
});
|
||||
|
||||
it('has visually hidden label', () => {
|
||||
wrapper = mountComponent();
|
||||
|
||||
expect(findToggleButton().props()).toMatchObject({
|
||||
label: 'Notifications',
|
||||
labelPosition: 'hidden',
|
||||
});
|
||||
});
|
||||
|
||||
describe('given project emails are disabled', () => {
|
||||
const subscribeDisabledDescription = 'Notifications have been disabled';
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ exports[`Upload dropzone component correctly overrides description and drop mess
|
|||
|
||||
<p
|
||||
class="gl-mb-0"
|
||||
data-testid="upload-text"
|
||||
>
|
||||
<span>
|
||||
Test %{linkStart}description%{linkEnd} message.
|
||||
|
@ -98,10 +99,15 @@ exports[`Upload dropzone component when dragging renders correct template when d
|
|||
|
||||
<p
|
||||
class="gl-mb-0"
|
||||
data-testid="upload-text"
|
||||
>
|
||||
<gl-sprintf-stub
|
||||
message="Drop or %{linkStart}upload%{linkEnd} files to attach"
|
||||
/>
|
||||
Drop or
|
||||
<gl-link-stub>
|
||||
|
||||
upload
|
||||
|
||||
</gl-link-stub>
|
||||
files to attach
|
||||
</p>
|
||||
</div>
|
||||
</button>
|
||||
|
@ -178,10 +184,15 @@ exports[`Upload dropzone component when dragging renders correct template when d
|
|||
|
||||
<p
|
||||
class="gl-mb-0"
|
||||
data-testid="upload-text"
|
||||
>
|
||||
<gl-sprintf-stub
|
||||
message="Drop or %{linkStart}upload%{linkEnd} files to attach"
|
||||
/>
|
||||
Drop or
|
||||
<gl-link-stub>
|
||||
|
||||
upload
|
||||
|
||||
</gl-link-stub>
|
||||
files to attach
|
||||
</p>
|
||||
</div>
|
||||
</button>
|
||||
|
@ -258,10 +269,15 @@ exports[`Upload dropzone component when dragging renders correct template when d
|
|||
|
||||
<p
|
||||
class="gl-mb-0"
|
||||
data-testid="upload-text"
|
||||
>
|
||||
<gl-sprintf-stub
|
||||
message="Drop or %{linkStart}upload%{linkEnd} files to attach"
|
||||
/>
|
||||
Drop or
|
||||
<gl-link-stub>
|
||||
|
||||
upload
|
||||
|
||||
</gl-link-stub>
|
||||
files to attach
|
||||
</p>
|
||||
</div>
|
||||
</button>
|
||||
|
@ -337,10 +353,15 @@ exports[`Upload dropzone component when dragging renders correct template when d
|
|||
|
||||
<p
|
||||
class="gl-mb-0"
|
||||
data-testid="upload-text"
|
||||
>
|
||||
<gl-sprintf-stub
|
||||
message="Drop or %{linkStart}upload%{linkEnd} files to attach"
|
||||
/>
|
||||
Drop or
|
||||
<gl-link-stub>
|
||||
|
||||
upload
|
||||
|
||||
</gl-link-stub>
|
||||
files to attach
|
||||
</p>
|
||||
</div>
|
||||
</button>
|
||||
|
@ -416,10 +437,15 @@ exports[`Upload dropzone component when dragging renders correct template when d
|
|||
|
||||
<p
|
||||
class="gl-mb-0"
|
||||
data-testid="upload-text"
|
||||
>
|
||||
<gl-sprintf-stub
|
||||
message="Drop or %{linkStart}upload%{linkEnd} files to attach"
|
||||
/>
|
||||
Drop or
|
||||
<gl-link-stub>
|
||||
|
||||
upload
|
||||
|
||||
</gl-link-stub>
|
||||
files to attach
|
||||
</p>
|
||||
</div>
|
||||
</button>
|
||||
|
@ -495,10 +521,15 @@ exports[`Upload dropzone component when no slot provided renders default dropzon
|
|||
|
||||
<p
|
||||
class="gl-mb-0"
|
||||
data-testid="upload-text"
|
||||
>
|
||||
<gl-sprintf-stub
|
||||
message="Drop or %{linkStart}upload%{linkEnd} files to attach"
|
||||
/>
|
||||
Drop or
|
||||
<gl-link-stub>
|
||||
|
||||
upload
|
||||
|
||||
</gl-link-stub>
|
||||
files to attach
|
||||
</p>
|
||||
</div>
|
||||
</button>
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue