diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-04-06 21:08:19 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-04-06 21:08:19 +0300 |
commit | da6cd333e7d29b356b398905c657be81a94b4621 (patch) | |
tree | 9455ef6355f90b710383d93aefa6b990117855b2 /app | |
parent | 5472bef68de87deeb67594a98e7eb35ff83929ec (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
21 files changed, 395 insertions, 40 deletions
diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js index 33d7da8fd53..d2856d99ef0 100644 --- a/app/assets/javascripts/api.js +++ b/app/assets/javascripts/api.js @@ -93,6 +93,7 @@ const Api = { notificationSettingsPath: '/api/:version/notification_settings', deployKeysPath: '/api/:version/deploy_keys', secureFilesPath: '/api/:version/projects/:project_id/secure_files', + dependencyProxyPath: '/api/:version/groups/:id/dependency_proxy/cache', group(groupId, callback = () => {}) { const url = Api.buildUrl(Api.groupPath).replace(':id', groupId); @@ -999,6 +1000,12 @@ const Api = { return result; }, + + deleteDependencyProxyCacheList(groupId, options = {}) { + const url = Api.buildUrl(this.dependencyProxyPath).replace(':id', groupId); + + return axios.delete(url, { params: { ...options } }); + }, }; export default Api; diff --git a/app/assets/javascripts/packages_and_registries/dependency_proxy/app.vue b/app/assets/javascripts/packages_and_registries/dependency_proxy/app.vue index eb112238c11..4ddefcbdf6a 100644 --- a/app/assets/javascripts/packages_and_registries/dependency_proxy/app.vue +++ b/app/assets/javascripts/packages_and_registries/dependency_proxy/app.vue @@ -1,13 +1,18 @@ <script> import { GlAlert, + GlDropdown, + GlDropdownItem, GlEmptyState, GlFormGroup, GlFormInputGroup, + GlModal, + GlModalDirective, GlSkeletonLoader, GlSprintf, } from '@gitlab/ui'; -import { s__ } from '~/locale'; +import { __, s__, n__, sprintf } from '~/locale'; +import Api from '~/api'; import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; import TitleArea from '~/vue_shared/components/registry/title_area.vue'; import ManifestsList from '~/packages_and_registries/dependency_proxy/components/manifests_list.vue'; @@ -22,16 +27,22 @@ import getDependencyProxyDetailsQuery from '~/packages_and_registries/dependency export default { components: { GlAlert, + GlDropdown, + GlDropdownItem, GlEmptyState, GlFormGroup, GlFormInputGroup, + GlModal, GlSkeletonLoader, GlSprintf, ClipboardButton, TitleArea, ManifestsList, }, - inject: ['groupPath', 'dependencyProxyAvailable', 'noManifestsIllustration'], + directives: { + GlModalDirective, + }, + inject: ['groupPath', 'groupId', 'dependencyProxyAvailable', 'noManifestsIllustration'], i18n: { proxyNotAvailableText: s__( 'DependencyProxy|Dependency Proxy feature is limited to public groups for now.', @@ -41,6 +52,19 @@ export default { blobCountAndSize: s__('DependencyProxy|Contains %{count} blobs of images (%{size})'), pageTitle: s__('DependencyProxy|Dependency Proxy'), noManifestTitle: s__('DependencyProxy|There are no images in the cache'), + deleteCacheAlertMessageSuccess: s__( + 'DependencyProxy|All items in the cache are scheduled for removal.', + ), + }, + confirmClearCacheModal: 'confirm-clear-cache-modal', + modalButtons: { + primary: { + text: s__('DependencyProxy|Clear cache'), + attributes: [{ variant: 'danger' }], + }, + secondary: { + text: __('Cancel'), + }, }, links: { DEPENDENCY_PROXY_DOCS_PATH, @@ -48,6 +72,8 @@ export default { data() { return { group: {}, + showDeleteCacheAlert: false, + deleteCacheAlertMessage: '', }; }, apollo: { @@ -80,6 +106,33 @@ export default { manifests() { return this.group.dependencyProxyManifests.nodes; }, + modalTitleWithCount() { + return sprintf( + n__( + 'Clear %{count} image from cache?', + 'Clear %{count} images from cache?', + this.group.dependencyProxyBlobCount, + ), + { + count: this.group.dependencyProxyBlobCount, + }, + ); + }, + modalConfirmationMessageWithCount() { + return sprintf( + n__( + 'You are about to clear %{count} image from the cache. Once you confirm, the next time a pipeline runs it must pull an image or tag from Docker Hub. Are you sure?', + 'You are about to clear %{count} images from the cache. Once you confirm, the next time a pipeline runs it must pull an image or tag from Docker Hub. Are you sure?', + this.group.dependencyProxyBlobCount, + ), + { + count: this.group.dependencyProxyBlobCount, + }, + ); + }, + showDeleteDropdown() { + return this.group.dependencyProxyBlobCount > 0; + }, }, methods: { fetchNextPage() { @@ -103,13 +156,47 @@ export default { }, }); }, + async submit() { + try { + await Api.deleteDependencyProxyCacheList(this.groupId); + + this.deleteCacheAlertMessage = this.$options.i18n.deleteCacheAlertMessageSuccess; + this.showDeleteCacheAlert = true; + } catch (err) { + this.deleteCacheAlertMessage = err; + this.showDeleteCacheAlert = true; + } + }, }, }; </script> <template> <div> - <title-area :title="$options.i18n.pageTitle" :info-messages="infoMessages" /> + <gl-alert + v-if="showDeleteCacheAlert" + data-testid="delete-cache-alert" + @dismiss="showDeleteCacheAlert = false" + > + {{ deleteCacheAlertMessage }} + </gl-alert> + <title-area :title="$options.i18n.pageTitle" :info-messages="infoMessages"> + <template v-if="showDeleteDropdown" #right-actions> + <gl-dropdown + icon="ellipsis_v" + text="More actions" + :text-sr-only="true" + category="tertiary" + no-caret + > + <gl-dropdown-item + v-gl-modal-directive="$options.confirmClearCacheModal" + variant="danger" + >{{ $options.i18n.clearCache }}</gl-dropdown-item + > + </gl-dropdown> + </template> + </title-area> <gl-alert v-if="!dependencyProxyAvailable" :dismissible="false" @@ -159,5 +246,15 @@ export default { :title="$options.i18n.noManifestTitle" /> </div> + + <gl-modal + :modal-id="$options.confirmClearCacheModal" + :title="modalTitleWithCount" + :action-primary="$options.modalButtons.primary" + :action-secondary="$options.modalButtons.secondary" + @primary="submit" + > + {{ modalConfirmationMessageWithCount }} + </gl-modal> </div> </template> diff --git a/app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue b/app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue index b996d3f15d1..8511f9bdb0f 100644 --- a/app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue +++ b/app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue @@ -10,7 +10,7 @@ import { import { formatStages } from '../utils'; import getLinkedPipelinesQuery from '../graphql/queries/get_linked_pipelines.query.graphql'; import getPipelineStagesQuery from '../graphql/queries/get_pipeline_stages.query.graphql'; -import { PIPELINE_STAGES_POLL_INTERVAL } from '../constants'; +import { COMMIT_BOX_POLL_INTERVAL } from '../constants'; export default { i18n: { @@ -65,7 +65,7 @@ export default { return getQueryHeaders(this.graphqlResourceEtag); }, query: getPipelineStagesQuery, - pollInterval: PIPELINE_STAGES_POLL_INTERVAL, + pollInterval: COMMIT_BOX_POLL_INTERVAL, variables() { return { fullPath: this.fullPath, diff --git a/app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_status.vue b/app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_status.vue new file mode 100644 index 00000000000..5a9d3129809 --- /dev/null +++ b/app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_status.vue @@ -0,0 +1,74 @@ +<script> +import { GlLoadingIcon, GlLink } from '@gitlab/ui'; +import CiIcon from '~/vue_shared/components/ci_icon.vue'; +import createFlash from '~/flash'; +import { + getQueryHeaders, + toggleQueryPollingByVisibility, +} from '~/pipelines/components/graph/utils'; +import getLatestPipelineStatusQuery from '../graphql/queries/get_latest_pipeline_status.query.graphql'; +import { COMMIT_BOX_POLL_INTERVAL, PIPELINE_STATUS_FETCH_ERROR } from '../constants'; + +export default { + PIPELINE_STATUS_FETCH_ERROR, + components: { + CiIcon, + GlLoadingIcon, + GlLink, + }, + inject: { + fullPath: { + default: '', + }, + iid: { + default: '', + }, + graphqlResourceEtag: { + default: '', + }, + }, + apollo: { + pipelineStatus: { + context() { + return getQueryHeaders(this.graphqlResourceEtag); + }, + query: getLatestPipelineStatusQuery, + pollInterval: COMMIT_BOX_POLL_INTERVAL, + variables() { + return { + fullPath: this.fullPath, + iid: this.iid, + }; + }, + update({ project }) { + return project?.pipeline?.detailedStatus || {}; + }, + error() { + createFlash({ message: this.$options.PIPELINE_STATUS_FETCH_ERROR }); + }, + }, + }, + data() { + return { + pipelineStatus: {}, + }; + }, + computed: { + loading() { + return this.$apollo.queries.pipelineStatus.loading; + }, + }, + mounted() { + toggleQueryPollingByVisibility(this.$apollo.queries.pipelineStatus); + }, +}; +</script> + +<template> + <div class="gl-display-inline-block gl-vertical-align-middle gl-mr-2"> + <gl-loading-icon v-if="loading" /> + <gl-link v-else :href="pipelineStatus.detailsPath"> + <ci-icon :status="pipelineStatus" :size="24" /> + </gl-link> + </div> +</template> diff --git a/app/assets/javascripts/projects/commit_box/info/constants.js b/app/assets/javascripts/projects/commit_box/info/constants.js index fc4f03482e2..be0bf715314 100644 --- a/app/assets/javascripts/projects/commit_box/info/constants.js +++ b/app/assets/javascripts/projects/commit_box/info/constants.js @@ -1 +1,7 @@ -export const PIPELINE_STAGES_POLL_INTERVAL = 10000; +import { __ } from '~/locale'; + +export const COMMIT_BOX_POLL_INTERVAL = 10000; + +export const PIPELINE_STATUS_FETCH_ERROR = __( + 'There was a problem fetching the latest pipeline status.', +); diff --git a/app/assets/javascripts/projects/commit_box/info/graphql/queries/get_latest_pipeline_status.query.graphql b/app/assets/javascripts/projects/commit_box/info/graphql/queries/get_latest_pipeline_status.query.graphql new file mode 100644 index 00000000000..cec96f82336 --- /dev/null +++ b/app/assets/javascripts/projects/commit_box/info/graphql/queries/get_latest_pipeline_status.query.graphql @@ -0,0 +1,14 @@ +query getLatestPipelineStatus($fullPath: ID!, $iid: ID!) { + project(fullPath: $fullPath) { + id + pipeline(iid: $iid) { + id + detailedStatus { + id + detailsPath + icon + group + } + } + } +} diff --git a/app/assets/javascripts/projects/commit_box/info/index.js b/app/assets/javascripts/projects/commit_box/info/index.js index 69fe2d30489..7500c152b6a 100644 --- a/app/assets/javascripts/projects/commit_box/info/index.js +++ b/app/assets/javascripts/projects/commit_box/info/index.js @@ -2,6 +2,7 @@ import { fetchCommitMergeRequests } from '~/commit_merge_requests'; import { initCommitPipelineMiniGraph } from './init_commit_pipeline_mini_graph'; import { initDetailsButton } from './init_details_button'; import { loadBranches } from './load_branches'; +import initCommitPipelineStatus from './init_commit_pipeline_status'; export const initCommitBoxInfo = () => { // Display commit related branches @@ -14,4 +15,6 @@ export const initCommitBoxInfo = () => { initCommitPipelineMiniGraph(); initDetailsButton(); + + initCommitPipelineStatus(); }; diff --git a/app/assets/javascripts/projects/commit_box/info/init_commit_pipeline_status.js b/app/assets/javascripts/projects/commit_box/info/init_commit_pipeline_status.js new file mode 100644 index 00000000000..d5e62531283 --- /dev/null +++ b/app/assets/javascripts/projects/commit_box/info/init_commit_pipeline_status.js @@ -0,0 +1,34 @@ +import Vue from 'vue'; +import VueApollo from 'vue-apollo'; +import createDefaultClient from '~/lib/graphql'; +import CommitBoxPipelineStatus from './components/commit_box_pipeline_status.vue'; + +Vue.use(VueApollo); + +const apolloProvider = new VueApollo({ + defaultClient: createDefaultClient({}, { useGet: true }), +}); + +export default (selector = '.js-commit-pipeline-status') => { + const el = document.querySelector(selector); + + if (!el) { + return; + } + + const { fullPath, iid, graphqlResourceEtag } = el.dataset; + + // eslint-disable-next-line no-new + new Vue({ + el, + apolloProvider, + provide: { + fullPath, + iid, + graphqlResourceEtag, + }, + render(createElement) { + return createElement(CommitBoxPipelineStatus); + }, + }); +}; diff --git a/app/assets/javascripts/reports/components/report_section.vue b/app/assets/javascripts/reports/components/report_section.vue index 7a490210f0b..ae201a61db6 100644 --- a/app/assets/javascripts/reports/components/report_section.vue +++ b/app/assets/javascripts/reports/components/report_section.vue @@ -172,7 +172,7 @@ export default { }, methods: { toggleCollapsed() { - if (this.trackAction && this.glFeatures.usersExpandingWidgetsUsageData) { + if (this.trackAction) { api.trackRedisHllUserEvent(this.trackAction); } diff --git a/app/assets/stylesheets/page_bundles/jira_connect_users.scss b/app/assets/stylesheets/page_bundles/jira_connect_users.scss index 6725bf8f1a1..602910adad9 100644 --- a/app/assets/stylesheets/page_bundles/jira_connect_users.scss +++ b/app/assets/stylesheets/page_bundles/jira_connect_users.scss @@ -1,13 +1 @@ -@import 'mixins_and_variables_and_functions'; - -.jira-connect-users-container { - margin-left: auto; - margin-right: auto; - width: px-to-rem(350px); -} - -.devise-layout-html body .navless-container { - @include media-breakpoint-down(xs) { - padding-top: 65px; - } -} +@import '../themes/theme_indigo'; diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 7baf876e792..3858873bb46 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -44,8 +44,9 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo push_frontend_feature_flag(:secure_vulnerability_training, project, default_enabled: :yaml) push_frontend_feature_flag(:issue_assignees_widget, @project, default_enabled: :yaml) push_frontend_feature_flag(:realtime_labels, project, default_enabled: :yaml) + # Usage data feature flags - push_frontend_feature_flag(:users_expanding_widgets_usage_data, project, default_enabled: :yaml) + # push_frontend_feature_flag(:diff_settings_usage_data, default_enabled: :yaml) end diff --git a/app/helpers/external_link_helper.rb b/app/helpers/external_link_helper.rb index c951d0daf96..53dacfe0566 100644 --- a/app/helpers/external_link_helper.rb +++ b/app/helpers/external_link_helper.rb @@ -5,7 +5,7 @@ module ExternalLinkHelper def external_link(body, url, options = {}) link = link_to url, { target: '_blank', rel: 'noopener noreferrer' }.merge(options) do - "#{body}#{sprite_icon('external-link', css_class: 'gl-ml-1')}".html_safe + "#{body}#{sprite_icon('external-link', css_class: 'gl-ml-2')}".html_safe end sanitize(link, tags: %w(a svg use), attributes: %w(target rel data-testid class href).concat(options.stringify_keys.keys)) end diff --git a/app/models/ci/job_artifact.rb b/app/models/ci/job_artifact.rb index 3426c4d5248..dff8bb89021 100644 --- a/app/models/ci/job_artifact.rb +++ b/app/models/ci/job_artifact.rb @@ -186,6 +186,7 @@ module Ci scope :downloadable, -> { where(file_type: DOWNLOADABLE_TYPES) } scope :unlocked, -> { joins(job: :pipeline).merge(::Ci::Pipeline.unlocked) } + scope :order_expired_asc, -> { order(expire_at: :asc) } scope :order_expired_desc, -> { order(expire_at: :desc) } scope :with_destroy_preloads, -> { includes(project: [:route, :statistics]) } @@ -273,6 +274,10 @@ module Ci self.where(project: project).sum(:size) end + def self.pluck_job_id + pluck(:job_id) + end + ## # FastDestroyAll concerns # rubocop: disable CodeReuse/ServiceClass diff --git a/app/presenters/merge_request_presenter.rb b/app/presenters/merge_request_presenter.rb index 6dd3908b21d..907bd37f37b 100644 --- a/app/presenters/merge_request_presenter.rb +++ b/app/presenters/merge_request_presenter.rb @@ -191,13 +191,17 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated end def mergeable_discussions_state - # This avoids calling MergeRequest#mergeable_discussions_state without - # considering the state of the MR first. If a MR isn't mergeable, we can - # safely short-circuit it. - if merge_request.mergeable_state?(skip_ci_check: true, skip_discussions_check: true) + if Feature.enabled?(:change_response_code_merge_status, project, default_enabled: :yaml) merge_request.mergeable_discussions_state? else - false + # This avoids calling MergeRequest#mergeable_discussions_state without + # considering the state of the MR first. If a MR isn't mergeable, we can + # safely short-circuit it. + if merge_request.mergeable_state?(skip_ci_check: true, skip_discussions_check: true) + merge_request.mergeable_discussions_state? + else + false + end end end diff --git a/app/serializers/merge_request_poll_widget_entity.rb b/app/serializers/merge_request_poll_widget_entity.rb index 12998d70a22..5c37337ab09 100644 --- a/app/serializers/merge_request_poll_widget_entity.rb +++ b/app/serializers/merge_request_poll_widget_entity.rb @@ -33,13 +33,17 @@ class MergeRequestPollWidgetEntity < Grape::Entity # Booleans expose :mergeable_discussions_state?, as: :mergeable_discussions_state do |merge_request| - # This avoids calling MergeRequest#mergeable_discussions_state without - # considering the state of the MR first. If a MR isn't mergeable, we can - # safely short-circuit it. - if merge_request.mergeable_state?(skip_ci_check: true, skip_discussions_check: true) + if Feature.enabled?(:change_response_code_merge_status, merge_request.project, default_enabled: :yaml) merge_request.mergeable_discussions_state? else - false + # This avoids calling MergeRequest#mergeable_discussions_state without + # considering the state of the MR first. If a MR isn't mergeable, we can + # safely short-circuit it. + if merge_request.mergeable_state?(skip_ci_check: true, skip_discussions_check: true) + merge_request.mergeable_discussions_state? + else + false + end end end diff --git a/app/services/ci/job_artifacts/update_unknown_locked_status_service.rb b/app/services/ci/job_artifacts/update_unknown_locked_status_service.rb new file mode 100644 index 00000000000..0d35a90ed04 --- /dev/null +++ b/app/services/ci/job_artifacts/update_unknown_locked_status_service.rb @@ -0,0 +1,79 @@ +# frozen_string_literal: true + +module Ci + module JobArtifacts + class UpdateUnknownLockedStatusService + include ::Gitlab::ExclusiveLeaseHelpers + include ::Gitlab::LoopHelpers + + BATCH_SIZE = 100 + LOOP_TIMEOUT = 5.minutes + LOOP_LIMIT = 100 + LARGE_LOOP_LIMIT = 500 + EXCLUSIVE_LOCK_KEY = 'unknown_status_job_artifacts:update:lock' + LOCK_TIMEOUT = 6.minutes + + def initialize + @removed_count = 0 + @locked_count = 0 + @start_at = Time.current + @loop_limit = Feature.enabled?(:ci_job_artifacts_backlog_large_loop_limit) ? LARGE_LOOP_LIMIT : LOOP_LIMIT + end + + def execute + in_lock(EXCLUSIVE_LOCK_KEY, ttl: LOCK_TIMEOUT, retries: 1) do + update_locked_status_on_unknown_artifacts + end + + { removed: @removed_count, locked: @locked_count } + end + + private + + def update_locked_status_on_unknown_artifacts + loop_until(timeout: LOOP_TIMEOUT, limit: @loop_limit) do + unknown_status_build_ids = safely_ordered_ci_job_artifacts_locked_unknown_relation.pluck_job_id.uniq + + locked_pipe_build_ids = ::Ci::Build + .with_pipeline_locked_artifacts + .id_in(unknown_status_build_ids) + .pluck_primary_key + + @locked_count += update_unknown_artifacts(locked_pipe_build_ids, Ci::JobArtifact.lockeds[:artifacts_locked]) + + unlocked_pipe_build_ids = unknown_status_build_ids - locked_pipe_build_ids + service_response = batch_destroy_artifacts(unlocked_pipe_build_ids) + @removed_count += service_response[:destroyed_artifacts_count] + end + end + + def update_unknown_artifacts(build_ids, locked_value) + return 0 unless build_ids.any? + + expired_locked_unknown_artifacts.for_job_ids(build_ids).update_all(locked: locked_value) + end + + def batch_destroy_artifacts(build_ids) + deleteable_artifacts_relation = + if build_ids.any? + expired_locked_unknown_artifacts.for_job_ids(build_ids) + else + Ci::JobArtifact.none + end + + Ci::JobArtifacts::DestroyBatchService.new(deleteable_artifacts_relation).execute + end + + def expired_locked_unknown_artifacts + # UPDATE queries perform better without the specific order and limit + # https://gitlab.com/gitlab-org/gitlab/-/merge_requests/76509#note_891260455 + Ci::JobArtifact.expired_before(@start_at).artifact_unknown + end + + def safely_ordered_ci_job_artifacts_locked_unknown_relation + # Adding the ORDER and LIMIT improves performance when we don't have build_id + expired_locked_unknown_artifacts.limit(BATCH_SIZE).order_expired_asc + end + end + end +end diff --git a/app/views/groups/dependency_proxies/show.html.haml b/app/views/groups/dependency_proxies/show.html.haml index 47caec717af..940a504438d 100644 --- a/app/views/groups/dependency_proxies/show.html.haml +++ b/app/views/groups/dependency_proxies/show.html.haml @@ -4,4 +4,4 @@ #js-dependency-proxy{ data: { group_path: @group.full_path, dependency_proxy_available: dependency_proxy_available.to_s, - no_manifests_illustration: image_path('illustrations/docker-empty-state.svg') } } + no_manifests_illustration: image_path('illustrations/docker-empty-state.svg'), group_id: @group.id } } diff --git a/app/views/jira_connect/users/show.html.haml b/app/views/jira_connect/users/show.html.haml index cf88acd6976..29805a2c42d 100644 --- a/app/views/jira_connect/users/show.html.haml +++ b/app/views/jira_connect/users/show.html.haml @@ -1,8 +1,14 @@ -.jira-connect-users-container.gl-text-center - - user_link = link_to(current_user.to_reference, user_path(current_user), target: '_blank', rel: 'noopener noreferrer') - %h2= _('You are signed in to GitLab as %{user_link}').html_safe % { user_link: user_link } +.gl-text-center.gl-mx-auto.gl-pt-6 + %h3.gl-mb-4 + = _('You are signed in to GitLab as:') - %p= s_('Integrations|You can now close this window and return to the GitLab for Jira application.') + .gl-display-flex.gl-flex-direction-column.gl-align-items-center.gl-mb-4 + = link_to user_path(current_user), target: '_blank', rel: 'noopener noreferrer' do + = user_avatar_without_link(user: current_user, size: 60, css_class: 'gl-mr-0! gl-mb-2', has_tooltip: false) + = link_to current_user.to_reference, user_path(current_user), target: '_blank', rel: 'noopener noreferrer' + + %p.gl-mb-6 + = s_('JiraService|You can now close this window and%{br}return to the GitLab for Jira application.').html_safe % { br: '<br>'.html_safe } - if @jira_app_link %p= external_link s_('Integrations|Return to GitLab for Jira'), @jira_app_link, class: 'gl-button btn btn-confirm' diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml index 5e15500c556..23572d1d6ac 100644 --- a/app/views/projects/commit/_commit_box.html.haml +++ b/app/views/projects/commit/_commit_box.html.haml @@ -47,9 +47,7 @@ - if can?(current_user, :read_pipeline, @last_pipeline) .well-segment.pipeline-info - .status-icon-container - = link_to project_pipeline_path(@project, @last_pipeline.id), class: "ci-status-icon-#{@last_pipeline.status}" do - = ci_icon_for_status(@last_pipeline.status) + .js-commit-pipeline-status{ data: { full_path: @project.full_path, iid: @last_pipeline.iid, graphql_resource_etag: graphql_etag_pipeline_path(@last_pipeline) } } #{ _('Pipeline') } = link_to "##{@last_pipeline.id}", project_pipeline_path(@project, @last_pipeline.id) = ci_label_for_status(@last_pipeline.status) diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml index 74d926e182d..bfb70e0d496 100644 --- a/app/workers/all_queues.yml +++ b/app/workers/all_queues.yml @@ -264,6 +264,15 @@ :weight: 1 :idempotent: true :tags: [] +- :name: cronjob:ci_update_locked_unknown_artifacts + :worker_name: Ci::UpdateLockedUnknownArtifactsWorker + :feature_category: :build_artifacts + :has_external_dependencies: + :urgency: :throttled + :resource_boundary: :unknown + :weight: 1 + :idempotent: + :tags: [] - :name: cronjob:clusters_integrations_check_prometheus_health :worker_name: Clusters::Integrations::CheckPrometheusHealthWorker :feature_category: :incident_management diff --git a/app/workers/ci/update_locked_unknown_artifacts_worker.rb b/app/workers/ci/update_locked_unknown_artifacts_worker.rb new file mode 100644 index 00000000000..2d37ebb3c93 --- /dev/null +++ b/app/workers/ci/update_locked_unknown_artifacts_worker.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +module Ci + class UpdateLockedUnknownArtifactsWorker # rubocop:disable Scalability/IdempotentWorker + include ApplicationWorker + + data_consistency :sticky + urgency :throttled + + # rubocop:disable Scalability/CronWorkerContext + # This worker does not perform work scoped to a context + include CronjobQueue + # rubocop:enable Scalability/CronWorkerContext + + feature_category :build_artifacts + + def perform + return unless ::Feature.enabled?(:ci_job_artifacts_backlog_work) + + artifact_counts = Ci::JobArtifacts::UpdateUnknownLockedStatusService.new.execute + + log_extra_metadata_on_done(:removed_count, artifact_counts[:removed]) + log_extra_metadata_on_done(:locked_count, artifact_counts[:locked]) + end + end +end |