From 6994ad8bf9041080c0ecbc896619d94cfa6e5e81 Mon Sep 17 00:00:00 2001 From: Nivedita Priyadarshini Date: Mon, 13 Jan 2025 19:31:00 +0530 Subject: [PATCH 1/8] add last stable status, allow retry for play store submission --- .../submission_component.html.erb | 6 +++ .../submission_component.html.erb | 5 +++ app/models/app_store_submission.rb | 1 + app/models/deprecated_submission.rb | 1 + app/models/google_firebase_submission.rb | 1 + app/models/play_store_submission.rb | 42 ++++++++++++------- app/models/store_submission.rb | 6 +++ app/models/test_flight_submission.rb | 1 + ..._add_retry_fields_for_store_submissions.rb | 5 +++ db/schema.rb | 3 +- 10 files changed, 56 insertions(+), 15 deletions(-) create mode 100644 db/migrate/20250113134320_add_retry_fields_for_store_submissions.rb diff --git a/app/components/v2/live_release/pre_prod_release/submission_component.html.erb b/app/components/v2/live_release/pre_prod_release/submission_component.html.erb index 415a7fdf0..c6ea0500d 100644 --- a/app/components/v2/live_release/pre_prod_release/submission_component.html.erb +++ b/app/components/v2/live_release/pre_prod_release/submission_component.html.erb @@ -3,6 +3,7 @@ <% card.with_action do %> <% if submission.triggerable? %> <%= render V2::ButtonComponent.new(label: "Send to #{submission.display}", + auto_label_case: false, scheme: :default, type: :button, options: trigger_store_submission_path(submission), @@ -13,6 +14,7 @@ <% end %> <% if submission.retryable? %> <%= render V2::ButtonComponent.new(label: "Retry sending to #{submission.display}", + auto_label_case: false, scheme: :default, type: :button, options: retry_store_submission_path(submission), @@ -41,6 +43,10 @@ <% end %> + <% if submission.failed? && submission.last_failed_event.present? %> + <%= render V2::AlertComponent.new(type: :error, title: raw(submission.last_failed_event.message), full_screen: false) %> + <% end %> + <%= render partial: "shared/play_store_review_rejected", locals: { show: submission.failed_with_action_required?, build:, actionable: true, diff --git a/app/components/v2/live_release/prod_release/submission_component.html.erb b/app/components/v2/live_release/prod_release/submission_component.html.erb index 3479f79e7..ba8832d35 100644 --- a/app/components/v2/live_release/prod_release/submission_component.html.erb +++ b/app/components/v2/live_release/prod_release/submission_component.html.erb @@ -145,6 +145,7 @@ <% card.with_action do %> <% if submission.retryable? %> <%= render V2::ButtonComponent.new(label: "Retry sending to #{submission.provider.display}", + auto_label_case: false, scheme: :default, type: :button, options: retry_store_submission_path(submission), @@ -187,6 +188,10 @@ <%= render V2::BadgeComponent.new(**status) %> + <% if submission.failed? && submission.last_failed_event.present? %> + <%= render V2::AlertComponent.new(type: :error, title: raw(submission.last_failed_event.message), full_screen: false) %> + <% end %> + <%= render V2::LiveRelease::BuildComponent.new(current_build, show_number: false, show_metadata: true, diff --git a/app/models/app_store_submission.rb b/app/models/app_store_submission.rb index b6aaa375a..8593076f7 100644 --- a/app/models/app_store_submission.rb +++ b/app/models/app_store_submission.rb @@ -6,6 +6,7 @@ # approved_at :datetime # config :jsonb # failure_reason :string +# last_stable_status :string # name :string # parent_release_type :string indexed => [parent_release_id] # prepared_at :datetime diff --git a/app/models/deprecated_submission.rb b/app/models/deprecated_submission.rb index 34297ec0d..002139cb1 100644 --- a/app/models/deprecated_submission.rb +++ b/app/models/deprecated_submission.rb @@ -6,6 +6,7 @@ # approved_at :datetime # config :jsonb # failure_reason :string +# last_stable_status :string # name :string # parent_release_type :string indexed => [parent_release_id] # prepared_at :datetime diff --git a/app/models/google_firebase_submission.rb b/app/models/google_firebase_submission.rb index 291cbbe1f..262ff2d2b 100644 --- a/app/models/google_firebase_submission.rb +++ b/app/models/google_firebase_submission.rb @@ -6,6 +6,7 @@ # approved_at :datetime # config :jsonb # failure_reason :string +# last_stable_status :string # name :string # parent_release_type :string indexed => [parent_release_id] # prepared_at :datetime diff --git a/app/models/play_store_submission.rb b/app/models/play_store_submission.rb index 09958e217..9cee7cd14 100644 --- a/app/models/play_store_submission.rb +++ b/app/models/play_store_submission.rb @@ -6,6 +6,7 @@ # approved_at :datetime # config :jsonb # failure_reason :string +# last_stable_status :string # name :string # parent_release_type :string indexed => [parent_release_id] # prepared_at :datetime @@ -127,7 +128,7 @@ def triggerable? end def retryable? - failed_with_action_required? + failed? || failed_with_action_required? end def internal_channel? @@ -144,19 +145,16 @@ def trigger! def retry! return unless retryable? - - if provider.build_present_in_channel?(submission_channel_id, build_number) - transaction do - finish_manually! - event_stamp!(reason: :finished_manually, kind: :notice, data: stamp_data) - release_platform_run.unblock_play_store_submissions! - end - - if parent_release.production? - on_prepare! - else - parent_release.rollout_complete!(self) - end + return check_manual_upload if failed_with_action_required? + raise "Not retryable" if last_stable_status.blank? + + case last_stable_status.to_sym + when :preprocessing + trigger! + when :preparing + start_prepare! + else + raise "Not retryable" end end @@ -262,6 +260,22 @@ def fully_release_previous_production_rollout! private + def check_manual_upload + if provider.build_present_in_channel?(submission_channel_id, build_number) + transaction do + finish_manually! + event_stamp!(reason: :finished_manually, kind: :notice, data: stamp_data) + release_platform_run.unblock_play_store_submissions! + end + + if parent_release.production? + on_prepare! + else + parent_release.rollout_complete!(self) + end + end + end + def on_start_prepare! StoreSubmissions::PlayStore::PrepareForReleaseJob.perform_later(id) end diff --git a/app/models/store_submission.rb b/app/models/store_submission.rb index 57a1dde5c..d68d47e64 100644 --- a/app/models/store_submission.rb +++ b/app/models/store_submission.rb @@ -6,6 +6,7 @@ # approved_at :datetime # config :jsonb # failure_reason :string +# last_stable_status :string # name :string # parent_release_type :string indexed => [parent_release_id] # prepared_at :datetime @@ -154,6 +155,10 @@ def finish_rollout_in_next_release? def conf = Config::Submission.from_json(config, read_only: true) + def last_failed_event + passports.where(reason: "failed").last + end + protected def reset_store_info! @@ -165,6 +170,7 @@ def reset_store_info! def set_failure_reason(args = nil) self.failure_reason = args&.fetch(:reason, :unknown_failure) + self.last_stable_status = status end def set_prepared_at! diff --git a/app/models/test_flight_submission.rb b/app/models/test_flight_submission.rb index 2d60c141c..372773147 100644 --- a/app/models/test_flight_submission.rb +++ b/app/models/test_flight_submission.rb @@ -6,6 +6,7 @@ # approved_at :datetime # config :jsonb # failure_reason :string +# last_stable_status :string # name :string # parent_release_type :string indexed => [parent_release_id] # prepared_at :datetime diff --git a/db/migrate/20250113134320_add_retry_fields_for_store_submissions.rb b/db/migrate/20250113134320_add_retry_fields_for_store_submissions.rb new file mode 100644 index 000000000..c620fb2d6 --- /dev/null +++ b/db/migrate/20250113134320_add_retry_fields_for_store_submissions.rb @@ -0,0 +1,5 @@ +class AddRetryFieldsForStoreSubmissions < ActiveRecord::Migration[7.2] + def change + add_column :store_submissions, :last_stable_status, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index e6d6255be..2bf97a508 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.2].define(version: 2025_01_10_160725) do +ActiveRecord::Schema[7.2].define(version: 2025_01_13_134320) do # These are extensions that must be enabled in order to support this database enable_extension "pg_stat_statements" enable_extension "pgcrypto" @@ -846,6 +846,7 @@ t.uuid "parent_release_id" t.jsonb "config" t.integer "sequence_number", limit: 2, default: 0, null: false + t.string "last_stable_status" t.index ["build_id"], name: "index_store_submissions_on_build_id" t.index ["parent_release_type", "parent_release_id"], name: "index_store_submissions_on_parent_release" t.index ["release_platform_run_id"], name: "index_store_submissions_on_release_platform_run_id" From e33bdc2baf238795de8cacbebd4d848483543933 Mon Sep 17 00:00:00 2001 From: Nivedita Priyadarshini Date: Tue, 14 Jan 2025 21:52:09 +0530 Subject: [PATCH 2/8] make the retryt method generic for all submission state machines --- app/models/app_store_submission.rb | 2 ++ app/models/google_firebase_submission.rb | 2 ++ app/models/play_store_submission.rb | 12 +----------- app/models/store_submission.rb | 17 ++++++++++++++++- app/models/test_flight_submission.rb | 2 ++ 5 files changed, 23 insertions(+), 12 deletions(-) diff --git a/app/models/app_store_submission.rb b/app/models/app_store_submission.rb index 8593076f7..992eba596 100644 --- a/app/models/app_store_submission.rb +++ b/app/models/app_store_submission.rb @@ -129,6 +129,8 @@ class AppStoreSubmission < StoreSubmission after_create_commit :poll_external_status + def retryable? = failed? + def pre_review? = PRE_PREPARE_STATES.include?(status) def change_build? = CHANGEABLE_STATES.include?(status) && editable? diff --git a/app/models/google_firebase_submission.rb b/app/models/google_firebase_submission.rb index 262ff2d2b..5c3c2590f 100644 --- a/app/models/google_firebase_submission.rb +++ b/app/models/google_firebase_submission.rb @@ -75,6 +75,8 @@ class GoogleFirebaseSubmission < StoreSubmission end end + def retryable? = failed? + def trigger! return unless actionable? return unless may_prepare? diff --git a/app/models/play_store_submission.rb b/app/models/play_store_submission.rb index 9cee7cd14..7c6e2bdad 100644 --- a/app/models/play_store_submission.rb +++ b/app/models/play_store_submission.rb @@ -144,18 +144,8 @@ def trigger! end def retry! - return unless retryable? return check_manual_upload if failed_with_action_required? - raise "Not retryable" if last_stable_status.blank? - - case last_stable_status.to_sym - when :preprocessing - trigger! - when :preparing - start_prepare! - else - raise "Not retryable" - end + super end def upload_build! diff --git a/app/models/store_submission.rb b/app/models/store_submission.rb index d68d47e64..a1d90c6ec 100644 --- a/app/models/store_submission.rb +++ b/app/models/store_submission.rb @@ -37,7 +37,7 @@ class StoreSubmission < ApplicationRecord belongs_to :release_platform_run belongs_to :parent_release, polymorphic: true # rubocop:disable Rails/InverseOf - belongs_to :production_release, -> { where(store_submissions: {parent_release_type: "ProductionRelease"}) }, foreign_key: "parent_release_id", optional: true + belongs_to :production_release, -> { where(store_submissions: { parent_release_type: "ProductionRelease" }) }, foreign_key: "parent_release_id", optional: true # rubocop:enable Rails/InverseOf belongs_to :build @@ -159,6 +159,21 @@ def last_failed_event passports.where(reason: "failed").last end + def retry! + return unless retryable? + target_state = last_stable_status&.to_sym + raise "Not retryable" if target_state.blank? + + permitted_transitions = aasm.permitted_transitions + permitted_event = permitted_transitions.find { |t| t[:state] == target_state }&.dig(:event) + + if permitted_event + public_send :"#{permitted_event}!" + else + raise "No retries available from the failed state - #{target_state}" + end + end + protected def reset_store_info! diff --git a/app/models/test_flight_submission.rb b/app/models/test_flight_submission.rb index 372773147..4da297cf0 100644 --- a/app/models/test_flight_submission.rb +++ b/app/models/test_flight_submission.rb @@ -81,6 +81,8 @@ class TestFlightSubmission < StoreSubmission end end + def retryable? = failed? + def internal_channel? submission_channel.internal? end From 8089be63f2314671e723d72aa92e18d9c4a1e37f Mon Sep 17 00:00:00 2001 From: Nivedita Priyadarshini Date: Tue, 14 Jan 2025 21:54:20 +0530 Subject: [PATCH 3/8] lint fix --- app/models/store_submission.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/store_submission.rb b/app/models/store_submission.rb index a1d90c6ec..584755d01 100644 --- a/app/models/store_submission.rb +++ b/app/models/store_submission.rb @@ -37,7 +37,7 @@ class StoreSubmission < ApplicationRecord belongs_to :release_platform_run belongs_to :parent_release, polymorphic: true # rubocop:disable Rails/InverseOf - belongs_to :production_release, -> { where(store_submissions: { parent_release_type: "ProductionRelease" }) }, foreign_key: "parent_release_id", optional: true + belongs_to :production_release, -> { where(store_submissions: {parent_release_type: "ProductionRelease"}) }, foreign_key: "parent_release_id", optional: true # rubocop:enable Rails/InverseOf belongs_to :build From da04c8bb77af71cc9298689abecd635998040a78 Mon Sep 17 00:00:00 2001 From: Nivedita Priyadarshini Date: Tue, 14 Jan 2025 22:24:17 +0530 Subject: [PATCH 4/8] handle upload as after pre-process, fix text overflow for alert component --- app/components/v2/alert_component.html.erb | 2 +- app/models/play_store_submission.rb | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/components/v2/alert_component.html.erb b/app/components/v2/alert_component.html.erb index aa73e92b8..dd43abe83 100644 --- a/app/components/v2/alert_component.html.erb +++ b/app/components/v2/alert_component.html.erb @@ -8,7 +8,7 @@ <%= render V2::IconComponent.new("v2/info.svg", size: :md) %> <%= type.to_s.humanize %>: - <%= title %> +
<%= title %>
<% if dismissible %> diff --git a/app/models/play_store_submission.rb b/app/models/play_store_submission.rb index 7c6e2bdad..ff423538f 100644 --- a/app/models/play_store_submission.rb +++ b/app/models/play_store_submission.rb @@ -65,7 +65,7 @@ class PlayStoreSubmission < StoreSubmission state :created, initial: true state(*STATES.keys) - event :preprocess do + event :preprocess, after: :on_preprocess! do transitions from: CHANGEABLE_STATES, to: :preprocessing end @@ -140,7 +140,6 @@ def trigger! preprocess! event_stamp!(reason: :triggered, kind: :notice, data: stamp_data) - StoreSubmissions::PlayStore::UploadJob.perform_later(id) end def retry! @@ -266,6 +265,10 @@ def check_manual_upload end end + def on_preprocess! + StoreSubmissions::PlayStore::UploadJob.perform_later(id) + end + def on_start_prepare! StoreSubmissions::PlayStore::PrepareForReleaseJob.perform_later(id) end From 5e2071dab1818376f723defd379c10e75907535c Mon Sep 17 00:00:00 2001 From: Nivedita Priyadarshini Date: Tue, 14 Jan 2025 23:37:42 +0530 Subject: [PATCH 5/8] handle post processing for store submissions in a callback --- app/components/v2/alert_component.html.erb | 2 +- app/models/google_firebase_submission.rb | 7 +++++-- app/models/test_flight_submission.rb | 7 +++++-- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/app/components/v2/alert_component.html.erb b/app/components/v2/alert_component.html.erb index dd43abe83..e75bf968b 100644 --- a/app/components/v2/alert_component.html.erb +++ b/app/components/v2/alert_component.html.erb @@ -8,7 +8,7 @@ <%= render V2::IconComponent.new("v2/info.svg", size: :md) %> <%= type.to_s.humanize %>: -
<%= title %>
+
<%= title %>
<% if dismissible %> diff --git a/app/models/google_firebase_submission.rb b/app/models/google_firebase_submission.rb index 5c3c2590f..d59b65646 100644 --- a/app/models/google_firebase_submission.rb +++ b/app/models/google_firebase_submission.rb @@ -57,7 +57,7 @@ class GoogleFirebaseSubmission < StoreSubmission state :created, initial: true state(*STATES.keys) - event :preprocess do + event :preprocess, after_commit: :on_preprocess! do transitions from: :created, to: :preprocessing end @@ -85,7 +85,6 @@ def trigger! # return mock_upload_to_firebase if sandbox_mode? preprocess! - StoreSubmissions::GoogleFirebase::UploadJob.perform_later(id) end def upload_build! @@ -160,6 +159,10 @@ def prepare_and_update!(release_info, build_status = nil) end end + def on_preprocess! + StoreSubmissions::GoogleFirebase::UploadJob.perform_later(id) + end + def on_prepare! StoreSubmissions::GoogleFirebase::PrepareForReleaseJob.perform_later(id) end diff --git a/app/models/test_flight_submission.rb b/app/models/test_flight_submission.rb index 4da297cf0..fe7a556d0 100644 --- a/app/models/test_flight_submission.rb +++ b/app/models/test_flight_submission.rb @@ -57,7 +57,7 @@ class TestFlightSubmission < StoreSubmission state :created, initial: true state(*STATES.keys) - event :preprocess do + event :preprocess, after_commit: :on_preprocess! do transitions from: :created, to: :preprocessing end @@ -93,7 +93,6 @@ def trigger! event_stamp!(reason: :triggered, kind: :notice, data: stamp_data) # return mock_start_release_in_testflight if sandbox_mode? preprocess! - StoreSubmissions::TestFlight::FindBuildJob.perform_async(id) end def start_release! @@ -174,6 +173,10 @@ def update_store_info!(release_info) save! end + def on_preprocess! + StoreSubmissions::TestFlight::FindBuildJob.perform_async(id) + end + def on_reject! event_stamp!(reason: :review_rejected, kind: :error, data: stamp_data) end From 4213f731e746a79b3e98522ef4233461cd026df7 Mon Sep 17 00:00:00 2001 From: Nivedita Priyadarshini Date: Tue, 14 Jan 2025 23:52:58 +0530 Subject: [PATCH 6/8] refactor --- app/models/app_store_submission.rb | 4 ++-- app/models/google_firebase_submission.rb | 2 +- app/models/play_store_submission.rb | 4 ++-- app/models/store_submission.rb | 2 +- app/models/test_flight_submission.rb | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/models/app_store_submission.rb b/app/models/app_store_submission.rb index 992eba596..1ef080cac 100644 --- a/app/models/app_store_submission.rb +++ b/app/models/app_store_submission.rb @@ -91,7 +91,7 @@ class AppStoreSubmission < StoreSubmission transitions from: :preparing, to: :prepared end - event :fail_prepare, before: :set_failure_reason, after_commit: :on_fail_prepare! do + event :fail_prepare, before: :set_failure_context, after_commit: :on_fail_prepare! do transitions from: :preparing, to: :failed_prepare end @@ -122,7 +122,7 @@ class AppStoreSubmission < StoreSubmission transitions from: [:submitted_for_review, :approved, :cancelling], to: :cancelled end - event :fail, before: :set_failure_reason, after_commit: :on_fail! do + event :fail, before: :set_failure_context, after_commit: :on_fail! do transitions to: :failed end end diff --git a/app/models/google_firebase_submission.rb b/app/models/google_firebase_submission.rb index d59b65646..e97f140ef 100644 --- a/app/models/google_firebase_submission.rb +++ b/app/models/google_firebase_submission.rb @@ -70,7 +70,7 @@ class GoogleFirebaseSubmission < StoreSubmission transitions to: :finished end - event :fail, before: :set_failure_reason, after_commit: :on_fail! do + event :fail, before: :set_failure_context, after_commit: :on_fail! do transitions to: :failed end end diff --git a/app/models/play_store_submission.rb b/app/models/play_store_submission.rb index ff423538f..3a7782ed6 100644 --- a/app/models/play_store_submission.rb +++ b/app/models/play_store_submission.rb @@ -87,11 +87,11 @@ class PlayStoreSubmission < StoreSubmission transitions from: :prepared, to: :review_failed end - event :fail, before: :set_failure_reason, after_commit: :on_fail! do + event :fail, before: :set_failure_context, after_commit: :on_fail! do transitions to: :failed end - event :fail_with_sync_option, before: :set_failure_reason do + event :fail_with_sync_option, before: :set_failure_context do transitions from: [:preparing, :prepared, :failed_with_action_required, :finished_manually], to: :failed_with_action_required end diff --git a/app/models/store_submission.rb b/app/models/store_submission.rb index 584755d01..52d8ac213 100644 --- a/app/models/store_submission.rb +++ b/app/models/store_submission.rb @@ -183,7 +183,7 @@ def reset_store_info! save! end - def set_failure_reason(args = nil) + def set_failure_context(args = nil) self.failure_reason = args&.fetch(:reason, :unknown_failure) self.last_stable_status = status end diff --git a/app/models/test_flight_submission.rb b/app/models/test_flight_submission.rb index fe7a556d0..1180a03f9 100644 --- a/app/models/test_flight_submission.rb +++ b/app/models/test_flight_submission.rb @@ -71,7 +71,7 @@ class TestFlightSubmission < StoreSubmission transitions from: :submitted_for_review, to: :review_failed end - event :fail, before: :set_failure_reason, after_commit: :on_fail! do + event :fail, before: :set_failure_context, after_commit: :on_fail! do transitions to: :failed end From a03e0611d481441f380f264152bf13218e569a34 Mon Sep 17 00:00:00 2001 From: Nivedita Priyadarshini Date: Wed, 15 Jan 2025 00:00:08 +0530 Subject: [PATCH 7/8] Allow retries from some more states --- app/models/google_firebase_submission.rb | 2 +- app/models/test_flight_submission.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/models/google_firebase_submission.rb b/app/models/google_firebase_submission.rb index e97f140ef..ca67d414f 100644 --- a/app/models/google_firebase_submission.rb +++ b/app/models/google_firebase_submission.rb @@ -58,7 +58,7 @@ class GoogleFirebaseSubmission < StoreSubmission state(*STATES.keys) event :preprocess, after_commit: :on_preprocess! do - transitions from: :created, to: :preprocessing + transitions from: [:created, :failed], to: :preprocessing end event :prepare, after_commit: :on_prepare! do diff --git a/app/models/test_flight_submission.rb b/app/models/test_flight_submission.rb index 1180a03f9..d1538f1fd 100644 --- a/app/models/test_flight_submission.rb +++ b/app/models/test_flight_submission.rb @@ -58,12 +58,12 @@ class TestFlightSubmission < StoreSubmission state(*STATES.keys) event :preprocess, after_commit: :on_preprocess! do - transitions from: :created, to: :preprocessing + transitions from: [:created, :failed], to: :preprocessing end event :submit_for_review, after_commit: :on_submit_for_review! do after { set_submitted_at! } - transitions from: [:created, :preprocessing], to: :submitted_for_review + transitions from: [:created, :preprocessing, :failed], to: :submitted_for_review end event :reject, after_commit: :on_reject! do From 54506eadda2ed14c138b6163d1adeaa348f9cbbb Mon Sep 17 00:00:00 2001 From: Nivedita Priyadarshini Date: Wed, 15 Jan 2025 13:06:37 +0530 Subject: [PATCH 8/8] ui fix --- app/components/v2/alert_component.html.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/components/v2/alert_component.html.erb b/app/components/v2/alert_component.html.erb index e75bf968b..ce19ebd44 100644 --- a/app/components/v2/alert_component.html.erb +++ b/app/components/v2/alert_component.html.erb @@ -8,7 +8,7 @@ <%= render V2::IconComponent.new("v2/info.svg", size: :md) %> <%= type.to_s.humanize %>: -
<%= title %>
+
<%= title %>
<% if dismissible %>