diff options
Diffstat (limited to 'app')
33 files changed, 345 insertions, 152 deletions
diff --git a/app/assets/javascripts/design_management/components/list/item.vue b/app/assets/javascripts/design_management/components/list/item.vue index 2169c9111d2..b6163491abc 100644 --- a/app/assets/javascripts/design_management/components/list/item.vue +++ b/app/assets/javascripts/design_management/components/list/item.vue @@ -137,8 +137,7 @@ export default { <span :title="icon.tooltip" :aria-label="icon.tooltip"> <gl-icon :name="icon.name" - :size="18" - use-deprecated-sizes + :size="16" :class="icon.classes" data-qa-selector="design_status_icon" :data-qa-status="icon.name" diff --git a/app/assets/javascripts/integrations/edit/components/jira_issues_fields.vue b/app/assets/javascripts/integrations/edit/components/jira_issues_fields.vue index e6bd0f53672..aea4a8b1c0b 100644 --- a/app/assets/javascripts/integrations/edit/components/jira_issues_fields.vue +++ b/app/assets/javascripts/integrations/edit/components/jira_issues_fields.vue @@ -1,15 +1,8 @@ <script> -import { - GlFormGroup, - GlFormCheckbox, - GlFormInput, - GlSprintf, - GlLink, - GlButton, - GlCard, -} from '@gitlab/ui'; +import { GlFormGroup, GlFormCheckbox, GlFormInput, GlSprintf, GlLink } from '@gitlab/ui'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import eventHub from '../event_hub'; +import JiraUpgradeCta from './jira_upgrade_cta.vue'; export default { name: 'JiraIssuesFields', @@ -19,8 +12,7 @@ export default { GlFormInput, GlSprintf, GlLink, - GlButton, - GlCard, + JiraUpgradeCta, JiraIssueCreationVulnerabilities: () => import('ee_component/integrations/edit/components/jira_issue_creation_vulnerabilities.vue'), }, @@ -84,11 +76,13 @@ export default { return !this.enableJiraIssues || Boolean(this.projectKey) || !this.validated; }, showJiraVulnerabilitiesOptions() { - return ( - this.enableJiraIssues && - this.showJiraVulnerabilitiesIntegration && - this.glFeatures.jiraForVulnerabilities - ); + return this.showJiraVulnerabilitiesIntegration && this.glFeatures.jiraForVulnerabilities; + }, + showUltimateUpgrade() { + return this.showJiraIssuesIntegration && !this.showJiraVulnerabilitiesIntegration; + }, + showPremiumUpgrade() { + return !this.showJiraIssuesIntegration; }, }, created() { @@ -135,27 +129,23 @@ export default { </template> </gl-form-checkbox> <jira-issue-creation-vulnerabilities - v-if="showJiraVulnerabilitiesOptions" + v-if="enableJiraIssues" :project-key="projectKey" :initial-is-enabled="initialEnableJiraVulnerabilities" :initial-issue-type-id="initialVulnerabilitiesIssuetype" + :show-full-feature="showJiraVulnerabilitiesOptions" data-testid="jira-for-vulnerabilities" @request-get-issue-types="getJiraIssueTypes" /> </template> - <gl-card v-else class="gl-mt-7"> - <strong>{{ __('This is a Premium feature') }}</strong> - <p>{{ __('Upgrade your plan to enable this feature of the Jira Integration.') }}</p> - <gl-button - v-if="upgradePlanPath" - category="primary" - variant="info" - :href="upgradePlanPath" - target="_blank" - > - {{ __('Upgrade your plan') }} - </gl-button> - </gl-card> + <jira-upgrade-cta + v-if="showUltimateUpgrade || showPremiumUpgrade" + class="gl-mt-2" + :class="{ 'gl-ml-6': showUltimateUpgrade }" + :upgrade-plan-path="upgradePlanPath" + :show-ultimate-message="showUltimateUpgrade" + :show-premium-message="showPremiumUpgrade" + /> </div> </gl-form-group> <template v-if="showJiraIssuesIntegration"> diff --git a/app/assets/javascripts/integrations/edit/components/jira_upgrade_cta.vue b/app/assets/javascripts/integrations/edit/components/jira_upgrade_cta.vue new file mode 100644 index 00000000000..9164e484440 --- /dev/null +++ b/app/assets/javascripts/integrations/edit/components/jira_upgrade_cta.vue @@ -0,0 +1,51 @@ +<script> +import { GlButton, GlCard } from '@gitlab/ui'; +import { s__, __ } from '~/locale'; + +export default { + components: { + GlButton, + GlCard, + }, + props: { + upgradePlanPath: { + type: String, + required: false, + default: '', + }, + showPremiumMessage: { + type: Boolean, + required: false, + default: false, + }, + showUltimateMessage: { + type: Boolean, + required: false, + default: false, + }, + }, + computed: { + title() { + return this.showUltimateMessage + ? this.$options.i18n.titleUltimate + : this.$options.i18n.titlePremium; + }, + }, + i18n: { + titleUltimate: s__('JiraService|This is an Ultimate feature'), + titlePremium: s__('JiraService|This is a Premium feature'), + content: s__('JiraService|Upgrade your plan to enable this feature of the Jira Integration.'), + upgrade: __('Upgrade your plan'), + }, +}; +</script> + +<template> + <gl-card> + <strong>{{ title }}</strong> + <p>{{ $options.i18n.content }}</p> + <gl-button v-if="upgradePlanPath" category="primary" variant="info" :href="upgradePlanPath"> + {{ $options.i18n.upgrade }} + </gl-button> + </gl-card> +</template> diff --git a/app/assets/javascripts/projects/commit/components/form_modal.vue b/app/assets/javascripts/projects/commit/components/form_modal.vue index a5f26416828..6eefa5f55e4 100644 --- a/app/assets/javascripts/projects/commit/components/form_modal.vue +++ b/app/assets/javascripts/projects/commit/components/form_modal.vue @@ -37,6 +37,11 @@ export default { type: String, required: true, }, + isCherryPick: { + type: Boolean, + required: false, + default: false, + }, }, data() { return { @@ -111,7 +116,7 @@ export default { <input type="hidden" name="authenticity_token" :value="$options.csrf.token" /> <gl-form-group - v-if="glFeatures.pickIntoProject" + v-if="glFeatures.pickIntoProject && isCherryPick" :label="i18n.projectLabel" label-for="start_project" data-testid="dropdown-group" diff --git a/app/assets/javascripts/projects/commit/init_cherry_pick_commit_modal.js b/app/assets/javascripts/projects/commit/init_cherry_pick_commit_modal.js index ad31ad14b2a..47ee8237fea 100644 --- a/app/assets/javascripts/projects/commit/init_cherry_pick_commit_modal.js +++ b/app/assets/javascripts/projects/commit/init_cherry_pick_commit_modal.js @@ -51,6 +51,7 @@ export default function initInviteMembersModal() { i18n: { ...I18N_CHERRY_PICK_MODAL, ...I18N_MODAL }, openModal: OPEN_CHERRY_PICK_MODAL, modalId: CHERRY_PICK_MODAL_ID, + isCherryPick: true, }, }), }); diff --git a/app/assets/javascripts/reports/codequality_report/grouped_codequality_reports_app.vue b/app/assets/javascripts/reports/codequality_report/grouped_codequality_reports_app.vue index 654508f0736..cfefdf82d85 100644 --- a/app/assets/javascripts/reports/codequality_report/grouped_codequality_reports_app.vue +++ b/app/assets/javascripts/reports/codequality_report/grouped_codequality_reports_app.vue @@ -62,7 +62,7 @@ export default { helpPath: this.codequalityHelpPath, }); - this.fetchReports(this.glFeatures.codequalityBackendComparison); + this.fetchReports(); }, methods: { ...mapActions(['fetchReports', 'setPaths']), 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 7182079860a..11f484b2cdf 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 @@ -1,11 +1,6 @@ <script> /* eslint-disable vue/no-v-html */ -import { - GlPopover, - GlLink, - GlDeprecatedSkeletonLoading as GlSkeletonLoading, - GlIcon, -} from '@gitlab/ui'; +import { GlPopover, GlLink, GlSkeletonLoader, GlIcon } from '@gitlab/ui'; import UserNameWithStatus from '~/sidebar/components/assignees/user_name_with_status.vue'; import { glEmojiTag } from '../../../emoji'; import UserAvatarImage from '../user_avatar/user_avatar_image.vue'; @@ -19,7 +14,7 @@ export default { GlIcon, GlLink, GlPopover, - GlSkeletonLoading, + GlSkeletonLoader, UserAvatarImage, UserNameWithStatus, }, @@ -65,15 +60,13 @@ export default { <div class="gl-p-2 flex-shrink-1"> <user-avatar-image :img-src="user.avatarUrl" :size="60" css-classes="gl-mr-3!" /> </div> - <div class="gl-p-2 gl-w-full"> + <div class="gl-p-2 gl-w-full gl-min-w-0"> <template v-if="userIsLoading"> - <!-- `gl-skeleton-loading` does not support equal length lines --> - <!-- This can be migrated to `gl-skeleton-loader` when https://gitlab.com/gitlab-org/gitlab-ui/-/issues/872 is completed --> - <gl-skeleton-loading - v-for="n in $options.maxSkeletonLines" - :key="n" - :lines="1" - class="animation-container-small gl-mb-2" + <gl-skeleton-loader + :lines="$options.maxSkeletonLines" + preserve-aspect-ratio="none" + equal-width-lines + :height="52" /> </template> <template v-else> diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb index 1e65974a3cd..3853797e0bf 100644 --- a/app/controllers/projects/commit_controller.rb +++ b/app/controllers/projects/commit_controller.rb @@ -20,7 +20,7 @@ class Projects::CommitController < Projects::ApplicationController before_action :define_note_vars, only: [:show, :diff_for_path, :diff_files] before_action :authorize_edit_tree!, only: [:revert, :cherry_pick] before_action do - push_frontend_feature_flag(:pick_into_project) + push_frontend_feature_flag(:pick_into_project, @project, default_enabled: :yaml) end BRANCH_SEARCH_LIMIT = 1000 diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 9081c9f7c57..4d612cd45d0 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -37,7 +37,6 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo push_frontend_feature_flag(:default_merge_ref_for_diffs, @project, default_enabled: :yaml) push_frontend_feature_flag(:core_security_mr_widget_counts, @project) push_frontend_feature_flag(:diffs_gradual_load, @project, default_enabled: true) - push_frontend_feature_flag(:codequality_backend_comparison, @project, default_enabled: :yaml) push_frontend_feature_flag(:local_file_reviews, default_enabled: :yaml) push_frontend_feature_flag(:paginated_notes, @project, default_enabled: :yaml) push_frontend_feature_flag(:new_pipelines_table, @project, default_enabled: :yaml) diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb index 45c1c35a655..3b218822395 100644 --- a/app/controllers/search_controller.rb +++ b/app/controllers/search_controller.rb @@ -47,7 +47,11 @@ class SearchController < ApplicationController params.require([:search, :scope]) scope = search_service.scope - count = search_service.search_results.formatted_count(scope) + + count = 0 + ApplicationRecord.with_fast_read_statement_timeout do + count = search_service.search_results.formatted_count(scope) + end # Users switching tabs will keep fetching the same tab counts so it's a # good idea to cache in their browser just for a short time. They can still diff --git a/app/helpers/sidebars_helper.rb b/app/helpers/sidebars_helper.rb index efb0fd8a6e3..b0fffe93c25 100644 --- a/app/helpers/sidebars_helper.rb +++ b/app/helpers/sidebars_helper.rb @@ -35,7 +35,8 @@ module SidebarsHelper def project_sidebar_context_data(project, user) { current_user: user, - container: project + container: project, + learn_gitlab_experiment_enabled: learn_gitlab_experiment_enabled?(project) } end end diff --git a/app/helpers/workhorse_helper.rb b/app/helpers/workhorse_helper.rb index 28dd1b00292..8785c4cdcbb 100644 --- a/app/helpers/workhorse_helper.rb +++ b/app/helpers/workhorse_helper.rb @@ -7,7 +7,7 @@ module WorkhorseHelper def send_git_blob(repository, blob, inline: true) headers.store(*Gitlab::Workhorse.send_git_blob(repository, blob)) - headers['Content-Disposition'] = inline ? 'inline' : content_disposition_attachment(repository.project, blob.name) + headers['Content-Disposition'] = content_disposition_for_blob(blob, inline) # If enabled, this will override the values set above workhorse_set_content_type! @@ -49,11 +49,9 @@ module WorkhorseHelper headers[Gitlab::Workhorse::DETECT_HEADER] = "true" end - def content_disposition_attachment(project, filename) - if Feature.enabled?(:attachment_with_filename, project, default_enabled: :yaml) - ActionDispatch::Http::ContentDisposition.format(disposition: 'attachment', filename: filename) - else - 'attachment' - end + def content_disposition_for_blob(blob, inline) + return 'inline' if inline + + ActionDispatch::Http::ContentDisposition.format(disposition: 'attachment', filename: blob.name) end end diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 57f5f66891e..3d8e9f4c126 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -377,11 +377,11 @@ module Ci end def other_manual_actions - pipeline.manual_actions.where.not(name: name) + pipeline.manual_actions.reject { |action| action.name == self.name } end def other_scheduled_actions - pipeline.scheduled_actions.where.not(name: name) + pipeline.scheduled_actions.reject { |action| action.name == self.name } end def pages_generator? diff --git a/app/models/concerns/milestoneish.rb b/app/models/concerns/milestoneish.rb index 8f7e6041d4b..eaf64f2541d 100644 --- a/app/models/concerns/milestoneish.rb +++ b/app/models/concerns/milestoneish.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Milestoneish - DISPLAY_ISSUES_LIMIT = 3000 + DISPLAY_ISSUES_LIMIT = 500 def total_issues_count @total_issues_count ||= Milestones::IssuesCountService.new(self).count diff --git a/app/models/deployment.rb b/app/models/deployment.rb index 0bfe6172154..d3280403bfd 100644 --- a/app/models/deployment.rb +++ b/app/models/deployment.rb @@ -171,7 +171,7 @@ class Deployment < ApplicationRecord end def commit - project.commit(sha) + @commit ||= project.commit(sha) end def commit_title @@ -250,7 +250,7 @@ class Deployment < ApplicationRecord return unless on_stop.present? return unless manual_actions - @stop_action ||= manual_actions.find_by(name: on_stop) + @stop_action ||= manual_actions.find { |action| action.name == self.on_stop } end def finished_at diff --git a/app/models/environment.rb b/app/models/environment.rb index 4cc65f4e295..4ee93b0ba4a 100644 --- a/app/models/environment.rb +++ b/app/models/environment.rb @@ -24,13 +24,13 @@ class Environment < ApplicationRecord has_many :self_managed_prometheus_alert_events, inverse_of: :environment has_many :alert_management_alerts, class_name: 'AlertManagement::Alert', inverse_of: :environment - has_one :last_deployment, -> { success.order('deployments.id DESC') }, class_name: 'Deployment' + has_one :last_deployment, -> { success.order('deployments.id DESC') }, class_name: 'Deployment', inverse_of: :environment has_one :last_deployable, through: :last_deployment, source: 'deployable', source_type: 'CommitStatus' has_one :last_pipeline, through: :last_deployable, source: 'pipeline' has_one :last_visible_deployment, -> { visible.distinct_on_environment }, inverse_of: :environment, class_name: 'Deployment' has_one :last_visible_deployable, through: :last_visible_deployment, source: 'deployable', source_type: 'CommitStatus' has_one :last_visible_pipeline, through: :last_visible_deployable, source: 'pipeline' - has_one :upcoming_deployment, -> { running.order('deployments.id DESC') }, class_name: 'Deployment' + has_one :upcoming_deployment, -> { running.order('deployments.id DESC') }, class_name: 'Deployment', inverse_of: :environment has_one :latest_opened_most_severe_alert, -> { order_severity_with_open_prometheus_alert }, class_name: 'AlertManagement::Alert', inverse_of: :environment before_validation :nullify_external_url diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 8f2ec2d6b88..e7f3762b9a3 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -37,7 +37,7 @@ class MergeRequest < ApplicationRecord SORTING_PREFERENCE_FIELD = :merge_requests_sort ALLOWED_TO_USE_MERGE_BASE_PIPELINE_FOR_COMPARISON = { - 'Ci::CompareCodequalityReportsService' => ->(project) { ::Gitlab::Ci::Features.display_codequality_backend_comparison?(project) } + 'Ci::CompareCodequalityReportsService' => ->(project) { true } }.freeze belongs_to :target_project, class_name: "Project" @@ -1564,8 +1564,6 @@ class MergeRequest < ApplicationRecord end def has_codequality_reports? - return false unless ::Gitlab::Ci::Features.display_codequality_backend_comparison?(project) - actual_head_pipeline&.has_reports?(Ci::JobArtifact.codequality_reports) end diff --git a/app/models/sidebars/projects/menus/learn_gitlab/menu.rb b/app/models/sidebars/projects/menus/learn_gitlab/menu.rb new file mode 100644 index 00000000000..4b572846d1a --- /dev/null +++ b/app/models/sidebars/projects/menus/learn_gitlab/menu.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +module Sidebars + module Projects + module Menus + module LearnGitlab + class Menu < ::Sidebars::Menu + override :link + def link + project_learn_gitlab_path(context.project) + end + + override :active_routes + def active_routes + { controller: :learn_gitlab } + end + + override :title + def title + _('Learn GitLab') + end + + override :extra_container_html_options + def nav_link_html_options + { class: 'home' } + end + + override :sprite_icon + def sprite_icon + 'home' + end + + override :render? + def render? + context.learn_gitlab_experiment_enabled + end + end + end + end + end +end diff --git a/app/models/sidebars/projects/panel.rb b/app/models/sidebars/projects/panel.rb index 5f4c7f32164..60cb804f5e8 100644 --- a/app/models/sidebars/projects/panel.rb +++ b/app/models/sidebars/projects/panel.rb @@ -8,6 +8,7 @@ module Sidebars set_scope_menu(Sidebars::Projects::Menus::Scope::Menu.new(context)) add_menu(Sidebars::Projects::Menus::ProjectOverview::Menu.new(context)) + add_menu(Sidebars::Projects::Menus::LearnGitlab::Menu.new(context)) end override :render_raw_menus_partial diff --git a/app/models/user.rb b/app/models/user.rb index 426309762ca..507e8cc2cf5 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -354,7 +354,7 @@ class User < ApplicationRecord # this state transition object in order to do a rollback. # For this reason the tradeoff is to disable this cop. after_transition any => :blocked do |user| - Ci::AbortPipelinesService.new.execute(user.pipelines, :user_blocked) + Ci::DropPipelineService.new.execute_async_for_all(user.pipelines, :user_blocked, user) Ci::DisableUserPipelineSchedulesService.new.execute(user) end # rubocop: enable CodeReuse/ServiceClass diff --git a/app/serializers/environment_serializer.rb b/app/serializers/environment_serializer.rb index 598ce5f9e4f..2bb9a7e7254 100644 --- a/app/serializers/environment_serializer.rb +++ b/app/serializers/environment_serializer.rb @@ -23,7 +23,7 @@ class EnvironmentSerializer < BaseSerializer latest: super(item.latest, opts) } end else - super(resource, opts) + super(batch_load(resource), opts) end end @@ -41,11 +41,59 @@ class EnvironmentSerializer < BaseSerializer # immediately. items = @paginator.paginate(items) if paginated? - environments = resource.where(id: items.map(&:last_id)).index_by(&:id) + environments = batch_load(resource.where(id: items.map(&:last_id))) + environments_by_id = environments.index_by(&:id) items.map do |item| - Item.new(item.folder, item.size, environments[item.last_id]) + Item.new(item.folder, item.size, environments_by_id[item.last_id]) end end + + def batch_load(resource) + resource = resource.preload(environment_associations) + + resource.all.tap do |environments| + environments.each do |environment| + # Batch loading the commits of the deployments + environment.last_deployment&.commit&.try(:lazy_author) + environment.upcoming_deployment&.commit&.try(:lazy_author) + end + end + end + + def environment_associations + { + last_deployment: deployment_associations, + upcoming_deployment: deployment_associations, + project: project_associations + } + end + + def deployment_associations + { + user: [], + cluster: [], + project: [], + deployable: { + user: [], + metadata: [], + pipeline: { + manual_actions: [], + scheduled_actions: [] + }, + project: project_associations + } + } + end + + def project_associations + { + project_feature: [], + route: [], + namespace: :route + } + end # rubocop: enable CodeReuse/ActiveRecord end + +EnvironmentSerializer.prepend_if_ee('EE::EnvironmentSerializer') diff --git a/app/services/ci/abort_pipelines_service.rb b/app/services/ci/abort_pipelines_service.rb index ad619dbdc41..43734c4dd39 100644 --- a/app/services/ci/abort_pipelines_service.rb +++ b/app/services/ci/abort_pipelines_service.rb @@ -3,7 +3,7 @@ module Ci class AbortPipelinesService # NOTE: This call fails pipelines in bulk without running callbacks. - # Only for pipeline abandonment scenarios (examples: project delete, user block) + # Only for pipeline abandonment scenarios (examples: project delete) def execute(pipelines, failure_reason) pipelines.cancelable.each_batch(of: 100) do |pipeline_batch| now = Time.current diff --git a/app/services/ci/drop_pipeline_service.rb b/app/services/ci/drop_pipeline_service.rb new file mode 100644 index 00000000000..f510943251b --- /dev/null +++ b/app/services/ci/drop_pipeline_service.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +module Ci + class DropPipelineService + # execute service asynchronously for each cancelable pipeline + def execute_async_for_all(pipelines, failure_reason, context_user) + pipelines.cancelable.select(:id).find_in_batches do |pipelines_batch| + Ci::DropPipelineWorker.bulk_perform_async_with_contexts( + pipelines_batch, + arguments_proc: -> (pipeline) { [pipeline.id, failure_reason] }, + context_proc: -> (_) { { user: context_user } } + ) + end + end + + def execute(pipeline, failure_reason, retries: 3) + Gitlab::OptimisticLocking.retry_lock(pipeline.cancelable_statuses, retries, name: 'ci_pipeline_drop_running') do |cancelables| + cancelables.find_in_batches do |batch| + preload_associations_for_drop(batch) + + batch.each do |job| + job.drop(failure_reason) + end + end + end + end + + private + + def preload_associations_for_drop(builds_batch) + ActiveRecord::Associations::Preloader.new.preload( # rubocop: disable CodeReuse/ActiveRecord + builds_batch, + [:project, :pipeline, :metadata, :deployment, :taggings] + ) + end + end +end diff --git a/app/views/admin/runners/show.html.haml b/app/views/admin/runners/show.html.haml index b5940fb41bc..705716c09b7 100644 --- a/app/views/admin/runners/show.html.haml +++ b/app/views/admin/runners/show.html.haml @@ -8,22 +8,10 @@ #js-runner-detail{ data: {runner_id: @runner.id} } - else %h2.page-title - = sprintf(s_('Runners|Runner #%{runner_id}'), {runner_id: @runner.id}) + = s_('Runners|Runner #%{runner_id}' % { runner_id: @runner.id }) + = render 'shared/runners/runner_type_badge', runner: @runner -- if @runner.instance_type? - .bs-callout.bs-callout-success - %h4= _('This runner processes jobs for all unassigned projects.') - %p - = _('If you want a runner to build only specific projects, restrict the project in the table below. After you restrict a runner to a project, you cannot change it back to a shared runner.') -- elsif @runner.group_type? - .bs-callout.bs-callout-success - %h4= _('This runner processes jobs for all projects in its group and subgroups.') -- else - .bs-callout.bs-callout-info - %h4= _('This runner processes jobs for assigned projects only.') - %p - = _('You cannot make this a shared runner.') -%hr += render 'shared/runners/runner_type_alert', runner: @runner .gl-mb-6 = render 'shared/runners/form', runner: @runner, runner_form_url: admin_runner_path(@runner), in_gitlab_com_admin_context: Gitlab.com? diff --git a/app/views/groups/runners/edit.html.haml b/app/views/groups/runners/edit.html.haml index c332009def4..3794c345aa6 100644 --- a/app/views/groups/runners/edit.html.haml +++ b/app/views/groups/runners/edit.html.haml @@ -1,6 +1,9 @@ - page_title _('Edit'), "#{@runner.description} ##{@runner.id}", _('Runners') -%h4 Runner ##{@runner.id} +%h2.page-title + = s_('Runners|Runner #%{runner_id}' % { runner_id: @runner.id }) + = render 'shared/runners/runner_type_badge', runner: @runner -%hr - = render 'shared/runners/form', runner: @runner, runner_form_url: group_runner_path(@group, @runner) += render 'shared/runners/runner_type_alert', runner: @runner + += render 'shared/runners/form', runner: @runner, runner_form_url: group_runner_path(@group, @runner) diff --git a/app/views/layouts/nav/sidebar/_project_menus.html.haml b/app/views/layouts/nav/sidebar/_project_menus.html.haml index aee506ead6c..8f2da398164 100644 --- a/app/views/layouts/nav/sidebar/_project_menus.html.haml +++ b/app/views/layouts/nav/sidebar/_project_menus.html.haml @@ -1,11 +1,3 @@ -- if project_nav_tab? :learn_gitlab - = nav_link(controller: :learn_gitlab, html_options: { class: 'home' }) do - = link_to project_learn_gitlab_path(@project) do - .nav-icon-container - = sprite_icon('home') - %span.nav-item-name - = _('Learn GitLab') - - if project_nav_tab? :files = nav_link(controller: sidebar_repository_paths, unless: -> { current_path?('projects/graphs#charts') }) do = link_to project_tree_path(@project), class: 'shortcuts-tree', data: { qa_selector: "repository_link" } do diff --git a/app/views/projects/runners/_runner.html.haml b/app/views/projects/runners/_runner.html.haml index bc043a5f394..bf2e746b4a4 100644 --- a/app/views/projects/runners/_runner.html.haml +++ b/app/views/projects/runners/_runner.html.haml @@ -1,44 +1,40 @@ -%li.runner{ id: dom_id(runner) } - %h4.gl-font-weight-normal - = runner_status_icon(runner, size: 16, icon_class: "gl-vertical-align-middle!") - - - if @project_runners.include?(runner) - = link_to _("%{token}...") % { token: runner.short_sha }, project_runner_path(@project, runner), class: 'commit-sha has-tooltip', title: _("Partial token for reference only") - +%li{ id: dom_id(runner) } + .gl-display-flex.gl-justify-content-space-between + %div + = runner_status_icon(runner, size: 16) + - if @project_runners.include?(runner) + = link_to "##{runner.id} (#{runner.short_sha})", project_runner_path(@project, runner) + - else + %span + = "##{runner.id} (#{runner.short_sha})" - if runner.locked? %span.has-tooltip{ title: _('Locked to current projects') } = sprite_icon('lock') - - = link_to edit_project_runner_path(@project, runner), class: 'btn gl-button btn-sm btn-icon', data: { testid: 'edit-runner-link' } do - = sprite_icon('pencil') - - - else - %span.commit-sha - = runner.short_sha - - .float-right - - if @project_runners.include?(runner) - - if runner.active? - = link_to _('Pause'), pause_project_runner_path(@project, runner), method: :post, class: 'btn gl-button btn-sm btn-danger', data: { confirm: _("Are you sure?") } - - else - = link_to _('Resume'), resume_project_runner_path(@project, runner), method: :post, class: 'btn gl-button btn-confirm btn-sm' - - if runner.belongs_to_one_project? - = link_to _('Remove runner'), project_runner_path(@project, runner), data: { confirm: _("Are you sure?") }, method: :delete, class: 'btn gl-button btn-danger btn-sm' - - else - - runner_project = @project.runner_projects.find_by(runner_id: runner) # rubocop: disable CodeReuse/ActiveRecord - = link_to _('Disable for this project'), project_runner_project_path(@project, runner_project), data: { confirm: _("Are you sure?") }, method: :delete, class: 'btn gl-button btn-danger btn-sm' - - elsif runner.project_type? - = form_for [@project, @project.runner_projects.new] do |f| - = f.hidden_field :runner_id, value: runner.id - = f.submit _('Enable for this project'), class: 'btn gl-button btn-sm' - .float-right - %small.light - \##{runner.id} + .gl-ml-2 + .btn-group.btn-group-sm + - if @project_runners.include?(runner) + = link_to edit_project_runner_path(@project, runner), class: 'btn gl-button btn-icon', title: _('Edit'), aria: { label: _('Edit') }, data: { testid: 'edit-runner-link', toggle: 'tooltip', placement: 'top', container: 'body' } do + = sprite_icon('pencil') + - if runner.active? + = link_to pause_project_runner_path(@project, runner), method: :post, class: 'btn gl-button btn-icon', title: _('Pause'), aria: { label: _('Pause') }, data: { toggle: 'tooltip', placement: 'top', container: 'body', confirm: _("Are you sure?") } do + = sprite_icon('pause') + - else + = link_to resume_project_runner_path(@project, runner), method: :post, class: 'btn gl-button btn-icon', title: _('Resume'), aria: { label: _('Resume') }, data: { toggle: 'tooltip', placement: 'top', container: 'body' } do + = sprite_icon('play') + - if runner.belongs_to_one_project? + = link_to _('Remove runner'), project_runner_path(@project, runner), data: { confirm: _("Are you sure?") }, method: :delete, class: 'btn gl-button btn-danger' + - else + - runner_project = @project.runner_projects.find_by(runner_id: runner) # rubocop: disable CodeReuse/ActiveRecord + = link_to _('Disable for this project'), project_runner_project_path(@project, runner_project), data: { confirm: _("Are you sure?") }, method: :delete, class: 'btn gl-button btn-danger' + - elsif runner.project_type? + = form_for [@project, @project.runner_projects.new] do |f| + = f.hidden_field :runner_id, value: runner.id + = f.submit _('Enable for this project'), class: 'btn gl-button' - if runner.description.present? - %p.runner-description + %p.gl-my-2 = runner.description - if runner.tags.present? - %p + .gl-my-2 - runner.tags.map(&:name).sort.each do |tag| %span.badge.gl-badge.sm.badge-pill.badge-primary = tag diff --git a/app/views/projects/runners/edit.html.haml b/app/views/projects/runners/edit.html.haml index f93cd23c83e..77150715158 100644 --- a/app/views/projects/runners/edit.html.haml +++ b/app/views/projects/runners/edit.html.haml @@ -1,6 +1,9 @@ -- page_title _('Edit'), "#{@runner.description} ##{@runner.id}", _('runners') +- page_title _('Edit'), "#{@runner.description} ##{@runner.id}", _('Runners') -%h4 Runner ##{@runner.id} +%h2.page-title + = s_('Runners|Runner #%{runner_id}' % { runner_id: @runner.id }) + = render 'shared/runners/runner_type_badge', runner: @runner -%hr - = render 'shared/runners/form', runner: @runner, runner_form_url: project_runner_path(@project, @runner) += render 'shared/runners/runner_type_alert', runner: @runner + += render 'shared/runners/form', runner: @runner, runner_form_url: project_runner_path(@project, @runner) diff --git a/app/views/shared/runners/_runner_type_alert.html.haml b/app/views/shared/runners/_runner_type_alert.html.haml new file mode 100644 index 00000000000..b83def8b802 --- /dev/null +++ b/app/views/shared/runners/_runner_type_alert.html.haml @@ -0,0 +1,20 @@ +.gl-alert.gl-alert-info.gl-my-5 + = sprite_icon('information-o', css_class: 'gl-alert-icon') + - if runner.instance_type? + %h4.gl-alert-title + = s_('Runners|This runner is available to all groups and projects in your GitLab instance.') + .gl-alert-body + = s_('Runners|Shared runners are available to every project in a GitLab instance. If you want a runner to build only specific projects, restrict the project in the table below. After you restrict a runner to a project, you cannot change it back to a shared runner.') + = link_to _('Learn more.'), help_page_path('ci/runners/README', anchor: 'shared-runners'), target: '_blank', rel: 'noopener noreferrer' + - elsif runner.group_type? + %h4.gl-alert-title + = s_('Runners|This runner is available to all projects and subgroups in a group.') + .gl-alert-body + = s_('Runners|Use Group runners when you want all projects in a group to have access to a set of runners.') + = link_to _('Learn more.'), help_page_path('ci/runners/README', anchor: 'group-runners'), target: '_blank', rel: 'noopener noreferrer' + - else + %h4.gl-alert-title + = s_('Runners|This runner is associated with specific projects.') + .gl-alert-body + = s_('Runners|You can set up a specific runner to be used by multiple projects but you cannot make this a shared runner.') + = link_to _('Learn more.'), help_page_path('ci/runners/README', anchor: 'specific-runners'), target: '_blank', rel: 'noopener noreferrer' diff --git a/app/views/shared/runners/_runner_type_badge.html.haml b/app/views/shared/runners/_runner_type_badge.html.haml new file mode 100644 index 00000000000..e0318006f09 --- /dev/null +++ b/app/views/shared/runners/_runner_type_badge.html.haml @@ -0,0 +1,10 @@ + +- if runner.instance_type? + %span.badge.badge-pill.gl-badge.badge-success + = s_('Runners|shared') +- elsif runner.group_type? + %span.badge.badge-pill.gl-badge.badge-success + = s_('Runners|group') +- else + %span.badge.badge-pill.gl-badge.badge-info + = s_('Runners|specific') diff --git a/app/views/shared/runners/show.html.haml b/app/views/shared/runners/show.html.haml index 1af04b808bf..757ec870f79 100644 --- a/app/views/shared/runners/show.html.haml +++ b/app/views/shared/runners/show.html.haml @@ -1,17 +1,8 @@ - page_title "#{@runner.description} ##{@runner.id}", _("Runners") -%h3.page-title - = s_('Runners|Runner #%{id}' % { id: @runner.id }) - .float-right - - if @runner.instance_type? - %span.runner-state.runner-state-shared - = s_('Runners|Shared') - - elsif @runner.group_type? - %span.runner-state.runner-state-shared - = s_('Runners|Group') - - else - %span.runner-state.runner-state-specific - = s_('Runners|Specific') +%h2.page-title + = s_('Runners|Runner #%{runner_id}' % { runner_id: @runner.id }) + = render 'shared/runners/runner_type_badge', runner: @runner .table-holder %table.table diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml index f14497509e1..692adcb6434 100644 --- a/app/workers/all_queues.yml +++ b/app/workers/all_queues.yml @@ -1251,6 +1251,14 @@ :weight: 3 :idempotent: :tags: [] +- :name: pipeline_default:ci_drop_pipeline + :feature_category: :continuous_integration + :has_external_dependencies: + :urgency: :low + :resource_boundary: :unknown + :weight: 3 + :idempotent: true + :tags: [] - :name: pipeline_default:ci_merge_requests_add_todo_when_build_fails :feature_category: :continuous_integration :has_external_dependencies: diff --git a/app/workers/ci/drop_pipeline_worker.rb b/app/workers/ci/drop_pipeline_worker.rb new file mode 100644 index 00000000000..d19157a47e8 --- /dev/null +++ b/app/workers/ci/drop_pipeline_worker.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Ci + class DropPipelineWorker + include ApplicationWorker + include PipelineQueue + + idempotent! + + def perform(pipeline_id, failure_reason) + Ci::Pipeline.find_by_id(pipeline_id).try do |pipeline| + Ci::DropPipelineService.new.execute(pipeline, failure_reason.to_sym) + end + end + end +end |