diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-08-08 18:06:56 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-08-08 18:06:56 +0300 |
commit | bfb24e1685fb574d3144865da29a21b38cb52883 (patch) | |
tree | d694d329da73d9a312a6f819edaebebc3b081491 | |
parent | e44c3e4832e43c77e9c29fad6e49f8d6066d7f5c (diff) |
Add latest changes from gitlab-org/gitlab@master
68 files changed, 983 insertions, 485 deletions
diff --git a/.gitlab/ci/rails.gitlab-ci.yml b/.gitlab/ci/rails.gitlab-ci.yml index 5aa0fabfadf..e11b81d9ddd 100644 --- a/.gitlab/ci/rails.gitlab-ci.yml +++ b/.gitlab/ci/rails.gitlab-ci.yml @@ -482,6 +482,7 @@ rspec:coverage: FILES_TO_DOWNLOAD: > config/bundler_setup.rb Gemfile + Gemfile.checksum Gemfile.lock scripts/merge-simplecov spec/simplecov_env_core.rb diff --git a/.gitlab/merge_request_templates/Security Release.md b/.gitlab/merge_request_templates/Security Release.md index b443dd7570e..772b73d1066 100644 --- a/.gitlab/merge_request_templates/Security Release.md +++ b/.gitlab/merge_request_templates/Security Release.md @@ -13,6 +13,7 @@ See [the general developer security release guidelines](https://gitlab.com/gitla ## Developer checklist - [ ] **On "Related issues" section, write down the [GitLab Security] issue it belongs to (i.e. `Related to <issue_id>`).** +- [ ] Familiarize yourself with the latest process to create Security merge requests: https://gitlab.com/gitlab-org/release/docs/blob/master/general/security/developer.md#process - [ ] Merge request targets `master`, or a versioned stable branch (`X-Y-stable-ee`). - [ ] Title of this merge request is the same as for all backports. - [ ] A [CHANGELOG entry] has been included, with `Changelog` trailer set to `security`. @@ -24,7 +25,7 @@ See [the general developer security release guidelines](https://gitlab.com/gitla - Trigger the [`e2e:package-and-test` job]. The docker image generated will be used by the AppSec engineer to validate the security vulnerability has been remediated. - [ ] For a backport MR targeting a versioned stable branch (`X-Y-stable-ee`). - [ ] Milestone is set to the version this backport applies to. A closed milestone can be assigned via [quick actions]. - - [ ] Ensure it's approved by a maintainer. + - [ ] Ensure it's approved by the same maintainer that reviewed and approved the merge request targeting the default branch. - [ ] Ensure this merge request and the related security issue have a `~severity::x` label **Note:** Reviewer/maintainer should not be a [Release Manager]. diff --git a/app/assets/javascripts/issues/index.js b/app/assets/javascripts/issues/index.js index 4d2df9e3602..e266966c665 100644 --- a/app/assets/javascripts/issues/index.js +++ b/app/assets/javascripts/issues/index.js @@ -63,7 +63,6 @@ export function initShow({ notesParams } = {}) { initRelatedIssues(TYPE_INCIDENT); } else { initIssueApp(issuableData, store); - initHeaderActions(store); } new Issue(); // eslint-disable-line no-new diff --git a/app/assets/javascripts/issues/show/components/app.vue b/app/assets/javascripts/issues/show/components/app.vue index fcdf1f7741b..633f336b0c0 100644 --- a/app/assets/javascripts/issues/show/components/app.vue +++ b/app/assets/javascripts/issues/show/components/app.vue @@ -15,6 +15,7 @@ import { visitUrl } from '~/lib/utils/url_utility'; import { __, sprintf } from '~/locale'; import ConfidentialityBadge from '~/vue_shared/components/confidentiality_badge.vue'; import { containsSensitiveToken, confirmSensitiveAction, i18n } from '~/lib/utils/secret_detection'; +import { WORK_ITEM_TYPE_VALUE_ISSUE } from '~/work_items/constants'; import { ISSUE_TYPE_PATH, INCIDENT_TYPE_PATH, POLLING_DELAY } from '../constants'; import eventHub from '../event_hub'; import getIssueStateQuery from '../queries/get_issue_state.query.graphql'; @@ -23,6 +24,8 @@ import Store from '../stores'; import DescriptionComponent from './description.vue'; import EditedComponent from './edited.vue'; import FormComponent from './form.vue'; +import HeaderActions from './header_actions.vue'; +import IssueHeader from './issue_header.vue'; import PinnedLinks from './pinned_links.vue'; import TitleComponent from './title.vue'; @@ -32,6 +35,8 @@ export default { GlIcon, GlBadge, GlIntersectionObserver, + HeaderActions, + IssueHeader, TitleComponent, EditedComponent, FormComponent, @@ -42,6 +47,11 @@ export default { GlTooltip: GlTooltipDirective, }, props: { + author: { + type: Object, + required: false, + default: () => ({}), + }, endpoint: { required: true, type: String, @@ -54,6 +64,11 @@ export default { required: true, type: Boolean, }, + createdAt: { + type: String, + required: false, + default: '', + }, enableAutocomplete: { type: Boolean, required: false, @@ -193,6 +208,36 @@ export default { required: false, default: null, }, + duplicatedToIssueUrl: { + type: String, + required: false, + default: '', + }, + movedToIssueUrl: { + type: String, + required: false, + default: '', + }, + promotedToEpicUrl: { + type: String, + required: false, + default: '', + }, + isFirstContribution: { + type: Boolean, + required: false, + default: false, + }, + serviceDeskReplyTo: { + type: String, + required: false, + default: '', + }, + workItemType: { + type: String, + required: false, + default: '', + }, }, data() { const store = new Store({ @@ -222,6 +267,9 @@ export default { }, }, computed: { + isIssue() { + return this.workItemType === WORK_ITEM_TYPE_VALUE_ISSUE; + }, issuableTemplates() { return this.store.formState.issuableTemplates; }, @@ -509,7 +557,13 @@ export default { :can-update="canUpdate" :title-html="state.titleHtml" :title-text="state.titleText" - /> + > + <template #actions> + <slot name="actions"> + <header-actions v-if="isIssue" /> + </slot> + </template> + </title-component> <gl-intersection-observer v-if="shouldShowStickyHeader" @@ -567,6 +621,23 @@ export default { </transition> </gl-intersection-observer> + <slot name="header"> + <issue-header + v-if="isIssue" + :author="author" + :confidential="isConfidential" + :created-at="createdAt" + :duplicated-to-issue-url="duplicatedToIssueUrl" + :is-first-contribution="isFirstContribution" + :is-hidden="isHidden" + :is-locked="isLocked" + :issuable-state="issuableStatus" + :moved-to-issue-url="movedToIssueUrl" + :promoted-to-epic-url="promotedToEpicUrl" + :service-desk-reply-to="serviceDeskReplyTo" + /> + </slot> + <pinned-links :zoom-meeting-url="zoomMeetingUrl" :published-incident-url="publishedIncidentUrl" diff --git a/app/assets/javascripts/issues/show/components/header_actions.vue b/app/assets/javascripts/issues/show/components/header_actions.vue index 321b12a0050..d05311cb1db 100644 --- a/app/assets/javascripts/issues/show/components/header_actions.vue +++ b/app/assets/javascripts/issues/show/components/header_actions.vue @@ -146,7 +146,7 @@ export default { variables() { return { fullPath: this.fullPath, - iid: this.iid, + iid: String(this.iid), }; }, update(data) { @@ -289,7 +289,7 @@ export default { mutation: promoteToEpicMutation, variables: { input: { - iid: this.iid, + iid: String(this.iid), projectPath: this.projectPath, }, }, diff --git a/app/assets/javascripts/issues/show/components/issue_header.vue b/app/assets/javascripts/issues/show/components/issue_header.vue new file mode 100644 index 00000000000..771b438f0da --- /dev/null +++ b/app/assets/javascripts/issues/show/components/issue_header.vue @@ -0,0 +1,126 @@ +<script> +import { GlLink, GlSprintf } from '@gitlab/ui'; +import { STATUS_OPEN, STATUS_REOPENED, TYPE_ISSUE, WORKSPACE_PROJECT } from '~/issues/constants'; +import { __, s__ } from '~/locale'; +import IssuableHeader from '~/vue_shared/issuable/show/components/issuable_header.vue'; + +export default { + TYPE_ISSUE, + WORKSPACE_PROJECT, + components: { + GlLink, + GlSprintf, + IssuableHeader, + }, + props: { + author: { + type: Object, + required: true, + }, + confidential: { + type: Boolean, + required: true, + }, + createdAt: { + type: String, + required: true, + }, + duplicatedToIssueUrl: { + type: String, + required: true, + }, + isFirstContribution: { + type: Boolean, + required: true, + }, + isHidden: { + type: Boolean, + required: true, + }, + isLocked: { + type: Boolean, + required: true, + }, + issuableState: { + type: String, + required: true, + }, + movedToIssueUrl: { + type: String, + required: true, + }, + promotedToEpicUrl: { + type: String, + required: true, + }, + serviceDeskReplyTo: { + type: String, + required: false, + default: '', + }, + }, + computed: { + closedStatusLink() { + return this.duplicatedToIssueUrl || this.movedToIssueUrl || this.promotedToEpicUrl; + }, + closedStatusText() { + if (this.duplicatedToIssueUrl) { + return s__('IssuableStatus|duplicated'); + } + if (this.movedToIssueUrl) { + return s__('IssuableStatus|moved'); + } + if (this.promotedToEpicUrl) { + return s__('IssuableStatus|promoted'); + } + return ''; + }, + isOpen() { + return this.issuableState === STATUS_OPEN || this.issuableState === STATUS_REOPENED; + }, + statusIcon() { + return this.isOpen ? 'issues' : 'issue-closed'; + }, + statusText() { + if (this.isOpen) { + return __('Open'); + } + if (this.closedStatusLink) { + return s__('IssuableStatus|Closed (%{link})'); + } + return s__('IssuableStatus|Closed'); + }, + }, +}; +</script> + +<template> + <issuable-header + class="gl-p-0 gl-mb-6 gl-mt-2 gl-sm-mt-0" + :author="author" + :blocked="isLocked" + :confidential="confidential" + :created-at="createdAt" + :is-first-contribution="isFirstContribution" + :is-hidden="isHidden" + :issuable-state="issuableState" + :issuable-type="$options.TYPE_ISSUE" + :service-desk-reply-to="serviceDeskReplyTo" + show-work-item-type-icon + :status-icon="statusIcon" + :workspace-type="$options.WORKSPACE_PROJECT" + > + <template #status-badge> + <gl-sprintf v-if="closedStatusLink" :message="statusText"> + <template #link> + <gl-link + class="gl-reset-color! gl-reset-font-size gl-text-decoration-underline" + :href="closedStatusLink" + >{{ closedStatusText }}</gl-link + > + </template> + </gl-sprintf> + <template v-else>{{ statusText }}</template> + </template> + </issuable-header> +</template> diff --git a/app/assets/javascripts/issues/show/components/title.vue b/app/assets/javascripts/issues/show/components/title.vue index 197a2594f4d..8c9e43b729b 100644 --- a/app/assets/javascripts/issues/show/components/title.vue +++ b/app/assets/javascripts/issues/show/components/title.vue @@ -53,16 +53,19 @@ export default { </script> <template> - <div class="title-container"> + <div + class="gl-display-flex gl-align-items-flex-start gl-flex-direction-column gl-sm-flex-direction-row gl-pt-3" + > <h1 v-safe-html="titleHtml" :class="{ 'issue-realtime-pre-pulse': preAnimation, 'issue-realtime-trigger-pulse': pulseAnimation, }" - class="title gl-font-size-h-display" + class="title gl-font-size-h-display gl-m-0!" data-testid="issue-title" dir="auto" ></h1> + <slot name="actions"></slot> </div> </template> diff --git a/app/assets/javascripts/issues/show/index.js b/app/assets/javascripts/issues/show/index.js index bc4284457f6..41e6daea4c1 100644 --- a/app/assets/javascripts/issues/show/index.js +++ b/app/assets/javascripts/issues/show/index.js @@ -2,8 +2,8 @@ import Vue from 'vue'; import { mapGetters } from 'vuex'; import errorTrackingStore from '~/error_tracking/store'; import { apolloProvider } from '~/graphql_shared/issuable_client'; -import { TYPE_INCIDENT } from '~/issues/constants'; -import { parseBoolean } from '~/lib/utils/common_utils'; +import { TYPE_INCIDENT, TYPE_ISSUE } from '~/issues/constants'; +import { convertObjectPropsToCamelCase, parseBoolean } from '~/lib/utils/common_utils'; import { scrollToTargetOnResize } from '~/lib/utils/resize_observer'; import IssueApp from './components/app.vue'; import HeaderActions from './components/header_actions.vue'; @@ -97,12 +97,17 @@ export function initIssueApp(issueData, store) { } const { fullPath, registerPath, signInPath } = el.dataset; + const headerActionsData = convertObjectPropsToCamelCase(JSON.parse(el.dataset.headerActionsData)); scrollToTargetOnResize(); - bootstrapApollo({ ...issueState, issueType: el.dataset.issueType }); + bootstrapApollo({ ...issueState, issueType: TYPE_ISSUE }); const { + authorId, + authorName, + authorUsername, + authorWebUrl, canCreateIncident, hasIssueWeightsFeature, hasIterationsFeature, @@ -121,6 +126,26 @@ export function initIssueApp(issueData, store) { signInPath, hasIssueWeightsFeature, hasIterationsFeature, + // for HeaderActions component + canCreateIssue: parseBoolean(headerActionsData.canCreateIssue), + canDestroyIssue: parseBoolean(headerActionsData.canDestroyIssue), + canPromoteToEpic: parseBoolean(headerActionsData.canPromoteToEpic), + canReopenIssue: parseBoolean(headerActionsData.canReopenIssue), + canReportSpam: parseBoolean(headerActionsData.canReportSpam), + canUpdateIssue: parseBoolean(headerActionsData.canUpdateIssue), + iid: headerActionsData.iid, + issuableId: headerActionsData.issuableId, + isIssueAuthor: parseBoolean(headerActionsData.isIssueAuthor), + issuePath: headerActionsData.issuePath, + issueType: headerActionsData.issueType, + newIssuePath: headerActionsData.newIssuePath, + projectPath: headerActionsData.projectPath, + projectId: headerActionsData.projectId, + reportAbusePath: headerActionsData.reportAbusePath, + reportedUserId: headerActionsData.reportedUserId, + reportedFromUrl: headerActionsData.reportedFromUrl, + submitAsSpamPath: headerActionsData.submitAsSpamPath, + issuableEmailAddress: headerActionsData.issuableEmailAddress, }, computed: { ...mapGetters(['getNoteableData']), @@ -129,6 +154,12 @@ export function initIssueApp(issueData, store) { return createElement(IssueApp, { props: { ...issueProps, + author: { + id: authorId, + name: authorName, + username: authorUsername, + webUrl: authorWebUrl, + }, isConfidential: this.getNoteableData?.confidential, isLocked: this.getNoteableData?.discussion_locked, issuableStatus: this.getNoteableData?.state, diff --git a/app/assets/javascripts/vue_shared/issuable/show/components/issuable_header.vue b/app/assets/javascripts/vue_shared/issuable/show/components/issuable_header.vue index 27276e55b13..29aef89a991 100644 --- a/app/assets/javascripts/vue_shared/issuable/show/components/issuable_header.vue +++ b/app/assets/javascripts/vue_shared/issuable/show/components/issuable_header.vue @@ -1,16 +1,7 @@ <script> -import { - GlIcon, - GlBadge, - GlButton, - GlSprintf, - GlTooltipDirective, - GlAvatarLink, - GlAvatarLabeled, -} from '@gitlab/ui'; - +import { GlIcon, GlBadge, GlButton, GlLink, GlSprintf, GlTooltipDirective } from '@gitlab/ui'; import { getIdFromGraphQLId } from '~/graphql_shared/utils'; -import { issuableStatusText, STATUS_OPEN } from '~/issues/constants'; +import { issuableStatusText, STATUS_OPEN, STATUS_REOPENED } from '~/issues/constants'; import { isExternal } from '~/lib/utils/url_utility'; import { __, n__, sprintf } from '~/locale'; import ConfidentialityBadge from '~/vue_shared/components/confidentiality_badge.vue'; @@ -23,8 +14,7 @@ export default { GlIcon, GlBadge, GlButton, - GlAvatarLink, - GlAvatarLabeled, + GlLink, GlSprintf, TimeAgoTooltip, WorkItemTypeIcon, @@ -66,21 +56,36 @@ export default { required: false, default: false, }, - taskCompletionStatus: { - type: Object, + isFirstContribution: { + type: Boolean, required: false, - default: null, + default: false, + }, + isHidden: { + type: Boolean, + required: false, + default: false, }, issuableType: { type: String, required: false, default: '', }, + serviceDeskReplyTo: { + type: String, + required: false, + default: '', + }, showWorkItemTypeIcon: { type: Boolean, required: false, default: false, }, + taskCompletionStatus: { + type: Object, + required: false, + default: null, + }, workspaceType: { type: String, required: false, @@ -92,10 +97,30 @@ export default { return issuableStatusText[this.issuableState]; }, badgeVariant() { - return this.issuableState === STATUS_OPEN ? 'success' : 'info'; + return this.issuableState === STATUS_OPEN || this.issuableState === STATUS_REOPENED + ? 'success' + : 'info'; + }, + blockedTooltip() { + return sprintf(__('This %{issuable} is locked. Only project members can comment.'), { + issuable: this.issuableType, + }); + }, + hiddenTooltip() { + return sprintf(__('This %{issuable} is hidden because its author has been banned'), { + issuable: this.issuableType, + }); + }, + shouldShowWorkItemTypeIcon() { + return this.showWorkItemTypeIcon && this.issuableType; }, createdMessage() { - return this.showWorkItemTypeIcon + if (this.serviceDeskReplyTo) { + return this.shouldShowWorkItemTypeIcon + ? __('created %{timeAgo} by %{email} via %{author}') + : __('Created %{timeAgo} by %{email} via %{author}'); + } + return this.shouldShowWorkItemTypeIcon ? __('created %{timeAgo} by %{author}') : __('Created %{timeAgo} by %{author}'); }, @@ -103,7 +128,7 @@ export default { return getIdFromGraphQLId(`${this.author.id}`); }, isAuthorExternal() { - return isExternal(this.author.webUrl); + return isExternal(this.author.webUrl ?? ''); }, taskStatusString() { const { count, completedCount } = this.taskCompletionStatus; @@ -144,60 +169,80 @@ export default { <slot name="status-badge">{{ badgeText }}</slot> </span> </gl-badge> - <span v-if="blocked" class="issuable-warning-icon" data-testid="blocked"> - <gl-icon name="lock" :aria-label="__('Blocked')" /> - </span> <confidentiality-badge v-if="confidential" :issuable-type="issuableType" :workspace-type="workspaceType" /> - <work-item-type-icon v-if="showWorkItemTypeIcon" :work-item-type="issuableType" show-text /> + <span v-if="blocked" class="issuable-warning-icon"> + <gl-icon + v-gl-tooltip.bottom + name="lock" + :title="blockedTooltip" + :aria-label="__('Blocked')" + /> + </span> + <span v-if="isHidden" class="issuable-warning-icon"> + <gl-icon + v-gl-tooltip.bottom + name="spam" + :title="hiddenTooltip" + :aria-label="__('Hidden')" + /> + </span> + <work-item-type-icon + v-if="shouldShowWorkItemTypeIcon" + show-text + :work-item-type="issuableType.toUpperCase()" + /> <gl-sprintf :message="createdMessage"> <template #timeAgo> <time-ago-tooltip class="gl-mx-2" :time="createdAt" /> </template> + <template #email> + {{ serviceDeskReplyTo }} + </template> <template #author> - <gl-avatar-link - :data-user-id="authorId" - :data-username="author.username" - :data-name="author.name" + <gl-link + class="gl-font-weight-bold gl-mx-2 js-user-link" :href="author.webUrl" - class="js-user-link gl-vertical-align-middle gl-mx-2" + :data-user-id="authorId" > - <gl-avatar-labeled - :size="24" - :src="author.avatarUrl" - :label="author.name" - :class="[ - { 'gl-display-none': !isAuthorExternal }, - 'gl-sm-display-inline-flex gl-mx-1', - ]" - > - <template #meta> - <gl-icon v-if="isAuthorExternal" name="external-link" class="gl-ml-1" /> - </template> - </gl-avatar-labeled> + <span :class="[{ 'gl-display-none': !isAuthorExternal }, 'gl-sm-display-inline']"> + {{ author.name }} + </span> + <gl-icon + v-if="isAuthorExternal" + name="external-link" + :aria-label="__('external link')" + /> <strong v-if="author.username" class="author gl-display-inline gl-sm-display-none!" >@{{ author.username }}</strong > - </gl-avatar-link> + </gl-link> </template> </gl-sprintf> + <gl-icon + v-if="isFirstContribution" + v-gl-tooltip + class="gl-mr-2" + name="first-contribution" + :title="__('1st contribution!')" + :aria-label="__('1st contribution!')" + /> <span v-if="taskCompletionStatus && hasTasks" - data-testid="task-status" class="gl-display-none gl-md-display-block gl-lg-display-inline-block" >{{ taskStatusString }}</span > <gl-button icon="chevron-double-lg-left" - class="gl-ml-auto gl-display-block gl-sm-display-none!" + class="gl-ml-auto gl-display-block gl-sm-display-none! js-sidebar-toggle" :aria-label="__('Expand sidebar')" @click="handleRightSidebarToggleClick" /> </div> - <div data-testid="header-actions" class="detail-page-header-actions gl-display-flex"> + <div class="detail-page-header-actions gl-display-flex"> <slot name="header-actions"></slot> </div> </div> diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb index 910b9e31394..ef9cd98021d 100644 --- a/app/helpers/issuables_helper.rb +++ b/app/helpers/issuables_helper.rb @@ -251,9 +251,17 @@ module IssuablesHelper def issue_only_initial_data(issuable) return {} unless issuable.is_a?(Issue) - { + data = { + authorId: issuable.author.id, + authorName: issuable.author.name, + authorUsername: issuable.author.username, + authorWebUrl: url_for(user_path(issuable.author)), + createdAt: issuable.created_at.to_time.iso8601, hasClosingMergeRequest: issuable.merge_requests_count(current_user) != 0, + isFirstContribution: issuable.first_contribution?, issueType: issuable.issue_type, + serviceDeskReplyTo: issuable.present(current_user: current_user).service_desk_reply_to, + workItemType: issuable.work_item_type.name, zoomMeetingUrl: ZoomMeeting.canonical_meeting_url(issuable), sentryIssueIdentifier: SentryIssue.find_by(issue: issuable)&.sentry_issue_identifier, # rubocop:disable CodeReuse/ActiveRecord iid: issuable.iid.to_s, @@ -261,6 +269,16 @@ module IssuablesHelper canCreateIncident: create_issue_type_allowed?(issuable.project, :incident), **incident_only_initial_data(issuable) } + + data.tap do |d| + if issuable.duplicated? && can?(current_user, :read_issue, issuable.duplicated_to) + d[:duplicatedToIssueUrl] = url_for([issuable.duplicated_to.project, issuable.duplicated_to, { only_path: false }]) + end + + if issuable.moved? && can?(current_user, :read_issue, issuable.moved_to) + d[:movedToIssueUrl] = url_for([issuable.moved_to.project, issuable.moved_to, { only_path: false }]) + end + end end def incident_only_initial_data(issue) diff --git a/app/models/ci/sources/pipeline.rb b/app/models/ci/sources/pipeline.rb index 4853c57d41f..5b6946b04fd 100644 --- a/app/models/ci/sources/pipeline.rb +++ b/app/models/ci/sources/pipeline.rb @@ -6,6 +6,11 @@ module Ci include Ci::Partitionable include Ci::NamespacedModelName include SafelyChangeColumnDefault + include IgnorableColumns + + ignore_columns [ + :pipeline_id_convert_to_bigint, :source_pipeline_id_convert_to_bigint + ], remove_with: '16.6', remove_after: '2023-10-22' columns_changing_default :partition_id diff --git a/app/views/admin/applications/index.html.haml b/app/views/admin/applications/index.html.haml index 063045033b6..6846fe8f4aa 100644 --- a/app/views/admin/applications/index.html.haml +++ b/app/views/admin/applications/index.html.haml @@ -15,7 +15,7 @@ .gl-new-card-actions = render Pajamas::ButtonComponent.new(size: :small, href: new_admin_application_path, button_options: { data: { qa_selector: 'new_application_button' } }) do - = _('New application') + = _('Add new application') - c.with_body do - if @applications.empty? %section.empty-state.gl-my-5.gl-text-center.gl-display-flex.gl-flex-direction-column diff --git a/app/views/projects/issuable/_show.html.haml b/app/views/projects/issuable/_show.html.haml index ec233bc9aff..769847f3830 100644 --- a/app/views/projects/issuable/_show.html.haml +++ b/app/views/projects/issuable/_show.html.haml @@ -7,5 +7,6 @@ = render "projects/issues/service_desk/alert_moved_from_service_desk", issue: issuable -= render 'shared/issue_type/details_header', issuable: issuable +- if issuable.work_item_type&.incident? + = render 'shared/issue_type/details_header', issuable: issuable = render 'shared/issue_type/details_content', issuable: issuable, api_awards_path: api_awards_path diff --git a/app/views/shared/issue_type/_details_content.html.haml b/app/views/shared/issue_type/_details_content.html.haml index 40a02fddbf3..249e296b41a 100644 --- a/app/views/shared/issue_type/_details_content.html.haml +++ b/app/views/shared/issue_type/_details_content.html.haml @@ -4,6 +4,7 @@ .issue-details.issuable-details.js-issue-details .detail-page-description.content-block.js-detail-page-description.gl-pt-3.gl-pb-0.gl-border-none #js-issuable-app{ data: { initial: issuable_initial_data(issuable).to_json, + header_actions_data: issue_header_actions_data(@project, issuable, current_user, @issuable_sidebar).to_json, issuable_id: issuable.id, full_path: @project.full_path, register_path: new_user_registration_path(redirect_to_referer: 'yes'), diff --git a/data/deprecations/15-11-legacy-shell-escaping-quoting.yml b/data/deprecations/15-11-legacy-shell-escaping-quoting.yml index 340be971e2c..cac8b17ac9a 100644 --- a/data/deprecations/15-11-legacy-shell-escaping-quoting.yml +++ b/data/deprecations/15-11-legacy-shell-escaping-quoting.yml @@ -13,7 +13,7 @@ # If an End of Support period applies, the announcement should be shared with GitLab Support # in the `#spt_managers` channel in Slack, and mention `@gitlab-com/support` in this MR. # - end_of_support_milestone: 17.9 # (optional) Use "XX.YY" format. The milestone when support for this feature will end. + end_of_support_milestone: # (optional) Use "XX.YY" format. The milestone when support for this feature will end. # # OTHER OPTIONAL FIELDS # diff --git a/data/deprecations/15-6-deprecate-config-fields-runner-helm-chart.yml b/data/deprecations/15-6-deprecate-config-fields-runner-helm-chart.yml index b93e2513c68..1bb7947739f 100644 --- a/data/deprecations/15-6-deprecate-config-fields-runner-helm-chart.yml +++ b/data/deprecations/15-6-deprecate-config-fields-runner-helm-chart.yml @@ -7,4 +7,4 @@ issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/379064 # (required) Link to the deprecation issue in GitLab body: | # (required) Do not modify this line, instead modify the lines below. From GitLab 13.6, users can [specify any runner configuration in the GitLab Runner Helm chart](https://docs.gitlab.com/runner/install/kubernetes.html). When we implemented this feature, we deprecated values in the GitLab Helm Chart configuration that were specific to GitLab Runner. These fields are deprecated and we plan to remove them in v1.0 of the GitLab Runner Helm chart. - end_of_support_milestone: "16.0" # (optional) Use "XX.YY" format. The milestone when support for this feature will end. + end_of_support_milestone: # (optional) Use "XX.YY" format. The milestone when support for this feature will end. diff --git a/data/deprecations/15-6-deprecate-post-api-v4-runner.yml b/data/deprecations/15-6-deprecate-post-api-v4-runner.yml index 786132b19b5..81c56d914d2 100644 --- a/data/deprecations/15-6-deprecate-post-api-v4-runner.yml +++ b/data/deprecations/15-6-deprecate-post-api-v4-runner.yml @@ -23,6 +23,6 @@ - `--maintenance-note` This change is a breaking change. You should [create a runner in the UI](../ci/runners/register_runner.md) to add configurations, and use the authentication token in the `gitlab-runner register` command instead. - end_of_support_milestone: "17.0" # (optional) Use "XX.YY" format. The milestone when support for this feature will end. + end_of_support_milestone: # (optional) Use "XX.YY" format. The milestone when support for this feature will end. tiers: # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate] documentation_url: https://docs.gitlab.com/ee/api/runners.html#register-a-new-runner # (optional) This is a link to the current documentation page diff --git a/data/deprecations/15-6-deprecate-runner-reg-token-helm.yml b/data/deprecations/15-6-deprecate-runner-reg-token-helm.yml index a01c995ae4f..0882d8ac894 100644 --- a/data/deprecations/15-6-deprecate-runner-reg-token-helm.yml +++ b/data/deprecations/15-6-deprecate-runner-reg-token-helm.yml @@ -14,4 +14,4 @@ The work is planned in [this epic](https://gitlab.com/groups/gitlab-org/-/epics/7633). From GitLab 17.0 and later, the methods to register runners introduced by the new GitLab Runner token architecture will be the only supported methods. - end_of_support_milestone: "17.0" # (optional) Use "XX.YY" format. The milestone when support for this feature will end. + end_of_support_milestone: # (optional) Use "XX.YY" format. The milestone when support for this feature will end. diff --git a/data/deprecations/15-6-deprecate-runner-register-command.yml b/data/deprecations/15-6-deprecate-runner-register-command.yml index 81e0f46078f..6443580d8f8 100644 --- a/data/deprecations/15-6-deprecate-runner-register-command.yml +++ b/data/deprecations/15-6-deprecate-runner-register-command.yml @@ -21,4 +21,4 @@ - `--maintenance-note` This change is a breaking change. You should [create a runner in the UI](../ci/runners/register_runner.md) to add configurations, and use the authentication token in the `gitlab-runner register` command instead. - end_of_support_milestone: "17.0" # (optional) Use "XX.YY" format. The milestone when support for this feature will end. + end_of_support_milestone: # (optional) Use "XX.YY" format. The milestone when support for this feature will end. diff --git a/data/deprecations/15-6-deprecate-runner-register-token-k8s-operator.yml b/data/deprecations/15-6-deprecate-runner-register-token-k8s-operator.yml index 61b117feb35..c4910f72887 100644 --- a/data/deprecations/15-6-deprecate-runner-register-token-k8s-operator.yml +++ b/data/deprecations/15-6-deprecate-runner-register-token-k8s-operator.yml @@ -17,7 +17,7 @@ - `--tag-list` This change is a breaking change. You should use an [authentication token](../ci/runners/register_runner.md) in the `gitlab-runner register` command instead. - end_of_support_milestone: "17.0" # (optional) Use "XX.YY" format. The milestone when support for this feature will end. + end_of_support_milestone: # (optional) Use "XX.YY" format. The milestone when support for this feature will end. tiers: # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate] documentation_url: https://docs.gitlab.com/runner/install/operator.html#install-the-kubernetes-operator # (optional) This is a link to the current documentation page image_url: # (optional) This is a link to a thumbnail image depicting the feature diff --git a/data/deprecations/15-7-deprecate-api-v4-runner-registration-token-reset-endpoints.yml b/data/deprecations/15-7-deprecate-api-v4-runner-registration-token-reset-endpoints.yml index 3a9690e0836..d617b5de531 100644 --- a/data/deprecations/15-7-deprecate-api-v4-runner-registration-token-reset-endpoints.yml +++ b/data/deprecations/15-7-deprecate-api-v4-runner-registration-token-reset-endpoints.yml @@ -21,6 +21,6 @@ This new architecture introduces a new method for registering runners and will eliminate the legacy [runner registration token](https://docs.gitlab.com/ee/security/token_overview.html#runner-registration-tokens). From GitLab 17.0 and later, the runner registration methods implemented by the new GitLab Runner token architecture will be the only supported methods. - end_of_support_milestone: "17.0" # (optional) Use "XX.YY" format. The milestone when support for this feature will end. + end_of_support_milestone: # (optional) Use "XX.YY" format. The milestone when support for this feature will end. tiers: # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate] documentation_url: https://docs.gitlab.com/ee/api/runners.html#register-a-new-runner # (optional) This is a link to the current documentation page diff --git a/data/deprecations/15-7-deprecate-gitlab-runner-exec-cmd.yml b/data/deprecations/15-7-deprecate-gitlab-runner-exec-cmd.yml index 0b52fa4d72d..a8b425d2dc4 100644 --- a/data/deprecations/15-7-deprecate-gitlab-runner-exec-cmd.yml +++ b/data/deprecations/15-7-deprecate-gitlab-runner-exec-cmd.yml @@ -7,7 +7,7 @@ issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/385235 # (required) Link to the deprecation issue in GitLab body: | # (required) Do not modify this line, instead modify the lines below. The [`gitlab-runner exec`](https://docs.gitlab.com/runner/commands/#gitlab-runner-exec) command is deprecated and will be fully removed from GitLab Runner in 16.0. The `gitlab-runner exec` feature was initially developed to provide the ability to validate a GitLab CI pipeline on a local system without needing to commit the updates to a GitLab instance. However, with the continued evolution of GitLab CI, replicating all GitLab CI features into `gitlab-runner exec` was no longer viable. Pipeline syntax and validation [simulation](https://docs.gitlab.com/ee/ci/pipeline_editor/#simulate-a-cicd-pipeline) are available in the GitLab pipeline editor. - end_of_support_milestone: "17.0" # (optional) Use "XX.YY" format. The milestone when support for this feature will end. + end_of_support_milestone: # (optional) Use "XX.YY" format. The milestone when support for this feature will end. # OTHER OPTIONAL FIELDS diff --git a/data/deprecations/15-7-deprecate-kas-metrics-port-in-gitlab-chart.yml b/data/deprecations/15-7-deprecate-kas-metrics-port-in-gitlab-chart.yml index 921bea87b38..64ec200f207 100644 --- a/data/deprecations/15-7-deprecate-kas-metrics-port-in-gitlab-chart.yml +++ b/data/deprecations/15-7-deprecate-kas-metrics-port-in-gitlab-chart.yml @@ -8,7 +8,7 @@ body: | # (required) Do not modify this line, instead modify the lines below. The `gitlab.kas.metrics.port` has been deprecated in favor of the new `gitlab.kas.observability.port` configuration field for the [GitLab Helm Chart](https://gitlab.com/gitlab-org/charts/gitlab/-/merge_requests/2839). This port is used for much more than just metrics, which warranted this change to avoid confusion in configuration. - end_of_support_milestone: "16.0" # (optional) Use "XX.YY" format. The milestone when support for this feature will end. + end_of_support_milestone: # (optional) Use "XX.YY" format. The milestone when support for this feature will end. # OTHER OPTIONAL FIELDS diff --git a/data/deprecations/15-7-deprecate-shimo-integration.yml b/data/deprecations/15-7-deprecate-shimo-integration.yml index aa92bdf943a..f9437a9f44e 100644 --- a/data/deprecations/15-7-deprecate-shimo-integration.yml +++ b/data/deprecations/15-7-deprecate-shimo-integration.yml @@ -14,7 +14,7 @@ # If an End of Support period applies, the announcement should be shared with GitLab Support # in the `#spt_managers` channel in Slack, and mention `@gitlab-com/support` in this MR. # - end_of_support_milestone: "16.0" # (optional) Use "XX.YY" format. The milestone when support for this feature will end. + end_of_support_milestone: # (optional) Use "XX.YY" format. The milestone when support for this feature will end. # # OTHER OPTIONAL FIELDS # diff --git a/data/deprecations/15-7-deprecate-zentao-integration.yml b/data/deprecations/15-7-deprecate-zentao-integration.yml index 332aaefdf04..72da0544ac3 100644 --- a/data/deprecations/15-7-deprecate-zentao-integration.yml +++ b/data/deprecations/15-7-deprecate-zentao-integration.yml @@ -14,7 +14,7 @@ # If an End of Support period applies, the announcement should be shared with GitLab Support # in the `#spt_managers` channel in Slack, and mention `@gitlab-com/support` in this MR. # - end_of_support_milestone: "16.0" # (optional) Use "XX.YY" format. The milestone when support for this feature will end. + end_of_support_milestone: # (optional) Use "XX.YY" format. The milestone when support for this feature will end. # # OTHER OPTIONAL FIELDS # diff --git a/data/deprecations/15-7-enable-period-in-terraform-state-name.yml b/data/deprecations/15-7-enable-period-in-terraform-state-name.yml index f56505ed2bc..19c32b1c9ef 100644 --- a/data/deprecations/15-7-enable-period-in-terraform-state-name.yml +++ b/data/deprecations/15-7-enable-period-in-terraform-state-name.yml @@ -17,6 +17,6 @@ 1. Run your Terraform commands. To use the full state name, including the period, [migrate to the full state file](https://docs.gitlab.com/ee/user/infrastructure/iac/terraform_state.html#migrate-to-a-gitlab-managed-terraform-state). - end_of_support_milestone: 16.0 + end_of_support_milestone: tiers: [Free, Silver, Gold, Core, Premium, Ultimate] documentation_url: 'https://docs.gitlab.com/ee/user/infrastructure/iac/troubleshooting.html#troubleshooting-terraform-state' diff --git a/data/deprecations/15-8-deprecate-backups-support-for-openstack-rackspace-apis.yml b/data/deprecations/15-8-deprecate-backups-support-for-openstack-rackspace-apis.yml index 0097f59d1f9..77982b66bc5 100644 --- a/data/deprecations/15-8-deprecate-backups-support-for-openstack-rackspace-apis.yml +++ b/data/deprecations/15-8-deprecate-backups-support-for-openstack-rackspace-apis.yml @@ -1,13 +1,13 @@ - title: "Automatic backup upload using Openstack Swift and Rackspace APIs" # (required) The name of the feature to be deprecated announcement_milestone: "15.8" # (required) The milestone when this feature was first announced as deprecated. removal_milestone: "15.10" # (required) The milestone when this feature is planned to be removed - end_of_support_milestone: "15.10" # (optional) Use "XX.YY" format. The milestone when support for this feature will end. + end_of_support_milestone: # (optional) Use "XX.YY" format. The milestone when support for this feature will end. breaking_change: true # (required) If this deprecation is a breaking change, set this value to true reporter: sranasinghe # (required) GitLab username of the person reporting the deprecation stage: Enablement # (required) String value of the stage that the feature was created in. e.g., Growth issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/387976 # (required) Link to the deprecation issue in GitLab body: | # (required) Do not modify this line, instead modify the lines below. - We are deprecating support for [uploading backups to remote storage](https://docs.gitlab.com/ee/raketasks/backup_gitlab.html#upload-backups-to-a-remote-cloud-storage) using Openstack Swift and Rackspace APIs. The support for these APIs depends on third-party libraries that are no longer actively maintained and have not been updated for Ruby 3. GitLab is switching over to Ruby 3 prior to EOL of Ruby 2 in order to stay up to date on security patches. + We are deprecating support for [uploading backups to remote storage](https://docs.gitlab.com/ee/raketasks/backup_gitlab.html#upload-backups-to-a-remote-cloud-storage) using Openstack Swift and Rackspace APIs. The support for these APIs depends on third-party libraries that are no longer actively maintained and have not been updated for Ruby 3. GitLab is switching over to Ruby 3 prior to EOL of Ruby 2 in order to stay up to date on security patches. - If you're using OpenStack, you need to change you configuration to use the S3 API instead of Swift. - If you're using Rackspace storage, you need to switch to a different provider or manually upload the backup file after the backup task is complete. diff --git a/data/deprecations/15-8-deprecate-slack-notifications-integration.yml b/data/deprecations/15-8-deprecate-slack-notifications-integration.yml index 8ac12d64a77..33f9e6ce3a7 100644 --- a/data/deprecations/15-8-deprecate-slack-notifications-integration.yml +++ b/data/deprecations/15-8-deprecate-slack-notifications-integration.yml @@ -19,7 +19,7 @@ # If an End of Support period applies, the announcement should be shared with GitLab Support # in the `#spt_managers` channel in Slack, and mention `@gitlab-com/support` in this MR. # - end_of_support_milestone: "17.0" # (optional) Use "XX.YY" format. The milestone when support for this feature will end. + end_of_support_milestone: # (optional) Use "XX.YY" format. The milestone when support for this feature will end. # # OTHER OPTIONAL FIELDS # diff --git a/data/deprecations/15-8-third-party-registries.yml b/data/deprecations/15-8-third-party-registries.yml index 83380581505..c4eddf2e64f 100644 --- a/data/deprecations/15-8-third-party-registries.yml +++ b/data/deprecations/15-8-third-party-registries.yml @@ -21,4 +21,4 @@ # If an End of Support period applies, the announcement should be shared with GitLab Support # in the `#spt_managers` channel in Slack, and mention `@gitlab-com/support` in this MR. # - end_of_support_milestone: 16.0 # (optional) Use "XX.YY" format. The milestone when support for this feature will end. + end_of_support_milestone: # (optional) Use "XX.YY" format. The milestone when support for this feature will end. diff --git a/data/deprecations/15-9-deprecate-ci-pre-clone-script.yml b/data/deprecations/15-9-deprecate-ci-pre-clone-script.yml index bb1dcc9412a..0d0aa6f0f04 100644 --- a/data/deprecations/15-9-deprecate-ci-pre-clone-script.yml +++ b/data/deprecations/15-9-deprecate-ci-pre-clone-script.yml @@ -13,7 +13,7 @@ # If an End of Support period applies, the announcement should be shared with GitLab Support # in the `#spt_managers` channel in Slack, and mention `@gitlab-com/support` in this MR. # - end_of_support_milestone: 16.0 # (optional) Use "XX.YY" format. The milestone when support for this feature will end. + end_of_support_milestone: # (optional) Use "XX.YY" format. The milestone when support for this feature will end. # # OTHER OPTIONAL FIELDS # diff --git a/data/deprecations/16-0-deprecate-omnibus-grafana.yml b/data/deprecations/16-0-deprecate-omnibus-grafana.yml index 93fd5c54071..61c04e9f042 100644 --- a/data/deprecations/16-0-deprecate-omnibus-grafana.yml +++ b/data/deprecations/16-0-deprecate-omnibus-grafana.yml @@ -18,6 +18,6 @@ In GitLab versions 16.0 to 16.2, you can still [re-enable the bundled Grafana](https://docs.gitlab.com/ee/administration/monitoring/performance/grafana_configuration.html#temporary-workaround). However, enabling the bundled Grafana will no longer work from GitLab 16.3. - end_of_support_milestone: 16.3 + end_of_support_milestone: tiers: # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate] documentation_url: https://docs.gitlab.com/omnibus/settings/grafana.html diff --git a/data/deprecations/16-0-deprecate-postgresql-13.yml b/data/deprecations/16-0-deprecate-postgresql-13.yml index 779a3074f39..828f9e64ab8 100644 --- a/data/deprecations/16-0-deprecate-postgresql-13.yml +++ b/data/deprecations/16-0-deprecate-postgresql-13.yml @@ -19,7 +19,7 @@ # If an End of Support period applies, the announcement should be shared with GitLab Support # in the `#spt_managers` channel in Slack, and mention `@gitlab-com/support` in this MR. # - end_of_support_milestone: 17.0 + end_of_support_milestone: # # OTHER OPTIONAL FIELDS # diff --git a/data/deprecations/16-1-windows-cmd-runner-shell-executor.yml b/data/deprecations/16-1-windows-cmd-runner-shell-executor.yml index 6e892a02a9b..c4c2435ba6f 100644 --- a/data/deprecations/16-1-windows-cmd-runner-shell-executor.yml +++ b/data/deprecations/16-1-windows-cmd-runner-shell-executor.yml @@ -9,5 +9,5 @@ issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/414864 # (required) Link to the deprecation issue in GitLab body: | # (required) Do not modify this line, instead modify the lines below. In GitLab 11.11 the Windows Batch executor, the CMD shell was deprecated in GitLab Runner in favor of PowerShell. Since then, the CMD shell has continued to be supported in GitLab Runner. However this has resulted in additional complexity for both the engineering team and customers using the Runner on Windows. We plan to fully remove support for Windows CMD from GitLab Runner in 17.0. Customers should plan to use PowerShell when using the runner on Windows with the shell executor. Customers can provide feedback or ask questions in the removal issue, [issue 29479](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/29479). - end_of_support_milestone: "17.0" # (optional) Use "XX.YY" format. The milestone when support for this feature will end. + end_of_support_milestone: # (optional) Use "XX.YY" format. The milestone when support for this feature will end. end_of_support_date: "2024-05-22" # (optional) The date of the milestone release when support for this feature will end. diff --git a/data/deprecations/ 16-2-cirunner_fields.yml b/data/deprecations/16-2-cirunner_fields.yml index 1d43ed1b7c9..c39ef9e3205 100644 --- a/data/deprecations/ 16-2-cirunner_fields.yml +++ b/data/deprecations/16-2-cirunner_fields.yml @@ -9,5 +9,5 @@ issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/41518 # (required) Link to the deprecation issue in GitLab body: | # (required) Do not modify this line, instead modify the lines below. These fields (`architectureName`, `ipAddress`, `platformName`, `revision`, `version`) are now deprecated from the [GraphQL `CiRunner`](https://docs.gitlab.com/ee/api/graphql/reference/#cirunner) type as they are duplicated with the introduction of runner managers grouped within a runner configuration. - end_of_support_milestone: "17.0" # (optional) Use "XX.YY" format. The milestone when support for this feature will end. - end_of_support_date: "2024-05-22" # (optional) The date of the milestone release when support for this feature will end. + end_of_support_milestone: # (optional) Use "XX.YY" format. The milestone when support for this feature will end. + end_of_support_date: # (optional) The date of the milestone release when support for this feature will end. diff --git a/data/deprecations/templates/_deprecation_template.md.erb b/data/deprecations/templates/_deprecation_template.md.erb index d2d95539ef9..2e0d2696f0f 100644 --- a/data/deprecations/templates/_deprecation_template.md.erb +++ b/data/deprecations/templates/_deprecation_template.md.erb @@ -58,7 +58,7 @@ For deprecation reviewers (Technical Writers only): <div class="deprecation-notes"> - Announced in GitLab <span class="milestone"><%= deprecation["announcement_milestone"]%></span> <% if deprecation["end_of_support_milestone"] -%> -- End of Support GitLab <span class="milestone"><%= deprecation["end_of_support_milestone"]%></span> +- End of Support in GitLab <span class="milestone"><%= deprecation["end_of_support_milestone"]%></span> <% end -%> <% if deprecation["removal_milestone"] -%> - Removal in GitLab <span class="milestone"><%= deprecation["removal_milestone"]%></span><% if deprecation["breaking_change"] -%> ([breaking change](https://docs.gitlab.com/ee/update/terminology.html#breaking-change))<% end %> diff --git a/db/migrate/20230723170936_initialize_conversion_of_ci_sources_pipelines_source.rb b/db/migrate/20230723170936_initialize_conversion_of_ci_sources_pipelines_source.rb new file mode 100644 index 00000000000..5ae867b755d --- /dev/null +++ b/db/migrate/20230723170936_initialize_conversion_of_ci_sources_pipelines_source.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +class InitializeConversionOfCiSourcesPipelinesSource < Gitlab::Database::Migration[2.1] + disable_ddl_transaction! + + TABLE = :ci_sources_pipelines + COLUMNS = %i[pipeline_id source_pipeline_id] + + def up + initialize_conversion_of_integer_to_bigint(TABLE, COLUMNS) + end + + def down + revert_initialize_conversion_of_integer_to_bigint(TABLE, COLUMNS) + end +end diff --git a/db/post_migrate/20230723171006_backfill_ci_sources_pipelines_source_conversion.rb b/db/post_migrate/20230723171006_backfill_ci_sources_pipelines_source_conversion.rb new file mode 100644 index 00000000000..7b5cfa9f49a --- /dev/null +++ b/db/post_migrate/20230723171006_backfill_ci_sources_pipelines_source_conversion.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +class BackfillCiSourcesPipelinesSourceConversion < Gitlab::Database::Migration[2.1] + restrict_gitlab_migration gitlab_schema: :gitlab_ci + + TABLE = :ci_sources_pipelines + COLUMNS = %i[pipeline_id source_pipeline_id] + SUB_BATCH_SIZE = 250 + + def up + backfill_conversion_of_integer_to_bigint(TABLE, COLUMNS, sub_batch_size: SUB_BATCH_SIZE) + end + + def down + revert_backfill_conversion_of_integer_to_bigint(TABLE, COLUMNS) + end +end diff --git a/db/schema_migrations/20230723170936 b/db/schema_migrations/20230723170936 new file mode 100644 index 00000000000..6d84aba9d7f --- /dev/null +++ b/db/schema_migrations/20230723170936 @@ -0,0 +1 @@ +ddba4eb1ed3e9d0e75d693ef79c05acdedce04489650bcc9ba28a5e53e227132
\ No newline at end of file diff --git a/db/schema_migrations/20230723171006 b/db/schema_migrations/20230723171006 new file mode 100644 index 00000000000..6f2c8f47c0e --- /dev/null +++ b/db/schema_migrations/20230723171006 @@ -0,0 +1 @@ +a6bca8200327cd36475931f140ed84629eb0962215eb8adeb3eccf5c60b4b71a
\ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 531e02b43fb..33621bff28f 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -298,6 +298,16 @@ BEGIN END; $$; +CREATE FUNCTION trigger_68d7b6653c7d() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + NEW."pipeline_id_convert_to_bigint" := NEW."pipeline_id"; + NEW."source_pipeline_id_convert_to_bigint" := NEW."source_pipeline_id"; + RETURN NEW; +END; +$$; + CREATE FUNCTION trigger_7f3d66a7d7f5() RETURNS trigger LANGUAGE plpgsql AS $$ @@ -14165,7 +14175,9 @@ CREATE TABLE ci_sources_pipelines ( source_pipeline_id integer, source_job_id bigint, partition_id bigint NOT NULL, - source_partition_id bigint NOT NULL + source_partition_id bigint NOT NULL, + pipeline_id_convert_to_bigint bigint, + source_pipeline_id_convert_to_bigint bigint ); CREATE SEQUENCE ci_sources_pipelines_id_seq @@ -35781,6 +35793,8 @@ CREATE TRIGGER trigger_1bd97da9c1a4 BEFORE INSERT OR UPDATE ON ci_pipelines FOR CREATE TRIGGER trigger_239c8032a8d6 BEFORE INSERT OR UPDATE ON ci_pipeline_chat_data FOR EACH ROW EXECUTE FUNCTION trigger_239c8032a8d6(); +CREATE TRIGGER trigger_68d7b6653c7d BEFORE INSERT OR UPDATE ON ci_sources_pipelines FOR EACH ROW EXECUTE FUNCTION trigger_68d7b6653c7d(); + CREATE TRIGGER trigger_7f3d66a7d7f5 BEFORE INSERT OR UPDATE ON ci_pipeline_variables FOR EACH ROW EXECUTE FUNCTION trigger_7f3d66a7d7f5(); CREATE TRIGGER trigger_b2d852e1e2cb BEFORE INSERT OR UPDATE ON ci_pipelines FOR EACH ROW EXECUTE FUNCTION trigger_b2d852e1e2cb(); diff --git a/doc/administration/broadcast_messages.md b/doc/administration/broadcast_messages.md index 556edbd3e5e..78a4ec798d1 100644 --- a/doc/administration/broadcast_messages.md +++ b/doc/administration/broadcast_messages.md @@ -60,6 +60,7 @@ To add a broadcast message: 1. On the left sidebar, expand the top-most chevron (**{chevron-down}**). 1. Select **Admin Area**. 1. Select **Messages**. +1. Select **Add new message**. 1. Add the text for the message to the **Message** field. You can style a message's content using Markdown, emoji, and the `a` and `br` HTML tags. The `br` tag inserts a line break. The `a` HTML tag accepts `class` and `style` attributes with the following CSS properties: - `color` diff --git a/doc/administration/reference_architectures/10k_users.md b/doc/administration/reference_architectures/10k_users.md index 2f3174d7fbb..c94c76c6532 100644 --- a/doc/administration/reference_architectures/10k_users.md +++ b/doc/administration/reference_architectures/10k_users.md @@ -1768,18 +1768,7 @@ Updates to example must be made at: --> ```ruby - # Avoid running unnecessary services on the Sidekiq server - gitaly['enable'] = false - postgresql['enable'] = false - redis['enable'] = false - nginx['enable'] = false - puma['enable'] = false - gitlab_workhorse['enable'] = false - prometheus['enable'] = false - alertmanager['enable'] = false - grafana['enable'] = false - gitlab_exporter['enable'] = false - gitlab_kas['enable'] = false + roles ["sidekiq_role"] # External URL ## This should match the URL of the external load balancer diff --git a/doc/administration/reference_architectures/25k_users.md b/doc/administration/reference_architectures/25k_users.md index 731568c487f..2b91d493145 100644 --- a/doc/administration/reference_architectures/25k_users.md +++ b/doc/administration/reference_architectures/25k_users.md @@ -1785,18 +1785,7 @@ Updates to example must be made at: --> ```ruby - # Avoid running unnecessary services on the Sidekiq server - gitaly['enable'] = false - postgresql['enable'] = false - redis['enable'] = false - nginx['enable'] = false - puma['enable'] = false - gitlab_workhorse['enable'] = false - prometheus['enable'] = false - alertmanager['enable'] = false - grafana['enable'] = false - gitlab_exporter['enable'] = false - gitlab_kas['enable'] = false + roles ["sidekiq_role"] # External URL ## This should match the URL of the external load balancer diff --git a/doc/administration/reference_architectures/3k_users.md b/doc/administration/reference_architectures/3k_users.md index cd95090788d..cf2d9667481 100644 --- a/doc/administration/reference_architectures/3k_users.md +++ b/doc/administration/reference_architectures/3k_users.md @@ -1718,18 +1718,7 @@ Updates to example must be made at: --> ```ruby - # Avoid running unnecessary services on the Sidekiq server - gitaly['enable'] = false - postgresql['enable'] = false - redis['enable'] = false - nginx['enable'] = false - puma['enable'] = false - gitlab_workhorse['enable'] = false - prometheus['enable'] = false - alertmanager['enable'] = false - grafana['enable'] = false - gitlab_exporter['enable'] = false - gitlab_kas['enable'] = false + roles ["sidekiq_role"] # External URL ## This should match the URL of the external load balancer diff --git a/doc/administration/reference_architectures/50k_users.md b/doc/administration/reference_architectures/50k_users.md index b2f01266b14..2187b0ff02c 100644 --- a/doc/administration/reference_architectures/50k_users.md +++ b/doc/administration/reference_architectures/50k_users.md @@ -1781,18 +1781,7 @@ Updates to example must be made at: --> ```ruby - # Avoid running unnecessary services on the Sidekiq server - gitaly['enable'] = false - postgresql['enable'] = false - redis['enable'] = false - nginx['enable'] = false - puma['enable'] = false - gitlab_workhorse['enable'] = false - prometheus['enable'] = false - alertmanager['enable'] = false - grafana['enable'] = false - gitlab_exporter['enable'] = false - gitlab_kas['enable'] = false + roles ["sidekiq_role"] # External URL ## This should match the URL of the external load balancer diff --git a/doc/administration/reference_architectures/5k_users.md b/doc/administration/reference_architectures/5k_users.md index 70f9b8a1724..2b85426de58 100644 --- a/doc/administration/reference_architectures/5k_users.md +++ b/doc/administration/reference_architectures/5k_users.md @@ -1706,18 +1706,7 @@ Updates to example must be made at: --> ```ruby - # Avoid running unnecessary services on the Sidekiq server - gitaly['enable'] = false - postgresql['enable'] = false - redis['enable'] = false - nginx['enable'] = false - puma['enable'] = false - gitlab_workhorse['enable'] = false - prometheus['enable'] = false - alertmanager['enable'] = false - grafana['enable'] = false - gitlab_exporter['enable'] = false - gitlab_kas['enable'] = false + roles ["sidekiq_role"] # External URL ## This should match the URL of the external load balancer diff --git a/doc/administration/sidekiq/index.md b/doc/administration/sidekiq/index.md index f0b4d2ebf1d..10fadc40a82 100644 --- a/doc/administration/sidekiq/index.md +++ b/doc/administration/sidekiq/index.md @@ -227,7 +227,6 @@ node than Sidekiq, follow the steps below. 1. Edit `/etc/gitlab/gitlab.rb`, and configure the registry URL: ```ruby - registry_external_url 'https://registry.example.com' gitlab_rails['registry_api_url'] = "https://registry.example.com" ``` diff --git a/doc/administration/system_hooks.md b/doc/administration/system_hooks.md index dcacacaf47b..1c84d4fadb8 100644 --- a/doc/administration/system_hooks.md +++ b/doc/administration/system_hooks.md @@ -57,6 +57,7 @@ To create a system hook: 1. On the left sidebar, expand the top-most chevron (**{chevron-down}**). 1. Select **Admin Area**. 1. On the left sidebar, select **System Hooks**. +1. Select **Add new webhook**. 1. Provide the **URL** and **Secret Token**. 1. Select the checkbox next to each optional **Trigger** you want to enable. 1. Select **Enable SSL verification**, if desired. diff --git a/doc/architecture/blueprints/ci_builds_runner_fleet_metrics/index.md b/doc/architecture/blueprints/ci_builds_runner_fleet_metrics/index.md index dd13ac0fd74..29b2bd0fd28 100644 --- a/doc/architecture/blueprints/ci_builds_runner_fleet_metrics/index.md +++ b/doc/architecture/blueprints/ci_builds_runner_fleet_metrics/index.md @@ -61,7 +61,72 @@ The following customer problems should be solved when addressing this question. #### Which runners have failures in the past hour? -## Implementation plan +## Implementation -We're currently in the research stage, and working on the [Proof of Concept](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/126863) -around Clickhouse. You can follow [this epic](https://gitlab.com/groups/gitlab-org/-/epics/10682) for more up-to-date status. +The current implementation plan is based on a +[Proof of Concept](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/126863). +For an up to date status, see [epic 10682](https://gitlab.com/groups/gitlab-org/-/epics/10682). + +### Database selection + +In FY23, ClickHouse [was selected as GitLab standard datastore](https://about.gitlab.com/company/team/structure/working-groups/clickhouse-datastore/#context) +for features with big data and insert-heavy requirements. +So we have chosen it for our CI analytics as well. + +### Scope of data + +We're starting with the denormalized version of the `ci_builds` table in the main database, +which will include fields from some other tables. For example, `ci_runners` and `ci_runner_machines`. + +[Immutability is a key constraint in ClickHouse](../../../development/database/clickhouse/index.md#how-it-differs-from-postgresql), +so we only use `finished` builds. + +### Developing behind feature flags + +It's hard to fully test data ingestion and query performance in development/staging environments. +That's why we plan to deliver those features to production behing feature flags and test the performance on real data. +Feature flags for data ingestion and API's will be separate. + +### Data ingestion + +A background worker will push `ci_builds` sorted by `(finished_at, id)` from Posgres to ClickHouse. +Every time the worker starts, it will find the most recently inserted build and continue from there. + +At some point we most likely will need to +[parallelize this worker because of the number of processed builds](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/126863#note_1494922639). + +We will start with most recent builds and will not upload all historical data. + +### "Raw data", materialized views and queries + +Ingested data will go to the "raw data" table in ClickHouse. +This table will use `ReplacingMergeTree` engine to deduplicate rows in case data ingestion mechanism accidentally submits the same batch twice. + +Raw data can be used directly do execute queries, but most of the time we will create specialized materialized views +using `AggregatingMergeTree` engine. +This will allow us to read significantly less data when performing queries. + +### Limitations and open questions + +The topics below require further investigation. + +#### Efficient way of querying data for namespaces + +We start with the PoC available only for administrators, +but very soon we will need to implement features on the group level. + +We can't just put denormalized "path" in the source table because it can be changed when groups or projects are moved. + +The simplest way of solving this is to always filter builds by `project_id`, +but this may be inefficient and require reading a significant portion of all data because ClickHouse stores data in big batches. + +#### Keeping the database schema up to date + +Right now we don't have any mechanism equivalent to migrations we use for PostgreSQL. +While developing our first features we will maintain database schema by hand and +continue developing mechanisms for migrations. + +#### Re-uploading data after changing the schema + +If we need to modify database schema, old data maybe incomplete. +In that case we can simply truncate the ClickHouse tables and reupload (part of) the data. diff --git a/doc/ci/environments/protected_environments.md b/doc/ci/environments/protected_environments.md index 0478c519439..41036c1f8fa 100644 --- a/doc/ci/environments/protected_environments.md +++ b/doc/ci/environments/protected_environments.md @@ -33,6 +33,7 @@ To protect an environment: 1. On the left sidebar, at the top, select **Search GitLab** (**{search}**) to find your project. 1. Select **Settings > CI/CD**. 1. Expand **Protected environments**. +1. Select **Protect an environment**. 1. From the **Environment** list, select the environment you want to protect. 1. In the **Allowed to deploy** list, select the role, users, or groups you want to give deploy access to. Keep in mind that: diff --git a/doc/development/fe_guide/frontend_goals.md b/doc/development/fe_guide/frontend_goals.md new file mode 100644 index 00000000000..05e9b0df389 --- /dev/null +++ b/doc/development/fe_guide/frontend_goals.md @@ -0,0 +1,31 @@ +--- +stage: none +group: unassigned +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments +--- + +# Frontend Goals + +This section defined the _desired state_ of the GitLab frontend how we see it over the next few years. + +## Cluster SPAs + +Currently, GitLab mostly follows Rails architecture and Rails routing which means every single time we're changing route, we have page reload. This results in long loading times because we are: + +- rendering HAML page; +- mounting Vue applications if we have any; +- fetching data for these applications + +Ideally, we should reduce the number of times user needs to go through this long process. This would be possible with converting GitLab into a single-page application but this would require significant refactoring and is not an achieavable short/mid-term goal. + +The realistic goal is to move to _multiple SPAs_ experience where we define the _clusters_ of pages that form the user flow, and move this cluster from Rails routing to a single-page application with client-side routing. This way, we can load all the relevant context from HAML only once, and fetch all the additional data from the API depending on the route. An example of a cluster could be the following pages: + +- issues list +- issue boards +- issue details page +- new issue +- editing an issue + +All of them have the same context (project path, current user etc.), we could easily fetch more data with issue-specific parameter (issue `iid`) and store the results on the client (so that opening the same issue won't require more API calls). This leads to a smooth user experience for navigating through issues. + +For navigation between clusters, we can still rely on Rails routing. These cases should be relatively more scarce than navigation within clusters. diff --git a/doc/development/fe_guide/index.md b/doc/development/fe_guide/index.md index a10c87e1fa1..6bebc585965 100644 --- a/doc/development/fe_guide/index.md +++ b/doc/development/fe_guide/index.md @@ -82,9 +82,11 @@ Now that our values have been defined, we can base our goals on these values and - Improve our pipelines speed - Build a better set of shared components with documentation +We have detailed description on how we see GitLab frontend in the future in [Frontend Goals](frontend_goals.md) section + ### Frontend onboarding course -The [Frontend onboarding course](onboarding_course/index.md) provides a 6-week structured curriculum to learn how to contribute to the GitLab frontend. +The [Frontend onboarding course](onboarding_course/index.md) provides a 6-week structured curriculum to learn how to contribute to the GitLab frontend. ### Browser Support diff --git a/doc/development/pipelines/internals.md b/doc/development/pipelines/internals.md index 82e95392989..09b23c46570 100644 --- a/doc/development/pipelines/internals.md +++ b/doc/development/pipelines/internals.md @@ -384,8 +384,10 @@ For this scenario, you have to: - This pattern does not work if a script relies on `git` to access the repository, because we don't have the repository without cloning or fetching. - The job using this pattern needs to have `curl` available. -- If you need to run `bundle install` in the job (even using `BUNDLE_ONLY`), you need to download the gems that are stored in the `gitlab-org/gitlab` project. - - You can use the `download_local_gems` shell command for that purpose. +- If you need to run `bundle install` in the job (even using `BUNDLE_ONLY`), you need to: + - Download the gems that are stored in the `gitlab-org/gitlab` project. + - You can use the `download_local_gems` shell command for that purpose. + - Include the `Gemfile`, `Gemfile.lock` and `Gemfile.checksum` (if applicable) #### Where is this pattern used? @@ -407,6 +409,7 @@ For this scenario, you have to: - `VERSION` - `rspec:coverage` for: - `config/bundler_setup.rb` + - `Gemfile.checksum` - `Gemfile.lock` - `Gemfile` - `scripts/merge-simplecov` diff --git a/doc/topics/data_seeder.md b/doc/topics/data_seeder.md index 19c0e05d8ed..c5cf80c349d 100644 --- a/doc/topics/data_seeder.md +++ b/doc/topics/data_seeder.md @@ -9,8 +9,8 @@ description: Data Seeder test data harness created by the Test Data Working Grou GitLab Data Seeder (GDS) is a test data seeding harness, that can seed test data into a user or group namespace. -The Data Seeder uses FactoryBot in the backend which makes maintenance extremely easy. When a Model is changed, -FactoryBot will already be reflected to account for the change. +The Data Seeder uses FactoryBot in the backend which makes maintenance straightforward. When a Model changes, +FactoryBot already reflects the change. ## Docker Setup @@ -40,7 +40,7 @@ Seeding Data for Administrator #### `:name` -Where `:name` is the file name. (This will reflect relative `.rb`, `.yml`, or `.json` files located in `ee/db/seeds/data_seeder`, or absolute paths to seed files) +Where `:name` is the filename. (This name reflects relative `.rb`, `.yml`, or `.json` files located in `ee/db/seeds/data_seeder`, or absolute paths to seed files.) #### `:namespace_id` @@ -64,18 +64,18 @@ The Data Seeder uses FactoryBot definitions from `spec/factories` which ... Factories reside in `spec/factories/*` and are fixtures for Rails models found in `app/models/*`. For example, For a model named `app/models/issue.rb`, the factory will be named `spec/factories/issues.rb`. For a model named `app/models/project.rb`, the factory will be named `app/models/projects.rb`. -There are currently three parsers that the GitLab Data Seeder supports. Ruby, YAML, and JSON. +There are three parsers that the GitLab Data Seeder supports. Ruby, YAML, and JSON. ### Ruby -All Ruby Seeds must define a `DataSeeder` class with a `#seed` instance method. You may structure your Ruby class as you wish. All FactoryBot [methods](https://www.rubydoc.info/gems/factory_bot/FactoryBot/Syntax/Methods) (`create`, `build`, `create_list`) will be included in the class automatically and may be called. +All Ruby Seeds must define a `DataSeeder` class with a `#seed` instance method. You may structure your Ruby class as you wish. All FactoryBot [methods](https://www.rubydoc.info/gems/factory_bot/FactoryBot/Syntax/Methods) (`create`, `build`, `create_list`) are included in the class automatically and may be called. -The `DataSeeder` class will have the following instance variables defined upon seeding: +The `DataSeeder` class contains the following instance variables defined upon seeding: - `@seed_file` - The `File` object. - `@owner` - The owner of the seed data. -- `@name` - The name of the seed. This will be the seed file name without the extension. -- `@group` - The root group that all seeded data will be created under. +- `@name` - The name of the seed. This is the seed filename without the extension. +- `@group` - The root group that all seeded data is created under. ```ruby # frozen_string_literal: true @@ -138,10 +138,10 @@ Given: `create(:iteration, :with_title, :current, title: 'My Iteration')` ||| |:-|:-| -| **:iteration** | This is the **Name** of the factory. The file name will be the plural form of this **Name** and reside under either `spec/factories/iterations.rb` or `ee/spec/factories/iterations.rb`. | +| **:iteration** | This is the **Name** of the factory. The filename will be the plural form of this **Name** and reside under either `spec/factories/iterations.rb` or `ee/spec/factories/iterations.rb`. | | **:with_title** | This is a **Trait** of the factory. [See how it's defined](https://gitlab.com/gitlab-org/gitlab/-/blob/9c2a1f98483921dd006d70fdaed316e21fc5652f/ee/spec/factories/iterations.rb#L21-23). | | **:current** | This is a **Trait** of the factory. [See how it's defined](https://gitlab.com/gitlab-org/gitlab/-/blob/9c2a1f98483921dd006d70fdaed316e21fc5652f/ee/spec/factories/iterations.rb#L29-31). | -| **title: 'My Iteration'** | This is an **Attribute** of the factory that will be passed to the Model for creation. | +| **title: 'My Iteration'** | This is an **Attribute** of the factory that is passed to the Model for creation. | ### Examples @@ -209,7 +209,7 @@ Examples of invalid IDs: #### ActiveRecord::AssociationTypeMismatch: Model expected, got ... which is an instance of String -This is currently a limitation for the seeder. +This is a limitation for the seeder. See the issue for [allowing parsing of raw Ruby objects](https://gitlab.com/gitlab-org/gitlab/-/issues/403079). @@ -255,7 +255,7 @@ group_milestones: #### Quirks -- You _must_ specify `group:` and have it be empty. This is because the Milestones factory will manipulate the factory in an `after(:build)`. If this is not present, the Milestone will not be associated properly with the Group. +- You _must_ specify `group:` and have it be empty. This is because the Milestones factory manipulates the factory in an `after(:build)`. If this is not present, the Milestone cannot be associated properly with the Group. ### [Epics](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/spec/factories/epics.rb) diff --git a/doc/update/deprecations.md b/doc/update/deprecations.md index be2f6386300..8213ee8c0e7 100644 --- a/doc/update/deprecations.md +++ b/doc/update/deprecations.md @@ -135,7 +135,6 @@ These three variables will be removed in GitLab 17.0. <div class="deprecation-notes"> - Announced in GitLab <span class="milestone">16.1</span> -- End of Support GitLab <span class="milestone">17.0</span> - Removal in GitLab <span class="milestone">17.0</span> ([breaking change](https://docs.gitlab.com/ee/update/terminology.html#breaking-change)) - To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/gitlab-org/gitlab/-/issues/414864). </div> @@ -150,7 +149,6 @@ In GitLab 11.11 the Windows Batch executor, the CMD shell was deprecated in GitL <div class="deprecation-notes"> - Announced in GitLab <span class="milestone">16.2</span> -- End of Support GitLab <span class="milestone">17.0</span> - Removal in GitLab <span class="milestone">17.0</span> ([breaking change](https://docs.gitlab.com/ee/update/terminology.html#breaking-change)) - To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/gitlab-org/gitlab/-/issues/41518). </div> @@ -195,7 +193,6 @@ Use `Vulnerability.hasRemediations` instead. <div class="deprecation-notes"> - Announced in GitLab <span class="milestone">15.11</span> -- End of Support GitLab <span class="milestone">17.9</span> - Removal in GitLab <span class="milestone">17.0</span> - To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/gitlab-org/gitlab/-/issues/406679). </div> @@ -302,7 +299,6 @@ are deprecated and will be removed from the GraphQL API. For installation instru <div class="deprecation-notes"> - Announced in GitLab <span class="milestone">15.6</span> -- End of Support GitLab <span class="milestone">17.0</span> - Removal in GitLab <span class="milestone">17.0</span> ([breaking change](https://docs.gitlab.com/ee/update/terminology.html#breaking-change)) - To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/gitlab-org/gitlab/-/issues/382077). </div> @@ -469,7 +465,6 @@ The [`project_fingerprint`](https://gitlab.com/groups/gitlab-org/-/epics/2791) a <div class="deprecation-notes"> - Announced in GitLab <span class="milestone">16.0</span> -- End of Support GitLab <span class="milestone">17.0</span> - Removal in GitLab <span class="milestone">17.0</span> ([breaking change](https://docs.gitlab.com/ee/update/terminology.html#breaking-change)) - To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/groups/gitlab-org/-/epics/9065). </div> @@ -490,7 +485,7 @@ PostgreSQL 14 will also be supported for instances that want to upgrade prior to <div class="deprecation-notes"> - Announced in GitLab <span class="milestone">15.9</span> -- End of Support GitLab <span class="milestone">16.0</span> +- End of Support in GitLab <span class="milestone">16.0</span> - Removal in GitLab <span class="milestone">17.0</span> ([breaking change](https://docs.gitlab.com/ee/update/terminology.html#breaking-change)) - To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/gitlab-org/gitlab/-/issues/390787). </div> @@ -509,7 +504,6 @@ While the above approach is recommended for most instances, Sidekiq can also be <div class="deprecation-notes"> - Announced in GitLab <span class="milestone">15.6</span> -- End of Support GitLab <span class="milestone">17.0</span> - Removal in GitLab <span class="milestone">17.0</span> ([breaking change](https://docs.gitlab.com/ee/update/terminology.html#breaking-change)) - To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/gitlab-org/gitlab/-/issues/379743). </div> @@ -539,7 +533,6 @@ This change is a breaking change. You should [create a runner in the UI](../ci/r <div class="deprecation-notes"> - Announced in GitLab <span class="milestone">15.6</span> -- End of Support GitLab <span class="milestone">17.0</span> - Removal in GitLab <span class="milestone">17.0</span> ([breaking change](https://docs.gitlab.com/ee/update/terminology.html#breaking-change)) - To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/gitlab-org/gitlab/-/issues/380872). </div> @@ -655,7 +648,6 @@ automatically from GitLab 16.0 onwards. <div class="deprecation-notes"> - Announced in GitLab <span class="milestone">15.9</span> -- End of Support GitLab <span class="milestone">17.0</span> - Removal in GitLab <span class="milestone">17.0</span> ([breaking change](https://docs.gitlab.com/ee/update/terminology.html#breaking-change)) - To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/gitlab-org/gitlab/-/issues/372411). </div> @@ -675,7 +667,6 @@ we'll be introducing support in [this epic](https://gitlab.com/groups/gitlab-org <div class="deprecation-notes"> - Announced in GitLab <span class="milestone">15.7</span> -- End of Support GitLab <span class="milestone">17.0</span> - Removal in GitLab <span class="milestone">17.0</span> ([breaking change](https://docs.gitlab.com/ee/update/terminology.html#breaking-change)) - To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/gitlab-org/gitlab/-/issues/383341). </div> @@ -731,7 +722,6 @@ Due to limited customer usage and capabilities, the Visual Reviews feature for R <div class="deprecation-notes"> - Announced in GitLab <span class="milestone">15.7</span> -- End of Support GitLab <span class="milestone">17.0</span> - Removal in GitLab <span class="milestone">17.0</span> ([breaking change](https://docs.gitlab.com/ee/update/terminology.html#breaking-change)) - To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/gitlab-org/gitlab/-/issues/385235). </div> @@ -829,7 +819,6 @@ removed in 17.0. <div class="deprecation-notes"> - Announced in GitLab <span class="milestone">15.6</span> -- End of Support GitLab <span class="milestone">17.0</span> - Removal in GitLab <span class="milestone">17.0</span> ([breaking change](https://docs.gitlab.com/ee/update/terminology.html#breaking-change)) - To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/gitlab-org/gitlab/-/issues/381111). </div> @@ -1005,7 +994,6 @@ be available in CI/CD jobs. <div class="deprecation-notes"> - Announced in GitLab <span class="milestone">16.0</span> -- End of Support GitLab <span class="milestone">16.3</span> - Removal in GitLab <span class="milestone">16.3</span> ([breaking change](https://docs.gitlab.com/ee/update/terminology.html#breaking-change)) - To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/7772). </div> @@ -1072,7 +1060,7 @@ Twitter OAuth 1.0a OmniAuth is being deprecated and removed on GitLab.com in Git <div class="deprecation-notes"> - Announced in GitLab <span class="milestone">15.11</span> -- End of Support GitLab <span class="milestone">16.1</span> +- End of Support in GitLab <span class="milestone">16.1</span> - Removal in GitLab <span class="milestone">16.1</span> - To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/29639). </div> @@ -1235,7 +1223,6 @@ This unintended functionality is deprecated in GitLab 15.8 and will be removed i <div class="deprecation-notes"> - Announced in GitLab <span class="milestone">15.6</span> -- End of Support GitLab <span class="milestone">16.0</span> - Removal in GitLab <span class="milestone">16.0</span> ([breaking change](https://docs.gitlab.com/ee/update/terminology.html#breaking-change)) - To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/gitlab-org/gitlab/-/issues/379064). </div> @@ -1452,7 +1439,6 @@ For more information, see [the deprecation notes for Consul 1.9.0](https://githu <div class="deprecation-notes"> - Announced in GitLab <span class="milestone">15.9</span> -- End of Support GitLab <span class="milestone">16.0</span> - Removal in GitLab <span class="milestone">16.0</span> ([breaking change](https://docs.gitlab.com/ee/update/terminology.html#breaking-change)) - To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/gitlab-org/gitlab/-/issues/391896). </div> @@ -1699,7 +1685,6 @@ The Jira DVCS connector is also deprecated for Jira 8.13 and earlier. You can on <div class="deprecation-notes"> - Announced in GitLab <span class="milestone">15.7</span> -- End of Support GitLab <span class="milestone">16.0</span> - Removal in GitLab <span class="milestone">16.0</span> ([breaking change](https://docs.gitlab.com/ee/update/terminology.html#breaking-change)) - To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/gitlab-org/gitlab/-/issues/383039). </div> @@ -2020,7 +2005,7 @@ Alternatives to using the `gitlab:import:repos` Rake task include: <div class="deprecation-notes"> - Announced in GitLab <span class="milestone">15.3</span> -- End of Support GitLab <span class="milestone">15.6</span> +- End of Support in GitLab <span class="milestone">15.6</span> - Removal in GitLab <span class="milestone">16.0</span> ([breaking change](https://docs.gitlab.com/ee/update/terminology.html#breaking-change)) - To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/gitlab-org/gitlab/-/issues/331468). </div> @@ -2191,7 +2176,6 @@ For more information, refer to [security report validation](https://docs.gitlab. <div class="deprecation-notes"> - Announced in GitLab <span class="milestone">15.7</span> -- End of Support GitLab <span class="milestone">16.0</span> - Removal in GitLab <span class="milestone">16.0</span> ([breaking change](https://docs.gitlab.com/ee/update/terminology.html#breaking-change)) - To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/gitlab-org/gitlab/-/issues/377824). </div> @@ -2243,7 +2227,6 @@ This may require updating your metrics collection targets to also scrape `/db_me <div class="deprecation-notes"> - Announced in GitLab <span class="milestone">15.7</span> -- End of Support GitLab <span class="milestone">16.0</span> - Removal in GitLab <span class="milestone">16.0</span> ([breaking change](https://docs.gitlab.com/ee/update/terminology.html#breaking-change)) - To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/gitlab-org/gitlab/-/issues/385564). </div> @@ -2372,7 +2355,6 @@ You can use the vulnerabilityFindingDismiss GraphQL mutation to set the status o <div class="deprecation-notes"> - Announced in GitLab <span class="milestone">15.8</span> -- End of Support GitLab <span class="milestone">16.0</span> - Removal in GitLab <span class="milestone">16.0</span> ([breaking change](https://docs.gitlab.com/ee/update/terminology.html#breaking-change)) - To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/gitlab-org/gitlab/-/issues/376216). </div> @@ -2416,7 +2398,6 @@ In GitLab 16.0 we will remove the ability to use a global ID in the work items p <div class="deprecation-notes"> - Announced in GitLab <span class="milestone">15.7</span> -- End of Support GitLab <span class="milestone">16.0</span> - Removal in GitLab <span class="milestone">16.0</span> ([breaking change](https://docs.gitlab.com/ee/update/terminology.html#breaking-change)) - To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/gitlab-org/gitlab/-/issues/377825). </div> @@ -2551,12 +2532,11 @@ Starting in GitLab 15.7 we started providing packages for openSUSE Leap 15.4, an <div class="deprecation-notes"> - Announced in GitLab <span class="milestone">15.8</span> -- End of Support GitLab <span class="milestone">15.10</span> - Removal in GitLab <span class="milestone">15.10</span> ([breaking change](https://docs.gitlab.com/ee/update/terminology.html#breaking-change)) - To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/gitlab-org/gitlab/-/issues/387976). </div> -We are deprecating support for [uploading backups to remote storage](https://docs.gitlab.com/ee/raketasks/backup_gitlab.html#upload-backups-to-a-remote-cloud-storage) using Openstack Swift and Rackspace APIs. The support for these APIs depends on third-party libraries that are no longer actively maintained and have not been updated for Ruby 3. GitLab is switching over to Ruby 3 prior to EOL of Ruby 2 in order to stay up to date on security patches. +We are deprecating support for [uploading backups to remote storage](https://docs.gitlab.com/ee/raketasks/backup_gitlab.html#upload-backups-to-a-remote-cloud-storage) using Openstack Swift and Rackspace APIs. The support for these APIs depends on third-party libraries that are no longer actively maintained and have not been updated for Ruby 3. GitLab is switching over to Ruby 3 prior to EOL of Ruby 2 in order to stay up to date on security patches. - If you're using OpenStack, you need to change you configuration to use the S3 API instead of Swift. - If you're using Rackspace storage, you need to switch to a different provider or manually upload the backup file after the backup task is complete. diff --git a/doc/user/project/badges.md b/doc/user/project/badges.md index be47d6f18bd..f52ca8c231a 100644 --- a/doc/user/project/badges.md +++ b/doc/user/project/badges.md @@ -133,6 +133,7 @@ To add a new badge to a project: 1. On the left sidebar, at the top, select **Search GitLab** (**{search}**) to find your project. 1. Select **Settings > General**. 1. Expand **Badges**. +1. Select **Add badge**. 1. Under **Link**, enter the URL that the badges should point to. 1. Under **Badge image URL**, enter the URL of the image that should be displayed. 1. Select **Add badge**. diff --git a/lib/gitlab/metrics/dashboard/defaults.rb b/lib/gitlab/metrics/dashboard/defaults.rb deleted file mode 100644 index 6a5f98a18c8..00000000000 --- a/lib/gitlab/metrics/dashboard/defaults.rb +++ /dev/null @@ -1,13 +0,0 @@ -# frozen_string_literal: true - -# Central point for managing default attributes from within -# the metrics dashboard module. -module Gitlab - module Metrics - module Dashboard - module Defaults - DEFAULT_PANEL_TYPE = 'area-chart' - end - end - end -end diff --git a/lib/gitlab/metrics/dashboard/stages/base_stage.rb b/lib/gitlab/metrics/dashboard/stages/base_stage.rb index c2a8a88108f..b869a633030 100644 --- a/lib/gitlab/metrics/dashboard/stages/base_stage.rb +++ b/lib/gitlab/metrics/dashboard/stages/base_stage.rb @@ -5,8 +5,6 @@ module Gitlab module Dashboard module Stages class BaseStage - include Gitlab::Metrics::Dashboard::Defaults - attr_reader :project, :dashboard, :params def initialize(project, dashboard, params) diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 869e246f591..d82166233e8 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -13898,6 +13898,9 @@ msgstr "" msgid "Created %{timeAgo} by %{author}" msgstr "" +msgid "Created %{timeAgo} by %{email} via %{author}" +msgstr "" + msgid "Created %{time_ago}" msgstr "" @@ -16962,6 +16965,11 @@ msgstr "" msgid "Dismissed" msgstr "" +msgid "Dismissed (%d reason)" +msgid_plural "Dismissed (%d reasons)" +msgstr[0] "" +msgstr[1] "" + msgid "Display" msgstr "" @@ -30924,9 +30932,6 @@ msgstr "" msgid "New User" msgstr "" -msgid "New application" -msgstr "" - msgid "New branch" msgstr "" @@ -42581,6 +42586,9 @@ msgstr "" msgid "SecurityReports|All clusters" msgstr "" +msgid "SecurityReports|All dismissal reasons" +msgstr "" + msgid "SecurityReports|All images" msgstr "" @@ -42644,6 +42652,12 @@ msgstr "" msgid "SecurityReports|Dismissed '%{vulnerabilityName}'. Turn off the hide dismissed toggle to view." msgstr "" +msgid "SecurityReports|Dismissed (all reasons)" +msgstr "" + +msgid "SecurityReports|Dismissed as..." +msgstr "" + msgid "SecurityReports|Does not have issue" msgstr "" @@ -55274,6 +55288,9 @@ msgstr "" msgid "created %{timeAgo} by %{author} in %{project_link}" msgstr "" +msgid "created %{timeAgo} by %{email} via %{author}" +msgstr "" + msgid "created by" msgstr "" diff --git a/spec/features/issues/user_views_issue_spec.rb b/spec/features/issues/user_views_issue_spec.rb index 17ff3e0c702..00aa7685e9d 100644 --- a/spec/features/issues/user_views_issue_spec.rb +++ b/spec/features/issues/user_views_issue_spec.rb @@ -39,42 +39,4 @@ RSpec.describe "User views issue", feature_category: :team_planning do expect(page).not_to have_link('Close issue') end end - - describe 'user status' do - subject { visit(project_issue_path(project, issue)) } - - context 'when showing status of the author of the issue' do - it_behaves_like 'showing user status' do - let(:user_with_status) { user } - end - end - - context 'when showing status of a user who commented on an issue', :js do - it_behaves_like 'showing user status' do - let(:user_with_status) { user } - end - end - - context 'when status message has an emoji', :js do - let_it_be(:message) { 'My status with an emoji' } - let_it_be(:message_emoji) { 'basketball' } - let_it_be(:status) { create(:user_status, user: user, emoji: 'smirk', message: "#{message} :#{message_emoji}:") } - - it 'correctly renders the emoji' do - wait_for_requests - - tooltip_span = page.first(".user-status-emoji[title^='#{message}']") - - tooltip_span.hover - - wait_for_requests - - tooltip = page.find('.tooltip .tooltip-inner') - - page.within(tooltip) do - expect(page).to have_emoji(message_emoji) - end - end - end - end end diff --git a/spec/features/projects/pages/user_adds_domain_spec.rb b/spec/features/projects/pages/user_adds_domain_spec.rb index 0f14729f736..14b01cb63d2 100644 --- a/spec/features/projects/pages/user_adds_domain_spec.rb +++ b/spec/features/projects/pages/user_adds_domain_spec.rb @@ -174,11 +174,16 @@ RSpec.describe 'User adds pages domain', :js, feature_category: :pages do expect(domain.key).to be_nil end - it 'shows the DNS ALIAS record', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/419686' do + it 'shows the DNS ALIAS record' do visit project_pages_path(project) within('#content-body') { click_link 'Edit' } - expect(page).to have_field :domain_dns, with: "#{domain.domain} ALIAS namespace1.example.com." + expect(page).to have_field :domain_dns, with: format( + "%{domain} ALIAS %{namespace}.%{pages_host}.", + domain: domain.domain, + namespace: domain.project.root_namespace.path, + pages_host: Settings.pages.host + ) end end end diff --git a/spec/frontend/issues/issue_spec.js b/spec/frontend/issues/issue_spec.js index 3b8a09714a7..bf2ca42f71f 100644 --- a/spec/frontend/issues/issue_spec.js +++ b/spec/frontend/issues/issue_spec.js @@ -1,7 +1,6 @@ -import { getByText } from '@testing-library/dom'; +import MockAdapter from 'axios-mock-adapter'; import htmlOpenIssue from 'test_fixtures/issues/open-issue.html'; import htmlClosedIssue from 'test_fixtures/issues/closed-issue.html'; -import MockAdapter from 'axios-mock-adapter'; import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures'; import { EVENT_ISSUABLE_VUE_APP_CHANGE } from '~/issuable/constants'; import Issue from '~/issues/issue'; @@ -26,14 +25,6 @@ describe('Issue', () => { }); const getIssueCounter = () => document.querySelector('.issue_counter'); - const getOpenStatusBox = () => - getByText(document, (_, el) => el.textContent.match(/Open/), { - selector: '.issuable-status-badge-open', - }); - const getClosedStatusBox = () => - getByText(document, (_, el) => el.textContent.match(/Closed/), { - selector: '.issuable-status-badge-closed', - }); describe.each` desc | isIssueInitiallyOpen | expectedCounterText @@ -48,9 +39,6 @@ describe('Issue', () => { } testContext.issueCounter = getIssueCounter(); - testContext.statusBoxClosed = getClosedStatusBox(); - testContext.statusBoxOpen = getOpenStatusBox(); - testContext.issueCounter.textContent = '1,001'; }); @@ -58,16 +46,6 @@ describe('Issue', () => { resetHTMLFixture(); }); - it(`has the proper visible status box when ${isIssueInitiallyOpen ? 'open' : 'closed'}`, () => { - if (isIssueInitiallyOpen) { - expect(testContext.statusBoxClosed).toHaveClass('hidden'); - expect(testContext.statusBoxOpen).not.toHaveClass('hidden'); - } else { - expect(testContext.statusBoxClosed).not.toHaveClass('hidden'); - expect(testContext.statusBoxOpen).toHaveClass('hidden'); - } - }); - describe('when vue app triggers change', () => { beforeEach(() => { document.dispatchEvent( @@ -80,16 +58,6 @@ describe('Issue', () => { ); }); - it('displays correct status box', () => { - if (isIssueInitiallyOpen) { - expect(testContext.statusBoxClosed).not.toHaveClass('hidden'); - expect(testContext.statusBoxOpen).toHaveClass('hidden'); - } else { - expect(testContext.statusBoxClosed).toHaveClass('hidden'); - expect(testContext.statusBoxOpen).not.toHaveClass('hidden'); - } - }); - it('updates issueCounter text', () => { expect(testContext.issueCounter).toBeVisible(); expect(testContext.issueCounter).toHaveText(expectedCounterText); diff --git a/spec/frontend/issues/show/components/issue_header_spec.js b/spec/frontend/issues/show/components/issue_header_spec.js new file mode 100644 index 00000000000..721801714aa --- /dev/null +++ b/spec/frontend/issues/show/components/issue_header_spec.js @@ -0,0 +1,128 @@ +import { GlLink, GlSprintf } from '@gitlab/ui'; +import { shallowMount } from '@vue/test-utils'; +import { STATUS_CLOSED, STATUS_OPEN } from '~/issues/constants'; +import IssueHeader from '~/issues/show/components/issue_header.vue'; +import { __, s__ } from '~/locale'; +import IssuableHeader from '~/vue_shared/issuable/show/components/issuable_header.vue'; + +describe('IssueHeader component', () => { + let wrapper; + + const findGlLink = () => wrapper.findComponent(GlLink); + const findIssuableHeader = () => wrapper.findComponent(IssuableHeader); + + const mountComponent = (props = {}) => { + wrapper = shallowMount(IssueHeader, { + propsData: { + author: { id: 48 }, + confidential: false, + createdAt: '2020-01-23T12:34:56.789Z', + duplicatedToIssueUrl: '', + isFirstContribution: false, + isHidden: false, + isLocked: false, + issuableState: 'opened', + movedToIssueUrl: '', + promotedToEpicUrl: '', + ...props, + }, + stubs: { + GlSprintf, + }, + }); + }; + + it('renders IssuableHeader component', () => { + mountComponent(); + + expect(findIssuableHeader().props()).toMatchObject({ + author: { id: 48 }, + blocked: false, + confidential: false, + createdAt: '2020-01-23T12:34:56.789Z', + isFirstContribution: false, + isHidden: false, + issuableState: 'opened', + issuableType: 'issue', + serviceDeskReplyTo: '', + showWorkItemTypeIcon: true, + statusIcon: 'issues', + workspaceType: 'project', + }); + }); + + describe('status badge slot', () => { + describe('when status is open', () => { + beforeEach(() => { + mountComponent({ issuableState: STATUS_OPEN }); + }); + + it('renders Open text', () => { + expect(findIssuableHeader().text()).toBe(__('Open')); + }); + + it('renders correct icon', () => { + expect(findIssuableHeader().props('statusIcon')).toBe('issues'); + }); + }); + + describe('when status is closed', () => { + beforeEach(() => { + mountComponent({ issuableState: STATUS_CLOSED }); + }); + + it('renders Closed text', () => { + expect(findIssuableHeader().text()).toBe(s__('IssuableStatus|Closed')); + }); + + it('renders correct icon', () => { + expect(findIssuableHeader().props('statusIcon')).toBe('issue-closed'); + }); + + describe('when issue is marked as duplicate', () => { + beforeEach(() => { + mountComponent({ + issuableState: STATUS_CLOSED, + duplicatedToIssueUrl: 'project/-/issue/5', + }); + }); + + it('renders `Closed (duplicated)`', () => { + expect(findIssuableHeader().text()).toMatchInterpolatedText('Closed (duplicated)'); + }); + + it('links to the duplicated issue', () => { + expect(findGlLink().attributes('href')).toBe('project/-/issue/5'); + }); + }); + + describe('when issue is marked as moved', () => { + beforeEach(() => { + mountComponent({ issuableState: STATUS_CLOSED, movedToIssueUrl: 'project/-/issue/6' }); + }); + + it('renders `Closed (moved)`', () => { + expect(findIssuableHeader().text()).toMatchInterpolatedText('Closed (moved)'); + }); + + it('links to the moved issue', () => { + expect(findGlLink().attributes('href')).toBe('project/-/issue/6'); + }); + }); + + describe('when issue is marked as promoted', () => { + beforeEach(() => { + mountComponent({ issuableState: STATUS_CLOSED, promotedToEpicUrl: 'group/-/epic/7' }); + }); + + it('renders `Closed (promoted)`', () => { + expect(findIssuableHeader().text()).toMatchInterpolatedText('Closed (promoted)'); + }); + + it('links to the promoted epic', () => { + expect(findGlLink().attributes('href')).toBe('group/-/epic/7'); + }); + }); + }); + }); +}); diff --git a/spec/frontend/vue_shared/issuable/show/components/issuable_header_spec.js b/spec/frontend/vue_shared/issuable/show/components/issuable_header_spec.js index 5b41337b8bd..4d08ad54e58 100644 --- a/spec/frontend/vue_shared/issuable/show/components/issuable_header_spec.js +++ b/spec/frontend/vue_shared/issuable/show/components/issuable_header_spec.js @@ -1,40 +1,55 @@ -import { GlButton, GlBadge, GlIcon, GlAvatarLabeled, GlAvatarLink, GlSprintf } from '@gitlab/ui'; -import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; -import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures'; -import { TYPE_ISSUE, WORKSPACE_PROJECT } from '~/issues/constants'; +import { GlBadge, GlButton, GlIcon, GlLink, GlSprintf } from '@gitlab/ui'; +import { shallowMount } from '@vue/test-utils'; +import { resetHTMLFixture, setHTMLFixture } from 'helpers/fixtures'; +import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; +import { + STATUS_CLOSED, + STATUS_OPEN, + STATUS_REOPENED, + TYPE_ISSUE, + WORKSPACE_PROJECT, +} from '~/issues/constants'; +import { __ } from '~/locale'; import ConfidentialityBadge from '~/vue_shared/components/confidentiality_badge.vue'; +import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; import IssuableHeader from '~/vue_shared/issuable/show/components/issuable_header.vue'; -import { mockIssuableShowProps, mockIssuable } from '../mock_data'; +import WorkItemTypeIcon from '~/work_items/components/work_item_type_icon.vue'; +import { mockIssuable, mockIssuableShowProps } from '../mock_data'; describe('IssuableHeader component', () => { let wrapper; - beforeEach(() => { - window.gon.gitlab_url = 'http://0.0.0.0'; - }); - - const findBadge = () => wrapper.findComponent(GlBadge); - const findBlockedIcon = () => wrapper.findByTestId('blocked').findComponent(GlIcon); - const findButton = () => wrapper.findComponent(GlButton); - const findGlAvatarLink = () => wrapper.findComponent(GlAvatarLink); - const findHeaderActions = () => wrapper.findByTestId('header-actions'); - const findTaskStatus = () => wrapper.findByTestId('task-status'); + const findConfidentialityBadge = () => wrapper.findComponent(ConfidentialityBadge); + const findStatusBadge = () => wrapper.findComponent(GlBadge); + const findToggleButton = () => wrapper.findComponent(GlButton); + const findAuthorLink = () => wrapper.findComponent(GlLink); + const findTimeAgoTooltip = () => wrapper.findComponent(TimeAgoTooltip); + const findWorkItemTypeIcon = () => wrapper.findComponent(WorkItemTypeIcon); + const findGlIconWithName = (name) => + wrapper.findAllComponents(GlIcon).filter((component) => component.props('name') === name); + const findIcon = (name) => + findGlIconWithName(name).exists() ? findGlIconWithName(name).at(0) : undefined; + const findBlockedIcon = () => findIcon('lock'); + const findHiddenIcon = () => findIcon('spam'); + const findExternalLinkIcon = () => findIcon('external-link'); + const findFirstContributionIcon = () => findIcon('first-contribution'); + const findComponentTooltip = (component) => getBinding(component.element, 'gl-tooltip'); const createComponent = (props = {}, { stubs } = {}) => { - wrapper = shallowMountExtended(IssuableHeader, { + wrapper = shallowMount(IssuableHeader, { + directives: { + GlTooltip: createMockDirective('gl-tooltip'), + }, propsData: { ...mockIssuable, ...mockIssuableShowProps, + issuableState: STATUS_OPEN, issuableType: TYPE_ISSUE, workspaceType: WORKSPACE_PROJECT, ...props, }, slots: { - 'status-badge': 'Open', - 'header-actions': ` - <button class="js-close">Close issuable</button> - <a class="js-new" href="/gitlab-org/gitlab-shell/-/issues/new">New issuable</a> - `, + 'header-actions': `Header actions slot`, }, stubs: { GlSprintf, @@ -43,110 +58,210 @@ describe('IssuableHeader component', () => { }); }; - afterEach(() => { - resetHTMLFixture(); + describe('status badge', () => { + describe('variant', () => { + it('is `success` when status is open', () => { + createComponent({ issuableState: STATUS_OPEN }); + + expect(findStatusBadge().props('variant')).toBe('success'); + }); + + it('is `success` when status is reopened', () => { + createComponent({ issuableState: STATUS_REOPENED }); + + expect(findStatusBadge().props('variant')).toBe('success'); + }); + + it('is `info` when status is closed', () => { + createComponent({ issuableState: STATUS_CLOSED }); + + expect(findStatusBadge().props('variant')).toBe('info'); + }); + }); + + describe('icon', () => { + it('renders when statusIcon prop exists', () => { + createComponent({ statusIcon: 'issues' }); + + expect(findStatusBadge().findComponent(GlIcon).props('name')).toBe('issues'); + }); + + it('does not render when statusIcon prop does not exist', () => { + createComponent({ statusIcon: '' }); + + expect(findStatusBadge().findComponent(GlIcon).exists()).toBe(false); + }); + }); + + it('renders status text', () => { + createComponent(); + + expect(findStatusBadge().text()).toBe(__('Open')); + }); }); - it('renders issuable status icon and text', () => { - createComponent(); - const status = findBadge(); - const statusIcon = status.findComponent(GlIcon); + describe('confidential badge', () => { + it('renders when issuable is confidential', () => { + createComponent({ confidential: true }); + + expect(findConfidentialityBadge().props()).toEqual({ + issuableType: 'issue', + workspaceType: 'project', + }); + }); + + it('does not render when issuable is not confidential', () => { + createComponent({ confidential: false }); - expect(status.text()).toBe('Open'); - expect(statusIcon.props('name')).toBe(mockIssuableShowProps.statusIcon); - expect(statusIcon.attributes('class')).toBe(mockIssuableShowProps.statusIconClass); + expect(findConfidentialityBadge().exists()).toBe(false); + }); }); - it('renders blocked icon when issuable is blocked', () => { - createComponent({ blocked: true }); + describe('blocked icon', () => { + it('renders when issuable is blocked', () => { + createComponent({ blocked: true }); + + expect(findBlockedIcon().props('ariaLabel')).toBe('Blocked'); + }); - expect(findBlockedIcon().props('name')).toBe('lock'); + it('has tooltip', () => { + createComponent({ blocked: true }); + + expect(findComponentTooltip(findBlockedIcon())).toBeDefined(); + expect(findBlockedIcon().attributes('title')).toBe( + 'This issue is locked. Only project members can comment.', + ); + }); + + it('does not render when issuable is not blocked', () => { + createComponent({ blocked: false }); + + expect(findBlockedIcon()).toBeUndefined(); + }); }); - it('renders confidential icon when issuable is confidential', () => { - createComponent({ confidential: true }); + describe('hidden icon', () => { + it('renders when issuable is hidden', () => { + createComponent({ isHidden: true }); - expect(wrapper.findComponent(ConfidentialityBadge).props()).toEqual({ - issuableType: 'issue', - workspaceType: 'project', + expect(findHiddenIcon().props('ariaLabel')).toBe('Hidden'); + }); + + it('has tooltip', () => { + createComponent({ isHidden: true }); + + expect(findComponentTooltip(findHiddenIcon())).toBeDefined(); + expect(findHiddenIcon().attributes('title')).toBe( + 'This issue is hidden because its author has been banned', + ); + }); + + it('does not render when issuable is not hidden', () => { + createComponent({ isHidden: false }); + + expect(findHiddenIcon()).toBeUndefined(); + }); + }); + + describe('work item type icon', () => { + it('renders when showWorkItemTypeIcon=true and work item type exists', () => { + createComponent({ showWorkItemTypeIcon: true, issuableType: 'issue' }); + + expect(findWorkItemTypeIcon().props()).toMatchObject({ + showText: true, + workItemType: 'ISSUE', + }); + }); + + it('does not render when showWorkItemTypeIcon=false', () => { + createComponent({ showWorkItemTypeIcon: false }); + + expect(findWorkItemTypeIcon().exists()).toBe(false); + }); + }); + + describe('timeago tooltip', () => { + it('renders', () => { + createComponent(); + + expect(findTimeAgoTooltip().props('time')).toBe('2020-06-29T13:52:56Z'); }); }); describe('author', () => { - it('renders avatar', () => { + it('renders link', () => { createComponent(); - const { username, name, webUrl, avatarUrl } = mockIssuable.author; - const avatar = findGlAvatarLink(); - expect(avatar.attributes()).toMatchObject({ + expect(findAuthorLink().text()).toContain('Administrator'); + expect(findAuthorLink().attributes()).toMatchObject({ + href: 'http://0.0.0.0:3000/root', 'data-user-id': '1', - 'data-username': username, - 'data-name': name, - href: webUrl, - }); - expect(avatar.findComponent(GlAvatarLabeled).attributes()).toMatchObject({ - size: '24', - src: avatarUrl, - label: name, }); - expect(avatar.findComponent(GlAvatarLabeled).findComponent(GlIcon).exists()).toBe(false); + expect(findAuthorLink().classes()).toContain('js-user-link'); }); describe('when author exists outside of GitLab', () => { - it("renders 'external-link' icon in avatar label", () => { - createComponent( - { - author: { - ...mockIssuable.author, - webUrl: 'https://jira.com/test-user/author.jpg', - }, - }, - { - stubs: { GlAvatarLabeled }, - }, - ); - const icon = wrapper.findComponent(GlAvatarLabeled).findComponent(GlIcon); - - expect(icon.props('name')).toBe('external-link'); + it('renders external link icon', () => { + createComponent({ author: { webUrl: 'https://example.com/test-user' } }); + + expect(findExternalLinkIcon().props('ariaLabel')).toBe('external link'); }); }); }); + describe('first contribution icon', () => { + it('renders when isFirstContribution=true', () => { + createComponent({ isFirstContribution: true }); + + expect(findFirstContributionIcon().props('ariaLabel')).toBe('1st contribution!'); + }); + + it('has tooltip', () => { + createComponent({ isFirstContribution: true }); + + expect(findComponentTooltip(findFirstContributionIcon())).toBeDefined(); + expect(findFirstContributionIcon().attributes('title')).toBe('1st contribution!'); + }); + + it('does not render when isFirstContribution=false', () => { + createComponent({ isFirstContribution: false }); + + expect(findFirstContributionIcon()).toBeUndefined(); + }); + }); + describe('task status', () => { it('renders task status text when `taskCompletionStatus` prop is defined', () => { createComponent(); - expect(findTaskStatus().text()).toContain('0 of 5 checklist items completed'); + expect(wrapper.text()).toContain('0 of 5 checklist items completed'); }); it('does not render task status text when tasks count is 0', () => { createComponent({ taskCompletionStatus: { count: 0, completedCount: 0 } }); - expect(findTaskStatus().exists()).toBe(false); + expect(wrapper.text()).not.toContain('checklist item'); }); }); - it('renders header actions', () => { - createComponent(); - const headerActions = findHeaderActions(); - - expect(headerActions.find('button.js-close').exists()).toBe(true); - expect(headerActions.find('a.js-new').exists()).toBe(true); - }); - describe('sidebar toggle button', () => { beforeEach(() => { setHTMLFixture('<button class="js-toggle-right-sidebar-button">Collapse sidebar</button>'); createComponent(); }); + afterEach(() => { + resetHTMLFixture(); + }); + it('renders', () => { - expect(findButton().props('icon')).toBe('chevron-double-lg-left'); + expect(findToggleButton().props('icon')).toBe('chevron-double-lg-left'); + expect(findToggleButton().attributes('aria-label')).toBe('Expand sidebar'); }); describe('when clicked', () => { it('emits a "toggle" event', () => { - findButton().vm.$emit('click'); + findToggleButton().vm.$emit('click'); expect(wrapper.emitted('toggle')).toEqual([[]]); }); @@ -157,10 +272,18 @@ describe('IssuableHeader component', () => { .spyOn(toggleSidebarButton, 'dispatchEvent') .mockImplementation(jest.fn); - findButton().vm.$emit('click'); + findToggleButton().vm.$emit('click'); expect(dispatchEvent).toHaveBeenCalledWith(expect.objectContaining({ type: 'click' })); }); }); }); + + describe('header actions', () => { + it('renders slot', () => { + createComponent(); + + expect(wrapper.text()).toContain('Header actions slot'); + }); + }); }); diff --git a/spec/helpers/issuables_helper_spec.rb b/spec/helpers/issuables_helper_spec.rb index 81147275745..7b5537c54cc 100644 --- a/spec/helpers/issuables_helper_spec.rb +++ b/spec/helpers/issuables_helper_spec.rb @@ -524,6 +524,64 @@ RSpec.describe IssuablesHelper, feature_category: :team_planning do end end end + + describe '#duplicatedToIssueUrl' do + let(:issue) { create(:issue, author: user) } + + before do + assign(:project, issue.project) + end + + context 'when issue is duplicated' do + before do + allow(issue).to receive(:duplicated?).and_return(true) + allow(issue).to receive(:duplicated_to).and_return(issue) + end + + it 'returns url' do + expect(helper.issuable_initial_data(issue)[:duplicatedToIssueUrl]).to be_truthy + end + end + + context 'when issue is not duplicated' do + before do + allow(issue).to receive(:duplicated?).and_return(false) + end + + it 'returns nil' do + expect(helper.issuable_initial_data(issue)[:duplicatedToIssueUrl]).to be_nil + end + end + end + + describe '#movedToIssueUrl' do + let(:issue) { create(:issue, author: user) } + + before do + assign(:project, issue.project) + end + + context 'when issue is moved' do + before do + allow(issue).to receive(:moved?).and_return(true) + allow(issue).to receive(:moved_to).and_return(issue) + end + + it 'returns url' do + expect(helper.issuable_initial_data(issue)[:movedToIssueUrl]).to be_truthy + end + end + + context 'when issue is not moved' do + before do + allow(issue).to receive(:moved?).and_return(false) + end + + it 'returns nil' do + expect(helper.issuable_initial_data(issue)[:movedToIssueUrl]).to be_nil + end + end + end end describe '#assignee_sidebar_data' do diff --git a/spec/lib/gitlab/cleanup/orphan_job_artifact_files_batch_spec.rb b/spec/lib/gitlab/cleanup/orphan_job_artifact_files_batch_spec.rb index 9fa24e5637f..56745759c5a 100644 --- a/spec/lib/gitlab/cleanup/orphan_job_artifact_files_batch_spec.rb +++ b/spec/lib/gitlab/cleanup/orphan_job_artifact_files_batch_spec.rb @@ -23,6 +23,8 @@ RSpec.describe Gitlab::Cleanup::OrphanJobArtifactFilesBatch do expect(batch.artifact_files.count).to eq(2) expect(batch.lost_and_found.count).to eq(1) expect(batch.lost_and_found.first.artifact_id).to eq(orphan_artifact.id) + expect(File.exist?(job_artifact.file.path)).to be_truthy + expect(File.exist?(orphan_artifact.file.path)).to be_falsey end end diff --git a/spec/lib/gitlab/metrics/dashboard/defaults_spec.rb b/spec/lib/gitlab/metrics/dashboard/defaults_spec.rb deleted file mode 100644 index b8556829a59..00000000000 --- a/spec/lib/gitlab/metrics/dashboard/defaults_spec.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -require 'fast_spec_helper' - -RSpec.describe Gitlab::Metrics::Dashboard::Defaults do - it { is_expected.to be_const_defined(:DEFAULT_PANEL_TYPE) } -end diff --git a/spec/views/projects/issues/show.html.haml_spec.rb b/spec/views/projects/issues/show.html.haml_spec.rb index 3f1496a24ce..e316ff58b95 100644 --- a/spec/views/projects/issues/show.html.haml_spec.rb +++ b/spec/views/projects/issues/show.html.haml_spec.rb @@ -5,129 +5,6 @@ require 'spec_helper' RSpec.describe 'projects/issues/show' do include_context 'project show action' - context 'when the issue is closed' do - before do - allow(issue).to receive(:closed?).and_return(true) - allow(view).to receive(:current_user).and_return(user) - end - - context 'when the issue was moved' do - let(:new_issue) { create(:issue, project: project, author: user) } - - before do - issue.moved_to = new_issue - end - - context 'when user can see the moved issue' do - before do - project.add_developer(user) - end - - it 'shows "Closed (moved)" if an issue has been moved and closed' do - render - - expect(rendered).to have_selector('.issuable-status-badge-closed:not(.hidden)', text: 'Closed (moved)') - end - - it 'shows "Closed (moved)" if an issue has been moved and discussion is locked' do - allow(issue).to receive(:discussion_locked).and_return(true) - render - - expect(rendered).to have_selector('.issuable-status-badge-closed:not(.hidden)', text: 'Closed (moved)') - end - - it 'links "moved" to the new issue the original issue was moved to' do - render - - expect(rendered).to have_selector("a[href=\"#{issue_path(new_issue)}\"]", text: 'moved') - end - - it 'does not show "closed (moved)" if an issue has been moved and reopened (not closed)' do - allow(issue).to receive(:closed?).and_return(false) - - render - - expect(rendered).not_to have_selector('.issuable-status-badge-closed:not(.hidden)', text: 'Closed (moved)') - end - end - - context 'when user cannot see moved issue' do - it 'does not show moved issue link' do - render - - expect(rendered).not_to have_selector("a[href=\"#{issue_path(new_issue)}\"]", text: 'moved') - end - end - end - - context 'when the issue was duplicated' do - let(:new_issue) { create(:issue, project: project, author: user) } - - before do - issue.duplicated_to = new_issue - end - - context 'when user can see the duplicated issue' do - before do - project.add_developer(user) - end - - it 'shows "Closed (duplicated)" if an issue has been duplicated' do - render - - expect(rendered).to have_selector('.issuable-status-badge-closed:not(.hidden)', text: 'Closed (duplicated)') - end - - it 'links "duplicated" to the new issue the original issue was duplicated to' do - render - - expect(rendered).to have_selector("a[href=\"#{issue_path(new_issue)}\"]", text: 'duplicated') - end - end - - context 'when user cannot see duplicated issue' do - it 'does not show duplicated issue link' do - render - - expect(rendered).not_to have_selector("a[href=\"#{issue_path(new_issue)}\"]", text: 'duplicated') - end - end - end - - it 'shows "Closed" if an issue has not been moved or duplicated' do - render - - expect(rendered).to have_selector('.issuable-status-badge-closed:not(.hidden)', text: 'Closed') - end - - it 'shows "Closed" if discussion is locked' do - allow(issue).to receive(:discussion_locked).and_return(true) - render - - expect(rendered).to have_selector('.issuable-status-badge-closed:not(.hidden)', text: 'Closed') - end - end - - context 'when the issue is open' do - before do - allow(issue).to receive(:closed?).and_return(false) - allow(issue).to receive(:discussion_locked).and_return(false) - end - - it 'shows "Open" if an issue has been moved' do - render - - expect(rendered).to have_selector('.issuable-status-badge-open:not(.hidden)', text: 'Open') - end - - it 'shows "Open" if discussion is locked' do - allow(issue).to receive(:discussion_locked).and_return(true) - render - - expect(rendered).to have_selector('.issuable-status-badge-open:not(.hidden)', text: 'Open') - end - end - context 'when the issue is related to a sentry error' do it 'renders a stack trace' do sentry_issue = double(:sentry_issue, sentry_issue_identifier: '1066622') |