diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-08-16 21:10:51 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-08-16 21:10:51 +0300 |
commit | e684f438e60b801b4eb8b19dd9489dce84503c03 (patch) | |
tree | 115e9d137f570580b17825ed13057d663a0761c0 | |
parent | d872c89ce4828a16d72ddeed8695077fdcaf7b30 (diff) |
Add latest changes from gitlab-org/gitlab@master
60 files changed, 786 insertions, 169 deletions
diff --git a/app/assets/javascripts/content_editor/components/toolbar_table_button.vue b/app/assets/javascripts/content_editor/components/toolbar_table_button.vue index 1b394f76641..46db806da94 100644 --- a/app/assets/javascripts/content_editor/components/toolbar_table_button.vue +++ b/app/assets/javascripts/content_editor/components/toolbar_table_button.vue @@ -5,17 +5,17 @@ import { clamp } from '../services/utils'; export const tableContentType = 'table'; -const MIN_ROWS = 3; -const MIN_COLS = 3; -const MAX_ROWS = 8; -const MAX_COLS = 8; +const MIN_ROWS = 5; +const MIN_COLS = 5; +const MAX_ROWS = 10; +const MAX_COLS = 10; export default { components: { + GlButton, GlDropdown, GlDropdownDivider, GlDropdownForm, - GlButton, }, inject: ['tiptapEditor'], data() { @@ -62,22 +62,22 @@ export default { }; </script> <template> - <gl-dropdown size="small" category="tertiary" icon="table"> - <gl-dropdown-form class="gl-px-3! gl-w-auto!"> - <div class="gl-w-auto!"> - <div v-for="r of list(maxRows)" :key="r" class="gl-display-flex"> - <gl-button - v-for="c of list(maxCols)" - :key="c" - :data-testid="`table-${r}-${c}`" - :class="{ 'gl-bg-blue-50!': r <= rows && c <= cols }" - :aria-label="getButtonLabel(r, c)" - class="gl-display-inline! gl-px-0! gl-w-5! gl-h-5! gl-rounded-0!" - @mouseover="setRowsAndCols(r, c)" - @click="insertTable()" - /> - </div> - <gl-dropdown-divider /> + <gl-dropdown size="small" category="tertiary" icon="table" class="table-dropdown"> + <gl-dropdown-form class="gl-px-3!"> + <div v-for="r of list(maxRows)" :key="r" class="gl-display-flex"> + <gl-button + v-for="c of list(maxCols)" + :key="c" + :data-testid="`table-${r}-${c}`" + :class="{ 'active gl-bg-blue-50!': r <= rows && c <= cols }" + :aria-label="getButtonLabel(r, c)" + class="table-creator-grid-item gl-display-inline gl-rounded-0! gl-w-6! gl-h-6! gl-p-0!" + @mouseover="setRowsAndCols(r, c)" + @click="insertTable()" + /> + </div> + <gl-dropdown-divider class="gl-my-3! gl-mx-n3!" /> + <div class="gl-px-1"> {{ getButtonLabel(rows, cols) }} </div> </gl-dropdown-form> diff --git a/app/assets/javascripts/integrations/overrides/api.js b/app/assets/javascripts/integrations/overrides/api.js new file mode 100644 index 00000000000..a379a864f9c --- /dev/null +++ b/app/assets/javascripts/integrations/overrides/api.js @@ -0,0 +1,10 @@ +import axios from '~/lib/utils/axios_utils'; + +export const fetchOverrides = (overridesPath, { page, perPage }) => { + return axios.get(overridesPath, { + params: { + page, + per_page: perPage, + }, + }); +}; diff --git a/app/assets/javascripts/integrations/overrides/components/integration_overrides.vue b/app/assets/javascripts/integrations/overrides/components/integration_overrides.vue index bfb16779854..707ac946b98 100644 --- a/app/assets/javascripts/integrations/overrides/components/integration_overrides.vue +++ b/app/assets/javascripts/integrations/overrides/components/integration_overrides.vue @@ -1,15 +1,127 @@ <script> +import { GlLink, GlLoadingIcon, GlPagination, GlTable } from '@gitlab/ui'; + +import { DEFAULT_PER_PAGE } from '~/api'; +import createFlash from '~/flash'; +import { fetchOverrides } from '~/integrations/overrides/api'; +import { parseIntPagination, normalizeHeaders } from '~/lib/utils/common_utils'; +import { truncateNamespace } from '~/lib/utils/text_utility'; +import { __, s__ } from '~/locale'; +import ProjectAvatar from '~/vue_shared/components/project_avatar.vue'; + export default { name: 'IntegrationOverrides', + components: { + GlLink, + GlLoadingIcon, + GlPagination, + GlTable, + ProjectAvatar, + }, props: { overridesPath: { type: String, required: true, }, }, + fields: [ + { + key: 'name', + label: __('Project'), + }, + ], + data() { + return { + isLoading: true, + overrides: [], + page: 1, + totalItems: 0, + }; + }, + computed: { + showPagination() { + return this.totalItems > this.$options.DEFAULT_PER_PAGE && this.overrides.length > 0; + }, + }, + mounted() { + this.loadOverrides(); + }, + methods: { + loadOverrides(page = this.page) { + this.isLoading = true; + + fetchOverrides(this.overridesPath, { + page, + perPage: this.$options.DEFAULT_PER_PAGE, + }) + .then(({ data, headers }) => { + const { page: newPage, total } = parseIntPagination(normalizeHeaders(headers)); + this.page = newPage; + this.totalItems = total; + this.overrides = data; + }) + .catch((error) => { + createFlash({ + message: this.$options.i18n.defaultErrorMessage, + error, + captureError: true, + }); + }) + .finally(() => { + this.isLoading = false; + }); + }, + truncateNamespace, + }, + DEFAULT_PER_PAGE, + i18n: { + defaultErrorMessage: s__( + 'Integrations|An error occurred while loading projects using custom settings.', + ), + tableEmptyText: s__('Integrations|There are no projects using custom settings'), + }, }; </script> <template> - <div></div> + <div> + <gl-table + :items="overrides" + :fields="$options.fields" + :busy="isLoading" + show-empty + :empty-text="$options.i18n.tableEmptyText" + > + <template #cell(name)="{ item }"> + <gl-link + class="gl-display-inline-flex gl-align-items-center gl-hover-text-decoration-none gl-text-body!" + :href="item.full_path" + > + <project-avatar + class="gl-mr-3" + :project-avatar-url="item.avatar_url" + :project-name="item.name" + aria-hidden="true" + /> + {{ truncateNamespace(item.full_name) }} / + + <strong>{{ item.name }}</strong> + </gl-link> + </template> + + <template #table-busy> + <gl-loading-icon size="md" class="gl-my-2" /> + </template> + </gl-table> + <div class="gl-display-flex gl-justify-content-center gl-mt-5"> + <gl-pagination + v-if="showPagination" + :per-page="$options.DEFAULT_PER_PAGE" + :total-items="totalItems" + :value="page" + :disabled="isLoading" + @input="loadOverrides" + /> + </div> + </div> </template> diff --git a/app/assets/javascripts/jobs/components/stages_dropdown.vue b/app/assets/javascripts/jobs/components/stages_dropdown.vue index 36b0ad43b14..1780afd39e8 100644 --- a/app/assets/javascripts/jobs/components/stages_dropdown.vue +++ b/app/assets/javascripts/jobs/components/stages_dropdown.vue @@ -2,10 +2,12 @@ import { GlLink, GlDropdown, GlDropdownItem } from '@gitlab/ui'; import { isEmpty } from 'lodash'; import CiIcon from '~/vue_shared/components/ci_icon.vue'; +import clipboardButton from '~/vue_shared/components/clipboard_button.vue'; export default { components: { CiIcon, + clipboardButton, GlDropdown, GlDropdownItem, GlLink, @@ -45,7 +47,7 @@ export default { <template> <div class="dropdown"> <div class="js-pipeline-info" data-testid="pipeline-info"> - <ci-icon :status="pipeline.details.status" class="vertical-align-middle" /> + <ci-icon :status="pipeline.details.status" /> <span class="font-weight-bold">{{ s__('Job|Pipeline') }}</span> <gl-link @@ -85,7 +87,14 @@ export default { </template> <gl-link v-else :href="pipeline.ref.path" class="link-commit ref-name">{{ pipeline.ref.name - }}</gl-link> + }}</gl-link + ><clipboard-button + :text="pipeline.ref.name" + :title="__('Copy reference')" + category="tertiary" + size="small" + data-testid="copy-source-ref-link" + /> </template> </div> diff --git a/app/assets/javascripts/notes/components/comment_field_layout.vue b/app/assets/javascripts/notes/components/comment_field_layout.vue index 47d14783d5d..9638c20e28c 100644 --- a/app/assets/javascripts/notes/components/comment_field_layout.vue +++ b/app/assets/javascripts/notes/components/comment_field_layout.vue @@ -14,6 +14,11 @@ export default { type: Object, required: true, }, + noteIsConfidential: { + type: Boolean, + required: false, + default: false, + }, noteableType: { type: String, required: false, @@ -38,6 +43,9 @@ export default { emailParticipants() { return this.noteableData.issue_email_participants?.map(({ email }) => email) || []; }, + showEmailParticipantsWarning() { + return this.emailParticipants.length && !this.noteIsConfidential; + }, }, }; </script> @@ -61,7 +69,7 @@ export default { /> <slot></slot> <email-participants-warning - v-if="emailParticipants.length" + v-if="showEmailParticipantsWarning" class="gl-border-t-1 gl-border-t-solid gl-border-t-gray-100 gl-rounded-base gl-rounded-top-left-none! gl-rounded-top-right-none!" :emails="emailParticipants" /> diff --git a/app/assets/javascripts/notes/components/comment_form.vue b/app/assets/javascripts/notes/components/comment_form.vue index 9504ed78778..2ebebd76e1e 100644 --- a/app/assets/javascripts/notes/components/comment_form.vue +++ b/app/assets/javascripts/notes/components/comment_form.vue @@ -380,6 +380,7 @@ export default { <comment-field-layout :with-alert-container="true" :noteable-data="getNoteableData" + :note-is-confidential="noteIsConfidential" :noteable-type="noteableType" > <markdown-field diff --git a/app/assets/javascripts/notes/components/note_form.vue b/app/assets/javascripts/notes/components/note_form.vue index 4ce81219f11..f2336e1b6f5 100644 --- a/app/assets/javascripts/notes/components/note_form.vue +++ b/app/assets/javascripts/notes/components/note_form.vue @@ -326,7 +326,10 @@ export default { ></div> <div class="flash-container timeline-content"></div> <form :data-line-code="lineCode" class="edit-note common-note-form js-quick-submit gfm-form"> - <comment-field-layout :noteable-data="getNoteableData"> + <comment-field-layout + :noteable-data="getNoteableData" + :note-is-confidential="discussion.confidential" + > <markdown-field :markdown-preview-path="markdownPreviewPath" :markdown-docs-path="markdownDocsPath" diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_related_links.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_related_links.vue index 43317130b08..ac6368a3025 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_related_links.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_related_links.vue @@ -1,6 +1,6 @@ <script> /* eslint-disable vue/no-v-html */ -import { s__ } from '~/locale'; +import { s__, n__ } from '~/locale'; export default { name: 'MRWidgetRelatedLinks', @@ -24,7 +24,8 @@ export default { if (this.state === 'closed') { return s__('mrWidget|Did not close'); } - return s__('mrWidget|Closes'); + + return n__('mrWidget|Closes issue', 'mrWidget|Closes issues', this.relatedLinks.closingCount); }, }, }; @@ -33,7 +34,8 @@ export default { <section class="mr-info-list gl-ml-7 gl-pb-5"> <p v-if="relatedLinks.closing">{{ closesText }} <span v-html="relatedLinks.closing"></span></p> <p v-if="relatedLinks.mentioned"> - {{ s__('mrWidget|Mentions') }} <span v-html="relatedLinks.mentioned"></span> + {{ n__('mrWidget|Mentions issue', 'mrWidget|Mentions issues', relatedLinks.mentionedCount) }} + <span v-html="relatedLinks.mentioned"></span> </p> <p v-if="relatedLinks.assignToMe"><span v-html="relatedLinks.assignToMe"></span></p> </section> diff --git a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js index 035fd51d8e3..8979fe621ac 100644 --- a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js +++ b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js @@ -72,7 +72,13 @@ export default class MergeRequestStore { const assignToMe = links.assign_to_closing; if (closing || mentioned || assignToMe) { - this.relatedLinks = { closing, mentioned, assignToMe }; + this.relatedLinks = { + closing, + mentioned, + assignToMe, + closingCount: links.closing_count, + mentionedCount: links.mentioned_count, + }; } } diff --git a/app/assets/stylesheets/components/content_editor.scss b/app/assets/stylesheets/components/content_editor.scss index ed352b2cc76..64abf5574fa 100644 --- a/app/assets/stylesheets/components/content_editor.scss +++ b/app/assets/stylesheets/components/content_editor.scss @@ -35,3 +35,22 @@ } } } + +.table-creator-grid-item { + box-shadow: inset 0 0 0 $gl-spacing-scale-2 $white, + inset $gl-spacing-scale-1 $gl-spacing-scale-1 0 #{$gl-spacing-scale-2 * 3 / 4} $gray-100, + inset #{-$gl-spacing-scale-1} #{-$gl-spacing-scale-1} 0 #{$gl-spacing-scale-2 * 3 / 4} $gray-100 !important; + + &.active { + box-shadow: inset 0 0 0 $gl-spacing-scale-2 $white, + inset $gl-spacing-scale-1 $gl-spacing-scale-1 0 $gl-spacing-scale-2 $blue-500, + inset #{-$gl-spacing-scale-1} #{-$gl-spacing-scale-1} 0 $gl-spacing-scale-2 $blue-500 !important; + } +} + +.table-dropdown .dropdown-menu { + @include gl-min-w-0; + @include gl-w-auto; + + @include gl-white-space-nowrap; +} diff --git a/app/controllers/admin/integrations_controller.rb b/app/controllers/admin/integrations_controller.rb index 108c7068fe2..50f97deb99d 100644 --- a/app/controllers/admin/integrations_controller.rb +++ b/app/controllers/admin/integrations_controller.rb @@ -26,8 +26,4 @@ class Admin::IntegrationsController < Admin::ApplicationController def find_or_initialize_non_project_specific_integration(name) Integration.find_or_initialize_non_project_specific_integration(name, instance: true) end - - def instance_level_integration_overrides? - Feature.enabled?(:instance_level_integration_overrides, default_enabled: :yaml) - end end diff --git a/app/helpers/clusters_helper.rb b/app/helpers/clusters_helper.rb index e9a75babb97..c355fa5cc67 100644 --- a/app/helpers/clusters_helper.rb +++ b/app/helpers/clusters_helper.rb @@ -24,7 +24,8 @@ module ClustersHelper agent_docs_url: help_page_path('user/clusters/agent/index'), install_docs_url: help_page_path('administration/clusters/kas'), get_started_docs_url: help_page_path('user/clusters/agent/index', anchor: 'define-a-configuration-repository'), - integration_docs_url: help_page_path('user/clusters/agent/index', anchor: 'get-started-with-gitops-and-the-gitlab-agent') + integration_docs_url: help_page_path('user/clusters/agent/index', anchor: 'get-started-with-gitops-and-the-gitlab-agent'), + kas_address: Gitlab::Kas.external_url } end diff --git a/app/helpers/integrations_helper.rb b/app/helpers/integrations_helper.rb index 734820f0e74..d06746c06e3 100644 --- a/app/helpers/integrations_helper.rb +++ b/app/helpers/integrations_helper.rb @@ -125,6 +125,17 @@ module IntegrationsHelper !Gitlab.com? end + def integration_tabs(integration:) + [ + { key: 'edit', text: _('Settings'), href: scoped_edit_integration_path(integration) }, + ({ key: 'overrides', text: s_('Integrations|Projects using custom settings'), href: scoped_overrides_integration_path(integration) } if instance_level_integration_overrides?) + ].compact + end + + def instance_level_integration_overrides? + Feature.enabled?(:instance_level_integration_overrides, default_enabled: :yaml) + end + def jira_issue_breadcrumb_link(issue_reference) link_to '', { class: 'gl-display-flex gl-align-items-center gl-white-space-nowrap' } do icon = image_tag image_path('illustrations/logos/jira.svg'), width: 15, height: 15, class: 'gl-mr-2' diff --git a/app/presenters/merge_request_presenter.rb b/app/presenters/merge_request_presenter.rb index 6bf5e0bd1b0..fc8a290f5f7 100644 --- a/app/presenters/merge_request_presenter.rb +++ b/app/presenters/merge_request_presenter.rb @@ -139,7 +139,6 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated end def mentioned_issues_links - mentioned_issues = issues_mentioned_but_not_closing(current_user) markdown( issues_sentence(project, mentioned_issues), pipeline: :gfm, @@ -239,6 +238,18 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated APPROVALS_WIDGET_BASE_TYPE end + def closing_issues + strong_memoize(:closing_issues) do + visible_closing_issues_for(current_user) + end + end + + def mentioned_issues + strong_memoize(:mentioned_issues) do + issues_mentioned_but_not_closing(current_user) + end + end + private def cached_can_be_reverted? @@ -253,10 +264,6 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated # rubocop: enable CodeReuse/ServiceClass end - def closing_issues - @closing_issues ||= visible_closing_issues_for(current_user) - end - def pipeline @pipeline ||= actual_head_pipeline end diff --git a/app/serializers/merge_request_widget_entity.rb b/app/serializers/merge_request_widget_entity.rb index 232e14fad1c..1c033dee5ff 100644 --- a/app/serializers/merge_request_widget_entity.rb +++ b/app/serializers/merge_request_widget_entity.rb @@ -110,9 +110,17 @@ class MergeRequestWidgetEntity < Grape::Entity presenter(merge_request).closing_issues_links end + expose :closing_count do |merge_request| + presenter(merge_request).closing_issues.size + end + expose :mentioned_but_not_closing do |merge_request| presenter(merge_request).mentioned_issues_links end + + expose :mentioned_count do |merge_request| + presenter(merge_request).mentioned_issues.size + end end expose :codeclimate, if: -> (mr, _) { head_pipeline_downloadable_path_for_report_type(:codequality) } do diff --git a/app/views/layouts/nav/sidebar/_group_menus.html.haml b/app/views/layouts/nav/sidebar/_group_menus.html.haml index 8a6183f8efe..a31d61a3eaf 100644 --- a/app/views/layouts/nav/sidebar/_group_menus.html.haml +++ b/app/views/layouts/nav/sidebar/_group_menus.html.haml @@ -1,6 +1,3 @@ -- if group_sidebar_link?(:wiki) - = render 'layouts/nav/sidebar/wiki_link', wiki_url: @group.wiki.web_url - - if group_sidebar_link?(:settings) = nav_link(path: group_settings_nav_link_paths) do = link_to edit_group_path(@group), class: 'has-sub-items' do diff --git a/app/views/layouts/nav/sidebar/_wiki_link.html.haml b/app/views/layouts/nav/sidebar/_wiki_link.html.haml deleted file mode 100644 index b6b63b75fcc..00000000000 --- a/app/views/layouts/nav/sidebar/_wiki_link.html.haml +++ /dev/null @@ -1,11 +0,0 @@ -= nav_link(controller: :wikis) do - = link_to wiki_url, class: 'shortcuts-wiki', data: { qa_selector: 'wiki_link' } do - .nav-icon-container - = sprite_icon('book') - %span.nav-item-name - = _('Wiki') - %ul.sidebar-sub-level-items.is-fly-out-only - = nav_link(controller: :wikis, html_options: { class: "fly-out-top-item" } ) do - = link_to wiki_url do - %strong.fly-out-top-item-name - = _('Wiki') diff --git a/app/views/shared/integrations/_form.html.haml b/app/views/shared/integrations/_form.html.haml index 62f8d986296..35f302a28a6 100644 --- a/app/views/shared/integrations/_form.html.haml +++ b/app/views/shared/integrations/_form.html.haml @@ -1,7 +1,4 @@ - integration = local_assigns.fetch(:integration) -%h3.page-title - = integration.title - = form_for integration, as: :service, url: scoped_integration_path(integration), method: :put, html: { class: 'gl-show-field-errors integration-settings-form js-integration-settings-form', data: { 'test-url' => scoped_test_integration_path(integration) } } do |form| = render 'shared/service_settings', form: form, integration: integration diff --git a/app/views/shared/integrations/_tabs.html.haml b/app/views/shared/integrations/_tabs.html.haml index ff97e371374..553401e47bd 100644 --- a/app/views/shared/integrations/_tabs.html.haml +++ b/app/views/shared/integrations/_tabs.html.haml @@ -1,14 +1,18 @@ -.tabs.gl-tabs - %div - %ul.nav.gl-tabs-nav{ role: 'tablist' } - %li.nav-item{ role: 'presentation' } - %a.nav-link.gl-tab-nav-item{ role: 'tab', href: scoped_edit_integration_path(integration) } - = _('Settings') +- active_tab = local_assigns.fetch(:active_tab, 'edit') +- active_classes = 'gl-tab-nav-item-active gl-tab-nav-item-active-indigo active' +- tabs = integration_tabs(integration: integration) - %li.nav-item{ role: 'presentation' } - %a.nav-link.gl-tab-nav-item.gl-tab-nav-item-active.gl-tab-nav-item-active-indigo.active{ role: 'tab', href: scoped_overrides_integration_path(integration) } - = s_('Integrations|Projects using custom settings') +- if tabs.length <= 1 + = yield +- else + .tabs.gl-tabs + %div + %ul.nav.gl-tabs-nav{ role: 'tablist' } + - tabs.each do |tab| + %li.nav-item{ role: 'presentation' } + %a.nav-link.gl-tab-nav-item{ role: 'tab', class: (active_classes if tab[:key] == active_tab), href: tab[:href] } + = tab[:text] - .tab-content.gl-tab-content - .tab-pane.active{ role: 'tabpanel' } - = yield + .tab-content.gl-tab-content + .tab-pane.gl-pt-3.active{ role: 'tabpanel' } + = yield diff --git a/app/views/shared/integrations/edit.html.haml b/app/views/shared/integrations/edit.html.haml index a996f72e2f4..02cb94e3555 100644 --- a/app/views/shared/integrations/edit.html.haml +++ b/app/views/shared/integrations/edit.html.haml @@ -3,4 +3,8 @@ - page_title @integration.title, _('Integrations') - @content_class = 'limit-container-width' unless fluid_layout -= render 'shared/integrations/form', integration: @integration +%h3.page-title + = @integration.title + += render 'shared/integrations/tabs', integration: @integration, active_tab: 'edit' do + = render 'shared/integrations/form', integration: @integration diff --git a/app/views/shared/integrations/overrides.html.haml b/app/views/shared/integrations/overrides.html.haml index 4d8cc94e967..dc87fae704c 100644 --- a/app/views/shared/integrations/overrides.html.haml +++ b/app/views/shared/integrations/overrides.html.haml @@ -6,5 +6,5 @@ %h3.page-title = @integration.title -= render 'shared/integrations/tabs', integration: @integration do += render 'shared/integrations/tabs', integration: @integration, active_tab: 'overrides' do .js-vue-integration-overrides{ data: integration_overrides_data(@integration) } diff --git a/config/application.rb b/config/application.rb index 2ef2ead8a8e..06a5a726d92 100644 --- a/config/application.rb +++ b/config/application.rb @@ -434,6 +434,21 @@ module Gitlab end end + # Load JH initializers under JH. Load ordering is: + # 1. prepend_helpers_path + # 2. before_zeitwerk + # 3. let_zeitwerk_take_over + # 4. move_initializers + # 5. load_config_initializers + # 6. load_jh_config_initializers + Gitlab.jh do + initializer :load_jh_config_initializers, after: :load_config_initializers do + Dir[Rails.root.join('jh/config/initializers/*.rb')].sort.each do |initializer| + load_config_initializer(initializer) + end + end + end + # Add assets for variants of GitLab. They should take precedence over CE. # This means if multiple files exist, e.g.: # diff --git a/db/post_migrate/20210805192450_update_trial_plans_ci_daily_pipeline_schedule_triggers.rb b/db/post_migrate/20210805192450_update_trial_plans_ci_daily_pipeline_schedule_triggers.rb new file mode 100644 index 00000000000..9552058dd73 --- /dev/null +++ b/db/post_migrate/20210805192450_update_trial_plans_ci_daily_pipeline_schedule_triggers.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +class UpdateTrialPlansCiDailyPipelineScheduleTriggers < ActiveRecord::Migration[6.1] + include Gitlab::Database::MigrationHelpers + + PREMIUM_TRIAL = 'premium_trial' + ULTIMATE_TRIAL = 'ultimate_trial' + EVERY_5_MINUTES = (1.day.in_minutes / 5).to_i + + class Plan < ActiveRecord::Base + self.table_name = 'plans' + self.inheritance_column = :_type_disabled + + has_one :limits, class_name: 'PlanLimits' + end + + class PlanLimits < ActiveRecord::Base + self.table_name = 'plan_limits' + self.inheritance_column = :_type_disabled + + belongs_to :plan + end + + def plan_limits_present? + premium_trial_plan = Plan.find_by(name: PREMIUM_TRIAL) + ultimate_trial_plan = Plan.find_by(name: ULTIMATE_TRIAL) + + premium_trial_plan && premium_trial_plan.limits && ultimate_trial_plan && ultimate_trial_plan.limits + end + + def up + return unless Gitlab.dev_env_or_com? + + if plan_limits_present? + create_or_update_plan_limit('ci_daily_pipeline_schedule_triggers', PREMIUM_TRIAL, EVERY_5_MINUTES) + create_or_update_plan_limit('ci_daily_pipeline_schedule_triggers', ULTIMATE_TRIAL, EVERY_5_MINUTES) + end + end + + def down + return unless Gitlab.dev_env_or_com? + + if plan_limits_present? + create_or_update_plan_limit('ci_daily_pipeline_schedule_triggers', PREMIUM_TRIAL, 0) + create_or_update_plan_limit('ci_daily_pipeline_schedule_triggers', ULTIMATE_TRIAL, 0) + end + end +end diff --git a/db/schema_migrations/20210805192450 b/db/schema_migrations/20210805192450 new file mode 100644 index 00000000000..1b9ae3880e0 --- /dev/null +++ b/db/schema_migrations/20210805192450 @@ -0,0 +1 @@ +a63f878d89269eb8a2a3cc3b0c81d700861031a079a4a69b56d45d73c4c7946e
\ No newline at end of file diff --git a/doc/.vale/gitlab/spelling-exceptions.txt b/doc/.vale/gitlab/spelling-exceptions.txt index f9db0f99973..43ff584b550 100644 --- a/doc/.vale/gitlab/spelling-exceptions.txt +++ b/doc/.vale/gitlab/spelling-exceptions.txt @@ -14,6 +14,7 @@ anonymized Ansible Anthos Apdex +Apparmor approvers architected architecting @@ -28,6 +29,7 @@ Atlassian auditability Auth0 Authentiq +Authy autocomplete autocompleted autocompletes @@ -186,6 +188,7 @@ enum enums ETag Excon +exfiltration expirable Facebook failover @@ -202,6 +205,7 @@ Filebeat Fio firewalled firewalling +fixup Flawfinder Flowdock Fluentd @@ -246,6 +250,7 @@ Haswell heatmap heatmaps Helm +Helmfile Heroku Herokuish Hexo diff --git a/doc/administration/geo/replication/disable_geo.md b/doc/administration/geo/replication/disable_geo.md index ba01c55a157..485a5ee1950 100644 --- a/doc/administration/geo/replication/disable_geo.md +++ b/doc/administration/geo/replication/disable_geo.md @@ -35,6 +35,7 @@ to do that. To remove the **primary** site: +1. [Remove all secondary Geo sites](remove_geo_site.md) 1. On the top bar, select **Menu >** **{admin}** **Admin**. 1. On the left sidebar, select **Geo > Nodes**. 1. Select **Remove** for the **primary** node. diff --git a/doc/administration/geo/replication/version_specific_updates.md b/doc/administration/geo/replication/version_specific_updates.md index e193fc630b9..155c06f90b8 100644 --- a/doc/administration/geo/replication/version_specific_updates.md +++ b/doc/administration/geo/replication/version_specific_updates.md @@ -11,6 +11,16 @@ Review this page for update instructions for your version. These steps accompany the [general steps](updating_the_geo_nodes.md#general-update-steps) for updating Geo nodes. +## Updating to GitLab 14.0/14.1 + +We found an issue where [Primary sites can not be removed from the UI](https://gitlab.com/gitlab-org/gitlab/-/issues/338231). + +This bug only exists in the UI and does not block the removal of Primary sites using any other method. + +### If you have already updated to an affected version and need to remove your Primary site + +You can manually remove the Primary site by using the [Geo Nodes API](../../../api/geo_nodes.md#delete-a-geo-node). + ## Updating to GitLab 13.12 We found an issue where [secondary nodes re-download all LFS files](https://gitlab.com/gitlab-org/gitlab/-/issues/334550) upon update. This bug: diff --git a/doc/administration/get_started.md b/doc/administration/get_started.md index ac717769d4f..6fe66aa1642 100644 --- a/doc/administration/get_started.md +++ b/doc/administration/get_started.md @@ -146,7 +146,7 @@ Backups of GitLab databases and filesystems are taken every 24 hours, and are ke - GitLab SaaS creates backups to ensure your data is secure, but you can't use these methods to export or back up your data yourself. - Issues are stored in the database. They can't be stored in Git itself. - You can use the project export option in: - - [The UI](../user/project/settings/import_export.md#exporting-a-project-and-its-data). + - [The UI](../user/project/settings/import_export.md#export-a-project-and-its-data). - [The API](../api/project_import_export.md#schedule-an-export). - [Group export](../user/group/settings/import_export.md) does *not* export the projects in it, but does export: - Epics diff --git a/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md b/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md index 9fccc280677..ee6e308d443 100644 --- a/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md +++ b/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md @@ -466,7 +466,7 @@ sudo gitlab-rake cache:clear ### Export a repository -It's typically recommended to export a project through [the web interface](../../user/project/settings/import_export.md#exporting-a-project-and-its-data) or through [the API](../../api/project_import_export.md). In situations where this is not working as expected, it may be preferable to export a project directly via the Rails console: +It's typically recommended to export a project through [the web interface](../../user/project/settings/import_export.md#export-a-project-and-its-data) or through [the API](../../api/project_import_export.md). In situations where this is not working as expected, it may be preferable to export a project directly via the Rails console: ```ruby user = User.find_by_username('USERNAME') diff --git a/doc/api/repositories.md b/doc/api/repositories.md index 0ef305d0240..9d464c94d99 100644 --- a/doc/api/repositories.md +++ b/doc/api/repositories.md @@ -112,8 +112,8 @@ Supported attributes: | Attribute | Type | Required | Description | | :-------- | :------- | :------- | :---------- | -| `id` | datatype | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user. | -| `sha` | datatype | yes | The blob SHA. | +| `id` | integer or string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user. | +| `sha` | string | yes | The blob SHA. | ## Get file archive diff --git a/doc/development/import_project.md b/doc/development/import_project.md index 71d8f8b34b9..69e5873cd87 100644 --- a/doc/development/import_project.md +++ b/doc/development/import_project.md @@ -16,7 +16,7 @@ There are several ways to import a project. ### Importing via UI -The first option is to simply [import the Project tarball file via the GitLab UI](../user/project/settings/import_export.md#importing-the-project): +The first option is to simply [import the Project tarball file via the GitLab UI](../user/project/settings/import_export.md#import-the-project): 1. Create the group `qa-perf-testing` 1. Import the [GitLab FOSS project tarball](https://gitlab.com/gitlab-org/quality/performance-data/-/blob/master/projects_export/gitlabhq_export.tar.gz) into the Group. diff --git a/doc/operations/incident_management/paging.md b/doc/operations/incident_management/paging.md index db419001343..6fdf880783a 100644 --- a/doc/operations/incident_management/paging.md +++ b/doc/operations/incident_management/paging.md @@ -27,10 +27,11 @@ Email notifications are available in projects for triggered alerts. Project members with the **Owner** or **Maintainer** roles have the option to receive a single email notification for new alerts. -1. Navigate to **Settings > Monitor**. -1. Expand the **Alerts** section. -1. In the **Integration settings** tab, select the checkbox - **Send a single email notification to Owners and Maintainers for new alerts**. +1. On the top bar, select **Menu > Projects** and find your project. +1. On the left sidebar, select **Settings > Monitor**. +1. Expand **Alerts**. +1. On the **Alert settings** tab, select the + **Send a single email notification to Owners and Maintainers for new alerts** checkbox. 1. Select **Save changes**. ## Paging **(PREMIUM)** diff --git a/doc/operations/incident_management/status_page.md b/doc/operations/incident_management/status_page.md index d14e4120511..db7b55424dd 100644 --- a/doc/operations/incident_management/status_page.md +++ b/doc/operations/incident_management/status_page.md @@ -39,19 +39,23 @@ To configure a GitLab Status Page you must: Only AWS S3 is supported as a deploy target. +Prerequisite: + +- You must have at least the Maintainer [role](../../user/permissions.md). + To provide GitLab with the AWS account information needed to push content to your Status Page: -1. Sign into GitLab as a user with Maintainer or greater [permissions](../../user/permissions.md). -1. Navigate to **{settings}** **Settings > Monitor**. Next to **Status Page**, - click **Expand**. -1. Click **Active** to enable the Status Page feature. -1. In **Status Page URL**, provide the URL to your external status page. -1. Provide the **S3 Bucket name**. For more information, see +1. On the top bar, select **Menu > Projects** and find your project. +1. On the left sidebar, select **Settings > Monitor**. +1. Expand **Status Page**. +1. Select the **Active** checkbox. +1. In the **Status Page URL** box, provide the URL for your external status page. +1. In the **S3 Bucket name** box, type the name of your S3 bucket. For more information, see [Bucket configuration documentation](https://docs.aws.amazon.com/AmazonS3/latest/dev/HostingWebsiteOnS3Setup.html). -1. Provide the **AWS region** for your bucket. For more information, see the +1. In the **AWS region** box, type the region for your bucket. For more information, see the [AWS documentation](https://github.com/aws/aws-sdk-ruby#configuration). -1. Provide your **AWS access key ID** and **AWS Secret access key**. -1. Click **Save changes**. +1. Enter your **AWS access key ID** and **AWS Secret access key**. +1. Select **Save changes**. ### Configure your AWS account @@ -69,8 +73,8 @@ the necessary CI/CD variables to deploy the Status Page to AWS S3: 1. Fork the [Status Page](https://gitlab.com/gitlab-org/status-page) project. You can do this through [Repository Mirroring](https://gitlab.com/gitlab-org/status-page#repository-mirroring), which ensures you get the up-to-date Status Page features. -1. Navigate to **{settings}** **Settings > CI/CD**. -1. Scroll to **Variables**, and click **Expand**. +1. On the left sidebar, select **Settings > CI/CD**. +1. Expand **Variables**. 1. Add the following variables from your Amazon Console: - `S3_BUCKET_NAME` - The name of the Amazon S3 bucket. If no bucket with the provided name exists, the first pipeline run creates @@ -80,8 +84,8 @@ the necessary CI/CD variables to deploy the Status Page to AWS S3: - `AWS_DEFAULT_REGION` - The AWS region. - `AWS_ACCESS_KEY_ID` - The AWS access key ID. - `AWS_SECRET_ACCESS_KEY` - The AWS secret. -1. Navigate to **CI/CD > Pipelines > Run Pipeline**, and run the pipeline to - deploy the Status Page to S3. +1. On the left sidebar, select **CI/CD > Pipelines**. +1. To deploy the Status Page to S3, select **Run pipeline**. WARNING: Consider limiting who can access issues in this project, as any user who can view @@ -92,7 +96,9 @@ the issue can potentially [publish comments to your GitLab Status Page](#publish After creating the CI/CD variables, configure the Project you want to use for Incident issues: -1. To view the Status Page settings, navigate to **{settings}** **Settings > Monitor > Status Page**. +1. On the top bar, select **Menu > Projects** and find your project. +1. On the left sidebar, select **Settings > Monitor**. +1. Expand **Status page**. 1. Fill in your cloud provider's credentials and make sure to select the **Active** checkbox. 1. Select **Save changes**. diff --git a/doc/user/admin_area/analytics/dev_ops_report.md b/doc/user/admin_area/analytics/dev_ops_report.md index 5a335718c6e..f07ccc11c60 100644 --- a/doc/user/admin_area/analytics/dev_ops_report.md +++ b/doc/user/admin_area/analytics/dev_ops_report.md @@ -44,7 +44,7 @@ feature is available. > - DAST and SAST metrics [added](https://gitlab.com/gitlab-org/gitlab/-/issues/328033) in GitLab 14.1. > - Fuzz Testing metrics [added](https://gitlab.com/gitlab-org/gitlab/-/issues/330398) in GitLab 14.2. > - Dependency Scanning metrics [added](https://gitlab.com/gitlab-org/gitlab/-/issues/328034) in GitLab 14.2. -> - Multiselect [added](https://gitlab.com/gitlab-org/gitlab/-/issues/333586) in GitLab 14.2. +> - Multi-select [added](https://gitlab.com/gitlab-org/gitlab/-/issues/333586) in GitLab 14.2. DevOps Adoption shows you which groups in your organization are using the most essential features of GitLab: diff --git a/doc/user/admin_area/settings/floc.md b/doc/user/admin_area/settings/floc.md index 31a626478ed..0e9d4e5d0c1 100644 --- a/doc/user/admin_area/settings/floc.md +++ b/doc/user/admin_area/settings/floc.md @@ -8,8 +8,8 @@ info: To determine the technical writer assigned to the Stage/Group associated w > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/60933) in GitLab Free 13.12. -Federated Learning of Conhorts (FLoC) is a feature that the Chrome browser has -rolled out, where users are categorized into different cohorts, so that +Federated Learning of Cohorts (FLoC) is a new feature of the Chrome browser. +It works by categorizing users into different cohorts, so that advertisers can use this data to uniquely target and track users. For more information, visit the [FLoC repository](https://github.com/WICG/floc). diff --git a/doc/user/admin_area/settings/rate_limit_on_notes_creation.md b/doc/user/admin_area/settings/rate_limit_on_notes_creation.md index 193f39542cf..7615ad6f81d 100644 --- a/doc/user/admin_area/settings/rate_limit_on_notes_creation.md +++ b/doc/user/admin_area/settings/rate_limit_on_notes_creation.md @@ -17,7 +17,7 @@ To change the note creation rate limit: 1. On the left sidebar, select **Settings > Network**. 1. Expand **Notes Rate Limits**. 1. Under **Max requests per minute per user**, enter the new value. -1. Optional. Under **List of users to be excluded from the limit**, list users to be excluded fromt the limit. +1. Optional. Under **List of users to be excluded from the limit**, list users to be excluded from the limit. 1. Select **Save changes**. This limit is: diff --git a/doc/user/admin_area/settings/visibility_and_access_controls.md b/doc/user/admin_area/settings/visibility_and_access_controls.md index 45256187a7e..c46aec76e57 100644 --- a/doc/user/admin_area/settings/visibility_and_access_controls.md +++ b/doc/user/admin_area/settings/visibility_and_access_controls.md @@ -211,7 +211,7 @@ You can specify from which hosting sites users can [import their projects](../.. ## Enable project export To enable the export of -[projects and their data](../../../user/project/settings/import_export.md#exporting-a-project-and-its-data): +[projects and their data](../../../user/project/settings/import_export.md#export-a-project-and-its-data): 1. Sign in to GitLab as a user with [Administrator role](../../permissions.md). 1. On the top bar, select **Menu >** **{admin}** **Admin**. diff --git a/doc/user/analytics/value_stream_analytics.md b/doc/user/analytics/value_stream_analytics.md index 4ad3a03a5b0..9a1aed9c39f 100644 --- a/doc/user/analytics/value_stream_analytics.md +++ b/doc/user/analytics/value_stream_analytics.md @@ -124,9 +124,9 @@ From the previous example we see the time used for each stage: - **Issue**: 2 hrs (09:00 to 11:00) - **Plan**: 1 hr (11:00 to 12:00) - **Code**: 2 hrs (12:00 to 14:00) -- **Test**: 5 mins +- **Test**: 5 minutes - **Review**: 5 hrs (14:00 to 19:00) -- **Staging**: 30 mins (19:00 to 19:30) +- **Staging**: 30 minutes (19:00 to 19:30) More information: diff --git a/doc/user/clusters/management_project_template.md b/doc/user/clusters/management_project_template.md index 22fc01de4b6..1de17396bf4 100644 --- a/doc/user/clusters/management_project_template.md +++ b/doc/user/clusters/management_project_template.md @@ -53,13 +53,13 @@ project. This image consists of a set of Bash utility scripts to support [Helm v This file has a list of paths to other Helmfiles for each app. They're all commented out by default, so you must uncomment the paths for the apps that you would like to manage. -By default, each `helmfile.yaml` in these sub-paths will have the attribute `installed: true`, which signifies that everytime -the pipeline runs, Helmfile will try to either install or update your apps according to the current state of your -cluster and Helm releases. If you change this attribute to `installed: false`, Helmfile will try to uninstall this app +By default, each `helmfile.yaml` in these sub-paths have the attribute `installed: true`. This signifies that every time +the pipeline runs, Helmfile tries to either install or update your apps according to the current state of your +cluster and Helm releases. If you change this attribute to `installed: false`, Helmfile tries try to uninstall this app from your cluster. [Read more](https://github.com/roboll/helmfile) about how Helmfile works. Furthermore, each app has an `applications/{app}/values.yaml` file. This is the -place where you can define some default values for your app's Helm chart. Some apps will already have defaults +place where you can define some default values for your app's Helm chart. Some apps already have defaults pre-defined by GitLab. #### Built-in applications @@ -80,7 +80,7 @@ The [built-in supported applications](https://gitlab.com/gitlab-org/project-temp - [Sentry](../infrastructure/clusters/manage/management_project_applications/sentry.md) - [Vault](../infrastructure/clusters/manage/management_project_applications/vault.md) -### Migrating from GitLab Managed Apps +### Migrate from GitLab Managed Apps If you had GitLab Managed Apps, either One-Click or CI/CD install, read the docs on how to [migrate from GitLab Managed Apps to project template](migrating_from_gma_to_project_template.md) diff --git a/doc/user/infrastructure/clusters/manage/management_project_applications/sentry.md b/doc/user/infrastructure/clusters/manage/management_project_applications/sentry.md index 1fe9b57cd4b..4d82fe389d2 100644 --- a/doc/user/infrastructure/clusters/manage/management_project_applications/sentry.md +++ b/doc/user/infrastructure/clusters/manage/management_project_applications/sentry.md @@ -1,6 +1,6 @@ --- stage: Monitor -group: Health +group: Monitor 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/infrastructure/index.md b/doc/user/infrastructure/index.md index 7e334e93c4f..b2d75a22615 100644 --- a/doc/user/infrastructure/index.md +++ b/doc/user/infrastructure/index.md @@ -7,7 +7,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w # Infrastructure management **(FREE)** GitLab provides you with great solutions to help you manage your -infrastrucure: +infrastructure: - [Infrastructure as Code and GitOps](iac/index.md) - [Kubernetes clusters](../project/clusters/index.md) diff --git a/doc/user/project/clusters/add_remove_clusters.md b/doc/user/project/clusters/add_remove_clusters.md index dc55b34d4ba..fba02183be5 100644 --- a/doc/user/project/clusters/add_remove_clusters.md +++ b/doc/user/project/clusters/add_remove_clusters.md @@ -42,8 +42,8 @@ providers. To host them on premises and with other providers, use either the EKS or GKE method to guide you through and enter your cluster's settings manually: -- [New cluster hosted on Google Kubernetes Engine (GKE)](add_eks_clusters.md). -- [New cluster hosted on Amazon Elastic Kubernetes Service (EKS)](add_gke_clusters.md). +- [New cluster hosted on Google Kubernetes Engine (GKE)](add_gke_clusters.md). +- [New cluster hosted on Amazon Elastic Kubernetes Service (EKS)](add_eks_clusters.md). ## Add existing cluster diff --git a/doc/user/project/import/gitlab_com.md b/doc/user/project/import/gitlab_com.md index f7eb5e43a79..f25b29317a7 100644 --- a/doc/user/project/import/gitlab_com.md +++ b/doc/user/project/import/gitlab_com.md @@ -15,7 +15,7 @@ To get to the importer page you need to go to "New project" page. NOTE: If you are interested in importing Wiki and Merge Request data to your new instance, -you'll need to follow the instructions for [exporting a project](../settings/import_export.md#exporting-a-project-and-its-data) +you'll need to follow the instructions for [exporting a project](../settings/import_export.md#export-a-project-and-its-data) ![New project page](img/gitlab_new_project_page_v12_2.png) diff --git a/doc/user/project/index.md b/doc/user/project/index.md index 8d61813dd5a..668a0dffd32 100644 --- a/doc/user/project/index.md +++ b/doc/user/project/index.md @@ -121,7 +121,7 @@ Kubernetes, Slack, and a lot more. - [Bitbucket to GitLab](import/bitbucket.md) - [Gitea to GitLab](import/gitea.md) - [FogBugz to GitLab](import/fogbugz.md) -- [Export a project from GitLab](settings/import_export.md#exporting-a-project-and-its-data) +- [Export a project from GitLab](settings/import_export.md#export-a-project-and-its-data) - [Importing and exporting projects between GitLab instances](settings/import_export.md) ## GitLab Workflow - VS Code extension diff --git a/doc/user/project/repository/reducing_the_repo_size_using_git.md b/doc/user/project/repository/reducing_the_repo_size_using_git.md index ecb32feff55..81429ea5384 100644 --- a/doc/user/project/repository/reducing_the_repo_size_using_git.md +++ b/doc/user/project/repository/reducing_the_repo_size_using_git.md @@ -23,7 +23,7 @@ over [`git filter-branch`](https://git-scm.com/docs/git-filter-branch) and WARNING: Rewriting repository history is a destructive operation. Make sure to back up your repository before you begin. The best way back up a repository is to -[export the project](../settings/import_export.md#exporting-a-project-and-its-data). +[export the project](../settings/import_export.md#export-a-project-and-its-data). ## Purge files from repository history @@ -44,7 +44,7 @@ To purge files from a GitLab repository: using a supported package manager or from source. 1. Generate a fresh [export from the - project](../settings/import_export.html#exporting-a-project-and-its-data) and download it. + project](../settings/import_export.html#export-a-project-and-its-data) and download it. This project export contains a backup copy of your repository *and* refs we can use to purge files from your repository. diff --git a/doc/user/project/settings/import_export.md b/doc/user/project/settings/import_export.md index cd0fa351e29..d0511419d38 100644 --- a/doc/user/project/settings/import_export.md +++ b/doc/user/project/settings/import_export.md @@ -32,8 +32,8 @@ To set up a project import/export: Note the following: -- Before you can import a project, you need to export the data first. - See [Exporting a project and its data](#exporting-a-project-and-its-data) +- Before you can import a project, you must export the data first. + See [Export a project and its data](#export-a-project-and-its-data) for how you can export a project through the UI. - Imports from a newer version of GitLab are not supported. The Importing GitLab version must be greater than or equal to the Exporting GitLab version. @@ -42,7 +42,7 @@ Note the following: - Exports are generated in your configured `shared_path`, a temporary shared directory, and are moved to your configured `uploads_directory`. Every 24 hours, a specific worker deletes these export files. - Group members are exported as project members, as long as the user has - maintainer or administrator access to the group where the exported project lives. + a maintainer or administrator role in the group where the exported project lives. - Project members with the [Owner role](../../permissions.md) are imported as Maintainers. - Imported users can be mapped by their primary email on self-managed instances, if an administrative user (not an owner) does the import. Otherwise, a supplementary comment is left to mention that the original author and @@ -51,10 +51,10 @@ Note the following: possible through a [professional services engagement](https://about.gitlab.com/services/migration/). - If an imported project contains merge requests originating from forks, then new branches associated with such merge requests are created - within a project during the import/export. Thus, the number of branches + in a project during the import/export. Thus, the number of branches in the exported project could be bigger than in the original project. - Deploy keys allowed to push to protected branches are not exported. Therefore, - you need to recreate this association by first enabling these deploy keys in your + you must recreate this association by first enabling these deploy keys in your imported project and then updating your protected branches accordingly. ## Version history @@ -141,7 +141,7 @@ NOTE: For more details on the specific data persisted in a project export, see the [`import_export.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/import_export/project/import_export.yml) file. -## Exporting a project and its data +## Export a project and its data Full project export functionality is limited to project maintainers and owners. You can configure such functionality through [project settings](index.md): @@ -156,18 +156,18 @@ To export a project and its data, follow these steps: ![Export button](img/import_export_export_button.png) -1. Once the export is generated, you should receive an email with a link to +1. After the export is generated, you should receive an email with a link to download the file: ![Email download link](img/import_export_mail_link.png) 1. Alternatively, you can come back to the project settings and download the - file from there, or generate a new export. Once the file is available, the page + file from there, or generate a new export. After the file is available, the page should show the **Download export** button: ![Download export](img/import_export_download_export.png) -## Importing the project +## Import the project 1. The GitLab project import feature is the first import option when creating a new project. Click on **GitLab export**: @@ -215,7 +215,7 @@ GitLab.com may have [different settings](../../gitlab_com/index.md#importexport) ### Import workaround for large repositories -[Maximum import size limitations](#importing-the-project) +[Maximum import size limitations](#import-the-project) can prevent an import from being successful. If changing the import limits is not possible, the following local workflow can be used to temporarily diff --git a/doc/user/project/settings/index.md b/doc/user/project/settings/index.md index 179fa755918..66fdace81ba 100644 --- a/doc/user/project/settings/index.md +++ b/doc/user/project/settings/index.md @@ -271,7 +271,7 @@ Enable [Service Desk](../service_desk.md) for your project to offer customer sup ### Export project -Learn how to [export a project](import_export.md#importing-the-project) in GitLab. +Learn how to [export a project](import_export.md#import-the-project) in GitLab. ### Advanced settings diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 9255d3dd4fe..ebf23280570 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -7181,6 +7181,9 @@ msgstr "" msgid "ClusterAgents|Go to the repository" msgstr "" +msgid "ClusterAgents|Install a new GitLab Agent" +msgstr "" + msgid "ClusterAgents|Install new Agent" msgstr "" @@ -7196,9 +7199,6 @@ msgstr "" msgid "ClusterAgents|Learn how to create an agent access token" msgstr "" -msgid "ClusterAgents|Learn more about installing the GitLab Agent" -msgstr "" - msgid "ClusterAgents|Name" msgstr "" @@ -14966,13 +14966,10 @@ msgstr "" msgid "Geo|Remove node" msgstr "" -msgid "Geo|Remove secondary node" -msgstr "" - msgid "Geo|Remove tracking database entry" msgstr "" -msgid "Geo|Removing a Geo secondary node stops the synchronization to that node. Are you sure?" +msgid "Geo|Removing a Geo node stops the synchronization to and from that node. Are you sure?" msgstr "" msgid "Geo|Replicated data is verified with the secondary node(s) using checksums" @@ -17912,6 +17909,9 @@ msgstr "" msgid "Integrations|All projects inheriting these settings will also be reset." msgstr "" +msgid "Integrations|An error occurred while loading projects using custom settings." +msgstr "" + msgid "Integrations|Browser limitations" msgstr "" @@ -18032,6 +18032,9 @@ msgstr "" msgid "Integrations|Standard" msgstr "" +msgid "Integrations|There are no projects using custom settings" +msgstr "" + msgid "Integrations|This integration, and inheriting projects were reset." msgstr "" @@ -39694,8 +39697,10 @@ msgstr "" msgid "mrWidget|Closed by" msgstr "" -msgid "mrWidget|Closes" -msgstr "" +msgid "mrWidget|Closes issue" +msgid_plural "mrWidget|Closes issues" +msgstr[0] "" +msgstr[1] "" msgid "mrWidget|Delete source branch" msgstr "" @@ -39730,8 +39735,10 @@ msgstr "" msgid "mrWidget|Members who can merge are allowed to add commits." msgstr "" -msgid "mrWidget|Mentions" -msgstr "" +msgid "mrWidget|Mentions issue" +msgid_plural "mrWidget|Mentions issues" +msgstr[0] "" +msgstr[1] "" msgid "mrWidget|Merge" msgstr "" diff --git a/package.json b/package.json index 29680e1a904..f941cfcee1b 100644 --- a/package.json +++ b/package.json @@ -125,7 +125,7 @@ "dateformat": "^4.5.1", "deckar01-task_list": "^2.3.1", "diff": "^3.4.0", - "dompurify": "^2.3.0", + "dompurify": "^2.3.1", "dropzone": "^4.2.0", "editorconfig": "^0.15.3", "emoji-regex": "^7.0.3", diff --git a/spec/features/admin/integrations/user_activates_mattermost_slash_command_spec.rb b/spec/features/admin/integrations/user_activates_mattermost_slash_command_spec.rb index 6f091d37995..c4626996d0c 100644 --- a/spec/features/admin/integrations/user_activates_mattermost_slash_command_spec.rb +++ b/spec/features/admin/integrations/user_activates_mattermost_slash_command_spec.rb @@ -11,6 +11,24 @@ RSpec.describe 'User activates the instance-level Mattermost Slash Command integ end let(:edit_path) { edit_admin_application_settings_integration_path(:mattermost_slash_commands) } + let(:overrides_path) { overrides_admin_application_settings_integration_path(:mattermost_slash_commands) } include_examples 'user activates the Mattermost Slash Command integration' + + it 'displays navigation tabs' do + expect(page).to have_link('Settings', href: edit_path) + expect(page).to have_link('Projects using custom settings', href: overrides_path) + end + + context 'when instance_level_integration_overrides is disabled' do + before do + stub_feature_flags(instance_level_integration_overrides: false) + visit_instance_integration('Mattermost slash commands') + end + + it 'does not display the overrides tab' do + expect(page).not_to have_link('Settings', href: edit_path) + expect(page).not_to have_link('Projects using custom settings', href: overrides_path) + end + end end diff --git a/spec/features/merge_request/user_sees_closing_issues_message_spec.rb b/spec/features/merge_request/user_sees_closing_issues_message_spec.rb index d6cdc15005b..7b7fff5c936 100644 --- a/spec/features/merge_request/user_sees_closing_issues_message_spec.rb +++ b/spec/features/merge_request/user_sees_closing_issues_message_spec.rb @@ -31,7 +31,7 @@ RSpec.describe 'Merge request > User sees closing issues message', :js do let(:merge_request_description) { "Description\n\nclosing #{issue_1.to_reference}, #{issue_2.to_reference}" } it 'does not display closing issue message' do - expect(page).to have_content("Closes #{issue_1.to_reference} and #{issue_2.to_reference}") + expect(page).to have_content("Closes issues #{issue_1.to_reference} and #{issue_2.to_reference}") end end @@ -39,7 +39,7 @@ RSpec.describe 'Merge request > User sees closing issues message', :js do let(:merge_request_description) { "Description\n\nRefers to #{issue_1.to_reference} and #{issue_2.to_reference}" } it 'does not display closing issue message' do - expect(page).to have_content("Mentions #{issue_1.to_reference} and #{issue_2.to_reference}") + expect(page).to have_content("Mentions issues #{issue_1.to_reference} and #{issue_2.to_reference}") end end @@ -47,8 +47,8 @@ RSpec.describe 'Merge request > User sees closing issues message', :js do let(:merge_request_title) { "closes #{issue_1.to_reference}\n\n refers to #{issue_2.to_reference}" } it 'does not display closing issue message' do - expect(page).to have_content("Closes #{issue_1.to_reference}") - expect(page).to have_content("Mentions #{issue_2.to_reference}") + expect(page).to have_content("Closes issue #{issue_1.to_reference}") + expect(page).to have_content("Mentions issue #{issue_2.to_reference}") end end @@ -56,7 +56,7 @@ RSpec.describe 'Merge request > User sees closing issues message', :js do let(:merge_request_title) { "closing #{issue_1.to_reference}, #{issue_2.to_reference}" } it 'does not display closing issue message' do - expect(page).to have_content("Closes #{issue_1.to_reference} and #{issue_2.to_reference}") + expect(page).to have_content("Closes issues #{issue_1.to_reference} and #{issue_2.to_reference}") end end @@ -64,7 +64,7 @@ RSpec.describe 'Merge request > User sees closing issues message', :js do let(:merge_request_title) { "Refers to #{issue_1.to_reference} and #{issue_2.to_reference}" } it 'does not display closing issue message' do - expect(page).to have_content("Mentions #{issue_1.to_reference} and #{issue_2.to_reference}") + expect(page).to have_content("Mentions issues #{issue_1.to_reference} and #{issue_2.to_reference}") end end @@ -72,8 +72,8 @@ RSpec.describe 'Merge request > User sees closing issues message', :js do let(:merge_request_title) { "closes #{issue_1.to_reference}\n\n refers to #{issue_2.to_reference}" } it 'does not display closing issue message' do - expect(page).to have_content("Closes #{issue_1.to_reference}") - expect(page).to have_content("Mentions #{issue_2.to_reference}") + expect(page).to have_content("Closes issue #{issue_1.to_reference}") + expect(page).to have_content("Mentions issue #{issue_2.to_reference}") end end end diff --git a/spec/frontend/content_editor/components/toolbar_table_button_spec.js b/spec/frontend/content_editor/components/toolbar_table_button_spec.js index 336391f9b63..056e5e04e1f 100644 --- a/spec/frontend/content_editor/components/toolbar_table_button_spec.js +++ b/spec/frontend/content_editor/components/toolbar_table_button_spec.js @@ -29,17 +29,17 @@ describe('content_editor/components/toolbar_table_button', () => { wrapper.destroy(); }); - it('renders a grid of 3x3 buttons to create a table', () => { - expect(getNumButtons()).toBe(9); // 3 x 3 + it('renders a grid of 5x5 buttons to create a table', () => { + expect(getNumButtons()).toBe(25); // 5x5 }); describe.each` row | col | numButtons | tableSize - ${1} | ${2} | ${9} | ${'1x2'} - ${2} | ${2} | ${9} | ${'2x2'} - ${2} | ${3} | ${12} | ${'2x3'} - ${3} | ${2} | ${12} | ${'3x2'} - ${3} | ${3} | ${16} | ${'3x3'} + ${3} | ${4} | ${25} | ${'3x4'} + ${4} | ${4} | ${25} | ${'4x4'} + ${4} | ${5} | ${30} | ${'4x5'} + ${5} | ${4} | ${30} | ${'5x4'} + ${5} | ${5} | ${36} | ${'5x5'} `('button($row, $col) in the table creator grid', ({ row, col, numButtons, tableSize }) => { describe('on mouse over', () => { beforeEach(async () => { @@ -50,9 +50,7 @@ describe('content_editor/components/toolbar_table_button', () => { it('marks all rows and cols before it as active', () => { const prevRow = Math.max(1, row - 1); const prevCol = Math.max(1, col - 1); - expect(wrapper.findByTestId(`table-${prevRow}-${prevCol}`).element).toHaveClass( - 'gl-bg-blue-50!', - ); + expect(wrapper.findByTestId(`table-${prevRow}-${prevCol}`).element).toHaveClass('active'); }); it('shows a help text indicating the size of the table being inserted', () => { @@ -89,8 +87,8 @@ describe('content_editor/components/toolbar_table_button', () => { }); }); - it('does not create more buttons than a 8x8 grid', async () => { - for (let i = 3; i < 8; i += 1) { + it('does not create more buttons than a 10x10 grid', async () => { + for (let i = 5; i < 10; i += 1) { expect(getNumButtons()).toBe(i * i); // eslint-disable-next-line no-await-in-loop @@ -98,6 +96,6 @@ describe('content_editor/components/toolbar_table_button', () => { expect(findDropdown().element).toHaveText(`Insert a ${i}x${i} table.`); } - expect(getNumButtons()).toBe(64); // 8x8 (and not 9x9) + expect(getNumButtons()).toBe(100); // 10x10 (and not 11x11) }); }); diff --git a/spec/frontend/integrations/overrides/components/integration_overrides_spec.js b/spec/frontend/integrations/overrides/components/integration_overrides_spec.js new file mode 100644 index 00000000000..dbed236d7df --- /dev/null +++ b/spec/frontend/integrations/overrides/components/integration_overrides_spec.js @@ -0,0 +1,146 @@ +import { GlTable, GlLink, GlPagination } from '@gitlab/ui'; +import { shallowMount, mount } from '@vue/test-utils'; +import MockAdapter from 'axios-mock-adapter'; +import waitForPromises from 'helpers/wait_for_promises'; +import { DEFAULT_PER_PAGE } from '~/api'; +import createFlash from '~/flash'; +import IntegrationOverrides from '~/integrations/overrides/components/integration_overrides.vue'; +import axios from '~/lib/utils/axios_utils'; +import httpStatus from '~/lib/utils/http_status'; +import ProjectAvatar from '~/vue_shared/components/project_avatar.vue'; + +jest.mock('~/flash'); + +const mockOverrides = Array(DEFAULT_PER_PAGE * 3) + .fill(1) + .map((_, index) => ({ + name: `test-proj-${index}`, + avatar_url: `avatar-${index}`, + full_path: `test-proj-${index}`, + full_name: `test-proj-${index}`, + })); + +describe('IntegrationOverrides', () => { + let wrapper; + let mockAxios; + + const defaultProps = { + overridesPath: 'mock/overrides', + }; + + const createComponent = ({ mountFn = shallowMount } = {}) => { + wrapper = mountFn(IntegrationOverrides, { + propsData: defaultProps, + }); + }; + + beforeEach(() => { + mockAxios = new MockAdapter(axios); + mockAxios.onGet(defaultProps.overridesPath).reply(httpStatus.OK, mockOverrides, { + 'X-TOTAL': mockOverrides.length, + 'X-PAGE': 1, + }); + }); + + afterEach(() => { + mockAxios.restore(); + wrapper.destroy(); + }); + + const findGlTable = () => wrapper.findComponent(GlTable); + const findPagination = () => wrapper.findComponent(GlPagination); + const findRowsAsModel = () => + findGlTable() + .findAllComponents(GlLink) + .wrappers.map((link) => { + const avatar = link.findComponent(ProjectAvatar); + + return { + href: link.attributes('href'), + avatarUrl: avatar.props('projectAvatarUrl'), + avatarName: avatar.props('projectName'), + text: link.text(), + }; + }); + + describe('while loading', () => { + it('sets GlTable `busy` attribute to `true`', () => { + createComponent(); + + const table = findGlTable(); + expect(table.exists()).toBe(true); + expect(table.attributes('busy')).toBe('true'); + }); + }); + + describe('when initial request is successful', () => { + it('sets GlTable `busy` attribute to `false`', async () => { + createComponent(); + await waitForPromises(); + + const table = findGlTable(); + expect(table.exists()).toBe(true); + expect(table.attributes('busy')).toBeFalsy(); + }); + + describe('table template', () => { + beforeEach(async () => { + createComponent({ mountFn: mount }); + await waitForPromises(); + }); + + it('renders overrides as rows in table', () => { + expect(findRowsAsModel()).toEqual( + mockOverrides.map((x) => ({ + href: x.full_path, + avatarUrl: x.avatar_url, + avatarName: x.name, + text: expect.stringContaining(x.full_name), + })), + ); + }); + }); + }); + + describe('when request fails', () => { + beforeEach(async () => { + mockAxios.onGet(defaultProps.overridesPath).reply(httpStatus.INTERNAL_SERVER_ERROR); + createComponent(); + await waitForPromises(); + }); + + it('calls createFlash', () => { + expect(createFlash).toHaveBeenCalledTimes(1); + expect(createFlash).toHaveBeenCalledWith({ + message: IntegrationOverrides.i18n.defaultErrorMessage, + captureError: true, + error: expect.any(Error), + }); + }); + }); + + describe('pagination', () => { + it('triggers fetch when `input` event is emitted', async () => { + createComponent(); + jest.spyOn(axios, 'get'); + await waitForPromises(); + + await findPagination().vm.$emit('input', 2); + expect(axios.get).toHaveBeenCalledWith(defaultProps.overridesPath, { + params: { page: 2, per_page: DEFAULT_PER_PAGE }, + }); + }); + + it('does not render with <=1 page', async () => { + mockAxios.onGet(defaultProps.overridesPath).reply(httpStatus.OK, [mockOverrides[0]], { + 'X-TOTAL': 1, + 'X-PAGE': 1, + }); + + createComponent(); + await waitForPromises(); + + expect(findPagination().exists()).toBe(false); + }); + }); +}); diff --git a/spec/frontend/jobs/components/stages_dropdown_spec.js b/spec/frontend/jobs/components/stages_dropdown_spec.js index b75d1707a8d..b0e95a2d5b6 100644 --- a/spec/frontend/jobs/components/stages_dropdown_spec.js +++ b/spec/frontend/jobs/components/stages_dropdown_spec.js @@ -20,6 +20,7 @@ describe('Stages Dropdown', () => { const findPipelineInfoText = () => wrapper.findByTestId('pipeline-info').text(); const findPipelinePath = () => wrapper.findByTestId('pipeline-path').attributes('href'); const findMRLinkPath = () => wrapper.findByTestId('mr-link').attributes('href'); + const findCopySourceBranchBtn = () => wrapper.findByTestId('copy-source-ref-link'); const findSourceBranchLinkPath = () => wrapper.findByTestId('source-branch-link').attributes('href'); const findTargetBranchLinkPath = () => @@ -70,6 +71,10 @@ describe('Stages Dropdown', () => { expect(actual).toBe(expected); }); + + it(`renders the source ref copy button`, () => { + expect(findCopySourceBranchBtn().exists()).toBe(true); + }); }); describe('with an "attached" merge request pipeline', () => { @@ -103,6 +108,10 @@ describe('Stages Dropdown', () => { mockPipelineWithAttachedMR.merge_request.target_branch_path, ); }); + + it(`renders the source ref copy button`, () => { + expect(findCopySourceBranchBtn().exists()).toBe(true); + }); }); describe('with a detached merge request pipeline', () => { @@ -130,5 +139,9 @@ describe('Stages Dropdown', () => { mockPipelineDetached.merge_request.source_branch_path, ); }); + + it(`renders the source ref copy button`, () => { + expect(findCopySourceBranchBtn().exists()).toBe(true); + }); }); }); diff --git a/spec/frontend/notes/components/comment_field_layout_spec.js b/spec/frontend/notes/components/comment_field_layout_spec.js index 4d9b4ea8c6f..90c989540b9 100644 --- a/spec/frontend/notes/components/comment_field_layout_spec.js +++ b/spec/frontend/notes/components/comment_field_layout_spec.js @@ -134,4 +134,18 @@ describe('Comment Field Layout Component', () => { ]); }); }); + + describe('issue has email participants, but note is confidential', () => { + it('does not show EmailParticipantsWarning', () => { + createWrapper({ + noteableData: { + ...noteableDataMock, + issue_email_participants: [{ email: 'someone@gitlab.com' }], + }, + noteIsConfidential: true, + }); + + expect(findEmailParticipantsWarning().exists()).toBe(false); + }); + }); }); diff --git a/spec/frontend/vue_mr_widget/components/mr_widget_related_links_spec.js b/spec/frontend/vue_mr_widget/components/mr_widget_related_links_spec.js index a879b06e858..6ea8ca10c02 100644 --- a/spec/frontend/vue_mr_widget/components/mr_widget_related_links_spec.js +++ b/spec/frontend/vue_mr_widget/components/mr_widget_related_links_spec.js @@ -17,7 +17,7 @@ describe('MRWidgetRelatedLinks', () => { it('returns Closes text for open merge request', () => { createComponent({ state: 'open', relatedLinks: {} }); - expect(wrapper.vm.closesText).toBe('Closes'); + expect(wrapper.vm.closesText).toBe('Closes issues'); }); it('returns correct text for closed merge request', () => { @@ -38,6 +38,7 @@ describe('MRWidgetRelatedLinks', () => { createComponent({ relatedLinks: { closing: '<a href="#">#23</a> and <a>#42</a>', + closingCount: 2, }, }); const content = wrapper @@ -45,7 +46,7 @@ describe('MRWidgetRelatedLinks', () => { .replace(/\n(\s)+/g, ' ') .trim(); - expect(content).toContain('Closes #23 and #42'); + expect(content).toContain('Closes issues #23 and #42'); expect(content).not.toContain('Mentions'); }); @@ -53,11 +54,17 @@ describe('MRWidgetRelatedLinks', () => { createComponent({ relatedLinks: { mentioned: '<a href="#">#7</a>', + mentionedCount: 1, }, }); - expect(wrapper.text().trim()).toContain('Mentions #7'); - expect(wrapper.text().trim()).not.toContain('Closes'); + const content = wrapper + .text() + .replace(/\n(\s)+/g, ' ') + .trim(); + + expect(content).toContain('Mentions issue #7'); + expect(content).not.toContain('Closes issues'); }); it('should have closing and mentioned issues at the same time', () => { @@ -65,6 +72,8 @@ describe('MRWidgetRelatedLinks', () => { relatedLinks: { closing: '<a href="#">#7</a>', mentioned: '<a href="#">#23</a> and <a>#42</a>', + closingCount: 1, + mentionedCount: 2, }, }); const content = wrapper @@ -72,8 +81,8 @@ describe('MRWidgetRelatedLinks', () => { .replace(/\n(\s)+/g, ' ') .trim(); - expect(content).toContain('Closes #7'); - expect(content).toContain('Mentions #23 and #42'); + expect(content).toContain('Closes issue #7'); + expect(content).toContain('Mentions issues #23 and #42'); }); it('should have assing issues link', () => { diff --git a/spec/helpers/clusters_helper_spec.rb b/spec/helpers/clusters_helper_spec.rb index f64afa1ed71..f1e19f17c72 100644 --- a/spec/helpers/clusters_helper_spec.rb +++ b/spec/helpers/clusters_helper_spec.rb @@ -82,6 +82,10 @@ RSpec.describe ClustersHelper do expect(subject[:get_started_docs_url]).to eq(help_page_path('user/clusters/agent/index', anchor: 'define-a-configuration-repository')) expect(subject[:integration_docs_url]).to eq(help_page_path('user/clusters/agent/index', anchor: 'get-started-with-gitops-and-the-gitlab-agent')) end + + it 'displays kas address' do + expect(subject[:kas_address]).to eq(Gitlab::Kas.external_url) + end end describe '#js_clusters_list_data' do diff --git a/spec/migrations/20210805192450_update_trial_plans_ci_daily_pipeline_schedule_triggers_spec.rb b/spec/migrations/20210805192450_update_trial_plans_ci_daily_pipeline_schedule_triggers_spec.rb new file mode 100644 index 00000000000..819120d43ef --- /dev/null +++ b/spec/migrations/20210805192450_update_trial_plans_ci_daily_pipeline_schedule_triggers_spec.rb @@ -0,0 +1,137 @@ +# frozen_string_literal: true + +require 'spec_helper' + +require_migration!('update_trial_plans_ci_daily_pipeline_schedule_triggers') + +RSpec.describe UpdateTrialPlansCiDailyPipelineScheduleTriggers, :migration do + let!(:plans) { table(:plans) } + let!(:plan_limits) { table(:plan_limits) } + let!(:premium_trial_plan) { plans.create!(name: 'premium_trial', title: 'Premium Trial') } + let!(:ultimate_trial_plan) { plans.create!(name: 'ultimate_trial', title: 'Ultimate Trial') } + + describe '#up' do + let!(:premium_trial_plan_limits) { plan_limits.create!(plan_id: premium_trial_plan.id, ci_daily_pipeline_schedule_triggers: 0) } + let!(:ultimate_trial_plan_limits) { plan_limits.create!(plan_id: ultimate_trial_plan.id, ci_daily_pipeline_schedule_triggers: 0) } + + context 'when the environment is dev or com' do + before do + allow(Gitlab).to receive(:dev_env_or_com?).and_return(true) + end + + it 'sets the trial plan limits for ci_daily_pipeline_schedule_triggers' do + disable_migrations_output { migrate! } + + expect(ultimate_trial_plan_limits.reload.ci_daily_pipeline_schedule_triggers).to eq(288) + expect(premium_trial_plan_limits.reload.ci_daily_pipeline_schedule_triggers).to eq(288) + end + + it 'does not change the plan limits if the ultimate trial plan is missing' do + ultimate_trial_plan.destroy! + + expect { disable_migrations_output { migrate! } }.not_to change { plan_limits.count } + expect(premium_trial_plan_limits.reload.ci_daily_pipeline_schedule_triggers).to eq(0) + end + + it 'does not change the plan limits if the ultimate trial plan limits is missing' do + ultimate_trial_plan_limits.destroy! + + expect { disable_migrations_output { migrate! } }.not_to change { plan_limits.count } + expect(premium_trial_plan_limits.reload.ci_daily_pipeline_schedule_triggers).to eq(0) + end + + it 'does not change the plan limits if the premium trial plan is missing' do + premium_trial_plan.destroy! + + expect { disable_migrations_output { migrate! } }.not_to change { plan_limits.count } + expect(ultimate_trial_plan_limits.reload.ci_daily_pipeline_schedule_triggers).to eq(0) + end + + it 'does not change the plan limits if the premium trial plan limits is missing' do + premium_trial_plan_limits.destroy! + + expect { disable_migrations_output { migrate! } }.not_to change { plan_limits.count } + expect(ultimate_trial_plan_limits.reload.ci_daily_pipeline_schedule_triggers).to eq(0) + end + end + + context 'when the environment is anything other than dev or com' do + before do + allow(Gitlab).to receive(:dev_env_or_com?).and_return(false) + end + + it 'does not update the plan limits' do + disable_migrations_output { migrate! } + + expect(premium_trial_plan_limits.reload.ci_daily_pipeline_schedule_triggers).to eq(0) + expect(ultimate_trial_plan_limits.reload.ci_daily_pipeline_schedule_triggers).to eq(0) + end + end + end + + describe '#down' do + let!(:premium_trial_plan_limits) { plan_limits.create!(plan_id: premium_trial_plan.id, ci_daily_pipeline_schedule_triggers: 288) } + let!(:ultimate_trial_plan_limits) { plan_limits.create!(plan_id: ultimate_trial_plan.id, ci_daily_pipeline_schedule_triggers: 288) } + + context 'when the environment is dev or com' do + before do + allow(Gitlab).to receive(:dev_env_or_com?).and_return(true) + end + + it 'sets the trial plan limits ci_daily_pipeline_schedule_triggers to zero' do + migrate_down! + + expect(ultimate_trial_plan_limits.reload.ci_daily_pipeline_schedule_triggers).to eq(0) + expect(premium_trial_plan_limits.reload.ci_daily_pipeline_schedule_triggers).to eq(0) + end + + it 'does not change the plan limits if the ultimate trial plan is missing' do + ultimate_trial_plan.destroy! + + expect { migrate_down! }.not_to change { plan_limits.count } + expect(premium_trial_plan_limits.reload.ci_daily_pipeline_schedule_triggers).to eq(288) + end + + it 'does not change the plan limits if the ultimate trial plan limits is missing' do + ultimate_trial_plan_limits.destroy! + + expect { migrate_down! }.not_to change { plan_limits.count } + expect(premium_trial_plan_limits.reload.ci_daily_pipeline_schedule_triggers).to eq(288) + end + + it 'does not change the plan limits if the premium trial plan is missing' do + premium_trial_plan.destroy! + + expect { migrate_down! }.not_to change { plan_limits.count } + expect(ultimate_trial_plan_limits.reload.ci_daily_pipeline_schedule_triggers).to eq(288) + end + + it 'does not change the plan limits if the premium trial plan limits is missing' do + premium_trial_plan_limits.destroy! + + expect { migrate_down! }.not_to change { plan_limits.count } + expect(ultimate_trial_plan_limits.reload.ci_daily_pipeline_schedule_triggers).to eq(288) + end + end + + context 'when the environment is anything other than dev or com' do + before do + allow(Gitlab).to receive(:dev_env_or_com?).and_return(false) + end + + it 'does not change the ultimate trial plan limits' do + migrate_down! + + expect(ultimate_trial_plan_limits.reload.ci_daily_pipeline_schedule_triggers).to eq(288) + expect(premium_trial_plan_limits.reload.ci_daily_pipeline_schedule_triggers).to eq(288) + end + end + end + + def migrate_down! + disable_migrations_output do + migrate! + described_class.new.down + end + end +end diff --git a/spec/serializers/merge_request_widget_entity_spec.rb b/spec/serializers/merge_request_widget_entity_spec.rb index fb1d157b360..35846b0d4ea 100644 --- a/spec/serializers/merge_request_widget_entity_spec.rb +++ b/spec/serializers/merge_request_widget_entity_spec.rb @@ -58,7 +58,7 @@ RSpec.describe MergeRequestWidgetEntity do data = described_class.new(resource, request: request, issues_links: true).as_json expect(data).to include(:issues_links) - expect(data[:issues_links]).to include(:assign_to_closing, :closing, :mentioned_but_not_closing) + expect(data[:issues_links]).to include(:assign_to_closing, :closing, :mentioned_but_not_closing, :closing_count, :mentioned_count) end it 'omits issue links by default' do diff --git a/yarn.lock b/yarn.lock index 319045af9d0..940b76ae245 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4568,10 +4568,10 @@ domhandler@^4.0.0, domhandler@^4.2.0: dependencies: domelementtype "^2.2.0" -dompurify@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.3.0.tgz#07bb39515e491588e5756b1d3e8375b5964814e2" - integrity sha512-VV5C6Kr53YVHGOBKO/F86OYX6/iLTw2yVSI721gKetxpHCK/V5TaLEf9ODjRgl1KLSWRMY6cUhAbv/c+IUnwQw== +dompurify@^2.3.0, dompurify@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.3.1.tgz#a47059ca21fd1212d3c8f71fdea6943b8bfbdf6a" + integrity sha512-xGWt+NHAQS+4tpgbOAI08yxW0Pr256Gu/FNE2frZVTbgrBUn8M7tz7/ktS/LZ2MHeGqz6topj0/xY+y8R5FBFw== domutils@^1.5.1: version "1.7.0" |