Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
8ce82c1eaf
commit
e1189e4c3b
|
@ -7,3 +7,4 @@
|
|||
/public/
|
||||
/tmp/
|
||||
/vendor/
|
||||
/sitespeed-result/
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
/public/
|
||||
/vendor/
|
||||
/tmp/
|
||||
/sitespeed-result/
|
||||
|
||||
# ignore stylesheets for now as this clashes with our linter
|
||||
*.css
|
||||
|
|
|
@ -1 +1 @@
|
|||
d4ea957f6131538cd78e490a585ea3a455251064
|
||||
40511f7a14ded77c826809d054d740a66e1c106f
|
||||
|
|
|
@ -41,7 +41,7 @@ export default {
|
|||
watch: {
|
||||
filterParams: {
|
||||
handler() {
|
||||
if (this.list.id) {
|
||||
if (this.list.id && !this.list.collapsed) {
|
||||
this.fetchItemsForList({ listId: this.list.id });
|
||||
}
|
||||
},
|
||||
|
|
|
@ -240,7 +240,7 @@ export default {
|
|||
},
|
||||
|
||||
updateList: (
|
||||
{ commit, state: { issuableType } },
|
||||
{ commit, state: { issuableType, boardItemsByListId = {} }, dispatch },
|
||||
{ listId, position, collapsed, backupList },
|
||||
) => {
|
||||
gqlClient
|
||||
|
@ -255,6 +255,12 @@ export default {
|
|||
.then(({ data }) => {
|
||||
if (data?.updateBoardList?.errors.length) {
|
||||
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(() => {
|
||||
|
|
|
@ -1,10 +1,65 @@
|
|||
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({
|
||||
defaultOptions: {
|
||||
...Image.options,
|
||||
uploadsPath: null,
|
||||
renderMarkdown: null,
|
||||
},
|
||||
addAttributes() {
|
||||
return {
|
||||
...this.parent?.(),
|
||||
uploading: {
|
||||
default: false,
|
||||
},
|
||||
src: {
|
||||
default: null,
|
||||
/*
|
||||
|
@ -14,17 +69,25 @@ const ExtendedImage = Image.extend({
|
|||
* attribute.
|
||||
*/
|
||||
parseHTML: (element) => {
|
||||
const img = element.querySelector('img');
|
||||
const img = resolveImageEl(element);
|
||||
|
||||
return {
|
||||
src: img.dataset.src || img.getAttribute('src'),
|
||||
};
|
||||
},
|
||||
},
|
||||
canonicalSrc: {
|
||||
default: null,
|
||||
parseHTML: (element) => {
|
||||
return {
|
||||
canonicalSrc: element.dataset.canonicalSrc,
|
||||
};
|
||||
},
|
||||
},
|
||||
alt: {
|
||||
default: null,
|
||||
parseHTML: (element) => {
|
||||
const img = element.querySelector('img');
|
||||
const img = resolveImageEl(element);
|
||||
|
||||
return {
|
||||
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 }) => {
|
||||
return {
|
||||
|
|
|
@ -3,3 +3,15 @@ export const hasSelection = (tiptapEditor) => {
|
|||
|
||||
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>
|
||||
import { GlTooltipDirective, GlIcon, GlLink, GlSafeHtmlDirective } from '@gitlab/ui';
|
||||
import { ApolloMutation } from 'vue-apollo';
|
||||
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
|
||||
import { __ } from '~/locale';
|
||||
import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
|
||||
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
|
||||
|
@ -48,6 +49,9 @@ export default {
|
|||
author() {
|
||||
return this.note.author;
|
||||
},
|
||||
authorId() {
|
||||
return getIdFromGraphQLId(this.author.id);
|
||||
},
|
||||
noteAnchorId() {
|
||||
return findNoteId(this.note.id);
|
||||
},
|
||||
|
@ -94,7 +98,7 @@ export default {
|
|||
v-once
|
||||
:href="author.webUrl"
|
||||
class="js-user-link"
|
||||
:data-user-id="author.id"
|
||||
:data-user-id="authorId"
|
||||
:data-username="author.username"
|
||||
>
|
||||
<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) => {
|
||||
return fetch(stripWhitespaceFromQuery(url, path), options);
|
||||
return fetch(stripWhitespaceFromQuery(url, uri), options);
|
||||
};
|
||||
|
||||
const requestLink = ApolloLink.split(
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
<script>
|
||||
import { GlFilteredSearchToken } from '@gitlab/ui';
|
||||
import { mapState } from 'vuex';
|
||||
// eslint-disable-next-line import/no-deprecated
|
||||
import { getParameterByName, setUrlParams, urlParamsToObject } from '~/lib/utils/url_utility';
|
||||
import { getParameterByName, setUrlParams, queryToObject } from '~/lib/utils/url_utility';
|
||||
import { s__ } from '~/locale';
|
||||
import {
|
||||
SEARCH_TOKEN_TYPE,
|
||||
|
@ -68,8 +67,7 @@ export default {
|
|||
},
|
||||
},
|
||||
created() {
|
||||
// eslint-disable-next-line import/no-deprecated
|
||||
const query = urlParamsToObject(window.location.search);
|
||||
const query = queryToObject(window.location.search);
|
||||
|
||||
const tokens = this.tokens
|
||||
.filter((token) => query[token.type])
|
||||
|
|
|
@ -66,6 +66,7 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
currentFilter: null,
|
||||
renderSkeleton: !this.shouldShow,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
@ -93,7 +94,7 @@ export default {
|
|||
return this.noteableData.noteableType;
|
||||
},
|
||||
allDiscussions() {
|
||||
if (this.isLoading) {
|
||||
if (this.renderSkeleton || this.isLoading) {
|
||||
const prerenderedNotesCount = parseInt(this.notesData.prerenderedNotesCount, 10) || 0;
|
||||
|
||||
return new Array(prerenderedNotesCount).fill({
|
||||
|
@ -122,6 +123,10 @@ export default {
|
|||
if (!this.isNotesFetched) {
|
||||
this.fetchNotes();
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
this.renderSkeleton = !this.shouldShow;
|
||||
});
|
||||
},
|
||||
discussionTabCounterText(val) {
|
||||
if (this.discussionsCount) {
|
||||
|
|
|
@ -74,6 +74,7 @@ const deriveProjectPathFromUrl = ($projectImportUrl) => {
|
|||
const bindEvents = () => {
|
||||
const $newProjectForm = $('#new_project');
|
||||
const $projectImportUrl = $('#project_import_url');
|
||||
const $projectImportUrlWarning = $('.js-import-url-warning');
|
||||
const $projectPath = $('.tab-pane.active #project_path');
|
||||
const $useTemplateBtn = $('.template-button > input');
|
||||
const $projectFieldsForm = $('.project-fields-form');
|
||||
|
@ -134,7 +135,25 @@ const bindEvents = () => {
|
|||
$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', () => {
|
||||
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':
|
||||
return () => import(/* webpackChunkName: 'blob_text_viewer' */ './text_viewer.vue');
|
||||
case 'download':
|
||||
// TODO (follow-up): import the download viewer
|
||||
return null; // () => import(/* webpackChunkName: 'blob_download_viewer' */ './download_viewer.vue');
|
||||
return () => import(/* webpackChunkName: 'blob_download_viewer' */ './download_viewer.vue');
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
@ -19,5 +18,10 @@ export const viewerProps = (type, blob) => {
|
|||
fileName: blob.name,
|
||||
readOnly: true,
|
||||
},
|
||||
download: {
|
||||
fileName: blob.name,
|
||||
filePath: blob.rawPath,
|
||||
fileSize: blob.rawSize,
|
||||
},
|
||||
}[type];
|
||||
};
|
||||
|
|
|
@ -87,6 +87,12 @@
|
|||
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 {
|
||||
transition-property: stroke-opacity;
|
||||
}
|
||||
|
|
|
@ -325,7 +325,11 @@ module Ci
|
|||
build.run_after_commit do
|
||||
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
|
||||
|
||||
|
|
|
@ -11,11 +11,48 @@ module Ci
|
|||
scope :queued_before, ->(time) { where(arel_table[:created_at].lt(time)) }
|
||||
|
||||
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!
|
||||
|
||||
self.upsert(entry.attributes.compact, returning: %w[build_id], unique_by: :build_id)
|
||||
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
|
||||
|
|
|
@ -224,7 +224,7 @@ module Ci
|
|||
end
|
||||
|
||||
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
|
||||
# in CI build records which the daily build metrics worker relies on.
|
||||
pipeline.run_after_commit { Ci::DailyBuildGroupReportResultsWorker.perform_in(10.minutes, pipeline.id) }
|
||||
|
|
|
@ -10,12 +10,12 @@ module PartitionedTable
|
|||
monthly: Gitlab::Database::Partitioning::MonthlyStrategy
|
||||
}.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}")
|
||||
|
||||
@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
|
||||
|
|
|
@ -9,7 +9,7 @@ class WebHookLog < ApplicationRecord
|
|||
|
||||
self.primary_key = :id
|
||||
|
||||
partitioned_by :created_at, strategy: :monthly
|
||||
partitioned_by :created_at, strategy: :monthly, retain_for: 3.months
|
||||
|
||||
belongs_to :web_hook
|
||||
|
||||
|
|
|
@ -46,6 +46,7 @@ module Groups
|
|||
def ensure_allowed_transfer
|
||||
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(:has_subscription) if has_subscription?
|
||||
raise_transfer_error(:invalid_policies) unless valid_policies?
|
||||
raise_transfer_error(:namespace_with_same_path) if namespace_with_same_path?
|
||||
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
|
||||
end
|
||||
|
||||
def has_subscription?
|
||||
@group.paid?
|
||||
end
|
||||
|
||||
def transfer_to_subgroup?
|
||||
@new_parent_group && \
|
||||
@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)
|
||||
|
||||
def execute
|
||||
return unless Gitlab::CurrentSettings.usage_ping_enabled?
|
||||
return if User.single_user&.requires_usage_stats_consent?
|
||||
return unless ServicePing::PermitDataCategoriesService.new.product_intelligence_enabled?
|
||||
|
||||
usage_data = Gitlab::UsageData.data(force_refresh: true)
|
||||
|
||||
|
|
|
@ -24,21 +24,6 @@
|
|||
"data-bind-in" => "#{'create_chat_team' if Gitlab.config.mattermost.enabled}"
|
||||
= f.submit s_('GroupSettings|Change group URL'), class: 'btn gl-button btn-warning'
|
||||
|
||||
.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', 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/transfer', group: @group
|
||||
= render 'groups/settings/remove', 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
|
||||
.col-sm-12
|
||||
%h1.mb-3.font-weight-normal
|
||||
= current_appearance&.title.presence || "GitLab"
|
||||
= current_appearance&.title.presence || _('GitLab')
|
||||
.row.mb-3
|
||||
.col-sm-7.order-12.order-sm-1.brand-holder
|
||||
- unless recently_confirmed_com?
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
= sprite_icon('document')
|
||||
%strong
|
||||
= search_blob_title(project, path)
|
||||
= copy_file_path_button(path)
|
||||
- if blob.data
|
||||
.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
|
||||
|
|
|
@ -8,7 +8,18 @@
|
|||
= _('Git repository 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
|
||||
= 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
|
||||
.form-group.col-md-6
|
||||
= f.label :import_url_user, class: 'label-bold' do
|
||||
|
|
|
@ -247,6 +247,15 @@
|
|||
:idempotent: true
|
||||
:tags:
|
||||
- :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
|
||||
:worker_name: Environments::AutoStopCronWorker
|
||||
:feature_category: :continuous_delivery
|
||||
|
@ -1365,6 +1374,15 @@
|
|||
:weight: 1
|
||||
:idempotent:
|
||||
: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
|
||||
:worker_name: Ci::BuildTraceChunkFlushWorker
|
||||
:feature_category: :continuous_integration
|
||||
|
@ -1585,6 +1603,15 @@
|
|||
:weight: 5
|
||||
:idempotent:
|
||||
: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
|
||||
:worker_name: Ci::BuildPrepareWorker
|
||||
:feature_category: :continuous_integration
|
||||
|
|
|
@ -1,16 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
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
|
||||
class ArchiveTraceWorker < ::Ci::ArchiveTraceWorker # rubocop:disable Scalability/IdempotentWorker
|
||||
# DEPRECATED: Not triggered since https://gitlab.com/gitlab-org/gitlab/-/merge_requests/64934/
|
||||
end
|
||||
|
|
|
@ -1,61 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class BuildFinishedWorker # rubocop:disable Scalability/IdempotentWorker
|
||||
include ApplicationWorker
|
||||
class BuildFinishedWorker < ::Ci::BuildFinishedWorker # rubocop:disable Scalability/IdempotentWorker
|
||||
# DEPRECATED: Not triggered since https://gitlab.com/gitlab-org/gitlab/-/merge_requests/64934/
|
||||
|
||||
sidekiq_options retry: 3
|
||||
include PipelineQueue
|
||||
|
||||
queue_namespace :pipeline_processing
|
||||
# We need to explicitly specify these settings. They aren't inheriting from the parent class.
|
||||
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.
|
||||
#
|
||||
ArchiveTraceWorker.perform_in(ARCHIVE_TRACES_IN, build.id)
|
||||
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
|
||||
def perform
|
||||
# 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
|
||||
Ci::Build.with_stale_live_trace.find_each(batch_size: 100) do |build|
|
||||
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
|
||||
|
||||
increment_counters(project)
|
||||
Gitlab::GithubImport::ObjectCounter.increment(project, object_type, :imported)
|
||||
|
||||
info(project.id, message: 'importer finished')
|
||||
rescue StandardError => e
|
||||
error(project.id, e, hash)
|
||||
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
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
@ -70,16 +58,6 @@ module Gitlab
|
|||
raise NotImplementedError
|
||||
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
|
||||
|
||||
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
|
||||
:diff_note
|
||||
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
|
||||
|
|
|
@ -16,14 +16,6 @@ module Gitlab
|
|||
def object_type
|
||||
:issue
|
||||
end
|
||||
|
||||
def counter_name
|
||||
:github_importer_imported_issues
|
||||
end
|
||||
|
||||
def counter_description
|
||||
'The number of imported GitHub issues'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -16,14 +16,6 @@ module Gitlab
|
|||
def object_type
|
||||
:lfs_object
|
||||
end
|
||||
|
||||
def counter_name
|
||||
:github_importer_imported_lfs_objects
|
||||
end
|
||||
|
||||
def counter_description
|
||||
'The number of imported GitHub Lfs Objects'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -16,14 +16,6 @@ module Gitlab
|
|||
def object_type
|
||||
:note
|
||||
end
|
||||
|
||||
def counter_name
|
||||
:github_importer_imported_notes
|
||||
end
|
||||
|
||||
def counter_description
|
||||
'The number of imported GitHub comments'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -18,14 +18,6 @@ module Gitlab
|
|||
def object_type
|
||||
:pull_request_merged_by
|
||||
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
|
||||
|
|
|
@ -18,14 +18,6 @@ module Gitlab
|
|||
def object_type
|
||||
:pull_request_review
|
||||
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
|
||||
|
|
|
@ -16,14 +16,6 @@ module Gitlab
|
|||
def object_type
|
||||
:pull_request
|
||||
end
|
||||
|
||||
def counter_name
|
||||
:github_importer_imported_pull_requests
|
||||
end
|
||||
|
||||
def counter_description
|
||||
'The number of imported GitHub pull requests'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -10,8 +10,7 @@ class PartitionCreationWorker
|
|||
idempotent!
|
||||
|
||||
def perform
|
||||
Gitlab::Database::Partitioning::PartitionCreator.new.create_partitions
|
||||
ensure
|
||||
Gitlab::Database::Partitioning::PartitionMonitoring.new.report_metrics
|
||||
# This worker has been removed in favor of Database::PartitionManagementWorker
|
||||
Database::PartitionManagementWorker.new.perform
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
---
|
||||
name: helm_packages
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/61014
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/331693
|
||||
milestone: '14.0'
|
||||
name: ci_build_finished_worker_namespace_changed
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/64934
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/335499
|
||||
milestone: '14.1'
|
||||
type: development
|
||||
group: group::package
|
||||
group: group::pipeline execution
|
||||
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
|
||||
type: development
|
||||
group: group::runner
|
||||
default_enabled: false
|
||||
default_enabled: true
|
||||
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']['cron'] ||= '0 0 * * *'
|
||||
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_creator']['cron'] ||= '21 */6 * * *'
|
||||
Settings.cron_jobs['postgres_dynamic_partitions_creator']['job_class'] ||= 'PartitionCreationWorker'
|
||||
Settings.cron_jobs['postgres_dynamic_partitions_manager'] ||= Settingslogic.new({})
|
||||
Settings.cron_jobs['postgres_dynamic_partitions_manager']['cron'] ||= '21 */6 * * *'
|
||||
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']['cron'] ||= '47 9 * * *'
|
||||
Settings.cron_jobs['ci_platform_metrics_update_cron_worker']['job_class'] = 'CiPlatformMetricsUpdateCronWorker'
|
||||
|
|
|
@ -3,15 +3,15 @@
|
|||
# Make sure we have loaded partitioned models here
|
||||
# (even with eager loading disabled).
|
||||
|
||||
Gitlab::Database::Partitioning::PartitionCreator.register(AuditEvent)
|
||||
Gitlab::Database::Partitioning::PartitionCreator.register(WebHookLog)
|
||||
Gitlab::Database::Partitioning::PartitionManager.register(AuditEvent)
|
||||
Gitlab::Database::Partitioning::PartitionManager.register(WebHookLog)
|
||||
|
||||
if Gitlab.ee?
|
||||
Gitlab::Database::Partitioning::PartitionCreator.register(IncidentManagement::PendingEscalations::Alert)
|
||||
Gitlab::Database::Partitioning::PartitionManager.register(IncidentManagement::PendingEscalations::Alert)
|
||||
end
|
||||
|
||||
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
|
||||
# ignore - happens when Rake tasks yet have to create a database, e.g. for testing
|
||||
end
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
data_category: Optional
|
||||
key_path: counts.ci_runners
|
||||
description: Total configured Runners in project
|
||||
description: Total configured Runners of all types
|
||||
product_section: ops
|
||||
product_stage: verify
|
||||
product_group: group::pipeline execution
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
data_category: Optional
|
||||
key_path: counts.ci_runners_instance_type_active
|
||||
name: "count_active_instance_ci_runners"
|
||||
description: Total active group Runners
|
||||
description: Total active Shared (Instance) Runners
|
||||
product_section: ops
|
||||
product_stage: verify
|
||||
product_group: group::pipeline execution
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
data_category: Optional
|
||||
key_path: counts.ci_runners_group_type_active
|
||||
name: "count_active_group_ci_runners"
|
||||
description: Total active instance Runners
|
||||
description: Total active Group Runners
|
||||
product_section: ops
|
||||
product_stage: verify
|
||||
product_group: group::pipeline execution
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
data_category: Optional
|
||||
key_path: counts.ci_runners_project_type_active
|
||||
name: "count_active_project_ci_runners"
|
||||
description: Total active project Runners
|
||||
description: Total active Specific (Project) Runners
|
||||
product_section: ops
|
||||
product_stage: verify
|
||||
product_group: group::pipeline execution
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
data_category: Optional
|
||||
key_path: counts.ci_runners_online
|
||||
name: "counts_online_runners"
|
||||
description: Total online Runners
|
||||
description: Total online Runners of all types
|
||||
product_section: ops
|
||||
product_stage: verify
|
||||
product_group: group::pipeline execution
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
data_category: Optional
|
||||
key_path: counts.ci_runners_instance_type_active_online
|
||||
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_stage: verify
|
||||
product_group: group::pipeline execution
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
data_category: Optional
|
||||
key_path: counts.ci_runners_group_type_active_online
|
||||
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_stage: verify
|
||||
product_group: group::pipeline execution
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
data_category: Optional
|
||||
key_path: counts.ci_runners_project_type_active_online
|
||||
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_stage: verify
|
||||
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,
|
||||
project_id bigint 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
|
||||
|
|
|
@ -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/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/helm_repository/index.html">Helm Charts</a></td><td>14.1+</td></tr>
|
||||
</table>
|
||||
</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) |
|
||||
| `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. |
|
||||
| `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_)
|
||||
| `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_)
|
||||
|
@ -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`. |
|
||||
| `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. |
|
||||
| `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_)
|
||||
| `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_)
|
||||
|
|
|
@ -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)
|
||||
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
|
||||
|
||||
> [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,
|
||||
"generic_packages_max_file_size": 5368709120,
|
||||
"helm_max_file_size": 5242880,
|
||||
"maven_max_file_size": 3221225472,
|
||||
"npm_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. |
|
||||
| `conan_max_file_size` | integer | no | Maximum Conan package file size in bytes. |
|
||||
| `generic_packages_max_file_size` | integer | no | Maximum generic package file size in bytes. |
|
||||
| `helm_max_file_size` | integer | no | Maximum Helm chart file size in bytes. |
|
||||
| `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. |
|
||||
| `nuget_max_file_size` | integer | no | Maximum NuGet package file size in bytes. |
|
||||
|
@ -75,6 +77,7 @@ Example response:
|
|||
{
|
||||
"conan_max_file_size": 3221225472,
|
||||
"generic_packages_max_file_size": 5368709120,
|
||||
"helm_max_file_size": 5242880,
|
||||
"maven_max_file_size": 3221225472,
|
||||
"npm_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).
|
||||
- [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
|
||||
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:
|
||||
- [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.
|
||||
- [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:
|
||||
- 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).
|
||||
|
|
|
@ -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_')
|
||||
```
|
||||
|
||||
### 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
|
||||
|
||||
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
|
||||
the [presentation](https://public.dalibo.com/exports/conferences/_archives/_2012/201211_explain/understanding_explain.pdf)
|
||||
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`
|
||||
|
||||
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)
|
||||
|
||||
|
@ -680,7 +680,7 @@ Tiers: `free`, `premium`, `ultimate`
|
|||
|
||||
### `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)
|
||||
|
||||
|
@ -694,7 +694,7 @@ Tiers: `free`, `premium`, `ultimate`
|
|||
|
||||
### `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)
|
||||
|
||||
|
@ -708,7 +708,7 @@ Tiers: `free`, `premium`, `ultimate`
|
|||
|
||||
### `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)
|
||||
|
||||
|
@ -722,7 +722,7 @@ Tiers: `free`, `premium`, `ultimate`
|
|||
|
||||
### `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)
|
||||
|
||||
|
@ -736,7 +736,7 @@ Tiers: `free`, `premium`, `ultimate`
|
|||
|
||||
### `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)
|
||||
|
||||
|
@ -750,7 +750,7 @@ Tiers: `free`, `premium`, `ultimate`
|
|||
|
||||
### `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)
|
||||
|
||||
|
@ -764,7 +764,7 @@ Tiers: `free`, `premium`, `ultimate`
|
|||
|
||||
### `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)
|
||||
|
||||
|
@ -18316,6 +18316,22 @@ Status: `data_available`
|
|||
|
||||
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`
|
||||
|
||||
Gitaly application performance
|
||||
|
|
|
@ -275,7 +275,7 @@ Post-processing is currently limited to a project's default branch, see the abov
|
|||
sequenceDiagram
|
||||
autonumber
|
||||
Rails->>+Sidekiq: gl-secret-detection-report.json
|
||||
Sidekiq-->+Sidekiq: BuildFinishedWorker
|
||||
Sidekiq-->+Sidekiq: Ci::BuildFinishedWorker
|
||||
Sidekiq-->+RevocationAPI: GET revocable keys types
|
||||
RevocationAPI-->>-Sidekiq: OK
|
||||
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
|
||||
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
|
||||
|
||||
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:
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
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.
|
||||
|
|
|
@ -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
|
||||
|
||||
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`.
|
||||
|
||||
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!
|
||||
end
|
||||
|
||||
after_validation do
|
||||
not_found! unless Feature.enabled?(:helm_packages, authorized_user_project)
|
||||
end
|
||||
|
||||
params do
|
||||
requires :id, type: String, desc: 'The ID or full path of a project'
|
||||
end
|
||||
|
|
|
@ -4,16 +4,17 @@ module Gitlab
|
|||
module Database
|
||||
module Partitioning
|
||||
class MonthlyStrategy
|
||||
attr_reader :model, :partitioning_key
|
||||
attr_reader :model, :partitioning_key, :retain_for
|
||||
|
||||
# We create this many partitions in the future
|
||||
HEADROOM = 6.months
|
||||
|
||||
delegate :table_name, to: :model
|
||||
|
||||
def initialize(model, partitioning_key)
|
||||
def initialize(model, partitioning_key, retain_for: nil)
|
||||
@model = model
|
||||
@partitioning_key = partitioning_key
|
||||
@retain_for = retain_for
|
||||
end
|
||||
|
||||
def current_partitions
|
||||
|
@ -27,13 +28,21 @@ module Gitlab
|
|||
desired_partitions - current_partitions
|
||||
end
|
||||
|
||||
def extra_partitions
|
||||
current_partitions - desired_partitions
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def desired_partitions
|
||||
[].tap do |parts|
|
||||
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
|
||||
next_date = min_date.next_month
|
||||
|
@ -52,13 +61,17 @@ module Gitlab
|
|||
# to start from MINVALUE to a specific date `x`. The range returned
|
||||
# does not include the range of the first, half-unbounded partition.
|
||||
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 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
|
||||
min_date = first_partition.from || first_partition.to
|
||||
end
|
||||
|
||||
if pruning_old_partitions?
|
||||
min_date ||= oldest_active_date
|
||||
end
|
||||
|
||||
# In case we don't have a partition yet
|
||||
min_date ||= Date.today
|
||||
min_date = min_date.beginning_of_month
|
||||
|
@ -72,6 +85,14 @@ module Gitlab
|
|||
TimePartition.new(table_name, lower_bound, upper_bound)
|
||||
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
|
||||
ActiveRecord::Base.connection
|
||||
end
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
module Gitlab
|
||||
module Database
|
||||
module Partitioning
|
||||
class PartitionCreator
|
||||
class PartitionManager
|
||||
def self.register(model)
|
||||
raise ArgumentError, "Only models with a #partitioning_strategy can be registered." unless model.respond_to?(:partitioning_strategy)
|
||||
|
||||
|
@ -15,7 +15,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
LEASE_TIMEOUT = 1.minute
|
||||
LEASE_KEY = 'database_partition_creation_%s'
|
||||
MANAGEMENT_LEASE_KEY = 'database_partition_management_%s'
|
||||
|
||||
attr_reader :models
|
||||
|
||||
|
@ -23,23 +23,25 @@ module Gitlab
|
|||
@models = models
|
||||
end
|
||||
|
||||
def create_partitions
|
||||
def sync_partitions
|
||||
Gitlab::AppLogger.info("Checking state of dynamic postgres partitions")
|
||||
|
||||
models.each do |model|
|
||||
# Double-checking before getting the lease:
|
||||
# The prevailing situation is no missing partitions
|
||||
next if missing_partitions(model).empty?
|
||||
# The prevailing situation is no missing partitions and no extra partitions
|
||||
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)
|
||||
create(partitions_to_create) unless partitions_to_create.empty?
|
||||
|
||||
next if partitions_to_create.empty?
|
||||
|
||||
create(model, partitions_to_create)
|
||||
if Feature.enabled?(:partition_pruning_dry_run)
|
||||
partitions_to_detach = extra_partitions(model)
|
||||
detach(partitions_to_detach) unless partitions_to_detach.empty?
|
||||
end
|
||||
end
|
||||
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
|
||||
|
||||
|
@ -51,15 +53,22 @@ module Gitlab
|
|||
model.partitioning_strategy.missing_partitions
|
||||
end
|
||||
|
||||
def only_with_exclusive_lease(model)
|
||||
lease = Gitlab::ExclusiveLease.new(LEASE_KEY % model.table_name, timeout: LEASE_TIMEOUT)
|
||||
def extra_partitions(model)
|
||||
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
|
||||
ensure
|
||||
lease&.cancel
|
||||
end
|
||||
|
||||
def create(model, partitions)
|
||||
def create(partitions)
|
||||
connection.transaction do
|
||||
with_lock_retries do
|
||||
partitions.each do |partition|
|
||||
|
@ -71,6 +80,18 @@ module Gitlab
|
|||
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)
|
||||
Gitlab::Database::WithLockRetries.new(
|
||||
klass: self.class,
|
|
@ -6,7 +6,7 @@ module Gitlab
|
|||
class PartitionMonitoring
|
||||
attr_reader :models
|
||||
|
||||
def initialize(models = PartitionCreator.models)
|
||||
def initialize(models = PartitionManager.models)
|
||||
@models = models
|
||||
end
|
||||
|
||||
|
|
|
@ -38,7 +38,10 @@ module Gitlab
|
|||
where('NOT EXISTS (?)', recent_actions)
|
||||
end
|
||||
|
||||
alias_method :reset, :reload
|
||||
def reset
|
||||
reload # rubocop:disable Cop/ActiveRecordAssociationReload
|
||||
clear_memoization(:bloat_size)
|
||||
end
|
||||
|
||||
def bloat_size
|
||||
strong_memoize(:bloat_size) { bloat_estimate&.bloat_size || 0 }
|
||||
|
|
|
@ -1,24 +1,24 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Count objects fetched or imported from Github in the context of the
|
||||
# project being imported.
|
||||
# Count objects fetched or imported from Github.
|
||||
module Gitlab
|
||||
module GithubImport
|
||||
class ObjectCounter
|
||||
OPERATIONS = %w[fetched imported].freeze
|
||||
COUNTER_LIST_KEY = 'github-importer/object-counters-list/%{project}/%{operation}'
|
||||
COUNTER_KEY = 'github-importer/object-counter/%{project}/%{operation}/%{object_type}'
|
||||
PROJECT_COUNTER_LIST_KEY = 'github-importer/object-counters-list/%{project}/%{operation}'
|
||||
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
|
||||
|
||||
class << self
|
||||
def increment(project, object_type, operation)
|
||||
validate_operation!(operation)
|
||||
|
||||
counter_key = COUNTER_KEY % { project: project.id, operation: operation, object_type: object_type }
|
||||
|
||||
add_counter_to_list(project, operation, counter_key)
|
||||
|
||||
CACHING.increment(counter_key)
|
||||
increment_project_counter(project, object_type, operation)
|
||||
increment_global_counter(object_type, operation)
|
||||
end
|
||||
|
||||
def summary(project)
|
||||
|
@ -37,12 +37,40 @@ module Gitlab
|
|||
|
||||
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)
|
||||
CACHING.set_add(counter_list_key(project, operation), key)
|
||||
end
|
||||
|
||||
def counter_list_key(project, operation)
|
||||
COUNTER_LIST_KEY % { project: project.id, operation: operation }
|
||||
PROJECT_COUNTER_LIST_KEY % { project: project.id, operation: operation }
|
||||
end
|
||||
|
||||
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: {
|
||||
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 },
|
||||
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
|
||||
|
|
|
@ -118,7 +118,7 @@ namespace :gitlab do
|
|||
|
||||
desc 'Create missing dynamic database partitions'
|
||||
task create_dynamic_partitions: :environment do
|
||||
Gitlab::Database::Partitioning::PartitionCreator.new.create_partitions
|
||||
Gitlab::Database::Partitioning::PartitionManager.new.sync_partitions
|
||||
end
|
||||
|
||||
# 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."
|
||||
msgstr ""
|
||||
|
||||
msgid "An error occurred while uploading the image. Please try again."
|
||||
msgstr ""
|
||||
|
||||
msgid "An error occurred while validating group path"
|
||||
msgstr ""
|
||||
|
||||
|
@ -11683,6 +11686,9 @@ msgstr ""
|
|||
msgid "Download %{name} artifact"
|
||||
msgstr ""
|
||||
|
||||
msgid "Download (%{fileSizeReadable})"
|
||||
msgstr ""
|
||||
|
||||
msgid "Download (%{size})"
|
||||
msgstr ""
|
||||
|
||||
|
@ -13075,9 +13081,6 @@ msgstr ""
|
|||
msgid "EscalationPolicies|Edit escalation policy"
|
||||
msgstr ""
|
||||
|
||||
msgid "EscalationPolicies|Elapsed time must be greater than or equal to zero."
|
||||
msgstr ""
|
||||
|
||||
msgid "EscalationPolicies|Email on-call user in schedule"
|
||||
msgstr ""
|
||||
|
||||
|
@ -13096,6 +13099,9 @@ msgstr ""
|
|||
msgid "EscalationPolicies|IF alert is not %{alertStatus} in %{minutes} minutes"
|
||||
msgstr ""
|
||||
|
||||
msgid "EscalationPolicies|Minutes must be between 0 and 1440."
|
||||
msgstr ""
|
||||
|
||||
msgid "EscalationPolicies|Remove escalation rule"
|
||||
msgstr ""
|
||||
|
||||
|
@ -16757,6 +16763,9 @@ msgstr[1] ""
|
|||
msgid "Importing..."
|
||||
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"
|
||||
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."
|
||||
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"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -157,6 +157,7 @@
|
|||
"prosemirror-inputrules": "^1.1.3",
|
||||
"prosemirror-markdown": "^1.5.1",
|
||||
"prosemirror-model": "^1.13.3",
|
||||
"prosemirror-state": "^1.3.4",
|
||||
"raphael": "^2.2.7",
|
||||
"raw-loader": "^4.0.2",
|
||||
"scrollparent": "^2.0.1",
|
||||
|
|
|
@ -24,7 +24,7 @@ gem 'parallel', '~> 1.19'
|
|||
gem 'rspec-parameterized', '~> 0.4.2'
|
||||
gem 'github_api', '~> 0.18.2'
|
||||
|
||||
gem 'chemlab', '~> 0.5'
|
||||
gem 'chemlab', '~> 0.7'
|
||||
gem 'chemlab-library-www-gitlab-com', '~> 0.1'
|
||||
|
||||
group :development do
|
||||
|
|
|
@ -41,18 +41,21 @@ GEM
|
|||
capybara-screenshot (1.0.23)
|
||||
capybara (>= 1.0, < 4)
|
||||
launchy
|
||||
chemlab (0.5.0)
|
||||
rake (~> 12.3.0)
|
||||
selenium-webdriver (~> 3.12)
|
||||
watir (~> 6.17)
|
||||
chemlab (0.7.2)
|
||||
colorize (~> 0.8)
|
||||
i18n (~> 1.8)
|
||||
rake (>= 12, < 14)
|
||||
selenium-webdriver (>= 3, < 5)
|
||||
watir (>= 6, < 8)
|
||||
chemlab-library-www-gitlab-com (0.1.1)
|
||||
chemlab (~> 0.4)
|
||||
childprocess (4.1.0)
|
||||
coderay (1.1.2)
|
||||
colorize (0.8.1)
|
||||
concord (0.1.5)
|
||||
adamantium (~> 0.2.0)
|
||||
equalizer (~> 0.0.9)
|
||||
concurrent-ruby (1.1.8)
|
||||
concurrent-ruby (1.1.9)
|
||||
descendants_tracker (0.0.4)
|
||||
thread_safe (~> 0.3, >= 0.3.1)
|
||||
diff-lcs (1.3)
|
||||
|
@ -129,7 +132,7 @@ GEM
|
|||
rack-test (1.1.0)
|
||||
rack (>= 1.0, < 3)
|
||||
rake (12.3.3)
|
||||
regexp_parser (1.6.0)
|
||||
regexp_parser (1.8.2)
|
||||
require_all (3.0.0)
|
||||
rest-client (2.1.0)
|
||||
http-accept (>= 1.7.0, < 2.0)
|
||||
|
@ -163,7 +166,7 @@ GEM
|
|||
rspec-core (>= 2, < 4, != 2.12.0)
|
||||
ruby-debug-ide (0.7.2)
|
||||
rake (>= 0.8.1)
|
||||
rubyzip (2.3.0)
|
||||
rubyzip (2.3.2)
|
||||
selenium-webdriver (4.0.0.beta4)
|
||||
childprocess (>= 0.5, < 5.0)
|
||||
rexml (~> 3.2)
|
||||
|
@ -186,9 +189,9 @@ GEM
|
|||
procto (~> 0.0.2)
|
||||
uuid (2.3.9)
|
||||
macaddr (~> 1.0)
|
||||
watir (6.18.0)
|
||||
watir (6.19.1)
|
||||
regexp_parser (>= 1.2, < 3)
|
||||
selenium-webdriver (>= 3.8)
|
||||
selenium-webdriver (>= 3.142.7)
|
||||
xpath (3.2.0)
|
||||
nokogiri (~> 1.8)
|
||||
zeitwerk (2.4.2)
|
||||
|
@ -202,7 +205,7 @@ DEPENDENCIES
|
|||
allure-rspec (~> 2.14.1)
|
||||
capybara (~> 3.29.0)
|
||||
capybara-screenshot (~> 1.0.23)
|
||||
chemlab (~> 0.5)
|
||||
chemlab (~> 0.7)
|
||||
chemlab-library-www-gitlab-com (~> 0.1)
|
||||
faker (~> 1.6, >= 1.6.6)
|
||||
github_api (~> 0.18.2)
|
||||
|
@ -224,4 +227,4 @@ DEPENDENCIES
|
|||
timecop (~> 0.9.1)
|
||||
|
||||
BUNDLED WITH
|
||||
2.1.4
|
||||
2.2.22
|
||||
|
|
|
@ -38,7 +38,7 @@ module QA
|
|||
element :project_creation_level_dropdown
|
||||
end
|
||||
|
||||
view 'app/views/groups/settings/_advanced.html.haml' do
|
||||
view 'app/views/groups/settings/_transfer.html.haml' do
|
||||
element :select_group_dropdown
|
||||
element :transfer_group_button
|
||||
end
|
||||
|
|
|
@ -179,6 +179,7 @@ module QA
|
|||
config.browser = Capybara.current_session.driver.browser # reuse Capybara session
|
||||
config.libraries = [GitlabHandbook]
|
||||
config.base_url = Runtime::Scenario.attributes[:gitlab_address] # reuse GitLab address
|
||||
config.hide_banner = true
|
||||
end
|
||||
end
|
||||
# rubocop: enable Metrics/AbcSize
|
||||
|
|
|
@ -116,6 +116,12 @@ class AutomatedCleanup
|
|||
delete_helm_releases(releases_to_delete)
|
||||
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:)
|
||||
kubernetes.cleanup_by_created_at(resource_type: 'pvc', created_before: threshold_time(days: days), wait: false)
|
||||
end
|
||||
|
@ -203,6 +209,10 @@ timed('Helm releases cleanup') do
|
|||
automated_cleanup.perform_helm_releases_cleanup!(days: 7)
|
||||
end
|
||||
|
||||
timed('Stale Namespace cleanup') do
|
||||
automated_cleanup.perform_stale_namespace_cleanup!(days: 14)
|
||||
end
|
||||
|
||||
timed('Stale PVC cleanup') do
|
||||
automated_cleanup.perform_stale_pvc_cleanup!(days: 30)
|
||||
end
|
||||
|
|
|
@ -5,5 +5,6 @@ FactoryBot.define do
|
|||
build factory: :ci_build
|
||||
project
|
||||
protected { build.protected }
|
||||
instance_runners_enabled { true }
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,15 +5,16 @@ require 'spec_helper'
|
|||
RSpec.describe 'Dropdown assignee', :js do
|
||||
include FilteredSearchHelpers
|
||||
|
||||
let!(:project) { create(:project) }
|
||||
let!(:user) { create(:user, name: 'administrator', username: 'root') }
|
||||
let_it_be(:project) { create(:project) }
|
||||
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(:filter_dropdown) { find("#{js_dropdown_assignee} .filter-dropdown") }
|
||||
|
||||
before do
|
||||
project.add_maintainer(user)
|
||||
sign_in(user)
|
||||
create(:issue, project: project)
|
||||
|
||||
visit project_issues_path(project)
|
||||
end
|
||||
|
|
|
@ -5,15 +5,16 @@ require 'spec_helper'
|
|||
RSpec.describe 'Dropdown author', :js do
|
||||
include FilteredSearchHelpers
|
||||
|
||||
let!(:project) { create(:project) }
|
||||
let!(:user) { create(:user, name: 'administrator', username: 'root') }
|
||||
let_it_be(:project) { create(:project) }
|
||||
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(:filter_dropdown) { find("#{js_dropdown_author} .filter-dropdown") }
|
||||
|
||||
before do
|
||||
project.add_maintainer(user)
|
||||
sign_in(user)
|
||||
create(:issue, project: project)
|
||||
|
||||
visit project_issues_path(project)
|
||||
end
|
||||
|
|
|
@ -5,8 +5,10 @@ require 'spec_helper'
|
|||
RSpec.describe 'Dropdown base', :js do
|
||||
include FilteredSearchHelpers
|
||||
|
||||
let!(:project) { create(:project) }
|
||||
let!(:user) { create(:user, name: 'administrator', username: 'root') }
|
||||
let_it_be(:project) { create(:project) }
|
||||
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(:js_dropdown_assignee) { '#js-dropdown-assignee' }
|
||||
let(:filter_dropdown) { find("#{js_dropdown_assignee} .filter-dropdown") }
|
||||
|
@ -18,7 +20,6 @@ RSpec.describe 'Dropdown base', :js do
|
|||
before do
|
||||
project.add_maintainer(user)
|
||||
sign_in(user)
|
||||
create(:issue, project: project)
|
||||
|
||||
visit project_issues_path(project)
|
||||
end
|
||||
|
|
|
@ -5,10 +5,11 @@ require 'spec_helper'
|
|||
RSpec.describe 'Dropdown emoji', :js do
|
||||
include FilteredSearchHelpers
|
||||
|
||||
let!(:project) { create(:project, :public) }
|
||||
let!(:user) { create(:user, name: 'administrator', username: 'root') }
|
||||
let!(:issue) { create(:issue, project: project) }
|
||||
let!(:award_emoji_star) { create(:award_emoji, name: 'star', user: user, awardable: issue) }
|
||||
let_it_be(:project) { create(:project, :public) }
|
||||
let_it_be(:user) { create(:user, name: 'administrator', username: 'root') }
|
||||
let_it_be(:issue) { create(:issue, project: project) }
|
||||
let_it_be(:award_emoji_star) { create(:award_emoji, name: 'star', user: user, awardable: issue) }
|
||||
|
||||
let(:filtered_search) { find('.filtered-search') }
|
||||
let(:js_dropdown_emoji) { '#js-dropdown-my-reaction' }
|
||||
let(:filter_dropdown) { find("#{js_dropdown_emoji} .filter-dropdown") }
|
||||
|
|
|
@ -5,8 +5,10 @@ require 'spec_helper'
|
|||
RSpec.describe 'Dropdown hint', :js do
|
||||
include FilteredSearchHelpers
|
||||
|
||||
let!(:project) { create(:project, :public) }
|
||||
let!(:user) { create(:user) }
|
||||
let_it_be(:project) { create(:project, :public) }
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:issue) { create(:issue, project: project) }
|
||||
|
||||
let(:filtered_search) { find('.filtered-search') }
|
||||
let(:js_dropdown_hint) { '#js-dropdown-hint' }
|
||||
let(:js_dropdown_operator) { '#js-dropdown-operator' }
|
||||
|
@ -21,8 +23,6 @@ RSpec.describe 'Dropdown hint', :js do
|
|||
|
||||
before do
|
||||
project.add_maintainer(user)
|
||||
create(:issue, project: project)
|
||||
create(:merge_request, source_project: project, target_project: project)
|
||||
end
|
||||
|
||||
context 'when user not logged in' do
|
||||
|
|
|
@ -5,22 +5,23 @@ require 'spec_helper'
|
|||
RSpec.describe 'Dropdown label', :js do
|
||||
include FilteredSearchHelpers
|
||||
|
||||
let(:project) { create(:project) }
|
||||
let(:user) { create(:user) }
|
||||
let_it_be(:project) { create(:project) }
|
||||
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(:filter_dropdown) { find('#js-dropdown-label .filter-dropdown') }
|
||||
|
||||
before do
|
||||
project.add_maintainer(user)
|
||||
sign_in(user)
|
||||
create(:issue, project: project)
|
||||
|
||||
visit project_issues_path(project)
|
||||
end
|
||||
|
||||
describe 'behavior' do
|
||||
it 'loads all the labels when opened' do
|
||||
create(:label, project: project, title: 'bug-label')
|
||||
filtered_search.set('label:=')
|
||||
|
||||
expect_filtered_search_dropdown_results(filter_dropdown, 1)
|
||||
|
|
|
@ -5,10 +5,11 @@ require 'spec_helper'
|
|||
RSpec.describe 'Dropdown milestone', :js do
|
||||
include FilteredSearchHelpers
|
||||
|
||||
let!(:project) { create(:project) }
|
||||
let!(:user) { create(:user) }
|
||||
let!(:milestone) { create(:milestone, title: 'v1.0', project: project) }
|
||||
let!(:uppercase_milestone) { create(:milestone, title: 'CAP_MILESTONE', project: project) }
|
||||
let_it_be(:project) { create(:project) }
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:milestone) { create(:milestone, title: 'v1.0', 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(:filter_dropdown) { find('#js-dropdown-milestone .filter-dropdown') }
|
||||
|
@ -16,7 +17,6 @@ RSpec.describe 'Dropdown milestone', :js do
|
|||
before do
|
||||
project.add_maintainer(user)
|
||||
sign_in(user)
|
||||
create(:issue, project: project)
|
||||
|
||||
visit project_issues_path(project)
|
||||
end
|
||||
|
|
|
@ -5,10 +5,11 @@ require 'spec_helper'
|
|||
RSpec.describe 'Dropdown release', :js do
|
||||
include FilteredSearchHelpers
|
||||
|
||||
let!(:project) { create(:project, :repository) }
|
||||
let!(:user) { create(:user) }
|
||||
let!(:release) { create(:release, tag: 'v1.0', project: project) }
|
||||
let!(:crazy_release) { create(:release, tag: '☺!/"#%&\'{}+,-.<>;=@]_`{|}🚀', project: project) }
|
||||
let_it_be(:project) { create(:project, :repository) }
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:release) { create(:release, tag: 'v1.0', 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(:filter_dropdown) { find('#js-dropdown-release .filter-dropdown') }
|
||||
|
@ -16,7 +17,6 @@ RSpec.describe 'Dropdown release', :js do
|
|||
before do
|
||||
project.add_maintainer(user)
|
||||
sign_in(user)
|
||||
create(:issue, project: project)
|
||||
|
||||
visit project_issues_path(project)
|
||||
end
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue