Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
c916c6f79b
commit
654281e682
48 changed files with 1201 additions and 61 deletions
|
@ -1,3 +1,4 @@
|
||||||
|
import * as Sentry from '@sentry/browser';
|
||||||
import { sanitize } from '~/lib/dompurify';
|
import { sanitize } from '~/lib/dompurify';
|
||||||
|
|
||||||
// We currently load + parse the data from the issue app and related merge request
|
// We currently load + parse the data from the issue app and related merge request
|
||||||
|
@ -7,10 +8,9 @@ export const parseIssuableData = () => {
|
||||||
try {
|
try {
|
||||||
if (cachedParsedData) return cachedParsedData;
|
if (cachedParsedData) return cachedParsedData;
|
||||||
|
|
||||||
const initialDataEl = document.getElementById('js-issuable-app-initial-data');
|
const initialDataEl = document.getElementById('js-issuable-app');
|
||||||
|
|
||||||
const parsedData = JSON.parse(initialDataEl.textContent.replace(/"/g, '"'));
|
|
||||||
|
|
||||||
|
const parsedData = JSON.parse(initialDataEl.dataset.initial);
|
||||||
parsedData.initialTitleHtml = sanitize(parsedData.initialTitleHtml);
|
parsedData.initialTitleHtml = sanitize(parsedData.initialTitleHtml);
|
||||||
parsedData.initialDescriptionHtml = sanitize(parsedData.initialDescriptionHtml);
|
parsedData.initialDescriptionHtml = sanitize(parsedData.initialDescriptionHtml);
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ export const parseIssuableData = () => {
|
||||||
|
|
||||||
return parsedData;
|
return parsedData;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e); // eslint-disable-line no-console
|
Sentry.captureException(e);
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
/* eslint-disable vue/no-v-html */
|
/* eslint-disable vue/no-v-html */
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import { GlFormGroup, GlDeprecatedButton, GlModal, GlToast, GlToggle } from '@gitlab/ui';
|
import { GlFormGroup, GlButton, GlModal, GlToast, GlToggle } from '@gitlab/ui';
|
||||||
import { mapState, mapActions } from 'vuex';
|
import { mapState, mapActions } from 'vuex';
|
||||||
import { __, s__, sprintf } from '~/locale';
|
import { __, s__, sprintf } from '~/locale';
|
||||||
import { visitUrl, getBaseURL } from '~/lib/utils/url_utility';
|
import { visitUrl, getBaseURL } from '~/lib/utils/url_utility';
|
||||||
|
@ -11,7 +11,7 @@ Vue.use(GlToast);
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
GlFormGroup,
|
GlFormGroup,
|
||||||
GlDeprecatedButton,
|
GlButton,
|
||||||
GlModal,
|
GlModal,
|
||||||
GlToggle,
|
GlToggle,
|
||||||
},
|
},
|
||||||
|
@ -123,7 +123,7 @@ export default {
|
||||||
<h4 class="js-section-header">
|
<h4 class="js-section-header">
|
||||||
{{ s__('SelfMonitoring|Self monitoring') }}
|
{{ s__('SelfMonitoring|Self monitoring') }}
|
||||||
</h4>
|
</h4>
|
||||||
<gl-deprecated-button class="js-settings-toggle">{{ __('Expand') }}</gl-deprecated-button>
|
<gl-button class="js-settings-toggle">{{ __('Expand') }}</gl-button>
|
||||||
<p class="js-section-sub-header">
|
<p class="js-section-sub-header">
|
||||||
{{ s__('SelfMonitoring|Enable or disable instance self monitoring') }}
|
{{ s__('SelfMonitoring|Enable or disable instance self monitoring') }}
|
||||||
</p>
|
</p>
|
||||||
|
@ -146,6 +146,7 @@ export default {
|
||||||
:ok-title="__('Delete project')"
|
:ok-title="__('Delete project')"
|
||||||
:cancel-title="__('Cancel')"
|
:cancel-title="__('Cancel')"
|
||||||
ok-variant="danger"
|
ok-variant="danger"
|
||||||
|
category="primary"
|
||||||
@ok="deleteProject"
|
@ok="deleteProject"
|
||||||
@cancel="hideSelfMonitorModal"
|
@cancel="hideSelfMonitorModal"
|
||||||
>
|
>
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
<script>
|
||||||
|
import { GlAvatarLink, GlAvatarLabeled } from '@gitlab/ui';
|
||||||
|
import { AVATAR_SIZE } from '../constants';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'GroupAvatar',
|
||||||
|
avatarSize: AVATAR_SIZE,
|
||||||
|
components: { GlAvatarLink, GlAvatarLabeled },
|
||||||
|
props: {
|
||||||
|
member: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
group() {
|
||||||
|
return this.member.sharedWithGroup;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<gl-avatar-link :href="group.webUrl">
|
||||||
|
<gl-avatar-labeled
|
||||||
|
:label="group.fullName"
|
||||||
|
:src="group.avatarUrl"
|
||||||
|
:alt="group.fullName"
|
||||||
|
:size="$options.avatarSize"
|
||||||
|
:entity-name="group.name"
|
||||||
|
:entity-id="group.id"
|
||||||
|
/>
|
||||||
|
</gl-avatar-link>
|
||||||
|
</template>
|
|
@ -0,0 +1,32 @@
|
||||||
|
<script>
|
||||||
|
import { GlAvatarLabeled } from '@gitlab/ui';
|
||||||
|
import { AVATAR_SIZE } from '../constants';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'InviteAvatar',
|
||||||
|
avatarSize: AVATAR_SIZE,
|
||||||
|
components: { GlAvatarLabeled },
|
||||||
|
props: {
|
||||||
|
member: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
invite() {
|
||||||
|
return this.member.invite;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<gl-avatar-labeled
|
||||||
|
:label="invite.email"
|
||||||
|
:src="invite.avatarUrl"
|
||||||
|
:alt="invite.email"
|
||||||
|
:size="$options.avatarSize"
|
||||||
|
:entity-name="invite.email"
|
||||||
|
:entity-id="member.id"
|
||||||
|
/>
|
||||||
|
</template>
|
|
@ -0,0 +1,55 @@
|
||||||
|
<script>
|
||||||
|
import { GlAvatarLink, GlAvatarLabeled, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
|
||||||
|
import { __ } from '~/locale';
|
||||||
|
import { AVATAR_SIZE } from '../constants';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'UserAvatar',
|
||||||
|
avatarSize: AVATAR_SIZE,
|
||||||
|
orphanedUserLabel: __('Orphaned member'),
|
||||||
|
components: { GlAvatarLink, GlAvatarLabeled },
|
||||||
|
directives: {
|
||||||
|
SafeHtml,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
member: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
user() {
|
||||||
|
return this.member.user;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<gl-avatar-link
|
||||||
|
v-if="user"
|
||||||
|
class="js-user-link"
|
||||||
|
:href="user.webUrl"
|
||||||
|
:data-user-id="user.id"
|
||||||
|
:data-username="user.username"
|
||||||
|
>
|
||||||
|
<gl-avatar-labeled
|
||||||
|
:label="user.name"
|
||||||
|
:sub-label="`@${user.username}`"
|
||||||
|
:src="user.avatarUrl"
|
||||||
|
:alt="user.name"
|
||||||
|
:size="$options.avatarSize"
|
||||||
|
:entity-name="user.name"
|
||||||
|
:entity-id="user.id"
|
||||||
|
/>
|
||||||
|
</gl-avatar-link>
|
||||||
|
|
||||||
|
<gl-avatar-labeled
|
||||||
|
v-else
|
||||||
|
:label="$options.orphanedUserLabel"
|
||||||
|
:alt="$options.orphanedUserLabel"
|
||||||
|
:size="$options.avatarSize"
|
||||||
|
:entity-name="$options.orphanedUserLabel"
|
||||||
|
:entity-id="member.id"
|
||||||
|
/>
|
||||||
|
</template>
|
|
@ -53,3 +53,12 @@ export const FIELDS = [
|
||||||
tdClass: 'col-actions',
|
tdClass: 'col-actions',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const AVATAR_SIZE = 48;
|
||||||
|
|
||||||
|
export const MEMBER_TYPES = {
|
||||||
|
user: 'user',
|
||||||
|
group: 'group',
|
||||||
|
invite: 'invite',
|
||||||
|
accessRequest: 'accessRequest',
|
||||||
|
};
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
<script>
|
||||||
|
import { kebabCase } from 'lodash';
|
||||||
|
import UserAvatar from '../avatars/user_avatar.vue';
|
||||||
|
import InviteAvatar from '../avatars/invite_avatar.vue';
|
||||||
|
import GroupAvatar from '../avatars/group_avatar.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'MemberAvatar',
|
||||||
|
components: { UserAvatar, InviteAvatar, GroupAvatar, AccessRequestAvatar: UserAvatar },
|
||||||
|
props: {
|
||||||
|
memberType: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
member: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
avatarComponent() {
|
||||||
|
// eslint-disable-next-line @gitlab/require-i18n-strings
|
||||||
|
return `${kebabCase(this.memberType)}-avatar`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<component :is="avatarComponent" :member="member" />
|
||||||
|
</template>
|
|
@ -3,11 +3,15 @@ import { mapState } from 'vuex';
|
||||||
import { GlTable } from '@gitlab/ui';
|
import { GlTable } from '@gitlab/ui';
|
||||||
import { FIELDS } from '../constants';
|
import { FIELDS } from '../constants';
|
||||||
import initUserPopovers from '~/user_popovers';
|
import initUserPopovers from '~/user_popovers';
|
||||||
|
import MemberAvatar from './member_avatar.vue';
|
||||||
|
import MembersTableCell from './members_table_cell.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'MembersTable',
|
name: 'MembersTable',
|
||||||
components: {
|
components: {
|
||||||
GlTable,
|
GlTable,
|
||||||
|
MemberAvatar,
|
||||||
|
MembersTableCell,
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(['members', 'tableFields']),
|
...mapState(['members', 'tableFields']),
|
||||||
|
@ -33,6 +37,12 @@ export default {
|
||||||
:empty-text="__('No members found')"
|
:empty-text="__('No members found')"
|
||||||
show-empty
|
show-empty
|
||||||
>
|
>
|
||||||
|
<template #cell(account)="{ item: member }">
|
||||||
|
<members-table-cell #default="{ memberType }" :member="member">
|
||||||
|
<member-avatar :member-type="memberType" :member="member" />
|
||||||
|
</members-table-cell>
|
||||||
|
</template>
|
||||||
|
|
||||||
<template #cell(source)>
|
<template #cell(source)>
|
||||||
<!-- Temporarily empty -->
|
<!-- Temporarily empty -->
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
<script>
|
||||||
|
import { MEMBER_TYPES } from '../constants';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'MembersTableCell',
|
||||||
|
props: {
|
||||||
|
member: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
isGroup() {
|
||||||
|
return Boolean(this.member.sharedWithGroup);
|
||||||
|
},
|
||||||
|
isInvite() {
|
||||||
|
return Boolean(this.member.invite);
|
||||||
|
},
|
||||||
|
isAccessRequest() {
|
||||||
|
return Boolean(this.member.requestedAt);
|
||||||
|
},
|
||||||
|
memberType() {
|
||||||
|
if (this.isGroup) {
|
||||||
|
return MEMBER_TYPES.group;
|
||||||
|
} else if (this.isInvite) {
|
||||||
|
return MEMBER_TYPES.invite;
|
||||||
|
} else if (this.isAccessRequest) {
|
||||||
|
return MEMBER_TYPES.accessRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
return MEMBER_TYPES.user;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
render() {
|
||||||
|
return this.$scopedSlots.default({
|
||||||
|
memberType: this.memberType,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -112,6 +112,7 @@ export default {
|
||||||
this.currentHighlightItem += 1;
|
this.currentHighlightItem += 1;
|
||||||
} else if (e.keyCode === ENTER_KEY_CODE && this.currentHighlightItem > -1) {
|
} else if (e.keyCode === ENTER_KEY_CODE && this.currentHighlightItem > -1) {
|
||||||
this.updateSelectedLabels([this.visibleLabels[this.currentHighlightItem]]);
|
this.updateSelectedLabels([this.visibleLabels[this.currentHighlightItem]]);
|
||||||
|
this.searchKey = '';
|
||||||
} else if (e.keyCode === ESC_KEY_CODE) {
|
} else if (e.keyCode === ESC_KEY_CODE) {
|
||||||
this.toggleDropdownContents();
|
this.toggleDropdownContents();
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,9 +62,7 @@
|
||||||
|
|
||||||
.issue-details.issuable-details
|
.issue-details.issuable-details
|
||||||
.detail-page-description.content-block
|
.detail-page-description.content-block
|
||||||
-# haml-lint:disable InlineJavaScript
|
#js-issuable-app{ data: { initial: issuable_initial_data(@issue).to_json} }
|
||||||
%script#js-issuable-app-initial-data{ type: "application/json" }= issuable_initial_data(@issue).to_json
|
|
||||||
#js-issuable-app
|
|
||||||
.title-container
|
.title-container
|
||||||
%h2.title= markdown_field(@issue, :title)
|
%h2.title= markdown_field(@issue, :title)
|
||||||
- if @issue.description.present?
|
- if @issue.description.present?
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Add empty dependencies value to ECS Deploy job
|
||||||
|
merge_request: 36862
|
||||||
|
author:
|
||||||
|
type: fixed
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Reset labels select search text on Enter
|
||||||
|
merge_request: 43285
|
||||||
|
author:
|
||||||
|
type: fixed
|
5
changelogs/unreleased/spec_job_token.yml
Normal file
5
changelogs/unreleased/spec_job_token.yml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Move job token specs to core
|
||||||
|
merge_request: 42374
|
||||||
|
author: Mathieu Parent
|
||||||
|
type: changed
|
|
@ -1,7 +1,7 @@
|
||||||
---
|
---
|
||||||
name: container_registry_fast_tag_delete
|
name: container_registry_fast_tag_delete
|
||||||
introduced_by_url:
|
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/23325
|
||||||
rollout_issue_url:
|
rollout_issue_url:
|
||||||
group:
|
group: group::package
|
||||||
type: development
|
type: development
|
||||||
default_enabled: true
|
default_enabled: true
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
---
|
---
|
||||||
name: invisible_captcha
|
name: invisible_captcha
|
||||||
introduced_by_url:
|
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/31625
|
||||||
rollout_issue_url:
|
rollout_issue_url:
|
||||||
group:
|
group: group::acquisition
|
||||||
type: development
|
type: development
|
||||||
default_enabled: false
|
default_enabled: false
|
||||||
|
|
14
doc/.vale/gitlab/InclusionAbleism.yml
Normal file
14
doc/.vale/gitlab/InclusionAbleism.yml
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
---
|
||||||
|
# Suggestion: gitlab.InclusionAbleism
|
||||||
|
#
|
||||||
|
# Suggests alternatives for words that foster ableism.
|
||||||
|
#
|
||||||
|
# For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles
|
||||||
|
extends: substitution
|
||||||
|
message: 'Use inclusive language. Consider "%s" instead of "%s".'
|
||||||
|
link: https://docs.gitlab.com/ee/development/documentation/styleguide.html#inclusive-language
|
||||||
|
level: suggestion
|
||||||
|
ignorecase: true
|
||||||
|
swap:
|
||||||
|
sanity (?:check|test): check for completeness
|
||||||
|
dummy: placeholder
|
16
doc/.vale/gitlab/InclusionCultural.yml
Normal file
16
doc/.vale/gitlab/InclusionCultural.yml
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
---
|
||||||
|
# Warning: gitlab.InclusionCultural
|
||||||
|
#
|
||||||
|
# Suggests alternatives for words that are culturally inappropriate.
|
||||||
|
#
|
||||||
|
# For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles
|
||||||
|
extends: substitution
|
||||||
|
message: 'Use inclusive language. Consider "%s" instead of "%s".'
|
||||||
|
link: https://docs.gitlab.com/ee/development/documentation/styleguide.html#inclusive-language
|
||||||
|
level: warning
|
||||||
|
ignorecase: true
|
||||||
|
swap:
|
||||||
|
blacklist(?:ed|ing|s)?: denylist
|
||||||
|
whitelist(?:ed|ing|s)?: allowlist
|
||||||
|
master: primary
|
||||||
|
slave: secondary
|
18
doc/.vale/gitlab/InclusionGender.yml
Normal file
18
doc/.vale/gitlab/InclusionGender.yml
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
---
|
||||||
|
# Suggestion: gitlab.InclusionGender
|
||||||
|
#
|
||||||
|
# Suggests alternatives for words that are gender-specific.
|
||||||
|
#
|
||||||
|
# For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles
|
||||||
|
extends: substitution
|
||||||
|
message: 'Use inclusive language. Consider "%s" instead of "%s".'
|
||||||
|
link: https://docs.gitlab.com/ee/development/documentation/styleguide.html#inclusive-language
|
||||||
|
level: suggestion
|
||||||
|
ignorecase: true
|
||||||
|
swap:
|
||||||
|
mankind: humanity, people
|
||||||
|
manpower: GitLab team members
|
||||||
|
he: they
|
||||||
|
his: their
|
||||||
|
she: they
|
||||||
|
hers: their
|
|
@ -12,7 +12,6 @@ level: warning
|
||||||
ignorecase: true
|
ignorecase: true
|
||||||
swap:
|
swap:
|
||||||
admin: administrator
|
admin: administrator
|
||||||
blacklist(ed|ing)?: denylist
|
|
||||||
code base: codebase
|
code base: codebase
|
||||||
config: configuration
|
config: configuration
|
||||||
distro: distribution
|
distro: distribution
|
||||||
|
@ -20,4 +19,3 @@ swap:
|
||||||
filesystem: file system
|
filesystem: file system
|
||||||
info: information
|
info: information
|
||||||
repo: repository
|
repo: repository
|
||||||
whitelist(ed|ing)?: allowlist
|
|
||||||
|
|
|
@ -424,17 +424,17 @@ server (with `gitaly_address`) unless you setup with special
|
||||||
storages:
|
storages:
|
||||||
default:
|
default:
|
||||||
gitaly_address: tcp://gitaly1.internal:8075
|
gitaly_address: tcp://gitaly1.internal:8075
|
||||||
path: /some/dummy/path
|
path: /some/local/path
|
||||||
storage1:
|
storage1:
|
||||||
gitaly_address: tcp://gitaly1.internal:8075
|
gitaly_address: tcp://gitaly1.internal:8075
|
||||||
path: /some/dummy/path
|
path: /some/local/path
|
||||||
storage2:
|
storage2:
|
||||||
gitaly_address: tcp://gitaly2.internal:8075
|
gitaly_address: tcp://gitaly2.internal:8075
|
||||||
path: /some/dummy/path
|
path: /some/local/path
|
||||||
```
|
```
|
||||||
|
|
||||||
NOTE: **Note:**
|
NOTE: **Note:**
|
||||||
`/some/dummy/path` should be set to a local folder that exists, however no data will be stored in
|
`/some/local/path` should be set to a local folder that exists, however no data will be stored in
|
||||||
this folder. This will no longer be necessary after
|
this folder. This will no longer be necessary after
|
||||||
[this issue](https://gitlab.com/gitlab-org/gitaly/-/issues/1282) is resolved.
|
[this issue](https://gitlab.com/gitlab-org/gitaly/-/issues/1282) is resolved.
|
||||||
|
|
||||||
|
@ -627,17 +627,17 @@ To configure Gitaly with TLS:
|
||||||
storages:
|
storages:
|
||||||
default:
|
default:
|
||||||
gitaly_address: tls://gitaly1.internal:9999
|
gitaly_address: tls://gitaly1.internal:9999
|
||||||
path: /some/dummy/path
|
path: /some/local/path
|
||||||
storage1:
|
storage1:
|
||||||
gitaly_address: tls://gitaly1.internal:9999
|
gitaly_address: tls://gitaly1.internal:9999
|
||||||
path: /some/dummy/path
|
path: /some/local/path
|
||||||
storage2:
|
storage2:
|
||||||
gitaly_address: tls://gitaly2.internal:9999
|
gitaly_address: tls://gitaly2.internal:9999
|
||||||
path: /some/dummy/path
|
path: /some/local/path
|
||||||
```
|
```
|
||||||
|
|
||||||
NOTE: **Note:**
|
NOTE: **Note:**
|
||||||
`/some/dummy/path` should be set to a local folder that exists, however no data will be stored
|
`/some/local/path` should be set to a local folder that exists, however no data will be stored
|
||||||
in this folder. This will no longer be necessary after
|
in this folder. This will no longer be necessary after
|
||||||
[Gitaly issue #1282](https://gitlab.com/gitlab-org/gitaly/-/issues/1282) is resolved.
|
[Gitaly issue #1282](https://gitlab.com/gitlab-org/gitaly/-/issues/1282) is resolved.
|
||||||
|
|
||||||
|
|
|
@ -547,14 +547,14 @@ To configure Praefect with TLS:
|
||||||
storages:
|
storages:
|
||||||
default:
|
default:
|
||||||
gitaly_address: tls://praefect1.internal:3305
|
gitaly_address: tls://praefect1.internal:3305
|
||||||
path: /some/dummy/path
|
path: /some/local/path
|
||||||
storage1:
|
storage1:
|
||||||
gitaly_address: tls://praefect2.internal:3305
|
gitaly_address: tls://praefect2.internal:3305
|
||||||
path: /some/dummy/path
|
path: /some/local/path
|
||||||
```
|
```
|
||||||
|
|
||||||
NOTE: **Note:**
|
NOTE: **Note:**
|
||||||
`/some/dummy/path` should be set to a local folder that exists, however no
|
`/some/local/path` should be set to a local folder that exists, however no
|
||||||
data will be stored in this folder. This will no longer be necessary after
|
data will be stored in this folder. This will no longer be necessary after
|
||||||
[this issue](https://gitlab.com/gitlab-org/gitaly/-/issues/1282) is resolved.
|
[this issue](https://gitlab.com/gitlab-org/gitaly/-/issues/1282) is resolved.
|
||||||
|
|
||||||
|
|
|
@ -71,7 +71,7 @@ The instructions make the assumption that you will be using the email address `i
|
||||||
sudo postfix start
|
sudo postfix start
|
||||||
```
|
```
|
||||||
|
|
||||||
1. Send the new `incoming` user a dummy email to test SMTP, by entering the following into the SMTP prompt:
|
1. Send the new `incoming` user an email to test SMTP, by entering the following into the SMTP prompt:
|
||||||
|
|
||||||
```plaintext
|
```plaintext
|
||||||
ehlo localhost
|
ehlo localhost
|
||||||
|
@ -251,7 +251,7 @@ Courier, which we will install later to add IMAP authentication, requires mailbo
|
||||||
|
|
||||||
If you get a `Connection refused` error instead, make sure your firewall is set up to allow inbound traffic on port 25.
|
If you get a `Connection refused` error instead, make sure your firewall is set up to allow inbound traffic on port 25.
|
||||||
|
|
||||||
1. Send the `incoming` user a dummy email to test SMTP, by entering the following into the SMTP prompt:
|
1. Send the `incoming` user an email to test SMTP, by entering the following into the SMTP prompt:
|
||||||
|
|
||||||
```plaintext
|
```plaintext
|
||||||
ehlo gitlab.example.com
|
ehlo gitlab.example.com
|
||||||
|
|
|
@ -6398,7 +6398,7 @@ type GeoNode {
|
||||||
name: String
|
name: String
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Package file registries of the GeoNode. Available only when feature flag `geo_package_file_replication` is enabled
|
Package file registries of the GeoNode
|
||||||
"""
|
"""
|
||||||
packageFileRegistries(
|
packageFileRegistries(
|
||||||
"""
|
"""
|
||||||
|
@ -6508,6 +6508,37 @@ type GeoNode {
|
||||||
last: Int
|
last: Int
|
||||||
): TerraformStateRegistryConnection
|
): TerraformStateRegistryConnection
|
||||||
|
|
||||||
|
"""
|
||||||
|
Find terraform state version registries on this Geo node. Available only when
|
||||||
|
feature flag `geo_terraform_state_version_replication` is enabled
|
||||||
|
"""
|
||||||
|
terraformStateVersionRegistries(
|
||||||
|
"""
|
||||||
|
Returns the elements in the list that come after the specified cursor.
|
||||||
|
"""
|
||||||
|
after: String
|
||||||
|
|
||||||
|
"""
|
||||||
|
Returns the elements in the list that come before the specified cursor.
|
||||||
|
"""
|
||||||
|
before: String
|
||||||
|
|
||||||
|
"""
|
||||||
|
Returns the first _n_ elements from the list.
|
||||||
|
"""
|
||||||
|
first: Int
|
||||||
|
|
||||||
|
"""
|
||||||
|
Filters registries by their ID
|
||||||
|
"""
|
||||||
|
ids: [ID!]
|
||||||
|
|
||||||
|
"""
|
||||||
|
Returns the last _n_ elements from the list.
|
||||||
|
"""
|
||||||
|
last: Int
|
||||||
|
): TerraformStateVersionRegistryConnection
|
||||||
|
|
||||||
"""
|
"""
|
||||||
The user-facing URL for this Geo node
|
The user-facing URL for this Geo node
|
||||||
"""
|
"""
|
||||||
|
@ -16923,6 +16954,86 @@ type TerraformStateRegistryEdge {
|
||||||
node: TerraformStateRegistry
|
node: TerraformStateRegistry
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
Represents the Geo sync and verification state of a terraform state version
|
||||||
|
"""
|
||||||
|
type TerraformStateVersionRegistry {
|
||||||
|
"""
|
||||||
|
Timestamp when the TerraformStateVersionRegistry was created
|
||||||
|
"""
|
||||||
|
createdAt: Time
|
||||||
|
|
||||||
|
"""
|
||||||
|
ID of the TerraformStateVersionRegistry
|
||||||
|
"""
|
||||||
|
id: ID!
|
||||||
|
|
||||||
|
"""
|
||||||
|
Error message during sync of the TerraformStateVersionRegistry
|
||||||
|
"""
|
||||||
|
lastSyncFailure: String
|
||||||
|
|
||||||
|
"""
|
||||||
|
Timestamp of the most recent successful sync of the TerraformStateVersionRegistry
|
||||||
|
"""
|
||||||
|
lastSyncedAt: Time
|
||||||
|
|
||||||
|
"""
|
||||||
|
Timestamp after which the TerraformStateVersionRegistry should be resynced
|
||||||
|
"""
|
||||||
|
retryAt: Time
|
||||||
|
|
||||||
|
"""
|
||||||
|
Number of consecutive failed sync attempts of the TerraformStateVersionRegistry
|
||||||
|
"""
|
||||||
|
retryCount: Int
|
||||||
|
|
||||||
|
"""
|
||||||
|
Sync state of the TerraformStateVersionRegistry
|
||||||
|
"""
|
||||||
|
state: RegistryState
|
||||||
|
|
||||||
|
"""
|
||||||
|
ID of the terraform state version
|
||||||
|
"""
|
||||||
|
terraformStateVersionId: ID!
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
The connection type for TerraformStateVersionRegistry.
|
||||||
|
"""
|
||||||
|
type TerraformStateVersionRegistryConnection {
|
||||||
|
"""
|
||||||
|
A list of edges.
|
||||||
|
"""
|
||||||
|
edges: [TerraformStateVersionRegistryEdge]
|
||||||
|
|
||||||
|
"""
|
||||||
|
A list of nodes.
|
||||||
|
"""
|
||||||
|
nodes: [TerraformStateVersionRegistry]
|
||||||
|
|
||||||
|
"""
|
||||||
|
Information to aid in pagination.
|
||||||
|
"""
|
||||||
|
pageInfo: PageInfo!
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
An edge in a connection.
|
||||||
|
"""
|
||||||
|
type TerraformStateVersionRegistryEdge {
|
||||||
|
"""
|
||||||
|
A cursor for use in pagination.
|
||||||
|
"""
|
||||||
|
cursor: String!
|
||||||
|
|
||||||
|
"""
|
||||||
|
The item at the end of the edge.
|
||||||
|
"""
|
||||||
|
node: TerraformStateVersionRegistry
|
||||||
|
}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Represents a requirement test report
|
Represents a requirement test report
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -17863,7 +17863,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "packageFileRegistries",
|
"name": "packageFileRegistries",
|
||||||
"description": "Package file registries of the GeoNode. Available only when feature flag `geo_package_file_replication` is enabled",
|
"description": "Package file registries of the GeoNode",
|
||||||
"args": [
|
"args": [
|
||||||
{
|
{
|
||||||
"name": "ids",
|
"name": "ids",
|
||||||
|
@ -18134,6 +18134,77 @@
|
||||||
"isDeprecated": false,
|
"isDeprecated": false,
|
||||||
"deprecationReason": null
|
"deprecationReason": null
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "terraformStateVersionRegistries",
|
||||||
|
"description": "Find terraform state version registries on this Geo node. Available only when feature flag `geo_terraform_state_version_replication` is enabled",
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"name": "ids",
|
||||||
|
"description": "Filters registries by their ID",
|
||||||
|
"type": {
|
||||||
|
"kind": "LIST",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "ID",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"defaultValue": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "after",
|
||||||
|
"description": "Returns the elements in the list that come after the specified cursor.",
|
||||||
|
"type": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "String",
|
||||||
|
"ofType": null
|
||||||
|
},
|
||||||
|
"defaultValue": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "before",
|
||||||
|
"description": "Returns the elements in the list that come before the specified cursor.",
|
||||||
|
"type": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "String",
|
||||||
|
"ofType": null
|
||||||
|
},
|
||||||
|
"defaultValue": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "first",
|
||||||
|
"description": "Returns the first _n_ elements from the list.",
|
||||||
|
"type": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "Int",
|
||||||
|
"ofType": null
|
||||||
|
},
|
||||||
|
"defaultValue": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "last",
|
||||||
|
"description": "Returns the last _n_ elements from the list.",
|
||||||
|
"type": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "Int",
|
||||||
|
"ofType": null
|
||||||
|
},
|
||||||
|
"defaultValue": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": {
|
||||||
|
"kind": "OBJECT",
|
||||||
|
"name": "TerraformStateVersionRegistryConnection",
|
||||||
|
"ofType": null
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "url",
|
"name": "url",
|
||||||
"description": "The user-facing URL for this Geo node",
|
"description": "The user-facing URL for this Geo node",
|
||||||
|
@ -49587,6 +49658,251 @@
|
||||||
"enumValues": null,
|
"enumValues": null,
|
||||||
"possibleTypes": null
|
"possibleTypes": null
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"kind": "OBJECT",
|
||||||
|
"name": "TerraformStateVersionRegistry",
|
||||||
|
"description": "Represents the Geo sync and verification state of a terraform state version",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "createdAt",
|
||||||
|
"description": "Timestamp when the TerraformStateVersionRegistry was created",
|
||||||
|
"args": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"type": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "Time",
|
||||||
|
"ofType": null
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"description": "ID of the TerraformStateVersionRegistry",
|
||||||
|
"args": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "ID",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "lastSyncFailure",
|
||||||
|
"description": "Error message during sync of the TerraformStateVersionRegistry",
|
||||||
|
"args": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"type": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "String",
|
||||||
|
"ofType": null
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "lastSyncedAt",
|
||||||
|
"description": "Timestamp of the most recent successful sync of the TerraformStateVersionRegistry",
|
||||||
|
"args": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"type": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "Time",
|
||||||
|
"ofType": null
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "retryAt",
|
||||||
|
"description": "Timestamp after which the TerraformStateVersionRegistry should be resynced",
|
||||||
|
"args": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"type": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "Time",
|
||||||
|
"ofType": null
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "retryCount",
|
||||||
|
"description": "Number of consecutive failed sync attempts of the TerraformStateVersionRegistry",
|
||||||
|
"args": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"type": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "Int",
|
||||||
|
"ofType": null
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "state",
|
||||||
|
"description": "Sync state of the TerraformStateVersionRegistry",
|
||||||
|
"args": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"type": {
|
||||||
|
"kind": "ENUM",
|
||||||
|
"name": "RegistryState",
|
||||||
|
"ofType": null
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "terraformStateVersionId",
|
||||||
|
"description": "ID of the terraform state version",
|
||||||
|
"args": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "ID",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"inputFields": null,
|
||||||
|
"interfaces": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"enumValues": null,
|
||||||
|
"possibleTypes": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "OBJECT",
|
||||||
|
"name": "TerraformStateVersionRegistryConnection",
|
||||||
|
"description": "The connection type for TerraformStateVersionRegistry.",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "edges",
|
||||||
|
"description": "A list of edges.",
|
||||||
|
"args": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"type": {
|
||||||
|
"kind": "LIST",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "OBJECT",
|
||||||
|
"name": "TerraformStateVersionRegistryEdge",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nodes",
|
||||||
|
"description": "A list of nodes.",
|
||||||
|
"args": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"type": {
|
||||||
|
"kind": "LIST",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "OBJECT",
|
||||||
|
"name": "TerraformStateVersionRegistry",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "pageInfo",
|
||||||
|
"description": "Information to aid in pagination.",
|
||||||
|
"args": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "OBJECT",
|
||||||
|
"name": "PageInfo",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"inputFields": null,
|
||||||
|
"interfaces": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"enumValues": null,
|
||||||
|
"possibleTypes": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "OBJECT",
|
||||||
|
"name": "TerraformStateVersionRegistryEdge",
|
||||||
|
"description": "An edge in a connection.",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "cursor",
|
||||||
|
"description": "A cursor for use in pagination.",
|
||||||
|
"args": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "String",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "node",
|
||||||
|
"description": "The item at the end of the edge.",
|
||||||
|
"args": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"type": {
|
||||||
|
"kind": "OBJECT",
|
||||||
|
"name": "TerraformStateVersionRegistry",
|
||||||
|
"ofType": null
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"inputFields": null,
|
||||||
|
"interfaces": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"enumValues": null,
|
||||||
|
"possibleTypes": null
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"kind": "OBJECT",
|
"kind": "OBJECT",
|
||||||
"name": "TestReport",
|
"name": "TestReport",
|
||||||
|
|
|
@ -2409,6 +2409,21 @@ Represents the sync and verification state of a terraform state.
|
||||||
| `state` | RegistryState | Sync state of the TerraformStateRegistry |
|
| `state` | RegistryState | Sync state of the TerraformStateRegistry |
|
||||||
| `terraformStateId` | ID! | ID of the TerraformState |
|
| `terraformStateId` | ID! | ID of the TerraformState |
|
||||||
|
|
||||||
|
### TerraformStateVersionRegistry
|
||||||
|
|
||||||
|
Represents the Geo sync and verification state of a terraform state version.
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
| ----- | ---- | ----------- |
|
||||||
|
| `createdAt` | Time | Timestamp when the TerraformStateVersionRegistry was created |
|
||||||
|
| `id` | ID! | ID of the TerraformStateVersionRegistry |
|
||||||
|
| `lastSyncFailure` | String | Error message during sync of the TerraformStateVersionRegistry |
|
||||||
|
| `lastSyncedAt` | Time | Timestamp of the most recent successful sync of the TerraformStateVersionRegistry |
|
||||||
|
| `retryAt` | Time | Timestamp after which the TerraformStateVersionRegistry should be resynced |
|
||||||
|
| `retryCount` | Int | Number of consecutive failed sync attempts of the TerraformStateVersionRegistry |
|
||||||
|
| `state` | RegistryState | Sync state of the TerraformStateVersionRegistry |
|
||||||
|
| `terraformStateVersionId` | ID! | ID of the terraform state version |
|
||||||
|
|
||||||
### TestReport
|
### TestReport
|
||||||
|
|
||||||
Represents a requirement test report.
|
Represents a requirement test report.
|
||||||
|
|
|
@ -663,10 +663,10 @@ Example response:
|
||||||
"weight": null,
|
"weight": null,
|
||||||
"has_tasks": false,
|
"has_tasks": false,
|
||||||
"_links": {
|
"_links": {
|
||||||
"self": "http://gitlab.dummy:3000/api/v4/projects/1/issues/1",
|
"self": "http://gitlab.example:3000/api/v4/projects/1/issues/1",
|
||||||
"notes": "http://gitlab.dummy:3000/api/v4/projects/1/issues/1/notes",
|
"notes": "http://gitlab.example:3000/api/v4/projects/1/issues/1/notes",
|
||||||
"award_emoji": "http://gitlab.dummy:3000/api/v4/projects/1/issues/1/award_emoji",
|
"award_emoji": "http://gitlab.example:3000/api/v4/projects/1/issues/1/award_emoji",
|
||||||
"project": "http://gitlab.dummy:3000/api/v4/projects/1"
|
"project": "http://gitlab.example:3000/api/v4/projects/1"
|
||||||
},
|
},
|
||||||
"references": {
|
"references": {
|
||||||
"short": "#1",
|
"short": "#1",
|
||||||
|
|
|
@ -19,7 +19,7 @@ If you just want to delete everything and start over with an empty DB (approxima
|
||||||
bundle exec rake db:reset RAILS_ENV=development
|
bundle exec rake db:reset RAILS_ENV=development
|
||||||
```
|
```
|
||||||
|
|
||||||
If you just want to delete everything and start over with dummy data (approximately 4 minutes). This
|
If you just want to delete everything and start over with sample data (approximately 4 minutes). This
|
||||||
also does `db:reset` and runs DB-specific migrations:
|
also does `db:reset` and runs DB-specific migrations:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
|
|
|
@ -418,8 +418,11 @@ We strive to create documentation that is inclusive. This section includes
|
||||||
guidance and examples in the following categories:
|
guidance and examples in the following categories:
|
||||||
|
|
||||||
- [Gender-specific wording](#avoid-gender-specific-wording).
|
- [Gender-specific wording](#avoid-gender-specific-wording).
|
||||||
|
(Tested in [`InclusionGender.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/.vale/gitlab/InclusionGender.yml).)
|
||||||
- [Ableist language](#avoid-ableist-language).
|
- [Ableist language](#avoid-ableist-language).
|
||||||
|
(Tested in [`InclusionAbleism.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/.vale/gitlab/InclusionAbleism.yml).)
|
||||||
- [Cultural sensitivity](#culturally-sensitive-language).
|
- [Cultural sensitivity](#culturally-sensitive-language).
|
||||||
|
(Tested in [`InclusionCultural.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/.vale/gitlab/InclusionCultural.yml).)
|
||||||
|
|
||||||
We write our developer documentation with inclusivity and diversity in mind. This
|
We write our developer documentation with inclusivity and diversity in mind. This
|
||||||
page is not an exhaustive reference, but describes some general guidelines and
|
page is not an exhaustive reference, but describes some general guidelines and
|
||||||
|
@ -433,11 +436,13 @@ a gender-neutral pronoun.
|
||||||
|
|
||||||
Avoid the use of gender-specific pronouns, unless referring to a specific person.
|
Avoid the use of gender-specific pronouns, unless referring to a specific person.
|
||||||
|
|
||||||
|
<!-- vale gitlab.InclusionGender = NO -->
|
||||||
| Use | Avoid |
|
| Use | Avoid |
|
||||||
|-----------------------------------|---------------------------------|
|
|-----------------------------------|---------------------------------|
|
||||||
| People, humanity | Mankind |
|
| People, humanity | Mankind |
|
||||||
| GitLab Team Members | Manpower |
|
| GitLab Team Members | Manpower |
|
||||||
| You can install; They can install | He can install; She can install |
|
| You can install; They can install | He can install; She can install |
|
||||||
|
<!-- vale gitlab.InclusionGender = YES -->
|
||||||
|
|
||||||
If you need to set up [Fake user information](#fake-user-information), use
|
If you need to set up [Fake user information](#fake-user-information), use
|
||||||
diverse or non-gendered names with common surnames.
|
diverse or non-gendered names with common surnames.
|
||||||
|
@ -446,6 +451,7 @@ diverse or non-gendered names with common surnames.
|
||||||
|
|
||||||
Avoid terms that are also used in negative stereotypes for different groups.
|
Avoid terms that are also used in negative stereotypes for different groups.
|
||||||
|
|
||||||
|
<!-- vale gitlab.InclusionAbleism = NO -->
|
||||||
| Use | Avoid |
|
| Use | Avoid |
|
||||||
|------------------------|----------------------|
|
|------------------------|----------------------|
|
||||||
| Check for completeness | Sanity check |
|
| Check for completeness | Sanity check |
|
||||||
|
@ -454,6 +460,7 @@ Avoid terms that are also used in negative stereotypes for different groups.
|
||||||
| Placeholder variable | Dummy variable |
|
| Placeholder variable | Dummy variable |
|
||||||
| Active/Inactive | Enabled/Disabled |
|
| Active/Inactive | Enabled/Disabled |
|
||||||
| On/Off | Enabled/Disabled |
|
| On/Off | Enabled/Disabled |
|
||||||
|
<!-- vale gitlab.InclusionAbleism = YES -->
|
||||||
|
|
||||||
Credit: [Avoid ableist language](https://developers.google.com/style/inclusive-documentation#ableist-language)
|
Credit: [Avoid ableist language](https://developers.google.com/style/inclusive-documentation#ableist-language)
|
||||||
in the Google Developer Style Guide.
|
in the Google Developer Style Guide.
|
||||||
|
@ -464,10 +471,12 @@ Avoid terms that reflect negative cultural stereotypes and history. In most
|
||||||
cases, you can replace terms such as `master` and `slave` with terms that are
|
cases, you can replace terms such as `master` and `slave` with terms that are
|
||||||
more precise and functional, such as `primary` and `secondary`.
|
more precise and functional, such as `primary` and `secondary`.
|
||||||
|
|
||||||
|
<!-- vale gitlab.InclusionCultural = NO -->
|
||||||
| Use | Avoid |
|
| Use | Avoid |
|
||||||
|----------------------|-----------------------|
|
|----------------------|-----------------------|
|
||||||
| Primary / secondary | Master / slave |
|
| Primary / secondary | Master / slave |
|
||||||
| Allowlist / denylist | Blacklist / whitelist |
|
| Allowlist / denylist | Blacklist / whitelist |
|
||||||
|
<!-- vale gitlab.InclusionCultural = YES -->
|
||||||
|
|
||||||
For more information see the following [Internet Draft specification](https://tools.ietf.org/html/draft-knodel-terminology-02).
|
For more information see the following [Internet Draft specification](https://tools.ietf.org/html/draft-knodel-terminology-02).
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ Please note that [S/MIME signed](../administration/smime_signing_email.md) email
|
||||||
## Mailer previews
|
## Mailer previews
|
||||||
|
|
||||||
Rails provides a way to preview our mailer templates in HTML and plaintext using
|
Rails provides a way to preview our mailer templates in HTML and plaintext using
|
||||||
dummy data.
|
sample data.
|
||||||
|
|
||||||
The previews live in [`app/mailers/previews`](https://gitlab.com/gitlab-org/gitlab-foss/tree/master/app/mailers/previews) and can be viewed at
|
The previews live in [`app/mailers/previews`](https://gitlab.com/gitlab-org/gitlab-foss/tree/master/app/mailers/previews) and can be viewed at
|
||||||
[`/rails/mailers`](http://localhost:3000/rails/mailers).
|
[`/rails/mailers`](http://localhost:3000/rails/mailers).
|
||||||
|
|
|
@ -97,7 +97,7 @@ The following options are available.
|
||||||
| Restrict by commit message (negative match)| **Starter** 11.1 | Only commit messages that do not match this regular expression are allowed to be pushed. Leave empty to allow any commit message. Uses multiline mode, which can be disabled using `(?-m)`. |
|
| Restrict by commit message (negative match)| **Starter** 11.1 | Only commit messages that do not match this regular expression are allowed to be pushed. Leave empty to allow any commit message. Uses multiline mode, which can be disabled using `(?-m)`. |
|
||||||
| Restrict by branch name | **Starter** 9.3 | Only branch names that match this regular expression are allowed to be pushed. Leave empty to allow any branch name. |
|
| Restrict by branch name | **Starter** 9.3 | Only branch names that match this regular expression are allowed to be pushed. Leave empty to allow any branch name. |
|
||||||
| Restrict by commit author's email | **Starter** 7.10 | Only commit author's email that match this regular expression are allowed to be pushed. Leave empty to allow any email. |
|
| Restrict by commit author's email | **Starter** 7.10 | Only commit author's email that match this regular expression are allowed to be pushed. Leave empty to allow any email. |
|
||||||
| Prohibited file names | **Starter** 7.10 | Any committed filenames that match this regular expression are not allowed to be pushed. Leave empty to allow any filenames. |
|
| Prohibited file names | **Starter** 7.10 | Any committed filenames that match this regular expression and do not already exist in the repository are not allowed to be pushed. Leave empty to allow any filenames. See [common examples](#prohibited-file-names). |
|
||||||
| Maximum file size | **Starter** 7.12 | Pushes that contain added or updated files that exceed this file size (in MB) are rejected. Set to 0 to allow files of any size. Files tracked by Git LFS are exempted. |
|
| Maximum file size | **Starter** 7.12 | Pushes that contain added or updated files that exceed this file size (in MB) are rejected. Set to 0 to allow files of any size. Files tracked by Git LFS are exempted. |
|
||||||
|
|
||||||
TIP: **Tip:**
|
TIP: **Tip:**
|
||||||
|
@ -178,6 +178,44 @@ pry.history
|
||||||
bash_history
|
bash_history
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Prohibited file names
|
||||||
|
|
||||||
|
> Introduced in [GitLab Starter](https://about.gitlab.com/pricing/) 7.10.
|
||||||
|
|
||||||
|
Each file name contained in a Git push is compared to the regular expression in this field. Filenames in Git consist of both the file's name and any directory that may precede it. A singular regular expression can contain multiple independent matches used as exclusions. File names can be broadly matched to any location in the repository, or restricted to specific locations. Filenames can also be partial matches used to exclude file types by extension.
|
||||||
|
|
||||||
|
The following examples make use of regex string boundary characters which match the beginning of a string (`^`), and the end (`$`). They also include instances where either the directory path or the filename can include `.` or `/`. Both of these special regex characters have to be escaped with a backslash `\` to be used as normal characters in a match condition.
|
||||||
|
|
||||||
|
Example: prevent pushing any `.exe` files to any location in the repository. This is an example of a partial match, which can match any filename that contains `.exe` at the end:
|
||||||
|
|
||||||
|
```plaintext
|
||||||
|
\.exe$
|
||||||
|
```
|
||||||
|
|
||||||
|
Example: prevent a specific configuration file in the repository root from being pushed:
|
||||||
|
|
||||||
|
```plaintext
|
||||||
|
^config\.yml$
|
||||||
|
```
|
||||||
|
|
||||||
|
Example: prevent a specific configuration file in a known directory from being pushed:
|
||||||
|
|
||||||
|
```plaintext
|
||||||
|
^directory-name\/config\.yml$
|
||||||
|
```
|
||||||
|
|
||||||
|
Example: prevent the specific file named `install.exe` from being pushed to any location in the repository. Note that the parenthesized expression `(^|\/)` will match either a file following a directory separator or a file in the root directory of the repository:
|
||||||
|
|
||||||
|
```plaintext
|
||||||
|
(^|\/)install\.exe$
|
||||||
|
```
|
||||||
|
|
||||||
|
Example: combining all of the above in a single expression. Note that all of the preceding expressions rely on the end of string character `$`, so we can move that part of each expression to the end of the grouped collection of match conditions where it will be appended to all matches:
|
||||||
|
|
||||||
|
```plaintext
|
||||||
|
(\.exe|^config\.yml|^directory-name\/config\.yml|(^|\/)install\.exe)$
|
||||||
|
```
|
||||||
|
|
||||||
<!-- ## Troubleshooting
|
<!-- ## Troubleshooting
|
||||||
|
|
||||||
Include any troubleshooting steps that you can foresee. If you know beforehand what issues
|
Include any troubleshooting steps that you can foresee. If you know beforehand what issues
|
||||||
|
|
|
@ -12,7 +12,7 @@ more repositories, by importing an SSH public key to your GitLab instance.
|
||||||
|
|
||||||
This is useful for cloning repositories to your Continuous
|
This is useful for cloning repositories to your Continuous
|
||||||
Integration (CI) server. By using deploy keys, you don't have to set up a
|
Integration (CI) server. By using deploy keys, you don't have to set up a
|
||||||
dummy user account.
|
fake user account.
|
||||||
|
|
||||||
There are two types of deploy keys:
|
There are two types of deploy keys:
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
.deploy_to_ecs:
|
.deploy_to_ecs:
|
||||||
image: 'registry.gitlab.com/gitlab-org/cloud-deploy/aws-ecs:latest'
|
image: 'registry.gitlab.com/gitlab-org/cloud-deploy/aws-ecs:latest'
|
||||||
|
dependencies: []
|
||||||
script:
|
script:
|
||||||
- ecs update-task-definition
|
- ecs update-task-definition
|
||||||
|
|
||||||
|
|
|
@ -9066,10 +9066,10 @@ msgstr ""
|
||||||
msgid "Dismiss Merge Request promotion"
|
msgid "Dismiss Merge Request promotion"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Dismiss Selected"
|
msgid "Dismiss Value Stream Analytics introduction box"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Dismiss Value Stream Analytics introduction box"
|
msgid "Dismiss selected"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Dismiss trial promotion"
|
msgid "Dismiss trial promotion"
|
||||||
|
@ -17931,6 +17931,9 @@ msgstr ""
|
||||||
msgid "Origin"
|
msgid "Origin"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Orphaned member"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Other Labels"
|
msgid "Other Labels"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
|
@ -103,6 +103,8 @@ module QA
|
||||||
end
|
end
|
||||||
|
|
||||||
def click_commit(commit_msg)
|
def click_commit(commit_msg)
|
||||||
|
wait_for_requests
|
||||||
|
|
||||||
within_element(:file_tree_table) do
|
within_element(:file_tree_table) do
|
||||||
click_on commit_msg
|
click_on commit_msg
|
||||||
end
|
end
|
||||||
|
|
|
@ -14,12 +14,8 @@ useMockIntersectionObserver();
|
||||||
jest.mock('~/lib/utils/poll');
|
jest.mock('~/lib/utils/poll');
|
||||||
|
|
||||||
const setupHTML = initialData => {
|
const setupHTML = initialData => {
|
||||||
document.body.innerHTML = `
|
document.body.innerHTML = `<div id="js-issuable-app"></div>`;
|
||||||
<div id="js-issuable-app"></div>
|
document.getElementById('js-issuable-app').dataset.initial = JSON.stringify(initialData);
|
||||||
<script id="js-issuable-app-initial-data" type="application/json">
|
|
||||||
${JSON.stringify(initialData)}
|
|
||||||
</script>
|
|
||||||
`;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('Issue show index', () => {
|
describe('Issue show index', () => {
|
||||||
|
|
|
@ -19,9 +19,8 @@ describe('RelatedMergeRequests', () => {
|
||||||
mockData = getJSONFixture(FIXTURE_PATH);
|
mockData = getJSONFixture(FIXTURE_PATH);
|
||||||
|
|
||||||
// put the fixture in DOM as the component expects
|
// put the fixture in DOM as the component expects
|
||||||
document.body.innerHTML = `<div id="js-issuable-app-initial-data">${JSON.stringify(
|
document.body.innerHTML = `<div id="js-issuable-app"></div>`;
|
||||||
mockData,
|
document.getElementById('js-issuable-app').dataset.initial = JSON.stringify(mockData);
|
||||||
)}</div>`;
|
|
||||||
|
|
||||||
mock = new MockAdapter(axios);
|
mock = new MockAdapter(axios);
|
||||||
mock.onGet(`${API_ENDPOINT}?per_page=100`).reply(200, mockData, { 'x-total': 2 });
|
mock.onGet(`${API_ENDPOINT}?per_page=100`).reply(200, mockData, { 'x-total': 2 });
|
||||||
|
|
|
@ -15,13 +15,16 @@ exports[`self monitor component When the self monitor project has not been creat
|
||||||
|
|
||||||
</h4>
|
</h4>
|
||||||
|
|
||||||
<gl-deprecated-button-stub
|
<gl-button-stub
|
||||||
|
buttontextclasses=""
|
||||||
|
category="primary"
|
||||||
class="js-settings-toggle"
|
class="js-settings-toggle"
|
||||||
size="md"
|
icon=""
|
||||||
variant="secondary"
|
size="medium"
|
||||||
|
variant="default"
|
||||||
>
|
>
|
||||||
Expand
|
Expand
|
||||||
</gl-deprecated-button-stub>
|
</gl-button-stub>
|
||||||
|
|
||||||
<p
|
<p
|
||||||
class="js-section-sub-header"
|
class="js-section-sub-header"
|
||||||
|
@ -56,6 +59,7 @@ exports[`self monitor component When the self monitor project has not been creat
|
||||||
|
|
||||||
<gl-modal-stub
|
<gl-modal-stub
|
||||||
cancel-title="Cancel"
|
cancel-title="Cancel"
|
||||||
|
category="primary"
|
||||||
modalclass=""
|
modalclass=""
|
||||||
modalid="delete-self-monitor-modal"
|
modalid="delete-self-monitor-modal"
|
||||||
ok-title="Delete project"
|
ok-title="Delete project"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { shallowMount } from '@vue/test-utils';
|
import { shallowMount } from '@vue/test-utils';
|
||||||
import { GlDeprecatedButton } from '@gitlab/ui';
|
import { GlButton } from '@gitlab/ui';
|
||||||
import { TEST_HOST } from 'helpers/test_constants';
|
import { TEST_HOST } from 'helpers/test_constants';
|
||||||
import SelfMonitor from '~/self_monitor/components/self_monitor_form.vue';
|
import SelfMonitor from '~/self_monitor/components/self_monitor_form.vue';
|
||||||
import { createStore } from '~/self_monitor/store';
|
import { createStore } from '~/self_monitor/store';
|
||||||
|
@ -42,7 +42,7 @@ describe('self monitor component', () => {
|
||||||
it('renders as an expand button by default', () => {
|
it('renders as an expand button by default', () => {
|
||||||
wrapper = shallowMount(SelfMonitor, { store });
|
wrapper = shallowMount(SelfMonitor, { store });
|
||||||
|
|
||||||
const button = wrapper.find(GlDeprecatedButton);
|
const button = wrapper.find(GlButton);
|
||||||
|
|
||||||
expect(button.text()).toBe('Expand');
|
expect(button.text()).toBe('Expand');
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
import { mount, createWrapper } from '@vue/test-utils';
|
||||||
|
import { getByText as getByTextHelper } from '@testing-library/dom';
|
||||||
|
import { GlAvatarLink } from '@gitlab/ui';
|
||||||
|
import { group as member } from '../mock_data';
|
||||||
|
import GroupAvatar from '~/vue_shared/components/members/avatars/group_avatar.vue';
|
||||||
|
|
||||||
|
describe('MemberList', () => {
|
||||||
|
let wrapper;
|
||||||
|
|
||||||
|
const group = member.sharedWithGroup;
|
||||||
|
|
||||||
|
const createComponent = (propsData = {}) => {
|
||||||
|
wrapper = mount(GroupAvatar, {
|
||||||
|
propsData: {
|
||||||
|
member,
|
||||||
|
...propsData,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const getByText = (text, options) =>
|
||||||
|
createWrapper(getByTextHelper(wrapper.element, text, options));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
createComponent();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
wrapper.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders link to group', () => {
|
||||||
|
const link = wrapper.find(GlAvatarLink);
|
||||||
|
|
||||||
|
expect(link.exists()).toBe(true);
|
||||||
|
expect(link.attributes('href')).toBe(group.webUrl);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders group's full name", () => {
|
||||||
|
expect(getByText(group.fullName).exists()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders group's avatar", () => {
|
||||||
|
expect(wrapper.find('img').attributes('src')).toBe(group.avatarUrl);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,38 @@
|
||||||
|
import { mount, createWrapper } from '@vue/test-utils';
|
||||||
|
import { getByText as getByTextHelper } from '@testing-library/dom';
|
||||||
|
import { invite as member } from '../mock_data';
|
||||||
|
import InviteAvatar from '~/vue_shared/components/members/avatars/invite_avatar.vue';
|
||||||
|
|
||||||
|
describe('MemberList', () => {
|
||||||
|
let wrapper;
|
||||||
|
|
||||||
|
const { invite } = member;
|
||||||
|
|
||||||
|
const createComponent = (propsData = {}) => {
|
||||||
|
wrapper = mount(InviteAvatar, {
|
||||||
|
propsData: {
|
||||||
|
member,
|
||||||
|
...propsData,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const getByText = (text, options) =>
|
||||||
|
createWrapper(getByTextHelper(wrapper.element, text, options));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
createComponent();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
wrapper.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders email as name', () => {
|
||||||
|
expect(getByText(invite.email).exists()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders avatar', () => {
|
||||||
|
expect(wrapper.find('img').attributes('src')).toBe(invite.avatarUrl);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,66 @@
|
||||||
|
import { mount, createWrapper } from '@vue/test-utils';
|
||||||
|
import { getByText as getByTextHelper } from '@testing-library/dom';
|
||||||
|
import { GlAvatarLink } from '@gitlab/ui';
|
||||||
|
import { member, orphanedMember } from '../mock_data';
|
||||||
|
import UserAvatar from '~/vue_shared/components/members/avatars/user_avatar.vue';
|
||||||
|
|
||||||
|
describe('MemberList', () => {
|
||||||
|
let wrapper;
|
||||||
|
|
||||||
|
const { user } = member;
|
||||||
|
|
||||||
|
const createComponent = (propsData = {}) => {
|
||||||
|
wrapper = mount(UserAvatar, {
|
||||||
|
propsData: {
|
||||||
|
member,
|
||||||
|
...propsData,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const getByText = (text, options) =>
|
||||||
|
createWrapper(getByTextHelper(wrapper.element, text, options));
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
wrapper.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders link to user's profile", () => {
|
||||||
|
createComponent();
|
||||||
|
|
||||||
|
const link = wrapper.find(GlAvatarLink);
|
||||||
|
|
||||||
|
expect(link.exists()).toBe(true);
|
||||||
|
expect(link.attributes()).toMatchObject({
|
||||||
|
href: user.webUrl,
|
||||||
|
'data-user-id': `${user.id}`,
|
||||||
|
'data-username': user.username,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders user's name", () => {
|
||||||
|
createComponent();
|
||||||
|
|
||||||
|
expect(getByText(user.name).exists()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders user's username", () => {
|
||||||
|
createComponent();
|
||||||
|
|
||||||
|
expect(getByText(`@${user.username}`).exists()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders user's avatar", () => {
|
||||||
|
createComponent();
|
||||||
|
|
||||||
|
expect(wrapper.find('img').attributes('src')).toBe(user.avatarUrl);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when user property does not exist', () => {
|
||||||
|
it('displays an orphaned user', () => {
|
||||||
|
createComponent({ member: orphanedMember });
|
||||||
|
|
||||||
|
expect(getByText('Orphaned member').exists()).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
61
spec/frontend/vue_shared/components/members/mock_data.js
Normal file
61
spec/frontend/vue_shared/components/members/mock_data.js
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
export const member = {
|
||||||
|
requestedAt: null,
|
||||||
|
canUpdate: false,
|
||||||
|
canRemove: false,
|
||||||
|
canOverride: false,
|
||||||
|
accessLevel: { integerValue: 50, stringValue: 'Owner' },
|
||||||
|
source: {
|
||||||
|
id: 178,
|
||||||
|
name: 'Foo Bar',
|
||||||
|
webUrl: 'https://gitlab.com/groups/foo-bar',
|
||||||
|
},
|
||||||
|
user: {
|
||||||
|
id: 123,
|
||||||
|
name: 'Administrator',
|
||||||
|
username: 'root',
|
||||||
|
webUrl: 'https://gitlab.com/root',
|
||||||
|
avatarUrl: 'https://www.gravatar.com/avatar/4816142ef496f956a277bedf1a40607b?s=80&d=identicon',
|
||||||
|
blocked: false,
|
||||||
|
twoFactorEnabled: false,
|
||||||
|
},
|
||||||
|
id: 238,
|
||||||
|
createdAt: '2020-07-17T16:22:46.923Z',
|
||||||
|
expiresAt: null,
|
||||||
|
usingLicense: false,
|
||||||
|
groupSso: false,
|
||||||
|
groupManagedAccount: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const group = {
|
||||||
|
accessLevel: { integerValue: 10, stringValue: 'Guest' },
|
||||||
|
sharedWithGroup: {
|
||||||
|
id: 24,
|
||||||
|
name: 'Commit451',
|
||||||
|
avatarUrl: '/uploads/-/system/user/avatar/1/avatar.png?width=40',
|
||||||
|
fullPath: 'parent-group/commit451',
|
||||||
|
fullName: 'Parent group / Commit451',
|
||||||
|
webUrl: 'https://gitlab.com/groups/parent-group/commit451',
|
||||||
|
},
|
||||||
|
id: 3,
|
||||||
|
createdAt: '2020-08-06T15:31:07.662Z',
|
||||||
|
expiresAt: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
const { user, ...memberNoUser } = member;
|
||||||
|
export const invite = {
|
||||||
|
...memberNoUser,
|
||||||
|
invite: {
|
||||||
|
email: 'jewel@hudsonwalter.biz',
|
||||||
|
avatarUrl: 'https://www.gravatar.com/avatar/cbab7510da7eec2f60f638261b05436d?s=80&d=identicon',
|
||||||
|
canResend: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const orphanedMember = memberNoUser;
|
||||||
|
|
||||||
|
export const accessRequest = {
|
||||||
|
...member,
|
||||||
|
requestedAt: '2020-07-17T16:22:46.923Z',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const members = [member];
|
|
@ -0,0 +1,36 @@
|
||||||
|
import { shallowMount } from '@vue/test-utils';
|
||||||
|
import { MEMBER_TYPES } from '~/vue_shared/components/members/constants';
|
||||||
|
import { member as memberMock, group, invite, accessRequest } from '../mock_data';
|
||||||
|
import MemberAvatar from '~/vue_shared/components/members/table/member_avatar.vue';
|
||||||
|
import UserAvatar from '~/vue_shared/components/members/avatars/user_avatar.vue';
|
||||||
|
import GroupAvatar from '~/vue_shared/components/members/avatars/group_avatar.vue';
|
||||||
|
import InviteAvatar from '~/vue_shared/components/members/avatars/invite_avatar.vue';
|
||||||
|
|
||||||
|
describe('MemberList', () => {
|
||||||
|
let wrapper;
|
||||||
|
|
||||||
|
const createComponent = propsData => {
|
||||||
|
wrapper = shallowMount(MemberAvatar, {
|
||||||
|
propsData,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
wrapper.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
test.each`
|
||||||
|
memberType | member | expectedComponent | expectedComponentName
|
||||||
|
${MEMBER_TYPES.user} | ${memberMock} | ${UserAvatar} | ${'UserAvatar'}
|
||||||
|
${MEMBER_TYPES.group} | ${group} | ${GroupAvatar} | ${'GroupAvatar'}
|
||||||
|
${MEMBER_TYPES.invite} | ${invite} | ${InviteAvatar} | ${'InviteAvatar'}
|
||||||
|
${MEMBER_TYPES.accessRequest} | ${accessRequest} | ${UserAvatar} | ${'UserAvatar'}
|
||||||
|
`(
|
||||||
|
'renders $expectedComponentName when `memberType` is $memberType',
|
||||||
|
({ memberType, member, expectedComponent }) => {
|
||||||
|
createComponent({ memberType, member });
|
||||||
|
|
||||||
|
expect(wrapper.find(expectedComponent).exists()).toBe(true);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
|
@ -0,0 +1,53 @@
|
||||||
|
import { mount, createLocalVue } from '@vue/test-utils';
|
||||||
|
import { MEMBER_TYPES } from '~/vue_shared/components/members/constants';
|
||||||
|
import { member as memberMock, group, invite, accessRequest } from '../mock_data';
|
||||||
|
import MembersTableCell from '~/vue_shared/components/members/table/members_table_cell.vue';
|
||||||
|
|
||||||
|
describe('MemberList', () => {
|
||||||
|
const WrappedComponent = {
|
||||||
|
props: {
|
||||||
|
memberType: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
render(createElement) {
|
||||||
|
return createElement('div', this.memberType);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const localVue = createLocalVue();
|
||||||
|
localVue.component('wrapped-component', WrappedComponent);
|
||||||
|
|
||||||
|
let wrapper;
|
||||||
|
|
||||||
|
const createComponent = propsData => {
|
||||||
|
wrapper = mount(MembersTableCell, {
|
||||||
|
localVue,
|
||||||
|
propsData,
|
||||||
|
scopedSlots: {
|
||||||
|
default: '<wrapped-component :member-type="props.memberType" />',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
wrapper.destroy();
|
||||||
|
wrapper = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
test.each`
|
||||||
|
member | expectedMemberType
|
||||||
|
${memberMock} | ${MEMBER_TYPES.user}
|
||||||
|
${group} | ${MEMBER_TYPES.group}
|
||||||
|
${invite} | ${MEMBER_TYPES.invite}
|
||||||
|
${accessRequest} | ${MEMBER_TYPES.accessRequest}
|
||||||
|
`(
|
||||||
|
'sets scoped slot prop `memberType` to $expectedMemberType',
|
||||||
|
({ member, expectedMemberType }) => {
|
||||||
|
createComponent({ member });
|
||||||
|
|
||||||
|
expect(wrapper.find(WrappedComponent).props('memberType')).toBe(expectedMemberType);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
|
@ -133,6 +133,19 @@ describe('DropdownContentsLabelsView', () => {
|
||||||
expect(wrapper.vm.currentHighlightItem).toBe(2);
|
expect(wrapper.vm.currentHighlightItem).toBe(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('resets the search text when the Enter key is pressed', () => {
|
||||||
|
wrapper.setData({
|
||||||
|
currentHighlightItem: 1,
|
||||||
|
searchKey: 'bug',
|
||||||
|
});
|
||||||
|
|
||||||
|
wrapper.vm.handleKeyDown({
|
||||||
|
keyCode: ENTER_KEY_CODE,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.vm.searchKey).toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
it('calls action `updateSelectedLabels` with currently highlighted label when Enter key is pressed', () => {
|
it('calls action `updateSelectedLabels` with currently highlighted label when Enter key is pressed', () => {
|
||||||
jest.spyOn(wrapper.vm, 'updateSelectedLabels').mockImplementation();
|
jest.spyOn(wrapper.vm, 'updateSelectedLabels').mockImplementation();
|
||||||
wrapper.setData({
|
wrapper.setData({
|
||||||
|
|
|
@ -9,7 +9,7 @@ RSpec.describe API::Helpers do
|
||||||
include described_class
|
include described_class
|
||||||
include TermsHelper
|
include TermsHelper
|
||||||
|
|
||||||
let(:user) { create(:user) }
|
let_it_be(:user, reload: true) { create(:user) }
|
||||||
let(:admin) { create(:admin) }
|
let(:admin) { create(:admin) }
|
||||||
let(:key) { create(:key, user: user) }
|
let(:key) { create(:key, user: user) }
|
||||||
|
|
||||||
|
@ -243,6 +243,67 @@ RSpec.describe API::Helpers do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "when authenticating using a job token" do
|
||||||
|
let_it_be(:job, reload: true) do
|
||||||
|
create(:ci_build, user: user, status: :running)
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:route_authentication_setting) { {} }
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow_any_instance_of(self.class).to receive(:route_authentication_setting)
|
||||||
|
.and_return(route_authentication_setting)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when route is allowed to be authenticated' do
|
||||||
|
let(:route_authentication_setting) { { job_token_allowed: true } }
|
||||||
|
|
||||||
|
it "returns a 401 response for an invalid token" do
|
||||||
|
env[Gitlab::Auth::AuthFinders::JOB_TOKEN_HEADER] = 'invalid token'
|
||||||
|
|
||||||
|
expect { current_user }.to raise_error /401/
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns a 401 response for a job that's not running" do
|
||||||
|
job.update!(status: :success)
|
||||||
|
env[Gitlab::Auth::AuthFinders::JOB_TOKEN_HEADER] = job.token
|
||||||
|
|
||||||
|
expect { current_user }.to raise_error /401/
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns a 403 response for a user without access" do
|
||||||
|
env[Gitlab::Auth::AuthFinders::JOB_TOKEN_HEADER] = job.token
|
||||||
|
allow_any_instance_of(Gitlab::UserAccess).to receive(:allowed?).and_return(false)
|
||||||
|
|
||||||
|
expect { current_user }.to raise_error /403/
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns a 403 response for a user who is blocked' do
|
||||||
|
user.block!
|
||||||
|
env[Gitlab::Auth::AuthFinders::JOB_TOKEN_HEADER] = job.token
|
||||||
|
|
||||||
|
expect { current_user }.to raise_error /403/
|
||||||
|
end
|
||||||
|
|
||||||
|
it "sets current_user" do
|
||||||
|
env[Gitlab::Auth::AuthFinders::JOB_TOKEN_HEADER] = job.token
|
||||||
|
|
||||||
|
expect(current_user).to eq(user)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when route is not allowed to be authenticated' do
|
||||||
|
let(:route_authentication_setting) { { job_token_allowed: false } }
|
||||||
|
|
||||||
|
it "sets current_user to nil" do
|
||||||
|
env[Gitlab::Auth::AuthFinders::JOB_TOKEN_HEADER] = job.token
|
||||||
|
allow_any_instance_of(Gitlab::UserAccess).to receive(:allowed?).and_return(true)
|
||||||
|
|
||||||
|
expect(current_user).to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '.handle_api_exception' do
|
describe '.handle_api_exception' do
|
||||||
|
|
|
@ -604,7 +604,7 @@ module KubernetesHelpers
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def kube_deployment(name: "kube-deployment", environment_slug: "production", project_slug: "project-path-slug", track: nil)
|
def kube_deployment(name: "kube-deployment", environment_slug: "production", project_slug: "project-path-slug", track: nil, replicas: 3)
|
||||||
{
|
{
|
||||||
"metadata" => {
|
"metadata" => {
|
||||||
"name" => name,
|
"name" => name,
|
||||||
|
@ -617,7 +617,7 @@ module KubernetesHelpers
|
||||||
"track" => track
|
"track" => track
|
||||||
}.compact
|
}.compact
|
||||||
},
|
},
|
||||||
"spec" => { "replicas" => 3 },
|
"spec" => { "replicas" => replicas },
|
||||||
"status" => {
|
"status" => {
|
||||||
"observedGeneration" => 4
|
"observedGeneration" => 4
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue