diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-04-19 15:15:59 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-04-19 15:15:59 +0300 |
commit | 2017bc90a671eac669f0114b6ef508e151409c4f (patch) | |
tree | 79bbbedede417d3ce13ae2e13dd1ad3bb069c975 /app | |
parent | 9450a63064cd1572f030628dbf155f5c047f28c7 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
32 files changed, 226 insertions, 244 deletions
diff --git a/app/assets/javascripts/admin/users/components/user_actions.vue b/app/assets/javascripts/admin/users/components/user_actions.vue index e21bb58cb4e..38c7d3f9b90 100644 --- a/app/assets/javascripts/admin/users/components/user_actions.vue +++ b/app/assets/javascripts/admin/users/components/user_actions.vue @@ -61,6 +61,9 @@ export default { hasEditAction() { return this.userActions.includes('edit'); }, + hasEditActionOnly() { + return this.hasEditAction === true && this.hasDeleteActions === false; + }, userPaths() { return generateUserPaths(this.paths, this.user.username); }, @@ -91,10 +94,13 @@ export default { class="gl-display-flex gl-justify-content-end gl-my-n2 gl-mx-n2" :data-testid="`user-actions-${user.id}`" > - <div v-if="hasEditAction" class="gl-p-2"> - <gl-button v-if="showButtonLabels" v-bind="editButtonAttrs" icon="pencil-square">{{ - $options.i18n.edit - }}</gl-button> + <div v-if="hasEditAction" class="gl-p-2" :class="{ 'gl-mr-3': hasEditActionOnly }"> + <gl-button + v-if="showButtonLabels" + v-bind="editButtonAttrs" + :class="{ 'gl-mr-7': hasEditActionOnly }" + >{{ $options.i18n.edit }}</gl-button + > <gl-button v-else v-gl-tooltip="$options.i18n.edit" @@ -106,11 +112,14 @@ export default { <div v-if="hasDropdownActions" class="gl-p-2"> <gl-disclosure-dropdown + icon="ellipsis_v" + category="tertiary" :toggle-text="$options.i18n.userAdministration" + text-sr-only data-testid="dropdown-toggle" data-qa-selector="user_actions_dropdown_toggle" :data-qa-username="user.username" - placement="left" + no-caret > <template v-for="action in dropdownSafeActions"> <component diff --git a/app/assets/javascripts/admin/users/components/users_table.vue b/app/assets/javascripts/admin/users/components/users_table.vue index e55622d40ba..2d2c598f953 100644 --- a/app/assets/javascripts/admin/users/components/users_table.vue +++ b/app/assets/javascripts/admin/users/components/users_table.vue @@ -135,7 +135,7 @@ export default { </template> <template #cell(settings)="{ item: user }"> - <user-actions :user="user" :paths="paths" /> + <user-actions :user="user" :paths="paths" :show-button-labels="true" /> </template> </gl-table> </div> diff --git a/app/assets/javascripts/diffs/components/diff_view.vue b/app/assets/javascripts/diffs/components/diff_view.vue index a2e052e0f93..348d6d1d78d 100644 --- a/app/assets/javascripts/diffs/components/diff_view.vue +++ b/app/assets/javascripts/diffs/components/diff_view.vue @@ -1,7 +1,6 @@ <script> import { mapGetters, mapState, mapActions } from 'vuex'; import { IdState } from 'vendor/vue-virtual-scroller'; -import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import DraftNote from '~/batch_comments/components/draft_note.vue'; import draftCommentsMixin from '~/diffs/mixins/draft_comments'; import { getCommentedLines } from '~/notes/components/multiline_comment_utils'; @@ -21,11 +20,7 @@ export default { DiffCommentCell, DraftNote, }, - mixins: [ - draftCommentsMixin, - IdState({ idProp: (vm) => vm.diffFile.file_hash }), - glFeatureFlagsMixin(), - ], + mixins: [draftCommentsMixin, IdState({ idProp: (vm) => vm.diffFile.file_hash })], props: { diffFile: { type: Object, @@ -265,10 +260,7 @@ export default { @stopdragging="onStopDragging" /> <diff-line - v-if=" - glFeatures.refactorCodeQualityInlineFindings && - codeQualityExpandedLines.includes(getCodeQualityLine(line)) - " + v-if="codeQualityExpandedLines.includes(getCodeQualityLine(line))" :key="line.line_code" :line="line" @hideCodeQualityFindings="hideCodeQualityFindings" diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_operations.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_operations.vue index ffb8c0eb88c..caeee7edefe 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_operations.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_operations.vue @@ -1,12 +1,10 @@ <script> import { GlButton, GlTooltipDirective, GlModalDirective } from '@gitlab/ui'; import Tracking from '~/tracking'; -import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import eventHub from '../../event_hub'; import { BUTTON_TOOLTIP_RETRY, BUTTON_TOOLTIP_CANCEL, TRACKING_CATEGORIES } from '../../constants'; import PipelineMultiActions from './pipeline_multi_actions.vue'; import PipelinesManualActions from './pipelines_manual_actions.vue'; -import PipelinesManualActionsLegacy from './pipelines_manual_actions_legacy.vue'; export default { BUTTON_TOOLTIP_RETRY, @@ -19,9 +17,8 @@ export default { GlButton, PipelineMultiActions, PipelinesManualActions, - PipelinesManualActionsLegacy, }, - mixins: [Tracking.mixin(), glFeatureFlagsMixin()], + mixins: [Tracking.mixin()], props: { pipeline: { type: Object, @@ -39,21 +36,11 @@ export default { }; }, computed: { - shouldLazyLoadActions() { - return this.glFeatures.lazyLoadPipelineDropdownActions; - }, hasActions() { return ( this.pipeline?.details?.has_manual_actions || this.pipeline?.details?.has_scheduled_actions ); }, - actions() { - if (!this.pipeline || !this.pipeline.details) { - return []; - } - const { details } = this.pipeline; - return [...(details.manual_actions || []), ...(details.scheduled_actions || [])]; - }, isCancelling() { return this.cancelingPipeline === this.pipeline.id; }, @@ -86,12 +73,7 @@ export default { <template> <div class="gl-text-right"> <div class="btn-group"> - <pipelines-manual-actions v-if="hasActions && shouldLazyLoadActions" :iid="pipeline.iid" /> - - <pipelines-manual-actions-legacy - v-if="actions.length > 0 && !shouldLazyLoadActions" - :actions="actions" - /> + <pipelines-manual-actions v-if="hasActions" :iid="pipeline.iid" /> <gl-button v-if="pipeline.flags.retryable" diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_manual_actions_legacy.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_manual_actions_legacy.vue deleted file mode 100644 index b08eb4153ce..00000000000 --- a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_manual_actions_legacy.vue +++ /dev/null @@ -1,112 +0,0 @@ -<script> -import { GlDropdown, GlDropdownItem, GlIcon, GlTooltipDirective } from '@gitlab/ui'; -import { createAlert } from '~/alert'; -import axios from '~/lib/utils/axios_utils'; -import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal'; -import { s__, __, sprintf } from '~/locale'; -import Tracking from '~/tracking'; -import GlCountdown from '~/vue_shared/components/gl_countdown.vue'; -import eventHub from '../../event_hub'; -import { TRACKING_CATEGORIES } from '../../constants'; - -export default { - name: 'PipelinesManualActionsLegacy', - directives: { - GlTooltip: GlTooltipDirective, - }, - components: { - GlCountdown, - GlDropdown, - GlDropdownItem, - GlIcon, - }, - mixins: [Tracking.mixin()], - props: { - actions: { - type: Array, - required: true, - }, - }, - data() { - return { - isLoading: false, - }; - }, - methods: { - async onClickAction(action) { - if (action.scheduled_at) { - const confirmationMessage = sprintf( - s__( - 'DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes.', - ), - { jobName: action.name }, - ); - - const confirmed = await confirmAction(confirmationMessage); - - if (!confirmed) { - return; - } - } - - this.isLoading = true; - - /** - * Ideally, the component would not make an api call directly. - * However, in order to use the eventhub and know when to - * toggle back the `isLoading` property we'd need an ID - * to track the request with a wacther - since this component - * is rendered at least 20 times in the same page, moving the - * api call directly here is the most performant solution - */ - axios - .post(`${action.path}.json`) - .then(() => { - this.isLoading = false; - eventHub.$emit('updateTable'); - }) - .catch(() => { - this.isLoading = false; - createAlert({ message: __('An error occurred while making the request.') }); - }); - }, - isActionDisabled(action) { - if (action.playable === undefined) { - return false; - } - - return !action.playable; - }, - trackClick() { - this.track('click_manual_actions', { label: TRACKING_CATEGORIES.table }); - }, - }, -}; -</script> -<template> - <gl-dropdown - v-gl-tooltip - :title="__('Run manual or delayed jobs')" - :loading="isLoading" - data-testid="pipelines-manual-actions-dropdown" - right - lazy - icon="play" - @shown="trackClick" - > - <gl-dropdown-item - v-for="action in actions" - :key="action.path" - :disabled="isActionDisabled(action)" - @click="onClickAction(action)" - > - <div class="gl-display-flex gl-justify-content-space-between gl-flex-wrap"> - {{ action.name }} - <span v-if="action.scheduled_at"> - <gl-icon name="clock" /> - <gl-countdown :end-date-string="action.scheduled_at" /> - </span> - </div> - </gl-dropdown-item> - </gl-dropdown> -</template> diff --git a/app/assets/javascripts/search/sidebar/components/app.vue b/app/assets/javascripts/search/sidebar/components/app.vue index ca5a9b70b34..81a57e96b3c 100644 --- a/app/assets/javascripts/search/sidebar/components/app.vue +++ b/app/assets/javascripts/search/sidebar/components/app.vue @@ -1,6 +1,8 @@ <script> import { mapState, mapGetters } from 'vuex'; import ScopeNavigation from '~/search/sidebar/components/scope_navigation.vue'; +import ScopeNewNavigation from '~/search/sidebar/components/scope_new_navigation.vue'; +import SidebarPortal from '~/super_sidebar/components/sidebar_portal.vue'; import { SCOPE_ISSUES, SCOPE_MERGE_REQUESTS, SCOPE_BLOB } from '../constants'; import ResultsFilters from './results_filters.vue'; import LanguageFilter from './language_filter/index.vue'; @@ -10,10 +12,12 @@ export default { components: { ResultsFilters, ScopeNavigation, + ScopeNewNavigation, LanguageFilter, + SidebarPortal, }, computed: { - ...mapState(['urlQuery']), + ...mapState(['urlQuery', 'useNewNavigation']), ...mapGetters(['currentScope']), showIssueAndMergeFilters() { return this.currentScope === SCOPE_ISSUES || this.currentScope === SCOPE_MERGE_REQUESTS; @@ -26,7 +30,15 @@ export default { </script> <template> + <section v-if="useNewNavigation"> + <sidebar-portal> + <scope-new-navigation /> + <results-filters v-if="showIssueAndMergeFilters" /> + <language-filter v-if="showBlobFilter" /> + </sidebar-portal> + </section> <section + v-else class="search-sidebar gl-display-flex gl-flex-direction-column gl-md-mr-5 gl-mb-6 gl-mt-5" > <scope-navigation /> diff --git a/app/assets/javascripts/search/sidebar/components/scope_navigation.vue b/app/assets/javascripts/search/sidebar/components/scope_navigation.vue index 1c81f652387..fc41baee831 100644 --- a/app/assets/javascripts/search/sidebar/components/scope_navigation.vue +++ b/app/assets/javascripts/search/sidebar/components/scope_navigation.vue @@ -3,8 +3,8 @@ import { GlNav, GlNavItem, GlIcon } from '@gitlab/ui'; import { mapActions, mapState } from 'vuex'; import { s__ } from '~/locale'; import Tracking from '~/tracking'; +import { formatSearchResultCount, addCountOverLimit } from '~/search/store/utils'; import { NAV_LINK_DEFAULT_CLASSES, NAV_LINK_COUNT_DEFAULT_CLASSES } from '../constants'; -import { formatSearchResultCount } from '../../store/utils'; import { slugifyWithUnderscore } from '../../../lib/utils/text_utility'; export default { @@ -28,11 +28,11 @@ export default { }, methods: { ...mapActions(['fetchSidebarCount']), - showFormatedCount(count) { - return formatSearchResultCount(count); + showFormatedCount(countString) { + return formatSearchResultCount(countString); }, - isCountOverLimit(count) { - return count.includes('+'); + isCountOverLimit(countString) { + return Boolean(addCountOverLimit(countString)); }, handleClick(scope) { this.track('click_menu_item', { label: `vertical_navigation_${scope}` }); diff --git a/app/assets/javascripts/search/sidebar/components/scope_new_navigation.vue b/app/assets/javascripts/search/sidebar/components/scope_new_navigation.vue new file mode 100644 index 00000000000..86b7cc577a6 --- /dev/null +++ b/app/assets/javascripts/search/sidebar/components/scope_new_navigation.vue @@ -0,0 +1,40 @@ +<script> +import { mapActions, mapState, mapGetters } from 'vuex'; +import { s__ } from '~/locale'; +import Tracking from '~/tracking'; +import NavItem from '~/super_sidebar/components/nav_item.vue'; +import { NAV_LINK_DEFAULT_CLASSES, NAV_LINK_COUNT_DEFAULT_CLASSES } from '../constants'; + +export default { + name: 'ScopeNewNavigation', + i18n: { + countOverLimitLabel: s__('GlobalSearch|Result count is over limit.'), + }, + components: { + NavItem, + }, + mixins: [Tracking.mixin()], + computed: { + ...mapState(['navigation', 'urlQuery']), + ...mapGetters(['navigationItems']), + }, + created() { + if (this.urlQuery?.search) { + this.fetchSidebarCount(); + } + }, + methods: { + ...mapActions(['fetchSidebarCount']), + }, + NAV_LINK_DEFAULT_CLASSES, + NAV_LINK_COUNT_DEFAULT_CLASSES, +}; +</script> + +<template> + <nav data-testid="search-filter" class="gl-py-2 gl-relative"> + <ul class="gl-px-2 gl-list-style-none"> + <nav-item v-for="item in navigationItems" :key="`menu-${item.title}`" :item="item" /> + </ul> + </nav> +</template> diff --git a/app/assets/javascripts/search/sidebar/constants/index.js b/app/assets/javascripts/search/sidebar/constants/index.js index 9cd366a87fd..9519154a571 100644 --- a/app/assets/javascripts/search/sidebar/constants/index.js +++ b/app/assets/javascripts/search/sidebar/constants/index.js @@ -12,7 +12,7 @@ export const NAV_LINK_DEFAULT_CLASSES = [ 'gl-justify-content-space-between', ]; export const NAV_LINK_COUNT_DEFAULT_CLASSES = ['gl-font-sm', 'gl-font-weight-normal']; -export const HR_DEFAULT_CLASSES = ['gl-m-5', 'gl-border-gray-100']; +export const HR_DEFAULT_CLASSES = ['gl-my-5', 'gl-mx-5', 'gl-border-gray-100']; export const ONLY_SHOW_MD = ['gl-display-none', 'gl-md-display-block']; export const TRACKING_LABEL_CHECKBOX = 'Checkbox'; diff --git a/app/assets/javascripts/super_sidebar/components/create_menu.vue b/app/assets/javascripts/super_sidebar/components/create_menu.vue index d3bb31a69fa..4cff4642cf7 100644 --- a/app/assets/javascripts/super_sidebar/components/create_menu.vue +++ b/app/assets/javascripts/super_sidebar/components/create_menu.vue @@ -1,6 +1,10 @@ <script> import { GlDisclosureDropdown, GlTooltip } from '@gitlab/ui'; import { __ } from '~/locale'; +import { DROPDOWN_Y_OFFSET } from '../constants'; + +// Left offset required for the dropdown to be aligned with the super sidebar +const DROPDOWN_X_OFFSET = -147; export default { components: { @@ -16,7 +20,22 @@ export default { required: true, }, }, + data() { + return { + dropdownOpen: false, + }; + }, toggleId: 'create-menu-toggle', + popperOptions: { + modifiers: [ + { + name: 'offset', + options: { + offset: [DROPDOWN_X_OFFSET, DROPDOWN_Y_OFFSET], + }, + }, + ], + }, }; </script> @@ -30,9 +49,17 @@ export default { text-sr-only :toggle-text="$options.i18n.createNew" :toggle-id="$options.toggleId" + :popper-options="$options.popperOptions" data-qa-selector="new_menu_toggle" + @shown="dropdownOpen = true" + @hidden="dropdownOpen = false" /> - <gl-tooltip :target="`#${$options.toggleId}`" placement="bottom" container="#super-sidebar"> + <gl-tooltip + v-if="!dropdownOpen" + :target="`#${$options.toggleId}`" + placement="bottom" + container="#super-sidebar" + > {{ $options.i18n.createNew }} </gl-tooltip> </div> diff --git a/app/assets/javascripts/super_sidebar/components/help_center.vue b/app/assets/javascripts/super_sidebar/components/help_center.vue index 32836ecb4e6..01b214f4b2b 100644 --- a/app/assets/javascripts/super_sidebar/components/help_center.vue +++ b/app/assets/javascripts/super_sidebar/components/help_center.vue @@ -6,7 +6,10 @@ import { PROMO_URL } from 'jh_else_ce/lib/utils/url_utility'; import { __ } from '~/locale'; import { STORAGE_KEY } from '~/whats_new/utils/notification'; import Tracking from '~/tracking'; -import { HELP_MENU_TRACKING_DEFAULTS } from '../constants'; +import { DROPDOWN_Y_OFFSET, HELP_MENU_TRACKING_DEFAULTS } from '../constants'; + +// Left offset required for the dropdown to be aligned with the super sidebar +const DROPDOWN_X_OFFSET = -4; export default { components: { @@ -185,12 +188,23 @@ export default { }); }, }, + popperOptions: { + modifiers: [ + { + name: 'offset', + options: { + offset: [DROPDOWN_X_OFFSET, DROPDOWN_Y_OFFSET], + }, + }, + ], + }, }; </script> <template> <gl-disclosure-dropdown ref="dropdown" + :popper-options="$options.popperOptions" @shown="trackDropdownToggle(true)" @hidden="trackDropdownToggle(false)" > diff --git a/app/assets/javascripts/super_sidebar/components/nav_item.vue b/app/assets/javascripts/super_sidebar/components/nav_item.vue index 53698a808a7..223fbe6d078 100644 --- a/app/assets/javascripts/super_sidebar/components/nav_item.vue +++ b/app/assets/javascripts/super_sidebar/components/nav_item.vue @@ -160,7 +160,7 @@ export default { ></div> <div class="gl-flex-shrink-0 gl-w-6 gl-mx-3"> <slot name="icon"> - <gl-icon v-if="item.icon" :name="item.icon" class="gl-ml-2" /> + <gl-icon v-if="item.icon" :name="item.icon" class="gl-ml-2 item-icon" /> <gl-icon v-else-if="draggable" name="grip" diff --git a/app/assets/javascripts/super_sidebar/components/pinned_section.vue b/app/assets/javascripts/super_sidebar/components/pinned_section.vue index 3c12e193d80..9595bdfb632 100644 --- a/app/assets/javascripts/super_sidebar/components/pinned_section.vue +++ b/app/assets/javascripts/super_sidebar/components/pinned_section.vue @@ -71,7 +71,7 @@ export default { @click.prevent="expanded = !expanded" > <div class="gl-flex-shrink-0 gl-w-6 gl-mx-3"> - <gl-icon name="thumbtack" class="gl-ml-2" /> + <gl-icon name="thumbtack" class="gl-ml-2 item-icon" /> </div> <span class="gl-font-weight-bold gl-font-sm gl-flex-grow-1">{{ $options.i18n.pinned }}</span> diff --git a/app/assets/javascripts/super_sidebar/components/user_menu.vue b/app/assets/javascripts/super_sidebar/components/user_menu.vue index d8452900e34..c90d1ad9c3e 100644 --- a/app/assets/javascripts/super_sidebar/components/user_menu.vue +++ b/app/assets/javascripts/super_sidebar/components/user_menu.vue @@ -11,9 +11,12 @@ import { s__, __, sprintf } from '~/locale'; import NewNavToggle from '~/nav/components/new_nav_toggle.vue'; import Tracking from '~/tracking'; import PersistentUserCallout from '~/persistent_user_callout'; -import { USER_MENU_TRACKING_DEFAULTS } from '../constants'; +import { USER_MENU_TRACKING_DEFAULTS, DROPDOWN_Y_OFFSET } from '../constants'; import UserNameGroup from './user_name_group.vue'; +// Left offset required for the dropdown to be aligned with the super sidebar +const DROPDOWN_X_OFFSET = -211; + export default { feedbackUrl: 'https://gitlab.com/gitlab-org/gitlab/-/issues/403059', i18n: { @@ -216,6 +219,16 @@ export default { }); }, }, + popperOptions: { + modifiers: [ + { + name: 'offset', + options: { + offset: [DROPDOWN_X_OFFSET, DROPDOWN_Y_OFFSET], + }, + }, + ], + }, }; </script> @@ -223,7 +236,7 @@ export default { <div> <gl-disclosure-dropdown ref="userDropdown" - placement="right" + :popper-options="$options.popperOptions" data-testid="user-dropdown" data-qa-selector="user_menu" @shown="onShow" diff --git a/app/assets/javascripts/super_sidebar/constants.js b/app/assets/javascripts/super_sidebar/constants.js index 3dbd12b4ff7..5c4a6a9dfc1 100644 --- a/app/assets/javascripts/super_sidebar/constants.js +++ b/app/assets/javascripts/super_sidebar/constants.js @@ -33,3 +33,5 @@ export const HELP_MENU_TRACKING_DEFAULTS = { export const SIDEBAR_PINS_EXPANDED_COOKIE = 'sidebar_pinned_section_expanded'; export const SIDEBAR_COOKIE_EXPIRATION = 365 * 10; + +export const DROPDOWN_Y_OFFSET = 4; diff --git a/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals.vue b/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals.vue index c19dfe663f4..25cf5335fb5 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals.vue @@ -5,7 +5,7 @@ import { STATUS_MERGED } from '~/issues/constants'; import { BV_SHOW_MODAL } from '~/lib/utils/constants'; import { HTTP_STATUS_UNAUTHORIZED } from '~/lib/utils/http_status'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; -import { s__, __ } from '~/locale'; +import { s__, __, sprintf } from '~/locale'; import { getIdFromGraphQLId } from '~/graphql_shared/utils'; import eventHub from '../../event_hub'; import approvalsMixin from '../../mixins/approvals'; @@ -84,13 +84,22 @@ export default { return Boolean(this.action); }, invalidRules() { - return this.approvals.approvalState?.invalidApproversRules || []; + return this.approvals.approvalState?.rules?.filter((rule) => rule.invalid) || []; + }, + invalidApprovedRules() { + return this.invalidRules.filter((rule) => rule.allowMergeWhenInvalid); + }, + invalidFailedRules() { + return this.invalidRules.filter((rule) => !rule.allowMergeWhenInvalid); }, hasInvalidRules() { return this.mr.mergeRequestApproversAvailable && this.invalidRules.length; }, - invalidRulesText() { - return this.invalidRules.length; + hasInvalidApprovedRules() { + return this.mr.mergeRequestApproversAvailable && this.invalidApprovedRules.length; + }, + hasInvalidFailedRules() { + return this.mr.mergeRequestApproversAvailable && this.invalidFailedRules.length; }, approvedBy() { return this.approvals.approvedBy?.nodes || []; @@ -133,11 +142,29 @@ export default { return null; }, - pluralizedRuleText() { - return this.invalidRules.length > 1 + pluralizedApprovedRuleText() { + return this.invalidApprovedRules.length > 1 ? this.$options.i18n.invalidRulesPlural : this.$options.i18n.invalidRuleSingular; }, + pluralizedFailedRuleText() { + return this.invalidFailedRules.length > 1 + ? this.$options.i18n.invalidFailedRulesPlural + : this.$options.i18n.invalidFailedRuleSingular; + }, + pluralizedRuleText() { + return [ + this.hasInvalidFailedRules + ? sprintf(this.pluralizedFailedRuleText, { rules: this.invalidFailedRules.length }) + : null, + this.hasInvalidApprovedRules + ? sprintf(this.pluralizedApprovedRuleText, { rules: this.invalidApprovedRules.length }) + : null, + ] + .filter((text) => Boolean(text)) + .join(', ') + .concat('.'); + }, }, methods: { approve() { @@ -205,11 +232,13 @@ export default { FETCH_LOADING, linkToInvalidRules: INVALID_RULES_DOCS_PATH, i18n: { - invalidRuleSingular: s__( - 'mrWidget|%{rules} invalid rule has been approved automatically, as no one can approve it.', + invalidRuleSingular: s__('mrWidget|%{rules} invalid rule has been approved automatically'), + invalidRulesPlural: s__('mrWidget|%{rules} invalid rules have been approved automatically'), + invalidFailedRuleSingular: s__( + "mrWidget|%{dangerStart}%{rules} rule can't be approved%{dangerEnd}", ), - invalidRulesPlural: s__( - 'mrWidget|%{rules} invalid rules have been approved automatically, as no one can approve them.', + invalidFailedRulesPlural: s__( + "mrWidget|%{dangerStart}%{rules} rules can't be approved%{dangerEnd}", ), learnMore: __('Learn more.'), }, @@ -255,7 +284,9 @@ export default { </div> <div v-if="hasInvalidRules" class="gl-text-gray-400 gl-mt-2" data-testid="invalid-rules"> <gl-sprintf :message="pluralizedRuleText"> - <template #rules>{{ invalidRulesText }}</template> + <template #danger="{ content }"> + <span class="gl-font-weight-bold text-danger">{{ content }}</span> + </template> </gl-sprintf> </div> </div> diff --git a/app/assets/javascripts/vue_shared/components/markdown/header.vue b/app/assets/javascripts/vue_shared/components/markdown/header.vue index 4e827d71126..3486f231b39 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/header.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/header.vue @@ -122,12 +122,7 @@ export default { return [`<details><summary>${expandText}</summary>`, `{text}`, '</details>'].join('\n'); }, showAiActions() { - return ( - this.resourceGlobalId && - this.glFeatures.openaiExperimentation && - this.glFeatures.summarizeNotes && - this.glFeatures.summarizeComments - ); + return this.resourceGlobalId && this.glFeatures.summarizeComments; }, }, watch: { diff --git a/app/assets/stylesheets/framework/super_sidebar.scss b/app/assets/stylesheets/framework/super_sidebar.scss index ba98e3b54c9..045ad4a8298 100644 --- a/app/assets/stylesheets/framework/super_sidebar.scss +++ b/app/assets/stylesheets/framework/super_sidebar.scss @@ -78,8 +78,9 @@ } } - .counter .gl-icon { - color: var(--gray-500, $gray-500); + .counter .gl-icon, + .item-icon { + color: var(--gray-600, $gray-500); } .counter:hover, diff --git a/app/controllers/clusters/base_controller.rb b/app/controllers/clusters/base_controller.rb index 2401d8b1044..dd5be596ad1 100644 --- a/app/controllers/clusters/base_controller.rb +++ b/app/controllers/clusters/base_controller.rb @@ -8,7 +8,7 @@ class Clusters::BaseController < ApplicationController helper_method :clusterable - feature_category :kubernetes_management + feature_category :deployment_management urgency :low, [ :index, :show, :environments, :cluster_status, :prometheus_proxy, :destroy, :new_cluster_docs, :connect, :new, :create_user diff --git a/app/controllers/google_api/authorizations_controller.rb b/app/controllers/google_api/authorizations_controller.rb index 536c5e347e7..8a3183ba615 100644 --- a/app/controllers/google_api/authorizations_controller.rb +++ b/app/controllers/google_api/authorizations_controller.rb @@ -6,7 +6,7 @@ module GoogleApi before_action :validate_session_key! - feature_category :kubernetes_management + feature_category :deployment_management urgency :low ## diff --git a/app/controllers/projects/blame_controller.rb b/app/controllers/projects/blame_controller.rb index cc0d3818e33..dbc82f5b314 100644 --- a/app/controllers/projects/blame_controller.rb +++ b/app/controllers/projects/blame_controller.rb @@ -8,24 +8,17 @@ class Projects::BlameController < Projects::ApplicationController before_action :require_non_empty_project before_action :assign_ref_vars before_action :authorize_read_code! + before_action :load_blob feature_category :source_code_management urgency :low, [:show] def show - @blob = @repository.blob_at(@commit.id, @path) - - unless @blob - return redirect_to_tree_root_for_missing_path(@project, @ref, @path) - end - load_environment load_blame end def page - @blob = @repository.blob_at(@commit.id, @path) - load_environment load_blame @@ -34,6 +27,14 @@ class Projects::BlameController < Projects::ApplicationController private + def load_blob + @blob = @repository.blob_at(@commit.id, @path) + + return if @blob + + redirect_to_tree_root_for_missing_path(@project, @ref, @path) + end + def load_environment environment_params = @repository.branch_exists?(@ref) ? { ref: @ref } : { commit: @commit } environment_params[:find_latest] = true diff --git a/app/controllers/projects/cluster_agents_controller.rb b/app/controllers/projects/cluster_agents_controller.rb index 9bc03640811..bd58bd3e470 100644 --- a/app/controllers/projects/cluster_agents_controller.rb +++ b/app/controllers/projects/cluster_agents_controller.rb @@ -6,7 +6,7 @@ class Projects::ClusterAgentsController < Projects::ApplicationController before_action :authorize_read_cluster_agent! before_action :set_kas_cookie, only: [:show], if: -> { current_user } - feature_category :kubernetes_management + feature_category :deployment_management urgency :low def show diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 73b8ca6aafb..a9e0b2d7a98 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -39,7 +39,6 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo push_frontend_feature_flag(:issue_assignees_widget, @project) push_frontend_feature_flag(:refactor_security_extension, @project) push_frontend_feature_flag(:deprecate_vulnerabilities_feedback, @project) - push_frontend_feature_flag(:refactor_code_quality_inline_findings, project) push_frontend_feature_flag(:moved_mr_sidebar, project) push_frontend_feature_flag(:single_file_file_by_file, project) push_frontend_feature_flag(:mr_experience_survey, project) diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb index 4f3369d5bd0..ea7bb51c1e6 100644 --- a/app/controllers/projects/pipelines_controller.rb +++ b/app/controllers/projects/pipelines_controller.rb @@ -22,7 +22,6 @@ class Projects::PipelinesController < Projects::ApplicationController before_action :authorize_update_pipeline!, only: [:retry, :cancel] before_action :ensure_pipeline, only: [:show, :downloadable_artifacts] before_action :reject_if_build_artifacts_size_refreshing!, only: [:destroy] - before_action :push_frontend_feature_flags, only: [:index] # Will be removed with https://gitlab.com/gitlab-org/gitlab/-/issues/225596 before_action :redirect_for_legacy_scope_filter, only: [:index], if: -> { request.format.html? } @@ -234,7 +233,7 @@ class Projects::PipelinesController < Projects::ApplicationController @pipelines, disable_coverage: true, preload: true, - disable_manual_and_scheduled_actions: Feature.enabled?(:lazy_load_pipeline_dropdown_actions, @project) + disable_manual_and_scheduled_actions: true ) end @@ -358,10 +357,6 @@ class Projects::PipelinesController < Projects::ApplicationController def tracking_project_source project end - - def push_frontend_feature_flags - push_frontend_feature_flag(:lazy_load_pipeline_dropdown_actions, @project) - end end Projects::PipelinesController.prepend_mod_with('Projects::PipelinesController') diff --git a/app/experiments/security_actions_continuous_onboarding_experiment.rb b/app/experiments/security_actions_continuous_onboarding_experiment.rb deleted file mode 100644 index 6adfbedc744..00000000000 --- a/app/experiments/security_actions_continuous_onboarding_experiment.rb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -class SecurityActionsContinuousOnboardingExperiment < ApplicationExperiment - def control_behavior - end - - def candidate_behavior - end -end diff --git a/app/models/onboarding/completion.rb b/app/models/onboarding/completion.rb index 0966a9f2912..afbd671f82e 100644 --- a/app/models/onboarding/completion.rb +++ b/app/models/onboarding/completion.rb @@ -13,7 +13,10 @@ module Onboarding :issue_created, :git_write, :merge_request_created, - :user_added + :user_added, + :license_scanning_run, + :secure_dependency_scanning_run, + :secure_dast_run ].freeze def initialize(project, current_user = nil) @@ -58,26 +61,10 @@ module Onboarding def action_columns [:code_added] + - tracked_actions.map { |action_key| ::Onboarding::Progress.column_name(action_key) } + ACTION_PATHS.map { |action_key| ::Onboarding::Progress.column_name(action_key) } end strong_memoize_attr :action_columns - def tracked_actions - ACTION_PATHS + deploy_section_tracked_actions - end - - def deploy_section_tracked_actions - experiment( - :security_actions_continuous_onboarding, - namespace: namespace, - user: current_user, - sticky_to: current_user - ) do |e| - e.control { [:security_scan_enabled] } - e.candidate { [:license_scanning_run, :secure_dependency_scanning_run, :secure_dast_run] } - end.run - end - attr_reader :project, :namespace, :current_user end end diff --git a/app/services/issues/create_service.rb b/app/services/issues/create_service.rb index 06977dec04b..2a3f0abf4cb 100644 --- a/app/services/issues/create_service.rb +++ b/app/services/issues/create_service.rb @@ -93,6 +93,7 @@ module Issues return if issue.assignees == old_assignees create_assignee_note(issue, old_assignees) + Gitlab::ResourceEvents::AssignmentEventRecorder.new(parent: issue, old_assignees: old_assignees).record end def resolve_discussions_with_issue(issue) diff --git a/app/services/issues/update_service.rb b/app/services/issues/update_service.rb index 877f0a73c82..4dd4e2978c0 100644 --- a/app/services/issues/update_service.rb +++ b/app/services/issues/update_service.rb @@ -75,6 +75,7 @@ module Issues return if issue.assignees == old_assignees create_assignee_note(issue, old_assignees) + Gitlab::ResourceEvents::AssignmentEventRecorder.new(parent: issue, old_assignees: old_assignees).record notification_service.async.reassigned_issue(issue, current_user, old_assignees) todo_service.reassigned_assignable(issue, current_user, old_assignees) track_incident_action(current_user, issue, :incident_assigned) diff --git a/app/services/merge_requests/handle_assignees_change_service.rb b/app/services/merge_requests/handle_assignees_change_service.rb index 51be4690af4..835d56a7070 100644 --- a/app/services/merge_requests/handle_assignees_change_service.rb +++ b/app/services/merge_requests/handle_assignees_change_service.rb @@ -15,6 +15,7 @@ module MergeRequests def execute(merge_request, old_assignees, options = {}) create_assignee_note(merge_request, old_assignees) notification_service.async.reassigned_merge_request(merge_request, current_user, old_assignees.to_a) + Gitlab::ResourceEvents::AssignmentEventRecorder.new(parent: merge_request, old_assignees: old_assignees).record todo_service.reassigned_assignable(merge_request, current_user, old_assignees) new_assignees = merge_request.assignees - old_assignees diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml index 6c3b4990023..0418bef3c1c 100644 --- a/app/workers/all_queues.yml +++ b/app/workers/all_queues.yml @@ -131,7 +131,7 @@ :tags: [] - :name: cluster_agent:clusters_agents_delete_expired_events :worker_name: Clusters::Agents::DeleteExpiredEventsWorker - :feature_category: :kubernetes_management + :feature_category: :deployment_management :has_external_dependencies: false :urgency: :low :resource_boundary: :unknown @@ -950,7 +950,7 @@ :tags: [] - :name: gcp_cluster:cluster_configure_istio :worker_name: ClusterConfigureIstioWorker - :feature_category: :kubernetes_management + :feature_category: :deployment_management :has_external_dependencies: true :urgency: :low :resource_boundary: :unknown @@ -959,7 +959,7 @@ :tags: [] - :name: gcp_cluster:cluster_install_app :worker_name: ClusterInstallAppWorker - :feature_category: :kubernetes_management + :feature_category: :deployment_management :has_external_dependencies: true :urgency: :low :resource_boundary: :unknown @@ -968,7 +968,7 @@ :tags: [] - :name: gcp_cluster:cluster_patch_app :worker_name: ClusterPatchAppWorker - :feature_category: :kubernetes_management + :feature_category: :deployment_management :has_external_dependencies: true :urgency: :low :resource_boundary: :unknown @@ -977,7 +977,7 @@ :tags: [] - :name: gcp_cluster:cluster_provision :worker_name: ClusterProvisionWorker - :feature_category: :kubernetes_management + :feature_category: :deployment_management :has_external_dependencies: false :urgency: :low :resource_boundary: :unknown @@ -986,7 +986,7 @@ :tags: [] - :name: gcp_cluster:cluster_update_app :worker_name: ClusterUpdateAppWorker - :feature_category: :kubernetes_management + :feature_category: :deployment_management :has_external_dependencies: false :urgency: :low :resource_boundary: :unknown @@ -995,7 +995,7 @@ :tags: [] - :name: gcp_cluster:cluster_upgrade_app :worker_name: ClusterUpgradeAppWorker - :feature_category: :kubernetes_management + :feature_category: :deployment_management :has_external_dependencies: true :urgency: :low :resource_boundary: :unknown @@ -1004,7 +1004,7 @@ :tags: [] - :name: gcp_cluster:cluster_wait_for_app_installation :worker_name: ClusterWaitForAppInstallationWorker - :feature_category: :kubernetes_management + :feature_category: :deployment_management :has_external_dependencies: true :urgency: :low :resource_boundary: :cpu @@ -1013,7 +1013,7 @@ :tags: [] - :name: gcp_cluster:cluster_wait_for_app_update :worker_name: ClusterWaitForAppUpdateWorker - :feature_category: :kubernetes_management + :feature_category: :deployment_management :has_external_dependencies: false :urgency: :low :resource_boundary: :unknown @@ -1022,7 +1022,7 @@ :tags: [] - :name: gcp_cluster:cluster_wait_for_ingress_ip_address :worker_name: ClusterWaitForIngressIpAddressWorker - :feature_category: :kubernetes_management + :feature_category: :deployment_management :has_external_dependencies: true :urgency: :low :resource_boundary: :unknown @@ -1031,7 +1031,7 @@ :tags: [] - :name: gcp_cluster:clusters_applications_activate_integration :worker_name: Clusters::Applications::ActivateIntegrationWorker - :feature_category: :kubernetes_management + :feature_category: :deployment_management :has_external_dependencies: false :urgency: :low :resource_boundary: :unknown @@ -1040,7 +1040,7 @@ :tags: [] - :name: gcp_cluster:clusters_applications_deactivate_integration :worker_name: Clusters::Applications::DeactivateIntegrationWorker - :feature_category: :kubernetes_management + :feature_category: :deployment_management :has_external_dependencies: false :urgency: :low :resource_boundary: :unknown @@ -1049,7 +1049,7 @@ :tags: [] - :name: gcp_cluster:clusters_applications_uninstall :worker_name: Clusters::Applications::UninstallWorker - :feature_category: :kubernetes_management + :feature_category: :deployment_management :has_external_dependencies: true :urgency: :low :resource_boundary: :unknown @@ -1058,7 +1058,7 @@ :tags: [] - :name: gcp_cluster:clusters_applications_wait_for_uninstall_app :worker_name: Clusters::Applications::WaitForUninstallAppWorker - :feature_category: :kubernetes_management + :feature_category: :deployment_management :has_external_dependencies: true :urgency: :low :resource_boundary: :cpu @@ -1067,7 +1067,7 @@ :tags: [] - :name: gcp_cluster:clusters_cleanup_project_namespace :worker_name: Clusters::Cleanup::ProjectNamespaceWorker - :feature_category: :kubernetes_management + :feature_category: :deployment_management :has_external_dependencies: true :urgency: :low :resource_boundary: :unknown @@ -1076,7 +1076,7 @@ :tags: [] - :name: gcp_cluster:clusters_cleanup_service_account :worker_name: Clusters::Cleanup::ServiceAccountWorker - :feature_category: :kubernetes_management + :feature_category: :deployment_management :has_external_dependencies: true :urgency: :low :resource_boundary: :unknown @@ -1085,7 +1085,7 @@ :tags: [] - :name: gcp_cluster:wait_for_cluster_creation :worker_name: WaitForClusterCreationWorker - :feature_category: :kubernetes_management + :feature_category: :deployment_management :has_external_dependencies: false :urgency: :low :resource_boundary: :unknown diff --git a/app/workers/concerns/cluster_agent_queue.rb b/app/workers/concerns/cluster_agent_queue.rb index 68de7cca135..8fdfba11111 100644 --- a/app/workers/concerns/cluster_agent_queue.rb +++ b/app/workers/concerns/cluster_agent_queue.rb @@ -5,6 +5,6 @@ module ClusterAgentQueue included do queue_namespace :cluster_agent - feature_category :kubernetes_management + feature_category :deployment_management end end diff --git a/app/workers/concerns/cluster_queue.rb b/app/workers/concerns/cluster_queue.rb index 60ba8785347..5f1a90a99d0 100644 --- a/app/workers/concerns/cluster_queue.rb +++ b/app/workers/concerns/cluster_queue.rb @@ -8,6 +8,6 @@ module ClusterQueue included do queue_namespace :gcp_cluster - feature_category :kubernetes_management + feature_category :deployment_management end end |