Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-05-04 15:09:12 +00:00
parent 8ed0a009f0
commit 856e2c64ee
66 changed files with 977 additions and 342 deletions

View file

@ -1093,8 +1093,8 @@
"description": "The name of a job to execute when the environment is about to be stopped."
},
"action": {
"enum": ["start", "prepare", "stop", "verify"],
"description": "Specifies what this job will do. 'start' (default) indicates the job will start the deployment. 'prepare'/'verify' indicates this will not affect the deployment. 'stop' indicates this will stop the deployment.",
"enum": ["start", "prepare", "stop", "verify", "access"],
"description": "Specifies what this job will do. 'start' (default) indicates the job will start the deployment. 'prepare'/'verify'/'access' indicates this will not affect the deployment. 'stop' indicates this will stop the deployment.",
"default": "start"
},
"auto_stop_in": {

View file

@ -1,73 +1,64 @@
<script>
import { GlToggle, GlLoadingIcon, GlTooltip, GlAlert } from '@gitlab/ui';
import { debounce } from 'lodash';
import { GlToggle, GlAlert } from '@gitlab/ui';
import axios from '~/lib/utils/axios_utils';
import { __ } from '~/locale';
import { DEBOUNCE_TOGGLE_DELAY, ERROR_MESSAGE } from '../constants';
import { ERROR_MESSAGE } from '../constants';
export default {
components: {
GlToggle,
GlLoadingIcon,
GlTooltip,
GlAlert,
},
inject: [
'updatePath',
'sharedRunnersAvailability',
'parentSharedRunnersAvailability',
'runnerEnabled',
'runnerDisabled',
'runnerAllowOverride',
'sharedRunnersSetting',
'parentSharedRunnersSetting',
'runnerEnabledValue',
'runnerDisabledValue',
'runnerAllowOverrideValue',
],
data() {
return {
isLoading: false,
enabled: true,
allowOverride: false,
value: this.sharedRunnersSetting,
error: null,
};
},
computed: {
toggleDisabled() {
return this.parentSharedRunnersAvailability === this.runnerDisabled || this.isLoading;
isSharedRunnersToggleDisabled() {
return this.parentSharedRunnersSetting === this.runnerDisabledValue;
},
enabledOrDisabledSetting() {
return this.enabled ? this.runnerEnabled : this.runnerDisabled;
sharedRunnersToggleValue() {
return this.value === this.runnerEnabledValue;
},
disabledWithOverrideSetting() {
return this.allowOverride ? this.runnerAllowOverride : this.runnerDisabled;
isOverrideToggleDisabled() {
// cannot override when sharing is enabled
return this.isSharedRunnersToggleDisabled || this.value === this.runnerEnabledValue;
},
overrideToggleValue() {
return this.value === this.runnerAllowOverrideValue;
},
},
created() {
if (this.sharedRunnersAvailability !== this.runnerEnabled) {
this.enabled = false;
}
if (this.sharedRunnersAvailability === this.runnerAllowOverride) {
this.allowOverride = true;
}
},
methods: {
generatePayload(data) {
return { shared_runners_setting: data };
onSharedRunnersToggle(value) {
const newSetting = value ? this.runnerEnabledValue : this.runnerDisabledValue;
this.updateSetting(newSetting);
},
enableOrDisable() {
this.updateRunnerSettings(this.generatePayload(this.enabledOrDisabledSetting));
onOverrideToggle(value) {
const newSetting = value ? this.runnerAllowOverrideValue : this.runnerDisabledValue;
this.updateSetting(newSetting);
},
updateSetting(setting) {
if (this.isLoading) {
return;
}
// reset override toggle to false if shared runners are enabled
this.allowOverride = false;
},
override() {
this.updateRunnerSettings(this.generatePayload(this.disabledWithOverrideSetting));
},
updateRunnerSettings: debounce(function debouncedUpdateRunnerSettings(setting) {
this.isLoading = true;
axios
.put(this.updatePath, setting)
.put(this.updatePath, { shared_runners_setting: setting })
.then(() => {
this.isLoading = false;
this.value = setting;
})
.catch((error) => {
const message = [
@ -76,51 +67,52 @@ export default {
].join(' ');
this.error = message;
})
.finally(() => {
this.isLoading = false;
});
}, DEBOUNCE_TOGGLE_DELAY),
},
},
};
</script>
<template>
<div ref="sharedRunnersForm">
<gl-alert v-if="error" variant="danger" :dismissible="false">{{ error }}</gl-alert>
<div>
<gl-alert v-if="error" variant="danger" :dismissible="false" class="gl-mb-5">
{{ error }}
</gl-alert>
<h4 class="gl-display-flex gl-align-items-center">
{{ __('Set up shared runner availability') }}
<gl-loading-icon v-if="isLoading" class="gl-ml-3" size="sm" inline />
</h4>
<section class="gl-mt-5">
<gl-toggle
v-model="enabled"
:disabled="toggleDisabled"
:label="__('Enable shared runners for this group')"
data-testid="enable-runners-toggle"
@change="enableOrDisable"
/>
<span class="gl-text-gray-600">
{{ __('Enable shared runners for all projects and subgroups in this group.') }}
</span>
</section>
<section v-if="!enabled" class="gl-mt-5">
<gl-toggle
v-model="allowOverride"
:disabled="toggleDisabled"
:label="__('Allow projects and subgroups to override the group setting')"
data-testid="override-runners-toggle"
@change="override"
/>
<span class="gl-text-gray-600">
{{ __('Allows projects or subgroups in this group to override the global setting.') }}
</span>
</section>
<gl-tooltip v-if="toggleDisabled" :target="() => $refs.sharedRunnersForm">
<gl-alert
v-if="isSharedRunnersToggleDisabled"
variant="warning"
:dismissible="false"
class="gl-mb-5"
>
{{ __('Shared runners are disabled for the parent group') }}
</gl-tooltip>
</gl-alert>
<section class="gl-mb-5">
<gl-toggle
:value="sharedRunnersToggleValue"
:is-loading="isLoading"
:disabled="isSharedRunnersToggleDisabled"
:label="__('Enable shared runners for this group')"
:help="__('Enable shared runners for all projects and subgroups in this group.')"
data-testid="shared-runners-toggle"
@change="onSharedRunnersToggle"
/>
</section>
<section class="gl-mb-5">
<gl-toggle
:value="overrideToggleValue"
:is-loading="isLoading"
:disabled="isOverrideToggleDisabled"
:label="__('Allow projects and subgroups to override the group setting')"
:help="__('Allows projects or subgroups in this group to override the global setting.')"
data-testid="override-runners-toggle"
@change="onOverrideToggle"
/>
</section>
</div>
</template>

View file

@ -1,6 +1,3 @@
import { __ } from '~/locale';
// Debounce delay in milliseconds
export const DEBOUNCE_TOGGLE_DELAY = 1000;
export const ERROR_MESSAGE = __('Refresh the page and try again.');

View file

@ -6,22 +6,22 @@ export default (containerId = 'update-shared-runners-form') => {
const {
updatePath,
sharedRunnersAvailability,
parentSharedRunnersAvailability,
runnerEnabled,
runnerDisabled,
runnerAllowOverride,
sharedRunnersSetting,
parentSharedRunnersSetting,
runnerEnabledValue,
runnerDisabledValue,
runnerAllowOverrideValue,
} = containerEl.dataset;
return new Vue({
el: containerEl,
provide: {
updatePath,
sharedRunnersAvailability,
parentSharedRunnersAvailability,
runnerEnabled,
runnerDisabled,
runnerAllowOverride,
sharedRunnersSetting,
parentSharedRunnersSetting,
runnerEnabledValue,
runnerDisabledValue,
runnerAllowOverrideValue,
},
render(createElement) {
return createElement(UpdateSharedRunnersForm);

View file

@ -1,3 +0,0 @@
import { mountApplications } from '~/pages/shared/wikis/edit';
mountApplications();

View file

@ -1,3 +1,6 @@
import Wikis from '~/pages/shared/wikis/wikis';
import { mountApplications } from '~/pages/shared/wikis/async_edit';
mountApplications();
export default new Wikis();

View file

@ -1,5 +1,3 @@
import { mountApplications } from '~/pages/shared/wikis/show';
import { mountApplications as mountEditApplications } from '~/pages/shared/wikis/async_edit';
mountApplications();
mountEditApplications();

View file

@ -3,11 +3,13 @@ import { GlButton } from '@gitlab/ui';
import getAppStatus from '~/pipeline_editor/graphql/queries/client/app_status.query.graphql';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { EDITOR_APP_STATUS_EMPTY } from '../../constants';
import FileTreePopover from '../popovers/file_tree_popover.vue';
import BranchSwitcher from './branch_switcher.vue';
export default {
components: {
BranchSwitcher,
FileTreePopover,
GlButton,
},
mixins: [glFeatureFlagMixin()],
@ -56,11 +58,13 @@ export default {
<div class="gl-mb-4">
<gl-button
v-if="showFileTreeToggle"
id="file-tree-toggle"
icon="file-tree"
data-testid="file-tree-toggle"
:aria-label="__('File Tree')"
@click="onFileTreeBtnClick"
/>
<file-tree-popover v-if="showFileTreeToggle" />
<branch-switcher
:has-unsaved-changes="hasUnsavedChanges"
:should-load-new-branch="shouldLoadNewBranch"

View file

@ -23,7 +23,7 @@ import CiEditorHeader from './editor/ci_editor_header.vue';
import TextEditor from './editor/text_editor.vue';
import CiLint from './lint/ci_lint.vue';
import EditorTab from './ui/editor_tab.vue';
import WalkthroughPopover from './walkthrough_popover.vue';
import WalkthroughPopover from './popovers/walkthrough_popover.vue';
export default {
i18n: {

View file

@ -0,0 +1,53 @@
<script>
import { GlPopover, GlOutsideDirective as Outside } from '@gitlab/ui';
import { s__, __ } from '~/locale';
import { FILE_TREE_POPOVER_DISMISSED_KEY } from '../../constants';
export default {
name: 'PipelineEditorFileTreePopover',
directives: { Outside },
i18n: {
description: s__(
'pipelineEditorWalkthrough|You can use the file tree to view your pipeline configuration files.',
),
learnMore: __('Learn more'),
},
components: {
GlPopover,
},
data() {
return {
showPopover: false,
};
},
mounted() {
this.showPopover = localStorage.getItem(FILE_TREE_POPOVER_DISMISSED_KEY) !== 'true';
},
methods: {
closePopover() {
this.showPopover = false;
},
dismissPermanently() {
this.closePopover();
localStorage.setItem(FILE_TREE_POPOVER_DISMISSED_KEY, 'true');
},
},
};
</script>
<template>
<gl-popover
v-if="showPopover"
show
show-close-button
target="file-tree-toggle"
triggers="manual"
placement="right"
data-qa-selector="file_tree_popover"
@close-button-clicked="dismissPermanently"
>
<div v-outside="closePopover" class="gl-display-flex gl-flex-direction-column">
<p class="gl-font-base">{{ $options.i18n.description }}</p>
</div>
</gl-popover>
</template>

View file

@ -49,6 +49,9 @@ export const BRANCH_PAGINATION_LIMIT = 20;
export const BRANCH_SEARCH_DEBOUNCE = '500';
export const SOURCE_EDITOR_DEBOUNCE = 500;
export const FILE_TREE_DISPLAY_KEY = 'pipeline_editor_file_tree_display';
export const FILE_TREE_POPOVER_DISMISSED_KEY = 'pipeline_editor_file_tree_popover_dismissed';
export const STARTER_TEMPLATE_NAME = 'Getting-Started';
export const pipelineEditorTrackingOptions = {

View file

@ -382,7 +382,7 @@ export default {
</script>
<template>
<div class="gl-mt-4 gl-relative">
<div class="gl-mt-4 gl-relative" data-qa-selector="pipeline_editor_app">
<gl-loading-icon v-if="isBlobContentLoading" size="lg" class="gl-m-3" />
<pipeline-editor-empty-state
v-else-if="showStartScreen"

View file

@ -8,7 +8,7 @@ import PipelineEditorFileNav from './components/file_nav/pipeline_editor_file_na
import PipelineEditorFileTree from './components/file_tree/container.vue';
import PipelineEditorHeader from './components/header/pipeline_editor_header.vue';
import PipelineEditorTabs from './components/pipeline_editor_tabs.vue';
import { CREATE_TAB } from './constants';
import { CREATE_TAB, FILE_TREE_DISPLAY_KEY } from './constants';
export default {
commitSectionRef: 'commitSectionRef',
@ -77,6 +77,9 @@ export default {
return this.showFileTree && this.glFeatures.pipelineEditorFileTree;
},
},
mounted() {
this.showFileTree = JSON.parse(localStorage.getItem(FILE_TREE_DISPLAY_KEY)) || false;
},
methods: {
closeBranchModal() {
this.showSwitchBranchModal = false;
@ -92,6 +95,7 @@ export default {
},
toggleFileTree() {
this.showFileTree = !this.showFileTree;
localStorage.setItem(FILE_TREE_DISPLAY_KEY, this.showFileTree);
},
switchBranch() {
this.showSwitchBranchModal = false;

View file

@ -229,6 +229,8 @@ class GroupsController < Groups::ApplicationController
protected
def render_show_html
Gitlab::Tracking.event('group_overview', 'render', user: current_user, namespace: @group)
render 'groups/show', locals: { trial: params[:trial] }
end

View file

@ -345,6 +345,8 @@ class ProjectsController < Projects::ApplicationController
#
# pages list order: repository readme, wiki home, issues list, customize workflow
def render_landing_page
Gitlab::Tracking.event('project_overview', 'render', user: current_user, project: @project.project)
if can?(current_user, :download_code, @project)
return render 'projects/no_repo' unless @project.repository_exists?

View file

@ -72,11 +72,11 @@ module Ci
def group_shared_runners_settings_data(group)
{
update_path: api_v4_groups_path(id: group.id),
shared_runners_availability: group.shared_runners_setting,
parent_shared_runners_availability: group.parent&.shared_runners_setting,
runner_enabled: Namespace::SR_ENABLED,
runner_disabled: Namespace::SR_DISABLED_AND_UNOVERRIDABLE,
runner_allow_override: Namespace::SR_DISABLED_WITH_OVERRIDE
shared_runners_setting: group.shared_runners_setting,
parent_shared_runners_setting: group.parent&.shared_runners_setting,
runner_enabled_value: Namespace::SR_ENABLED,
runner_disabled_value: Namespace::SR_DISABLED_AND_UNOVERRIDABLE,
runner_allow_override_value: Namespace::SR_DISABLED_WITH_OVERRIDE
}
end

View file

@ -0,0 +1,8 @@
# frozen_string_literal: true
module Packages
module Cleanup
def self.table_name_prefix
'packages_cleanup_'
end
end
end

View file

@ -0,0 +1,32 @@
# frozen_string_literal: true
module Packages
module Cleanup
class Policy < ApplicationRecord
include Schedulable
KEEP_N_DUPLICATED_PACKAGE_FILES_VALUES = %w[all 1 10 20 30 40 50].freeze
self.primary_key = :project_id
belongs_to :project
validates :project, presence: true
validates :keep_n_duplicated_package_files,
inclusion: {
in: KEEP_N_DUPLICATED_PACKAGE_FILES_VALUES,
message: 'keep_n_duplicated_package_files is invalid'
}
# used by Schedulable
def self.active
where.not(keep_n_duplicated_package_files: 'all')
end
def set_next_run_at
# fixed cadence of 12 hours
self.next_run_at = Time.zone.now + 12.hours
end
end
end
end

View file

@ -238,6 +238,7 @@ class Project < ApplicationRecord
has_many :package_files, through: :packages, class_name: 'Packages::PackageFile'
# debian_distributions and associated component_files must be destroyed by ruby code in order to properly remove carrierwave uploads
has_many :debian_distributions, class_name: 'Packages::Debian::ProjectDistribution', dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
has_one :packages_cleanup_policy, class_name: 'Packages::Cleanup::Policy', inverse_of: :project
has_one :import_state, autosave: true, class_name: 'ProjectImportState', inverse_of: :project
has_one :import_export_upload, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
@ -1014,6 +1015,10 @@ class Project < ApplicationRecord
packages.where(package_type: package_type).exists?
end
def packages_cleanup_policy
super || build_packages_cleanup_policy
end
def first_auto_devops_config
return namespace.first_auto_devops_config if auto_devops&.enabled.nil?

View file

@ -8,7 +8,7 @@
%p.gl-mb-0
= s_('GitLabPages|Removing pages will prevent them from being exposed to the outside world.')
.card-footer
= link_to s_('GitLabPages|Remove pages'), project_pages_path(@project), data: { confirm: s_('GitLabPages|Are you sure?')}, method: :delete, class: "btn gl-button btn-danger"
= link_to s_('GitLabPages|Remove pages'), project_pages_path(@project), data: { confirm: s_('GitLabPages|Are you sure?'), 'confirm-btn-variant': 'danger'}, method: :delete, class: "btn gl-button btn-danger", "aria-label": s_('GitLabPages|Remove pages')
- else
.nothing-here-block
= s_('GitLabPages|Only project maintainers can remove pages')

View file

@ -20,7 +20,7 @@
= gl_badge_tag s_('GitLabPages|Expired'), variant: :danger
%div
= link_to s_('GitLabPages|Edit'), project_pages_domain_path(@project, domain), class: "btn gl-button btn-sm btn-grouped btn-confirm btn-inverted"
= link_to s_('GitLabPages|Remove'), project_pages_domain_path(@project, domain), data: { confirm: s_('GitLabPages|Are you sure?')}, method: :delete, class: "btn gl-button btn-danger btn-sm btn-grouped"
= link_to s_('GitLabPages|Remove'), project_pages_domain_path(@project, domain), data: { confirm: s_('GitLabPages|Are you sure?'), 'confirm-btn-variant': 'danger'}, "aria-label": s_("GitLabPages|Remove domain"), method: :delete, class: "btn gl-button btn-danger btn-sm btn-grouped"
- if domain.needs_verification?
%li.list-group-item.bs-callout-warning
- details_link_start = "<a href='#{project_pages_domain_path(@project, domain)}'>".html_safe

View file

@ -38,7 +38,8 @@
= domain_presenter.pages_domain.subject || _('missing')
= link_to _('Remove'),
clean_certificate_project_pages_domain_path(@project, domain_presenter),
data: { confirm: _('Are you sure?') },
data: { confirm: _('Are you sure?'), 'confirm-btn-variant': 'danger' },
'aria-label': s_("GitLabPages|Remove certificate"),
class: 'gl-button btn btn-danger btn-sm',
method: :delete
- else

View file

@ -0,0 +1,21 @@
description: Render event to understand if users view this page more on average
category: group_overview
action: show
label_description:
property_description:
value_description:
extra_properties:
identifiers:
product_section: growth
product_stage:
product_group: group::conversion
product_category:
milestone: "15.0"
introduced_by_url:
distributions:
- ce
- ee
tiers:
- free
- premium
- ultimate

View file

@ -0,0 +1,21 @@
description: Render event to understand if users view this page more on average
category: project_overview
action: show
label_description:
property_description:
value_description:
extra_properties:
identifiers:
product_section: growth
product_stage:
product_group: group::conversion
product_category:
milestone: "15.0"
introduced_by_url:
distributions:
- ce
- ee
tiers:
- free
- premium
- ultimate

View file

@ -0,0 +1,19 @@
- name: "Vulnerability Report sort by State" # (required) The name of the feature to be deprecated
announcement_milestone: "15.0" # (required) The milestone when this feature was first announced as deprecated.
announcement_date: "2022-05-22" # (required) The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "15.2" # (required) The milestone when this feature is planned to be removed
removal_date: "2022-07-22" # (required) The date of the milestone release when this feature is planned to be removed. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
breaking_change: false # (required) If this deprecation is a breaking change, set this value to true
reporter: matt_wilson # (required) GitLab username of the person reporting the deprecation
stage: Secure # (required) String value of the stage that the feature was created in. e.g., Growth
issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/360516 # (required) Link to the deprecation issue in GitLab
body: | # (required) Do not modify this line, instead modify the lines below.
The ability to sort the Vulnerability Report by the `State` column was disabled and put behind a feature flag in GitLab 14.10 due to a refactor
of the underlying data model. The feature flag has remained off by default as further refactoring will be required to ensure sorting
by this value remains performant. Due to very low usage of the `State` column for sorting, the feature flag will instead be removed in
GitLab 15.2 to simplify the codebase and prevent any unwanted performance degradation.
# The following items are not published on the docs page, but may be used in the future.
tiers: Ultimate # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate]
documentation_url: # (optional) This is a link to the current documentation page
image_url: # (optional) This is a link to a thumbnail image depicting the feature
video_url: # (optional) Use the youtube thumbnail URL with the structure of https://img.youtube.com/vi/UNIQUEID/hqdefault.jpg

View file

@ -0,0 +1,9 @@
---
table_name: packages_cleanup_policies
classes:
- Packages::Cleanup::Policy
feature_categories:
- package_registry
description: Cleanup policy parameters for packages.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85918
milestone: '15.0'

View file

@ -0,0 +1,22 @@
# frozen_string_literal: true
class CreatePackagesCleanupPolicies < Gitlab::Database::Migration[2.0]
enable_lock_retries!
def up
create_table :packages_cleanup_policies, id: false do |t|
t.timestamps_with_timezone null: false
t.references :project,
primary_key: true,
default: nil,
index: false,
foreign_key: { to_table: :projects, on_delete: :cascade }
t.datetime_with_timezone :next_run_at, null: true
t.text :keep_n_duplicated_package_files, default: 'all', null: false, limit: 255
end
end
def down
drop_table :packages_cleanup_policies
end
end

View file

@ -0,0 +1 @@
56ebfb7a97217af23f12db7f93c47104be30da7633a22caf9e74547d5a27d29b

View file

@ -17892,6 +17892,15 @@ CREATE SEQUENCE packages_build_infos_id_seq
ALTER SEQUENCE packages_build_infos_id_seq OWNED BY packages_build_infos.id;
CREATE TABLE packages_cleanup_policies (
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
project_id bigint NOT NULL,
next_run_at timestamp with time zone,
keep_n_duplicated_package_files text DEFAULT 'all'::text NOT NULL,
CONSTRAINT check_e53f35ab7b CHECK ((char_length(keep_n_duplicated_package_files) <= 255))
);
CREATE TABLE packages_composer_cache_files (
id bigint NOT NULL,
created_at timestamp with time zone NOT NULL,
@ -24956,6 +24965,9 @@ ALTER TABLE ONLY operations_user_lists
ALTER TABLE ONLY packages_build_infos
ADD CONSTRAINT packages_build_infos_pkey PRIMARY KEY (id);
ALTER TABLE ONLY packages_cleanup_policies
ADD CONSTRAINT packages_cleanup_policies_pkey PRIMARY KEY (project_id);
ALTER TABLE ONLY packages_composer_cache_files
ADD CONSTRAINT packages_composer_cache_files_pkey PRIMARY KEY (id);
@ -32354,6 +32366,9 @@ ALTER TABLE ONLY namespace_settings
ALTER TABLE ONLY self_managed_prometheus_alert_events
ADD CONSTRAINT fk_rails_3936dadc62 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
ALTER TABLE ONLY packages_cleanup_policies
ADD CONSTRAINT fk_rails_393ba98591 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
ALTER TABLE ONLY approval_project_rules_groups
ADD CONSTRAINT fk_rails_396841e79e FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;

View file

@ -122,7 +122,7 @@ Note the following when promoting a secondary:
WARNING:
The `gitlab-ctl promote-to-primary-node` and `gitlab-ctl promoted-db` commands are
deprecated in GitLab 14.5 and later, and are scheduled to [be removed in GitLab 15.0](https://gitlab.com/gitlab-org/gitlab/-/issues/345207).
deprecated in GitLab 14.5 and later, and [removed in GitLab 15.0](https://gitlab.com/gitlab-org/gitlab/-/issues/345207).
Use `gitlab-ctl geo promote` instead.
1. SSH in to your **secondary** site and login as root:
@ -212,7 +212,7 @@ Use `gitlab-ctl geo promote` instead.
WARNING:
The `gitlab-ctl promote-to-primary-node` and `gitlab-ctl promoted-db` commands are
deprecated in GitLab 14.5 and later, and are scheduled to [be removed in GitLab 15.0](https://gitlab.com/gitlab-org/gitlab/-/issues/345207).
deprecated in GitLab 14.5 and later, and [removed in GitLab 15.0](https://gitlab.com/gitlab-org/gitlab/-/issues/345207).
Use `gitlab-ctl geo promote` instead.
The `gitlab-ctl promote-to-primary-node` command cannot be used yet in
@ -290,7 +290,7 @@ do this manually.
WARNING:
The `gitlab-ctl promote-to-primary-node` and `gitlab-ctl promoted-db` commands are
deprecated in GitLab 14.5 and later, and are scheduled to [be removed in GitLab 15.0](https://gitlab.com/gitlab-org/gitlab/-/issues/345207).
deprecated in GitLab 14.5 and later, and [removed in GitLab 15.0](https://gitlab.com/gitlab-org/gitlab/-/issues/345207).
Use `gitlab-ctl geo promote` instead.
The `gitlab-ctl promote-to-primary-node` command cannot be used yet in
@ -404,7 +404,7 @@ site first.
WARNING:
The `gitlab-ctl promote-to-primary-node` and `gitlab-ctl promoted-db` commands are
deprecated in GitLab 14.5 and later, and are scheduled to [be removed in GitLab 15.0](https://gitlab.com/gitlab-org/gitlab/-/issues/345207).
deprecated in GitLab 14.5 and later, and [removed in GitLab 15.0](https://gitlab.com/gitlab-org/gitlab/-/issues/345207).
Use `gitlab-ctl geo promote` instead.
The `gitlab-ctl promote-to-primary-node` command cannot be used in conjunction with

View file

@ -264,7 +264,7 @@ follow these steps to avoid unnecessary data loss:
WARNING:
The `gitlab-ctl promote-to-primary-node` and `gitlab-ctl promoted-db` commands are
deprecated in GitLab 14.5 and later, and are scheduled to [be removed in GitLab 15.0](https://gitlab.com/gitlab-org/gitlab/-/issues/345207).
deprecated in GitLab 14.5 and later, and [removed in GitLab 15.0](https://gitlab.com/gitlab-org/gitlab/-/issues/345207).
Use `gitlab-ctl geo promote` instead.
NOTE:

View file

@ -247,7 +247,7 @@ To promote the secondary site running GitLab 14.4 and earlier:
WARNING:
The `gitlab-ctl promote-to-primary-node` and `gitlab-ctl promoted-db` commands are
deprecated in GitLab 14.5 and later, and are scheduled to [be removed in GitLab 15.0](https://gitlab.com/gitlab-org/gitlab/-/issues/345207).
deprecated in GitLab 14.5 and later, and [removed in GitLab 15.0](https://gitlab.com/gitlab-org/gitlab/-/issues/345207).
Use `gitlab-ctl geo promote` instead.
1. SSH in to your **secondary** site and login as root:

View file

@ -1,6 +1,6 @@
---
stage: Enablement
group: Distribution
stage: Create
group: Source Code
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
@ -14,7 +14,7 @@ drop-in replacement.
Regular SSH operations become slow as the number of users grows because OpenSSH
searches for a key to authorize a user via a linear search. In the worst case,
such as when the user is not authorized to access GitLab, OpenSSH will scan the
such as when the user is not authorized to access GitLab, OpenSSH scans the
entire file to search for a key. This can take significant time and disk I/O,
which delays users attempting to push or pull to a repository. Making
matters worse, if users add or remove keys frequently, the operating system may
@ -33,21 +33,22 @@ able to accept a fingerprint. Check the version of OpenSSH on your server with `
Unlike [Cloud Native GitLab](https://docs.gitlab.com/charts/), Omnibus GitLab by default
manages an `authorized_keys` file that is located in the
`git` user's home directory. For most installations, this will be located under
`/var/opt/gitlab/.ssh/authorized_keys`, but you can use the following command to locate the `authorized_keys` on your system:
`git` user's home directory. For most installations, this file is located under
`/var/opt/gitlab/.ssh/authorized_keys`, but you can use the following command to
locate the `authorized_keys` on your system:
```shell
getent passwd git | cut -d: -f6 | awk '{print $1"/.ssh/authorized_keys"}'
```
The `authorized_keys` file contains all the public SSH keys for users allowed to access GitLab. However, to maintain a
single source of truth, [Geo](../geo/index.md) needs to be configured to perform SSH fingerprint
single source of truth, [Geo](../geo/index.md) must be configured to perform SSH fingerprint
lookups via database lookup.
As part of [setting up Geo](../geo/index.md#setup-instructions),
you are required to follow the steps outlined below for both the primary and
secondary nodes, but the `Write to "authorized keys" file` checkbox
only needs to be unchecked on the primary node since it is reflected
secondary nodes, but **Write to "authorized keys" file**
must be unchecked only on the primary node, because it is reflected
automatically on the secondary if database replication is working.
## Setting up fast lookup via GitLab Shell
@ -56,8 +57,8 @@ GitLab Shell provides a way to authorize SSH users via a fast, indexed lookup
to the GitLab database. GitLab Shell uses the fingerprint of the SSH key to
check whether the user is authorized to access GitLab.
Add the following to your `sshd_config` file. This is usually located at
`/etc/ssh/sshd_config`, but it will be `/assets/sshd_config` if you're using
Add the following to your `sshd_config` file. This file is usually located at
`/etc/ssh/sshd_config`, but it is at `/assets/sshd_config` if you're using
Omnibus Docker:
```plaintext
@ -84,17 +85,14 @@ file (start the line with a `#` to comment it), and from your local machine, att
ssh -T git@gitlab.example.com
```
A successful pull or [welcome message](../../user/ssh.md#verify-that-you-can-connect) would mean that GitLab was able to find the key in the database,
since it is not present in the file anymore.
NOTE:
For Omnibus Docker, `AuthorizedKeysCommand` is setup by default in
GitLab 11.11 and later.
A successful pull or [welcome message](../../user/ssh.md#verify-that-you-can-connect)
means that GitLab was able to find the key in the database,
as it is not present in the file.
NOTE:
For Installations from source, the command would be located at
`/home/git/gitlab-shell/bin/gitlab-shell-authorized-keys-check` if [the install from source](../../install/installation.md#install-gitlab-shell) instructions were followed.
You might want to consider creating a wrapper script somewhere else since this command needs to be
You might want to consider creating a wrapper script somewhere else, as this command must be
owned by `root` and not be writable by group or others. You could also consider changing the ownership of this command
as required, but that might require temporary ownership changes during `gitlab-shell` upgrades.
@ -123,7 +121,7 @@ or for users to re-add their keys.
## How to go back to using the `authorized_keys` file
This is a brief overview. Please refer to the above instructions for more context.
This overview is brief. Refer to the above instructions for more context.
1. [Rebuild the `authorized_keys` file](../raketasks/maintenance.md#rebuild-authorized_keys-file).
1. Enable writes to the `authorized_keys` file.
@ -136,27 +134,35 @@ This is a brief overview. Please refer to the above instructions for more contex
## Use `gitlab-sshd` instead of OpenSSH
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/299109) in GitLab 14.5.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/299109) in GitLab 14.5 as an **Alpha** release.
> - [Changed](https://gitlab.com/gitlab-com/gl-infra/production/-/issues/6321) to a **Beta** release in GitLab 15.0.
WARNING:
`gitlab-sshd` is in [**Alpha**](../../policy/alpha-beta-support.md#alpha-features).
It is not ready for production use.
NOTE:
`gitlab-sshd` is in [**Beta**](../../policy/alpha-beta-support.md#beta-features).
`gitlab-sshd` is [a standalone SSH server](https://gitlab.com/gitlab-org/gitlab-shell/-/tree/main/internal/sshd)
written in Go. It is provided as a part of `gitlab-shell` package. It has a lower memory
use as a OpenSSH alternative and supports
[group access restriction by IP address](../../user/group/index.md) for applications
running behind the proxy.
written in Go. It is provided as a part of the `gitlab-shell` package. It has a lower memory
use as a OpenSSH alternative, and supports
[group access restriction by IP address](../../user/group/index.md) for applications
running behind the proxy.
`gitlab-sshd` is a lightweight alternative to OpenSSH for providing
[SSH operations](https://gitlab.com/gitlab-org/gitlab-shell/-/blob/71a7f34a476f778e62f8fe7a453d632d395eaf8f/doc/features.md).
While OpenSSH uses a restricted shell approach, `gitlab-sshd` behaves more like a
modern multi-threaded server application, responding to incoming requests. The major
difference is that OpenSSH uses SSH as a transport protocol while `gitlab-sshd` uses Remote Procedure Calls (RPCs).
The capabilities of GitLab Shell are not limited to Git operations.
If you are considering switching from OpenSSH to `gitlab-sshd`, consider these concerns:
- The `gitlab-sshd` component is only available for
[Cloud Native Helm Charts](https://docs.gitlab.com/charts/) deployments.
- `gitlab-sshd` supports the PROXY protocol. It can run behind proxy servers that rely
on it, such as HAProxy.
- `gitlab-sshd` does not share a SSH port with the system administrator's OpenSSH,
and requires a bind to port 22.
- `gitlab-sshd` **does not** support SSH certificates.
on it, such as HAProxy. The PROXY protocol not enabled by default, but can be enabled with a Helm chart setting.
- By default, `gitlab-sshd` binds to port 22, but you can configure a different port in the Helm chart.
- `gitlab-sshd` **does not** support SSH certificates. For more details, read
[issue #495](https://gitlab.com/gitlab-org/gitlab-shell/-/issues/495).
To switch from OpenSSH to `gitlab-sshd`:
@ -178,7 +184,11 @@ GitLab supports `authorized_keys` database lookups with [SELinux](https://en.wik
Because the SELinux policy is static, GitLab doesn't support the ability to change
internal webserver ports at the moment. Administrators would have to create a special `.te`
file for the environment, since it isn't generated dynamically.
file for the environment, as it isn't generated dynamically.
### Additional documentation
Additional technical documentation for `gitlab-sshd` may be found on the [GitLab Shell](https://gitlab.com/gitlab-org/gitlab-shell/-/blob/main/README.md) documentation page.
## Troubleshooting
@ -187,7 +197,8 @@ or causing high CPU load, be sure to check the size of `/var/log/btmp`, and ensu
If this file is very large, GitLab SSH fast lookup can cause the bottleneck to be hit more frequently, thus decreasing performance even further.
If you are able to, you may consider disabling [`UsePAM` in your `sshd_config`](https://linux.die.net/man/5/sshd_config) to avoid reading `/var/log/btmp` altogether.
Running `strace` and `lsof` on a running `sshd: git` process can return useful debugging information. To get an `strace` on an in-progress Git over SSH connection for IP `x.x.x.x`, run:
Running `strace` and `lsof` on a running `sshd: git` process returns debugging information.
To get an `strace` on an in-progress Git over SSH connection for IP `x.x.x.x`, run:
```plaintext
sudo strace -s 10000 -p $(sudo netstat -tp | grep x.x.x.x | egrep 'ssh.*: git' | sed -e 's/.*ESTABLISHED *//' -e 's#/.*##')

View file

@ -169,3 +169,99 @@ Any error or warning is logged in Sidekiq's log file.
If you have a Geo setup, the rollback is not reflected automatically
on the **secondary** node. You may need to wait for a backfill operation to kick-in and remove
the remaining repositories from the special `@hashed/` folder manually.
## Troubleshooting
The Rake task might not be able to complete the migration to hashed storage.
Checks on the instance will continue to report that there is legacy data:
```plaintext
* Found 1 projects using Legacy Storage
- janedoe/testproject (id: 1234)
```
If you have a subscription, [raise a ticket with GitLab support](https://support.gitlab.com)
as most of the fixes are relatively high risk, involving running code on the Rails console.
### Read only projects
If you have [set projects read only](../troubleshooting/gitlab_rails_cheat_sheet.md#make-a-project-read-only-can-only-be-done-in-the-console)
they might fail to migrate.
1. [Start a Rails console](../operations/rails_console.md#starting-a-rails-console-session).
1. Check if the project is read only:
```ruby
project = Project.find_by_full_path('janedoe/testproject')
project.repository_read_only
```
1. If it returns `true` (not `nil` or `false`), set it writable:
```ruby
project.update!(repository_read_only: false)
```
1. [Re-run the migration Rake task](#migrate-to-hashed-storage).
1. Set the project read-only again:
```ruby
project.update!(repository_read_only: true)
```
### Projects pending deletion
Check the project details in the admin area. If deleting the project failed
it will show as `Marked For Deletion At ..`, `Scheduled Deletion At ..` and
`pending removal`, but the dates will not be recent.
Delete the project using the Rails console:
1. [Start a Rails console](../operations/rails_console.md#starting-a-rails-console-session).
1. With the following code, select the project to be deleted and account to action it:
```ruby
project = Project.find_by_full_path('janedoe/testproject')
user = User.find_by_username('admin_handle')
puts "\nproject selected for deletion is:\nID: #{project.id}\nPATH: #{project.full_path}\nNAME: #{project.name}\n\n"
```
- Replace `janedoe/testproject` with your project path from the Rake take output or from the admin area.
- Replace `admin_handle` with the handle of an instance administrator or with `root`.
- Verify the output before proceeding. **There are no other checks performed**.
1. [Destroy the project](../troubleshooting/gitlab_rails_cheat_sheet.md#destroy-a-project) **immediately**:
```ruby
Projects::DestroyService.new(project, user).execute
```
If destroying the project generates a stack trace relating to encryption or the error `OpenSSL::Cipher::CipherError`:
1. [Verify your GitLab secrets](check.md#verify-database-values-can-be-decrypted-using-the-current-secrets).
1. If the affected projects have secrets that cannot be decrypted it will be necessary to remove those specific secrets.
[Our documentation for dealing with lost secrets](../../raketasks/backup_restore.md#when-the-secrets-file-is-lost)
is for loss of all secrets, but it's possible for specific projects to be affected. For example,
to [reset specific runner registration tokens](../../raketasks/backup_restore.md#reset-runner-registration-tokens)
for a specific project ID:
```sql
UPDATE projects SET runners_token = null, runners_token_encrypted = null where id = 1234;
```
### `Repository cannot be moved from` errors in Sidekiq log
Projects might fail to migrate with errors in the Sidekiq log:
```shell
# grep 'Repository cannot be moved' /var/log/gitlab/sidekiq/current
{"severity":"ERROR","time":"2021-02-29T02:29:02.021Z","message":"Repository cannot be moved from 'janedoe/testproject' to '@hashed<value>' (PROJECT_ID=1234)"}
```
This might be caused by [a bug](https://gitlab.com/gitlab-org/gitlab/-/issues/259605) in the original code for hashed storage migration.
[There is a workaround for projects still affected by this issue](https://gitlab.com/-/snippets/2039252).

View file

@ -652,7 +652,7 @@ To delete a stopped environment in the GitLab UI:
You can define a job that accesses an environment for various purposes, such as verification or preparation. This
effectively bypasses deployment creation, so that you can adjust your CD workflow more accurately.
To do so, add either `action: prepare` or `action: verify` to the `environment` section of your job:
To do so, add either `action: prepare`, `action: verify`, or `action: access` to the `environment` section of your job:
```yaml
build:

View file

@ -1559,7 +1559,7 @@ environment.
#### `environment:action`
Use the `action` keyword to specify jobs that prepare, start, stop, or verify environments.
Use the `action` keyword to specify how the job interacts with the environment.
**Keyword type**: Job keyword. You can use it only as part of a job.
@ -1571,6 +1571,7 @@ Use the `action` keyword to specify jobs that prepare, start, stop, or verify en
| `prepare` | Indicates that the job is only preparing the environment. It does not trigger deployments. [Read more about preparing environments](../environments/index.md#access-an-environment-for-preparation-or-verification-purposes). |
| `stop` | Indicates that the job stops a deployment. For more detail, read [Stop an environment](../environments/index.md#stop-an-environment). |
| `verify` | Indicates that the job is only verifying the environment. It does not trigger deployments. [Read more about verifying environments](../environments/index.md#access-an-environment-for-preparation-or-verification-purposes). |
| `access` | Indicates that the job is only accessing the environment. It does not trigger deployments. [Read more about accessing environments](../environments/index.md#access-an-environment-for-preparation-or-verification-purposes). |
**Example of `environment:action`**:

View file

@ -7,7 +7,7 @@ info: "See the Technical Writers assigned to Development Guidelines: https://abo
# Feature flags in the development of GitLab
**NOTE**:
NOTE:
The documentation below covers feature flags used by GitLab to deploy its own features, which **is not** the same
as the [feature flags offered as part of the product](../../operations/feature_flags.md).
@ -451,6 +451,22 @@ Feature.enabled?(:a_feature, project) && Feature.disabled?(:a_feature_override,
/chatops run feature set --project=gitlab-org/gitlab a_feature_override true
```
#### Use actors for verifying in production
WARNING:
Using production as a testing environment is not recommended. Use our testing
environments for testing features that are not production-ready.
While the staging environment provides a way to test features in an environment
that resembles production, it doesn't allow you to compare before-and-after
performance metrics specific to production environment. It can be useful to have a
project in production with your development feature flag enabled, to allow tools
like Sitespeed reports to reveal the metrics of the new code under a feature flag.
This approach is even more useful if you're already tracking the old codebase in
Sitespeed, enabling you to compare performance accurately before and after the
feature flag's rollout.
### Enable additional objects as actors
To use feature gates based on actors, the model needs to respond to

View file

@ -426,8 +426,11 @@ end
#### `update-example-snapshots.rb` script
The `scripts/glfm/update-example-snapshots.rb` script uses the GLFM `spec.txt` specification
file to update example snapshots:
The `scripts/glfm/update-example-snapshots.rb` script uses the GLFM
`glfm_specification/output/spec.txt` specification file and the
`glfm_specification/input/gitlab_flavored_markdown/glfm_example_status.yml`
file to create and update the [example snapshot](#example-snapshot-files)
YAML files:
```mermaid
graph LR
@ -436,6 +439,7 @@ subgraph script:
end
subgraph input:<br/>input specification file
B[spec.txt] --> A
C[glfm_example_status.yml] --> A
end
subgraph output:<br/>example snapshot files
A --> E[examples_index.yml]

View file

@ -9,12 +9,12 @@ info: To determine the technical writer assigned to the Stage/Group associated w
NOTE:
GitLab Dedicated is currently in limited availability. Please [contact us](#contact-us) if you are interested.
GitLab Dedicated is a fully isolated, single-tenant GitLab instance that is:
GitLab Dedicated is a fully isolated, single-tenant SaaS service that is:
- Hosted and managed by GitLab, Inc.
- Deployed in a region of choice in AWS.
- Deployed in a region of choice on AWS.
GitLab Dedicated enables you to offload the operational overhead of managing the DevOps Platform. It offers a high level of tenant isolation and deployment customization, ideal for enterprises in highly-regulated industries. By deploying your GitLab instance onto a separate Cloud Infrastructure from other tenants, GitLab Dedicated helps you better meet your security and compliance requirements.
GitLab Dedicated enables you to offload the operational overhead of managing the DevOps Platform. It offers a high level of tenant isolation and deployment customization, ideal for enterprises in highly-regulated industries. By deploying your GitLab instance onto separate Cloud Infrastructure from other tenants, GitLab Dedicated helps you better meet your security and compliance requirements.
## Available features
@ -25,9 +25,9 @@ GitLab Dedicated enables you to offload the operational overhead of managing the
You can specify an AWS IAM Principal and preferred Availability Zones during onboarding to enable this functionality.
- Upgrade strategy:
- Monthly upgrades tracking one release behind the latest (n-1), with the latest security release.
- Out of band security patches provided for high severity items.
- Out of band security patches provided for high severity releases.
- Backup strategy: regular backups taken and tested.
- Choice of Cloud Region: upon onboarding, choose the cloud region where you want to deploy your instance. Some AWS regions have limited features and as a result, we are not able to deploy production instances to those regions. See below for the [full list of regions](#aws-regions-not-supported) not currently supported.
- Choice of cloud region: upon onboarding, choose the cloud region where you want to deploy your instance. Some AWS regions have limited features and as a result, we are not able to deploy production instances to those regions. See below for the [full list of regions](#aws-regions-not-supported) not currently supported.
- Security: Data encrypted at rest and in transit using latest encryption standards.
- Application: Self-managed [Ultimate feature set](https://about.gitlab.com/pricing/self-managed/feature-comparison/) with the exception of the unsupported features [listed below](#features-not-available-at-launch).

View file

@ -30,6 +30,7 @@ GitLab SaaS or GitLab self-managed:
- [GitLab SaaS](gitlab_com/index.md): The GitLab software-as-a-service offering.
You don't need to install anything to use GitLab SaaS, you only need to
[sign up](https://gitlab.com/users/sign_up) and start using GitLab straight away.
- [GitLab Dedicated](gitlab_dedicated/index.md): a single-tenant SaaS service for highly regulated and large enterprises.
- [GitLab self-managed](self_managed/index.md): Install, administer, and maintain
your own GitLab instance.

View file

@ -61,6 +61,15 @@ be present during the 16.x cycle to avoid breaking the API signature, and will b
**Planned removal milestone: 16.0 (2023-05-22)**
### Vulnerability Report sort by State
The ability to sort the Vulnerability Report by the `State` column was disabled and put behind a feature flag in GitLab 14.10 due to a refactor
of the underlying data model. The feature flag has remained off by default as further refactoring will be required to ensure sorting
by this value remains performant. Due to very low usage of the `State` column for sorting, the feature flag will instead be removed in
GitLab 15.2 to simplify the codebase and prevent any unwanted performance degradation.
**Planned removal milestone: 15.2 (2022-07-22)**
## 14.10
### Dependency Scanning default Java version changed to 17

View file

@ -630,6 +630,16 @@ for how to proceed.
### 14.0.0
Prerequisites:
- The [GitLab 14.0 release post contains several important notes](https://about.gitlab.com/releases/2021/06/22/gitlab-14-0-released/#upgrade)
about pre-requisites including [using Patroni instead of repmgr](../administration/postgresql/replication_and_failover.md#switching-from-repmgr-to-patroni),
migrating [to hashed storage](../administration/raketasks/storage.md#migrate-to-hashed-storage),
and [to Puma](../administration/operations/puma.md).
- The support of PostgreSQL 11 [has been dropped](../install/requirements.md#database). Make sure to [update your database](https://docs.gitlab.com/omnibus/settings/database.html#upgrade-packaged-postgresql-server) to version 12 before updating to GitLab 14.0.
Long running batched background database migrations:
- Database changes made by the upgrade to GitLab 14.0 can take hours or days to complete on larger GitLab instances.
These [batched background migrations](#batched-background-migrations) update whole database tables to mitigate primary key overflow and must be finished before upgrading to GitLab 14.2 or higher.
- Due to an issue where `BatchedBackgroundMigrationWorkers` were
@ -650,13 +660,12 @@ for how to proceed.
See how to [resolve this error](../user/admin_area/monitoring/background_migrations.md#database-migrations-failing-because-of-batched-background-migration-not-finished).
Other issues:
- In GitLab 13.3 some [pipeline processing methods were deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/218536)
and this code was completely removed in GitLab 14.0. If you plan to upgrade from
**GitLab 13.2 or older** directly to 14.0 ([unsupported](#upgrading-to-a-new-major-version)), you should not have any pipelines running
when you upgrade or the pipelines might report the wrong status when the upgrade completes.
**GitLab 13.2 or older** directly to 14.0, this is [unsupported](#upgrading-to-a-new-major-version).
You should instead follow a [supported upgrade path](#upgrade-paths).
- The support of PostgreSQL 11 [has been dropped](../install/requirements.md#database). Make sure to [update your database](https://docs.gitlab.com/omnibus/settings/database.html#upgrade-packaged-postgresql-server) to version 12 before updating to GitLab 14.0.
- See [Maintenance mode issue in GitLab 13.9 to 14.4](#maintenance-mode-issue-in-gitlab-139-to-144).
- See [Custom Rack Attack initializers](#custom-rack-attack-initializers) if you persist your own custom Rack Attack
initializers during upgrades.
@ -673,7 +682,16 @@ for how to proceed.
### 13.12.0
See [Maintenance mode issue in GitLab 13.9 to 14.4](#maintenance-mode-issue-in-gitlab-139-to-144).
- See [Maintenance mode issue in GitLab 13.9 to 14.4](#maintenance-mode-issue-in-gitlab-139-to-144).
- Check the GitLab database [has no references to legacy storage](../administration/raketasks/storage.md#on-legacy-storage).
The GitLab 14.0 pre-install check will cause the package update to fail if there is unmigrated data:
```plaintext
Checking for unmigrated data on legacy storage
Legacy storage is no longer supported. Please migrate your data to hashed storage.
```
### 13.11.0

View file

@ -44,7 +44,7 @@ module Gitlab
validates :action,
type: String,
inclusion: { in: %w[start stop prepare verify], message: 'should be start, stop, prepare, or verify' },
inclusion: { in: %w[start stop prepare verify access], message: 'should be start, stop, prepare, verify, or access' },
allow_nil: true
validates :deployment_tier,

View file

@ -348,6 +348,7 @@ operations_strategies: :gitlab_main
operations_strategies_user_lists: :gitlab_main
operations_user_lists: :gitlab_main
packages_build_infos: :gitlab_main
packages_cleanup_policies: :gitlab_main
packages_composer_cache_files: :gitlab_main
packages_composer_metadata: :gitlab_main
packages_conan_file_metadata: :gitlab_main

View file

@ -17455,6 +17455,12 @@ msgstr ""
msgid "GitLabPages|Remove"
msgstr ""
msgid "GitLabPages|Remove certificate"
msgstr ""
msgid "GitLabPages|Remove domain"
msgstr ""
msgid "GitLabPages|Remove pages"
msgstr ""
@ -35028,9 +35034,6 @@ msgstr ""
msgid "Set up new password"
msgstr ""
msgid "Set up shared runner availability"
msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
@ -45710,6 +45713,9 @@ msgstr ""
msgid "pipelineEditorWalkthrough|Use the %{boldStart}commit changes%{boldEnd} button at the bottom of the page to run the pipeline."
msgstr ""
msgid "pipelineEditorWalkthrough|You can use the file tree to view your pipeline configuration files."
msgstr ""
msgid "pod_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""

View file

@ -5,6 +5,10 @@ module QA
module Project
module PipelineEditor
class Show < QA::Page::Base
view 'app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue' do
element :pipeline_editor_app, required: true
end
view 'app/assets/javascripts/pipeline_editor/components/file_nav/branch_switcher.vue' do
element :branch_selector_button, required: true
element :branch_menu_item_button
@ -46,6 +50,21 @@ module QA
element :file_editor_container
end
view 'app/assets/javascripts/pipeline_editor/components/popovers/file_tree_popover.vue' do
element :file_tree_popover
end
def initialize
dismiss_file_tree_popover if has_element?(:file_tree_popover)
super
end
def dismiss_file_tree_popover
# clicking outside the popover will dismiss it
click_element(:pipeline_editor_app)
end
def open_branch_selector_dropdown
click_element(:branch_selector_button)
end

View file

@ -71,6 +71,17 @@ RSpec.describe GroupsController, factory_default: :keep do
context 'when the group is not importing' do
it_behaves_like 'details view'
it 'tracks page views', :snowplow do
subject
expect_snowplow_event(
category: 'group_overview',
action: 'render',
user: user,
namespace: group
)
end
end
context 'when the group is importing' do
@ -81,6 +92,17 @@ RSpec.describe GroupsController, factory_default: :keep do
it 'redirects to the import status page' do
expect(subject).to redirect_to group_import_path(group)
end
it 'does not track page views', :snowplow do
subject
expect_no_snowplow_event(
category: 'group_overview',
action: 'render',
user: user,
namespace: group
)
end
end
end

View file

@ -309,6 +309,35 @@ RSpec.describe ProjectsController do
expect(response.body).to have_content('LICENSE') # would be 'MIT license' if stub not works
end
describe 'tracking events', :snowplow do
before do
allow(controller).to receive(:current_user).and_return(user)
get_show
end
it 'tracks page views' do
expect_snowplow_event(
category: 'project_overview',
action: 'render',
user: user,
project: public_project
)
end
context 'when the project is importing' do
let_it_be(:public_project) { create(:project, :public, :import_scheduled) }
it 'does not track page views' do
expect_no_snowplow_event(
category: 'project_overview',
action: 'render',
user: user,
project: public_project
)
end
end
end
describe "PUC highlighting" do
render_views

View file

@ -0,0 +1,16 @@
# frozen_string_literal: true
FactoryBot.define do
factory :packages_cleanup_policy, class: 'Packages::Cleanup::Policy' do
project
keep_n_duplicated_package_files { '10' }
trait :runnable do
after(:create) do |policy|
# next_run_at will be set before_save to Time.now + cadence, so this ensures the policy is active
policy.update_column(:next_run_at, Time.zone.now - 1.day)
end
end
end
end

View file

@ -17,7 +17,7 @@ RSpec.describe 'Group CI/CD settings' do
end
describe 'Runners section' do
let(:shared_runners_toggle) { page.find('[data-testid="enable-runners-toggle"]') }
let(:shared_runners_toggle) { page.find('[data-testid="shared-runners-toggle"]') }
before do
visit group_settings_ci_cd_path(group)
@ -31,6 +31,16 @@ RSpec.describe 'Group CI/CD settings' do
it 'has "Enable shared runners for this group" toggle', :js do
expect(shared_runners_toggle).to have_content(_('Enable shared runners for this group'))
end
it 'clicks on toggle to enable setting', :js do
expect(group.shared_runners_setting).to be(Namespace::SR_ENABLED)
shared_runners_toggle.find('button').click
wait_for_requests
group.reload
expect(group.shared_runners_setting).to be(Namespace::SR_DISABLED_AND_UNOVERRIDABLE)
end
end
describe 'Auto DevOps form' do

View file

@ -12,6 +12,8 @@ RSpec.describe 'Pipeline Editor', :js do
let(:other_branch) { 'test' }
before do
stub_feature_flags(pipeline_editor_file_tree: false)
sign_in(user)
project.add_developer(user)
@ -22,11 +24,7 @@ RSpec.describe 'Pipeline Editor', :js do
wait_for_requests
end
it 'user sees the Pipeline Editor page' do
expect(page).to have_content('Pipeline Editor')
end
describe 'Branch switcher' do
shared_examples 'default branch switcher behavior' do
def switch_to_branch(branch)
find('[data-testid="branch-selector"]').click
@ -68,6 +66,28 @@ RSpec.describe 'Pipeline Editor', :js do
end
end
it 'user sees the Pipeline Editor page' do
expect(page).to have_content('Pipeline Editor')
end
describe 'Branch Switcher (pipeline_editor_file_tree disabled)' do
it_behaves_like 'default branch switcher behavior'
end
describe 'Branch Switcher (pipeline_editor_file_tree enabled)' do
before do
stub_feature_flags(pipeline_editor_file_tree: true)
visit project_ci_pipeline_editor_path(project)
wait_for_requests
# close button for the popover
find('[data-testid="close-button"]').click
end
it_behaves_like 'default branch switcher behavior'
end
describe 'Editor navigation' do
context 'when no change is made' do
it 'user can navigate away without a browser alert' do

View file

@ -1,47 +1,46 @@
import { GlLoadingIcon, GlAlert } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { GlAlert } from '@gitlab/ui';
import MockAxiosAdapter from 'axios-mock-adapter';
import { nextTick } from 'vue';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
import SharedRunnersForm from '~/group_settings/components/shared_runners_form.vue';
import axios from '~/lib/utils/axios_utils';
const provide = {
updatePath: '/test/update',
sharedRunnersAvailability: 'enabled',
parentSharedRunnersAvailability: null,
runnerDisabled: 'disabled',
runnerEnabled: 'enabled',
runnerAllowOverride: 'allow_override',
};
jest.mock('~/flash');
const UPDATE_PATH = '/test/update';
const RUNNER_ENABLED_VALUE = 'enabled';
const RUNNER_DISABLED_VALUE = 'disabled_and_unoverridable';
const RUNNER_ALLOW_OVERRIDE_VALUE = 'disabled_with_override';
describe('group_settings/components/shared_runners_form', () => {
let wrapper;
let mock;
const createComponent = (provides = {}) => {
wrapper = shallowMount(SharedRunnersForm, {
const createComponent = (provide = {}) => {
wrapper = shallowMountExtended(SharedRunnersForm, {
provide: {
updatePath: UPDATE_PATH,
sharedRunnersSetting: RUNNER_ENABLED_VALUE,
parentSharedRunnersSetting: null,
runnerEnabledValue: RUNNER_ENABLED_VALUE,
runnerDisabledValue: RUNNER_DISABLED_VALUE,
runnerAllowOverrideValue: RUNNER_ALLOW_OVERRIDE_VALUE,
...provide,
...provides,
},
});
};
const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
const findErrorAlert = () => wrapper.find(GlAlert);
const findEnabledToggle = () => wrapper.find('[data-testid="enable-runners-toggle"]');
const findOverrideToggle = () => wrapper.find('[data-testid="override-runners-toggle"]');
const changeToggle = (toggle) => toggle.vm.$emit('change', !toggle.props('value'));
const findAlert = (variant) =>
wrapper
.findAllComponents(GlAlert)
.filter((w) => w.props('variant') === variant)
.at(0);
const findSharedRunnersToggle = () => wrapper.findByTestId('shared-runners-toggle');
const findOverrideToggle = () => wrapper.findByTestId('override-runners-toggle');
const getSharedRunnersSetting = () => JSON.parse(mock.history.put[0].data).shared_runners_setting;
const isLoadingIconVisible = () => findLoadingIcon().exists();
beforeEach(() => {
mock = new MockAxiosAdapter(axios);
mock.onPut(provide.updatePath).reply(200);
mock.onPut(UPDATE_PATH).reply(200);
});
afterEach(() => {
@ -51,102 +50,122 @@ describe('group_settings/components/shared_runners_form', () => {
mock.restore();
});
describe('with default', () => {
describe('default state', () => {
beforeEach(() => {
createComponent();
});
it('loading icon does not exist', () => {
expect(isLoadingIconVisible()).toBe(false);
it('"Enable shared runners" toggle is enabled', () => {
expect(findSharedRunnersToggle().props()).toMatchObject({
isLoading: false,
disabled: false,
});
});
it('enabled toggle exists', () => {
expect(findEnabledToggle().exists()).toBe(true);
});
it('override toggle does not exist', () => {
expect(findOverrideToggle().exists()).toBe(false);
it('"Override the group setting" is disabled', () => {
expect(findOverrideToggle().props()).toMatchObject({
isLoading: false,
disabled: true,
});
});
});
describe('loading icon', () => {
it('shows and hides the loading icon on request', async () => {
describe('When group disabled shared runners', () => {
it(`toggles are not disabled with setting ${RUNNER_DISABLED_VALUE}`, () => {
createComponent({ sharedRunnersSetting: RUNNER_DISABLED_VALUE });
expect(findSharedRunnersToggle().props('disabled')).toBe(false);
expect(findOverrideToggle().props('disabled')).toBe(false);
});
});
describe('When parent group disabled shared runners', () => {
it('toggles are disabled', () => {
createComponent({
sharedRunnersSetting: RUNNER_DISABLED_VALUE,
parentSharedRunnersSetting: RUNNER_DISABLED_VALUE,
});
expect(findSharedRunnersToggle().props('disabled')).toBe(true);
expect(findOverrideToggle().props('disabled')).toBe(true);
expect(findAlert('warning').exists()).toBe(true);
});
});
describe('loading state', () => {
beforeEach(() => {
createComponent();
});
expect(isLoadingIconVisible()).toBe(false);
findEnabledToggle().vm.$emit('change', true);
it('is not loading by default', () => {
expect(findSharedRunnersToggle().props('isLoading')).toBe(false);
expect(findOverrideToggle().props('isLoading')).toBe(false);
});
it('is loading immediately after request', async () => {
findSharedRunnersToggle().vm.$emit('change', true);
await nextTick();
expect(isLoadingIconVisible()).toBe(true);
expect(findSharedRunnersToggle().props('isLoading')).toBe(true);
expect(findOverrideToggle().props('isLoading')).toBe(true);
});
it('does not update settings while loading', async () => {
findSharedRunnersToggle().vm.$emit('change', true);
findSharedRunnersToggle().vm.$emit('change', false);
await waitForPromises();
expect(isLoadingIconVisible()).toBe(false);
expect(mock.history.put.length).toBe(1);
});
it('is not loading state after completed request', async () => {
findSharedRunnersToggle().vm.$emit('change', true);
await waitForPromises();
expect(findSharedRunnersToggle().props('isLoading')).toBe(false);
expect(findOverrideToggle().props('isLoading')).toBe(false);
});
});
describe('enable toggle', () => {
describe('"Enable shared runners" toggle', () => {
beforeEach(() => {
createComponent();
});
it('enabling the toggle sends correct payload', async () => {
findEnabledToggle().vm.$emit('change', true);
it('sends correct payload when turned on', async () => {
findSharedRunnersToggle().vm.$emit('change', true);
await waitForPromises();
expect(getSharedRunnersSetting()).toEqual(provide.runnerEnabled);
expect(findOverrideToggle().exists()).toBe(false);
expect(getSharedRunnersSetting()).toEqual(RUNNER_ENABLED_VALUE);
expect(findOverrideToggle().props('disabled')).toBe(true);
});
it('disabling the toggle sends correct payload', async () => {
findEnabledToggle().vm.$emit('change', false);
it('sends correct payload when turned off', async () => {
findSharedRunnersToggle().vm.$emit('change', false);
await waitForPromises();
expect(getSharedRunnersSetting()).toEqual(provide.runnerDisabled);
expect(findOverrideToggle().exists()).toBe(true);
expect(getSharedRunnersSetting()).toEqual(RUNNER_DISABLED_VALUE);
expect(findOverrideToggle().props('disabled')).toBe(false);
});
});
describe('override toggle', () => {
describe('"Override the group setting" toggle', () => {
beforeEach(() => {
createComponent({ sharedRunnersAvailability: provide.runnerAllowOverride });
createComponent({ sharedRunnersSetting: RUNNER_ALLOW_OVERRIDE_VALUE });
});
it('enabling the override toggle sends correct payload', async () => {
findOverrideToggle().vm.$emit('change', true);
await waitForPromises();
expect(getSharedRunnersSetting()).toEqual(provide.runnerAllowOverride);
expect(getSharedRunnersSetting()).toEqual(RUNNER_ALLOW_OVERRIDE_VALUE);
});
it('disabling the override toggle sends correct payload', async () => {
findOverrideToggle().vm.$emit('change', false);
await waitForPromises();
expect(getSharedRunnersSetting()).toEqual(provide.runnerDisabled);
});
});
describe('toggle disabled state', () => {
it(`toggles are not disabled with setting ${provide.runnerDisabled}`, () => {
createComponent({ sharedRunnersAvailability: provide.runnerDisabled });
expect(findEnabledToggle().props('disabled')).toBe(false);
expect(findOverrideToggle().props('disabled')).toBe(false);
});
it('toggles are disabled', () => {
createComponent({
sharedRunnersAvailability: provide.runnerDisabled,
parentSharedRunnersAvailability: provide.runnerDisabled,
});
expect(findEnabledToggle().props('disabled')).toBe(true);
expect(findOverrideToggle().props('disabled')).toBe(true);
expect(getSharedRunnersSetting()).toEqual(RUNNER_DISABLED_VALUE);
});
});
@ -156,16 +175,16 @@ describe('group_settings/components/shared_runners_form', () => {
${{ error: 'Undefined error' }} | ${'Undefined error Refresh the page and try again.'}
`(`with error $errorObj`, ({ errorObj, message }) => {
beforeEach(async () => {
mock.onPut(provide.updatePath).reply(500, errorObj);
mock.onPut(UPDATE_PATH).reply(500, errorObj);
createComponent();
changeToggle(findEnabledToggle());
findSharedRunnersToggle().vm.$emit('change', false);
await waitForPromises();
});
it('error should be shown', () => {
expect(findErrorAlert().text()).toBe(message);
expect(findAlert('danger').text()).toBe(message);
});
});
});

View file

@ -5,6 +5,7 @@ import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import createMockApollo from 'helpers/mock_apollo_helper';
import BranchSwitcher from '~/pipeline_editor/components/file_nav/branch_switcher.vue';
import PipelineEditorFileNav from '~/pipeline_editor/components/file_nav/pipeline_editor_file_nav.vue';
import FileTreePopover from '~/pipeline_editor/components/popovers/file_tree_popover.vue';
import getAppStatus from '~/pipeline_editor/graphql/queries/client/app_status.query.graphql';
import { EDITOR_APP_STATUS_EMPTY, EDITOR_APP_STATUS_VALID } from '~/pipeline_editor/constants';
@ -47,6 +48,7 @@ describe('Pipeline editor file nav', () => {
const findBranchSwitcher = () => wrapper.findComponent(BranchSwitcher);
const findFileTreeBtn = () => wrapper.findByTestId('file-tree-toggle');
const findPopoverContainer = () => wrapper.findComponent(FileTreePopover);
afterEach(() => {
wrapper.destroy();
@ -64,31 +66,47 @@ describe('Pipeline editor file nav', () => {
it('does not render the file tree button', () => {
expect(findFileTreeBtn().exists()).toBe(false);
});
it('does not render the file tree popover', () => {
expect(findPopoverContainer().exists()).toBe(false);
});
});
describe('with pipelineEditorFileTree feature flag ON', () => {
describe('when editor is in the empty state', () => {
it('does not render the file tree button', () => {
beforeEach(() => {
createComponent({
appStatus: EDITOR_APP_STATUS_EMPTY,
isNewCiConfigFile: false,
pipelineEditorFileTree: true,
});
});
it('does not render the file tree button', () => {
expect(findFileTreeBtn().exists()).toBe(false);
});
it('does not render the file tree popover', () => {
expect(findPopoverContainer().exists()).toBe(false);
});
});
describe('when user is about to create their config file for the first time', () => {
it('does not render the file tree button', () => {
beforeEach(() => {
createComponent({
appStatus: EDITOR_APP_STATUS_VALID,
isNewCiConfigFile: true,
pipelineEditorFileTree: true,
});
});
it('does not render the file tree button', () => {
expect(findFileTreeBtn().exists()).toBe(false);
});
it('does not render the file tree popover', () => {
expect(findPopoverContainer().exists()).toBe(false);
});
});
describe('when editor has a non-empty config file open', () => {
@ -105,6 +123,10 @@ describe('Pipeline editor file nav', () => {
expect(findFileTreeBtn().props('icon')).toBe('file-tree');
});
it('renders the file tree popover', () => {
expect(findPopoverContainer().exists()).toBe(true);
});
it('file tree button emits toggle-file-tree event', () => {
expect(wrapper.emitted('toggle-file-tree')).toBe(undefined);

View file

@ -3,7 +3,7 @@ import { shallowMount, mount } from '@vue/test-utils';
import Vue, { nextTick } from 'vue';
import setWindowLocation from 'helpers/set_window_location_helper';
import CiConfigMergedPreview from '~/pipeline_editor/components/editor/ci_config_merged_preview.vue';
import WalkthroughPopover from '~/pipeline_editor/components/walkthrough_popover.vue';
import WalkthroughPopover from '~/pipeline_editor/components/popovers/walkthrough_popover.vue';
import CiLint from '~/pipeline_editor/components/lint/ci_lint.vue';
import PipelineEditorTabs from '~/pipeline_editor/components/pipeline_editor_tabs.vue';
import EditorTab from '~/pipeline_editor/components/ui/editor_tab.vue';

View file

@ -0,0 +1,44 @@
import { nextTick } from 'vue';
import { GlPopover } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import FileTreePopover from '~/pipeline_editor/components/popovers/file_tree_popover.vue';
import { FILE_TREE_POPOVER_DISMISSED_KEY } from '~/pipeline_editor/constants';
describe('FileTreePopover component', () => {
let wrapper;
const findPopover = () => wrapper.findComponent(GlPopover);
const createComponent = (mountFn = shallowMount) => {
wrapper = mountFn(FileTreePopover);
};
afterEach(() => {
localStorage.clear();
wrapper.destroy();
});
describe('default', () => {
beforeEach(async () => {
createComponent();
});
it('renders dismissable popover', async () => {
expect(findPopover().exists()).toBe(true);
findPopover().vm.$emit('close-button-clicked');
await nextTick();
expect(findPopover().exists()).toBe(false);
});
});
describe('when popover has already been dismissed before', () => {
it('does not render popover', async () => {
localStorage.setItem(FILE_TREE_POPOVER_DISMISSED_KEY, 'true');
createComponent();
expect(findPopover().exists()).toBe(false);
});
});
});

View file

@ -1,6 +1,6 @@
import { mount, shallowMount } from '@vue/test-utils';
import Vue from 'vue';
import WalkthroughPopover from '~/pipeline_editor/components/walkthrough_popover.vue';
import WalkthroughPopover from '~/pipeline_editor/components/popovers/walkthrough_popover.vue';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
Vue.config.ignoredElements = ['gl-emoji'];

View file

@ -10,7 +10,13 @@ import PipelineEditorFileTree from '~/pipeline_editor/components/file_tree/conta
import BranchSwitcher from '~/pipeline_editor/components/file_nav/branch_switcher.vue';
import PipelineEditorHeader from '~/pipeline_editor/components/header/pipeline_editor_header.vue';
import PipelineEditorTabs from '~/pipeline_editor/components/pipeline_editor_tabs.vue';
import { MERGED_TAB, VISUALIZE_TAB, CREATE_TAB, LINT_TAB } from '~/pipeline_editor/constants';
import {
MERGED_TAB,
VISUALIZE_TAB,
CREATE_TAB,
LINT_TAB,
FILE_TREE_DISPLAY_KEY,
} from '~/pipeline_editor/constants';
import PipelineEditorHome from '~/pipeline_editor/pipeline_editor_home.vue';
import { mockLintResponse, mockCiYml } from './mock_data';
@ -55,6 +61,7 @@ describe('Pipeline editor home wrapper', () => {
const findHelpBtn = () => wrapper.findByTestId('drawer-toggle');
afterEach(() => {
localStorage.clear();
wrapper.destroy();
});
@ -252,34 +259,69 @@ describe('Pipeline editor home wrapper', () => {
});
describe('with pipelineEditorFileTree feature flag ON', () => {
beforeEach(() => {
createComponent({
glFeatures: {
pipelineEditorFileTree: true,
},
stubs: {
GlButton,
PipelineEditorFileNav,
},
describe('button toggle', () => {
beforeEach(() => {
createComponent({
glFeatures: {
pipelineEditorFileTree: true,
},
stubs: {
GlButton,
PipelineEditorFileNav,
},
});
});
it('shows button toggle', () => {
expect(findFileTreeBtn().exists()).toBe(true);
});
it('toggles the drawer on button click', async () => {
await toggleFileTree();
expect(findPipelineEditorFileTree().exists()).toBe(true);
await toggleFileTree();
expect(findPipelineEditorFileTree().exists()).toBe(false);
});
it('sets the display state in local storage', async () => {
await toggleFileTree();
expect(localStorage.getItem(FILE_TREE_DISPLAY_KEY)).toBe('true');
await toggleFileTree();
expect(localStorage.getItem(FILE_TREE_DISPLAY_KEY)).toBe('false');
});
});
it('shows button toggle', () => {
expect(findFileTreeBtn().exists()).toBe(true);
describe('when file tree display state is saved in local storage', () => {
beforeEach(() => {
localStorage.setItem(FILE_TREE_DISPLAY_KEY, 'true');
createComponent({
glFeatures: { pipelineEditorFileTree: true },
stubs: { PipelineEditorFileNav },
});
});
it('shows the file tree by default', () => {
expect(findPipelineEditorFileTree().exists()).toBe(true);
});
});
it('hides the file tree by default', () => {
expect(findPipelineEditorFileTree().exists()).toBe(false);
});
describe('when file tree display state is not saved in local storage', () => {
beforeEach(() => {
createComponent({
glFeatures: { pipelineEditorFileTree: true },
stubs: { PipelineEditorFileNav },
});
});
it('toggles the drawer on button click', async () => {
await toggleFileTree();
expect(findPipelineEditorFileTree().exists()).toBe(true);
await toggleFileTree();
expect(findPipelineEditorFileTree().exists()).toBe(false);
it('hides the file tree by default', () => {
expect(findPipelineEditorFileTree().exists()).toBe(false);
});
});
});
});

View file

@ -99,17 +99,17 @@ RSpec.describe Ci::RunnersHelper do
let(:runner_constants) do
{
runner_enabled: Namespace::SR_ENABLED,
runner_disabled: Namespace::SR_DISABLED_AND_UNOVERRIDABLE,
runner_allow_override: Namespace::SR_DISABLED_WITH_OVERRIDE
runner_enabled_value: Namespace::SR_ENABLED,
runner_disabled_value: Namespace::SR_DISABLED_AND_UNOVERRIDABLE,
runner_allow_override_value: Namespace::SR_DISABLED_WITH_OVERRIDE
}
end
it 'returns group data for top level group' do
result = {
update_path: "/api/v4/groups/#{parent.id}",
shared_runners_availability: Namespace::SR_ENABLED,
parent_shared_runners_availability: nil
shared_runners_setting: Namespace::SR_ENABLED,
parent_shared_runners_setting: nil
}.merge(runner_constants)
expect(helper.group_shared_runners_settings_data(parent)).to eq result
@ -118,8 +118,8 @@ RSpec.describe Ci::RunnersHelper do
it 'returns group data for child group' do
result = {
update_path: "/api/v4/groups/#{group.id}",
shared_runners_availability: Namespace::SR_DISABLED_AND_UNOVERRIDABLE,
parent_shared_runners_availability: Namespace::SR_ENABLED
shared_runners_setting: Namespace::SR_DISABLED_AND_UNOVERRIDABLE,
parent_shared_runners_setting: Namespace::SR_ENABLED
}.merge(runner_constants)
expect(helper.group_shared_runners_settings_data(group)).to eq result

View file

@ -92,35 +92,18 @@ RSpec.describe Gitlab::Ci::Config::Entry::Environment do
end
context 'when valid action is used' do
let(:config) do
{ name: 'production',
action: 'start' }
where(:action) do
%w(start stop prepare verify access)
end
it 'is valid' do
expect(entry).to be_valid
end
end
with_them do
let(:config) do
{ name: 'production', action: action }
end
context 'when prepare action is used' do
let(:config) do
{ name: 'production',
action: 'prepare' }
end
it 'is valid' do
expect(entry).to be_valid
end
end
context 'when verify action is used' do
let(:config) do
{ name: 'production',
action: 'verify' }
end
it 'is valid' do
expect(entry).to be_valid
it 'is valid' do
expect(entry).to be_valid
end
end
end
@ -159,7 +142,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Environment do
describe '#errors' do
it 'contains error about invalid action' do
expect(entry.errors)
.to include 'environment action should be start, stop, prepare, or verify'
.to include 'environment action should be start, stop, prepare, verify, or access'
end
end
end

View file

@ -90,42 +90,22 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Deployment do
end
end
context 'when job has environment attribute with stop action' do
let(:attributes) do
{
environment: 'production',
options: { environment: { name: 'production', action: 'stop' } }
}
context 'when job does not start environment' do
where(:action) do
%w(stop prepare verify access)
end
it 'returns nothing' do
is_expected.to be_nil
end
end
with_them do
let(:attributes) do
{
environment: 'production',
options: { environment: { name: 'production', action: action } }
}
end
context 'when job has environment attribute with prepare action' do
let(:attributes) do
{
environment: 'production',
options: { environment: { name: 'production', action: 'prepare' } }
}
end
it 'returns nothing' do
is_expected.to be_nil
end
end
context 'when job has environment attribute with verify action' do
let(:attributes) do
{
environment: 'production',
options: { environment: { name: 'production', action: 'verify' } }
}
end
it 'returns nothing' do
is_expected.to be_nil
it 'returns nothing' do
is_expected.to be_nil
end
end
end

View file

@ -550,6 +550,7 @@ project:
- project_registry
- packages
- package_files
- packages_cleanup_policy
- tracing_setting
- alerting_setting
- project_setting

View file

@ -57,6 +57,16 @@ RSpec.describe Schedulable do
it_behaves_like '.runnable_schedules'
end
context 'for a packages cleanup policy' do
# let! is used to reset the next_run_at value before each spec
let(:object) { create(:packages_cleanup_policy, :runnable) }
let(:non_runnable_object) { create(:packages_cleanup_policy) }
it_behaves_like '#schedule_next_run!'
it_behaves_like 'before_save callback'
it_behaves_like '.runnable_schedules'
end
describe '#next_run_at' do
let(:schedulable_instance) do
Class.new(ActiveRecord::Base) do

View file

@ -0,0 +1,28 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Packages::Cleanup::Policy, type: :model do
describe 'relationships' do
it { is_expected.to belong_to(:project) }
end
describe 'validations' do
it { is_expected.to validate_presence_of(:project) }
it do
is_expected
.to validate_inclusion_of(:keep_n_duplicated_package_files)
.in_array(described_class::KEEP_N_DUPLICATED_PACKAGE_FILES_VALUES)
.with_message('keep_n_duplicated_package_files is invalid')
end
end
describe '.active' do
let_it_be(:active_policy) { create(:packages_cleanup_policy) }
let_it_be(:inactive_policy) { create(:packages_cleanup_policy, keep_n_duplicated_package_files: 'all') }
subject { described_class.active }
it { is_expected.to contain_exactly(active_policy) }
end
end

View file

@ -135,6 +135,7 @@ RSpec.describe Project, factory_default: :keep do
it { is_expected.to have_many(:packages).class_name('Packages::Package') }
it { is_expected.to have_many(:package_files).class_name('Packages::PackageFile') }
it { is_expected.to have_many(:debian_distributions).class_name('Packages::Debian::ProjectDistribution').dependent(:destroy) }
it { is_expected.to have_one(:packages_cleanup_policy).class_name('Packages::Cleanup::Policy').inverse_of(:project) }
it { is_expected.to have_many(:pipeline_artifacts).dependent(:restrict_with_error) }
it { is_expected.to have_many(:terraform_states).class_name('Terraform::State').inverse_of(:project) }
it { is_expected.to have_many(:timelogs) }

View file

@ -233,6 +233,23 @@ RSpec.shared_examples 'User creates wiki page' do
.and have_content("Last edited by #{user.name}")
.and have_content("My awesome wiki!")
end
context 'when a server side validation error is returned' do
it "still displays edit form", :js do
click_link("New page")
page.within(".wiki-form") do
fill_in(:wiki_title, with: "home")
fill_in(:wiki_content, with: "My awesome home page!")
end
# Submits page with a name already in use to trigger a validation error
click_button("Create page")
expect(page).to have_field(:wiki_title)
expect(page).to have_field(:wiki_content)
end
end
end
it "shows the emoji autocompletion dropdown", :js do