diff options
34 files changed, 432 insertions, 503 deletions
diff --git a/app/assets/javascripts/editor/schema/ci.json b/app/assets/javascripts/editor/schema/ci.json index db11afbf382..87d869cc996 100644 --- a/app/assets/javascripts/editor/schema/ci.json +++ b/app/assets/javascripts/editor/schema/ci.json @@ -1612,7 +1612,7 @@ "description": "Creates N instances of the same job that run in parallel.", "default": 0, "minimum": 2, - "maximum": 50 + "maximum": 200 }, { "type": "object", @@ -1631,7 +1631,7 @@ ] } }, - "maxItems": 50 + "maxItems": 200 } }, "additionalProperties": false, diff --git a/app/assets/javascripts/import_entities/import_groups/components/import_actions_cell.vue b/app/assets/javascripts/import_entities/import_groups/components/import_actions_cell.vue index deaf2654424..8d72942447c 100644 --- a/app/assets/javascripts/import_entities/import_groups/components/import_actions_cell.vue +++ b/app/assets/javascripts/import_entities/import_groups/components/import_actions_cell.vue @@ -1,15 +1,27 @@ <script> -import { GlButton, GlIcon, GlTooltipDirective as GlTooltip } from '@gitlab/ui'; +import { + GlButton, + GlDropdown, + GlDropdownItem, + GlIcon, + GlTooltipDirective as GlTooltip, +} from '@gitlab/ui'; export default { components: { GlIcon, GlButton, + GlDropdown, + GlDropdownItem, }, directives: { GlTooltip, }, props: { + isProjectsImportEnabled: { + type: Boolean, + required: true, + }, isFinished: { type: Boolean, required: true, @@ -23,13 +35,32 @@ export default { required: true, }, }, + methods: { + importGroup(extraArgs = {}) { + this.$emit('import-group', extraArgs); + }, + }, }; </script> <template> <span class="gl-white-space-nowrap gl-inline-flex gl-align-items-center"> + <gl-dropdown + v-if="isProjectsImportEnabled && isAvailableForImport" + :text="isFinished ? __('Re-import with projects') : __('Import with projects')" + :disabled="isInvalid" + variant="confirm" + category="secondary" + data-qa-selector="import_group_button" + split + @click="importGroup({ migrateProjects: true })" + > + <gl-dropdown-item @click="importGroup({ migrateProjects: false })">{{ + isFinished ? __('Re-import without projects') : __('Import without projects') + }}</gl-dropdown-item> + </gl-dropdown> <gl-button - v-if="isAvailableForImport" + v-else-if="isAvailableForImport" :disabled="isInvalid" variant="confirm" category="secondary" diff --git a/app/assets/javascripts/import_entities/import_groups/components/import_table.vue b/app/assets/javascripts/import_entities/import_groups/components/import_table.vue index 6412f26fde7..c590d832568 100644 --- a/app/assets/javascripts/import_entities/import_groups/components/import_table.vue +++ b/app/assets/javascripts/import_entities/import_groups/components/import_table.vue @@ -2,6 +2,8 @@ import { GlAlert, GlButton, + GlDropdown, + GlDropdownItem, GlEmptyState, GlIcon, GlLink, @@ -15,6 +17,7 @@ import { import { debounce } from 'lodash'; import { createAlert } from '~/flash'; import { s__, __, n__, sprintf } from '~/locale'; +import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import PaginationBar from '~/vue_shared/components/pagination_bar/pagination_bar.vue'; import HelpPopover from '~/vue_shared/components/help_popover.vue'; import { getGroupPathAvailability } from '~/rest_api'; @@ -47,6 +50,8 @@ export default { components: { GlAlert, GlButton, + GlDropdown, + GlDropdownItem, GlEmptyState, GlIcon, GlLink, @@ -65,6 +70,7 @@ export default { directives: { GlTooltip: GlTooltipDirective, }, + mixins: [glFeatureFlagsMixin()], props: { sourceUrl: { type: String, @@ -128,32 +134,36 @@ export default { { key: 'webUrl', label: s__('BulkImport|Source group'), - thClass: `${DEFAULT_TH_CLASSES} gl-pl-0! import-jobs-from-col`, + thClass: `${DEFAULT_TH_CLASSES} gl-pl-0! gl-w-half`, // eslint-disable-next-line @gitlab/require-i18n-strings tdClass: `${DEFAULT_TD_CLASSES} gl-pl-0!`, }, { key: 'importTarget', label: s__('BulkImport|New group'), - thClass: `${DEFAULT_TH_CLASSES} import-jobs-to-col`, + thClass: `${DEFAULT_TH_CLASSES} gl-w-half`, tdClass: DEFAULT_TD_CLASSES, }, { key: 'progress', label: __('Status'), - thClass: `${DEFAULT_TH_CLASSES} import-jobs-status-col`, + thClass: `${DEFAULT_TH_CLASSES}`, tdClass: DEFAULT_TD_CLASSES, tdAttr: { 'data-qa-selector': 'import_status_indicator' }, }, { key: 'actions', label: '', - thClass: `${DEFAULT_TH_CLASSES} import-jobs-cta-col`, + thClass: `${DEFAULT_TH_CLASSES}`, tdClass: DEFAULT_TD_CLASSES, }, ], computed: { + isProjectsImportEnabled() { + return Boolean(this.glFeatures.bulkImportProjects); + }, + groups() { return this.bulkImportSourceGroups?.nodes ?? []; }, @@ -260,7 +270,7 @@ export default { const table = this.getTableRef(); const matches = new Set(); this.groups.forEach((g, idx) => { - if (!this.importGroups[g.id]) { + if (!this.importTargets[g.id]) { this.setDefaultImportTarget(g); } @@ -375,13 +385,14 @@ export default { } }, - importSelectedGroups() { + importSelectedGroups(extraArgs = {}) { const importRequests = this.groupsTableData .filter((group) => this.selectedGroupsIds.includes(group.id)) .map((group) => ({ sourceGroupId: group.id, targetNamespace: group.importTarget.targetNamespace.fullPath, newName: group.importTarget.newName, + ...extraArgs, })); this.importGroups(importRequests); @@ -521,6 +532,7 @@ export default { gitlabLogo: window.gon.gitlab_logo, PAGE_SIZES, permissionsHelpPath: helpPagePath('user/permissions', { anchor: 'group-members-permissions' }), + betaFeatureHelpPath: helpPagePath('policy/alpha-beta-support', { anchor: 'beta-features' }), popoverOptions: { title: __('What is listed here?') }, i18n, LOCAL_STORAGE_KEY: 'gl-bulk-imports-status-page-size-v1', @@ -637,7 +649,7 @@ export default { </gl-empty-state> <template v-else> <div - class="gl-bg-gray-10 gl-border-solid gl-border-gray-200 gl-border-0 gl-border-b-1 gl-px-4 gl-display-flex gl-align-items-center import-table-bar" + class="gl-bg-gray-10 gl-border-solid gl-border-gray-200 gl-border-0 gl-border-b-1 gl-px-4 gl-display-flex gl-align-items-center gl-sticky gl-z-index-3 import-table-bar" > <span data-test-id="selection-count"> <gl-sprintf :message="__('%{count} selected')"> @@ -646,7 +658,22 @@ export default { </template> </gl-sprintf> </span> + <gl-dropdown + v-if="isProjectsImportEnabled" + :text="s__('BulkImport|Import with projects')" + :disabled="!hasSelectedGroups" + variant="confirm" + category="primary" + class="gl-ml-4" + split + @click="importSelectedGroups({ migrateProjects: true })" + > + <gl-dropdown-item @click="importSelectedGroups({ migrateProjects: false })"> + {{ s__('BulkImport|Import without projects') }} + </gl-dropdown-item> + </gl-dropdown> <gl-button + v-else category="primary" variant="confirm" class="gl-ml-4" @@ -654,6 +681,22 @@ export default { @click="importSelectedGroups" >{{ s__('BulkImport|Import selected') }}</gl-button > + <span class="gl-ml-3"> + <gl-icon name="information-o" :size="12" class="gl-text-blue-600" /> + <gl-sprintf + :message=" + s__( + 'BulkImport|Importing projects is a %{docsLinkStart}Beta%{docsLinkEnd} feature.', + ) + " + > + <template #docsLink="{ content }" + ><gl-link :href="$options.betaFeatureHelpPath" target="_blank">{{ + content + }}</gl-link></template + > + </gl-sprintf> + </span> </div> <gl-table ref="table" @@ -661,6 +704,7 @@ export default { data-qa-selector="import_table" :tbody-tr-class="rowClasses" :tbody-tr-attr="qaRowAttributes" + thead-class="gl-sticky gl-z-index-2 gl-bg-gray-10" :items="groupsTableData" :fields="$options.fields" selectable @@ -711,6 +755,7 @@ export default { </template> <template #cell(actions)="{ item: group }"> <import-actions-cell + :is-projects-import-enabled="isProjectsImportEnabled" :is-finished="group.flags.isFinished" :is-available-for-import="group.flags.isAvailableForImport" :is-invalid="group.flags.isInvalid" @@ -720,6 +765,7 @@ export default { sourceGroupId: group.id, targetNamespace: group.importTarget.targetNamespace.fullPath, newName: group.importTarget.newName, + ...$event, }, ]) " diff --git a/app/assets/javascripts/import_entities/import_groups/graphql/client_factory.js b/app/assets/javascripts/import_entities/import_groups/graphql/client_factory.js index 913a5a659b3..de0595360bf 100644 --- a/app/assets/javascripts/import_entities/import_groups/graphql/client_factory.js +++ b/app/assets/javascripts/import_entities/import_groups/graphql/client_factory.js @@ -153,6 +153,7 @@ export function createResolvers({ endpoints }) { source_full_path: op.group.fullPath, destination_namespace: op.targetNamespace, destination_name: op.newName, + migrate_projects: op.migrateProjects, })), }); diff --git a/app/assets/javascripts/import_entities/import_groups/graphql/typedefs.graphql b/app/assets/javascripts/import_entities/import_groups/graphql/typedefs.graphql index c48e22a7717..83d17a5baa7 100644 --- a/app/assets/javascripts/import_entities/import_groups/graphql/typedefs.graphql +++ b/app/assets/javascripts/import_entities/import_groups/graphql/typedefs.graphql @@ -74,6 +74,7 @@ input ImportRequestInput { sourceGroupId: ID! targetNamespace: String! newName: String! + migrateProjects: Boolean! } extend type Mutation { diff --git a/app/assets/javascripts/import_entities/import_projects/components/provider_repo_table_row.vue b/app/assets/javascripts/import_entities/import_projects/components/provider_repo_table_row.vue index b8faf349375..da5dcfa71e3 100644 --- a/app/assets/javascripts/import_entities/import_projects/components/provider_repo_table_row.vue +++ b/app/assets/javascripts/import_entities/import_projects/components/provider_repo_table_row.vue @@ -140,7 +140,7 @@ export default { > <template v-if="repo.importSource.target">{{ repo.importSource.target }}</template> <template v-else-if="isImportNotStarted"> - <div class="import-entities-target-select gl-display-flex gl-align-items-stretch gl-w-full"> + <div class="gl-display-flex gl-align-items-stretch gl-w-full"> <import-group-dropdown #default="{ namespaces }" :text="importTarget.targetNamespace"> <template v-if="namespaces.length"> <gl-dropdown-section-header>{{ __('Groups') }}</gl-dropdown-section-header> @@ -161,7 +161,7 @@ export default { }}</gl-dropdown-item> </import-group-dropdown> <div - class="import-entities-target-select-separator gl-px-3 gl-display-flex gl-align-items-center gl-border-solid gl-border-0 gl-border-t-1 gl-border-b-1" + class="gl-px-3 gl-display-flex gl-align-items-center gl-border-solid gl-border-0 gl-border-t-1 gl-border-b-1" > / </div> diff --git a/app/assets/javascripts/pages/admin/broadcast_messages/broadcast_message.js b/app/assets/javascripts/pages/admin/broadcast_messages/broadcast_message.js deleted file mode 100644 index 40348e0b18a..00000000000 --- a/app/assets/javascripts/pages/admin/broadcast_messages/broadcast_message.js +++ /dev/null @@ -1,60 +0,0 @@ -import $ from 'jquery'; -import { debounce } from 'lodash'; -import { createAlert } from '~/flash'; -import axios from '~/lib/utils/axios_utils'; -import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants'; -import { __ } from '~/locale'; - -export default () => { - const $broadcastMessageTheme = $('.js-broadcast-message-theme'); - const $broadcastMessageType = $('.js-broadcast-message-type'); - const $broadcastBannerMessagePreview = $('.js-broadcast-banner-message-preview [role="alert"]'); - const $broadcastMessage = $('.js-broadcast-message-message'); - const $jsBroadcastMessagePreview = $('#broadcast-message-preview'); - - const reloadPreview = function reloadPreview() { - const previewPath = $broadcastMessage.data('previewPath'); - const message = $broadcastMessage.val(); - const type = $broadcastMessageType.val(); - const theme = $broadcastMessageTheme.val(); - - axios - .post(previewPath, { - broadcast_message: { - message, - broadcast_type: type, - theme, - }, - }) - .then(({ data }) => { - $jsBroadcastMessagePreview.html(data); - }) - .catch(() => - createAlert({ - message: __('An error occurred while rendering preview broadcast message'), - }), - ); - }; - - $broadcastMessageTheme.on('change', reloadPreview); - - $broadcastMessageType.on('change', () => { - const $broadcastMessageColorFormGroup = $('.js-broadcast-message-background-color-form-group'); - const $broadcastMessageDismissableFormGroup = $('.js-broadcast-message-dismissable-form-group'); - const $broadcastNotificationMessagePreview = $('.js-broadcast-notification-message-preview'); - - $broadcastMessageColorFormGroup.toggleClass('hidden'); - $broadcastMessageDismissableFormGroup.toggleClass('hidden'); - $broadcastBannerMessagePreview.toggleClass('hidden'); - $broadcastNotificationMessagePreview.toggleClass('hidden'); - - reloadPreview(); - }); - - $broadcastMessage.on( - 'input', - debounce(() => { - reloadPreview(); - }, DEFAULT_DEBOUNCE_AND_THROTTLE_MS), - ); -}; diff --git a/app/assets/javascripts/pages/admin/broadcast_messages/edit/index.js b/app/assets/javascripts/pages/admin/broadcast_messages/edit/index.js index 25036984082..94cae500a1e 100644 --- a/app/assets/javascripts/pages/admin/broadcast_messages/edit/index.js +++ b/app/assets/javascripts/pages/admin/broadcast_messages/edit/index.js @@ -1,8 +1,3 @@ import initEditBroadcastMessage from '~/admin/broadcast_messages/edit'; -import initBroadcastMessagesForm from '../broadcast_message'; -if (gon.features.vueBroadcastMessages) { - initEditBroadcastMessage(); -} else { - initBroadcastMessagesForm(); -} +initEditBroadcastMessage(); diff --git a/app/assets/javascripts/pages/admin/broadcast_messages/index/index.js b/app/assets/javascripts/pages/admin/broadcast_messages/index/index.js index 1f37df2b340..2662496be05 100644 --- a/app/assets/javascripts/pages/admin/broadcast_messages/index/index.js +++ b/app/assets/javascripts/pages/admin/broadcast_messages/index/index.js @@ -1,10 +1,3 @@ import initBroadcastMessages from '~/admin/broadcast_messages'; -import initDeprecatedRemoveRowBehavior from '~/behaviors/deprecated_remove_row_behavior'; -import initBroadcastMessagesForm from '../broadcast_message'; -if (gon.features.vueBroadcastMessages) { - initBroadcastMessages(); -} else { - initBroadcastMessagesForm(); - initDeprecatedRemoveRowBehavior(); -} +initBroadcastMessages(); diff --git a/app/assets/stylesheets/framework/contextual_sidebar.scss b/app/assets/stylesheets/framework/contextual_sidebar.scss index 4362155a2e0..1e05441c731 100644 --- a/app/assets/stylesheets/framework/contextual_sidebar.scss +++ b/app/assets/stylesheets/framework/contextual_sidebar.scss @@ -135,14 +135,6 @@ @include gl-font-weight-normal; flex: none; } - - .icon-container { - @include gl-mr-3; - @include gl-p-3; - background-color: $t-gray-a-08; - border: 1px solid $t-gray-a-08; - border-radius: $border-radius-default; - } } @mixin top-level-item { @@ -466,6 +458,7 @@ // PANELS-SPECIFIC // +.icon-avatar, .settings-avatar { svg { margin: auto; diff --git a/app/assets/stylesheets/page_bundles/import.scss b/app/assets/stylesheets/page_bundles/import.scss index cd5e6d32e4e..a6c08e344f9 100644 --- a/app/assets/stylesheets/page_bundles/import.scss +++ b/app/assets/stylesheets/page_bundles/import.scss @@ -1,48 +1,10 @@ @import 'mixins_and_variables_and_functions'; -.import-jobs-from-col { - width: 37%; -} - - -.import-jobs-to-col { - width: 37%; -} - -.import-jobs-status-col { - width: 25%; -} - -.import-jobs-cta-col { - width: 1%; -} - -.import-entities-target-select { - &.disabled { - .import-entities-target-select-separator { - color: var(--gray-400, $gray-400); - border-color: var(--gray-100, $gray-100); - background-color: var(--gray-10, $gray-10); - } - } - - .import-entities-target-select-separator { - border-color: var(--gray-200, $gray-200); - background-color: var(--gray-10, $gray-10); - } - - .gl-form-input { - box-shadow: inset 0 0 0 1px var(--gray-200, $gray-200); - } -} - $import-bar-height: $gl-spacing-scale-11; .import-table-bar { - @include gl-sticky; height: $import-bar-height; top: $header-height; - z-index: 3; html.with-performance-bar & { top: calc(#{$header-height} + #{$performance-bar-height}); @@ -50,16 +12,11 @@ $import-bar-height: $gl-spacing-scale-11; } .import-table { - border-collapse: separate; - thead { - @include gl-sticky; - background-color: var(--gray-10, $gray-10); top: calc(#{$header-height} + #{$import-bar-height}); - z-index: 3; html.with-performance-bar & { - top: calc(#{$header-height + $performance-bar-height} + #{$import-bar-height}); + top: calc(#{$header-height} + #{$performance-bar-height} + #{$import-bar-height}); } } } diff --git a/app/controllers/admin/broadcast_messages_controller.rb b/app/controllers/admin/broadcast_messages_controller.rb index 093c5667a24..d641a26c9fb 100644 --- a/app/controllers/admin/broadcast_messages_controller.rb +++ b/app/controllers/admin/broadcast_messages_controller.rb @@ -99,7 +99,6 @@ module Admin end def push_features - push_frontend_feature_flag(:vue_broadcast_messages, current_user) push_frontend_feature_flag(:role_targeted_broadcast_messages, current_user) end end diff --git a/app/controllers/import/bulk_imports_controller.rb b/app/controllers/import/bulk_imports_controller.rb index 82212a3ed89..e9705c45116 100644 --- a/app/controllers/import/bulk_imports_controller.rb +++ b/app/controllers/import/bulk_imports_controller.rb @@ -5,6 +5,9 @@ class Import::BulkImportsController < ApplicationController before_action :ensure_bulk_import_enabled before_action :verify_blocked_uri, only: :status + before_action only: :status do + push_frontend_feature_flag(:bulk_import_projects) + end feature_category :importers urgency :low @@ -128,6 +131,7 @@ class Import::BulkImportsController < ApplicationController destination_name destination_slug destination_namespace + migrate_projects ] end diff --git a/app/helpers/broadcast_messages_helper.rb b/app/helpers/broadcast_messages_helper.rb index 9827f075e54..01d28ed3221 100644 --- a/app/helpers/broadcast_messages_helper.rb +++ b/app/helpers/broadcast_messages_helper.rb @@ -50,14 +50,6 @@ module BroadcastMessagesHelper end end - def broadcast_type_options - BroadcastMessage.broadcast_types.keys.map { |w| [w.humanize, w] } - end - - def broadcast_theme_options - BroadcastMessage.themes.keys - end - def target_access_level_options BroadcastMessage::ALLOWED_TARGET_ACCESS_LEVELS.map do |access_level| [Gitlab::Access.human_access(access_level), access_level] diff --git a/app/views/admin/broadcast_messages/_form.html.haml b/app/views/admin/broadcast_messages/_form.html.haml deleted file mode 100644 index 4e05eb31010..00000000000 --- a/app/views/admin/broadcast_messages/_form.html.haml +++ /dev/null @@ -1,67 +0,0 @@ -#broadcast-message-preview - = render 'preview' - -= gitlab_ui_form_for [:admin, @broadcast_message], html: { class: 'broadcast-message-form js-quick-submit js-requires-input'} do |f| - = form_errors(@broadcast_message) - - .form-group.row.mt-4 - .col-sm-2.col-form-label - = f.label :message - .col-sm-10 - = f.text_area :message, class: "form-control gl-form-input js-autosize js-broadcast-message-message", - required: true, - dir: 'auto', - data: { preview_path: preview_admin_broadcast_messages_path } - .form-group.row - .col-sm-2.col-form-label - = f.label :broadcast_type, _('Type') - .col-sm-10 - = f.select :broadcast_type, broadcast_type_options, {}, class: 'form-control js-broadcast-message-type' - .form-group.row.js-broadcast-message-background-color-form-group{ class: ('hidden' unless @broadcast_message.banner?) } - .col-sm-2.col-form-label - = f.label :theme, _("Theme") - .col-sm-10 - .input-group - = f.select :theme, broadcast_theme_options, {}, class: 'form-control js-broadcast-message-theme' - - .form-group.row.js-broadcast-message-dismissable-form-group{ class: ('hidden' unless @broadcast_message.banner?) } - .col-sm-2.col-form-label.pt-0 - = f.label :starts_at, _("Dismissable") - .col-sm-10 - = f.gitlab_ui_checkbox_component :dismissable, _('Allow users to dismiss the broadcast message') - - if Feature.enabled?(:role_targeted_broadcast_messages) - .form-group.row - .col-sm-2.col-form-label - = f.label :target_access_levels, _('Target roles') - .col-sm-10 - - target_access_level_options.each do |human_access_level, access_level| - = f.gitlab_ui_checkbox_component :target_access_levels, human_access_level, checked_value: access_level, unchecked_value: false, checkbox_options: { multiple: true } - .form-text.text-muted - = _('The broadcast message displays only to users in projects and groups who have these roles.') - .form-group.row.js-toggle-colors-container.toggle-colors.hide - .col-sm-2.col-form-label - = f.label :font, _("Font Color") - .col-sm-10 - = f.color_field :font, class: "form-control gl-form-input text-font-color" - .form-group.row - .col-sm-2.col-form-label - = f.label :target_path, _('Target Path') - .col-sm-10 - = f.text_field :target_path, class: "form-control gl-form-input" - .form-text.text-muted - = _('Paths can contain wildcards, like */welcome') - .form-group.row - .col-sm-2.col-form-label - = f.label :starts_at, _("Starts at (UTC)") - .col-sm-10.datetime-controls - = f.datetime_select :starts_at, {}, class: 'form-control form-control-inline' - .form-group.row - .col-sm-2.col-form-label - = f.label :ends_at, _("Ends at (UTC)") - .col-sm-10.datetime-controls - = f.datetime_select :ends_at, {}, class: 'form-control form-control-inline' - .form-actions - - if @broadcast_message.persisted? - = f.submit _("Update broadcast message"), pajamas_button: true - - else - = f.submit _("Add broadcast message"), pajamas_button: true diff --git a/app/views/admin/broadcast_messages/_table.html.haml b/app/views/admin/broadcast_messages/_table.html.haml deleted file mode 100644 index c5cd333f9dd..00000000000 --- a/app/views/admin/broadcast_messages/_table.html.haml +++ /dev/null @@ -1,38 +0,0 @@ -- targeted_broadcast_messages_enabled = Feature.enabled?(:role_targeted_broadcast_messages) - -- if @broadcast_messages.any? - .table-responsive - %table.table.b-table.gl-table - %thead - %tr - %th= _('Status') - %th= _('Preview') - %th= _('Starts') - %th= _('Ends') - - if targeted_broadcast_messages_enabled - %th= _('Target roles') - %th= _('Target Path') - %th= _('Type') - %th - %tbody - - @broadcast_messages.each do |message| - %tr - %td - = broadcast_message_status(message) - %td - = broadcast_message(message, preview: true) - %td - = message.starts_at - %td - = message.ends_at - - if targeted_broadcast_messages_enabled - %td - = target_access_levels_display(message.target_access_levels) - %td - = message.target_path - %td - = message.broadcast_type.capitalize - %td.gl-white-space-nowrap< - = link_to sprite_icon('pencil', css_class: 'gl-icon'), edit_admin_broadcast_message_path(message), title: _('Edit'), class: 'btn btn-icon gl-button' - = link_to sprite_icon('remove', css_class: 'gl-icon'), admin_broadcast_message_path(message), method: :delete, remote: true, title: _('Remove'), class: 'js-remove-tr btn btn-icon gl-button btn-danger gl-ml-3' - = paginate @broadcast_messages, theme: 'gitlab' diff --git a/app/views/admin/broadcast_messages/edit.html.haml b/app/views/admin/broadcast_messages/edit.html.haml index 28301833f7d..bef435c07f7 100644 --- a/app/views/admin/broadcast_messages/edit.html.haml +++ b/app/views/admin/broadcast_messages/edit.html.haml @@ -1,19 +1,15 @@ - breadcrumb_title _("Messages") - page_title _("Broadcast Messages") -- vue_app_enabled = Feature.enabled?(:vue_broadcast_messages, current_user) -- if vue_app_enabled - #js-broadcast-message{ data: { - id: @broadcast_message.id, - message: @broadcast_message.message, - broadcast_type: @broadcast_message.broadcast_type, - theme: @broadcast_message.theme, - dismissable: @broadcast_message.dismissable.to_s, - target_access_levels: @broadcast_message.target_access_levels, - target_path: @broadcast_message.target_path, - starts_at: @broadcast_message.starts_at, - ends_at: @broadcast_message.ends_at, - target_access_level_options: target_access_level_options.to_json, - } } -- else - = render 'form' +#js-broadcast-message{ data: { + id: @broadcast_message.id, + message: @broadcast_message.message, + broadcast_type: @broadcast_message.broadcast_type, + theme: @broadcast_message.theme, + dismissable: @broadcast_message.dismissable.to_s, + target_access_levels: @broadcast_message.target_access_levels, + target_path: @broadcast_message.target_path, + starts_at: @broadcast_message.starts_at, + ends_at: @broadcast_message.ends_at, + target_access_level_options: target_access_level_options.to_json, +} } diff --git a/app/views/admin/broadcast_messages/index.html.haml b/app/views/admin/broadcast_messages/index.html.haml index 7a005f9c982..2fb59570231 100644 --- a/app/views/admin/broadcast_messages/index.html.haml +++ b/app/views/admin/broadcast_messages/index.html.haml @@ -1,31 +1,25 @@ - breadcrumb_title _("Messages") - page_title _("Broadcast Messages") -- vue_app_enabled = Feature.enabled?(:vue_broadcast_messages, current_user) %h1.page-title.gl-font-size-h-display = _('Broadcast Messages') %p.light = _('Use banners and notifications to notify your users about scheduled maintenance, recent upgrades, and more.') -- if vue_app_enabled - #js-broadcast-messages{ data: { - page: params[:page] || 1, - target_access_level_options: target_access_level_options.to_json, - messages_count: @broadcast_messages.total_count, - messages: @broadcast_messages.map { |message| { - id: message.id, - status: broadcast_message_status(message), - preview: broadcast_message(message, preview: true), - starts_at: message.starts_at.to_s, - ends_at: message.ends_at.to_s, - target_roles: target_access_levels_display(message.target_access_levels), - target_path: message.target_path, - type: message.broadcast_type.capitalize, - edit_path: edit_admin_broadcast_message_path(message), - delete_path: admin_broadcast_message_path(message) + '.js' - } }.to_json - } } -- else - = render 'form' - %br.clearfix - = render 'table' +#js-broadcast-messages{ data: { + page: params[:page] || 1, + target_access_level_options: target_access_level_options.to_json, + messages_count: @broadcast_messages.total_count, + messages: @broadcast_messages.map { |message| { + id: message.id, + status: broadcast_message_status(message), + preview: broadcast_message(message, preview: true), + starts_at: message.starts_at.to_s, + ends_at: message.ends_at.to_s, + target_roles: target_access_levels_display(message.target_access_levels), + target_path: message.target_path, + type: message.broadcast_type.capitalize, + edit_path: edit_admin_broadcast_message_path(message), + delete_path: admin_broadcast_message_path(message) + '.js' + } }.to_json +} } diff --git a/app/views/shared/nav/_your_work_scope_header.html.haml b/app/views/shared/nav/_your_work_scope_header.html.haml index 9edebcc1f75..86172fb14ed 100644 --- a/app/views/shared/nav/_your_work_scope_header.html.haml +++ b/app/views/shared/nav/_your_work_scope_header.html.haml @@ -1,5 +1,6 @@ %li.context-header - = link_to root_url do - %span.icon-container - = sprite_icon "work" - %span.sidebar-context-title= _("Your work") + = link_to root_url, title: _('Your work'), class: 'has-tooltip', data: { container: 'body', placement: 'right' } do + %span.avatar-container.icon-avatar.rect-avatar.s32 + = sprite_icon('work', size: 18) + %span.sidebar-context-title + = _('Your work') diff --git a/config/feature_flags/development/vue_broadcast_messages.yml b/config/feature_flags/development/vue_broadcast_messages.yml deleted file mode 100644 index bc9425e93ba..00000000000 --- a/config/feature_flags/development/vue_broadcast_messages.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: vue_broadcast_messages -introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/98127" -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/368847 -milestone: '15.4' -type: development -group: group::optimize -default_enabled: false diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 6c7f6cb58aa..894a40fa23a 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -20456,6 +20456,7 @@ Represents a vulnerability. | <a id="vulnerabilityseverity"></a>`severity` | [`VulnerabilitySeverity`](#vulnerabilityseverity) | Severity of the vulnerability (INFO, UNKNOWN, LOW, MEDIUM, HIGH, CRITICAL). | | <a id="vulnerabilitystate"></a>`state` | [`VulnerabilityState`](#vulnerabilitystate) | State of the vulnerability (DETECTED, CONFIRMED, RESOLVED, DISMISSED). | | <a id="vulnerabilitytitle"></a>`title` | [`String`](#string) | Title of the vulnerability. | +| <a id="vulnerabilityupdatedat"></a>`updatedAt` | [`Time`](#time) | Timestamp of when the vulnerability was last updated. | | <a id="vulnerabilityusernotescount"></a>`userNotesCount` | [`Int!`](#int) | Number of user notes attached to the vulnerability. | | <a id="vulnerabilityuserpermissions"></a>`userPermissions` | [`VulnerabilityPermissions!`](#vulnerabilitypermissions) | Permissions for the current user on the resource. | | <a id="vulnerabilityvulnerabilitypath"></a>`vulnerabilityPath` | [`String`](#string) | Path to the vulnerability's details page. | diff --git a/doc/ci/yaml/index.md b/doc/ci/yaml/index.md index c1fc24863db..9ce807cf5d6 100644 --- a/doc/ci/yaml/index.md +++ b/doc/ci/yaml/index.md @@ -2867,6 +2867,8 @@ You must: ### `parallel` +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/336576) in GitLab 15.9, the maximum value for `parallel` is increased from 50 to 200. + Use `parallel` to run a job multiple times in parallel in a single pipeline. Multiple runners must exist, or a single runner must be configured to run multiple jobs concurrently. @@ -2877,7 +2879,7 @@ Parallel jobs are named sequentially from `job_name 1/N` to `job_name N/N`. **Possible inputs**: -- A numeric value from `2` to `50`. +- A numeric value from `2` to `200`. **Example of `parallel`**: @@ -2902,6 +2904,7 @@ This example creates 5 jobs that run in parallel, named `test 1/5` to `test 5/5` > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/15356) in GitLab 13.3. > - The job naming style was [improved in GitLab 13.4](https://gitlab.com/gitlab-org/gitlab/-/issues/230452). +> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/336576) in GitLab 15.9, the maximum number of permutations is increased from 50 to 200. Use `parallel:matrix` to run a job multiple times in parallel in a single pipeline, but with different variable values for each instance of the job. @@ -2914,7 +2917,7 @@ Multiple runners must exist, or a single runner must be configured to run multip - The variable names can use only numbers, letters, and underscores (`_`). - The values must be either a string, or an array of strings. -- The number of permutations cannot exceed 50. +- The number of permutations cannot exceed 200. **Example of `parallel:matrix`**: diff --git a/doc/development/integrations/secure.md b/doc/development/integrations/secure.md index 190a6f6eda2..f5bb2df2494 100644 --- a/doc/development/integrations/secure.md +++ b/doc/development/integrations/secure.md @@ -350,9 +350,9 @@ version is 14.0.6, the report is validated against version 14.0.6. GitLab uses the [`json_schemer`](https://www.rubydoc.info/gems/json_schemer) gem to perform validation. -Ongoing improvements to report validation are tracked [in this epic](https://gitlab.com/groups/gitlab-org/-/epics/6968). +Ongoing improvements to report validation are tracked [in this epic](https://gitlab.com/groups/gitlab-org/-/epics/8900). In the meantime, you can see which versions are supported in the -[source code](https://gitlab.com/gitlab-org/gitlab/-/blob/08dd756429731a0cca1e27ca9d59eea226398a7d/lib/gitlab/ci/parsers/security/validators/schema_validator.rb#L9-27). +[source code](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/parsers/security/validators/schema_validator.rb#L9). Remember to pick the correct version for your instance, for example [`v15.7.3-ee`](https://gitlab.com/gitlab-org/gitlab/-/blob/v15.7.3-ee/lib/gitlab/ci/parsers/security/validators/schema_validator.rb#L9). #### Validate locally diff --git a/doc/user/group/import/index.md b/doc/user/group/import/index.md index d5ad78b6e2c..e4141f3ac04 100644 --- a/doc/user/group/import/index.md +++ b/doc/user/group/import/index.md @@ -61,6 +61,11 @@ You can migrate: - By direct transfer using either the UI or the [API](../../../api/bulk_imports.md). - Many groups at once. +You can migrate: + +- With projects - this feature is in [Beta](../../../policy/alpha-beta-support.md#beta-features) and not ready for production use. +- Without projects. + Not all group and project resources are imported. See list of migrated resources below: - [Migrated group items](#migrated-group-items). @@ -107,7 +112,7 @@ After you have authorized access to the source GitLab instance, you are redirect importer page. The top-level groups on the connected source instance you have the Owner role for are listed. 1. By default, the proposed group namespaces match the names as they exist in source instance, but based on your permissions, you can choose to edit these names before you proceed to import any of them. -1. Next to the groups you want to import, select **Import**. +1. Next to the groups you want to import, select from the dropdown **Import with projects** or **Import without projects**. Remember that importing projects is in [Beta](../../../policy/alpha-beta-support.md#beta-features). This feature is not ready for production use. On self-manage GitLab, if `bulk_import_projects` feature flag is disabled and the feature is not available, select **Import**. 1. The **Status** column shows the import status of each group. If you leave the page open, it updates in real-time. 1. After a group has been imported, select its GitLab path to open its GitLab URL. diff --git a/doc/user/packages/package_registry/index.md b/doc/user/packages/package_registry/index.md index caa305999c5..ab5d652b731 100644 --- a/doc/user/packages/package_registry/index.md +++ b/doc/user/packages/package_registry/index.md @@ -151,8 +151,6 @@ Several known issues exist when you allow anyone to pull from the Package Regist - Project-level endpoints are supported. Group-level and instance-level endpoints are not supported. Support for group-level endpoints is proposed in [issue 383537](https://gitlab.com/gitlab-org/gitlab/-/issues/383537). - It does not work with the [Composer](../composer_repository/index.md#install-a-composer-package), because Composer only has a group endpoint. -- It does not work with the [Debian](../debian_repository/index.md#install-a-package) repository. Support for the Debian repository is proposed in [issue 385258](https://gitlab.com/gitlab-org/gitlab/-/issues/385258). -- It does not work with the [Ruby gems](../rubygems_registry/index.md#install-a-ruby-gem) repository. Support for the Ruby gems repository is proposed in [issue 385259](https://gitlab.com/gitlab-org/gitlab/-/issues/385259). - It will work with Conan, but using [`conan search`](../conan_repository/index.md#search-for-conan-packages-in-the-package-registry) does not work. ## Accepting contributions diff --git a/doc/user/packages/package_registry/supported_functionality.md b/doc/user/packages/package_registry/supported_functionality.md new file mode 100644 index 00000000000..3d92836ed82 --- /dev/null +++ b/doc/user/packages/package_registry/supported_functionality.md @@ -0,0 +1,126 @@ +--- +stage: Package +group: Package Registry +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments +--- + +# Supported package functionality + +The GitLab Package Registry supports different functionalities for each package type. This support includes publishing +and pulling packages, request forwarding, managing duplicates, and authentication. + +## Publishing packages **(FREE)** + +Packages can be published to your project, group, or instance. + +| Package type | Project | Group | Instance | +|-----------------------------------------------------|---------|-------|----------| +| [Maven](../maven_repository/index.md) | Y | N | N | +| [npm](../npm_registry/index.md) | Y | N | N | +| [NuGet](../nuget_repository/index.md) | Y | N | N | +| [PyPI](../pypi_repository/index.md) | Y | N | N | +| [Generic packages](../generic_packages/index.md) | Y | N | N | +| [Terraform](../terraform_module_registry/index.md) | Y | N | N | +| [Composer](../composer_repository/index.md) | N | Y | N | +| [Conan](../conan_repository/index.md) | Y | N | N | +| [Helm](../helm_repository/index.md) | Y | N | N | +| [Debian](../debian_repository/index.md) | Y | N | N | +| [Go](../go_proxy/index.md) | Y | N | Y | +| [Ruby gems](../rubygems_registry/index.md) | Y | N | N | + +## Pulling packages **(FREE)** + +Packages can be pulled from your project, group, or instance. + +| Package type | Project | Group | Instance | +|-----------------------------------------------------|---------|-------|----------| +| [Maven](../maven_repository/index.md) | Y | Y | Y | +| [npm](../npm_registry/index.md) | Y | Y | Y | +| [NuGet](../nuget_repository/index.md) | Y | Y | N | +| [PyPI](../pypi_repository/index.md) | Y | Y | N | +| [Generic packages](../generic_packages/index.md) | Y | N | N | +| [Terraform](../terraform_module_registry/index.md) | N | Y | N | +| [Composer](../composer_repository/index.md) | Y | Y | N | +| [Conan](../conan_repository/index.md) | Y | N | Y | +| [Helm](../helm_repository/index.md) | Y | N | N | +| [Debian](../debian_repository/index.md) | Y | N | N | +| [Go](../go_proxy/index.md) | Y | N | Y | +| [Ruby gems](../rubygems_registry/index.md) | Y | N | N | + +## Forwarding requests **(PREMIUM)** + +Requests for packages not found in your GitLab project are forwarded to the public registry. For example, Maven Central, npmjs, or PyPI. + +| Package type | Supports request forwarding | +|-----------------------------------------------------|-----------------------------| +| [Maven](../maven_repository/index.md) | Y | +| [npm](../npm_registry/index.md) | Y | +| [NuGet](../nuget_repository/index.md) | N | +| [PyPI](../pypi_repository/index.md) | Y | +| [Generic packages](../generic_packages/index.md) | N | +| [Terraform](../terraform_module_registry/index.md) | N | +| [Composer](../composer_repository/index.md) | N | +| [Conan](../conan_repository/index.md) | N | +| [Helm](../helm_repository/index.md) | N | +| [Debian](../debian_repository/index.md) | N | +| [Go](../go_proxy/index.md) | N | +| [Ruby gems](../rubygems_registry/index.md) | N | + +## Allow or prevent duplicates **(FREE)** + +By default, the GitLab package registry either allows or prevents duplicates based on the default of that specific package manager format. + +| Package type | Duplicates allowed? | +|-----------------------------------------------------|---------------------| +| [Maven](../maven_repository/index.md) | Y (configurable) | +| [npm](../npm_registry/index.md) | N | +| [NuGet](../nuget_repository/index.md) | Y | +| [PyPI](../pypi_repository/index.md) | N | +| [Generic packages](../generic_packages/index.md) | Y (configurable) | +| [Terraform](../terraform_module_registry/index.md) | N | +| [Composer](../composer_repository/index.md) | N | +| [Conan](../conan_repository/index.md) | N | +| [Helm](../helm_repository/index.md) | Y | +| [Debian](../debian_repository/index.md) | Y | +| [Go](../go_proxy/index.md) | N | +| [Ruby gems](../rubygems_registry/index.md) | Y | + +## Authentication tokens **(FREE)** + +GitLab tokens are used to authenticate with the GitLab Package Registry. + +The following tokens are supported: + +| Package type | Supported tokens | +|-----------------------------------------------------|------------------------------------------------------------------------| +| [Maven](../maven_repository/index.md) | Personal access, job tokens, deploy (project or group), project access | +| [npm](../npm_registry/index.md) | Personal access, job tokens, deploy (project or group), project access | +| [NuGet](../nuget_repository/index.md) | Personal access, job tokens, deploy (project or group), project access | +| [PyPI](../pypi_repository/index.md) | Personal access, job tokens, deploy (project or group), project access | +| [Generic packages](../generic_packages/index.md) | Personal access, job tokens, deploy (project or group), project access | +| [Terraform](../terraform_module_registry/index.md) | Personal access, job tokens, deploy (project or group), project access | +| [Composer](../composer_repository/index.md) | Personal access, job tokens, deploy (project or group), project access | +| [Conan](../conan_repository/index.md) | Personal access, job tokens, project access | +| [Helm](../helm_repository/index.md) | Personal access, job tokens, deploy (project or group) | +| [Debian](../debian_repository/index.md) | Personal access, job tokens, deploy (project or group) | +| [Go](../go_proxy/index.md) | Personal access, job tokens, project access | +| [Ruby gems](../rubygems_registry/index.md) | Personal access, job tokens, deploy (project or group) | + +## Authentication protocols **(FREE)** + +The following authentication protocols are supported: + +| Package type | Supported auth protocols | +|-----------------------------------------------------|--------------------------| +| [Maven](../maven_repository/index.md) | Headers | +| [npm](../npm_registry/index.md) | OAuth | +| [NuGet](../nuget_repository/index.md) | Basic auth | +| [PyPI](../pypi_repository/index.md) | Basic auth | +| [Generic packages](../generic_packages/index.md) | Basic auth | +| [Terraform](../terraform_module_registry/index.md) | Token | +| [Composer](../composer_repository/index.md) | OAuth | +| [Conan](../conan_repository/index.md) | OAuth, Basic auth | +| [Helm](../helm_repository/index.md) | Basic auth | +| [Debian](../debian_repository/index.md) | Basic auth | +| [Go](../go_proxy/index.md) | Basic auth | +| [Ruby gems](../rubygems_registry/index.md) | Token | diff --git a/lib/gitlab/ci/config/entry/product/parallel.rb b/lib/gitlab/ci/config/entry/product/parallel.rb index 5c78a8f68c7..e91714e3f5c 100644 --- a/lib/gitlab/ci/config/entry/product/parallel.rb +++ b/lib/gitlab/ci/config/entry/product/parallel.rb @@ -12,7 +12,7 @@ module Gitlab strategy :ParallelBuilds, if: -> (config) { config.is_a?(Numeric) } strategy :MatrixBuilds, if: -> (config) { config.is_a?(Hash) } - PARALLEL_LIMIT = 50 + PARALLEL_LIMIT = 200 class ParallelBuilds < ::Gitlab::Config::Entry::Node include ::Gitlab::Config::Entry::Validatable diff --git a/locale/gitlab.pot b/locale/gitlab.pot index da7029d383b..6685f71f073 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -2270,9 +2270,6 @@ msgstr "" msgid "Add approvers" msgstr "" -msgid "Add broadcast message" -msgstr "" - msgid "Add child epic to an epic" msgstr "" @@ -4130,9 +4127,6 @@ msgstr "" msgid "Allow use of licensed EE features" msgstr "" -msgid "Allow users to dismiss the broadcast message" -msgstr "" - msgid "Allow users to register any application to use GitLab as an OAuth provider" msgstr "" @@ -4508,9 +4502,6 @@ msgstr "" msgid "An error occurred while removing issues." msgstr "" -msgid "An error occurred while rendering preview broadcast message" -msgstr "" - msgid "An error occurred while rendering the editor" msgstr "" @@ -7444,6 +7435,15 @@ msgstr "" msgid "BulkImport|Import selected" msgstr "" +msgid "BulkImport|Import with projects" +msgstr "" + +msgid "BulkImport|Import without projects" +msgstr "" + +msgid "BulkImport|Importing projects is a %{docsLinkStart}Beta%{docsLinkEnd} feature." +msgstr "" + msgid "BulkImport|Importing the group failed." msgstr "" @@ -14662,9 +14662,6 @@ msgstr "" msgid "Dismiss trial promotion" msgstr "" -msgid "Dismissable" -msgstr "" - msgid "Dismissed" msgstr "" @@ -15517,9 +15514,6 @@ msgstr "" msgid "Ends" msgstr "" -msgid "Ends at (UTC)" -msgstr "" - msgid "Ends on" msgstr "" @@ -21218,6 +21212,12 @@ msgstr "" msgid "Import timed out. Import took longer than %{import_jobs_expiration} seconds" msgstr "" +msgid "Import with projects" +msgstr "" + +msgid "Import without projects" +msgstr "" + msgid "ImportAProjectModal|Import from a project" msgstr "" @@ -30172,9 +30172,6 @@ msgstr "" msgid "Path:" msgstr "" -msgid "Paths can contain wildcards, like */welcome" -msgstr "" - msgid "Paths to protect with rate limiting" msgstr "" @@ -34612,6 +34609,12 @@ msgstr "" msgid "Re-import" msgstr "" +msgid "Re-import with projects" +msgstr "" + +msgid "Re-import without projects" +msgstr "" + msgid "Re-request review" msgstr "" @@ -40307,9 +40310,6 @@ msgstr "" msgid "Starts %{startsIn}" msgstr "" -msgid "Starts at (UTC)" -msgstr "" - msgid "Starts escalations for this incident" msgstr "" @@ -41936,9 +41936,6 @@ msgstr "" msgid "The branch or tag does not exist" msgstr "" -msgid "The broadcast message displays only to users in projects and groups who have these roles." -msgstr "" - msgid "The character highlighter helps you keep the subject line to %{titleLength} characters and wrap the body at %{bodyLength} so they are readable in git." msgstr "" @@ -42405,9 +42402,6 @@ msgstr "" msgid "The vulnerability is no longer detected. Verify the vulnerability has been remediated before changing its status." msgstr "" -msgid "Theme" -msgstr "" - msgid "There are currently no mirrored repositories." msgstr "" @@ -44987,9 +44981,6 @@ msgstr "" msgid "Update approvers" msgstr "" -msgid "Update broadcast message" -msgstr "" - msgid "Update failed" msgstr "" diff --git a/spec/features/admin/admin_broadcast_messages_spec.rb b/spec/features/admin/admin_broadcast_messages_spec.rb deleted file mode 100644 index a6bbdd70fc3..00000000000 --- a/spec/features/admin/admin_broadcast_messages_spec.rb +++ /dev/null @@ -1,98 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'Admin Broadcast Messages', feature_category: :onboarding do - before do - admin = create(:admin) - sign_in(admin) - stub_feature_flags(vue_broadcast_messages: false) - gitlab_enable_admin_mode_sign_in(admin) - create( - :broadcast_message, - :expired, - message: 'Migration to new server', - target_access_levels: [Gitlab::Access::DEVELOPER] - ) - visit admin_broadcast_messages_path - end - - it 'see broadcast messages list' do - expect(page).to have_content 'Migration to new server' - end - - it 'creates a customized broadcast banner message' do - fill_in 'broadcast_message_message', with: 'Application update from **4:00 CST to 5:00 CST**' - fill_in 'broadcast_message_target_path', with: '*/user_onboarded' - select 'light-indigo', from: 'broadcast_message_theme' - select Date.today.next_year.year, from: 'broadcast_message_ends_at_1i' - check 'Guest' - check 'Owner' - click_button 'Add broadcast message' - - expect(page).to have_current_path admin_broadcast_messages_path, ignore_query: true - expect(page).to have_content 'Application update from 4:00 CST to 5:00 CST' - expect(page).to have_content 'Guest, Owner' - expect(page).to have_content '*/user_onboarded' - expect(page).to have_selector 'strong', text: '4:00 CST to 5:00 CST' - expect(page).to have_selector %(.light-indigo[role=alert]) - end - - it 'creates a customized broadcast notification message' do - fill_in 'broadcast_message_message', with: 'Application update from **4:00 CST to 5:00 CST**' - fill_in 'broadcast_message_target_path', with: '*/user_onboarded' - select 'Notification', from: 'broadcast_message_broadcast_type' - select Date.today.next_year.year, from: 'broadcast_message_ends_at_1i' - check 'Reporter' - check 'Developer' - check 'Maintainer' - click_button 'Add broadcast message' - - expect(page).to have_current_path admin_broadcast_messages_path, ignore_query: true - expect(page).to have_content 'Application update from 4:00 CST to 5:00 CST' - expect(page).to have_content 'Reporter, Developer, Maintainer' - expect(page).to have_content '*/user_onboarded' - expect(page).to have_content 'Notification' - expect(page).to have_selector 'strong', text: '4:00 CST to 5:00 CST' - end - - it 'edit an existing broadcast message' do - click_link 'Edit' - fill_in 'broadcast_message_message', with: 'Application update RIGHT NOW' - check 'Reporter' - click_button 'Update broadcast message' - - expect(page).to have_current_path admin_broadcast_messages_path, ignore_query: true - expect(page).to have_content 'Application update RIGHT NOW' - - page.within('.table-responsive') do - expect(page).to have_content 'Reporter, Developer' - end - end - - it 'remove an existing broadcast message' do - click_link 'Remove' - - expect(page).to have_current_path admin_broadcast_messages_path, ignore_query: true - expect(page).not_to have_content 'Migration to new server' - end - - it 'updates a preview of a customized broadcast banner message', :js do - fill_in 'broadcast_message_message', with: "Live **Markdown** previews. :tada:" - - page.within('.js-broadcast-banner-message-preview') do - expect(page).to have_selector('strong', text: 'Markdown') - expect(page).to have_emoji('tada') - end - end - - it 'updates a preview of a customized broadcast notification message', :js do - fill_in 'broadcast_message_message', with: "Live **Markdown** previews. :tada:" - select 'Notification', from: 'broadcast_message_broadcast_type' - - page.within('#broadcast-message-preview') do - expect(page).to have_selector('strong', text: 'Markdown') - expect(page).to have_emoji('tada') - end - end -end diff --git a/spec/frontend/import_entities/import_groups/components/import_actions_cell_spec.js b/spec/frontend/import_entities/import_groups/components/import_actions_cell_spec.js index cd56f573011..da7fb4e060d 100644 --- a/spec/frontend/import_entities/import_groups/components/import_actions_cell_spec.js +++ b/spec/frontend/import_entities/import_groups/components/import_actions_cell_spec.js @@ -1,4 +1,4 @@ -import { GlButton, GlIcon } from '@gitlab/ui'; +import { GlButton, GlIcon, GlDropdown, GlDropdownItem } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; import ImportActionsCell from '~/import_entities/import_groups/components/import_actions_cell.vue'; @@ -8,6 +8,7 @@ describe('import actions cell', () => { const createComponent = (props) => { wrapper = shallowMount(ImportActionsCell, { propsData: { + isProjectsImportEnabled: false, isFinished: false, isAvailableForImport: false, isInvalid: false, @@ -78,4 +79,39 @@ describe('import actions cell', () => { expect(wrapper.emitted('import-group')).toHaveLength(1); }); + + describe.each` + isFinished | expectedAction + ${false} | ${'Import'} + ${true} | ${'Re-import'} + `( + 'when import projects is enabled, group is available for import and finish status is $status', + ({ isFinished, expectedAction }) => { + beforeEach(() => { + createComponent({ isProjectsImportEnabled: true, isAvailableForImport: true, isFinished }); + }); + + it('render import dropdown', () => { + const dropdown = wrapper.findComponent(GlDropdown); + expect(dropdown.props('text')).toBe(`${expectedAction} with projects`); + expect(dropdown.findComponent(GlDropdownItem).text()).toBe( + `${expectedAction} without projects`, + ); + }); + + it('request migrate projects by default', async () => { + const dropdown = wrapper.findComponent(GlDropdown); + dropdown.vm.$emit('click'); + + expect(wrapper.emitted('import-group')[0]).toStrictEqual([{ migrateProjects: true }]); + }); + + it('request not to migrate projects via dropdown option', async () => { + const dropdown = wrapper.findComponent(GlDropdown); + dropdown.findComponent(GlDropdownItem).vm.$emit('click'); + + expect(wrapper.emitted('import-group')[0]).toStrictEqual([{ migrateProjects: false }]); + }); + }, + ); }); diff --git a/spec/frontend/import_entities/import_groups/components/import_table_spec.js b/spec/frontend/import_entities/import_groups/components/import_table_spec.js index d094fbb0e9d..bd79e20e698 100644 --- a/spec/frontend/import_entities/import_groups/components/import_table_spec.js +++ b/spec/frontend/import_entities/import_groups/components/import_table_spec.js @@ -49,6 +49,8 @@ describe('import table', () => { const findImportSelectedButton = () => wrapper.findAll('button').wrappers.find((w) => w.text() === 'Import selected'); + const findImportSelectedDropdown = () => + wrapper.findAll('.gl-dropdown').wrappers.find((w) => w.text().includes('Import with projects')); const findImportButtons = () => wrapper.findAll('button').wrappers.filter((w) => w.text() === 'Import'); const findPaginationDropdown = () => wrapper.find('[data-testid="page-size"]'); @@ -64,7 +66,12 @@ describe('import table', () => { const selectRow = (idx) => wrapper.findAll('tbody td input[type=checkbox]').at(idx).setChecked(true); - const createComponent = ({ bulkImportSourceGroups, importGroups, defaultTargetNamespace }) => { + const createComponent = ({ + bulkImportSourceGroups, + importGroups, + defaultTargetNamespace, + glFeatures = {}, + }) => { apolloProvider = createMockApollo( [ [ @@ -93,6 +100,9 @@ describe('import table', () => { directives: { GlTooltip: createMockDirective(), }, + provide: { + glFeatures, + }, apolloProvider, }); }; @@ -530,16 +540,16 @@ describe('import table', () => { mutation: importGroupsMutation, variables: { importRequests: [ - { + expect.objectContaining({ targetNamespace: AVAILABLE_NAMESPACES[0].fullPath, newName: NEW_GROUPS[0].lastImportTarget.newName, sourceGroupId: NEW_GROUPS[0].id, - }, - { + }), + expect.objectContaining({ targetNamespace: AVAILABLE_NAMESPACES[0].fullPath, newName: NEW_GROUPS[1].lastImportTarget.newName, sourceGroupId: NEW_GROUPS[1].id, - }, + }), ], }, }); @@ -610,4 +620,83 @@ describe('import table', () => { expect(wrapper.findComponent(GlAlert).exists()).toBe(false); }); }); + + describe('when import projects is enabled', () => { + const NEW_GROUPS = [ + generateFakeEntry({ id: 1, status: STATUSES.NONE }), + generateFakeEntry({ id: 2, status: STATUSES.NONE }), + generateFakeEntry({ id: 3, status: STATUSES.FINISHED }), + ]; + + beforeEach(() => { + createComponent({ + bulkImportSourceGroups: () => ({ + nodes: NEW_GROUPS, + pageInfo: FAKE_PAGE_INFO, + versionValidation: FAKE_VERSION_VALIDATION, + }), + glFeatures: { + bulkImportProjects: true, + }, + }); + jest.spyOn(apolloProvider.defaultClient, 'mutate'); + return waitForPromises(); + }); + + it('renders import all dropdown', async () => { + expect(findImportSelectedDropdown().exists()).toBe(true); + }); + + it('includes migrateProjects: true when dropdown is clicked', async () => { + await selectRow(0); + await selectRow(1); + await nextTick(); + await findImportSelectedDropdown().find('button').trigger('click'); + expect(apolloProvider.defaultClient.mutate).toHaveBeenCalledWith({ + mutation: importGroupsMutation, + variables: { + importRequests: [ + expect.objectContaining({ + targetNamespace: AVAILABLE_NAMESPACES[0].fullPath, + newName: NEW_GROUPS[0].lastImportTarget.newName, + sourceGroupId: NEW_GROUPS[0].id, + migrateProjects: true, + }), + expect.objectContaining({ + targetNamespace: AVAILABLE_NAMESPACES[0].fullPath, + newName: NEW_GROUPS[1].lastImportTarget.newName, + sourceGroupId: NEW_GROUPS[1].id, + migrateProjects: true, + }), + ], + }, + }); + }); + + it('includes migrateProjects: false when dropdown item is clicked', async () => { + await selectRow(0); + await selectRow(1); + await nextTick(); + await findImportSelectedDropdown().find('.gl-dropdown-item button').trigger('click'); + expect(apolloProvider.defaultClient.mutate).toHaveBeenCalledWith({ + mutation: importGroupsMutation, + variables: { + importRequests: [ + expect.objectContaining({ + targetNamespace: AVAILABLE_NAMESPACES[0].fullPath, + newName: NEW_GROUPS[0].lastImportTarget.newName, + sourceGroupId: NEW_GROUPS[0].id, + migrateProjects: false, + }), + expect.objectContaining({ + targetNamespace: AVAILABLE_NAMESPACES[0].fullPath, + newName: NEW_GROUPS[1].lastImportTarget.newName, + sourceGroupId: NEW_GROUPS[1].id, + migrateProjects: false, + }), + ], + }, + }); + }); + }); }); diff --git a/spec/lib/gitlab/ci/config/entry/product/matrix_spec.rb b/spec/lib/gitlab/ci/config/entry/product/matrix_spec.rb index 394d91466bf..cbd3109522c 100644 --- a/spec/lib/gitlab/ci/config/entry/product/matrix_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/product/matrix_spec.rb @@ -29,7 +29,7 @@ RSpec.describe ::Gitlab::Ci::Config::Entry::Product::Matrix do [ { 'VAR_1' => (1..10).to_a, - 'VAR_2' => (11..20).to_a + 'VAR_2' => (11..31).to_a } ] end @@ -41,7 +41,7 @@ RSpec.describe ::Gitlab::Ci::Config::Entry::Product::Matrix do describe '#errors' do it 'returns error about too many jobs' do expect(matrix.errors) - .to include('matrix config generates too many jobs (maximum is 50)') + .to include('matrix config generates too many jobs (maximum is 200)') end end end diff --git a/spec/lib/gitlab/ci/config/entry/product/parallel_spec.rb b/spec/lib/gitlab/ci/config/entry/product/parallel_spec.rb index a16f1cf9e43..ec21519a8f6 100644 --- a/spec/lib/gitlab/ci/config/entry/product/parallel_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/product/parallel_spec.rb @@ -33,10 +33,10 @@ RSpec.describe ::Gitlab::Ci::Config::Entry::Product::Parallel do it_behaves_like 'invalid config', /must be greater than or equal to 2/ end - context 'when it is bigger than 50' do - let(:config) { 51 } + context 'when it is bigger than 200' do + let(:config) { 201 } - it_behaves_like 'invalid config', /must be less than or equal to 50/ + it_behaves_like 'invalid config', /must be less than or equal to 200/ end context 'when it is not an integer' do diff --git a/spec/views/admin/broadcast_messages/index.html.haml_spec.rb b/spec/views/admin/broadcast_messages/index.html.haml_spec.rb deleted file mode 100644 index ba998085bf9..00000000000 --- a/spec/views/admin/broadcast_messages/index.html.haml_spec.rb +++ /dev/null @@ -1,52 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'admin/broadcast_messages/index' do - let(:role_targeted_broadcast_messages) { true } - let(:vue_broadcast_messages) { false } - - let_it_be(:message) { create(:broadcast_message, broadcast_type: 'banner', target_access_levels: [Gitlab::Access::GUEST, Gitlab::Access::DEVELOPER]) } - - before do - assign(:broadcast_messages, BroadcastMessage.page(1)) - assign(:broadcast_message, BroadcastMessage.new) - - stub_feature_flags(role_targeted_broadcast_messages: role_targeted_broadcast_messages) - stub_feature_flags(vue_broadcast_messages: vue_broadcast_messages) - - render - end - - describe 'Target roles select and table column' do - it 'rendered' do - expect(rendered).to have_content('Target roles') - expect(rendered).to have_content('Owner') - expect(rendered).to have_content('Guest, Developer') - end - - context 'when feature flag is off' do - let(:role_targeted_broadcast_messages) { false } - - it 'is not rendered' do - expect(rendered).not_to have_content('Target roles') - expect(rendered).not_to have_content('Owner') - expect(rendered).not_to have_content('Guest, Developer') - end - end - end - - describe 'Vue application' do - it 'is not rendered' do - expect(rendered).not_to have_selector('#js-broadcast-messages') - end - - context 'when feature flag is on' do - let(:vue_broadcast_messages) { true } - - it 'is rendered' do - expect(rendered).to have_selector('#js-broadcast-messages') - end - end - end -end |