Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
61829a6fc9
commit
63fde89de0
|
|
@ -1,6 +1,6 @@
|
|||
import $ from 'jquery';
|
||||
import Pikaday from 'pikaday';
|
||||
import { parsePikadayDate, toISODateFormat } from '~/lib/utils/datetime_utility';
|
||||
import { newDate, toISODateFormat } from '~/lib/utils/datetime_utility';
|
||||
|
||||
export default function initDatePickers() {
|
||||
$('.datepicker').each(function initPikaday() {
|
||||
|
|
@ -12,7 +12,7 @@ export default function initDatePickers() {
|
|||
theme: 'gl-datepicker-theme animate-picker',
|
||||
format: 'yyyy-mm-dd',
|
||||
container: $datePicker.parent().get(0),
|
||||
parse: (dateString) => parsePikadayDate(dateString),
|
||||
parse: (dateString) => newDate(dateString),
|
||||
toString: (date) => toISODateFormat(date),
|
||||
onSelect(dateText) {
|
||||
$datePicker.val(calendar.toString(dateText));
|
||||
|
|
@ -20,7 +20,7 @@ export default function initDatePickers() {
|
|||
firstDay: gon.first_day_of_week,
|
||||
});
|
||||
|
||||
calendar.setDate(parsePikadayDate(datePickerVal));
|
||||
calendar.setDate(newDate(datePickerVal));
|
||||
|
||||
$datePicker.data('pikaday', calendar);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,12 +1,7 @@
|
|||
<script>
|
||||
import { GlTooltip, GlIcon } from '@gitlab/ui';
|
||||
import dateFormat from '~/lib/dateformat';
|
||||
import {
|
||||
getDayDifference,
|
||||
getTimeago,
|
||||
dateInWords,
|
||||
parsePikadayDate,
|
||||
} from '~/lib/utils/datetime_utility';
|
||||
import { getDayDifference, getTimeago, dateInWords, newDate } from '~/lib/utils/datetime_utility';
|
||||
import { __ } from '~/locale';
|
||||
|
||||
export default {
|
||||
|
|
@ -66,7 +61,7 @@ export default {
|
|||
return standardDateFormat;
|
||||
},
|
||||
issueDueDate() {
|
||||
return parsePikadayDate(this.date);
|
||||
return newDate(this.date);
|
||||
},
|
||||
timeDifference() {
|
||||
const today = new Date();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { identity, memoize, isEmpty } from 'lodash';
|
||||
import { initEmojiMap, getAllEmoji, searchEmoji } from '~/emoji';
|
||||
import { parsePikadayDate } from '~/lib/utils/datetime_utility';
|
||||
import { newDate } from '~/lib/utils/datetime_utility';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { COMMANDS } from '../constants';
|
||||
|
||||
|
|
@ -38,7 +38,7 @@ function parseMilestone(milestone) {
|
|||
return milestone;
|
||||
}
|
||||
|
||||
const dueDate = milestone.due_date ? parsePikadayDate(milestone.due_date) : null;
|
||||
const dueDate = milestone.due_date ? newDate(milestone.due_date) : null;
|
||||
const expired = dueDate ? Date.now() > dueDate.getTime() : false;
|
||||
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import {
|
|||
} from '~/work_items/constants';
|
||||
import AjaxCache from './lib/utils/ajax_cache';
|
||||
import { spriteIcon } from './lib/utils/common_utils';
|
||||
import { parsePikadayDate } from './lib/utils/datetime_utility';
|
||||
import { newDate } from './lib/utils/datetime_utility';
|
||||
import { unicodeLetters } from './lib/utils/regexp';
|
||||
import { renderVueComponentForLegacyJS } from './render_vue_component_for_legacy_js';
|
||||
|
||||
|
|
@ -556,7 +556,7 @@ class GfmAutoComplete {
|
|||
return m;
|
||||
}
|
||||
|
||||
const dueDate = m.due_date ? parsePikadayDate(m.due_date) : null;
|
||||
const dueDate = m.due_date ? newDate(m.due_date) : null;
|
||||
const expired = dueDate ? Date.now() > dueDate.getTime() : false;
|
||||
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ export const TYPENAME_DESIGN_VERSION = 'DesignManagement::Version';
|
|||
export const TYPENAME_DISCUSSION = 'Discussion';
|
||||
export const TYPENAME_EPIC = 'Epic';
|
||||
export const TYPENAME_EPIC_BOARD = 'Boards::EpicBoard';
|
||||
export const TYPENAME_FEATURE_FLAG = 'FeatureFlag';
|
||||
export const TYPENAME_GROUP = 'Group';
|
||||
export const TYPENAME_ISSUE = 'Issue';
|
||||
export const TYPENAME_ITERATION = 'Iteration';
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import $ from 'jquery';
|
|||
import Pikaday from 'pikaday';
|
||||
import GfmAutoComplete from 'ee_else_ce/gfm_auto_complete';
|
||||
import Autosave from '~/autosave';
|
||||
import { parsePikadayDate, toISODateFormat } from '~/lib/utils/datetime_utility';
|
||||
import { newDate, toISODateFormat } from '~/lib/utils/datetime_utility';
|
||||
import { queryToObject, objectToQuery } from '~/lib/utils/url_utility';
|
||||
import UsersSelect from '~/users_select';
|
||||
import ZenMode from '~/zen_mode';
|
||||
|
|
@ -113,7 +113,7 @@ export default class IssuableForm {
|
|||
theme: 'gl-datepicker-theme animate-picker',
|
||||
format: 'yyyy-mm-dd',
|
||||
container: $issuableDueDate.parent().get(0),
|
||||
parse: (dateString) => parsePikadayDate(dateString),
|
||||
parse: (dateString) => newDate(dateString),
|
||||
toString: (date) => toISODateFormat(date),
|
||||
onSelect: (dateText) => {
|
||||
$issuableDueDate.val(calendar.toString(dateText));
|
||||
|
|
@ -121,7 +121,7 @@ export default class IssuableForm {
|
|||
},
|
||||
firstDay: gon.first_day_of_week,
|
||||
});
|
||||
calendar.setDate(parsePikadayDate($issuableDueDate.val()));
|
||||
calendar.setDate(newDate($issuableDueDate.val()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +0,0 @@
|
|||
/**
|
||||
* Formats dates in Pickaday
|
||||
* @param {String} dateString Date in yyyy-mm-dd format
|
||||
* @return {Date} UTC format
|
||||
*/
|
||||
export const parsePikadayDate = (dateString) => {
|
||||
const parts = dateString.split('-');
|
||||
const year = parseInt(parts[0], 10);
|
||||
const month = parseInt(parts[1] - 1, 10);
|
||||
const day = parseInt(parts[2], 10);
|
||||
|
||||
return new Date(year, month, day);
|
||||
};
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
export * from './datetime/timeago_utility';
|
||||
export * from './datetime/date_format_utility';
|
||||
export * from './datetime/date_calculation_utility';
|
||||
export * from './datetime/pikaday_utility';
|
||||
export * from './datetime/date_format_utility';
|
||||
export * from './datetime/locale_dateformat';
|
||||
export * from './datetime/timeago_utility';
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { parsePikadayDate } from '~/lib/utils/datetime_utility';
|
||||
import { newDate } from '~/lib/utils/datetime_utility';
|
||||
|
||||
/**
|
||||
* This method is to be used with `Array.prototype.sort` function
|
||||
|
|
@ -14,8 +14,8 @@ import { parsePikadayDate } from '~/lib/utils/datetime_utility';
|
|||
export function sortMilestonesByDueDate(milestoneA, milestoneB) {
|
||||
const rawDueDateA = milestoneA.due_date || milestoneA.dueDate;
|
||||
const rawDueDateB = milestoneB.due_date || milestoneB.dueDate;
|
||||
const dueDateA = rawDueDateA ? parsePikadayDate(rawDueDateA) : null;
|
||||
const dueDateB = rawDueDateB ? parsePikadayDate(rawDueDateB) : null;
|
||||
const dueDateA = rawDueDateA ? newDate(rawDueDateA) : null;
|
||||
const dueDateB = rawDueDateB ? newDate(rawDueDateB) : null;
|
||||
const expiredA = milestoneA.expired || Date.now() > dueDateA?.getTime();
|
||||
const expiredB = milestoneB.expired || Date.now() > dueDateB?.getTime();
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import MetadataItem from '~/vue_shared/components/registry/metadata_item.vue';
|
|||
import TitleArea from '~/vue_shared/components/registry/title_area.vue';
|
||||
import { MODEL_ENTITIES } from '~/ml/model_registry/constants';
|
||||
import ModelVersionList from '~/ml/model_registry/components/model_version_list.vue';
|
||||
import CandidateList from '~/ml/model_registry/components/candidate_list.vue';
|
||||
import ModelDetail from '~/ml/model_registry/components/model_detail.vue';
|
||||
import ModelVersionCreate from '~/ml/model_registry/components/model_version_create.vue';
|
||||
import ActionsDropdown from '~/ml/model_registry/components/actions_dropdown.vue';
|
||||
|
|
@ -21,7 +20,6 @@ import ModelEdit from '../components/model_edit.vue';
|
|||
|
||||
const ROUTE_DETAILS = 'details';
|
||||
const ROUTE_VERSIONS = 'versions';
|
||||
const ROUTE_CANDIDATES = 'candidates';
|
||||
|
||||
const deletionSuccessfulAlert = {
|
||||
id: 'ml-model-deleted-successfully',
|
||||
|
|
@ -40,11 +38,6 @@ const routes = [
|
|||
name: ROUTE_VERSIONS,
|
||||
component: ModelVersionList,
|
||||
},
|
||||
{
|
||||
path: '/candidates',
|
||||
name: ROUTE_CANDIDATES,
|
||||
component: CandidateList,
|
||||
},
|
||||
{ path: '*', redirect: { name: ROUTE_DETAILS } },
|
||||
];
|
||||
|
||||
|
|
@ -142,9 +135,6 @@ export default {
|
|||
versionCount() {
|
||||
return this.model?.versionCount || 0;
|
||||
},
|
||||
candidateCount() {
|
||||
return this.model?.candidateCount || 0;
|
||||
},
|
||||
tabIndex() {
|
||||
return routes.findIndex(({ name }) => name === this.$route.name);
|
||||
},
|
||||
|
|
@ -185,7 +175,6 @@ export default {
|
|||
modelVersionEntity: MODEL_ENTITIES.modelVersion,
|
||||
ROUTE_DETAILS,
|
||||
ROUTE_VERSIONS,
|
||||
ROUTE_CANDIDATES,
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
@ -229,12 +218,6 @@ export default {
|
|||
<gl-badge class="gl-tab-counter-badge">{{ versionCount }}</gl-badge>
|
||||
</template>
|
||||
</gl-tab>
|
||||
<gl-tab @click="goTo($options.ROUTE_CANDIDATES)">
|
||||
<template #title>
|
||||
{{ s__('MlModelRegistry|Version candidates') }}
|
||||
<gl-badge class="gl-tab-counter-badge">{{ candidateCount }}</gl-badge>
|
||||
</template>
|
||||
</gl-tab>
|
||||
|
||||
<router-view :model-id="model.id" :model="model" />
|
||||
</gl-tabs>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<script>
|
||||
import { GlEmptyState, GlSprintf, GlLink, GlSkeletonLoader } from '@gitlab/ui';
|
||||
import EmptyResult from '~/vue_shared/components/empty_result.vue';
|
||||
import HarborListHeader from '~/packages_and_registries/harbor_registry/components/list/harbor_list_header.vue';
|
||||
import PersistedSearch from '~/packages_and_registries/shared/components/persisted_search.vue';
|
||||
import HarborList from '~/packages_and_registries/harbor_registry/components/list/harbor_list.vue';
|
||||
|
|
@ -37,6 +38,7 @@ export default {
|
|||
GlEmptyState,
|
||||
GlSprintf,
|
||||
GlLink,
|
||||
EmptyResult,
|
||||
PersistedSearch,
|
||||
CliCommands: () =>
|
||||
import(
|
||||
|
|
@ -164,7 +166,6 @@ export default {
|
|||
v-if="showConnectionError"
|
||||
:title="$options.i18n.connectionErrorTitle"
|
||||
:svg-path="containersErrorImage"
|
||||
:svg-height="null"
|
||||
>
|
||||
<template #description>
|
||||
<p>
|
||||
|
|
@ -218,10 +219,10 @@ export default {
|
|||
@prev-page="fetchPrevPage"
|
||||
@next-page="fetchNextPage"
|
||||
/>
|
||||
<empty-result v-else-if="name" data-testid="emptySearch" />
|
||||
<gl-empty-state
|
||||
v-else
|
||||
:svg-path="noContainersImage"
|
||||
:svg-height="null"
|
||||
data-testid="emptySearch"
|
||||
:title="emptyStateTexts.title"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import { GlFormRadio } from '@gitlab/ui';
|
||||
import { dateInWords, parsePikadayDate } from '~/lib/utils/datetime_utility';
|
||||
import { dateInWords, newDate } from '~/lib/utils/datetime_utility';
|
||||
import { __ } from '~/locale';
|
||||
import { dateFields } from '../../constants';
|
||||
import SidebarFormattedDate from './sidebar_formatted_date.vue';
|
||||
|
|
@ -45,7 +45,7 @@ export default {
|
|||
return this.$options.i18n.noDate;
|
||||
}
|
||||
|
||||
return dateInWords(parsePikadayDate(dateFixed), true);
|
||||
return dateInWords(newDate(dateFixed), true);
|
||||
},
|
||||
formattedInheritedDate() {
|
||||
const dateFromMilestones = this.issuable[dateFields[this.dateType].dateFromMilestones];
|
||||
|
|
@ -53,7 +53,7 @@ export default {
|
|||
return this.$options.i18n.noDate;
|
||||
}
|
||||
|
||||
return dateInWords(parsePikadayDate(dateFromMilestones), true);
|
||||
return dateInWords(newDate(dateFromMilestones), true);
|
||||
},
|
||||
},
|
||||
i18n: {
|
||||
|
|
|
|||
|
|
@ -85,6 +85,9 @@ export default {
|
|||
linkedMergeRequests() {
|
||||
return this.workItemDevelopment?.closingMergeRequests?.nodes || [];
|
||||
},
|
||||
featureFlags() {
|
||||
return this.workItemDevelopment?.featureFlags?.nodes || [];
|
||||
},
|
||||
shouldShowEmptyState() {
|
||||
return this.isRelatedDevelopmentListEmpty ? this.workItemsAlphaEnabled : true;
|
||||
},
|
||||
|
|
@ -92,7 +95,7 @@ export default {
|
|||
return this.workItemDevelopment && this.shouldShowEmptyState;
|
||||
},
|
||||
isRelatedDevelopmentListEmpty() {
|
||||
return !this.error && this.linkedMergeRequests.length === 0;
|
||||
return !this.error && this.linkedMergeRequests.length === 0 && this.featureFlags.length === 0;
|
||||
},
|
||||
showAutoCloseInformation() {
|
||||
return (
|
||||
|
|
@ -171,7 +174,11 @@ export default {
|
|||
:aria-label="__('Add branch or merge request')"
|
||||
/>
|
||||
</div>
|
||||
<template v-if="isRelatedDevelopmentListEmpty">
|
||||
<work-item-development-relationship-list
|
||||
v-if="!isRelatedDevelopmentListEmpty"
|
||||
:work-item-dev-widget="workItemDevelopment"
|
||||
/>
|
||||
<template v-else>
|
||||
<span v-if="!canUpdate" class="gl-text-secondary">{{ __('None') }}</span>
|
||||
<template v-else>
|
||||
<gl-button category="secondary" size="small" data-testid="create-mr-button">{{
|
||||
|
|
@ -182,6 +189,5 @@ export default {
|
|||
}}</gl-button>
|
||||
</template>
|
||||
</template>
|
||||
<work-item-development-relationship-list v-else :work-item-dev-widget="workItemDevelopment" />
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
<script>
|
||||
import { GlIcon, GlTooltip, GlLink } from '@gitlab/ui';
|
||||
import { __ } from '~/locale';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlIcon,
|
||||
GlTooltip,
|
||||
GlLink,
|
||||
},
|
||||
props: {
|
||||
itemContent: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
icon({ active }) {
|
||||
return active ? 'feature-flag' : 'feature-flag-disabled';
|
||||
},
|
||||
iconColor({ active }) {
|
||||
return active ? 'gl-text-blue-500' : 'gl-text-gray-500';
|
||||
},
|
||||
flagStatus(flag) {
|
||||
return flag.active ? __('Enabled') : __('Disabled');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
ref="flagInfo"
|
||||
class="gl-grid-cols-[auto, 1fr] gl-grid gl-w-fit gl-gap-2 gl-gap-5 gl-p-2 gl-pl-0 gl-pr-3"
|
||||
>
|
||||
<gl-link
|
||||
:href="itemContent.path"
|
||||
class="gl-truncate gl-text-primary hover:gl-text-primary hover:gl-underline"
|
||||
>
|
||||
<gl-icon :name="icon(itemContent)" :class="iconColor(itemContent)" />
|
||||
{{ itemContent.name }}
|
||||
</gl-link>
|
||||
<gl-tooltip :target="() => $refs.flagInfo" placement="top">
|
||||
<span class="gl-inline-block gl-font-bold"> {{ __('Feature flag') }} </span>
|
||||
<span class="gl-inline-block">{{ itemContent.name }} {{ itemContent.reference }}</span>
|
||||
<span class="gl-inline-block gl-text-secondary">{{ flagStatus(itemContent) }}</span>
|
||||
</gl-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -22,20 +22,20 @@ export default {
|
|||
GlTooltip: GlTooltipDirective,
|
||||
},
|
||||
props: {
|
||||
mergeRequest: {
|
||||
itemContent: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
assignees() {
|
||||
return this.mergeRequest?.assignees?.nodes || [];
|
||||
return this.itemContent?.assignees?.nodes || [];
|
||||
},
|
||||
stateIconClass() {
|
||||
return {
|
||||
'gl-text-green-500': this.mergeRequest.state === STATUS_OPEN,
|
||||
'gl-text-red-500': this.mergeRequest.state === STATUS_CLOSED,
|
||||
'gl-text-blue-500': this.mergeRequest.state === STATUS_MERGED,
|
||||
'gl-text-green-500': this.itemContent.state === STATUS_OPEN,
|
||||
'gl-text-red-500': this.itemContent.state === STATUS_CLOSED,
|
||||
'gl-text-blue-500': this.itemContent.state === STATUS_MERGED,
|
||||
};
|
||||
},
|
||||
stateIcon() {
|
||||
|
|
@ -44,7 +44,7 @@ export default {
|
|||
[STATUS_MERGED]: 'merge',
|
||||
[STATUS_CLOSED]: 'merge-request-close',
|
||||
};
|
||||
return stateIcons[this.mergeRequest.state];
|
||||
return stateIcons[this.itemContent.state];
|
||||
},
|
||||
assigneesCollapsedTooltip() {
|
||||
if (this.assignees.length > 2) {
|
||||
|
|
@ -55,7 +55,7 @@ export default {
|
|||
return '';
|
||||
},
|
||||
projectPath() {
|
||||
return `${this.mergeRequest.project.namespace.path}/${this.mergeRequest.project.name}`;
|
||||
return `${this.itemContent.project.namespace.path}/${this.itemContent.project.name}`;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
@ -63,15 +63,15 @@ export default {
|
|||
<template>
|
||||
<div class="gl-mb-2 gl-flex gl-items-center gl-justify-between gl-gap-2">
|
||||
<gl-link
|
||||
:href="mergeRequest.webUrl"
|
||||
:href="itemContent.webUrl"
|
||||
class="gfm-merge_request gl-truncate gl-text-gray-900 hover:gl-text-gray-900 hover:gl-underline"
|
||||
data-reference-type="merge_request"
|
||||
:data-project-path="projectPath"
|
||||
:data-iid="mergeRequest.iid"
|
||||
:data-mr-title="mergeRequest.title"
|
||||
:data-iid="itemContent.iid"
|
||||
:data-mr-title="itemContent.title"
|
||||
data-placement="left"
|
||||
>
|
||||
<gl-icon :name="stateIcon" :class="stateIconClass" /> {{ mergeRequest.title }}
|
||||
<gl-icon :name="stateIcon" :class="stateIconClass" /> {{ itemContent.title }}
|
||||
</gl-link>
|
||||
<gl-avatars-inline
|
||||
v-if="assignees.length"
|
||||
|
|
|
|||
|
|
@ -2,8 +2,10 @@
|
|||
import { GlButton } from '@gitlab/ui';
|
||||
import { sprintf, __ } from '~/locale';
|
||||
import { renderGFM } from '~/behaviors/markdown/render_gfm';
|
||||
import { TYPENAME_FEATURE_FLAG } from '~/graphql_shared/constants';
|
||||
|
||||
import WorkItemDevelopmentMrItem from './work_item_development_mr_item.vue';
|
||||
import WorkItemDevelopmentFfItem from './work_item_development_ff_item.vue';
|
||||
|
||||
const DEFAULT_RENDER_COUNT = 3;
|
||||
|
||||
|
|
@ -25,12 +27,22 @@ export default {
|
|||
},
|
||||
computed: {
|
||||
list() {
|
||||
// keeping as a separate prop , will be appending with FF and branches
|
||||
return [...this.mergeRequests];
|
||||
// keeping as a separate prop, will be appending with branches
|
||||
return [...this.sortedFeatureFlags, ...this.mergeRequests];
|
||||
},
|
||||
mergeRequests() {
|
||||
return this.workItemDevWidget.closingMergeRequests?.nodes || [];
|
||||
},
|
||||
featureFlags() {
|
||||
return this.workItemDevWidget.featureFlags?.nodes || [];
|
||||
},
|
||||
sortedFeatureFlags() {
|
||||
const flagsSortedByRelationshipDate = [...this.featureFlags].reverse();
|
||||
const enabledFlags = flagsSortedByRelationshipDate.filter((flag) => flag.active);
|
||||
const disabledFlags = flagsSortedByRelationshipDate.filter((flag) => !flag.active);
|
||||
|
||||
return [...enabledFlags, ...disabledFlags];
|
||||
},
|
||||
hiddenItemsLabel() {
|
||||
const { moreCount } = this;
|
||||
return sprintf(__('+ %{moreCount} more'), { moreCount });
|
||||
|
|
@ -53,11 +65,24 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
itemComponent(item) {
|
||||
return this.isMergeRequest(item) ? WorkItemDevelopmentMrItem : 'li';
|
||||
let component;
|
||||
|
||||
if (this.isMergeRequest(item)) {
|
||||
component = WorkItemDevelopmentMrItem;
|
||||
} else if (this.isFeatureFlag(item)) {
|
||||
component = WorkItemDevelopmentFfItem;
|
||||
} else {
|
||||
component = 'li';
|
||||
}
|
||||
return component;
|
||||
},
|
||||
isMergeRequest(item) {
|
||||
return item.fromMrDescription !== undefined;
|
||||
},
|
||||
isFeatureFlag(item) {
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
return item.__typename === TYPENAME_FEATURE_FLAG;
|
||||
},
|
||||
async toggleShowLess() {
|
||||
this.showLess = !this.showLess;
|
||||
await this.$nextTick();
|
||||
|
|
@ -76,7 +101,7 @@ export default {
|
|||
<div>
|
||||
<ul ref="list-body" class="gl-m-0 gl-list-none gl-p-0" data-testid="work-item-dev-items-list">
|
||||
<li v-for="item in uncollapsedItems" :key="itemId(item)" class="gl-mr-3">
|
||||
<component :is="itemComponent(item)" :merge-request="itemObject(item)" />
|
||||
<component :is="itemComponent(item)" :item-content="itemObject(item)" />
|
||||
</li>
|
||||
</ul>
|
||||
<gl-button
|
||||
|
|
|
|||
|
|
@ -55,12 +55,6 @@
|
|||
}
|
||||
|
||||
:root.gl-dark {
|
||||
.terms {
|
||||
.logo-text {
|
||||
fill: var(--black);
|
||||
}
|
||||
}
|
||||
|
||||
.md :not(pre.code) > code {
|
||||
background-color: $gray-200;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -95,15 +95,12 @@ module AppearancesHelper
|
|||
end
|
||||
|
||||
def brand_header_logo(options = {})
|
||||
add_gitlab_white_text = options[:add_gitlab_white_text] || false
|
||||
add_gitlab_black_text = options[:add_gitlab_black_text] || false
|
||||
add_gitlab_logo_text = options[:add_gitlab_logo_text] || false
|
||||
|
||||
if current_appearance&.header_logo?
|
||||
image_tag current_appearance.header_logo_path, class: 'brand-header-logo', alt: ''
|
||||
elsif add_gitlab_white_text
|
||||
render partial: 'shared/logo_with_white_text', formats: :svg
|
||||
elsif add_gitlab_black_text
|
||||
render partial: 'shared/logo_with_black_text', formats: :svg
|
||||
elsif add_gitlab_logo_text
|
||||
render partial: 'shared/logo_with_text', formats: :svg
|
||||
else
|
||||
render partial: 'shared/logo', formats: :svg
|
||||
end
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@ module ContainerRegistry
|
|||
help_page_path: help_page_path('user/packages/container_registry/index.md'),
|
||||
two_factor_auth_help_link: help_page_path('user/profile/account/two_factor_authentication.md'),
|
||||
personal_access_tokens_help_link: help_page_path('user/profile/personal_access_tokens.md'),
|
||||
no_containers_image: image_path('illustrations/docker-empty-state.svg'),
|
||||
containers_error_image: image_path('illustrations/docker-error-state.svg'),
|
||||
no_containers_image: image_path('illustrations/status/status-nothing-md.svg'),
|
||||
containers_error_image: image_path('illustrations/status/status-fail-md.svg'),
|
||||
repository_url: escape_once(project.container_registry_url),
|
||||
registry_host_url_with_port: escape_once(registry_config.host_port),
|
||||
expiration_policy_help_page_path:
|
||||
|
|
|
|||
|
|
@ -516,14 +516,7 @@ module Ci
|
|||
end
|
||||
|
||||
def ensure_manager(system_xid, &blk)
|
||||
# rubocop: disable Performance/ActiveRecordSubtransactionMethods -- This is used only in API endpoints outside of transactions
|
||||
RunnerManager.safe_find_or_create_by!(
|
||||
runner_id: id,
|
||||
runner_type: runner_type,
|
||||
sharding_key_id: sharding_key_id,
|
||||
system_xid: system_xid.to_s,
|
||||
&blk)
|
||||
# rubocop: enable Performance/ActiveRecordSubtransactionMethods
|
||||
RunnerManager.safe_find_or_create_by!(runner_id: id, system_xid: system_xid.to_s, &blk) # rubocop: disable Performance/ActiveRecordSubtransactionMethods
|
||||
end
|
||||
|
||||
def registration_available?
|
||||
|
|
|
|||
|
|
@ -43,8 +43,6 @@ module Ci
|
|||
finished: 100
|
||||
}, _suffix: true
|
||||
|
||||
enum runner_type: Runner.runner_types
|
||||
|
||||
has_many :runner_manager_builds, inverse_of: :runner_manager, foreign_key: :runner_machine_id,
|
||||
class_name: 'Ci::RunnerManagerBuild'
|
||||
has_many :builds, through: :runner_manager_builds, class_name: 'Ci::Build'
|
||||
|
|
@ -52,9 +50,7 @@ module Ci
|
|||
class_name: 'Ci::RunnerVersion'
|
||||
|
||||
validates :runner, presence: true
|
||||
validates :runner_type, presence: true, on: :create
|
||||
validates :system_xid, presence: true, length: { maximum: 64 }
|
||||
validates :sharding_key_id, presence: true, on: :create, unless: :instance_type?
|
||||
validates :version, length: { maximum: 2048 }
|
||||
validates :revision, length: { maximum: 255 }
|
||||
validates :platform, length: { maximum: 255 }
|
||||
|
|
@ -62,10 +58,6 @@ module Ci
|
|||
validates :ip_address, length: { maximum: 1024 }
|
||||
validates :config, json_schema: { filename: 'ci_runner_config' }
|
||||
|
||||
validate :no_sharding_key_id, if: :instance_type?
|
||||
|
||||
before_validation :copy_runner_fields
|
||||
|
||||
cached_attr_reader :version, :revision, :platform, :architecture, :ip_address, :contacted_at, :executor_type
|
||||
|
||||
# The `STALE_TIMEOUT` constant defines the how far past the last contact or creation date a runner manager
|
||||
|
|
@ -181,19 +173,6 @@ module Ci
|
|||
Ci::Runners::ProcessRunnerVersionUpdateWorker.perform_async(new_version)
|
||||
end
|
||||
|
||||
def copy_runner_fields
|
||||
return unless runner
|
||||
|
||||
self.runner_type = runner.runner_type
|
||||
self.sharding_key_id = runner.sharding_key_id
|
||||
end
|
||||
|
||||
def no_sharding_key_id
|
||||
return if sharding_key_id.nil?
|
||||
|
||||
errors.add(:runner_manager, 'cannot have sharding_key_id assigned')
|
||||
end
|
||||
|
||||
def self.version_regex_expression_for_version(version)
|
||||
case version
|
||||
when /\d+\.\d+\.\d+/
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
- page_title _("Harbor Registry")
|
||||
|
||||
#js-harbor-registry-list-group{ data: { endpoint: group_harbor_repositories_path(@group),
|
||||
"no_containers_image" => image_path('illustrations/docker-empty-state.svg'),
|
||||
"containers_error_image" => image_path('illustrations/docker-error-state.svg'),
|
||||
"no_containers_image" => image_path('illustrations/status/status-nothing-md.svg'),
|
||||
"containers_error_image" => image_path('illustrations/status/status-fail-md.svg'),
|
||||
"repository_url" => @group.harbor_integration.hostname,
|
||||
"harbor_integration_project_name" => @group.harbor_integration.project_name,
|
||||
full_path: @group.full_path,
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@
|
|||
"help_page_path" => help_page_path('user/packages/container_registry/index.md'),
|
||||
"two_factor_auth_help_link" => help_page_path('user/profile/account/two_factor_authentication.md'),
|
||||
"personal_access_tokens_help_link" => help_page_path('user/profile/personal_access_tokens.md'),
|
||||
"no_containers_image" => image_path('illustrations/docker-empty-state.svg'),
|
||||
"containers_error_image" => image_path('illustrations/docker-error-state.svg'),
|
||||
"no_containers_image" => image_path('illustrations/status/status-nothing-md.svg'),
|
||||
"containers_error_image" => image_path('illustrations/status/status-fail-md.svg'),
|
||||
"registry_host_url_with_port" => escape_once(registry_config.host_port),
|
||||
"garbage_collection_help_page_path" => help_page_path('administration/packages/container_registry.md', anchor: 'container-registry-garbage-collection'),
|
||||
"run_cleanup_policies_help_page_path" => help_page_path('administration/packages/container_registry.md', anchor: 'run-the-cleanup-policy-now'),
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
%header.navbar.fixed-top.navbar-gitlab.justify-content-center
|
||||
.gl-hidden.lg:gl-block
|
||||
= render partial: 'shared/logo_with_white_text', formats: :svg
|
||||
= render partial: 'shared/logo_with_text', formats: :svg
|
||||
.lg:gl-hidden
|
||||
= render partial: 'shared/logo', formats: :svg
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
.limit-container-width.gl-my-5{ class: container_class }
|
||||
= render Pajamas::CardComponent.new( body_options: { class: 'gl-p-0' }, header_options: { class: 'gl-flex gl-items-center gl-justify-between' }) do |c|
|
||||
- c.with_header do
|
||||
= brand_header_logo({add_gitlab_black_text: true})
|
||||
= brand_header_logo({add_gitlab_logo_text: true})
|
||||
- if current_user
|
||||
.gl-flex.gl-gap-2.gl-items-center
|
||||
.gl-text-right.gl-leading-normal
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
- page_title _("Harbor Registry")
|
||||
|
||||
#js-harbor-registry-list-project{ data: { endpoint: project_harbor_repositories_path(@project),
|
||||
"no_containers_image" => image_path('illustrations/docker-empty-state.svg'),
|
||||
"containers_error_image" => image_path('illustrations/docker-error-state.svg'),
|
||||
"no_containers_image" => image_path('illustrations/status/status-nothing-md.svg'),
|
||||
"containers_error_image" => image_path('illustrations/status/status-fail-md.svg'),
|
||||
"repository_url" => @project.harbor_integration.hostname,
|
||||
"harbor_integration_project_name" => @project.harbor_integration.project_name,
|
||||
"project_name" => @project.name,
|
||||
|
|
|
|||
|
|
@ -1,12 +0,0 @@
|
|||
<svg aria-hidden="true" class="tanuki-logo" width="111" height="24" viewBox="0 0 111 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path class="logo-text" d="M44.814 9.042h3.645c-.608-3.875-3.963-6.574-8.33-6.574-5.166 0-9.043 3.798-9.043 10.16 0 6.248 3.703 10.123 9.15 10.123 4.887 0 8.386-3.144 8.386-8.234v-2.37h-8.01v2.794h4.55c-.058 2.816-1.938 4.599-4.908 4.599-3.305 0-5.57-2.477-5.57-6.95 0-4.445 2.303-6.913 5.494-6.913 2.38 0 4.01 1.272 4.636 3.365Zm6.218 13.438h3.49V7.68h-3.49v14.8Zm1.76-17.151c1.109 0 2.014-.85 2.014-1.89s-.905-1.9-2.014-1.9c-1.109 0-2.024.849-2.024 1.9s.9 1.89 2.017 1.89h.007ZM64.971 7.68H62.05V4.126h-3.49v3.556h-2.1v2.699h2.1v8.233c-.018 2.786 2.007 4.16 4.628 4.079a7.089 7.089 0 0 0 2.055-.348l-.59-2.73a4.247 4.247 0 0 1-1.02.137c-.878 0-1.582-.309-1.582-1.717v-7.662h2.921V7.68Zm2.701 14.8h12.272v-2.998H71.25V2.737h-3.578V22.48Zm18.957.3c2.323 0 3.71-1.09 4.347-2.333h.115v2.033h3.36v-9.91c0-3.913-3.19-5.09-6.016-5.09-3.113 0-5.504 1.388-6.275 4.087l3.26.464c.345-1.013 1.329-1.88 3.04-1.88 1.62 0 2.506.829 2.506 2.285v.057c0 1.002-1.05 1.051-3.664 1.33-2.872.309-5.619 1.166-5.619 4.502-.01 2.912 2.12 4.455 4.946 4.455Zm1.147-2.56c-1.456 0-2.498-.666-2.498-1.948 0-1.34 1.167-1.899 2.72-2.121.917-.125 2.75-.357 3.2-.722v1.744c.01 1.643-1.321 3.042-3.422 3.042v.005Zm9.244 2.26h3.433v-2.332h.201c.551 1.08 1.698 2.593 4.244 2.593 3.489 0 6.102-2.768 6.102-7.644 0-4.936-2.69-7.616-6.112-7.616-2.613 0-3.702 1.57-4.234 2.641h-.147V2.737h-3.486V22.48Zm3.423-7.403c0-2.88 1.234-4.734 3.48-4.734 2.323 0 3.52 1.976 3.52 4.734 0 2.759-1.214 4.8-3.52 4.8-2.227 0-3.48-1.928-3.48-4.8Z"
|
||||
fill="#171321"/>
|
||||
<path class="tanuki-shape tanuki" d="m24.507 9.5-.034-.09L21.082.562a.896.896 0 0 0-1.694.091l-2.29 7.01H7.825L5.535.653a.898.898 0 0 0-1.694-.09L.451 9.411.416 9.5a6.297 6.297 0 0 0 2.09 7.278l.012.01.03.022 5.16 3.867 2.56 1.935 1.554 1.176a1.051 1.051 0 0 0 1.268 0l1.555-1.176 2.56-1.935 5.197-3.89.014-.01A6.297 6.297 0 0 0 24.507 9.5Z"
|
||||
fill="#E24329"/>
|
||||
<path class="tanuki-shape right-cheek" d="m24.507 9.5-.034-.09a11.44 11.44 0 0 0-4.56 2.051l-7.447 5.632 4.742 3.584 5.197-3.89.014-.01A6.297 6.297 0 0 0 24.507 9.5Z"
|
||||
fill="#FC6D26"/>
|
||||
<path class="tanuki-shape chin" d="m7.707 20.677 2.56 1.935 1.555 1.176a1.051 1.051 0 0 0 1.268 0l1.555-1.176 2.56-1.935-4.743-3.584-4.755 3.584Z"
|
||||
fill="#FCA326"/>
|
||||
<path class="tanuki-shape left-cheek" d="M5.01 11.461a11.43 11.43 0 0 0-4.56-2.05L.416 9.5a6.297 6.297 0 0 0 2.09 7.278l.012.01.03.022 5.16 3.867 4.745-3.584-7.444-5.632Z"
|
||||
fill="#FC6D26"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.5 KiB |
|
|
@ -0,0 +1,17 @@
|
|||
<svg aria-hidden="true" class="tanuki-logo" width="111" height="24" viewBox="0 0 111 24" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path class="gl-fill-current"
|
||||
d="M44.814 9.042h3.645c-.608-3.875-3.963-6.574-8.33-6.574-5.166 0-9.043 3.798-9.043 10.16 0 6.248 3.703 10.123 9.15 10.123 4.887 0 8.386-3.144 8.386-8.234v-2.37h-8.01v2.794h4.55c-.058 2.816-1.938 4.599-4.908 4.599-3.305 0-5.57-2.477-5.57-6.95 0-4.445 2.303-6.913 5.494-6.913 2.38 0 4.01 1.272 4.636 3.365Zm6.218 13.438h3.49V7.68h-3.49v14.8Zm1.76-17.151c1.109 0 2.014-.85 2.014-1.89s-.905-1.9-2.014-1.9c-1.109 0-2.024.849-2.024 1.9s.9 1.89 2.017 1.89h.007ZM64.971 7.68H62.05V4.126h-3.49v3.556h-2.1v2.699h2.1v8.233c-.018 2.786 2.007 4.16 4.628 4.079a7.089 7.089 0 0 0 2.055-.348l-.59-2.73a4.247 4.247 0 0 1-1.02.137c-.878 0-1.582-.309-1.582-1.717v-7.662h2.921V7.68Zm2.701 14.8h12.272v-2.998H71.25V2.737h-3.578V22.48Zm18.957.3c2.323 0 3.71-1.09 4.347-2.333h.115v2.033h3.36v-9.91c0-3.913-3.19-5.09-6.016-5.09-3.113 0-5.504 1.388-6.275 4.087l3.26.464c.345-1.013 1.329-1.88 3.04-1.88 1.62 0 2.506.829 2.506 2.285v.057c0 1.002-1.05 1.051-3.664 1.33-2.872.309-5.619 1.166-5.619 4.502-.01 2.912 2.12 4.455 4.946 4.455Zm1.147-2.56c-1.456 0-2.498-.666-2.498-1.948 0-1.34 1.167-1.899 2.72-2.121.917-.125 2.75-.357 3.2-.722v1.744c.01 1.643-1.321 3.042-3.422 3.042v.005Zm9.244 2.26h3.433v-2.332h.201c.551 1.08 1.698 2.593 4.244 2.593 3.489 0 6.102-2.768 6.102-7.644 0-4.936-2.69-7.616-6.112-7.616-2.613 0-3.702 1.57-4.234 2.641h-.147V2.737h-3.486V22.48Zm3.423-7.403c0-2.88 1.234-4.734 3.48-4.734 2.323 0 3.52 1.976 3.52 4.734 0 2.759-1.214 4.8-3.52 4.8-2.227 0-3.48-1.928-3.48-4.8Z" />
|
||||
<path class="tanuki-shape tanuki"
|
||||
d="m24.507 9.5-.034-.09L21.082.562a.896.896 0 0 0-1.694.091l-2.29 7.01H7.825L5.535.653a.898.898 0 0 0-1.694-.09L.451 9.411.416 9.5a6.297 6.297 0 0 0 2.09 7.278l.012.01.03.022 5.16 3.867 2.56 1.935 1.554 1.176a1.051 1.051 0 0 0 1.268 0l1.555-1.176 2.56-1.935 5.197-3.89.014-.01A6.297 6.297 0 0 0 24.507 9.5Z"
|
||||
fill="#E24329" />
|
||||
<path class="tanuki-shape right-cheek"
|
||||
d="m24.507 9.5-.034-.09a11.44 11.44 0 0 0-4.56 2.051l-7.447 5.632 4.742 3.584 5.197-3.89.014-.01A6.297 6.297 0 0 0 24.507 9.5Z"
|
||||
fill="#FC6D26" />
|
||||
<path class="tanuki-shape chin"
|
||||
d="m7.707 20.677 2.56 1.935 1.555 1.176a1.051 1.051 0 0 0 1.268 0l1.555-1.176 2.56-1.935-4.743-3.584-4.755 3.584Z"
|
||||
fill="#FCA326" />
|
||||
<path class="tanuki-shape left-cheek"
|
||||
d="M5.01 11.461a11.43 11.43 0 0 0-4.56-2.05L.416 9.5a6.297 6.297 0 0 0 2.09 7.278l.012.01.03.022 5.16 3.867 4.745-3.584-7.444-5.632Z"
|
||||
fill="#FC6D26" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
|
|
@ -1,12 +0,0 @@
|
|||
<svg aria-hidden="true" class="tanuki-logo" width="111" height="24" viewBox="0 0 111 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path class="logo-text" d="M44.814 9.042h3.645c-.608-3.875-3.963-6.574-8.33-6.574-5.166 0-9.043 3.798-9.043 10.16 0 6.248 3.703 10.123 9.15 10.123 4.887 0 8.386-3.144 8.386-8.234v-2.37h-8.01v2.794h4.55c-.058 2.816-1.938 4.599-4.908 4.599-3.305 0-5.57-2.477-5.57-6.95 0-4.445 2.303-6.913 5.494-6.913 2.38 0 4.01 1.272 4.636 3.365Zm6.218 13.438h3.49V7.68h-3.49v14.8Zm1.76-17.151c1.109 0 2.014-.85 2.014-1.89s-.905-1.9-2.014-1.9c-1.109 0-2.024.849-2.024 1.9s.9 1.89 2.017 1.89h.007ZM64.971 7.68H62.05V4.126h-3.49v3.556h-2.1v2.699h2.1v8.233c-.018 2.786 2.007 4.16 4.628 4.079a7.089 7.089 0 0 0 2.055-.348l-.59-2.73a4.247 4.247 0 0 1-1.02.137c-.878 0-1.582-.309-1.582-1.717v-7.662h2.921V7.68Zm2.701 14.8h12.272v-2.998H71.25V2.737h-3.578V22.48Zm18.957.3c2.323 0 3.71-1.09 4.347-2.333h.115v2.033h3.36v-9.91c0-3.913-3.19-5.09-6.016-5.09-3.113 0-5.504 1.388-6.275 4.087l3.26.464c.345-1.013 1.329-1.88 3.04-1.88 1.62 0 2.506.829 2.506 2.285v.057c0 1.002-1.05 1.051-3.664 1.33-2.872.309-5.619 1.166-5.619 4.502-.01 2.912 2.12 4.455 4.946 4.455Zm1.147-2.56c-1.456 0-2.498-.666-2.498-1.948 0-1.34 1.167-1.899 2.72-2.121.917-.125 2.75-.357 3.2-.722v1.744c.01 1.643-1.321 3.042-3.422 3.042v.005Zm9.244 2.26h3.433v-2.332h.201c.551 1.08 1.698 2.593 4.244 2.593 3.489 0 6.102-2.768 6.102-7.644 0-4.936-2.69-7.616-6.112-7.616-2.613 0-3.702 1.57-4.234 2.641h-.147V2.737h-3.486V22.48Zm3.423-7.403c0-2.88 1.234-4.734 3.48-4.734 2.323 0 3.52 1.976 3.52 4.734 0 2.759-1.214 4.8-3.52 4.8-2.227 0-3.48-1.928-3.48-4.8Z"
|
||||
fill="#fff"/>
|
||||
<path class="tanuki-shape tanuki" d="m24.507 9.5-.034-.09L21.082.562a.896.896 0 0 0-1.694.091l-2.29 7.01H7.825L5.535.653a.898.898 0 0 0-1.694-.09L.451 9.411.416 9.5a6.297 6.297 0 0 0 2.09 7.278l.012.01.03.022 5.16 3.867 2.56 1.935 1.554 1.176a1.051 1.051 0 0 0 1.268 0l1.555-1.176 2.56-1.935 5.197-3.89.014-.01A6.297 6.297 0 0 0 24.507 9.5Z"
|
||||
fill="#E24329"/>
|
||||
<path class="tanuki-shape right-cheek" d="m24.507 9.5-.034-.09a11.44 11.44 0 0 0-4.56 2.051l-7.447 5.632 4.742 3.584 5.197-3.89.014-.01A6.297 6.297 0 0 0 24.507 9.5Z"
|
||||
fill="#FC6D26"/>
|
||||
<path class="tanuki-shape chin" d="m7.707 20.677 2.56 1.935 1.555 1.176a1.051 1.051 0 0 0 1.268 0l1.555-1.176 2.56-1.935-4.743-3.584-4.755 3.584Z"
|
||||
fill="#FCA326"/>
|
||||
<path class="tanuki-shape left-cheek" d="M5.01 11.461a11.43 11.43 0 0 0-4.56-2.05L.416 9.5a6.297 6.297 0 0 0 2.09 7.278l.012.01.03.022 5.16 3.867 4.745-3.584-7.444-5.632Z"
|
||||
fill="#FC6D26"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.5 KiB |
|
|
@ -1,10 +0,0 @@
|
|||
---
|
||||
migration_job_name: BackfillRunnerTypeAndShardingKeyIdOnCiRunnerManagers
|
||||
description: >
|
||||
Backfills the `runner_type` and `sharding_key_id` columns from `ci_runners`.
|
||||
The `sharding_key_id` column will serve as the sharding key in the future partitioned (by `runner_type`) table.
|
||||
feature_category: runners
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/166916
|
||||
milestone: '17.5'
|
||||
queued_migration_version: 20241003110148
|
||||
finalized_by: # version of the migration that finalized this BBM
|
||||
|
|
@ -5,23 +5,11 @@ class QueueBackfillRunnerTypeAndShardingKeyIdOnCiRunnerManagers < Gitlab::Databa
|
|||
|
||||
restrict_gitlab_migration gitlab_schema: :gitlab_ci
|
||||
|
||||
MIGRATION = 'BackfillRunnerTypeAndShardingKeyIdOnCiRunnerManagers'
|
||||
DELAY_INTERVAL = 2.minutes
|
||||
BATCH_SIZE = 1000
|
||||
SUB_BATCH_SIZE = 100
|
||||
|
||||
def up
|
||||
queue_batched_background_migration(
|
||||
MIGRATION,
|
||||
:ci_runner_machines,
|
||||
:id,
|
||||
job_interval: DELAY_INTERVAL,
|
||||
batch_size: BATCH_SIZE,
|
||||
sub_batch_size: SUB_BATCH_SIZE
|
||||
)
|
||||
# no-op
|
||||
end
|
||||
|
||||
def down
|
||||
delete_batched_background_migration(MIGRATION, :ci_runner_machines, :id, [])
|
||||
# no-op
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,120 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Editor Extensions
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
# Custom queries in the VS Code extension
|
||||
|
||||
This extension adds a **GitLab Workflow**
|
||||
[sidebar](https://gitlab.com/gitlab-org/gitlab-vscode-extension/-/tree/main?ref_type=heads#sidebar-details)
|
||||
to VS Code. This sidebar shows default search queries for each of your projects:
|
||||
|
||||
- Issues assigned to me
|
||||
- Issues created by me
|
||||
- Merge requests assigned to me
|
||||
- Merge requests created by me
|
||||
- Merge requests I'm reviewing
|
||||
|
||||
## View search query results in VS Code
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- You're a member of a GitLab project.
|
||||
- You've [installed the extension](https://marketplace.visualstudio.com/items?itemName=GitLab.gitlab-workflow).
|
||||
- You've signed in to your GitLab instance, as described in [Setup](https://gitlab.com/gitlab-org/gitlab-vscode-extension/-/tree/main/#setup).
|
||||
|
||||
To see search results from your project:
|
||||
|
||||
1. On the left vertical menu bar, select **GitLab Workflow** (**{tanuki}**) to display the extension sidebar.
|
||||
1. On the sidebar, expand **Issues and merge requests**.
|
||||
1. Select a project to view its queries, then select the query you want to run.
|
||||
1. Below the query title, select the search result you want to see.
|
||||
1. If your search result is a merge request, select what you want to view in VS Code:
|
||||
- **Overview**: the description, status, and any comments on this merge request.
|
||||
- The **filenames** of all files changed in this merge request. Select a file to view a diff
|
||||
of its changes.
|
||||
1. If your search result is an issue, select it to view its description, history, and comments in VS Code.
|
||||
|
||||
## Create a custom query
|
||||
|
||||
Any custom queries you define override the default queries shown in the
|
||||
[VS Code sidebar](https://gitlab.com/gitlab-org/gitlab-vscode-extension/-/tree/main?ref_type=heads#sidebar-details),
|
||||
under **Issues and Merge requests**.
|
||||
|
||||
To override the extension's default queries and replace them with your own:
|
||||
|
||||
1. In VS Code, on the top bar, go to **Code > Preferences > Settings**.
|
||||
1. On the top right corner, select **Open Settings (JSON)** to edit your `settings.json` file.
|
||||
1. In the file, define `gitlab.customQueries`, like in this example. Each query should be an entry
|
||||
in the `gitlab.customQueries` JSON array:
|
||||
|
||||
```json
|
||||
{
|
||||
"gitlab.customQueries": [
|
||||
{
|
||||
"name": "Issues assigned to me",
|
||||
"type": "issues",
|
||||
"scope": "assigned_to_me",
|
||||
"noItemText": "No issues assigned to you.",
|
||||
"state": "opened"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
1. Optional. When you customize `gitlab.customQueries`, your definition overrides all default queries.
|
||||
To restore any of the default queries, copy them from the `default` array in the extension's
|
||||
[`desktop.package.json` file](https://gitlab.com/gitlab-org/gitlab-vscode-extension/-/blob/8e4350232154fe5bf0ef8a6c0765b2eac0496dc7/desktop.package.json#L955-998).
|
||||
1. Save your changes.
|
||||
|
||||
### Supported parameters for all queries
|
||||
|
||||
Not all item types support all parameters. These parameters apply to all query types:
|
||||
|
||||
| Parameter | Required | Default | Definition |
|
||||
|--------------|----------|-------------------|------------|
|
||||
| `name` | **{check-circle}** Yes | not applicable | The label to show in the GitLab panel. |
|
||||
| `noItemText` | **{dotted-circle}** No | `No items found.` | The text to show if the query returns no items. |
|
||||
| `type` | **{dotted-circle}** No | `merge_requests` | Which item types to return. Possible values: `issues`, `merge_requests`, `epics`, `snippets`, `vulnerabilities`. Snippets [don't support](../../api/project_snippets.md) any other filters. Epics are available only on GitLab Premium and Ultimate.|
|
||||
|
||||
### Supported parameters for issue, epic, and merge request queries
|
||||
|
||||
| Parameter | Required | Default | Definition |
|
||||
|--------------------|------------------------|--------------|------------|
|
||||
| `assignee` | **{dotted-circle}** No | not applicable | Return items assigned to the given username. `None` returns unassigned GitLab items. `Any` returns GitLab items with an assignee. Not available for epics and vulnerabilities. |
|
||||
| `author` | **{dotted-circle}** No | not applicable | Return items created by the given username. |
|
||||
| `confidential` | **{dotted-circle}** No | not applicable | Filter confidential or public issues. Available only for issues. |
|
||||
| `createdAfter` | **{dotted-circle}** No | not applicable | Return items created after the given date. |
|
||||
| `createdBefore` | **{dotted-circle}** No | not applicable | Return items created before the given date. |
|
||||
| `draft` | **{dotted-circle}** No | `no` | Filter merge requests against their draft status: `yes` returns only merge requests in [draft status](../../user/project/merge_requests/drafts.md), `no` returns only merge requests not in draft status. Available only for merge requests. |
|
||||
| `excludeAssignee` | **{dotted-circle}** No | not applicable | Return items not assigned to the given username. Available only for issues. For the current user, set to `<current_user>`. |
|
||||
| `excludeAuthor` | **{dotted-circle}** No | not applicable | Return items not created by the given username. Available only for issues. For the current user, set to `<current_user>`. |
|
||||
| `excludeLabels` | **{dotted-circle}** No | `[]` | Array of label names. Available only for issues. To be returned, items must have none of the labels in the array. Predefined names are case-insensitive. |
|
||||
| `excludeMilestone` | **{dotted-circle}** No | not applicable | The milestone title to exclude. Available only for issues. |
|
||||
| `excludeSearch` | **{dotted-circle}** No | not applicable | Search GitLab items that doesn't have the search key in their title or description. Works only with issues. |
|
||||
| `labels` | **{dotted-circle}** No | `[]` | Array of label names. To be returned, items must have all labels in the array. `None` returns items with no labels. `Any` returns items with at least one label. Predefined names are case-insensitive. |
|
||||
| `maxResults` | **{dotted-circle}** No | 20 | The number of results to show. |
|
||||
| `milestone` | **{dotted-circle}** No | not applicable | The milestone title. `None` lists all items with no milestone. `Any` lists all items with an assigned milestone. Not available for epics and vulnerabilities. |
|
||||
| `orderBy` | **{dotted-circle}** No | `created_at` | Return entities ordered by the selected value. Possible values: `created_at`, `updated_at`, `priority`, `due_date`, `relative_position`, `label_priority`, `milestone_due`, `popularity`, `weight`. Some values are specific to issues, and some to merge requests. For more information, see [List merge requests](../../api/merge_requests.md#list-merge-requests). |
|
||||
| `reviewer` | **{dotted-circle}** No | not applicable | Return merge requests assigned for review to this username. For the current user, set to `<current_user>`. `None` returns items without a reviewer. `Any` returns items with a reviewer. |
|
||||
| `scope` | **{dotted-circle}** No | `all` | Return GitLab items for the given scope. Not applicable for epics. Possible values: `assigned_to_me`, `created_by_me`, `all`. |
|
||||
| `search` | **{dotted-circle}** No | not applicable | Search GitLab items against their title and description. |
|
||||
| `searchIn` | **{dotted-circle}** No | `all` | Change the scope of the `excludeSearch` search attribute. Possible values: `all`, `title`, `description`. Works only with issues. |
|
||||
| `sort` | **{dotted-circle}** No | `desc` | Return issues sorted in ascending or descending order. Possible values: `asc`, `desc`. |
|
||||
| `state` | **{dotted-circle}** No | `opened` | Return all issues, or only those matching a particular state. Possible values: `all`, `opened`, `closed`. |
|
||||
| `updatedAfter` | **{dotted-circle}** No | not applicable | Return items updated after the given date. |
|
||||
| `updatedBefore` | **{dotted-circle}** No | not applicable | Return items updated before the given date. |
|
||||
|
||||
### Supported parameters for vulnerability report queries
|
||||
|
||||
Vulnerability reports don't share
|
||||
[any common query parameters](../../api/vulnerability_findings.md)
|
||||
with other entry types. Each parameter listed in this table works with vulnerability reports only:
|
||||
|
||||
| Parameter | Required | Default | Definition |
|
||||
|--------------------|------------------------|----------------|------------|
|
||||
| `confidenceLevels` | **{dotted-circle}** No | `all` | Returns vulnerabilities belonging to specified confidence levels. Possible values: `undefined`, `ignore`, `unknown`, `experimental`, `low`, `medium`, `high`, `confirmed`. |
|
||||
| `reportTypes` | **{dotted-circle}** No | Not applicable | Returns vulnerabilities belonging to specified report types. Possible values: `sast`, `dast`, `dependency_scanning`, `container_scanning`. |
|
||||
| `scope` | **{dotted-circle}** No | `dismissed` | Returns vulnerability findings for the given scope. Possible values: `all`, `dismissed`. For more information, see the [Vulnerability findings API](../../api/vulnerability_findings.md). |
|
||||
| `severityLevels` | **{dotted-circle}** No | `all` | Returns vulnerabilities belonging to specified severity levels. Possible values: `undefined`, `info`, `unknown`, `low`, `medium`, `high`, `critical`. |
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Editor Extensions
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
# GitLab remote URL format
|
||||
|
||||
In VS Code, you can browse GitLab repositories
|
||||
[in read-only mode](https://gitlab.com/gitlab-org/gitlab-vscode-extension/-/blob/main/README.md#browse-a-repository-without-cloning)
|
||||
with a custom remote URL.
|
||||
|
||||
GitLab remote URLs require these parameters:
|
||||
|
||||
- `instanceUrl`: The GitLab instance URL, not including `https://` or `http://`.
|
||||
- If the GitLab instance [uses a relative URL](../../install/relative_url.md), include the relative URL in the URL.
|
||||
- For example, the URL for the `main` branch of the project `templates/ui` on the instance `example.com/gitlab` is
|
||||
`gitlab-remote://example.com/gitlab/<label>?project=templates/ui&ref=main`.
|
||||
- `label`: The text Visual Studio Code uses as the name of this workspace folder:
|
||||
- It must appear immediately after the instance URL.
|
||||
- It can't contain unescaped URL components, such as `/` or `?`.
|
||||
- For an instance installed at the domain root, such as `https://gitlab.com`, the label must be the first path element.
|
||||
- For URLs that refer to the root of a repository, the label must be the last path element.
|
||||
- VS Code treats any path elements that appear after the label as a path inside the repository. For example,
|
||||
`gitlab-remote://gitlab.com/GitLab/app?project=gitlab-org/gitlab&ref=master` refers to the `app` directory of
|
||||
the `gitlab-org/gitlab` repository on GitLab.com.
|
||||
- `projectId`: Can be either the numeric ID (like `5261717`) or the namespace (`gitlab-org/gitlab-vscode-extension`) of the
|
||||
project. If your instance uses a reverse proxy, specify `projectId` with the numeric ID. For more information, see
|
||||
[issue 18775](https://gitlab.com/gitlab-org/gitlab/-/issues/18775).
|
||||
- `gitReference`: The repository branch or commit SHA.
|
||||
|
||||
The parameters are then placed together in this order:
|
||||
|
||||
```plaintext
|
||||
gitlab-remote://<INSTANCE_URL>/<LABEL>?project=<PROJECT_ID>&ref=<GIT_REFERENCE>
|
||||
```
|
||||
|
||||
For example, the `projectID` for the main GitLab project is `278964`, so the remote URL for the main GitLab project is:
|
||||
|
||||
```plaintext
|
||||
gitlab-remote://gitlab.com/<LABEL>?project=278964&ref=master
|
||||
```
|
||||
|
||||
## Browse a repository in read-only mode
|
||||
|
||||
With this extension, you can browse a GitLab repository in read-only mode without cloning it.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- You have [registered an access token](https://gitlab.com/gitlab-org/gitlab-vscode-extension/#setup) for that GitLab instance.
|
||||
|
||||
To browse a GitLab repository in read-only mode:
|
||||
|
||||
1. Open the command palette by pressing <kbd>Control</kbd> + <kbd>Shift</kbd> + <kbd>P</kbd>.
|
||||
1. Run the **GitLab: Open Remote Repository** command.
|
||||
1. Select **Open in current window**, **Open in new window**, or **Add to workspace**.
|
||||
1. To add a repository, select `Enter gitlab-remote URL`, then enter the `gitlab-remote://` URL for your desired project.
|
||||
1. To view a repository you've already added, select **Choose a project**, then select your desired project from the dropdown list.
|
||||
1. In the dropdown list, select the Git branch you want to view, then press <kbd>Enter</kbd> to confirm.
|
||||
|
||||
To add a `gitlab-remote` URL to your workspace file, see
|
||||
[Workspace file](https://code.visualstudio.com/docs/editor/multi-root-workspaces#_workspace-file) in the VS Code documentation.
|
||||
|
|
@ -13,16 +13,21 @@ DETAILS:
|
|||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/368434) in GitLab 15.11.
|
||||
> - Detection of personal access tokens with a custom prefix was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/411146) in GitLab 16.1. GitLab self-managed only.
|
||||
|
||||
When you create an issue or epic, propose a merge request, or write a comment, you might accidentally post a sensitive value.
|
||||
For example, you might paste in the details of an API request or an environment variable that contains an authentication token.
|
||||
When you create an issue, propose a merge request, or write a comment, you might accidentally post a
|
||||
secret. For example, you might paste in the details of an API request or an environment variable
|
||||
that contains an authentication token. If a secret is leaked it could be used to do harm.
|
||||
|
||||
When you edit the description or comment in an issue, epic, or merge request, GitLab checks if it contains a sensitive token.
|
||||
If a token is found, a warning message is displayed. You can then edit your description or comment before posting it.
|
||||
This check happens in your browser before the message is sent to the server.
|
||||
The check is always on; you don't have to set it up.
|
||||
Client-side secret detection helps to minimize the risk of that happening. When you edit the
|
||||
description or comment in an issue or merge request, GitLab checks if it contains a secret. If a
|
||||
secret is found, a warning message is displayed. You can then edit the description or comment to
|
||||
remove the secret before posting your message, or add the description or comment as it is. This
|
||||
check occurs in your browser, so the secret is not revealed to anyone else unless you add it to
|
||||
GitLab. The check is always on; you don't have to set it up.
|
||||
|
||||
Your text is checked for the following secret types:
|
||||
Client-side secret detection checks only the following for secrets:
|
||||
|
||||
- GitLab [personal access tokens](../../../../security/tokens/index.md#personal-access-tokens)
|
||||
- If a [personal access token prefix](../../../../administration/settings/account_and_limit_settings.md#personal-access-token-prefix) has been configured, a token using this prefix is checked.
|
||||
- GitLab [feed tokens](../../../../security/tokens/index.md#feed-token)
|
||||
- Comments in issues or merge requests.
|
||||
- Descriptions of issues or merge requests.
|
||||
|
||||
For details of which types of secrets are covered by client-side secret detection, see
|
||||
[Detected secrets](../detected_secrets.md).
|
||||
|
|
|
|||
|
|
@ -1,32 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module BackgroundMigration
|
||||
class BackfillRunnerTypeAndShardingKeyIdOnCiRunnerManagers < BatchedMigrationJob
|
||||
operation_name :backfill_runner_type_and_sharding_key_on_ci_runner_machines
|
||||
feature_category :runner
|
||||
|
||||
UPDATE_RUNNER_MANAGERS_SQL = <<~SQL
|
||||
UPDATE ci_runner_machines
|
||||
SET
|
||||
sharding_key_id = ci_runners.sharding_key_id,
|
||||
runner_type = ci_runners.runner_type
|
||||
FROM ci_runners
|
||||
WHERE ci_runner_machines.runner_id = ci_runners.id
|
||||
AND ci_runner_machines.id IN (?);
|
||||
SQL
|
||||
|
||||
class CiRunnerManager < ::Ci::ApplicationRecord
|
||||
self.table_name = 'ci_runner_machines'
|
||||
end
|
||||
|
||||
def perform
|
||||
each_sub_batch do |sub_batch|
|
||||
sub_batch = sub_batch.where(sharding_key_id: nil).limit(sub_batch_size).select(:id)
|
||||
|
||||
connection.exec_update(CiRunnerManager.sanitize_sql([UPDATE_RUNNER_MANAGERS_SQL, sub_batch]))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -23125,6 +23125,9 @@ msgstr ""
|
|||
msgid "Feature disabled"
|
||||
msgstr ""
|
||||
|
||||
msgid "Feature flag"
|
||||
msgstr ""
|
||||
|
||||
msgid "Feature flag status"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -35082,9 +35085,6 @@ msgstr ""
|
|||
msgid "MlModelRegistry|Using the MLflow client"
|
||||
msgstr ""
|
||||
|
||||
msgid "MlModelRegistry|Version candidates"
|
||||
msgstr ""
|
||||
|
||||
msgid "MlModelRegistry|Version description"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -7,11 +7,6 @@ FactoryBot.define do
|
|||
|
||||
creation_state { :finished }
|
||||
|
||||
after(:build) do |runner_manager, evaluator|
|
||||
runner_manager.runner_type ||= evaluator.runner.runner_type
|
||||
runner_manager.sharding_key_id ||= evaluator.runner.sharding_key_id
|
||||
end
|
||||
|
||||
trait :unregistered do
|
||||
contacted_at { nil }
|
||||
creation_state { :started }
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ describe('date_picker behavior', () => {
|
|||
|
||||
beforeEach(() => {
|
||||
pikadayMock = jest.spyOn(Pikaday, 'default');
|
||||
parseMock = jest.spyOn(utils, 'parsePikadayDate');
|
||||
parseMock = jest.spyOn(utils, 'newDate');
|
||||
setHTMLFixture(`
|
||||
<div>
|
||||
<input class="datepicker" value="2020-10-01" />
|
||||
|
|
|
|||
|
|
@ -316,12 +316,6 @@ describe('formatTime', () => {
|
|||
});
|
||||
|
||||
describe('datefix', () => {
|
||||
describe('parsePikadayDate', () => {
|
||||
it('should return a UTC date', () => {
|
||||
expect(datetimeUtility.parsePikadayDate('2020-01-29')).toEqual(new Date(2020, 0, 29));
|
||||
});
|
||||
});
|
||||
|
||||
describe('toISODateFormat', () => {
|
||||
it('should format a Date object into yyyy-mm-dd format', () => {
|
||||
expect(datetimeUtility.toISODateFormat(new Date('2020-01-29:00:00'))).toEqual('2020-01-29');
|
||||
|
|
|
|||
|
|
@ -106,9 +106,7 @@ describe('ml/model_registry/apps/show_ml_model', () => {
|
|||
const findVersionsCountBadge = () => findVersionsTab().findComponent(GlBadge);
|
||||
const findModelVersionList = () => wrapper.findComponent(ModelVersionList);
|
||||
const findModelDetail = () => wrapper.findComponent(ModelDetail);
|
||||
const findCandidateTab = () => wrapper.findAllComponents(GlTab).at(2);
|
||||
const findCandidateList = () => wrapper.findComponent(CandidateList);
|
||||
const findCandidatesCountBadge = () => findCandidateTab().findComponent(GlBadge);
|
||||
const findTitleArea = () => wrapper.findComponent(TitleArea);
|
||||
const findVersionCountMetadataItem = () => findTitleArea().findComponent(MetadataItem);
|
||||
const findActionsDropdown = () => wrapper.findComponent(ActionsDropdown);
|
||||
|
|
@ -197,10 +195,6 @@ describe('ml/model_registry/apps/show_ml_model', () => {
|
|||
it('shows the number of versions in the tab', () => {
|
||||
expect(findVersionsCountBadge().text()).toBe(model.versionCount.toString());
|
||||
});
|
||||
|
||||
it('shows the number of candidates in the tab', () => {
|
||||
expect(findCandidatesCountBadge().text()).toBe(model.candidateCount.toString());
|
||||
});
|
||||
});
|
||||
|
||||
describe('Model loading', () => {
|
||||
|
|
@ -245,17 +239,6 @@ describe('ml/model_registry/apps/show_ml_model', () => {
|
|||
expect(findCandidateList().exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('shows candidate list when location hash is `#/candidates`', async () => {
|
||||
await createWrapper({ mountFn: mountExtended });
|
||||
|
||||
await findCandidateTab().vm.$emit('click');
|
||||
|
||||
expect(findTabs().props('value')).toBe(2);
|
||||
expect(findModelDetail().exists()).toBe(false);
|
||||
expect(findModelVersionList().exists()).toBe(false);
|
||||
expect(findCandidateList().props('modelId')).toBe(model.id);
|
||||
});
|
||||
|
||||
describe.each`
|
||||
location | tab | navigatedTo
|
||||
${'#/'} | ${findDetailTab} | ${0}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,72 @@
|
|||
import { GlLink, GlIcon, GlTooltip } from '@gitlab/ui';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import { workItemDevelopmentFeatureFlagNodes } from 'jest/work_items/mock_data';
|
||||
import WorkItemDevelopmentFfItem from '~/work_items/components/work_item_development/work_item_development_ff_item.vue';
|
||||
|
||||
jest.mock('~/alert');
|
||||
|
||||
describe('WorkItemDevelopmentFfItem', () => {
|
||||
let wrapper;
|
||||
|
||||
const enabledFeatureFlag = workItemDevelopmentFeatureFlagNodes[0];
|
||||
const disabledFeatureFlag = workItemDevelopmentFeatureFlagNodes[1];
|
||||
|
||||
const createComponent = ({ featureFlag = enabledFeatureFlag }) => {
|
||||
wrapper = shallowMount(WorkItemDevelopmentFfItem, {
|
||||
propsData: {
|
||||
itemContent: featureFlag,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const findFlagIcon = () => wrapper.findComponent(GlIcon);
|
||||
const findFlagLink = () => wrapper.findComponent(GlLink);
|
||||
const findFlagTooltip = () => wrapper.findComponent(GlTooltip);
|
||||
|
||||
describe('feature flag status icon', () => {
|
||||
it.each`
|
||||
state | icon | featureFlag | iconClass
|
||||
${'Enabled'} | ${'feature-flag'} | ${enabledFeatureFlag} | ${'gl-text-blue-500'}
|
||||
${'Disabled'} | ${'feature-flag-disabled'} | ${disabledFeatureFlag} | ${'gl-text-gray-500'}
|
||||
`(
|
||||
'renders icon "$icon" when the state of the feature flag is "$state"',
|
||||
({ icon, iconClass, featureFlag }) => {
|
||||
createComponent({ featureFlag });
|
||||
|
||||
expect(findFlagIcon().props('name')).toBe(icon);
|
||||
expect(findFlagIcon().attributes('class')).toBe(iconClass);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe('feature flag link and name', () => {
|
||||
it('should render the flag path and name', () => {
|
||||
createComponent({ featureFlag: enabledFeatureFlag });
|
||||
|
||||
expect(findFlagLink().attributes('href')).toBe(enabledFeatureFlag.path);
|
||||
expect(findFlagLink().attributes('href')).toContain(`/edit`);
|
||||
|
||||
expect(findFlagLink().text()).toBe(enabledFeatureFlag.name);
|
||||
});
|
||||
});
|
||||
|
||||
describe('eature flag tooltip', () => {
|
||||
it('should render the tooltip with flag name, reference and "Enabled" copy if active', () => {
|
||||
createComponent({ featureFlag: enabledFeatureFlag });
|
||||
|
||||
expect(findFlagTooltip().exists()).toBe(true);
|
||||
expect(findFlagTooltip().text()).toBe(
|
||||
`Feature flag ${enabledFeatureFlag.name} ${enabledFeatureFlag.reference} Enabled`,
|
||||
);
|
||||
});
|
||||
|
||||
it('should render the tooltip with flag name, reference and "Disabled" copy if not active', () => {
|
||||
createComponent({ featureFlag: disabledFeatureFlag });
|
||||
|
||||
expect(findFlagTooltip().exists()).toBe(true);
|
||||
expect(findFlagTooltip().text()).toBe(
|
||||
`Feature flag ${disabledFeatureFlag.name} ${disabledFeatureFlag.reference} Disabled`,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import { GlLink, GlIcon, GlAvatarsInline, GlAvatarLink, GlAvatar } from '@gitlab/ui';
|
||||
import { shallowMount, mount } from '@vue/test-utils';
|
||||
import { workItemDevelopmentNodes } from 'jest/work_items/mock_data';
|
||||
import { workItemDevelopmentMRNodes } from 'jest/work_items/mock_data';
|
||||
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
|
||||
import { STATUS_OPEN, STATUS_CLOSED, STATUS_MERGED } from '~/issues/constants';
|
||||
import WorkItemDevelopmentMRItem from '~/work_items/components/work_item_development/work_item_development_mr_item.vue';
|
||||
|
|
@ -8,7 +8,7 @@ import WorkItemDevelopmentMRItem from '~/work_items/components/work_item_develop
|
|||
describe('WorkItemDevelopmentMRItem', () => {
|
||||
let wrapper;
|
||||
|
||||
const openMergeRequest = workItemDevelopmentNodes[0].mergeRequest;
|
||||
const openMergeRequest = workItemDevelopmentMRNodes[0].mergeRequest;
|
||||
const closedMergeRequest = {
|
||||
...openMergeRequest,
|
||||
state: STATUS_CLOSED,
|
||||
|
|
@ -18,12 +18,12 @@ describe('WorkItemDevelopmentMRItem', () => {
|
|||
state: STATUS_MERGED,
|
||||
};
|
||||
|
||||
const mergeRequestWithNoAssignees = workItemDevelopmentNodes[1].mergeRequest;
|
||||
const mergeRequestWithNoAssignees = workItemDevelopmentMRNodes[1].mergeRequest;
|
||||
|
||||
const createComponent = ({ mergeRequest = openMergeRequest, mountFn = shallowMount } = {}) => {
|
||||
wrapper = mountFn(WorkItemDevelopmentMRItem, {
|
||||
propsData: {
|
||||
mergeRequest,
|
||||
itemContent: mergeRequest,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -6,12 +6,31 @@ import WorkItemDevelopmentRelationshipList from '~/work_items/components/work_it
|
|||
describe('WorkItemDevelopmentRelationshipList', () => {
|
||||
let wrapper;
|
||||
|
||||
const devWidgetWithLessNumberOfItems = {
|
||||
const devWidgetWithTwoItems = {
|
||||
...workItemDevelopmentFragmentResponse(),
|
||||
closingMergeRequests: {
|
||||
nodes: [workItemDevelopmentFragmentResponse().closingMergeRequests.nodes[0]],
|
||||
__typename: 'WorkItemClosingMergeRequestConnection',
|
||||
},
|
||||
featureFlags: {
|
||||
nodes: [workItemDevelopmentFragmentResponse().featureFlags.nodes[0]],
|
||||
__typename: 'FeatureFlagConnection',
|
||||
},
|
||||
};
|
||||
|
||||
const devWidgetWithThreeItems = {
|
||||
...workItemDevelopmentFragmentResponse(),
|
||||
closingMergeRequests: {
|
||||
nodes: [workItemDevelopmentFragmentResponse().closingMergeRequests.nodes[0]],
|
||||
__typename: 'WorkItemClosingMergeRequestConnection',
|
||||
},
|
||||
featureFlags: {
|
||||
nodes: [
|
||||
workItemDevelopmentFragmentResponse().featureFlags.nodes[0],
|
||||
workItemDevelopmentFragmentResponse().featureFlags.nodes[1],
|
||||
],
|
||||
__typename: 'FeatureFlagConnection',
|
||||
},
|
||||
};
|
||||
|
||||
const createComponent = ({ workItemDevWidget = workItemDevelopmentFragmentResponse() } = {}) => {
|
||||
|
|
@ -36,8 +55,13 @@ describe('WorkItemDevelopmentRelationshipList', () => {
|
|||
expect(findShowMoreButton().exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('should not render the more button when the number of items are more than 3', () => {
|
||||
createComponent({ workItemDevWidget: devWidgetWithLessNumberOfItems });
|
||||
it('should not render the more button when the number of items are exactly 3', () => {
|
||||
createComponent({ workItemDevWidget: devWidgetWithThreeItems });
|
||||
expect(findShowMoreButton().exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('should not render the more button when the number of items are less than 3', () => {
|
||||
createComponent({ workItemDevWidget: devWidgetWithTwoItems });
|
||||
expect(findShowMoreButton().exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import { STATE_CLOSED, STATE_OPEN } from '~/work_items/constants';
|
|||
import {
|
||||
workItemResponseFactory,
|
||||
workItemDevelopmentFragmentResponse,
|
||||
workItemDevelopmentNodes,
|
||||
workItemDevelopmentMRNodes,
|
||||
} from 'jest/work_items/mock_data';
|
||||
|
||||
import WorkItemDevelopment from '~/work_items/components/work_item_development/work_item_development.vue';
|
||||
|
|
@ -30,11 +30,15 @@ describe('WorkItemDevelopment CE', () => {
|
|||
});
|
||||
const workItemWithOneMR = workItemResponseFactory({
|
||||
developmentWidgetPresent: true,
|
||||
developmentItems: workItemDevelopmentFragmentResponse([workItemDevelopmentNodes[0]], true),
|
||||
developmentItems: workItemDevelopmentFragmentResponse(
|
||||
[workItemDevelopmentMRNodes[0]],
|
||||
true,
|
||||
null,
|
||||
),
|
||||
});
|
||||
const workItemWithMRList = workItemResponseFactory({
|
||||
developmentWidgetPresent: true,
|
||||
developmentItems: workItemDevelopmentFragmentResponse(workItemDevelopmentNodes, true),
|
||||
developmentItems: workItemDevelopmentFragmentResponse(workItemDevelopmentMRNodes, true, null),
|
||||
});
|
||||
|
||||
const projectWorkItemResponseWithMRList = {
|
||||
|
|
@ -94,11 +98,11 @@ describe('WorkItemDevelopment CE', () => {
|
|||
const workItemWithEmptyMRList = workItemResponseFactory({
|
||||
canUpdate: true,
|
||||
developmentWidgetPresent: true,
|
||||
developmentItems: workItemDevelopmentFragmentResponse([]),
|
||||
developmentItems: workItemDevelopmentFragmentResponse([], false, null),
|
||||
});
|
||||
const workItemWithAutoCloseFlagEnabled = workItemResponseFactory({
|
||||
developmentWidgetPresent: true,
|
||||
developmentItems: workItemDevelopmentFragmentResponse(workItemDevelopmentNodes, true),
|
||||
developmentItems: workItemDevelopmentFragmentResponse(workItemDevelopmentMRNodes, true, null),
|
||||
});
|
||||
|
||||
const successQueryHandlerWithEmptyMRList = jest.fn().mockResolvedValue({
|
||||
|
|
@ -264,8 +268,8 @@ describe('WorkItemDevelopment CE', () => {
|
|||
it.each`
|
||||
queryHandler | message | workItemState | linkedMRsNumber
|
||||
${successQueryHandlerWithOneMR} | ${'This task will be closed when the following is merged.'} | ${STATE_OPEN} | ${1}
|
||||
${successQueryHandlerWithMRList} | ${'This task will be closed when any of the following is merged.'} | ${STATE_OPEN} | ${workItemDevelopmentNodes.length}
|
||||
${successQueryHandlerWithClosedWorkItem} | ${'The task was closed automatically when a branch was merged.'} | ${STATE_CLOSED} | ${workItemDevelopmentNodes.length}
|
||||
${successQueryHandlerWithMRList} | ${'This task will be closed when any of the following is merged.'} | ${STATE_OPEN} | ${workItemDevelopmentMRNodes.length}
|
||||
${successQueryHandlerWithClosedWorkItem} | ${'The task was closed automatically when a branch was merged.'} | ${STATE_CLOSED} | ${workItemDevelopmentMRNodes.length}
|
||||
`(
|
||||
'when the workItemState is `$workItemState` and number of linked MRs is `$linkedMRsNumber` shows message `$message`',
|
||||
async ({ queryHandler, message }) => {
|
||||
|
|
|
|||
|
|
@ -899,7 +899,7 @@ export const workItemBlockedByLinkedItemsResponse = {
|
|||
},
|
||||
};
|
||||
|
||||
export const workItemDevelopmentNodes = [
|
||||
export const workItemDevelopmentMRNodes = [
|
||||
{
|
||||
fromMrDescription: true,
|
||||
mergeRequest: {
|
||||
|
|
@ -1084,15 +1084,47 @@ export const workItemDevelopmentNodes = [
|
|||
},
|
||||
];
|
||||
|
||||
export const workItemDevelopmentFeatureFlagNodes = [
|
||||
{
|
||||
active: true,
|
||||
id: 'gid://gitlab/Operations::FeatureFlag/1',
|
||||
name: 'flag1',
|
||||
path: 'http://127.0.0.1:3000/flightjs/Flight/-/feature_flags/1/edit',
|
||||
reference: '[feature_flag:1]',
|
||||
__typename: 'FeatureFlag',
|
||||
},
|
||||
{
|
||||
active: false,
|
||||
id: 'gid://gitlab/Operations::FeatureFlag/2',
|
||||
name: 'flag2',
|
||||
path: 'http://127.0.0.1:3000/flightjs/Flight/-/feature_flags/2/edit',
|
||||
reference: '[feature_flag:2]',
|
||||
__typename: 'FeatureFlag',
|
||||
},
|
||||
{
|
||||
active: false,
|
||||
id: 'gid://gitlab/Operations::FeatureFlag/3',
|
||||
name: 'flag3',
|
||||
path: 'http://127.0.0.1:3000/flightjs/Flight/-/feature_flags/3/edit',
|
||||
reference: '[feature_flag:3]',
|
||||
__typename: 'FeatureFlag',
|
||||
},
|
||||
];
|
||||
|
||||
export const workItemDevelopmentFragmentResponse = (
|
||||
nodes = workItemDevelopmentNodes,
|
||||
mrNodes = workItemDevelopmentMRNodes,
|
||||
willAutoCloseByMergeRequest = false,
|
||||
featureFlagNodes = workItemDevelopmentFeatureFlagNodes,
|
||||
) => {
|
||||
return {
|
||||
type: 'DEVELOPMENT',
|
||||
willAutoCloseByMergeRequest,
|
||||
featureFlags: {
|
||||
nodes: featureFlagNodes,
|
||||
__typename: 'FeatureFlagConnection',
|
||||
},
|
||||
closingMergeRequests: {
|
||||
nodes,
|
||||
nodes: mrNodes,
|
||||
__typename: 'WorkItemClosingMergeRequestConnection',
|
||||
},
|
||||
__typename: 'WorkItemWidgetDevelopment',
|
||||
|
|
|
|||
|
|
@ -262,21 +262,11 @@ RSpec.describe AppearancesHelper do
|
|||
end
|
||||
end
|
||||
|
||||
context 'with add_gitlab_white_text option' do
|
||||
let(:options) { { add_gitlab_white_text: true } }
|
||||
context 'with add_gitlab_logo_text option' do
|
||||
let(:options) { { add_gitlab_logo_text: true } }
|
||||
|
||||
it 'renders shared/logo_with_white_text partial' do
|
||||
expect(helper).to receive(:render).with(partial: 'shared/logo_with_white_text', formats: :svg)
|
||||
|
||||
subject
|
||||
end
|
||||
end
|
||||
|
||||
context 'with add_gitlab_black_text option' do
|
||||
let(:options) { { add_gitlab_black_text: true } }
|
||||
|
||||
it 'renders shared/logo_with_black_text partial' do
|
||||
expect(helper).to receive(:render).with(partial: 'shared/logo_with_black_text', formats: :svg)
|
||||
it 'renders shared/logo_with_text partial' do
|
||||
expect(helper).to receive(:render).with(partial: 'shared/logo_with_text', formats: :svg)
|
||||
|
||||
subject
|
||||
end
|
||||
|
|
|
|||
|
|
@ -32,8 +32,8 @@ RSpec.describe ContainerRegistry::ContainerRegistryHelper, feature_category: :co
|
|||
help_page_path: help_page_path('user/packages/container_registry/index.md'),
|
||||
two_factor_auth_help_link: help_page_path('user/profile/account/two_factor_authentication.md'),
|
||||
personal_access_tokens_help_link: help_page_path('user/profile/personal_access_tokens.md'),
|
||||
no_containers_image: match_asset_path('illustrations/docker-empty-state.svg'),
|
||||
containers_error_image: match_asset_path('illustrations/docker-error-state.svg'),
|
||||
no_containers_image: match_asset_path('illustrations/status/status-nothing-md.svg'),
|
||||
containers_error_image: match_asset_path('illustrations/status/status-fail-md.svg'),
|
||||
repository_url: escape_once(project.container_registry_url),
|
||||
registry_host_url_with_port: escape_once(Gitlab.config.registry.host_port),
|
||||
expiration_policy_help_page_path:
|
||||
|
|
|
|||
|
|
@ -86,9 +86,7 @@ RSpec.describe API::Ci::Helpers::Runner, feature_category: :runner do
|
|||
end
|
||||
|
||||
describe '#current_runner_manager', :freeze_time, feature_category: :fleet_visibility do
|
||||
let_it_be(:group) { create(:group) }
|
||||
|
||||
let(:runner) { create(:ci_runner, :group, token: 'foo', groups: [group]) }
|
||||
let(:runner) { create(:ci_runner, token: 'foo') }
|
||||
let(:runner_manager) { create(:ci_runner_machine, runner: runner, system_xid: 'bar', contacted_at: 1.hour.ago) }
|
||||
|
||||
subject(:current_runner_manager) { helper.current_runner_manager }
|
||||
|
|
@ -115,8 +113,6 @@ RSpec.describe API::Ci::Helpers::Runner, feature_category: :runner do
|
|||
expect(current_runner_manager.system_xid).to eq('new_system_id')
|
||||
expect(current_runner_manager.contacted_at).to be_nil
|
||||
expect(current_runner_manager.runner).to eq(runner)
|
||||
expect(current_runner_manager.runner_type).to eq(runner.runner_type)
|
||||
expect(current_runner_manager.sharding_key_id).to eq(runner.sharding_key_id)
|
||||
end
|
||||
|
||||
it 'creates a new <legacy> runner manager if system_id is not specified', :aggregate_failures do
|
||||
|
|
@ -127,8 +123,6 @@ RSpec.describe API::Ci::Helpers::Runner, feature_category: :runner do
|
|||
expect(current_runner_manager).not_to be_nil
|
||||
expect(current_runner_manager.system_xid).to eq(::API::Ci::Helpers::Runner::LEGACY_SYSTEM_XID)
|
||||
expect(current_runner_manager.runner).to eq(runner)
|
||||
expect(current_runner_manager.runner_type).to eq(runner.runner_type)
|
||||
expect(current_runner_manager.sharding_key_id).to eq(runner.sharding_key_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,59 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::BackgroundMigration::BackfillRunnerTypeAndShardingKeyIdOnCiRunnerManagers,
|
||||
schema: 20241003110148, migration: :gitlab_ci, feature_category: :runner do
|
||||
let(:connection) { Ci::ApplicationRecord.connection }
|
||||
|
||||
describe '#perform' do
|
||||
let(:runner_managers) { table(:ci_runner_machines) }
|
||||
let(:runners) { table(:ci_runners) }
|
||||
let(:args) do
|
||||
min, max = runner_managers.pick('MIN(id)', 'MAX(id)')
|
||||
|
||||
{
|
||||
start_id: min,
|
||||
end_id: max,
|
||||
batch_table: 'ci_runner_machines',
|
||||
batch_column: 'id',
|
||||
sub_batch_size: 100,
|
||||
pause_ms: 0,
|
||||
connection: connection
|
||||
}
|
||||
end
|
||||
|
||||
let!(:instance_runner) { runners.create!(runner_type: 1) }
|
||||
let!(:group_runner) { runners.create!(runner_type: 2, sharding_key_id: 89) }
|
||||
let!(:project_runner1) { runners.create!(runner_type: 3, sharding_key_id: 10) }
|
||||
let!(:project_runner2) { runners.create!(runner_type: 3, sharding_key_id: 100) }
|
||||
|
||||
let!(:runner_manager1) { create_runner_manager(instance_runner, system_xid: 'a') }
|
||||
let!(:runner_manager2_1) { create_runner_manager(group_runner, system_xid: 'a') }
|
||||
let!(:runner_manager2_2) { create_runner_manager(group_runner, system_xid: 'b') }
|
||||
let!(:runner_manager3) { create_runner_manager(project_runner1, system_xid: 'a') }
|
||||
let!(:runner_manager4) { create_runner_manager(project_runner2, system_xid: 'b') }
|
||||
|
||||
subject(:perform_migration) { described_class.new(**args).perform }
|
||||
|
||||
it 'backfills runner_type and sharding_key_id', :aggregate_failures do
|
||||
expect { perform_migration }
|
||||
.to change { runner_manager2_1.reload.sharding_key_id }.from(nil).to(group_runner.sharding_key_id)
|
||||
.and change { runner_manager2_2.reload.sharding_key_id }.from(nil).to(group_runner.sharding_key_id)
|
||||
.and change { runner_manager3.reload.sharding_key_id }.from(nil).to(project_runner1.sharding_key_id)
|
||||
.and change { runner_manager4.reload.sharding_key_id }.from(nil).to(project_runner2.sharding_key_id)
|
||||
.and not_change { runner_manager2_1.reload.runner_type }.from(group_runner.runner_type)
|
||||
.and not_change { runner_manager2_2.reload.runner_type }.from(group_runner.runner_type)
|
||||
.and not_change { runner_manager3.reload.runner_type }.from(project_runner1.runner_type)
|
||||
.and not_change { runner_manager4.reload.runner_type }.from(project_runner2.runner_type)
|
||||
|
||||
expect(runner_manager1.sharding_key_id).to be_nil
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def create_runner_manager(runner, **attrs)
|
||||
runner_managers.create!(runner_id: runner.id, runner_type: runner.runner_type, **attrs)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
require_migration!
|
||||
|
||||
RSpec.describe QueueBackfillRunnerTypeAndShardingKeyIdOnCiRunnerManagers, migration: :gitlab_ci, feature_category: :runner do
|
||||
let!(:batched_migration) { described_class::MIGRATION }
|
||||
|
||||
it 'schedules a new batched migration' do
|
||||
reversible_migration do |migration|
|
||||
migration.before -> {
|
||||
expect(batched_migration).not_to have_scheduled_batched_migration
|
||||
}
|
||||
|
||||
migration.after -> {
|
||||
expect(batched_migration).to have_scheduled_batched_migration(
|
||||
gitlab_schema: :gitlab_ci,
|
||||
table_name: :ci_runner_machines,
|
||||
column_name: :id,
|
||||
interval: described_class::DELAY_INTERVAL,
|
||||
batch_size: described_class::BATCH_SIZE,
|
||||
sub_batch_size: described_class::SUB_BATCH_SIZE
|
||||
)
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -3,9 +3,6 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Ci::RunnerManager, feature_category: :fleet_visibility, type: :model do
|
||||
let_it_be(:group) { create(:group) }
|
||||
let_it_be(:project) { create(:project, group: group) }
|
||||
|
||||
it_behaves_like 'having unique enum values'
|
||||
|
||||
it_behaves_like 'it has loose foreign keys' do
|
||||
|
|
@ -21,32 +18,12 @@ RSpec.describe Ci::RunnerManager, feature_category: :fleet_visibility, type: :mo
|
|||
it { is_expected.to validate_presence_of(:runner) }
|
||||
it { is_expected.to validate_presence_of(:system_xid) }
|
||||
it { is_expected.to validate_length_of(:system_xid).is_at_most(64) }
|
||||
it { is_expected.to validate_presence_of(:runner_type).on(:create) }
|
||||
it { is_expected.to validate_presence_of(:sharding_key_id).on(:create) }
|
||||
it { is_expected.to validate_length_of(:version).is_at_most(2048) }
|
||||
it { is_expected.to validate_length_of(:revision).is_at_most(255) }
|
||||
it { is_expected.to validate_length_of(:platform).is_at_most(255) }
|
||||
it { is_expected.to validate_length_of(:architecture).is_at_most(255) }
|
||||
it { is_expected.to validate_length_of(:ip_address).is_at_most(1024) }
|
||||
|
||||
context 'when runner manager is instance type', :aggregate_failures do
|
||||
let(:runner_manager) { build(:ci_runner_machine, runner_type: :instance_type) }
|
||||
|
||||
it { expect(runner_manager).to be_valid }
|
||||
|
||||
context 'when sharding_key_id is present' do
|
||||
let(:runner_manager) do
|
||||
build(:ci_runner_machine, runner: build(:ci_runner, sharding_key_id: non_existing_record_id))
|
||||
end
|
||||
|
||||
it 'is invalid' do
|
||||
expect(runner_manager).to be_invalid
|
||||
expect(runner_manager.errors.full_messages).to contain_exactly(
|
||||
'Runner manager cannot have sharding_key_id assigned')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when runner has config' do
|
||||
it 'is valid' do
|
||||
runner_manager = build(:ci_runner_machine, config: { gpus: "all" })
|
||||
|
|
@ -62,60 +39,6 @@ RSpec.describe Ci::RunnerManager, feature_category: :fleet_visibility, type: :mo
|
|||
expect(runner_manager).not_to be_valid
|
||||
end
|
||||
end
|
||||
|
||||
describe 'shading_key_id validations' do
|
||||
let(:runner_manager) { build(:ci_runner_machine, runner: runner) }
|
||||
|
||||
context 'with instance runner' do
|
||||
let(:runner) { build(:ci_runner, :instance) }
|
||||
|
||||
it { expect(runner).to be_valid }
|
||||
|
||||
context 'when sharding_key_id is not present' do
|
||||
before do
|
||||
runner.sharding_key_id = nil
|
||||
runner_manager.sharding_key_id = nil
|
||||
end
|
||||
|
||||
it { expect(runner_manager).to be_valid }
|
||||
end
|
||||
end
|
||||
|
||||
context 'with group runner' do
|
||||
let(:runner) { build(:ci_runner, :group, groups: [group]) }
|
||||
|
||||
it { expect(runner_manager).to be_valid }
|
||||
|
||||
context 'when sharding_key_id is not present' do
|
||||
before do
|
||||
runner.sharding_key_id = nil
|
||||
runner_manager.sharding_key_id = nil
|
||||
end
|
||||
|
||||
it 'adds error to model', :aggregate_failures do
|
||||
expect(runner_manager).not_to be_valid
|
||||
expect(runner_manager.errors[:sharding_key_id]).to contain_exactly("can't be blank")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with project runner' do
|
||||
let(:runner) { build(:ci_runner, :project, projects: [project]) }
|
||||
|
||||
it { expect(runner).to be_valid }
|
||||
|
||||
context 'when sharding_key_id is not present' do
|
||||
before do
|
||||
runner.sharding_key_id = nil
|
||||
end
|
||||
|
||||
it 'adds error to model', :aggregate_failures do
|
||||
expect(runner_manager).not_to be_valid
|
||||
expect(runner_manager.errors[:sharding_key_id]).to contain_exactly("can't be blank")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'status scopes', :freeze_time do
|
||||
|
|
@ -665,44 +588,4 @@ RSpec.describe Ci::RunnerManager, feature_category: :fleet_visibility, type: :mo
|
|||
it { is_expected.to contain_exactly existing_build }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#set_runner_type_and_sharding_key_id' do
|
||||
let(:runner_manager) { build(:ci_runner_machine, runner: runner) }
|
||||
|
||||
before do
|
||||
runner_manager.runner_type = nil
|
||||
runner_manager.sharding_key_id = nil
|
||||
end
|
||||
|
||||
shared_examples 'when sharding_key_id is not present' do
|
||||
before do
|
||||
runner_manager.save!
|
||||
end
|
||||
|
||||
it 'sets runner_type and sharding_key_id from runner', :aggregate_failures do
|
||||
expect(runner_manager.runner_type).to eq(runner.runner_type)
|
||||
expect(runner_manager.sharding_key_id).to eq(runner.sharding_key_id)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with instance runner' do
|
||||
let(:runner) { build(:ci_runner, :instance) }
|
||||
|
||||
it { expect(runner).to be_valid }
|
||||
|
||||
it_behaves_like 'when sharding_key_id is not present'
|
||||
end
|
||||
|
||||
context 'with group runner' do
|
||||
let(:runner) { build(:ci_runner, :group, groups: [group]) }
|
||||
|
||||
it_behaves_like 'when sharding_key_id is not present'
|
||||
end
|
||||
|
||||
context 'with project runner' do
|
||||
let(:runner) { build(:ci_runner, :project, projects: [project]) }
|
||||
|
||||
it_behaves_like 'when sharding_key_id is not present'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in New Issue