Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
8ed0a009f0
commit
856e2c64ee
66 changed files with 977 additions and 342 deletions
|
@ -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": {
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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.');
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
import { mountApplications } from '~/pages/shared/wikis/edit';
|
||||
|
||||
mountApplications();
|
|
@ -1,3 +1,6 @@
|
|||
import Wikis from '~/pages/shared/wikis/wikis';
|
||||
import { mountApplications } from '~/pages/shared/wikis/async_edit';
|
||||
|
||||
mountApplications();
|
||||
|
||||
export default new Wikis();
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import { mountApplications } from '~/pages/shared/wikis/show';
|
||||
import { mountApplications as mountEditApplications } from '~/pages/shared/wikis/async_edit';
|
||||
|
||||
mountApplications();
|
||||
mountEditApplications();
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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>
|
|
@ -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 = {
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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?
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
8
app/models/packages/cleanup.rb
Normal file
8
app/models/packages/cleanup.rb
Normal file
|
@ -0,0 +1,8 @@
|
|||
# frozen_string_literal: true
|
||||
module Packages
|
||||
module Cleanup
|
||||
def self.table_name_prefix
|
||||
'packages_cleanup_'
|
||||
end
|
||||
end
|
||||
end
|
32
app/models/packages/cleanup/policy.rb
Normal file
32
app/models/packages/cleanup/policy.rb
Normal 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
|
|
@ -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?
|
||||
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
21
config/events/1651580551_groups_controller_show_render.yml
Normal file
21
config/events/1651580551_groups_controller_show_render.yml
Normal 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
|
21
config/events/1651581659_projects_controller_show_render.yml
Normal file
21
config/events/1651581659_projects_controller_show_render.yml
Normal 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
|
|
@ -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
|
9
db/docs/packages_cleanup_policies.yml
Normal file
9
db/docs/packages_cleanup_policies.yml
Normal 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'
|
|
@ -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
|
1
db/schema_migrations/20220425120604
Normal file
1
db/schema_migrations/20220425120604
Normal file
|
@ -0,0 +1 @@
|
|||
56ebfb7a97217af23f12db7f93c47104be30da7633a22caf9e74547d5a27d29b
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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#/.*##')
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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`**:
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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).
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ""
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
16
spec/factories/packages/cleanup/policies.rb
Normal file
16
spec/factories/packages/cleanup/policies.rb
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -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'];
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -550,6 +550,7 @@ project:
|
|||
- project_registry
|
||||
- packages
|
||||
- package_files
|
||||
- packages_cleanup_policy
|
||||
- tracing_setting
|
||||
- alerting_setting
|
||||
- project_setting
|
||||
|
|
|
@ -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
|
||||
|
|
28
spec/models/packages/cleanup/policy_spec.rb
Normal file
28
spec/models/packages/cleanup/policy_spec.rb
Normal 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
|
|
@ -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) }
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue