diff --git a/app/assets/stylesheets/framework/icons.scss b/app/assets/stylesheets/framework/icons.scss index 37a2264122dcf1c714c68dc177b0bc6ad8f823d9..9d09c160551211ef91513d844695331448855708 100644 --- a/app/assets/stylesheets/framework/icons.scss +++ b/app/assets/stylesheets/framework/icons.scss @@ -36,6 +36,7 @@ .ci-status-icon-pending, .ci-status-icon-waiting-for-resource, +.ci-status-icon-waiting-for-callback, .ci-status-icon-failed-with-warnings, .ci-status-icon-success-with-warnings { @include icon-styles($orange-500, $orange-100); diff --git a/app/assets/stylesheets/page_bundles/ci_status.scss b/app/assets/stylesheets/page_bundles/ci_status.scss index 17886ab954a42bef82fe424b571c0452277a33f4..f2129aa6841695450f549a97a478416546444c6a 100644 --- a/app/assets/stylesheets/page_bundles/ci_status.scss +++ b/app/assets/stylesheets/page_bundles/ci_status.scss @@ -48,6 +48,7 @@ &.ci-pending, &.ci-waiting-for-resource, + &.ci-waiting-for-callback, &.ci-failed-with-warnings, &.ci-success-with-warnings { @include status-color( diff --git a/app/graphql/types/ci/pipeline_status_enum.rb b/app/graphql/types/ci/pipeline_status_enum.rb index c8e031e18ead006ed585ea5f54caae6a60670284..17cf48bb5cf95cb1a7413d14590cb592495ec0d4 100644 --- a/app/graphql/types/ci/pipeline_status_enum.rb +++ b/app/graphql/types/ci/pipeline_status_enum.rb @@ -7,6 +7,7 @@ class PipelineStatusEnum < BaseEnum created: 'Pipeline has been created.', waiting_for_resource: 'A resource (for example, a runner) that the pipeline requires to run is unavailable.', preparing: 'Pipeline is preparing to run.', + waiting_for_callback: 'Pipeline is waiting for an external action.', pending: 'Pipeline has not started running yet.', running: 'Pipeline is running.', failed: 'At least one stage of the pipeline failed.', diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index a7909fcedfb154d55cbbeb29a32ed905989efe84..16541adf37062289f2e4e3089fdee774123e00d4 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -190,6 +190,7 @@ class Pipeline < Ci::ApplicationRecord # this is needed to ensure tests to be covered transition [:running] => :running + transition [:waiting_for_callback] => :waiting_for_callback end event :request_resource do @@ -204,6 +205,10 @@ class Pipeline < Ci::ApplicationRecord transition any - [:running] => :running end + event :wait_for_callback do + transition any - [:waiting_for_callback] => :waiting_for_callback + end + event :skip do transition any - [:skipped] => :skipped end @@ -555,7 +560,7 @@ def self.internal_sources end def self.bridgeable_statuses - ::Ci::Pipeline::AVAILABLE_STATUSES - %w[created waiting_for_resource preparing pending] + ::Ci::Pipeline::AVAILABLE_STATUSES - %w[created waiting_for_resource waiting_for_callback preparing pending] end def self.auto_devops_pipelines_completed_total @@ -851,6 +856,7 @@ def set_status(new_status) when 'created' then nil when 'waiting_for_resource' then request_resource when 'preparing' then prepare + when 'waiting_for_callback' then wait_for_callback when 'pending' then enqueue when 'running' then run when 'success' then succeed diff --git a/app/models/ci/stage.rb b/app/models/ci/stage.rb index 3a498972153dd1b5113cdcaea4aa50b5d547373b..3d2df9a45ef127d326465b31dbe599098eb2f363 100644 --- a/app/models/ci/stage.rb +++ b/app/models/ci/stage.rb @@ -78,6 +78,10 @@ class Stage < Ci::ApplicationRecord transition any - [:running] => :running end + event :wait_for_callback do + transition any - [:waiting_for_callback] => :waiting_for_callback + end + event :skip do transition any - [:skipped] => :skipped end @@ -109,6 +113,7 @@ def set_status(new_status) when 'created' then nil when 'waiting_for_resource' then request_resource when 'preparing' then prepare + when 'waiting_for_callback' then wait_for_callback when 'pending' then enqueue when 'running' then run when 'success' then succeed diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb index 1695b407daf7f7761152023462c3413df7f18f41..9f77bd8ebe226e320f6173e6c103bbedb0de134e 100644 --- a/app/models/commit_status.rb +++ b/app/models/commit_status.rb @@ -159,15 +159,15 @@ class CommitStatus < Ci::ApplicationRecord end event :drop do - transition [:created, :waiting_for_resource, :preparing, :pending, :running, :manual, :scheduled] => :failed + transition [:created, :waiting_for_resource, :preparing, :waiting_for_callback, :pending, :running, :manual, :scheduled] => :failed end event :success do - transition [:created, :waiting_for_resource, :preparing, :pending, :running] => :success + transition [:created, :waiting_for_resource, :preparing, :waiting_for_callback, :pending, :running] => :success end event :cancel do - transition [:created, :waiting_for_resource, :preparing, :pending, :running, :manual, :scheduled] => :canceled + transition [:created, :waiting_for_resource, :preparing, :waiting_for_callback, :pending, :running, :manual, :scheduled] => :canceled end before_transition [:created, :waiting_for_resource, :preparing, :skipped, :manual, :scheduled] => :pending do |commit_status| diff --git a/app/models/concerns/ci/has_status.rb b/app/models/concerns/ci/has_status.rb index 2971ecb04b8465c76c7ea87b0937baf6a3cbdbf9..fb2b12e5f0045e1a57cbe5e9bc931aae9e634168 100644 --- a/app/models/concerns/ci/has_status.rb +++ b/app/models/concerns/ci/has_status.rb @@ -6,19 +6,20 @@ module HasStatus DEFAULT_STATUS = 'created' BLOCKED_STATUS = %w[manual scheduled].freeze - AVAILABLE_STATUSES = %w[created waiting_for_resource preparing pending running success failed canceled skipped manual scheduled].freeze + AVAILABLE_STATUSES = %w[created waiting_for_resource preparing waiting_for_callback pending running success failed canceled skipped manual scheduled].freeze STARTED_STATUSES = %w[running success failed].freeze - ACTIVE_STATUSES = %w[waiting_for_resource preparing pending running].freeze + ACTIVE_STATUSES = %w[waiting_for_resource preparing waiting_for_callback pending running].freeze COMPLETED_STATUSES = %w[success failed canceled skipped].freeze STOPPED_STATUSES = COMPLETED_STATUSES + BLOCKED_STATUS - ORDERED_STATUSES = %w[failed preparing pending running waiting_for_resource manual scheduled canceled success skipped created].freeze + ORDERED_STATUSES = %w[failed preparing pending running waiting_for_callback waiting_for_resource manual scheduled canceled success skipped created].freeze PASSED_WITH_WARNINGS_STATUSES = %w[failed canceled].to_set.freeze IGNORED_STATUSES = %w[manual].to_set.freeze ALIVE_STATUSES = (ACTIVE_STATUSES + ['created']).freeze CANCELABLE_STATUSES = (ALIVE_STATUSES + ['scheduled']).freeze STATUSES_ENUM = { created: 0, pending: 1, running: 2, success: 3, failed: 4, canceled: 5, skipped: 6, manual: 7, - scheduled: 8, preparing: 9, waiting_for_resource: 10 }.freeze + scheduled: 8, preparing: 9, waiting_for_resource: 10, + waiting_for_callback: 11 }.freeze UnknownStatusError = Class.new(StandardError) @@ -58,6 +59,7 @@ def stopped_statuses state :created, value: 'created' state :waiting_for_resource, value: 'waiting_for_resource' state :preparing, value: 'preparing' + state :waiting_for_callback, value: 'waiting_for_callback' state :pending, value: 'pending' state :running, value: 'running' state :failed, value: 'failed' @@ -72,6 +74,7 @@ def stopped_statuses scope :waiting_for_resource, -> { with_status(:waiting_for_resource) } scope :preparing, -> { with_status(:preparing) } scope :relevant, -> { without_status(:created) } + scope :waiting_for_callback, -> { with_status(:waiting_for_callback) } scope :running, -> { with_status(:running) } scope :pending, -> { with_status(:pending) } scope :success, -> { with_status(:success) } diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 3cb61f8af1df98976aa56f4a3c393541b79bb30b..17c1c182e72a940c74808c29120bca2974426a4b 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -22395,7 +22395,7 @@ Represents a file or directory in the project repository that has been locked. | <a id="pipelinesourcejob"></a>`sourceJob` | [`CiJob`](#cijob) | Job where pipeline was triggered from. | | <a id="pipelinestages"></a>`stages` | [`CiStageConnection`](#cistageconnection) | Stages of the pipeline. (see [Connections](#connections)) | | <a id="pipelinestartedat"></a>`startedAt` | [`Time`](#time) | Timestamp when the pipeline was started. | -| <a id="pipelinestatus"></a>`status` | [`PipelineStatusEnum!`](#pipelinestatusenum) | Status of the pipeline (CREATED, WAITING_FOR_RESOURCE, PREPARING, PENDING, RUNNING, FAILED, SUCCESS, CANCELED, SKIPPED, MANUAL, SCHEDULED). | +| <a id="pipelinestatus"></a>`status` | [`PipelineStatusEnum!`](#pipelinestatusenum) | Status of the pipeline (CREATED, WAITING_FOR_RESOURCE, PREPARING, WAITING_FOR_CALLBACK, PENDING, RUNNING, FAILED, SUCCESS, CANCELED, SKIPPED, MANUAL, SCHEDULED). | | <a id="pipelinestuck"></a>`stuck` | [`Boolean!`](#boolean) | If the pipeline is stuck. | | <a id="pipelinetestreportsummary"></a>`testReportSummary` | [`TestReportSummary!`](#testreportsummary) | Summary of the test report generated by the pipeline. | | <a id="pipelinetotaljobs"></a>`totalJobs` | [`Int!`](#int) | The total number of jobs in the pipeline. | @@ -27923,6 +27923,7 @@ Values for sorting inherited variables. | <a id="cijobstatusscheduled"></a>`SCHEDULED` | A job that is scheduled. | | <a id="cijobstatusskipped"></a>`SKIPPED` | A job that is skipped. | | <a id="cijobstatussuccess"></a>`SUCCESS` | A job that is success. | +| <a id="cijobstatuswaiting_for_callback"></a>`WAITING_FOR_CALLBACK` | A job that is waiting for callback. | | <a id="cijobstatuswaiting_for_resource"></a>`WAITING_FOR_RESOURCE` | A job that is waiting for resource. | ### `CiJobTokenScopeDirection` @@ -29511,6 +29512,7 @@ Event type of the pipeline associated with a merge request. | <a id="pipelinestatusenumscheduled"></a>`SCHEDULED` | Pipeline is scheduled to run. | | <a id="pipelinestatusenumskipped"></a>`SKIPPED` | Pipeline was skipped. | | <a id="pipelinestatusenumsuccess"></a>`SUCCESS` | Pipeline completed successfully. | +| <a id="pipelinestatusenumwaiting_for_callback"></a>`WAITING_FOR_CALLBACK` | Pipeline is waiting for an external action. | | <a id="pipelinestatusenumwaiting_for_resource"></a>`WAITING_FOR_RESOURCE` | A resource (for example, a runner) that the pipeline requires to run is unavailable. | ### `ProductAnalyticsState` diff --git a/lib/gitlab/ci/status/composite.rb b/lib/gitlab/ci/status/composite.rb index 1ba78b357e53de84f344c85ac8492b8e19ef6327..fe4f6db95490834db57c050a203d2e695e69c4bd 100644 --- a/lib/gitlab/ci/status/composite.rb +++ b/lib/gitlab/ci/status/composite.rb @@ -61,6 +61,8 @@ def status 'running' elsif any_of?(:waiting_for_resource) 'waiting_for_resource' + elsif any_of?(:waiting_for_callback) + 'waiting_for_callback' elsif any_of?(:manual) 'manual' elsif any_of?(:scheduled) diff --git a/lib/gitlab/ci/status/waiting_for_callback.rb b/lib/gitlab/ci/status/waiting_for_callback.rb new file mode 100644 index 0000000000000000000000000000000000000000..0184a910edecba480c606f7cf6ceea9946205fad --- /dev/null +++ b/lib/gitlab/ci/status/waiting_for_callback.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +module Gitlab + module Ci + module Status + class WaitingForCallback < Status::Core + def text + s_('CiStatusText|Waiting') + end + + def label + s_('CiStatusLabel|waiting for callback') + end + + def icon + 'status_pending' + end + + def favicon + 'favicon_status_pending' + end + + def group + 'waiting-for-callback' + end + + def details_path + nil + end + end + end + end +end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index b8ee7c63d1e81a833d91b97c6ac4a0e3c048e878..48df831f014c9c92d3dfb540825f2c526d4a547d 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -10364,6 +10364,9 @@ msgstr "" msgid "CiStatusLabel|skipped" msgstr "" +msgid "CiStatusLabel|waiting for callback" +msgstr "" + msgid "CiStatusLabel|waiting for delayed job" msgstr "" diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb index 867db96aaafd1c923123392995ca5aa7c52d2c43..18415a6079fb5b81617707526a65d01945863e6c 100644 --- a/spec/factories/ci/builds.rb +++ b/spec/factories/ci/builds.rb @@ -117,6 +117,11 @@ status { 'running' } end + trait :waiting_for_callback do + started + status { 'waiting_for_callback' } + end + trait :pending do with_token queued_at { 'Di 29. Okt 09:50:59 CET 2013' } diff --git a/spec/factories/commit_statuses.rb b/spec/factories/commit_statuses.rb index 7d0176d0683bf76b223ba472bc9cb03633dfc1fa..e0b34bc39d8a0d4922333bbcaf211fdaeff0bf16 100644 --- a/spec/factories/commit_statuses.rb +++ b/spec/factories/commit_statuses.rb @@ -32,6 +32,10 @@ status { 'running' } end + trait :waiting_for_callback do + status { 'waiting_for_callback' } + end + trait :pending do status { 'pending' } end diff --git a/spec/helpers/ci/jobs_helper_spec.rb b/spec/helpers/ci/jobs_helper_spec.rb index 884fe7a018e87202c1e989c608684daea3d4e255..af369f7d42064f661921ba7abc77ad76dfdf6e0c 100644 --- a/spec/helpers/ci/jobs_helper_spec.rb +++ b/spec/helpers/ci/jobs_helper_spec.rb @@ -43,6 +43,7 @@ "scheduled" => "SCHEDULED", "skipped" => "SKIPPED", "success" => "SUCCESS", + "waiting_for_callback" => "WAITING_FOR_CALLBACK", "waiting_for_resource" => "WAITING_FOR_RESOURCE" }) end diff --git a/spec/lib/gitlab/ci/status/composite_spec.rb b/spec/lib/gitlab/ci/status/composite_spec.rb index 32b95c433b24ab7eb01bf480948d47b0438a95d4..e46ad5732352ba3ed54f347ae46d86b8c2766963 100644 --- a/spec/lib/gitlab/ci/status/composite_spec.rb +++ b/spec/lib/gitlab/ci/status/composite_spec.rb @@ -57,6 +57,7 @@ %i[pending created skipped success] | true | 'skipped' | false %i[running created skipped success] | true | 'skipped' | false %i[success waiting_for_resource] | false | 'waiting_for_resource' | false + %i[success waiting_for_callback] | false | 'waiting_for_callback' | false %i[success manual] | false | 'manual' | false %i[success scheduled] | false | 'scheduled' | false %i[created preparing] | false | 'preparing' | false diff --git a/spec/lib/gitlab/ci/status/waiting_for_callback_spec.rb b/spec/lib/gitlab/ci/status/waiting_for_callback_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..6c833e9613756ccec81fba52ebaed19cad1e6995 --- /dev/null +++ b/spec/lib/gitlab/ci/status/waiting_for_callback_spec.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Ci::Status::WaitingForCallback, feature_category: :deployment_management do + subject do + described_class.new(double, double) + end + + describe '#text' do + it { expect(subject.text).to eq 'Waiting' } + end + + describe '#label' do + it { expect(subject.label).to eq 'waiting for callback' } + end + + describe '#icon' do + it { expect(subject.icon).to eq 'status_pending' } + end + + describe '#favicon' do + it { expect(subject.favicon).to eq 'favicon_status_pending' } + end + + describe '#group' do + it { expect(subject.group).to eq 'waiting-for-callback' } + end + + describe '#name' do + it { expect(subject.name).to eq 'WAITING_FOR_CALLBACK' } + end + + describe '#details_path' do + it { expect(subject.details_path).to be_nil } + end +end diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index 0094f56d96ea07919b3cbdc0ed339dcc6a0e883f..e0ca7276dc462af2014103ab30d93e521e9abeae 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -2819,7 +2819,7 @@ def create_pipeline(status, ref, sha) subject { described_class.bridgeable_statuses } it { is_expected.to be_an(Array) } - it { is_expected.not_to include('created', 'waiting_for_resource', 'preparing', 'pending') } + it { is_expected.to contain_exactly('running', 'success', 'failed', 'canceled', 'skipped', 'manual', 'scheduled') } end describe '#status', :sidekiq_inline do @@ -3176,6 +3176,7 @@ def create_pipeline(status, ref, sha) %i[ enqueue request_resource + wait_for_callback prepare run skip diff --git a/spec/models/ci/stage_spec.rb b/spec/models/ci/stage_spec.rb index 1be50083cd465b4d92c4559d459a5a775cdbd5d8..4951f57fe6fb8616591620e30d6d14515ca0a7e2 100644 --- a/spec/models/ci/stage_spec.rb +++ b/spec/models/ci/stage_spec.rb @@ -166,6 +166,18 @@ end end + context 'when build is waiting for callback' do + before do + create(:ci_build, :waiting_for_callback, stage_id: stage.id) + end + + it 'updates status to waiting for callback' do + expect { stage.update_legacy_status } + .to change { stage.reload.status } + .to 'waiting_for_callback' + end + end + context 'when stage is skipped because is empty' do it 'updates status to skipped' do expect { stage.update_legacy_status } diff --git a/spec/models/commit_status_spec.rb b/spec/models/commit_status_spec.rb index ac356bcd65a4a3a3dda13a51b93817680e70b9f3..9371fde4d4b98e03bcc196766df86befed434107 100644 --- a/spec/models/commit_status_spec.rb +++ b/spec/models/commit_status_spec.rb @@ -156,7 +156,7 @@ def create_status(**opts) describe '.cancelable' do subject { described_class.cancelable } - %i[running pending waiting_for_resource preparing created scheduled].each do |status| + %i[running pending waiting_for_resource waiting_for_callback preparing created scheduled].each do |status| context "when #{status} commit status" do let!(:commit_status) { create(:commit_status, status, pipeline: pipeline) } diff --git a/spec/models/concerns/ci/has_status_spec.rb b/spec/models/concerns/ci/has_status_spec.rb index 5e0a430aa130f8e299c9cecec997eb181bd8ccfd..95f17c4f85478c3b69985aade68d174146c19cfc 100644 --- a/spec/models/concerns/ci/has_status_spec.rb +++ b/spec/models/concerns/ci/has_status_spec.rb @@ -55,6 +55,22 @@ it { is_expected.to eq 'waiting_for_resource' } end + context 'all waiting for callback' do + let!(:statuses) do + [create(type, status: :waiting_for_callback), create(type, status: :waiting_for_callback)] + end + + it { is_expected.to eq 'waiting_for_callback' } + end + + context 'at least one waiting for callback' do + let!(:statuses) do + [create(type, status: :success), create(type, status: :waiting_for_callback)] + end + + it { is_expected.to eq 'waiting_for_callback' } + end + context 'all preparing' do let!(:statuses) do [create(type, status: :preparing), create(type, status: :preparing)] @@ -225,7 +241,7 @@ end end - %i[created waiting_for_resource preparing running pending success + %i[created waiting_for_callback waiting_for_resource preparing running pending success failed canceled skipped].each do |status| it_behaves_like 'having a job', status end @@ -271,7 +287,7 @@ describe '.alive' do subject { CommitStatus.alive } - %i[running pending waiting_for_resource preparing created].each do |status| + %i[running pending waiting_for_callback waiting_for_resource preparing created].each do |status| it_behaves_like 'containing the job', status end @@ -283,7 +299,7 @@ describe '.alive_or_scheduled' do subject { CommitStatus.alive_or_scheduled } - %i[running pending waiting_for_resource preparing created scheduled].each do |status| + %i[running pending waiting_for_callback waiting_for_resource preparing created scheduled].each do |status| it_behaves_like 'containing the job', status end @@ -319,7 +335,7 @@ describe '.cancelable' do subject { CommitStatus.cancelable } - %i[running pending waiting_for_resource preparing created scheduled].each do |status| + %i[running pending waiting_for_callback waiting_for_resource preparing created scheduled].each do |status| it_behaves_like 'containing the job', status end