Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-07-15 15:09:41 +00:00
parent 01d2d6c869
commit 1581767ea1
65 changed files with 1144 additions and 233 deletions

View File

@ -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

View File

@ -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"

View File

@ -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>

View File

@ -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();

View File

@ -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();

View File

@ -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"

View File

@ -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>

View File

@ -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

View File

@ -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,
},

View File

@ -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>

View File

@ -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) } }),
});
};

View File

@ -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>

View File

@ -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" />

View File

@ -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',
]),
},
});
},

View File

@ -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) {

View File

@ -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

View File

@ -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 }

View File

@ -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

View File

@ -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

View File

@ -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'

View File

@ -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')

View File

@ -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 }

View File

@ -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

View File

@ -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 } }

View File

@ -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',
];

View File

@ -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

View File

@ -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

View File

@ -0,0 +1 @@
e0a2de69a3c9d616b87207b764e33fa3326627e065f28fc200c1414f08ee9fff

View File

@ -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

View File

@ -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

View File

@ -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` |

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -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'

View File

@ -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

View File

@ -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'

View File

@ -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. |

View File

@ -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).

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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`.

View File

@ -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

View File

@ -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

View File

@ -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 ""

View File

@ -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'

View File

@ -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

View File

@ -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();

View File

@ -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,
});
});
});

View File

@ -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);
});
});
});

View File

@ -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,
});
});
});

View File

@ -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 });

View File

@ -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) }

View File

@ -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
{

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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