Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
01d2d6c869
commit
1581767ea1
|
@ -6,11 +6,11 @@
|
|||
# - We should support the latest ESR of Firefox: 78, because it used quite a lot.
|
||||
# - We use Edge/Chrome >= 84 because 83 had an annoying bug which would mean we
|
||||
# need to polyfill Array.reduce: https://bugs.chromium.org/p/chromium/issues/detail?id=1049982
|
||||
# - Safari 13 because it is the second latest major version of Safari
|
||||
# - Safari 13.1 because it is the current minor version of the previous major version
|
||||
#
|
||||
# See also this epic: https://gitlab.com/groups/gitlab-org/-/epics/3957
|
||||
#
|
||||
chrome >= 84
|
||||
edge >= 84
|
||||
firefox >= 78
|
||||
safari >= 13.0.4
|
||||
safari >= 13.1
|
||||
|
|
|
@ -224,6 +224,7 @@
|
|||
|
||||
.code-patterns: &code-patterns
|
||||
- "{package.json,yarn.lock}"
|
||||
- ".browserslistrc"
|
||||
- "babel.config.js"
|
||||
- "jest.config.{base,integration,unit}.js"
|
||||
- ".csscomb.json"
|
||||
|
@ -249,6 +250,7 @@
|
|||
|
||||
.code-backstage-patterns: &code-backstage-patterns
|
||||
- "{package.json,yarn.lock}"
|
||||
- ".browserslistrc"
|
||||
- "babel.config.js"
|
||||
- "jest.config.{base,integration,unit}.js"
|
||||
- ".csscomb.json"
|
||||
|
@ -277,6 +279,7 @@
|
|||
|
||||
.code-qa-patterns: &code-qa-patterns
|
||||
- "{package.json,yarn.lock}"
|
||||
- ".browserslistrc"
|
||||
- "babel.config.js"
|
||||
- "jest.config.{base,integration,unit}.js"
|
||||
- ".csscomb.json"
|
||||
|
@ -301,6 +304,7 @@
|
|||
|
||||
.code-backstage-qa-patterns: &code-backstage-qa-patterns
|
||||
- "{package.json,yarn.lock}"
|
||||
- ".browserslistrc"
|
||||
- "babel.config.js"
|
||||
- "jest.config.{base,integration,unit}.js"
|
||||
- ".csscomb.json"
|
||||
|
|
|
@ -140,6 +140,16 @@ export default {
|
|||
},
|
||||
(line) => line.type,
|
||||
),
|
||||
lineContent: memoize(
|
||||
(line) => {
|
||||
if (line.isConflictMarker) {
|
||||
return line.type === CONFLICT_MARKER_THEIR ? 'HEAD//our changes' : 'origin//their changes';
|
||||
}
|
||||
|
||||
return line.rich_text;
|
||||
},
|
||||
(line) => line.line_code,
|
||||
),
|
||||
CONFLICT_MARKER,
|
||||
CONFLICT_MARKER_THEIR,
|
||||
CONFLICT_OUR,
|
||||
|
@ -261,15 +271,14 @@ export default {
|
|||
</div>
|
||||
<div
|
||||
:key="props.line.left.line_code"
|
||||
:class="[$options.parallelViewLeftLineType(props), { parallel: !props.inline }]"
|
||||
:class="[
|
||||
$options.parallelViewLeftLineType(props),
|
||||
{ parallel: !props.inline, 'gl-font-weight-bold': props.line.left.isConflictMarker },
|
||||
]"
|
||||
class="diff-td line_content with-coverage left-side"
|
||||
data-testid="left-content"
|
||||
>
|
||||
<strong v-if="props.line.left.isConflictMarker">{{
|
||||
$options.conflictText(props.line.left)
|
||||
}}</strong>
|
||||
<span v-else v-html="props.line.left.rich_text"></span>
|
||||
</div>
|
||||
v-html="$options.lineContent(props.line.left)"
|
||||
></div>
|
||||
</template>
|
||||
<template
|
||||
v-else-if="
|
||||
|
@ -386,15 +395,12 @@ export default {
|
|||
{
|
||||
hll: props.isHighlighted,
|
||||
hll: props.isCommented,
|
||||
'gl-font-weight-bold': props.line.right.type === $options.CONFLICT_MARKER_THEIR,
|
||||
},
|
||||
]"
|
||||
class="diff-td line_content with-coverage right-side parallel"
|
||||
>
|
||||
<strong v-if="props.line.right.type === $options.CONFLICT_MARKER_THEIR">{{
|
||||
$options.conflictText(props.line.right)
|
||||
}}</strong>
|
||||
<span v-else v-html="props.line.right.rich_text"></span>
|
||||
</div>
|
||||
v-html="$options.lineContent(props.line.right)"
|
||||
></div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div data-testid="right-empty-cell" class="diff-td diff-line-num old_line empty-cell"></div>
|
||||
|
|
|
@ -17,6 +17,7 @@ export default {
|
|||
},
|
||||
mounted() {
|
||||
this.width = this.$el.parentNode.offsetWidth;
|
||||
window.test = this;
|
||||
|
||||
this.$_itemsWithSizeWatcher = this.$watch('vscrollParent.itemsWithSize', async () => {
|
||||
await this.$nextTick();
|
||||
|
@ -27,6 +28,14 @@ export default {
|
|||
this.startedRender = true;
|
||||
requestIdleCallback(() => {
|
||||
this.nextItem = nextItem;
|
||||
|
||||
if (this.nextIndex === this.maxLength - 1) {
|
||||
this.$nextTick(() => {
|
||||
if (this.vscrollParent.itemsWithSize[this.maxLength - 1].size !== 0) {
|
||||
this.clearRendering();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} else if (this.startedRender) {
|
||||
this.clearRendering();
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import ShortcutsNavigation from '../../behaviors/shortcuts/shortcuts_navigation';
|
||||
import initTerraformNotification from '../../projects/terraform_notification';
|
||||
import { initSidebarTracking } from '../shared/nav/sidebar_tracking';
|
||||
import Project from './project';
|
||||
|
||||
new Project(); // eslint-disable-line no-new
|
||||
new ShortcutsNavigation(); // eslint-disable-line no-new
|
||||
initSidebarTracking();
|
||||
initTerraformNotification();
|
||||
|
|
|
@ -104,6 +104,11 @@ export default {
|
|||
required: false,
|
||||
default: '',
|
||||
},
|
||||
issuesHelpPath: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
lfsHelpPath: {
|
||||
type: String,
|
||||
required: false,
|
||||
|
@ -438,8 +443,13 @@ export default {
|
|||
>
|
||||
<project-setting-row
|
||||
ref="issues-settings"
|
||||
:help-path="issuesHelpPath"
|
||||
:label="$options.i18n.issuesLabel"
|
||||
:help-text="s__('ProjectSettings|Lightweight issue tracking system.')"
|
||||
:help-text="
|
||||
s__(
|
||||
'ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project.',
|
||||
)
|
||||
"
|
||||
>
|
||||
<project-feature-setting
|
||||
v-model="issuesAccessLevel"
|
||||
|
|
|
@ -1,27 +1,18 @@
|
|||
<script>
|
||||
import { GlAlert, GlLink, GlSprintf, GlIcon } from '@gitlab/ui';
|
||||
import { GlIcon } from '@gitlab/ui';
|
||||
import { uniqueId } from 'lodash';
|
||||
import { helpPagePath } from '~/helpers/help_page_helper';
|
||||
import { s__ } from '~/locale';
|
||||
import SourceEditor from '~/vue_shared/components/source_editor.vue';
|
||||
import getCurrentBranch from '../../graphql/queries/client/current_branch.graphql';
|
||||
|
||||
export default {
|
||||
i18n: {
|
||||
viewOnlyMessage: s__('Pipelines|Merged YAML is view only'),
|
||||
unavailableDefaultTitle: s__('Pipelines|Merged YAML unavailable'),
|
||||
unavailableDefaultText: s__(
|
||||
'Pipelines|The merged YAML view is only available for the default branch. %{linkStart}Learn more.%{linkEnd}',
|
||||
),
|
||||
},
|
||||
components: {
|
||||
SourceEditor,
|
||||
GlAlert,
|
||||
GlIcon,
|
||||
GlLink,
|
||||
GlSprintf,
|
||||
},
|
||||
inject: ['ciConfigPath', 'defaultBranch'],
|
||||
inject: ['ciConfigPath'],
|
||||
props: {
|
||||
ciConfigData: {
|
||||
type: Object,
|
||||
|
@ -33,15 +24,6 @@ export default {
|
|||
failureType: null,
|
||||
};
|
||||
},
|
||||
// This is not the best practice, don't copy me (@samdbeckham)
|
||||
// This is a temporary workaround to unblock a release.
|
||||
// See this comment for more information on this approach
|
||||
// https://gitlab.com/gitlab-org/gitlab/-/merge_requests/65972#note_626095648
|
||||
apollo: {
|
||||
currentBranch: {
|
||||
query: getCurrentBranch,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
fileGlobalId() {
|
||||
return `${this.ciConfigPath}-${uniqueId()}`;
|
||||
|
@ -49,44 +31,24 @@ export default {
|
|||
mergedYaml() {
|
||||
return this.ciConfigData.mergedYaml;
|
||||
},
|
||||
isOnDefaultBranch() {
|
||||
return this.currentBranch === this.defaultBranch;
|
||||
},
|
||||
expandedConfigHelpPath() {
|
||||
return helpPagePath('ci/pipeline_editor/index', { anchor: 'view-expanded-configuration' });
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="isOnDefaultBranch">
|
||||
<div class="gl-display-flex gl-align-items-center">
|
||||
<gl-icon :size="16" name="lock" class="gl-text-gray-500 gl-mr-3" />
|
||||
{{ $options.i18n.viewOnlyMessage }}
|
||||
</div>
|
||||
<div class="gl-mt-3 gl-border-solid gl-border-gray-100 gl-border-1">
|
||||
<source-editor
|
||||
ref="editor"
|
||||
:value="mergedYaml"
|
||||
:file-name="ciConfigPath"
|
||||
:file-global-id="fileGlobalId"
|
||||
:editor-options="{ readOnly: true }"
|
||||
v-on="$listeners"
|
||||
/>
|
||||
</div>
|
||||
<div class="gl-display-flex gl-align-items-center">
|
||||
<gl-icon :size="16" name="lock" class="gl-text-gray-500 gl-mr-3" />
|
||||
{{ $options.i18n.viewOnlyMessage }}
|
||||
</div>
|
||||
<div class="gl-mt-3 gl-border-solid gl-border-gray-100 gl-border-1">
|
||||
<source-editor
|
||||
ref="editor"
|
||||
:value="mergedYaml"
|
||||
:file-name="ciConfigPath"
|
||||
:file-global-id="fileGlobalId"
|
||||
:editor-options="{ readOnly: true }"
|
||||
v-on="$listeners"
|
||||
/>
|
||||
</div>
|
||||
<gl-alert
|
||||
v-else
|
||||
variant="info"
|
||||
:dismissible="false"
|
||||
:title="$options.i18n.unavailableDefaultTitle"
|
||||
>
|
||||
<gl-sprintf :message="$options.i18n.unavailableDefaultText">
|
||||
<template #link="{ content }">
|
||||
<gl-link :href="expandedConfigHelpPath" target="_blank">{{ content }}</gl-link>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</gl-alert>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#import "~/pipelines/graphql/fragments/pipeline_stages_connection.fragment.graphql"
|
||||
|
||||
query getCiConfigData($projectPath: ID!, $content: String!) {
|
||||
ciConfig(projectPath: $projectPath, content: $content) {
|
||||
query getCiConfigData($projectPath: ID!, $sha: String, $content: String!) {
|
||||
ciConfig(projectPath: $projectPath, sha: $sha, content: $content) {
|
||||
errors
|
||||
mergedYaml
|
||||
status
|
||||
|
|
|
@ -20,6 +20,7 @@ import updateCommitShaMutation from './graphql/mutations/update_commit_sha.mutat
|
|||
import getBlobContent from './graphql/queries/blob_content.graphql';
|
||||
import getCiConfigData from './graphql/queries/ci_config.graphql';
|
||||
import getAppStatus from './graphql/queries/client/app_status.graphql';
|
||||
import getCommitSha from './graphql/queries/client/commit_sha.graphql';
|
||||
import getCurrentBranch from './graphql/queries/client/current_branch.graphql';
|
||||
import getIsNewCiConfigFile from './graphql/queries/client/is_new_ci_config_file.graphql';
|
||||
import getTemplate from './graphql/queries/get_starter_template.query.graphql';
|
||||
|
@ -128,6 +129,7 @@ export default {
|
|||
variables() {
|
||||
return {
|
||||
projectPath: this.projectFullPath,
|
||||
sha: this.commitSha,
|
||||
content: this.currentCiFileContent,
|
||||
};
|
||||
},
|
||||
|
@ -153,6 +155,9 @@ export default {
|
|||
appStatus: {
|
||||
query: getAppStatus,
|
||||
},
|
||||
commitSha: {
|
||||
query: getCommitSha,
|
||||
},
|
||||
currentBranch: {
|
||||
query: getCurrentBranch,
|
||||
},
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
<script>
|
||||
import { GlBanner } from '@gitlab/ui';
|
||||
import { helpPagePath } from '~/helpers/help_page_helper';
|
||||
import { parseBoolean, setCookie, getCookie } from '~/lib/utils/common_utils';
|
||||
import { s__ } from '~/locale';
|
||||
|
||||
export default {
|
||||
name: 'TerraformNotification',
|
||||
i18n: {
|
||||
title: s__('TerraformBanner|Using Terraform? Try the GitLab Managed Terraform State'),
|
||||
description: s__(
|
||||
'TerraformBanner|The GitLab managed Terraform state backend can store your Terraform state easily and securely, and spares you from setting up additional remote resources. Its features include: versioning, encryption of the state file both in transit and at rest, locking, and remote Terraform plan/apply execution.',
|
||||
),
|
||||
buttonText: s__("TerraformBanner|Learn more about GitLab's Backend State"),
|
||||
},
|
||||
components: {
|
||||
GlBanner,
|
||||
},
|
||||
props: {
|
||||
projectId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isVisible: true,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
bannerDissmisedKey() {
|
||||
return `terraform_notification_dismissed_for_project_${this.projectId}`;
|
||||
},
|
||||
docsUrl() {
|
||||
return helpPagePath('user/infrastructure/terraform_state');
|
||||
},
|
||||
},
|
||||
created() {
|
||||
if (parseBoolean(getCookie(this.bannerDissmisedKey))) {
|
||||
this.isVisible = false;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleClose() {
|
||||
setCookie(this.bannerDissmisedKey, true);
|
||||
this.isVisible = false;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<div v-if="isVisible">
|
||||
<div class="gl-py-5">
|
||||
<gl-banner
|
||||
:title="$options.i18n.title"
|
||||
:button-text="$options.i18n.buttonText"
|
||||
:button-link="docsUrl"
|
||||
variant="introduction"
|
||||
@close="handleClose"
|
||||
>
|
||||
<p>{{ $options.i18n.description }}</p>
|
||||
</gl-banner>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
|
@ -0,0 +1,18 @@
|
|||
import Vue from 'vue';
|
||||
import TerraformNotification from './components/terraform_notification.vue';
|
||||
|
||||
export default () => {
|
||||
const el = document.querySelector('.js-terraform-notification');
|
||||
|
||||
if (!el) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const { projectId } = el.dataset;
|
||||
|
||||
return new Vue({
|
||||
el,
|
||||
render: (createElement) =>
|
||||
createElement(TerraformNotification, { props: { projectId: Number(projectId) } }),
|
||||
});
|
||||
};
|
|
@ -0,0 +1,41 @@
|
|||
<script>
|
||||
import { GlAlert, GlLink, GlSprintf } from '@gitlab/ui';
|
||||
import { s__ } from '~/locale';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlSprintf,
|
||||
GlAlert,
|
||||
GlLink,
|
||||
},
|
||||
inject: ['autoDevopsHelpPagePath', 'autoDevopsPath'],
|
||||
i18n: {
|
||||
primaryButtonText: s__('SecurityConfiguration|Enable Auto DevOps'),
|
||||
body: s__(
|
||||
'SecurityConfiguration|Quickly enable all continuous testing and compliance tools by enabling %{linkStart}Auto DevOps%{linkEnd}',
|
||||
),
|
||||
},
|
||||
methods: {
|
||||
dismissMethod() {
|
||||
this.$emit('dismiss');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<gl-alert
|
||||
variant="info"
|
||||
:primary-button-link="autoDevopsPath"
|
||||
:primary-button-text="$options.i18n.primaryButtonText"
|
||||
@dismiss="dismissMethod"
|
||||
>
|
||||
<gl-sprintf :message="$options.i18n.body">
|
||||
<template #link="{ content }">
|
||||
<gl-link :href="autoDevopsHelpPagePath">
|
||||
{{ content }}
|
||||
</gl-link>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</gl-alert>
|
||||
</template>
|
|
@ -2,6 +2,7 @@
|
|||
import { GlTab, GlTabs, GlSprintf, GlLink } from '@gitlab/ui';
|
||||
import { __, s__ } from '~/locale';
|
||||
import UserCalloutDismisser from '~/vue_shared/components/user_callout_dismisser.vue';
|
||||
import AutoDevOpsAlert from './auto_dev_ops_alert.vue';
|
||||
import FeatureCard from './feature_card.vue';
|
||||
import SectionLayout from './section_layout.vue';
|
||||
import UpgradeBanner from './upgrade_banner.vue';
|
||||
|
@ -31,6 +32,7 @@ export default {
|
|||
FeatureCard,
|
||||
SectionLayout,
|
||||
UpgradeBanner,
|
||||
AutoDevOpsAlert,
|
||||
UserCalloutDismisser,
|
||||
},
|
||||
props: {
|
||||
|
@ -47,6 +49,16 @@ export default {
|
|||
required: false,
|
||||
default: false,
|
||||
},
|
||||
autoDevopsEnabled: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
canEnableAutoDevops: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
gitlabCiHistoryPath: {
|
||||
type: String,
|
||||
required: false,
|
||||
|
@ -67,16 +79,26 @@ export default {
|
|||
canViewCiHistory() {
|
||||
return Boolean(this.gitlabCiPresent && this.gitlabCiHistoryPath);
|
||||
},
|
||||
shouldShowDevopsAlert() {
|
||||
return !this.autoDevopsEnabled && !this.gitlabCiPresent && this.canEnableAutoDevops;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<article>
|
||||
<user-callout-dismisser
|
||||
v-if="shouldShowDevopsAlert"
|
||||
feature-name="security_configuration_devops_alert"
|
||||
>
|
||||
<template #default="{ dismiss, shouldShowCallout }">
|
||||
<auto-dev-ops-alert v-if="shouldShowCallout" class="gl-mt-3" @dismiss="dismiss" />
|
||||
</template>
|
||||
</user-callout-dismisser>
|
||||
<header>
|
||||
<h1 class="gl-font-size-h1">{{ $options.i18n.securityConfiguration }}</h1>
|
||||
</header>
|
||||
|
||||
<user-callout-dismisser v-if="canUpgrade" feature-name="security_configuration_upgrade_banner">
|
||||
<template #default="{ dismiss, shouldShowCallout }">
|
||||
<upgrade-banner v-if="shouldShowCallout" @close="dismiss" />
|
||||
|
|
|
@ -20,6 +20,8 @@ export const initRedesignedSecurityConfiguration = (el) => {
|
|||
features,
|
||||
latestPipelinePath,
|
||||
gitlabCiHistoryPath,
|
||||
autoDevopsHelpPagePath,
|
||||
autoDevopsPath,
|
||||
} = el.dataset;
|
||||
|
||||
const { augmentedSecurityFeatures, augmentedComplianceFeatures } = augmentFeatures(
|
||||
|
@ -34,6 +36,8 @@ export const initRedesignedSecurityConfiguration = (el) => {
|
|||
provide: {
|
||||
projectPath,
|
||||
upgradePath,
|
||||
autoDevopsHelpPagePath,
|
||||
autoDevopsPath,
|
||||
},
|
||||
render(createElement) {
|
||||
return createElement(RedesignedSecurityConfigurationApp, {
|
||||
|
@ -42,7 +46,11 @@ export const initRedesignedSecurityConfiguration = (el) => {
|
|||
augmentedSecurityFeatures,
|
||||
latestPipelinePath,
|
||||
gitlabCiHistoryPath,
|
||||
...parseBooleanDataAttributes(el, ['gitlabCiPresent']),
|
||||
...parseBooleanDataAttributes(el, [
|
||||
'gitlabCiPresent',
|
||||
'autoDevopsEnabled',
|
||||
'canEnableAutoDevops',
|
||||
]),
|
||||
},
|
||||
});
|
||||
},
|
||||
|
|
|
@ -125,17 +125,22 @@
|
|||
}
|
||||
}
|
||||
|
||||
// Will be moved to @gitlab/ui in https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1462
|
||||
.gl-md-mt-11 {
|
||||
// Will be moved to @gitlab/ui (without the !important) in https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1462
|
||||
// We only need the bang (!) version until the non-bang version is added to
|
||||
// @gitlab/ui utitlities.scss. Once there, it will get loaded in the correct
|
||||
// order to properly override `.gl-mt-6` which is used for narrower screen
|
||||
// widths (currently that style gets added to the application.css stylesheet
|
||||
// after this one, so it takes precedence).
|
||||
.gl-md-mt-11\! {
|
||||
@media (min-width: $breakpoint-md) {
|
||||
margin-top: $gl-spacing-scale-11;
|
||||
margin-top: $gl-spacing-scale-11 !important;
|
||||
}
|
||||
}
|
||||
|
||||
// Same as above
|
||||
.gl-md-pt-11 {
|
||||
// Same as above (also without the !important) but for overriding `.gl-pt-6`
|
||||
.gl-md-pt-11\! {
|
||||
@media (min-width: $breakpoint-md) {
|
||||
padding-top: $gl-spacing-scale-11;
|
||||
padding-top: $gl-spacing-scale-11 !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -160,13 +165,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
// Will be moved to @gitlab/ui in https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1463
|
||||
.gl-xs-mt-6 {
|
||||
@media (max-width: $breakpoint-sm) {
|
||||
margin-top: $gl-spacing-scale-6;
|
||||
}
|
||||
}
|
||||
|
||||
// Will be moved to @gitlab/ui in https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1165
|
||||
.gl-xs-mb-4 {
|
||||
@media (max-width: $breakpoint-sm) {
|
||||
|
@ -181,13 +179,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
// Will be moved to @gitlab/ui in https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1463
|
||||
.gl-xs-pt-6 {
|
||||
@media (max-width: $breakpoint-sm) {
|
||||
padding-top: $gl-spacing-scale-6;
|
||||
}
|
||||
}
|
||||
|
||||
// Will be moved to @gitlab/ui in https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1168
|
||||
.gl-sm-pr-3 {
|
||||
@media (min-width: $breakpoint-sm) {
|
||||
|
|
|
@ -350,6 +350,10 @@ module ProjectsHelper
|
|||
nil
|
||||
end
|
||||
|
||||
def show_terraform_banner?(project)
|
||||
project.repository_languages.with_programming_language('HCL').exists? && project.terraform_states.empty?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def tab_ability_map
|
||||
|
@ -530,7 +534,8 @@ module ProjectsHelper
|
|||
pagesAvailable: Gitlab.config.pages.enabled,
|
||||
pagesAccessControlEnabled: Gitlab.config.pages.access_control,
|
||||
pagesAccessControlForced: ::Gitlab::Pages.access_control_is_forced?,
|
||||
pagesHelpPath: help_page_path('user/project/pages/introduction', anchor: 'gitlab-pages-access-control')
|
||||
pagesHelpPath: help_page_path('user/project/pages/introduction', anchor: 'gitlab-pages-access-control'),
|
||||
issuesHelpPath: help_page_path('user/project/issues/index')
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
@ -8,6 +8,10 @@ class RepositoryLanguage < ApplicationRecord
|
|||
|
||||
default_scope { includes(:programming_language) } # rubocop:disable Cop/DefaultScope
|
||||
|
||||
scope :with_programming_language, ->(name) do
|
||||
joins(:programming_language).merge(ProgrammingLanguage.with_name_case_insensitive(name))
|
||||
end
|
||||
|
||||
validates :project, presence: true
|
||||
validates :share, inclusion: { in: 0..100, message: "The share of a language is between 0 and 100" }
|
||||
validates :programming_language, uniqueness: { scope: :project_id }
|
||||
|
|
|
@ -34,7 +34,8 @@ class UserCallout < ApplicationRecord
|
|||
security_configuration_upgrade_banner: 32,
|
||||
cloud_licensing_subscription_activation_banner: 33, # EE-only
|
||||
trial_status_reminder_d14: 34, # EE-only
|
||||
trial_status_reminder_d3: 35 # EE-only
|
||||
trial_status_reminder_d3: 35, # EE-only
|
||||
security_configuration_devops_alert: 36 # EE-only
|
||||
}
|
||||
|
||||
validates :user, presence: true
|
||||
|
|
|
@ -5,18 +5,18 @@
|
|||
= render_if_exists 'admin/application_settings/help_text_setting', form: f
|
||||
|
||||
.form-group
|
||||
= f.label :help_page_text, class: 'label-bold'
|
||||
= f.label :help_page_text, _('Additional text to show on the Help page'), class: 'label-bold'
|
||||
= f.text_area :help_page_text, class: 'form-control gl-form-input', rows: 4
|
||||
.form-text.text-muted= _('Markdown enabled')
|
||||
.form-group
|
||||
.form-check
|
||||
= f.check_box :help_page_hide_commercial_content, class: 'form-check-input'
|
||||
= f.label :help_page_hide_commercial_content, class: 'form-check-label' do
|
||||
= _('Hide marketing-related entries from help')
|
||||
= _('Hide marketing-related entries from the Help page.')
|
||||
.form-group
|
||||
= f.label :help_page_support_url, _('Support page URL'), class: 'label-bold'
|
||||
= f.text_field :help_page_support_url, class: 'form-control gl-form-input', placeholder: 'http://company.example.com/getting-help', :'aria-describedby' => 'support_help_block'
|
||||
%span.form-text.text-muted#support_help_block= _('Alternate support URL for help page and help dropdown')
|
||||
%span.form-text.text-muted#support_help_block= _('Alternate support URL for Help page and Help dropdown')
|
||||
|
||||
- if show_documentation_base_url_field?
|
||||
.form-group
|
||||
|
|
|
@ -27,11 +27,12 @@
|
|||
%section.settings.as-help-page.no-animate#js-help-settings{ class: ('expanded' if expanded_by_default?) }
|
||||
.settings-header
|
||||
%h4
|
||||
= _('Help page')
|
||||
= _('Sign-in and Help page')
|
||||
%button.btn.gl-button.btn-default.js-settings-toggle{ type: 'button' }
|
||||
= expanded_by_default? ? _('Collapse') : _('Expand')
|
||||
%p
|
||||
= _('Help page text and support page url.')
|
||||
= _('Additional text for the sign-in and Help page.')
|
||||
= link_to s_('Learn more.'), help_page_path('user/admin_area/settings/help_page.md'), target: '_blank', rel: 'noopener noreferrer'
|
||||
.settings-content
|
||||
= render 'help_page'
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
.card-header
|
||||
= _('Quick help')
|
||||
%ul.content-list
|
||||
%li= link_to _('See our website for getting help'), support_url
|
||||
%li= link_to _('See our website for help'), support_url
|
||||
%li
|
||||
%button.btn-blank.btn-link.js-trigger-search-bar{ type: 'button' }
|
||||
= _('Use the search bar on the top of this page')
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
= render 'peek/bar'
|
||||
= render "layouts/header/empty"
|
||||
.layout-page
|
||||
.content-wrapper.content-wrapper-margin.gl-md-pt-11.gl-xs-pt-6
|
||||
.content-wrapper.content-wrapper-margin.gl-pt-6{ class: 'gl-md-pt-11!' }
|
||||
.alert-wrapper.gl-force-block-formatting-context
|
||||
= render "layouts/broadcast"
|
||||
.limit-container-width{ class: container_class }
|
||||
|
|
|
@ -9,3 +9,4 @@
|
|||
= render 'shared/auto_devops_implicitly_enabled_banner', project: project
|
||||
= render_if_exists 'projects/above_size_limit_warning', project: project
|
||||
= render_if_exists 'shared/shared_runners_minutes_limit', project: project, classes: [container_class, ("limit-container-width" unless fluid_layout)]
|
||||
= render_if_exists 'projects/terraform_banner', project: project
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
- @content_class = "container-limited limit-container-width" unless fluid_layout
|
||||
|
||||
- if show_terraform_banner?(project)
|
||||
.container-fluid{ class: @content_class }
|
||||
.js-terraform-notification{ data: { project_id: project.id } }
|
|
@ -19,6 +19,10 @@ const plugins = [
|
|||
'@babel/plugin-proposal-private-methods',
|
||||
// See: https://gitlab.com/gitlab-org/gitlab/-/issues/229146
|
||||
'@babel/plugin-transform-arrow-functions',
|
||||
// See: https://gitlab.com/gitlab-org/gitlab/-/issues/336216
|
||||
'@babel/plugin-proposal-optional-chaining',
|
||||
// See: https://gitlab.com/gitlab-org/gitlab/-/issues/336216
|
||||
'@babel/plugin-proposal-nullish-coalescing-operator',
|
||||
'lodash',
|
||||
];
|
||||
|
||||
|
|
|
@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/332272
|
|||
milestone: '14.0'
|
||||
type: development
|
||||
group: group::pipeline execution
|
||||
default_enabled: false
|
||||
default_enabled: true
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ChangeDefaultJobTokenScopeEnabled < ActiveRecord::Migration[6.1]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
def up
|
||||
with_lock_retries do
|
||||
change_column_default :project_ci_cd_settings, :job_token_scope_enabled, from: false, to: true
|
||||
end
|
||||
end
|
||||
|
||||
def down
|
||||
with_lock_retries do
|
||||
change_column_default :project_ci_cd_settings, :job_token_scope_enabled, from: true, to: false
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1 @@
|
|||
e0a2de69a3c9d616b87207b764e33fa3326627e065f28fc200c1414f08ee9fff
|
|
@ -16753,7 +16753,7 @@ CREATE TABLE project_ci_cd_settings (
|
|||
auto_rollback_enabled boolean DEFAULT false NOT NULL,
|
||||
keep_latest_artifact boolean DEFAULT true NOT NULL,
|
||||
restrict_user_defined_variables boolean DEFAULT false NOT NULL,
|
||||
job_token_scope_enabled boolean DEFAULT false NOT NULL
|
||||
job_token_scope_enabled boolean DEFAULT true NOT NULL
|
||||
);
|
||||
|
||||
CREATE SEQUENCE project_ci_cd_settings_id_seq
|
||||
|
|
|
@ -487,7 +487,7 @@ The replication process is now complete.
|
|||
PostgreSQL connections, which can improve performance even when using in a
|
||||
single instance installation.
|
||||
|
||||
We recommend using PgBouncer if you use GitLab in a highly available
|
||||
We recommend using PgBouncer if you use GitLab in a highly available
|
||||
configuration with a cluster of nodes supporting a Geo **primary** site and
|
||||
two other clusters of nodes supporting a Geo **secondary** site. One for the
|
||||
main database and the other for the tracking database. For more information,
|
||||
|
@ -550,12 +550,12 @@ Leader instance**:
|
|||
|
||||
```ruby
|
||||
roles(['patroni_role'])
|
||||
|
||||
|
||||
consul['services'] = %w(postgresql)
|
||||
consul['configuration'] = {
|
||||
retry_join: %w[CONSUL_PRIMARY1_IP CONSUL_PRIMARY2_IP CONSUL_PRIMARY3_IP]
|
||||
}
|
||||
|
||||
|
||||
# You need one entry for each secondary, with a unique name following PostgreSQL slot_name constraints:
|
||||
#
|
||||
# Configuration syntax is: 'unique_slotname' => { 'type' => 'physical' },
|
||||
|
@ -567,6 +567,8 @@ Leader instance**:
|
|||
patroni['use_pg_rewind'] = true
|
||||
patroni['postgresql']['max_wal_senders'] = 8 # Use double of the amount of patroni/reserved slots (3 patronis + 1 reserved slot for a Geo secondary).
|
||||
patroni['postgresql']['max_replication_slots'] = 8 # Use double of the amount of patroni/reserved slots (3 patronis + 1 reserved slot for a Geo secondary).
|
||||
patroni['username'] = 'PATRONI_API_USERNAME'
|
||||
patroni['password'] = 'PATRONI_API_PASSWORD'
|
||||
patroni['replication_password'] = 'PLAIN_TEXT_POSTGRESQL_REPLICATION_PASSWORD'
|
||||
|
||||
# We list all secondary instances as they can all become a Standby Leader
|
||||
|
@ -727,16 +729,18 @@ For each Patroni instance on the secondary site:
|
|||
patroni['standby_cluster']['host'] = 'INTERNAL_LOAD_BALANCER_PRIMARY_IP'
|
||||
patroni['standby_cluster']['port'] = INTERNAL_LOAD_BALANCER_PRIMARY_PORT
|
||||
patroni['standby_cluster']['primary_slot_name'] = 'geo_secondary' # Or the unique replication slot name you setup before
|
||||
patroni['username'] = 'PATRONI_API_USERNAME'
|
||||
patroni['password'] = 'PATRONI_API_PASSWORD'
|
||||
patroni['replication_password'] = 'PLAIN_TEXT_POSTGRESQL_REPLICATION_PASSWORD'
|
||||
patroni['use_pg_rewind'] = true
|
||||
patroni['postgresql']['max_wal_senders'] = 5 # A minimum of three for one replica, plus two for each additional replica
|
||||
patroni['postgresql']['max_replication_slots'] = 5 # A minimum of three for one replica, plus two for each additional replica
|
||||
|
||||
|
||||
postgresql['pgbouncer_user_password'] = 'PGBOUNCER_PASSWORD_HASH'
|
||||
postgresql['sql_replication_password'] = 'POSTGRESQL_REPLICATION_PASSWORD_HASH'
|
||||
postgresql['sql_user_password'] = 'POSTGRESQL_PASSWORD_HASH'
|
||||
postgresql['listen_address'] = '0.0.0.0' # You can use a public or VPC address here instead
|
||||
|
||||
|
||||
gitlab_rails['dbpassword'] = 'POSTGRESQL_PASSWORD'
|
||||
gitlab_rails['enable'] = true
|
||||
gitlab_rails['auto_migrate'] = false
|
||||
|
@ -754,7 +758,7 @@ For each Patroni instance on the secondary site:
|
|||
- If you are configuring a Patroni standby cluster on a site that previously had a working Patroni cluster:
|
||||
|
||||
```shell
|
||||
gitlab-ctl stop patroni
|
||||
gitlab-ctl stop patroni
|
||||
rm -rf /var/opt/gitlab/postgresql/data
|
||||
/opt/gitlab/embedded/bin/patronictl -c /var/opt/gitlab/patroni/patroni.yaml remove postgresql-ha
|
||||
gitlab-ctl reconfigure
|
||||
|
@ -900,6 +904,8 @@ For each Patroni instance on the secondary site for the tracking database:
|
|||
]
|
||||
|
||||
# Patroni configuration
|
||||
patroni['username'] = 'PATRONI_API_USERNAME'
|
||||
patroni['password'] = 'PATRONI_API_PASSWORD'
|
||||
patroni['replication_password'] = 'PLAIN_TEXT_POSTGRESQL_REPLICATION_PASSWORD'
|
||||
patroni['postgresql']['max_wal_senders'] = 5 # A minimum of three for one replica, plus two for each additional replica
|
||||
|
||||
|
|
|
@ -121,6 +121,7 @@ The following metrics are available:
|
|||
| `action_cable_single_client_transmissions_total` | Counter | 13.10 | The number of ActionCable messages transmitted to any client in any channel | `server_mode` |
|
||||
| `action_cable_subscription_confirmations_total` | Counter | 13.10 | The number of ActionCable subscriptions from clients confirmed | `server_mode` |
|
||||
| `action_cable_subscription_rejections_total` | Counter | 13.10 | The number of ActionCable subscriptions from clients rejected | `server_mode` |
|
||||
| `action_cable_transmitted_bytes` | Histogram | 14.1 | Message size, in bytes, transmitted over action cable | `operation`, `channel` |
|
||||
| `gitlab_issuable_fast_count_by_state_total` | Counter | 13.5 | Total number of row count operations on issue/merge request list pages | |
|
||||
| `gitlab_issuable_fast_count_by_state_failures_total` | Counter | 13.5 | Number of soft-failed row count operations on issue/merge request list pages | |
|
||||
| `gitlab_external_http_total` | Counter | 13.8 | Total number of HTTP calls to external systems | `controller`, `action` |
|
||||
|
|
|
@ -157,6 +157,13 @@ We will need the following password information for the application's database u
|
|||
sudo gitlab-ctl pg-password-md5 POSTGRESQL_USERNAME
|
||||
```
|
||||
|
||||
#### Patroni information
|
||||
|
||||
We will need the following password information for the Patroni API:
|
||||
|
||||
- `PATRONI_API_USERNAME`. A username for basic auth to the API
|
||||
- `PATRONI_API_PASSWORD`. A password for basic auth to the API
|
||||
|
||||
#### PgBouncer information
|
||||
|
||||
When using default setup, minimum configuration requires:
|
||||
|
@ -236,6 +243,11 @@ postgresql['sql_replication_password'] = 'POSTGRESQL_REPLICATION_PASSWORD_HASH'
|
|||
# Replace POSTGRESQL_PASSWORD_HASH with a generated md5 value
|
||||
postgresql['sql_user_password'] = 'POSTGRESQL_PASSWORD_HASH'
|
||||
|
||||
# Replace PATRONI_API_USERNAME with a username for Patroni Rest API calls (use the same username in all nodes)
|
||||
patroni['username'] = 'PATRONI_API_USERNAME'
|
||||
# Replace PATRONI_API_PASSWORD with a password for Patroni Rest API calls (use the same password in all nodes)
|
||||
patroni['password'] = 'PATRONI_API_PASSWORD'
|
||||
|
||||
# Sets `max_replication_slots` to double the number of database nodes.
|
||||
# Patroni uses one extra slot per node when initiating the replication.
|
||||
patroni['postgresql']['max_replication_slots'] = X
|
||||
|
@ -246,7 +258,7 @@ patroni['postgresql']['max_replication_slots'] = X
|
|||
patroni['postgresql']['max_wal_senders'] = X+1
|
||||
|
||||
# Replace XXX.XXX.XXX.XXX/YY with Network Address
|
||||
postgresql['trust_auth_cidr_addresses'] = %w(XXX.XXX.XXX.XXX/YY)
|
||||
postgresql['trust_auth_cidr_addresses'] = %w(XXX.XXX.XXX.XXX/YY 127.0.0.1/32)
|
||||
|
||||
# Replace placeholders:
|
||||
#
|
||||
|
@ -259,8 +271,8 @@ consul['configuration'] = {
|
|||
# END user configuration
|
||||
```
|
||||
|
||||
You do not need an additional or different configuration for replica nodes. As a matter of fact, you don't have to have
|
||||
a predetermined primary node. Therefore all database nodes use the same configuration.
|
||||
All database nodes use the same configuration. The leader node is not determined in configuration,
|
||||
and there is no additional or different configuration for either leader or replica nodes.
|
||||
|
||||
Once the configuration of a node is done, you must [reconfigure Omnibus GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure)
|
||||
on each node for the changes to take effect.
|
||||
|
@ -555,10 +567,12 @@ gitlab_rails['auto_migrate'] = false
|
|||
|
||||
postgresql['pgbouncer_user_password'] = '771a8625958a529132abe6f1a4acb19c'
|
||||
postgresql['sql_user_password'] = '450409b85a0223a214b5fb1484f34d0f'
|
||||
patroni['username'] = 'PATRONI_API_USERNAME'
|
||||
patroni['password'] = 'PATRONI_API_PASSWORD'
|
||||
patroni['postgresql']['max_replication_slots'] = 6
|
||||
patroni['postgresql']['max_wal_senders'] = 7
|
||||
|
||||
postgresql['trust_auth_cidr_addresses'] = %w(10.6.0.0/16)
|
||||
postgresql['trust_auth_cidr_addresses'] = %w(10.6.0.0/16 127.0.0.1/32)
|
||||
|
||||
# Configure the Consul agent
|
||||
consul['services'] = %w(postgresql)
|
||||
|
@ -642,12 +656,15 @@ postgresql['sql_user_password'] = '450409b85a0223a214b5fb1484f34d0f'
|
|||
# Patroni uses one extra slot per node when initiating the replication.
|
||||
patroni['postgresql']['max_replication_slots'] = 6
|
||||
|
||||
patroni['username'] = 'PATRONI_API_USERNAME'
|
||||
patroni['password'] = 'PATRONI_API_PASSWORD'
|
||||
|
||||
# Set `max_wal_senders` to one more than the number of replication slots in the cluster.
|
||||
# This is used to prevent replication from using up all of the
|
||||
# available database connections.
|
||||
patroni['postgresql']['max_wal_senders'] = 7
|
||||
|
||||
postgresql['trust_auth_cidr_addresses'] = %w(10.6.0.0/16)
|
||||
postgresql['trust_auth_cidr_addresses'] = %w(10.6.0.0/16 127.0.0.1/32)
|
||||
|
||||
consul['configuration'] = {
|
||||
server: true,
|
||||
|
@ -721,6 +738,97 @@ functional or does not have a leader, Patroni and by extension PostgreSQL will n
|
|||
API which can be accessed via its [default port](https://docs.gitlab.com/omnibus/package-information/defaults.html#patroni)
|
||||
on each node.
|
||||
|
||||
### Check replication status
|
||||
|
||||
Run `gitlab-ctl patroni members` to query Patroni for a summary of the cluster status:
|
||||
|
||||
```plaintext
|
||||
+ Cluster: postgresql-ha (6970678148837286213) ------+---------+---------+----+-----------+
|
||||
| Member | Host | Role | State | TL | Lag in MB |
|
||||
+-------------------------------------+--------------+---------+---------+----+-----------+
|
||||
| gitlab-database-1.example.com | 172.18.0.111 | Replica | running | 5 | 0 |
|
||||
| gitlab-database-2.example.com | 172.18.0.112 | Replica | running | 5 | 100 |
|
||||
| gitlab-database-3.example.com | 172.18.0.113 | Leader | running | 5 | |
|
||||
+-------------------------------------+--------------+---------+---------+----+-----------+
|
||||
```
|
||||
|
||||
To verify the status of replication:
|
||||
|
||||
```shell
|
||||
echo 'select * from pg_stat_wal_receiver\x\g\x \n select * from pg_stat_replication\x\g\x' | gitlab-psql
|
||||
```
|
||||
|
||||
The same command can be run on all three database servers, and will return any information
|
||||
about replication available depending on the role the server is performing.
|
||||
|
||||
The leader should return one record per replica:
|
||||
|
||||
```sql
|
||||
-[ RECORD 1 ]----+------------------------------
|
||||
pid | 371
|
||||
usesysid | 16384
|
||||
usename | gitlab_replicator
|
||||
application_name | gitlab-database-1.example.com
|
||||
client_addr | 172.18.0.111
|
||||
client_hostname |
|
||||
client_port | 42900
|
||||
backend_start | 2021-06-14 08:01:59.580341+00
|
||||
backend_xmin |
|
||||
state | streaming
|
||||
sent_lsn | 0/EA13220
|
||||
write_lsn | 0/EA13220
|
||||
flush_lsn | 0/EA13220
|
||||
replay_lsn | 0/EA13220
|
||||
write_lag |
|
||||
flush_lag |
|
||||
replay_lag |
|
||||
sync_priority | 0
|
||||
sync_state | async
|
||||
reply_time | 2021-06-18 19:17:14.915419+00
|
||||
```
|
||||
|
||||
Investigate further if:
|
||||
|
||||
- There are missing or extra records.
|
||||
- `reply_time` is not current.
|
||||
|
||||
The `lsn` fields relate to which write-ahead-log segments have been replicated.
|
||||
Run the following on the leader to find out the current LSN:
|
||||
|
||||
```shell
|
||||
echo 'SELECT pg_current_wal_lsn();' | gitlab-psql
|
||||
```
|
||||
|
||||
If a replica is not in sync, `gitlab-ctl patroni members` indicates the volume
|
||||
of missing data, and the `lag` fields indicate the elapsed time.
|
||||
|
||||
Read more about the data returned by the leader
|
||||
[in the PostgreSQL documentation](https://www.postgresql.org/docs/12/monitoring-stats.html#PG-STAT-REPLICATION-VIEW),
|
||||
including other values for the `state` field.
|
||||
|
||||
The replicas should return:
|
||||
|
||||
```sql
|
||||
-[ RECORD 1 ]---------+-------------------------------------------------------------------------------------------------
|
||||
pid | 391
|
||||
status | streaming
|
||||
receive_start_lsn | 0/D000000
|
||||
receive_start_tli | 5
|
||||
received_lsn | 0/EA13220
|
||||
received_tli | 5
|
||||
last_msg_send_time | 2021-06-18 19:16:54.807375+00
|
||||
last_msg_receipt_time | 2021-06-18 19:16:54.807512+00
|
||||
latest_end_lsn | 0/EA13220
|
||||
latest_end_time | 2021-06-18 19:07:23.844879+00
|
||||
slot_name | gitlab-database-1.example.com
|
||||
sender_host | 172.18.0.113
|
||||
sender_port | 5432
|
||||
conninfo | user=gitlab_replicator host=172.18.0.113 port=5432 application_name=gitlab-database-1.example.com
|
||||
```
|
||||
|
||||
Read more about the data returned by the replica
|
||||
[in the PostgreSQL documentation](https://www.postgresql.org/docs/12/monitoring-stats.html#PG-STAT-WAL-RECEIVER-VIEW).
|
||||
|
||||
### Selecting the appropriate Patroni replication method
|
||||
|
||||
[Review the Patroni documentation carefully](https://patroni.readthedocs.io/en/latest/SETTINGS.html#postgresql)
|
||||
|
@ -1017,6 +1125,29 @@ postgresql['trust_auth_cidr_addresses'] = %w(123.123.123.123/32 <other_cidrs>)
|
|||
|
||||
[Reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
|
||||
|
||||
### Reinitialize a replica
|
||||
|
||||
If replication is not occurring, it may be necessary to reinitialize a replica.
|
||||
|
||||
1. On any server in the cluster, determine the Cluster and Member names,
|
||||
and check the replication lag by running `gitlab-ctl patroni members`. Here is an example:
|
||||
|
||||
```plaintext
|
||||
+ Cluster: postgresql-ha (6970678148837286213) ------+---------+---------+----+-----------+
|
||||
| Member | Host | Role | State | TL | Lag in MB |
|
||||
+-------------------------------------+--------------+---------+---------+----+-----------+
|
||||
| gitlab-database-1.example.com | 172.18.0.111 | Replica | running | 5 | 0 |
|
||||
| gitlab-database-2.example.com | 172.18.0.112 | Replica | running | 5 | 100 |
|
||||
| gitlab-database-3.example.com | 172.18.0.113 | Leader | running | 5 | |
|
||||
+-------------------------------------+--------------+---------+---------+----+-----------+
|
||||
```
|
||||
|
||||
1. Reinitialize the affected replica server:
|
||||
|
||||
```plaintext
|
||||
gitlab-ctl patroni reinitialize-replica postgresql-ha gitlab-database-2.example.com
|
||||
```
|
||||
|
||||
### Reset the Patroni state in Consul
|
||||
|
||||
WARNING:
|
||||
|
@ -1058,6 +1189,70 @@ To reset the Patroni state in Consul:
|
|||
|
||||
If you are still seeing issues, the next step is restoring the last healthy backup.
|
||||
|
||||
### Errors in the Patroni log about a `pg_hba.conf` entry for `127.0.0.1`
|
||||
|
||||
The following log entry in the Patroni log indicates the replication is not working
|
||||
and a configuration change is needed:
|
||||
|
||||
```plaintext
|
||||
FATAL: no pg_hba.conf entry for replication connection from host "127.0.0.1", user "gitlab_replicator"
|
||||
```
|
||||
|
||||
To fix the problem, ensure the loopback interface is included in the CIDR addresses list:
|
||||
|
||||
1. Edit `/etc/gitlab/gitlab.rb`:
|
||||
|
||||
```ruby
|
||||
postgresql['trust_auth_cidr_addresses'] = %w(<other_cidrs> 127.0.0.1/32)
|
||||
```
|
||||
|
||||
1. [Reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
|
||||
1. Check that [all the replicas are synchronized](#check-replication-status)
|
||||
|
||||
### Errors in Patroni logs: the requested start point is ahead of the WAL flush position
|
||||
|
||||
This error indicates that the database is not replicating:
|
||||
|
||||
```plaintext
|
||||
FATAL: could not receive data from WAL stream: ERROR: requested starting point 0/5000000 is ahead of the WAL flush position of this server 0/4000388
|
||||
```
|
||||
|
||||
This example error is from a replica that was initially misconfigured, and had never replicated.
|
||||
|
||||
Fix it [by reinitializing the replica](#reinitialize-a-replica).
|
||||
|
||||
### Patroni fails to start with `MemoryError`
|
||||
|
||||
Patroni may fail to start, logging an error and stack trace:
|
||||
|
||||
```plaintext
|
||||
MemoryError
|
||||
Traceback (most recent call last):
|
||||
File "/opt/gitlab/embedded/bin/patroni", line 8, in <module>
|
||||
sys.exit(main())
|
||||
[..]
|
||||
File "/opt/gitlab/embedded/lib/python3.7/ctypes/__init__.py", line 273, in _reset_cache
|
||||
CFUNCTYPE(c_int)(lambda: None)
|
||||
```
|
||||
|
||||
If the stack trace ends with `CFUNCTYPE(c_int)(lambda: None)`, this code triggers `MemoryError`
|
||||
if the Linux server has been hardened for security.
|
||||
|
||||
The code causes Python to write temporary executable files, and if it cannot find a filesystem
|
||||
in which to do this, for example if `noexec` is set on the `/tmp` filesystem, it fails with
|
||||
`MemoryError` ([read more in the issue](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/6184)).
|
||||
|
||||
Workarounds:
|
||||
|
||||
- Remove `noexec` from the mount options for filesystems like `/tmp` and `/var/tmp`.
|
||||
- If set to enforcing, SELinux may also prevent these operations. Verify the issue is fixed by setting
|
||||
SELinux to permissive.
|
||||
|
||||
Omnibus GitLab has shipped with Patroni since 13.1 along with a build of Python 3.7.
|
||||
Workarounds should stop being required when GitLab 14.x starts shipping with
|
||||
[a later version of Python](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/6164) as
|
||||
the code which causes this was removed from Python 3.8.
|
||||
|
||||
### Issues with other components
|
||||
|
||||
If you're running into an issue with a component not outlined here, be sure to check the troubleshooting section of their specific documentation page:
|
||||
|
|
|
@ -598,8 +598,12 @@ in the second step, do not supply the `EXTERNAL_URL` value.
|
|||
# Replace POSTGRESQL_PASSWORD_HASH with a generated md5 value
|
||||
postgresql['sql_user_password'] = '<postgresql_password_hash>'
|
||||
|
||||
# Set up basic authentication for the Patroni API (use the same username/password in all nodes).
|
||||
patroni['username'] = '<patroni_api_username>'
|
||||
patroni['password'] = '<patroni_api_password>'
|
||||
|
||||
# Replace XXX.XXX.XXX.XXX/YY with Network Address
|
||||
postgresql['trust_auth_cidr_addresses'] = %w(10.6.0.0/24)
|
||||
postgresql['trust_auth_cidr_addresses'] = %w(10.6.0.0/24 127.0.0.1/32)
|
||||
|
||||
# Set the network addresses that the exporters will listen on for monitoring
|
||||
node_exporter['listen_address'] = '0.0.0.0:9100'
|
||||
|
@ -1403,7 +1407,7 @@ in the second step, do not supply the `EXTERNAL_URL` value.
|
|||
postgresql['sql_user_password'] = "<praefect_postgresql_password_hash>"
|
||||
|
||||
# Replace XXX.XXX.XXX.XXX/YY with Network Address
|
||||
postgresql['trust_auth_cidr_addresses'] = %w(10.6.0.0/24)
|
||||
postgresql['trust_auth_cidr_addresses'] = %w(10.6.0.0/24 127.0.0.1/32)
|
||||
|
||||
# Set the network addresses that the exporters will listen on for monitoring
|
||||
node_exporter['listen_address'] = '0.0.0.0:9100'
|
||||
|
@ -1681,7 +1685,7 @@ On each node:
|
|||
# balancer.
|
||||
gitlab_rails['internal_api_url'] = 'https://gitlab.example.com'
|
||||
|
||||
# Gitaly
|
||||
# Gitaly
|
||||
gitaly['enable'] = true
|
||||
|
||||
# Make Gitaly accept connections on all network interfaces. You must use
|
||||
|
|
|
@ -600,8 +600,12 @@ in the second step, do not supply the `EXTERNAL_URL` value.
|
|||
# Replace POSTGRESQL_PASSWORD_HASH with a generated md5 value
|
||||
postgresql['sql_user_password'] = '<postgresql_password_hash>'
|
||||
|
||||
# Set up basic authentication for the Patroni API (use the same username/password in all nodes).
|
||||
patroni['username'] = '<patroni_api_username>'
|
||||
patroni['password'] = '<patroni_api_password>'
|
||||
|
||||
# Replace XXX.XXX.XXX.XXX/YY with Network Address
|
||||
postgresql['trust_auth_cidr_addresses'] = %w(10.6.0.0/24)
|
||||
postgresql['trust_auth_cidr_addresses'] = %w(10.6.0.0/24 127.0.0.1/32)
|
||||
|
||||
# Set the network addresses that the exporters will listen on for monitoring
|
||||
node_exporter['listen_address'] = '0.0.0.0:9100'
|
||||
|
@ -863,7 +867,7 @@ a node and change its status from primary to replica (and vice versa).
|
|||
redis_exporter['flags'] = {
|
||||
'redis.addr' => 'redis://10.6.0.51:6379',
|
||||
'redis.password' => 'redis-password-goes-here',
|
||||
}
|
||||
}
|
||||
|
||||
# Prevent database migrations from running on upgrade automatically
|
||||
gitlab_rails['auto_migrate'] = false
|
||||
|
@ -1421,7 +1425,7 @@ in the second step, do not supply the `EXTERNAL_URL` value.
|
|||
postgresql['sql_user_password'] = "<praefect_postgresql_password_hash>"
|
||||
|
||||
# Replace XXX.XXX.XXX.XXX/YY with Network Address
|
||||
postgresql['trust_auth_cidr_addresses'] = %w(10.6.0.0/24)
|
||||
postgresql['trust_auth_cidr_addresses'] = %w(10.6.0.0/24 127.0.0.1/32)
|
||||
|
||||
# Set the network addresses that the exporters will listen on for monitoring
|
||||
node_exporter['listen_address'] = '0.0.0.0:9100'
|
||||
|
@ -1699,7 +1703,7 @@ On each node:
|
|||
# balancer.
|
||||
gitlab_rails['internal_api_url'] = 'https://gitlab.example.com'
|
||||
|
||||
# Gitaly
|
||||
# Gitaly
|
||||
gitaly['enable'] = true
|
||||
|
||||
# Make Gitaly accept connections on all network interfaces. You must use
|
||||
|
|
|
@ -848,7 +848,7 @@ in the second step, do not supply the `EXTERNAL_URL` value.
|
|||
```ruby
|
||||
# Disable all components except Patroni and Consul
|
||||
roles(['patroni_role'])
|
||||
|
||||
|
||||
# PostgreSQL configuration
|
||||
postgresql['listen_address'] = '0.0.0.0'
|
||||
|
||||
|
@ -866,7 +866,7 @@ in the second step, do not supply the `EXTERNAL_URL` value.
|
|||
|
||||
# Prevent database migrations from running on upgrade automatically
|
||||
gitlab_rails['auto_migrate'] = false
|
||||
|
||||
|
||||
# Configure the Consul agent
|
||||
consul['services'] = %w(postgresql)
|
||||
## Enable service discovery for Prometheus
|
||||
|
@ -882,8 +882,12 @@ in the second step, do not supply the `EXTERNAL_URL` value.
|
|||
# Replace POSTGRESQL_PASSWORD_HASH with a generated md5 value
|
||||
postgresql['sql_user_password'] = '<postgresql_password_hash>'
|
||||
|
||||
# Set up basic authentication for the Patroni API (use the same username/password in all nodes).
|
||||
patroni['username'] = '<patroni_api_username>'
|
||||
patroni['password'] = '<patroni_api_password>'
|
||||
|
||||
# Replace XXX.XXX.XXX.XXX/YY with Network Address
|
||||
postgresql['trust_auth_cidr_addresses'] = %w(10.6.0.0/24)
|
||||
postgresql['trust_auth_cidr_addresses'] = %w(10.6.0.0/24 127.0.0.1/32)
|
||||
|
||||
# Set the network addresses that the exporters will listen on for monitoring
|
||||
node_exporter['listen_address'] = '0.0.0.0:9100'
|
||||
|
@ -1127,7 +1131,7 @@ in the second step, do not supply the `EXTERNAL_URL` value.
|
|||
postgresql['sql_user_password'] = "<praefect_postgresql_password_hash>"
|
||||
|
||||
# Replace XXX.XXX.XXX.XXX/YY with Network Address
|
||||
postgresql['trust_auth_cidr_addresses'] = %w(10.6.0.0/24)
|
||||
postgresql['trust_auth_cidr_addresses'] = %w(10.6.0.0/24 127.0.0.1/32)
|
||||
|
||||
# Set the network addresses that the exporters will listen on for monitoring
|
||||
node_exporter['listen_address'] = '0.0.0.0:9100'
|
||||
|
|
|
@ -608,8 +608,12 @@ in the second step, do not supply the `EXTERNAL_URL` value.
|
|||
# Replace POSTGRESQL_PASSWORD_HASH with a generated md5 value
|
||||
postgresql['sql_user_password'] = '<postgresql_password_hash>'
|
||||
|
||||
# Set up basic authentication for the Patroni API (use the same username/password in all nodes).
|
||||
patroni['username'] = '<patroni_api_username>'
|
||||
patroni['password'] = '<patroni_api_password>'
|
||||
|
||||
# Replace XXX.XXX.XXX.XXX/YY with Network Address
|
||||
postgresql['trust_auth_cidr_addresses'] = %w(10.6.0.0/24)
|
||||
postgresql['trust_auth_cidr_addresses'] = %w(10.6.0.0/24 127.0.0.1/32)
|
||||
|
||||
# Set the network addresses that the exporters will listen on for monitoring
|
||||
node_exporter['listen_address'] = '0.0.0.0:9100'
|
||||
|
@ -872,7 +876,7 @@ a node and change its status from primary to replica (and vice versa).
|
|||
'redis.addr' => 'redis://10.6.0.51:6379',
|
||||
'redis.password' => 'redis-password-goes-here',
|
||||
}
|
||||
|
||||
|
||||
# Prevent database migrations from running on upgrade automatically
|
||||
gitlab_rails['auto_migrate'] = false
|
||||
```
|
||||
|
@ -1425,7 +1429,7 @@ in the second step, do not supply the `EXTERNAL_URL` value.
|
|||
postgresql['sql_user_password'] = "<praefect_postgresql_password_hash>"
|
||||
|
||||
# Replace XXX.XXX.XXX.XXX/YY with Network Address
|
||||
postgresql['trust_auth_cidr_addresses'] = %w(10.6.0.0/24)
|
||||
postgresql['trust_auth_cidr_addresses'] = %w(10.6.0.0/24 127.0.0.1/32)
|
||||
|
||||
# Set the network addresses that the exporters will listen on for monitoring
|
||||
node_exporter['listen_address'] = '0.0.0.0:9100'
|
||||
|
@ -1703,7 +1707,7 @@ On each node:
|
|||
# balancer.
|
||||
gitlab_rails['internal_api_url'] = 'https://gitlab.example.com'
|
||||
|
||||
# Gitaly
|
||||
# Gitaly
|
||||
gitaly['enable'] = true
|
||||
|
||||
# Make Gitaly accept connections on all network interfaces. You must use
|
||||
|
@ -1929,7 +1933,7 @@ To configure the Sidekiq nodes, on each one:
|
|||
## Set number of Sidekiq threads per queue process to the recommend number of 10
|
||||
sidekiq['max_concurrency'] = 10
|
||||
|
||||
# Monitoring
|
||||
# Monitoring
|
||||
consul['enable'] = true
|
||||
consul['monitoring_service_discovery'] = true
|
||||
|
||||
|
|
|
@ -844,7 +844,7 @@ in the second step, do not supply the `EXTERNAL_URL` value.
|
|||
# Sets `max_replication_slots` to double the number of database nodes.
|
||||
# Patroni uses one extra slot per node when initiating the replication.
|
||||
patroni['postgresql']['max_replication_slots'] = 8
|
||||
|
||||
|
||||
# Set `max_wal_senders` to one more than the number of replication slots in the cluster.
|
||||
# This is used to prevent replication from using up all of the
|
||||
# available database connections.
|
||||
|
@ -871,8 +871,12 @@ in the second step, do not supply the `EXTERNAL_URL` value.
|
|||
# Replace POSTGRESQL_PASSWORD_HASH with a generated md5 value
|
||||
postgresql['sql_user_password'] = '<postgresql_password_hash>'
|
||||
|
||||
# Set up basic authentication for the Patroni API (use the same username/password in all nodes).
|
||||
patroni['username'] = '<patroni_api_username>'
|
||||
patroni['password'] = '<patroni_api_password>'
|
||||
|
||||
# Replace XXX.XXX.XXX.XXX/YY with Network Address
|
||||
postgresql['trust_auth_cidr_addresses'] = %w(10.6.0.0/24)
|
||||
postgresql['trust_auth_cidr_addresses'] = %w(10.6.0.0/24 127.0.0.1/32)
|
||||
|
||||
# Set the network addresses that the exporters will listen on for monitoring
|
||||
node_exporter['listen_address'] = '0.0.0.0:9100'
|
||||
|
@ -1116,7 +1120,7 @@ in the second step, do not supply the `EXTERNAL_URL` value.
|
|||
postgresql['sql_user_password'] = "<praefect_postgresql_password_hash>"
|
||||
|
||||
# Replace XXX.XXX.XXX.XXX/YY with Network Address
|
||||
postgresql['trust_auth_cidr_addresses'] = %w(10.6.0.0/24)
|
||||
postgresql['trust_auth_cidr_addresses'] = %w(10.6.0.0/24 127.0.0.1/32)
|
||||
|
||||
# Set the network addresses that the exporters will listen on for monitoring
|
||||
node_exporter['listen_address'] = '0.0.0.0:9100'
|
||||
|
|
|
@ -15290,6 +15290,7 @@ Name of the feature that the callout is for.
|
|||
| <a id="usercalloutfeaturenameenumpipeline_needs_banner"></a>`PIPELINE_NEEDS_BANNER` | Callout feature name for pipeline_needs_banner. |
|
||||
| <a id="usercalloutfeaturenameenumpipeline_needs_hover_tip"></a>`PIPELINE_NEEDS_HOVER_TIP` | Callout feature name for pipeline_needs_hover_tip. |
|
||||
| <a id="usercalloutfeaturenameenumregistration_enabled_callout"></a>`REGISTRATION_ENABLED_CALLOUT` | Callout feature name for registration_enabled_callout. |
|
||||
| <a id="usercalloutfeaturenameenumsecurity_configuration_devops_alert"></a>`SECURITY_CONFIGURATION_DEVOPS_ALERT` | Callout feature name for security_configuration_devops_alert. |
|
||||
| <a id="usercalloutfeaturenameenumsecurity_configuration_upgrade_banner"></a>`SECURITY_CONFIGURATION_UPGRADE_BANNER` | Callout feature name for security_configuration_upgrade_banner. |
|
||||
| <a id="usercalloutfeaturenameenumservice_templates_deprecated_callout"></a>`SERVICE_TEMPLATES_DEPRECATED_CALLOUT` | Callout feature name for service_templates_deprecated_callout. |
|
||||
| <a id="usercalloutfeaturenameenumsuggest_pipeline"></a>`SUGGEST_PIPELINE` | Callout feature name for suggest_pipeline. |
|
||||
|
|
|
@ -248,13 +248,13 @@ tries to steal tokens from other jobs.
|
|||
#### Limit GitLab CI/CD job token access
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/328553) in GitLab 14.1.
|
||||
> - [Deployed behind a feature flag](../user/feature_flags.md), disabled by default.
|
||||
> - Disabled on GitLab.com.
|
||||
> - Not recommended for production use.
|
||||
> - To use in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-ci-job-token-scope-limit). **(FREE SELF)**
|
||||
> - [Deployed behind a feature flag](../user/feature_flags.md), enabled by default.
|
||||
> - Enabled on GitLab.com.
|
||||
> - Recommended for production use.
|
||||
> - For GitLab self-managed instances, GitLab administrators can opt to [disable it](#enable-or-disable-ci-job-token-scope-limit). **(FREE SELF)**
|
||||
|
||||
This in-development feature might not be available for your use. There can be
|
||||
[risks when enabling features still in development](../user/feature_flags.md#risks-when-enabling-features-still-in-development).
|
||||
There can be
|
||||
[risks when disabling released features](../user/feature_flags.md#risks-when-disabling-released-features).
|
||||
Refer to this feature's version history for more details.
|
||||
|
||||
You can limit the access scope of a project's CI/CD job token to increase the
|
||||
|
@ -292,16 +292,10 @@ the feature with more strategic control of the access permissions.
|
|||
|
||||
##### Enable or disable CI job token scope limit **(FREE SELF)**
|
||||
|
||||
The GitLab CI/CD job token access scope limit is under development and not ready for production
|
||||
use. It is deployed behind a feature flag that is **disabled by default**.
|
||||
The GitLab CI/CD job token access scope limit is under development but ready for production
|
||||
use. It is deployed behind a feature flag that is **enabled by default**.
|
||||
[GitLab administrators with access to the GitLab Rails console](../administration/feature_flags.md)
|
||||
can enable it.
|
||||
|
||||
To enable it:
|
||||
|
||||
```ruby
|
||||
Feature.enable(:ci_scoped_job_token)
|
||||
```
|
||||
can disable the feature.
|
||||
|
||||
To disable it:
|
||||
|
||||
|
@ -309,6 +303,12 @@ To disable it:
|
|||
Feature.disable(:ci_scoped_job_token)
|
||||
```
|
||||
|
||||
To enable it:
|
||||
|
||||
```ruby
|
||||
Feature.enable(:ci_scoped_job_token)
|
||||
```
|
||||
|
||||
### Impersonation tokens
|
||||
|
||||
Impersonation tokens are a type of [personal access token](../user/profile/personal_access_tokens.md).
|
||||
|
|
|
@ -85,9 +85,6 @@ where:
|
|||
[extended configuration merged into the job](../yaml/index.md#merge-details).
|
||||
- YAML anchors are [replaced with the linked configuration](../yaml/index.md#anchors).
|
||||
|
||||
NOTE:
|
||||
You can only see the expanded view when editing the [default branch](../../user/project/repository/branches/default.md).
|
||||
|
||||
## Commit changes to CI configuration
|
||||
|
||||
The commit form appears at the bottom of each tab in the editor so you can commit
|
||||
|
|
|
@ -16,7 +16,7 @@ The following features remain available to Bronze and Starter customers, even th
|
|||
the tiers are no longer mentioned in GitLab documentation:
|
||||
|
||||
- [Activate GitLab EE with a license](../user/admin_area/license.md)
|
||||
- [Adding a help message to the login page](../user/admin_area/settings/help_page.md#adding-a-help-message-to-the-login-page)
|
||||
- [Add a help message to the sign-in page](../user/admin_area/settings/help_page.md#add-a-help-message-to-the-sign-in-page)
|
||||
- [Burndown and burnup charts](../user/project/milestones/burndown_and_burnup_charts.md),
|
||||
including [per-project charts](../user/project/milestones/index.md#project-burndown-charts) and
|
||||
[per-group charts](../user/project/milestones/index.md#group-burndown-charts)
|
||||
|
|
|
@ -5,41 +5,58 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
type: howto
|
||||
---
|
||||
|
||||
# Customizing the 'Help' and login page messages
|
||||
# Customize the Help and sign-in page messages
|
||||
|
||||
In large organizations, it is useful to have information about who to contact or where
|
||||
to go for help. You can customize and display this information on the GitLab server's
|
||||
`/help` page and on the GitLab login page.
|
||||
to go for help. You can customize and display this information on the GitLab `/help` page and on
|
||||
the GitLab sign-in page.
|
||||
|
||||
## Adding a help message to the help page
|
||||
## Add a help message to the Help page
|
||||
|
||||
You can add a help message, which is shown on the GitLab `/help` page (for example,
|
||||
<https://gitlab.com/help>) in a new section at the top of the `/help` page:
|
||||
You can add a help message, which is shown at the top of the GitLab `/help` page (for example,
|
||||
<https://gitlab.com/help>):
|
||||
|
||||
1. On the top bar, select **Menu >** **{admin}** **Admin**.
|
||||
1. In the left sidebar, select **Settings > Preferences**, then expand **Help page**.
|
||||
1. Under **Help page text**, fill in the information you wish to display on `/help`.
|
||||
1. Save your changes. You can now see the message on `/help`.
|
||||
1. Under **Additional text to show on the Help page**, fill in the information you wish to display on `/help`.
|
||||
1. Select **Save changes**. You can now see the message on `/help`.
|
||||
|
||||
NOTE:
|
||||
By default, `/help` is visible to unauthenticated users. However, if the
|
||||
[**Public** visibility level](visibility_and_access_controls.md#restricted-visibility-levels)
|
||||
is restricted, `/help` is visible only to signed-in users.
|
||||
|
||||
## Adding a help message to the login page **(STARTER)**
|
||||
## Add a help message to the sign-in page **(STARTER)**
|
||||
|
||||
You can add a help message, which is shown on the GitLab login page in a new section
|
||||
titled `Need Help?`, located below the login page message:
|
||||
You can add a help message, which is shown on the GitLab sign-in page. The message appears in a new
|
||||
section titled **Need Help?**, located below the sign-in page message:
|
||||
|
||||
1. On the top bar, select **Menu >** **{admin}** **Admin**.
|
||||
1. In the left sidebar, select **Settings > Preferences**, then expand **Help page**.
|
||||
1. Under **Help text**, fill in the information you wish to display on the login page.
|
||||
1. Under **Additional text to show on the sign-in page**, fill in the information you wish to
|
||||
display on the sign-in page.
|
||||
1. Select **Save changes**. You can now see the message on the sign-in page.
|
||||
|
||||
![help message on login page](img/help_page_help_text_v12_3.png)
|
||||
## Hide marketing-related entries from the Help page
|
||||
|
||||
1. Save your changes.
|
||||
GitLab marketing-related entries are occasionally shown on the Help page. To hide these entries:
|
||||
|
||||
![help message on login page example](img/help_page_help_text_ex_v12_3.png)
|
||||
1. On the top bar, select **Menu >** **{admin}** **Admin**.
|
||||
1. In the left sidebar, select **Settings > Preferences**, then expand **Help page**.
|
||||
1. Select the **Hide marketing-related entries from the Help page** checkbox.
|
||||
1. Select **Save changes**.
|
||||
|
||||
## Set a custom Support page URL
|
||||
|
||||
You can specify a custom URL to which users are directed when they:
|
||||
|
||||
- Select **Support** from the Help dropdown.
|
||||
- Select **See our website for help** on the Help page.
|
||||
|
||||
1. On the top bar, select **Menu >** **{admin}** **Admin**.
|
||||
1. In the left sidebar, select **Settings > Preferences**, then expand **Help page**.
|
||||
1. Enter the URL in the **Support page URL** field.
|
||||
1. Select **Save changes**.
|
||||
|
||||
<!-- ## Troubleshooting
|
||||
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 48 KiB |
Binary file not shown.
Before Width: | Height: | Size: 19 KiB |
|
@ -12,6 +12,7 @@ type: reference
|
|||
NOTE:
|
||||
This is the user documentation. To configure the Advanced Search,
|
||||
visit the [administrator documentation](../../integration/elasticsearch.md).
|
||||
Advanced Search is enabled in GitLab.com.
|
||||
|
||||
GitLab Advanced Search expands on the Basic Search with an additional set of
|
||||
features for faster, more advanced searches across the entire GitLab instance
|
||||
|
@ -34,6 +35,11 @@ The Advanced Search can be useful in various scenarios:
|
|||
Advanced Search is based on Elasticsearch, which is a purpose-built full
|
||||
text search engine that can be horizontally scaled so that it can provide
|
||||
search results in 1-2 seconds in most cases.
|
||||
- **Code Maintenance:**
|
||||
Finding all the code that needs to be updated at once across an entire
|
||||
instance can save time spent maintaining code.
|
||||
This is especially helpful for organizations with more than 10 active projects.
|
||||
This can also help build confidence is code refactoring to identify unknown impacts.
|
||||
- **Promote innersourcing:**
|
||||
Your company may consist of many different developer teams each of which has
|
||||
their own group where the various projects are hosted. Some of your applications
|
||||
|
|
|
@ -72,6 +72,7 @@ module Gitlab
|
|||
.deep_merge(rules_attributes)
|
||||
.deep_merge(allow_failure_criteria_attributes)
|
||||
.deep_merge(@cache.cache_attributes)
|
||||
.deep_merge(runner_tags)
|
||||
end
|
||||
|
||||
def bridge?
|
||||
|
@ -211,6 +212,16 @@ module Gitlab
|
|||
end
|
||||
end
|
||||
|
||||
def runner_tags
|
||||
{ tag_list: evaluate_runner_tags }.compact
|
||||
end
|
||||
|
||||
def evaluate_runner_tags
|
||||
@seed_attributes[:tag_list]&.map do |tag|
|
||||
ExpandVariables.expand_existing(tag, evaluate_context.variables)
|
||||
end
|
||||
end
|
||||
|
||||
# If a job uses `allow_failure:exit_codes` and `rules:allow_failure`
|
||||
# we need to prevent the exit codes from being persisted because they
|
||||
# would break the behavior defined by `rules:allow_failure`.
|
||||
|
|
|
@ -49,14 +49,14 @@ module Gitlab
|
|||
end
|
||||
|
||||
def kas_endpoint_url
|
||||
Gitlab::Kas.internal_url.delete_prefix('grpc://')
|
||||
Gitlab::Kas.internal_url.sub(%r{^grpc://|^grpcs://}, '')
|
||||
end
|
||||
|
||||
def credentials
|
||||
if Rails.env.test? || Rails.env.development?
|
||||
:this_channel_is_insecure
|
||||
else
|
||||
if URI(Gitlab::Kas.internal_url).scheme == 'grpcs'
|
||||
GRPC::Core::ChannelCredentials.new
|
||||
else
|
||||
:this_channel_is_insecure
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ module Gitlab
|
|||
TRANSMIT_SUBSCRIPTION_CONFIRMATION = :action_cable_subscription_confirmations_total
|
||||
TRANSMIT_SUBSCRIPTION_REJECTION = :action_cable_subscription_rejections_total
|
||||
BROADCAST = :action_cable_broadcasts_total
|
||||
DATA_TRANSMITTED_BYTES = :action_cable_transmitted_bytes
|
||||
|
||||
def transmit_subscription_confirmation(event)
|
||||
confirm_subscription_counter.increment
|
||||
|
@ -23,6 +24,14 @@ module Gitlab
|
|||
|
||||
def transmit(event)
|
||||
transmit_counter.increment
|
||||
|
||||
if event.payload.present?
|
||||
channel = event.payload[:channel_class]
|
||||
operation = operation_name_from(event.payload)
|
||||
data_size = ::ActiveSupport::JSON.encode(event.payload[:data]).bytesize
|
||||
|
||||
transmitted_bytes_histogram.observe({ channel: channel, operation: operation }, data_size)
|
||||
end
|
||||
end
|
||||
|
||||
def broadcast(event)
|
||||
|
@ -31,6 +40,13 @@ module Gitlab
|
|||
|
||||
private
|
||||
|
||||
# When possible tries to query operation name
|
||||
def operation_name_from(payload)
|
||||
data = payload.dig(:data, 'result', 'data') || {}
|
||||
|
||||
data.each_key.first
|
||||
end
|
||||
|
||||
def transmit_counter
|
||||
strong_memoize("transmission_counter") do
|
||||
::Gitlab::Metrics.counter(
|
||||
|
@ -66,6 +82,12 @@ module Gitlab
|
|||
)
|
||||
end
|
||||
end
|
||||
|
||||
def transmitted_bytes_histogram
|
||||
strong_memoize("transmitted_bytes_histogram") do
|
||||
::Gitlab::Metrics.histogram(DATA_TRANSMITTED_BYTES, 'Message size, in bytes, transmitted over action cable')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2188,6 +2188,15 @@ msgstr ""
|
|||
msgid "Additional text"
|
||||
msgstr ""
|
||||
|
||||
msgid "Additional text for the sign-in and Help page."
|
||||
msgstr ""
|
||||
|
||||
msgid "Additional text to show on the Help page"
|
||||
msgstr ""
|
||||
|
||||
msgid "Additional text to show on the sign-in page"
|
||||
msgstr ""
|
||||
|
||||
msgid "Address"
|
||||
msgstr ""
|
||||
|
||||
|
@ -3459,7 +3468,7 @@ msgstr ""
|
|||
msgid "Also unassign this user from related issues and merge requests"
|
||||
msgstr ""
|
||||
|
||||
msgid "Alternate support URL for help page and help dropdown"
|
||||
msgid "Alternate support URL for Help page and Help dropdown"
|
||||
msgstr ""
|
||||
|
||||
msgid "Alternatively, you can convert your account to a managed account by the %{group_name} group."
|
||||
|
@ -6409,12 +6418,18 @@ msgstr ""
|
|||
msgid "Checkout"
|
||||
msgstr ""
|
||||
|
||||
msgid "Checkout|$%{selectedPlanPrice} per pack per year"
|
||||
msgstr ""
|
||||
|
||||
msgid "Checkout|$%{selectedPlanPrice} per user per year"
|
||||
msgstr ""
|
||||
|
||||
msgid "Checkout|%{cardType} ending in %{lastFourDigits}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Checkout|%{name}'s CI minutes"
|
||||
msgstr ""
|
||||
|
||||
msgid "Checkout|%{name}'s GitLab subscription"
|
||||
msgstr ""
|
||||
|
||||
|
@ -6433,6 +6448,9 @@ msgstr ""
|
|||
msgid "Checkout|(x%{numberOfUsers})"
|
||||
msgstr ""
|
||||
|
||||
msgid "Checkout|(x%{quantity})"
|
||||
msgstr ""
|
||||
|
||||
msgid "Checkout|Billing address"
|
||||
msgstr ""
|
||||
|
||||
|
@ -16183,12 +16201,6 @@ msgstr ""
|
|||
msgid "Help"
|
||||
msgstr ""
|
||||
|
||||
msgid "Help page"
|
||||
msgstr ""
|
||||
|
||||
msgid "Help page text and support page url."
|
||||
msgstr ""
|
||||
|
||||
msgid "Helps prevent bots from brute-force attacks."
|
||||
msgstr ""
|
||||
|
||||
|
@ -16248,7 +16260,7 @@ msgstr ""
|
|||
msgid "Hide list"
|
||||
msgstr ""
|
||||
|
||||
msgid "Hide marketing-related entries from help"
|
||||
msgid "Hide marketing-related entries from the Help page."
|
||||
msgstr ""
|
||||
|
||||
msgid "Hide payload"
|
||||
|
@ -24227,9 +24239,6 @@ msgstr ""
|
|||
msgid "Pipelines|Merged YAML is view only"
|
||||
msgstr ""
|
||||
|
||||
msgid "Pipelines|Merged YAML unavailable"
|
||||
msgstr ""
|
||||
|
||||
msgid "Pipelines|More Information"
|
||||
msgstr ""
|
||||
|
||||
|
@ -24266,9 +24275,6 @@ msgstr ""
|
|||
msgid "Pipelines|The GitLab CI configuration could not be updated."
|
||||
msgstr ""
|
||||
|
||||
msgid "Pipelines|The merged YAML view is only available for the default branch. %{linkStart}Learn more.%{linkEnd}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Pipelines|There are currently no finished pipelines."
|
||||
msgstr ""
|
||||
|
||||
|
@ -25838,6 +25844,9 @@ msgstr ""
|
|||
msgid "ProjectSettings|Fast-forward merges only."
|
||||
msgstr ""
|
||||
|
||||
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
|
||||
msgstr ""
|
||||
|
||||
msgid "ProjectSettings|Forks"
|
||||
msgstr ""
|
||||
|
||||
|
@ -25859,9 +25868,6 @@ msgstr ""
|
|||
msgid "ProjectSettings|LFS objects from this repository are available to forks. %{linkStart}How do I remove them?%{linkEnd}"
|
||||
msgstr ""
|
||||
|
||||
msgid "ProjectSettings|Lightweight issue tracking system."
|
||||
msgstr ""
|
||||
|
||||
msgid "ProjectSettings|Manages large files such as audio, video, and graphics files."
|
||||
msgstr ""
|
||||
|
||||
|
@ -29033,6 +29039,9 @@ msgstr ""
|
|||
msgid "SecurityConfiguration|Enable %{feature}"
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityConfiguration|Enable Auto DevOps"
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityConfiguration|Enabled"
|
||||
msgstr ""
|
||||
|
||||
|
@ -29066,6 +29075,9 @@ msgstr ""
|
|||
msgid "SecurityConfiguration|Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityConfiguration|Quickly enable all continuous testing and compliance tools by enabling %{linkStart}Auto DevOps%{linkEnd}"
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityConfiguration|Runtime security metrics for application environments."
|
||||
msgstr ""
|
||||
|
||||
|
@ -29408,7 +29420,7 @@ msgstr ""
|
|||
msgid "See metrics"
|
||||
msgstr ""
|
||||
|
||||
msgid "See our website for getting help"
|
||||
msgid "See our website for help"
|
||||
msgstr ""
|
||||
|
||||
msgid "See the affected projects in the GitLab admin panel"
|
||||
|
@ -30330,6 +30342,9 @@ msgstr ""
|
|||
msgid "Sign up was successful! Please confirm your email to sign in."
|
||||
msgstr ""
|
||||
|
||||
msgid "Sign-in and Help page"
|
||||
msgstr ""
|
||||
|
||||
msgid "Sign-in count:"
|
||||
msgstr ""
|
||||
|
||||
|
@ -32187,6 +32202,15 @@ msgstr ""
|
|||
msgid "Terraform"
|
||||
msgstr ""
|
||||
|
||||
msgid "TerraformBanner|Learn more about GitLab's Backend State"
|
||||
msgstr ""
|
||||
|
||||
msgid "TerraformBanner|The GitLab managed Terraform state backend can store your Terraform state easily and securely, and spares you from setting up additional remote resources. Its features include: versioning, encryption of the state file both in transit and at rest, locking, and remote Terraform plan/apply execution."
|
||||
msgstr ""
|
||||
|
||||
msgid "TerraformBanner|Using Terraform? Try the GitLab Managed Terraform State"
|
||||
msgstr ""
|
||||
|
||||
msgid "Terraform|%{name} successfully removed"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -571,8 +571,8 @@ RSpec.describe 'Admin updates settings' do
|
|||
new_documentation_url = 'https://docs.gitlab.com'
|
||||
|
||||
page.within('.as-help-page') do
|
||||
fill_in 'Help page text', with: 'Example text'
|
||||
check 'Hide marketing-related entries from help'
|
||||
fill_in 'Additional text to show on the Help page', with: 'Example text'
|
||||
check 'Hide marketing-related entries from the Help page.'
|
||||
fill_in 'Support page URL', with: new_support_url
|
||||
fill_in 'Documentation pages URL', with: new_documentation_url
|
||||
click_button 'Save changes'
|
||||
|
|
|
@ -65,7 +65,7 @@ RSpec.describe 'Help Pages' do
|
|||
end
|
||||
|
||||
it 'uses a custom support url' do
|
||||
expect(page).to have_link "See our website for getting help", href: "http://example.com/help"
|
||||
expect(page).to have_link "See our website for help", href: "http://example.com/help"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
import { GlIcon, GlAlert } from '@gitlab/ui';
|
||||
import { GlIcon } from '@gitlab/ui';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
|
||||
import { EDITOR_READY_EVENT } from '~/editor/constants';
|
||||
import CiConfigMergedPreview from '~/pipeline_editor/components/editor/ci_config_merged_preview.vue';
|
||||
import { mockLintResponse, mockCiConfigPath } from '../../mock_data';
|
||||
|
||||
const DEFAULT_BRANCH = 'main';
|
||||
|
||||
describe('Text editor component', () => {
|
||||
let wrapper;
|
||||
|
||||
|
@ -18,7 +16,7 @@ describe('Text editor component', () => {
|
|||
},
|
||||
};
|
||||
|
||||
const createComponent = ({ props = {}, currentBranch = DEFAULT_BRANCH } = {}) => {
|
||||
const createComponent = ({ props = {} } = {}) => {
|
||||
wrapper = shallowMount(CiConfigMergedPreview, {
|
||||
propsData: {
|
||||
ciConfigData: mockLintResponse,
|
||||
|
@ -26,45 +24,20 @@ describe('Text editor component', () => {
|
|||
},
|
||||
provide: {
|
||||
ciConfigPath: mockCiConfigPath,
|
||||
defaultBranch: DEFAULT_BRANCH,
|
||||
},
|
||||
stubs: {
|
||||
SourceEditor: MockSourceEditor,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
currentBranch,
|
||||
};
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const findIcon = () => wrapper.findComponent(GlIcon);
|
||||
const findAlert = () => wrapper.findComponent(GlAlert);
|
||||
const findEditor = () => wrapper.findComponent(MockSourceEditor);
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
// This is testing a temporary feature.
|
||||
// It may be slightly hacky code that doesn't follow best practice.
|
||||
// See the related MR for more information.
|
||||
// https://gitlab.com/gitlab-org/gitlab/-/merge_requests/65972#note_626095644
|
||||
describe('on a non-default branch', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({ currentBranch: 'feature' });
|
||||
});
|
||||
|
||||
it('does not load the editor', () => {
|
||||
expect(findEditor().exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('renders an informational alert', () => {
|
||||
expect(findAlert().exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when status is valid', () => {
|
||||
beforeEach(() => {
|
||||
createComponent();
|
||||
|
|
|
@ -23,9 +23,10 @@ import {
|
|||
mockBlobContentQueryResponse,
|
||||
mockBlobContentQueryResponseEmptyCiFile,
|
||||
mockBlobContentQueryResponseNoCiFile,
|
||||
mockCiYml,
|
||||
mockCommitSha,
|
||||
mockDefaultBranch,
|
||||
mockProjectFullPath,
|
||||
mockCiYml,
|
||||
mockNewCommitShaResults,
|
||||
} from './mock_data';
|
||||
|
||||
|
@ -95,10 +96,16 @@ describe('Pipeline editor app component', () => {
|
|||
];
|
||||
|
||||
const resolvers = {
|
||||
Query: {
|
||||
commitSha() {
|
||||
return mockCommitSha;
|
||||
},
|
||||
},
|
||||
Mutation: {
|
||||
updateCommitSha: mockUpdateCommitSha,
|
||||
},
|
||||
};
|
||||
|
||||
mockApollo = createMockApollo(handlers, resolvers);
|
||||
|
||||
const options = {
|
||||
|
@ -170,6 +177,7 @@ describe('Pipeline editor app component', () => {
|
|||
expect(mockCiConfigData).toHaveBeenCalledWith({
|
||||
content: mockCiYml,
|
||||
projectPath: mockProjectFullPath,
|
||||
sha: mockCommitSha,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
import { GlBanner } from '@gitlab/ui';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import { setCookie, parseBoolean } from '~/lib/utils/common_utils';
|
||||
import TerraformNotification from '~/projects/terraform_notification/components/terraform_notification.vue';
|
||||
|
||||
jest.mock('~/lib/utils/common_utils');
|
||||
|
||||
const bannerDissmisedKey = 'terraform_notification_dismissed_for_project_1';
|
||||
|
||||
describe('TerraformNotificationBanner', () => {
|
||||
let wrapper;
|
||||
|
||||
const propsData = {
|
||||
projectId: 1,
|
||||
};
|
||||
const findBanner = () => wrapper.findComponent(GlBanner);
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = shallowMount(TerraformNotification, {
|
||||
propsData,
|
||||
stubs: { GlBanner },
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
parseBoolean.mockReturnValue(false);
|
||||
});
|
||||
|
||||
describe('when the dismiss cookie is set', () => {
|
||||
beforeEach(() => {
|
||||
parseBoolean.mockReturnValue(true);
|
||||
wrapper = shallowMount(TerraformNotification, {
|
||||
propsData,
|
||||
});
|
||||
});
|
||||
|
||||
it('should not render the banner', () => {
|
||||
expect(findBanner().exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the dismiss cookie is not set', () => {
|
||||
it('should render the banner', () => {
|
||||
expect(findBanner().exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when close button is clicked', () => {
|
||||
beforeEach(async () => {
|
||||
await findBanner().vm.$emit('close');
|
||||
});
|
||||
|
||||
it('should set the cookie with the bannerDissmisedKey', () => {
|
||||
expect(setCookie).toHaveBeenCalledWith(bannerDissmisedKey, true);
|
||||
});
|
||||
|
||||
it('should remove the banner', () => {
|
||||
expect(findBanner().exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,55 @@
|
|||
import { GlAlert } from '@gitlab/ui';
|
||||
import { mount } from '@vue/test-utils';
|
||||
import AutoDevopsAlert from '~/security_configuration/components/auto_dev_ops_alert.vue';
|
||||
|
||||
const autoDevopsHelpPagePath = '/autoDevopsHelpPagePath';
|
||||
const autoDevopsPath = '/enableAutoDevopsPath';
|
||||
|
||||
describe('AutoDevopsAlert component', () => {
|
||||
let wrapper;
|
||||
|
||||
const createComponent = () => {
|
||||
wrapper = mount(AutoDevopsAlert, {
|
||||
provide: {
|
||||
autoDevopsHelpPagePath,
|
||||
autoDevopsPath,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const findAlert = () => wrapper.findComponent(GlAlert);
|
||||
|
||||
beforeEach(() => {
|
||||
createComponent();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
it('contains correct body text', () => {
|
||||
expect(wrapper.text()).toContain('Quickly enable all');
|
||||
});
|
||||
|
||||
it('renders the link correctly', () => {
|
||||
const link = wrapper.find('a');
|
||||
|
||||
expect(link.attributes('href')).toBe(autoDevopsHelpPagePath);
|
||||
expect(link.text()).toBe('Auto DevOps');
|
||||
});
|
||||
|
||||
it('bubbles up dismiss events from the GlAlert', () => {
|
||||
expect(wrapper.emitted('dismiss')).toBe(undefined);
|
||||
|
||||
findAlert().vm.$emit('dismiss');
|
||||
|
||||
expect(wrapper.emitted('dismiss')).toEqual([[]]);
|
||||
});
|
||||
|
||||
it('has a button pointing to autoDevopsPath', () => {
|
||||
expect(findAlert().props()).toMatchObject({
|
||||
primaryButtonText: 'Enable Auto DevOps',
|
||||
primaryButtonLink: autoDevopsPath,
|
||||
});
|
||||
});
|
||||
});
|
|
@ -2,6 +2,7 @@ import { GlTab } from '@gitlab/ui';
|
|||
import { mount } from '@vue/test-utils';
|
||||
import { makeMockUserCalloutDismisser } from 'helpers/mock_user_callout_dismisser';
|
||||
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
|
||||
import AutoDevopsAlert from '~/security_configuration/components/auto_dev_ops_alert.vue';
|
||||
import {
|
||||
SAST_NAME,
|
||||
SAST_SHORT_NAME,
|
||||
|
@ -13,6 +14,7 @@ import {
|
|||
LICENSE_COMPLIANCE_HELP_PATH,
|
||||
} from '~/security_configuration/components/constants';
|
||||
import FeatureCard from '~/security_configuration/components/feature_card.vue';
|
||||
|
||||
import RedesignedSecurityConfigurationApp, {
|
||||
i18n,
|
||||
} from '~/security_configuration/components/redesigned_app.vue';
|
||||
|
@ -23,6 +25,8 @@ import {
|
|||
} from '~/vue_shared/security_reports/constants';
|
||||
|
||||
const upgradePath = '/upgrade';
|
||||
const autoDevopsHelpPagePath = '/autoDevopsHelpPagePath';
|
||||
const autoDevopsPath = '/autoDevopsPath';
|
||||
const gitlabCiHistoryPath = 'test/historyPath';
|
||||
|
||||
describe('redesigned App component', () => {
|
||||
|
@ -37,6 +41,8 @@ describe('redesigned App component', () => {
|
|||
propsData,
|
||||
provide: {
|
||||
upgradePath,
|
||||
autoDevopsHelpPagePath,
|
||||
autoDevopsPath,
|
||||
},
|
||||
stubs: {
|
||||
UserCalloutDismisser: makeMockUserCalloutDismisser({
|
||||
|
@ -76,6 +82,7 @@ describe('redesigned App component', () => {
|
|||
container: findByTestId('compliance-testing-tab'),
|
||||
});
|
||||
const findUpgradeBanner = () => wrapper.findComponent(UpgradeBanner);
|
||||
const findAutoDevopsAlert = () => wrapper.findComponent(AutoDevopsAlert);
|
||||
|
||||
const securityFeaturesMock = [
|
||||
{
|
||||
|
@ -154,6 +161,44 @@ describe('redesigned App component', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('autoDevOpsAlert', () => {
|
||||
describe('given the right props', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({
|
||||
augmentedSecurityFeatures: securityFeaturesMock,
|
||||
augmentedComplianceFeatures: complianceFeaturesMock,
|
||||
autoDevopsEnabled: false,
|
||||
gitlabCiPresent: false,
|
||||
canEnableAutoDevops: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('should show AutoDevopsAlert', () => {
|
||||
expect(findAutoDevopsAlert().exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('calls the dismiss callback when closing the AutoDevopsAlert', () => {
|
||||
expect(userCalloutDismissSpy).not.toHaveBeenCalled();
|
||||
|
||||
findAutoDevopsAlert().vm.$emit('dismiss');
|
||||
|
||||
expect(userCalloutDismissSpy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('given the wrong props', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({
|
||||
augmentedSecurityFeatures: securityFeaturesMock,
|
||||
augmentedComplianceFeatures: complianceFeaturesMock,
|
||||
});
|
||||
});
|
||||
it('should not show AutoDevopsAlert', () => {
|
||||
expect(findAutoDevopsAlert().exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('upgrade banner', () => {
|
||||
const makeAvailable = (available) => (feature) => ({ ...feature, available });
|
||||
|
||||
|
|
|
@ -876,6 +876,37 @@ RSpec.describe ProjectsHelper do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#show_terraform_banner?' do
|
||||
let_it_be(:ruby) { create(:programming_language, name: 'Ruby') }
|
||||
let_it_be(:hcl) { create(:programming_language, name: 'HCL') }
|
||||
|
||||
subject { helper.show_terraform_banner?(project) }
|
||||
|
||||
before do
|
||||
create(:repository_language, project: project, programming_language: language, share: 1)
|
||||
end
|
||||
|
||||
context 'the project does not contain terraform files' do
|
||||
let(:language) { ruby }
|
||||
|
||||
it { is_expected.to be_falsey }
|
||||
end
|
||||
|
||||
context 'the project contains terraform files' do
|
||||
let(:language) { hcl }
|
||||
|
||||
it { is_expected.to be_truthy }
|
||||
|
||||
context 'the project already has a terraform state' do
|
||||
before do
|
||||
create(:terraform_state, project: project)
|
||||
end
|
||||
|
||||
it { is_expected.to be_falsey }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#project_title' do
|
||||
subject { helper.project_title(project) }
|
||||
|
||||
|
|
|
@ -91,6 +91,20 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
|
|||
end
|
||||
end
|
||||
|
||||
context 'with job:tags' do
|
||||
let(:attributes) do
|
||||
{
|
||||
name: 'rspec',
|
||||
ref: 'master',
|
||||
job_variables: [{ key: 'VARIABLE', value: 'value', public: true }],
|
||||
tag_list: ['static-tag', '$VARIABLE', '$NO_VARIABLE']
|
||||
}
|
||||
end
|
||||
|
||||
it { is_expected.to include(tag_list: ['static-tag', 'value', '$NO_VARIABLE']) }
|
||||
it { is_expected.to include(yaml_variables: [{ key: 'VARIABLE', value: 'value', public: true }]) }
|
||||
end
|
||||
|
||||
context 'with cache:key' do
|
||||
let(:attributes) do
|
||||
{
|
||||
|
|
|
@ -30,10 +30,11 @@ RSpec.describe Gitlab::Kas::Client do
|
|||
|
||||
describe 'gRPC calls' do
|
||||
let(:token) { instance_double(JSONWebToken::HMACToken, encoded: 'test-token') }
|
||||
let(:kas_url) { 'grpc://example.kas.internal' }
|
||||
|
||||
before do
|
||||
allow(Gitlab::Kas).to receive(:enabled?).and_return(true)
|
||||
allow(Gitlab::Kas).to receive(:internal_url).and_return('grpc://example.kas.internal')
|
||||
allow(Gitlab::Kas).to receive(:internal_url).and_return(kas_url)
|
||||
|
||||
expect(JSONWebToken::HMACToken).to receive(:new)
|
||||
.with(Gitlab::Kas.secret)
|
||||
|
@ -80,5 +81,21 @@ RSpec.describe Gitlab::Kas::Client do
|
|||
|
||||
it { expect(subject).to eq(agent_configurations) }
|
||||
end
|
||||
|
||||
describe 'with grpcs' do
|
||||
let(:stub) { instance_double(Gitlab::Agent::ConfigurationProject::Rpc::ConfigurationProject::Stub) }
|
||||
let(:kas_url) { 'grpcs://example.kas.internal' }
|
||||
|
||||
it 'uses a ChannelCredentials object' do
|
||||
expect(Gitlab::Agent::ConfigurationProject::Rpc::ConfigurationProject::Stub).to receive(:new)
|
||||
.with('example.kas.internal', instance_of(GRPC::Core::ChannelCredentials), timeout: described_class::TIMEOUT)
|
||||
.and_return(stub)
|
||||
|
||||
allow(stub).to receive(:list_agent_config_files)
|
||||
.and_return(double(config_files: []))
|
||||
|
||||
described_class.new.list_agent_config_files(project: project)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,7 +5,7 @@ require 'spec_helper'
|
|||
RSpec.describe Gitlab::Metrics::Subscribers::ActionCable, :request_store do
|
||||
let(:subscriber) { described_class.new }
|
||||
let(:counter) { double(:counter) }
|
||||
let(:data) { { data: { event: 'updated' } } }
|
||||
let(:data) { { 'result' => { 'data' => { 'event' => 'updated' } } } }
|
||||
let(:channel_class) { 'IssuesChannel' }
|
||||
let(:event) do
|
||||
double(
|
||||
|
@ -35,6 +35,17 @@ RSpec.describe Gitlab::Metrics::Subscribers::ActionCable, :request_store do
|
|||
|
||||
subscriber.transmit(event)
|
||||
end
|
||||
|
||||
it 'tracks size of payload as JSON' do
|
||||
allow(::Gitlab::Metrics).to receive(:histogram).with(
|
||||
:action_cable_transmitted_bytes, /transmit/
|
||||
).and_return(counter)
|
||||
message_size = ::ActiveSupport::JSON.encode(data).bytesize
|
||||
|
||||
expect(counter).to receive(:observe).with({ channel: channel_class, operation: 'event' }, message_size)
|
||||
|
||||
subscriber.transmit(event)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#broadcast' do
|
||||
|
|
|
@ -22,8 +22,8 @@ RSpec.describe ProjectCiCdSetting do
|
|||
end
|
||||
|
||||
describe '#job_token_scope_enabled' do
|
||||
it 'is false by default' do
|
||||
expect(described_class.new.job_token_scope_enabled).to be_falsey
|
||||
it 'is true by default' do
|
||||
expect(described_class.new.job_token_scope_enabled).to be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -889,10 +889,10 @@ RSpec.describe 'Git HTTP requests' do
|
|||
context 'when admin mode is enabled', :enable_admin_mode do
|
||||
it_behaves_like 'can download code only'
|
||||
|
||||
it 'downloads from other project get status 403' do
|
||||
it 'downloads from other project get status 404' do
|
||||
clone_get "#{other_project.full_path}.git", user: 'gitlab-ci-token', password: build.token
|
||||
|
||||
expect(response).to have_gitlab_http_status(:forbidden)
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1490,10 +1490,10 @@ RSpec.describe 'Git HTTP requests' do
|
|||
context 'when admin mode is enabled', :enable_admin_mode do
|
||||
it_behaves_like 'can download code only'
|
||||
|
||||
it 'downloads from other project get status 403' do
|
||||
it 'downloads from other project get status 404' do
|
||||
clone_get "#{other_project.full_path}.git", user: 'gitlab-ci-token', password: build.token
|
||||
|
||||
expect(response).to have_gitlab_http_status(:forbidden)
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -574,7 +574,7 @@ RSpec.describe 'Git LFS API and storage' do
|
|||
let(:pipeline) { create(:ci_empty_pipeline, project: other_project) }
|
||||
|
||||
# I'm not sure what this tests that is different from the previous test
|
||||
it_behaves_like 'LFS http 403 response'
|
||||
it_behaves_like 'LFS http 404 response'
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1049,7 +1049,7 @@ RSpec.describe 'Git LFS API and storage' do
|
|||
let(:pipeline) { create(:ci_empty_pipeline, project: other_project) }
|
||||
|
||||
# I'm not sure what this tests that is different from the previous test
|
||||
it_behaves_like 'LFS http 403 response'
|
||||
it_behaves_like 'LFS http 404 response'
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,144 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Ci::CreatePipelineService do
|
||||
let_it_be(:group) { create(:group, :private) }
|
||||
let_it_be(:group_variable) { create(:ci_group_variable, group: group, key: 'RUNNER_TAG', value: 'group')}
|
||||
let_it_be(:project) { create(:project, :repository, group: group) }
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
||||
let(:service) { described_class.new(project, user, ref: 'master') }
|
||||
let(:pipeline) { service.execute(:push) }
|
||||
let(:job) { pipeline.builds.find_by(name: 'job') }
|
||||
|
||||
before do
|
||||
project.add_developer(user)
|
||||
stub_ci_pipeline_yaml_file config
|
||||
end
|
||||
|
||||
context 'when the variable is set' do
|
||||
let(:config) do
|
||||
<<~EOS
|
||||
variables:
|
||||
KUBERNETES_RUNNER: kubernetes
|
||||
|
||||
job:
|
||||
tags:
|
||||
- docker
|
||||
- $KUBERNETES_RUNNER
|
||||
script:
|
||||
- echo "Hello runner selector feature"
|
||||
EOS
|
||||
end
|
||||
|
||||
it 'uses the evaluated variable' do
|
||||
expect(pipeline).to be_created_successfully
|
||||
expect(job.tags.pluck(:name)).to match_array(%w[docker kubernetes])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the tag is composed by two variables' do
|
||||
let(:config) do
|
||||
<<~EOS
|
||||
variables:
|
||||
CLOUD_PROVIDER: aws
|
||||
KUBERNETES_RUNNER: kubernetes
|
||||
ENVIRONMENT_NAME: prod
|
||||
|
||||
job:
|
||||
tags:
|
||||
- docker
|
||||
- $CLOUD_PROVIDER-$KUBERNETES_RUNNER-$ENVIRONMENT_NAME
|
||||
script:
|
||||
- echo "Hello runner selector feature"
|
||||
EOS
|
||||
end
|
||||
|
||||
it 'uses the evaluated variables' do
|
||||
expect(pipeline).to be_created_successfully
|
||||
expect(job.tags.pluck(:name)).to match_array(%w[docker aws-kubernetes-prod])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the variable is not set' do
|
||||
let(:config) do
|
||||
<<~EOS
|
||||
job:
|
||||
tags:
|
||||
- docker
|
||||
- $KUBERNETES_RUNNER
|
||||
script:
|
||||
- echo "Hello runner selector feature"
|
||||
EOS
|
||||
end
|
||||
|
||||
it 'uses the variable as a regular string' do
|
||||
expect(pipeline).to be_created_successfully
|
||||
expect(job.tags.pluck(:name)).to match_array(%w[docker $KUBERNETES_RUNNER])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the tag uses group variables' do
|
||||
let(:config) do
|
||||
<<~EOS
|
||||
job:
|
||||
tags:
|
||||
- docker
|
||||
- $RUNNER_TAG
|
||||
script:
|
||||
- echo "Hello runner selector feature"
|
||||
EOS
|
||||
end
|
||||
|
||||
it 'uses the evaluated variables' do
|
||||
expect(pipeline).to be_created_successfully
|
||||
expect(job.tags.pluck(:name)).to match_array(%w[docker group])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the tag has the same variable name defined for both group and project' do
|
||||
let_it_be(:project_variable) { create(:ci_variable, project: project, key: 'RUNNER_TAG', value: 'project') }
|
||||
|
||||
let(:config) do
|
||||
<<~EOS
|
||||
variables:
|
||||
RUNNER_TAG: pipeline
|
||||
job:
|
||||
tags:
|
||||
- docker
|
||||
- $RUNNER_TAG
|
||||
script:
|
||||
- echo "Hello runner selector feature"
|
||||
EOS
|
||||
end
|
||||
|
||||
it 'uses the project variable instead of group due to variable precedence' do
|
||||
expect(pipeline).to be_created_successfully
|
||||
expect(job.tags.pluck(:name)).to match_array(%w[docker project])
|
||||
end
|
||||
end
|
||||
|
||||
context 'with parallel:matrix config' do
|
||||
let(:tags) { pipeline.builds.map(&:tags).flatten.pluck(:name) }
|
||||
|
||||
let(:config) do
|
||||
<<~EOS
|
||||
job:
|
||||
parallel:
|
||||
matrix:
|
||||
- PROVIDER: [aws, gcp]
|
||||
STACK: [monitoring, backup, app]
|
||||
tags:
|
||||
- ${PROVIDER}-${STACK}
|
||||
script:
|
||||
- echo "Hello runner selector feature"
|
||||
EOS
|
||||
end
|
||||
|
||||
it 'uses the evaluated variables' do
|
||||
expect(pipeline).to be_created_successfully
|
||||
expect(tags).to match_array(%w[aws-monitoring aws-backup aws-app gcp-monitoring gcp-backup gcp-app])
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,69 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe 'projects/_flash_messages' do
|
||||
let_it_be(:template) { 'projects/flash_messages' }
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
||||
let_it_be(:ruby) { create(:programming_language, name: 'Ruby') }
|
||||
let_it_be(:html) { create(:programming_language, name: 'HTML') }
|
||||
let_it_be(:hcl) { create(:programming_language, name: 'HCL') }
|
||||
|
||||
before do
|
||||
allow(view).to receive(:current_user).and_return(user)
|
||||
allow(view).to receive(:can?).with(user, :download_code, project).and_return(true)
|
||||
end
|
||||
|
||||
context 'when current_user has download_code permission' do
|
||||
context 'when user has a terraform state' do
|
||||
let_it_be(:project) { create(:project) }
|
||||
let_it_be(:terraform_state) { create(:terraform_state, :locked, :with_version, project: project) }
|
||||
|
||||
it "doesn't show the terraform notification banner" do
|
||||
render(template, project: project)
|
||||
expect(view.content_for(:flash_message)).not_to have_selector('.js-terraform-notification')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there are no .tf files in the repository' do
|
||||
let_it_be(:project) { create(:project) }
|
||||
let_it_be(:mock_repo_languages) do
|
||||
{ project => { ruby => 0.5, html => 0.5 } }
|
||||
end
|
||||
|
||||
before do
|
||||
mock_repo_languages.each do |project, lang_shares|
|
||||
lang_shares.each do |lang, share|
|
||||
create(:repository_language, project: project, programming_language: lang, share: share)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "doesn't show the terraform notification banner" do
|
||||
render(template, project: project)
|
||||
expect(view.content_for(:flash_message)).not_to have_selector('.js-terraform-notification')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when .tf files are present in the repository and user does not have any terraform states' do
|
||||
let_it_be(:project) { create(:project) }
|
||||
let_it_be(:mock_repo_languages) do
|
||||
{ project => { ruby => 0.5, hcl => 0.5 } }
|
||||
end
|
||||
|
||||
before do
|
||||
mock_repo_languages.each do |project, lang_shares|
|
||||
lang_shares.each do |lang, share|
|
||||
create(:repository_language, project: project, programming_language: lang, share: share)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it 'shows the terraform notification banner' do
|
||||
render(template, project: project)
|
||||
expect(view.content_for(:flash_message)).to have_selector('.js-terraform-notification')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue