diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-06-19 15:09:51 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-06-19 15:09:51 +0300 |
commit | eef2437c0a359ec3437d31d1b1ea959e54c71458 (patch) | |
tree | 92ea24b2d26a057881171827e777604780838633 /app | |
parent | 021a832cb8e90a0305452381ccf4ce15dc5e0ebd (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
14 files changed, 176 insertions, 12 deletions
diff --git a/app/assets/javascripts/ci/runner/components/cells/runner_status_cell.vue b/app/assets/javascripts/ci/runner/components/cells/runner_status_cell.vue index 149c69e5307..e287e4e17d1 100644 --- a/app/assets/javascripts/ci/runner/components/cells/runner_status_cell.vue +++ b/app/assets/javascripts/ci/runner/components/cells/runner_status_cell.vue @@ -22,6 +22,12 @@ export default { paused() { return this.runner.paused; }, + contactedAt() { + return this.runner.contactedAt; + }, + status() { + return this.runner.status; + }, }, }; </script> @@ -29,7 +35,8 @@ export default { <template> <div> <runner-status-badge - :runner="runner" + :contacted-at="contactedAt" + :status="status" class="gl-display-inline-block gl-max-w-full gl-text-truncate" /> <runner-paused-badge diff --git a/app/assets/javascripts/ci/runner/components/runner_header.vue b/app/assets/javascripts/ci/runner/components/runner_header.vue index 61d8d264601..f46e894bf2e 100644 --- a/app/assets/javascripts/ci/runner/components/runner_header.vue +++ b/app/assets/javascripts/ci/runner/components/runner_header.vue @@ -38,7 +38,7 @@ export default { <div> <h1 class="gl-font-size-h-display gl-my-0">{{ name }}</h1> <div class="gl-display-flex gl-align-items-flex-start gl-gap-3 gl-flex-wrap gl-mt-3"> - <runner-status-badge :runner="runner" /> + <runner-status-badge :contacted-at="runner.contactedAt" :status="runner.status" /> <runner-type-badge :type="runner.runnerType" /> <span v-if="runner.createdAt"> <gl-sprintf :message="__('%{locked} created %{timeago}')"> diff --git a/app/assets/javascripts/ci/runner/components/runner_managers_table.vue b/app/assets/javascripts/ci/runner/components/runner_managers_table.vue index d1d5d0432f2..10790c398b0 100644 --- a/app/assets/javascripts/ci/runner/components/runner_managers_table.vue +++ b/app/assets/javascripts/ci/runner/components/runner_managers_table.vue @@ -5,6 +5,7 @@ import { s__ } from '~/locale'; import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue'; import { tableField } from '../utils'; import { I18N_STATUS_NEVER_CONTACTED } from '../constants'; +import RunnerStatusBadge from './runner_status_badge.vue'; export default { name: 'RunnerManagersTable', @@ -13,6 +14,7 @@ export default { TimeAgo, HelpPopover, GlIntersperse, + RunnerStatusBadge, RunnerUpgradeStatusIcon: () => import('ee_component/ci/runner/components/runner_upgrade_status_icon.vue'), }, @@ -25,6 +27,7 @@ export default { }, fields: [ tableField({ key: 'systemId', label: s__('Runners|System ID') }), + tableField({ key: 'status', label: s__('Runners|Status') }), tableField({ key: 'version', label: s__('Runners|Version') }), tableField({ key: 'ipAddress', label: s__('Runners|IP Address') }), tableField({ key: 'executorName', label: s__('Runners|Executor') }), @@ -48,6 +51,9 @@ export default { {{ s__('Runners|The unique ID for each runner that uses this configuration.') }} </help-popover> </template> + <template #cell(status)="{ item = {} }"> + <runner-status-badge :contacted-at="item.contactedAt" :status="item.status" /> + </template> <template #cell(version)="{ item = {} }"> {{ item.version }} <template v-if="item.revision">({{ item.revision }})</template> diff --git a/app/assets/javascripts/ci/runner/components/runner_status_badge.vue b/app/assets/javascripts/ci/runner/components/runner_status_badge.vue index d084408781e..c2c52bd756a 100644 --- a/app/assets/javascripts/ci/runner/components/runner_status_badge.vue +++ b/app/assets/javascripts/ci/runner/components/runner_status_badge.vue @@ -26,21 +26,27 @@ export default { GlTooltip: GlTooltipDirective, }, props: { - runner: { - required: true, - type: Object, + contactedAt: { + type: String, + required: false, + default: null, + }, + status: { + type: String, + required: false, + default: null, }, }, computed: { contactedAtTimeAgo() { - if (this.runner.contactedAt) { - return getTimeago().format(this.runner.contactedAt); + if (this.contactedAt) { + return getTimeago().format(this.contactedAt); } // Prevent "just now" from being rendered, in case data is missing. return __('never'); }, badge() { - switch (this.runner?.status) { + switch (this.status) { case STATUS_ONLINE: return { icon: 'status-active', @@ -68,7 +74,7 @@ export default { variant: 'warning', label: I18N_STATUS_STALE, // runner may have contacted (or not) and be stale: consider both cases. - tooltip: this.runner.contactedAt + tooltip: this.contactedAt ? this.timeAgoTooltip(I18N_STALE_TIMEAGO_TOOLTIP) : I18N_STALE_NEVER_CONTACTED_TOOLTIP, }; diff --git a/app/assets/javascripts/ci/runner/graphql/show/runner_manager_shared.fragment.graphql b/app/assets/javascripts/ci/runner/graphql/show/runner_manager_shared.fragment.graphql index 008ec91b5d9..ead005d1252 100644 --- a/app/assets/javascripts/ci/runner/graphql/show/runner_manager_shared.fragment.graphql +++ b/app/assets/javascripts/ci/runner/graphql/show/runner_manager_shared.fragment.graphql @@ -1,6 +1,7 @@ fragment CiRunnerManagerShared on CiRunnerManager { id systemId + status version revision executorName diff --git a/app/assets/javascripts/work_items/components/notes/work_item_note.vue b/app/assets/javascripts/work_items/components/notes/work_item_note.vue index f3b21dacfe9..7ad424868c6 100644 --- a/app/assets/javascripts/work_items/components/notes/work_item_note.vue +++ b/app/assets/javascripts/work_items/components/notes/work_item_note.vue @@ -154,6 +154,9 @@ export default { isWorkItemAuthor() { return getIdFromGraphQLId(this.workItem?.author?.id) === getIdFromGraphQLId(this.author.id); }, + projectName() { + return this.workItem?.project?.name; + }, }, apollo: { workItem: { @@ -329,6 +332,9 @@ export default { :can-report-abuse="!isCurrentUserAuthorOfNote" :is-work-item-author="isWorkItemAuthor" :work-item-type="workItemType" + :is-author-contributor="note.authorIsContributor" + :max-access-level-of-author="note.maxAccessLevelOfAuthor" + :project-name="projectName" @startReplying="showReplyForm" @startEditing="startEditing" @error="($event) => $emit('error', $event)" diff --git a/app/assets/javascripts/work_items/components/notes/work_item_note_actions.vue b/app/assets/javascripts/work_items/components/notes/work_item_note_actions.vue index a9dc800ae73..b32a8c78c93 100644 --- a/app/assets/javascripts/work_items/components/notes/work_item_note_actions.vue +++ b/app/assets/javascripts/work_items/components/notes/work_item_note_actions.vue @@ -84,6 +84,21 @@ export default { required: false, default: false, }, + isAuthorContributor: { + type: Boolean, + required: false, + default: false, + }, + maxAccessLevelOfAuthor: { + type: String, + required: false, + default: '', + }, + projectName: { + type: String, + required: false, + default: '', + }, }, computed: { assignUserActionText() { @@ -96,6 +111,17 @@ export default { workItemType: this.workItemType.toLowerCase(), }); }, + displayMemberBadgeText() { + return sprintf(__('This user has the %{access} role in the %{name} project.'), { + access: this.maxAccessLevelOfAuthor.toLowerCase(), + name: this.projectName, + }); + }, + displayContributorBadgeText() { + return sprintf(__('This user has previously committed to the %{name} project.'), { + name: this.projectName, + }); + }, }, methods: { @@ -140,6 +166,24 @@ export default { > {{ __('Author') }} </user-access-role-badge> + <user-access-role-badge + v-if="maxAccessLevelOfAuthor" + v-gl-tooltip + class="gl-mr-3 gl-display-none gl-sm-display-block" + :title="displayMemberBadgeText" + data-testid="max-access-level-badge" + > + {{ maxAccessLevelOfAuthor }} + </user-access-role-badge> + <user-access-role-badge + v-else-if="isAuthorContributor" + v-gl-tooltip + class="gl-mr-3 gl-display-none gl-sm-display-block" + :title="displayContributorBadgeText" + data-testid="contributor-badge" + > + {{ __('Contributor') }} + </user-access-role-badge> <emoji-picker v-if="showAwardEmoji && glFeatures.workItemsMvc2" toggle-class="note-action-button note-emoji-button btn-icon btn-default-tertiary" diff --git a/app/assets/javascripts/work_items/components/work_item_actions.vue b/app/assets/javascripts/work_items/components/work_item_actions.vue index 9c6ce7ab702..76a04bede61 100644 --- a/app/assets/javascripts/work_items/components/work_item_actions.vue +++ b/app/assets/javascripts/work_items/components/work_item_actions.vue @@ -8,11 +8,14 @@ import { GlModalDirective, GlToggle, } from '@gitlab/ui'; + import * as Sentry from '@sentry/browser'; -import { s__ } from '~/locale'; + +import { __, s__ } from '~/locale'; import Tracking from '~/tracking'; import toast from '~/vue_shared/plugins/global_toast'; import { isLoggedIn } from '~/lib/utils/common_utils'; + import { sprintfWorkItem, I18N_WORK_ITEM_DELETE, @@ -22,10 +25,15 @@ import { TEST_ID_NOTIFICATIONS_TOGGLE_FORM, TEST_ID_DELETE_ACTION, TEST_ID_PROMOTE_ACTION, + TEST_ID_COPY_CREATE_NOTE_EMAIL_ACTION, + TEST_ID_COPY_REFERENCE_ACTION, WIDGET_TYPE_NOTIFICATIONS, I18N_WORK_ITEM_ERROR_CONVERTING, WORK_ITEM_TYPE_VALUE_KEY_RESULT, WORK_ITEM_TYPE_VALUE_OBJECTIVE, + I18N_WORK_ITEM_COPY_CREATE_NOTE_EMAIL, + I18N_WORK_ITEM_ERROR_COPY_REFERENCE, + I18N_WORK_ITEM_ERROR_COPY_EMAIL, } from '../constants'; import updateWorkItemNotificationsMutation from '../graphql/update_work_item_notifications.mutation.graphql'; import convertWorkItemMutation from '../graphql/work_item_convert.mutation.graphql'; @@ -38,6 +46,9 @@ export default { notifications: s__('WorkItem|Notifications'), notificationOn: s__('WorkItem|Notifications turned on.'), notificationOff: s__('WorkItem|Notifications turned off.'), + copyReference: __('Copy reference'), + referenceCopied: __('Reference copied'), + emailAddressCopied: __('Email address copied'), }, components: { GlDropdown, @@ -55,6 +66,8 @@ export default { notificationsToggleTestId: TEST_ID_NOTIFICATIONS_TOGGLE_ACTION, notificationsToggleFormTestId: TEST_ID_NOTIFICATIONS_TOGGLE_FORM, confidentialityTestId: TEST_ID_CONFIDENTIALITY_TOGGLE_ACTION, + copyReferenceTestId: TEST_ID_COPY_REFERENCE_ACTION, + copyCreateNoteEmailTestId: TEST_ID_COPY_CREATE_NOTE_EMAIL_ACTION, deleteActionTestId: TEST_ID_DELETE_ACTION, promoteActionTestId: TEST_ID_PROMOTE_ACTION, inject: ['fullPath'], @@ -99,6 +112,21 @@ export default { required: false, default: false, }, + workItemReference: { + type: String, + required: false, + default: null, + }, + workItemCreateNoteEmail: { + type: String, + required: false, + default: null, + }, + isModal: { + type: Boolean, + required: false, + default: false, + }, }, apollo: { workItemTypes: { @@ -122,6 +150,15 @@ export default { deleteWorkItem: sprintfWorkItem(I18N_WORK_ITEM_DELETE, this.workItemType), areYouSureDelete: sprintfWorkItem(I18N_WORK_ITEM_ARE_YOU_SURE_DELETE, this.workItemType), convertError: sprintfWorkItem(I18N_WORK_ITEM_ERROR_CONVERTING, this.workItemType), + copyCreateNoteEmail: sprintfWorkItem( + I18N_WORK_ITEM_COPY_CREATE_NOTE_EMAIL, + this.workItemType, + ), + copyReferenceError: sprintfWorkItem(I18N_WORK_ITEM_ERROR_COPY_REFERENCE, this.workItemType), + copyCreateNoteEmailError: sprintfWorkItem( + I18N_WORK_ITEM_ERROR_COPY_EMAIL, + this.workItemType, + ), }; }, canPromoteToObjective() { @@ -142,6 +179,12 @@ export default { }, }, methods: { + copyToClipboard(text, message) { + if (this.isModal) { + navigator.clipboard.writeText(text); + } + toast(message); + }, handleToggleWorkItemConfidentiality() { this.track('click_toggle_work_item_confidentiality'); this.$emit('toggleWorkItemConfidentiality', !this.isConfidential); @@ -287,6 +330,22 @@ export default { : $options.i18n.enableTaskConfidentiality }}</gl-dropdown-item > + </template> + <gl-dropdown-item + ref="workItemReference" + :data-testid="$options.copyReferenceTestId" + :data-clipboard-text="workItemReference" + @click="copyToClipboard(workItemReference, $options.i18n.referenceCopied)" + >{{ $options.i18n.copyReference }}</gl-dropdown-item + > + <template v-if="$options.isLoggedIn && workItemCreateNoteEmail"> + <gl-dropdown-item + ref="workItemCreateNoteEmail" + :data-testid="$options.copyCreateNoteEmailTestId" + :data-clipboard-text="workItemCreateNoteEmail" + @click="copyToClipboard(workItemCreateNoteEmail, $options.i18n.emailAddressCopied)" + >{{ i18n.copyCreateNoteEmail }}</gl-dropdown-item + > <gl-dropdown-divider v-if="canDelete" /> </template> <gl-dropdown-item diff --git a/app/assets/javascripts/work_items/components/work_item_detail.vue b/app/assets/javascripts/work_items/components/work_item_detail.vue index ad759bc69e9..1ac40fe7dcb 100644 --- a/app/assets/javascripts/work_items/components/work_item_detail.vue +++ b/app/assets/javascripts/work_items/components/work_item_detail.vue @@ -515,7 +515,6 @@ export default { @error="updateError = $event" /> <work-item-actions - v-if="canUpdate || canDelete" :work-item-id="workItem.id" :subscribed-to-notifications="workItemNotificationsSubscribed" :work-item-type="workItemType" @@ -524,6 +523,9 @@ export default { :can-update="canUpdate" :is-confidential="workItem.confidential" :is-parent-confidential="parentWorkItemConfidentiality" + :work-item-reference="workItem.reference" + :work-item-create-note-email="workItem.createNoteEmail" + :is-modal="isModal" @deleteWorkItem="$emit('deleteWorkItem', { workItemType, workItemId: workItem.id })" @toggleWorkItemConfidentiality="toggleConfidentiality" @error="updateError = $event" diff --git a/app/assets/javascripts/work_items/constants.js b/app/assets/javascripts/work_items/constants.js index 6710c762c2e..f3beaebf403 100644 --- a/app/assets/javascripts/work_items/constants.js +++ b/app/assets/javascripts/work_items/constants.js @@ -92,6 +92,17 @@ export const I18N_WORK_ITEM_CONFIDENTIALITY_CHECKBOX_TOOLTIP = s__( 'WorkItem|A non-confidential %{workItemType} cannot be assigned to a confidential parent %{parentWorkItemType}.', ); +export const I18N_WORK_ITEM_ERROR_COPY_REFERENCE = s__( + 'WorkItem|Something went wrong while copying the %{workItemType} reference. Please try again.', +); +export const I18N_WORK_ITEM_ERROR_COPY_EMAIL = s__( + 'WorkItem|Something went wrong while copying the %{workItemType} email address. Please try again.', +); + +export const I18N_WORK_ITEM_COPY_CREATE_NOTE_EMAIL = s__( + 'WorkItem|Copy %{workItemType} email address', +); + export const sprintfWorkItem = (msg, workItemTypeArg, parentWorkItemType = '') => { const workItemType = workItemTypeArg || s__('WorkItem|Work item'); return capitalizeFirstCharacter( @@ -217,6 +228,8 @@ export const TEST_ID_NOTIFICATIONS_TOGGLE_ACTION = 'notifications-toggle-action' export const TEST_ID_NOTIFICATIONS_TOGGLE_FORM = 'notifications-toggle-form'; export const TEST_ID_DELETE_ACTION = 'delete-action'; export const TEST_ID_PROMOTE_ACTION = 'promote-action'; +export const TEST_ID_COPY_REFERENCE_ACTION = 'copy-reference-action'; +export const TEST_ID_COPY_CREATE_NOTE_EMAIL_ACTION = 'copy-create-note-email-action'; export const ADD = 'ADD'; export const MARK_AS_DONE = 'MARK_AS_DONE'; diff --git a/app/assets/javascripts/work_items/graphql/notes/work_item_note.fragment.graphql b/app/assets/javascripts/work_items/graphql/notes/work_item_note.fragment.graphql index 973c6fde474..c8b7d379074 100644 --- a/app/assets/javascripts/work_items/graphql/notes/work_item_note.fragment.graphql +++ b/app/assets/javascripts/work_items/graphql/notes/work_item_note.fragment.graphql @@ -10,6 +10,8 @@ fragment WorkItemNote on Note { createdAt lastEditedAt url + authorIsContributor + maxAccessLevelOfAuthor lastEditedBy { ...User webPath diff --git a/app/assets/javascripts/work_items/graphql/work_item.fragment.graphql b/app/assets/javascripts/work_items/graphql/work_item.fragment.graphql index 02243baf28d..20a8d127e7d 100644 --- a/app/assets/javascripts/work_items/graphql/work_item.fragment.graphql +++ b/app/assets/javascripts/work_items/graphql/work_item.fragment.graphql @@ -11,10 +11,13 @@ fragment WorkItem on WorkItem { createdAt updatedAt closedAt + reference(full: true) + createNoteEmail project { id fullPath archived + name } author { ...Author diff --git a/app/assets/stylesheets/fonts.scss b/app/assets/stylesheets/fonts.scss index 369f7072aa4..bc49d17fcbb 100644 --- a/app/assets/stylesheets/fonts.scss +++ b/app/assets/stylesheets/fonts.scss @@ -21,6 +21,7 @@ Usage: */ @font-face { font-family: 'GitLab Mono'; + font-weight: 100 900; font-display: optional; font-style: normal; src: font-url('gitlab-mono/GitLabMono.woff2') format('woff2'); @@ -28,6 +29,7 @@ Usage: @font-face { font-family: 'GitLab Mono'; + font-weight: 100 900; font-display: optional; font-style: italic; src: font-url('gitlab-mono/GitLabMono-Italic.woff2') format('woff2'); diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 27a6fc84209..bb1bfe8c889 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -1258,7 +1258,7 @@ module Ci def id_tokens_variables Gitlab::Ci::Variables::Collection.new.tap do |variables| id_tokens.each do |var_name, token_data| - token = Gitlab::Ci::JwtV2.for_build(self, aud: token_data['aud']) + token = Gitlab::Ci::JwtV2.for_build(self, aud: expanded_id_token_aud(token_data['aud'])) variables.append(key: var_name, value: token, public: false, masked: true) end @@ -1267,6 +1267,19 @@ module Ci end end + def expanded_id_token_aud(aud) + return unless aud + + strong_memoize_with(:expanded_id_token_aud, aud) do + # `aud` can be a string or an array of strings. + if aud.is_a?(Array) + aud.map { |x| ExpandVariables.expand(x, -> { scoped_variables.sort_and_expand_all }) } + else + ExpandVariables.expand(aud, -> { scoped_variables.sort_and_expand_all }) + end + end + end + def cache_for_online_runners(&block) Rails.cache.fetch( ['has-online-runners', id], |