Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
ba9892d3c1
commit
4dc41ac252
|
@ -30,9 +30,11 @@ import {
|
|||
TOKEN_TITLE_ASSIGNEE,
|
||||
TOKEN_TITLE_AUTHOR,
|
||||
TOKEN_TITLE_CONFIDENTIAL,
|
||||
TOKEN_TITLE_CONTACT,
|
||||
TOKEN_TITLE_LABEL,
|
||||
TOKEN_TITLE_MILESTONE,
|
||||
TOKEN_TITLE_MY_REACTION,
|
||||
TOKEN_TITLE_ORGANIZATION,
|
||||
TOKEN_TITLE_RELEASE,
|
||||
TOKEN_TITLE_TYPE,
|
||||
} from '~/vue_shared/components/filtered_search_bar/constants';
|
||||
|
@ -54,9 +56,11 @@ import {
|
|||
TOKEN_TYPE_ASSIGNEE,
|
||||
TOKEN_TYPE_AUTHOR,
|
||||
TOKEN_TYPE_CONFIDENTIAL,
|
||||
TOKEN_TYPE_CONTACT,
|
||||
TOKEN_TYPE_LABEL,
|
||||
TOKEN_TYPE_MILESTONE,
|
||||
TOKEN_TYPE_MY_REACTION,
|
||||
TOKEN_TYPE_ORGANIZATION,
|
||||
TOKEN_TYPE_RELEASE,
|
||||
TOKEN_TYPE_TYPE,
|
||||
UPDATED_DESC,
|
||||
|
@ -113,6 +117,8 @@ export default {
|
|||
'autocompleteAwardEmojisPath',
|
||||
'calendarPath',
|
||||
'canBulkUpdate',
|
||||
'canReadCrmContact',
|
||||
'canReadCrmOrganization',
|
||||
'emptyStateSvgPath',
|
||||
'exportCsvPath',
|
||||
'fullPath',
|
||||
|
@ -360,6 +366,28 @@ export default {
|
|||
});
|
||||
}
|
||||
|
||||
if (this.canReadCrmContact) {
|
||||
tokens.push({
|
||||
type: TOKEN_TYPE_CONTACT,
|
||||
title: TOKEN_TITLE_CONTACT,
|
||||
icon: 'user',
|
||||
token: GlFilteredSearchToken,
|
||||
operators: OPERATOR_IS_ONLY,
|
||||
unique: true,
|
||||
});
|
||||
}
|
||||
|
||||
if (this.canReadCrmOrganization) {
|
||||
tokens.push({
|
||||
type: TOKEN_TYPE_ORGANIZATION,
|
||||
title: TOKEN_TITLE_ORGANIZATION,
|
||||
icon: 'users',
|
||||
token: GlFilteredSearchToken,
|
||||
operators: OPERATOR_IS_ONLY,
|
||||
unique: true,
|
||||
});
|
||||
}
|
||||
|
||||
if (this.eeSearchTokens.length) {
|
||||
tokens.push(...this.eeSearchTokens);
|
||||
}
|
||||
|
|
|
@ -82,6 +82,8 @@ export function mountIssuesListApp() {
|
|||
canBulkUpdate,
|
||||
canEdit,
|
||||
canImportIssues,
|
||||
canReadCrmContact,
|
||||
canReadCrmOrganization,
|
||||
email,
|
||||
emailsHelpPagePath,
|
||||
emptyStateSvgPath,
|
||||
|
@ -131,6 +133,8 @@ export function mountIssuesListApp() {
|
|||
autocompleteAwardEmojisPath,
|
||||
calendarPath,
|
||||
canBulkUpdate: parseBoolean(canBulkUpdate),
|
||||
canReadCrmContact: parseBoolean(canReadCrmContact),
|
||||
canReadCrmOrganization: parseBoolean(canReadCrmOrganization),
|
||||
emptyStateSvgPath,
|
||||
fullPath,
|
||||
groupPath,
|
||||
|
|
|
@ -105,7 +105,7 @@ export default (resolvers = {}, config = {}) => {
|
|||
const {
|
||||
baseUrl,
|
||||
batchMax = 10,
|
||||
cacheConfig,
|
||||
cacheConfig = { typePolicies: {}, possibleTypes: {} },
|
||||
fetchPolicy = fetchPolicies.CACHE_FIRST,
|
||||
typeDefs,
|
||||
path = '/api/graphql',
|
||||
|
@ -221,9 +221,15 @@ export default (resolvers = {}, config = {}) => {
|
|||
typeDefs,
|
||||
link: appLink,
|
||||
cache: new InMemoryCache({
|
||||
typePolicies,
|
||||
possibleTypes,
|
||||
...cacheConfig,
|
||||
typePolicies: {
|
||||
...typePolicies,
|
||||
...cacheConfig.typePolicies,
|
||||
},
|
||||
possibleTypes: {
|
||||
...possibleTypes,
|
||||
...cacheConfig.possibleTypes,
|
||||
},
|
||||
}),
|
||||
resolvers,
|
||||
defaultOptions: {
|
||||
|
|
|
@ -107,7 +107,7 @@ export default {
|
|||
<td v-if="error" class="js-error-lazy-load-diff diff-loading-error-block">
|
||||
{{ __('Unable to load the diff') }}
|
||||
<button
|
||||
class="btn-link btn-link-retry gl-p-0 js-toggle-lazy-diff-retry-button"
|
||||
class="gl-button btn-link btn-link-retry gl-p-0 js-toggle-lazy-diff-retry-button gl-reset-font-size!"
|
||||
@click="fetchDiff"
|
||||
>
|
||||
{{ __('Try again') }}
|
||||
|
|
|
@ -58,7 +58,7 @@ export default {
|
|||
<div class="issuable-note-warning" data-testid="email-participants-warning">
|
||||
<gl-sprintf :message="message">
|
||||
<template #andMore>
|
||||
<button type="button" class="btn-transparent btn-link" @click="showMoreParticipants">
|
||||
<button type="button" class="gl-button btn-link" @click="showMoreParticipants">
|
||||
{{ moreLabel }}
|
||||
</button>
|
||||
</template>
|
||||
|
|
|
@ -72,7 +72,7 @@ export default {
|
|||
{{ replies.length }} {{ n__('reply', 'replies', replies.length) }}
|
||||
</gl-button>
|
||||
{{ __('Last reply by') }}
|
||||
<a :href="lastReply.author.path" class="btn btn-link author-link gl-mx-2">
|
||||
<a :href="lastReply.author.path" class="btn btn-link author-link gl-mx-2 gl-button">
|
||||
{{ lastReply.author.name }}
|
||||
</a>
|
||||
<time-ago-tooltip :time="lastReply.created_at" tooltip-placement="bottom" />
|
||||
|
|
|
@ -27,7 +27,7 @@ export default {
|
|||
<div id="peek-view-add-request" class="view">
|
||||
<form class="form-inline" @submit.prevent>
|
||||
<button
|
||||
class="btn-blank btn-link bold gl-text-blue-300"
|
||||
class="btn-link bold gl-text-blue-300 gl-button"
|
||||
type="button"
|
||||
:title="__(`Add request manually`)"
|
||||
@click="toggleInput"
|
||||
|
|
|
@ -150,7 +150,7 @@ export default {
|
|||
<div id="js-peek" :class="env">
|
||||
<div
|
||||
v-if="currentRequest"
|
||||
class="d-flex container-fluid container-limited justify-content-center"
|
||||
class="d-flex container-fluid container-limited justify-content-center gl-align-items-center"
|
||||
data-qa-selector="performance_bar"
|
||||
>
|
||||
<div id="peek-view-host" class="view">
|
||||
|
|
|
@ -55,7 +55,12 @@ export default {
|
|||
{{ __('None') }}
|
||||
<template v-if="editable">
|
||||
-
|
||||
<button type="button" class="btn-link" data-testid="assign-yourself" @click="assignSelf">
|
||||
<button
|
||||
type="button"
|
||||
class="gl-button btn-link gl-reset-color!"
|
||||
data-testid="assign-yourself"
|
||||
@click="assignSelf"
|
||||
>
|
||||
{{ __('assign yourself') }}
|
||||
</button>
|
||||
</template>
|
||||
|
|
|
@ -26,7 +26,7 @@ export default {
|
|||
};
|
||||
</script>
|
||||
<template>
|
||||
<button type="button" class="btn-link">
|
||||
<button type="button" class="gl-button btn-link">
|
||||
<assignee-avatar :user="user" :img-size="24" :issuable-type="issuableType" />
|
||||
<user-name-with-status
|
||||
:name="user.name"
|
||||
|
|
|
@ -123,7 +123,7 @@ export default {
|
|||
:user="user"
|
||||
:issuable-type="issuableType"
|
||||
/>
|
||||
<button v-if="hasMoreThanTwoAssignees" class="btn-link" type="button">
|
||||
<button v-if="hasMoreThanTwoAssignees" class="btn-link gl-button" type="button">
|
||||
<span
|
||||
class="avatar-counter sidebar-avatar-counter gl-display-flex gl-align-items-center gl-pl-3"
|
||||
>
|
||||
|
|
|
@ -120,7 +120,7 @@ export default {
|
|||
<div v-if="renderShowMoreSection" class="user-list-more gl-hover-text-blue-800">
|
||||
<button
|
||||
type="button"
|
||||
class="btn-link"
|
||||
class="btn-link gl-button gl-reset-color!"
|
||||
data-qa-selector="more_assignees_link"
|
||||
@click="toggleShowLess"
|
||||
>
|
||||
|
|
|
@ -17,7 +17,7 @@ export default {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<button type="button" class="btn-link">
|
||||
<button type="button" class="btn-link gl-button">
|
||||
<reviewer-avatar :user="user" :img-size="24" />
|
||||
<span class="author"> {{ user.name }} </span>
|
||||
</button>
|
||||
|
|
|
@ -95,7 +95,7 @@ export default {
|
|||
>
|
||||
<gl-icon v-if="hasNoUsers" name="user" :aria-label="__('None')" />
|
||||
<collapsed-reviewer v-for="user in collapsedUsers" :key="user.id" :user="user" />
|
||||
<button v-if="hasMoreThanTwoReviewers" class="btn-link" type="button">
|
||||
<button v-if="hasMoreThanTwoReviewers" class="btn-link gl-button" type="button">
|
||||
<span
|
||||
class="avatar-counter sidebar-avatar-counter gl-display-flex gl-align-items-center gl-pl-3"
|
||||
>
|
||||
|
|
|
@ -2,6 +2,7 @@ import produce from 'immer';
|
|||
import VueApollo from 'vue-apollo';
|
||||
import getIssueStateQuery from '~/issues/show/queries/get_issue_state.query.graphql';
|
||||
import createDefaultClient from '~/lib/graphql';
|
||||
import { temporaryConfig } from '~/work_items/graphql/provider';
|
||||
|
||||
const resolvers = {
|
||||
Mutation: {
|
||||
|
@ -15,7 +16,12 @@ const resolvers = {
|
|||
},
|
||||
};
|
||||
|
||||
export const defaultClient = createDefaultClient(resolvers);
|
||||
export const defaultClient = createDefaultClient(
|
||||
resolvers,
|
||||
// should be removed with the rollout of work item assignees FF
|
||||
// https://gitlab.com/gitlab-org/gitlab/-/issues/363030
|
||||
temporaryConfig,
|
||||
);
|
||||
|
||||
export const apolloProvider = new VueApollo({
|
||||
defaultClient,
|
||||
|
|
|
@ -17,7 +17,7 @@ const ERROR_HTML = `<div class="nothing-here-block">${spriteIcon(
|
|||
's16',
|
||||
)} Could not load diff</div>`;
|
||||
const COLLAPSED_HTML =
|
||||
'<div class="nothing-here-block diff-collapsed">This diff is collapsed. <button class="click-to-expand btn btn-link">Click to expand it.</button></div>';
|
||||
'<div class="nothing-here-block diff-collapsed">This diff is collapsed. <button class="click-to-expand btn btn-link gl-button">Click to expand it.</button></div>';
|
||||
|
||||
export default class SingleFileDiff {
|
||||
constructor(file) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { __ } from '~/locale';
|
||||
import { __, s__ } from '~/locale';
|
||||
|
||||
export const DEBOUNCE_DELAY = 500;
|
||||
export const MAX_RECENT_TOKENS_SIZE = 3;
|
||||
|
@ -46,11 +46,13 @@ export const SortDirection = {
|
|||
export const FILTERED_SEARCH_LABELS = 'labels';
|
||||
export const FILTERED_SEARCH_TERM = 'filtered-search-term';
|
||||
|
||||
export const TOKEN_TITLE_AUTHOR = __('Author');
|
||||
export const TOKEN_TITLE_ASSIGNEE = __('Assignee');
|
||||
export const TOKEN_TITLE_MILESTONE = __('Milestone');
|
||||
export const TOKEN_TITLE_LABEL = __('Label');
|
||||
export const TOKEN_TITLE_TYPE = __('Type');
|
||||
export const TOKEN_TITLE_RELEASE = __('Release');
|
||||
export const TOKEN_TITLE_MY_REACTION = __('My-Reaction');
|
||||
export const TOKEN_TITLE_AUTHOR = __('Author');
|
||||
export const TOKEN_TITLE_CONFIDENTIAL = __('Confidential');
|
||||
export const TOKEN_TITLE_CONTACT = s__('Crm|Contact');
|
||||
export const TOKEN_TITLE_LABEL = __('Label');
|
||||
export const TOKEN_TITLE_MILESTONE = __('Milestone');
|
||||
export const TOKEN_TITLE_MY_REACTION = __('My-Reaction');
|
||||
export const TOKEN_TITLE_ORGANIZATION = s__('Crm|Organization');
|
||||
export const TOKEN_TITLE_RELEASE = __('Release');
|
||||
export const TOKEN_TITLE_TYPE = __('Type');
|
||||
|
|
|
@ -22,10 +22,6 @@ export default {
|
|||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
statusBadgeClass: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
statusIcon: {
|
||||
type: String,
|
||||
required: true,
|
||||
|
@ -162,7 +158,6 @@ export default {
|
|||
<template v-else>
|
||||
<issuable-title
|
||||
:issuable="issuable"
|
||||
:status-badge-class="statusBadgeClass"
|
||||
:status-icon="statusIcon"
|
||||
:enable-edit="enableEdit"
|
||||
@edit-issuable="$emit('edit-issuable', $event)"
|
||||
|
|
|
@ -40,11 +40,6 @@ export default {
|
|||
required: false,
|
||||
default: '',
|
||||
},
|
||||
statusBadgeClass: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
statusIcon: {
|
||||
type: String,
|
||||
required: false,
|
||||
|
@ -113,11 +108,7 @@ export default {
|
|||
<template>
|
||||
<div class="detail-page-header">
|
||||
<div class="detail-page-header-body">
|
||||
<gl-badge
|
||||
class="issuable-status-badge gl-mr-3"
|
||||
:class="statusBadgeClass"
|
||||
:variant="badgeVariant"
|
||||
>
|
||||
<gl-badge class="issuable-status-badge gl-mr-3" :variant="badgeVariant">
|
||||
<gl-icon v-if="statusIcon" :name="statusIcon" :class="statusIconClass" />
|
||||
<span class="gl-display-none gl-sm-display-block"><slot name="status-badge"></slot></span>
|
||||
</gl-badge>
|
||||
|
|
|
@ -17,11 +17,6 @@ export default {
|
|||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
statusBadgeClass: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
statusIcon: {
|
||||
type: String,
|
||||
required: false,
|
||||
|
@ -108,7 +103,6 @@ export default {
|
|||
<div class="issuable-show-container" data-qa-selector="issuable_show_container">
|
||||
<issuable-header
|
||||
:issuable-state="issuable.state"
|
||||
:status-badge-class="statusBadgeClass"
|
||||
:status-icon="statusIcon"
|
||||
:status-icon-class="statusIconClass"
|
||||
:blocked="issuable.blocked"
|
||||
|
@ -127,7 +121,6 @@ export default {
|
|||
|
||||
<issuable-body
|
||||
:issuable="issuable"
|
||||
:status-badge-class="statusBadgeClass"
|
||||
:status-icon="statusIcon"
|
||||
:status-icon-class="statusIconClass"
|
||||
:enable-edit="enableEdit"
|
||||
|
|
|
@ -29,10 +29,6 @@ export default {
|
|||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
statusBadgeClass: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
statusIcon: {
|
||||
type: String,
|
||||
required: true,
|
||||
|
@ -92,11 +88,7 @@ export default {
|
|||
<div
|
||||
class="issue-sticky-header-text gl-display-flex gl-align-items-center gl-mx-auto gl-px-5"
|
||||
>
|
||||
<gl-badge
|
||||
class="gl-white-space-nowrap gl-mr-3"
|
||||
:class="statusBadgeClass"
|
||||
:variant="badgeVariant"
|
||||
>
|
||||
<gl-badge class="gl-white-space-nowrap gl-mr-3" :variant="badgeVariant">
|
||||
<gl-icon v-if="statusIcon" class="gl-sm-display-none" :name="statusIcon" />
|
||||
<span class="gl-display-none gl-sm-display-block">
|
||||
<slot name="status-badge"></slot>
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
<script>
|
||||
import { GlAvatar, GlLink } from '@gitlab/ui';
|
||||
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlAvatar,
|
||||
GlLink,
|
||||
},
|
||||
props: {
|
||||
assignees: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
getUserId(id) {
|
||||
return getIdFromGraphQLId(id);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="gl-display-flex">
|
||||
<span class="gl-font-weight-bold gl-w-15 gl-pt-1">{{ __('Assignee(s)') }}</span>
|
||||
<div class="gl-mb-4">
|
||||
<gl-link
|
||||
v-for="user in assignees"
|
||||
:key="user.id"
|
||||
:href="user.webUrl"
|
||||
:title="`test`"
|
||||
:data-user-id="getUserId(user.id)"
|
||||
data-placement="top"
|
||||
class="gl-text-decoration-none! gl-text-black-normal! gl-display-flex gl-md-display-inline-flex! gl-align-items-center gl-mb-4 gl-md-mb-0 gl-mr-4 js-user-link"
|
||||
>
|
||||
<gl-avatar :size="24" :src="user.avatarUrl" />
|
||||
<span class="gl-pl-2">{{ user.name }}</span>
|
||||
</gl-link>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
|
@ -1,23 +1,27 @@
|
|||
<script>
|
||||
import { GlAlert, GlSkeletonLoader } from '@gitlab/ui';
|
||||
import { i18n } from '../constants';
|
||||
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||
import { i18n, WIDGET_TYPE_ASSIGNEE } from '../constants';
|
||||
import workItemQuery from '../graphql/work_item.query.graphql';
|
||||
import workItemTitleSubscription from '../graphql/work_item_title.subscription.graphql';
|
||||
import WorkItemActions from './work_item_actions.vue';
|
||||
import WorkItemState from './work_item_state.vue';
|
||||
import WorkItemTitle from './work_item_title.vue';
|
||||
import WorkItemLinks from './work_item_links/work_item_links.vue';
|
||||
import WorkItemAssignees from './work_item_assignees.vue';
|
||||
|
||||
export default {
|
||||
i18n,
|
||||
components: {
|
||||
GlAlert,
|
||||
GlSkeletonLoader,
|
||||
WorkItemAssignees,
|
||||
WorkItemActions,
|
||||
WorkItemTitle,
|
||||
WorkItemState,
|
||||
WorkItemLinks,
|
||||
},
|
||||
mixins: [glFeatureFlagMixin()],
|
||||
props: {
|
||||
workItemId: {
|
||||
type: String,
|
||||
|
@ -68,6 +72,12 @@ export default {
|
|||
canDelete() {
|
||||
return this.workItem?.userPermissions?.deleteWorkItem;
|
||||
},
|
||||
workItemAssigneesEnabled() {
|
||||
return this.glFeatures.workItemAssignees;
|
||||
},
|
||||
workItemAssignees() {
|
||||
return this.workItem?.mockWidgets?.find((widget) => widget.type === WIDGET_TYPE_ASSIGNEE);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -102,6 +112,10 @@ export default {
|
|||
@error="error = $event"
|
||||
/>
|
||||
</div>
|
||||
<work-item-assignees
|
||||
v-if="workItemAssigneesEnabled && workItemAssignees"
|
||||
:assignees="workItemAssignees.nodes"
|
||||
/>
|
||||
<work-item-state
|
||||
:work-item="workItem"
|
||||
@error="error = $event"
|
||||
|
|
|
@ -12,3 +12,5 @@ export const i18n = {
|
|||
};
|
||||
|
||||
export const DEFAULT_MODAL_TYPE = 'Task';
|
||||
|
||||
export const WIDGET_TYPE_ASSIGNEE = 'ASSIGNEES';
|
||||
|
|
|
@ -1,11 +1,57 @@
|
|||
import Vue from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import createDefaultClient from '~/lib/graphql';
|
||||
import typeDefs from './typedefs.graphql';
|
||||
|
||||
export const temporaryConfig = {
|
||||
typeDefs,
|
||||
cacheConfig: {
|
||||
possibleTypes: {
|
||||
LocalWorkItemWidget: ['LocalWorkItemAssignees'],
|
||||
},
|
||||
typePolicies: {
|
||||
WorkItem: {
|
||||
fields: {
|
||||
mockWidgets: {
|
||||
read() {
|
||||
return [
|
||||
{
|
||||
__typename: 'LocalWorkItemAssignees',
|
||||
type: 'ASSIGNEES',
|
||||
nodes: [
|
||||
{
|
||||
__typename: 'UserCore',
|
||||
id: 'gid://gitlab/User/1',
|
||||
avatarUrl: '',
|
||||
webUrl: '',
|
||||
// eslint-disable-next-line @gitlab/require-i18n-strings
|
||||
name: 'John Doe',
|
||||
username: 'doe_I',
|
||||
},
|
||||
{
|
||||
__typename: 'UserCore',
|
||||
id: 'gid://gitlab/User/2',
|
||||
avatarUrl: '',
|
||||
webUrl: '',
|
||||
// eslint-disable-next-line @gitlab/require-i18n-strings
|
||||
name: 'Marcus Rutherford',
|
||||
username: 'ruthfull',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export function createApolloProvider() {
|
||||
Vue.use(VueApollo);
|
||||
|
||||
const defaultClient = createDefaultClient();
|
||||
const defaultClient = createDefaultClient({}, temporaryConfig);
|
||||
|
||||
return new VueApollo({
|
||||
defaultClient,
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
enum LocalWidgetType {
|
||||
ASSIGNEES
|
||||
}
|
||||
|
||||
interface LocalWorkItemWidget {
|
||||
type: LocalWidgetType!
|
||||
}
|
||||
|
||||
type LocalWorkItemAssignees implements LocalWorkItemWidget {
|
||||
type: LocalWidgetType!
|
||||
nodes: [UserCore]
|
||||
}
|
||||
|
||||
extend type WorkItem {
|
||||
mockWidgets: [LocalWorkItemWidget]
|
||||
}
|
|
@ -3,5 +3,17 @@
|
|||
query workItem($id: WorkItemID!) {
|
||||
workItem(id: $id) {
|
||||
...WorkItem
|
||||
mockWidgets @client {
|
||||
... on LocalWorkItemAssignees {
|
||||
type
|
||||
nodes {
|
||||
id
|
||||
avatarUrl
|
||||
name
|
||||
username
|
||||
webUrl
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
@import 'framework/kbd';
|
||||
@import 'framework/header';
|
||||
@import 'framework/highlight';
|
||||
@import 'framework/issue_box';
|
||||
@import 'framework/lists';
|
||||
@import 'framework/logo';
|
||||
@import 'framework/job_log';
|
||||
|
|
|
@ -357,25 +357,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
.btn-link {
|
||||
padding: 0;
|
||||
background-color: transparent;
|
||||
color: $blue-600;
|
||||
font-weight: normal;
|
||||
border-radius: 0;
|
||||
border-color: transparent;
|
||||
border-width: 0;
|
||||
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus {
|
||||
color: $blue-800;
|
||||
text-decoration: underline;
|
||||
background-color: transparent;
|
||||
border-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
// The .btn-svg class is available for legacy icon buttons to
|
||||
// preserve a 34px height and have 16x16 icons at the same time.
|
||||
// Once a button is migrated (to the current 32px height)
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
/**
|
||||
* Issue box for showing Open/Closed state:
|
||||
* Used for Issue#show page, MergeRequest#show page etc
|
||||
*
|
||||
*/
|
||||
|
||||
.status-box {
|
||||
padding: 0 $gl-btn-padding;
|
||||
|
||||
border-radius: $border-radius-default;
|
||||
display: block;
|
||||
float: left;
|
||||
margin-right: $gl-padding-8;
|
||||
color: $white;
|
||||
font-size: $gl-font-size;
|
||||
line-height: $gl-line-height-24;
|
||||
|
||||
&.status-box-closed,
|
||||
&.status-box-mr-closed {
|
||||
background-color: $red-500;
|
||||
}
|
||||
|
||||
&.status-box-issue-closed,
|
||||
&.status-box-alert-resolved,
|
||||
&.status-box-mr-merged {
|
||||
background-color: $blue-500;
|
||||
}
|
||||
|
||||
&.status-box-open {
|
||||
background-color: $green-500;
|
||||
}
|
||||
|
||||
&.status-box-expired {
|
||||
background-color: $orange-500;
|
||||
}
|
||||
|
||||
&.status-box-upcoming {
|
||||
background: $gl-text-color-secondary;
|
||||
}
|
||||
}
|
|
@ -1,3 +1,14 @@
|
|||
.status-box {
|
||||
padding: 0 $gl-btn-padding;
|
||||
border-radius: $border-radius-default;
|
||||
display: block;
|
||||
float: left;
|
||||
margin-right: $gl-padding-8;
|
||||
color: $white;
|
||||
font-size: $gl-font-size;
|
||||
line-height: $gl-line-height-24;
|
||||
}
|
||||
|
||||
.issuable-warning-icon {
|
||||
background-color: $orange-50;
|
||||
border-radius: $border-radius-default;
|
||||
|
|
|
@ -50,6 +50,7 @@ class Projects::IssuesController < Projects::ApplicationController
|
|||
push_frontend_feature_flag(:paginated_issue_discussions, project)
|
||||
push_frontend_feature_flag(:realtime_labels, project)
|
||||
push_force_frontend_feature_flag(:work_items, project&.work_items_feature_flag_enabled?)
|
||||
push_frontend_feature_flag(:work_item_assignees)
|
||||
end
|
||||
|
||||
around_action :allow_gitaly_ref_name_caching, only: [:discussions]
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
class Projects::WorkItemsController < Projects::ApplicationController
|
||||
before_action do
|
||||
push_force_frontend_feature_flag(:work_items, project&.work_items_feature_flag_enabled?)
|
||||
push_frontend_feature_flag(:work_item_assignees)
|
||||
end
|
||||
|
||||
feature_category :team_planning
|
||||
|
|
|
@ -42,6 +42,7 @@ class ProjectsController < Projects::ApplicationController
|
|||
push_licensed_feature(:file_locks) if @project.present? && @project.licensed_feature_available?(:file_locks)
|
||||
push_licensed_feature(:security_orchestration_policies) if @project.present? && @project.licensed_feature_available?(:security_orchestration_policies)
|
||||
push_force_frontend_feature_flag(:work_items, @project&.work_items_feature_flag_enabled?)
|
||||
push_frontend_feature_flag(:work_item_assignees)
|
||||
end
|
||||
|
||||
layout :determine_layout
|
||||
|
|
|
@ -86,6 +86,12 @@ module Types
|
|||
field :version, GraphQL::Types::String, null: true,
|
||||
description: 'Version of the runner.'
|
||||
|
||||
markdown_field :maintenance_note_html, null: true
|
||||
|
||||
def maintenance_note_html_resolver
|
||||
::MarkupHelper.markdown(object.maintenance_note, context.to_h.dup)
|
||||
end
|
||||
|
||||
def job_count
|
||||
# We limit to 1 above the JOB_COUNT_LIMIT to indicate that more items exist after JOB_COUNT_LIMIT
|
||||
runner.builds.limit(JOB_COUNT_LIMIT + 1).count
|
||||
|
|
|
@ -31,15 +31,15 @@ module IssuesHelper
|
|||
updated_mr_header_enabled = Feature.enabled?(:updated_mr_header, @project)
|
||||
|
||||
if item.try(:expired?)
|
||||
'status-box-expired'
|
||||
'gl-bg-orange-500'
|
||||
elsif item.try(:merged?)
|
||||
updated_mr_header_enabled ? 'badge-info' : 'status-box-mr-merged'
|
||||
updated_mr_header_enabled ? 'badge-info' : 'gl-bg-blue-500'
|
||||
elsif item.closed?
|
||||
item.is_a?(MergeRequest) && updated_mr_header_enabled ? 'badge-danger' : 'status-box-mr-closed'
|
||||
item.is_a?(MergeRequest) && updated_mr_header_enabled ? 'badge-danger' : 'gl-bg-red-500'
|
||||
elsif item.try(:upcoming?)
|
||||
'status-box-upcoming'
|
||||
'gl-bg-gray-500'
|
||||
else
|
||||
item.is_a?(MergeRequest) && updated_mr_header_enabled ? 'badge-success' : 'status-box-open'
|
||||
item.is_a?(MergeRequest) && updated_mr_header_enabled ? 'badge-success' : 'gl-bg-green-500'
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -218,6 +218,8 @@ module IssuesHelper
|
|||
can_bulk_update: can?(current_user, :admin_issue, project).to_s,
|
||||
can_edit: can?(current_user, :admin_project, project).to_s,
|
||||
can_import_issues: can?(current_user, :import_issues, @project).to_s,
|
||||
can_read_crm_contact: can?(current_user, :read_crm_contact, project.group).to_s,
|
||||
can_read_crm_organization: can?(current_user, :read_crm_organization, project.group).to_s,
|
||||
email: current_user&.notification_email_or_default,
|
||||
emails_help_page_path: help_page_path('development/emails', anchor: 'email-namespace'),
|
||||
export_csv_path: export_csv_project_issues_path(project),
|
||||
|
@ -238,6 +240,8 @@ module IssuesHelper
|
|||
|
||||
def group_issues_list_data(group, current_user)
|
||||
common_issues_list_data(group, current_user).merge(
|
||||
can_read_crm_contact: can?(current_user, :read_crm_contact, group).to_s,
|
||||
can_read_crm_organization: can?(current_user, :read_crm_organization, group).to_s,
|
||||
has_any_issues: @has_issues.to_s,
|
||||
has_any_projects: @has_projects.to_s
|
||||
)
|
||||
|
|
|
@ -100,17 +100,22 @@ module TodosHelper
|
|||
def todo_target_state_pill(todo)
|
||||
return unless show_todo_state?(todo)
|
||||
|
||||
type =
|
||||
state = todo.target.state.to_s
|
||||
|
||||
case todo.target
|
||||
when MergeRequest
|
||||
'mr'
|
||||
if state == 'closed'
|
||||
background_class = 'gl-bg-red-500'
|
||||
elsif state == 'merged'
|
||||
background_class = 'gl-bg-blue-500'
|
||||
end
|
||||
when Issue
|
||||
'issue'
|
||||
background_class = 'gl-bg-blue-500' if state == 'closed'
|
||||
when AlertManagement::Alert
|
||||
'alert'
|
||||
background_class = 'gl-bg-blue-500' if state == 'resolved'
|
||||
end
|
||||
|
||||
tag.span class: "gl-my-0 gl-px-2 status-box status-box-#{type}-#{todo.target.state.to_s.dasherize}" do
|
||||
tag.span class: "gl-my-0 gl-px-2 status-box #{background_class}" do
|
||||
todo.target.state.to_s.capitalize
|
||||
end
|
||||
end
|
||||
|
|
|
@ -38,11 +38,12 @@ module Packages
|
|||
raise GoZipSizeError, "#{version.mod.name}@#{version.name}.#{type} exceeds size limit" if file.size > project.actual_limits.golang_max_file_size
|
||||
|
||||
digests = {
|
||||
md5: Digest::MD5.hexdigest(content),
|
||||
sha1: Digest::SHA1.hexdigest(content),
|
||||
sha256: Digest::SHA256.hexdigest(content)
|
||||
}
|
||||
|
||||
digests[:md5] = Digest::MD5.hexdigest(content) unless Gitlab::FIPS.enabled?
|
||||
|
||||
[file, digests]
|
||||
end
|
||||
|
||||
|
|
|
@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/350322
|
|||
milestone: '14.8'
|
||||
type: development
|
||||
group: group::pipeline execution
|
||||
default_enabled: false
|
||||
default_enabled: true
|
||||
|
|
|
@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/357869
|
|||
milestone: '14.10'
|
||||
type: development
|
||||
group: group::sharding
|
||||
default_enabled: false
|
||||
default_enabled: true
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
---
|
||||
name: ci_show_all_projects_with_usage_sorted_descending
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/83680
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/362331
|
||||
milestone: '15.0'
|
||||
name: work_item_assignees
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/88003
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/363030
|
||||
milestone: '15.1'
|
||||
type: development
|
||||
group: group::pipeline execution
|
||||
group: group::project management
|
||||
default_enabled: false
|
|
@ -99,8 +99,6 @@
|
|||
- 1
|
||||
- - cronjob
|
||||
- 1
|
||||
- - dast_site_validation
|
||||
- 1
|
||||
- - default
|
||||
- 1
|
||||
- - delete_diff_files
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class TempIndexForProjectNamespaceMemberBackfill < Gitlab::Database::Migration[1.0]
|
||||
class TempIndexForProjectNamespaceMemberBackfill < Gitlab::Database::Migration[2.0]
|
||||
INDEX_NAME = 'tmp_index_for_namespace_id_migration_on_project_members'
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ScheduleBackfillProjectMemberNamespaceId < Gitlab::Database::Migration[1.0]
|
||||
class ScheduleBackfillProjectMemberNamespaceId < Gitlab::Database::Migration[2.0]
|
||||
restrict_gitlab_migration gitlab_schema: :gitlab_main
|
||||
|
||||
MIGRATION = 'BackfillProjectMemberNamespaceId'
|
||||
INTERVAL = 2.minutes
|
||||
BATCH_SIZE = 1_000
|
||||
|
|
|
@ -101,7 +101,7 @@ explorer. GraphiQL explorer is available for:
|
|||
|
||||
1. Open the [GraphiQL explorer tool](https://gitlab.com/-/graphql-explorer).
|
||||
1. Paste the `query` listed above into the left window of your GraphiQL explorer tool.
|
||||
1. Click Play to get the result shown here:
|
||||
1. Select **Play** to get the result shown here:
|
||||
|
||||
![GraphiQL explorer search for boards](img/user_query_example_v13_2.png)
|
||||
|
||||
|
|
|
@ -81,7 +81,7 @@ explorer. GraphiQL explorer is available for:
|
|||
|
||||
1. Open the [GraphiQL explorer tool](https://gitlab.com/-/graphql-explorer).
|
||||
1. Paste the `query` listed above into the left window of your GraphiQL explorer tool.
|
||||
1. Click Play to get the result shown here:
|
||||
1. Select **Play** to get the result shown here:
|
||||
|
||||
![GraphiQL explore custom emoji query](img/custom_emoji_query_example.png)
|
||||
|
||||
|
|
|
@ -9609,6 +9609,7 @@ Represents the total number of issues and their weights for a particular day.
|
|||
| <a id="cirunnerjobcount"></a>`jobCount` | [`Int`](#int) | Number of jobs processed by the runner (limited to 1000, plus one to indicate that more items exist). |
|
||||
| <a id="cirunnerlocked"></a>`locked` | [`Boolean`](#boolean) | Indicates the runner is locked. |
|
||||
| <a id="cirunnermaintenancenote"></a>`maintenanceNote` | [`String`](#string) | Runner's maintenance notes. |
|
||||
| <a id="cirunnermaintenancenotehtml"></a>`maintenanceNoteHtml` | [`String`](#string) | The GitLab Flavored Markdown rendering of `maintenance_note`. |
|
||||
| <a id="cirunnermaximumtimeout"></a>`maximumTimeout` | [`Int`](#int) | Maximum timeout (in seconds) for jobs processed by the runner. |
|
||||
| <a id="cirunnerpaused"></a>`paused` | [`Boolean!`](#boolean) | Indicates the runner is paused and not available to run jobs. |
|
||||
| <a id="cirunnerplatformname"></a>`platformName` | [`String`](#string) | Platform provided by the runner. |
|
||||
|
|
|
@ -73,7 +73,7 @@ explorer. GraphiQL explorer is available for:
|
|||
|
||||
1. Open the [GraphiQL explorer tool](https://gitlab.com/-/graphql-explorer).
|
||||
1. Paste the `query` listed above into the left window of your GraphiQL explorer tool.
|
||||
1. Click Play to get the result shown here:
|
||||
1. Select **Play** to get the result shown here:
|
||||
|
||||
![GraphiQL explorer search for boards](img/users_query_example_v13_8.png)
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ file, as well as information and history about our changelog process.
|
|||
|
||||
## Overview
|
||||
|
||||
Each bullet point, or **entry**, in our
|
||||
Each list item, or **entry**, in our
|
||||
[`CHANGELOG.md`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/CHANGELOG.md)
|
||||
file is generated from the subject line of a Git commit. Commits are included
|
||||
when they contain the `Changelog` [Git trailer](https://git-scm.com/docs/git-interpret-trailers).
|
||||
|
|
|
@ -429,7 +429,7 @@ ALTER TABLE ONLY vulnerability_occurrence_pipelines
|
|||
In this example we expect to delete all associated `vulnerability_occurrence_pipelines` records
|
||||
whenever we delete the `ci_pipelines` record associated with them. In this case
|
||||
you might end up with some vulnerability page in GitLab which shows an occurrence
|
||||
of a vulnerability. However, when you try to click a link to the pipeline, you get
|
||||
of a vulnerability. However, when you try to select a link to the pipeline, you get
|
||||
a 404, because the pipeline is deleted. Then, when you navigate back you might find the
|
||||
occurrence has disappeared too.
|
||||
|
||||
|
|
|
@ -87,7 +87,7 @@ The easiest way to access tracing from a GDK environment is through the
|
|||
[performance-bar](../administration/monitoring/performance/performance_bar.md). This can be shown
|
||||
by typing `p` `b` in the browser window.
|
||||
|
||||
Once the performance bar is enabled, click on the **Trace** link in the performance bar to go to
|
||||
Once the performance bar is enabled, select **Trace** in the performance bar to go to
|
||||
the Jaeger UI.
|
||||
|
||||
The Jaeger search UI returns a query for the `Correlation-ID` of the current request. Normally,
|
||||
|
|
|
@ -114,12 +114,12 @@ pipeline in the main `gitlab` repository as well as in `gitlab-docs`. Create an
|
|||
a different name first and test it to ensure you do not break the pipelines.
|
||||
|
||||
1. In [`gitlab-docs`](https://gitlab.com/gitlab-org/gitlab-docs), go to **{rocket}** **CI/CD > Pipelines**.
|
||||
1. Click the **Run pipeline** button.
|
||||
1. Select **Run pipeline**.
|
||||
1. See that a new pipeline is running. The jobs that build the images are in the first
|
||||
stage, `build-images`. You can click the pipeline number to see the larger pipeline
|
||||
graph, or click the first (`build-images`) stage in the mini pipeline graph to
|
||||
stage, `build-images`. You can select the pipeline number to see the larger pipeline
|
||||
graph, or select the first (`build-images`) stage in the mini pipeline graph to
|
||||
expose the jobs that build the images.
|
||||
1. Click the **play** (**{play}**) button next to the images you want to rebuild.
|
||||
1. Select the **play** (**{play}**) button next to the images you want to rebuild.
|
||||
- Normally, you do not need to rebuild the `image:gitlab-docs-base` image, as it
|
||||
rarely changes. If it does need to be rebuilt, be sure to only run `image:docs-lint`
|
||||
after it is finished rebuilding.
|
||||
|
@ -133,7 +133,7 @@ and deploys it to <https://docs.gitlab.com>.
|
|||
To build and deploy the site immediately (must have the Maintainer role):
|
||||
|
||||
1. In [`gitlab-docs`](https://gitlab.com/gitlab-org/gitlab-docs), go to **{rocket}** **CI/CD > Schedules**.
|
||||
1. For the `Build docs.gitlab.com every 4 hours` scheduled pipeline, click the **play** (**{play}**) button.
|
||||
1. For the `Build docs.gitlab.com every 4 hours` scheduled pipeline, select the **play** (**{play}**) button.
|
||||
|
||||
Read more about [documentation deployments](deployment_process.md).
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
|
||||
## Testing experiments with RSpec
|
||||
|
||||
In the course of working with experiments, you'll probably want to utilize the RSpec
|
||||
In the course of working with experiments, you'll probably want to use the RSpec
|
||||
tooling that's built in. This happens automatically for files in `spec/experiments`, but
|
||||
for other files and specs you want to include it in, you can specify the `:experiment` type:
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ This document lists the different implementations of CSV export in GitLab codeba
|
|||
| As email attachment | - Asynchronously process the query with background job.<br>- Email uses the export as an attachment. | - Asynchronous processing. | - Requires users use a different app (email) to download the CSV.<br>- Email providers may limit attachment size. | - [Export issues](../user/project/issues/csv_export.md)<br>- [Export merge requests](../user/project/merge_requests/csv_export.md) |
|
||||
| As downloadable link in email (*) | - Asynchronously process the query with background job.<br>- Email uses an export link. | - Asynchronous processing.<br>- Bypasses email provider attachment size limit. | - Requires users use a different app (email).<br>- Requires additional storage and cleanup. | [Export User Permissions](https://gitlab.com/gitlab-org/gitlab/-/issues/1772) |
|
||||
| Polling (non-persistent state) | - Asynchronously processes the query with the background job.<br>- Frontend(FE) polls every few seconds to check if CSV file is ready. | - Asynchronous processing.<br>- Automatically downloads to local machine on completion.<br>- In-app solution. | - Non-persistable request - request expires when user navigates to a different page.<br>- API is processed for each polling request. | [Export Vulnerabilities](../user/application_security/vulnerability_report/#export-vulnerability-details) |
|
||||
| Polling (persistent state) (*) | - Asynchronously processes the query with background job.<br>- Backend (BE) maintains the export state<br>- FE polls every few seconds to check status.<br>- FE shows 'Download link' when export is ready.<br>- User can download or regenerate a new report. | - Asynchronous processing.<br>- No database calls made during the polling requests (HTTP 304 status is returned until export status changes).<br>- Does not require user to stay on page until export is complete.<br>- In-app solution.<br>- Can be expanded into a generic CSV feature (such as dashboard / CSV API). | - Requires to maintain export states in DB.<br>- Does not automatically download the CSV export to local machine, requires users to click 'Download' button. | [Export Merge Commits Report](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/43055) |
|
||||
| Polling (persistent state) (*) | - Asynchronously processes the query with background job.<br>- Backend (BE) maintains the export state<br>- FE polls every few seconds to check status.<br>- FE shows 'Download link' when export is ready.<br>- User can download or regenerate a new report. | - Asynchronous processing.<br>- No database calls made during the polling requests (HTTP 304 status is returned until export status changes).<br>- Does not require user to stay on page until export is complete.<br>- In-app solution.<br>- Can be expanded into a generic CSV feature (such as dashboard / CSV API). | - Requires to maintain export states in DB.<br>- Does not automatically download the CSV export to local machine, requires users to select 'Download'. | [Export Merge Commits Report](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/43055) |
|
||||
|
||||
NOTE:
|
||||
Export types marked as * are currently work in progress.
|
||||
|
|
|
@ -189,10 +189,10 @@ To see what polyfills are being used:
|
|||
1. Navigate to your merge request.
|
||||
1. In the secondary menu below the title of the merge request, click **Pipelines**, then
|
||||
click the pipeline you want to view, to display the jobs in that pipeline.
|
||||
1. Click the [`compile-production-assets`](https://gitlab.com/gitlab-org/gitlab/-/jobs/641770154) job.
|
||||
1. In the right-hand sidebar, scroll to **Job Artifacts**, and click **Browse**.
|
||||
1. Click the **webpack-report** folder to open it, and click **index.html**.
|
||||
1. In the upper left corner of the page, click the right arrow **{angle-right}**
|
||||
1. Select the [`compile-production-assets`](https://gitlab.com/gitlab-org/gitlab/-/jobs/641770154) job.
|
||||
1. In the right-hand sidebar, scroll to **Job Artifacts**, and select **Browse**.
|
||||
1. Select the **webpack-report** folder to open it, and select **index.html**.
|
||||
1. In the upper left corner of the page, select the right arrow **{angle-right}**
|
||||
to display the explorer.
|
||||
1. In the **Search modules** field, enter `gitlab/node_modules/core-js` to see
|
||||
which polyfills are being loaded and where:
|
||||
|
|
|
@ -64,7 +64,7 @@ main pieces of the desired UI and UX of a registry page. The most important comp
|
|||
|
||||
- `code-instruction`: represents a copyable box containing code. Supports multiline and single line
|
||||
code boxes. Snowplow tracks the code copy event.
|
||||
- `details-row`: represents a row of details. Used to add additional info in the details area of
|
||||
- `details-row`: represents a row of details. Used to add additional information in the details area of
|
||||
the `list-item` component.
|
||||
- `history-item`: represents a history list item used to build a timeline.
|
||||
- `list-item`: represents a list element in the registry. It supports: left action, left primary and
|
||||
|
|
|
@ -83,8 +83,8 @@ As we'll be using [Amazon S3 object storage](#amazon-s3-object-storage), our EC2
|
|||
|
||||
### Create an IAM Policy
|
||||
|
||||
1. Navigate to the IAM dashboard and click on **Policies** in the left menu.
|
||||
1. Click **Create policy**, select the `JSON` tab, and add a policy. We want to [follow security best practices and grant _least privilege_](https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#grant-least-privilege), giving our role only the permissions needed to perform the required actions.
|
||||
1. Navigate to the IAM dashboard and select **Policies** in the left menu.
|
||||
1. Select **Create policy**, select the `JSON` tab, and add a policy. We want to [follow security best practices and grant _least privilege_](https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#grant-least-privilege), giving our role only the permissions needed to perform the required actions.
|
||||
1. Assuming you prefix the S3 bucket names with `gl-` as shown in the diagram, add the following policy:
|
||||
|
||||
```json
|
||||
|
@ -114,17 +114,17 @@ As we'll be using [Amazon S3 object storage](#amazon-s3-object-storage), our EC2
|
|||
}
|
||||
```
|
||||
|
||||
1. Click **Review policy**, give your policy a name (we'll use `gl-s3-policy`), and click **Create policy**.
|
||||
1. Select **Review policy**, give your policy a name (we'll use `gl-s3-policy`), and select **Create policy**.
|
||||
|
||||
### Create an IAM Role
|
||||
|
||||
1. Still on the IAM dashboard, click on **Roles** in the left menu, and
|
||||
click **Create role**.
|
||||
1. Create a new role by selecting **AWS service > EC2**, then click
|
||||
1. Still on the IAM dashboard, select **Roles** in the left menu, and
|
||||
select **Create role**.
|
||||
1. Create a new role by selecting **AWS service > EC2**, then select
|
||||
**Next: Permissions**.
|
||||
1. In the policy filter, search for the `gl-s3-policy` we created above, select it, and click **Tags**.
|
||||
1. Add tags if needed and click **Review**.
|
||||
1. Give the role a name (we'll use `GitLabS3Access`) and click **Create Role**.
|
||||
1. In the policy filter, search for the `gl-s3-policy` we created above, select it, and select **Tags**.
|
||||
1. Add tags if needed and select **Review**.
|
||||
1. Give the role a name (we'll use `GitLabS3Access`) and select **Create Role**.
|
||||
|
||||
We'll use this role when we [create a launch configuration](#create-a-launch-configuration) later on.
|
||||
|
||||
|
@ -140,14 +140,14 @@ Internet Gateway.
|
|||
We'll now create a VPC, a virtual networking environment that you'll control:
|
||||
|
||||
1. Sign in to [Amazon Web Services](https://console.aws.amazon.com/vpc/home).
|
||||
1. Select **Your VPCs** from the left menu and then click **Create VPC**.
|
||||
1. Select **Your VPCs** from the left menu and then select **Create VPC**.
|
||||
At the "Name tag" enter `gitlab-vpc` and at the "IPv4 CIDR block" enter
|
||||
`10.0.0.0/16`. If you don't require dedicated hardware, you can leave
|
||||
"Tenancy" as default. Click **Yes, Create** when ready.
|
||||
"Tenancy" as default. Select **Yes, Create** when ready.
|
||||
|
||||
![Create VPC](img/create_vpc.png)
|
||||
|
||||
1. Select the VPC, click **Actions**, click **Edit DNS resolution**, and enable DNS resolution. Hit **Save** when done.
|
||||
1. Select the VPC, select **Actions**, select **Edit DNS resolution**, and enable DNS resolution. Hit **Save** when done.
|
||||
|
||||
### Subnets
|
||||
|
||||
|
@ -160,7 +160,7 @@ We will create private and public subnets to match load balancers and
|
|||
RDS instances as well:
|
||||
|
||||
1. Select **Subnets** from the left menu.
|
||||
1. Click **Create subnet**. Give it a descriptive name tag based on the IP,
|
||||
1. Select **Create subnet**. Give it a descriptive name tag based on the IP,
|
||||
for example `gitlab-public-10.0.0.0`, select the VPC we created previously, select an availability zone (we'll use `us-west-2a`),
|
||||
and at the IPv4 CIDR block let's give it a 24 subnet `10.0.0.0/24`:
|
||||
|
||||
|
@ -176,7 +176,7 @@ RDS instances as well:
|
|||
| `gitlab-private-10.0.3.0` | private | `us-west-2b` | `10.0.3.0/24` |
|
||||
|
||||
1. Once all the subnets are created, enable **Auto-assign IPv4** for the two public subnets:
|
||||
1. Select each public subnet in turn, click **Actions**, and click **Modify auto-assign IP settings**. Enable the option and save.
|
||||
1. Select each public subnet in turn, select **Actions**, and select **Modify auto-assign IP settings**. Enable the option and save.
|
||||
|
||||
### Internet Gateway
|
||||
|
||||
|
@ -184,8 +184,8 @@ Now, still on the same dashboard, go to Internet Gateways and
|
|||
create a new one:
|
||||
|
||||
1. Select **Internet Gateways** from the left menu.
|
||||
1. Click **Create internet gateway**, give it the name `gitlab-gateway` and
|
||||
click **Create**.
|
||||
1. Select **Create internet gateway**, give it the name `gitlab-gateway` and
|
||||
select **Create**.
|
||||
1. Select it from the table, and then under the **Actions** dropdown choose
|
||||
"Attach to VPC".
|
||||
|
||||
|
@ -197,12 +197,12 @@ create a new one:
|
|||
|
||||
Instances deployed in our private subnets need to connect to the internet for updates, but should not be reachable from the public internet. To achieve this, we'll make use of [NAT Gateways](https://docs.aws.amazon.com/vpc/latest/userguide/vpc-nat-gateway.html) deployed in each of our public subnets:
|
||||
|
||||
1. Navigate to the VPC dashboard and click on **NAT Gateways** in the left menu bar.
|
||||
1. Click **Create NAT Gateway** and complete the following:
|
||||
1. Navigate to the VPC dashboard and select **NAT Gateways** in the left menu bar.
|
||||
1. Select **Create NAT Gateway** and complete the following:
|
||||
1. **Subnet**: Select `gitlab-public-10.0.0.0` from the dropdown.
|
||||
1. **Elastic IP Allocation ID**: Enter an existing Elastic IP or click **Allocate Elastic IP address** to allocate a new IP to your NAT gateway.
|
||||
1. **Elastic IP Allocation ID**: Enter an existing Elastic IP or select **Allocate Elastic IP address** to allocate a new IP to your NAT gateway.
|
||||
1. Add tags if needed.
|
||||
1. Click **Create NAT Gateway**.
|
||||
1. Select **Create NAT Gateway**.
|
||||
|
||||
Create a second NAT gateway but this time place it in the second public subnet, `gitlab-public-10.0.2.0`.
|
||||
|
||||
|
@ -215,23 +215,23 @@ We need to create a route table for our public subnets to reach the internet via
|
|||
On the VPC dashboard:
|
||||
|
||||
1. Select **Route Tables** from the left menu.
|
||||
1. Click **Create Route Table**.
|
||||
1. Select **Create Route Table**.
|
||||
1. At the "Name tag" enter `gitlab-public` and choose `gitlab-vpc` under "VPC".
|
||||
1. Click **Create**.
|
||||
1. Select **Create**.
|
||||
|
||||
We now need to add our internet gateway as a new target and have
|
||||
it receive traffic from any destination.
|
||||
|
||||
1. Select **Route Tables** from the left menu and select the `gitlab-public`
|
||||
route to show the options at the bottom.
|
||||
1. Select the **Routes** tab, click **Edit routes > Add route** and set `0.0.0.0/0`
|
||||
1. Select the **Routes** tab, select **Edit routes > Add route** and set `0.0.0.0/0`
|
||||
as the destination. In the target column, select the `gitlab-gateway` we created previously.
|
||||
Hit **Save routes** once done.
|
||||
|
||||
Next, we must associate the **public** subnets to the route table:
|
||||
|
||||
1. Select the **Subnet Associations** tab and click **Edit subnet associations**.
|
||||
1. Check only the public subnets and click **Save**.
|
||||
1. Select the **Subnet Associations** tab and select **Edit subnet associations**.
|
||||
1. Check only the public subnets and select **Save**.
|
||||
|
||||
#### Private Route Tables
|
||||
|
||||
|
@ -251,7 +251,7 @@ We'll create a load balancer to evenly distribute inbound traffic on ports `80`
|
|||
|
||||
On the EC2 dashboard, look for Load Balancer in the left navigation bar:
|
||||
|
||||
1. Click the **Create Load Balancer** button.
|
||||
1. Select **Create Load Balancer**.
|
||||
1. Choose the **Classic Load Balancer**.
|
||||
1. Give it a name (we'll use `gitlab-loadbalancer`) and for the **Create LB Inside** option, select `gitlab-vpc` from the dropdown menu.
|
||||
1. In the **Listeners** section, set the following listeners:
|
||||
|
@ -259,10 +259,10 @@ On the EC2 dashboard, look for Load Balancer in the left navigation bar:
|
|||
- TCP port 22 for both load balancer and instance protocols and ports
|
||||
- HTTPS port 443 for load balancer protocol and ports, forwarding to HTTP port 80 on the instance (we will configure GitLab to listen on port 80 [later in the guide](#add-support-for-proxied-ssl))
|
||||
1. In the **Select Subnets** section, select both public subnets from the list so that the load balancer can route traffic to both availability zones.
|
||||
1. We'll add a security group for our load balancer to act as a firewall to control what traffic is allowed through. Click **Assign Security Groups** and select **Create a new security group**, give it a name
|
||||
1. We'll add a security group for our load balancer to act as a firewall to control what traffic is allowed through. Select **Assign Security Groups** and select **Create a new security group**, give it a name
|
||||
(we'll use `gitlab-loadbalancer-sec-group`) and description, and allow both HTTP and HTTPS traffic
|
||||
from anywhere (`0.0.0.0/0, ::/0`). Also allow SSH traffic, select a custom source, and add a single trusted IP address or an IP address range in CIDR notation. This will allow users to perform Git actions over SSH.
|
||||
1. Click **Configure Security Settings** and set the following:
|
||||
1. Select **Configure Security Settings** and set the following:
|
||||
1. Select an SSL/TLS certificate from ACM or upload a certificate to IAM.
|
||||
1. Under **Select a Cipher**, pick a predefined security policy from the dropdown. You can see a breakdown of [Predefined SSL Security Policies for Classic Load Balancers](https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-security-policy-table.html) in the AWS docs. Check the GitLab codebase for a list of [supported SSL ciphers and protocols](https://gitlab.com/gitlab-org/gitlab/-/blob/9ee7ad433269b37251e0dd5b5e00a0f00d8126b4/lib/support/nginx/gitlab-ssl#L97-99).
|
||||
1. Click **Configure Health Check** and set up a health check for your EC2 instances.
|
||||
|
@ -270,9 +270,9 @@ On the EC2 dashboard, look for Load Balancer in the left navigation bar:
|
|||
1. For **Ping Port**, enter 80.
|
||||
1. For **Ping Path** - we recommend that you [use the Readiness check endpoint](../../administration/load_balancer.md#readiness-check). You'll need to add [the VPC IP Address Range (CIDR)](https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-security-groups.html#elb-vpc-nacl) to the [IP Allowlist](../../administration/monitoring/ip_whitelist.md) for the [Health Check endpoints](../../user/admin_area/monitoring/health_check.md)
|
||||
1. Keep the default **Advanced Details** or adjust them according to your needs.
|
||||
1. Click **Add EC2 Instances** - don't add anything as we will create an Auto Scaling Group later to manage instances for us.
|
||||
1. Click **Add Tags** and add any tags you need.
|
||||
1. Click **Review and Create**, review all your settings, and click **Create** if you're happy.
|
||||
1. Select **Add EC2 Instances** - don't add anything as we will create an Auto Scaling Group later to manage instances for us.
|
||||
1. Select **Add Tags** and add any tags you need.
|
||||
1. Select **Review and Create**, review all your settings, and select **Create** if you're happy.
|
||||
|
||||
After the Load Balancer is up and running, you can revisit your Security
|
||||
Groups to refine the access only through the ELB and any other requirements
|
||||
|
@ -280,19 +280,19 @@ you might have.
|
|||
|
||||
### Configure DNS for Load Balancer
|
||||
|
||||
On the Route 53 dashboard, click **Hosted zones** in the left navigation bar:
|
||||
On the Route 53 dashboard, select **Hosted zones** in the left navigation bar:
|
||||
|
||||
1. Select an existing hosted zone or, if you do not already have one for your domain, click **Create Hosted Zone**, enter your domain name, and click **Create**.
|
||||
1. Click **Create Record Set** and provide the following values:
|
||||
1. Select **Create Record Set** and provide the following values:
|
||||
1. **Name:** Use the domain name (the default value) or enter a subdomain.
|
||||
1. **Type:** Select **A - IPv4 address**.
|
||||
1. **Alias:** Defaults to **No**. Select **Yes**.
|
||||
1. **Alias Target:** Find the **ELB Classic Load Balancers** section and select the classic load balancer we created earlier.
|
||||
1. **Routing Policy:** We'll use **Simple** but you can choose a different policy based on your use case.
|
||||
1. **Evaluate Target Health:** We'll set this to **No** but you can choose to have the load balancer route traffic based on target health.
|
||||
1. Click **Create**.
|
||||
1. Select **Create**.
|
||||
1. If you registered your domain through Route 53, you're done. If you used a different domain registrar, you need to update your DNS records with your domain registrar. You'll need to:
|
||||
1. Click on **Hosted zones** and select the domain you added above.
|
||||
1. Select **Hosted zones** and select the domain you added above.
|
||||
1. You'll see a list of `NS` records. From your domain registrar's administrator panel, add each of these as `NS` records to your domain's DNS records. These steps may vary between domain registrars. If you're stuck, Google **"name of your registrar" add DNS records** and you should find a help article specific to your domain registrar.
|
||||
|
||||
The steps for doing this vary depending on which registrar you use and is beyond the scope of this guide.
|
||||
|
@ -308,22 +308,22 @@ create the actual RDS instance.
|
|||
We need a security group for our database that will allow inbound traffic from the instances we'll deploy in our `gitlab-loadbalancer-sec-group` later on:
|
||||
|
||||
1. From the EC2 dashboard, select **Security Groups** from the left menu bar.
|
||||
1. Click **Create security group**.
|
||||
1. Select **Create security group**.
|
||||
1. Give it a name (we'll use `gitlab-rds-sec-group`), a description, and select the `gitlab-vpc` from the **VPC** dropdown.
|
||||
1. In the **Inbound rules** section, click **Add rule** and set the following:
|
||||
1. In the **Inbound rules** section, select **Add rule** and set the following:
|
||||
1. **Type:** search for and select the **PostgreSQL** rule.
|
||||
1. **Source type:** set as "Custom".
|
||||
1. **Source:** select the `gitlab-loadbalancer-sec-group` we created earlier.
|
||||
1. When done, click **Create security group**.
|
||||
1. When done, select **Create security group**.
|
||||
|
||||
### RDS Subnet Group
|
||||
|
||||
1. Navigate to the RDS dashboard and select **Subnet Groups** from the left menu.
|
||||
1. Click on **Create DB Subnet Group**.
|
||||
1. Select **Create DB Subnet Group**.
|
||||
1. Under **Subnet group details**, enter a name (we'll use `gitlab-rds-group`), a description, and choose the `gitlab-vpc` from the VPC dropdown.
|
||||
1. From the **Availability Zones** dropdown, select the Availability Zones that include the subnets you've configured. In our case, we'll add `eu-west-2a` and `eu-west-2b`.
|
||||
1. From the **Subnets** dropdown, select the two private subnets (`10.0.1.0/24` and `10.0.3.0/24`) as we defined them in the [subnets section](#subnets).
|
||||
1. Click **Create** when ready.
|
||||
1. Select **Create** when ready.
|
||||
|
||||
### Create the database
|
||||
|
||||
|
@ -332,7 +332,7 @@ Avoid using burstable instances (t class instances) for the database as this cou
|
|||
|
||||
Now, it's time to create the database:
|
||||
|
||||
1. Navigate to the RDS dashboard, select **Databases** from the left menu, and click **Create database**.
|
||||
1. Navigate to the RDS dashboard, select **Databases** from the left menu, and select **Create database**.
|
||||
1. Select **Standard Create** for the database creation method.
|
||||
1. Select **PostgreSQL** as the database engine and select the minimum PostgreSQL version as defined for your GitLab version in our [database requirements](../../install/requirements.md#postgresql-requirements).
|
||||
1. Since this is a production server, let's choose **Production** from the **Templates** section.
|
||||
|
@ -355,7 +355,7 @@ Now, it's time to create the database:
|
|||
1. Configure your preferred backup settings.
|
||||
1. The only other change we'll make here is to disable auto minor version updates under **Maintenance**.
|
||||
1. Leave all the other settings as is or tweak according to your needs.
|
||||
1. Once you're happy, click **Create database**.
|
||||
1. Once you're happy, select **Create database**.
|
||||
|
||||
Now that the database is created, let's move on to setting up Redis with ElastiCache.
|
||||
|
||||
|
@ -368,24 +368,24 @@ persistence and is used to store session data, temporary cache information, and
|
|||
|
||||
1. Navigate to the EC2 dashboard.
|
||||
1. Select **Security Groups** from the left menu.
|
||||
1. Click **Create security group** and fill in the details. Give it a name (we'll use `gitlab-redis-sec-group`),
|
||||
1. Select **Create security group** and fill in the details. Give it a name (we'll use `gitlab-redis-sec-group`),
|
||||
add a description, and choose the VPC we created previously
|
||||
1. In the **Inbound rules** section, click **Add rule** and add a **Custom TCP** rule, set port `6379`, and set the "Custom" source as the `gitlab-loadbalancer-sec-group` we created earlier.
|
||||
1. When done, click **Create security group**.
|
||||
1. In the **Inbound rules** section, select **Add rule** and add a **Custom TCP** rule, set port `6379`, and set the "Custom" source as the `gitlab-loadbalancer-sec-group` we created earlier.
|
||||
1. When done, select **Create security group**.
|
||||
|
||||
### Redis Subnet Group
|
||||
|
||||
1. Navigate to the ElastiCache dashboard from your AWS console.
|
||||
1. Go to **Subnet Groups** in the left menu, and create a new subnet group (we'll name ours `gitlab-redis-group`).
|
||||
Make sure to select our VPC and its [private subnets](#subnets). Click
|
||||
**Create** when ready.
|
||||
Make sure to select our VPC and its [private subnets](#subnets).
|
||||
1. Select **Create** when ready.
|
||||
|
||||
![ElastiCache subnet](img/ec_subnet.png)
|
||||
|
||||
### Create the Redis Cluster
|
||||
|
||||
1. Navigate back to the ElastiCache dashboard.
|
||||
1. Select **Redis** on the left menu and click **Create** to create a new
|
||||
1. Select **Redis** on the left menu and select **Create** to create a new
|
||||
Redis cluster. Do not enable **Cluster Mode** as it is [not supported](../../administration/redis/replication_and_failover_external.md#requirements). Even without cluster mode on, you still get the
|
||||
chance to deploy Redis in multiple availability zones.
|
||||
1. In the settings section:
|
||||
|
@ -404,7 +404,7 @@ persistence and is used to store session data, temporary cache information, and
|
|||
1. In the security settings, edit the security groups and choose the
|
||||
`gitlab-redis-sec-group` we had previously created.
|
||||
1. Leave the rest of the settings to their default values or edit to your liking.
|
||||
1. When done, click **Create**.
|
||||
1. When done, select **Create**.
|
||||
|
||||
## Setting up Bastion Hosts
|
||||
|
||||
|
@ -418,29 +418,29 @@ If you do not want to maintain bastion hosts, you can set up [AWS Systems Manage
|
|||
|
||||
### Create Bastion Host A
|
||||
|
||||
1. Navigate to the EC2 Dashboard and click on **Launch instance**.
|
||||
1. Navigate to the EC2 Dashboard and select **Launch instance**.
|
||||
1. Select the **Ubuntu Server 18.04 LTS (HVM)** AMI.
|
||||
1. Choose an instance type. We'll use a `t2.micro` as we'll only use the bastion host to SSH into our other instances.
|
||||
1. Click **Configure Instance Details**.
|
||||
1. Select **Configure Instance Details**.
|
||||
1. Under **Network**, select the `gitlab-vpc` from the dropdown menu.
|
||||
1. Under **Subnet**, select the public subnet we created earlier (`gitlab-public-10.0.0.0`).
|
||||
1. Double check that under **Auto-assign Public IP** you have **Use subnet setting (Enable)** selected.
|
||||
1. Leave everything else as default and click **Add Storage**.
|
||||
1. Leave everything else as default and select **Add Storage**.
|
||||
1. For storage, we'll leave everything as default and only add an 8GB root volume. We won't store anything on this instance.
|
||||
1. Click **Add Tags** and on the next screen click **Add Tag**.
|
||||
1. Select **Add Tags** and on the next screen select **Add Tag**.
|
||||
1. We'll only set `Key: Name` and `Value: Bastion Host A`.
|
||||
1. Click **Configure Security Group**.
|
||||
1. Select **Configure Security Group**.
|
||||
1. Select **Create a new security group**, enter a **Security group name** (we'll use `bastion-sec-group`), and add a description.
|
||||
1. We'll enable SSH access from anywhere (`0.0.0.0/0`). If you want stricter security, specify a single IP address or an IP address range in CIDR notation.
|
||||
1. Click **Review and Launch**
|
||||
1. Review all your settings and, if you're happy, click **Launch**.
|
||||
1. Acknowledge that you have access to an existing key pair or create a new one. Click **Launch Instance**.
|
||||
1. Select **Review and Launch**
|
||||
1. Review all your settings and, if you're happy, select **Launch**.
|
||||
1. Acknowledge that you have access to an existing key pair or create a new one. Select **Launch Instance**.
|
||||
|
||||
Confirm that you can SSH into the instance:
|
||||
|
||||
1. On the EC2 Dashboard, click on **Instances** in the left menu.
|
||||
1. On the EC2 Dashboard, select **Instances** in the left menu.
|
||||
1. Select **Bastion Host A** from your list of instances.
|
||||
1. Click **Connect** and follow the connection instructions.
|
||||
1. Select **Connect** and follow the connection instructions.
|
||||
1. If you are able to connect successfully, let's move on to setting up our second bastion host for redundancy.
|
||||
|
||||
### Create Bastion Host B
|
||||
|
@ -466,16 +466,16 @@ From the EC2 dashboard:
|
|||
|
||||
1. Use the section below titled "[Find official GitLab-created AMI IDs on AWS](#find-official-gitlab-created-ami-ids-on-aws)" to find the correct AMI to launch.
|
||||
1. After clicking **Launch** on the desired AMI, select an instance type based on your workload. Consult the [hardware requirements](../../install/requirements.md#hardware-requirements) to choose one that fits your needs (at least `c5.xlarge`, which is sufficient to accommodate 100 users).
|
||||
1. Click **Configure Instance Details**:
|
||||
1. Select **Configure Instance Details**:
|
||||
1. In the **Network** dropdown, select `gitlab-vpc`, the VPC we created earlier.
|
||||
1. In the **Subnet** dropdown, select `gitlab-private-10.0.1.0` from the list of subnets we created earlier.
|
||||
1. Double check that **Auto-assign Public IP** is set to `Use subnet setting (Disable)`.
|
||||
1. Click **Add Storage**.
|
||||
1. Select **Add Storage**.
|
||||
1. The root volume is 8GiB by default and should be enough given that we won't store any data there.
|
||||
1. Click **Add Tags** and add any tags you may need. In our case, we'll only set `Key: Name` and `Value: GitLab`.
|
||||
1. Click **Configure Security Group**. Check **Select an existing security group** and select the `gitlab-loadbalancer-sec-group` we created earlier.
|
||||
1. Click **Review and launch** followed by **Launch** if you're happy with your settings.
|
||||
1. Finally, acknowledge that you have access to the selected private key file or create a new one. Click **Launch Instances**.
|
||||
1. Select **Add Tags** and add any tags you may need. In our case, we'll only set `Key: Name` and `Value: GitLab`.
|
||||
1. Select **Configure Security Group**. Check **Select an existing security group** and select the `gitlab-loadbalancer-sec-group` we created earlier.
|
||||
1. Select **Review and launch** followed by **Launch** if you're happy with your settings.
|
||||
1. Finally, acknowledge that you have access to the selected private key file or create a new one. Select **Launch Instances**.
|
||||
|
||||
### Add custom configuration
|
||||
|
||||
|
@ -501,7 +501,7 @@ Since we're adding our SSL certificate at the load balancer, we do not need the
|
|||
|
||||
From your GitLab instance, connect to the RDS instance to verify access and to install the required `pg_trgm` and `btree_gist` extensions.
|
||||
|
||||
To find the host or endpoint, navigate to **Amazon RDS > Databases** and click on the database you created earlier. Look for the endpoint under the **Connectivity & security** tab.
|
||||
To find the host or endpoint, navigate to **Amazon RDS > Databases** and select the database you created earlier. Look for the endpoint under the **Connectivity & security** tab.
|
||||
|
||||
Do not to include the colon and port number:
|
||||
|
||||
|
@ -580,23 +580,23 @@ It should be enabled and configured on a separate EC2 instance in one of the
|
|||
|
||||
Let's create an EC2 instance where we'll install Gitaly:
|
||||
|
||||
1. From the EC2 dashboard, click **Launch instance**.
|
||||
1. From the EC2 dashboard, select **Launch instance**.
|
||||
1. Choose an AMI. In this example, we'll select the **Ubuntu Server 18.04 LTS (HVM), SSD Volume Type**.
|
||||
1. Choose an instance type. We'll pick a `c5.xlarge`.
|
||||
1. Click **Configure Instance Details**.
|
||||
1. Select **Configure Instance Details**.
|
||||
1. In the **Network** dropdown, select `gitlab-vpc`, the VPC we created earlier.
|
||||
1. In the **Subnet** dropdown, select `gitlab-private-10.0.1.0` from the list of subnets we created earlier.
|
||||
1. Double check that **Auto-assign Public IP** is set to `Use subnet setting (Disable)`.
|
||||
1. Click **Add Storage**.
|
||||
1. Select **Add Storage**.
|
||||
1. Increase the Root volume size to `20 GiB` and change the **Volume Type** to `Provisioned IOPS SSD (io1)`. (This is an arbitrary size. Create a volume big enough for your repository storage requirements.)
|
||||
1. For **IOPS** set `1000` (20 GiB x 50 IOPS). You can provision up to 50 IOPS per GiB. If you select a larger volume, increase the IOPS accordingly. Workloads where many small files are written in a serialized manner, like `git`, requires performant storage, hence the choice of `Provisioned IOPS SSD (io1)`.
|
||||
1. Click on **Add Tags** and add your tags. In our case, we'll only set `Key: Name` and `Value: Gitaly`.
|
||||
1. Click on **Configure Security Group** and let's **Create a new security group**.
|
||||
1. Select **Add Tags** and add your tags. In our case, we'll only set `Key: Name` and `Value: Gitaly`.
|
||||
1. Select **Configure Security Group** and let's **Create a new security group**.
|
||||
1. Give your security group a name and description. We'll use `gitlab-gitaly-sec-group` for both.
|
||||
1. Create a **Custom TCP** rule and add port `8075` to the **Port Range**. For the **Source**, select the `gitlab-loadbalancer-sec-group`.
|
||||
1. Also add an inbound rule for SSH from the `bastion-sec-group` so that we can connect using [SSH Agent Forwarding](#use-ssh-agent-forwarding) from the Bastion hosts.
|
||||
1. Click **Review and launch** followed by **Launch** if you're happy with your settings.
|
||||
1. Finally, acknowledge that you have access to the selected private key file or create a new one. Click **Launch Instances**.
|
||||
1. Select **Review and launch** followed by **Launch** if you're happy with your settings.
|
||||
1. Finally, acknowledge that you have access to the selected private key file or create a new one. Select **Launch Instances**.
|
||||
|
||||
NOTE:
|
||||
Instead of storing configuration _and_ repository data on the root volume, you can also choose to add an additional EBS volume for repository storage. Follow the same guidance as above. See the [Amazon EBS pricing](https://aws.amazon.com/ebs/pricing/). We do not recommend using EFS as it may negatively impact the performance of GitLab. You can review the [relevant documentation](../../administration/nfs.md#avoid-using-cloud-based-file-systems) for more details.
|
||||
|
@ -679,9 +679,9 @@ When our [auto scaling group](#create-an-auto-scaling-group) spins up new instan
|
|||
On the EC2 dashboard:
|
||||
|
||||
1. Select the `GitLab` instance we [created earlier](#install-gitlab).
|
||||
1. Click on **Actions**, scroll down to **Image** and click **Create Image**.
|
||||
1. Select **Actions**, scroll down to **Image** and select **Create Image**.
|
||||
1. Give your image a name and description (we'll use `GitLab-Source` for both).
|
||||
1. Leave everything else as default and click **Create Image**
|
||||
1. Leave everything else as default and select **Create Image**
|
||||
|
||||
Now we have a custom AMI that we'll use to create our launch configuration the next step.
|
||||
|
||||
|
@ -691,17 +691,17 @@ Now we have a custom AMI that we'll use to create our launch configuration the n
|
|||
|
||||
From the EC2 dashboard:
|
||||
|
||||
1. Select **Launch Configurations** from the left menu and click **Create launch configuration**.
|
||||
1. Select **Launch Configurations** from the left menu and select **Create launch configuration**.
|
||||
1. Select **My AMIs** from the left menu and select the `GitLab` custom AMI we created above.
|
||||
1. Select an instance type best suited for your needs (at least a `c5.xlarge`) and click **Configure details**.
|
||||
1. Select an instance type best suited for your needs (at least a `c5.xlarge`) and select **Configure details**.
|
||||
1. Enter a name for your launch configuration (we'll use `gitlab-ha-launch-config`).
|
||||
1. **Do not** check **Request Spot Instance**.
|
||||
1. From the **IAM Role** dropdown, pick the `GitLabAdmin` instance role we [created earlier](#create-an-iam-ec2-instance-role-and-profile).
|
||||
1. Leave the rest as defaults and click **Add Storage**.
|
||||
1. The root volume is 8GiB by default and should be enough given that we won't store any data there. Click **Configure Security Group**.
|
||||
1. Leave the rest as defaults and select **Add Storage**.
|
||||
1. The root volume is 8GiB by default and should be enough given that we won't store any data there. Select **Configure Security Group**.
|
||||
1. Check **Select and existing security group** and select the `gitlab-loadbalancer-sec-group` we created earlier.
|
||||
1. Click **Review**, review your changes, and click **Create launch configuration**.
|
||||
1. Acknowledge that you have access to the private key or create a new one. Click **Create launch configuration**.
|
||||
1. Select **Review**, review your changes, and select **Create launch configuration**.
|
||||
1. Acknowledge that you have access to the private key or create a new one. Select **Create launch configuration**.
|
||||
|
||||
### Create an auto scaling group
|
||||
|
||||
|
@ -713,7 +713,7 @@ From the EC2 dashboard:
|
|||
1. Expand the **Advanced Details** section and check the **Receive traffic from one or more load balancers** option.
|
||||
1. From the **Classic Load Balancers** dropdown, select the load balancer we created earlier.
|
||||
1. For **Health Check Type**, select **ELB**.
|
||||
1. We'll leave our **Health Check Grace Period** as the default `300` seconds. Click **Configure scaling policies**.
|
||||
1. We'll leave our **Health Check Grace Period** as the default `300` seconds. Select **Configure scaling policies**.
|
||||
1. Check **Use scaling policies to adjust the capacity of this group**.
|
||||
1. For this group we'll scale between 2 and 4 instances where one instance will be added if CPU
|
||||
utilization is greater than 60% and one instance is removed if it falls
|
||||
|
|
|
@ -114,7 +114,7 @@ to the end of the Bitbucket authorization callback URL.
|
|||
if you installed from source.
|
||||
|
||||
On the sign-in page there should now be a Bitbucket icon below the regular
|
||||
sign-in form. Click the icon to begin the authentication process. Bitbucket asks
|
||||
sign-in form. Select the icon to begin the authentication process. Bitbucket asks
|
||||
the user to sign in and authorize the GitLab application. If successful, the user
|
||||
is returned to GitLab and signed in.
|
||||
|
||||
|
|
|
@ -108,6 +108,6 @@ Facebook. Facebook generates an app ID and secret key for you to use.
|
|||
installed GitLab via Omnibus or from source respectively.
|
||||
|
||||
On the sign in page there should now be a Facebook icon below the regular sign
|
||||
in form. Click the icon to begin the authentication process. Facebook asks the
|
||||
in form. Select the icon to begin the authentication process. Facebook asks the
|
||||
user to sign in and authorize the GitLab application. If everything goes well
|
||||
the user is returned to GitLab and signed in.
|
||||
|
|
|
@ -50,7 +50,7 @@ In Google's side:
|
|||
To do so you should:
|
||||
|
||||
1. Go to the [Google API Console](https://console.developers.google.com/apis/dashboard).
|
||||
1. Click on **ENABLE APIS AND SERVICES** button at the top of the page.
|
||||
1. Select **ENABLE APIS AND SERVICES** at the top of the page.
|
||||
1. Find each of the above APIs. On the page for the API, press the **ENABLE** button.
|
||||
It may take a few minutes for the API to be fully functional.
|
||||
|
||||
|
@ -123,6 +123,6 @@ On your GitLab server:
|
|||
respectively.
|
||||
|
||||
On the sign in page there should now be a Google icon below the regular sign in
|
||||
form. Click the icon to begin the authentication process. Google asks the
|
||||
form. Select the icon to begin the authentication process. Google asks the
|
||||
user to sign in and authorize the GitLab application. If everything goes well
|
||||
the user is returned to GitLab and is signed in.
|
||||
|
|
|
@ -15,7 +15,7 @@ To get the credentials (a pair of Client ID and Client Secret), you must [create
|
|||
|
||||
1. Sign in to [Salesforce](https://login.salesforce.com/).
|
||||
|
||||
1. In Setup, enter `App Manager` in the Quick Find box, click **App Manager**, then click **New Connected App**.
|
||||
1. In Setup, enter `App Manager` in the Quick Find box, select **App Manager**, then select **New Connected App**.
|
||||
|
||||
1. Fill in the application details into the following fields:
|
||||
- **Connected App Name** and **API Name**: Set to any value but consider something like `<Organization>'s GitLab`, `<Your Name>'s GitLab`, or something else that is descriptive.
|
||||
|
@ -24,14 +24,14 @@ To get the credentials (a pair of Client ID and Client Secret), you must [create
|
|||
|
||||
![Salesforce App Details](img/salesforce_app_details.png)
|
||||
|
||||
1. Select **API (Enable OAuth Settings)** and click on **Enable OAuth Settings**.
|
||||
1. Select **API (Enable OAuth Settings)** and select **Enable OAuth Settings**.
|
||||
1. Fill in the application details into the following fields:
|
||||
- **Callback URL**: The callback URL of your GitLab installation. For example, `https://gitlab.example.com/users/auth/salesforce/callback`.
|
||||
- **Selected OAuth Scopes**: Move `Access your basic information (id, profile, email, address, phone)` and `Allow access to your unique identifier (openid)` to the right column.
|
||||
|
||||
![Salesforce OAuth App Details](img/salesforce_oauth_app_details.png)
|
||||
|
||||
1. Click **Save**.
|
||||
1. Select **Save**.
|
||||
|
||||
1. On your GitLab server, open the configuration file.
|
||||
|
||||
|
@ -86,7 +86,7 @@ To get the credentials (a pair of Client ID and Client Secret), you must [create
|
|||
to take effect if you installed GitLab via Omnibus or from source respectively.
|
||||
|
||||
On the sign in page, there should now be a Salesforce icon below the regular sign in form.
|
||||
Click the icon to begin the authentication process. Salesforce asks the user to sign in and authorize the GitLab application.
|
||||
Select the icon to begin the authentication process. Salesforce asks the user to sign in and authorize the GitLab application.
|
||||
If everything goes well, the user is returned to GitLab and is signed in.
|
||||
|
||||
NOTE:
|
||||
|
|
|
@ -160,7 +160,7 @@ At a minimum the IdP *must* provide a claim containing the user's email address
|
|||
See [the assertions list](#assertions) for other available claims.
|
||||
|
||||
On the sign in page there should now be a SAML button below the regular sign in form.
|
||||
Click the icon to begin the authentication process. If everything goes well the user
|
||||
Select the icon to begin the authentication process. If everything goes well the user
|
||||
is returned to GitLab and signed in.
|
||||
|
||||
### Use multiple SAML identity providers
|
||||
|
@ -486,7 +486,7 @@ In addition to the changes in GitLab, make sure that your IdP is returning the
|
|||
### `auto_sign_in_with_provider`
|
||||
|
||||
You can add this setting to your GitLab configuration to automatically redirect you
|
||||
to your SAML server for authentication. This removes the requirement to click a button
|
||||
to your SAML server for authentication. This removes the requirement to select a button
|
||||
before actually signing in.
|
||||
|
||||
For Omnibus package:
|
||||
|
@ -812,7 +812,7 @@ The following guidance is based on this Okta article, on adding a [SAML Applicat
|
|||
1. The last part of the configuration is the feedback section where you can
|
||||
just say you're a customer and creating an app for internal use.
|
||||
1. When you have your app you can see a few tabs on the top of the app's
|
||||
profile. Click on the SAML 2.0 configuration instructions button.
|
||||
profile. Select the SAML 2.0 configuration instructions button.
|
||||
1. On the screen that comes up take note of the
|
||||
**Identity Provider Single Sign-On URL** which you can use for the
|
||||
`idp_sso_target_url` on your GitLab configuration file.
|
||||
|
|
|
@ -88,4 +88,4 @@ Twitter. Twitter generates a client ID and secret key for you to use.
|
|||
1. [Reconfigure](../administration/restart_gitlab.md#omnibus-gitlab-reconfigure) or [restart GitLab](../administration/restart_gitlab.md#installations-from-source) for the changes to take effect if you
|
||||
installed GitLab via Omnibus or from source respectively.
|
||||
|
||||
On the sign in page there should now be a Twitter icon below the regular sign in form. Click the icon to begin the authentication process. Twitter asks the user to sign in and authorize the GitLab application. If everything goes well the user is returned to GitLab and signed in.
|
||||
On the sign in page there should now be a Twitter icon below the regular sign in form. Select the icon to begin the authentication process. Twitter asks the user to sign in and authorize the GitLab application. If everything goes well the user is returned to GitLab and signed in.
|
||||
|
|
|
@ -98,11 +98,11 @@ The following assumes you already have Vault installed and running.
|
|||
|
||||
1. Go to your Vault UI (example: [http://127.0.0.1:8200/ui/vault/auth?with=oidc](http://127.0.0.1:8200/ui/vault/auth?with=oidc)).
|
||||
1. If the `OIDC` method is not currently selected, open the dropdown and select it.
|
||||
1. Click the **Sign in With GitLab** button, which opens a modal window:
|
||||
1. Select **Sign in With GitLab**, which opens a modal window:
|
||||
|
||||
![Sign into Vault with GitLab](img/sign_into_vault_with_gitlab_v12_6.png)
|
||||
|
||||
1. Click **Authorize** on the modal to allow Vault to sign in through GitLab. This redirects you back to your Vault UI as a signed-in user.
|
||||
1. Select **Authorize** to allow Vault to sign in through GitLab. This redirects you back to your Vault UI as a signed-in user.
|
||||
|
||||
![Authorize Vault to connect with GitLab](img/authorize_vault_with_gitlab_v12_6.png)
|
||||
|
||||
|
@ -127,7 +127,7 @@ The following assumes you already have Vault installed and running.
|
|||
[Redirect URIs](https://www.vaultproject.io/docs/auth/jwt#redirect-uris).
|
||||
|
||||
After running the command, it presents a link in the terminal.
|
||||
Click the link in the terminal and a browser tab opens that confirms you're signed into Vault via OIDC:
|
||||
Select the link in the terminal and a browser tab opens that confirms you're signed into Vault via OIDC:
|
||||
|
||||
![Signed into Vault via OIDC](img/signed_into_vault_via_oidc_v12_6.png)
|
||||
|
||||
|
|
|
@ -321,8 +321,8 @@ You can [cancel the subscription](#enable-or-disable-automatic-renewal) to disab
|
|||
1. From either your personal homepage or the group's page, go to **Settings > Usage Quotas**.
|
||||
1. For each locked project, total by how much its **Usage** exceeds the free quota and purchased
|
||||
storage. You must purchase the storage increment that exceeds this total.
|
||||
1. Click **Purchase more storage** and you are taken to the Customers Portal.
|
||||
1. Click **Add new subscription**.
|
||||
1. Select **Purchase more storage** and you are taken to the Customers Portal.
|
||||
1. Select **Add new subscription**.
|
||||
1. Scroll to **Purchase add-on subscriptions** and select **Buy storage subscription**.
|
||||
1. In the **Subscription details** section select the name of the user or group from the dropdown.
|
||||
1. Enter the desired quantity of storage packs.
|
||||
|
|
|
@ -39,7 +39,6 @@ containing a CRON expression for when the scans will be run.
|
|||
|
||||
```yaml
|
||||
starboard:
|
||||
vulnerability_report:
|
||||
cadence: '0 0 * * *' # Daily at 00:00 (Kubernetes cluster time)
|
||||
```
|
||||
|
||||
|
@ -58,8 +57,8 @@ namespaces, you can use this configuration:
|
|||
|
||||
```yaml
|
||||
starboard:
|
||||
vulnerability_report:
|
||||
cadence: '0 0 * * *'
|
||||
vulnerability_report:
|
||||
namespaces:
|
||||
- development
|
||||
- staging
|
||||
|
|
|
@ -69,6 +69,14 @@ Example response without attribute `select`:
|
|||
}
|
||||
```
|
||||
|
||||
Example request with attribute `select = package_file`:
|
||||
|
||||
```shell
|
||||
curl --header "PRIVATE-TOKEN: <your_access_token>" \
|
||||
--upload-file path/to/file.txt \
|
||||
"https://gitlab.example.com/api/v4/projects/24/packages/generic/my_package/0.0.1/file.txt?select=package_file"
|
||||
```
|
||||
|
||||
Example response with attribute `select = package_file`:
|
||||
|
||||
```json
|
||||
|
|
|
@ -1455,7 +1455,7 @@ msgstr[1] ""
|
|||
msgid "1-9 contributions"
|
||||
msgstr ""
|
||||
|
||||
msgid "1. Effective June 1, 2022, all free tier public projects will be %{minutes_quota_link}."
|
||||
msgid "1. Effective June 1, 2022, all GitLab Free tier public projects will be %{minutes_quota_link}."
|
||||
msgstr ""
|
||||
|
||||
msgid "10-19 contributions"
|
||||
|
@ -1470,7 +1470,7 @@ msgstr ""
|
|||
msgid "1st contribution!"
|
||||
msgstr ""
|
||||
|
||||
msgid "2. Before July 1, 2022, all free tier public open source projects must %{enrollment_link} to continue to receive GitLab Ultimate benefits."
|
||||
msgid "2. Before July 1, 2022, all GitLab Free tier public open source projects must %{enrollment_link} to continue to receive GitLab Ultimate benefits."
|
||||
msgstr ""
|
||||
|
||||
msgid "20-29 contributions"
|
||||
|
@ -7407,7 +7407,7 @@ msgstr ""
|
|||
msgid "Changes the title to \"%{title_param}\"."
|
||||
msgstr ""
|
||||
|
||||
msgid "Changes to free tier public projects"
|
||||
msgid "Changes to GitLab Free tier public projects"
|
||||
msgstr ""
|
||||
|
||||
msgid "Changes to the title have not been saved"
|
||||
|
@ -10849,6 +10849,9 @@ msgstr ""
|
|||
msgid "Critical vulnerabilities present"
|
||||
msgstr ""
|
||||
|
||||
msgid "Crm|Contact"
|
||||
msgstr ""
|
||||
|
||||
msgid "Crm|Contact has been added."
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -29,20 +29,14 @@ module QA
|
|||
start_pipeline_via_api_with_variable(i + 1)
|
||||
wait_for_pipelines
|
||||
|
||||
# Is inheritable when true
|
||||
expect(child1_pipeline).to have_variable(key: key, value: value),
|
||||
"Expected to find `{key: 'TEST_VAR', value: 'This is great!'}`" \
|
||||
" but got #{child1_pipeline.pipeline_variables}"
|
||||
# When forward:pipeline_variables is true
|
||||
expect_downstream_pipeline_to_inherit_variable
|
||||
|
||||
# Is not inheritable when false
|
||||
expect(child2_pipeline).not_to have_variable(key: key, value: value),
|
||||
"Did not expect to find `{key: 'TEST_VAR', value: 'This is great!'}`" \
|
||||
" but got #{child2_pipeline.pipeline_variables}"
|
||||
# When forward:pipeline_variables is false
|
||||
expect_downstream_pipeline_not_to_inherit_variable(upstream_project, 'child2_trigger')
|
||||
|
||||
# Is not inheritable by default
|
||||
expect(downstream1_pipeline).not_to have_variable(key: key, value: value),
|
||||
"Did not expect to find `{key: 'TEST_VAR', value: 'This is great!'}`" \
|
||||
" but got #{downstream1_pipeline.pipeline_variables}"
|
||||
# When forward:pipeline_variables is default
|
||||
expect_downstream_pipeline_not_to_inherit_variable(downstream1_project, 'downstream1_trigger')
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -84,6 +78,20 @@ module QA
|
|||
YAML
|
||||
}
|
||||
end
|
||||
|
||||
def expect_downstream_pipeline_to_inherit_variable
|
||||
pipeline = downstream_pipeline(upstream_project, 'child1_trigger')
|
||||
expect(pipeline).to have_variable(key: key, value: value),
|
||||
"Expected to find `{key: 'TEST_VAR', value: 'This is great!'}`" \
|
||||
" but got #{pipeline.pipeline_variables}"
|
||||
end
|
||||
|
||||
def expect_downstream_pipeline_not_to_inherit_variable(project, bridge_name)
|
||||
pipeline = downstream_pipeline(project, bridge_name)
|
||||
expect(pipeline).not_to have_variable(key: key, value: value),
|
||||
"Did not expect to find `{key: 'TEST_VAR', value: 'This is great!'}`" \
|
||||
" but got #{pipeline.pipeline_variables}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -66,7 +66,8 @@ module QA
|
|||
|
||||
def wait_for_pipelines
|
||||
Support::Waiter.wait_until(max_duration: 300, sleep_interval: 10) do
|
||||
upstream_pipeline.status == 'success' && downstream1_pipeline.status == 'success'
|
||||
upstream_pipeline.status == 'success' &&
|
||||
downstream_pipeline(downstream1_project, 'downstream1_trigger').status == 'success'
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -106,31 +107,10 @@ module QA
|
|||
end
|
||||
end
|
||||
|
||||
def child1_pipeline
|
||||
def downstream_pipeline(project, bridge_name)
|
||||
Resource::Pipeline.fabricate_via_api! do |pipeline|
|
||||
pipeline.project = upstream_project
|
||||
pipeline.id = upstream_pipeline.downstream_pipeline_id(bridge_name: 'child1_trigger')
|
||||
end
|
||||
end
|
||||
|
||||
def child2_pipeline
|
||||
Resource::Pipeline.fabricate_via_api! do |pipeline|
|
||||
pipeline.project = upstream_project
|
||||
pipeline.id = upstream_pipeline.downstream_pipeline_id(bridge_name: 'child2_trigger')
|
||||
end
|
||||
end
|
||||
|
||||
def downstream1_pipeline
|
||||
Resource::Pipeline.fabricate_via_api! do |pipeline|
|
||||
pipeline.project = downstream1_project
|
||||
pipeline.id = upstream_pipeline.downstream_pipeline_id(bridge_name: 'downstream1_trigger')
|
||||
end
|
||||
end
|
||||
|
||||
def downstream2_pipeline
|
||||
Resource::Pipeline.fabricate_via_api! do |pipeline|
|
||||
pipeline.project = downstream2_project
|
||||
pipeline.id = upstream_pipeline.downstream_pipeline_id(bridge_name: 'downstream2_trigger')
|
||||
pipeline.project = project
|
||||
pipeline.id = upstream_pipeline.downstream_pipeline_id(bridge_name: bridge_name)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -122,8 +122,8 @@ RSpec.describe 'Milestone' do
|
|||
|
||||
click_link 'Reopen Milestone'
|
||||
|
||||
expect(page).not_to have_selector('.status-box-closed')
|
||||
expect(page).to have_selector('.status-box-open')
|
||||
expect(page).not_to have_selector('.gl-bg-red-500')
|
||||
expect(page).to have_selector('.gl-bg-green-500')
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -133,8 +133,8 @@ RSpec.describe 'Milestone' do
|
|||
|
||||
click_link 'Reopen Milestone'
|
||||
|
||||
expect(page).not_to have_selector('.status-box-closed')
|
||||
expect(page).to have_selector('.status-box-open')
|
||||
expect(page).not_to have_selector('.gl-bg-red-500')
|
||||
expect(page).to have_selector('.gl-bg-green-500')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -36,9 +36,11 @@ import {
|
|||
TOKEN_TYPE_ASSIGNEE,
|
||||
TOKEN_TYPE_AUTHOR,
|
||||
TOKEN_TYPE_CONFIDENTIAL,
|
||||
TOKEN_TYPE_CONTACT,
|
||||
TOKEN_TYPE_LABEL,
|
||||
TOKEN_TYPE_MILESTONE,
|
||||
TOKEN_TYPE_MY_REACTION,
|
||||
TOKEN_TYPE_ORGANIZATION,
|
||||
TOKEN_TYPE_RELEASE,
|
||||
TOKEN_TYPE_TYPE,
|
||||
urlSortParams,
|
||||
|
@ -65,6 +67,8 @@ describe('CE IssuesListApp component', () => {
|
|||
autocompleteAwardEmojisPath: 'autocomplete/award/emojis/path',
|
||||
calendarPath: 'calendar/path',
|
||||
canBulkUpdate: false,
|
||||
canReadCrmContact: false,
|
||||
canReadCrmOrganization: false,
|
||||
emptyStateSvgPath: 'empty-state.svg',
|
||||
exportCsvPath: 'export/csv/path',
|
||||
fullPath: 'path/to/project',
|
||||
|
@ -589,6 +593,21 @@ describe('CE IssuesListApp component', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('when user does not have CRM enabled', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = mountComponent({
|
||||
provide: { canReadCrmContact: false, canReadCrmOrganization: false },
|
||||
});
|
||||
});
|
||||
|
||||
it('does not render Contact or Organization tokens', () => {
|
||||
expect(findIssuableList().props('searchTokens')).not.toMatchObject([
|
||||
{ type: TOKEN_TYPE_CONTACT },
|
||||
{ type: TOKEN_TYPE_ORGANIZATION },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when all tokens are available', () => {
|
||||
const originalGon = window.gon;
|
||||
|
||||
|
@ -601,7 +620,13 @@ describe('CE IssuesListApp component', () => {
|
|||
current_user_avatar_url: mockCurrentUser.avatar_url,
|
||||
};
|
||||
|
||||
wrapper = mountComponent({ provide: { isSignedIn: true } });
|
||||
wrapper = mountComponent({
|
||||
provide: {
|
||||
canReadCrmContact: true,
|
||||
canReadCrmOrganization: true,
|
||||
isSignedIn: true,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
@ -617,9 +642,11 @@ describe('CE IssuesListApp component', () => {
|
|||
{ type: TOKEN_TYPE_ASSIGNEE, preloadedAuthors },
|
||||
{ type: TOKEN_TYPE_AUTHOR, preloadedAuthors },
|
||||
{ type: TOKEN_TYPE_CONFIDENTIAL },
|
||||
{ type: TOKEN_TYPE_CONTACT },
|
||||
{ type: TOKEN_TYPE_LABEL },
|
||||
{ type: TOKEN_TYPE_MILESTONE },
|
||||
{ type: TOKEN_TYPE_MY_REACTION },
|
||||
{ type: TOKEN_TYPE_ORGANIZATION },
|
||||
{ type: TOKEN_TYPE_RELEASE },
|
||||
{ type: TOKEN_TYPE_TYPE },
|
||||
]);
|
||||
|
|
|
@ -159,7 +159,6 @@ describe('IssuableBody', () => {
|
|||
expect(titleEl.exists()).toBe(true);
|
||||
expect(titleEl.props()).toMatchObject({
|
||||
issuable: issuableBodyProps.issuable,
|
||||
statusBadgeClass: issuableBodyProps.statusBadgeClass,
|
||||
statusIcon: issuableBodyProps.statusIcon,
|
||||
enableEdit: issuableBodyProps.enableEdit,
|
||||
});
|
||||
|
|
|
@ -47,7 +47,6 @@ describe('IssuableShowRoot', () => {
|
|||
|
||||
describe('template', () => {
|
||||
const {
|
||||
statusBadgeClass,
|
||||
statusIcon,
|
||||
statusIconClass,
|
||||
enableEdit,
|
||||
|
@ -69,7 +68,6 @@ describe('IssuableShowRoot', () => {
|
|||
expect(issuableHeader.exists()).toBe(true);
|
||||
expect(issuableHeader.props()).toMatchObject({
|
||||
issuableState: state,
|
||||
statusBadgeClass,
|
||||
statusIcon,
|
||||
statusIconClass,
|
||||
blocked,
|
||||
|
@ -91,7 +89,6 @@ describe('IssuableShowRoot', () => {
|
|||
expect(issuableBody.exists()).toBe(true);
|
||||
expect(issuableBody.props()).toMatchObject({
|
||||
issuable: mockIssuable,
|
||||
statusBadgeClass,
|
||||
statusIcon,
|
||||
enableEdit,
|
||||
enableAutocomplete,
|
||||
|
|
|
@ -98,9 +98,6 @@ describe('IssuableTitle', () => {
|
|||
|
||||
expect(stickyHeaderEl.exists()).toBe(true);
|
||||
expect(stickyHeaderEl.findComponent(GlBadge).props('variant')).toBe('success');
|
||||
expect(stickyHeaderEl.findComponent(GlBadge).classes()).toContain(
|
||||
mockIssuableShowProps.statusBadgeClass,
|
||||
);
|
||||
expect(stickyHeaderEl.findComponent(GlIcon).props('name')).toBe(
|
||||
issuableTitleProps.statusIcon,
|
||||
);
|
||||
|
|
|
@ -36,7 +36,6 @@ export const mockIssuableShowProps = {
|
|||
enableTaskList: true,
|
||||
enableEdit: true,
|
||||
showFieldTitle: false,
|
||||
statusBadgeClass: 'issuable-status-badge-open',
|
||||
statusIcon: 'issues',
|
||||
statusIconClass: 'gl-sm-display-none',
|
||||
taskCompletionStatus: {
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import { GlLink } from '@gitlab/ui';
|
||||
import WorkItemAssignees from '~/work_items/components/work_item_assignees.vue';
|
||||
|
||||
const mockAssignees = [
|
||||
{
|
||||
__typename: 'UserCore',
|
||||
id: 'gid://gitlab/User/1',
|
||||
avatarUrl: '',
|
||||
webUrl: '',
|
||||
name: 'John Doe',
|
||||
username: 'doe_I',
|
||||
},
|
||||
{
|
||||
__typename: 'UserCore',
|
||||
id: 'gid://gitlab/User/2',
|
||||
avatarUrl: '',
|
||||
webUrl: '',
|
||||
name: 'Marcus Rutherford',
|
||||
username: 'ruthfull',
|
||||
},
|
||||
];
|
||||
|
||||
describe('WorkItemAssignees component', () => {
|
||||
let wrapper;
|
||||
|
||||
const findAssigneeLinks = () => wrapper.findAllComponents(GlLink);
|
||||
|
||||
const createComponent = () => {
|
||||
wrapper = shallowMount(WorkItemAssignees, {
|
||||
propsData: {
|
||||
assignees: mockAssignees,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
it('should pass the correct data-user-id attribute', () => {
|
||||
createComponent();
|
||||
|
||||
expect(findAssigneeLinks().at(0).attributes('data-user-id')).toBe('1');
|
||||
});
|
||||
});
|
|
@ -7,9 +7,11 @@ import waitForPromises from 'helpers/wait_for_promises';
|
|||
import WorkItemDetail from '~/work_items/components/work_item_detail.vue';
|
||||
import WorkItemState from '~/work_items/components/work_item_state.vue';
|
||||
import WorkItemTitle from '~/work_items/components/work_item_title.vue';
|
||||
import WorkItemAssignees from '~/work_items/components/work_item_assignees.vue';
|
||||
import { i18n } from '~/work_items/constants';
|
||||
import workItemQuery from '~/work_items/graphql/work_item.query.graphql';
|
||||
import workItemTitleSubscription from '~/work_items/graphql/work_item_title.subscription.graphql';
|
||||
import { temporaryConfig } from '~/work_items/graphql/provider';
|
||||
import { workItemTitleSubscriptionResponse, workItemQueryResponse } from '../mock_data';
|
||||
|
||||
describe('WorkItemDetail component', () => {
|
||||
|
@ -24,18 +26,32 @@ describe('WorkItemDetail component', () => {
|
|||
const findSkeleton = () => wrapper.findComponent(GlSkeletonLoader);
|
||||
const findWorkItemTitle = () => wrapper.findComponent(WorkItemTitle);
|
||||
const findWorkItemState = () => wrapper.findComponent(WorkItemState);
|
||||
const findWorkItemAssignees = () => wrapper.findComponent(WorkItemAssignees);
|
||||
|
||||
const createComponent = ({
|
||||
workItemId = workItemQueryResponse.data.workItem.id,
|
||||
handler = successHandler,
|
||||
subscriptionHandler = initialSubscriptionHandler,
|
||||
assigneesEnabled = false,
|
||||
includeAssigneesWidget = false,
|
||||
} = {}) => {
|
||||
wrapper = shallowMount(WorkItemDetail, {
|
||||
apolloProvider: createMockApollo([
|
||||
apolloProvider: createMockApollo(
|
||||
[
|
||||
[workItemQuery, handler],
|
||||
[workItemTitleSubscription, subscriptionHandler],
|
||||
]),
|
||||
],
|
||||
{},
|
||||
{
|
||||
typePolicies: includeAssigneesWidget ? temporaryConfig.cacheConfig.typePolicies : {},
|
||||
},
|
||||
),
|
||||
propsData: { workItemId },
|
||||
provide: {
|
||||
glFeatures: {
|
||||
workItemAssignees: assigneesEnabled,
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -118,4 +134,33 @@ describe('WorkItemDetail component', () => {
|
|||
|
||||
expect(wrapper.emitted('workItemUpdated')).toEqual([[], []]);
|
||||
});
|
||||
|
||||
describe('when assignees feature flag is enabled', () => {
|
||||
it('renders assignees component when assignees widget is returned from the API', async () => {
|
||||
createComponent({
|
||||
assigneesEnabled: true,
|
||||
includeAssigneesWidget: true,
|
||||
});
|
||||
await waitForPromises();
|
||||
|
||||
expect(findWorkItemAssignees().exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('does not render assignees component when assignees widget is not returned from the API', async () => {
|
||||
createComponent({
|
||||
assigneesEnabled: true,
|
||||
includeAssigneesWidget: false,
|
||||
});
|
||||
await waitForPromises();
|
||||
|
||||
expect(findWorkItemAssignees().exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
it('does not render assignees component when assignees feature flag is disabled', async () => {
|
||||
createComponent();
|
||||
await waitForPromises();
|
||||
|
||||
expect(findWorkItemAssignees().exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -12,7 +12,7 @@ RSpec.describe GitlabSchema.types['CiRunner'] do
|
|||
id description created_at contacted_at maximum_timeout access_level active paused status
|
||||
version short_sha revision locked run_untagged ip_address runner_type tag_list
|
||||
project_count job_count admin_url edit_admin_url user_permissions executor_name architecture_name platform_name
|
||||
maintenance_note groups projects jobs token_expires_at
|
||||
maintenance_note maintenance_note_html groups projects jobs token_expires_at
|
||||
]
|
||||
|
||||
expect(described_class).to include_graphql_fields(*expected_fields)
|
||||
|
|
|
@ -440,6 +440,90 @@ RSpec.describe IssuesHelper do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#status_box_class' do
|
||||
context 'when updated_mr_header feature flag is enabled' do
|
||||
before do
|
||||
stub_feature_flags(updated_mr_header: true)
|
||||
end
|
||||
|
||||
context 'when object is expired' do
|
||||
it 'returns orange background' do
|
||||
milestone = build(:milestone, due_date: Date.today.prev_month)
|
||||
expect(helper.status_box_class(milestone)).to eq('gl-bg-orange-500')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when object is merged' do
|
||||
it 'returns blue background' do
|
||||
merge_request = build(:merge_request, :merged)
|
||||
expect(helper.status_box_class(merge_request)).to eq('badge-info')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when object is closed' do
|
||||
it 'returns red background' do
|
||||
merge_request = build(:merge_request, :closed)
|
||||
expect(helper.status_box_class(merge_request)).to eq('badge-danger')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when object is upcoming' do
|
||||
it 'returns gray background' do
|
||||
milestone = build(:milestone, start_date: Date.today.next_month)
|
||||
expect(helper.status_box_class(milestone)).to eq('gl-bg-gray-500')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when object is opened' do
|
||||
it 'returns green background' do
|
||||
merge_request = build(:merge_request, :opened)
|
||||
expect(helper.status_box_class(merge_request)).to eq('badge-success')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when updated_mr_header feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(updated_mr_header: false)
|
||||
end
|
||||
|
||||
context 'when object is expired' do
|
||||
it 'returns orange background' do
|
||||
milestone = build(:milestone, due_date: Date.today.prev_month)
|
||||
expect(helper.status_box_class(milestone)).to eq('gl-bg-orange-500')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when object is merged' do
|
||||
it 'returns blue background' do
|
||||
merge_request = build(:merge_request, :merged)
|
||||
expect(helper.status_box_class(merge_request)).to eq('gl-bg-blue-500')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when object is closed' do
|
||||
it 'returns red background' do
|
||||
merge_request = build(:merge_request, :closed)
|
||||
expect(helper.status_box_class(merge_request)).to eq('gl-bg-red-500')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when object is upcoming' do
|
||||
it 'returns gray background' do
|
||||
milestone = build(:milestone, start_date: Date.today.next_month)
|
||||
expect(helper.status_box_class(milestone)).to eq('gl-bg-gray-500')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when object is opened' do
|
||||
it 'returns green background' do
|
||||
merge_request = build(:merge_request, :opened)
|
||||
expect(helper.status_box_class(merge_request)).to eq('gl-bg-green-500')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#issue_hidden?' do
|
||||
context 'when issue is hidden' do
|
||||
let_it_be(:banned_user) { build(:user, :banned) }
|
||||
|
|
|
@ -152,7 +152,7 @@ RSpec.describe TodosHelper do
|
|||
shared_examples 'a rendered state pill' do |attr|
|
||||
it 'returns expected html' do
|
||||
aggregate_failures do
|
||||
expect(subject).to have_css(".status-box-#{attr[:type]}-#{attr[:state].dasherize}")
|
||||
expect(subject).to have_css(attr[:css])
|
||||
expect(subject).to have_content(attr[:state].capitalize)
|
||||
end
|
||||
end
|
||||
|
@ -167,12 +167,20 @@ RSpec.describe TodosHelper do
|
|||
|
||||
it_behaves_like 'no state pill'
|
||||
|
||||
context 'closed MR' do
|
||||
before do
|
||||
todo.target.update!(state: 'closed')
|
||||
end
|
||||
|
||||
it_behaves_like 'a rendered state pill', css: '.gl-bg-red-500', state: 'closed'
|
||||
end
|
||||
|
||||
context 'merged MR' do
|
||||
before do
|
||||
todo.target.update!(state: 'merged')
|
||||
end
|
||||
|
||||
it_behaves_like 'a rendered state pill', type: 'mr', state: 'merged'
|
||||
it_behaves_like 'a rendered state pill', css: '.gl-bg-blue-500', state: 'merged'
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -186,7 +194,7 @@ RSpec.describe TodosHelper do
|
|||
todo.target.update!(state: 'closed')
|
||||
end
|
||||
|
||||
it_behaves_like 'a rendered state pill', type: 'issue', state: 'closed'
|
||||
it_behaves_like 'a rendered state pill', css: '.gl-bg-blue-500', state: 'closed'
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -200,7 +208,7 @@ RSpec.describe TodosHelper do
|
|||
todo.target.resolve!
|
||||
end
|
||||
|
||||
it_behaves_like 'a rendered state pill', type: 'alert', state: 'resolved'
|
||||
it_behaves_like 'a rendered state pill', css: '.gl-bg-blue-500', state: 'resolved'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -12,7 +12,7 @@ RSpec.describe 'Query.runner(id)' do
|
|||
create(:ci_runner, :instance, description: 'Runner 1', contacted_at: 2.hours.ago,
|
||||
active: true, version: 'adfe156', revision: 'a', locked: true, ip_address: '127.0.0.1', maximum_timeout: 600,
|
||||
access_level: 0, tag_list: %w[tag1 tag2], run_untagged: true, executor_type: :custom,
|
||||
maintenance_note: 'Test maintenance note')
|
||||
maintenance_note: '**Test maintenance note**')
|
||||
end
|
||||
|
||||
let_it_be(:inactive_instance_runner) do
|
||||
|
@ -66,6 +66,8 @@ RSpec.describe 'Query.runner(id)' do
|
|||
'architectureName' => runner.architecture,
|
||||
'platformName' => runner.platform,
|
||||
'maintenanceNote' => runner.maintenance_note,
|
||||
'maintenanceNoteHtml' =>
|
||||
runner.maintainer_note.present? ? a_string_including('<strong>Test maintenance note</strong>') : '',
|
||||
'jobCount' => 0,
|
||||
'jobs' => a_hash_including("count" => 0, "nodes" => [], "pageInfo" => anything),
|
||||
'projectCount' => nil,
|
||||
|
|
|
@ -35,6 +35,22 @@ RSpec.describe Packages::Go::CreatePackageService do
|
|||
expect(file.file_sha1).not_to be_nil
|
||||
expect(file.file_sha256).not_to be_nil
|
||||
end
|
||||
|
||||
context 'with FIPS mode', :fips_mode do
|
||||
it 'does not generate file_md5' do
|
||||
file_name = "#{version.name}.#{type}"
|
||||
expect(subject.package_files.map { |f| f.file_name }).to include(file_name)
|
||||
|
||||
file = subject.package_files.with_file_name(file_name).first
|
||||
expect(file).not_to be_nil
|
||||
expect(file.file).not_to be_nil
|
||||
expect(file.size).to eq(file.file.size)
|
||||
expect(file.file_name).to eq(file_name)
|
||||
expect(file.file_md5).to be_nil
|
||||
expect(file.file_sha1).not_to be_nil
|
||||
expect(file.file_sha256).not_to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#execute' do
|
||||
|
|
|
@ -192,7 +192,6 @@ RSpec.describe 'Every Sidekiq worker' do
|
|||
'CreateGithubWebhookWorker' => 3,
|
||||
'CreateNoteDiffFileWorker' => 3,
|
||||
'CreatePipelineWorker' => 3,
|
||||
'DastSiteValidationWorker' => 3,
|
||||
'DeleteContainerRepositoryWorker' => 3,
|
||||
'DeleteDiffFilesWorker' => 3,
|
||||
'DeleteMergedBranchesWorker' => 3,
|
||||
|
|
Loading…
Reference in New Issue