diff options
214 files changed, 906 insertions, 520 deletions
diff --git a/.rubocop_todo/style/open_struct_use.yml b/.rubocop_todo/style/open_struct_use.yml index e6af3b7bf24..172023c8fc1 100644 --- a/.rubocop_todo/style/open_struct_use.yml +++ b/.rubocop_todo/style/open_struct_use.yml @@ -8,11 +8,6 @@ Style/OpenStructUse: - ee/spec/lib/gitlab/legacy_github_import/project_creator_spec.rb - lib/gitlab/testing/request_inspector_middleware.rb - spec/factories/wiki_pages.rb - - spec/graphql/mutations/branches/create_spec.rb - - spec/graphql/mutations/clusters/agent_tokens/create_spec.rb - - spec/graphql/mutations/clusters/agents/create_spec.rb - - spec/graphql/mutations/clusters/agents/delete_spec.rb - - spec/graphql/mutations/commits/create_spec.rb - spec/helpers/application_settings_helper_spec.rb - spec/helpers/profiles_helper_spec.rb - spec/lib/gitlab/gitaly_client/blobs_stitcher_spec.rb diff --git a/app/assets/javascripts/authentication/two_factor_auth/components/recovery_codes.vue b/app/assets/javascripts/authentication/two_factor_auth/components/recovery_codes.vue index fe801cd460f..6a2e5f5c2c5 100644 --- a/app/assets/javascripts/authentication/two_factor_auth/components/recovery_codes.vue +++ b/app/assets/javascripts/authentication/two_factor_auth/components/recovery_codes.vue @@ -101,9 +101,9 @@ export default { <template> <div> - <h3 class="page-title"> + <h1 class="page-title"> {{ $options.i18n.pageTitle }} - </h3> + </h1> <hr /> <gl-alert variant="info" :dismissible="false"> {{ $options.i18n.alertTitle }} diff --git a/app/assets/javascripts/environments/components/environment_form.vue b/app/assets/javascripts/environments/components/environment_form.vue index 1d1d8d61b66..681036e4ba2 100644 --- a/app/assets/javascripts/environments/components/environment_form.vue +++ b/app/assets/javascripts/environments/components/environment_form.vue @@ -81,9 +81,9 @@ export default { </script> <template> <div> - <h3 class="page-title"> + <h1 class="page-title"> {{ title }} - </h3> + </h1> <hr /> <div class="row gl-mt-3 gl-mb-3"> <div class="col-lg-3"> diff --git a/app/assets/javascripts/environments/components/environments_detail_header.vue b/app/assets/javascripts/environments/components/environments_detail_header.vue index d71b553a878..7acb453b58b 100644 --- a/app/assets/javascripts/environments/components/environments_detail_header.vue +++ b/app/assets/javascripts/environments/components/environments_detail_header.vue @@ -94,9 +94,9 @@ export default { <template> <header class="top-area gl-justify-content-between"> <div class="gl-display-flex gl-flex-grow-1 gl-align-items-center"> - <h3 class="page-title"> + <h1 class="page-title"> {{ environment.name }} - </h3> + </h1> <p v-if="shouldShowCancelAutoStopButton" class="gl-mb-0 gl-ml-3" data-testid="auto-stops-at"> <gl-sprintf :message="$options.i18n.autoStopAtText"> <template #autoStopAt> diff --git a/app/assets/javascripts/error_tracking/components/error_details.vue b/app/assets/javascripts/error_tracking/components/error_details.vue index ad139f81079..c369d879739 100644 --- a/app/assets/javascripts/error_tracking/components/error_details.vue +++ b/app/assets/javascripts/error_tracking/components/error_details.vue @@ -364,7 +364,6 @@ export default { v-if="error.gitlabIssuePath" data-qa-selector="view_issue_button" :href="error.gitlabIssuePath" - variant="success" >{{ __('View issue') }}</gl-dropdown-item > <gl-dropdown-item diff --git a/app/assets/javascripts/feature_flags/components/new_feature_flag.vue b/app/assets/javascripts/feature_flags/components/new_feature_flag.vue index 865c1e677cd..041c9e5cd14 100644 --- a/app/assets/javascripts/feature_flags/components/new_feature_flag.vue +++ b/app/assets/javascripts/feature_flags/components/new_feature_flag.vue @@ -24,7 +24,7 @@ export default { </script> <template> <div> - <h3 class="page-title">{{ s__('FeatureFlags|New feature flag') }}</h3> + <h1 class="page-title">{{ s__('FeatureFlags|New feature flag') }}</h1> <gl-alert v-if="error.length" variant="warning" class="gl-mb-5" :dismissible="false"> <p v-for="(message, index) in error" :key="index" class="gl-mb-0">{{ message }}</p> diff --git a/app/assets/javascripts/jira_import/components/jira_import_form.vue b/app/assets/javascripts/jira_import/components/jira_import_form.vue index 4e5e9f4ade7..6f49aad1b77 100644 --- a/app/assets/javascripts/jira_import/components/jira_import_form.vue +++ b/app/assets/javascripts/jira_import/components/jira_import_form.vue @@ -254,7 +254,7 @@ export default { </gl-sprintf> </gl-alert> - <h3 class="page-title">{{ __('New Jira import') }}</h3> + <h1 class="page-title">{{ __('New Jira import') }}</h1> <hr /> diff --git a/app/assets/javascripts/jobs/components/stuck_block.vue b/app/assets/javascripts/jobs/components/stuck_block.vue index f9cde61e917..d7a26d22406 100644 --- a/app/assets/javascripts/jobs/components/stuck_block.vue +++ b/app/assets/javascripts/jobs/components/stuck_block.vue @@ -1,5 +1,5 @@ <script> -import { GlAlert, GlBadge, GlLink } from '@gitlab/ui'; +import { GlAlert, GlBadge, GlLink, GlSprintf } from '@gitlab/ui'; import { s__ } from '~/locale'; /** * Renders Stuck Runners block for job's view. @@ -9,6 +9,7 @@ export default { GlAlert, GlBadge, GlLink, + GlSprintf, }, props: { hasOfflineRunnersForProject: { @@ -29,11 +30,15 @@ export default { hasNoRunnersWithCorrespondingTags() { return this.tags.length > 0; }, + protectedBranchSettingsDocsLink() { + return 'https://docs.gitlab.com/runner/security/index.html#reduce-the-security-risk-of-using-privileged-containers'; + }, stuckData() { if (this.hasNoRunnersWithCorrespondingTags) { return { - text: s__(`Job|This job is stuck because you don't have - any active runners online or available with any of these tags assigned to them:`), + text: s__( + `Job|This job is stuck because of one of the following problems. There are no active runners online, no runners for the %{linkStart}protected branch%{linkEnd}, or no runners that match all of the job's tags:`, + ), dataTestId: 'job-stuck-with-tags', showTags: true, }; @@ -59,7 +64,17 @@ export default { <template> <gl-alert variant="warning" :dismissible="false"> <p class="gl-mb-0" :data-testid="stuckData.dataTestId"> - {{ stuckData.text }} + <gl-sprintf :message="stuckData.text"> + <template #link="{ content }"> + <a + class="gl-display-inline-block" + :href="protectedBranchSettingsDocsLink" + target="_blank" + > + {{ content }} + </a> + </template> + </gl-sprintf> <template v-if="stuckData.showTags"> <gl-badge v-for="tag in tags" :key="tag" variant="info"> {{ tag }} diff --git a/app/assets/javascripts/performance_bar/components/add_request.vue b/app/assets/javascripts/performance_bar/components/add_request.vue index 8396f00810d..9ac6b0e6403 100644 --- a/app/assets/javascripts/performance_bar/components/add_request.vue +++ b/app/assets/javascripts/performance_bar/components/add_request.vue @@ -1,7 +1,12 @@ -import { __ } from '~/locale'; - <script> +import { GlForm, GlFormInput, GlButton } from '@gitlab/ui'; + export default { + components: { + GlForm, + GlButton, + GlFormInput, + }, data() { return { inputEnabled: false, @@ -24,25 +29,26 @@ export default { }; </script> <template> - <div id="peek-view-add-request" class="view"> - <form class="form-inline" @submit.prevent> - <button - class="btn-link bold gl-text-blue-300 gl-button" - type="button" - :title="__(`Add request manually`)" + <div id="peek-view-add-request" class="view gl-display-flex"> + <gl-form class="gl-display-flex gl-align-items-center" @submit.prevent> + <gl-button + class="gl-text-blue-300! gl-mr-2" + category="tertiary" + variant="link" + icon="plus" + size="small" + :title="__('Add request manually')" @click="toggleInput" - > - + - </button> - <input + /> + <gl-form-input v-if="inputEnabled" v-model="urlOrRequestId" type="text" :placeholder="__(`URL or request ID`)" - class="form-control form-control-sm d-inline-block ml-1" + class="gl-ml-2" @keyup.enter="addRequest" @keyup.esc="clearForm" /> - </form> + </gl-form> </div> </template> diff --git a/app/assets/javascripts/runner/components/runner_jobs.vue b/app/assets/javascripts/runner/components/runner_jobs.vue index 4eb1312b204..57afdc4b9be 100644 --- a/app/assets/javascripts/runner/components/runner_jobs.vue +++ b/app/assets/javascripts/runner/components/runner_jobs.vue @@ -1,5 +1,5 @@ <script> -import { GlDeprecatedSkeletonLoading as GlSkeletonLoading } from '@gitlab/ui'; +import { GlSkeletonLoader } from '@gitlab/ui'; import { createAlert } from '~/flash'; import runnerJobsQuery from '../graphql/show/runner_jobs.query.graphql'; import { I18N_FETCH_ERROR, I18N_NO_JOBS_FOUND, RUNNER_DETAILS_JOBS_PAGE_SIZE } from '../constants'; @@ -11,7 +11,7 @@ import RunnerPagination from './runner_pagination.vue'; export default { name: 'RunnerJobs', components: { - GlSkeletonLoading, + GlSkeletonLoader, RunnerJobsTable, RunnerPagination, }, @@ -68,7 +68,9 @@ export default { <template> <div class="gl-pt-3"> - <gl-skeleton-loading v-if="loading" class="gl-py-5" /> + <div v-if="loading" class="gl-py-5"> + <gl-skeleton-loader /> + </div> <runner-jobs-table v-else-if="jobs.items.length" :jobs="jobs.items" /> <p v-else>{{ $options.I18N_NO_JOBS_FOUND }}</p> diff --git a/app/assets/javascripts/vue_shared/components/notes/system_note.vue b/app/assets/javascripts/vue_shared/components/notes/system_note.vue index 7c660121b1a..3593ea16968 100644 --- a/app/assets/javascripts/vue_shared/components/notes/system_note.vue +++ b/app/assets/javascripts/vue_shared/components/notes/system_note.vue @@ -18,7 +18,7 @@ */ import { GlButton, - GlDeprecatedSkeletonLoading as GlSkeletonLoading, + GlSkeletonLoader, GlTooltipDirective, GlIcon, GlSafeHtmlDirective as SafeHtml, @@ -46,7 +46,7 @@ export default { noteHeader, TimelineEntryItem, GlButton, - GlSkeletonLoading, + GlSkeletonLoader, }, directives: { GlTooltip: GlTooltipDirective, @@ -172,7 +172,7 @@ export default { </div> <div v-if="shouldShowDescriptionVersion" class="description-version pt-2"> <pre v-if="isLoadingDescriptionVersion" class="loading-state"> - <gl-skeleton-loading /> + <gl-skeleton-loader /> </pre> <pre v-else v-safe-html="descriptionVersion" class="wrapper mt-2"></pre> <gl-button @@ -218,7 +218,9 @@ export default { </tr> </table> </div> - <gl-skeleton-loading v-else-if="showLines" class="gl-mt-4" /> + <div v-else-if="showLines" class="mt-4"> + <gl-skeleton-loader /> + </div> </div> </div> </timeline-entry-item> diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view.vue index 134575b7a27..7c11b895323 100644 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view.vue +++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view.vue @@ -193,6 +193,7 @@ export default { :key="label.id" :label="label" :is-label-set="label.set" + :is-label-indeterminate="label.indeterminate" :highlight="index === currentHighlightItem" @clickLabel="handleLabelClick(label)" /> diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/label_item.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/label_item.vue index dd40add6376..1f52f4c064f 100644 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/label_item.vue +++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/label_item.vue @@ -1,5 +1,6 @@ <script> import { GlLink, GlIcon } from '@gitlab/ui'; +import { __ } from '~/locale'; export default { functional: true, @@ -12,6 +13,11 @@ export default { type: Boolean, required: true, }, + isLabelIndeterminate: { + type: Boolean, + required: false, + default: false, + }, highlight: { type: Boolean, required: false, @@ -19,7 +25,7 @@ export default { }, }, render(h, { props, listeners }) { - const { label, highlight, isLabelSet } = props; + const { label, highlight, isLabelSet, isLabelIndeterminate } = props; const labelColorBox = h('span', { class: 'dropdown-label-box gl-flex-shrink-0 gl-top-0 gl-mr-3', @@ -33,18 +39,36 @@ export default { const checkedIcon = h(GlIcon, { class: { - 'gl-mr-3 gl-flex-shrink-0': true, + 'gl-mr-3 gl-flex-shrink-0 has-tooltip': true, hidden: !isLabelSet, }, + attrs: { + title: __('Selected for all items.'), + 'data-testid': 'checked-icon', + }, props: { name: 'mobile-issue-close', }, }); + const indeterminateIcon = h(GlIcon, { + class: { + 'gl-mr-3 gl-flex-shrink-0 has-tooltip': true, + hidden: !isLabelIndeterminate, + }, + attrs: { + title: __('Selected for some items.'), + 'data-testid': 'indeterminate-icon', + }, + props: { + name: 'dash', + }, + }); + const noIcon = h('span', { class: { 'gl-mr-5 gl-pr-3': true, - hidden: isLabelSet, + hidden: isLabelSet || isLabelIndeterminate, }, attrs: { 'data-testid': 'no-icon', @@ -63,7 +87,7 @@ export default { }, }, }, - [noIcon, checkedIcon, labelColorBox, labelTitle], + [noIcon, checkedIcon, indeterminateIcon, labelColorBox, labelTitle], ); return h( diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue index 7e259cb8b96..b514e4a27af 100644 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue +++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue @@ -198,11 +198,12 @@ export default { !state.showDropdownButton && !state.showDropdownContents ) { - let filterFn = (label) => label.touched; - if (this.isDropdownVariantEmbedded) { - filterFn = (label) => label.set; - } - this.handleDropdownClose(state.labels.filter(filterFn)); + const filterTouchedLabelsFn = (label) => label.touched; + const filterSetLabelsFn = (label) => label.set; + const labels = this.isDropdownVariantEmbedded + ? state.labels.filter(filterSetLabelsFn) + : state.labels.filter(filterTouchedLabelsFn); + this.handleDropdownClose(labels, state.labels.filter(filterTouchedLabelsFn)); } }, /** @@ -265,11 +266,11 @@ export default { isInDropdownContents ); }, - handleDropdownClose(labels) { - // Only emit label updates if there are any labels to update - // on UI. + handleDropdownClose(labels, touchedLabels) { + // Only emit label updates if there are any + // labels to update on UI. if (labels.length) this.$emit('updateSelectedLabels', labels); - this.$emit('onDropdownClose'); + this.$emit('onDropdownClose', touchedLabels); }, handleCollapsedValueClick() { this.$emit('toggleCollapse'); diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/getters.js b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/getters.js index d14f96720b7..ef3eedd9bb2 100644 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/getters.js +++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/getters.js @@ -8,9 +8,10 @@ import { DropdownVariant } from '../constants'; * @param {object} state */ export const dropdownButtonText = (state, getters) => { - const selectedLabels = getters.isDropdownVariantSidebar - ? state.labels.filter((label) => label.set) - : state.selectedLabels; + const selectedLabels = + getters.isDropdownVariantSidebar || getters.isDropdownVariantEmbedded + ? state.labels.filter((label) => label.set || label.indeterminate) + : state.selectedLabels; if (!selectedLabels.length) { return state.dropdownButtonText || __('Label'); diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/mutations.js b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/mutations.js index 9e64f03fe84..43b23994cdf 100644 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/mutations.js +++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/mutations.js @@ -2,8 +2,39 @@ import { isScopedLabel, scopedLabelKey } from '~/lib/utils/common_utils'; import { DropdownVariant } from '../constants'; import * as types from './mutation_types'; +const transformLabels = (labels, selectedLabels) => + labels.map((label) => { + const selectedLabel = selectedLabels.find(({ id }) => id === label.id); + + return { + ...label, + set: Boolean(selectedLabel?.set), + indeterminate: Boolean(selectedLabel?.indeterminate), + }; + }); + export default { [types.SET_INITIAL_STATE](state, props) { + // We need to ensure that selectedLabels have + // `set` & `indeterminate` properties defined. + if (props.selectedLabels?.length) { + props.selectedLabels.forEach((label) => { + /* eslint-disable no-param-reassign */ + if (label.set === undefined && label.indeterminate === undefined) { + label.set = true; + label.indeterminate = false; + } else if (label.set === undefined && label.indeterminate !== undefined) { + label.set = false; + } else if (label.set !== undefined && label.indeterminate === undefined) { + label.indeterminate = false; + } else { + label.set = false; + label.indeterminate = false; + } + /* eslint-enable no-param-reassign */ + }); + } + Object.assign(state, { ...props }); }, @@ -36,10 +67,7 @@ export default { // selectedLabels array. state.labelsFetchInProgress = false; state.labelsFetched = true; - state.labels = labels.map((label) => ({ - ...label, - set: state.selectedLabels.some((selectedLabel) => selectedLabel.id === label.id), - })); + state.labels = transformLabels(labels, state.selectedLabels); }, [types.RECEIVE_SET_LABELS_FAILURE](state) { state.labelsFetchInProgress = false; @@ -62,7 +90,8 @@ export default { const candidateLabel = state.labels.find((label) => labelId === label.id); if (candidateLabel) { candidateLabel.touched = true; - candidateLabel.set = !candidateLabel.set; + candidateLabel.set = candidateLabel.indeterminate ? true : !candidateLabel.set; + candidateLabel.indeterminate = false; } if (isScopedLabel(candidateLabel)) { @@ -80,9 +109,6 @@ export default { }, [types.UPDATE_LABELS_SET_STATE](state) { - state.labels = state.labels.map((label) => ({ - ...label, - set: state.selectedLabels.some((selectedLabel) => selectedLabel.id === label.id), - })); + state.labels = transformLabels(state.labels, state.selectedLabels); }, }; diff --git a/app/models/concerns/storage/legacy_namespace.rb b/app/models/concerns/storage/legacy_namespace.rb index 948190dfadf..e418842a30b 100644 --- a/app/models/concerns/storage/legacy_namespace.rb +++ b/app/models/concerns/storage/legacy_namespace.rb @@ -23,22 +23,8 @@ module Storage former_parent_full_path = parent_was&.full_path parent_full_path = parent&.full_path Gitlab::UploadsTransfer.new.move_namespace(path, former_parent_full_path, parent_full_path) - - if any_project_with_pages_deployed? - run_after_commit do - Gitlab::PagesTransfer.new.async.move_namespace(path, former_parent_full_path, parent_full_path) - end - end else Gitlab::UploadsTransfer.new.rename_namespace(full_path_before_last_save, full_path) - - if any_project_with_pages_deployed? - full_path_was = full_path_before_last_save - - run_after_commit do - Gitlab::PagesTransfer.new.async.rename_namespace(full_path_was, full_path) - end - end end # If repositories moved successfully we need to diff --git a/app/services/projects/after_rename_service.rb b/app/services/projects/after_rename_service.rb index a3d54bc6b58..2ed4346e5ca 100644 --- a/app/services/projects/after_rename_service.rb +++ b/app/services/projects/after_rename_service.rb @@ -95,20 +95,6 @@ module Projects .new .rename_project(path_before, project_path, namespace_full_path) end - - if project.pages_deployed? - # Block will be evaluated in the context of project so we need - # to bind to a local variable to capture it, as the instance - # variable and method aren't available on Project - path_before_local = @path_before - - project.run_after_commit_or_now do - Gitlab::PagesTransfer - .new - .async - .rename_project(path_before_local, path, namespace.full_path) - end - end end def log_completion diff --git a/app/services/projects/transfer_service.rb b/app/services/projects/transfer_service.rb index 2ad5c303be2..666227951c6 100644 --- a/app/services/projects/transfer_service.rb +++ b/app/services/projects/transfer_service.rb @@ -120,7 +120,6 @@ module Projects # Overridden in EE def post_update_hooks(project) - move_pages(project) ensure_personal_project_owner_membership(project) end @@ -232,13 +231,6 @@ module Projects ) end - def move_pages(project) - return unless project.pages_deployed? - - transfer = Gitlab::PagesTransfer.new.async - transfer.move_project(project.path, @old_namespace.full_path, @new_namespace.full_path) - end - def old_wiki_repo_path "#{old_path}#{::Gitlab::GlRepository::WIKI.path_suffix}" end diff --git a/app/views/abuse_reports/new.html.haml b/app/views/abuse_reports/new.html.haml index 78fa16c13a5..3d85c203603 100644 --- a/app/views/abuse_reports/new.html.haml +++ b/app/views/abuse_reports/new.html.haml @@ -1,5 +1,5 @@ - page_title _("Report abuse to admin") -%h3.page-title +%h1.page-title = _("Report abuse to admin") %p = _("Please use this form to report to the admin users who create spam issues, comments or behave inappropriately.") diff --git a/app/views/admin/abuse_reports/index.html.haml b/app/views/admin/abuse_reports/index.html.haml index 8b1bbbc17c7..731683e55ef 100644 --- a/app/views/admin/abuse_reports/index.html.haml +++ b/app/views/admin/abuse_reports/index.html.haml @@ -1,6 +1,6 @@ - page_title _('Abuse Reports') -%h3.page-title= _('Abuse Reports') +%h1.page-title= _('Abuse Reports') .row-content-block.second-block = form_tag admin_abuse_reports_path, method: :get, class: 'filter-form' do diff --git a/app/views/admin/applications/edit.html.haml b/app/views/admin/applications/edit.html.haml index 42f7f6c3d66..189bbc5894e 100644 --- a/app/views/admin/applications/edit.html.haml +++ b/app/views/admin/applications/edit.html.haml @@ -2,7 +2,7 @@ - breadcrumb_title @application.name - page_title _("Edit"), @application.name, _("Applications") -%h3.page-title +%h1.page-title = _('Edit application') - @url = admin_application_path(@application) = render 'form', application: @application diff --git a/app/views/admin/applications/index.html.haml b/app/views/admin/applications/index.html.haml index 890155ee604..7c6d1d75e96 100644 --- a/app/views/admin/applications/index.html.haml +++ b/app/views/admin/applications/index.html.haml @@ -1,6 +1,6 @@ - page_title s_('AdminArea|Instance OAuth applications') -%h3.page-title +%h1.page-title = s_('AdminArea|Instance OAuth applications') %p.light - docs_link_path = help_page_path('integration/oauth_provider') diff --git a/app/views/admin/applications/new.html.haml b/app/views/admin/applications/new.html.haml index 731cb51e2e4..6dba2406330 100644 --- a/app/views/admin/applications/new.html.haml +++ b/app/views/admin/applications/new.html.haml @@ -1,7 +1,7 @@ - breadcrumb_title _("Add new application") - page_title _("Add new application") -%h3.page-title +%h1.page-title = _("Add new application") - @url = admin_applications_path = render 'form', application: @application diff --git a/app/views/admin/applications/show.html.haml b/app/views/admin/applications/show.html.haml index d9c683cbcc3..7884b9fd89e 100644 --- a/app/views/admin/applications/show.html.haml +++ b/app/views/admin/applications/show.html.haml @@ -1,6 +1,6 @@ - page_title @application.name, _("Applications") -%h3.page-title +%h1.page-title Application: #{@application.name} = render 'shared/doorkeeper/applications/show', diff --git a/app/views/admin/background_jobs/show.html.haml b/app/views/admin/background_jobs/show.html.haml index bab9fa02928..6ad4ab74ba2 100644 --- a/app/views/admin/background_jobs/show.html.haml +++ b/app/views/admin/background_jobs/show.html.haml @@ -1,6 +1,6 @@ - page_title _("Background Jobs") -%h3.page-title= _('Background Jobs') +%h1.page-title= _('Background Jobs') %p.light - sidekiq_link_url = 'http://sidekiq.org/' - sidekiq_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: sidekiq_link_url } diff --git a/app/views/admin/broadcast_messages/index.html.haml b/app/views/admin/broadcast_messages/index.html.haml index 8b657eda0c0..6a60d5418f4 100644 --- a/app/views/admin/broadcast_messages/index.html.haml +++ b/app/views/admin/broadcast_messages/index.html.haml @@ -2,7 +2,7 @@ - page_title _("Broadcast Messages") - targeted_broadcast_messages_enabled = Feature.enabled?(:role_targeted_broadcast_messages) -%h3.page-title +%h1.page-title = _('Broadcast Messages') %p.light = _('Use banners and notifications to notify your users about scheduled maintenance, recent upgrades, and more.') diff --git a/app/views/admin/deploy_keys/edit.html.haml b/app/views/admin/deploy_keys/edit.html.haml index f85b37b3640..87de176245e 100644 --- a/app/views/admin/deploy_keys/edit.html.haml +++ b/app/views/admin/deploy_keys/edit.html.haml @@ -1,5 +1,5 @@ - page_title _('Edit Deploy Key') -%h3.page-title= _('Edit public deploy key') +%h1.page-title= _('Edit public deploy key') %hr %div diff --git a/app/views/admin/deploy_keys/new.html.haml b/app/views/admin/deploy_keys/new.html.haml index fe2bc8530f7..e3053001dd7 100644 --- a/app/views/admin/deploy_keys/new.html.haml +++ b/app/views/admin/deploy_keys/new.html.haml @@ -1,5 +1,5 @@ - page_title _('New Deploy Key') -%h3.page-title= _('New public deploy key') +%h1.page-title= _('New public deploy key') %hr %div diff --git a/app/views/admin/gitaly_servers/index.html.haml b/app/views/admin/gitaly_servers/index.html.haml index 0b06f145687..b9134c7670e 100644 --- a/app/views/admin/gitaly_servers/index.html.haml +++ b/app/views/admin/gitaly_servers/index.html.haml @@ -1,7 +1,7 @@ - breadcrumb_title _("Gitaly Servers") - page_title _("Gitaly Servers") -%h3.page-title= _("Gitaly Servers") +%h1.page-title= _("Gitaly Servers") %hr .gitaly_servers - if @gitaly_servers.any? diff --git a/app/views/admin/groups/edit.html.haml b/app/views/admin/groups/edit.html.haml index 8e9e1a58a17..e18f757bfd9 100644 --- a/app/views/admin/groups/edit.html.haml +++ b/app/views/admin/groups/edit.html.haml @@ -1,4 +1,4 @@ - page_title _("Edit"), @group.name, _("Groups") -%h3.page-title= _('Edit group: %{group_name}') % { group_name: @group.name } +%h1.page-title= _('Edit group: %{group_name}') % { group_name: @group.name } %hr = render 'form', visibility_level: @group.visibility_level diff --git a/app/views/admin/groups/new.html.haml b/app/views/admin/groups/new.html.haml index 553e8638e52..781a7c15df4 100644 --- a/app/views/admin/groups/new.html.haml +++ b/app/views/admin/groups/new.html.haml @@ -1,4 +1,4 @@ - page_title _("New Group") -%h3.page-title= _('New group') +%h1.page-title= _('New group') %hr = render 'form', visibility_level: default_group_visibility diff --git a/app/views/admin/groups/show.html.haml b/app/views/admin/groups/show.html.haml index 39b2fa41c80..3419b234710 100644 --- a/app/views/admin/groups/show.html.haml +++ b/app/views/admin/groups/show.html.haml @@ -4,7 +4,7 @@ - page_title @group.name, _("Groups") - current_user_is_group_owner = @group && @group.has_owner?(current_user) -%h3.page-title +%h1.page-title = _('Group: %{group_name}') % { group_name: @group.full_name } = link_to admin_group_edit_path(@group), class: "btn btn-default gl-button float-right", data: { qa_selector: 'edit_group_link' } do diff --git a/app/views/admin/health_check/show.html.haml b/app/views/admin/health_check/show.html.haml index a289cea0d5a..9cafefc023a 100644 --- a/app/views/admin/health_check/show.html.haml +++ b/app/views/admin/health_check/show.html.haml @@ -1,7 +1,7 @@ - page_title _('Health Check') - no_errors = @errors.blank? -%h3.page-title= page_title +%h1.page-title= page_title .bs-callout.clearfix .float-left %p diff --git a/app/views/admin/hook_logs/show.html.haml b/app/views/admin/hook_logs/show.html.haml index ca2737ca56f..abfabbb5eb6 100644 --- a/app/views/admin/hook_logs/show.html.haml +++ b/app/views/admin/hook_logs/show.html.haml @@ -1,5 +1,5 @@ - page_title _('Request details') -%h3.page-title +%h1.page-title = _("Request details") %hr diff --git a/app/views/admin/identities/edit.html.haml b/app/views/admin/identities/edit.html.haml index 0fd1f2f547f..28466658aff 100644 --- a/app/views/admin/identities/edit.html.haml +++ b/app/views/admin/identities/edit.html.haml @@ -2,7 +2,7 @@ - add_to_breadcrumbs @user.name, admin_user_identities_path(@user) - breadcrumb_title _('Edit Identity') - page_title _("Edit"), @identity.provider, _("Identities"), @user.name, _("Users") -%h3.page-title +%h1.page-title = _('Edit identity for %{user_name}') % { user_name: @user.name } %hr diff --git a/app/views/admin/identities/new.html.haml b/app/views/admin/identities/new.html.haml index b4f37057c51..8ca27581693 100644 --- a/app/views/admin/identities/new.html.haml +++ b/app/views/admin/identities/new.html.haml @@ -2,6 +2,6 @@ - add_to_breadcrumbs @user.name, admin_user_identities_path(@user) - breadcrumb_title _('New Identity') - page_title _('New Identity') -%h3.page-title= _('New identity') +%h1.page-title= _('New identity') %hr = render 'form' diff --git a/app/views/admin/labels/edit.html.haml b/app/views/admin/labels/edit.html.haml index 44dd2b6a646..bd9db7530a6 100644 --- a/app/views/admin/labels/edit.html.haml +++ b/app/views/admin/labels/edit.html.haml @@ -1,7 +1,7 @@ - add_to_breadcrumbs _("Labels"), admin_labels_path - breadcrumb_title _("Edit Label") - page_title _("Edit"), @label.name, _("Labels") -%h3.page-title +%h1.page-title = _('Edit Label') %hr = render 'shared/labels/form', url: admin_label_path(@label), back_path: admin_labels_path diff --git a/app/views/admin/labels/index.html.haml b/app/views/admin/labels/index.html.haml index 66fd18e1b76..fb278f19185 100644 --- a/app/views/admin/labels/index.html.haml +++ b/app/views/admin/labels/index.html.haml @@ -3,7 +3,7 @@ %div = link_to new_admin_label_path, class: "float-right btn gl-button btn-confirm" do = _('New label') - %h3.page-title + %h1.page-title = _('Labels') %hr - if @labels.present? diff --git a/app/views/admin/labels/new.html.haml b/app/views/admin/labels/new.html.haml index 5166bdb4d20..0976416b592 100644 --- a/app/views/admin/labels/new.html.haml +++ b/app/views/admin/labels/new.html.haml @@ -1,5 +1,5 @@ - page_title _("New Label") -%h3.page-title +%h1.page-title = _('New Label') %hr = render 'shared/labels/form', url: admin_labels_path, back_path: admin_labels_path diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml index 16f6e71d79b..3fb3f1f5e54 100644 --- a/app/views/admin/projects/show.html.haml +++ b/app/views/admin/projects/show.html.haml @@ -5,7 +5,7 @@ - @content_class = "admin-projects" - current_user_is_group_owner = @group && @group.has_owner?(current_user) -%h3.page-title +%h1.page-title = _('Project: %{name}') % { name: @project.full_name } = link_to edit_project_path(@project), class: "btn btn-default gl-button float-right" do = sprite_icon('pencil-square', css_class: 'gl-icon') diff --git a/app/views/admin/spam_logs/index.html.haml b/app/views/admin/spam_logs/index.html.haml index 2a36c991ed2..d6f5f5a17da 100644 --- a/app/views/admin/spam_logs/index.html.haml +++ b/app/views/admin/spam_logs/index.html.haml @@ -1,5 +1,5 @@ - page_title _("Spam Logs") -%h3.page-title= _('Spam Logs') +%h1.page-title= _('Spam Logs') %hr - if @spam_logs.present? .table-holder diff --git a/app/views/admin/topics/edit.html.haml b/app/views/admin/topics/edit.html.haml index 4416bb0fe18..a242a3d6eea 100644 --- a/app/views/admin/topics/edit.html.haml +++ b/app/views/admin/topics/edit.html.haml @@ -1,4 +1,4 @@ - page_title _("Edit"), @topic.name, _("Topics") -%h3.page-title= _('Edit topic: %{topic_name}') % { topic_name: @topic.name } +%h1.page-title= _('Edit topic: %{topic_name}') % { topic_name: @topic.name } %hr = render 'form', url: admin_topic_path(@topic) diff --git a/app/views/admin/topics/new.html.haml b/app/views/admin/topics/new.html.haml index 8b4a8ac269e..428e46efed7 100644 --- a/app/views/admin/topics/new.html.haml +++ b/app/views/admin/topics/new.html.haml @@ -1,4 +1,4 @@ - page_title _("New topic") -%h3.page-title= _('New topic') +%h1.page-title= _('New topic') %hr = render 'form', url: admin_topics_path(@topic) diff --git a/app/views/admin/users/_head.html.haml b/app/views/admin/users/_head.html.haml index 9007a36f19f..17a97bbdc66 100644 --- a/app/views/admin/users/_head.html.haml +++ b/app/views/admin/users/_head.html.haml @@ -1,6 +1,6 @@ .gl-display-flex.gl-flex-wrap.gl-justify-content-space-between.gl-align-items-center.gl-py-3.gl-mb-5.gl-border-b-solid.gl-border-gray-100.gl-border-b-1 .gl-my-3 - %h3.page-title.gl-m-0 + %h1.page-title.gl-m-0 = @user.name - if @user.blocked_pending_approval? %span.gl-text-red-500 diff --git a/app/views/admin/users/edit.html.haml b/app/views/admin/users/edit.html.haml index e3ebb691ba9..d6c65741422 100644 --- a/app/views/admin/users/edit.html.haml +++ b/app/views/admin/users/edit.html.haml @@ -1,5 +1,5 @@ - page_title _("Edit"), @user.name, _("Users") -%h3.page-title +%h1.page-title = _("Edit user: %{user_name}") % { user_name: @user.name } %hr = render 'form' diff --git a/app/views/admin/users/new.html.haml b/app/views/admin/users/new.html.haml index 08aa7c3c9d2..ad11f797b2c 100644 --- a/app/views/admin/users/new.html.haml +++ b/app/views/admin/users/new.html.haml @@ -1,5 +1,5 @@ - page_title _("New User") -%h3.page-title +%h1.page-title = s_('AdminUsers|New user') %hr = render 'form' diff --git a/app/views/doorkeeper/applications/edit.html.haml b/app/views/doorkeeper/applications/edit.html.haml index 99e6a5eca19..2d00760a7d1 100644 --- a/app/views/doorkeeper/applications/edit.html.haml +++ b/app/views/doorkeeper/applications/edit.html.haml @@ -1,5 +1,5 @@ - page_title _("Edit"), @application.name, _("Applications") - @content_class = "limit-container-width" unless fluid_layout -%h3.page-title= _('Edit application') +%h1.page-title= _('Edit application') = render 'shared/doorkeeper/applications/form', url: doorkeeper_submit_path(@application) diff --git a/app/views/doorkeeper/applications/new.html.haml b/app/views/doorkeeper/applications/new.html.haml index a66fab20d7c..15f3700e077 100644 --- a/app/views/doorkeeper/applications/new.html.haml +++ b/app/views/doorkeeper/applications/new.html.haml @@ -1,6 +1,6 @@ - page_title _("New Application") -%h3.page-title= _("New Application") +%h1.page-title= _("New Application") %hr diff --git a/app/views/doorkeeper/applications/show.html.haml b/app/views/doorkeeper/applications/show.html.haml index 3a568421ce9..1fb843e95b0 100644 --- a/app/views/doorkeeper/applications/show.html.haml +++ b/app/views/doorkeeper/applications/show.html.haml @@ -3,7 +3,7 @@ - page_title @application.name, _("Applications") - @content_class = "limit-container-width" unless fluid_layout -%h3.page-title +%h1.page-title = _("Application: %{name}") % { name: @application.name } = render 'shared/doorkeeper/applications/show', diff --git a/app/views/doorkeeper/authorizations/error.html.haml b/app/views/doorkeeper/authorizations/error.html.haml index 32b4ccb0fe6..aa4f8b892ad 100644 --- a/app/views/doorkeeper/authorizations/error.html.haml +++ b/app/views/doorkeeper/authorizations/error.html.haml @@ -1,3 +1,3 @@ -%h3.page-title= _("An error has occurred") +%h1.page-title= _("An error has occurred") %main{ :role => "main" } %pre= @pre_auth.error_response.body[:error_description] diff --git a/app/views/doorkeeper/authorizations/redirect.html.haml b/app/views/doorkeeper/authorizations/redirect.html.haml index a9ac92fd087..f32fb3181c6 100644 --- a/app/views/doorkeeper/authorizations/redirect.html.haml +++ b/app/views/doorkeeper/authorizations/redirect.html.haml @@ -1,4 +1,4 @@ -%h3.page-title= _("Redirecting") +%h1.page-title= _("Redirecting") %div %a{ :href => redirect_uri } Click here to redirect to #{redirect_uri} diff --git a/app/views/doorkeeper/authorizations/show.html.haml b/app/views/doorkeeper/authorizations/show.html.haml index e4bfd69e7f8..f4f937fe14d 100644 --- a/app/views/doorkeeper/authorizations/show.html.haml +++ b/app/views/doorkeeper/authorizations/show.html.haml @@ -1,3 +1,3 @@ -%h3.page-title= _("Authorization code:") +%h1.page-title= _("Authorization code:") %main{ :role => "main" } %code#authorization_code= params[:code] diff --git a/app/views/groups/labels/edit.html.haml b/app/views/groups/labels/edit.html.haml index d9b8f99ea0c..bd0efa9d778 100644 --- a/app/views/groups/labels/edit.html.haml +++ b/app/views/groups/labels/edit.html.haml @@ -2,7 +2,7 @@ - breadcrumb_title _("Edit") - page_title _("Edit"), @label.name, _("Labels") -%h3.page-title +%h1.page-title = _('Edit Label') %hr diff --git a/app/views/groups/labels/new.html.haml b/app/views/groups/labels/new.html.haml index 75b4ad5c795..07e45cc05fb 100644 --- a/app/views/groups/labels/new.html.haml +++ b/app/views/groups/labels/new.html.haml @@ -2,7 +2,7 @@ - breadcrumb_title _("New") - page_title _("New Label") -%h3.page-title +%h1.page-title = _('New Label') %hr diff --git a/app/views/groups/milestones/edit.html.haml b/app/views/groups/milestones/edit.html.haml index 187c2d24b56..1e7d38b3dac 100644 --- a/app/views/groups/milestones/edit.html.haml +++ b/app/views/groups/milestones/edit.html.haml @@ -3,7 +3,7 @@ - render "header_title" -%h3.page-title +%h1.page-title = _('Edit Milestone') %hr diff --git a/app/views/groups/milestones/new.html.haml b/app/views/groups/milestones/new.html.haml index 0d4565706d4..9df6d656f82 100644 --- a/app/views/groups/milestones/new.html.haml +++ b/app/views/groups/milestones/new.html.haml @@ -2,7 +2,7 @@ - breadcrumb_title _("New") - page_title _("Milestones"), @milestone.name, _("Milestones") -%h3.page-title +%h1.page-title = _("New Milestone") %hr diff --git a/app/views/groups/settings/applications/edit.html.haml b/app/views/groups/settings/applications/edit.html.haml index cba4892eef9..7967586d312 100644 --- a/app/views/groups/settings/applications/edit.html.haml +++ b/app/views/groups/settings/applications/edit.html.haml @@ -1,5 +1,5 @@ - page_title _("Edit"), @application.name, _("Group applications") - @content_class = "limit-container-width" unless fluid_layout -%h3.page-title= _('Edit group application') +%h1.page-title= _('Edit group application') = render 'shared/doorkeeper/applications/form', url: group_settings_application_path(@group, @application) diff --git a/app/views/groups/settings/applications/show.html.haml b/app/views/groups/settings/applications/show.html.haml index 6e7f6ce4df0..a2f079a7215 100644 --- a/app/views/groups/settings/applications/show.html.haml +++ b/app/views/groups/settings/applications/show.html.haml @@ -3,7 +3,7 @@ - page_title @application.name, _("Group applications") - @content_class = "limit-container-width" unless fluid_layout -%h3.page-title +%h1.page-title = _("Group application: %{name}") % { name: @application.name } = render 'shared/doorkeeper/applications/show', diff --git a/app/views/import/bitbucket/status.html.haml b/app/views/import/bitbucket/status.html.haml index 8946ab898e0..52cb60db749 100644 --- a/app/views/import/bitbucket/status.html.haml +++ b/app/views/import/bitbucket/status.html.haml @@ -1,7 +1,7 @@ - page_title _('Bitbucket import') - header_title _('Projects'), root_path -%h3.page-title.d-flex +%h1.page-title.d-flex .gl-display-flex.gl-align-items-center.gl-justify-content-center = sprite_icon('bitbucket', css_class: 'gl-mr-2') = _('Import projects from Bitbucket') diff --git a/app/views/import/bitbucket_server/new.html.haml b/app/views/import/bitbucket_server/new.html.haml index 721447186a6..718832b0a1b 100644 --- a/app/views/import/bitbucket_server/new.html.haml +++ b/app/views/import/bitbucket_server/new.html.haml @@ -2,7 +2,7 @@ - header_title _("New project"), new_project_path - add_to_breadcrumbs s_('ProjectsNew|Import project'), new_project_path(anchor: 'import_project') -%h3.page-title.d-flex +%h1.page-title.d-flex .gl-display-flex.gl-align-items-center.gl-justify-content-center = sprite_icon('bitbucket', css_class: 'gl-mr-2') = _('Import repositories from Bitbucket Server') diff --git a/app/views/import/bitbucket_server/status.html.haml b/app/views/import/bitbucket_server/status.html.haml index 79b2810e06d..4cdef031036 100644 --- a/app/views/import/bitbucket_server/status.html.haml +++ b/app/views/import/bitbucket_server/status.html.haml @@ -1,6 +1,6 @@ - page_title _('Bitbucket Server import') -%h3.page-title.d-flex +%h1.page-title.d-flex .gl-display-flex.gl-align-items-center.gl-justify-content-center = sprite_icon('bitbucket', css_class: 'gl-mr-2') = _('Import projects from Bitbucket Server') diff --git a/app/views/import/fogbugz/new.html.haml b/app/views/import/fogbugz/new.html.haml index d716d08529c..ef4bff678a6 100644 --- a/app/views/import/fogbugz/new.html.haml +++ b/app/views/import/fogbugz/new.html.haml @@ -2,7 +2,7 @@ - header_title _("New project"), new_project_path - add_to_breadcrumbs s_('ProjectsNew|Import project'), new_project_path(anchor: 'import_project') -%h3.page-title.d-flex +%h1.page-title.d-flex .gl-display-flex.gl-align-items-center.gl-justify-content-center = sprite_icon('bug', css_class: 'gl-mr-2') = _('Import projects from FogBugz') diff --git a/app/views/import/fogbugz/new_user_map.html.haml b/app/views/import/fogbugz/new_user_map.html.haml index 93572e14a65..330cd8d3f36 100644 --- a/app/views/import/fogbugz/new_user_map.html.haml +++ b/app/views/import/fogbugz/new_user_map.html.haml @@ -2,7 +2,7 @@ - header_title _("New project"), new_project_path - add_to_breadcrumbs s_('ProjectsNew|Import project'), new_project_path(anchor: 'import_project') -%h3.page-title.d-flex +%h1.page-title.d-flex .gl-display-flex.gl-align-items-center.gl-justify-content-center = sprite_icon('bug', css_class: 'gl-mr-2') = _('Import projects from FogBugz') diff --git a/app/views/import/fogbugz/status.html.haml b/app/views/import/fogbugz/status.html.haml index dcc0e94441c..e858b9f1c4b 100644 --- a/app/views/import/fogbugz/status.html.haml +++ b/app/views/import/fogbugz/status.html.haml @@ -1,5 +1,5 @@ - page_title _("FogBugz import") -%h3.page-title.d-flex +%h1.page-title.d-flex .gl-display-flex.gl-align-items-center.gl-justify-content-center = sprite_icon('bug', css_class: 'gl-mr-2') = _('Import projects from FogBugz') diff --git a/app/views/import/gitea/new.html.haml b/app/views/import/gitea/new.html.haml index de717ce87eb..6dfa78cbcd2 100644 --- a/app/views/import/gitea/new.html.haml +++ b/app/views/import/gitea/new.html.haml @@ -2,7 +2,7 @@ - header_title _("New project"), new_project_path - add_to_breadcrumbs s_('ProjectsNew|Import project'), new_project_path(anchor: 'import_project') -%h3.page-title +%h1.page-title = custom_icon('gitea_logo') = _('Import Projects from Gitea') diff --git a/app/views/import/gitea/status.html.haml b/app/views/import/gitea/status.html.haml index 1bdcec0c574..c34c3ffaa3b 100644 --- a/app/views/import/gitea/status.html.haml +++ b/app/views/import/gitea/status.html.haml @@ -1,5 +1,5 @@ - page_title _("Gitea Import") -%h3.page-title +%h1.page-title = custom_icon('gitea_logo') = _('Import Projects from Gitea') diff --git a/app/views/import/github/new.html.haml b/app/views/import/github/new.html.haml index fbb27ba620a..cfd65e116c5 100644 --- a/app/views/import/github/new.html.haml +++ b/app/views/import/github/new.html.haml @@ -3,7 +3,7 @@ - header_title _("New project"), new_project_path - add_to_breadcrumbs s_('ProjectsNew|Import project'), new_project_path(anchor: 'import_project') -%h3.page-title +%h1.page-title = title %p diff --git a/app/views/import/github/status.html.haml b/app/views/import/github/status.html.haml index 26b048c8195..b79c5c3092f 100644 --- a/app/views/import/github/status.html.haml +++ b/app/views/import/github/status.html.haml @@ -1,6 +1,6 @@ - title = has_ci_cd_only_params? ? _('Connect repositories from GitHub') : _('GitHub import') - page_title title -%h3.page-title.mb-0.gl-display-flex +%h1.page-title.mb-0.gl-display-flex .gl-display-flex.gl-align-items-center.gl-justify-content-center = sprite_icon('github', css_class: 'gl-mr-2') = _('Import repositories from GitHub') diff --git a/app/views/import/gitlab/status.html.haml b/app/views/import/gitlab/status.html.haml index b7b1fae1b73..6ec8eb0eff8 100644 --- a/app/views/import/gitlab/status.html.haml +++ b/app/views/import/gitlab/status.html.haml @@ -1,5 +1,5 @@ - page_title _("GitLab.com import") -%h3.page-title +%h1.page-title = sprite_icon('heart', css_class: 'gl-vertical-align-middle') = _('Import projects from GitLab.com') diff --git a/app/views/import/gitlab_projects/new.html.haml b/app/views/import/gitlab_projects/new.html.haml index 533d0d13be3..4c4ca33926d 100644 --- a/app/views/import/gitlab_projects/new.html.haml +++ b/app/views/import/gitlab_projects/new.html.haml @@ -2,7 +2,7 @@ - header_title _("New project"), new_project_path - add_to_breadcrumbs s_('ProjectsNew|Import project'), new_project_path(anchor: 'import_project') -%h3.page-title.d-flex +%h1.page-title.d-flex .gl-display-flex.gl-align-items-center.gl-justify-content-center = sprite_icon('tanuki', css_class: 'gl-mr-2') = _('Import an exported GitLab project') diff --git a/app/views/import/manifest/new.html.haml b/app/views/import/manifest/new.html.haml index a949e14e273..5415dbcbea6 100644 --- a/app/views/import/manifest/new.html.haml +++ b/app/views/import/manifest/new.html.haml @@ -3,7 +3,7 @@ - add_to_breadcrumbs s_('ProjectsNew|Import project'), new_project_path(anchor: 'import_project') -%h3.page-title +%h1.page-title = _('Manifest file import') = render 'import/shared/errors' diff --git a/app/views/import/manifest/status.html.haml b/app/views/import/manifest/status.html.haml index 45d03575713..9856e0976f9 100644 --- a/app/views/import/manifest/status.html.haml +++ b/app/views/import/manifest/status.html.haml @@ -1,6 +1,6 @@ - page_title _("Manifest import") -%h3.page-title +%h1.page-title = _('Manifest file import') = render 'import/githubish_status', provider: 'manifest' diff --git a/app/views/import/phabricator/new.html.haml b/app/views/import/phabricator/new.html.haml index 0249dc446e4..d9cdc3903ed 100644 --- a/app/views/import/phabricator/new.html.haml +++ b/app/views/import/phabricator/new.html.haml @@ -2,7 +2,7 @@ - header_title _("New project"), new_project_path - add_to_breadcrumbs s_('ProjectsNew|Import project'), new_project_path(anchor: 'import_project') -%h3.page-title.d-flex +%h1.page-title.d-flex .gl-display-flex.gl-align-items-center.gl-justify-content-center = sprite_icon('issues', css_class: 'gl-mr-2') = _('Import tasks from Phabricator into issues') diff --git a/app/views/invites/show.html.haml b/app/views/invites/show.html.haml index 3622fc46983..2789bea70b1 100644 --- a/app/views/invites/show.html.haml +++ b/app/views/invites/show.html.haml @@ -1,5 +1,5 @@ - page_title _("Invitation") -%h3.page-title= _("Invitation") +%h1.page-title= _("Invitation") - if current_user_matches_invite? - if member? diff --git a/app/views/profiles/chat_names/new.html.haml b/app/views/profiles/chat_names/new.html.haml index f008abf376d..0f03516b697 100644 --- a/app/views/profiles/chat_names/new.html.haml +++ b/app/views/profiles/chat_names/new.html.haml @@ -1,4 +1,4 @@ -%h3.page-title +%h1.page-title = _("Authorization required") %main{ :role => "main" } %p.h4 diff --git a/app/views/profiles/passwords/new.html.haml b/app/views/profiles/passwords/new.html.haml index 9154c94abb6..8d743e72bc3 100644 --- a/app/views/profiles/passwords/new.html.haml +++ b/app/views/profiles/passwords/new.html.haml @@ -1,7 +1,7 @@ - page_title _('New Password') - breadcrumb_title _('New Password') -%h3.page-title= _('Set up new password') +%h1.page-title= _('Set up new password') %hr = form_for @user, url: profile_password_path, method: :post do |f| %p.slead diff --git a/app/views/projects/_visibility_modal.html.haml b/app/views/projects/_visibility_modal.html.haml index db98b978f04..25fd0b78ac8 100644 --- a/app/views/projects/_visibility_modal.html.haml +++ b/app/views/projects/_visibility_modal.html.haml @@ -5,7 +5,7 @@ .modal-dialog .modal-content .modal-header - %h3.page-title= _('Reduce this project’s visibility?') + %h1.page-title= _('Reduce this project’s visibility?') %button.close{ type: "button", "data-dismiss": "modal", "aria-label" => _('Close') } %span{ "aria-hidden": "true" }= sprite_icon("close") .modal-body diff --git a/app/views/projects/blob/_new_dir.html.haml b/app/views/projects/blob/_new_dir.html.haml index 3cc9fea56e2..3d87e2daeeb 100644 --- a/app/views/projects/blob/_new_dir.html.haml +++ b/app/views/projects/blob/_new_dir.html.haml @@ -2,7 +2,7 @@ .modal-dialog.modal-lg .modal-content .modal-header - %h3.page-title= _('Create New Directory') + %h1.page-title= _('Create New Directory') %button.close{ type: "button", "data-dismiss": "modal", "aria-label" => _('Close') } %span{ "aria-hidden": "true" } × .modal-body diff --git a/app/views/projects/blob/_remove.html.haml b/app/views/projects/blob/_remove.html.haml index 1463fcf8052..24603ee54c8 100644 --- a/app/views/projects/blob/_remove.html.haml +++ b/app/views/projects/blob/_remove.html.haml @@ -2,7 +2,7 @@ .modal-dialog .modal-content .modal-header - %h3.page-title Delete #{@blob.name} + %h1.page-title Delete #{@blob.name} %button.close{ type: "button", "data-dismiss": "modal", "aria-label" => _('Close') } %span{ "aria-hidden": "true" } × diff --git a/app/views/projects/blob/_upload.html.haml b/app/views/projects/blob/_upload.html.haml index 629fa9c0e8a..59e9c2d73a8 100644 --- a/app/views/projects/blob/_upload.html.haml +++ b/app/views/projects/blob/_upload.html.haml @@ -2,7 +2,7 @@ .modal-dialog.modal-lg .modal-content .modal-header - %h3.page-title= title + %h1.page-title= title %button.close{ type: "button", "data-dismiss": "modal", "aria-label" => _('Close') } %span{ "aria-hidden": "true" } × .modal-body diff --git a/app/views/projects/blob/edit.html.haml b/app/views/projects/blob/edit.html.haml index f80601ef221..2771fba79c5 100644 --- a/app/views/projects/blob/edit.html.haml +++ b/app/views/projects/blob/edit.html.haml @@ -15,7 +15,7 @@ = _('Someone edited the file the same time you did. Please check out %{link_start}the file %{icon}%{link_end} and make sure your changes will not unintentionally remove theirs.').html_safe % { link_start: blob_link_start, link_end: '</a>'.html_safe , icon: external_link_icon } -%h3.page-title.blob-edit-page-title +%h1.page-title.blob-edit-page-title Edit file .file-editor = gl_tabs_nav({ class: 'js-edit-mode nav-links gl-border-0'}) do diff --git a/app/views/projects/blob/new.html.haml b/app/views/projects/blob/new.html.haml index 60877db581f..b2922e22713 100644 --- a/app/views/projects/blob/new.html.haml +++ b/app/views/projects/blob/new.html.haml @@ -1,7 +1,7 @@ - breadcrumb_title _("Repository") - page_title _("New File"), @path.presence, @ref -%h3.page-title.blob-new-page-title +%h1.page-title.blob-new-page-title = _('New file') .file-editor = form_tag(project_create_blob_path(@project, @id), method: :post, class: 'js-edit-blob-form js-new-blob-form js-quick-submit js-requires-input', data: blob_editor_paths(@project)) do diff --git a/app/views/projects/branches/new.html.haml b/app/views/projects/branches/new.html.haml index c06f60bd05d..1e12e17a874 100644 --- a/app/views/projects/branches/new.html.haml +++ b/app/views/projects/branches/new.html.haml @@ -5,7 +5,7 @@ = render Pajamas::AlertComponent.new(variant: :danger) do |c| = c.body do = @error -%h3.page-title +%h1.page-title = _('New Branch') %hr diff --git a/app/views/projects/compare/index.html.haml b/app/views/projects/compare/index.html.haml index 12d3f28dc20..33ff8a5add7 100644 --- a/app/views/projects/compare/index.html.haml +++ b/app/views/projects/compare/index.html.haml @@ -1,7 +1,7 @@ - breadcrumb_title _("Compare Revisions") - page_title _("Compare") -%h3.page-title +%h1.page-title = _("Compare Git revisions") .sub-header-block - example_branch = capture do diff --git a/app/views/projects/deploy_keys/edit.html.haml b/app/views/projects/deploy_keys/edit.html.haml index 263b0025fe8..98dc15c17f6 100644 --- a/app/views/projects/deploy_keys/edit.html.haml +++ b/app/views/projects/deploy_keys/edit.html.haml @@ -1,5 +1,5 @@ - page_title _('Edit Deploy Key') -%h3.page-title= _('Edit Deploy Key') +%h1.page-title= _('Edit Deploy Key') %hr %div diff --git a/app/views/projects/environments/terminal.html.haml b/app/views/projects/environments/terminal.html.haml index ee31985eaf0..571578dd921 100644 --- a/app/views/projects/environments/terminal.html.haml +++ b/app/views/projects/environments/terminal.html.haml @@ -6,7 +6,7 @@ .top-area .row .col-sm-6 - %h3.page-title + %h1.page-title = _("Terminal for environment") = @environment.name diff --git a/app/views/projects/hook_logs/show.html.haml b/app/views/projects/hook_logs/show.html.haml index f6861e4119e..1f71e1b7055 100644 --- a/app/views/projects/hook_logs/show.html.haml +++ b/app/views/projects/hook_logs/show.html.haml @@ -2,7 +2,7 @@ - add_to_breadcrumbs _('Webhook Settings'), namespace_project_hooks_path - page_title _('Webhook Logs') -%h3.page-title +%h1.page-title = _("Request details") %hr diff --git a/app/views/projects/imports/new.html.haml b/app/views/projects/imports/new.html.haml index dc2bcfa33bb..4abe58faf80 100644 --- a/app/views/projects/imports/new.html.haml +++ b/app/views/projects/imports/new.html.haml @@ -1,5 +1,5 @@ - page_title _("Import repository") -%h3.page-title +%h1.page-title = _('Import repository') %hr diff --git a/app/views/projects/issues/edit.html.haml b/app/views/projects/issues/edit.html.haml index 353ff9c1cc2..00199b42c43 100644 --- a/app/views/projects/issues/edit.html.haml +++ b/app/views/projects/issues/edit.html.haml @@ -1,6 +1,6 @@ - page_title _("Edit"), "#{@issue.title} (#{@issue.to_reference})", _("Issues") -%h3.page-title +%h1.page-title Edit Issue ##{@issue.iid} %hr diff --git a/app/views/projects/labels/edit.html.haml b/app/views/projects/labels/edit.html.haml index 8023fb93c64..eb7e5b43d38 100644 --- a/app/views/projects/labels/edit.html.haml +++ b/app/views/projects/labels/edit.html.haml @@ -2,7 +2,7 @@ - breadcrumb_title _("Edit") - page_title _("Edit"), @label.name, _("Labels") -%h3.page-title +%h1.page-title = _('Edit Label') %hr = render 'shared/labels/form', url: project_label_path(@project, @label), back_path: project_labels_path(@project) diff --git a/app/views/projects/labels/new.html.haml b/app/views/projects/labels/new.html.haml index 1ae87bf93d1..a5c5c1f404c 100644 --- a/app/views/projects/labels/new.html.haml +++ b/app/views/projects/labels/new.html.haml @@ -2,7 +2,7 @@ - breadcrumb_title _("New") - page_title _("New Label") -%h3.page-title +%h1.page-title = _('New Label') %hr = render 'shared/labels/form', url: project_labels_path(@project), back_path: project_labels_path(@project) diff --git a/app/views/projects/merge_requests/creations/_new_submit.html.haml b/app/views/projects/merge_requests/creations/_new_submit.html.haml index 085841a61c7..6e400ae560a 100644 --- a/app/views/projects/merge_requests/creations/_new_submit.html.haml +++ b/app/views/projects/merge_requests/creations/_new_submit.html.haml @@ -1,4 +1,4 @@ -%h1.h2.gl-mb-5 +%h1.page-title = _('New merge request') = gitlab_ui_form_for [@project, @merge_request], html: { class: 'merge-request-form common-note-form js-requires-input js-quick-submit' } do |f| = render 'shared/issuable/form', f: f, issuable: @merge_request, commits: @commits, presenter: @mr_presenter diff --git a/app/views/projects/merge_requests/edit.html.haml b/app/views/projects/merge_requests/edit.html.haml index 5fcb5d3f876..497eddff9ac 100644 --- a/app/views/projects/merge_requests/edit.html.haml +++ b/app/views/projects/merge_requests/edit.html.haml @@ -2,6 +2,6 @@ - breadcrumb_title @merge_request.to_reference - page_title _("Edit"), "#{@merge_request.title} (#{@merge_request.to_reference}", _("Merge requests") -%h3.page-title +%h1.page-title Edit merge request #{@merge_request.to_reference} = render 'form' diff --git a/app/views/projects/milestones/edit.html.haml b/app/views/projects/milestones/edit.html.haml index 0d040a5cdb3..5017f116232 100644 --- a/app/views/projects/milestones/edit.html.haml +++ b/app/views/projects/milestones/edit.html.haml @@ -2,7 +2,7 @@ - add_to_breadcrumbs _('Milestones'), project_milestones_path(@project) - page_title _('Edit'), @milestone.title, _('Milestones') -%h3.page-title +%h1.page-title = _('Edit Milestone') %hr diff --git a/app/views/projects/milestones/new.html.haml b/app/views/projects/milestones/new.html.haml index 721506a2201..d982e09e2c2 100644 --- a/app/views/projects/milestones/new.html.haml +++ b/app/views/projects/milestones/new.html.haml @@ -2,7 +2,7 @@ - breadcrumb_title _('New') - page_title _('New Milestone') -%h3.page-title +%h1.page-title = _('New Milestone') %hr diff --git a/app/views/projects/pages/show.html.haml b/app/views/projects/pages/show.html.haml index 64760d8972f..e302c6cb68a 100644 --- a/app/views/projects/pages/show.html.haml +++ b/app/views/projects/pages/show.html.haml @@ -1,7 +1,7 @@ - page_title _('Pages') - if @project.pages_enabled? - %h3.page-title.with-button + %h1.page-title.with-button = s_('GitLabPages|Pages') - if can?(current_user, :update_pages, @project) && (Gitlab.config.pages.external_http || Gitlab.config.pages.external_https) diff --git a/app/views/projects/pages_domains/new.html.haml b/app/views/projects/pages_domains/new.html.haml index 0b794226c7f..5ac9ae732e3 100644 --- a/app/views/projects/pages_domains/new.html.haml +++ b/app/views/projects/pages_domains/new.html.haml @@ -1,6 +1,6 @@ - add_to_breadcrumbs _("Pages"), project_pages_path(@project) - page_title _('New Pages Domain') -%h3.page-title +%h1.page-title = _("New Pages Domain") = render 'projects/pages_domains/helper_text' %div diff --git a/app/views/projects/pages_domains/show.html.haml b/app/views/projects/pages_domains/show.html.haml index 215af766e5a..50328a94eab 100644 --- a/app/views/projects/pages_domains/show.html.haml +++ b/app/views/projects/pages_domains/show.html.haml @@ -11,7 +11,7 @@ .container-fluid.container-limited = _("This domain is not verified. You will need to verify ownership before access is enabled.") -%h3.page-title +%h1.page-title = _('Pages Domain') = render 'projects/pages_domains/helper_text' %div diff --git a/app/views/projects/pipeline_schedules/edit.html.haml b/app/views/projects/pipeline_schedules/edit.html.haml index 51f0c58330d..eb2c87b8db5 100644 --- a/app/views/projects/pipeline_schedules/edit.html.haml +++ b/app/views/projects/pipeline_schedules/edit.html.haml @@ -3,7 +3,7 @@ - page_title _("Edit"), @schedule.description, _("Pipeline Schedule") - add_page_specific_style 'page_bundles/pipeline_schedules' -%h3.page-title +%h1.page-title = _("Edit Pipeline Schedule") %hr diff --git a/app/views/projects/pipeline_schedules/new.html.haml b/app/views/projects/pipeline_schedules/new.html.haml index 1b50090e445..6e2da1b9430 100644 --- a/app/views/projects/pipeline_schedules/new.html.haml +++ b/app/views/projects/pipeline_schedules/new.html.haml @@ -5,7 +5,7 @@ - add_to_breadcrumbs("Pipelines", project_pipelines_path(@project)) -%h3.page-title +%h1.page-title = _("Schedule a new pipeline") %hr diff --git a/app/views/projects/pipelines/new.html.haml b/app/views/projects/pipelines/new.html.haml index e92f14fcc63..06949c7e113 100644 --- a/app/views/projects/pipelines/new.html.haml +++ b/app/views/projects/pipelines/new.html.haml @@ -1,7 +1,7 @@ - breadcrumb_title _('Pipelines') - page_title s_('Pipeline|Run pipeline') -%h3.page-title +%h1.page-title = s_('Pipeline|Run pipeline') %hr diff --git a/app/views/projects/snippets/edit.html.haml b/app/views/projects/snippets/edit.html.haml index 9f5af1cfe1e..8a25a58a8fe 100644 --- a/app/views/projects/snippets/edit.html.haml +++ b/app/views/projects/snippets/edit.html.haml @@ -3,7 +3,7 @@ - page_title _("Edit"), "#{@snippet.title} (#{@snippet.to_reference})", _("Snippets") - @content_class = "limit-container-width" unless fluid_layout -%h3.page-title +%h1.page-title = _("Edit Snippet") %hr = render "shared/snippets/form", url: project_snippet_path(@project, @snippet) diff --git a/app/views/projects/snippets/new.html.haml b/app/views/projects/snippets/new.html.haml index d55a1160d48..df64f817064 100644 --- a/app/views/projects/snippets/new.html.haml +++ b/app/views/projects/snippets/new.html.haml @@ -3,7 +3,7 @@ - page_title _("New Snippet") - @content_class = "limit-container-width" unless fluid_layout -%h3.page-title +%h1.page-title = _("New Snippet") %hr = render "shared/snippets/form", url: project_snippets_path(@project, @snippet) diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml index 428d87c6f03..d88397bbc7c 100644 --- a/app/views/projects/tags/new.html.haml +++ b/app/views/projects/tags/new.html.haml @@ -6,7 +6,7 @@ = c.body do = @error -%h3.page-title +%h1.page-title = s_('TagsPage|New Tag') %hr diff --git a/app/views/projects/tracings/show.html.haml b/app/views/projects/tracings/show.html.haml index c9aac68b19d..f306853b0c7 100644 --- a/app/views/projects/tracings/show.html.haml +++ b/app/views/projects/tracings/show.html.haml @@ -17,7 +17,7 @@ = html_escape(s_('Deprecations|The logs and tracing features were deprecated in GitLab 14.7, and are %{removal_link_start} scheduled for removal %{link_end} in GitLab 15.0. For information on a possible replacement, %{opstrace_link_start} learn more about Opstrace %{link_end}.')) % {removal_link_start: removal_epic_link_start, opstrace_link_start: opstrace_link_start, link_end: link_end } - if @project.tracing_external_url.present? - %h3.page-title= _('Tracing') + %h1.page-title= _('Tracing') .gl-alert.gl-alert-info.gl-mb-5 .gl-alert-container = sprite_icon('information-o', css_class: 'gl-icon gl-alert-icon gl-alert-icon-no-title') diff --git a/app/views/projects/usage_quotas/index.html.haml b/app/views/projects/usage_quotas/index.html.haml index 6dce9b27409..c5fe545c4ae 100644 --- a/app/views/projects/usage_quotas/index.html.haml +++ b/app/views/projects/usage_quotas/index.html.haml @@ -6,7 +6,7 @@ = c.body do = _('Refresh the page in a few minutes to view usage.') -%h3.page-title +%h1.page-title = s_('UsageQuota|Usage Quotas') .row diff --git a/app/views/sent_notifications/unsubscribe.html.haml b/app/views/sent_notifications/unsubscribe.html.haml index cacfd601d4d..86c5ad9d0a6 100644 --- a/app/views/sent_notifications/unsubscribe.html.haml +++ b/app/views/sent_notifications/unsubscribe.html.haml @@ -6,7 +6,7 @@ - noteable_url = show_project_path ? url_for([@sent_notification.project, noteable]) : breadcrumb_title_link - page_title _('Unsubscribe'), noteable_text, noteable_type.pluralize, project_path -%h3.page-title +%h1.page-title = _("Unsubscribe from %{type}") % { type: noteable_type } %p diff --git a/app/views/shared/_captcha_check.html.haml b/app/views/shared/_captcha_check.html.haml index 3d611c22491..b7b19a6e8f1 100644 --- a/app/views/shared/_captcha_check.html.haml +++ b/app/views/shared/_captcha_check.html.haml @@ -3,7 +3,7 @@ - script = local_assigns.fetch(:script, true) - method = params[:action] == 'create' ? :post : :put -%h3.page-title +%h1.page-title = _('Anti-spam verification') %hr diff --git a/app/views/shared/integrations/overrides.html.haml b/app/views/shared/integrations/overrides.html.haml index 4619675cfef..15889351af3 100644 --- a/app/views/shared/integrations/overrides.html.haml +++ b/app/views/shared/integrations/overrides.html.haml @@ -3,7 +3,7 @@ - page_title @integration.title, _('Integrations') - @content_class = 'limit-container-width' unless fluid_layout -%h3.page-title +%h1.page-title = @integration.title .js-vue-integration-overrides{ data: integration_overrides_data(@integration, project: @project, group: @group) } diff --git a/app/views/shared/wikis/diff.html.haml b/app/views/shared/wikis/diff.html.haml index 0eeceac28c8..8ef13ff8a28 100644 --- a/app/views/shared/wikis/diff.html.haml +++ b/app/views/shared/wikis/diff.html.haml @@ -5,7 +5,7 @@ .wiki-page-header.top-area.has-sidebar-toggle.flex-column.flex-lg-row = wiki_sidebar_toggle_button - %h3.page-title.gl-flex-grow-1 + %h1.page-title.gl-flex-grow-1 = link_to_wiki_page @page %span.light · diff --git a/app/views/shared/wikis/edit.html.haml b/app/views/shared/wikis/edit.html.haml index e0860bc473d..b5786fcb8b5 100644 --- a/app/views/shared/wikis/edit.html.haml +++ b/app/views/shared/wikis/edit.html.haml @@ -7,7 +7,7 @@ .js-wiki-edit-page.wiki-page-header.top-area.has-sidebar-toggle.flex-column.flex-lg-row = wiki_sidebar_toggle_button - %h3.page-title.gl-flex-grow-1 + %h1.page-title.gl-flex-grow-1 - if @page.persisted? = link_to_wiki_page @page %span.light diff --git a/app/views/shared/wikis/history.html.haml b/app/views/shared/wikis/history.html.haml index afbed3b0f42..be55fef2621 100644 --- a/app/views/shared/wikis/history.html.haml +++ b/app/views/shared/wikis/history.html.haml @@ -4,7 +4,7 @@ .wiki-page-header.top-area.has-sidebar-toggle.flex-column.flex-lg-row = wiki_sidebar_toggle_button - %h3.page-title + %h1.page-title = link_to_wiki_page @page %span.light · diff --git a/app/views/shared/wikis/pages.html.haml b/app/views/shared/wikis/pages.html.haml index abe7753b9f1..1a12993afe1 100644 --- a/app/views/shared/wikis/pages.html.haml +++ b/app/views/shared/wikis/pages.html.haml @@ -5,7 +5,7 @@ - wiki_sort_options = [{ text: s_("Wiki|Title"), value: 'title', href: wiki_path(@wiki, action: :pages, sort: Wiki::TITLE_ORDER)}, { text: s_("Wiki|Created date"), value: 'created_at', href: wiki_path(@wiki, action: :pages, sort: Wiki::CREATED_AT_ORDER) }] .wiki-page-header.top-area.flex-column.flex-lg-row - %h3.page-title.gl-flex-grow-1 + %h1.page-title.gl-flex-grow-1 = s_("Wiki|Wiki Pages") .nav-controls.pb-md-3.pb-lg-0 diff --git a/app/views/snippets/edit.html.haml b/app/views/snippets/edit.html.haml index f737e347c39..09d92a70c10 100644 --- a/app/views/snippets/edit.html.haml +++ b/app/views/snippets/edit.html.haml @@ -3,7 +3,7 @@ - content_for :prefetch_asset_tags do - webpack_preload_asset_tag('monaco') -%h3.page-title +%h1.page-title = _("Edit Snippet") %hr = render 'shared/snippets/form', url: gitlab_snippet_path(@snippet) diff --git a/app/views/users/unsubscribes/show.html.haml b/app/views/users/unsubscribes/show.html.haml index 8b3dc69f3a7..b22b8097333 100644 --- a/app/views/users/unsubscribes/show.html.haml +++ b/app/views/users/unsubscribes/show.html.haml @@ -1,5 +1,5 @@ - page_title _("Unsubscribe"), _("Admin Notifications") -%h3.page-title Unsubscribe from Admin notifications +%h1.page-title Unsubscribe from Admin notifications %hr = form_tag unsubscribe_path(Base64.urlsafe_encode64(@email)) do diff --git a/doc/.vale/gitlab/BadPlurals.yml b/doc/.vale/gitlab/BadPlurals.yml index efc55ac3d56..63f002fec94 100644 --- a/doc/.vale/gitlab/BadPlurals.yml +++ b/doc/.vale/gitlab/BadPlurals.yml @@ -6,9 +6,8 @@ # For a list of all options, see https://docs.errata.ai/vale/styles extends: existence message: 'Rewrite "%s" to be plural, without parentheses.' -link: https://docs.gitlab.com/ee/development/documentation/styleguide/word_list.html +link: https://docs.gitlab.com/ee/development/documentation/styleguide/word_list.html#s level: warning -scope: raw ignorecase: true raw: - '\b\w+\(s\)(?<!http\(s\))' diff --git a/doc/administration/application_settings_cache.md b/doc/administration/application_settings_cache.md index b3aac932fa8..33649cf53fc 100644 --- a/doc/administration/application_settings_cache.md +++ b/doc/administration/application_settings_cache.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Memory 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 --- diff --git a/doc/administration/operations/sidekiq_memory_killer.md b/doc/administration/operations/sidekiq_memory_killer.md index e48ac6e65eb..d9558c3d7a6 100644 --- a/doc/administration/operations/sidekiq_memory_killer.md +++ b/doc/administration/operations/sidekiq_memory_killer.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Memory 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 --- diff --git a/doc/administration/postgresql/database_load_balancing.md b/doc/administration/postgresql/database_load_balancing.md index 3b37ffd2d9d..0b6e00902e9 100644 --- a/doc/administration/postgresql/database_load_balancing.md +++ b/doc/administration/postgresql/database_load_balancing.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/administration/postgresql/external.md b/doc/administration/postgresql/external.md index f4e4c7f8bef..5d693793a92 100644 --- a/doc/administration/postgresql/external.md +++ b/doc/administration/postgresql/external.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/administration/postgresql/index.md b/doc/administration/postgresql/index.md index 15425a6d9f2..a8e69041f89 100644 --- a/doc/administration/postgresql/index.md +++ b/doc/administration/postgresql/index.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/administration/postgresql/pgbouncer.md b/doc/administration/postgresql/pgbouncer.md index 046b8da691f..5a39565cef7 100644 --- a/doc/administration/postgresql/pgbouncer.md +++ b/doc/administration/postgresql/pgbouncer.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 type: reference diff --git a/doc/administration/postgresql/replication_and_failover.md b/doc/administration/postgresql/replication_and_failover.md index bcac2ff3a2d..e9b607ad5d4 100644 --- a/doc/administration/postgresql/replication_and_failover.md +++ b/doc/administration/postgresql/replication_and_failover.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/administration/postgresql/standalone.md b/doc/administration/postgresql/standalone.md index b21625acb56..5428e44ccc0 100644 --- a/doc/administration/postgresql/standalone.md +++ b/doc/administration/postgresql/standalone.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/administration/troubleshooting/elasticsearch.md b/doc/administration/troubleshooting/elasticsearch.md index 97e625eb0a3..52de2f696dd 100644 --- a/doc/administration/troubleshooting/elasticsearch.md +++ b/doc/administration/troubleshooting/elasticsearch.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Global Search 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 --- diff --git a/doc/administration/troubleshooting/postgresql.md b/doc/administration/troubleshooting/postgresql.md index d0ed3c5c12a..7f32d01d617 100644 --- a/doc/administration/troubleshooting/postgresql.md +++ b/doc/administration/troubleshooting/postgresql.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md index 124bbd3d5f4..850440040a4 100644 --- a/doc/api/merge_requests.md +++ b/doc/api/merge_requests.md @@ -69,7 +69,7 @@ Parameters: | `in` | string | no | Modify the scope of the `search` attribute. `title`, `description`, or a string joining them with comma. Default is `title,description`. | | `wip` | string | no | Filter merge requests against their `wip` status. `yes` to return *only* draft merge requests, `no` to return *non-draft* merge requests. | | `not` | Hash | no | Return merge requests that do not match the parameters supplied. Accepts: `labels`, `milestone`, `author_id`, `author_username`, `assignee_id`, `assignee_username`, `reviewer_id`, `reviewer_username`, `my_reaction_emoji`. | -| `environment` | string | no | Returns merge requests deployed to the given environment. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`) | +| `environment` | string | no | Returns merge requests deployed to the given environment. | | `deployed_before` | datetime | no | Return merge requests deployed before the given date/time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`) | | `deployed_after` | datetime | no | Return merge requests deployed after the given date/time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`) | @@ -265,13 +265,13 @@ Parameters: | `approved_by_ids` **(PREMIUM)** | integer array | no | Returns merge requests which have been approved by all the users with the given `id`s (Max: 5). `None` returns merge requests with no approvals. `Any` returns merge requests with an approval. | | `reviewer_id` | integer | no | Returns merge requests which have the user as a [reviewer](../user/project/merge_requests/getting_started.md#reviewer) with the given user `id`. `None` returns merge requests with no reviewers. `Any` returns merge requests with any reviewer. Mutually exclusive with `reviewer_username`. | | `reviewer_username` | string | no | Returns merge requests which have the user as a [reviewer](../user/project/merge_requests/getting_started.md#reviewer) with the given `username`. `None` returns merge requests with no reviewers. `Any` returns merge requests with any reviewer. Mutually exclusive with `reviewer_id`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49341) in GitLab 13.8. | - | `my_reaction_emoji` | string | no | Return merge requests reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. | | `source_branch` | string | no | Return merge requests with the given source branch. | | `target_branch` | string | no | Return merge requests with the given target branch. | | `search` | string | no | Search merge requests against their `title` and `description`. | | `wip` | string | no | Filter merge requests against their `wip` status. `yes` to return *only* draft merge requests, `no` to return *non-draft* merge requests. | | `not` | Hash | no | Return merge requests that do not match the parameters supplied. Accepts: `labels`, `milestone`, `author_id`, `author_username`, `assignee_id`, `assignee_username`, `reviewer_id`, `reviewer_username`, `my_reaction_emoji`. | +| `environment` | string | no | Returns merge requests deployed to the given environment. | ```json [ diff --git a/doc/api/suggestions.md b/doc/api/suggestions.md index 0a3e7d54f88..c174c0f5264 100644 --- a/doc/api/suggestions.md +++ b/doc/api/suggestions.md @@ -36,7 +36,7 @@ Example response: "id": 36, "from_line": 10, "to_line": 10, - "appliable": false, + "applicable": false, "applied": true, "from_content": " \"--talk-name=org.freedesktop.\",\n", "to_content": " \"--talk-name=org.free.\",\n \"--talk-name=org.desktop.\",\n" diff --git a/doc/architecture/blueprints/ci_data_decay/decomposition_partitioning_comparison.png b/doc/architecture/blueprints/ci_data_decay/decomposition_partitioning_comparison.png Binary files differnew file mode 100644 index 00000000000..47a53ad9be5 --- /dev/null +++ b/doc/architecture/blueprints/ci_data_decay/decomposition_partitioning_comparison.png diff --git a/doc/architecture/blueprints/ci_data_decay/pipeline_partitioning.md b/doc/architecture/blueprints/ci_data_decay/pipeline_partitioning.md new file mode 100644 index 00000000000..62a4e0f7b7d --- /dev/null +++ b/doc/architecture/blueprints/ci_data_decay/pipeline_partitioning.md @@ -0,0 +1,435 @@ +--- +stage: none +group: unassigned +comments: false +description: 'Pipeline data partitioning design' +--- + +# Pipeline data partitioning design + +_Disclaimer: The following contains information related to upcoming products, +features, and functionality._ + +_It is important to note that the information presented is for informational +purposes only. Please do not rely on this information for purchasing or +planning purposes._ + +_As with all projects, the items mentioned in this document and linked pages +are subject to change or delay. The development, release and timing of any +products, features, or functionality remain at the sole discretion of GitLab +Inc._ + +## What problem are we trying to solve? + +We want to partition the CI/CD dataset, because some of the database tables are +extremely large, which might be challenging in terms of scaling single node +reads, even after we ship the CI/CD database decomposition. + +We want to reduce the risk of database performance degradation by transforming +a few of the largest database tables into smaller ones using PostgreSQL +declarative partitioning. + +See more details about this effort in [the parent blueprint](index.md). + +![pipeline data time decay](pipeline_data_time_decay.png) + +## How are CI/CD data decomposition, partitioning, and time-decay related? + +CI/CD decomposition is an extraction of a CI/CD database cluster out of the +"main" database cluster, to make it possible to have a different primary +database receiving writes. The main benefit is doubling the capacity for writes +and data storage. The new database cluster will not have to serve reads / +writes for non-CI/CD database tables, so this offers some additional capacity +for reads too. + +CI/CD partitioning is dividing large CI/CD database tables into smaller ones. +This will improve reads capacity on every CI/CD database node, because it is +much less expensive to read data from small tables, than from large +multi-terabytes tables. We can add more CI/CD database replicas to better +handle the increase in the number of SQL queries that are reading data, but we +need partitioning to perform a single read more efficiently. Performance in +other aspects will improve too, because PostgreSQL will be more efficient in +maintaining multiple small tables than in maintaining a very large database +table. + +CI/CD time-decay allows us to benefit from the strong time-decay +characteristics of pipeline data. It can be implemented in many different ways, +but using partitioning to implement time-decay might be especially beneficial. +When implementing a time decay we usually mark data as archived, and migrate it +out of a database to a different place when data is no longer relevant or +needed. Our dataset is extremely large (tens of terabytes), so moving such a +high volume of data is challenging. When time-decay is implemented using +partitioning, we can archive the entire partition (or set of partitions) by +simply updating a single record in one of our database tables. It is one of the +least expensive ways to implement time-decay patterns at a database level. + +![decomposition_partitioning_comparison.png](decomposition_partitioning_comparison.png) + +## Why do we need to partition CI/CD data? + +We need to partition CI/CD data because our database tables storing pipelines, +builds, and artifacts are too large. The `ci_builds` database table size is +currently around 2.5 TB with an index of around 1.4 GB. This is too much and +violates our [principle of 100 GB max size](../database_scaling/size-limits.md). +We also want to [build alerting](https://gitlab.com/gitlab-com/gl-infra/tamland/-/issues/5) +to notify us when this number is exceeded. + +We’ve seen numerous S1 and S2 database-related production environment +incidents, over the last couple of months, for example: + +- S1: 2022-03-17 [Increase in writes in `ci_builds` table](https://gitlab.com/gitlab-com/gl-infra/production/-/issues/6625) +- S1: 2021-11-22 [Excessive buffer read in replicas for `ci_job_artifacts`](https://gitlab.com/gitlab-com/gl-infra/production/-/issues/5952) +- S2: 2022-04-12 [Transactions detected that have been running for more than 10m](https://gitlab.com/gitlab-com/gl-infra/production/-/issues/6821) +- S2: 2022-04-06 [Database contention plausibly caused by excessive `ci_builds` reads](https://gitlab.com/gitlab-com/gl-infra/production/-/issues/6773) +- S2: 2022-03-18 [Unable to remove a foreign key on `ci_builds`](https://gitlab.com/gitlab-com/gl-infra/production/-/issues/6642) + +We have approximately 50 `ci_*` prefixed database tables, and some of them +would benefit from partitioning. + +A simple SQL query to get this data: + +```sql +WITH tables AS (SELECT table_name FROM information_schema.tables WHERE table_name LIKE 'ci_%') + SELECT table_name, + pg_size_pretty(pg_total_relation_size(quote_ident(table_name))) AS total_size, + pg_size_pretty(pg_relation_size(quote_ident(table_name))) AS table_size, + pg_size_pretty(pg_indexes_size(quote_ident(table_name))) AS index_size, + pg_total_relation_size(quote_ident(table_name)) AS total_size_bytes + FROM tables ORDER BY total_size_bytes DESC; +``` + +See data from March 2022: + +| Table name | Total size | Index size | +|-------------------------|------------|------------| +| `ci_builds` | 3.5 TB | 1 TB | +| `ci_builds_metadata` | 1.8 TB | 150 GB | +| `ci_job_artifacts` | 600 GB | 300 GB | +| `ci_pipelines` | 400 GB | 300 GB | +| `ci_stages` | 200 GB | 120 GB | +| `ci_pipeline_variables` | 100 GB | 20 GB | +| (...around 40 more) | | | + +Based on the table above, it is clear that there are tables with a lot of +stored data. + +While we have almost 50 CI/CD-related database tables, we are initially +interested in partitioning only 6 of them. We can start by partitioning the +most interesting tables in an iterative way, but we also should have a strategy +for partitioning the remaining ones if needed. This document is an attempt to +capture this strategy, describe as many details as possible, to share this +knowledge among engineering teams. + +## How do we want to partition CI/CD data? + +We want to partition the CI/CD tables in iterations. It might not be feasible +to partition all of the 6 initial tables at once, so an iterative strategy +might be necessary. We also want to have a strategy for partitioning the +remaining database tables when it becomes necessary. + +It is also important to avoid large data migrations. We store almost 6 +terabytes of data in the biggest CI/CD tables, in many different columns and +indexes. Migrating this amount of data might be challenging and could cause +instability in the production environment. Due to this concern, we’ve developed +a way to attach an existing database table as a partition zero without downtime +and excessive database locking, what has been demonstrated in one of the +[first proofs of concept](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/80186). +This makes creation of a partitioned schema possible without a downtime (for +example using a routing table `p_ci_pipelines`), by attaching an existing +`ci_pipelines` table as partition zero without exclusive locking. It will be +possible to use the legacy table as usual, but we can create the next partition +when needed and the `p_ci_pipelines` table will be used for routing queries. To +use the routing table we need to find a good partitioning key. + +Our plan is to use logical partition IDs. We want to start with the +`ci_pipelines` table and create a `partition_id` column with a `DEFAULT` value +of `100` or `1000`. Using a `DEFAULT` value avoids the challenge of backfilling +this value for every row. Adding a `CHECK` constraint prior to attaching the +first partition tells PostgreSQL that we’ve already ensured consistency and +there is no need to check it while holding an exclusive table lock when +attaching this table as a partition to the routing table (partitioned schema +definition). We will increment this value every time we create a new partition +for `p_ci_pipelines`, and the partitioning strategy will be `LIST` +partitioning. + +We will also create a `partition_id` column in the other initial 6 database +tables we want to iteratively partition. After a new pipeline is created, it +will get a `partition_id` assigned, and all the related resources, like builds +and artifacts, will share the same value. We want to add the `partition_id` +column into all 6 problematic tables because we can avoid backfilling this data +when we decide it is time to start partitioning them. + +We want to partition CI/CD data iteratively, so we will start with the +pipelines table, and create at least one, but likely two, partitions. The +pipelines table will be partitioned using the `LIST` partitioning strategy. It +is possible that, after some time, `p_ci_pipelines` will store data in two +partitions with IDs of `100` and `101`. Then we will try partitioning +`ci_builds`. Therefore we might want to use `RANGE` partitioning in +`p_ci_builds` with IDs `100` and `101`, because builds for the two logical +partitions used will still be stored in a single table. + +Physical partitioning and logical partitioning will be separated, and a +strategy will be determined when we implement partitioning for the respective +database tables. Using `RANGE` partitioning works similarly to using `LIST` +partitioning in database tables other than `ci_pipelines`, but because we can +guarantee continuity of `partition_id` values, using `RANGE` partitioning might +be a better strategy. + +## Why do we want to use explicit logical partition ids? + +Partitioning CI/CD data using a logical `partition_id` has several benefits. We +could partition by a primary key, but this would introduce much more complexity +and additional cognitive load required to understand how the data is being +structured and stored in partitions. + +CI/CD data is hierarchical data. Stages belong to pipelines, builds belong to +stages, artifacts belong to builds (with rare exceptions). We are designing a +partitioning strategy that reflects this hierarchy, to reduce the complexity +and therefore cognitive load for contributors. With an explicit `partition_id` +associated with a pipeline, we can cascade the partition ID number when trying +to retrieve all resources associated with a pipeline. We know that for a +pipeline `12345` with a `partition_id` of `102`, we are always able to find +associated resources in logical partitions with number `102` in other routing +tables, and PostgreSQL will know in which partitions these records are being +stored in for every table. + +Another interesting benefit for using a single and incremental latest +`partition_id` number, associated with pipelines, is that in theory we can +cache it in Redis or in memory to avoid excessive reads from the database to +find this number, though we might not need to do this. + +The single and uniform `partition_id` value for pipeline data gives us more +choices later on than primary-keys-based partitioning. + +## Splitting large partitions into smaller ones + +We want to start with the initial `pipeline_id` number `100` (or higher, like +`1000`, depending on our calculations and estimations). We do not want to start +from 1, because existing tables are also large already, and we might want to +split them into smaller partitions. If we start with `100`, we will be able to +create partitions for `partition_id` of `1`, `20`, `45`, and move existing +records there by updating `partition_id` from `100` to a smaller number. + +PostgreSQL will move these records into their respective partitions in a +consistent way, provided that we do it in a transaction for all pipeline +resources at the same time. If we ever decide to split large partitions into +smaller ones (it's not yet clear if we will need to do this), we might be able +to just use background migrations to update partition IDs, and PostgreSQL is +smart enough to move rows between partitions on its own. + +## Storing partitions metadata in the database + +In order to build an efficient mechanism that will be responsible for creating +new partitions, and to implement time decay we want to introduce a partitioning +metadata table, called `ci_partitions`. In that table we would store metadata +about all the logical partitions, with many pipelines per partition. We may +need to store a range of pipeline ids per logical partition. Using it we will +be able to find the `partition_id` number for a given pipeline ID and we will +also find information about which logical partitions are “active” or +“archived”, which will help us to implement a time-decay pattern using database +declarative partitioning. + +`ci_partitions` table will store information about a partition identifier, +pipeline ids range it is valid for and whether the partitions have been +archived or not. Additional columns with timestamps may be helpful too. + +## Implementing a time-decay pattern using partitioning + +We can use `ci_partitions` to implement a time-decay pattern using declarative +partitioning. By telling PostgreSQL which logical partitions are archived we +can stop reading from these partitions using a SQL query like the one below. + +```sql +SELECT * FROM ci_builds WHERE partition_id IN ( + SELECT id FROM ci_partitions WHERE active = true +); +``` + +This query will make it possible to limit the number of partitions we will read +from, and therefore will cut access to "archived" pipeline data, using our data +retention policy for CI/CD data. Ideally we do not want to read from more than +two partitions at once, so we need to align the automatic partitioning +mechanisms with the time-decay policy. We will still need to implement new +access patterns for the archived data, presumably through the API, but the cost +of storing archived data in PostgreSQL will be reduced significantly this way. + +There are some technical details here that are out of the scope of this +description, but by using this strategy we can "archive" data, and make it much +less expensive to reside in our PostgreSQL cluster by simply toggling a boolean +column value. + +## Accessing partitioned data + +It will be possible to access partitioned data whether it has been archived or +not, in most places in GitLab. On a merge request page, we will always show +pipeline details even if the merge request was created years ago. We can do +that because `ci_partitions` will be a lookup table associating a pipeline ID +with its `partition_id`, and we will be able to find the partition that the +pipeline data is stored in. + +We will need to constrain access to searching through pipelines, builds, +artifacts etc. Search can not be done through all partitions, as it would not +be efficient enough, hence we will need to find a better way of searching +through archived pipelines data. It will be necessary to have different access +patterns to access archived data in the UI and API. + +There are a few challenges in enforcing usage of the `partition_id` +partitioning key in PostgreSQL. To make it easier to update our application to +support this, we have designed a new queries analyzer in our +[proof of concept merge request](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/80186). +It helps to find queries that are not using the partitioning key. + +In a [separate proof of concept merge request](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/84071) +and [related issue](https://gitlab.com/gitlab-org/gitlab/-/issues/357090) we +demonstrated that using the uniform `partition_id` makes it possible to extend +Rails associations with an additional scope modifier so we can provide the +partitioning key in the SQL query. + +Using instance dependent associations, we can easily append a partitioning key +to SQL queries that are supposed to retrieve associated pipeline resources, for +example: + +```ruby +has_many :builds, -> (pipeline) { where(partition_id: pipeline.partition_id) } +``` + +The problem with this approach is that it makes preloading much more difficult +as instance dependent associations can not be used with preloads: + +```plaintext +ArgumentError: The association scope 'builds' is instance dependent (the +scope block takes an argument). Preloading instance dependent scopes is not +supported. +``` + +We also need to build a proof of concept for removing data on the PostgreSQL +side (using foreign keys with `ON DELETE CASCADE`) and removing data through +Rails associations, as this might be an important area of uncertainty. + +We need to [better understand](https://gitlab.com/gitlab-org/gitlab/-/issues/360148) +how unique constraints we are currently using will perform when using the +partitioned schema. + +We have also designed a query analyzer that makes it possible to detect direct +usage of zero partitions, legacy tables that have been attached as first +partitions to routing tables, to ensure that all queries are targeting +partitioned schema or partitioned routing tables, like `p_ci_pipelines`. + +## Why not partition using the project or namespace ID? + +We do not want to partition using `project_id` or `namespace_id` because +sharding and podding is a different problem to solve, on a different layer of +the application. It doesn't solve the original problem statement of performance +growing worse over time as we build up infrequently read data. We may want to +introduce pods in the future, and that might become the primary mechanism of +separating data based on the group or project the data is associated with. + +In theory we could use either `project_id` or `namespace_id` as a second +partitioning dimension, but this would add more complexity to a problem that is +already very complex. + +## Partitioning builds queuing tables + +We also want to partition our builds queuing tables. We currently have two: +`ci_pending_builds` and `ci_running_builds`. These tables are different from +other CI/CD data tables, as there are business rules in our product that make +all data stored in them invalid after 24 hours. + +As a result, we will need to use a different strategy to partition those +database tables, by removing partitions entirely after these are older than 24 +hours, and always reading from two partitions through a routing table. The +strategy to partition these tables is well understood, but requires a solid +Ruby-based automation to manage the creation and deletion of these partitions. +To achieve that we will collaborate with the Database team to adapt +[existing database partitioning tools](../../../development/database/table_partitioning.md) +to support CI/CD data partitioning. + +## Iterating to reduce the risk + +This strategy should reduce the risk of implementing CI/CD partitioning to +acceptable levels. We are also focusing on implementing partitioning for +reading only two partitions initially to make it possible to detach zero +partitions in case of problems in our production environment. Every iteration +phase, described below has a revert strategy and before shipping database +changes we want to test them in our benchmarking environment. + +The main way of reducing risk in case of this effort is iteration and making +things reversible. Shipping changes, described in this document, in a safe and +reliable way is our priority. + +## Iterations + +We want to focus on Phase 1 iteration first. The goal and the main objective of +this iteration is to partition the biggest 6 CI/CD database tables into 6 +routing tables (partitioned schema) and 12 partitions. This will leave our +Rails SQL queries mostly unchanged, but it will also make it possible to +perform emergency detachment of "zero partitions" if there is a database +performance degradation. This will cut users off their old data, but the +application will remain up and running, which is a better alternative to +application-wide outage. + +1. **Phase 0**: Build CI/CD data partitioning strategy: Done. ✅ +1. **Phase 1**: Partition the 6 biggest CI/CD database tables. + 1. Create partitioned schemas for all 6 database tables. + 1. Design a way to cascade `partition_id` to all partitioned resources. + 1. Implement initial query analyzers validating that we target routing tables. + 1. Attach zero partitions to the partitioned database tables. + 1. Update the application to target routing tables and partitioned tables. + 1. Measure the performance and efficiency of this solution. + + **Revert strategy**: Switch back to using concrete partitions instead of routing tables. + +1. **Phase 2**: Add a partitioning key to add SQL queries targeting partitioned tables. + 1. Implement query analyzer to check if queries targeting partitioned tables + are using proper partitioning keys. + 1. Modify existing queries to make sure that all of them are using a + partitioning key as a filter. + + **Revert strategy**: Use feature flags, query by query. + +1. **Phase 3**: Build new partitioned data access patterns. + 1. Build a new API or extend an existing one to allow access to data stored in + partitions that are supposed to be excluded based on the time-decay data + retention policy. + + **Revert strategy**: Feature flags. + +1. **Phase 4**: Introduce time-decay mechanisms built on top of partitioning. + 1. Build time-decay policy mechanisms. + 1. Enable the time-decay strategy on GitLab.com. +1. **Phase 5**: Introduce mechanisms for creating partitions automatically. + 1. Make it possible to create partitions in an automatic way. + 1. Deliver the new architecture to self-managed instances. + +## Conclusions + +We want to build a solid strategy for partitioning CI/CD data. We are aware of +the fact that it is difficult to iterate on this design, because a mistake made +in managing the database schema of our multi-terabyte PostgreSQL instance might +not be easily reversible without potential downtime. That is the reason we are +spending a significant amount of time to research and refine our partitioning +strategy. The strategy, described in this document, is subject to iteration as +well. Whenever we find a better way to reduce the risk and improve our plan, we +should update this document as well. + +We’ve managed to find a way to avoid large-scale data migrations, and we are +building an iterative strategy for partitioning CI/CD data. We documented our +strategy here to share knowledge and solicit feedback from other team members. + +## Who + +Authors: + +<!-- vale gitlab.Spelling = NO --> + +| Role | Who | +|--------|----------------| +| Author | Grzegorz Bizon | + +Recommenders: + +| Role | Who | +|------------------------|-----------------| +| Distingiushed Engineer | Kamil Trzciński | + +<!-- vale gitlab.Spelling = YES --> diff --git a/doc/architecture/blueprints/database/scalability/patterns/index.md b/doc/architecture/blueprints/database/scalability/patterns/index.md index dadf3634407..4a9bb003763 100644 --- a/doc/architecture/blueprints/database/scalability/patterns/index.md +++ b/doc/architecture/blueprints/database/scalability/patterns/index.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 comments: false diff --git a/doc/architecture/blueprints/database/scalability/patterns/read_mostly.md b/doc/architecture/blueprints/database/scalability/patterns/read_mostly.md index 02b56841507..75fcf10f048 100644 --- a/doc/architecture/blueprints/database/scalability/patterns/read_mostly.md +++ b/doc/architecture/blueprints/database/scalability/patterns/read_mostly.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 comments: false diff --git a/doc/architecture/blueprints/database/scalability/patterns/time_decay.md b/doc/architecture/blueprints/database/scalability/patterns/time_decay.md index b4614cde9d4..7399fd8048d 100644 --- a/doc/architecture/blueprints/database/scalability/patterns/time_decay.md +++ b/doc/architecture/blueprints/database/scalability/patterns/time_decay.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 comments: false diff --git a/doc/development/adding_database_indexes.md b/doc/development/adding_database_indexes.md index 0f30734c04b..b44a2861735 100644 --- a/doc/development/adding_database_indexes.md +++ b/doc/development/adding_database_indexes.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/development/cached_queries.md b/doc/development/cached_queries.md index bfa518ff7bf..b0bf7c7b6f5 100644 --- a/doc/development/cached_queries.md +++ b/doc/development/cached_queries.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Memory 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 --- diff --git a/doc/development/creating_enums.md b/doc/development/creating_enums.md index 1f04f4c9712..a5a642eed86 100644 --- a/doc/development/creating_enums.md +++ b/doc/development/creating_enums.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/development/database/add_foreign_key_to_existing_column.md b/doc/development/database/add_foreign_key_to_existing_column.md index bfd455ef9da..9842814816f 100644 --- a/doc/development/database/add_foreign_key_to_existing_column.md +++ b/doc/development/database/add_foreign_key_to_existing_column.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/development/database/avoiding_downtime_in_migrations.md b/doc/development/database/avoiding_downtime_in_migrations.md index 57d8a1fab78..e7c57ca242b 100644 --- a/doc/development/database/avoiding_downtime_in_migrations.md +++ b/doc/development/database/avoiding_downtime_in_migrations.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/development/database/background_migrations.md b/doc/development/database/background_migrations.md index 80ba0336bda..7519f7276b6 100644 --- a/doc/development/database/background_migrations.md +++ b/doc/development/database/background_migrations.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/development/database/batched_background_migrations.md b/doc/development/database/batched_background_migrations.md index b466b63e9c6..c05ffe2cbf5 100644 --- a/doc/development/database/batched_background_migrations.md +++ b/doc/development/database/batched_background_migrations.md @@ -1,6 +1,6 @@ --- type: reference, dev -stage: Enablement +stage: Data Stores group: Database info: "See the Technical Writers assigned to Development Guidelines: https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments-to-development-guidelines" --- diff --git a/doc/development/database/client_side_connection_pool.md b/doc/development/database/client_side_connection_pool.md index 60c8665df87..dc52a551407 100644 --- a/doc/development/database/client_side_connection_pool.md +++ b/doc/development/database/client_side_connection_pool.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/development/database/constraint_naming_convention.md b/doc/development/database/constraint_naming_convention.md index a22ddc1551c..72f16c20559 100644 --- a/doc/development/database/constraint_naming_convention.md +++ b/doc/development/database/constraint_naming_convention.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/development/database/database_lab.md b/doc/development/database/database_lab.md index 1c8694b113d..5346df2690d 100644 --- a/doc/development/database/database_lab.md +++ b/doc/development/database/database_lab.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/development/database/database_migration_pipeline.md b/doc/development/database/database_migration_pipeline.md index ce7e1801abc..496bd09bf1d 100644 --- a/doc/development/database/database_migration_pipeline.md +++ b/doc/development/database/database_migration_pipeline.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/development/database/database_reviewer_guidelines.md b/doc/development/database/database_reviewer_guidelines.md index ca9ca36b156..c8ab1e7353a 100644 --- a/doc/development/database/database_reviewer_guidelines.md +++ b/doc/development/database/database_reviewer_guidelines.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/development/database/dbcheck-migrations-job.md b/doc/development/database/dbcheck-migrations-job.md index af72e28a875..49f8b183272 100644 --- a/doc/development/database/dbcheck-migrations-job.md +++ b/doc/development/database/dbcheck-migrations-job.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/development/database/deleting_migrations.md b/doc/development/database/deleting_migrations.md index be9009f365d..8354cb62d0c 100644 --- a/doc/development/database/deleting_migrations.md +++ b/doc/development/database/deleting_migrations.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/development/database/efficient_in_operator_queries.md b/doc/development/database/efficient_in_operator_queries.md index 2503be826ea..a8104c3d7e9 100644 --- a/doc/development/database/efficient_in_operator_queries.md +++ b/doc/development/database/efficient_in_operator_queries.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/development/database/index.md b/doc/development/database/index.md index 0363d13ed4c..b427f54ff3c 100644 --- a/doc/development/database/index.md +++ b/doc/development/database/index.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/development/database/keyset_pagination.md b/doc/development/database/keyset_pagination.md index 88928feb927..d87d09d82bc 100644 --- a/doc/development/database/keyset_pagination.md +++ b/doc/development/database/keyset_pagination.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/development/database/layout_and_access_patterns.md b/doc/development/database/layout_and_access_patterns.md index a3e2fefb2a3..99a50b503aa 100644 --- a/doc/development/database/layout_and_access_patterns.md +++ b/doc/development/database/layout_and_access_patterns.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/development/database/loose_foreign_keys.md b/doc/development/database/loose_foreign_keys.md index ef6d13d3cfc..6889b9123ca 100644 --- a/doc/development/database/loose_foreign_keys.md +++ b/doc/development/database/loose_foreign_keys.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/development/database/maintenance_operations.md b/doc/development/database/maintenance_operations.md index 9e7a35531ca..85df185c024 100644 --- a/doc/development/database/maintenance_operations.md +++ b/doc/development/database/maintenance_operations.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/development/database/migrations_for_multiple_databases.md b/doc/development/database/migrations_for_multiple_databases.md index 023b28ce6a6..2500071f4cf 100644 --- a/doc/development/database/migrations_for_multiple_databases.md +++ b/doc/development/database/migrations_for_multiple_databases.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/development/database/multiple_databases.md b/doc/development/database/multiple_databases.md index c622d4f50ff..324d1a6c6ef 100644 --- a/doc/development/database/multiple_databases.md +++ b/doc/development/database/multiple_databases.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Sharding 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 --- @@ -597,3 +597,21 @@ way to replace cascading deletes so we don't end up with orphaned data or records that point to nowhere, which might lead to bugs. As such we created ["loose foreign keys"](loose_foreign_keys.md) which is an asynchronous process of cleaning up orphaned records. + +## Locking writes on the tables that don't belong to the database schemas + +When the CI database is promoted and the two databases are fully split, +as an extra safeguard against creating a split brain situation, +run the Rake task `gitlab:db:lock_writes`. This command locks writes on: + +- The `gitlab_main` tables on the CI Database. +- The `gitlab_ci` tables on the Main Database. + +This Rake task adds triggers to all the tables, to prevent any +`INSERT`, `UPDATE`, `DELETE`, or `TRUNCATE` statements from running +against the tables that need to be locked. + +If this task was run against a GitLab setup that uses only a single database +for both `gitlab_main` and `gitlab_ci` tables, then no tables will be locked. + +To undo the operation, run the opposite Rake task: `gitlab:db:unlock_writes`. diff --git a/doc/development/database/not_null_constraints.md b/doc/development/database/not_null_constraints.md index af7d569e282..947ac6cd875 100644 --- a/doc/development/database/not_null_constraints.md +++ b/doc/development/database/not_null_constraints.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/development/database/pagination_guidelines.md b/doc/development/database/pagination_guidelines.md index b5cbae9ad05..d70a34519fb 100644 --- a/doc/development/database/pagination_guidelines.md +++ b/doc/development/database/pagination_guidelines.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/development/database/pagination_performance_guidelines.md b/doc/development/database/pagination_performance_guidelines.md index 90e4faf2de7..7a0c6131e2c 100644 --- a/doc/development/database/pagination_performance_guidelines.md +++ b/doc/development/database/pagination_performance_guidelines.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/development/database/post_deployment_migrations.md b/doc/development/database/post_deployment_migrations.md index 799eefdb875..a49c77ca047 100644 --- a/doc/development/database/post_deployment_migrations.md +++ b/doc/development/database/post_deployment_migrations.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/development/database/rename_database_tables.md b/doc/development/database/rename_database_tables.md index 7a76c028042..44f95b1d277 100644 --- a/doc/development/database/rename_database_tables.md +++ b/doc/development/database/rename_database_tables.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/development/database/setting_multiple_values.md b/doc/development/database/setting_multiple_values.md index 0f23aae9f79..cba15a73430 100644 --- a/doc/development/database/setting_multiple_values.md +++ b/doc/development/database/setting_multiple_values.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/development/database/strings_and_the_text_data_type.md b/doc/development/database/strings_and_the_text_data_type.md index 7aa529e1518..d764e54aa76 100644 --- a/doc/development/database/strings_and_the_text_data_type.md +++ b/doc/development/database/strings_and_the_text_data_type.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/development/database/table_partitioning.md b/doc/development/database/table_partitioning.md index 34cb73978bc..51f2c443047 100644 --- a/doc/development/database/table_partitioning.md +++ b/doc/development/database/table_partitioning.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/development/database/transaction_guidelines.md b/doc/development/database/transaction_guidelines.md index 2806bd217db..503a713443a 100644 --- a/doc/development/database/transaction_guidelines.md +++ b/doc/development/database/transaction_guidelines.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/development/database_debugging.md b/doc/development/database_debugging.md index e749fd6453b..c734799cdc4 100644 --- a/doc/development/database_debugging.md +++ b/doc/development/database_debugging.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/development/database_query_comments.md b/doc/development/database_query_comments.md index e4133633a77..9cfa5540c83 100644 --- a/doc/development/database_query_comments.md +++ b/doc/development/database_query_comments.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/development/database_review.md b/doc/development/database_review.md index ceb8c9e5906..2f4b7ca7c10 100644 --- a/doc/development/database_review.md +++ b/doc/development/database_review.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/development/db_dump.md b/doc/development/db_dump.md index 0c63bf06e07..f2076cbc410 100644 --- a/doc/development/db_dump.md +++ b/doc/development/db_dump.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/development/documentation/versions.md b/doc/development/documentation/versions.md index 81b9108364e..067c37d30aa 100644 --- a/doc/development/documentation/versions.md +++ b/doc/development/documentation/versions.md @@ -132,7 +132,7 @@ To remove a page: ```markdown --- - stage: Enablement + stage: Data Stores group: Global Search 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 remove_date: '2022-08-02' diff --git a/doc/development/elasticsearch.md b/doc/development/elasticsearch.md index 7c67b3495ba..3745a9e50ad 100644 --- a/doc/development/elasticsearch.md +++ b/doc/development/elasticsearch.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Global Search 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 --- diff --git a/doc/development/foreign_keys.md b/doc/development/foreign_keys.md index c20c70623ae..7709197a43a 100644 --- a/doc/development/foreign_keys.md +++ b/doc/development/foreign_keys.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/development/graphql_guide/batchloader.md b/doc/development/graphql_guide/batchloader.md index 0e90f89ff7a..4cdf379fe5e 100644 --- a/doc/development/graphql_guide/batchloader.md +++ b/doc/development/graphql_guide/batchloader.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/development/hash_indexes.md b/doc/development/hash_indexes.md index 881369e429b..731639b6f06 100644 --- a/doc/development/hash_indexes.md +++ b/doc/development/hash_indexes.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/development/image_scaling.md b/doc/development/image_scaling.md index e1ffbdb766a..93575429369 100644 --- a/doc/development/image_scaling.md +++ b/doc/development/image_scaling.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Memory 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 --- diff --git a/doc/development/insert_into_tables_in_batches.md b/doc/development/insert_into_tables_in_batches.md index cd659a3d19b..c8bb4ce1c6d 100644 --- a/doc/development/insert_into_tables_in_batches.md +++ b/doc/development/insert_into_tables_in_batches.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 description: "Sometimes it is necessary to store large amounts of records at once, which can be inefficient diff --git a/doc/development/iterating_tables_in_batches.md b/doc/development/iterating_tables_in_batches.md index 8813fe560db..384b19cf618 100644 --- a/doc/development/iterating_tables_in_batches.md +++ b/doc/development/iterating_tables_in_batches.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/development/ordering_table_columns.md b/doc/development/ordering_table_columns.md index 00ce15fcc10..42e5588e010 100644 --- a/doc/development/ordering_table_columns.md +++ b/doc/development/ordering_table_columns.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/development/query_performance.md b/doc/development/query_performance.md index bc1f753c012..87e41c78e19 100644 --- a/doc/development/query_performance.md +++ b/doc/development/query_performance.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/development/query_recorder.md b/doc/development/query_recorder.md index 17f2fecc1bc..263bcf66fad 100644 --- a/doc/development/query_recorder.md +++ b/doc/development/query_recorder.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/development/serializing_data.md b/doc/development/serializing_data.md index 48e756d015b..76e9dc0617e 100644 --- a/doc/development/serializing_data.md +++ b/doc/development/serializing_data.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/development/service_ping/metrics_instrumentation.md b/doc/development/service_ping/metrics_instrumentation.md index ff567e3b1b3..b2332241c8a 100644 --- a/doc/development/service_ping/metrics_instrumentation.md +++ b/doc/development/service_ping/metrics_instrumentation.md @@ -293,7 +293,7 @@ There is support for: There is no support for: -- `add`, `sum`, `histogram` for database metrics. +- `add`, `histogram` for database metrics. You can [track the progress to support these](https://gitlab.com/groups/gitlab-org/-/epics/6118). diff --git a/doc/development/single_table_inheritance.md b/doc/development/single_table_inheritance.md index 0783721e628..c8d082e8a67 100644 --- a/doc/development/single_table_inheritance.md +++ b/doc/development/single_table_inheritance.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/development/sql.md b/doc/development/sql.md index 4b6153b7205..17250992588 100644 --- a/doc/development/sql.md +++ b/doc/development/sql.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/development/swapping_tables.md b/doc/development/swapping_tables.md index cb038a3b85a..d6c5b8f0662 100644 --- a/doc/development/swapping_tables.md +++ b/doc/development/swapping_tables.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/development/understanding_explain_plans.md b/doc/development/understanding_explain_plans.md index e06ece38135..3fc071bc5ff 100644 --- a/doc/development/understanding_explain_plans.md +++ b/doc/development/understanding_explain_plans.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/development/verifying_database_capabilities.md b/doc/development/verifying_database_capabilities.md index bda9c68eae5..9433e9d5f78 100644 --- a/doc/development/verifying_database_capabilities.md +++ b/doc/development/verifying_database_capabilities.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/integration/elasticsearch.md b/doc/integration/elasticsearch.md index ded1449682d..64d229b39b8 100644 --- a/doc/integration/elasticsearch.md +++ b/doc/integration/elasticsearch.md @@ -1,6 +1,6 @@ --- type: reference -stage: Enablement +stage: Data Stores group: Global Search 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 --- diff --git a/doc/update/mysql_to_postgresql.md b/doc/update/mysql_to_postgresql.md index 40dc57c554b..8ac2791c82b 100644 --- a/doc/update/mysql_to_postgresql.md +++ b/doc/update/mysql_to_postgresql.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/update/upgrading_postgresql_using_slony.md b/doc/update/upgrading_postgresql_using_slony.md index f2bbc8d7558..d3102ca4591 100644 --- a/doc/update/upgrading_postgresql_using_slony.md +++ b/doc/update/upgrading_postgresql_using_slony.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/user/admin_area/monitoring/background_migrations.md b/doc/user/admin_area/monitoring/background_migrations.md index b666c0c5ad2..53d5056bb65 100644 --- a/doc/user/admin_area/monitoring/background_migrations.md +++ b/doc/user/admin_area/monitoring/background_migrations.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Database 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 --- diff --git a/doc/user/permissions.md b/doc/user/permissions.md index d429312161e..de2bf0a8eb6 100644 --- a/doc/user/permissions.md +++ b/doc/user/permissions.md @@ -270,6 +270,7 @@ More details about the permissions for some project-level features follow. | Cancel and retry jobs | | | | ✓ | ✓ | ✓ | | Create new [environments](../ci/environments/index.md) | | | | ✓ | ✓ | ✓ | | Delete job logs or job artifacts | | | | ✓ (*4*) | ✓ | ✓ | +| Run CI/CD pipeline | | | | ✓ | ✓ | ✓ | | Run CI/CD pipeline for a protected branch | | | | ✓ (*5*) | ✓ (*5*) | ✓ | | Stop [environments](../ci/environments/index.md) | | | | ✓ | ✓ | ✓ | | View a job with [debug logging](../ci/variables/index.md#debug-logging) | | | | ✓ | ✓ | ✓ | diff --git a/doc/user/search/advanced_search.md b/doc/user/search/advanced_search.md index 5435a9d027c..4caef55a0b3 100644 --- a/doc/user/search/advanced_search.md +++ b/doc/user/search/advanced_search.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Global Search 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" type: reference diff --git a/doc/user/search/global_search/advanced_search_syntax.md b/doc/user/search/global_search/advanced_search_syntax.md index 219531070b3..9daa7afd6de 100644 --- a/doc/user/search/global_search/advanced_search_syntax.md +++ b/doc/user/search/global_search/advanced_search_syntax.md @@ -1,5 +1,5 @@ --- -stage: Enablement +stage: Data Stores group: Global Search 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 --- diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 6c3fa19e630..24dd8764de5 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -6906,6 +6906,9 @@ msgstr "" msgid "CICDAnalytics|Shared Runners Usage" msgstr "" +msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners" +msgstr "" + msgid "CICDAnalytics|Shared runner pipeline minute duration by month" msgstr "" @@ -6918,6 +6921,9 @@ msgstr "" msgid "CICDAnalytics|Something went wrong while fetching release statistics" msgstr "" +msgid "CICDAnalytics|What is shared runner duration?" +msgstr "" + msgid "CICDAnalytics|What is shared runner usage?" msgstr "" @@ -22053,10 +22059,10 @@ msgstr "" msgid "Job|This job failed because the necessary resources were not successfully created." msgstr "" -msgid "Job|This job is stuck because the project doesn't have any runners online assigned to it." +msgid "Job|This job is stuck because of one of the following problems. There are no active runners online, no runners for the %{linkStart}protected branch%{linkEnd}, or no runners that match all of the job's tags:" msgstr "" -msgid "Job|This job is stuck because you don't have any active runners online or available with any of these tags assigned to them:" +msgid "Job|This job is stuck because the project doesn't have any runners online assigned to it." msgstr "" msgid "Job|This job is stuck because you don't have any active runners that can run this job." @@ -34563,6 +34569,12 @@ msgstr "" msgid "Selected commits" msgstr "" +msgid "Selected for all items." +msgstr "" + +msgid "Selected for some items." +msgstr "" + msgid "Selected levels cannot be used by non-admin users for groups, projects or snippets. If the public level is restricted, user profiles are only visible to logged in users." msgstr "" @@ -40852,7 +40864,7 @@ msgstr "" msgid "UsageQuota|Shared bits of code and text." msgstr "" -msgid "UsageQuota|Shared runner usage" +msgid "UsageQuota|Shared runner duration" msgstr "" msgid "UsageQuota|Snippets" @@ -40873,6 +40885,12 @@ msgstr "" msgid "UsageQuota|Storage used" msgstr "" +msgid "UsageQuota|The table below shows current period usage" +msgstr "" + +msgid "UsageQuota|The table below shows usage since %{timeElapsed}" +msgstr "" + msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace." msgstr "" diff --git a/spec/features/admin/admin_groups_spec.rb b/spec/features/admin/admin_groups_spec.rb index 90dde7340d5..3dc11b806a0 100644 --- a/spec/features/admin/admin_groups_spec.rb +++ b/spec/features/admin/admin_groups_spec.rb @@ -60,8 +60,7 @@ RSpec.describe 'Admin Groups' do expect(page).to have_current_path admin_group_path(Group.find_by(path: path_component)), ignore_query: true content = page.find('#content-body') - h3_texts = content.all('h3').collect(&:text).join("\n") - expect(h3_texts).to match group_name + expect(page).to have_content group_name li_texts = content.all('li').collect(&:text).join("\n") expect(li_texts).to match group_name expect(li_texts).to match path_component diff --git a/spec/features/admin/users/users_spec.rb b/spec/features/admin/users/users_spec.rb index a05e1531949..a59e3330628 100644 --- a/spec/features/admin/users/users_spec.rb +++ b/spec/features/admin/users/users_spec.rb @@ -492,9 +492,7 @@ RSpec.describe 'Admin::Users' do within(:css, '.gl-mb-3 + .card') do click_link group.name end - within(:css, 'h3.page-title') do - expect(page).to have_content "Group: #{group.name}" - end + expect(page).to have_content "Group: #{group.name}" expect(page).to have_content project.name end diff --git a/spec/features/projects/jobs_spec.rb b/spec/features/projects/jobs_spec.rb index befaf85fc1e..f0d41c1dd11 100644 --- a/spec/features/projects/jobs_spec.rb +++ b/spec/features/projects/jobs_spec.rb @@ -987,7 +987,9 @@ RSpec.describe 'Jobs', :clean_gitlab_redis_shared_state do it 'renders message about job being stuck because of no runners with the specified tags' do expect(page).to have_selector('[data-testid="job-stuck-with-tags"') - expect(page).to have_content("This job is stuck because you don't have any active runners online or available with any of these tags assigned to them:") + expect(page).to have_content("This job is stuck because of one of the following problems. There are no active runners online, no runners for the ") + expect(page).to have_content("protected branch") + expect(page).to have_content(", or no runners that match all of the job's tags:") end end @@ -997,7 +999,9 @@ RSpec.describe 'Jobs', :clean_gitlab_redis_shared_state do it 'renders message about job being stuck because of no runners with the specified tags' do expect(page).to have_selector('[data-testid="job-stuck-with-tags"') - expect(page).to have_content("This job is stuck because you don't have any active runners online or available with any of these tags assigned to them:") + expect(page).to have_content("This job is stuck because of one of the following problems. There are no active runners online, no runners for the ") + expect(page).to have_content("protected branch") + expect(page).to have_content(", or no runners that match all of the job's tags:") end end diff --git a/spec/frontend/feature_flags/components/new_feature_flag_spec.js b/spec/frontend/feature_flags/components/new_feature_flag_spec.js index 9c1657bc0d2..688ba54f919 100644 --- a/spec/frontend/feature_flags/components/new_feature_flag_spec.js +++ b/spec/frontend/feature_flags/components/new_feature_flag_spec.js @@ -61,7 +61,7 @@ describe('New feature flag form', () => { }); it('renders form title', () => { - expect(wrapper.find('h3').text()).toEqual('New feature flag'); + expect(wrapper.text()).toContain('New feature flag'); }); it('should render feature flag form', () => { diff --git a/spec/frontend/performance_bar/components/add_request_spec.js b/spec/frontend/performance_bar/components/add_request_spec.js index 5422481439e..627e004ce3e 100644 --- a/spec/frontend/performance_bar/components/add_request_spec.js +++ b/spec/frontend/performance_bar/components/add_request_spec.js @@ -1,12 +1,16 @@ -import { shallowMount } from '@vue/test-utils'; +import { mount } from '@vue/test-utils'; import { nextTick } from 'vue'; +import { GlFormInput, GlButton } from '@gitlab/ui'; import AddRequest from '~/performance_bar/components/add_request.vue'; describe('add request form', () => { let wrapper; + const findGlFormInput = () => wrapper.findComponent(GlFormInput); + const findGlButton = () => wrapper.findComponent(GlButton); + beforeEach(() => { - wrapper = shallowMount(AddRequest); + wrapper = mount(AddRequest); }); afterEach(() => { @@ -14,35 +18,35 @@ describe('add request form', () => { }); it('hides the input on load', () => { - expect(wrapper.find('input').exists()).toBe(false); + expect(findGlFormInput().exists()).toBe(false); }); describe('when clicking the button', () => { beforeEach(async () => { - wrapper.find('button').trigger('click'); + findGlButton().trigger('click'); await nextTick(); }); it('shows the form', () => { - expect(wrapper.find('input').exists()).toBe(true); + expect(findGlFormInput().exists()).toBe(true); }); describe('when pressing escape', () => { beforeEach(async () => { - wrapper.find('input').trigger('keyup.esc'); + findGlFormInput().trigger('keyup.esc'); await nextTick(); }); it('hides the input', () => { - expect(wrapper.find('input').exists()).toBe(false); + expect(findGlFormInput().exists()).toBe(false); }); }); describe('when submitting the form', () => { beforeEach(async () => { - wrapper.find('input').setValue('http://gitlab.example.com/users/root/calendar.json'); + findGlFormInput().setValue('http://gitlab.example.com/users/root/calendar.json'); await nextTick(); - wrapper.find('input').trigger('keyup.enter'); + findGlFormInput().trigger('keyup.enter'); await nextTick(); }); @@ -54,13 +58,13 @@ describe('add request form', () => { }); it('hides the input', () => { - expect(wrapper.find('input').exists()).toBe(false); + expect(findGlFormInput().exists()).toBe(false); }); it('clears the value for next time', async () => { - wrapper.find('button').trigger('click'); + findGlButton().trigger('click'); await nextTick(); - expect(wrapper.find('input').text()).toEqual(''); + expect(findGlFormInput().text()).toEqual(''); }); }); }); diff --git a/spec/frontend/runner/components/runner_jobs_spec.js b/spec/frontend/runner/components/runner_jobs_spec.js index 8ac5685a0dd..20582aaaf40 100644 --- a/spec/frontend/runner/components/runner_jobs_spec.js +++ b/spec/frontend/runner/components/runner_jobs_spec.js @@ -1,4 +1,4 @@ -import { GlDeprecatedSkeletonLoading as GlSkeletonLoading } from '@gitlab/ui'; +import { GlSkeletonLoader } from '@gitlab/ui'; import Vue from 'vue'; import VueApollo from 'vue-apollo'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; @@ -28,7 +28,7 @@ describe('RunnerJobs', () => { let wrapper; let mockRunnerJobsQuery; - const findGlSkeletonLoading = () => wrapper.findComponent(GlSkeletonLoading); + const findGlSkeletonLoading = () => wrapper.findComponent(GlSkeletonLoader); const findRunnerJobsTable = () => wrapper.findComponent(RunnerJobsTable); const findRunnerPagination = () => wrapper.findComponent(RunnerPagination); diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view_spec.js index 42202db4935..00c8e3a814a 100644 --- a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view_spec.js +++ b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view_spec.js @@ -226,12 +226,7 @@ describe('DropdownContentsLabelsView', () => { preventDefault: fakePreventDefault, }); - expect(wrapper.vm.updateSelectedLabels).toHaveBeenCalledWith([ - { - ...mockLabels[2], - set: true, - }, - ]); + expect(wrapper.vm.updateSelectedLabels).toHaveBeenCalledWith([mockLabels[2]]); }); it('calls action `toggleDropdownContents` when Esc key is pressed', () => { diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/label_item_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/label_item_spec.js index bd1705e7693..bedb6204088 100644 --- a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/label_item_spec.js +++ b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/label_item_spec.js @@ -1,4 +1,4 @@ -import { GlIcon, GlLink } from '@gitlab/ui'; +import { GlLink } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; import LabelItem from '~/vue_shared/components/sidebar/labels_select_vue/label_item.vue'; @@ -45,18 +45,26 @@ describe('LabelItem', () => { wrapperTemp.destroy(); }); - it('renders visible gl-icon component when `isLabelSet` prop is true', () => { - const wrapperTemp = createComponent({ - isLabelSet: true, - }); - - const iconEl = wrapperTemp.find(GlIcon); - - expect(iconEl.isVisible()).toBe(true); - expect(iconEl.props('name')).toBe('mobile-issue-close'); - - wrapperTemp.destroy(); - }); + it.each` + isLabelSet | isLabelIndeterminate | testId | iconName + ${true} | ${false} | ${'checked-icon'} | ${'mobile-issue-close'} + ${false} | ${true} | ${'indeterminate-icon'} | ${'dash'} + `( + 'renders visible gl-icon component when `isLabelSet` prop is $isLabelSet and `isLabelIndeterminate` is $isLabelIndeterminate', + ({ isLabelSet, isLabelIndeterminate, testId, iconName }) => { + const wrapperTemp = createComponent({ + isLabelSet, + isLabelIndeterminate, + }); + + const iconEl = wrapperTemp.find(`[data-testid="${testId}"]`); + + expect(iconEl.isVisible()).toBe(true); + expect(iconEl.props('name')).toBe(iconName); + + wrapperTemp.destroy(); + }, + ); it('renders visible span element as placeholder instead of gl-icon when `isLabelSet` prop is false', () => { const wrapperTemp = createComponent({ diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/labels_select_root_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/labels_select_root_spec.js index d5ea1a5acd1..c150410ff8e 100644 --- a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/labels_select_root_spec.js +++ b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/labels_select_root_spec.js @@ -46,9 +46,15 @@ describe('LabelsSelectRoot', () => { describe('methods', () => { describe('handleVuexActionDispatch', () => { + const touchedLabels = [ + { + id: 2, + touched: true, + }, + ]; + it('calls `handleDropdownClose` when params `action.type` is `toggleDropdownContents` and state has `showDropdownButton` & `showDropdownContents` props `false`', () => { createComponent(); - jest.spyOn(wrapper.vm, 'handleDropdownClose').mockImplementation(); wrapper.vm.handleVuexActionDispatch( { type: 'toggleDropdownContents' }, @@ -59,14 +65,12 @@ describe('LabelsSelectRoot', () => { }, ); - expect(wrapper.vm.handleDropdownClose).toHaveBeenCalledWith( - expect.arrayContaining([ - { - id: 2, - touched: true, - }, - ]), - ); + // We're utilizing `onDropdownClose` event emitted from the component to always include `touchedLabels` + // while the first param of the method is the labels list which were added/removed. + expect(wrapper.emitted('updateSelectedLabels')).toBeTruthy(); + expect(wrapper.emitted('updateSelectedLabels')[0]).toEqual([touchedLabels]); + expect(wrapper.emitted('onDropdownClose')).toBeTruthy(); + expect(wrapper.emitted('onDropdownClose')[0]).toEqual([touchedLabels]); }); it('calls `handleDropdownClose` with state.labels filterd using `set` prop when dropdown variant is `embedded`', () => { @@ -75,8 +79,6 @@ describe('LabelsSelectRoot', () => { variant: 'embedded', }); - jest.spyOn(wrapper.vm, 'handleDropdownClose').mockImplementation(); - wrapper.vm.handleVuexActionDispatch( { type: 'toggleDropdownContents' }, { @@ -86,34 +88,17 @@ describe('LabelsSelectRoot', () => { }, ); - expect(wrapper.vm.handleDropdownClose).toHaveBeenCalledWith( - expect.arrayContaining([ + expect(wrapper.emitted('updateSelectedLabels')).toBeTruthy(); + expect(wrapper.emitted('updateSelectedLabels')[0]).toEqual([ + [ { id: 2, set: true, }, - ]), - ); - }); - }); - - describe('handleDropdownClose', () => { - beforeEach(() => { - createComponent(); - }); - - it('emits `updateSelectedLabels` & `onDropdownClose` events on component when provided `labels` param is not empty', () => { - wrapper.vm.handleDropdownClose([{ id: 1 }, { id: 2 }]); - - expect(wrapper.emitted().updateSelectedLabels).toBeTruthy(); - expect(wrapper.emitted().onDropdownClose).toBeTruthy(); - }); - - it('emits only `onDropdownClose` event on component when provided `labels` param is empty', () => { - wrapper.vm.handleDropdownClose([]); - - expect(wrapper.emitted().updateSelectedLabels).toBeFalsy(); - expect(wrapper.emitted().onDropdownClose).toBeTruthy(); + ], + ]); + expect(wrapper.emitted('onDropdownClose')).toBeTruthy(); + expect(wrapper.emitted('onDropdownClose')[0]).toEqual([[]]); }); }); diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/store/getters_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/store/getters_spec.js index 1f899e84897..6ad46dbe898 100644 --- a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/store/getters_spec.js +++ b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/store/getters_spec.js @@ -17,24 +17,39 @@ describe('LabelsSelect Getters', () => { }, ); - it('returns label title when state.labels has only 1 label', () => { - const labels = [{ id: 1, title: 'Foobar', set: true }]; + describe.each` + dropdownVariant | isDropdownVariantSidebar | isDropdownVariantEmbedded + ${'sidebar'} | ${true} | ${false} + ${'embedded'} | ${false} | ${true} + `( + 'when dropdown variant is $dropdownVariant', + ({ isDropdownVariantSidebar, isDropdownVariantEmbedded }) => { + it('returns label title when state.labels has only 1 label', () => { + const labels = [{ id: 1, title: 'Foobar', set: true }]; - expect(getters.dropdownButtonText({ labels }, { isDropdownVariantSidebar: true })).toBe( - 'Foobar', - ); - }); + expect( + getters.dropdownButtonText( + { labels }, + { isDropdownVariantSidebar, isDropdownVariantEmbedded }, + ), + ).toBe('Foobar'); + }); - it('returns first label title and remaining labels count when state.labels has more than 1 label', () => { - const labels = [ - { id: 1, title: 'Foo', set: true }, - { id: 2, title: 'Bar', set: true }, - ]; + it('returns first label title and remaining labels count when state.labels has more than 1 label', () => { + const labels = [ + { id: 1, title: 'Foo', set: true }, + { id: 2, title: 'Bar', set: true }, + ]; - expect(getters.dropdownButtonText({ labels }, { isDropdownVariantSidebar: true })).toBe( - 'Foo +1 more', - ); - }); + expect( + getters.dropdownButtonText( + { labels }, + { isDropdownVariantSidebar, isDropdownVariantEmbedded }, + ), + ).toBe('Foo +1 more'); + }); + }, + ); }); describe('selectedLabelsList', () => { diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/store/mutations_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/store/mutations_spec.js index a60e6f52862..1819e750324 100644 --- a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/store/mutations_spec.js +++ b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/store/mutations_spec.js @@ -80,7 +80,10 @@ describe('LabelsSelect Mutations', () => { }); describe(`${types.RECEIVE_SET_LABELS_SUCCESS}`, () => { - const selectedLabels = [{ id: 2 }, { id: 4 }]; + const selectedLabels = [ + { id: 2, set: true }, + { id: 4, set: true }, + ]; const labels = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }]; it('sets value of `state.labelsFetchInProgress` to false', () => { @@ -196,20 +199,23 @@ describe('LabelsSelect Mutations', () => { it('updates labels `set` state to match selected labels', () => { const state = { labels: [ - { id: 1, title: 'scoped::test', set: false }, - { id: 2, set: true, title: 'scoped::one', touched: true }, - { id: 3, title: '' }, - { id: 4, title: '' }, + { id: 1, title: 'scoped::test', set: false, indeterminate: false }, + { id: 2, title: 'scoped::one', set: true, indeterminate: false, touched: true }, + { id: 3, title: '', set: false, indeterminate: false }, + { id: 4, title: '', set: false, indeterminate: false }, + ], + selectedLabels: [ + { id: 1, set: true }, + { id: 3, set: true }, ], - selectedLabels: [{ id: 1 }, { id: 3 }], }; mutations[types.UPDATE_LABELS_SET_STATE](state); expect(state.labels).toEqual([ - { id: 1, title: 'scoped::test', set: true }, - { id: 2, set: false, title: 'scoped::one', touched: true }, - { id: 3, title: '', set: true }, - { id: 4, title: '', set: false }, + { id: 1, title: 'scoped::test', set: true, indeterminate: false }, + { id: 2, title: 'scoped::one', set: false, indeterminate: false, touched: true }, + { id: 3, title: '', set: true, indeterminate: false }, + { id: 4, title: '', set: false, indeterminate: false }, ]); }); }); diff --git a/spec/graphql/mutations/branches/create_spec.rb b/spec/graphql/mutations/branches/create_spec.rb index e378a8e3d41..9b92753a349 100644 --- a/spec/graphql/mutations/branches/create_spec.rb +++ b/spec/graphql/mutations/branches/create_spec.rb @@ -7,9 +7,10 @@ RSpec.describe Mutations::Branches::Create do let_it_be(:project) { create(:project, :public, :repository) } let_it_be(:user) { create(:user) } - let_it_be(:context) do + + let(:context) do GraphQL::Query::Context.new( - query: OpenStruct.new(schema: nil), + query: double('query', schema: nil), values: { current_user: user }, object: nil ) diff --git a/spec/graphql/mutations/clusters/agent_tokens/create_spec.rb b/spec/graphql/mutations/clusters/agent_tokens/create_spec.rb index 45d421509d0..f60aa16c68c 100644 --- a/spec/graphql/mutations/clusters/agent_tokens/create_spec.rb +++ b/spec/graphql/mutations/clusters/agent_tokens/create_spec.rb @@ -10,7 +10,7 @@ RSpec.describe Mutations::Clusters::AgentTokens::Create do let(:context) do GraphQL::Query::Context.new( - query: OpenStruct.new(schema: nil), + query: double('query', schema: nil), # rubocop:disable RSpec/VerifiedDoubles values: { current_user: user }, object: nil ) diff --git a/spec/graphql/mutations/clusters/agents/create_spec.rb b/spec/graphql/mutations/clusters/agents/create_spec.rb index c80b6f6cdad..62498357d68 100644 --- a/spec/graphql/mutations/clusters/agents/create_spec.rb +++ b/spec/graphql/mutations/clusters/agents/create_spec.rb @@ -9,7 +9,7 @@ RSpec.describe Mutations::Clusters::Agents::Create do let(:user) { create(:user) } let(:context) do GraphQL::Query::Context.new( - query: OpenStruct.new(schema: nil), + query: double('query', schema: nil), # rubocop:disable RSpec/VerifiedDoubles values: { current_user: user }, object: nil ) diff --git a/spec/graphql/mutations/clusters/agents/delete_spec.rb b/spec/graphql/mutations/clusters/agents/delete_spec.rb index e0ecff5fe44..ab01a2d93b8 100644 --- a/spec/graphql/mutations/clusters/agents/delete_spec.rb +++ b/spec/graphql/mutations/clusters/agents/delete_spec.rb @@ -10,7 +10,7 @@ RSpec.describe Mutations::Clusters::Agents::Delete do let(:user) { create(:user) } let(:context) do GraphQL::Query::Context.new( - query: OpenStruct.new(schema: nil), + query: double('query', schema: nil), # rubocop:disable RSpec/VerifiedDoubles values: { current_user: user }, object: nil ) diff --git a/spec/graphql/mutations/commits/create_spec.rb b/spec/graphql/mutations/commits/create_spec.rb index 097e70bada6..456f6dc03f7 100644 --- a/spec/graphql/mutations/commits/create_spec.rb +++ b/spec/graphql/mutations/commits/create_spec.rb @@ -10,7 +10,7 @@ RSpec.describe Mutations::Commits::Create do let(:context) do GraphQL::Query::Context.new( - query: OpenStruct.new(schema: nil), + query: double('query', schema: nil), # rubocop:disable RSpec/VerifiedDoubles values: { current_user: user }, object: nil ) diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb index a077eb4da0b..06b6123b092 100644 --- a/spec/models/namespace_spec.rb +++ b/spec/models/namespace_spec.rb @@ -973,24 +973,6 @@ RSpec.describe Namespace do expect_project_directories_at('parent/renamed', with_pages: false) end end - - context 'when the project has pages deployed' do - before do - project.pages_metadatum.update!(deployed: true) - end - - it 'correctly moves the repository, uploads and pages', :sidekiq_inline do - child.update!(path: 'renamed') - - expect_project_directories_at('parent/renamed') - end - - it 'performs the move async of pages async' do - expect(PagesTransferWorker).to receive(:perform_async).with('rename_namespace', ['parent/child', 'parent/renamed']) - - child.update!(path: 'renamed') - end - end end context 'renaming parent' do @@ -1002,24 +984,6 @@ RSpec.describe Namespace do expect_project_directories_at('renamed/child', with_pages: false) end end - - context 'when the project has pages deployed' do - before do - project.pages_metadatum.update!(deployed: true) - end - - it 'correctly moves the repository, uploads and pages', :sidekiq_inline do - parent.update!(path: 'renamed') - - expect_project_directories_at('renamed/child') - end - - it 'performs the move async of pages async' do - expect(PagesTransferWorker).to receive(:perform_async).with('rename_namespace', %w(parent renamed)) - - parent.update!(path: 'renamed') - end - end end context 'moving from one parent to another' do @@ -1031,24 +995,6 @@ RSpec.describe Namespace do expect_project_directories_at('new_parent/child', with_pages: false) end end - - context 'when the project has pages deployed' do - before do - project.pages_metadatum.update!(deployed: true) - end - - it 'correctly moves the repository, uploads and pages', :sidekiq_inline do - child.update!(parent: new_parent) - - expect_project_directories_at('new_parent/child') - end - - it 'performs the move async of pages async' do - expect(PagesTransferWorker).to receive(:perform_async).with('move_namespace', %w(child parent new_parent)) - - child.update!(parent: new_parent) - end - end end context 'moving from having a parent to root' do @@ -1060,24 +1006,6 @@ RSpec.describe Namespace do expect_project_directories_at('child', with_pages: false) end end - - context 'when the project has pages deployed' do - before do - project.pages_metadatum.update!(deployed: true) - end - - it 'correctly moves the repository, uploads and pages', :sidekiq_inline do - child.update!(parent: nil) - - expect_project_directories_at('child') - end - - it 'performs the move async of pages async' do - expect(PagesTransferWorker).to receive(:perform_async).with('move_namespace', ['child', 'parent', nil]) - - child.update!(parent: nil) - end - end end context 'moving from root to having a parent' do @@ -1089,24 +1017,6 @@ RSpec.describe Namespace do expect_project_directories_at('new_parent/parent/child', with_pages: false) end end - - context 'when the project has pages deployed' do - before do - project.pages_metadatum.update!(deployed: true) - end - - it 'correctly moves the repository, uploads and pages', :sidekiq_inline do - parent.update!(parent: new_parent) - - expect_project_directories_at('new_parent/parent/child') - end - - it 'performs the move async of pages async' do - expect(PagesTransferWorker).to receive(:perform_async).with('move_namespace', ['parent', nil, 'new_parent']) - - parent.update!(parent: new_parent) - end - end end end end diff --git a/spec/services/projects/after_rename_service_spec.rb b/spec/services/projects/after_rename_service_spec.rb index a8db87e48d0..a9329f092fa 100644 --- a/spec/services/projects/after_rename_service_spec.rb +++ b/spec/services/projects/after_rename_service_spec.rb @@ -64,22 +64,11 @@ RSpec.describe Projects::AfterRenameService do allow(project_storage).to receive(:rename_repo) { true } end - context 'when the project has pages deployed' do - it 'schedules a move of the pages directory' do - allow(project).to receive(:pages_deployed?).and_return(true) - - expect(PagesTransferWorker).to receive(:perform_async).with('rename_project', anything) - - service_execute - end - end - context 'when the project does not have pages deployed' do it 'does nothing with the pages directory' do allow(project).to receive(:pages_deployed?).and_return(false) expect(PagesTransferWorker).not_to receive(:perform_async) - expect(Gitlab::PagesTransfer).not_to receive(:new) service_execute end @@ -172,29 +161,6 @@ RSpec.describe Projects::AfterRenameService do end end - context 'gitlab pages' do - context 'when the project has pages deployed' do - it 'schedules a move of the pages directory' do - allow(project).to receive(:pages_deployed?).and_return(true) - - expect(PagesTransferWorker).to receive(:perform_async).with('rename_project', anything) - - service_execute - end - end - - context 'when the project does not have pages deployed' do - it 'does nothing with the pages directory' do - allow(project).to receive(:pages_deployed?).and_return(false) - - expect(PagesTransferWorker).not_to receive(:perform_async) - expect(Gitlab::PagesTransfer).not_to receive(:new) - - service_execute - end - end - end - context 'attachments' do let(:uploader) { create(:upload, :issuable_upload, :with_file, model: project) } let(:file_uploader) { build(:file_uploader, project: project) } diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb index e547ace1d9f..bebe80b710b 100644 --- a/spec/services/projects/transfer_service_spec.rb +++ b/spec/services/projects/transfer_service_spec.rb @@ -53,9 +53,6 @@ RSpec.describe Projects::TransferService do allow_next_instance_of(Gitlab::UploadsTransfer) do |service| allow(service).to receive(:move_project).and_return(true) end - allow_next_instance_of(Gitlab::PagesTransfer) do |service| - allow(service).to receive(:move_project).and_return(true) - end group.add_owner(user) end @@ -725,15 +722,6 @@ RSpec.describe Projects::TransferService do group.add_owner(user) end - it 'schedules a job when pages are deployed' do - project.mark_pages_as_deployed - - expect(PagesTransferWorker).to receive(:perform_async) - .with("move_project", [project.path, user.namespace.full_path, group.full_path]) - - execute_transfer - end - it 'does not schedule a job when no pages are deployed' do expect(PagesTransferWorker).not_to receive(:perform_async) |