diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-02-16 15:09:03 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-02-16 15:09:03 +0300 |
commit | 8215fc964a189ae5c876a10f2e7d61933a725e24 (patch) | |
tree | acdd0abd951ca0f392c7617821ab347d77c1a623 /app | |
parent | a57cec4bb89b61d210d4e413571b1d85d76179f6 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
17 files changed, 210 insertions, 84 deletions
diff --git a/app/assets/javascripts/notes/components/note_header.vue b/app/assets/javascripts/notes/components/note_header.vue index 101421523d0..6932af61c69 100644 --- a/app/assets/javascripts/notes/components/note_header.vue +++ b/app/assets/javascripts/notes/components/note_header.vue @@ -1,9 +1,9 @@ <script> /* eslint-disable vue/no-v-html */ -import { GlIcon, GlLoadingIcon, GlTooltipDirective, GlSprintf } from '@gitlab/ui'; +import { GlIcon, GlLoadingIcon, GlTooltipDirective } from '@gitlab/ui'; import { mapActions } from 'vuex'; -import { isUserBusy } from '~/set_status_modal/utils'; import timeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; +import UserNameWithStatus from '../../sidebar/components/assignees/user_name_with_status.vue'; export default { components: { @@ -12,7 +12,7 @@ export default { import('ee_component/vue_shared/components/user_avatar/badges/gitlab_team_member_badge.vue'), GlIcon, GlLoadingIcon, - GlSprintf, + UserNameWithStatus, }, directives: { GlTooltip: GlTooltipDirective, @@ -90,10 +90,6 @@ export default { } return false; }, - authorIsBusy() { - const { status } = this.author; - return status?.availability && isUserBusy(status.availability); - }, emojiElement() { return this.$refs?.authorStatus?.querySelector('gl-emoji'); }, @@ -133,6 +129,9 @@ export default { this.$refs.authorNameLink.dispatchEvent(new Event('mouseleave')); this.isUsernameLinkHovered = false; }, + userAvailability(selectedAuthor) { + return selectedAuthor?.availability || ''; + }, }, }; </script> @@ -158,12 +157,11 @@ export default { :data-username="author.username" > <slot name="note-header-info"></slot> - <span class="note-header-author-name gl-font-weight-bold"> - <gl-sprintf v-if="authorIsBusy" :message="s__('UserAvailability|%{author} (Busy)')"> - <template #author>{{ authorName }}</template> - </gl-sprintf> - <template v-else>{{ authorName }}</template> - </span> + <user-name-with-status + :name="authorName" + :availability="userAvailability(author)" + container-classes="note-header-author-name gl-font-weight-bold" + /> </a> <span v-if="authorStatus" diff --git a/app/assets/javascripts/pipelines/components/graph/linked_pipelines_column.vue b/app/assets/javascripts/pipelines/components/graph/linked_pipelines_column.vue index 30d64a58675..3ce77a1c60a 100644 --- a/app/assets/javascripts/pipelines/components/graph/linked_pipelines_column.vue +++ b/app/assets/javascripts/pipelines/components/graph/linked_pipelines_column.vue @@ -61,6 +61,9 @@ export default { isUpstream() { return this.type === UPSTREAM; }, + minWidth() { + return this.isUpstream ? 0 : this.$options.minWidth; + }, }, methods: { getPipelineData(pipeline) { @@ -132,8 +135,8 @@ export default { this.$emit('pipelineExpandToggle', jobName, expanded); }, - showDownstreamContainer(id) { - return !this.isUpstream && (this.isExpanded(id) || this.isLoadingPipeline(id)); + showContainer(id) { + return this.isExpanded(id) || this.isLoadingPipeline(id); }, }, }; @@ -164,8 +167,8 @@ export default { @pipelineExpandToggle="onPipelineExpandToggle" /> <div - v-if="showDownstreamContainer(pipeline.id)" - :style="{ minWidth: $options.minWidth }" + v-if="showContainer(pipeline.id)" + :style="{ minWidth }" class="gl-display-inline-block" > <pipeline-graph diff --git a/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue b/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue index 9438c117346..0a762563114 100644 --- a/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue +++ b/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue @@ -84,7 +84,7 @@ export default { }; </script> <template> - <main-graph-wrapper class="gl-px-6"> + <main-graph-wrapper class="gl-px-6" data-testid="stage-column"> <template #stages> <div data-testid="stage-column-title" diff --git a/app/assets/javascripts/set_status_modal/components/user_availability_status.vue b/app/assets/javascripts/set_status_modal/components/user_availability_status.vue deleted file mode 100644 index e86d94f86c6..00000000000 --- a/app/assets/javascripts/set_status_modal/components/user_availability_status.vue +++ /dev/null @@ -1,26 +0,0 @@ -<script> -import { AVAILABILITY_STATUS, isUserBusy, isValidAvailibility } from '../utils'; - -export default { - name: 'UserAvailabilityStatus', - props: { - availability: { - type: String, - required: true, - validator: isValidAvailibility, - }, - }, - computed: { - isBusy() { - const { availability = AVAILABILITY_STATUS.NOT_SET } = this; - return isUserBusy(availability); - }, - }, -}; -</script> - -<template> - <span v-if="isBusy" class="gl-font-weight-normal gl-text-gray-500">{{ - s__('UserAvailability|(Busy)') - }}</span> -</template> diff --git a/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue b/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue index 639cc8a72ef..bed264341a5 100644 --- a/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue +++ b/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue @@ -10,7 +10,7 @@ import { BV_SHOW_MODAL, BV_HIDE_MODAL } from '~/lib/utils/constants'; import { __, s__ } from '~/locale'; import { updateUserStatus } from '~/rest_api'; import EmojiMenuInModal from './emoji_menu_in_modal'; -import { isUserBusy, isValidAvailibility } from './utils'; +import { isUserBusy } from './utils'; const emojiMenuClass = 'js-modal-status-emoji-menu'; export const AVAILABILITY_STATUS = { @@ -46,7 +46,6 @@ export default { currentAvailability: { type: String, required: false, - validator: isValidAvailibility, default: '', }, canSetUserAvailability: { diff --git a/app/assets/javascripts/set_status_modal/utils.js b/app/assets/javascripts/set_status_modal/utils.js index faee4012ef4..e17d95adb25 100644 --- a/app/assets/javascripts/set_status_modal/utils.js +++ b/app/assets/javascripts/set_status_modal/utils.js @@ -3,7 +3,5 @@ export const AVAILABILITY_STATUS = { NOT_SET: 'not_set', }; -export const isUserBusy = (status) => status === AVAILABILITY_STATUS.BUSY; - -export const isValidAvailibility = (availability) => - availability.length ? Object.values(AVAILABILITY_STATUS).includes(availability) : true; +export const isUserBusy = (status = '') => + Boolean(status.length && status.toLowerCase().trim() === AVAILABILITY_STATUS.BUSY); diff --git a/app/assets/javascripts/sidebar/components/assignees/assignee_avatar_link.vue b/app/assets/javascripts/sidebar/components/assignees/assignee_avatar_link.vue index fbbe2e341a7..d0a65b48522 100644 --- a/app/assets/javascripts/sidebar/components/assignees/assignee_avatar_link.vue +++ b/app/assets/javascripts/sidebar/components/assignees/assignee_avatar_link.vue @@ -1,8 +1,46 @@ <script> import { GlTooltipDirective, GlLink } from '@gitlab/ui'; import { __, sprintf } from '~/locale'; +import { isUserBusy } from '~/set_status_modal/utils'; import AssigneeAvatar from './assignee_avatar.vue'; +const I18N = { + BUSY: __('Busy'), + CANNOT_MERGE: __('Cannot merge'), + LC_CANNOT_MERGE: __('cannot merge'), +}; + +const paranthesize = (str) => `(${str})`; + +const generateAssigneeTooltip = ({ + name, + availability, + cannotMerge = true, + tooltipHasName = false, +}) => { + if (!tooltipHasName) { + return cannotMerge ? I18N.CANNOT_MERGE : ''; + } + + const statusInformation = []; + if (availability && isUserBusy(availability)) { + statusInformation.push(I18N.BUSY); + } + + if (cannotMerge) { + statusInformation.push(I18N.LC_CANNOT_MERGE); + } + + if (tooltipHasName && statusInformation.length) { + return sprintf(__('%{name} %{status}'), { + name, + status: statusInformation.map(paranthesize).join(' '), + }); + } + + return name; +}; + export default { components: { AssigneeAvatar, @@ -37,15 +75,13 @@ export default { return this.issuableType === 'merge_request' && !this.user.can_merge; }, tooltipTitle() { - if (this.cannotMerge && this.tooltipHasName) { - return sprintf(__('%{userName} (cannot merge)'), { userName: this.user.name }); - } else if (this.cannotMerge) { - return __('Cannot merge'); - } else if (this.tooltipHasName) { - return this.user.name; - } - - return ''; + const { name = '', availability = '' } = this.user; + return generateAssigneeTooltip({ + name, + availability, + cannotMerge: this.cannotMerge, + tooltipHasName: this.tooltipHasName, + }); }, tooltipOption() { return { diff --git a/app/assets/javascripts/sidebar/components/assignees/assignees.vue b/app/assets/javascripts/sidebar/components/assignees/assignees.vue index 84e7110e2b2..5dc1ab90e00 100644 --- a/app/assets/javascripts/sidebar/components/assignees/assignees.vue +++ b/app/assets/javascripts/sidebar/components/assignees/assignees.vue @@ -36,7 +36,6 @@ export default { sortedAssigness() { const canMergeUsers = this.users.filter((user) => user.can_merge); const canNotMergeUsers = this.users.filter((user) => !user.can_merge); - return [...canMergeUsers, ...canNotMergeUsers]; }, }, diff --git a/app/assets/javascripts/sidebar/components/assignees/collapsed_assignee.vue b/app/assets/javascripts/sidebar/components/assignees/collapsed_assignee.vue index 2f654409561..af4227fa48d 100644 --- a/app/assets/javascripts/sidebar/components/assignees/collapsed_assignee.vue +++ b/app/assets/javascripts/sidebar/components/assignees/collapsed_assignee.vue @@ -1,9 +1,11 @@ <script> import AssigneeAvatar from './assignee_avatar.vue'; +import UserNameWithStatus from './user_name_with_status.vue'; export default { components: { AssigneeAvatar, + UserNameWithStatus, }, props: { user: { @@ -16,12 +18,20 @@ export default { default: 'issue', }, }, + computed: { + availability() { + return this.user?.availability || ''; + }, + }, }; </script> - <template> <button type="button" class="btn-link"> <assignee-avatar :user="user" :img-size="24" :issuable-type="issuableType" /> - <span class="author"> {{ user.name }} </span> + <user-name-with-status + :name="user.name" + :availability="availability" + container-classes="author" + /> </button> </template> diff --git a/app/assets/javascripts/sidebar/components/assignees/collapsed_assignee_list.vue b/app/assets/javascripts/sidebar/components/assignees/collapsed_assignee_list.vue index b713b0f960c..20667e695ce 100644 --- a/app/assets/javascripts/sidebar/components/assignees/collapsed_assignee_list.vue +++ b/app/assets/javascripts/sidebar/components/assignees/collapsed_assignee_list.vue @@ -1,11 +1,30 @@ <script> import { GlIcon, GlTooltipDirective } from '@gitlab/ui'; import { __, sprintf } from '~/locale'; +import { isUserBusy } from '~/set_status_modal/utils'; import CollapsedAssignee from './collapsed_assignee.vue'; const DEFAULT_MAX_COUNTER = 99; const DEFAULT_RENDER_COUNT = 5; +const generateCollapsedAssigneeTooltip = ({ renderUsers, allUsers, tooltipTitleMergeStatus }) => { + const names = renderUsers.map(({ name, availability }) => { + if (availability && isUserBusy(availability)) { + return sprintf(__('%{name} (Busy)'), { name }); + } + return name; + }); + + if (!allUsers.length) { + return __('Assignee(s)'); + } + if (allUsers.length > names.length) { + names.push(sprintf(__('+ %{amount} more'), { amount: allUsers.length - names.length })); + } + const text = names.join(', '); + return tooltipTitleMergeStatus ? `${text} (${tooltipTitleMergeStatus})` : text; +}; + export default { directives: { GlTooltip: GlTooltipDirective, @@ -74,19 +93,11 @@ export default { tooltipTitle() { const maxRender = Math.min(DEFAULT_RENDER_COUNT, this.users.length); const renderUsers = this.users.slice(0, maxRender); - const names = renderUsers.map((u) => u.name); - - if (!this.users.length) { - return __('Assignee(s)'); - } - - if (this.users.length > names.length) { - names.push(sprintf(__('+ %{amount} more'), { amount: this.users.length - names.length })); - } - - const text = names.join(', '); - - return this.tooltipTitleMergeStatus ? `${text} (${this.tooltipTitleMergeStatus})` : text; + return generateCollapsedAssigneeTooltip({ + renderUsers, + allUsers: this.users, + tooltipTitleMergeStatus: this.tooltipTitleMergeStatus, + }); }, tooltipOptions() { diff --git a/app/assets/javascripts/sidebar/components/assignees/uncollapsed_assignee_list.vue b/app/assets/javascripts/sidebar/components/assignees/uncollapsed_assignee_list.vue index 31d5d7c0077..36775648809 100644 --- a/app/assets/javascripts/sidebar/components/assignees/uncollapsed_assignee_list.vue +++ b/app/assets/javascripts/sidebar/components/assignees/uncollapsed_assignee_list.vue @@ -1,12 +1,14 @@ <script> import { __, sprintf } from '~/locale'; import AssigneeAvatarLink from './assignee_avatar_link.vue'; +import UserNameWithStatus from './user_name_with_status.vue'; const DEFAULT_RENDER_COUNT = 5; export default { components: { AssigneeAvatarLink, + UserNameWithStatus, }, props: { users: { @@ -55,6 +57,9 @@ export default { toggleShowLess() { this.showLess = !this.showLess; }, + userAvailability(u) { + return u?.availability || ''; + }, }, }; </script> @@ -68,7 +73,7 @@ export default { :issuable-type="issuableType" > <div class="ml-2 gl-line-height-normal"> - <div>{{ firstUser.name }}</div> + <user-name-with-status :name="firstUser.name" :availability="userAvailability(firstUser)" /> <div>{{ username }}</div> </div> </assignee-avatar-link> diff --git a/app/assets/javascripts/sidebar/components/assignees/user_name_with_status.vue b/app/assets/javascripts/sidebar/components/assignees/user_name_with_status.vue new file mode 100644 index 00000000000..41b3b6c9a45 --- /dev/null +++ b/app/assets/javascripts/sidebar/components/assignees/user_name_with_status.vue @@ -0,0 +1,40 @@ +<script> +import { GlSprintf } from '@gitlab/ui'; +import { isUserBusy } from '~/set_status_modal/utils'; + +export default { + name: 'UserNameWithStatus', + components: { + GlSprintf, + }, + props: { + name: { + type: String, + required: true, + }, + containerClasses: { + type: String, + required: false, + default: '', + }, + availability: { + type: String, + required: false, + default: '', + }, + }, + computed: { + isBusy() { + return isUserBusy(this.availability); + }, + }, +}; +</script> +<template> + <span :class="containerClasses"> + <gl-sprintf v-if="isBusy" :message="s__('UserAvailability|%{author} (Busy)')"> + <template #author>{{ name }}</template> + </gl-sprintf> + <template v-else>{{ name }}</template> + </span> +</template> diff --git a/app/assets/javascripts/vue_shared/components/user_popover/user_popover.vue b/app/assets/javascripts/vue_shared/components/user_popover/user_popover.vue index f121036540e..37bde089de8 100644 --- a/app/assets/javascripts/vue_shared/components/user_popover/user_popover.vue +++ b/app/assets/javascripts/vue_shared/components/user_popover/user_popover.vue @@ -6,7 +6,7 @@ import { GlDeprecatedSkeletonLoading as GlSkeletonLoading, GlIcon, } from '@gitlab/ui'; -import UserAvailabilityStatus from '~/set_status_modal/components/user_availability_status.vue'; +import UserNameWithStatus from '~/sidebar/components/assignees/user_name_with_status.vue'; import { glEmojiTag } from '../../../emoji'; import UserAvatarImage from '../user_avatar/user_avatar_image.vue'; @@ -26,7 +26,7 @@ export default { GlPopover, GlSkeletonLoading, UserAvatarImage, - UserAvailabilityStatus, + UserNameWithStatus, }, props: { target: { @@ -66,7 +66,7 @@ export default { ); }, availabilityStatus() { - return this.user?.status?.availability || null; + return this.user?.status?.availability || ''; }, }, }; @@ -93,11 +93,7 @@ export default { <template v-else> <div class="gl-mb-3"> <h5 class="gl-m-0"> - {{ user.name }} - <user-availability-status - v-if="availabilityStatus" - :availability="availabilityStatus" - /> + <user-name-with-status :name="user.name" :availability="availabilityStatus" /> </h5> <span class="gl-text-gray-500">@{{ user.username }}</span> </div> diff --git a/app/models/packages/composer/cache_file.rb b/app/models/packages/composer/cache_file.rb index 92659644b06..ecd7596b989 100644 --- a/app/models/packages/composer/cache_file.rb +++ b/app/models/packages/composer/cache_file.rb @@ -16,6 +16,8 @@ module Packages scope :with_namespace, ->(namespace) { where(namespace: namespace) } scope :with_sha, ->(sha) { where(file_sha256: sha) } + scope :expired, -> { where("delete_at <= ?", Time.current) } + scope :without_namespace, -> { where(namespace_id: nil) } end end end diff --git a/app/services/ci/pipeline_trigger_service.rb b/app/services/ci/pipeline_trigger_service.rb index a31f5e9056e..dbbaefb2b2f 100644 --- a/app/services/ci/pipeline_trigger_service.rb +++ b/app/services/ci/pipeline_trigger_service.rb @@ -17,6 +17,9 @@ module Ci private + PAYLOAD_VARIABLE_KEY = 'TRIGGER_PAYLOAD' + PAYLOAD_VARIABLE_HIDDEN_PARAMS = %i(token).freeze + def create_pipeline_from_trigger(trigger) # this check is to not leak the presence of the project if user cannot read it return unless trigger.project == project @@ -70,9 +73,23 @@ module Ci end def variables + if ::Feature.enabled?(:ci_trigger_payload_into_pipeline, project, default_enabled: :yaml) + param_variables + [payload_variable] + else + param_variables + end + end + + def param_variables params[:variables].to_h.map do |key, value| { key: key, value: value } end end + + def payload_variable + { key: PAYLOAD_VARIABLE_KEY, + value: params.except(*PAYLOAD_VARIABLE_HIDDEN_PARAMS).to_json, + variable_type: :file } + end end end diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml index 6d4e6de30d9..59ab0a4d05b 100644 --- a/app/workers/all_queues.yml +++ b/app/workers/all_queues.yml @@ -267,6 +267,14 @@ :weight: 1 :idempotent: :tags: [] +- :name: cronjob:packages_composer_cache_cleanup + :feature_category: :package_registry + :has_external_dependencies: + :urgency: :low + :resource_boundary: :unknown + :weight: 1 + :idempotent: true + :tags: [] - :name: cronjob:pages_domain_removal_cron :feature_category: :pages :has_external_dependencies: diff --git a/app/workers/packages/composer/cache_cleanup_worker.rb b/app/workers/packages/composer/cache_cleanup_worker.rb new file mode 100644 index 00000000000..638e50e18c4 --- /dev/null +++ b/app/workers/packages/composer/cache_cleanup_worker.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +module Packages + module Composer + class CacheCleanupWorker + include ApplicationWorker + include CronjobQueue # rubocop:disable Scalability/CronWorkerContext + + feature_category :package_registry + + idempotent! + + def perform + ::Packages::Composer::CacheFile.without_namespace.find_in_batches do |cache_files| + cache_files.each(&:destroy) + rescue ActiveRecord::RecordNotFound + # ignore. likely due to object already being deleted. + end + + ::Packages::Composer::CacheFile.expired.find_in_batches do |cache_files| + cache_files.each(&:destroy) + rescue ActiveRecord::RecordNotFound + # ignore. likely due to object already being deleted. + end + rescue => e + Gitlab::ErrorTracking.log_exception(e) + end + end + end +end |