diff options
76 files changed, 545 insertions, 303 deletions
@@ -166,7 +166,7 @@ gem 'seed-fu', '~> 2.3.7' gem 'elasticsearch-model', '~> 7.2' gem 'elasticsearch-rails', '~> 7.2', require: 'elasticsearch/rails/instrumentation' gem 'elasticsearch-api', '7.13.3' -gem 'aws-sdk-core', '~> 3.167.0' +gem 'aws-sdk-core', '~> 3.168.1' gem 'aws-sdk-cloudformation', '~> 1' gem 'aws-sdk-s3', '~> 1.117.1' gem 'faraday_middleware-aws-sigv4', '~>0.3.0' diff --git a/Gemfile.checksum b/Gemfile.checksum index 38044e328d2..81478ea92d9 100644 --- a/Gemfile.checksum +++ b/Gemfile.checksum @@ -33,9 +33,9 @@ {"name":"awesome_print","version":"1.9.2","platform":"ruby","checksum":"e99b32b704acff16d768b3468680793ced40bfdc4537eb07e06a4be11133786e"}, {"name":"awrence","version":"1.1.1","platform":"ruby","checksum":"9be584c97408ed92d5e1ca11740853646fe270de675f2f8dd44e8233226dfc97"}, {"name":"aws-eventstream","version":"1.2.0","platform":"ruby","checksum":"ffa53482c92880b001ff2fb06919b9bb82fd847cbb0fa244985d2ebb6dd0d1df"}, -{"name":"aws-partitions","version":"1.658.0","platform":"ruby","checksum":"bba2e21fc87c4e68c7ba5c09e3cd2b81d59ca86111ab236eaf9c5a8ae207b3fc"}, +{"name":"aws-partitions","version":"1.664.0","platform":"ruby","checksum":"cf7e9d07fb7d3517e4e349cf83d724fb7751caecc9b7a1abfd4e665be69ec9c0"}, {"name":"aws-sdk-cloudformation","version":"1.41.0","platform":"ruby","checksum":"31e47539719734413671edf9b1a31f8673fbf9688549f50c41affabbcb1c6b26"}, -{"name":"aws-sdk-core","version":"3.167.0","platform":"ruby","checksum":"d371856ad86f8bff08928059ee09b7cb9bca8ebf36bf5081f12424e4f491b624"}, +{"name":"aws-sdk-core","version":"3.168.1","platform":"ruby","checksum":"2b83c2cc0dde47293a9ff4f4ad1c4d49913c718d0daba8d1b357b5315fdad6ee"}, {"name":"aws-sdk-kms","version":"1.59.0","platform":"ruby","checksum":"6c002ebf8e404625c8338ca12ae69b1329399f9dc1b0ebca474e00ff06700153"}, {"name":"aws-sdk-s3","version":"1.117.1","platform":"ruby","checksum":"76f6dac5baeb2b78616eb34c6af650c1b7a15c1078b169d1b27e8421904c509d"}, {"name":"aws-sigv4","version":"1.5.1","platform":"ruby","checksum":"d68c87fff4ee843b4b92b23c7f31f957f254ec6eb064181f7119124aab8b8bb4"}, diff --git a/Gemfile.lock b/Gemfile.lock index f232b7bf025..5b870696af2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -185,11 +185,11 @@ GEM awesome_print (1.9.2) awrence (1.1.1) aws-eventstream (1.2.0) - aws-partitions (1.658.0) + aws-partitions (1.664.0) aws-sdk-cloudformation (1.41.0) aws-sdk-core (~> 3, >= 3.99.0) aws-sigv4 (~> 1.1) - aws-sdk-core (3.167.0) + aws-sdk-core (3.168.1) aws-eventstream (~> 1, >= 1.0.2) aws-partitions (~> 1, >= 1.651.0) aws-sigv4 (~> 1.5) @@ -1594,7 +1594,7 @@ DEPENDENCIES autoprefixer-rails (= 10.2.5.1) awesome_print aws-sdk-cloudformation (~> 1) - aws-sdk-core (~> 3.167.0) + aws-sdk-core (~> 3.168.1) aws-sdk-s3 (~> 1.117.1) babosa (~> 1.0.4) base32 (~> 0.3.0) diff --git a/app/assets/javascripts/boards/components/board_content_sidebar.vue b/app/assets/javascripts/boards/components/board_content_sidebar.vue index 65b6bc6b98e..a2ac1f3fe2c 100644 --- a/app/assets/javascripts/boards/components/board_content_sidebar.vue +++ b/app/assets/javascripts/boards/components/board_content_sidebar.vue @@ -34,10 +34,10 @@ export default { MountingPortal, SidebarHealthStatusWidget: () => import('ee_component/sidebar/components/health_status/sidebar_health_status_widget.vue'), + SidebarIterationWidget: () => + import('ee_component/sidebar/components/iteration/sidebar_iteration_widget.vue'), SidebarWeightWidget: () => import('ee_component/sidebar/components/weight/sidebar_weight_widget.vue'), - IterationSidebarDropdownWidget: () => - import('ee_component/sidebar/components/iteration_sidebar_dropdown_widget.vue'), }, mixins: [glFeatureFlagMixin()], inject: { @@ -193,7 +193,7 @@ export default { :issuable-type="issuableType" data-testid="sidebar-milestones" /> - <iteration-sidebar-dropdown-widget + <sidebar-iteration-widget v-if="iterationFeatureAvailable && !isIncidentSidebar" :iid="activeBoardItem.iid" :workspace-path="projectPathForActiveIssue" diff --git a/app/assets/javascripts/issuable/bulk_update_sidebar/constants.js b/app/assets/javascripts/issuable/bulk_update_sidebar/constants.js deleted file mode 100644 index 68133ceb3c7..00000000000 --- a/app/assets/javascripts/issuable/bulk_update_sidebar/constants.js +++ /dev/null @@ -1,23 +0,0 @@ -import { __ } from '~/locale'; - -export const statusDropdownOptions = [ - { - text: __('Open'), - value: 'reopen', - }, - { - text: __('Closed'), - value: 'close', - }, -]; - -export const subscriptionsDropdownOptions = [ - { - text: __('Subscribe'), - value: 'subscribe', - }, - { - text: __('Unsubscribe'), - value: 'unsubscribe', - }, -]; diff --git a/app/assets/javascripts/issuable/bulk_update_sidebar/index.js b/app/assets/javascripts/issuable/bulk_update_sidebar/index.js deleted file mode 100644 index b7cb805ee37..00000000000 --- a/app/assets/javascripts/issuable/bulk_update_sidebar/index.js +++ /dev/null @@ -1,75 +0,0 @@ -import Vue from 'vue'; -import VueApollo from 'vue-apollo'; -import { gqlClient } from '../../issues/list/graphql'; -import StatusDropdown from './components/status_dropdown.vue'; -import SubscriptionsDropdown from './components/subscriptions_dropdown.vue'; -import MoveIssuesButton from './components/move_issues_button.vue'; -import issuableBulkUpdateActions from './issuable_bulk_update_actions'; -import IssuableBulkUpdateSidebar from './issuable_bulk_update_sidebar'; - -export function initBulkUpdateSidebar(prefixId) { - const el = document.querySelector('.issues-bulk-update'); - - if (!el) { - return; - } - - issuableBulkUpdateActions.init({ prefixId }); - new IssuableBulkUpdateSidebar(); // eslint-disable-line no-new -} - -export function initStatusDropdown() { - const el = document.querySelector('.js-status-dropdown'); - - if (!el) { - return null; - } - - return new Vue({ - el, - name: 'StatusDropdownRoot', - render: (createElement) => createElement(StatusDropdown), - }); -} - -export function initSubscriptionsDropdown() { - const el = document.querySelector('.js-subscriptions-dropdown'); - - if (!el) { - return null; - } - - return new Vue({ - el, - name: 'SubscriptionsDropdownRoot', - render: (createElement) => createElement(SubscriptionsDropdown), - }); -} - -export function initMoveIssuesButton() { - const el = document.querySelector('.js-move-issues'); - - if (!el) { - return null; - } - - const { dataset } = el; - - Vue.use(VueApollo); - const apolloProvider = new VueApollo({ - defaultClient: gqlClient, - }); - - return new Vue({ - el, - name: 'MoveIssuesRoot', - apolloProvider, - render: (createElement) => - createElement(MoveIssuesButton, { - props: { - projectFullPath: dataset.projectFullPath, - projectsFetchPath: dataset.projectsFetchPath, - }, - }), - }); -} diff --git a/app/assets/javascripts/issuable/index.js b/app/assets/javascripts/issuable/index.js index 10dbefce503..ed336deb2ed 100644 --- a/app/assets/javascripts/issuable/index.js +++ b/app/assets/javascripts/issuable/index.js @@ -1,12 +1,25 @@ import { GlToast } from '@gitlab/ui'; import Vue from 'vue'; -import IssuableContext from '~/issuable/issuable_context'; import { parseBoolean } from '~/lib/utils/common_utils'; import Sidebar from '~/right_sidebar'; import { getSidebarOptions } from '~/sidebar/mount_sidebar'; import CsvImportExportButtons from './components/csv_import_export_buttons.vue'; import IssuableByEmail from './components/issuable_by_email.vue'; import IssuableHeaderWarnings from './components/issuable_header_warnings.vue'; +import issuableBulkUpdateActions from './issuable_bulk_update_actions'; +import IssuableBulkUpdateSidebar from './issuable_bulk_update_sidebar'; +import IssuableContext from './issuable_context'; + +export function initBulkUpdateSidebar(prefixId) { + const el = document.querySelector('.issues-bulk-update'); + + if (!el) { + return; + } + + issuableBulkUpdateActions.init({ prefixId }); + new IssuableBulkUpdateSidebar(); // eslint-disable-line no-new +} export function initCsvImportExportButtons() { const el = document.querySelector('.js-csv-import-export-buttons'); diff --git a/app/assets/javascripts/issuable/bulk_update_sidebar/issuable_bulk_update_actions.js b/app/assets/javascripts/issuable/issuable_bulk_update_actions.js index 14824820c0d..14824820c0d 100644 --- a/app/assets/javascripts/issuable/bulk_update_sidebar/issuable_bulk_update_actions.js +++ b/app/assets/javascripts/issuable/issuable_bulk_update_actions.js diff --git a/app/assets/javascripts/issuable/bulk_update_sidebar/issuable_bulk_update_sidebar.js b/app/assets/javascripts/issuable/issuable_bulk_update_sidebar.js index b46a95c7dfa..095da60a583 100644 --- a/app/assets/javascripts/issuable/bulk_update_sidebar/issuable_bulk_update_sidebar.js +++ b/app/assets/javascripts/issuable/issuable_bulk_update_sidebar.js @@ -3,7 +3,12 @@ import $ from 'jquery'; import issuableEventHub from '~/issues/list/eventhub'; import LabelsSelect from '~/labels/labels_select'; -import { mountMilestoneDropdown } from '~/sidebar/mount_sidebar'; +import { + mountMilestoneDropdown, + mountMoveIssuesButton, + mountStatusDropdown, + mountSubscriptionsDropdown, +} from '~/sidebar/mount_sidebar'; import IssuableBulkUpdateActions from './issuable_bulk_update_actions'; const HIDDEN_CLASS = 'hidden'; @@ -56,6 +61,9 @@ export default class IssuableBulkUpdateSidebar { initDropdowns() { new LabelsSelect(); mountMilestoneDropdown(); + mountMoveIssuesButton(); + mountStatusDropdown(); + mountSubscriptionsDropdown(); // Checking IS_EE and using ee_else_ce is odd, but we do it here to satisfy // the import/no-unresolved lint rule when FOSS_ONLY=1, even though at diff --git a/app/assets/javascripts/issues/list/components/issues_list_app.vue b/app/assets/javascripts/issues/list/components/issues_list_app.vue index eb1c17ba7e5..cfb1de2e680 100644 --- a/app/assets/javascripts/issues/list/components/issues_list_app.vue +++ b/app/assets/javascripts/issues/list/components/issues_list_app.vue @@ -572,11 +572,8 @@ export default { }, async handleBulkUpdateClick() { if (!this.hasInitBulkEdit) { - const bulkUpdateSidebar = await import('~/issuable/bulk_update_sidebar'); + const bulkUpdateSidebar = await import('~/issuable'); bulkUpdateSidebar.initBulkUpdateSidebar('issuable_'); - bulkUpdateSidebar.initStatusDropdown(); - bulkUpdateSidebar.initSubscriptionsDropdown(); - bulkUpdateSidebar.initMoveIssuesButton(); const usersSelect = await import('~/users_select'); const UsersSelect = usersSelect.default; diff --git a/app/assets/javascripts/issues/show/components/incidents/incident_tabs.vue b/app/assets/javascripts/issues/show/components/incidents/incident_tabs.vue index 5725d0f8d6a..a3efb0bbbea 100644 --- a/app/assets/javascripts/issues/show/components/incidents/incident_tabs.vue +++ b/app/assets/javascripts/issues/show/components/incidents/incident_tabs.vue @@ -5,12 +5,25 @@ import { trackIncidentDetailsViewsOptions } from '~/incidents/constants'; import { s__ } from '~/locale'; import Tracking from '~/tracking'; import AlertDetailsTable from '~/vue_shared/components/alert_details_table.vue'; -import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import DescriptionComponent from '../description.vue'; import getAlert from './graphql/queries/get_alert.graphql'; import HighlightBar from './highlight_bar.vue'; import TimelineTab from './timeline_events_tab.vue'; +export const incidentTabsI18n = Object.freeze({ + summaryTitle: s__('Incident|Summary'), + metricsTitle: s__('Incident|Metrics'), + alertsTitle: s__('Incident|Alert details'), + timelineTitle: s__('Incident|Timeline'), +}); + +export const TAB_NAMES = Object.freeze({ + SUMMARY: '', + ALERTS: 'alerts', + METRICS: 'metrics', + TIMELINE: 'timeline', +}); + export default { components: { AlertDetailsTable, @@ -22,8 +35,8 @@ export default { IncidentMetricTab: () => import('ee_component/issues/show/components/incidents/incident_metric_tab.vue'), }, - mixins: [glFeatureFlagsMixin()], - inject: ['fullPath', 'iid'], + inject: ['fullPath', 'iid', 'uploadMetricsFeatureAvailable'], + i18n: incidentTabsI18n, apollo: { alert: { query: getAlert, @@ -46,12 +59,44 @@ export default { data() { return { alert: null, + activeTabIndex: 0, }; }, computed: { loading() { return this.$apollo.queries.alert.loading; }, + tabMapping() { + const availableTabs = [TAB_NAMES.SUMMARY]; + + if (this.uploadMetricsFeatureAvailable) { + availableTabs.push(TAB_NAMES.METRICS); + } + if (this.alert) { + availableTabs.push(TAB_NAMES.ALERTS); + } + + availableTabs.push(TAB_NAMES.TIMELINE); + + const tabNamesToIndex = {}; + const tabIndexToName = {}; + + availableTabs.forEach((item, index) => { + tabNamesToIndex[item] = index; + tabIndexToName[index] = item; + }); + + return { tabNamesToIndex, tabIndexToName }; + }, + currentTabIndex: { + get() { + return this.activeTabIndex; + }, + set(index) { + this.handleTabChange(index); + this.activeTabIndex = index; + }, + }, }, mounted() { this.trackPageViews(); @@ -91,25 +136,33 @@ export default { <template> <div> <gl-tabs + v-model="currentTabIndex" content-class="gl-reset-line-height" class="gl-mt-n3" data-testid="incident-tabs" - @input="handleTabChange" > - <gl-tab :title="s__('Incident|Summary')"> + <gl-tab :title="$options.i18n.summaryTitle" data-testid="summary-tab"> <highlight-bar :alert="alert" /> <description-component v-bind="$attrs" v-on="$listeners" /> </gl-tab> - <incident-metric-tab /> + <gl-tab + v-if="uploadMetricsFeatureAvailable" + :title="$options.i18n.metricsTitle" + data-testid="metrics-tab" + > + <incident-metric-tab /> + </gl-tab> <gl-tab v-if="alert" class="alert-management-details" - :title="s__('Incident|Alert details')" + :title="$options.i18n.alertsTitle" data-testid="alert-details-tab" > <alert-details-table :alert="alert" :loading="loading" /> </gl-tab> - <timeline-tab /> + <gl-tab :title="$options.i18n.timelineTitle" data-testid="timeline-tab"> + <timeline-tab /> + </gl-tab> </gl-tabs> </div> </template> diff --git a/app/assets/javascripts/issues/show/components/incidents/timeline_events_tab.vue b/app/assets/javascripts/issues/show/components/incidents/timeline_events_tab.vue index 5f70d9acac9..c8237766505 100644 --- a/app/assets/javascripts/issues/show/components/incidents/timeline_events_tab.vue +++ b/app/assets/javascripts/issues/show/components/incidents/timeline_events_tab.vue @@ -1,5 +1,5 @@ <script> -import { GlButton, GlEmptyState, GlLoadingIcon, GlTab } from '@gitlab/ui'; +import { GlButton, GlEmptyState, GlLoadingIcon } from '@gitlab/ui'; import { convertToGraphQLId } from '~/graphql_shared/utils'; import { TYPE_ISSUE } from '~/graphql_shared/constants'; import { fetchPolicies } from '~/lib/graphql'; @@ -15,7 +15,6 @@ export default { GlButton, GlEmptyState, GlLoadingIcon, - GlTab, CreateTimelineEvent, IncidentTimelineEventsList, }, @@ -77,7 +76,7 @@ export default { </script> <template> - <gl-tab :title="$options.i18n.title"> + <div> <gl-loading-icon v-if="timelineEventLoading" size="lg" color="dark" class="gl-mt-5" /> <gl-empty-state v-else-if="showEmptyState" @@ -106,5 +105,5 @@ export default { > {{ $options.i18n.addEventButton }} </gl-button> - </gl-tab> + </div> </template> diff --git a/app/assets/javascripts/labels/labels_select.js b/app/assets/javascripts/labels/labels_select.js index 65dda804a20..515b0a79a03 100644 --- a/app/assets/javascripts/labels/labels_select.js +++ b/app/assets/javascripts/labels/labels_select.js @@ -4,7 +4,7 @@ import $ from 'jquery'; import { difference, isEqual, escape, sortBy, template, union } from 'lodash'; import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown'; -import IssuableBulkUpdateActions from '~/issuable/bulk_update_sidebar/issuable_bulk_update_actions'; +import IssuableBulkUpdateActions from '~/issuable/issuable_bulk_update_actions'; import { isScopedLabel } from '~/lib/utils/common_utils'; import { createAlert } from '~/flash'; import axios from '~/lib/utils/axios_utils'; diff --git a/app/assets/javascripts/pages/groups/merge_requests/index.js b/app/assets/javascripts/pages/groups/merge_requests/index.js index 377ba0f13a9..bf0147ca885 100644 --- a/app/assets/javascripts/pages/groups/merge_requests/index.js +++ b/app/assets/javascripts/pages/groups/merge_requests/index.js @@ -1,11 +1,7 @@ import addExtraTokensForMergeRequests from 'ee_else_ce/filtered_search/add_extra_tokens_for_merge_requests'; import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys'; -import { - initBulkUpdateSidebar, - initStatusDropdown, - initSubscriptionsDropdown, -} from '~/issuable/bulk_update_sidebar'; import { FILTERED_SEARCH } from '~/filtered_search/constants'; +import { initBulkUpdateSidebar } from '~/issuable'; import initFilteredSearch from '~/pages/search/init_filtered_search'; import projectSelect from '~/project_select'; @@ -13,8 +9,6 @@ const ISSUABLE_BULK_UPDATE_PREFIX = 'merge_request_'; addExtraTokensForMergeRequests(IssuableFilteredSearchTokenKeys); initBulkUpdateSidebar(ISSUABLE_BULK_UPDATE_PREFIX); -initStatusDropdown(); -initSubscriptionsDropdown(); initFilteredSearch({ page: FILTERED_SEARCH.MERGE_REQUESTS, diff --git a/app/assets/javascripts/pages/projects/merge_requests/index/index.js b/app/assets/javascripts/pages/projects/merge_requests/index/index.js index 2399aafc9b5..b3a09cc0be3 100644 --- a/app/assets/javascripts/pages/projects/merge_requests/index/index.js +++ b/app/assets/javascripts/pages/projects/merge_requests/index/index.js @@ -1,20 +1,13 @@ import addExtraTokensForMergeRequests from 'ee_else_ce/filtered_search/add_extra_tokens_for_merge_requests'; import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation'; import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys'; -import { initCsvImportExportButtons, initIssuableByEmail } from '~/issuable'; -import { - initBulkUpdateSidebar, - initStatusDropdown, - initSubscriptionsDropdown, -} from '~/issuable/bulk_update_sidebar'; import { FILTERED_SEARCH } from '~/filtered_search/constants'; +import { initBulkUpdateSidebar, initCsvImportExportButtons, initIssuableByEmail } from '~/issuable'; import { ISSUABLE_INDEX } from '~/issuable/constants'; import initFilteredSearch from '~/pages/search/init_filtered_search'; import UsersSelect from '~/users_select'; initBulkUpdateSidebar(ISSUABLE_INDEX.MERGE_REQUEST); -initStatusDropdown(); -initSubscriptionsDropdown(); addExtraTokensForMergeRequests(IssuableFilteredSearchTokenKeys); IssuableFilteredSearchTokenKeys.removeTokensForKeys('iteration'); diff --git a/app/assets/javascripts/sidebar/components/copy_email_to_clipboard.vue b/app/assets/javascripts/sidebar/components/copy_email/copy_email_to_clipboard.vue index fd652583f76..fd652583f76 100644 --- a/app/assets/javascripts/sidebar/components/copy_email_to_clipboard.vue +++ b/app/assets/javascripts/sidebar/components/copy_email/copy_email_to_clipboard.vue diff --git a/app/assets/javascripts/issuable/bulk_update_sidebar/components/move_issues_button.vue b/app/assets/javascripts/sidebar/components/move/move_issues_button.vue index 6e287ac3bb7..a3a5072fc2d 100644 --- a/app/assets/javascripts/issuable/bulk_update_sidebar/components/move_issues_button.vue +++ b/app/assets/javascripts/sidebar/components/move/move_issues_button.vue @@ -13,7 +13,7 @@ import { import issuableEventHub from '~/issues/list/eventhub'; import getIssuesQuery from 'ee_else_ce/issues/list/queries/get_issues.query.graphql'; import getIssuesCountQuery from 'ee_else_ce/issues/list/queries/get_issues_counts.query.graphql'; -import moveIssueMutation from './graphql/mutations/move_issue.mutation.graphql'; +import moveIssueMutation from '../../queries/move_issue.mutation.graphql'; export default { name: 'MoveIssuesButton', diff --git a/app/assets/javascripts/issuable/bulk_update_sidebar/components/status_dropdown.vue b/app/assets/javascripts/sidebar/components/status/status_dropdown.vue index ba94932289e..7763ec00091 100644 --- a/app/assets/javascripts/issuable/bulk_update_sidebar/components/status_dropdown.vue +++ b/app/assets/javascripts/sidebar/components/status/status_dropdown.vue @@ -1,7 +1,7 @@ <script> import { GlDropdown, GlDropdownItem } from '@gitlab/ui'; import { __ } from '~/locale'; -import { statusDropdownOptions } from '../constants'; +import { statusDropdownOptions } from '../../constants'; export default { components: { diff --git a/app/assets/javascripts/issuable/bulk_update_sidebar/components/subscriptions_dropdown.vue b/app/assets/javascripts/sidebar/components/subscriptions/subscriptions_dropdown.vue index 8774b065c22..4c3ba76d12d 100644 --- a/app/assets/javascripts/issuable/bulk_update_sidebar/components/subscriptions_dropdown.vue +++ b/app/assets/javascripts/sidebar/components/subscriptions/subscriptions_dropdown.vue @@ -1,7 +1,7 @@ <script> import { GlDropdown, GlDropdownItem } from '@gitlab/ui'; import { __ } from '~/locale'; -import { subscriptionsDropdownOptions } from '../constants'; +import { subscriptionsDropdownOptions } from '../../constants'; export default { subscriptionsDropdownOptions, diff --git a/app/assets/javascripts/sidebar/constants.js b/app/assets/javascripts/sidebar/constants.js index 67b9b540e91..499b03cd931 100644 --- a/app/assets/javascripts/sidebar/constants.js +++ b/app/assets/javascripts/sidebar/constants.js @@ -350,3 +350,25 @@ export const escalationStatusQuery = getEscalationStatusQuery; export const escalationStatusMutation = updateEscalationStatusMutation; export const HOW_TO_TRACK_TIME = __('How to track time'); + +export const statusDropdownOptions = [ + { + text: __('Open'), + value: 'reopen', + }, + { + text: __('Closed'), + value: 'close', + }, +]; + +export const subscriptionsDropdownOptions = [ + { + text: __('Subscribe'), + value: 'subscribe', + }, + { + text: __('Unsubscribe'), + value: 'unsubscribe', + }, +]; diff --git a/app/assets/javascripts/sidebar/mount_sidebar.js b/app/assets/javascripts/sidebar/mount_sidebar.js index b37486283ca..3fc98f86316 100644 --- a/app/assets/javascripts/sidebar/mount_sidebar.js +++ b/app/assets/javascripts/sidebar/mount_sidebar.js @@ -6,6 +6,7 @@ import { convertToGraphQLId } from '~/graphql_shared/utils'; import initInviteMembersModal from '~/invite_members/init_invite_members_modal'; import initInviteMembersTrigger from '~/invite_members/init_invite_members_trigger'; import { IssuableType } from '~/issues/constants'; +import { gqlClient } from '~/issues/list/graphql'; import { isInIssuePage, isInDesignPage, @@ -14,33 +15,36 @@ import { parseBoolean, } from '~/lib/utils/common_utils'; import { __ } from '~/locale'; -import CollapsedAssigneeList from '~/sidebar/components/assignees/collapsed_assignee_list.vue'; -import SidebarAssigneesWidget from '~/sidebar/components/assignees/sidebar_assignees_widget.vue'; -import SidebarConfidentialityWidget from '~/sidebar/components/confidential/sidebar_confidentiality_widget.vue'; -import SidebarDueDateWidget from '~/sidebar/components/date/sidebar_date_widget.vue'; -import MilestoneDropdown from '~/sidebar/components/milestone/milestone_dropdown.vue'; -import SidebarParticipantsWidget from '~/sidebar/components/participants/sidebar_participants_widget.vue'; -import SidebarReferenceWidget from '~/sidebar/components/reference/sidebar_reference_widget.vue'; -import SidebarDropdownWidget from '~/sidebar/components/sidebar_dropdown_widget.vue'; -import SidebarTodoWidget from '~/sidebar/components/todo_toggle/sidebar_todo_widget.vue'; import { apolloProvider } from '~/graphql_shared/issuable_client'; -import trackShowInviteMemberLink from '~/sidebar/track_invite_members'; import { DropdownVariant } from '~/vue_shared/components/sidebar/labels_select_vue/constants'; import LabelsSelectWidget from '~/vue_shared/components/sidebar/labels_select_widget/labels_select_root.vue'; import { LabelType } from '~/vue_shared/components/sidebar/labels_select_widget/constants'; -import Translate from '../vue_shared/translate'; +import Translate from '~/vue_shared/translate'; +import CollapsedAssigneeList from './components/assignees/collapsed_assignee_list.vue'; import SidebarAssignees from './components/assignees/sidebar_assignees.vue'; -import CopyEmailToClipboard from './components/copy_email_to_clipboard.vue'; +import SidebarAssigneesWidget from './components/assignees/sidebar_assignees_widget.vue'; +import SidebarConfidentialityWidget from './components/confidential/sidebar_confidentiality_widget.vue'; +import CopyEmailToClipboard from './components/copy_email/copy_email_to_clipboard.vue'; +import SidebarDueDateWidget from './components/date/sidebar_date_widget.vue'; import SidebarEscalationStatus from './components/incidents/sidebar_escalation_status.vue'; import IssuableLockForm from './components/lock/issuable_lock_form.vue'; +import MilestoneDropdown from './components/milestone/milestone_dropdown.vue'; +import MoveIssuesButton from './components/move/move_issues_button.vue'; +import SidebarParticipantsWidget from './components/participants/sidebar_participants_widget.vue'; +import SidebarReferenceWidget from './components/reference/sidebar_reference_widget.vue'; import SidebarReviewers from './components/reviewers/sidebar_reviewers.vue'; import SidebarReviewersInputs from './components/reviewers/sidebar_reviewers_inputs.vue'; import SidebarSeverity from './components/severity/sidebar_severity.vue'; +import SidebarDropdownWidget from './components/sidebar_dropdown_widget.vue'; +import StatusDropdown from './components/status/status_dropdown.vue'; import SidebarSubscriptionsWidget from './components/subscriptions/sidebar_subscriptions_widget.vue'; +import SubscriptionsDropdown from './components/subscriptions/subscriptions_dropdown.vue'; import SidebarTimeTracking from './components/time_tracking/sidebar_time_tracking.vue'; +import SidebarTodoWidget from './components/todo_toggle/sidebar_todo_widget.vue'; import { IssuableAttributeType } from './constants'; -import SidebarMoveIssue from './lib/sidebar_move_issue'; import CrmContacts from './components/crm_contacts/crm_contacts.vue'; +import SidebarMoveIssue from './lib/sidebar_move_issue'; +import trackShowInviteMemberLink from './track_invite_members'; Vue.use(Translate); Vue.use(VueApollo); @@ -635,6 +639,59 @@ function mountCopyEmailToClipboard() { }); } +export function mountMoveIssuesButton() { + const el = document.querySelector('.js-move-issues'); + + if (!el) { + return null; + } + + Vue.use(VueApollo); + + return new Vue({ + el, + name: 'MoveIssuesRoot', + apolloProvider: new VueApollo({ + defaultClient: gqlClient, + }), + render: (createElement) => + createElement(MoveIssuesButton, { + props: { + projectFullPath: el.dataset.projectFullPath, + projectsFetchPath: el.dataset.projectsFetchPath, + }, + }), + }); +} + +export function mountStatusDropdown() { + const el = document.querySelector('.js-status-dropdown'); + + if (!el) { + return null; + } + + return new Vue({ + el, + name: 'StatusDropdownRoot', + render: (createElement) => createElement(StatusDropdown), + }); +} + +export function mountSubscriptionsDropdown() { + const el = document.querySelector('.js-subscriptions-dropdown'); + + if (!el) { + return null; + } + + return new Vue({ + el, + name: 'SubscriptionsDropdownRoot', + render: (createElement) => createElement(SubscriptionsDropdown), + }); +} + const isAssigneesWidgetShown = (isInIssuePage() || isInDesignPage() || isInMRPage()) && gon.features.issueAssigneesWidget; diff --git a/app/assets/javascripts/issuable/bulk_update_sidebar/components/graphql/mutations/move_issue.mutation.graphql b/app/assets/javascripts/sidebar/queries/move_issue.mutation.graphql index d350072425b..d350072425b 100644 --- a/app/assets/javascripts/issuable/bulk_update_sidebar/components/graphql/mutations/move_issue.mutation.graphql +++ b/app/assets/javascripts/sidebar/queries/move_issue.mutation.graphql diff --git a/app/assets/javascripts/vue_shared/components/metric_images/metric_images_tab.vue b/app/assets/javascripts/vue_shared/components/metric_images/metric_images_tab.vue index e23721da223..2cadc87eca3 100644 --- a/app/assets/javascripts/vue_shared/components/metric_images/metric_images_tab.vue +++ b/app/assets/javascripts/vue_shared/components/metric_images/metric_images_tab.vue @@ -1,5 +1,5 @@ <script> -import { GlFormGroup, GlFormInput, GlLoadingIcon, GlModal, GlTab } from '@gitlab/ui'; +import { GlFormGroup, GlFormInput, GlLoadingIcon, GlModal } from '@gitlab/ui'; import { mapState, mapActions } from 'vuex'; import { __, s__ } from '~/locale'; import UploadDropzone from '~/vue_shared/components/upload_dropzone/upload_dropzone.vue'; @@ -11,7 +11,6 @@ export default { GlFormInput, GlLoadingIcon, GlModal, - GlTab, MetricImagesTable, UploadDropzone, }, @@ -82,7 +81,7 @@ export default { </script> <template> - <gl-tab :title="s__('Incident|Metrics')" data-testid="metrics-tab"> + <div> <div v-if="isLoadingMetricImages"> <gl-loading-icon class="gl-p-5" size="sm" /> </div> @@ -117,5 +116,5 @@ export default { :drop-description-message="$options.i18n.dropDescription" @change="openMetricDialog" /> - </gl-tab> + </div> </template> diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index 99284ea0a64..cf20ea90fe9 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -83,18 +83,9 @@ $darken-dark-factor: 10% !default; $darken-border-factor: 5% !default; $darken-border-dashed-factor: 25% !default; -$white: #fff !default; -$white-normal: #f0f0f0 !default; -$white-dark: #eaeaea !default; -$white-transparent: rgba($white, 0.8) !default; - $purple: #6d49cb !default; $purple-light: #ede8fb !default; -$black: #000 !default; -$black-transparent: rgba(0, 0, 0, 0.3) !default; -$almost-black: #242424 !default; - $green-50: #ecf4ee !default; $green-100: #c3e6cd !default; $green-200: #91d4a8 !default; @@ -183,6 +174,15 @@ $t-gray-a-08: rgba($gray-950, 0.08) !default; $t-gray-a-16: rgba($gray-950, 0.16) !default; $t-gray-a-24: rgba($gray-950, 0.24) !default; +$white: #fff !default; +$white-normal: $gray-50 !default; +$white-dark: darken($gray-50, 2) !default; +$white-transparent: rgba($white, 0.8) !default; + +$black: #000 !default; +$black-transparent: $t-gray-a-24 !default; +$almost-black: $gray-950 !default; + $greens: ( '50': $green-50, '100': $green-100, @@ -380,7 +380,7 @@ $well-expand-item: #e8f2f7 !default; $well-inner-border: #eef0f2 !default; $well-light-border: #f1f1f1; $well-light-text-color: #5b6169; -$nav-active-bg: rgba($black, 0.08); +$nav-active-bg: $t-gray-a-08; /* * Text @@ -732,8 +732,8 @@ $commit-stat-summary-height: 36px; /* * Common */ -$common-gray-light: #bbb; -$common-gray-dark: #444; +$common-gray-light: $gray-200; +$common-gray-dark: $gray-800; /* * Files diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index fa3c87490f1..4507cc2d1c6 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -4,7 +4,7 @@ $system-note-svg-size: 1rem; @mixin vertical-line($left) { &::before { content: ''; - border-left: 2px solid var(--gray-10, $gray-10); + border-left: 2px solid var(--gray-50, $gray-50); position: absolute; top: $gl-padding-6; bottom: 0; @@ -416,17 +416,17 @@ $system-note-svg-size: 1rem; .timeline-icon { display: flex; align-items: center; - background-color: $gray-10; + background-color: $gray-50; width: $system-note-icon-size; height: $system-note-icon-size; - border: 1px solid $gray-10; + border: 1px solid $gray-50; border-radius: $system-note-icon-size; margin: -6px 0 0; svg { width: $system-note-svg-size; height: $system-note-svg-size; - fill: $gray-400; + fill: $gray-600; display: block; margin: 0 auto; } diff --git a/app/assets/stylesheets/startup/startup-dark.scss b/app/assets/stylesheets/startup/startup-dark.scss index 11131cc1a4b..90bb028ef10 100644 --- a/app/assets/stylesheets/startup/startup-dark.scss +++ b/app/assets/stylesheets/startup/startup-dark.scss @@ -19,7 +19,7 @@ body.gl-dark { --black: #fff; } :root { - --white: #333; + --white: #333238; } *, *::before, @@ -117,7 +117,7 @@ button::-moz-focus-inner, kbd { padding: 0.2rem 0.4rem; font-size: 90%; - color: #333; + color: #333238; background-color: #ececef; border-radius: 0.2rem; } @@ -142,7 +142,7 @@ kbd kbd { font-weight: 400; line-height: 1.5; color: #ececef; - background-color: #333; + background-color: #333238; background-clip: padding-box; border: 1px solid #737278; border-radius: 0.25rem; @@ -158,7 +158,7 @@ kbd kbd { opacity: 1; } .form-control:disabled { - background-color: #333238; + background-color: #24232a; opacity: 1; } .form-inline { @@ -215,7 +215,7 @@ kbd kbd { color: #ececef; text-align: left; list-style: none; - background-color: #333; + background-color: #333238; background-clip: padding-box; border: 1px solid rgba(255, 255, 255, 0.15); border-radius: 0.25rem; @@ -410,7 +410,7 @@ a.gl-badge.badge-info:active { background-color: #0b5cad; } a.gl-badge.badge-info:active { - box-shadow: 0 0 0 1px #333, 0 0 0 3px #1f75cb; + box-shadow: 0 0 0 1px #333238, 0 0 0 3px #1f75cb; outline: none; } .gl-badge.badge-success { @@ -423,7 +423,7 @@ a.gl-badge.badge-success:active { background-color: #24663b; } a.gl-badge.badge-success:active { - box-shadow: 0 0 0 1px #333, 0 0 0 3px #1f75cb; + box-shadow: 0 0 0 1px #333238, 0 0 0 3px #1f75cb; outline: none; } .gl-badge.badge-warning { @@ -436,7 +436,7 @@ a.gl-badge.badge-warning:active { background-color: #8f4700; } a.gl-badge.badge-warning:active { - box-shadow: 0 0 0 1px #333, 0 0 0 3px #1f75cb; + box-shadow: 0 0 0 1px #333238, 0 0 0 3px #1f75cb; outline: none; } .gl-button .gl-badge { @@ -444,7 +444,7 @@ a.gl-badge.badge-warning:active { } .gl-form-input, .gl-form-input.form-control { - background-color: #333; + background-color: #333238; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Noto Sans", Ubuntu, Cantarell, "Helvetica Neue", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; @@ -527,11 +527,11 @@ a.gl-badge.badge-warning:active { border-radius: 0.25rem; } .gl-button.gl-button.btn-default { - background-color: #333; + background-color: #333238; } .gl-button.gl-button.btn-default:active, .gl-button.gl-button.btn-default.active { - box-shadow: inset 0 0 0 1px #a4a3a8, 0 0 0 1px #333, 0 0 0 3px #1f75cb; + box-shadow: inset 0 0 0 1px #a4a3a8, 0 0 0 1px #333238, 0 0 0 3px #1f75cb; outline: none; background-color: #434248; } @@ -613,7 +613,7 @@ html { font-size: 0.875rem; font-weight: 400; padding: 6px 10px; - background-color: #333; + background-color: #333238; border-color: #434248; color: #ececef; color: #ececef; @@ -625,7 +625,7 @@ html { } .btn:active, .btn.active { - background-color: #444; + background-color: #434248; border-color: #4f4f4f; color: #ececef; } @@ -649,7 +649,7 @@ html { position: relative; } .dropdown-menu-toggle:active { - box-shadow: 0 0 0 1px #333, 0 0 0 3px #1f75cb; + box-shadow: 0 0 0 1px #333238, 0 0 0 3px #1f75cb; outline: none; } .search-input-container .dropdown-menu { @@ -657,7 +657,7 @@ html { } .dropdown-menu-toggle { padding: 6px 8px 6px 10px; - background-color: #333; + background-color: #333238; color: #ececef; font-size: 14px; text-align: left; @@ -689,7 +689,7 @@ html { font-size: 0.875rem; font-weight: 400; padding: 8px 0; - background-color: #333; + background-color: #333238; border: 1px solid #434248; border-radius: 0.25rem; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); @@ -722,15 +722,15 @@ html { } .dropdown-menu li > a:active, .dropdown-menu li button:active { - background-color: #4f4f4f; + background-color: #4e4c53; color: #ececef; outline: 0; text-decoration: none; } .dropdown-menu li > a:active, .dropdown-menu li button:active { - box-shadow: inset 0 0 0 2px #1f75cb, inset 0 0 0 3px #333, - inset 0 0 0 1px #333; + box-shadow: inset 0 0 0 2px #1f75cb, inset 0 0 0 3px #333238, + inset 0 0 0 1px #333238; outline: none; } .dropdown-menu .divider { @@ -765,7 +765,7 @@ html { input { border-radius: 0.25rem; color: #ececef; - background-color: #333; + background-color: #333238; } .form-control { border-radius: 4px; @@ -1010,7 +1010,7 @@ kbd { float: left; margin-right: 5px; border-radius: 50%; - border: 1px solid #333; + border: 1px solid #333238; } .notification-dot { background-color: #9e5400; @@ -1049,7 +1049,7 @@ kbd { } .context-header .avatar-container { flex: 0 0 32px; - background-color: #333; + background-color: #333238; } .context-header .sidebar-context-title { overflow: hidden; @@ -1142,7 +1142,7 @@ kbd { font-weight: 600; } .nav-sidebar li.active:not(.fly-out-top-item) > a:not(.has-sub-items) { - background-color: rgba(255, 255, 255, 0.08); + background-color: rgba(251, 250, 253, 0.08); } .nav-sidebar ul { padding-left: 0; @@ -1189,7 +1189,7 @@ kbd { margin-bottom: -0.25rem; margin-top: 0; position: relative; - color: #333; + color: #333238; background: var(--black, #fff); } .nav-sidebar @@ -1417,7 +1417,7 @@ kbd { .close-nav-button { height: 48px; padding: 0 16px; - background-color: #333238; + background-color: #24232a; border: 0; color: #89888d; display: flex; @@ -1564,7 +1564,7 @@ svg.s16 { margin-left: 5px; line-height: 25px; width: 98%; - color: #333; + color: #333238; background: none; } .search .search-input-container { @@ -1627,7 +1627,7 @@ svg.s16 { width: 40px; height: 40px; padding: 0; - background: #222; + background: #212027; overflow: hidden; box-shadow: inset 0 0 0 1px rgba(251, 250, 253, 0.1); } @@ -1792,10 +1792,10 @@ body.gl-dark { --dark-icon-color-orange-2: #b37a5d; --gl-text-color: #ececef; --border-color: #4f4f4f; - --white: #333; + --white: #333238; --black: #fff; --gray-light: #333238; - --svg-status-bg: #333; + --svg-status-bg: #333238; } .nav-sidebar, .toggle-sidebar-button, @@ -1830,7 +1830,7 @@ body.gl-dark .navbar-gitlab .navbar-sub-nav > li.active > button, body.gl-dark .navbar-gitlab .navbar-nav > li.active > a, body.gl-dark .navbar-gitlab .navbar-nav > li.active > button { color: #ececef; - background-color: #333; + background-color: #333238; } body.gl-dark .navbar-gitlab .navbar-sub-nav { color: #ececef; @@ -1862,10 +1862,10 @@ body.gl-dark } body.gl-dark .navbar-gitlab .nav > li.active > a { color: #ececef; - background-color: #333; + background-color: #333238; } body.gl-dark .navbar-gitlab .nav > li.active > a .notification-dot { - border-color: #333; + border-color: #333238; } body.gl-dark .navbar-gitlab @@ -2036,10 +2036,10 @@ body.gl-dark { --dark-icon-color-orange-2: #b37a5d; --gl-text-color: #ececef; --border-color: #4f4f4f; - --white: #333; + --white: #333238; --black: #fff; --gray-light: #333238; - --svg-status-bg: #333; + --svg-status-bg: #333238; } .tab-width-8 { tab-size: 8; diff --git a/app/assets/stylesheets/startup/startup-general.scss b/app/assets/stylesheets/startup/startup-general.scss index 7fb373bb6f4..8ac4df55d0b 100644 --- a/app/assets/stylesheets/startup/startup-general.scss +++ b/app/assets/stylesheets/startup/startup-general.scss @@ -606,8 +606,8 @@ html { } .btn:active, .btn.active { - background-color: #eaeaea; - border-color: #e3e3e3; + background-color: #e6e6ea; + border-color: #dedee3; color: #333238; } .btn svg { @@ -1123,7 +1123,7 @@ kbd { font-weight: 600; } .nav-sidebar li.active:not(.fly-out-top-item) > a:not(.has-sub-items) { - background-color: rgba(0, 0, 0, 0.08); + background-color: rgba(31, 30, 36, 0.08); } .nav-sidebar ul { padding-left: 0; diff --git a/app/assets/stylesheets/startup/startup-signin.scss b/app/assets/stylesheets/startup/startup-signin.scss index 7ae158b3930..3979488b6cc 100644 --- a/app/assets/stylesheets/startup/startup-signin.scss +++ b/app/assets/stylesheets/startup/startup-signin.scss @@ -647,8 +647,8 @@ body.navless { } .btn:active, .btn.active { - background-color: #eaeaea; - border-color: #e3e3e3; + background-color: #e6e6ea; + border-color: #dedee3; color: #333238; } .btn svg { diff --git a/app/assets/stylesheets/themes/_dark.scss b/app/assets/stylesheets/themes/_dark.scss index a3474d2ed50..a3738568f9d 100644 --- a/app/assets/stylesheets/themes/_dark.scss +++ b/app/assets/stylesheets/themes/_dark.scss @@ -11,6 +11,20 @@ $gray-800: #dcdcde; $gray-900: #ececef; $gray-950: #fbfafd; +$gray-lightest: lighten($gray-10, 1); +$gray-light: lighten($gray-10, 2); +$gray-lighter: darken($gray-50, 4); +$gray-normal: $gray-50; +$gray-dark: darken($gray-100, 2); +$gray-darker: darken($gray-200, 2); +$gray-darkest: $gray-700; + +$black: #fff; +$black-normal: $gray-900; +$white: $gray-50; +$white-normal: $gray-50; +$white-dark: $gray-100; + $green-50: #0a4020; $green-100: #0d532a; $green-200: #24663b; @@ -83,21 +97,6 @@ $purple-800: #cbbbf2; $purple-900: #e1d8f9; $purple-950: #f4f0ff; -$gray-lightest: #222; -$gray-light: $gray-50; -$gray-lighter: #303030; -$gray-normal: #333; -$gray-dark: $gray-100; -$gray-darker: #4f4f4f; -$gray-darkest: #c4c4c4; - -$black: #fff; -$black-normal: $gray-900; -$white: #333; -$white-light: #2b2b2b; -$white-normal: #333; -$white-dark: #444; - $theme-indigo-50: #1a1a40; $border-color: #4f4f4f; diff --git a/app/services/projects/update_pages_service.rb b/app/services/projects/update_pages_service.rb index 6a963e7fcd1..0fadd75669e 100644 --- a/app/services/projects/update_pages_service.rb +++ b/app/services/projects/update_pages_service.rb @@ -63,16 +63,19 @@ module Projects end def build_commit_status + stage = create_stage + GenericCommitStatus.new( user: build.user, ci_stage: stage, name: 'pages:deploy', - stage: 'deploy' + stage: 'deploy', + stage_idx: stage.position ) end # rubocop: disable Performance/ActiveRecordSubtransactionMethods - def stage + def create_stage build.pipeline.stages.safe_find_or_create_by(name: 'deploy', pipeline_id: build.pipeline.id) do |stage| stage.position = GenericCommitStatus::EXTERNAL_STAGE_IDX stage.project = build.project diff --git a/config/metrics/settings/20210216180841_background_upload.yml b/config/metrics/settings/20210216180841_background_upload.yml index e9d9d475b06..22f7e5e078d 100644 --- a/config/metrics/settings/20210216180841_background_upload.yml +++ b/config/metrics/settings/20210216180841_background_upload.yml @@ -7,7 +7,8 @@ product_stage: enablement product_group: memory product_category: memory value_type: boolean -status: active +status: broken +repair_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/382900 time_frame: none data_source: system distribution: diff --git a/config/metrics/settings/20210216180851_background_upload.yml b/config/metrics/settings/20210216180851_background_upload.yml index b7a0d328b8b..21536e35303 100644 --- a/config/metrics/settings/20210216180851_background_upload.yml +++ b/config/metrics/settings/20210216180851_background_upload.yml @@ -8,7 +8,8 @@ product_stage: enablement product_group: memory product_category: memory value_type: boolean -status: active +status: broken +repair_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/382900 time_frame: none data_source: system distribution: diff --git a/config/metrics/settings/20210216180900_background_upload.yml b/config/metrics/settings/20210216180900_background_upload.yml index ed6931dd443..df93b11c656 100644 --- a/config/metrics/settings/20210216180900_background_upload.yml +++ b/config/metrics/settings/20210216180900_background_upload.yml @@ -7,7 +7,8 @@ product_stage: enablement product_group: memory product_category: memory value_type: boolean -status: active +status: broken +repair_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/382900 time_frame: none data_source: system distribution: diff --git a/config/metrics/settings/20210216180909_background_upload.yml b/config/metrics/settings/20210216180909_background_upload.yml index c18a22d1634..32871d5d30e 100644 --- a/config/metrics/settings/20210216180909_background_upload.yml +++ b/config/metrics/settings/20210216180909_background_upload.yml @@ -7,7 +7,8 @@ product_stage: enablement product_group: memory product_category: memory value_type: boolean -status: active +status: broken +repair_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/382900 time_frame: none data_source: system distribution: diff --git a/config/metrics/settings/20210216180918_background_upload.yml b/config/metrics/settings/20210216180918_background_upload.yml index 940273ff972..186e7c8b2b9 100644 --- a/config/metrics/settings/20210216180918_background_upload.yml +++ b/config/metrics/settings/20210216180918_background_upload.yml @@ -7,7 +7,8 @@ product_stage: enablement product_group: memory product_category: memory value_type: boolean -status: active +status: broken +repair_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/382900 time_frame: none data_source: system distribution: diff --git a/db/post_migrate/20221121152048_remove_unused_feedback_migration_index.rb b/db/post_migrate/20221121152048_remove_unused_feedback_migration_index.rb new file mode 100644 index 00000000000..b5dbafccd3a --- /dev/null +++ b/db/post_migrate/20221121152048_remove_unused_feedback_migration_index.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +class RemoveUnusedFeedbackMigrationIndex < Gitlab::Database::Migration[2.0] + INDEX_NAME = "tmp_idx_for_vulnerability_feedback_migration" + WHERE_CLAUSE = "migrated_to_state_transition = false AND feedback_type = 0" + + disable_ddl_transaction! + + def up + remove_concurrent_index_by_name( + :vulnerability_feedback, + INDEX_NAME + ) + end + + def down + add_concurrent_index( + :vulnerability_feedback, + %i[migrated_to_state_transition feedback_type], + where: WHERE_CLAUSE, + name: INDEX_NAME + ) + end +end diff --git a/db/post_migrate/20221121152515_add_supporting_index_for_vulnerabilities_feedback_migration2.rb b/db/post_migrate/20221121152515_add_supporting_index_for_vulnerabilities_feedback_migration2.rb new file mode 100644 index 00000000000..8c55f2da957 --- /dev/null +++ b/db/post_migrate/20221121152515_add_supporting_index_for_vulnerabilities_feedback_migration2.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +class AddSupportingIndexForVulnerabilitiesFeedbackMigration2 < Gitlab::Database::Migration[2.0] + INDEX_NAME = "tmp_idx_for_vulnerability_feedback_migration" + WHERE_CLAUSE = "migrated_to_state_transition = false AND feedback_type = 0" + + disable_ddl_transaction! + + def up + add_concurrent_index( + :vulnerability_feedback, + :id, + where: WHERE_CLAUSE, + name: INDEX_NAME + ) + end + + def down + remove_concurrent_index_by_name( + :vulnerability_feedback, + INDEX_NAME + ) + end +end diff --git a/db/schema_migrations/20221121152048 b/db/schema_migrations/20221121152048 new file mode 100644 index 00000000000..8d19b1ff54e --- /dev/null +++ b/db/schema_migrations/20221121152048 @@ -0,0 +1 @@ +daf3e3b4d3b7b6487542f5cc418b0308bc22da13c0ac6f189ab3fb9352e23898
\ No newline at end of file diff --git a/db/schema_migrations/20221121152515 b/db/schema_migrations/20221121152515 new file mode 100644 index 00000000000..cb105448807 --- /dev/null +++ b/db/schema_migrations/20221121152515 @@ -0,0 +1 @@ +23e3d67029b004c63e4c0843ca58556e259c5795075a772043418181335e3349
\ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 5c208a21ea2..f93c5f65684 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -31408,7 +31408,7 @@ CREATE UNIQUE INDEX taggings_idx ON taggings USING btree (tag_id, taggable_id, t CREATE UNIQUE INDEX term_agreements_unique_index ON term_agreements USING btree (user_id, term_id); -CREATE INDEX tmp_idx_for_vulnerability_feedback_migration ON vulnerability_feedback USING btree (migrated_to_state_transition, feedback_type) WHERE ((migrated_to_state_transition = false) AND (feedback_type = 0)); +CREATE INDEX tmp_idx_for_vulnerability_feedback_migration ON vulnerability_feedback USING btree (id) WHERE ((migrated_to_state_transition = false) AND (feedback_type = 0)); CREATE INDEX tmp_idx_vulnerabilities_on_id_where_report_type_7_99 ON vulnerabilities USING btree (id) WHERE (report_type = ANY (ARRAY[7, 99])); diff --git a/doc/user/project/integrations/webhooks.md b/doc/user/project/integrations/webhooks.md index 399e9f32ae1..be8b4cfed01 100644 --- a/doc/user/project/integrations/webhooks.md +++ b/doc/user/project/integrations/webhooks.md @@ -132,16 +132,15 @@ The feature is not ready for production use. If a webhook fails repeatedly, it may be disabled automatically. Webhooks that return response codes in the `5xx` range are understood to be failing -intermittently, and are temporarily disabled. This lasts initially -for 10 minutes. If the hook continues to fail, the back-off period is -extended on each retry, up to a maximum disabled period of 24 hours. +intermittently and are temporarily disabled. These webhooks are initially disabled +for 1 minute, which is extended on each retry up to a maximum of 24 hours. -Webhooks that return failure codes in the `4xx` range are understood to be -misconfigured, and these are disabled until you manually re-enable -them. These webhooks are not automatically retried. +Webhooks that return response codes in the `4xx` range are understood to be +misconfigured and are permanently disabled until you manually re-enable +them yourself. -See [troubleshooting](#troubleshoot-webhooks) for information on -how to see if a webhook is disabled, and how to re-enable it. +See [Troubleshooting](#troubleshoot-webhooks) for more information on +disabled webhooks and how to re-enable them. ## Test a webhook diff --git a/lib/api/commit_statuses.rb b/lib/api/commit_statuses.rb index 954b572c9b1..e785d10f7e7 100644 --- a/lib/api/commit_statuses.rb +++ b/lib/api/commit_statuses.rb @@ -110,13 +110,22 @@ module API authorize! :update_pipeline, pipeline + # rubocop: disable Performance/ActiveRecordSubtransactionMethods + stage = pipeline.stages.safe_find_or_create_by!(name: 'external') do |stage| + stage.position = GenericCommitStatus::EXTERNAL_STAGE_IDX + stage.project = pipeline.project + end + # rubocop: enable Performance/ActiveRecordSubtransactionMethods + status = GenericCommitStatus.running_or_pending.find_or_initialize_by( project: user_project, pipeline: pipeline, name: name, ref: ref, user: current_user, - protected: user_project.protected_for?(ref) + protected: user_project.protected_for?(ref), + ci_stage: stage, + stage_idx: stage.position ) updatable_optional_attributes = %w[target_url description coverage] diff --git a/lib/gitlab/audit/auditor.rb b/lib/gitlab/audit/auditor.rb index 4a6e4e2e06e..178c6e0be7d 100644 --- a/lib/gitlab/audit/auditor.rb +++ b/lib/gitlab/audit/auditor.rb @@ -84,14 +84,23 @@ module Gitlab end def record(events) - log_events(events) unless @stream_only - send_to_stream(events) + @stream_only ? send_to_stream(events) : log_events_and_stream(events) end - def log_events(events) + def log_events_and_stream(events) log_authentication_event - log_to_database(events) + saved_events = log_to_database(events) + + # we only want to override events with saved_events when it successfully saves into database. + # we are doing so to ensure events in memory reflects events saved in database and have id column. + events = saved_events if saved_events.present? + + log_to_file_and_stream(events) + end + + def log_to_file_and_stream(events) log_to_file(events) + send_to_stream(events) end def audit_enabled? @@ -145,7 +154,13 @@ module Gitlab end def log_to_database(events) - AuditEvent.bulk_insert!(events) + if events.one? + events.first.save! + events + else + event_ids = AuditEvent.bulk_insert!(events, returns: :ids) + AuditEvent.id_in(event_ids) + end rescue ActiveRecord::RecordInvalid => e ::Gitlab::ErrorTracking.track_exception(e, audit_operation: @name) end diff --git a/lib/gitlab/ci/build/context/build.rb b/lib/gitlab/ci/build/context/build.rb index a1a8e9288c7..a2b330f19b1 100644 --- a/lib/gitlab/ci/build/context/build.rb +++ b/lib/gitlab/ci/build/context/build.rb @@ -50,3 +50,5 @@ module Gitlab end end end + +Gitlab::Ci::Build::Context::Build.prepend_mod_with('Gitlab::Ci::Build::Context::Build') diff --git a/lib/gitlab/usage_data.rb b/lib/gitlab/usage_data.rb index 82db102e858..238fd9a92cd 100644 --- a/lib/gitlab/usage_data.rb +++ b/lib/gitlab/usage_data.rb @@ -299,7 +299,7 @@ module Gitlab object_store: { enabled: alt_usage_data { config['enabled'] }, direct_upload: alt_usage_data { config['direct_upload'] }, - background_upload: alt_usage_data { config['background_upload'] }, + background_upload: alt_usage_data { false }, # This setting no longer exists provider: alt_usage_data { config['connection']['provider'] } } } diff --git a/scripts/undercoverage b/scripts/undercoverage index 86153671d6a..348f421c0d5 100755 --- a/scripts/undercoverage +++ b/scripts/undercoverage @@ -21,6 +21,13 @@ end compare_base = ARGV[0] compare_base ||= IO.popen(%w(git merge-base origin/master HEAD)) { |p| p.read.chomp } +coverage_file_path = 'coverage/lcov/gitlab.lcov' + +result = if File.exist?(coverage_file_path) + Undercover::CLI.run(%W(-c #{compare_base})) + else + warn "#{coverage_file_path} doesn't exist" + 0 + end -result = Undercover::CLI.run(%W(-c #{compare_base})) exit result diff --git a/spec/frontend/issues/list/components/issues_list_app_spec.js b/spec/frontend/issues/list/components/issues_list_app_spec.js index d0c93c896b3..eb72968cb18 100644 --- a/spec/frontend/issues/list/components/issues_list_app_spec.js +++ b/spec/frontend/issues/list/components/issues_list_app_spec.js @@ -61,7 +61,7 @@ import { TOKEN_TYPE_TYPE, } from '~/vue_shared/components/filtered_search_bar/constants'; -import('~/issuable/bulk_update_sidebar'); +import('~/issuable'); import('~/users_select'); jest.mock('@sentry/browser'); diff --git a/spec/frontend/issues/show/components/incidents/incident_tabs_spec.js b/spec/frontend/issues/show/components/incidents/incident_tabs_spec.js index 458c1c3f858..33a3a6eddfc 100644 --- a/spec/frontend/issues/show/components/incidents/incident_tabs_spec.js +++ b/spec/frontend/issues/show/components/incidents/incident_tabs_spec.js @@ -1,10 +1,11 @@ -import { GlTab } from '@gitlab/ui'; -import { shallowMount } from '@vue/test-utils'; import merge from 'lodash/merge'; +import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_helper'; import { trackIncidentDetailsViewsOptions } from '~/incidents/constants'; import DescriptionComponent from '~/issues/show/components/description.vue'; import HighlightBar from '~/issues/show/components/incidents/highlight_bar.vue'; -import IncidentTabs from '~/issues/show/components/incidents/incident_tabs.vue'; +import IncidentTabs, { + incidentTabsI18n, +} from '~/issues/show/components/incidents/incident_tabs.vue'; import INVALID_URL from '~/lib/utils/invalid_url'; import Tracking from '~/tracking'; import AlertDetailsTable from '~/vue_shared/components/alert_details_table.vue'; @@ -16,11 +17,24 @@ const mockAlert = { iid: '1', }; +const defaultMocks = { + $apollo: { + queries: { + alert: { + loading: true, + }, + timelineEvents: { + loading: false, + }, + }, + }, +}; + describe('Incident Tabs component', () => { let wrapper; - const mountComponent = (data = {}, options = {}) => { - wrapper = shallowMount( + const mountComponent = ({ data = {}, options = {}, mount = shallowMountExtended } = {}) => { + wrapper = mount( IncidentTabs, merge( { @@ -29,7 +43,7 @@ describe('Incident Tabs component', () => { }, stubs: { DescriptionComponent: true, - MetricsTab: true, + IncidentMetricTab: true, }, provide: { fullPath: '', @@ -37,41 +51,37 @@ describe('Incident Tabs component', () => { projectId: '', issuableId: '', uploadMetricsFeatureAvailable: true, + slaFeatureAvailable: true, + canUpdate: true, + canUpdateTimelineEvent: true, }, data() { return { alert: mockAlert, ...data }; }, - mocks: { - $apollo: { - queries: { - alert: { - loading: true, - }, - timelineEvents: { - loading: false, - }, - }, - }, - }, + mocks: defaultMocks, }, options, ), ); }; - const findTabs = () => wrapper.findAllComponents(GlTab); - const findSummaryTab = () => findTabs().at(0); - const findAlertDetailsTab = () => wrapper.find('[data-testid="alert-details-tab"]'); + const findSummaryTab = () => wrapper.findByTestId('summary-tab'); + const findTimelineTab = () => wrapper.findByTestId('timeline-tab'); + const findAlertDetailsTab = () => wrapper.findByTestId('alert-details-tab'); const findAlertDetailsComponent = () => wrapper.findComponent(AlertDetailsTable); const findDescriptionComponent = () => wrapper.findComponent(DescriptionComponent); const findHighlightBarComponent = () => wrapper.findComponent(HighlightBar); + const findTabButtonByFilter = (filter) => wrapper.findAllByRole('tab').filter(filter); + const findTimelineTabButton = () => + findTabButtonByFilter((inner) => inner.text() === incidentTabsI18n.timelineTitle).at(0); + const findActiveTabs = () => findTabButtonByFilter((inner) => inner.classes('active')); - describe('empty state', () => { + describe('with no alerts', () => { beforeEach(() => { - mountComponent({ alert: null }); + mountComponent({ data: { alert: null } }); }); - it('does not show the alert details tab', () => { + it('does not show the alert details tab option', () => { expect(findAlertDetailsComponent().exists()).toBe(false); }); }); @@ -83,7 +93,12 @@ describe('Incident Tabs component', () => { it('renders the summary tab', () => { expect(findSummaryTab().exists()).toBe(true); - expect(findSummaryTab().attributes('title')).toBe('Summary'); + expect(findSummaryTab().attributes('title')).toBe(incidentTabsI18n.summaryTitle); + }); + + it('renders the timeline tab', () => { + expect(findTimelineTab().exists()).toBe(true); + expect(findTimelineTab().attributes('title')).toBe(incidentTabsI18n.timelineTitle); }); it('renders the alert details tab', () => { @@ -125,4 +140,22 @@ describe('Incident Tabs component', () => { expect(Tracking.event).toHaveBeenCalledWith(category, action); }); }); + + describe('tab changing', () => { + beforeEach(() => { + mountComponent({ mount: mountExtended }); + }); + + it('shows only the summary tab by default', async () => { + expect(findActiveTabs()).toHaveLength(1); + expect(findActiveTabs().at(0).text()).toBe(incidentTabsI18n.summaryTitle); + }); + + it("shows the timeline tab after it's clicked", async () => { + await findTimelineTabButton().trigger('click'); + + expect(findActiveTabs()).toHaveLength(1); + expect(findActiveTabs().at(0).text()).toBe(incidentTabsI18n.timelineTitle); + }); + }); }); diff --git a/spec/frontend/sidebar/assignee_title_spec.js b/spec/frontend/sidebar/components/assignees/assignee_title_spec.js index 14a6bdbf907..14a6bdbf907 100644 --- a/spec/frontend/sidebar/assignee_title_spec.js +++ b/spec/frontend/sidebar/components/assignees/assignee_title_spec.js diff --git a/spec/frontend/sidebar/assignees_realtime_spec.js b/spec/frontend/sidebar/components/assignees/assignees_realtime_spec.js index ae8f07bf901..5b6f4d3b557 100644 --- a/spec/frontend/sidebar/assignees_realtime_spec.js +++ b/spec/frontend/sidebar/components/assignees/assignees_realtime_spec.js @@ -11,7 +11,7 @@ import Mock, { issuableQueryResponse, subscriptionNullResponse, subscriptionResponse, -} from './mock_data'; +} from '../../mock_data'; Vue.use(VueApollo); diff --git a/spec/frontend/sidebar/assignees_spec.js b/spec/frontend/sidebar/components/assignees/assignees_spec.js index 7cf7fd33022..6971ae2f9ed 100644 --- a/spec/frontend/sidebar/assignees_spec.js +++ b/spec/frontend/sidebar/components/assignees/assignees_spec.js @@ -5,7 +5,7 @@ import { trimText } from 'helpers/text_helper'; import UsersMockHelper from 'helpers/user_mock_data_helper'; import Assignee from '~/sidebar/components/assignees/assignees.vue'; import AssigneeAvatarLink from '~/sidebar/components/assignees/assignee_avatar_link.vue'; -import UsersMock from './mock_data'; +import UsersMock from '../../mock_data'; describe('Assignee component', () => { const getDefaultProps = () => ({ diff --git a/spec/frontend/sidebar/issuable_assignees_spec.js b/spec/frontend/sidebar/components/assignees/issuable_assignees_spec.js index 1161fefcc64..1161fefcc64 100644 --- a/spec/frontend/sidebar/issuable_assignees_spec.js +++ b/spec/frontend/sidebar/components/assignees/issuable_assignees_spec.js diff --git a/spec/frontend/sidebar/sidebar_assignees_spec.js b/spec/frontend/sidebar/components/assignees/sidebar_assignees_spec.js index 2cb2425532b..58b174059fa 100644 --- a/spec/frontend/sidebar/sidebar_assignees_spec.js +++ b/spec/frontend/sidebar/components/assignees/sidebar_assignees_spec.js @@ -8,7 +8,7 @@ import SidebarAssignees from '~/sidebar/components/assignees/sidebar_assignees.v import SidebarService from '~/sidebar/services/sidebar_service'; import SidebarMediator from '~/sidebar/sidebar_mediator'; import SidebarStore from '~/sidebar/stores/sidebar_store'; -import Mock from './mock_data'; +import Mock from '../../mock_data'; describe('sidebar assignees', () => { let wrapper; diff --git a/spec/frontend/sidebar/components/copy_email_to_clipboard_spec.js b/spec/frontend/sidebar/components/copy_email/copy_email_to_clipboard_spec.js index 69a8d645973..9f94a1b3f3a 100644 --- a/spec/frontend/sidebar/components/copy_email_to_clipboard_spec.js +++ b/spec/frontend/sidebar/components/copy_email/copy_email_to_clipboard_spec.js @@ -1,5 +1,5 @@ import { shallowMount } from '@vue/test-utils'; -import CopyEmailToClipboard from '~/sidebar/components/copy_email_to_clipboard.vue'; +import CopyEmailToClipboard from '~/sidebar/components/copy_email/copy_email_to_clipboard.vue'; import CopyableField from '~/vue_shared/components/sidebar/copyable_field.vue'; describe('CopyEmailToClipboard component', () => { diff --git a/spec/frontend/sidebar/components/crm_contacts_spec.js b/spec/frontend/sidebar/components/crm_contacts/crm_contacts_spec.js index 6d76fa1f9df..2281b38cc53 100644 --- a/spec/frontend/sidebar/components/crm_contacts_spec.js +++ b/spec/frontend/sidebar/components/crm_contacts/crm_contacts_spec.js @@ -11,7 +11,7 @@ import { getIssueCrmContactsQueryResponse, issueCrmContactsUpdateResponse, issueCrmContactsUpdateNullResponse, -} from './mock_data'; +} from '../mock_data'; jest.mock('~/flash'); diff --git a/spec/frontend/sidebar/lock/__snapshots__/edit_form_spec.js.snap b/spec/frontend/sidebar/components/lock/__snapshots__/edit_form_spec.js.snap index 18d4df297df..18d4df297df 100644 --- a/spec/frontend/sidebar/lock/__snapshots__/edit_form_spec.js.snap +++ b/spec/frontend/sidebar/components/lock/__snapshots__/edit_form_spec.js.snap diff --git a/spec/frontend/sidebar/lock/constants.js b/spec/frontend/sidebar/components/lock/constants.js index b9f08e9286d..b9f08e9286d 100644 --- a/spec/frontend/sidebar/lock/constants.js +++ b/spec/frontend/sidebar/components/lock/constants.js diff --git a/spec/frontend/sidebar/lock/edit_form_buttons_spec.js b/spec/frontend/sidebar/components/lock/edit_form_buttons_spec.js index 2abb0c24d7d..2abb0c24d7d 100644 --- a/spec/frontend/sidebar/lock/edit_form_buttons_spec.js +++ b/spec/frontend/sidebar/components/lock/edit_form_buttons_spec.js diff --git a/spec/frontend/sidebar/lock/edit_form_spec.js b/spec/frontend/sidebar/components/lock/edit_form_spec.js index 4ae9025ee39..4ae9025ee39 100644 --- a/spec/frontend/sidebar/lock/edit_form_spec.js +++ b/spec/frontend/sidebar/components/lock/edit_form_spec.js diff --git a/spec/frontend/sidebar/lock/issuable_lock_form_spec.js b/spec/frontend/sidebar/components/lock/issuable_lock_form_spec.js index 8f825847cfc..8f825847cfc 100644 --- a/spec/frontend/sidebar/lock/issuable_lock_form_spec.js +++ b/spec/frontend/sidebar/components/lock/issuable_lock_form_spec.js diff --git a/spec/frontend/issuable/bulk_update_sidebar/components/move_issues_button_spec.js b/spec/frontend/sidebar/components/move/move_issues_button_spec.js index c432d722637..4eed5785977 100644 --- a/spec/frontend/issuable/bulk_update_sidebar/components/move_issues_button_spec.js +++ b/spec/frontend/sidebar/components/move/move_issues_button_spec.js @@ -9,9 +9,9 @@ import { useMockLocationHelper } from 'helpers/mock_window_location_helper'; import createFlash from '~/flash'; import { logError } from '~/lib/logger'; import IssuableMoveDropdown from '~/vue_shared/components/sidebar/issuable_move_dropdown.vue'; -import MoveIssuesButton from '~/issuable/bulk_update_sidebar/components/move_issues_button.vue'; import issuableEventHub from '~/issues/list/eventhub'; -import moveIssueMutation from '~/issuable/bulk_update_sidebar/components/graphql/mutations/move_issue.mutation.graphql'; +import MoveIssuesButton from '~/sidebar/components/move/move_issues_button.vue'; +import moveIssueMutation from '~/sidebar/queries/move_issue.mutation.graphql'; import getIssuesQuery from 'ee_else_ce/issues/list/queries/get_issues.query.graphql'; import getIssuesCountsQuery from 'ee_else_ce/issues/list/queries/get_issues_counts.query.graphql'; import { getIssuesCountsQueryResponse, getIssuesQueryResponse } from 'jest/issues/list/mock_data'; diff --git a/spec/frontend/sidebar/participants_spec.js b/spec/frontend/sidebar/components/participants/participants_spec.js index f7a626a189c..f7a626a189c 100644 --- a/spec/frontend/sidebar/participants_spec.js +++ b/spec/frontend/sidebar/components/participants/participants_spec.js diff --git a/spec/frontend/sidebar/reviewer_title_spec.js b/spec/frontend/sidebar/components/reviewers/reviewer_title_spec.js index 68ecd62e4c6..68ecd62e4c6 100644 --- a/spec/frontend/sidebar/reviewer_title_spec.js +++ b/spec/frontend/sidebar/components/reviewers/reviewer_title_spec.js diff --git a/spec/frontend/sidebar/reviewers_spec.js b/spec/frontend/sidebar/components/reviewers/reviewers_spec.js index 229f7ffbe04..229f7ffbe04 100644 --- a/spec/frontend/sidebar/reviewers_spec.js +++ b/spec/frontend/sidebar/components/reviewers/reviewers_spec.js diff --git a/spec/frontend/issuable/bulk_update_sidebar/components/status_dropdown_spec.js b/spec/frontend/sidebar/components/status/status_dropdown_spec.js index 2f281cb88f9..5a75299c3a4 100644 --- a/spec/frontend/issuable/bulk_update_sidebar/components/status_dropdown_spec.js +++ b/spec/frontend/sidebar/components/status/status_dropdown_spec.js @@ -1,7 +1,7 @@ import { GlDropdown, GlDropdownItem } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; -import StatusDropdown from '~/issuable/bulk_update_sidebar/components/status_dropdown.vue'; -import { statusDropdownOptions } from '~/issuable/bulk_update_sidebar/constants'; +import StatusDropdown from '~/sidebar/components/status/status_dropdown.vue'; +import { statusDropdownOptions } from '~/sidebar/constants'; describe('SubscriptionsDropdown component', () => { let wrapper; diff --git a/spec/frontend/issuable/bulk_update_sidebar/components/subscriptions_dropdown_spec.js b/spec/frontend/sidebar/components/subscriptions/subscriptions_dropdown_spec.js index 56ef7a1ed39..3fb8214606c 100644 --- a/spec/frontend/issuable/bulk_update_sidebar/components/subscriptions_dropdown_spec.js +++ b/spec/frontend/sidebar/components/subscriptions/subscriptions_dropdown_spec.js @@ -1,8 +1,8 @@ import { GlDropdown, GlDropdownItem } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; import { nextTick } from 'vue'; -import SubscriptionsDropdown from '~/issuable/bulk_update_sidebar/components/subscriptions_dropdown.vue'; -import { subscriptionsDropdownOptions } from '~/issuable/bulk_update_sidebar/constants'; +import SubscriptionsDropdown from '~/sidebar/components/subscriptions/subscriptions_dropdown.vue'; +import { subscriptionsDropdownOptions } from '~/sidebar/constants'; describe('SubscriptionsDropdown component', () => { let wrapper; diff --git a/spec/frontend/sidebar/subscriptions_spec.js b/spec/frontend/sidebar/components/subscriptions/subscriptions_spec.js index 1a1aa370eef..1a1aa370eef 100644 --- a/spec/frontend/sidebar/subscriptions_spec.js +++ b/spec/frontend/sidebar/components/subscriptions/subscriptions_spec.js diff --git a/spec/frontend/sidebar/__snapshots__/todo_spec.js.snap b/spec/frontend/sidebar/components/todo_toggle/__snapshots__/todo_spec.js.snap index 846f45345e7..846f45345e7 100644 --- a/spec/frontend/sidebar/__snapshots__/todo_spec.js.snap +++ b/spec/frontend/sidebar/components/todo_toggle/__snapshots__/todo_spec.js.snap diff --git a/spec/frontend/sidebar/todo_spec.js b/spec/frontend/sidebar/components/todo_toggle/todo_spec.js index 8e6597bf80f..8e6597bf80f 100644 --- a/spec/frontend/sidebar/todo_spec.js +++ b/spec/frontend/sidebar/components/todo_toggle/todo_spec.js diff --git a/spec/frontend/sidebar/sidebar_move_issue_spec.js b/spec/frontend/sidebar/lib/sidebar_move_issue_spec.js index 195cc6ddeeb..6e365df329b 100644 --- a/spec/frontend/sidebar/sidebar_move_issue_spec.js +++ b/spec/frontend/sidebar/lib/sidebar_move_issue_spec.js @@ -8,7 +8,7 @@ import SidebarService from '~/sidebar/services/sidebar_service'; import SidebarMediator from '~/sidebar/sidebar_mediator'; import SidebarStore from '~/sidebar/stores/sidebar_store'; import { GitLabDropdown } from '~/deprecated_jquery_dropdown/gl_dropdown'; -import Mock from './mock_data'; +import Mock from '../mock_data'; jest.mock('~/flash'); diff --git a/spec/frontend/sidebar/sidebar_store_spec.js b/spec/frontend/sidebar/stores/sidebar_store_spec.js index 3930dabfcfa..3f4b80409c2 100644 --- a/spec/frontend/sidebar/sidebar_store_spec.js +++ b/spec/frontend/sidebar/stores/sidebar_store_spec.js @@ -1,6 +1,6 @@ import UsersMockHelper from 'helpers/user_mock_data_helper'; import SidebarStore from '~/sidebar/stores/sidebar_store'; -import Mock from './mock_data'; +import Mock from '../mock_data'; const ASSIGNEE = { id: 2, diff --git a/spec/lib/gitlab/audit/auditor_spec.rb b/spec/lib/gitlab/audit/auditor_spec.rb index f743515e616..4b16333d913 100644 --- a/spec/lib/gitlab/audit/auditor_spec.rb +++ b/spec/lib/gitlab/audit/auditor_spec.rb @@ -68,6 +68,7 @@ RSpec.describe Gitlab::Audit::Auditor do expect(logger).to have_received(:info).with( hash_including( + 'id' => AuditEvent.last.id, 'author_id' => author.id, 'author_name' => author.name, 'entity_id' => group.id, @@ -112,6 +113,7 @@ RSpec.describe Gitlab::Audit::Auditor do expect(logger).to have_received(:info).with( hash_including( + 'id' => AuditEvent.last.id, 'author_id' => author.id, 'author_name' => author.name, 'entity_id' => group.id, @@ -244,7 +246,9 @@ RSpec.describe Gitlab::Audit::Auditor do let(:audit!) { auditor.audit(context) } before do - allow(AuditEvent).to receive(:bulk_insert!).and_raise(ActiveRecord::RecordInvalid) + expect_next_instance_of(AuditEvent) do |instance| + allow(instance).to receive(:save!).and_raise(ActiveRecord::RecordInvalid) + end allow(Gitlab::ErrorTracking).to receive(:track_exception) end @@ -261,5 +265,27 @@ RSpec.describe Gitlab::Audit::Auditor do expect { auditor.audit(context) }.not_to raise_exception end end + + context 'when audit event is not saved in database due to some database infra issue' do + let(:audit!) { auditor.audit(context) } + + before do + allow_any_instance_of(auditor) do |auditor_instance| + allow(auditor_instance).to receive(:log_to_database).and_return(nil) + end + end + + it 'calls log_to_file_and_stream with in memory events' do + audit! + + expect_any_instance_of(auditor) do |auditor_instance| + expect(auditor_instance).to receive(:log_to_file_and_stream).with(include(kind_of(AuditEvent))) + end + end + + it 'does not throw exception' do + expect { auditor.audit(context) }.not_to raise_exception + end + end end end diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb index 1418adb535f..79cd9ffc950 100644 --- a/spec/lib/gitlab/usage_data_spec.rb +++ b/spec/lib/gitlab/usage_data_spec.rb @@ -598,7 +598,7 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do external_diffs: { enabled: false }, lfs: { enabled: true, object_store: { enabled: false, direct_upload: true, background_upload: false, provider: "AWS" } }, uploads: { enabled: nil, object_store: { enabled: false, direct_upload: true, background_upload: false, provider: "AWS" } }, - packages: { enabled: true, object_store: { enabled: false, direct_upload: false, background_upload: true, provider: "AWS" } } } + packages: { enabled: true, object_store: { enabled: false, direct_upload: false, background_upload: false, provider: "AWS" } } } ) end diff --git a/spec/requests/api/commit_statuses_spec.rb b/spec/requests/api/commit_statuses_spec.rb index dc5d9620dc4..8cfac4b736f 100644 --- a/spec/requests/api/commit_statuses_spec.rb +++ b/spec/requests/api/commit_statuses_spec.rb @@ -167,7 +167,7 @@ RSpec.describe API::CommitStatuses do let!(:pipeline) { create(:ci_pipeline, project: project, sha: sha, ref: 'ref') } let(:params) { { state: 'pending' } } - shared_examples_for 'creates a commit status for the existing pipeline' do + shared_examples_for 'creates a commit status for the existing pipeline with an external stage' do it do expect do post api(post_url, developer), params: params @@ -176,19 +176,73 @@ RSpec.describe API::CommitStatuses do job = pipeline.statuses.find_by_name(json_response['name']) expect(response).to have_gitlab_http_status(:created) + expect(job.ci_stage.name).to eq('external') + expect(job.ci_stage.position).to eq(GenericCommitStatus::EXTERNAL_STAGE_IDX) + expect(job.ci_stage.pipeline).to eq(pipeline) expect(job.status).to eq('pending') expect(job.stage_idx).to eq(GenericCommitStatus::EXTERNAL_STAGE_IDX) end end - it_behaves_like 'creates a commit status for the existing pipeline' + shared_examples_for 'updates the commit status with an external stage' do + before do + post api(post_url, developer), params: { state: 'pending' } + end + + it 'updates the commit status with the external stage' do + post api(post_url, developer), params: { state: 'running' } + job = pipeline.statuses.find_by_name(json_response['name']) + + expect(job.ci_stage.name).to eq('external') + expect(job.ci_stage.position).to eq(GenericCommitStatus::EXTERNAL_STAGE_IDX) + expect(job.ci_stage.pipeline).to eq(pipeline) + expect(job.status).to eq('running') + expect(job.stage_idx).to eq(GenericCommitStatus::EXTERNAL_STAGE_IDX) + end + end context 'with pipeline for merge request' do let!(:merge_request) { create(:merge_request, :with_detached_merge_request_pipeline, source_project: project) } let!(:pipeline) { merge_request.all_pipelines.last } let(:sha) { pipeline.sha } - it_behaves_like 'creates a commit status for the existing pipeline' + it_behaves_like 'creates a commit status for the existing pipeline with an external stage' + end + + context 'when an external stage does not exist' do + context 'when the commit status does not exist' do + it_behaves_like 'creates a commit status for the existing pipeline with an external stage' + end + + context 'when the commit status exists' do + it_behaves_like 'updates the commit status with an external stage' + end + end + + context 'when an external stage already exists' do + let(:stage) { create(:ci_stage, name: 'external', pipeline: pipeline, position: 1_000_000) } + + context 'when the commit status exists' do + it_behaves_like 'updates the commit status with an external stage' + end + + context 'when the commit status does not exist' do + it_behaves_like 'creates a commit status for the existing pipeline with an external stage' + end + end + end + + context 'when the pipeline does not exist' do + it 'creates a commit status and a stage' do + expect do + post api(post_url, developer), params: { state: 'pending' } + end.to change { Ci::Pipeline.count }.by(1) + job = Ci::Pipeline.last.statuses.find_by_name(json_response['name']) + + expect(job.ci_stage.name).to eq('external') + expect(job.ci_stage.position).to eq(GenericCommitStatus::EXTERNAL_STAGE_IDX) + expect(job.status).to eq('pending') + expect(job.stage_idx).to eq(GenericCommitStatus::EXTERNAL_STAGE_IDX) end end end diff --git a/spec/services/ci/pipelines/add_job_service_spec.rb b/spec/services/ci/pipelines/add_job_service_spec.rb index e735b2752d9..c62aa9506bd 100644 --- a/spec/services/ci/pipelines/add_job_service_spec.rb +++ b/spec/services/ci/pipelines/add_job_service_spec.rb @@ -5,7 +5,7 @@ require 'spec_helper' RSpec.describe Ci::Pipelines::AddJobService do include ExclusiveLeaseHelpers - let_it_be(:pipeline) { create(:ci_pipeline) } + let_it_be_with_reload(:pipeline) { create(:ci_pipeline) } let(:job) { build(:ci_build) } @@ -35,7 +35,7 @@ RSpec.describe Ci::Pipelines::AddJobService do end it 'assigns partition_id to job and metadata' do - pipeline.partition_id = 123 + pipeline.partition_id = ci_testing_partition_id expect { execute } .to change(job, :partition_id).to(pipeline.partition_id) diff --git a/spec/services/projects/update_pages_service_spec.rb b/spec/services/projects/update_pages_service_spec.rb index a69db3b9970..d908a169898 100644 --- a/spec/services/projects/update_pages_service_spec.rb +++ b/spec/services/projects/update_pages_service_spec.rb @@ -320,10 +320,11 @@ RSpec.describe Projects::UpdatePagesService do end context 'when retrying the job' do + let(:stage) { create(:ci_stage, position: 1_000_000, name: 'deploy', pipeline: pipeline) } let!(:older_deploy_job) do create(:generic_commit_status, :failed, pipeline: pipeline, ref: build.ref, - stage: 'deploy', + ci_stage: stage, name: 'pages:deploy') end @@ -337,13 +338,15 @@ RSpec.describe Projects::UpdatePagesService do expect(execute).to eq(:success) expect(older_deploy_job.reload).to be_retried + expect(deploy_status.ci_stage).to eq(stage) + expect(deploy_status.stage_idx).to eq(stage.position) end end private def deploy_status - GenericCommitStatus.find_by(name: 'pages:deploy') + GenericCommitStatus.where(name: 'pages:deploy').last end def execute |