Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
8ce82c1eaf
commit
e1189e4c3b
|
@ -7,3 +7,4 @@
|
||||||
/public/
|
/public/
|
||||||
/tmp/
|
/tmp/
|
||||||
/vendor/
|
/vendor/
|
||||||
|
/sitespeed-result/
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
/public/
|
/public/
|
||||||
/vendor/
|
/vendor/
|
||||||
/tmp/
|
/tmp/
|
||||||
|
/sitespeed-result/
|
||||||
|
|
||||||
# ignore stylesheets for now as this clashes with our linter
|
# ignore stylesheets for now as this clashes with our linter
|
||||||
*.css
|
*.css
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
d4ea957f6131538cd78e490a585ea3a455251064
|
40511f7a14ded77c826809d054d740a66e1c106f
|
||||||
|
|
|
@ -41,7 +41,7 @@ export default {
|
||||||
watch: {
|
watch: {
|
||||||
filterParams: {
|
filterParams: {
|
||||||
handler() {
|
handler() {
|
||||||
if (this.list.id) {
|
if (this.list.id && !this.list.collapsed) {
|
||||||
this.fetchItemsForList({ listId: this.list.id });
|
this.fetchItemsForList({ listId: this.list.id });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -240,7 +240,7 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
updateList: (
|
updateList: (
|
||||||
{ commit, state: { issuableType } },
|
{ commit, state: { issuableType, boardItemsByListId = {} }, dispatch },
|
||||||
{ listId, position, collapsed, backupList },
|
{ listId, position, collapsed, backupList },
|
||||||
) => {
|
) => {
|
||||||
gqlClient
|
gqlClient
|
||||||
|
@ -255,6 +255,12 @@ export default {
|
||||||
.then(({ data }) => {
|
.then(({ data }) => {
|
||||||
if (data?.updateBoardList?.errors.length) {
|
if (data?.updateBoardList?.errors.length) {
|
||||||
commit(types.UPDATE_LIST_FAILURE, backupList);
|
commit(types.UPDATE_LIST_FAILURE, backupList);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only fetch when board items havent been fetched on a collapsed list
|
||||||
|
if (!boardItemsByListId[listId]) {
|
||||||
|
dispatch('fetchItemsForList', { listId });
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
|
|
|
@ -1,10 +1,65 @@
|
||||||
import { Image } from '@tiptap/extension-image';
|
import { Image } from '@tiptap/extension-image';
|
||||||
import { defaultMarkdownSerializer } from 'prosemirror-markdown/src/to_markdown';
|
import { VueNodeViewRenderer } from '@tiptap/vue-2';
|
||||||
|
import { Plugin, PluginKey } from 'prosemirror-state';
|
||||||
|
import { __ } from '~/locale';
|
||||||
|
import ImageWrapper from '../components/wrappers/image.vue';
|
||||||
|
import { uploadFile } from '../services/upload_file';
|
||||||
|
import { getImageAlt, readFileAsDataURL } from '../services/utils';
|
||||||
|
|
||||||
|
export const acceptedMimes = ['image/jpeg', 'image/png', 'image/gif', 'image/jpg'];
|
||||||
|
|
||||||
|
const resolveImageEl = (element) =>
|
||||||
|
element.nodeName === 'IMG' ? element : element.querySelector('img');
|
||||||
|
|
||||||
|
const startFileUpload = async ({ editor, file, uploadsPath, renderMarkdown }) => {
|
||||||
|
const encodedSrc = await readFileAsDataURL(file);
|
||||||
|
const { view } = editor;
|
||||||
|
|
||||||
|
editor.commands.setImage({ uploading: true, src: encodedSrc });
|
||||||
|
|
||||||
|
const { state } = view;
|
||||||
|
const position = state.selection.from - 1;
|
||||||
|
const { tr } = state;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { src, canonicalSrc } = await uploadFile({ file, uploadsPath, renderMarkdown });
|
||||||
|
|
||||||
|
view.dispatch(
|
||||||
|
tr.setNodeMarkup(position, undefined, {
|
||||||
|
uploading: false,
|
||||||
|
src: encodedSrc,
|
||||||
|
alt: getImageAlt(src),
|
||||||
|
canonicalSrc,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
editor.commands.deleteRange({ from: position, to: position + 1 });
|
||||||
|
editor.emit('error', __('An error occurred while uploading the image. Please try again.'));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleFileEvent = ({ editor, file, uploadsPath, renderMarkdown }) => {
|
||||||
|
if (acceptedMimes.includes(file?.type)) {
|
||||||
|
startFileUpload({ editor, file, uploadsPath, renderMarkdown });
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
const ExtendedImage = Image.extend({
|
const ExtendedImage = Image.extend({
|
||||||
|
defaultOptions: {
|
||||||
|
...Image.options,
|
||||||
|
uploadsPath: null,
|
||||||
|
renderMarkdown: null,
|
||||||
|
},
|
||||||
addAttributes() {
|
addAttributes() {
|
||||||
return {
|
return {
|
||||||
...this.parent?.(),
|
...this.parent?.(),
|
||||||
|
uploading: {
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
src: {
|
src: {
|
||||||
default: null,
|
default: null,
|
||||||
/*
|
/*
|
||||||
|
@ -14,17 +69,25 @@ const ExtendedImage = Image.extend({
|
||||||
* attribute.
|
* attribute.
|
||||||
*/
|
*/
|
||||||
parseHTML: (element) => {
|
parseHTML: (element) => {
|
||||||
const img = element.querySelector('img');
|
const img = resolveImageEl(element);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
src: img.dataset.src || img.getAttribute('src'),
|
src: img.dataset.src || img.getAttribute('src'),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
canonicalSrc: {
|
||||||
|
default: null,
|
||||||
|
parseHTML: (element) => {
|
||||||
|
return {
|
||||||
|
canonicalSrc: element.dataset.canonicalSrc,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
alt: {
|
alt: {
|
||||||
default: null,
|
default: null,
|
||||||
parseHTML: (element) => {
|
parseHTML: (element) => {
|
||||||
const img = element.querySelector('img');
|
const img = resolveImageEl(element);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
alt: img.getAttribute('alt'),
|
alt: img.getAttribute('alt'),
|
||||||
|
@ -44,9 +107,58 @@ const ExtendedImage = Image.extend({
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
|
addCommands() {
|
||||||
|
return {
|
||||||
|
...this.parent(),
|
||||||
|
uploadImage: ({ file }) => () => {
|
||||||
|
const { uploadsPath, renderMarkdown } = this.options;
|
||||||
|
|
||||||
|
handleFileEvent({ file, uploadsPath, renderMarkdown, editor: this.editor });
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
addProseMirrorPlugins() {
|
||||||
|
const { editor } = this;
|
||||||
|
|
||||||
|
return [
|
||||||
|
new Plugin({
|
||||||
|
key: new PluginKey('handleDropAndPasteImages'),
|
||||||
|
props: {
|
||||||
|
handlePaste: (_, event) => {
|
||||||
|
const { uploadsPath, renderMarkdown } = this.options;
|
||||||
|
|
||||||
|
return handleFileEvent({
|
||||||
|
editor,
|
||||||
|
file: event.clipboardData.files[0],
|
||||||
|
uploadsPath,
|
||||||
|
renderMarkdown,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleDrop: (_, event) => {
|
||||||
|
const { uploadsPath, renderMarkdown } = this.options;
|
||||||
|
|
||||||
|
return handleFileEvent({
|
||||||
|
editor,
|
||||||
|
file: event.dataTransfer.files[0],
|
||||||
|
uploadsPath,
|
||||||
|
renderMarkdown,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
},
|
||||||
|
addNodeView() {
|
||||||
|
return VueNodeViewRenderer(ImageWrapper);
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const serializer = defaultMarkdownSerializer.nodes.image;
|
const serializer = (state, node) => {
|
||||||
|
const { alt, canonicalSrc, src, title } = node.attrs;
|
||||||
|
const quotedTitle = title ? ` ${state.quote(title)}` : '';
|
||||||
|
|
||||||
|
state.write(`![${state.esc(alt || '')}](${state.esc(canonicalSrc || src)}${quotedTitle})`);
|
||||||
|
};
|
||||||
|
|
||||||
export const configure = ({ renderMarkdown, uploadsPath }) => {
|
export const configure = ({ renderMarkdown, uploadsPath }) => {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -3,3 +3,15 @@ export const hasSelection = (tiptapEditor) => {
|
||||||
|
|
||||||
return from < to;
|
return from < to;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getImageAlt = (src) => {
|
||||||
|
return src.replace(/^.*\/|\..*$/g, '').replace(/\W+/g, ' ');
|
||||||
|
};
|
||||||
|
|
||||||
|
export const readFileAsDataURL = (file) => {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.addEventListener('load', (e) => resolve(e.target.result), { once: true });
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { GlTooltipDirective, GlIcon, GlLink, GlSafeHtmlDirective } from '@gitlab/ui';
|
import { GlTooltipDirective, GlIcon, GlLink, GlSafeHtmlDirective } from '@gitlab/ui';
|
||||||
import { ApolloMutation } from 'vue-apollo';
|
import { ApolloMutation } from 'vue-apollo';
|
||||||
|
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
|
||||||
import { __ } from '~/locale';
|
import { __ } from '~/locale';
|
||||||
import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
|
import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
|
||||||
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
|
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
|
||||||
|
@ -48,6 +49,9 @@ export default {
|
||||||
author() {
|
author() {
|
||||||
return this.note.author;
|
return this.note.author;
|
||||||
},
|
},
|
||||||
|
authorId() {
|
||||||
|
return getIdFromGraphQLId(this.author.id);
|
||||||
|
},
|
||||||
noteAnchorId() {
|
noteAnchorId() {
|
||||||
return findNoteId(this.note.id);
|
return findNoteId(this.note.id);
|
||||||
},
|
},
|
||||||
|
@ -94,7 +98,7 @@ export default {
|
||||||
v-once
|
v-once
|
||||||
:href="author.webUrl"
|
:href="author.webUrl"
|
||||||
class="js-user-link"
|
class="js-user-link"
|
||||||
:data-user-id="author.id"
|
:data-user-id="authorId"
|
||||||
:data-username="author.username"
|
:data-username="author.username"
|
||||||
>
|
>
|
||||||
<span class="note-header-author-name gl-font-weight-bold">{{ author.name }}</span>
|
<span class="note-header-author-name gl-font-weight-bold">{{ author.name }}</span>
|
||||||
|
|
|
@ -97,7 +97,7 @@ export default (resolvers = {}, config = {}) => {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const fetchIntervention = (url, options) => {
|
const fetchIntervention = (url, options) => {
|
||||||
return fetch(stripWhitespaceFromQuery(url, path), options);
|
return fetch(stripWhitespaceFromQuery(url, uri), options);
|
||||||
};
|
};
|
||||||
|
|
||||||
const requestLink = ApolloLink.split(
|
const requestLink = ApolloLink.split(
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { GlFilteredSearchToken } from '@gitlab/ui';
|
import { GlFilteredSearchToken } from '@gitlab/ui';
|
||||||
import { mapState } from 'vuex';
|
import { mapState } from 'vuex';
|
||||||
// eslint-disable-next-line import/no-deprecated
|
import { getParameterByName, setUrlParams, queryToObject } from '~/lib/utils/url_utility';
|
||||||
import { getParameterByName, setUrlParams, urlParamsToObject } from '~/lib/utils/url_utility';
|
|
||||||
import { s__ } from '~/locale';
|
import { s__ } from '~/locale';
|
||||||
import {
|
import {
|
||||||
SEARCH_TOKEN_TYPE,
|
SEARCH_TOKEN_TYPE,
|
||||||
|
@ -68,8 +67,7 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
// eslint-disable-next-line import/no-deprecated
|
const query = queryToObject(window.location.search);
|
||||||
const query = urlParamsToObject(window.location.search);
|
|
||||||
|
|
||||||
const tokens = this.tokens
|
const tokens = this.tokens
|
||||||
.filter((token) => query[token.type])
|
.filter((token) => query[token.type])
|
||||||
|
|
|
@ -66,6 +66,7 @@ export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
currentFilter: null,
|
currentFilter: null,
|
||||||
|
renderSkeleton: !this.shouldShow,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -93,7 +94,7 @@ export default {
|
||||||
return this.noteableData.noteableType;
|
return this.noteableData.noteableType;
|
||||||
},
|
},
|
||||||
allDiscussions() {
|
allDiscussions() {
|
||||||
if (this.isLoading) {
|
if (this.renderSkeleton || this.isLoading) {
|
||||||
const prerenderedNotesCount = parseInt(this.notesData.prerenderedNotesCount, 10) || 0;
|
const prerenderedNotesCount = parseInt(this.notesData.prerenderedNotesCount, 10) || 0;
|
||||||
|
|
||||||
return new Array(prerenderedNotesCount).fill({
|
return new Array(prerenderedNotesCount).fill({
|
||||||
|
@ -122,6 +123,10 @@ export default {
|
||||||
if (!this.isNotesFetched) {
|
if (!this.isNotesFetched) {
|
||||||
this.fetchNotes();
|
this.fetchNotes();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.renderSkeleton = !this.shouldShow;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
discussionTabCounterText(val) {
|
discussionTabCounterText(val) {
|
||||||
if (this.discussionsCount) {
|
if (this.discussionsCount) {
|
||||||
|
|
|
@ -74,6 +74,7 @@ const deriveProjectPathFromUrl = ($projectImportUrl) => {
|
||||||
const bindEvents = () => {
|
const bindEvents = () => {
|
||||||
const $newProjectForm = $('#new_project');
|
const $newProjectForm = $('#new_project');
|
||||||
const $projectImportUrl = $('#project_import_url');
|
const $projectImportUrl = $('#project_import_url');
|
||||||
|
const $projectImportUrlWarning = $('.js-import-url-warning');
|
||||||
const $projectPath = $('.tab-pane.active #project_path');
|
const $projectPath = $('.tab-pane.active #project_path');
|
||||||
const $useTemplateBtn = $('.template-button > input');
|
const $useTemplateBtn = $('.template-button > input');
|
||||||
const $projectFieldsForm = $('.project-fields-form');
|
const $projectFieldsForm = $('.project-fields-form');
|
||||||
|
@ -134,7 +135,25 @@ const bindEvents = () => {
|
||||||
$projectPath.val($projectPath.val().trim());
|
$projectPath.val($projectPath.val().trim());
|
||||||
});
|
});
|
||||||
|
|
||||||
$projectImportUrl.keyup(() => deriveProjectPathFromUrl($projectImportUrl));
|
function updateUrlPathWarningVisibility() {
|
||||||
|
const url = $projectImportUrl.val();
|
||||||
|
const URL_PATTERN = /(?:git|https?):\/\/.*\/.*\.git$/;
|
||||||
|
const isUrlValid = URL_PATTERN.test(url);
|
||||||
|
$projectImportUrlWarning.toggleClass('hide', isUrlValid);
|
||||||
|
}
|
||||||
|
|
||||||
|
let isProjectImportUrlDirty = false;
|
||||||
|
$projectImportUrl.on('blur', () => {
|
||||||
|
isProjectImportUrlDirty = true;
|
||||||
|
updateUrlPathWarningVisibility();
|
||||||
|
});
|
||||||
|
$projectImportUrl.on('keyup', () => {
|
||||||
|
deriveProjectPathFromUrl($projectImportUrl);
|
||||||
|
// defer error message till first input blur
|
||||||
|
if (isProjectImportUrlDirty) {
|
||||||
|
updateUrlPathWarningVisibility();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
$('.js-import-git-toggle-button').on('click', () => {
|
$('.js-import-git-toggle-button').on('click', () => {
|
||||||
const $projectMirror = $('#project_mirror');
|
const $projectMirror = $('#project_mirror');
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
<script>
|
||||||
|
import { GlIcon, GlLink } from '@gitlab/ui';
|
||||||
|
import { numberToHumanSize } from '~/lib/utils/number_utils';
|
||||||
|
import { sprintf, __ } from '~/locale';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
GlIcon,
|
||||||
|
GlLink,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
fileName: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
filePath: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
fileSize: {
|
||||||
|
type: Number,
|
||||||
|
required: false,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
downloadFileSize() {
|
||||||
|
return numberToHumanSize(this.fileSize);
|
||||||
|
},
|
||||||
|
downloadText() {
|
||||||
|
if (this.fileSize > 0) {
|
||||||
|
return sprintf(__('Download (%{fileSizeReadable})'), {
|
||||||
|
fileSizeReadable: this.downloadFileSize,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return __('Download');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="gl-text-center gl-py-13 gl-bg-gray-50">
|
||||||
|
<gl-link :href="filePath" rel="nofollow" :download="fileName" target="_blank">
|
||||||
|
<div>
|
||||||
|
<gl-icon :size="16" name="download" class="gl-text-gray-900" />
|
||||||
|
</div>
|
||||||
|
<h4>{{ downloadText }}</h4>
|
||||||
|
</gl-link>
|
||||||
|
</div>
|
||||||
|
</template>
|
|
@ -5,8 +5,7 @@ export const loadViewer = (type) => {
|
||||||
case 'text':
|
case 'text':
|
||||||
return () => import(/* webpackChunkName: 'blob_text_viewer' */ './text_viewer.vue');
|
return () => import(/* webpackChunkName: 'blob_text_viewer' */ './text_viewer.vue');
|
||||||
case 'download':
|
case 'download':
|
||||||
// TODO (follow-up): import the download viewer
|
return () => import(/* webpackChunkName: 'blob_download_viewer' */ './download_viewer.vue');
|
||||||
return null; // () => import(/* webpackChunkName: 'blob_download_viewer' */ './download_viewer.vue');
|
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -19,5 +18,10 @@ export const viewerProps = (type, blob) => {
|
||||||
fileName: blob.name,
|
fileName: blob.name,
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
},
|
},
|
||||||
|
download: {
|
||||||
|
fileName: blob.name,
|
||||||
|
filePath: blob.rawPath,
|
||||||
|
fileSize: blob.rawSize,
|
||||||
|
},
|
||||||
}[type];
|
}[type];
|
||||||
};
|
};
|
||||||
|
|
|
@ -87,6 +87,12 @@
|
||||||
padding-bottom: $gl-spacing-scale-8;
|
padding-bottom: $gl-spacing-scale-8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Will be moved to @gitlab/ui in https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1495
|
||||||
|
.gl-py-13 {
|
||||||
|
padding-top: $gl-spacing-scale-13;
|
||||||
|
padding-bottom: $gl-spacing-scale-13;
|
||||||
|
}
|
||||||
|
|
||||||
.gl-transition-property-stroke-opacity {
|
.gl-transition-property-stroke-opacity {
|
||||||
transition-property: stroke-opacity;
|
transition-property: stroke-opacity;
|
||||||
}
|
}
|
||||||
|
|
|
@ -325,7 +325,11 @@ module Ci
|
||||||
build.run_after_commit do
|
build.run_after_commit do
|
||||||
build.run_status_commit_hooks!
|
build.run_status_commit_hooks!
|
||||||
|
|
||||||
BuildFinishedWorker.perform_async(id)
|
if Feature.enabled?(:ci_build_finished_worker_namespace_changed, build.project, default_enabled: :yaml)
|
||||||
|
Ci::BuildFinishedWorker.perform_async(id)
|
||||||
|
else
|
||||||
|
::BuildFinishedWorker.perform_async(id)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -11,11 +11,48 @@ module Ci
|
||||||
scope :queued_before, ->(time) { where(arel_table[:created_at].lt(time)) }
|
scope :queued_before, ->(time) { where(arel_table[:created_at].lt(time)) }
|
||||||
|
|
||||||
def self.upsert_from_build!(build)
|
def self.upsert_from_build!(build)
|
||||||
entry = self.new(build: build, project: build.project, protected: build.protected?)
|
entry = self.new(args_from_build(build))
|
||||||
|
|
||||||
entry.validate!
|
entry.validate!
|
||||||
|
|
||||||
self.upsert(entry.attributes.compact, returning: %w[build_id], unique_by: :build_id)
|
self.upsert(entry.attributes.compact, returning: %w[build_id], unique_by: :build_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.args_from_build(build)
|
||||||
|
args = {
|
||||||
|
build: build,
|
||||||
|
project: build.project,
|
||||||
|
protected: build.protected?
|
||||||
|
}
|
||||||
|
|
||||||
|
if Feature.enabled?(:ci_pending_builds_maintain_shared_runners_data, type: :development, default_enabled: :yaml)
|
||||||
|
args.merge(instance_runners_enabled: shareable?(build))
|
||||||
|
else
|
||||||
|
args
|
||||||
|
end
|
||||||
|
end
|
||||||
|
private_class_method :args_from_build
|
||||||
|
|
||||||
|
def self.shareable?(build)
|
||||||
|
shared_runner_enabled?(build) &&
|
||||||
|
builds_access_level?(build) &&
|
||||||
|
project_not_removed?(build)
|
||||||
|
end
|
||||||
|
private_class_method :shareable?
|
||||||
|
|
||||||
|
def self.shared_runner_enabled?(build)
|
||||||
|
build.project.shared_runners.exists?
|
||||||
|
end
|
||||||
|
private_class_method :shared_runner_enabled?
|
||||||
|
|
||||||
|
def self.project_not_removed?(build)
|
||||||
|
!build.project.pending_delete?
|
||||||
|
end
|
||||||
|
private_class_method :project_not_removed?
|
||||||
|
|
||||||
|
def self.builds_access_level?(build)
|
||||||
|
build.project.project_feature.builds_access_level.nil? || build.project.project_feature.builds_access_level > 0
|
||||||
|
end
|
||||||
|
private_class_method :builds_access_level?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -224,7 +224,7 @@ module Ci
|
||||||
end
|
end
|
||||||
|
|
||||||
after_transition [:created, :waiting_for_resource, :preparing, :pending, :running] => :success do |pipeline|
|
after_transition [:created, :waiting_for_resource, :preparing, :pending, :running] => :success do |pipeline|
|
||||||
# We wait a little bit to ensure that all BuildFinishedWorkers finish first
|
# We wait a little bit to ensure that all Ci::BuildFinishedWorkers finish first
|
||||||
# because this is where some metrics like code coverage is parsed and stored
|
# because this is where some metrics like code coverage is parsed and stored
|
||||||
# in CI build records which the daily build metrics worker relies on.
|
# in CI build records which the daily build metrics worker relies on.
|
||||||
pipeline.run_after_commit { Ci::DailyBuildGroupReportResultsWorker.perform_in(10.minutes, pipeline.id) }
|
pipeline.run_after_commit { Ci::DailyBuildGroupReportResultsWorker.perform_in(10.minutes, pipeline.id) }
|
||||||
|
|
|
@ -10,12 +10,12 @@ module PartitionedTable
|
||||||
monthly: Gitlab::Database::Partitioning::MonthlyStrategy
|
monthly: Gitlab::Database::Partitioning::MonthlyStrategy
|
||||||
}.freeze
|
}.freeze
|
||||||
|
|
||||||
def partitioned_by(partitioning_key, strategy:)
|
def partitioned_by(partitioning_key, strategy:, **kwargs)
|
||||||
strategy_class = PARTITIONING_STRATEGIES[strategy.to_sym] || raise(ArgumentError, "Unknown partitioning strategy: #{strategy}")
|
strategy_class = PARTITIONING_STRATEGIES[strategy.to_sym] || raise(ArgumentError, "Unknown partitioning strategy: #{strategy}")
|
||||||
|
|
||||||
@partitioning_strategy = strategy_class.new(self, partitioning_key)
|
@partitioning_strategy = strategy_class.new(self, partitioning_key, **kwargs)
|
||||||
|
|
||||||
Gitlab::Database::Partitioning::PartitionCreator.register(self)
|
Gitlab::Database::Partitioning::PartitionManager.register(self)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -9,7 +9,7 @@ class WebHookLog < ApplicationRecord
|
||||||
|
|
||||||
self.primary_key = :id
|
self.primary_key = :id
|
||||||
|
|
||||||
partitioned_by :created_at, strategy: :monthly
|
partitioned_by :created_at, strategy: :monthly, retain_for: 3.months
|
||||||
|
|
||||||
belongs_to :web_hook
|
belongs_to :web_hook
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,7 @@ module Groups
|
||||||
def ensure_allowed_transfer
|
def ensure_allowed_transfer
|
||||||
raise_transfer_error(:group_is_already_root) if group_is_already_root?
|
raise_transfer_error(:group_is_already_root) if group_is_already_root?
|
||||||
raise_transfer_error(:same_parent_as_current) if same_parent?
|
raise_transfer_error(:same_parent_as_current) if same_parent?
|
||||||
|
raise_transfer_error(:has_subscription) if has_subscription?
|
||||||
raise_transfer_error(:invalid_policies) unless valid_policies?
|
raise_transfer_error(:invalid_policies) unless valid_policies?
|
||||||
raise_transfer_error(:namespace_with_same_path) if namespace_with_same_path?
|
raise_transfer_error(:namespace_with_same_path) if namespace_with_same_path?
|
||||||
raise_transfer_error(:group_contains_images) if group_projects_contain_registry_images?
|
raise_transfer_error(:group_contains_images) if group_projects_contain_registry_images?
|
||||||
|
@ -73,6 +74,10 @@ module Groups
|
||||||
@new_parent_group && @new_parent_group.id == @group.parent_id
|
@new_parent_group && @new_parent_group.id == @group.parent_id
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def has_subscription?
|
||||||
|
@group.paid?
|
||||||
|
end
|
||||||
|
|
||||||
def transfer_to_subgroup?
|
def transfer_to_subgroup?
|
||||||
@new_parent_group && \
|
@new_parent_group && \
|
||||||
@group.self_and_descendants.pluck_primary_key.include?(@new_parent_group.id)
|
@group.self_and_descendants.pluck_primary_key.include?(@new_parent_group.id)
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module ServicePing
|
||||||
|
class PermitDataCategoriesService
|
||||||
|
STANDARD_CATEGORY = 'Standard'
|
||||||
|
SUBSCRIPTION_CATEGORY = 'Subscription'
|
||||||
|
OPERATIONAL_CATEGORY = 'Operational'
|
||||||
|
OPTIONAL_CATEGORY = 'Optional'
|
||||||
|
CATEGORIES = [
|
||||||
|
STANDARD_CATEGORY,
|
||||||
|
SUBSCRIPTION_CATEGORY,
|
||||||
|
OPERATIONAL_CATEGORY,
|
||||||
|
OPTIONAL_CATEGORY
|
||||||
|
].to_set.freeze
|
||||||
|
|
||||||
|
def execute
|
||||||
|
return [] unless product_intelligence_enabled?
|
||||||
|
|
||||||
|
CATEGORIES
|
||||||
|
end
|
||||||
|
|
||||||
|
def product_intelligence_enabled?
|
||||||
|
pings_enabled? && !User.single_user&.requires_usage_stats_consent?
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def pings_enabled?
|
||||||
|
::Gitlab::CurrentSettings.usage_ping_enabled?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ServicePing::PermitDataCategoriesService.prepend_mod_with('ServicePing::PermitDataCategoriesService')
|
|
@ -18,8 +18,7 @@ module ServicePing
|
||||||
SubmissionError = Class.new(StandardError)
|
SubmissionError = Class.new(StandardError)
|
||||||
|
|
||||||
def execute
|
def execute
|
||||||
return unless Gitlab::CurrentSettings.usage_ping_enabled?
|
return unless ServicePing::PermitDataCategoriesService.new.product_intelligence_enabled?
|
||||||
return if User.single_user&.requires_usage_stats_consent?
|
|
||||||
|
|
||||||
usage_data = Gitlab::UsageData.data(force_refresh: true)
|
usage_data = Gitlab::UsageData.data(force_refresh: true)
|
||||||
|
|
||||||
|
|
|
@ -24,21 +24,6 @@
|
||||||
"data-bind-in" => "#{'create_chat_team' if Gitlab.config.mattermost.enabled}"
|
"data-bind-in" => "#{'create_chat_team' if Gitlab.config.mattermost.enabled}"
|
||||||
= f.submit s_('GroupSettings|Change group URL'), class: 'btn gl-button btn-warning'
|
= f.submit s_('GroupSettings|Change group URL'), class: 'btn gl-button btn-warning'
|
||||||
|
|
||||||
.sub-section
|
= render 'groups/settings/transfer', group: @group
|
||||||
%h4.warning-title= s_('GroupSettings|Transfer group')
|
|
||||||
= form_for @group, url: transfer_group_path(@group), method: :put, html: { class: 'js-group-transfer-form' } do |f|
|
|
||||||
.form-group
|
|
||||||
= dropdown_tag('Select parent group', options: { toggle_class: 'js-groups-dropdown', title: 'Parent Group', filter: true, dropdown_class: 'dropdown-open-top dropdown-group-transfer', placeholder: 'Search groups', data: { data: parent_group_options(@group), qa_selector: 'select_group_dropdown' } })
|
|
||||||
= hidden_field_tag 'new_parent_group_id'
|
|
||||||
|
|
||||||
%ul
|
|
||||||
- side_effects_link_start = '<a href="https://docs.gitlab.com/ee/user/project/index.html#redirects-when-changing-repository-paths" target="_blank">'.html_safe
|
|
||||||
- warning_text = s_("GroupSettings|Be careful. Changing a group's parent can have unintended %{side_effects_link_start}side effects%{side_effects_link_end}.") % { side_effects_link_start: side_effects_link_start, side_effects_link_end: '</a>'.html_safe }
|
|
||||||
%li= warning_text.html_safe
|
|
||||||
%li= s_('GroupSettings|You can only transfer the group to a group you manage.')
|
|
||||||
%li= s_('GroupSettings|You will need to update your local repositories to point to the new location.')
|
|
||||||
%li= s_("GroupSettings|If the parent group's visibility is lower than the group current visibility, visibility levels for subgroups and projects will be changed to match the new parent group's visibility.")
|
|
||||||
= f.submit s_('GroupSettings|Transfer group'), class: 'btn gl-button btn-warning', data: { qa_selector: "transfer_group_button" }
|
|
||||||
|
|
||||||
= render 'groups/settings/remove', group: @group
|
= render 'groups/settings/remove', group: @group
|
||||||
= render_if_exists 'groups/settings/restore', group: @group
|
= render_if_exists 'groups/settings/restore', group: @group
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
.sub-section
|
||||||
|
%h4.warning-title= s_('GroupSettings|Transfer group')
|
||||||
|
= form_for group, url: transfer_group_path(group), method: :put, html: { class: 'js-group-transfer-form' } do |f|
|
||||||
|
.form-group
|
||||||
|
= dropdown_tag('Select parent group', options: { toggle_class: 'js-groups-dropdown', title: 'Parent Group', filter: true, dropdown_class: 'dropdown-open-top dropdown-group-transfer', placeholder: 'Search groups', disabled: group.paid?, data: { data: parent_group_options(group), qa_selector: 'select_group_dropdown' } })
|
||||||
|
= hidden_field_tag 'new_parent_group_id'
|
||||||
|
|
||||||
|
%ul
|
||||||
|
- side_effects_link_start = '<a href="https://docs.gitlab.com/ee/user/project/index.html#redirects-when-changing-repository-paths" target="_blank">'.html_safe
|
||||||
|
- warning_text = s_("GroupSettings|Be careful. Changing a group's parent can have unintended %{side_effects_link_start}side effects%{side_effects_link_end}.") % { side_effects_link_start: side_effects_link_start, side_effects_link_end: '</a>'.html_safe }
|
||||||
|
%li= warning_text.html_safe
|
||||||
|
%li= s_('GroupSettings|You can only transfer the group to a group you manage.')
|
||||||
|
%li= s_('GroupSettings|You will need to update your local repositories to point to the new location.')
|
||||||
|
%li= s_("GroupSettings|If the parent group's visibility is lower than the group current visibility, visibility levels for subgroups and projects will be changed to match the new parent group's visibility.")
|
||||||
|
|
||||||
|
- if group.paid?
|
||||||
|
.gl-alert.gl-alert-info.gl-mb-5{ data: { testid: 'group-to-transfer-has-linked-subscription-alert' } }
|
||||||
|
= sprite_icon('information-o', size: 16, css_class: 'gl-icon gl-alert-icon gl-alert-icon-no-title')
|
||||||
|
.gl-alert-body
|
||||||
|
= html_escape(_("This group can't be transfered because it is linked to a subscription. To transfer this group, %{linkStart}link the subscription%{linkEnd} with a different group.")) % { linkStart: "<a href=\"#{help_page_path('subscriptions/index', anchor: 'change-the-linked-namespace')}\">".html_safe, linkEnd: '</a>'.html_safe }
|
||||||
|
|
||||||
|
= f.submit s_('GroupSettings|Transfer group'), class: 'btn gl-button btn-warning', data: { qa_selector: "transfer_group_button" }
|
|
@ -14,7 +14,7 @@
|
||||||
.row.mt-3
|
.row.mt-3
|
||||||
.col-sm-12
|
.col-sm-12
|
||||||
%h1.mb-3.font-weight-normal
|
%h1.mb-3.font-weight-normal
|
||||||
= current_appearance&.title.presence || "GitLab"
|
= current_appearance&.title.presence || _('GitLab')
|
||||||
.row.mb-3
|
.row.mb-3
|
||||||
.col-sm-7.order-12.order-sm-1.brand-holder
|
.col-sm-7.order-12.order-sm-1.brand-holder
|
||||||
- unless recently_confirmed_com?
|
- unless recently_confirmed_com?
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
= sprite_icon('document')
|
= sprite_icon('document')
|
||||||
%strong
|
%strong
|
||||||
= search_blob_title(project, path)
|
= search_blob_title(project, path)
|
||||||
|
= copy_file_path_button(path)
|
||||||
- if blob.data
|
- if blob.data
|
||||||
.file-content.code.term{ data: { qa_selector: 'file_text_content' } }
|
.file-content.code.term{ data: { qa_selector: 'file_text_content' } }
|
||||||
= render 'shared/file_highlight', blob: blob, first_line_number: blob.startline, blob_link: blob_link, highlight_line: blob.highlight_line
|
= render 'shared/file_highlight', blob: blob, first_line_number: blob.startline, blob_link: blob_link, highlight_line: blob.highlight_line
|
||||||
|
|
|
@ -8,7 +8,18 @@
|
||||||
= _('Git repository URL')
|
= _('Git repository URL')
|
||||||
= f.text_field :import_url, value: import_url.sanitized_url,
|
= f.text_field :import_url, value: import_url.sanitized_url,
|
||||||
autocomplete: 'off', class: 'form-control gl-form-input', placeholder: 'https://gitlab.company.com/group/project.git', required: true
|
autocomplete: 'off', class: 'form-control gl-form-input', placeholder: 'https://gitlab.company.com/group/project.git', required: true
|
||||||
|
= render 'shared/global_alert',
|
||||||
|
variant: :warning,
|
||||||
|
alert_class: 'gl-mt-3 js-import-url-warning hide',
|
||||||
|
dismissible: false,
|
||||||
|
close_button_class: 'js-close-2fa-enabled-success-alert' do
|
||||||
|
.gl-alert-body
|
||||||
|
= s_('Import|A repository URL usually ends in a .git suffix, although this is not required. Double check to make sure your repository URL is correct.')
|
||||||
|
|
||||||
|
.gl-alert.gl-alert-not-dismissible.gl-alert-warning.gl-mt-3.hide#project_import_url_warning
|
||||||
|
.gl-alert-container
|
||||||
|
= sprite_icon('warning-solid', css_class: 'gl-icon s16 gl-alert-icon gl-alert-icon-no-title')
|
||||||
|
.gl-alert-content{ role: 'alert' }
|
||||||
.row
|
.row
|
||||||
.form-group.col-md-6
|
.form-group.col-md-6
|
||||||
= f.label :import_url_user, class: 'label-bold' do
|
= f.label :import_url_user, class: 'label-bold' do
|
||||||
|
|
|
@ -247,6 +247,15 @@
|
||||||
:idempotent: true
|
:idempotent: true
|
||||||
:tags:
|
:tags:
|
||||||
- :exclude_from_kubernetes
|
- :exclude_from_kubernetes
|
||||||
|
- :name: cronjob:database_partition_management
|
||||||
|
:worker_name: Database::PartitionManagementWorker
|
||||||
|
:feature_category: :database
|
||||||
|
:has_external_dependencies:
|
||||||
|
:urgency: :low
|
||||||
|
:resource_boundary: :unknown
|
||||||
|
:weight: 1
|
||||||
|
:idempotent: true
|
||||||
|
:tags: []
|
||||||
- :name: cronjob:environments_auto_stop_cron
|
- :name: cronjob:environments_auto_stop_cron
|
||||||
:worker_name: Environments::AutoStopCronWorker
|
:worker_name: Environments::AutoStopCronWorker
|
||||||
:feature_category: :continuous_delivery
|
:feature_category: :continuous_delivery
|
||||||
|
@ -1365,6 +1374,15 @@
|
||||||
:weight: 1
|
:weight: 1
|
||||||
:idempotent:
|
:idempotent:
|
||||||
:tags: []
|
:tags: []
|
||||||
|
- :name: pipeline_background:ci_archive_trace
|
||||||
|
:worker_name: Ci::ArchiveTraceWorker
|
||||||
|
:feature_category: :continuous_integration
|
||||||
|
:has_external_dependencies:
|
||||||
|
:urgency: :low
|
||||||
|
:resource_boundary: :unknown
|
||||||
|
:weight: 1
|
||||||
|
:idempotent:
|
||||||
|
:tags: []
|
||||||
- :name: pipeline_background:ci_build_trace_chunk_flush
|
- :name: pipeline_background:ci_build_trace_chunk_flush
|
||||||
:worker_name: Ci::BuildTraceChunkFlushWorker
|
:worker_name: Ci::BuildTraceChunkFlushWorker
|
||||||
:feature_category: :continuous_integration
|
:feature_category: :continuous_integration
|
||||||
|
@ -1585,6 +1603,15 @@
|
||||||
:weight: 5
|
:weight: 5
|
||||||
:idempotent:
|
:idempotent:
|
||||||
:tags: []
|
:tags: []
|
||||||
|
- :name: pipeline_processing:ci_build_finished
|
||||||
|
:worker_name: Ci::BuildFinishedWorker
|
||||||
|
:feature_category: :continuous_integration
|
||||||
|
:has_external_dependencies:
|
||||||
|
:urgency: :high
|
||||||
|
:resource_boundary: :cpu
|
||||||
|
:weight: 5
|
||||||
|
:idempotent:
|
||||||
|
:tags: []
|
||||||
- :name: pipeline_processing:ci_build_prepare
|
- :name: pipeline_processing:ci_build_prepare
|
||||||
:worker_name: Ci::BuildPrepareWorker
|
:worker_name: Ci::BuildPrepareWorker
|
||||||
:feature_category: :continuous_integration
|
:feature_category: :continuous_integration
|
||||||
|
|
|
@ -1,16 +1,5 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class ArchiveTraceWorker # rubocop:disable Scalability/IdempotentWorker
|
class ArchiveTraceWorker < ::Ci::ArchiveTraceWorker # rubocop:disable Scalability/IdempotentWorker
|
||||||
include ApplicationWorker
|
# DEPRECATED: Not triggered since https://gitlab.com/gitlab-org/gitlab/-/merge_requests/64934/
|
||||||
|
|
||||||
sidekiq_options retry: 3
|
|
||||||
include PipelineBackgroundQueue
|
|
||||||
|
|
||||||
# rubocop: disable CodeReuse/ActiveRecord
|
|
||||||
def perform(job_id)
|
|
||||||
Ci::Build.without_archived_trace.find_by(id: job_id).try do |job|
|
|
||||||
Ci::ArchiveTraceService.new.execute(job, worker_name: self.class.name)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
# rubocop: enable CodeReuse/ActiveRecord
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,61 +1,9 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class BuildFinishedWorker # rubocop:disable Scalability/IdempotentWorker
|
class BuildFinishedWorker < ::Ci::BuildFinishedWorker # rubocop:disable Scalability/IdempotentWorker
|
||||||
include ApplicationWorker
|
# DEPRECATED: Not triggered since https://gitlab.com/gitlab-org/gitlab/-/merge_requests/64934/
|
||||||
|
|
||||||
sidekiq_options retry: 3
|
# We need to explicitly specify these settings. They aren't inheriting from the parent class.
|
||||||
include PipelineQueue
|
|
||||||
|
|
||||||
queue_namespace :pipeline_processing
|
|
||||||
urgency :high
|
urgency :high
|
||||||
worker_resource_boundary :cpu
|
worker_resource_boundary :cpu
|
||||||
|
|
||||||
ARCHIVE_TRACES_IN = 2.minutes.freeze
|
|
||||||
|
|
||||||
# rubocop: disable CodeReuse/ActiveRecord
|
|
||||||
def perform(build_id)
|
|
||||||
Ci::Build.find_by(id: build_id).try do |build|
|
|
||||||
process_build(build)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
# rubocop: enable CodeReuse/ActiveRecord
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
# Processes a single CI build that has finished.
|
|
||||||
#
|
|
||||||
# This logic resides in a separate method so that EE can extend it more
|
|
||||||
# easily.
|
|
||||||
#
|
|
||||||
# @param [Ci::Build] build The build to process.
|
|
||||||
def process_build(build)
|
|
||||||
# We execute these in sync to reduce IO.
|
|
||||||
build.parse_trace_sections!
|
|
||||||
build.update_coverage
|
|
||||||
Ci::BuildReportResultService.new.execute(build)
|
|
||||||
|
|
||||||
# We execute these async as these are independent operations.
|
|
||||||
BuildHooksWorker.perform_async(build.id)
|
|
||||||
ChatNotificationWorker.perform_async(build.id) if build.pipeline.chat?
|
|
||||||
|
|
||||||
if build.failed?
|
|
||||||
::Ci::MergeRequests::AddTodoWhenBuildFailsWorker.perform_async(build.id)
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# We want to delay sending a build trace to object storage operation to
|
|
||||||
# validate that this fixes a race condition between this and flushing live
|
|
||||||
# trace chunks and chunks being removed after consolidation and putting
|
|
||||||
# them into object storage archive.
|
|
||||||
#
|
|
||||||
# TODO This is temporary fix we should improve later, after we validate
|
|
||||||
# that this is indeed the culprit.
|
|
||||||
#
|
|
||||||
# See https://gitlab.com/gitlab-org/gitlab/-/issues/267112 for more
|
|
||||||
# details.
|
|
||||||
#
|
|
||||||
ArchiveTraceWorker.perform_in(ARCHIVE_TRACES_IN, build.id)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
BuildFinishedWorker.prepend_mod_with('BuildFinishedWorker')
|
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Ci
|
||||||
|
class ArchiveTraceWorker # rubocop:disable Scalability/IdempotentWorker
|
||||||
|
include ApplicationWorker
|
||||||
|
|
||||||
|
sidekiq_options retry: 3
|
||||||
|
include PipelineBackgroundQueue
|
||||||
|
|
||||||
|
# rubocop: disable CodeReuse/ActiveRecord
|
||||||
|
def perform(job_id)
|
||||||
|
Ci::Build.without_archived_trace.find_by(id: job_id).try do |job|
|
||||||
|
Ci::ArchiveTraceService.new.execute(job, worker_name: self.class.name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
# rubocop: enable CodeReuse/ActiveRecord
|
||||||
|
end
|
||||||
|
end
|
|
@ -12,7 +12,7 @@ module Ci
|
||||||
# rubocop: disable CodeReuse/ActiveRecord
|
# rubocop: disable CodeReuse/ActiveRecord
|
||||||
def perform
|
def perform
|
||||||
# Archive stale live traces which still resides in redis or database
|
# Archive stale live traces which still resides in redis or database
|
||||||
# This could happen when ArchiveTraceWorker sidekiq jobs were lost by receiving SIGKILL
|
# This could happen when Ci::ArchiveTraceWorker sidekiq jobs were lost by receiving SIGKILL
|
||||||
# More details in https://gitlab.com/gitlab-org/gitlab-foss/issues/36791
|
# More details in https://gitlab.com/gitlab-org/gitlab-foss/issues/36791
|
||||||
Ci::Build.with_stale_live_trace.find_each(batch_size: 100) do |build|
|
Ci::Build.with_stale_live_trace.find_each(batch_size: 100) do |build|
|
||||||
Ci::ArchiveTraceService.new.execute(build, worker_name: self.class.name)
|
Ci::ArchiveTraceService.new.execute(build, worker_name: self.class.name)
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Ci
|
||||||
|
class BuildFinishedWorker # rubocop:disable Scalability/IdempotentWorker
|
||||||
|
include ApplicationWorker
|
||||||
|
|
||||||
|
sidekiq_options retry: 3
|
||||||
|
include PipelineQueue
|
||||||
|
|
||||||
|
queue_namespace :pipeline_processing
|
||||||
|
urgency :high
|
||||||
|
worker_resource_boundary :cpu
|
||||||
|
|
||||||
|
ARCHIVE_TRACES_IN = 2.minutes.freeze
|
||||||
|
|
||||||
|
# rubocop: disable CodeReuse/ActiveRecord
|
||||||
|
def perform(build_id)
|
||||||
|
Ci::Build.find_by(id: build_id).try do |build|
|
||||||
|
process_build(build)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
# rubocop: enable CodeReuse/ActiveRecord
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# Processes a single CI build that has finished.
|
||||||
|
#
|
||||||
|
# This logic resides in a separate method so that EE can extend it more
|
||||||
|
# easily.
|
||||||
|
#
|
||||||
|
# @param [Ci::Build] build The build to process.
|
||||||
|
def process_build(build)
|
||||||
|
# We execute these in sync to reduce IO.
|
||||||
|
build.parse_trace_sections!
|
||||||
|
build.update_coverage
|
||||||
|
Ci::BuildReportResultService.new.execute(build)
|
||||||
|
|
||||||
|
# We execute these async as these are independent operations.
|
||||||
|
BuildHooksWorker.perform_async(build.id)
|
||||||
|
ChatNotificationWorker.perform_async(build.id) if build.pipeline.chat?
|
||||||
|
|
||||||
|
if build.failed?
|
||||||
|
::Ci::MergeRequests::AddTodoWhenBuildFailsWorker.perform_async(build.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# We want to delay sending a build trace to object storage operation to
|
||||||
|
# validate that this fixes a race condition between this and flushing live
|
||||||
|
# trace chunks and chunks being removed after consolidation and putting
|
||||||
|
# them into object storage archive.
|
||||||
|
#
|
||||||
|
# TODO This is temporary fix we should improve later, after we validate
|
||||||
|
# that this is indeed the culprit.
|
||||||
|
#
|
||||||
|
# See https://gitlab.com/gitlab-org/gitlab/-/issues/267112 for more
|
||||||
|
# details.
|
||||||
|
#
|
||||||
|
archive_trace_worker_class(build).perform_in(ARCHIVE_TRACES_IN, build.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
def archive_trace_worker_class(build)
|
||||||
|
if Feature.enabled?(:ci_build_finished_worker_namespace_changed, build.project, default_enabled: :yaml)
|
||||||
|
Ci::ArchiveTraceWorker
|
||||||
|
else
|
||||||
|
::ArchiveTraceWorker
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
Ci::BuildFinishedWorker.prepend_mod_with('Ci::BuildFinishedWorker')
|
|
@ -36,25 +36,13 @@ module Gitlab
|
||||||
|
|
||||||
importer_class.new(object, project, client).execute
|
importer_class.new(object, project, client).execute
|
||||||
|
|
||||||
increment_counters(project)
|
Gitlab::GithubImport::ObjectCounter.increment(project, object_type, :imported)
|
||||||
|
|
||||||
info(project.id, message: 'importer finished')
|
info(project.id, message: 'importer finished')
|
||||||
rescue StandardError => e
|
rescue StandardError => e
|
||||||
error(project.id, e, hash)
|
error(project.id, e, hash)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Counters incremented:
|
|
||||||
# - global (prometheus): for metrics in Grafana
|
|
||||||
# - project (redis): used in FinishImportWorker to report number of objects imported
|
|
||||||
def increment_counters(project)
|
|
||||||
counter.increment
|
|
||||||
Gitlab::GithubImport::ObjectCounter.increment(project, object_type, :imported)
|
|
||||||
end
|
|
||||||
|
|
||||||
def counter
|
|
||||||
@counter ||= Gitlab::Metrics.counter(counter_name, counter_description)
|
|
||||||
end
|
|
||||||
|
|
||||||
def object_type
|
def object_type
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
end
|
end
|
||||||
|
@ -70,16 +58,6 @@ module Gitlab
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns the name (as a Symbol) of the Prometheus counter.
|
|
||||||
def counter_name
|
|
||||||
raise NotImplementedError
|
|
||||||
end
|
|
||||||
|
|
||||||
# Returns the description (as a String) of the Prometheus counter.
|
|
||||||
def counter_description
|
|
||||||
raise NotImplementedError
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
attr_accessor :github_id
|
attr_accessor :github_id
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Database
|
||||||
|
class PartitionManagementWorker
|
||||||
|
include ApplicationWorker
|
||||||
|
|
||||||
|
sidekiq_options retry: 3
|
||||||
|
include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
|
||||||
|
|
||||||
|
feature_category :database
|
||||||
|
idempotent!
|
||||||
|
|
||||||
|
def perform
|
||||||
|
Gitlab::Database::Partitioning::PartitionManager.new.sync_partitions
|
||||||
|
ensure
|
||||||
|
Gitlab::Database::Partitioning::PartitionMonitoring.new.report_metrics
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -16,14 +16,6 @@ module Gitlab
|
||||||
def object_type
|
def object_type
|
||||||
:diff_note
|
:diff_note
|
||||||
end
|
end
|
||||||
|
|
||||||
def counter_name
|
|
||||||
:github_importer_imported_diff_notes
|
|
||||||
end
|
|
||||||
|
|
||||||
def counter_description
|
|
||||||
'The number of imported GitHub pull request review comments'
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -16,14 +16,6 @@ module Gitlab
|
||||||
def object_type
|
def object_type
|
||||||
:issue
|
:issue
|
||||||
end
|
end
|
||||||
|
|
||||||
def counter_name
|
|
||||||
:github_importer_imported_issues
|
|
||||||
end
|
|
||||||
|
|
||||||
def counter_description
|
|
||||||
'The number of imported GitHub issues'
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -16,14 +16,6 @@ module Gitlab
|
||||||
def object_type
|
def object_type
|
||||||
:lfs_object
|
:lfs_object
|
||||||
end
|
end
|
||||||
|
|
||||||
def counter_name
|
|
||||||
:github_importer_imported_lfs_objects
|
|
||||||
end
|
|
||||||
|
|
||||||
def counter_description
|
|
||||||
'The number of imported GitHub Lfs Objects'
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -16,14 +16,6 @@ module Gitlab
|
||||||
def object_type
|
def object_type
|
||||||
:note
|
:note
|
||||||
end
|
end
|
||||||
|
|
||||||
def counter_name
|
|
||||||
:github_importer_imported_notes
|
|
||||||
end
|
|
||||||
|
|
||||||
def counter_description
|
|
||||||
'The number of imported GitHub comments'
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -18,14 +18,6 @@ module Gitlab
|
||||||
def object_type
|
def object_type
|
||||||
:pull_request_merged_by
|
:pull_request_merged_by
|
||||||
end
|
end
|
||||||
|
|
||||||
def counter_name
|
|
||||||
:github_importer_imported_pull_requests_merged_by
|
|
||||||
end
|
|
||||||
|
|
||||||
def counter_description
|
|
||||||
'The number of imported GitHub pull requests merged by'
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -18,14 +18,6 @@ module Gitlab
|
||||||
def object_type
|
def object_type
|
||||||
:pull_request_review
|
:pull_request_review
|
||||||
end
|
end
|
||||||
|
|
||||||
def counter_name
|
|
||||||
:github_importer_imported_pull_request_reviews
|
|
||||||
end
|
|
||||||
|
|
||||||
def counter_description
|
|
||||||
'The number of imported GitHub pull request reviews'
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -16,14 +16,6 @@ module Gitlab
|
||||||
def object_type
|
def object_type
|
||||||
:pull_request
|
:pull_request
|
||||||
end
|
end
|
||||||
|
|
||||||
def counter_name
|
|
||||||
:github_importer_imported_pull_requests
|
|
||||||
end
|
|
||||||
|
|
||||||
def counter_description
|
|
||||||
'The number of imported GitHub pull requests'
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -10,8 +10,7 @@ class PartitionCreationWorker
|
||||||
idempotent!
|
idempotent!
|
||||||
|
|
||||||
def perform
|
def perform
|
||||||
Gitlab::Database::Partitioning::PartitionCreator.new.create_partitions
|
# This worker has been removed in favor of Database::PartitionManagementWorker
|
||||||
ensure
|
Database::PartitionManagementWorker.new.perform
|
||||||
Gitlab::Database::Partitioning::PartitionMonitoring.new.report_metrics
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
---
|
---
|
||||||
name: helm_packages
|
name: ci_build_finished_worker_namespace_changed
|
||||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/61014
|
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/64934
|
||||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/331693
|
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/335499
|
||||||
milestone: '14.0'
|
milestone: '14.1'
|
||||||
type: development
|
type: development
|
||||||
group: group::package
|
group: group::pipeline execution
|
||||||
default_enabled: false
|
default_enabled: false
|
|
@ -0,0 +1,8 @@
|
||||||
|
---
|
||||||
|
name: ci_pending_builds_maintain_shared_runners_data
|
||||||
|
introduced_by_url:
|
||||||
|
rollout_issue_url:
|
||||||
|
milestone: '14.1'
|
||||||
|
type: development
|
||||||
|
group: group::pipeline execution
|
||||||
|
default_enabled: false
|
|
@ -0,0 +1,8 @@
|
||||||
|
---
|
||||||
|
name: partition_pruning_dry_run
|
||||||
|
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/65093
|
||||||
|
rollout_issue_url:
|
||||||
|
milestone: '14.1'
|
||||||
|
type: development
|
||||||
|
group: group::database
|
||||||
|
default_enabled: false
|
|
@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59763
|
||||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/328700
|
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/328700
|
||||||
type: development
|
type: development
|
||||||
group: group::runner
|
group: group::runner
|
||||||
default_enabled: false
|
default_enabled: true
|
||||||
milestone: '13.12'
|
milestone: '13.12'
|
||||||
|
|
|
@ -540,9 +540,9 @@ Settings.cron_jobs['authorized_project_update_periodic_recalculate_worker']['job
|
||||||
Settings.cron_jobs['update_container_registry_info_worker'] ||= Settingslogic.new({})
|
Settings.cron_jobs['update_container_registry_info_worker'] ||= Settingslogic.new({})
|
||||||
Settings.cron_jobs['update_container_registry_info_worker']['cron'] ||= '0 0 * * *'
|
Settings.cron_jobs['update_container_registry_info_worker']['cron'] ||= '0 0 * * *'
|
||||||
Settings.cron_jobs['update_container_registry_info_worker']['job_class'] = 'UpdateContainerRegistryInfoWorker'
|
Settings.cron_jobs['update_container_registry_info_worker']['job_class'] = 'UpdateContainerRegistryInfoWorker'
|
||||||
Settings.cron_jobs['postgres_dynamic_partitions_creator'] ||= Settingslogic.new({})
|
Settings.cron_jobs['postgres_dynamic_partitions_manager'] ||= Settingslogic.new({})
|
||||||
Settings.cron_jobs['postgres_dynamic_partitions_creator']['cron'] ||= '21 */6 * * *'
|
Settings.cron_jobs['postgres_dynamic_partitions_manager']['cron'] ||= '21 */6 * * *'
|
||||||
Settings.cron_jobs['postgres_dynamic_partitions_creator']['job_class'] ||= 'PartitionCreationWorker'
|
Settings.cron_jobs['postgres_dynamic_partitions_manager']['job_class'] ||= 'Database::PartitionManagementWorker'
|
||||||
Settings.cron_jobs['ci_platform_metrics_update_cron_worker'] ||= Settingslogic.new({})
|
Settings.cron_jobs['ci_platform_metrics_update_cron_worker'] ||= Settingslogic.new({})
|
||||||
Settings.cron_jobs['ci_platform_metrics_update_cron_worker']['cron'] ||= '47 9 * * *'
|
Settings.cron_jobs['ci_platform_metrics_update_cron_worker']['cron'] ||= '47 9 * * *'
|
||||||
Settings.cron_jobs['ci_platform_metrics_update_cron_worker']['job_class'] = 'CiPlatformMetricsUpdateCronWorker'
|
Settings.cron_jobs['ci_platform_metrics_update_cron_worker']['job_class'] = 'CiPlatformMetricsUpdateCronWorker'
|
||||||
|
|
|
@ -3,15 +3,15 @@
|
||||||
# Make sure we have loaded partitioned models here
|
# Make sure we have loaded partitioned models here
|
||||||
# (even with eager loading disabled).
|
# (even with eager loading disabled).
|
||||||
|
|
||||||
Gitlab::Database::Partitioning::PartitionCreator.register(AuditEvent)
|
Gitlab::Database::Partitioning::PartitionManager.register(AuditEvent)
|
||||||
Gitlab::Database::Partitioning::PartitionCreator.register(WebHookLog)
|
Gitlab::Database::Partitioning::PartitionManager.register(WebHookLog)
|
||||||
|
|
||||||
if Gitlab.ee?
|
if Gitlab.ee?
|
||||||
Gitlab::Database::Partitioning::PartitionCreator.register(IncidentManagement::PendingEscalations::Alert)
|
Gitlab::Database::Partitioning::PartitionManager.register(IncidentManagement::PendingEscalations::Alert)
|
||||||
end
|
end
|
||||||
|
|
||||||
begin
|
begin
|
||||||
Gitlab::Database::Partitioning::PartitionCreator.new.create_partitions unless ENV['DISABLE_POSTGRES_PARTITION_CREATION_ON_STARTUP']
|
Gitlab::Database::Partitioning::PartitionManager.new.sync_partitions unless ENV['DISABLE_POSTGRES_PARTITION_CREATION_ON_STARTUP']
|
||||||
rescue ActiveRecord::ActiveRecordError, PG::Error
|
rescue ActiveRecord::ActiveRecordError, PG::Error
|
||||||
# ignore - happens when Rake tasks yet have to create a database, e.g. for testing
|
# ignore - happens when Rake tasks yet have to create a database, e.g. for testing
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
---
|
---
|
||||||
data_category: Optional
|
data_category: Optional
|
||||||
key_path: counts.ci_runners
|
key_path: counts.ci_runners
|
||||||
description: Total configured Runners in project
|
description: Total configured Runners of all types
|
||||||
product_section: ops
|
product_section: ops
|
||||||
product_stage: verify
|
product_stage: verify
|
||||||
product_group: group::pipeline execution
|
product_group: group::pipeline execution
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
data_category: Optional
|
data_category: Optional
|
||||||
key_path: counts.ci_runners_instance_type_active
|
key_path: counts.ci_runners_instance_type_active
|
||||||
name: "count_active_instance_ci_runners"
|
name: "count_active_instance_ci_runners"
|
||||||
description: Total active group Runners
|
description: Total active Shared (Instance) Runners
|
||||||
product_section: ops
|
product_section: ops
|
||||||
product_stage: verify
|
product_stage: verify
|
||||||
product_group: group::pipeline execution
|
product_group: group::pipeline execution
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
data_category: Optional
|
data_category: Optional
|
||||||
key_path: counts.ci_runners_group_type_active
|
key_path: counts.ci_runners_group_type_active
|
||||||
name: "count_active_group_ci_runners"
|
name: "count_active_group_ci_runners"
|
||||||
description: Total active instance Runners
|
description: Total active Group Runners
|
||||||
product_section: ops
|
product_section: ops
|
||||||
product_stage: verify
|
product_stage: verify
|
||||||
product_group: group::pipeline execution
|
product_group: group::pipeline execution
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
data_category: Optional
|
data_category: Optional
|
||||||
key_path: counts.ci_runners_project_type_active
|
key_path: counts.ci_runners_project_type_active
|
||||||
name: "count_active_project_ci_runners"
|
name: "count_active_project_ci_runners"
|
||||||
description: Total active project Runners
|
description: Total active Specific (Project) Runners
|
||||||
product_section: ops
|
product_section: ops
|
||||||
product_stage: verify
|
product_stage: verify
|
||||||
product_group: group::pipeline execution
|
product_group: group::pipeline execution
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
data_category: Optional
|
data_category: Optional
|
||||||
key_path: counts.ci_runners_online
|
key_path: counts.ci_runners_online
|
||||||
name: "counts_online_runners"
|
name: "counts_online_runners"
|
||||||
description: Total online Runners
|
description: Total online Runners of all types
|
||||||
product_section: ops
|
product_section: ops
|
||||||
product_stage: verify
|
product_stage: verify
|
||||||
product_group: group::pipeline execution
|
product_group: group::pipeline execution
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
data_category: Optional
|
data_category: Optional
|
||||||
key_path: counts.ci_runners_instance_type_active_online
|
key_path: counts.ci_runners_instance_type_active_online
|
||||||
name: "count_instance_active_online_ci_runners"
|
name: "count_instance_active_online_ci_runners"
|
||||||
description: Total active and online instance Runners
|
description: Total active and online Shared (Instance) Runners
|
||||||
product_section: ops
|
product_section: ops
|
||||||
product_stage: verify
|
product_stage: verify
|
||||||
product_group: group::pipeline execution
|
product_group: group::pipeline execution
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
data_category: Optional
|
data_category: Optional
|
||||||
key_path: counts.ci_runners_group_type_active_online
|
key_path: counts.ci_runners_group_type_active_online
|
||||||
name: "count_group_active_online_ci_runners"
|
name: "count_group_active_online_ci_runners"
|
||||||
description: Total active and online group Runners
|
description: Total active and online Group Runners
|
||||||
product_section: ops
|
product_section: ops
|
||||||
product_stage: verify
|
product_stage: verify
|
||||||
product_group: group::pipeline execution
|
product_group: group::pipeline execution
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
data_category: Optional
|
data_category: Optional
|
||||||
key_path: counts.ci_runners_project_type_active_online
|
key_path: counts.ci_runners_project_type_active_online
|
||||||
name: "count_project_active_online_ci_runners"
|
name: "count_project_active_online_ci_runners"
|
||||||
description: Total active and online project Runners
|
description: Total active and online Specific (Project) Runners
|
||||||
product_section: ops
|
product_section: ops
|
||||||
product_stage: verify
|
product_stage: verify
|
||||||
product_group: group::pipeline execution
|
product_group: group::pipeline execution
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": ["string", "null"],
|
||||||
|
"enum": ["Standard", "Subscription", "Operational", "Optional"]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
---
|
||||||
|
key_path: settings.collected_data_categories
|
||||||
|
name: collected_data_categories
|
||||||
|
description: List of collected data categories corresponding to instance settings
|
||||||
|
product_section: growth
|
||||||
|
product_stage: growth
|
||||||
|
product_group: group::product intelligence
|
||||||
|
product_category: collection
|
||||||
|
value_type: object
|
||||||
|
status: implemented
|
||||||
|
milestone: "14.1"
|
||||||
|
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/65336
|
||||||
|
time_frame: none
|
||||||
|
data_source: system
|
||||||
|
data_category: Standard
|
||||||
|
distribution:
|
||||||
|
- ce
|
||||||
|
- ee
|
||||||
|
tier:
|
||||||
|
- free
|
||||||
|
- premium
|
||||||
|
- ultimate
|
||||||
|
value_json_schema: 'config/metrics/objects_schemas/collected_data_categories_schema.json'
|
|
@ -0,0 +1,19 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class AddInstanceRunnersEnabledToCiPendingBuild < ActiveRecord::Migration[6.1]
|
||||||
|
include Gitlab::Database::MigrationHelpers
|
||||||
|
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
|
def up
|
||||||
|
with_lock_retries do
|
||||||
|
add_column :ci_pending_builds, :instance_runners_enabled, :boolean, null: false, default: false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
with_lock_retries do
|
||||||
|
remove_column :ci_pending_builds, :instance_runners_enabled
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1 @@
|
||||||
|
9ba27b5e2599262846a06736db72fb0d31dc904e2ef4d167c1ee9530feb6367f
|
|
@ -10833,7 +10833,8 @@ CREATE TABLE ci_pending_builds (
|
||||||
build_id bigint NOT NULL,
|
build_id bigint NOT NULL,
|
||||||
project_id bigint NOT NULL,
|
project_id bigint NOT NULL,
|
||||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||||
protected boolean DEFAULT false NOT NULL
|
protected boolean DEFAULT false NOT NULL,
|
||||||
|
instance_runners_enabled boolean DEFAULT false NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE SEQUENCE ci_pending_builds_id_seq
|
CREATE SEQUENCE ci_pending_builds_id_seq
|
||||||
|
|
|
@ -26,6 +26,7 @@ The Package Registry supports the following formats:
|
||||||
<tr><td><a href="https://docs.gitlab.com/ee/user/packages/nuget_repository/index.html">NuGet</a></td><td>12.8+</td></tr>
|
<tr><td><a href="https://docs.gitlab.com/ee/user/packages/nuget_repository/index.html">NuGet</a></td><td>12.8+</td></tr>
|
||||||
<tr><td><a href="https://docs.gitlab.com/ee/user/packages/pypi_repository/index.html">PyPI</a></td><td>12.10+</td></tr>
|
<tr><td><a href="https://docs.gitlab.com/ee/user/packages/pypi_repository/index.html">PyPI</a></td><td>12.10+</td></tr>
|
||||||
<tr><td><a href="https://docs.gitlab.com/ee/user/packages/generic_packages/index.html">Generic packages</a></td><td>13.5+</td></tr>
|
<tr><td><a href="https://docs.gitlab.com/ee/user/packages/generic_packages/index.html">Generic packages</a></td><td>13.5+</td></tr>
|
||||||
|
<tr><td><a href="https://docs.gitlab.com/ee/user/packages/helm_repository/index.html">Helm Charts</a></td><td>14.1+</td></tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -26,7 +26,7 @@ GET /projects/:id/packages
|
||||||
| `id` | integer/string | yes | ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) |
|
| `id` | integer/string | yes | ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) |
|
||||||
| `order_by`| string | no | The field to use as order. One of `created_at` (default), `name`, `version`, or `type`. |
|
| `order_by`| string | no | The field to use as order. One of `created_at` (default), `name`, `version`, or `type`. |
|
||||||
| `sort` | string | no | The direction of the order, either `asc` (default) for ascending order or `desc` for descending order. |
|
| `sort` | string | no | The direction of the order, either `asc` (default) for ascending order or `desc` for descending order. |
|
||||||
| `package_type` | string | no | Filter the returned packages by type. One of `conan`, `maven`, `npm`, `pypi`, `composer`, `nuget`, or `golang`. (_Introduced in GitLab 12.9_)
|
| `package_type` | string | no | Filter the returned packages by type. One of `conan`, `maven`, `npm`, `pypi`, `composer`, `nuget`, `helm`, or `golang`. (_Introduced in GitLab 12.9_)
|
||||||
| `package_name` | string | no | Filter the project packages with a fuzzy search by name. (_Introduced in GitLab 12.9_)
|
| `package_name` | string | no | Filter the project packages with a fuzzy search by name. (_Introduced in GitLab 12.9_)
|
||||||
| `include_versionless` | boolean | no | When set to true, versionless packages are included in the response. (_Introduced in GitLab 13.8_)
|
| `include_versionless` | boolean | no | When set to true, versionless packages are included in the response. (_Introduced in GitLab 13.8_)
|
||||||
| `status` | string | no | Filter the returned packages by status. One of `default` (default), `hidden`, or `processing`. (_Introduced in GitLab 13.9_)
|
| `status` | string | no | Filter the returned packages by status. One of `default` (default), `hidden`, or `processing`. (_Introduced in GitLab 13.9_)
|
||||||
|
@ -91,7 +91,7 @@ GET /groups/:id/packages
|
||||||
| `exclude_subgroups` | boolean | false | If the parameter is included as true, packages from projects from subgroups are not listed. Default is `false`. |
|
| `exclude_subgroups` | boolean | false | If the parameter is included as true, packages from projects from subgroups are not listed. Default is `false`. |
|
||||||
| `order_by`| string | no | The field to use as order. One of `created_at` (default), `name`, `version`, `type`, or `project_path`. |
|
| `order_by`| string | no | The field to use as order. One of `created_at` (default), `name`, `version`, `type`, or `project_path`. |
|
||||||
| `sort` | string | no | The direction of the order, either `asc` (default) for ascending order or `desc` for descending order. |
|
| `sort` | string | no | The direction of the order, either `asc` (default) for ascending order or `desc` for descending order. |
|
||||||
| `package_type` | string | no | Filter the returned packages by type. One of `conan`, `maven`, `npm`, `pypi`, `composer`, `nuget`, or `golang`. (_Introduced in GitLab 12.9_) |
|
| `package_type` | string | no | Filter the returned packages by type. One of `conan`, `maven`, `npm`, `pypi`, `composer`, `nuget`, `helm`, or `golang`. (_Introduced in GitLab 12.9_) |
|
||||||
| `package_name` | string | no | Filter the project packages with a fuzzy search by name. (_[Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/30980) in GitLab 13.0_)
|
| `package_name` | string | no | Filter the project packages with a fuzzy search by name. (_[Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/30980) in GitLab 13.0_)
|
||||||
| `include_versionless` | boolean | no | When set to true, versionless packages are included in the response. (_Introduced in GitLab 13.8_)
|
| `include_versionless` | boolean | no | When set to true, versionless packages are included in the response. (_Introduced in GitLab 13.8_)
|
||||||
| `status` | string | no | Filter the returned packages by status. One of `default` (default), `hidden`, or `processing`. (_Introduced in GitLab 13.9_)
|
| `status` | string | no | Filter the returned packages by status. One of `default` (default), `hidden`, or `processing`. (_Introduced in GitLab 13.9_)
|
||||||
|
|
|
@ -22,23 +22,6 @@ These endpoints do not adhere to the standard API authentication methods.
|
||||||
See the [Helm registry documentation](../../user/packages/helm_repository/index.md)
|
See the [Helm registry documentation](../../user/packages/helm_repository/index.md)
|
||||||
for details on which headers and token types are supported.
|
for details on which headers and token types are supported.
|
||||||
|
|
||||||
## Enable the Helm API
|
|
||||||
|
|
||||||
The Helm API for GitLab is behind a feature flag that is disabled by default. GitLab
|
|
||||||
administrators with access to the GitLab Rails console can enable this API for your instance.
|
|
||||||
|
|
||||||
To enable it:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
Feature.enable(:helm_packages)
|
|
||||||
```
|
|
||||||
|
|
||||||
To disable it:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
Feature.disable(:helm_packages)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Download a chart index
|
## Download a chart index
|
||||||
|
|
||||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/62757) in GitLab 14.1.
|
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/62757) in GitLab 14.1.
|
||||||
|
|
|
@ -38,6 +38,7 @@ Example response:
|
||||||
{
|
{
|
||||||
"conan_max_file_size": 3221225472,
|
"conan_max_file_size": 3221225472,
|
||||||
"generic_packages_max_file_size": 5368709120,
|
"generic_packages_max_file_size": 5368709120,
|
||||||
|
"helm_max_file_size": 5242880,
|
||||||
"maven_max_file_size": 3221225472,
|
"maven_max_file_size": 3221225472,
|
||||||
"npm_max_file_size": 524288000,
|
"npm_max_file_size": 524288000,
|
||||||
"nuget_max_file_size": 524288000,
|
"nuget_max_file_size": 524288000,
|
||||||
|
@ -59,6 +60,7 @@ PUT /application/plan_limits
|
||||||
| `plan_name` | string | yes | Name of the plan to update. |
|
| `plan_name` | string | yes | Name of the plan to update. |
|
||||||
| `conan_max_file_size` | integer | no | Maximum Conan package file size in bytes. |
|
| `conan_max_file_size` | integer | no | Maximum Conan package file size in bytes. |
|
||||||
| `generic_packages_max_file_size` | integer | no | Maximum generic package file size in bytes. |
|
| `generic_packages_max_file_size` | integer | no | Maximum generic package file size in bytes. |
|
||||||
|
| `helm_max_file_size` | integer | no | Maximum Helm chart file size in bytes. |
|
||||||
| `maven_max_file_size` | integer | no | Maximum Maven package file size in bytes. |
|
| `maven_max_file_size` | integer | no | Maximum Maven package file size in bytes. |
|
||||||
| `npm_max_file_size` | integer | no | Maximum NPM package file size in bytes. |
|
| `npm_max_file_size` | integer | no | Maximum NPM package file size in bytes. |
|
||||||
| `nuget_max_file_size` | integer | no | Maximum NuGet package file size in bytes. |
|
| `nuget_max_file_size` | integer | no | Maximum NuGet package file size in bytes. |
|
||||||
|
@ -75,6 +77,7 @@ Example response:
|
||||||
{
|
{
|
||||||
"conan_max_file_size": 3221225472,
|
"conan_max_file_size": 3221225472,
|
||||||
"generic_packages_max_file_size": 5368709120,
|
"generic_packages_max_file_size": 5368709120,
|
||||||
|
"helm_max_file_size": 5242880,
|
||||||
"maven_max_file_size": 3221225472,
|
"maven_max_file_size": 3221225472,
|
||||||
"npm_max_file_size": 524288000,
|
"npm_max_file_size": 524288000,
|
||||||
"nuget_max_file_size": 524288000,
|
"nuget_max_file_size": 524288000,
|
||||||
|
|
|
@ -62,6 +62,9 @@ The following guides provide a quick introduction and links to follow on more ad
|
||||||
- Guide on [understanding EXPLAIN plans](../understanding_explain_plans.md).
|
- Guide on [understanding EXPLAIN plans](../understanding_explain_plans.md).
|
||||||
- [Explaining the unexplainable series in `depesz`](https://www.depesz.com/tag/unexplainable/).
|
- [Explaining the unexplainable series in `depesz`](https://www.depesz.com/tag/unexplainable/).
|
||||||
|
|
||||||
|
We also have licensed access to The Art of PostgreSQL available, if you are interested in getting access please check out the
|
||||||
|
[issue (confidential)](https://gitlab.com/gitlab-org/database-team/team-tasks/-/issues/23).
|
||||||
|
|
||||||
Finally, you can find various guides in the [Database guides](index.md) page that cover more specific
|
Finally, you can find various guides in the [Database guides](index.md) page that cover more specific
|
||||||
topics and use cases. The most frequently required during database reviewing are the following:
|
topics and use cases. The most frequently required during database reviewing are the following:
|
||||||
|
|
||||||
|
|
|
@ -151,7 +151,7 @@ test its execution using `CREATE INDEX CONCURRENTLY` in the `#database-lab` Slac
|
||||||
- Provide a public link to the plan from either:
|
- Provide a public link to the plan from either:
|
||||||
- [postgres.ai](https://postgres.ai/): Follow the link in `#database-lab` and generate a shareable, public link
|
- [postgres.ai](https://postgres.ai/): Follow the link in `#database-lab` and generate a shareable, public link
|
||||||
by clicking the **Share** button in the upper right corner.
|
by clicking the **Share** button in the upper right corner.
|
||||||
- [explain.depesz.com](https://explain.depesz.com): Paste both the plan and the query used in the form.
|
- [explain.depesz.com](https://explain.depesz.com) or [explain.dalibo.com](https://explain.dalibo.com): Paste both the plan and the query used in the form.
|
||||||
- When providing query plans, make sure it hits enough data:
|
- When providing query plans, make sure it hits enough data:
|
||||||
- You can use a GitLab production replica to test your queries on a large scale,
|
- You can use a GitLab production replica to test your queries on a large scale,
|
||||||
through the `#database-lab` Slack channel or through [ChatOps](understanding_explain_plans.md#chatops).
|
through the `#database-lab` Slack channel or through [ChatOps](understanding_explain_plans.md#chatops).
|
||||||
|
|
|
@ -513,6 +513,11 @@ expect(experiment(:example)).to track(:my_event, value: 1, property: '_property_
|
||||||
experiment(:example, :variant_name, foo: :bar).track(:my_event, value: 1, property: '_property_')
|
experiment(:example, :variant_name, foo: :bar).track(:my_event, value: 1, property: '_property_')
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Recording and assignment tracking
|
||||||
|
|
||||||
|
To test assignment tracking and the `record!` method, you can use or adopt the following
|
||||||
|
shared example: [tracks assignment and records the subject](https://gitlab.com/gitlab-org/gitlab/blob/master/spec/support/shared_examples/lib/gitlab/experimentation_shared_examples.rb).
|
||||||
|
|
||||||
## Experiments in the client layer
|
## Experiments in the client layer
|
||||||
|
|
||||||
This is in flux as of GitLab 13.10, and can't be documented just yet.
|
This is in flux as of GitLab 13.10, and can't be documented just yet.
|
||||||
|
|
|
@ -825,3 +825,5 @@ For more information about the available options, run:
|
||||||
A more extensive guide on understanding query plans can be found in
|
A more extensive guide on understanding query plans can be found in
|
||||||
the [presentation](https://public.dalibo.com/exports/conferences/_archives/_2012/201211_explain/understanding_explain.pdf)
|
the [presentation](https://public.dalibo.com/exports/conferences/_archives/_2012/201211_explain/understanding_explain.pdf)
|
||||||
from [Dalibo.org](https://www.dalibo.com/en/).
|
from [Dalibo.org](https://www.dalibo.com/en/).
|
||||||
|
|
||||||
|
Depesz's blog also has a good [section](https://www.depesz.com/tag/unexplainable) dedicated to query plans.
|
||||||
|
|
|
@ -666,7 +666,7 @@ Tiers: `free`, `premium`, `ultimate`
|
||||||
|
|
||||||
### `counts.ci_runners`
|
### `counts.ci_runners`
|
||||||
|
|
||||||
Total configured Runners in project
|
Total configured Runners of all types
|
||||||
|
|
||||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210216175520_ci_runners.yml)
|
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210216175520_ci_runners.yml)
|
||||||
|
|
||||||
|
@ -680,7 +680,7 @@ Tiers: `free`, `premium`, `ultimate`
|
||||||
|
|
||||||
### `counts.ci_runners_group_type_active`
|
### `counts.ci_runners_group_type_active`
|
||||||
|
|
||||||
Total active instance Runners
|
Total active Group Runners
|
||||||
|
|
||||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210502050341_ci_runners_group_type_active.yml)
|
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210502050341_ci_runners_group_type_active.yml)
|
||||||
|
|
||||||
|
@ -694,7 +694,7 @@ Tiers: `free`, `premium`, `ultimate`
|
||||||
|
|
||||||
### `counts.ci_runners_group_type_active_online`
|
### `counts.ci_runners_group_type_active_online`
|
||||||
|
|
||||||
Total active and online group Runners
|
Total active and online Group Runners
|
||||||
|
|
||||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210502051922_ci_runners_group_type_active_online.yml)
|
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210502051922_ci_runners_group_type_active_online.yml)
|
||||||
|
|
||||||
|
@ -708,7 +708,7 @@ Tiers: `free`, `premium`, `ultimate`
|
||||||
|
|
||||||
### `counts.ci_runners_instance_type_active`
|
### `counts.ci_runners_instance_type_active`
|
||||||
|
|
||||||
Total active group Runners
|
Total active Shared (Instance) Runners
|
||||||
|
|
||||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210502045402_ci_runners_instance_type_active.yml)
|
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210502045402_ci_runners_instance_type_active.yml)
|
||||||
|
|
||||||
|
@ -722,7 +722,7 @@ Tiers: `free`, `premium`, `ultimate`
|
||||||
|
|
||||||
### `counts.ci_runners_instance_type_active_online`
|
### `counts.ci_runners_instance_type_active_online`
|
||||||
|
|
||||||
Total active and online instance Runners
|
Total active and online Shared (Instance) Runners
|
||||||
|
|
||||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210502051651_ci_runners_instance_type_active_online.yml)
|
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210502051651_ci_runners_instance_type_active_online.yml)
|
||||||
|
|
||||||
|
@ -736,7 +736,7 @@ Tiers: `free`, `premium`, `ultimate`
|
||||||
|
|
||||||
### `counts.ci_runners_online`
|
### `counts.ci_runners_online`
|
||||||
|
|
||||||
Total online Runners
|
Total online Runners of all types
|
||||||
|
|
||||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210502050942_ci_runners_online.yml)
|
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210502050942_ci_runners_online.yml)
|
||||||
|
|
||||||
|
@ -750,7 +750,7 @@ Tiers: `free`, `premium`, `ultimate`
|
||||||
|
|
||||||
### `counts.ci_runners_project_type_active`
|
### `counts.ci_runners_project_type_active`
|
||||||
|
|
||||||
Total active project Runners
|
Total active Specific (Project) Runners
|
||||||
|
|
||||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210502050834_ci_runners_project_type_active.yml)
|
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210502050834_ci_runners_project_type_active.yml)
|
||||||
|
|
||||||
|
@ -764,7 +764,7 @@ Tiers: `free`, `premium`, `ultimate`
|
||||||
|
|
||||||
### `counts.ci_runners_project_type_active_online`
|
### `counts.ci_runners_project_type_active_online`
|
||||||
|
|
||||||
Total active and online project Runners
|
Total active and online Specific (Project) Runners
|
||||||
|
|
||||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210502052036_ci_runners_project_type_active_online.yml)
|
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210502052036_ci_runners_project_type_active_online.yml)
|
||||||
|
|
||||||
|
@ -18316,6 +18316,22 @@ Status: `data_available`
|
||||||
|
|
||||||
Tiers: `free`, `premium`, `ultimate`
|
Tiers: `free`, `premium`, `ultimate`
|
||||||
|
|
||||||
|
### `settings.collected_data_categories`
|
||||||
|
|
||||||
|
List of collected data categories corresponding to instance settings
|
||||||
|
|
||||||
|
[Object JSON schema](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/objects_schemas/collected_data_categories_schema.json)
|
||||||
|
|
||||||
|
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/settings/20210702140138_collected_data_categories.yml)
|
||||||
|
|
||||||
|
Group: `group::product intelligence`
|
||||||
|
|
||||||
|
Data Category: `Standard`
|
||||||
|
|
||||||
|
Status: `implemented`
|
||||||
|
|
||||||
|
Tiers: `free`, `premium`, `ultimate`
|
||||||
|
|
||||||
### `settings.gitaly_apdex`
|
### `settings.gitaly_apdex`
|
||||||
|
|
||||||
Gitaly application performance
|
Gitaly application performance
|
||||||
|
|
|
@ -275,7 +275,7 @@ Post-processing is currently limited to a project's default branch, see the abov
|
||||||
sequenceDiagram
|
sequenceDiagram
|
||||||
autonumber
|
autonumber
|
||||||
Rails->>+Sidekiq: gl-secret-detection-report.json
|
Rails->>+Sidekiq: gl-secret-detection-report.json
|
||||||
Sidekiq-->+Sidekiq: BuildFinishedWorker
|
Sidekiq-->+Sidekiq: Ci::BuildFinishedWorker
|
||||||
Sidekiq-->+RevocationAPI: GET revocable keys types
|
Sidekiq-->+RevocationAPI: GET revocable keys types
|
||||||
RevocationAPI-->>-Sidekiq: OK
|
RevocationAPI-->>-Sidekiq: OK
|
||||||
Sidekiq->>+RevocationAPI: POST revoke revocable keys
|
Sidekiq->>+RevocationAPI: POST revoke revocable keys
|
||||||
|
|
|
@ -18,24 +18,6 @@ packages whenever you need to use them as a dependency.
|
||||||
For documentation of the specific API endpoints that Helm package manager
|
For documentation of the specific API endpoints that Helm package manager
|
||||||
clients use, see the [Helm API documentation](../../../api/packages/helm.md).
|
clients use, see the [Helm API documentation](../../../api/packages/helm.md).
|
||||||
|
|
||||||
## Enable the Helm repository feature
|
|
||||||
|
|
||||||
Helm repository support is still a work in progress. It's gated behind a feature flag that's
|
|
||||||
**disabled by default**. [GitLab administrators with access to the GitLab Rails console](../../../administration/feature_flags.md)
|
|
||||||
can opt to enable it.
|
|
||||||
|
|
||||||
To enable it:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
Feature.enable(:helm_packages)
|
|
||||||
```
|
|
||||||
|
|
||||||
To disable it:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
Feature.disable(:helm_packages)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Build a Helm package
|
## Build a Helm package
|
||||||
|
|
||||||
Creating a Helm package is documented [in the Helm documentation](https://helm.sh/docs/intro/using_helm/#creating-your-own-charts).
|
Creating a Helm package is documented [in the Helm documentation](https://helm.sh/docs/intro/using_helm/#creating-your-own-charts).
|
||||||
|
@ -73,8 +55,16 @@ Once built, a chart can be uploaded to the `stable` channel with `curl` or `helm
|
||||||
To install the latest version of a chart, use the following command:
|
To install the latest version of a chart, use the following command:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
helm repo add project-1 https://gitlab.example.com/api/v4/projects/1/packages/helm/stable
|
helm repo add --username <username> --password <personal_access_token> project-1 https://gitlab.example.com/api/v4/projects/1/packages/helm/stable
|
||||||
helm install my-release project-1/mychart
|
helm install my-release project-1/mychart
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If the repo has previously been added, you may need to run:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
helm repo update
|
||||||
|
```
|
||||||
|
|
||||||
|
To update the Helm client with the most currently available charts.
|
||||||
|
|
||||||
See [Using Helm](https://helm.sh/docs/intro/using_helm/) for more information.
|
See [Using Helm](https://helm.sh/docs/intro/using_helm/) for more information.
|
||||||
|
|
|
@ -35,7 +35,7 @@ For information on how to create and upload a package, view the GitLab documenta
|
||||||
## Use GitLab CI/CD to build packages
|
## Use GitLab CI/CD to build packages
|
||||||
|
|
||||||
You can use [GitLab CI/CD](../../../ci/index.md) to build packages.
|
You can use [GitLab CI/CD](../../../ci/index.md) to build packages.
|
||||||
For Maven, NuGet, npm, Conan, and PyPI packages, and Composer dependencies, you can
|
For Maven, NuGet, npm, Conan, Helm, and PyPI packages, and Composer dependencies, you can
|
||||||
authenticate with GitLab by using the `CI_JOB_TOKEN`.
|
authenticate with GitLab by using the `CI_JOB_TOKEN`.
|
||||||
|
|
||||||
CI/CD templates, which you can use to get started, are in [this repository](https://gitlab.com/gitlab-org/gitlab/-/tree/master/lib/gitlab/ci/templates).
|
CI/CD templates, which you can use to get started, are in [this repository](https://gitlab.com/gitlab-org/gitlab/-/tree/master/lib/gitlab/ci/templates).
|
||||||
|
|
|
@ -29,10 +29,6 @@ module API
|
||||||
require_packages_enabled!
|
require_packages_enabled!
|
||||||
end
|
end
|
||||||
|
|
||||||
after_validation do
|
|
||||||
not_found! unless Feature.enabled?(:helm_packages, authorized_user_project)
|
|
||||||
end
|
|
||||||
|
|
||||||
params do
|
params do
|
||||||
requires :id, type: String, desc: 'The ID or full path of a project'
|
requires :id, type: String, desc: 'The ID or full path of a project'
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,16 +4,17 @@ module Gitlab
|
||||||
module Database
|
module Database
|
||||||
module Partitioning
|
module Partitioning
|
||||||
class MonthlyStrategy
|
class MonthlyStrategy
|
||||||
attr_reader :model, :partitioning_key
|
attr_reader :model, :partitioning_key, :retain_for
|
||||||
|
|
||||||
# We create this many partitions in the future
|
# We create this many partitions in the future
|
||||||
HEADROOM = 6.months
|
HEADROOM = 6.months
|
||||||
|
|
||||||
delegate :table_name, to: :model
|
delegate :table_name, to: :model
|
||||||
|
|
||||||
def initialize(model, partitioning_key)
|
def initialize(model, partitioning_key, retain_for: nil)
|
||||||
@model = model
|
@model = model
|
||||||
@partitioning_key = partitioning_key
|
@partitioning_key = partitioning_key
|
||||||
|
@retain_for = retain_for
|
||||||
end
|
end
|
||||||
|
|
||||||
def current_partitions
|
def current_partitions
|
||||||
|
@ -27,13 +28,21 @@ module Gitlab
|
||||||
desired_partitions - current_partitions
|
desired_partitions - current_partitions
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def extra_partitions
|
||||||
|
current_partitions - desired_partitions
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def desired_partitions
|
def desired_partitions
|
||||||
[].tap do |parts|
|
[].tap do |parts|
|
||||||
min_date, max_date = relevant_range
|
min_date, max_date = relevant_range
|
||||||
|
|
||||||
parts << partition_for(upper_bound: min_date)
|
if pruning_old_partitions? && min_date <= oldest_active_date
|
||||||
|
min_date = oldest_active_date.beginning_of_month
|
||||||
|
else
|
||||||
|
parts << partition_for(upper_bound: min_date)
|
||||||
|
end
|
||||||
|
|
||||||
while min_date < max_date
|
while min_date < max_date
|
||||||
next_date = min_date.next_month
|
next_date = min_date.next_month
|
||||||
|
@ -52,13 +61,17 @@ module Gitlab
|
||||||
# to start from MINVALUE to a specific date `x`. The range returned
|
# to start from MINVALUE to a specific date `x`. The range returned
|
||||||
# does not include the range of the first, half-unbounded partition.
|
# does not include the range of the first, half-unbounded partition.
|
||||||
def relevant_range
|
def relevant_range
|
||||||
if first_partition = current_partitions.min
|
if (first_partition = current_partitions.min)
|
||||||
# Case 1: First partition starts with MINVALUE, i.e. from is nil -> start with first real partition
|
# Case 1: First partition starts with MINVALUE, i.e. from is nil -> start with first real partition
|
||||||
# Case 2: Rather unexpectedly, first partition does not start with MINVALUE, i.e. from is not nil
|
# Case 2: Rather unexpectedly, first partition does not start with MINVALUE, i.e. from is not nil
|
||||||
# In this case, use first partition beginning as a start
|
# In this case, use first partition beginning as a start
|
||||||
min_date = first_partition.from || first_partition.to
|
min_date = first_partition.from || first_partition.to
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if pruning_old_partitions?
|
||||||
|
min_date ||= oldest_active_date
|
||||||
|
end
|
||||||
|
|
||||||
# In case we don't have a partition yet
|
# In case we don't have a partition yet
|
||||||
min_date ||= Date.today
|
min_date ||= Date.today
|
||||||
min_date = min_date.beginning_of_month
|
min_date = min_date.beginning_of_month
|
||||||
|
@ -72,6 +85,14 @@ module Gitlab
|
||||||
TimePartition.new(table_name, lower_bound, upper_bound)
|
TimePartition.new(table_name, lower_bound, upper_bound)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def pruning_old_partitions?
|
||||||
|
Feature.enabled?(:partition_pruning_dry_run) && retain_for.present?
|
||||||
|
end
|
||||||
|
|
||||||
|
def oldest_active_date
|
||||||
|
(Date.today - retain_for).beginning_of_month
|
||||||
|
end
|
||||||
|
|
||||||
def connection
|
def connection
|
||||||
ActiveRecord::Base.connection
|
ActiveRecord::Base.connection
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
module Gitlab
|
module Gitlab
|
||||||
module Database
|
module Database
|
||||||
module Partitioning
|
module Partitioning
|
||||||
class PartitionCreator
|
class PartitionManager
|
||||||
def self.register(model)
|
def self.register(model)
|
||||||
raise ArgumentError, "Only models with a #partitioning_strategy can be registered." unless model.respond_to?(:partitioning_strategy)
|
raise ArgumentError, "Only models with a #partitioning_strategy can be registered." unless model.respond_to?(:partitioning_strategy)
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ module Gitlab
|
||||||
end
|
end
|
||||||
|
|
||||||
LEASE_TIMEOUT = 1.minute
|
LEASE_TIMEOUT = 1.minute
|
||||||
LEASE_KEY = 'database_partition_creation_%s'
|
MANAGEMENT_LEASE_KEY = 'database_partition_management_%s'
|
||||||
|
|
||||||
attr_reader :models
|
attr_reader :models
|
||||||
|
|
||||||
|
@ -23,23 +23,25 @@ module Gitlab
|
||||||
@models = models
|
@models = models
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_partitions
|
def sync_partitions
|
||||||
Gitlab::AppLogger.info("Checking state of dynamic postgres partitions")
|
Gitlab::AppLogger.info("Checking state of dynamic postgres partitions")
|
||||||
|
|
||||||
models.each do |model|
|
models.each do |model|
|
||||||
# Double-checking before getting the lease:
|
# Double-checking before getting the lease:
|
||||||
# The prevailing situation is no missing partitions
|
# The prevailing situation is no missing partitions and no extra partitions
|
||||||
next if missing_partitions(model).empty?
|
next if missing_partitions(model).empty? && extra_partitions(model).empty?
|
||||||
|
|
||||||
only_with_exclusive_lease(model) do
|
only_with_exclusive_lease(model, lease_key: MANAGEMENT_LEASE_KEY) do
|
||||||
partitions_to_create = missing_partitions(model)
|
partitions_to_create = missing_partitions(model)
|
||||||
|
create(partitions_to_create) unless partitions_to_create.empty?
|
||||||
|
|
||||||
next if partitions_to_create.empty?
|
if Feature.enabled?(:partition_pruning_dry_run)
|
||||||
|
partitions_to_detach = extra_partitions(model)
|
||||||
create(model, partitions_to_create)
|
detach(partitions_to_detach) unless partitions_to_detach.empty?
|
||||||
|
end
|
||||||
end
|
end
|
||||||
rescue StandardError => e
|
rescue StandardError => e
|
||||||
Gitlab::AppLogger.error("Failed to create partition(s) for #{model.table_name}: #{e.class}: #{e.message}")
|
Gitlab::AppLogger.error("Failed to create / detach partition(s) for #{model.table_name}: #{e.class}: #{e.message}")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -51,15 +53,22 @@ module Gitlab
|
||||||
model.partitioning_strategy.missing_partitions
|
model.partitioning_strategy.missing_partitions
|
||||||
end
|
end
|
||||||
|
|
||||||
def only_with_exclusive_lease(model)
|
def extra_partitions(model)
|
||||||
lease = Gitlab::ExclusiveLease.new(LEASE_KEY % model.table_name, timeout: LEASE_TIMEOUT)
|
return [] unless Feature.enabled?(:partition_pruning_dry_run)
|
||||||
|
return [] unless connection.table_exists?(model.table_name)
|
||||||
|
|
||||||
|
model.partitioning_strategy.extra_partitions
|
||||||
|
end
|
||||||
|
|
||||||
|
def only_with_exclusive_lease(model, lease_key:)
|
||||||
|
lease = Gitlab::ExclusiveLease.new(lease_key % model.table_name, timeout: LEASE_TIMEOUT)
|
||||||
|
|
||||||
yield if lease.try_obtain
|
yield if lease.try_obtain
|
||||||
ensure
|
ensure
|
||||||
lease&.cancel
|
lease&.cancel
|
||||||
end
|
end
|
||||||
|
|
||||||
def create(model, partitions)
|
def create(partitions)
|
||||||
connection.transaction do
|
connection.transaction do
|
||||||
with_lock_retries do
|
with_lock_retries do
|
||||||
partitions.each do |partition|
|
partitions.each do |partition|
|
||||||
|
@ -71,6 +80,18 @@ module Gitlab
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def detach(partitions)
|
||||||
|
connection.transaction do
|
||||||
|
with_lock_retries do
|
||||||
|
partitions.each { |p| detach_one_partition(p) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def detach_one_partition(partition)
|
||||||
|
Gitlab::AppLogger.info("Planning to detach #{partition.partition_name} for table #{partition.table}")
|
||||||
|
end
|
||||||
|
|
||||||
def with_lock_retries(&block)
|
def with_lock_retries(&block)
|
||||||
Gitlab::Database::WithLockRetries.new(
|
Gitlab::Database::WithLockRetries.new(
|
||||||
klass: self.class,
|
klass: self.class,
|
|
@ -6,7 +6,7 @@ module Gitlab
|
||||||
class PartitionMonitoring
|
class PartitionMonitoring
|
||||||
attr_reader :models
|
attr_reader :models
|
||||||
|
|
||||||
def initialize(models = PartitionCreator.models)
|
def initialize(models = PartitionManager.models)
|
||||||
@models = models
|
@models = models
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,10 @@ module Gitlab
|
||||||
where('NOT EXISTS (?)', recent_actions)
|
where('NOT EXISTS (?)', recent_actions)
|
||||||
end
|
end
|
||||||
|
|
||||||
alias_method :reset, :reload
|
def reset
|
||||||
|
reload # rubocop:disable Cop/ActiveRecordAssociationReload
|
||||||
|
clear_memoization(:bloat_size)
|
||||||
|
end
|
||||||
|
|
||||||
def bloat_size
|
def bloat_size
|
||||||
strong_memoize(:bloat_size) { bloat_estimate&.bloat_size || 0 }
|
strong_memoize(:bloat_size) { bloat_estimate&.bloat_size || 0 }
|
||||||
|
|
|
@ -1,24 +1,24 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
# Count objects fetched or imported from Github in the context of the
|
# Count objects fetched or imported from Github.
|
||||||
# project being imported.
|
|
||||||
module Gitlab
|
module Gitlab
|
||||||
module GithubImport
|
module GithubImport
|
||||||
class ObjectCounter
|
class ObjectCounter
|
||||||
OPERATIONS = %w[fetched imported].freeze
|
OPERATIONS = %w[fetched imported].freeze
|
||||||
COUNTER_LIST_KEY = 'github-importer/object-counters-list/%{project}/%{operation}'
|
PROJECT_COUNTER_LIST_KEY = 'github-importer/object-counters-list/%{project}/%{operation}'
|
||||||
COUNTER_KEY = 'github-importer/object-counter/%{project}/%{operation}/%{object_type}'
|
PROJECT_COUNTER_KEY = 'github-importer/object-counter/%{project}/%{operation}/%{object_type}'
|
||||||
|
|
||||||
|
GLOBAL_COUNTER_KEY = 'github_importer_%{operation}_%{object_type}'
|
||||||
|
GLOBAL_COUNTER_DESCRIPTION = 'The number of %{operation} Github %{object_type}'
|
||||||
|
|
||||||
CACHING = Gitlab::Cache::Import::Caching
|
CACHING = Gitlab::Cache::Import::Caching
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
def increment(project, object_type, operation)
|
def increment(project, object_type, operation)
|
||||||
validate_operation!(operation)
|
validate_operation!(operation)
|
||||||
|
|
||||||
counter_key = COUNTER_KEY % { project: project.id, operation: operation, object_type: object_type }
|
increment_project_counter(project, object_type, operation)
|
||||||
|
increment_global_counter(object_type, operation)
|
||||||
add_counter_to_list(project, operation, counter_key)
|
|
||||||
|
|
||||||
CACHING.increment(counter_key)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def summary(project)
|
def summary(project)
|
||||||
|
@ -37,12 +37,40 @@ module Gitlab
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
# Global counters are long lived, in Prometheus,
|
||||||
|
# and it's used to report the health of the Github Importer
|
||||||
|
# in the Grafana Dashboard
|
||||||
|
# https://dashboards.gitlab.net/d/2zgM_rImz/github-importer?orgId=1
|
||||||
|
def increment_global_counter(object_type, operation)
|
||||||
|
key = GLOBAL_COUNTER_KEY % {
|
||||||
|
operation: operation,
|
||||||
|
object_type: object_type
|
||||||
|
}
|
||||||
|
description = GLOBAL_COUNTER_DESCRIPTION % {
|
||||||
|
operation: operation,
|
||||||
|
object_type: object_type.to_s.humanize
|
||||||
|
}
|
||||||
|
|
||||||
|
Gitlab::Metrics.counter(key.to_sym, description).increment
|
||||||
|
end
|
||||||
|
|
||||||
|
# Project counters are short lived, in Redis,
|
||||||
|
# and it's used to report how successful a project
|
||||||
|
# import was with the #summary method.
|
||||||
|
def increment_project_counter(project, object_type, operation)
|
||||||
|
counter_key = PROJECT_COUNTER_KEY % { project: project.id, operation: operation, object_type: object_type }
|
||||||
|
|
||||||
|
add_counter_to_list(project, operation, counter_key)
|
||||||
|
|
||||||
|
CACHING.increment(counter_key)
|
||||||
|
end
|
||||||
|
|
||||||
def add_counter_to_list(project, operation, key)
|
def add_counter_to_list(project, operation, key)
|
||||||
CACHING.set_add(counter_list_key(project, operation), key)
|
CACHING.set_add(counter_list_key(project, operation), key)
|
||||||
end
|
end
|
||||||
|
|
||||||
def counter_list_key(project, operation)
|
def counter_list_key(project, operation)
|
||||||
COUNTER_LIST_KEY % { project: project.id, operation: operation }
|
PROJECT_COUNTER_LIST_KEY % { project: project.id, operation: operation }
|
||||||
end
|
end
|
||||||
|
|
||||||
def validate_operation!(operation)
|
def validate_operation!(operation)
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Gitlab
|
||||||
|
module Usage
|
||||||
|
module Metrics
|
||||||
|
module Instrumentations
|
||||||
|
class CollectedDataCategoriesMetric < GenericMetric
|
||||||
|
def value
|
||||||
|
::ServicePing::PermitDataCategoriesService.new.execute
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -256,7 +256,8 @@ module Gitlab
|
||||||
settings: {
|
settings: {
|
||||||
ldap_encrypted_secrets_enabled: alt_usage_data(fallback: nil) { Gitlab::Auth::Ldap::Config.encrypted_secrets.active? },
|
ldap_encrypted_secrets_enabled: alt_usage_data(fallback: nil) { Gitlab::Auth::Ldap::Config.encrypted_secrets.active? },
|
||||||
operating_system: alt_usage_data(fallback: nil) { operating_system },
|
operating_system: alt_usage_data(fallback: nil) { operating_system },
|
||||||
gitaly_apdex: alt_usage_data { gitaly_apdex }
|
gitaly_apdex: alt_usage_data { gitaly_apdex },
|
||||||
|
collected_data_categories: alt_usage_data(fallback: []) { Gitlab::Usage::Metrics::Instrumentations::CollectedDataCategoriesMetric.new(time_frame: 'none').value }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
|
@ -118,7 +118,7 @@ namespace :gitlab do
|
||||||
|
|
||||||
desc 'Create missing dynamic database partitions'
|
desc 'Create missing dynamic database partitions'
|
||||||
task create_dynamic_partitions: :environment do
|
task create_dynamic_partitions: :environment do
|
||||||
Gitlab::Database::Partitioning::PartitionCreator.new.create_partitions
|
Gitlab::Database::Partitioning::PartitionManager.new.sync_partitions
|
||||||
end
|
end
|
||||||
|
|
||||||
# This is targeted towards deploys and upgrades of GitLab.
|
# This is targeted towards deploys and upgrades of GitLab.
|
||||||
|
|
|
@ -3855,6 +3855,9 @@ msgstr ""
|
||||||
msgid "An error occurred while updating the notification settings. Please try again."
|
msgid "An error occurred while updating the notification settings. Please try again."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "An error occurred while uploading the image. Please try again."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "An error occurred while validating group path"
|
msgid "An error occurred while validating group path"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -11683,6 +11686,9 @@ msgstr ""
|
||||||
msgid "Download %{name} artifact"
|
msgid "Download %{name} artifact"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Download (%{fileSizeReadable})"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Download (%{size})"
|
msgid "Download (%{size})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -13075,9 +13081,6 @@ msgstr ""
|
||||||
msgid "EscalationPolicies|Edit escalation policy"
|
msgid "EscalationPolicies|Edit escalation policy"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "EscalationPolicies|Elapsed time must be greater than or equal to zero."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "EscalationPolicies|Email on-call user in schedule"
|
msgid "EscalationPolicies|Email on-call user in schedule"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -13096,6 +13099,9 @@ msgstr ""
|
||||||
msgid "EscalationPolicies|IF alert is not %{alertStatus} in %{minutes} minutes"
|
msgid "EscalationPolicies|IF alert is not %{alertStatus} in %{minutes} minutes"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "EscalationPolicies|Minutes must be between 0 and 1440."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "EscalationPolicies|Remove escalation rule"
|
msgid "EscalationPolicies|Remove escalation rule"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -16757,6 +16763,9 @@ msgstr[1] ""
|
||||||
msgid "Importing..."
|
msgid "Importing..."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Import|A repository URL usually ends in a .git suffix, although this is not required. Double check to make sure your repository URL is correct."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Improve customer support with Service Desk"
|
msgid "Improve customer support with Service Desk"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -33477,6 +33486,9 @@ msgstr ""
|
||||||
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
|
msgid "This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "This group can't be transfered because it is linked to a subscription. To transfer this group, %{linkStart}link the subscription%{linkEnd} with a different group."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "This group cannot be invited to a project inside a group with enforced SSO"
|
msgid "This group cannot be invited to a project inside a group with enforced SSO"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
|
@ -157,6 +157,7 @@
|
||||||
"prosemirror-inputrules": "^1.1.3",
|
"prosemirror-inputrules": "^1.1.3",
|
||||||
"prosemirror-markdown": "^1.5.1",
|
"prosemirror-markdown": "^1.5.1",
|
||||||
"prosemirror-model": "^1.13.3",
|
"prosemirror-model": "^1.13.3",
|
||||||
|
"prosemirror-state": "^1.3.4",
|
||||||
"raphael": "^2.2.7",
|
"raphael": "^2.2.7",
|
||||||
"raw-loader": "^4.0.2",
|
"raw-loader": "^4.0.2",
|
||||||
"scrollparent": "^2.0.1",
|
"scrollparent": "^2.0.1",
|
||||||
|
|
|
@ -24,7 +24,7 @@ gem 'parallel', '~> 1.19'
|
||||||
gem 'rspec-parameterized', '~> 0.4.2'
|
gem 'rspec-parameterized', '~> 0.4.2'
|
||||||
gem 'github_api', '~> 0.18.2'
|
gem 'github_api', '~> 0.18.2'
|
||||||
|
|
||||||
gem 'chemlab', '~> 0.5'
|
gem 'chemlab', '~> 0.7'
|
||||||
gem 'chemlab-library-www-gitlab-com', '~> 0.1'
|
gem 'chemlab-library-www-gitlab-com', '~> 0.1'
|
||||||
|
|
||||||
group :development do
|
group :development do
|
||||||
|
|
|
@ -41,18 +41,21 @@ GEM
|
||||||
capybara-screenshot (1.0.23)
|
capybara-screenshot (1.0.23)
|
||||||
capybara (>= 1.0, < 4)
|
capybara (>= 1.0, < 4)
|
||||||
launchy
|
launchy
|
||||||
chemlab (0.5.0)
|
chemlab (0.7.2)
|
||||||
rake (~> 12.3.0)
|
colorize (~> 0.8)
|
||||||
selenium-webdriver (~> 3.12)
|
i18n (~> 1.8)
|
||||||
watir (~> 6.17)
|
rake (>= 12, < 14)
|
||||||
|
selenium-webdriver (>= 3, < 5)
|
||||||
|
watir (>= 6, < 8)
|
||||||
chemlab-library-www-gitlab-com (0.1.1)
|
chemlab-library-www-gitlab-com (0.1.1)
|
||||||
chemlab (~> 0.4)
|
chemlab (~> 0.4)
|
||||||
childprocess (4.1.0)
|
childprocess (4.1.0)
|
||||||
coderay (1.1.2)
|
coderay (1.1.2)
|
||||||
|
colorize (0.8.1)
|
||||||
concord (0.1.5)
|
concord (0.1.5)
|
||||||
adamantium (~> 0.2.0)
|
adamantium (~> 0.2.0)
|
||||||
equalizer (~> 0.0.9)
|
equalizer (~> 0.0.9)
|
||||||
concurrent-ruby (1.1.8)
|
concurrent-ruby (1.1.9)
|
||||||
descendants_tracker (0.0.4)
|
descendants_tracker (0.0.4)
|
||||||
thread_safe (~> 0.3, >= 0.3.1)
|
thread_safe (~> 0.3, >= 0.3.1)
|
||||||
diff-lcs (1.3)
|
diff-lcs (1.3)
|
||||||
|
@ -129,7 +132,7 @@ GEM
|
||||||
rack-test (1.1.0)
|
rack-test (1.1.0)
|
||||||
rack (>= 1.0, < 3)
|
rack (>= 1.0, < 3)
|
||||||
rake (12.3.3)
|
rake (12.3.3)
|
||||||
regexp_parser (1.6.0)
|
regexp_parser (1.8.2)
|
||||||
require_all (3.0.0)
|
require_all (3.0.0)
|
||||||
rest-client (2.1.0)
|
rest-client (2.1.0)
|
||||||
http-accept (>= 1.7.0, < 2.0)
|
http-accept (>= 1.7.0, < 2.0)
|
||||||
|
@ -163,7 +166,7 @@ GEM
|
||||||
rspec-core (>= 2, < 4, != 2.12.0)
|
rspec-core (>= 2, < 4, != 2.12.0)
|
||||||
ruby-debug-ide (0.7.2)
|
ruby-debug-ide (0.7.2)
|
||||||
rake (>= 0.8.1)
|
rake (>= 0.8.1)
|
||||||
rubyzip (2.3.0)
|
rubyzip (2.3.2)
|
||||||
selenium-webdriver (4.0.0.beta4)
|
selenium-webdriver (4.0.0.beta4)
|
||||||
childprocess (>= 0.5, < 5.0)
|
childprocess (>= 0.5, < 5.0)
|
||||||
rexml (~> 3.2)
|
rexml (~> 3.2)
|
||||||
|
@ -186,9 +189,9 @@ GEM
|
||||||
procto (~> 0.0.2)
|
procto (~> 0.0.2)
|
||||||
uuid (2.3.9)
|
uuid (2.3.9)
|
||||||
macaddr (~> 1.0)
|
macaddr (~> 1.0)
|
||||||
watir (6.18.0)
|
watir (6.19.1)
|
||||||
regexp_parser (>= 1.2, < 3)
|
regexp_parser (>= 1.2, < 3)
|
||||||
selenium-webdriver (>= 3.8)
|
selenium-webdriver (>= 3.142.7)
|
||||||
xpath (3.2.0)
|
xpath (3.2.0)
|
||||||
nokogiri (~> 1.8)
|
nokogiri (~> 1.8)
|
||||||
zeitwerk (2.4.2)
|
zeitwerk (2.4.2)
|
||||||
|
@ -202,7 +205,7 @@ DEPENDENCIES
|
||||||
allure-rspec (~> 2.14.1)
|
allure-rspec (~> 2.14.1)
|
||||||
capybara (~> 3.29.0)
|
capybara (~> 3.29.0)
|
||||||
capybara-screenshot (~> 1.0.23)
|
capybara-screenshot (~> 1.0.23)
|
||||||
chemlab (~> 0.5)
|
chemlab (~> 0.7)
|
||||||
chemlab-library-www-gitlab-com (~> 0.1)
|
chemlab-library-www-gitlab-com (~> 0.1)
|
||||||
faker (~> 1.6, >= 1.6.6)
|
faker (~> 1.6, >= 1.6.6)
|
||||||
github_api (~> 0.18.2)
|
github_api (~> 0.18.2)
|
||||||
|
@ -224,4 +227,4 @@ DEPENDENCIES
|
||||||
timecop (~> 0.9.1)
|
timecop (~> 0.9.1)
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
2.1.4
|
2.2.22
|
||||||
|
|
|
@ -38,7 +38,7 @@ module QA
|
||||||
element :project_creation_level_dropdown
|
element :project_creation_level_dropdown
|
||||||
end
|
end
|
||||||
|
|
||||||
view 'app/views/groups/settings/_advanced.html.haml' do
|
view 'app/views/groups/settings/_transfer.html.haml' do
|
||||||
element :select_group_dropdown
|
element :select_group_dropdown
|
||||||
element :transfer_group_button
|
element :transfer_group_button
|
||||||
end
|
end
|
||||||
|
|
|
@ -179,6 +179,7 @@ module QA
|
||||||
config.browser = Capybara.current_session.driver.browser # reuse Capybara session
|
config.browser = Capybara.current_session.driver.browser # reuse Capybara session
|
||||||
config.libraries = [GitlabHandbook]
|
config.libraries = [GitlabHandbook]
|
||||||
config.base_url = Runtime::Scenario.attributes[:gitlab_address] # reuse GitLab address
|
config.base_url = Runtime::Scenario.attributes[:gitlab_address] # reuse GitLab address
|
||||||
|
config.hide_banner = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
# rubocop: enable Metrics/AbcSize
|
# rubocop: enable Metrics/AbcSize
|
||||||
|
|
|
@ -116,6 +116,12 @@ class AutomatedCleanup
|
||||||
delete_helm_releases(releases_to_delete)
|
delete_helm_releases(releases_to_delete)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def perform_stale_namespace_cleanup!(days:)
|
||||||
|
kubernetes_client = Tooling::KubernetesClient.new(namespace: nil)
|
||||||
|
|
||||||
|
kubernetes_client.cleanup_review_app_namespaces(created_before: threshold_time(days: days), wait: false)
|
||||||
|
end
|
||||||
|
|
||||||
def perform_stale_pvc_cleanup!(days:)
|
def perform_stale_pvc_cleanup!(days:)
|
||||||
kubernetes.cleanup_by_created_at(resource_type: 'pvc', created_before: threshold_time(days: days), wait: false)
|
kubernetes.cleanup_by_created_at(resource_type: 'pvc', created_before: threshold_time(days: days), wait: false)
|
||||||
end
|
end
|
||||||
|
@ -203,6 +209,10 @@ timed('Helm releases cleanup') do
|
||||||
automated_cleanup.perform_helm_releases_cleanup!(days: 7)
|
automated_cleanup.perform_helm_releases_cleanup!(days: 7)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
timed('Stale Namespace cleanup') do
|
||||||
|
automated_cleanup.perform_stale_namespace_cleanup!(days: 14)
|
||||||
|
end
|
||||||
|
|
||||||
timed('Stale PVC cleanup') do
|
timed('Stale PVC cleanup') do
|
||||||
automated_cleanup.perform_stale_pvc_cleanup!(days: 30)
|
automated_cleanup.perform_stale_pvc_cleanup!(days: 30)
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,5 +5,6 @@ FactoryBot.define do
|
||||||
build factory: :ci_build
|
build factory: :ci_build
|
||||||
project
|
project
|
||||||
protected { build.protected }
|
protected { build.protected }
|
||||||
|
instance_runners_enabled { true }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,15 +5,16 @@ require 'spec_helper'
|
||||||
RSpec.describe 'Dropdown assignee', :js do
|
RSpec.describe 'Dropdown assignee', :js do
|
||||||
include FilteredSearchHelpers
|
include FilteredSearchHelpers
|
||||||
|
|
||||||
let!(:project) { create(:project) }
|
let_it_be(:project) { create(:project) }
|
||||||
let!(:user) { create(:user, name: 'administrator', username: 'root') }
|
let_it_be(:user) { create(:user, name: 'administrator', username: 'root') }
|
||||||
|
let_it_be(:issue) { create(:issue, project: project) }
|
||||||
|
|
||||||
let(:js_dropdown_assignee) { '#js-dropdown-assignee' }
|
let(:js_dropdown_assignee) { '#js-dropdown-assignee' }
|
||||||
let(:filter_dropdown) { find("#{js_dropdown_assignee} .filter-dropdown") }
|
let(:filter_dropdown) { find("#{js_dropdown_assignee} .filter-dropdown") }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
project.add_maintainer(user)
|
project.add_maintainer(user)
|
||||||
sign_in(user)
|
sign_in(user)
|
||||||
create(:issue, project: project)
|
|
||||||
|
|
||||||
visit project_issues_path(project)
|
visit project_issues_path(project)
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,15 +5,16 @@ require 'spec_helper'
|
||||||
RSpec.describe 'Dropdown author', :js do
|
RSpec.describe 'Dropdown author', :js do
|
||||||
include FilteredSearchHelpers
|
include FilteredSearchHelpers
|
||||||
|
|
||||||
let!(:project) { create(:project) }
|
let_it_be(:project) { create(:project) }
|
||||||
let!(:user) { create(:user, name: 'administrator', username: 'root') }
|
let_it_be(:user) { create(:user, name: 'administrator', username: 'root') }
|
||||||
|
let_it_be(:issue) { create(:issue, project: project) }
|
||||||
|
|
||||||
let(:js_dropdown_author) { '#js-dropdown-author' }
|
let(:js_dropdown_author) { '#js-dropdown-author' }
|
||||||
let(:filter_dropdown) { find("#{js_dropdown_author} .filter-dropdown") }
|
let(:filter_dropdown) { find("#{js_dropdown_author} .filter-dropdown") }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
project.add_maintainer(user)
|
project.add_maintainer(user)
|
||||||
sign_in(user)
|
sign_in(user)
|
||||||
create(:issue, project: project)
|
|
||||||
|
|
||||||
visit project_issues_path(project)
|
visit project_issues_path(project)
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,8 +5,10 @@ require 'spec_helper'
|
||||||
RSpec.describe 'Dropdown base', :js do
|
RSpec.describe 'Dropdown base', :js do
|
||||||
include FilteredSearchHelpers
|
include FilteredSearchHelpers
|
||||||
|
|
||||||
let!(:project) { create(:project) }
|
let_it_be(:project) { create(:project) }
|
||||||
let!(:user) { create(:user, name: 'administrator', username: 'root') }
|
let_it_be(:user) { create(:user, name: 'administrator', username: 'root') }
|
||||||
|
let_it_be(:issue) { create(:issue, project: project) }
|
||||||
|
|
||||||
let(:filtered_search) { find('.filtered-search') }
|
let(:filtered_search) { find('.filtered-search') }
|
||||||
let(:js_dropdown_assignee) { '#js-dropdown-assignee' }
|
let(:js_dropdown_assignee) { '#js-dropdown-assignee' }
|
||||||
let(:filter_dropdown) { find("#{js_dropdown_assignee} .filter-dropdown") }
|
let(:filter_dropdown) { find("#{js_dropdown_assignee} .filter-dropdown") }
|
||||||
|
@ -18,7 +20,6 @@ RSpec.describe 'Dropdown base', :js do
|
||||||
before do
|
before do
|
||||||
project.add_maintainer(user)
|
project.add_maintainer(user)
|
||||||
sign_in(user)
|
sign_in(user)
|
||||||
create(:issue, project: project)
|
|
||||||
|
|
||||||
visit project_issues_path(project)
|
visit project_issues_path(project)
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,10 +5,11 @@ require 'spec_helper'
|
||||||
RSpec.describe 'Dropdown emoji', :js do
|
RSpec.describe 'Dropdown emoji', :js do
|
||||||
include FilteredSearchHelpers
|
include FilteredSearchHelpers
|
||||||
|
|
||||||
let!(:project) { create(:project, :public) }
|
let_it_be(:project) { create(:project, :public) }
|
||||||
let!(:user) { create(:user, name: 'administrator', username: 'root') }
|
let_it_be(:user) { create(:user, name: 'administrator', username: 'root') }
|
||||||
let!(:issue) { create(:issue, project: project) }
|
let_it_be(:issue) { create(:issue, project: project) }
|
||||||
let!(:award_emoji_star) { create(:award_emoji, name: 'star', user: user, awardable: issue) }
|
let_it_be(:award_emoji_star) { create(:award_emoji, name: 'star', user: user, awardable: issue) }
|
||||||
|
|
||||||
let(:filtered_search) { find('.filtered-search') }
|
let(:filtered_search) { find('.filtered-search') }
|
||||||
let(:js_dropdown_emoji) { '#js-dropdown-my-reaction' }
|
let(:js_dropdown_emoji) { '#js-dropdown-my-reaction' }
|
||||||
let(:filter_dropdown) { find("#{js_dropdown_emoji} .filter-dropdown") }
|
let(:filter_dropdown) { find("#{js_dropdown_emoji} .filter-dropdown") }
|
||||||
|
|
|
@ -5,8 +5,10 @@ require 'spec_helper'
|
||||||
RSpec.describe 'Dropdown hint', :js do
|
RSpec.describe 'Dropdown hint', :js do
|
||||||
include FilteredSearchHelpers
|
include FilteredSearchHelpers
|
||||||
|
|
||||||
let!(:project) { create(:project, :public) }
|
let_it_be(:project) { create(:project, :public) }
|
||||||
let!(:user) { create(:user) }
|
let_it_be(:user) { create(:user) }
|
||||||
|
let_it_be(:issue) { create(:issue, project: project) }
|
||||||
|
|
||||||
let(:filtered_search) { find('.filtered-search') }
|
let(:filtered_search) { find('.filtered-search') }
|
||||||
let(:js_dropdown_hint) { '#js-dropdown-hint' }
|
let(:js_dropdown_hint) { '#js-dropdown-hint' }
|
||||||
let(:js_dropdown_operator) { '#js-dropdown-operator' }
|
let(:js_dropdown_operator) { '#js-dropdown-operator' }
|
||||||
|
@ -21,8 +23,6 @@ RSpec.describe 'Dropdown hint', :js do
|
||||||
|
|
||||||
before do
|
before do
|
||||||
project.add_maintainer(user)
|
project.add_maintainer(user)
|
||||||
create(:issue, project: project)
|
|
||||||
create(:merge_request, source_project: project, target_project: project)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when user not logged in' do
|
context 'when user not logged in' do
|
||||||
|
|
|
@ -5,22 +5,23 @@ require 'spec_helper'
|
||||||
RSpec.describe 'Dropdown label', :js do
|
RSpec.describe 'Dropdown label', :js do
|
||||||
include FilteredSearchHelpers
|
include FilteredSearchHelpers
|
||||||
|
|
||||||
let(:project) { create(:project) }
|
let_it_be(:project) { create(:project) }
|
||||||
let(:user) { create(:user) }
|
let_it_be(:user) { create(:user) }
|
||||||
|
let_it_be(:issue) { create(:issue, project: project) }
|
||||||
|
let_it_be(:label) { create(:label, project: project, title: 'bug-label') }
|
||||||
|
|
||||||
let(:filtered_search) { find('.filtered-search') }
|
let(:filtered_search) { find('.filtered-search') }
|
||||||
let(:filter_dropdown) { find('#js-dropdown-label .filter-dropdown') }
|
let(:filter_dropdown) { find('#js-dropdown-label .filter-dropdown') }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
project.add_maintainer(user)
|
project.add_maintainer(user)
|
||||||
sign_in(user)
|
sign_in(user)
|
||||||
create(:issue, project: project)
|
|
||||||
|
|
||||||
visit project_issues_path(project)
|
visit project_issues_path(project)
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'behavior' do
|
describe 'behavior' do
|
||||||
it 'loads all the labels when opened' do
|
it 'loads all the labels when opened' do
|
||||||
create(:label, project: project, title: 'bug-label')
|
|
||||||
filtered_search.set('label:=')
|
filtered_search.set('label:=')
|
||||||
|
|
||||||
expect_filtered_search_dropdown_results(filter_dropdown, 1)
|
expect_filtered_search_dropdown_results(filter_dropdown, 1)
|
||||||
|
|
|
@ -5,10 +5,11 @@ require 'spec_helper'
|
||||||
RSpec.describe 'Dropdown milestone', :js do
|
RSpec.describe 'Dropdown milestone', :js do
|
||||||
include FilteredSearchHelpers
|
include FilteredSearchHelpers
|
||||||
|
|
||||||
let!(:project) { create(:project) }
|
let_it_be(:project) { create(:project) }
|
||||||
let!(:user) { create(:user) }
|
let_it_be(:user) { create(:user) }
|
||||||
let!(:milestone) { create(:milestone, title: 'v1.0', project: project) }
|
let_it_be(:milestone) { create(:milestone, title: 'v1.0', project: project) }
|
||||||
let!(:uppercase_milestone) { create(:milestone, title: 'CAP_MILESTONE', project: project) }
|
let_it_be(:uppercase_milestone) { create(:milestone, title: 'CAP_MILESTONE', project: project) }
|
||||||
|
let_it_be(:issue) { create(:issue, project: project) }
|
||||||
|
|
||||||
let(:filtered_search) { find('.filtered-search') }
|
let(:filtered_search) { find('.filtered-search') }
|
||||||
let(:filter_dropdown) { find('#js-dropdown-milestone .filter-dropdown') }
|
let(:filter_dropdown) { find('#js-dropdown-milestone .filter-dropdown') }
|
||||||
|
@ -16,7 +17,6 @@ RSpec.describe 'Dropdown milestone', :js do
|
||||||
before do
|
before do
|
||||||
project.add_maintainer(user)
|
project.add_maintainer(user)
|
||||||
sign_in(user)
|
sign_in(user)
|
||||||
create(:issue, project: project)
|
|
||||||
|
|
||||||
visit project_issues_path(project)
|
visit project_issues_path(project)
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,10 +5,11 @@ require 'spec_helper'
|
||||||
RSpec.describe 'Dropdown release', :js do
|
RSpec.describe 'Dropdown release', :js do
|
||||||
include FilteredSearchHelpers
|
include FilteredSearchHelpers
|
||||||
|
|
||||||
let!(:project) { create(:project, :repository) }
|
let_it_be(:project) { create(:project, :repository) }
|
||||||
let!(:user) { create(:user) }
|
let_it_be(:user) { create(:user) }
|
||||||
let!(:release) { create(:release, tag: 'v1.0', project: project) }
|
let_it_be(:release) { create(:release, tag: 'v1.0', project: project) }
|
||||||
let!(:crazy_release) { create(:release, tag: '☺!/"#%&\'{}+,-.<>;=@]_`{|}🚀', project: project) }
|
let_it_be(:crazy_release) { create(:release, tag: '☺!/"#%&\'{}+,-.<>;=@]_`{|}🚀', project: project) }
|
||||||
|
let_it_be(:issue) { create(:issue, project: project) }
|
||||||
|
|
||||||
let(:filtered_search) { find('.filtered-search') }
|
let(:filtered_search) { find('.filtered-search') }
|
||||||
let(:filter_dropdown) { find('#js-dropdown-release .filter-dropdown') }
|
let(:filter_dropdown) { find('#js-dropdown-release .filter-dropdown') }
|
||||||
|
@ -16,7 +17,6 @@ RSpec.describe 'Dropdown release', :js do
|
||||||
before do
|
before do
|
||||||
project.add_maintainer(user)
|
project.add_maintainer(user)
|
||||||
sign_in(user)
|
sign_in(user)
|
||||||
create(:issue, project: project)
|
|
||||||
|
|
||||||
visit project_issues_path(project)
|
visit project_issues_path(project)
|
||||||
end
|
end
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue