- <% descriptions.each_with_index do |desc, idx| %>
-
- <%= desc %>
-
- <% end %>
-
diff --git a/app/components/meta_table_component.rb b/app/components/meta_table_component.rb
deleted file mode 100644
index 3dea1fa6f..000000000
--- a/app/components/meta_table_component.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-class MetaTableComponent < ViewComponent::Base
- renders_many :descriptions, MetaTable::MetaDescriptionComponent
-
- def bg_color(idx)
- idx.odd? ? "bg-white" : "bg-slate-50"
- end
-end
diff --git a/app/components/notification_settings_component.rb b/app/components/notification_settings_component.rb
index 35ade1cfb..e0b54425f 100644
--- a/app/components/notification_settings_component.rb
+++ b/app/components/notification_settings_component.rb
@@ -1,28 +1,12 @@
class NotificationSettingsComponent < ViewComponent::Base
include ApplicationHelper
- include ButtonHelper
- include AssetsHelper
InvalidNotificationSettings = Class.new(StandardError)
NOTIFICATIONS = {
release_scheduled: {icon: "v2/clock.svg", description: "Your scheduled release will run in a few hours"},
release_started: {icon: "v2/zap.svg", description: "A new release was started for the release train"},
- step_started: {icon: "v2/play.svg", description: "A step was started for the release train"},
- build_available: {icon: "v2/drill.svg", description: "A new build is available for a direct download"},
- step_failed: {icon: "v2/alert_circle.svg", description: "A step failed to run fully for the release train"},
backmerge_failed: {icon: "v2/alert_circle.svg", description: "Tramline failed to create a backmerge PR for the commit in the release"},
- submit_for_review: {icon: "v2/clipboard_list.svg", description: "A build was submitted for review to store for the release"},
- review_approved: {icon: "v2/clipboard_check.svg", description: "A production build review was approved by the store"},
- review_failed: {icon: "v2/alert_circle.svg", description: "A production build review was rejected by the store"},
- staged_rollout_updated: {icon: "v2/arrow_big_up_dash.svg", description: "The staged rollout was increased for the production build in the store"},
- staged_rollout_paused: {icon: "v2/pause.svg", description: "The staged rollout was paused for the production build in the store"},
- staged_rollout_resumed: {icon: "v2/play.svg", description: "The staged rollout was resumed for the production build in the store"},
- staged_rollout_halted: {icon: "v2/stop_circle.svg", description: "The staged rollout was halted for the production build in the store"},
- staged_rollout_completed: {icon: "v2/sparkles.svg", description: "The staged rollout was completed for the production build in the store"},
- staged_rollout_fully_released: {icon: "v2/fast_forward.svg", description: "The staged rollout was fully released to 100% for the production build in the store"},
- deployment_finished: {icon: "v2/truck.svg", description: "The distribution was successful to a channel"},
- deployment_failed: {icon: "v2/alert_circle.svg", description: "The distribution to a channel failed"},
release_ended: {icon: "v2/sparkles.svg", description: "The release finished successfully"},
release_stopped: {icon: "v2/stop_circle.svg", description: "The release was stopped before it finished"},
release_health_events: {icon: "v2/heart_pulse.svg", description: "A health event has occurred for the release"},
diff --git a/app/components/release_monitoring_component.html.erb b/app/components/release_monitoring_component.html.erb
deleted file mode 100644
index 30bece8ec..000000000
--- a/app/components/release_monitoring_component.html.erb
+++ /dev/null
@@ -1,98 +0,0 @@
-
- <% if show_version_info %>
-
<%= build_identifier %>
- <% end %>
-
-
- <% if :staged_rollout.in? metrics %>
- <% if staged_rollout.nil? %>
- <%= render StatCardComponent.new("Staged Rollout",
- type: :empty,
- empty_stat_help_text: "This data loads up when the staged/phased rollout begins.") %>
- <% else %>
- <%= render ProgressCardComponent.new(name: "Staged Rollout",
- current: staged_rollout_percentage,
- subtitle: staged_rollout_text,
- provider: store_provider,
- size:,
- external_url: external_link) %>
- <% end %>
- <% end %>
-
- <% if :adoption_rate.in? metrics %>
- <% if empty_component? %>
- <%= render StatCardComponent.new("Adoption Rate",
- type: :empty,
- empty_stat_help_text: "This data gets pulled from your monitoring integration.") %>
- <% else %>
- <%= render ProgressCardComponent.new(name: "Adoption Rate",
- current: adoption_rate,
- subtitle: "Last 24 hours",
- provider: monitoring_provider,
- size:,
- external_url: external_link) %>
- <% end %>
- <% end %>
-
- <% if :stability.in? metrics %>
- <% if empty_component? %>
- <%= render StatCardComponent.new("Stability",
- type: :empty,
- empty_stat_help_text: "This data gets pulled from your monitoring integration.") %>
- <% else %>
- <%= render MetricCardComponent.new(name: "Stability",
- values: { "users" => user_stability, "sessions" => session_stability },
- provider: monitoring_provider,
- size:,
- external_url: monitoring_provider_url) %>
- <% end %>
- <% end %>
-
- <% if :errors.in? metrics %>
- <% if empty_component? %>
- <%= render StatCardComponent.new("Errors",
- type: :empty,
- empty_stat_help_text: "This data gets pulled from your monitoring integration.") %>
- <% else %>
- <%= render MetricCardComponent.new(name: "Errors",
- values: { "total" => errors_count, "new" => new_errors_count },
- provider: monitoring_provider,
- size:,
- external_url: monitoring_provider_url) %>
- <% end %>
- <% end %>
-
- <% if show_release_health? %>
-
-
diff --git a/app/components/staged_rollout_component.rb b/app/components/staged_rollout_component.rb
deleted file mode 100644
index bc4771891..000000000
--- a/app/components/staged_rollout_component.rb
+++ /dev/null
@@ -1,125 +0,0 @@
-class StagedRolloutComponent < ViewComponent::Base
- include ApplicationHelper
- include ButtonHelper
- include AssetsHelper
-
- HALT_CONFIRM = "You are about to halt the rollout of this build to production.\n\nAre you sure?"
- START_RELEASE_CONFIRM = "You are about to release this build to the first stage in production.\n\nAre you sure?"
- RELEASE_CONFIRM = "You are about to release this build to the next stage in production.\n\nAre you sure?"
- FULLY_RELEASE_CONFIRM = "You are about to release this build to all users in production.\n\nAre you sure?"
- PAUSE_RELEASE_CONFIRM = "You are about to pause the scheduled phased release in production.\n\nAre you sure?"
- RESUME_RELEASE_CONFIRM = "You are about to resume the scheduled phased release in production.\n\nAre you sure?"
- RESUME_HALTED_CONFIRM = "You are about to resume the halted rollout of this build in production.\n\nAre you sure?"
-
- def initialize(staged_rollout)
- @staged_rollout = staged_rollout
- @deployment_run = @staged_rollout.deployment_run
- @step_run = @deployment_run.step_run
- @release = @step_run.release
- end
-
- delegate :started?,
- :failed?,
- :stopped?,
- :completed?,
- :paused?,
- :fully_released?,
- :config,
- :started?,
- :created?,
- :current_stage,
- :last_rollout_percentage,
- to: :staged_rollout
- delegate :controllable_rollout?, :rolloutable?, :automatic_rollout?, to: :deployment_run
-
- def actions
- actions = []
-
- if controllable_rollout?
- actions << {form_url: increase_release_path, confirm: START_RELEASE_CONFIRM, type: :blue, name: "Start Rollout"} if created?
- actions << {form_url: increase_release_path, confirm: RELEASE_CONFIRM, type: :blue, name: "Increase Rollout"} if started?
- actions << {form_url: increase_release_path, confirm: RELEASE_CONFIRM, type: :blue, name: "Retry"} if failed?
- actions << {form_url: resume_release_path, confirm: RESUME_HALTED_CONFIRM, type: :blue, name: "Resume Rollout"} if stopped?
- end
-
- if automatic_rollout?
- actions << {form_url: resume_release_path, confirm: RESUME_RELEASE_CONFIRM, type: :blue, name: "Resume Phased Release"} if paused?
- actions << {form_url: pause_release_path, confirm: PAUSE_RELEASE_CONFIRM, type: :amber, name: "Pause Phased Release"} if started? && last_rollout_percentage
- end
-
- if last_rollout_percentage
- actions << {form_url: halt_release_path, confirm: HALT_CONFIRM, type: :red, name: "Halt Release"} if started? || paused?
- actions << {form_url: full_release_path, confirm: FULLY_RELEASE_CONFIRM, type: :blue, name: "Release to 100%"} if started?
- end
-
- actions
- end
-
- def current_stage_perc
- return "0%" if last_rollout_percentage.nil?
- "#{last_rollout_percentage}%"
- end
-
- def stage_help
- return if completed? || fully_released?
- return "Halted at the #{current_stage.succ.ordinalize} stage of rollout" if stopped?
- return "Paused at the #{current_stage.succ.ordinalize} stage of rollout" if paused?
-
- if started?
- "In the #{current_stage.succ.ordinalize} stage of rollout"
- else
- "Rollout has not kicked-off yet"
- end
- end
-
- private
-
- delegate :writer?, to: :helpers
- attr_reader :release, :step_run, :deployment_run, :staged_rollout
-
- def increase_release_path
- increase_deployment_run_staged_rollout_path(deployment_run)
- end
-
- def halt_release_path
- halt_deployment_run_staged_rollout_path(deployment_run)
- end
-
- def pause_release_path
- pause_deployment_run_staged_rollout_path(deployment_run)
- end
-
- def resume_release_path
- resume_deployment_run_staged_rollout_path(deployment_run)
- end
-
- def full_release_path
- fully_release_deployment_run_staged_rollout_path(deployment_run)
- end
-
- def badge
- status = staged_rollout.status.to_sym
-
- status, styles =
- case status
- when :created
- ["Ready", :routine]
- when :started
- ["Active", :ongoing]
- when :failed
- ["Failed", :failure]
- when :completed
- ["Completed", :success]
- when :stopped
- ["Halted", :inert]
- when :fully_released
- ["Released to all users", :success]
- when :paused
- ["Paused phased release", :ongoing]
- else
- ["Unknown", :neutral]
- end
-
- status_badge(status, styles)
- end
-end
diff --git a/app/components/tab_group_component.html.erb b/app/components/tab_group_component.html.erb
deleted file mode 100644
index bdf3ba65b..000000000
--- a/app/components/tab_group_component.html.erb
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
- <% tab_headings.each do |tab| %>
-
-
-
- <% end %>
-
-
-
- <% tabs.each do |tab| %>
- <%= tab %>
- <% end %>
-
<%= render V2::AlertComponent.new(type: :info, title: "You can edit the release notes until you prepare for review. Once prepared, you won't be able to change the release notes.", full_screen: false) %>
diff --git a/app/components/v2/release_list_component.rb b/app/components/v2/release_list_component.rb
index e367ac546..5c2934aa7 100644
--- a/app/components/v2/release_list_component.rb
+++ b/app/components/v2/release_list_component.rb
@@ -40,7 +40,7 @@ def release_startable?
end
def release_options
- return [] unless train.manually_startable?
+ return [] if train.inactive?
upcoming_release_startable = train.upcoming_release_startable?
return [] if train.ongoing_release && !upcoming_release_startable
@@ -117,13 +117,8 @@ def no_release_empty_state
end
else
platform = train.release_platforms.first.platform
- text =
- if train.product_v2?
- "You can now start creating new releases. We have added some default submissions settings for you. This involves picking the right workflows and configuring the right channels for build distribution. Please review these before starting a release."
- else
- "You can now start creating new releases. Please review the release steps and submissions settings before starting a release."
- end
- button_link = train.product_v2? ? edit_app_train_platform_submission_config_path(app, train, platform) : steps_app_train_path(app, train)
+ text = "You can now start creating new releases. We have added some default submissions settings for you. This involves picking the right workflows and configuring the right channels for build distribution. Please review these before starting a release."
+ button_link = edit_app_train_platform_submission_config_path(app, train, platform)
{
title: "Create your very first release",
text:,
diff --git a/app/components/v2/release_overview_component.html.erb b/app/components/v2/release_overview_component.html.erb
index 59e474220..8cd1a4d7a 100644
--- a/app/components/v2/release_overview_component.html.erb
+++ b/app/components/v2/release_overview_component.html.erb
@@ -87,10 +87,6 @@
- <% if release.is_v2? %>
- <%= render V2::PlatformOverviewV2Component.new(release, occupy: false) %>
- <% else %>
- <%= render V2::PlatformOverviewComponent.new(release, occupy: false) %>
- <% end %>
+ <%= render V2::PlatformOverviewV2Component.new(release, occupy: false) %>
<% end %>
<% end %>
diff --git a/app/controllers/api/v1/apps_controller.rb b/app/controllers/api/v1/apps_controller.rb
index 8ebec6a63..b8cb88249 100644
--- a/app/controllers/api/v1/apps_controller.rb
+++ b/app/controllers/api/v1/apps_controller.rb
@@ -10,10 +10,6 @@ def app
end
def latest_store_version
- if app.production_store_rollouts.none?
- return app.latest_store_step_runs.map(&:release_info).group_by { _1[:platform] }
- end
-
app.production_store_rollouts
.group_by(&:platform)
.transform_values { |rollouts| rollouts.max_by(&:updated_at) }
diff --git a/app/controllers/api/v1/builds_controller.rb b/app/controllers/api/v1/builds_controller.rb
index 853a031f9..01510ffca 100644
--- a/app/controllers/api/v1/builds_controller.rb
+++ b/app/controllers/api/v1/builds_controller.rb
@@ -2,17 +2,11 @@ class Api::V1::BuildsController < ApiController
def external_metadata
render json: {error: "No metadata provided"}, status: :unprocessable_entity and return if build_params[:external_metadata].blank?
- step_run = app.step_runs.where(build_number: build_params[:version_code], build_version: build_params[:version_name]).first
build = app.builds.where(build_number: build_params[:version_code], version_name: build_params[:version_name]).first
- raise ActiveRecord::RecordNotFound if step_run.blank? && build.blank?
+ raise ActiveRecord::RecordNotFound if build.blank?
- new_metadata =
- if step_run.present?
- ExternalBuild.find_or_initialize_by(step_run:).update_or_insert!(build_params[:external_metadata].map(&:to_h))
- else
- ExternalBuild.find_or_initialize_by(build:).update_or_insert!(build_params[:external_metadata].map(&:to_h))
- end
+ new_metadata = ExternalBuild.find_or_initialize_by(build:).update_or_insert!(build_params[:external_metadata].map(&:to_h))
if new_metadata.errors.present?
render json: {error: new_metadata.errors}, status: :unprocessable_entity
diff --git a/app/controllers/api/v1/releases_controller.rb b/app/controllers/api/v1/releases_controller.rb
index bdc8ed25b..504032e2e 100644
--- a/app/controllers/api/v1/releases_controller.rb
+++ b/app/controllers/api/v1/releases_controller.rb
@@ -7,20 +7,9 @@ def show
private
def all_versions
- store_releases = releases.flat_map do |release|
- if release.is_v2?
- release
- .production_store_rollouts
- .reorder(:updated_at)
- .flat_map(&:release_info)
- else
- release
- .all_store_step_runs
- &.map(&:release_info)
- end
- end
-
- store_releases.group_by { |sr| sr[:platform] }
+ releases
+ .flat_map { |release| release.production_store_rollouts.reorder(:updated_at).flat_map(&:release_info) }
+ .then { |store_releases| store_releases.group_by { _1[:platform] } }
end
def releases
diff --git a/app/controllers/app_configs_controller.rb b/app/controllers/app_configs_controller.rb
index 4660968c2..e2bc46990 100644
--- a/app/controllers/app_configs_controller.rb
+++ b/app/controllers/app_configs_controller.rb
@@ -39,7 +39,6 @@ def pick_category
when Integration.categories[:version_control] then configure_version_control
when Integration.categories[:ci_cd] then configure_ci_cd
when Integration.categories[:monitoring] then configure_monitoring
- when Integration.categories[:notification] then configure_notification_channel
when Integration.categories[:build_channel] then configure_build_channel
else raise "Invalid integration category."
end
@@ -53,10 +52,6 @@ def configure_version_control
set_code_repositories if further_setup_by_category?.dig(:version_control, :further_setup)
end
- def configure_notification_channel
- set_notification_channels if @app.notifications_set_up?
- end
-
def configure_ci_cd
set_ci_cd_projects if further_setup_by_category?.dig(:ci_cd, :further_setup)
end
@@ -78,7 +73,6 @@ def app_config_params
.require(:app_config)
.permit(
:code_repository,
- :notification_channel,
:bitrise_project_id,
:firebase_android_config,
:firebase_ios_config,
@@ -93,7 +87,6 @@ def app_config_params
def parsed_app_config_params
app_config_params
.merge(code_repository: app_config_params[:code_repository]&.safe_json_parse)
- .merge(notification_channel: app_config_params[:notification_channel]&.safe_json_parse)
.merge(bitrise_project_id: app_config_params[:bitrise_project_id]&.safe_json_parse)
.merge(bugsnag_config(app_config_params.slice(*BUGSNAG_CONFIG_PARAMS)))
.merge(firebase_ios_config: app_config_params[:firebase_ios_config]&.safe_json_parse)
@@ -121,10 +114,6 @@ def set_code_repositories
@code_repositories = @app.vcs_provider.repos(workspace)
end
- def set_notification_channels
- @notification_channels = @app.notification_provider.channels if @app.notifications_set_up?
- end
-
def set_integration_category
if Integration.categories.key?(params[:integration_category])
@integration_category = params[:integration_category]
diff --git a/app/controllers/build_queues_controller.rb b/app/controllers/build_queues_controller.rb
index 2e57f1743..b9ab0ec47 100644
--- a/app/controllers/build_queues_controller.rb
+++ b/app/controllers/build_queues_controller.rb
@@ -5,20 +5,10 @@ class BuildQueuesController < SignedInApplicationController
before_action :set_build_queue, only: %i[apply]
def apply
- if @release.is_v2?
- if (result = Action.apply_build_queue!(@build_queue)).ok?
- redirect_to changeset_tracking_release_path(@release), notice: "Build queue has been applied and emptied."
- else
- redirect_to current_release_path, flash: {error: t(".failure", errors: result.error.message)}
- end
+ if (result = Action.apply_build_queue!(@build_queue)).ok?
+ redirect_to changeset_tracking_release_path(@release), notice: "Build queue has been applied and emptied."
else
- @release.with_lock do
- locked_release_error and return unless @release.committable?
- already_triggered_error and return unless @build_queue.is_active?
- @build_queue.apply!
- end
-
- redirect_to current_release_path, notice: "Build queue has been applied and emptied."
+ redirect_to current_release_path, flash: {error: t(".failure", errors: result.error.message)}
end
end
diff --git a/app/controllers/commits_controller.rb b/app/controllers/commits_controller.rb
deleted file mode 100644
index ffcf8f9a7..000000000
--- a/app/controllers/commits_controller.rb
+++ /dev/null
@@ -1,56 +0,0 @@
-class CommitsController < SignedInApplicationController
- around_action :set_time_zone
- before_action :require_write_access!, only: %i[apply]
- before_action :set_release, only: %i[apply]
- before_action :set_commit, only: %i[apply]
- before_action :set_release_platform_run, only: %i[apply]
- before_action :ensure_release_platform_run, only: %i[apply]
-
- def apply
- @release_platform_run.with_lock do
- locked_release_error and return unless @release_platform_run.on_track?
- already_triggered_error and return if @release_platform_run.commit_applied?(@commit)
- @commit.trigger_step_runs_for(@release_platform_run, force: true)
- end
-
- redirect_to current_release_path, notice: "Steps have been triggered for the commit."
- end
-
- private
-
- def already_triggered_error
- redirect_to current_release_path, flash: {error: "Cannot re-apply a commit to a release!"}
- end
-
- def locked_release_error
- redirect_to current_release_path, flash: {error: "Cannot apply a commit to a locked release."}
- end
-
- def ensure_release_platform_run
- if @release_platform_run.blank?
- redirect_to current_release_path, flash: {error: "Could not find the release!"}
- end
- end
-
- def set_release_platform_run
- @release_platform_run = @release.release_platform_runs.find_by(id: commit_params[:release_platform_run_id])
- end
-
- def set_commit
- @commit = Commit.find(params[:id])
- end
-
- def set_release
- @release = Release.friendly.find(params[:release_id])
- end
-
- def commit_params
- params.require(:commit).permit(
- :release_platform_run_id
- )
- end
-
- def current_release_path
- release_path(@release)
- end
-end
diff --git a/app/controllers/concerns/tabbable.rb b/app/controllers/concerns/tabbable.rb
index 17a5ff062..0c5c826e6 100644
--- a/app/controllers/concerns/tabbable.rb
+++ b/app/controllers/concerns/tabbable.rb
@@ -15,10 +15,9 @@ def set_app_config_tabs
def set_train_config_tabs
@tab_configuration = [
[1, "Release Settings", edit_app_train_path(@app, @train), "v2/cog.svg"],
- ([2, "Steps", steps_app_train_path(@app, @train), "v2/route.svg"] unless v2?),
- ([2, "Android Flow Settings", edit_app_train_platform_submission_config_path(@app, @train, :android), "v2/logo_google_play_store_bw.svg"] if @app.cross_platform? && v2?),
- ([3, "iOS Flow Settings", edit_app_train_platform_submission_config_path(@app, @train, :ios), "v2/logo_app_store_bw.svg"] if @app.cross_platform? && v2?),
- ([2, "Submission Settings", edit_app_train_platform_submission_config_path(@app, @train, @app.platform), "v2/sliders.svg"] if v2? && !@app.cross_platform? && v2?),
+ ([2, "Android Flow Settings", edit_app_train_platform_submission_config_path(@app, @train, :android), "v2/logo_google_play_store_bw.svg"] if @app.cross_platform?),
+ ([3, "iOS Flow Settings", edit_app_train_platform_submission_config_path(@app, @train, :ios), "v2/logo_app_store_bw.svg"] if @app.cross_platform?),
+ ([2, "Submission Settings", edit_app_train_platform_submission_config_path(@app, @train, @app.platform), "v2/sliders.svg"] unless @app.cross_platform?),
[4, "Notification Settings", app_train_notification_settings_path(@app, @train), "bell.svg"],
[5, "Release Health Rules", rules_app_train_path(@app, @train), "v2/heart_pulse.svg"],
[6, "Reldex Settings", edit_app_train_release_index_path(@app, @train), "v2/ruler.svg"]
diff --git a/app/controllers/deployment_runs_controller.rb b/app/controllers/deployment_runs_controller.rb
deleted file mode 100644
index 11b6c93a5..000000000
--- a/app/controllers/deployment_runs_controller.rb
+++ /dev/null
@@ -1,63 +0,0 @@
-class DeploymentRunsController < SignedInApplicationController
- before_action :set_deployment_run
- before_action :ensure_reviewable, only: [:submit_for_review]
- before_action :ensure_releasable, only: [:start_release]
- before_action :ensure_preparable, only: [:prepare_release]
-
- def submit_for_review
- Deployments::AppStoreConnect::Release.submit_for_review!(@deployment_run)
-
- if @deployment_run.failed?
- redirect_back fallback_location: root_path, flash: {error: "Failed to submit for review due to #{@deployment_run.display_attr(:failure_reason)}"}
- else
- redirect_back fallback_location: root_path, notice: "Submitted for review!"
- end
- end
-
- def start_release
- @deployment_run.start_release!
-
- if @deployment_run.failed?
- redirect_back fallback_location: root_path, flash: {error: "Failed to start the release due to #{@deployment_run.display_attr(:failure_reason)}"}
- else
- redirect_back fallback_location: root_path, notice: "The release has kicked-off!"
- end
- end
-
- def prepare_release
- @deployment_run.start_prepare_release!(force: deployment_run_params[:force])
-
- redirect_back fallback_location: root_path, notice: "The new release has begun preparing."
- end
-
- private
-
- def deployment_run_params
- params.require(:deployment_run).permit(:force)
- end
-
- def ensure_reviewable
- unless @deployment_run.reviewable?
- redirect_back fallback_location: root_path,
- flash: {error: "Cannot perform this operation. This deployment cannot be submitted for a review."}
- end
- end
-
- def ensure_releasable
- unless @deployment_run.releasable?
- redirect_back fallback_location: root_path,
- flash: {error: "Cannot perform this operation. This deployment cannot be released."}
- end
- end
-
- def ensure_preparable
- unless @deployment_run.may_start_prepare_release?
- redirect_back fallback_location: root_path,
- flash: {error: "Cannot perform this operation. This deployment cannot be prepared for release."}
- end
- end
-
- def set_deployment_run
- @deployment_run = DeploymentRun.find_by(id: params[:id])
- end
-end
diff --git a/app/controllers/deployments_controller.rb b/app/controllers/deployments_controller.rb
deleted file mode 100644
index 2515be007..000000000
--- a/app/controllers/deployments_controller.rb
+++ /dev/null
@@ -1,29 +0,0 @@
-class DeploymentsController < SignedInApplicationController
- before_action :require_write_access!, only: %i[start]
- before_action :set_release
- before_action :set_step_run
- before_action :set_deployment
-
- def start
- Triggers::Deployment.call(step_run: @step_run, deployment: @deployment)
- redirect_back fallback_location: root_path, notice: "Deployment successfully started!"
- end
-
- private
-
- def set_release
- @release =
- ReleasePlatformRun
- .joins(release_platform: :app)
- .where(apps: {organization: current_organization})
- .find_by(id: params[:release_id])
- end
-
- def set_step_run
- @step_run = @release.step_runs.find_by(id: params[:step_run_id])
- end
-
- def set_deployment
- @deployment = @step_run.deployments.find_by(id: params[:id])
- end
-end
diff --git a/app/controllers/integration_listeners/github_controller.rb b/app/controllers/integration_listeners/github_controller.rb
index 1af982f6f..92f0fe407 100644
--- a/app/controllers/integration_listeners/github_controller.rb
+++ b/app/controllers/integration_listeners/github_controller.rb
@@ -26,27 +26,15 @@ def handle_ping
end
def handle_push
- response =
- if train.product_v2?
- result = Action.process_push_webhook(train, push_params)
- result.ok? ? result.value! : Response.new(:unprocessable_entity, "Error processing push")
- else
- WebhookHandlers::Push.process(train, push_params)
- end
-
+ result = Action.process_push_webhook(train, push_params)
+ response = result.ok? ? result.value! : Response.new(:unprocessable_entity, "Error processing push")
Rails.logger.debug { response.body }
head response.status
end
def handle_pull_request
- response =
- if train.product_v2?
- result = Action.process_pull_request_webhook(train, pull_request_params)
- result.ok? ? result.value! : Response.new(:unprocessable_entity, "Error processing pull request")
- else
- WebhookHandlers::PullRequest.process(train, pull_request_params)
- end
-
+ result = Action.process_pull_request_webhook(train, pull_request_params)
+ response = result.ok? ? result.value! : Response.new(:unprocessable_entity, "Error processing pull request")
Rails.logger.debug { response.body }
head response.status
end
diff --git a/app/controllers/integration_listeners/gitlab_controller.rb b/app/controllers/integration_listeners/gitlab_controller.rb
index f8a1f6e28..7a10beb38 100644
--- a/app/controllers/integration_listeners/gitlab_controller.rb
+++ b/app/controllers/integration_listeners/gitlab_controller.rb
@@ -26,27 +26,15 @@ def handle_ping
end
def handle_push
- response =
- if train.product_v2?
- result = Action.process_push_webhook(train, push_params)
- result.ok? ? result.value! : Response.new(:unprocessable_entity, "Error processing push")
- else
- WebhookHandlers::Push.process(train, push_params)
- end
-
+ result = Action.process_push_webhook(train, push_params)
+ response = result.ok? ? result.value! : Response.new(:unprocessable_entity, "Error processing push")
Rails.logger.debug { response.body }
head response.status
end
def handle_pull_request
- response =
- if train.product_v2?
- result = Action.process_pull_request_webhook(train, pull_request_params)
- result.ok? ? result.value! : Response.new(:unprocessable_entity, "Error processing pull request")
- else
- WebhookHandlers::PullRequest.process(train, pull_request_params)
- end
-
+ result = Action.process_pull_request_webhook(train, pull_request_params)
+ response = result.ok? ? result.value! : Response.new(:unprocessable_entity, "Error processing pull request")
Rails.logger.debug { response.body }
head response.status
end
diff --git a/app/controllers/notification_settings_controller.rb b/app/controllers/notification_settings_controller.rb
index 9bcc2319a..14afe29df 100644
--- a/app/controllers/notification_settings_controller.rb
+++ b/app/controllers/notification_settings_controller.rb
@@ -10,11 +10,7 @@ class NotificationSettingsController < SignedInApplicationController
def index
if @train.send_notifications?
- @notification_settings = if @train.product_v2?
- @train.notification_settings.where(kind: NotificationSetting.kinds.values - NotificationSetting::DEPRECATED_KINDS.values)
- else
- @train.notification_settings.where(kind: NotificationSetting.kinds.values - NotificationSetting::V2_KINDS.values)
- end
+ @notification_settings = @train.notification_settings.where(kind: NotificationSetting.kinds.values)
end
set_train_config_tabs
diff --git a/app/controllers/release_metadata_controller.rb b/app/controllers/release_metadata_controller.rb
index f5e86a40d..6bd5b5ef3 100644
--- a/app/controllers/release_metadata_controller.rb
+++ b/app/controllers/release_metadata_controller.rb
@@ -1,13 +1,9 @@
class ReleaseMetadataController < SignedInApplicationController
include Tabbable
- before_action :require_write_access!, only: %i[edit update]
- before_action :set_release, only: %i[edit update index update_all]
- before_action :set_release_platform, only: %i[edit update]
- before_action :set_release_platform_run, only: %i[edit update]
- before_action :set_train, only: %i[edit update index update_all]
- before_action :set_app_from_train, only: %i[edit update index update_all]
- before_action :ensure_editable, only: %i[edit update]
+ before_action :set_release, only: %i[index update_all]
+ before_action :set_train, only: %i[index update_all]
+ before_action :set_app_from_train, only: %i[index update_all]
def index
set_metadata
@@ -18,20 +14,6 @@ def index
end
end
- def edit
- @release_metadatum = @release_platform_run.release_metadatum
- end
-
- def update
- @release_metadatum = ReleaseMetadata.find(params[:id])
-
- if @release_metadatum.update(release_metadata_params)
- redirect_to release_path(@release), notice: t(".success")
- else
- render :edit, status: :unprocessable_entity
- end
- end
-
def update_all
language = params.require(:language)
rm_params = params.require(:release_metadata)
@@ -80,22 +62,10 @@ def set_metadata
@android_metadata = @release.android_release_platform_run&.metadata_for(@language, :android)
end
- def release_metadata_params
- params.require(:release_metadata).permit(:release_notes, :promo_text)
- end
-
def set_release
@release = Release.friendly.find(params[:release_id])
end
- def set_release_platform
- @release_platform = @release.release_platforms.friendly.find(params[:release_platform_id])
- end
-
- def set_release_platform_run
- @release_platform_run = @release.release_platform_runs.find_by(release_platform: @release_platform)
- end
-
def set_train
@train = @release.train
end
@@ -103,11 +73,4 @@ def set_train
def set_app_from_train
@app = @train.app
end
-
- def ensure_editable
- unless @release_platform_run.metadata_editable?
- redirect_back fallback_location: release_path(@release),
- flash: {error: t(".metadata_not_editable")}
- end
- end
end
diff --git a/app/controllers/releases_controller.rb b/app/controllers/releases_controller.rb
index 0579992f7..058ea10a9 100644
--- a/app/controllers/releases_controller.rb
+++ b/app/controllers/releases_controller.rb
@@ -12,14 +12,7 @@ def index
end
def show
- if @release.is_v2?
- redirect_to live_release_active_tab
- return
- end
-
- set_commits
- set_pull_requests
- render :show
+ redirect_to live_release_active_tab
end
def create
@@ -207,10 +200,6 @@ def set_pull_requests
@mid_release_prs = @release.mid_release_prs
end
- def set_commits
- @commits = @release.applied_commits.sequential.includes(step_runs: :step)
- end
-
def current_release_path(current_release)
release_path(current_release)
end
diff --git a/app/controllers/signed_in_application_controller.rb b/app/controllers/signed_in_application_controller.rb
index c6bb510f2..60036bc59 100644
--- a/app/controllers/signed_in_application_controller.rb
+++ b/app/controllers/signed_in_application_controller.rb
@@ -189,10 +189,6 @@ def turbo_frame_request_variant
request.variant = :turbo_frame if turbo_frame_request?
end
- def v2?
- @train&.product_v2?
- end
-
def stream_flash
turbo_stream.update("flash_stream", V2::FlashComponent.new(flash))
end
diff --git a/app/controllers/staged_rollouts_controller.rb b/app/controllers/staged_rollouts_controller.rb
deleted file mode 100644
index d1bed2a00..000000000
--- a/app/controllers/staged_rollouts_controller.rb
+++ /dev/null
@@ -1,101 +0,0 @@
-class StagedRolloutsController < SignedInApplicationController
- before_action :require_write_access!, only: %i[increase halt]
- before_action :set_deployment_run
- before_action :set_staged_rollout
- before_action :ensure_controlled_rolloutable, only: [:increase, :halt]
- before_action :ensure_rolloutable, only: [:fully_release, :resume]
- before_action :ensure_auto_rolloutable, only: [:pause]
-
- def increase
- @staged_rollout.move_to_next_stage!
-
- if @staged_rollout.failed?
- redirect_back fallback_location: root_path, flash: {error: "Failed to increase the rollout. Please retry!"}
- else
- redirect_back fallback_location: root_path, notice: "Increased the rollout!"
- end
- end
-
- def pause
- unless @staged_rollout.may_pause?
- redirect_back fallback_location: root_path, flash: {error: "Rollout cannot be paused at this stage."}
- return
- end
-
- @staged_rollout.pause_release!
-
- if @staged_rollout.paused?
- redirect_back fallback_location: root_path, notice: "Paused the rollout!"
- else
- redirect_back fallback_location: root_path, flash: {error: "Failed to pause the rollout. Please retry!"}
- end
- end
-
- def resume
- unless @staged_rollout.may_resume?
- redirect_back fallback_location: root_path, flash: {error: "Rollout cannot be resumed at this stage."}
- return
- end
-
- @staged_rollout.resume_release!
-
- if @staged_rollout.started?
- redirect_back fallback_location: root_path, notice: "Resumed the rollout!"
- else
- redirect_back fallback_location: root_path, flash: {error: "Failed to resume the rollout. Please retry!"}
- end
- end
-
- def halt
- unless @staged_rollout.may_halt?
- redirect_back fallback_location: root_path, flash: {error: "Rollout cannot be halted at this stage."}
- return
- end
-
- @staged_rollout.halt_release!
-
- if @staged_rollout.stopped?
- redirect_back fallback_location: root_path, notice: "Halted the rollout!"
- else
- redirect_back fallback_location: root_path, flash: {error: "Failed to halt the rollout. Please retry!"}
- end
- end
-
- def fully_release
- @staged_rollout.fully_release!
-
- if @staged_rollout.fully_released?
- redirect_back fallback_location: root_path, notice: "Released to all users!"
- else
- redirect_back fallback_location: root_path, flash: {error: "Failed to release to all users due to #{@deployment_run.display_attr(:failure_reason)}"}
- end
- end
-
- private
-
- def ensure_rolloutable
- unless @deployment_run.rolloutable?
- redirect_back fallback_location: root_path, flash: {error: "Cannot perform this operation. The deployment is not in rollout stage."}
- end
- end
-
- def ensure_controlled_rolloutable
- unless @deployment_run.controllable_rollout?
- redirect_back fallback_location: root_path, flash: {error: "Cannot perform this operation. The deployment is not in rollout stage."}
- end
- end
-
- def ensure_auto_rolloutable
- unless @deployment_run.automatic_rollout?
- redirect_back fallback_location: root_path, flash: {error: "Cannot perform this operation. The deployment is not in rollout stage."}
- end
- end
-
- def set_staged_rollout
- @staged_rollout = @deployment_run.staged_rollout
- end
-
- def set_deployment_run
- @deployment_run = DeploymentRun.find_by(id: params[:deployment_run_id])
- end
-end
diff --git a/app/controllers/step_runs_controller.rb b/app/controllers/step_runs_controller.rb
deleted file mode 100644
index 61776feb1..000000000
--- a/app/controllers/step_runs_controller.rb
+++ /dev/null
@@ -1,71 +0,0 @@
-class StepRunsController < SignedInApplicationController
- before_action :require_write_access!, only: %i[start retry_ci_workflow]
- before_action :set_release
- before_action :set_step, only: %i[start]
- before_action :set_step_run, only: %i[retry_ci_workflow sync_store_status]
- before_action :ensure_startable, only: %i[start]
- before_action :ensure_syncable, only: %i[sync_store_status]
-
- # FIXME: This action incorrectly consumes a step_id and not a step_run_id as the route suggests
- def start
- Triggers::StepRun.call(@step, @release.last_commit, @release)
- redirect_back fallback_location: root_path, notice: "Step successfully started"
- end
-
- def retry_ci_workflow
- @step_run.retry_ci!
- redirect_back fallback_location: root_path, notice: "CI workflow retried!"
- rescue
- error = "Failed to retry the CI workflow! Contact support if the issue persists."
- redirect_back fallback_location: root_path, flash: {error:}
- end
-
- def sync_store_status
- @step_run.sync_store_status!
-
- if @step_run.deployment_restarted?
- redirect_back fallback_location: root_path, notice: "Status resolved on the console UI, the release train will move forward."
- else
- redirect_back fallback_location: root_path, flash: {error: "Status remains unresolved on the console UI. Please make sure to submit the changes for review in a public track."}
- end
- rescue
- error = "Failed to sync the store status! Contact support if the issue persists."
- redirect_back fallback_location: root_path, flash: {error:}
- end
-
- private
-
- def set_step_run
- @step_run = @release.step_runs.find(params[:id])
- end
-
- def ensure_startable
- unless @release.manually_startable_step?(@step)
- redirect_back fallback_location: root_path,
- flash: {error: "Cannot perform this operation. This step cannot be started."}
- end
- end
-
- def ensure_syncable
- unless @step_run.failed_with_action_required?
- redirect_back fallback_location: root_path,
- flash: {error: "Cannot perform this operation. This step does not require a manual submission."}
- end
- end
-
- def set_release
- @release =
- ReleasePlatformRun
- .joins(release_platform: :app)
- .where(apps: {organization: current_organization})
- .find(params[:release_id])
- end
-
- def set_step
- @step = @release.release_platform.steps.friendly.find(params[:id])
- end
-
- def deployment_attributes
- params.require(:step_runs).permit(deployment_attributes: [:integration_id, :build_artifact_channel])
- end
-end
diff --git a/app/controllers/steps_controller.rb b/app/controllers/steps_controller.rb
deleted file mode 100644
index 96e3db044..000000000
--- a/app/controllers/steps_controller.rb
+++ /dev/null
@@ -1,151 +0,0 @@
-class StepsController < SignedInApplicationController
- using RefinedString
- using RefinedInteger
-
- before_action :require_write_access!, only: %i[new create update]
- before_action :set_train, only: %i[new create update]
- before_action :set_release_platform, only: %i[new create update]
- before_action :set_ci_actions, only: %i[new create]
- before_action :integrations_are_ready?, only: %i[new create]
- around_action :set_time_zone
-
- def new
- kind = params.extract!(:kind).require(:kind)
-
- head :forbidden and return if @train.active_runs.exists?
- head :forbidden and return if kind.blank?
-
- @step = @release_platform.steps.new(kind:)
-
- if @step.release? && @release_platform.has_release_step?
- redirect_back fallback_location: app_train_releases_path(@app, @train), flash: {error: "You can only have one release step in a train!"}
- end
-
- set_build_channels
- end
-
- def create
- head :forbidden and return if @train.active_runs.exists?
- @step = @release_platform.steps.new(parsed_step_params)
-
- respond_to do |format|
- if @step.save
- format.html { new_step_redirect }
- else
- set_build_channels
- format.html { render :new, status: :unprocessable_entity }
- end
- end
- end
-
- def update
- @step =
- Step
- .joins(release_platform: :app)
- .where(release_platforms: {apps: {organization: current_organization}})
- .friendly
- .find(params[:id])
- head :forbidden and return if @train.active_runs.exists?
-
- if @step.update(parsed_step_params)
- redirect_to steps_app_train_path(@app, @train), notice: "Step was successfully updated."
- else
- @ci_actions = @step.ci_cd_provider.workflows
- redirect_to steps_app_train_path(@app, @train), flash: {error: @step.errors.full_messages.to_sentence}
- end
- end
-
- private
-
- def new_step_redirect
- if @app.guided_train_setup?
- redirect_to app_path(@app), notice: "Step was successfully created."
- else
- redirect_to app_train_releases_path(@app, @train), notice: "Step was successfully created."
- end
- end
-
- def set_step
- @step = @train.steps.friendly.find(params[:id])
- end
-
- def set_train
- @train = @app.trains.friendly.find(params[:train_id])
- end
-
- def set_release_platform
- @release_platform = @train.release_platforms.friendly.find(params[:platform_id])
- end
-
- def step_params
- params.require(:step).permit(
- :name,
- :description,
- :ci_cd_channel,
- :release_suffix,
- :kind,
- :auto_deploy,
- :build_artifact_name_pattern,
- :app_variant_id
- )
- end
-
- def parsed_step_params
- step_params
- .merge(parsed_deployments_params)
- .merge(ci_cd_channel: step_params[:ci_cd_channel]&.safe_json_parse)
- end
-
- def integrations_are_ready?
- unless @train.ready?
- redirect_to app_train_releases_path(@app, @train), alert: "Cannot create steps before notifiers are complete."
- end
- end
-
- def set_ci_actions
- @ci_actions = @train.ci_cd_provider.workflows
- end
-
- def set_build_channels
- @build_channel_integrations = set_build_channel_integrations
- @selected_integration = @build_channel_integrations.first # TODO: what is first even?
- @selected_build_channels =
- Integration.find_build_channels(@selected_integration.last, with_production: @step.release?)
- end
-
- def deployments_params
- params
- .require(:step)
- .permit(deployments_attributes: [
- :integration_id,
- :build_artifact_channel,
- :deployment_number,
- :is_staged_rollout,
- :notes,
- :staged_rollout_config
- ])
- end
-
- def parsed_deployments_params
- deployments_params.merge(deployments_attributes: parsed_deployments_attributes)
- end
-
- def parsed_deployments_attributes
- deployments_params[:deployments_attributes].to_h.to_h do |number, attributes|
- [
- number,
- attributes.merge(
- staged_rollout_config: attributes[:staged_rollout_config]&.safe_csv_parse,
- build_artifact_channel: attributes[:build_artifact_channel]&.safe_json_parse
- )
- ]
- end
- end
-
- def set_build_channel_integrations
- @release_platform
- .build_channel_integrations
- .map { |bc| [bc.providable.display, bc.id] }
- .push(Integration::EXTERNAL_BUILD_INTEGRATION[:build_integration])
- end
-end
diff --git a/app/controllers/trains_controller.rb b/app/controllers/trains_controller.rb
index c3e354ce1..da22d5a6d 100644
--- a/app/controllers/trains_controller.rb
+++ b/app/controllers/trains_controller.rb
@@ -6,8 +6,8 @@ class TrainsController < SignedInApplicationController
before_action :require_write_access!, only: %i[new create edit update destroy activate deactivate]
around_action :set_time_zone
- before_action :set_train, only: %i[edit update destroy activate deactivate steps rules]
- before_action :set_train_config_tabs, only: %i[edit update steps rules destroy activate deactivate]
+ before_action :set_train, only: %i[edit update destroy activate deactivate rules]
+ before_action :set_train_config_tabs, only: %i[edit update rules destroy activate deactivate]
before_action :validate_integration_status, only: %i[new create]
before_action :set_notification_channels, only: %i[new create edit update]
@@ -19,10 +19,6 @@ def edit
@edit_not_allowed = @train.active_runs.exists?
end
- def steps
- @edit_not_allowed = @train.active_runs.exists?
- end
-
def rules
@unconfigured = @train.monitoring_provider.nil?
end
@@ -81,11 +77,9 @@ def deactivate
def new_train_redirect
if @app.trains.size == 1
redirect_to app_path(@app), notice: "Train was successfully created."
- elsif v2?
+ else
platform = @train.release_platforms.first.platform
redirect_to edit_app_train_platform_submission_config_path(@app, @train, platform), notice: "Train was successfully created."
- else
- redirect_to steps_app_train_path(@app, @train), notice: "Train was successfully created."
end
end
@@ -123,7 +117,6 @@ def train_params
:release_schedule_enabled,
:stop_automatic_releases_on_failure,
:continuous_backmerge_enabled,
- :manual_release,
:compact_build_notes,
:tag_all_store_releases,
:tag_platform_releases,
@@ -162,7 +155,6 @@ def train_update_params
:release_schedule_enabled,
:stop_automatic_releases_on_failure,
:continuous_backmerge_enabled,
- :manual_release,
:compact_build_notes,
:tag_all_store_releases,
:tag_platform_releases,
@@ -196,7 +188,7 @@ def train_path
def set_notification_channels
@notification_channels = @app.notification_provider.channels if @app.notifications_set_up?
- @current_notification_channel = @train&.notification_channel || @app.config.notification_channel
+ @current_notification_channel = @train&.notification_channel
end
def build_queue_config_params
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 93cf830df..2df11a80c 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -32,6 +32,10 @@ module ApplicationHelper
inert: "bg-main-400 dark:bg-main-200"
}
+ def setup_instruction_color(is_completed)
+ is_completed ? "bg-green-400" : "bg-blue-200"
+ end
+
def status_picker(picker, status)
picker[status.to_sym] || {text: status.humanize, status: :neutral}
end
@@ -44,23 +48,6 @@ def resolve_color(color)
end
end
- def write_only(&block)
- return concat(content_tag(:div, capture(&block), class: "hidden")) unless writer?
- yield(block)
- end
-
- def sidebar_active_path(path, style)
- if current_page?(path)
- style
- end
- end
-
- def sidebar_active_resource(resource, style)
- if resource.eql?(controller_name)
- style
- end
- end
-
def toggle_for(hide, full_width: false, &block)
render(
partial: "shared/toggle_button",
@@ -70,18 +57,6 @@ def toggle_for(hide, full_width: false, &block)
)
end
- def dynamic_header_color
- if Rails.env.development?
- "bg-rose-100"
- else
- "bg-white"
- end
- end
-
- def step_color(step_kind)
- (step_kind == "release") ? "amber" : "slate"
- end
-
def version_in_progress(version)
version.to_semverish.to_s(patch_glob: true)
end
@@ -114,15 +89,6 @@ def current_deploy
}
end
- NOTE_BOX_COLORS = {
- info: "text-amber-500",
- error: "text-red-500"
- }.freeze
-
- def note_box_color(type)
- NOTE_BOX_COLORS[type]
- end
-
def status_badge(status, custom = [], fixed = nil, pulse: false)
return if status.blank?
@@ -146,10 +112,6 @@ def status_badge(status, custom = [], fixed = nil, pulse: false)
content_tag(:span, status, class: classes)
end
- def dev_show(&blk)
- yield blk if Rails.env.development?
- end
-
def display_channels(channels, with_none: false)
channels
.map { |chan| [yield(chan), chan.to_json] }
@@ -177,10 +139,6 @@ def user_avatar(name, **)
Initials.svg(name, **)
end
- def safe_simple_format(text)
- simple_format(h(text))
- end
-
def comment
end
diff --git a/app/helpers/apps_helper.rb b/app/helpers/apps_helper.rb
deleted file mode 100644
index 7c3034c13..000000000
--- a/app/helpers/apps_helper.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-module AppsHelper
- def setup_instruction_color(is_completed)
- is_completed ? "bg-green-400" : "bg-blue-200"
- end
-
- def store_logo(app)
- return "integrations/logo_app_store.png" if app.ios?
- "integrations/logo_google_play_store.png" if app.android?
- end
-end
diff --git a/app/helpers/assets_helper.rb b/app/helpers/assets_helper.rb
index d35fa4e37..eddeb43fb 100644
--- a/app/helpers/assets_helper.rb
+++ b/app/helpers/assets_helper.rb
@@ -1,6 +1,4 @@
module AssetsHelper
- include SvgHelper
-
def inline_svg(asset_name, classname: "svg-container")
return if asset_name.blank?
content_tag(:div, safe_svg(inline_file(asset_name)), class: classname)
@@ -16,4 +14,8 @@ def inline_file(asset_name)
Rails.root.join("public/assets/#{asset_path}").read
end
end
+
+ def safe_svg(body)
+ sanitize(body, tags: Loofah::HTML5::WhiteList::SVG_ELEMENTS, attributes: Loofah::HTML5::WhiteList::SVG_ATTRIBUTES)
+ end
end
diff --git a/app/helpers/button_helper.rb b/app/helpers/button_helper.rb
deleted file mode 100644
index 308ba42ef..000000000
--- a/app/helpers/button_helper.rb
+++ /dev/null
@@ -1,120 +0,0 @@
-module ButtonHelper
- using RefinedString
- BASE_OPTS = "btn group"
-
- BUTTON_OPTIONS = {
- green: {
- class: "#{BASE_OPTS} bg-emerald-500 hover:bg-emerald-600 text-white"
- },
- blue: {
- class: "#{BASE_OPTS} bg-indigo-500 hover:bg-indigo-600 text-white"
- },
- red: {
- class: "#{BASE_OPTS} bg-rose-500 hover:bg-rose-600 text-white"
- },
- neutral: {
- class: "#{BASE_OPTS} border-slate-300 hover:border-slate-400 text-slate-600"
- },
- slate: {
- class: "#{BASE_OPTS} border-slate-400 hover:border-slate-600 text-slate-600"
- },
- amber: {
- class: "#{BASE_OPTS} bg-amber-500 hover:bg-amber-600 text-white"
- },
- disabled:
- {
- class: "#{BASE_OPTS} opacity-30 disabled cursor-not-allowed bg-transparent border-slate-300 hover:border-slate-300",
- disabled: true
- }
- }
-
- def apply_button_loader(value)
- concat content_tag(:span, value, class: "group-disabled:hidden")
- concat content_tag(:span, "Processing...", class: "hidden group-disabled:inline group-disabled:opacity-60")
- end
-
- def apply_button_options(options, new_options)
- options ||= {}
- options[:class] ||= ""
- options[:class] << " #{new_options[:class]}"
- options[:class].squish
- options.merge(new_options.except(:class))
- end
-
- def apply_button_styles(style, options, html_options, block)
- new_opts = BUTTON_OPTIONS[style]
-
- if block
- options = apply_button_options(options, new_opts)
- else
- html_options = apply_button_options(html_options, new_opts)
- end
-
- [options, html_options]
- end
-
- # link that looks like a styled button
- def decorated_link_to(style, name = nil, options = nil, html_options = nil, &block)
- options, html_options = apply_button_styles(style, options, html_options, block)
- name = name&.better_titleize
- link_to(name, options, html_options, &block)
- end
-
- # styled button with path
- def decorated_button_to(style, name = nil, options = nil, html_options = nil, &block)
- options, html_options = apply_button_styles(style, options, html_options, block)
- name = name&.better_titleize
-
- # if there is no block, the button loader is auto-applied on clicks
- # when block is supplied, the user is expected to attach the button loader inside the block
- if block || style.eql?(:disabled)
- button_to(name, options, html_options, &block)
- else
- button_to(options, html_options) { apply_button_loader(name) }
- end
- end
-
- # styled button tag
- def decorated_button_tag(style, options = nil, html_options = nil, &block)
- options, html_options = apply_button_styles(style, options, html_options, block)
- button_tag(options, html_options, &block)
- end
-
- # auth-aware link that looks like a styled button
- def authz_link_to(style, name = nil, options = nil, html_options = nil, &block)
- style = :disabled unless writer?
-
- if style == :disabled
- if block
- name = "javascript:void(0);"
- else
- options = "javascript:void(0);"
- end
- end
-
- decorated_link_to(style, name, options, html_options, &block)
- end
-
- # auth-aware styled button with path
- def authz_button_to(style, name = nil, options = nil, html_options = nil, &)
- style = :disabled unless writer?
- decorated_button_to(style, name, options, html_options, &)
- end
-
- class AuthzForms < ActionView::Helpers::FormBuilder
- def decorated_submit(style, value, options, &block)
- _options, html_options = @template.apply_button_styles(style, {}, options, nil)
-
- if block || style.eql?(:disabled)
- button(value, html_options, &block)
- else
- button(html_options) { @template.apply_button_loader(value) }
- end
- end
-
- def authz_submit(style, value = nil, options = nil, &)
- style = :disabled unless @template.writer?
- decorated_submit(style, value, options, &)
- end
- end
-end
diff --git a/app/helpers/deployments_helper.rb b/app/helpers/deployments_helper.rb
deleted file mode 100644
index e55e9333c..000000000
--- a/app/helpers/deployments_helper.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-module DeploymentsHelper
- def deployment_channel_name(chan)
- chan["is_internal"] ? chan["name"] + " (Internal)" : chan["name"]
- end
-
- def show_deployment(deployment)
- display = deployment.display_attr(:integration_type)
- display += " ā¢ #{deployment_channel_name(deployment.build_artifact_channel)}" if deployment.display_channel?
- display
- end
-end
diff --git a/app/helpers/enhanced_form_helper.rb b/app/helpers/enhanced_form_helper.rb
index db2ccd178..fa8557ad3 100644
--- a/app/helpers/enhanced_form_helper.rb
+++ b/app/helpers/enhanced_form_helper.rb
@@ -1,6 +1,4 @@
module EnhancedFormHelper
- include ButtonHelper
-
class BetterForm < ActionView::Helpers::FormBuilder
def mandatory_label(method, *args)
options = args.extract_options!
diff --git a/app/helpers/external_release_helper.rb b/app/helpers/external_release_helper.rb
deleted file mode 100644
index c4979663c..000000000
--- a/app/helpers/external_release_helper.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-module ExternalReleaseHelper
- def external_release_status_timestamp(external_release)
- if external_release.released_at
- "Released #{ago_in_words(external_release.released_at)}"
- elsif external_release.reviewed_at
- "Reviewed #{ago_in_words(external_release.reviewed_at)}"
- else
- "Changed #{ago_in_words(external_release.updated_at)}"
- end
- end
-end
diff --git a/app/helpers/releases_helper.rb b/app/helpers/releases_helper.rb
deleted file mode 100644
index 5031a353a..000000000
--- a/app/helpers/releases_helper.rb
+++ /dev/null
@@ -1,164 +0,0 @@
-module ReleasesHelper
- include ApplicationHelper
- include Memery
-
- SHOW_RELEASE_STATUS = {
- finished: ["Completed", :success],
- stopped: ["Stopped", :inert],
- created: ["Running", :ongoing],
- on_track: ["Running", :ongoing],
- upcoming: ["Upcoming", :inert],
- post_release: ["Finalizing", :neutral],
- post_release_started: ["Finalizing", :neutral],
- post_release_failed: ["Finalizing", :neutral],
- partially_finished: ["Partially Finished", :ongoing],
- stopped_after_partial_finish: ["Stopped & Partially Finished", :inert]
- }
-
- def release_status_badge(status)
- status, styles = SHOW_RELEASE_STATUS.fetch(status.to_sym)
- status_badge(status, styles)
- end
-
- def build_status_badge(step_run)
- status, styles =
- case step_run.status.to_sym
- when :ci_workflow_triggered, :on_track
- ["Waiting for CI", :routine]
- when :ci_workflow_started
- ["In progress", :ongoing]
- when :build_ready
- ["Looking for build to deploy", :ongoing]
- when :deployment_started, :deployment_restarted
- ["Deployments in progress", :ongoing]
- when :build_found_in_store
- ["Build found in store", :routine]
- when :build_not_found_in_store
- ["Build not found in store", :failure]
- when :success
- ["Success", :success]
- when :ci_workflow_failed
- ["CI workflow failure", :failure]
- when :ci_workflow_unavailable
- ["CI workflow not found", :failure]
- when :ci_workflow_halted
- ["CI workflow cancelled", :inert]
- when :build_unavailable
- ["Build unavailable", :failure]
- when :deployment_failed
- ["Deployment failed", :failure]
- when :failed_with_action_required
- ["Needs manual submission", :failure]
- when :cancelling
- ["Cancelling", :inert]
- when :cancelled
- ["Cancelled", :inert]
- when :cancelled_before_start
- ["Overwritten", :neutral]
- else
- ["Unknown", :neutral]
- end
-
- status_badge(status, styles)
- end
-
- def deployment_run_status_badge(deployment_run)
- status, styles =
- case deployment_run.status.to_sym
- when :created
- ["About to start", :inert]
- when :started
- ["Running", :ongoing]
- when :preparing_release
- ["Preparing store version", :ongoing]
- when :prepared_release
- ["Ready for review", :ongoing]
- when :failed_prepare_release
- ["Failed to start release", :inert]
- when :submitted_for_review
- ["Submitted for review", :ongoing]
- when :review_failed
- ["Review rejected", :failure]
- when :ready_to_release
- ["Review approved", :ongoing]
- when :uploading
- ["Uploading", :routine]
- when :uploaded
- ["Uploaded", :routine]
- when :rollout_started
- ["Release in progress", :routine]
- when :released
- ["Released", :success]
- when :failed
- ["Failed", :failure]
- when :failed_with_action_required
- ["Needs manual submission", :failure]
- else
- ["Unknown", :neutral]
- end
-
- status_badge(status, styles)
- end
-
- def pull_request_status(pull_request)
- case pull_request.state.to_sym
- when :open
- :success
- when :closed
- :ongoing
- else
- :neutral
- end
- end
-
- def pull_request_badge(pull_request)
- status_badge(pull_request.state, pull_request_status(pull_request))
- end
-
- def stop_release_warning(release)
- message = ""
- message += "You have finished release to one of the platforms. " if release.partially_finished?
- message += "You have unmerged commits in this release branch. " if release.all_commits.size > 1
- message + "Are you sure you want to stop the release?"
- end
-
- def formatted_commit_info(commit)
- name = commit.author_name || commit.author_login
- author_link = commit.author_url || "mailto:#{commit.author_email}"
- author_url = link_to_external(name, author_link, class: "underline")
- builder = content_tag(:code, commit.short_sha)
- builder += " ā¢ "
- builder += author_url + " committed " + ago_in_words(commit.timestamp)
- builder += " ā¢ applied " + ago_in_words(commit.applied_at) if commit.applied_at.present?
- builder
- end
-
- def blocked_step_release_link(release)
- release_url = if release.ongoing?
- hotfix_release_app_train_releases_path(release.train.app, release.train)
- else
- ongoing_release_app_train_releases_path(release.train.app, release.train)
- end
- link_text = release.ongoing? ? "current hotfix release" : "current ongoing release"
- link_to link_text, release_url, class: "underline"
- end
-
- def release_title(release)
- if release.hotfix?
- concat content_tag :span, release.release_version.to_s, class: "pr-2"
- concat inline_svg("band_aid.svg", classname: "w-6 align-middle inline-flex")
- content_tag :span, "hotfix release", class: "ml-2 text-sm bg-amber-50 px-2 py-1"
- else
- release.release_version
- end
- end
-
- def hotfixed_from(release)
- hotfixed_from = release.hotfixed_from
- content_tag(:div, class: "inline-flex") do
- concat content_tag(:span, "(hotfixed from ".html_safe)
- concat link_to content_tag(:code, hotfixed_from.release_version.to_s), hotfixed_from.live_release_link, class: "underline"
- concat content_tag(:span, ")")
- end
- end
-end
diff --git a/app/helpers/scheduled_releases_helper.rb b/app/helpers/scheduled_releases_helper.rb
deleted file mode 100644
index 2b69720d0..000000000
--- a/app/helpers/scheduled_releases_helper.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-module ScheduledReleasesHelper
- def scheduled_release_badge(scheduled_release)
- if scheduled_release.is_success?
- status_badge("success", :success)
- elsif !scheduled_release.pending?
- status_badge("skipped", :neutral)
- else
- status_badge("scheduled", :ongoing)
- end
- end
-
- def scheduled_release_text(scheduled_release)
- time_format(scheduled_release.scheduled_at)
- end
-end
diff --git a/app/helpers/steps_helper.rb b/app/helpers/steps_helper.rb
deleted file mode 100644
index 9003dfc75..000000000
--- a/app/helpers/steps_helper.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-module StepsHelper
- def show_ci_cd(step)
- "#{step.ci_cd_provider.display} ā¢ #{step.ci_cd_channel["name"]}"
- end
-
- def platform_subtitle(app, step)
- "For the #{step.release_platform.display_attr(:platform)} platform" if app.cross_platform?
- end
-
- def auto_deploy_status_badge(step)
- step.auto_deploy? ? "" : "Manual Distribution"
- end
-
- def auto_deploy_status(step)
- step.auto_deploy? ? "ON" : "OFF"
- end
-
- def build_artifact_pattern_text(step)
- return unless step.has_uploadables?
- if step.build_artifact_name_pattern.present?
- "picks up build with the pattern #{step.build_artifact_name_pattern} from the above workflow"
- else
- "picks up the largest build from the above workflow"
- end
- end
-end
diff --git a/app/helpers/svg_helper.rb b/app/helpers/svg_helper.rb
deleted file mode 100644
index cd0a53b2c..000000000
--- a/app/helpers/svg_helper.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-module SvgHelper
- def safe_svg(body)
- sanitize(body, tags: Loofah::HTML5::WhiteList::SVG_ELEMENTS, attributes: Loofah::HTML5::WhiteList::SVG_ATTRIBUTES)
- end
-end
diff --git a/app/helpers/trains_helper.rb b/app/helpers/trains_helper.rb
index bd12c6aa0..3750c4a4b 100644
--- a/app/helpers/trains_helper.rb
+++ b/app/helpers/trains_helper.rb
@@ -1,23 +1,4 @@
module TrainsHelper
- def steps_heading(release_platform)
- return release_platform.display_attr(:platform) + " Steps" if release_platform.app.cross_platform?
- "Steps"
- end
-
- def start_release_text(train, major: false)
- text = train.automatic? ? "Manually start " : "Start "
- text += major ? "major " : "minor "
- text += "release "
- text + train.next_version(major_only: major)
- end
-
- def start_upcoming_release_text(ongoing_release, major: false)
- text = "Prepare next "
- text += major ? "major " : "minor "
- text += "release "
- text + ongoing_release.next_version(major_only: major)
- end
-
def release_schedule(train)
if train.automatic?
date = time_format(train.kickoff_at, with_year: true, with_time: false)
@@ -28,20 +9,4 @@ def release_schedule(train)
"No release schedule"
end
end
-
- def build_queue_config(train)
- if train.build_queue_enabled?
- "Applied every #{train.build_queue_wait_time.inspect} OR #{train.build_queue_size} commits"
- else
- "Applied with every new commit"
- end
- end
-
- def backmerge_text(train)
- if train.continuous_backmerge?
- "Changes on the release branch will be merged continuously to the working branch."
- else
- "Changes on the release branch will be merged to the working branch at the end of the release."
- end
- end
end
diff --git a/app/javascript/controllers/branching_selector_controller.js b/app/javascript/controllers/domain/branching_selector_controller.js
similarity index 100%
rename from app/javascript/controllers/branching_selector_controller.js
rename to app/javascript/controllers/domain/branching_selector_controller.js
diff --git a/app/javascript/controllers/domain/release_index_component_controller.js b/app/javascript/controllers/domain/reldex_component_controller.js
similarity index 100%
rename from app/javascript/controllers/domain/release_index_component_controller.js
rename to app/javascript/controllers/domain/reldex_component_controller.js
diff --git a/app/javascript/controllers/domain/release_index_controller.js b/app/javascript/controllers/domain/reldex_controller.js
similarity index 100%
rename from app/javascript/controllers/domain/release_index_controller.js
rename to app/javascript/controllers/domain/reldex_controller.js
diff --git a/app/javascript/controllers/dropdown_stream_controller.js b/app/javascript/controllers/dropdown_stream_controller.js
deleted file mode 100644
index 9529047fa..000000000
--- a/app/javascript/controllers/dropdown_stream_controller.js
+++ /dev/null
@@ -1,78 +0,0 @@
-import {Controller} from "@hotwired/stimulus"
-import {useMutation} from "stimulus-use"
-import {get} from "@rails/request.js"
-
-export default class extends Controller {
- static values = {
- dynamicSelectUrl: String,
- dynamicSelectKey: String,
- showElementIf: String,
- }
- static targets = ["dynamicSelect", "showElement", "hideElement"]
-
- connect() {
- useMutation(this, {childList: true, subtree: true, element: this.dynamicSelectTarget})
- this.showElementOnDynamicSelectChange()
- }
-
- mutate() {
- this.showElementOnDynamicSelectChange()
- }
-
- fetchDynamicSelect(event) {
- let url = new URL(this.dynamicSelectUrlValue)
- url.searchParams.set(this.dynamicSelectKeyValue, event.target.selectedOptions[0].value);
- url.searchParams.set('target', this.dynamicSelectTarget.id);
-
- get(url, {responseKind: "turbo-stream"})
- }
-
- showElementOnDynamicSelectChange() {
- const selectedShowElementValue = this.dynamicSelectTarget.selectedOptions[0].value
-
- if (this.showingElementAllowed()) {
- const parsedSelected = this.__safeJSONParse(selectedShowElementValue)
- const parsedMatchers = this.__safeJSONParse(this.showElementIfValue)
-
- if (parsedSelected && parsedMatchers) {
- this.showElementTarget.hidden = !this.__is_any(parsedSelected, parsedMatchers)
- } else {
- this.showElementTarget.hidden = selectedShowElementValue !== this.showElementIfValue
- }
-
- if (this.hasHideElementTarget) this.hideElementTarget.hidden = !this.showElementTarget.hidden
- }
- }
-
- showingElementAllowed() {
- return this.hasShowElementIfValue && this.hasShowElementTarget
- }
-
- __is_any(obj, matcher) {
- let flag = false;
-
- for (const [key, value] of Object.entries(matcher)) {
- if (this.__hasKeySetTo(obj, key, value)) {
- flag = true
- }
- }
-
- return flag;
- }
-
- __hasKeySetTo(obj, k, v) {
- return obj.hasOwnProperty(k) && obj[k] === v;
- }
-
- __safeJSONParse(str) {
- let parsedJSON = null;
-
- try {
- parsedJSON = JSON.parse(str);
- } catch (e) {
- return false;
- }
-
- return parsedJSON;
- }
-}
diff --git a/app/jobs/build_queue_application_job.rb b/app/jobs/build_queue_application_job.rb
index d028ac569..c61328794 100644
--- a/app/jobs/build_queue_application_job.rb
+++ b/app/jobs/build_queue_application_job.rb
@@ -8,14 +8,6 @@ def perform(build_queue_id)
return unless build_queue.release.committable?
return unless build_queue.is_active?
- if build_queue.release.is_v2?
- Signal.build_queue_can_be_applied!(build_queue)
- else
- build_queue.release.with_lock do
- return unless build_queue.release.committable?
- return unless build_queue.is_active?
- build_queue.apply!
- end
- end
+ Signal.build_queue_can_be_applied!(build_queue)
end
end
diff --git a/app/jobs/deployments/app_store_connect/find_live_release_job.rb b/app/jobs/deployments/app_store_connect/find_live_release_job.rb
deleted file mode 100644
index b91976bf6..000000000
--- a/app/jobs/deployments/app_store_connect/find_live_release_job.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-class Deployments::AppStoreConnect::FindLiveReleaseJob
- include Sidekiq::Job
- extend Loggable
- extend Backoffable
-
- queue_as :high
- sidekiq_options retry: 6000
-
- sidekiq_retry_in do |_count, ex|
- if ex.is_a?(Deployments::AppStoreConnect::Release::ReleaseNotFullyLive)
- 5.minutes.to_i
- else
- elog(ex)
- :kill
- end
- end
-
- def perform(deployment_run_id)
- Deployments::AppStoreConnect::Release.track_live_release_status(DeploymentRun.find(deployment_run_id))
- end
-end
diff --git a/app/jobs/deployments/app_store_connect/prepare_for_release_job.rb b/app/jobs/deployments/app_store_connect/prepare_for_release_job.rb
deleted file mode 100644
index 6c9c15f48..000000000
--- a/app/jobs/deployments/app_store_connect/prepare_for_release_job.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-class Deployments::AppStoreConnect::PrepareForReleaseJob
- include Sidekiq::Job
- extend Backoffable
-
- queue_as :high
- sidekiq_options retry: 3
-
- sidekiq_retry_in do |count, ex|
- if ex.is_a?(Deployments::AppStoreConnect::Release::PreparedVersionNotFoundError)
- backoff_in(attempt: count, period: :minutes, type: :static, factor: 1).to_i
- else
- :kill
- end
- end
-
- sidekiq_retries_exhausted do |msg, ex|
- run = DeploymentRun.find(msg["args"].first)
- run.fail_with_error(ex)
- end
-
- def perform(deployment_run_id, force = false)
- run = DeploymentRun.find(deployment_run_id)
- Deployments::AppStoreConnect::Release.prepare_for_release!(run, force:)
- end
-end
diff --git a/app/jobs/deployments/app_store_connect/test_flight_release_job.rb b/app/jobs/deployments/app_store_connect/test_flight_release_job.rb
deleted file mode 100644
index 72c999bd3..000000000
--- a/app/jobs/deployments/app_store_connect/test_flight_release_job.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-class Deployments::AppStoreConnect::TestFlightReleaseJob < ApplicationJob
- queue_as :high
-
- def perform(deployment_run_id)
- Deployments::AppStoreConnect::Release.to_test_flight!(DeploymentRun.find(deployment_run_id))
- end
-end
diff --git a/app/jobs/deployments/app_store_connect/update_build_notes_job.rb b/app/jobs/deployments/app_store_connect/update_build_notes_job.rb
deleted file mode 100644
index 216642671..000000000
--- a/app/jobs/deployments/app_store_connect/update_build_notes_job.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-class Deployments::AppStoreConnect::UpdateBuildNotesJob < ApplicationJob
- queue_as :high
-
- def perform(deployment_run_id)
- run = DeploymentRun.find(deployment_run_id)
- return unless run.send_notes?
- return unless run.test_flight_release?
-
- Deployments::AppStoreConnect::Release.update_build_notes!(run)
- end
-end
diff --git a/app/jobs/deployments/app_store_connect/update_external_release_job.rb b/app/jobs/deployments/app_store_connect/update_external_release_job.rb
deleted file mode 100644
index e4a92b72d..000000000
--- a/app/jobs/deployments/app_store_connect/update_external_release_job.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-class Deployments::AppStoreConnect::UpdateExternalReleaseJob
- include Sidekiq::Job
- extend Loggable
- extend Backoffable
-
- queue_as :high
- sidekiq_options retry: 2000
-
- sidekiq_retry_in do |count, ex|
- if ex.is_a?(Deployments::AppStoreConnect::Release::ExternalReleaseNotInTerminalState)
- backoff_in(attempt: count, period: :minutes, type: :static, factor: 5).to_i
- else
- elog(ex)
- :kill
- end
- end
-
- def perform(deployment_run_id)
- Deployments::AppStoreConnect::Release.update_external_release(DeploymentRun.find(deployment_run_id))
- end
-end
diff --git a/app/jobs/deployments/google_firebase/update_build_notes_job.rb b/app/jobs/deployments/google_firebase/update_build_notes_job.rb
deleted file mode 100644
index 0d11f7ba3..000000000
--- a/app/jobs/deployments/google_firebase/update_build_notes_job.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-class Deployments::GoogleFirebase::UpdateBuildNotesJob < ApplicationJob
- queue_as :high
-
- def perform(deployment_run_id, release_name)
- run = DeploymentRun.find(deployment_run_id)
- return unless run.send_notes?
- return unless run.google_firebase_integration?
-
- Deployments::GoogleFirebase::Release.update_build_notes!(run, release_name)
- end
-end
diff --git a/app/jobs/deployments/google_firebase/update_upload_status_job.rb b/app/jobs/deployments/google_firebase/update_upload_status_job.rb
deleted file mode 100644
index 7074b21e9..000000000
--- a/app/jobs/deployments/google_firebase/update_upload_status_job.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-class Deployments::GoogleFirebase::UpdateUploadStatusJob
- include Sidekiq::Job
- extend Loggable
- extend Backoffable
-
- queue_as :high
- sidekiq_options retry: 5
-
- sidekiq_retry_in do |count, ex|
- if ex.is_a?(Deployments::GoogleFirebase::Release::UploadNotComplete)
- backoff_in(attempt: count, period: :minutes, type: :static, factor: 2).to_i
- else
- elog(ex)
- :kill
- end
- end
-
- def perform(deployment_run_id, op_name)
- run = DeploymentRun.find(deployment_run_id)
- return unless run.google_firebase_integration?
-
- Deployments::GoogleFirebase::Release.update_upload_status!(run, op_name)
- end
-end
diff --git a/app/jobs/deployments/google_firebase/upload_job.rb b/app/jobs/deployments/google_firebase/upload_job.rb
deleted file mode 100644
index f5ee76143..000000000
--- a/app/jobs/deployments/google_firebase/upload_job.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-class Deployments::GoogleFirebase::UploadJob < ApplicationJob
- queue_as :high
-
- def perform(deployment_run_id)
- run = DeploymentRun.find(deployment_run_id)
- return unless run.google_firebase_integration?
-
- Deployments::GoogleFirebase::Release.upload!(run)
- end
-end
diff --git a/app/jobs/deployments/google_play_store/upload.rb b/app/jobs/deployments/google_play_store/upload.rb
deleted file mode 100644
index 6a309a601..000000000
--- a/app/jobs/deployments/google_play_store/upload.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-class Deployments::GooglePlayStore::Upload < ApplicationJob
- queue_as :high
-
- def perform(deployment_run_id)
- run = DeploymentRun.find(deployment_run_id)
- return unless run.google_play_store_integration?
-
- Deployments::GooglePlayStore::Release.upload!(run)
- end
-end
diff --git a/app/jobs/deployments/release_job.rb b/app/jobs/deployments/release_job.rb
deleted file mode 100644
index b25308715..000000000
--- a/app/jobs/deployments/release_job.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-class Deployments::ReleaseJob < ApplicationJob
- queue_as :high
-
- def perform(deployment_run_id)
- DeploymentRun.find(deployment_run_id).start_release!
- end
-end
diff --git a/app/jobs/deployments/slack_job.rb b/app/jobs/deployments/slack_job.rb
deleted file mode 100644
index 6caa4e20c..000000000
--- a/app/jobs/deployments/slack_job.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-class Deployments::SlackJob < ApplicationJob
- queue_as :high
-
- def perform(deployment_run_id)
- run = DeploymentRun.find(deployment_run_id)
- return unless run.slack_integration?
- run.push_to_slack!
- end
-end
diff --git a/app/jobs/refresh_reldex_job.rb b/app/jobs/refresh_reldex_job.rb
index 5d0690634..f2dc89d98 100644
--- a/app/jobs/refresh_reldex_job.rb
+++ b/app/jobs/refresh_reldex_job.rb
@@ -6,17 +6,9 @@ def perform(train_id)
train = Train.find(train_id)
train.releases.finished.each do |release|
- if release.is_v2?
- Queries::ReleaseBreakdown.warm(release.id, [:reldex])
- else
- Queries::ReleaseSummary.warm(release.id)
- end
+ Queries::ReleaseBreakdown.warm(release.id, [:reldex])
end
- if train.product_v2?
- Queries::DevopsReport.warm(train)
- else
- Charts::DevopsReport.warm(train)
- end
+ Queries::DevopsReport.warm(train)
end
end
diff --git a/app/jobs/refresh_reports_job.rb b/app/jobs/refresh_reports_job.rb
index 9cdcea12a..2ef31e0f7 100644
--- a/app/jobs/refresh_reports_job.rb
+++ b/app/jobs/refresh_reports_job.rb
@@ -5,17 +5,7 @@ class RefreshReportsJob < ApplicationJob
def perform(release_id)
release = Release.find(release_id)
train = release.train
-
- if release.is_v2?
- Queries::ReleaseBreakdown.warm(release.id)
- else
- Queries::ReleaseSummary.warm(release_id)
- end
-
- if train.product_v2?
- Queries::DevopsReport.warm(train)
- else
- Charts::DevopsReport.warm(train)
- end
+ Queries::ReleaseBreakdown.warm(release.id)
+ Queries::DevopsReport.warm(train)
end
end
diff --git a/app/jobs/releases/cancel_step_run.rb b/app/jobs/releases/cancel_step_run.rb
deleted file mode 100644
index 5ded69fed..000000000
--- a/app/jobs/releases/cancel_step_run.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-class Releases::CancelStepRun < ApplicationJob
- include Loggable
-
- queue_as :high
-
- def perform(step_run_id)
- step_run = StepRun.find(step_run_id)
- return unless step_run.active?
-
- Rails.logger.debug { "Cancelling step run - #{step_run_id}" }
- step_run.cancel!
- end
-end
diff --git a/app/jobs/releases/cancel_workflow_run_job.rb b/app/jobs/releases/cancel_workflow_run_job.rb
deleted file mode 100644
index 6c3496815..000000000
--- a/app/jobs/releases/cancel_workflow_run_job.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-class Releases::CancelWorkflowRunJob < ApplicationJob
- extend Backoffable
- WorkflowRunNotFound = Class.new(StandardError)
-
- queue_as :high
- retry_on WorkflowRunNotFound, wait: ->(c) { backoff_in(attempt: c, period: :seconds, type: :linear) }, attempts: 500
-
- def perform(step_run_id)
- step_run = StepRun.find(step_run_id)
- return unless step_run.cancelling?
- raise WorkflowRunNotFound unless step_run.workflow_found?
-
- Rails.logger.debug { "Cancelling workflow for step run - #{step_run_id}" }
- step_run.cancel_ci_workflow!
- step_run.cancel!
- end
-end
diff --git a/app/jobs/releases/fetch_health_metrics_job.rb b/app/jobs/releases/fetch_health_metrics_job.rb
deleted file mode 100644
index 0affaae3d..000000000
--- a/app/jobs/releases/fetch_health_metrics_job.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-class Releases::FetchHealthMetricsJob < ApplicationJob
- queue_as :high
-
- RELEASE_MONITORING_PERIOD_IN_DAYS = 15
-
- def perform(deployment_run_id, frequency)
- run = DeploymentRun.find(deployment_run_id)
- return if run.release.stopped?
- return if run.release.finished? && run.release.completed_at < RELEASE_MONITORING_PERIOD_IN_DAYS.days.ago
-
- begin
- run.fetch_health_data!
- ensure
- Releases::FetchHealthMetricsJob.set(wait: frequency).perform_later(deployment_run_id, frequency)
- end
- end
-end
diff --git a/app/jobs/releases/find_build_job.rb b/app/jobs/releases/find_build_job.rb
deleted file mode 100644
index d170e2b68..000000000
--- a/app/jobs/releases/find_build_job.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-class Releases::FindBuildJob
- include Sidekiq::Job
- extend Loggable
- extend Backoffable
-
- queue_as :high
- sidekiq_options retry: 8
-
- sidekiq_retry_in do |count, ex|
- if ex.is_a?(Installations::Error) && ex.reason == :build_not_found
- backoff_in(attempt: count, period: :minutes).to_i
- else
- elog(ex)
- :kill
- end
- end
-
- sidekiq_retries_exhausted do |msg, ex|
- if ex.is_a?(Installations::Error) && ex.reason == :build_not_found
- run = StepRun.find(msg["args"].first)
- run.build_not_found!
- run.event_stamp!(reason: :build_not_found_in_store, kind: :error, data: {version: run.build_version})
- end
- end
-
- def perform(step_run_id)
- run = StepRun.find(step_run_id)
- return unless run.active?
-
- run.find_build.value!
- run.build_found!
- run.event_stamp!(reason: :build_found_in_store, kind: :notice, data: {version: run.build_version})
- end
-end
diff --git a/app/jobs/releases/find_workflow_run.rb b/app/jobs/releases/find_workflow_run.rb
deleted file mode 100644
index f25db4b97..000000000
--- a/app/jobs/releases/find_workflow_run.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-class Releases::FindWorkflowRun
- include Sidekiq::Job
- include Loggable
-
- queue_as :high
- sidekiq_options retry: 25
-
- sidekiq_retry_in do |count, exception|
- if exception.is_a?(Installations::Error) && exception.reason == :workflow_run_not_found
- 10 * (count + 1)
- else
- :kill
- end
- end
-
- sidekiq_retries_exhausted do |msg, ex|
- if ex.is_a?(Installations::Error) && ex.reason == :workflow_run_not_found
- run = StepRun.find(msg["args"].first)
- run.ci_unavailable! if run.may_ci_unavailable?
- run.event_stamp!(reason: :ci_workflow_unavailable, kind: :error, data: {})
- end
- end
-
- def perform(step_run_id)
- step_run = StepRun.find(step_run_id)
- step_run.find_and_update_workflow_run
- step_run.ci_start! if step_run.may_ci_start?
- rescue => e
- elog(e)
- raise # TODO: remove this and elog in the retry-kill case
- end
-end
diff --git a/app/jobs/releases/pre_release_job.rb b/app/jobs/releases/pre_release_job.rb
index a3cf3d603..8772b9ae4 100644
--- a/app/jobs/releases/pre_release_job.rb
+++ b/app/jobs/releases/pre_release_job.rb
@@ -6,8 +6,7 @@ def perform(release_id)
if release.retrigger_for_hotfix?
latest_commit = release.latest_commit_hash(sha_only: false)
- return WebhookProcessors::Push.process(release, latest_commit, []) unless release.is_v2?
- return Signal.commits_have_landed!(release, latest_commit, []) if release.is_v2?
+ return Signal.commits_have_landed!(release, latest_commit, [])
end
Triggers::PreRelease.call(release)
diff --git a/app/jobs/releases/trigger_workflow_run_job.rb b/app/jobs/releases/trigger_workflow_run_job.rb
deleted file mode 100644
index 85b1470fc..000000000
--- a/app/jobs/releases/trigger_workflow_run_job.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-class Releases::TriggerWorkflowRunJob < ApplicationJob
- include Loggable
-
- queue_as :high
-
- def perform(step_run_id)
- step_run = StepRun.find(step_run_id)
- return unless step_run.active?
- return step_run.cancel! unless step_run.commit.applicable?
-
- step_run.trigger_ci_worfklow_run!
- end
-end
diff --git a/app/jobs/releases/upload_artifact.rb b/app/jobs/releases/upload_artifact.rb
deleted file mode 100644
index c03bed538..000000000
--- a/app/jobs/releases/upload_artifact.rb
+++ /dev/null
@@ -1,31 +0,0 @@
-class Releases::UploadArtifact
- include Sidekiq::Job
- extend Loggable
- queue_as :high
-
- sidekiq_options retry: 3
-
- sidekiq_retry_in do |count, exception|
- if exception.is_a?(Installations::Error) && exception && exception.reason == :artifact_not_found
- 10 * (count + 1)
- else
- :kill
- end
- end
-
- sidekiq_retries_exhausted do |msg, ex|
- elog(ex)
- run = StepRun.find(msg["args"].first)
- run.build_upload_failed!
- run.event_stamp!(reason: :build_unavailable, kind: :error, data: {version: run.build_version})
- end
-
- def perform(step_run_id, artifacts_url)
- run = StepRun.find(step_run_id)
- return unless run.active?
-
- run.artifacts_url = artifacts_url
- run.upload_artifact!
- run.event_stamp!(reason: :build_available, kind: :notice, data: {version: run.build_version})
- end
-end
diff --git a/app/jobs/webhook_processors/close_pull_request_job.rb b/app/jobs/webhook_processors/close_pull_request_job.rb
deleted file mode 100644
index 2789067d7..000000000
--- a/app/jobs/webhook_processors/close_pull_request_job.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-class WebhookProcessors::ClosePullRequestJob < ApplicationJob
- queue_as :high
-
- def perform(train_id, pr_attributes)
- head_ref = pr_attributes[:head_ref]
- number = pr_attributes[:number]
-
- Train.find(train_id).open_active_prs_for(head_ref).where(number:).find_each do |pr|
- pr.update_or_insert!(pr_attributes)
- pr.release.event_stamp!(reason: :pr_merged, kind: :success, data: {url: pr.url, number: pr.number, base_branch: pr.base_ref})
- end
- end
-end
diff --git a/app/jobs/webhook_processors/open_pull_request_job.rb b/app/jobs/webhook_processors/open_pull_request_job.rb
deleted file mode 100644
index 11173fbed..000000000
--- a/app/jobs/webhook_processors/open_pull_request_job.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-class WebhookProcessors::OpenPullRequestJob < ApplicationJob
- queue_as :high
-
- def perform(train_id, pr_attributes)
- Rails.logger.debug { "Create/update PR with attributes: #{pr_attributes}" }
-
- base_ref = pr_attributes[:base_ref]
-
- train = Train.find(train_id)
- release = train.active_runs.where(branch_name: base_ref).sole
-
- return unless release
-
- pr = release.pull_requests.mid_release.open.build
- pr.update_or_insert!(pr_attributes)
- end
-end
diff --git a/app/jobs/webhook_processors/push_job.rb b/app/jobs/webhook_processors/push_job.rb
deleted file mode 100644
index c6b927764..000000000
--- a/app/jobs/webhook_processors/push_job.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-class WebhookProcessors::PushJob < ApplicationJob
- queue_as :high
-
- def perform(train_run_id, head_commit, rest_commits)
- run = Release.find(train_run_id)
- return unless run.committable?
- WebhookProcessors::Push.process(run, head_commit, rest_commits)
- end
-end
diff --git a/app/jobs/workflow_processors/workflow_run_job.rb b/app/jobs/workflow_processors/workflow_run_job.rb
deleted file mode 100644
index def1c8bcc..000000000
--- a/app/jobs/workflow_processors/workflow_run_job.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-class WorkflowProcessors::WorkflowRunJob < ApplicationJob
- queue_as :high
-
- def perform(step_run_id)
- run = StepRun.find(step_run_id)
- return unless run.active?
- return unless run.ci_workflow_started?
-
- WorkflowProcessors::WorkflowRun.process(run)
- end
-end
diff --git a/app/libs/charts/devops_report.rb b/app/libs/charts/devops_report.rb
deleted file mode 100644
index 9b7749616..000000000
--- a/app/libs/charts/devops_report.rb
+++ /dev/null
@@ -1,260 +0,0 @@
-class Charts::DevopsReport
- include Memery
- include Loggable
- using RefinedString
-
- def self.warm(train) = new(train).warm
-
- def self.all(train) = new(train).all
-
- def initialize(train)
- @train = train
- @organization = train.organization
- @team_colors = organization.team_colors
- end
-
- def warm
- cache.write(cache_key, report)
- rescue => e
- elog(e)
- end
-
- def all
- cache.fetch(cache_key)
- end
-
- def report
- {
- refreshed_at: Time.current,
- mobile_devops: {
- duration: {
- data: duration,
- type: "area",
- value_format: "time",
- name: "devops.duration"
- },
- frequency: {
- data: frequency,
- type: "area",
- value_format: "number",
- name: "devops.frequency"
- },
- time_in_review: {
- data: time_in_review,
- type: "area",
- value_format: "time",
- name: "devops.time_in_review"
- },
- hotfixes: {
- data: hotfixes,
- type: "area",
- value_format: "number",
- name: "devops.hotfixes"
- },
- time_in_phases: {
- data: time_in_phases,
- stacked: true,
- type: "stacked-bar",
- value_format: "time",
- name: "devops.time_in_phases",
- height: "250"
- },
- reldex_scores: {
- data: reldex_scores,
- type: "line",
- value_format: "number",
- name: "devops.reldex",
- height: "250",
- show_y_axis: true,
- y_annotations: [
- {y: 0..train.release_index.tolerable_range.min, text: "Mediocre", color: "mediocre"},
- {y: train.release_index.tolerable_range.max, text: "Excellent", color: "excellent"}
- ]
- }
- },
- operational_efficiency: {
- stability_contributors: {
- data: release_stability_contributors,
- type: "line",
- value_format: "number",
- name: "operational_efficiency.stability_contributors",
- show_y_axis: true
- },
- contributors: {
- data: contributors,
- type: "line",
- value_format: "number",
- name: "operational_efficiency.contributors",
- show_y_axis: true
- },
- team_stability_contributors: {
- data: team_stability_contributors,
- stacked: true,
- type: "stacked-bar",
- value_format: "number",
- name: "operational_efficiency.team_stability_contributors",
- colors: team_colors,
- show_y_axis: true,
- height: "250"
- },
- team_contributors: {
- data: team_contributors,
- stacked: true,
- type: "stacked-bar",
- value_format: "number",
- name: "operational_efficiency.team_contributors",
- colors: team_colors,
- show_y_axis: true,
- height: "250"
- }
- }
- }
- end
-
- LAST_RELEASES = 6
- LAST_TIME_PERIOD = 6
-
- memoize def duration(last: LAST_RELEASES)
- finished_releases(last)
- .group_by(&:release_version)
- .sort_by { |v, _| v.to_semverish }.to_h
- .transform_values { {duration: _1.first.duration.seconds} }
- end
-
- memoize def frequency(period = :month, format = "%b", last: LAST_TIME_PERIOD)
- finished_releases(last, hotfix: true)
- .reorder("")
- .group_by_period(period, :completed_at, last: last, current: true, format:)
- .count
- .transform_values { {releases: _1} }
- end
-
- memoize def reldex_scores(last: 10)
- return if train.release_index.blank?
-
- finished_releases(last)
- .group_by(&:release_version)
- .sort_by { |v, _| v.to_semverish }.to_h
- .transform_values { {reldex: _1.first.index_score&.value} }
- end
-
- memoize def release_stability_contributors(last: LAST_RELEASES)
- finished_releases(last)
- .group_by(&:release_version)
- .sort_by { |v, _| v.to_semverish }.to_h
- .transform_values { _1.flat_map(&:all_commits).flat_map(&:author_email) }
- .transform_values { {contributors: _1.uniq.size} }
- end
-
- memoize def contributors(last: LAST_RELEASES)
- finished_releases(last)
- .group_by(&:release_version)
- .sort_by { |v, _| v.to_semverish }.to_h
- .transform_values { {contributors: _1.flat_map(&:release_changelog).compact.flat_map(&:unique_authors).size} }
- end
-
- memoize def team_stability_contributors(last: LAST_RELEASES)
- finished_releases(last)
- .group_by(&:release_version)
- .sort_by { |v, _| v.to_semverish }.to_h
- .transform_values { |releases| release_summary(releases[0]).team_stability_commits }
- .compact_blank
- end
-
- memoize def team_contributors(last: LAST_RELEASES)
- finished_releases(last)
- .group_by(&:release_version)
- .sort_by { |v, _| v.to_semverish }.to_h
- .transform_values { |releases| release_summary(releases[0]).team_release_commits }
- .compact_blank
- end
-
- memoize def time_in_review(last: LAST_RELEASES)
- train
- .external_releases
- .includes(deployment_run: [:deployment, {step_run: {release_platform_run: [:release]}}])
- .where.not(reviewed_at: nil)
- .filter { _1.deployment_run.production_release_happened? }
- .last(last)
- .group_by(&:build_version)
- .sort_by { |v, _| v.to_semverish }.to_h
- .transform_values { _1.flat_map(&:review_time) }
- .transform_values { {time: _1.sum(&:seconds) / _1.size.to_f} }
- end
-
- memoize def hotfixes(last: LAST_RELEASES)
- by_version =
- finished_releases(last)
- .flat_map(&:step_runs)
- .flat_map(&:deployment_runs)
- .filter { _1.production_release_happened? }
- .group_by { _1.release.release_version }
- .sort_by { |v, _| v.to_semverish }.to_h
-
- by_version.transform_values do |runs|
- runs.group_by(&:platform).transform_values { |d| d.size.pred }
- end
- end
-
- def recovery_time
- # group by rel
- # recovery time: time between last rollout and next hotfix (line graph, across platforms)
- raise NotImplementedError
- end
-
- memoize def time_in_phases(last: LAST_RELEASES)
- by_version =
- finished_releases(last)
- .flat_map(&:step_runs)
- .group_by(&:release_version)
- .sort_by { |v, _| v.to_semverish }.to_h
-
- by_version.transform_values do |runs|
- runs.group_by(&:platform).transform_values do |by_platform|
- by_platform.group_by(&:name).transform_values do
- _1.pluck(:updated_at).max - _1.pluck(:scheduled_at).min
- end
- end
- end
- end
-
- # ci workflow time (per step, area graph)
- def ci_workflow_time
- raise NotImplementedError
- end
-
- def automations_run
- raise NotImplementedError
- end
-
- attr_reader :train, :organization, :team_colors
-
- private
-
- delegate :cache, to: Rails
-
- memoize def finished_releases(n, hotfix: false)
- releases =
- train
- .releases
- .limit(n)
- .finished
- .reorder("completed_at DESC")
- .includes(:release_changelog, {release_platform_runs: [:release_platform]}, :all_commits, step_runs: [:deployment_runs, :step])
-
- return releases if hotfix
- releases.release
- end
-
- memoize def release_summary(release)
- Queries::ReleaseSummary.new(release.id)
- end
-
- def cache_key
- "train/#{train.id}/devops_report"
- end
-
- def thaw
- cache.delete(cache_key)
- end
-end
diff --git a/app/libs/computations/release/reldex_parameters.rb b/app/libs/computations/release/reldex_parameters.rb
deleted file mode 100644
index 810f9c643..000000000
--- a/app/libs/computations/release/reldex_parameters.rb
+++ /dev/null
@@ -1,47 +0,0 @@
-class Computations::Release::ReldexParameters
- def self.call(release)
- new(release).call
- end
-
- def initialize(release)
- @release = release
- end
-
- delegate :deployment_runs, :completed_at, :scheduled_at, :all_commits, :previous_release, :stability_commits, :duration, :all_hotfixes, to: :@release
-
- def call
- rollout_duration = 0
- stability_duration = 0
- rollout_fixes = 0
- rollout_changes = 0
- days_since_last_release = 0
-
- submitted_at = deployment_runs.filter_map(&:submitted_at).min
- rollout_started_at = deployment_runs.filter_map(&:release_started_at).min
- platform_store_versions = deployment_runs.reached_production.group_by(&:platform)
- max_store_versions = platform_store_versions.transform_values(&:size).values.max
- first_store_version = platform_store_versions.values.flatten.min_by(&:created_at)
-
- rollout_fixes = max_store_versions - 1 if max_store_versions.present?
- rollout_duration = ActiveSupport::Duration.build(completed_at - rollout_started_at).in_days if rollout_started_at.present?
- stability_duration = ActiveSupport::Duration.build(submitted_at - scheduled_at).in_days if submitted_at.present?
- days_since_last_release = ActiveSupport::Duration.build(completed_at - previous_release&.completed_at).in_days if previous_release.present?
-
- if rollout_fixes > 0
- base_commit = first_store_version.step_run.commit
- head_commit = all_commits.reorder(timestamp: :desc).first
- rollout_changes = all_commits.between_commits(base_commit, head_commit).size
- end
-
- {
- hotfixes: all_hotfixes.size,
- rollout_fixes:,
- rollout_duration:,
- duration: duration.in_days,
- stability_duration:,
- stability_changes: stability_commits.count,
- days_since_last_release:,
- rollout_changes:
- }
- end
-end
diff --git a/app/libs/computations/release/step_statuses.rb b/app/libs/computations/release/step_statuses.rb
index 92ac52803..b2605b4c0 100644
--- a/app/libs/computations/release/step_statuses.rb
+++ b/app/libs/computations/release/step_statuses.rb
@@ -60,7 +60,7 @@ def release_candidate_status
end
def notes_status
- return STATUS[:unblocked] if any_platforms? { |rp| rp.metadata_editable_v2? }
+ return STATUS[:unblocked] if any_platforms? { |rp| rp.metadata_editable? }
STATUS[:success]
end
diff --git a/app/libs/coordinators.rb b/app/libs/coordinators.rb
index cc961c878..e1f667adb 100644
--- a/app/libs/coordinators.rb
+++ b/app/libs/coordinators.rb
@@ -39,10 +39,14 @@
# ā¢ This does not replace internal state machines of other sub-models.
# ā¢ It currently does not have any state of its own.
module Coordinators
- # TODO: [V2] fixes:
- # metadata
-
module Signals
+ def self.release_has_started!(release)
+ release.notify!("New release has commenced!", :release_started, release.notification_params)
+ Releases::PreReleaseJob.perform_later(release.id)
+ Releases::FetchCommitLogJob.perform_later(release.id)
+ RefreshReportsJob.perform_later(release.hotfixed_from.id) if release.hotfix?
+ end
+
def self.commits_have_landed!(release, head_commit, rest_commits)
Coordinators::ProcessCommits.call(release, head_commit, rest_commits)
end
@@ -97,10 +101,6 @@ def self.apply_build_queue!(build_queue)
Res.new { Coordinators::ApplyBuildQueue.call(build_queue) }
end
- def self.save_metadata!(release, metadata)
- # TODO: [V2] save metadata
- end
-
def self.start_workflow_run!(workflow_run)
Res.new do
raise "release is not actionable" unless workflow_run.triggering_release.actionable?
diff --git a/app/libs/coordinators/finalize_release.rb b/app/libs/coordinators/finalize_release.rb
index a2704fc8c..61cbdf3f7 100644
--- a/app/libs/coordinators/finalize_release.rb
+++ b/app/libs/coordinators/finalize_release.rb
@@ -46,8 +46,7 @@ def call
def on_finish!
release.update_train_version!
release.event_stamp!(reason: :finished, kind: :success, data: {version: release_version})
- notify_data = release.notification_params.merge(release.finalize_phase_metadata)
- release.notify!("Release has finished!", :release_ended, notify_data)
+ release.notify!("Release has finished!", :release_ended, release.notification_params)
RefreshReportsJob.perform_later(release.id)
end
diff --git a/app/libs/coordinators/finish_platform_run.rb b/app/libs/coordinators/finish_platform_run.rb
index 948c0b144..1bf96c3dc 100644
--- a/app/libs/coordinators/finish_platform_run.rb
+++ b/app/libs/coordinators/finish_platform_run.rb
@@ -19,7 +19,7 @@ def call
end
end
- RefreshPlatformBreakdownJob.perform_later(release_platform_run.id) if release.is_v2?
+ RefreshPlatformBreakdownJob.perform_later(release_platform_run.id)
ReleasePlatformRuns::CreateTagJob.perform_later(release_platform_run.id, last_commit.id) if train.tag_platform_at_release_end?
release_platform_run.event_stamp!(reason: :finished, kind: :success, data: {version: release_platform_run.release_version})
app.refresh_external_app
diff --git a/app/libs/coordinators/process_commits.rb b/app/libs/coordinators/process_commits.rb
index 221421456..edb275c21 100644
--- a/app/libs/coordinators/process_commits.rb
+++ b/app/libs/coordinators/process_commits.rb
@@ -28,11 +28,6 @@ def call
end
attempt_backmerge!(created_head_commit, created_rest_commits)
-
- # TODO: [V2] move this to trigger release
- if release.all_commits.size.eql?(1)
- release.notify!("New release has commenced!", :release_started, release.notification_params)
- end
end
private
diff --git a/app/libs/coordinators/start_release.rb b/app/libs/coordinators/start_release.rb
index 9bbfad62b..1cde48f7d 100644
--- a/app/libs/coordinators/start_release.rb
+++ b/app/libs/coordinators/start_release.rb
@@ -27,8 +27,6 @@ def call
raise "Could not kickoff a hotfix because the source tag does not exist" if hotfix_from_new_branch? && !hotfix_tag_exists?
raise "Could not kickoff a hotfix because the source release branch does not exist" if hotfix_from_previous_branch? && !hotfix_branch_exists?
raise "Cannot start a train that is not active!" if train.inactive?
- # TODO [V2]: Remove this method
- raise "Cannot start a train that has no release step. Please add at least one release step to the train." unless train.startable?
raise "No more releases can be started until the ongoing release is finished!" if train.ongoing_release.present? && automatic
raise "No more releases can be started until the ongoing release is finished!" if train.upcoming_release.present? && !hotfix?
raise "Upcoming releases are not allowed for your train." if train.ongoing_release.present? && !train.upcoming_release_startable? && !hotfix?
@@ -36,7 +34,7 @@ def call
raise "Hotfix platform - #{hotfix_platform} is not valid!" if invalid_hotfix_platform?
kickoff
- RefreshReportsJob.perform_later(release.hotfixed_from.id) if release.hotfix?
+ Coordinators::Signals.release_has_started!(release)
release
end
@@ -69,7 +67,7 @@ def create_release
hotfix_platform: (hotfix_platform if hotfix?),
custom_version: custom_version,
release_pilot_id: Current.user&.id,
- is_v2: train.product_v2?
+ is_v2: true # TODO: remove this after full removal of v2
)
end
diff --git a/app/libs/deployments/app_store_connect/release.rb b/app/libs/deployments/app_store_connect/release.rb
deleted file mode 100644
index 2acec69d7..000000000
--- a/app/libs/deployments/app_store_connect/release.rb
+++ /dev/null
@@ -1,315 +0,0 @@
-module Deployments
- module AppStoreConnect
- class Release
- include Loggable
-
- ExternalReleaseNotInTerminalState = Class.new(StandardError)
- ReleaseNotFullyLive = Class.new(StandardError)
- PreparedVersionNotFoundError = Class.new(StandardError)
-
- RETRYABLE_FAILURE_REASONS = [:attachment_upload_in_progress]
-
- def self.kickoff!(deployment_run)
- new(deployment_run).kickoff!
- end
-
- def self.update_build_notes!(deployment_run)
- new(deployment_run).update_build_notes!
- end
-
- def self.update_external_release(deployment_run)
- new(deployment_run).update_external_release
- end
-
- def self.to_test_flight!(deployment_run)
- new(deployment_run).to_test_flight!
- end
-
- def self.prepare_for_release!(deployment_run, force: false)
- new(deployment_run).prepare_for_release!(force:)
- end
-
- def self.submit_for_review!(deployment_run)
- new(deployment_run).submit_for_review!
- end
-
- def self.start_release!(deployment_run)
- new(deployment_run).start_release!
- end
-
- def self.track_live_release_status(deployment_run)
- new(deployment_run).track_live_release_status
- end
-
- def self.complete_phased_release!(deployment_run)
- new(deployment_run).complete_phased_release!
- end
-
- def self.pause_phased_release!(deployment_run)
- new(deployment_run).pause_phased_release!
- end
-
- def self.resume_phased_release!(deployment_run)
- new(deployment_run).resume_phased_release!
- end
-
- def self.halt_phased_release!(deployment_run)
- new(deployment_run).halt_phased_release!
- end
-
- def initialize(deployment_run)
- @deployment_run = deployment_run
- end
-
- attr_reader :deployment_run
- alias_method :run, :deployment_run
- delegate :provider,
- :deployment_channel,
- :build_number,
- :release_version,
- :staged_rollout?,
- :app_store_release?,
- :test_flight_release?,
- :app_store_integration?,
- :app_store?,
- :staged_rollout_config,
- :release_metadatum,
- :internal_channel?,
- :deployment_notes,
- to: :run
-
- def kickoff!
- return run.start_prepare_release! if app_store_release?
- Deployments::AppStoreConnect::TestFlightReleaseJob.perform_later(run.id) if test_flight_release?
- end
-
- # NOTE: likely moves to internal/beta step
- def to_test_flight!
- return unless test_flight_release?
-
- Deployments::AppStoreConnect::UpdateBuildNotesJob.perform_later(run.id)
-
- return internal_release! if internal_channel?
-
- result = provider.release_to_testflight(deployment_channel, build_number)
-
- unless result.ok?
- run.fail_with_error(result.error)
- return
- end
-
- run.submit_for_review!
- end
-
- # NOTE: likely moves to internal/beta step
- def update_build_notes!
- provider.update_release_notes(build_number, deployment_notes)
- end
-
- # NOTE: moves to store submission
- def prepare_for_release!(force: false)
- return unless app_store_release?
-
- metadata = [{
- whats_new: release_metadatum.release_notes,
- promotional_text: release_metadatum.promo_text,
- locale: release_metadatum.locale
- }]
-
- result = provider.prepare_release(build_number, release_version, staged_rollout?, metadata, force)
-
- unless result.ok?
- case result.error.reason
- when :release_not_found then raise PreparedVersionNotFoundError
- when :release_already_exists then run.fail_prepare_release!(reason: result.error.reason)
- else run.fail_with_error(result.error)
- end
-
- return
- end
-
- unless valid_release?(result.value!)
- run.dispatch_fail!(reason: :invalid_release)
- return
- end
-
- create_or_update_external_release(result.value!)
-
- run.prepare_release!
- run.event_stamp!(reason: :inflight_release_replaced, kind: :notice, data: {version: release_version}) if force
- end
-
- # NOTE: moves to store submission
- def submit_for_review!
- return unless app_store_release?
-
- result = provider.submit_release(build_number, release_version)
-
- unless result.ok?
- return run.update(failure_reason: result.error.reason) if result.error.reason.in? RETRYABLE_FAILURE_REASONS
- return run.fail_with_error(result.error)
- end
-
- run.submit_for_review!
- end
-
- # NOTE: moves to store submission
- def update_external_release
- return unless run.step_run.active? && app_store_integration?
-
- result = find_release
-
- unless result.ok?
- elog(result.error)
- raise ExternalReleaseNotInTerminalState, "Retrying in some time..."
- end
-
- release_info = result.value!
- create_or_update_external_release(release_info)
-
- if release_info.success?
- return run.ready_to_release! if app_store?
- run.complete!
- elsif release_info.failed?
- run.dispatch_fail!(reason: :developer_rejected)
- elsif release_info.waiting_for_review? && run.review_failed?
- # A failed review was re-submitted or responded to outside Tramline
- run.submit_for_review!(resubmission: true)
- else
- run.fail_review! if release_info.review_failed? && !run.review_failed?
- raise ExternalReleaseNotInTerminalState, "Retrying in some time..."
- end
- end
-
- # NOTE: likely moves to rollout step
- def start_release!
- return unless app_store_release?
-
- run.engage_release!
-
- result = provider.start_release(build_number)
-
- unless result.ok?
- run.fail_with_error(result.error)
- return
- end
-
- run.create_staged_rollout!(config: staged_rollout_config) if staged_rollout?
-
- Deployments::AppStoreConnect::FindLiveReleaseJob.perform_async(run.id)
- end
-
- # NOTE: likely moves to rollout step
- def track_live_release_status
- return unless app_store_release?
-
- result = provider.find_live_release
-
- unless result.ok?
- elog(result.error)
- raise ReleaseNotFullyLive, "Retrying in some time..."
- end
-
- release_info = result.value!
- create_or_update_external_release(release_info)
-
- if release_info.live?(build_number)
- return run.complete! unless staged_rollout?
- run.staged_rollout.update_stage(
- release_info.phased_release_stage,
- finish_rollout: release_info.phased_release_complete?
- )
- return if release_info.phased_release_complete?
- end
-
- raise ReleaseNotFullyLive, "Retrying in some time..."
- end
-
- # NOTE: likely moves to rollout step
- def complete_phased_release!
- return unless app_store_release?
-
- result = provider.complete_phased_release
-
- if result.ok?
- create_or_update_external_release(result.value!)
- else
- run.fail_with_error(result.error)
- end
-
- result
- end
-
- # NOTE: likely moves to rollout step
- def pause_phased_release!
- return unless app_store_release?
-
- result = provider.pause_phased_release
-
- if result.ok?
- release_info = result.value!
- create_or_update_external_release(release_info)
- run.staged_rollout.update_stage(
- release_info.phased_release_stage,
- finish_rollout: release_info.phased_release_complete?
- )
- end
-
- result
- end
-
- # NOTE: likely moves to rollout step
- def resume_phased_release!
- return unless app_store_release?
-
- result = provider.resume_phased_release
-
- if result.ok?
- release_info = result.value!
- create_or_update_external_release(release_info)
- run.staged_rollout.update_stage(
- release_info.phased_release_stage,
- finish_rollout: release_info.phased_release_complete?
- )
- end
-
- result
- end
-
- # NOTE: likely moves to rollout step
- def halt_phased_release!
- return unless app_store_release?
-
- provider.halt_phased_release
- end
-
- private
-
- # NOTE: likely moves to internal/beta step
- def internal_release!
- result = find_release
- unless result.ok?
- run.fail_with_error(result.error)
- return
- end
-
- release_info = result.value!
- create_or_update_external_release(release_info)
- run.complete!
- end
-
- def find_release
- return provider.find_release(build_number) if app_store?
- provider.find_build(build_number)
- end
-
- def create_or_update_external_release(release_info)
- (run.external_release || run.build_external_release).update(release_info.attributes)
- end
-
- def valid_release?(release_info)
- release_info.valid?(build_number, release_version, staged_rollout?)
- end
- end
- end
-end
diff --git a/app/libs/deployments/google_firebase/release.rb b/app/libs/deployments/google_firebase/release.rb
deleted file mode 100644
index 82803d1a3..000000000
--- a/app/libs/deployments/google_firebase/release.rb
+++ /dev/null
@@ -1,113 +0,0 @@
-module Deployments
- module GoogleFirebase
- class Release
- include Loggable
-
- UploadNotComplete = Class.new(StandardError)
-
- def self.kickoff!(deployment_run)
- new(deployment_run).kickoff!
- end
-
- def self.upload!(deployment_run)
- new(deployment_run).upload!
- end
-
- def self.update_upload_status!(deployment_run, op_name)
- new(deployment_run).update_upload_status!(op_name)
- end
-
- def self.update_build_notes!(deployment_run, release_id)
- new(deployment_run).update_build_notes!(release_id)
- end
-
- def self.start_release!(deployment_run)
- new(deployment_run).start_release!
- end
-
- def initialize(deployment_run)
- @deployment_run = deployment_run
- end
-
- attr_reader :deployment_run
- alias_method :run, :deployment_run
- delegate :provider,
- :deployment_channel,
- :build_number,
- :release_version,
- :google_firebase_integration?,
- :release_platform,
- :step_run,
- :deployment_notes,
- to: :run
- delegate :platform, to: :release_platform
-
- def kickoff!
- if (similar_run = step_run.similar_deployment_runs_for(run).find(&:has_uploaded?))
- run.create_external_release(similar_run.external_release.attributes.except("id", "created_at", "updated_at"))
- return run.upload!
- end
- Deployments::GoogleFirebase::UploadJob.perform_later(run.id)
- end
-
- def upload!
- return unless google_firebase_integration?
-
- run.with_lock do
- return if run.uploaded?
-
- run.build_artifact.with_open do |file|
- result = provider.upload(file, run.build_artifact.file.filename.to_s, platform:)
- if result.ok?
- run.start_upload!(op_name: result.value!)
- else
- run.fail_with_error(result.error)
- end
- end
- end
- end
-
- def update_upload_status!(op_name)
- return unless google_firebase_integration?
-
- result = provider.get_upload_status(op_name)
- unless result.ok?
- run.fail_with_error(result.error)
- return
- end
-
- op_info = result.value!
- raise UploadNotComplete unless op_info.done?
-
- release_info = op_info.release
- run.create_external_release(external_id: release_info.id,
- name: release_info.name,
- build_number: release_info.build_number,
- added_at: release_info.added_at,
- status: op_info.status,
- external_link: release_info.console_link)
- run.upload!
- Deployments::GoogleFirebase::UpdateBuildNotesJob.perform_later(run.id, release_info.id)
- end
-
- def update_build_notes!(release_id)
- provider.update_release_notes(release_id, deployment_notes)
- end
-
- def start_release!
- return unless google_firebase_integration?
-
- return run.complete! if deployment_channel == GoogleFirebaseIntegration::EMPTY_CHANNEL[:id].to_s
-
- result = provider.release(run.external_release.external_id, [deployment_channel])
-
- unless result.ok?
- run.fail_with_error(result.error)
- return
- end
-
- run.complete!
- end
- end
- end
-end
diff --git a/app/libs/deployments/google_play_store/release.rb b/app/libs/deployments/google_play_store/release.rb
deleted file mode 100644
index 2244fc37e..000000000
--- a/app/libs/deployments/google_play_store/release.rb
+++ /dev/null
@@ -1,181 +0,0 @@
-module Deployments
- module GooglePlayStore
- class Release
- include Loggable
-
- def self.kickoff!(deployment_run)
- new(deployment_run).kickoff!
- end
-
- def self.upload!(deployment_run)
- new(deployment_run).upload!
- end
-
- def self.start_release!(deployment_run)
- new(deployment_run).start_release!
- end
-
- def self.release_with(deployment_run, rollout_value:)
- new(deployment_run).release_with(rollout_value:)
- end
-
- def self.halt_release!(deployment_run)
- new(deployment_run).halt_release!
- end
-
- def self.release_to_all!(deployment_run)
- new(deployment_run).release_to_all!
- end
-
- def initialize(deployment_run)
- @deployment_run = deployment_run
- end
-
- attr_reader :deployment_run
- alias_method :run, :deployment_run
- delegate :provider,
- :deployment_channel,
- :build_number,
- :release_version,
- :staged_rollout?,
- :release_metadatum,
- :google_play_store_integration?,
- :staged_rollout_config,
- :production_channel?,
- :deployment_notes,
- :one_percent_beta_release?,
- to: :run
-
- def kickoff!
- return run.upload! if run.step_run.similar_deployment_runs_for(run).any?(&:has_uploaded?)
- Deployments::GooglePlayStore::Upload.perform_later(run.id)
- end
-
- # NOTE: likely moves to internal/beta step
- def upload!
- return unless google_play_store_integration?
-
- run.with_lock do
- return if run.uploaded?
-
- run.build_artifact.with_open do |file|
- result = provider.upload(file)
- if result.ok?
- run.upload!
- else
- run.fail_with_error(result.error)
- end
- end
- end
- end
-
- # NOTE: likely moves to rollout step
- def start_release!
- return unless google_play_store_integration?
-
- skip_release = run.step_run.deployment_restarted? && provider.build_present_in_channel?(deployment_channel, build_number)
-
- if staged_rollout?
- run.engage_release!
- create_draft_release!(skip_release:)
- else
- fully_release!(skip_release:)
- end
- end
-
- # NOTE: likely moves to rollout step
- def halt_release!
- return unless google_play_store_integration?
- return unless run.rollout_started?
-
- provider.halt_release(
- deployment_channel,
- build_number,
- release_version,
- run.staged_rollout.last_rollout_percentage
- )
- end
-
- # NOTE: likely moves to rollout step
- def release_to_all!
- return unless google_play_store_integration?
-
- result = provider.rollout_release(
- deployment_channel,
- build_number,
- release_version,
- Deployment::FULL_ROLLOUT_VALUE,
- release_notes
- )
-
- run.fail_with_error(result.error) unless result.ok?
- result
- end
-
- # NOTE: likely moves to rollout step
- def release_with(rollout_value:)
- return unless google_play_store_integration?
-
- result = provider.rollout_release(
- deployment_channel,
- build_number,
- release_version,
- rollout_value,
- release_notes
- )
-
- run.fail_with_error(result.error) unless result.ok?
- result
- end
-
- private
-
- # NOTE: likely moves to rollout step
- def fully_release!(skip_release: false)
- return run.complete! if skip_release
-
- rollout_value = one_percent_beta_release? ? BigDecimal("1") : Deployment::FULL_ROLLOUT_VALUE
- result = provider.rollout_release(
- deployment_channel,
- build_number,
- release_version,
- rollout_value,
- release_notes
- )
-
- if result.ok?
- run.complete!
- else
- run.fail_with_error(result.error)
- end
- end
-
- # NOTE: moves to submission step
- def create_draft_release!(skip_release: false)
- return run.create_staged_rollout!(config: staged_rollout_config) if skip_release
-
- result = provider.create_draft_release(
- deployment_channel,
- build_number,
- release_version,
- release_notes
- )
-
- if result.ok?
- run.create_staged_rollout!(config: staged_rollout_config)
- else
- run.fail_with_error(result.error)
- end
- end
-
- def release_notes
- return [] if deployment_notes.blank?
-
- [{
- language: release_metadatum.locale,
- text: deployment_notes
- }]
- end
- end
- end
-end
diff --git a/app/libs/notifiers/slack/builder.rb b/app/libs/notifiers/slack/builder.rb
index c58bc8fe6..1d12bc460 100644
--- a/app/libs/notifiers/slack/builder.rb
+++ b/app/libs/notifiers/slack/builder.rb
@@ -4,25 +4,11 @@ class Builder
# there are individual classes for each message so that any state, if necessary, can be encapsulated
# think of them as view components
RENDERERS = {
- deployment_finished: Renderers::DeploymentFinished,
release_ended: Renderers::ReleaseEnded,
release_stopped: Renderers::ReleaseStopped,
release_started: Renderers::ReleaseStarted,
- step_started: Renderers::StepStarted,
- step_failed: Renderers::StepFailed,
- build_available: Renderers::BuildAvailable,
- submit_for_review: Renderers::SubmitForReview,
- review_approved: Renderers::ReviewApproved,
- review_failed: Renderers::ReviewFailed,
- staged_rollout_updated: Renderers::StagedRolloutUpdated,
release_scheduled: Renderers::ReleaseScheduled,
backmerge_failed: Renderers::BackmergeFailed,
- staged_rollout_paused: Renderers::StagedRolloutPaused,
- staged_rollout_resumed: Renderers::StagedRolloutResumed,
- staged_rollout_halted: Renderers::StagedRolloutHalted,
- staged_rollout_completed: Renderers::StagedRolloutCompleted,
- staged_rollout_fully_released: Renderers::StagedRolloutFullyReleased,
- deployment_failed: Renderers::DeploymentFailed,
release_health_events: Renderers::ReleaseHealthEvents,
build_available_v2: Renderers::BuildAvailableV2,
internal_release_finished: Renderers::InternalReleaseFinished,
diff --git a/app/libs/notifiers/slack/renderers/base.rb b/app/libs/notifiers/slack/renderers/base.rb
index 70f47eb00..012261ef3 100644
--- a/app/libs/notifiers/slack/renderers/base.rb
+++ b/app/libs/notifiers/slack/renderers/base.rb
@@ -1,13 +1,11 @@
class Notifiers::Slack::Renderers::Base
include Rails.application.routes.url_helpers
- include DeploymentsHelper
include ActionView::Helpers::JavaScriptHelper
NOTIFIERS_RELATIVE_PATH = "app/views/notifiers/slack".freeze
ROOT_PATH = Rails.root.join(NOTIFIERS_RELATIVE_PATH)
HEADER_TEMPLATE = "header.json.erb".freeze
FOOTER_TEMPLATE = "footer.json.erb".freeze
- FOOTER_V2_TEMPLATE = "footer_v2.json.erb".freeze
def self.render_json(**args)
new(**args).render_json
@@ -40,7 +38,7 @@ def render_notification
end
def render_footer
- file = File.read(File.join(ROOT_PATH, @is_v2 ? FOOTER_V2_TEMPLATE : FOOTER_TEMPLATE))
+ file = File.read(File.join(ROOT_PATH, FOOTER_TEMPLATE))
ERB.new(file).result(binding)
end
@@ -48,11 +46,6 @@ def template_file
File.read(File.join(ROOT_PATH, self.class::TEMPLATE_FILE))
end
- def deployment_channel_display_name
- return unless @deployment_channel
- deployment_channel_name(@deployment_channel)
- end
-
def safe_string(s) = escape_javascript(s)
def google_managed_publishing_text
diff --git a/app/libs/notifiers/slack/renderers/build_available.rb b/app/libs/notifiers/slack/renderers/build_available.rb
deleted file mode 100644
index 5e5b2abc9..000000000
--- a/app/libs/notifiers/slack/renderers/build_available.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-module Notifiers
- module Slack
- class Renderers::BuildAvailable < Renderers::Base
- TEMPLATE_FILE = "build_available.json.erb".freeze
- end
- end
-end
diff --git a/app/libs/notifiers/slack/renderers/deployment_failed.rb b/app/libs/notifiers/slack/renderers/deployment_failed.rb
deleted file mode 100644
index 69ae36bfd..000000000
--- a/app/libs/notifiers/slack/renderers/deployment_failed.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-module Notifiers
- module Slack
- class Renderers::DeploymentFailed < Renderers::Base
- TEMPLATE_FILE = "deployment_failed.json.erb".freeze
- end
- end
-end
diff --git a/app/libs/notifiers/slack/renderers/deployment_finished.rb b/app/libs/notifiers/slack/renderers/deployment_finished.rb
deleted file mode 100644
index 65393e1f8..000000000
--- a/app/libs/notifiers/slack/renderers/deployment_finished.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-module Notifiers
- module Slack
- class Renderers::DeploymentFinished < Renderers::Base
- TEMPLATE_FILE = "deployment_finished.json.erb".freeze
-
- def sanitized_build_notes
- safe_string("*Build notes*\n```#{@build_notes}```")
- end
-
- def sanitized_release_notes
- safe_string(":spiral_note_pad: *What's New*\n\n```#{@release_notes}```")
- end
- end
- end
-end
diff --git a/app/libs/notifiers/slack/renderers/release_ended.rb b/app/libs/notifiers/slack/renderers/release_ended.rb
index 7d65eb543..8cc3ffbeb 100644
--- a/app/libs/notifiers/slack/renderers/release_ended.rb
+++ b/app/libs/notifiers/slack/renderers/release_ended.rb
@@ -1,7 +1,14 @@
module Notifiers
module Slack
+ include ActionView::Helpers::DateHelper
+
class Renderers::ReleaseEnded < Renderers::Base
TEMPLATE_FILE = "release_ended.json.erb".freeze
end
+
+ def total_run_time
+ return "N/A" if @release_completed_at.blank? || @release_started_at.blank?
+ distance_of_time_in_words(@release_started_at, @release_completed_at)
+ end
end
end
diff --git a/app/libs/notifiers/slack/renderers/review_approved.rb b/app/libs/notifiers/slack/renderers/review_approved.rb
deleted file mode 100644
index b7d360194..000000000
--- a/app/libs/notifiers/slack/renderers/review_approved.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-module Notifiers
- module Slack
- class Renderers::ReviewApproved < Renderers::Base
- TEMPLATE_FILE = "review_approved.json.erb".freeze
- end
- end
-end
diff --git a/app/libs/notifiers/slack/renderers/review_failed.rb b/app/libs/notifiers/slack/renderers/review_failed.rb
deleted file mode 100644
index d04ace021..000000000
--- a/app/libs/notifiers/slack/renderers/review_failed.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-module Notifiers
- module Slack
- class Renderers::ReviewFailed < Renderers::Base
- TEMPLATE_FILE = "review_failed.json.erb".freeze
-
- def apple_review_failed_text
- "You can resolve the rejection from the App Store Connect, or, submit a new build for review."
- end
- end
- end
-end
diff --git a/app/libs/notifiers/slack/renderers/staged_rollout_completed.rb b/app/libs/notifiers/slack/renderers/staged_rollout_completed.rb
deleted file mode 100644
index 21214e025..000000000
--- a/app/libs/notifiers/slack/renderers/staged_rollout_completed.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-module Notifiers
- module Slack
- class Renderers::StagedRolloutCompleted < Renderers::Base
- TEMPLATE_FILE = "staged_rollout_updated.json.erb".freeze
-
- def main_text
- "Staged rollout for the release is now *complete* at stage *#{@current_stage} (#{@rollout_percentage}%)*."
- end
-
- def secondary_text
- "The rollout on this release is now locked on Tramline and cannot be altered further."
- end
- end
- end
-end
diff --git a/app/libs/notifiers/slack/renderers/staged_rollout_fully_released.rb b/app/libs/notifiers/slack/renderers/staged_rollout_fully_released.rb
deleted file mode 100644
index d3caac3e9..000000000
--- a/app/libs/notifiers/slack/renderers/staged_rollout_fully_released.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-module Notifiers
- module Slack
- class Renderers::StagedRolloutFullyReleased < Renderers::Base
- TEMPLATE_FILE = "staged_rollout_updated.json.erb".freeze
-
- def main_text
- "Your staged rollout has been accelerated to a *full release to all users* from stage #{@current_stage} (#{@rollout_percentage}%)."
- end
-
- def secondary_text = nil
- end
- end
-end
diff --git a/app/libs/notifiers/slack/renderers/staged_rollout_halted.rb b/app/libs/notifiers/slack/renderers/staged_rollout_halted.rb
deleted file mode 100644
index 13c42538e..000000000
--- a/app/libs/notifiers/slack/renderers/staged_rollout_halted.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-module Notifiers
- module Slack
- class Renderers::StagedRolloutHalted < Renderers::Base
- TEMPLATE_FILE = "staged_rollout_updated.json.erb".freeze
-
- def main_text
- "Staged rollout for the release was *halted* at *#{@current_stage} (#{@rollout_percentage}%)*."
- end
-
- def secondary_text
- "You can not change this release from Tramline any more."
- end
- end
- end
-end
diff --git a/app/libs/notifiers/slack/renderers/staged_rollout_paused.rb b/app/libs/notifiers/slack/renderers/staged_rollout_paused.rb
deleted file mode 100644
index 837cbae84..000000000
--- a/app/libs/notifiers/slack/renderers/staged_rollout_paused.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-module Notifiers
- module Slack
- class Renderers::StagedRolloutPaused < Renderers::Base
- TEMPLATE_FILE = "staged_rollout_updated.json.erb".freeze
-
- def main_text
- "Staged rollout for the release was *paused* at *#{@current_stage} (#{@rollout_percentage}%)*."
- end
-
- def secondary_text
- "You can choose to *Resume* or *Halt* your release from the live release page."
- end
- end
- end
-end
diff --git a/app/libs/notifiers/slack/renderers/staged_rollout_resumed.rb b/app/libs/notifiers/slack/renderers/staged_rollout_resumed.rb
deleted file mode 100644
index 00c6ef6f5..000000000
--- a/app/libs/notifiers/slack/renderers/staged_rollout_resumed.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-module Notifiers
- module Slack
- class Renderers::StagedRolloutResumed < Renderers::Base
- TEMPLATE_FILE = "staged_rollout_updated.json.erb".freeze
-
- def main_text
- "Staged rollout for the release was *resumed* at *#{@current_stage} (#{@rollout_percentage}%)*."
- end
-
- def secondary_text
- "You can choose to *Pause* or *Halt* your release from the live release page."
- end
- end
- end
-end
diff --git a/app/libs/notifiers/slack/renderers/staged_rollout_updated.rb b/app/libs/notifiers/slack/renderers/staged_rollout_updated.rb
deleted file mode 100644
index a3f958392..000000000
--- a/app/libs/notifiers/slack/renderers/staged_rollout_updated.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-module Notifiers
- module Slack
- class Renderers::StagedRolloutUpdated < Renderers::Base
- TEMPLATE_FILE = "staged_rollout_updated.json.erb".freeze
-
- def main_text
- if @is_fully_released
- "Your staged rollout is *complete*, and your update has rolled out to all users."
- else
- "Your staged rollout is *active*, and you are currently on stage *#{@current_stage} (#{@rollout_percentage}%)*."
- end
- end
-
- def secondary_text
- if @is_fully_released
- "View your release on the #{store}."
- else
- action_text
- end
- end
-
- def store
- if @is_app_store_production
- "App Store"
- elsif @is_play_store_production
- "Play Console"
- end
- end
-
- def action_text
- if @is_app_store_production
- "You can choose to *Pause*, *Halt*, or *Release to 100%*."
- elsif @is_play_store_production
- "You can choose to *Increase*, *Halt*, or *Release to 100%*."
- end
- end
- end
- end
-end
diff --git a/app/libs/notifiers/slack/renderers/step_failed.rb b/app/libs/notifiers/slack/renderers/step_failed.rb
deleted file mode 100644
index 32c6fb046..000000000
--- a/app/libs/notifiers/slack/renderers/step_failed.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-module Notifiers
- module Slack
- class Renderers::StepFailed < Renderers::Base
- TEMPLATE_FILE = "step_failed.json.erb".freeze
-
- def manual_submission_required_text
- "- Due to a previous rejection, new changes cannot be submitted to the store from Tramline. Please submit the current build (#{@build_number}) for review manually from the Google Play Console by creating a release in a public track (eg. Closed testing, Open testing). Once that is done, you can sync the store status with Tramline and move forward with the release train."
- end
- end
- end
-end
diff --git a/app/libs/notifiers/slack/renderers/step_started.rb b/app/libs/notifiers/slack/renderers/step_started.rb
deleted file mode 100644
index 4defe076a..000000000
--- a/app/libs/notifiers/slack/renderers/step_started.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-module Notifiers
- module Slack
- class Renderers::StepStarted < Renderers::Base
- TEMPLATE_FILE = "step_started.json.erb".freeze
- end
- end
-end
diff --git a/app/libs/notifiers/slack/renderers/submit_for_review.rb b/app/libs/notifiers/slack/renderers/submit_for_review.rb
deleted file mode 100644
index c38bf7573..000000000
--- a/app/libs/notifiers/slack/renderers/submit_for_review.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-module Notifiers
- module Slack
- class Renderers::SubmitForReview < Renderers::Base
- TEMPLATE_FILE = "submit_for_review.json.erb".freeze
-
- def sanitized_release_notes
- safe_string(":spiral_note_pad: *What's New*\n\n```#{@release_notes}```")
- end
-
- def submitted_text
- return "resubmitted" if @resubmission
- "submitted"
- end
- end
- end
-end
diff --git a/app/libs/queries/events.rb b/app/libs/queries/events.rb
index 9c7822597..38b72b396 100644
--- a/app/libs/queries/events.rb
+++ b/app/libs/queries/events.rb
@@ -16,24 +16,10 @@ def initialize(release:, params:)
attr_reader :release, :params
def all
- ids = release.is_v2? ? v2_stampable_ids : stampable_ids
- Passport.where(stampable_id: ids).order(event_timestamp: :desc)
+ Passport.where(stampable_id: stampable_ids).order(event_timestamp: :desc)
end
def stampable_ids
- release
- .release_platform_runs
- .joins(:release_platform)
- .where(ActiveRecord::Base.sanitize_sql_for_conditions(params.filter_by(FILTER_MAPPING)))
- .left_joins(step_runs: [:commit, [deployment_runs: :staged_rollout]])
- .pluck("commits.id, release_platform_runs.id, step_runs.id, deployment_runs.id, staged_rollouts.id")
- .flatten
- .uniq
- .compact
- .push(release.id)
- end
-
- def v2_stampable_ids
run_ids = release
.release_platform_runs
.joins(:release_platform)
diff --git a/app/libs/queries/release_summary.rb b/app/libs/queries/release_summary.rb
deleted file mode 100644
index d99288133..000000000
--- a/app/libs/queries/release_summary.rb
+++ /dev/null
@@ -1,216 +0,0 @@
-class Queries::ReleaseSummary
- include Memery
- include Loggable
-
- def self.warm(release_id)
- new(release_id).warm
- end
-
- def self.all(release_id)
- new(release_id).all
- end
-
- def initialize(release_id)
- @release_id = release_id
- end
-
- def warm
- cache.write(cache_key, data)
- rescue => e
- elog(e)
- end
-
- def all
- cache.fetch(cache_key)
- end
-
- def team_stability_commits
- cache.fetch(team_stability_commits_cache_key) { release.stability_commits.count_by_team(release.organization) }
- end
-
- def team_release_commits
- cache.fetch(team_release_commits_cache_key) { release.release_changelog&.commits_by_team }
- end
-
- class Queries::ReleaseSummary::Overall
- include ActiveModel::Model
- include ActiveModel::Attributes
-
- attribute :tag, :string
- attribute :tag_url, :string
- attribute :version, :string
- attribute :kickoff_at, :datetime
- attribute :finished_at, :datetime
- attribute :backmerge_pr_count, :integer
- attribute :backmerge_failure_count, :integer
- attribute :commits_count, :integer
- attribute :duration, :integer
- attribute :is_hotfix, :boolean
- attribute :hotfixed_from, :string
- attribute :hotfixes, default: {}
-
- def self.from_release(release)
- attributes = {
- tag: release.tag_name,
- tag_url: release.tag_url,
- version: release.release_version,
- kickoff_at: release.scheduled_at,
- finished_at: release.completed_at,
- backmerge_pr_count: release.backmerge_prs.size,
- backmerge_failure_count: release.backmerge_failure_count,
- commits_count: release.all_commits.size,
- duration: release.duration&.seconds,
- is_hotfix: release.hotfix?,
- hotfixes: release.all_hotfixes.map { |r| [r.release_version, r.live_release_link] }.to_h
- }
-
- if release.hotfix?
- attributes[:hotfixed_from] = release.hotfixed_from.release_version
- end
-
- new(attributes)
- end
-
- def inspect
- format(
- "#",
- attributes: attributes.map { |key, value| "#{key}=#{value}" }.join(" ")
- )
- end
- end
-
- class Queries::ReleaseSummary::StepsSummary
- def self.from_release(release)
- attributes = release.release_platform_runs.map do |pr|
- release_phase_start = pr.step_runs_for(pr.release_platform.release_step)&.first&.scheduled_at
- pr.release_platform.active_steps_for(release).map do |step|
- step_runs = pr.step_runs_for(step).sequential
- last_step_run = step_runs.last
- started_at = step_runs.first&.scheduled_at
- ended_at = (step.review? ? release_phase_start : step_runs.last&.updated_at) if last_step_run && !last_step_run.active?
- {
- name: step.name,
- platform: pr.display_attr(:platform),
- platform_raw: pr.platform,
- started_at: started_at,
- phase: step.kind,
- ended_at: ended_at,
- duration: (ActiveSupport::Duration.seconds(ended_at - started_at) if started_at && ended_at),
- builds_created_count: step_runs.not_failed.size
- }
- end
- end
-
- new(attributes.flatten.map { StepSummary.new(_1) })
- end
-
- def initialize(steps_summary)
- @steps_summary = steps_summary
- end
-
- def all = @steps_summary
-
- class StepSummary
- include ActiveModel::Model
- include ActiveModel::Attributes
-
- attribute :started_at, :datetime
- attribute :ended_at, :datetime
- attribute :duration, :integer
- attribute :platform, :string
- attribute :platform_raw, :string
- attribute :phase, :string
- attribute :builds_created_count, :integer
- attribute :name, :string
- end
- end
-
- class Queries::ReleaseSummary::StoreVersions
- def self.from_release(release)
- attributes = release.deployment_runs.order(created_at: :desc).reached_production.map do |dr|
- {
- version: dr.step_run.build_version,
- build_number: dr.step_run.build_number,
- built_at: dr.scheduled_at,
- submitted_at: dr.submitted_at,
- release_started_at: dr.release_started_at,
- staged_rollouts: dr.staged_rollout_events,
- platform: dr.release_platform_run.display_attr(:platform)
- }
- end
-
- new(attributes.map { StoreVersion.new(_1) })
- end
-
- def initialize(store_versions)
- @store_versions = store_versions
- end
-
- def all = @store_versions
-
- class StoreVersion
- include ActiveModel::Model
- include ActiveModel::Attributes
-
- attribute :version, :string
- attribute :build_number, :string
- attribute :built_at, :datetime
- attribute :submitted_at, :datetime
- attribute :release_started_at, :datetime
- attribute :staged_rollouts, array: true, default: []
- attribute :platform, :string
-
- def inspect
- format(
- "#",
- attributes: attributes.map { |key, value| "#{key}=#{value}" }.join(" ")
- )
- end
- end
- end
-
- private
-
- attr_reader :release_id
- delegate :cache, to: Rails
-
- memoize def release
- Release
- .where(id: release_id)
- .includes(:all_commits,
- :pull_requests,
- train: [:release_platforms],
- release_platform_runs: {step_runs: {deployment_runs: [{deployment: [:integration]}, :staged_rollout]}})
- .sole
- end
-
- def data
- {
- overall: Overall.from_release(release),
- steps_summary: StepsSummary.from_release(release),
- store_versions: StoreVersions.from_release(release),
- pull_requests: release.pull_requests.automatic,
- team_stability_commits: team_stability_commits,
- team_release_commits: team_release_commits,
- reldex: release.index_score
- }
- end
-
- def thaw
- cache.delete(cache_key)
- cache.delete(team_stability_commits_cache_key)
- cache.delete(team_release_commits_cache_key)
- end
-
- def cache_key
- "release/#{release_id}/summary"
- end
-
- def team_stability_commits_cache_key
- "release/#{release_id}/team_stability_commits"
- end
-
- def team_release_commits_cache_key
- "release/#{release_id}/team_release_commits"
- end
-end
diff --git a/app/libs/triggers/deployment.rb b/app/libs/triggers/deployment.rb
deleted file mode 100644
index 3142b183f..000000000
--- a/app/libs/triggers/deployment.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-class Triggers::Deployment
- include Memery
- delegate :transaction, to: ::DeploymentRun
-
- def self.call(step_run:, deployment:)
- new(step_run:, deployment:).call
- end
-
- def initialize(step_run:, deployment:)
- @step_run = step_run
- @deployment = deployment
- @starting_time = Time.current
- end
-
- def call
- transaction do
- step_run
- .deployment_runs
- .create!(deployment:, scheduled_at: starting_time)
- .dispatch!
- end
- end
-
- private
-
- attr_reader :deployment, :step_run, :starting_time
-end
diff --git a/app/libs/triggers/step_run.rb b/app/libs/triggers/step_run.rb
deleted file mode 100644
index 9d1ea6858..000000000
--- a/app/libs/triggers/step_run.rb
+++ /dev/null
@@ -1,29 +0,0 @@
-class Triggers::StepRun
- def self.call(step, commit, release_platform_run)
- new(step, commit, release_platform_run).call
- end
-
- def initialize(step, commit, release_platform_run)
- @step = step
- @release_platform_run = release_platform_run
- @commit = commit
- end
-
- # FIXME: should we take a lock around this release? what is someone double triggers the run?
- def call
- release_platform_run.correct_version!
- release_platform_run
- .step_runs
- .create!(step:, scheduled_at: Time.current, commit:, build_version:, sign_required: false)
- end
-
- private
-
- attr_reader :step, :release_platform_run, :commit
-
- def build_version
- version = release_platform_run.release_version
- version += "-" + step.release_suffix if step.suffixable?
- version
- end
-end
diff --git a/app/libs/webhook_handlers/base.rb b/app/libs/webhook_handlers/base.rb
deleted file mode 100644
index 1c3410cb7..000000000
--- a/app/libs/webhook_handlers/base.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-class WebhookHandlers::Base
- include SiteHttp
- include Memery
-
- GITHUB = WebhookHandlers::Github
- GITLAB = WebhookHandlers::Gitlab
-
- attr_reader :payload, :train
-
- def self.process(train, payload)
- new(train, payload).process
- end
-
- def initialize(train, payload)
- @train = train
- @payload = payload
- end
-
- def release
- @release ||= train.active_runs.for_branch(branch_name)
- end
-
- private
-
- delegate :vcs_provider, to: :train
-end
diff --git a/app/libs/webhook_handlers/github/pull_request.rb b/app/libs/webhook_handlers/github/pull_request.rb
deleted file mode 100644
index e10a20390..000000000
--- a/app/libs/webhook_handlers/github/pull_request.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-class WebhookHandlers::Github::PullRequest
- using RefinedHash
- attr_reader :payload
-
- def initialize(payload)
- @payload = payload
- end
-
- def head_branch_name
- payload[:pull_request][:head][:ref]
- end
-
- def base_branch_name
- payload[:pull_request][:base][:ref]
- end
-
- def closed?
- pull_request[:state] == "closed"
- end
-
- def opened?
- pull_request[:state] == "open"
- end
-
- def repository_name
- payload[:repository][:full_name]
- end
-
- def pull_request
- Installations::Response::Keys
- .transform([payload[:pull_request]], GithubIntegration::PR_TRANSFORMATIONS)
- .first
- .merge_if_present(source: :github)
- end
-end
diff --git a/app/libs/webhook_handlers/github/push.rb b/app/libs/webhook_handlers/github/push.rb
deleted file mode 100644
index 63b75e44c..000000000
--- a/app/libs/webhook_handlers/github/push.rb
+++ /dev/null
@@ -1,48 +0,0 @@
-class WebhookHandlers::Github::Push
- attr_reader :payload
-
- def initialize(payload)
- @payload = payload
- end
-
- def head_commit
- head_commit_payload
- end
-
- def rest_commits
- rest_commits_payload.reject { |commit| commit[:commit_hash] == head_commit[:commit_hash] }
- end
-
- def valid_branch?
- payload["ref"]&.include?("refs/heads/")
- end
-
- # github adds tag events as part of the push events
- def valid_tag?
- payload["ref"]&.include?("refs/tags/")
- end
-
- def branch_name
- payload["ref"].delete_prefix("refs/heads/") if valid_branch?
- end
-
- def repository_name
- payload["repository"]["full_name"]
- end
-
- private
-
- def commits
- payload["commits"].presence || []
- end
-
- def head_commit_payload
- Installations::Response::Keys
- .transform([payload["head_commit"]], GithubIntegration::COMMITS_HOOK_TRANSFORMATIONS)
- .first
- end
-
- def rest_commits_payload
- Installations::Response::Keys.transform(commits, GithubIntegration::COMMITS_HOOK_TRANSFORMATIONS)
- end
-end
diff --git a/app/libs/webhook_handlers/gitlab/pull_request.rb b/app/libs/webhook_handlers/gitlab/pull_request.rb
deleted file mode 100644
index a2a3cc060..000000000
--- a/app/libs/webhook_handlers/gitlab/pull_request.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-class WebhookHandlers::Gitlab::PullRequest
- attr_reader :payload
-
- def initialize(payload)
- @payload = payload
- end
-
- def head_branch_name
- payload["object_attributes"]["source_branch"]
- end
-
- def base_branch_name
- payload["object_attributes"]["target_branch"]
- end
-
- def closed?
- pull_request[:state] == "closed" || pull_request[:state] == "merged"
- end
-
- def opened?
- pull_request[:state] == "opened"
- end
-
- def repository_name
- payload["project"]["path_with_namespace"]
- end
-
- def pull_request
- Installations::Response::Keys
- .transform([payload["object_attributes"]], GitlabIntegration::WEBHOOK_PR_TRANSFORMATIONS)
- .first
- .merge(source: :gitlab)
- end
-end
diff --git a/app/libs/webhook_handlers/gitlab/push.rb b/app/libs/webhook_handlers/gitlab/push.rb
deleted file mode 100644
index 00304b460..000000000
--- a/app/libs/webhook_handlers/gitlab/push.rb
+++ /dev/null
@@ -1,54 +0,0 @@
-class WebhookHandlers::Gitlab::Push
- attr_reader :payload, :train
-
- def initialize(payload, train)
- @payload = payload
- @train = train
- end
-
- def head_commit
- head_commit_payload
- .find { |commit| commit[:commit_hash] == head_sha }
- end
-
- def rest_commits
- commits_payload
- .reject { |commit| commit[:commit_hash] == head_sha }
- end
-
- # we do not listen to gitlab tag events, they are not included in the push events as with github
- def valid_tag?
- false
- end
-
- def valid_branch?
- payload["ref"]&.include?("refs/heads/")
- end
-
- def branch_name
- payload["ref"].delete_prefix("refs/heads/") if valid_branch?
- end
-
- def repository_name
- payload["project"]["path_with_namespace"]
- end
-
- private
-
- def commits
- payload["commits"].presence || []
- end
-
- def head_sha
- payload["checkout_sha"]
- end
-
- def head_commit_payload
- return [train.vcs_provider.get_commit(head_sha)] if commits.blank?
- commits_payload
- end
-
- def commits_payload
- Installations::Response::Keys.transform(commits, GitlabIntegration::COMMITS_HOOK_TRANSFORMATIONS)
- end
-end
diff --git a/app/libs/webhook_handlers/pull_request.rb b/app/libs/webhook_handlers/pull_request.rb
deleted file mode 100644
index 9b874ebfd..000000000
--- a/app/libs/webhook_handlers/pull_request.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-class WebhookHandlers::PullRequest < WebhookHandlers::Base
- def process
- return Response.new(:accepted, "Invalid repo/branch") unless valid_repo?
-
- if opened? && train.active_release_for?(base_branch_name)
- WebhookProcessors::OpenPullRequestJob.perform_later(train.id, pull_request)
- end
-
- if closed? && open_active_prs?
- WebhookProcessors::ClosePullRequestJob.perform_later(train.id, pull_request)
- end
-
- Response.new(:accepted)
- end
-
- private
-
- delegate :pull_request, :closed?, :opened?, :head_branch_name, :base_branch_name, :repository_name, to: :runner
-
- memoize def runner
- return GITHUB::PullRequest.new(payload) if vcs_provider.integration.github_integration?
- GITLAB::PullRequest.new(payload) if vcs_provider.integration.gitlab_integration?
- end
-
- memoize def open_active_prs?
- train.open_active_prs_for?(head_branch_name)
- end
-
- def valid_repo?
- (train.app.config&.code_repository_name == repository_name)
- end
-end
diff --git a/app/libs/webhook_handlers/push.rb b/app/libs/webhook_handlers/push.rb
deleted file mode 100644
index 25e3e0243..000000000
--- a/app/libs/webhook_handlers/push.rb
+++ /dev/null
@@ -1,29 +0,0 @@
-class WebhookHandlers::Push < WebhookHandlers::Base
- def process
- return Response.new(:accepted) if valid_tag?
- return Response.new(:accepted, "No release") unless release
- return Response.new(:accepted) unless release.committable?
- return Response.new(:accepted, "Skipping the commit") unless relevant_commit?
- return Response.new(:accepted, "Invalid repo/branch") unless valid_repo_and_branch?
-
- WebhookProcessors::PushJob.perform_later(release.id, head_commit, rest_commits)
- Response.new(:accepted)
- end
-
- private
-
- delegate :branch_name, :repository_name, :valid_tag?, :head_commit, :rest_commits, to: :runner
-
- memoize def runner
- return GITHUB::Push.new(payload) if vcs_provider.integration.github_integration?
- GITLAB::Push.new(payload, train) if vcs_provider.integration.gitlab_integration?
- end
-
- def relevant_commit?
- release.release_branch == branch_name
- end
-
- def valid_repo_and_branch?
- (train.app.config&.code_repository_name == repository_name) if branch_name
- end
-end
diff --git a/app/libs/webhook_processors/push.rb b/app/libs/webhook_processors/push.rb
deleted file mode 100644
index 48daf782c..000000000
--- a/app/libs/webhook_processors/push.rb
+++ /dev/null
@@ -1,64 +0,0 @@
-class WebhookProcessors::Push
- include Loggable
-
- def self.process(release, head_commit, rest_commits)
- new(release, head_commit, rest_commits).process
- end
-
- def initialize(release, head_commit, rest_commits = [])
- @release = release
- @head_commit = head_commit
- @rest_commits = rest_commits
- end
-
- def process
- release.with_lock do
- return unless release.committable?
- release.close_pre_release_prs
-
- if release.created?
- release.start!
- release.release_platform_runs.each(&:start!)
- end
-
- create_other_commits!
- create_head_commit!
- end
-
- # TODO: [V2] move this to trigger release
- if release.all_commits.size.eql?(1)
- release.notify!("New release has commenced!", :release_started, release.notification_params)
- end
- end
-
- private
-
- delegate :train, to: :release
- attr_reader :release, :head_commit, :rest_commits
-
- def create_head_commit!
- Commit.find_or_create_by!(commit_params(head_commit)).trigger!
- end
-
- def create_other_commits!
- rest_commits.each { Commit.find_or_create_by!(commit_params(_1)).add_to_build_queue!(is_head_commit: false) }
- end
-
- def commit_params(attributes)
- attributes
- .slice(:commit_hash, :message, :timestamp, :author_name, :author_email, :author_login, :url)
- .merge(release:)
- .merge(parents: commit_log.find { _1[:commit_hash] == attributes[:commit_hash] }&.dig(:parents))
- end
-
- # TODO: fetch parents for Gitlab commits also
- def commit_log
- return @commit_log ||= [train.vcs_provider.get_commit(head_commit[:commit_hash])] if rest_commits.empty?
-
- @commit_log ||= train.vcs_provider.commit_log(rest_commits.first[:commit_hash], head_commit[:commit_hash])
- @commit_log << train.vcs_provider.get_commit(rest_commits.first[:commit_hash])
- rescue => e
- elog(e)
- @commit_log = []
- end
-end
diff --git a/app/libs/workflow_processors/workflow_run.rb b/app/libs/workflow_processors/workflow_run.rb
deleted file mode 100644
index 04531f281..000000000
--- a/app/libs/workflow_processors/workflow_run.rb
+++ /dev/null
@@ -1,76 +0,0 @@
-class WorkflowProcessors::WorkflowRun
- include Memery
- GITHUB = WorkflowProcessors::Github::WorkflowRun
- BITRISE = WorkflowProcessors::Bitrise::WorkflowRun
-
- class WorkflowRunUnknownStatus < StandardError; end
-
- def self.process(step_run)
- new(step_run).process
- end
-
- def initialize(step_run)
- @step_run = step_run
- end
-
- def process
- return re_enqueue if in_progress?
- update_status!
- end
-
- private
-
- def re_enqueue
- WorkflowProcessors::WorkflowRunJob.set(wait: wait_time).perform_later(step_run.id)
- end
-
- attr_reader :step_run
- delegate :release, :build_artifact_name_pattern, to: :step_run
- delegate :in_progress?, :successful?, :failed?, :halted?, :artifacts_url, to: :runner
-
- def update_status!
- if successful?
- step_run.artifacts_url = artifacts_url
- step_run.finish_ci!
- step_run.event_stamp!(reason: :ci_finished, kind: :success, data: stamp_data)
- elsif failed?
- step_run.fail_ci!
- step_run.event_stamp!(reason: :ci_workflow_failed, kind: :error, data: stamp_data)
- elsif halted?
- step_run.cancel_ci!
- step_run.event_stamp!(reason: :ci_workflow_halted, kind: :error, data: stamp_data)
- else
- raise WorkflowRunUnknownStatus
- end
- end
-
- memoize def runner
- return GITHUB.new(workflow_run) if github_integration?
- BITRISE.new(step_run.ci_cd_provider, workflow_run, build_artifact_name_pattern) if bitrise_integration?
- end
-
- delegate :github_integration?, :bitrise_integration?, to: :integration
-
- def integration
- step_run.ci_cd_provider.integration
- end
-
- memoize def workflow_run
- step_run.get_workflow_run
- end
-
- def wait_time
- if Rails.env.development?
- 1.minute
- else
- 2.minutes
- end
- end
-
- def stamp_data
- {
- ref: step_run.ci_ref,
- url: step_run.ci_link
- }
- end
-end
diff --git a/app/models/app.rb b/app/models/app.rb
index 05c086458..fe36beec6 100644
--- a/app/models/app.rb
+++ b/app/models/app.rb
@@ -33,13 +33,10 @@ class App < ApplicationRecord
has_many :external_apps, inverse_of: :app, dependent: :destroy
has_many :trains, -> { sequential }, dependent: :destroy, inverse_of: :app
has_many :releases, through: :trains
- has_many :step_runs, through: :releases
- has_many :deployment_runs, through: :releases
has_many :production_store_rollouts, -> { production }, through: :releases
has_many :builds, through: :releases
has_many :release_platforms, dependent: :destroy
has_many :release_platform_runs, through: :releases
- has_many :steps, through: :release_platforms
validate :no_trains_are_running, on: :update
validates :bundle_identifier, uniqueness: {scope: [:platform, :organization_id]}
@@ -117,21 +114,13 @@ def ready?
end
def guided_train_setup?
- trains.none? || train_in_creation&.product_v2?
+ trains.none? || train_in_creation.present?
end
def train_in_creation
trains.first if trains.size == 1
end
- def latest_store_step_runs
- deployment_runs
- .reached_production
- .group_by(&:platform)
- .to_h { |platform, runs| [platform, runs.max_by(&:updated_at)&.step_run] }
- .values
- end
-
# NOTE: fetches and uses latest build numbers from the stores, if added,
# to reduce build upload rejection probability
def bump_build_number!(release_version: nil)
diff --git a/app/models/app_config.rb b/app/models/app_config.rb
index 1e0211e4d..d0721d53c 100644
--- a/app/models/app_config.rb
+++ b/app/models/app_config.rb
@@ -10,7 +10,6 @@
# code_repository :json
# firebase_android_config :jsonb
# firebase_ios_config :jsonb
-# notification_channel :json
# created_at :datetime not null
# updated_at :datetime not null
# app_id :uuid not null, indexed
@@ -18,11 +17,10 @@
#
class AppConfig < ApplicationRecord
has_paper_trail
- include Notifiable
include AppConfigurable
PLATFORM_AWARE_CONFIG_SCHEMA = Rails.root.join("config/schema/platform_aware_integration_config.json")
- self.ignored_columns += %w[bugsnag_project_id firebase_crashlytics_android_config firebase_crashlytics_ios_config]
+ self.ignored_columns += %w[bugsnag_project_id firebase_crashlytics_android_config firebase_crashlytics_ios_config notification_channel]
belongs_to :app
has_many :variants, class_name: "AppVariant", dependent: :destroy
@@ -102,11 +100,21 @@ def further_setup_by_category?
end
def bugsnag_project(platform)
- pick_bugsnag_project_id(platform)
+ case platform
+ when "android" then bugsnag_android_config["project_id"]
+ when "ios" then bugsnag_ios_config["project_id"]
+ else
+ raise ArgumentError, INVALID_PLATFORM_ERROR
+ end
end
def bugsnag_release_stage(platform)
- pick_bugsnag_release_stage(platform)
+ case platform
+ when "android" then bugsnag_android_config["release_stage"]
+ when "ios" then bugsnag_ios_config["release_stage"]
+ else
+ raise ArgumentError, INVALID_PLATFORM_ERROR
+ end
end
def ci_cd_workflows
diff --git a/app/models/app_store_integration.rb b/app/models/app_store_integration.rb
index 436249c81..bd5f05465 100644
--- a/app/models/app_store_integration.rb
+++ b/app/models/app_store_integration.rb
@@ -23,9 +23,9 @@ class AppStoreIntegration < ApplicationRecord
include Displayable
include Loggable
- delegate :app, to: :integration
+ delegate :integrable, to: :integration
delegate :cache, to: Rails
- delegate :refresh_external_app, to: :app
+ delegate :refresh_external_app, :bundle_identifier, to: :integrable
validate :correct_key, on: :create
before_create :set_external_details_on_app
@@ -96,20 +96,12 @@ class AppStoreIntegration < ApplicationRecord
APP_STORE_CONNECT_URL_TEMPLATE =
Addressable::Template.new("https://appstoreconnect.apple.com/apps/{app_id}/testflight/ios/{external_id}")
- unless Set.new(BUILD_TRANSFORMATIONS.keys).superset?(Set.new(ExternalRelease.minimum_required))
- raise InvalidTransformations
- end
-
- unless Set.new(RELEASE_TRANSFORMATIONS.keys).superset?(Set.new(ExternalRelease.minimum_required))
- raise InvalidTransformations
- end
-
def access_key
OpenSSL::PKey::EC.new(p8_key)
end
def installation
- Installations::Apple::AppStoreConnect::Api.new(app.bundle_identifier, key_id, issuer_id, access_key)
+ Installations::Apple::AppStoreConnect::Api.new(bundle_identifier, key_id, issuer_id, access_key)
end
def creatable?
@@ -145,7 +137,7 @@ def public_icon_img
end
def connection_data
- "Bundle Identifier: #{app.bundle_identifier}"
+ "Bundle Identifier: #{bundle_identifier}"
end
def find_build(build_number)
@@ -242,7 +234,7 @@ def build_channels(with_production:)
end
def build_channels_cache_key
- "app/#{app.id}/app_store_integration/#{id}/build_channels"
+ "app/#{integrable.id}/app_store_integration/#{id}/build_channels"
end
def to_s
@@ -250,23 +242,23 @@ def to_s
end
def deep_link(_, _)
- "itms-beta://beta.itunes.apple.com/v1/app/#{app.external_id}"
+ "itms-beta://beta.itunes.apple.com/v1/app/#{integrable.external_id}"
end
def inflight_store_link
- "https://appstoreconnect.apple.com/apps/#{app.external_id}/distribution/ios/version/inflight"
+ "https://appstoreconnect.apple.com/apps/#{integrable.external_id}/distribution/ios/version/inflight"
end
def deliverable_store_link
- "https://appstoreconnect.apple.com/apps/#{app.external_id}/distribution/ios/version/deliverable"
+ "https://appstoreconnect.apple.com/apps/#{integrable.external_id}/distribution/ios/version/deliverable"
end
def build_info(build_info)
- TestFlightInfo.new(build_info.merge(external_link: APP_STORE_CONNECT_URL_TEMPLATE.expand(app_id: app.external_id, external_id: build_info[:external_id]).to_s))
+ TestFlightInfo.new(build_info.merge(external_link: APP_STORE_CONNECT_URL_TEMPLATE.expand(app_id: integrable.external_id, external_id: build_info[:external_id]).to_s))
end
def release_info(build_info)
- AppStoreReleaseInfo.new(build_info.merge(external_link: APP_STORE_CONNECT_URL_TEMPLATE.expand(app_id: app.external_id, external_id: build_info[:external_id]).to_s))
+ AppStoreReleaseInfo.new(build_info.merge(external_link: APP_STORE_CONNECT_URL_TEMPLATE.expand(app_id: integrable.external_id, external_id: build_info[:external_id]).to_s))
end
def correct_key
@@ -276,7 +268,7 @@ def correct_key
end
def set_external_details_on_app
- app.set_external_details(find_app[:id])
+ integrable.set_external_details(find_app[:id])
end
class TestFlightInfo
diff --git a/app/models/app_store_submission.rb b/app/models/app_store_submission.rb
index 40e875ffe..70253ba37 100644
--- a/app/models/app_store_submission.rb
+++ b/app/models/app_store_submission.rb
@@ -343,7 +343,7 @@ def on_fail!(args = nil)
end
def update_store_version
- # TODO: [V2] [post-alpha] update store version details when release metadata changes or build is updated
+ # TODO: update store version details when release metadata changes
# update whats new, build
end
diff --git a/app/models/app_variant.rb b/app/models/app_variant.rb
index 32a688cb0..58712a793 100644
--- a/app/models/app_variant.rb
+++ b/app/models/app_variant.rb
@@ -17,7 +17,6 @@ class AppVariant < ApplicationRecord
include AppConfigurable
belongs_to :app_config
- has_many :steps, dependent: :nullify
validates :bundle_identifier, presence: true, uniqueness: {scope: :app_config_id}
validate :duplicate_bundle_identifier
diff --git a/app/models/bitbucket_integration.rb b/app/models/bitbucket_integration.rb
index 42abff800..fa5625657 100644
--- a/app/models/bitbucket_integration.rb
+++ b/app/models/bitbucket_integration.rb
@@ -25,7 +25,7 @@ class BitbucketIntegration < ApplicationRecord
attr_accessor :code
before_create :complete_access
- delegate :app, to: :integration
+ delegate :integrable, to: :integration
delegate :code_repository_name, to: :app_config
delegate :cache, to: Rails
@@ -367,7 +367,7 @@ def set_tokens(tokens)
end
def app_config
- app.config
+ integrable.config
end
def redirect_uri
@@ -379,6 +379,6 @@ def events_url(params)
end
def workflows_cache_key(branch_name)
- "app/#{app.id}/bitbucket_integration/#{id}/workflows/#{branch_name}"
+ "app/#{integrable.id}/bitbucket_integration/#{id}/workflows/#{branch_name}"
end
end
diff --git a/app/models/bitrise_integration.rb b/app/models/bitrise_integration.rb
index 35d29000e..3aa47c8f2 100644
--- a/app/models/bitrise_integration.rb
+++ b/app/models/bitrise_integration.rb
@@ -56,7 +56,7 @@ class BitriseIntegration < ApplicationRecord
encrypts :access_token, deterministic: true
- delegate :app, to: :integration
+ delegate :integrable, to: :integration
delegate :bitrise_project, to: :app_config
alias_method :project, :bitrise_project
delegate :cache, to: Rails
@@ -162,7 +162,7 @@ def workflow_retriable?
private
def app_config
- app.config
+ integrable.config
end
def correct_key
@@ -172,6 +172,6 @@ def correct_key
end
def workflows_cache_key
- "app/#{app.id}/bitrise_integration/#{id}/workflows"
+ "app/#{integrable.id}/bitrise_integration/#{id}/workflows"
end
end
diff --git a/app/models/bugsnag_integration.rb b/app/models/bugsnag_integration.rb
index 4ac838991..8556d22d8 100644
--- a/app/models/bugsnag_integration.rb
+++ b/app/models/bugsnag_integration.rb
@@ -48,7 +48,7 @@ class BugsnagIntegration < ApplicationRecord
encrypts :access_token, deterministic: true
delegate :cache, to: Rails
- delegate :app, to: :integration
+ delegate :integrable, to: :integration
delegate :bugsnag_project, :bugsnag_release_stage, to: :app_config
alias_method :project, :bugsnag_project
alias_method :release_stage, :bugsnag_release_stage
@@ -121,7 +121,7 @@ def project_id(platform)
end
def app_config
- integration.app.config
+ integrable.config
end
def correct_key
@@ -131,6 +131,6 @@ def correct_key
end
def list_projects_cache_key
- "app/#{app.id}/bugsnag_integration/#{id}/list_projects"
+ "app/#{integrable.id}/bugsnag_integration/#{id}/list_projects"
end
end
diff --git a/app/models/build_artifact.rb b/app/models/build_artifact.rb
index 3bf14eaf5..8f8a29d8d 100644
--- a/app/models/build_artifact.rb
+++ b/app/models/build_artifact.rb
@@ -8,15 +8,15 @@
# created_at :datetime not null
# updated_at :datetime not null
# build_id :uuid indexed
-# step_run_id :uuid indexed
#
require "zip"
class BuildArtifact < ApplicationRecord
include Rails.application.routes.url_helpers
- belongs_to :step_run, optional: true, inverse_of: :build_artifact
- belongs_to :build, optional: true, inverse_of: :artifact
+ self.ignored_columns += ["step_run_id"]
+
+ belongs_to :build, inverse_of: :artifact
has_one_attached :file
delegate :create_and_upload!, to: ActiveStorage::Blob
@@ -30,10 +30,6 @@ def self.find_via_signed_id(signed_id)
find_by(id: attachment.record_id)
end
- def parent
- step_run || build
- end
-
def save_file!(artifact_stream)
transaction do
self.file = create_and_upload!(io: artifact_stream.file, filename: gen_filename(artifact_stream.ext))
@@ -43,7 +39,7 @@ def save_file!(artifact_stream)
end
def gen_filename(ext)
- "#{app.slug}-#{parent.build_version}-build#{ext}"
+ "#{app.slug}-#{build.build_version}-build#{ext}"
end
def get_filename
@@ -65,7 +61,7 @@ def download_url
end
def app
- parent.release_platform_run.app
+ build.release_platform_run.app
end
delegate :organization, to: :app
diff --git a/app/models/build_queue.rb b/app/models/build_queue.rb
index e8ef38dba..cc7fe7206 100644
--- a/app/models/build_queue.rb
+++ b/app/models/build_queue.rb
@@ -20,17 +20,6 @@ class BuildQueue < ApplicationRecord
after_create_commit :schedule_kickoff!
- def apply!
- head_commit&.trigger_step_runs
- update!(applied_at: Time.current, is_active: false)
- release.create_build_queue!
- end
-
- def add_commit!(commit, can_apply: true)
- commits << commit
- apply! if commits.size >= build_queue_size && can_apply
- end
-
def add_commit_v2!(commit, can_apply: true)
commits << commit
if commits.size >= build_queue_size && can_apply
diff --git a/app/models/commit.rb b/app/models/commit.rb
index 497a97a1f..f8a5cf889 100644
--- a/app/models/commit.rb
+++ b/app/models/commit.rb
@@ -25,7 +25,6 @@ class Commit < ApplicationRecord
self.implicit_order_column = :timestamp
- has_many :step_runs, dependent: :nullify, inverse_of: :commit
has_many :release_platform_runs, dependent: :nullify, inverse_of: :last_commit
belongs_to :release, inverse_of: :all_commits
belongs_to :build_queue, inverse_of: :commits, optional: true
@@ -62,13 +61,6 @@ def self.count_by_team(org)
res
end
- def self.between(base_step_run, head_step_run)
- return none if head_step_run.nil?
- return none if base_step_run.nil? && head_step_run.nil?
-
- between_commits(base_step_run&.commit, head_step_run.commit)
- end
-
def self.between_commits(base_commit, head_commit)
return none if head_commit.nil?
return none if base_commit.nil? && head_commit.nil?
@@ -98,10 +90,6 @@ def author_url
url
end
- def run_for(step, release_platform_run)
- step_runs.where(step:, release_platform_run:).last
- end
-
def stale?
release.applied_commits.last != self
end
@@ -110,43 +98,6 @@ def short_sha
commit_hash[0, 7]
end
- def step_runs_for(platform_run)
- step_runs.where(release_platform_run: platform_run).includes(:step)
- end
-
- def applied_at
- step_runs.map(&:created_at).min
- end
-
- def trigger_step_runs_for(platform_run, force: false)
- return if release.hotfix? && !force
- platform_run.bump_version!
- platform_run.update!(last_commit: self)
-
- platform_run.release_platform.ordered_steps_until(platform_run.current_step_number).each do |step|
- next if release.hotfix? || step.manual_trigger_only?
- Triggers::StepRun.call(step, self, platform_run)
- end
- end
-
- def trigger_step_runs
- return unless applicable?
-
- release_platform_runs.have_not_submitted_production.each do |run|
- trigger_step_runs_for(run)
- end
- end
-
- def add_to_build_queue!(is_head_commit: true)
- return unless release.queue_commit?(self)
- release.active_build_queue.add_commit!(self, can_apply: is_head_commit)
- end
-
- def trigger!
- return add_to_build_queue! if release.queue_commit?(self)
- trigger_step_runs
- end
-
def notification_params
release.notification_params.merge(
{
diff --git a/app/models/concerns/app_configurable.rb b/app/models/concerns/app_configurable.rb
index 99e69cd1a..dd2b12aaa 100644
--- a/app/models/concerns/app_configurable.rb
+++ b/app/models/concerns/app_configurable.rb
@@ -1,7 +1,12 @@
module AppConfigurable
- include PlatformAwareness
+ INVALID_PLATFORM_ERROR = "platform must be valid"
def firebase_app(platform)
- pick_firebase_app_id(platform)
+ case platform
+ when "android" then firebase_android_config["app_id"]
+ when "ios" then firebase_ios_config["app_id"]
+ else
+ raise ArgumentError, INVALID_PLATFORM_ERROR
+ end
end
end
diff --git a/app/models/concerns/commitable.rb b/app/models/concerns/commitable.rb
index ab6f89eef..7954dbac1 100644
--- a/app/models/concerns/commitable.rb
+++ b/app/models/concerns/commitable.rb
@@ -57,7 +57,7 @@ def parents = commit["parents"]
def message = commit["message"]
- def team = nil # TODO: [V2] stub
+ def team = nil # TODO: stub
def pull_request = nil
diff --git a/app/models/concerns/notifiable.rb b/app/models/concerns/notifiable.rb
deleted file mode 100644
index a5f7607db..000000000
--- a/app/models/concerns/notifiable.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-module Notifiable
- def notification_channel_id
- return if notification_channel.blank?
- notification_channel["id"]
- end
-
- def notification_channel_name
- return if notification_channel.blank?
- notification_channel["name"]
- end
-end
diff --git a/app/models/concerns/platform_awareness.rb b/app/models/concerns/platform_awareness.rb
deleted file mode 100644
index 2724706a0..000000000
--- a/app/models/concerns/platform_awareness.rb
+++ /dev/null
@@ -1,38 +0,0 @@
-module PlatformAwareness
- INVALID_PLATFORM_ERROR = "platform must be valid"
- def pick_firebase_app_id(platform)
- case platform
- when "android" then firebase_android_config["app_id"]
- when "ios" then firebase_ios_config["app_id"]
- else
- raise ArgumentError, INVALID_PLATFORM_ERROR
- end
- end
-
- def pick_bugsnag_release_stage(platform)
- case platform
- when "android" then bugsnag_android_config["release_stage"]
- when "ios" then bugsnag_ios_config["release_stage"]
- else
- raise ArgumentError, INVALID_PLATFORM_ERROR
- end
- end
-
- def pick_bugsnag_project_id(platform)
- case platform
- when "android" then bugsnag_android_config["project_id"]
- when "ios" then bugsnag_ios_config["project_id"]
- else
- raise ArgumentError, INVALID_PLATFORM_ERROR
- end
- end
-
- def pick_firebase_crashlytics_app_id(platform)
- case platform
- when "android" then firebase_crashlytics_android_config["app_id"]
- when "ios" then firebase_crashlytics_ios_config["app_id"]
- else
- raise ArgumentError, INVALID_PLATFORM_ERROR
- end
- end
-end
diff --git a/app/models/config/release_platform.rb b/app/models/config/release_platform.rb
index 3e1bcfcb9..e1781c9dd 100644
--- a/app/models/config/release_platform.rb
+++ b/app/models/config/release_platform.rb
@@ -68,6 +68,12 @@ def as_json(options = {})
}
end
+ def has_restricted_public_channels?
+ return false if ios?
+ return true if production_release.present?
+ internal_release&.submissions&.any?(&:restricted_public_channel?) || beta_release&.submissions&.any?(&:restricted_public_channel?)
+ end
+
def pick_internal_workflow
internal_workflow || release_candidate_workflow
end
diff --git a/app/models/config/submission.rb b/app/models/config/submission.rb
index 6424420d0..38134f562 100644
--- a/app/models/config/submission.rb
+++ b/app/models/config/submission.rb
@@ -60,6 +60,11 @@ def submission_class
submission_type.constantize
end
+ def restricted_public_channel?
+ return false unless submission_type == "GooglePlayStoreSubmission"
+ GooglePlayStoreIntegration::PUBLIC_CHANNELS.include?(submission_external.identifier)
+ end
+
def next
release_step_config.submissions.where("number > ?", number).order(:number).first
end
diff --git a/app/models/deployment.rb b/app/models/deployment.rb
deleted file mode 100644
index b4ef9ed2a..000000000
--- a/app/models/deployment.rb
+++ /dev/null
@@ -1,202 +0,0 @@
-# == Schema Information
-#
-# Table name: deployments
-#
-# id :uuid not null, primary key
-# build_artifact_channel :jsonb indexed => [integration_id, step_id]
-# deployment_number :integer default(0), not null, indexed => [step_id]
-# discarded_at :datetime indexed
-# is_staged_rollout :boolean default(FALSE)
-# notes :string default("no_notes"), not null
-# send_build_notes :boolean
-# staged_rollout_config :decimal(, ) default([]), is an Array
-# created_at :datetime not null
-# updated_at :datetime not null
-# integration_id :uuid indexed => [build_artifact_channel, step_id], indexed
-# step_id :uuid not null, indexed => [build_artifact_channel, integration_id], indexed => [deployment_number], indexed
-#
-class Deployment < ApplicationRecord
- has_paper_trail
- include Displayable
- include Discard::Model
-
- self.implicit_order_column = :deployment_number
-
- has_many :deployment_runs, dependent: :destroy
- belongs_to :step, inverse_of: :deployments
- belongs_to :integration, optional: true
-
- enum :notes, {build_notes: "build_notes", release_notes: "release_notes", no_notes: "no_notes"}
-
- validates :deployment_number, presence: true
- validates :build_artifact_channel, uniqueness: {scope: [:integration_id, :step_id], conditions: -> { kept }}
- validate :staged_rollout_is_allowed
- validate :correct_staged_rollout_config, if: :staged_rollout?, on: :create
- validate :non_prod_build_channel, if: -> { step.review? }
-
- delegate :google_play_store_integration?,
- :slack_integration?,
- :store?,
- :app_store_integration?,
- :controllable_rollout?,
- :google_firebase_integration?, :project_link, to: :integration, allow_nil: true
- delegate :train, :app, :notify!, :release_platform, to: :step
-
- scope :sequential, -> { order("deployments.deployment_number ASC") }
-
- before_save :set_deployment_number, if: :new_record?
- before_save :set_default_staged_rollout, if: [:new_record?, :app_store_integration?, :staged_rollout?]
- before_save :set_default_prod_notes_config, if: [:new_record?, :production_channel?]
-
- FULL_ROLLOUT_VALUE = BigDecimal("100")
-
- def staged_rollout? = is_staged_rollout
-
- def send_notes?
- !no_notes?
- end
-
- def set_deployment_number
- self.deployment_number = step.all_deployments.maximum(:deployment_number).to_i + 1
- end
-
- def external?
- integration.nil?
- end
-
- def uploadable?
- slack_integration? || google_firebase_integration? || google_play_store_integration? || (release_platform.android? && external?)
- end
-
- def findable?
- release_platform.ios? && app_store_integration?
- end
-
- def first?
- step.deployments.minimum(:deployment_number).to_i == deployment_number
- end
-
- def last?
- step.deployments.maximum(:deployment_number).to_i == deployment_number
- end
-
- def next
- step.deployments.where("deployment_number > ?", deployment_number)&.first
- end
-
- def previous
- step.deployments.where(deployment_number: ...deployment_number).last
- end
-
- def deployment_channel
- build_artifact_channel["id"]
- end
-
- def deployment_channel_name
- build_artifact_channel["name"]
- end
-
- def production_channel?
- store? && build_artifact_channel["is_production"]
- end
-
- def internal_channel?
- build_artifact_channel["is_internal"]
- end
-
- def requires_review?
- google_play_store_integration? && deployment_channel.in?(%w[production beta alpha])
- end
-
- def integration_type
- return :app_store if app_store?
- return :testflight if test_flight?
- return :google_play_store if google_play_store_integration?
- return :slack if slack_integration?
- return :firebase if google_firebase_integration?
- :external
- end
-
- def display_channel?
- !external? && !app_store?
- end
-
- def test_flight?
- !production_channel? && app_store_integration?
- end
-
- def app_store?
- production_channel? && app_store_integration?
- end
-
- def notification_params
- step.notification_params
- .merge(train.notification_params)
- .merge(
- {
- is_staged_rollout_deployment: staged_rollout?,
- is_production_channel: production_channel?,
- is_play_store_production: production_channel? && google_play_store_integration?,
- is_app_store_production: app_store?,
- deployment_channel_type: integration_type&.to_s&.titleize,
- deployment_channel: build_artifact_channel,
- deployment_channel_asset_link: integration&.public_icon_img,
- requires_review: requires_review?
- }
- )
- end
-
- def replicate(new_step)
- new_deployment = dup
- new_step.deployments << new_deployment
- end
-
- def one_percent_beta_release?
- train.one_percent_beta_release? && google_play_store_integration? && deployment_channel == "beta"
- end
-
- private
-
- def set_default_prod_notes_config
- self.notes = "release_notes"
- end
-
- def set_default_staged_rollout
- self.staged_rollout_config = AppStoreIntegration::DEFAULT_PHASED_RELEASE_SEQUENCE
- end
-
- def staged_rollout_is_allowed
- if is_staged_rollout && !production_channel?
- errors.add(:is_staged_rollout, :prod_only)
- end
- end
-
- def correct_staged_rollout_config
- if app_store_integration?
- errors.add(:staged_rollout_config, :not_allowed) if staged_rollout_config.present?
- return
- end
-
- if staged_rollout_config.size < 1
- errors.add(:staged_rollout_config, :at_least_one)
- end
-
- if staged_rollout_config[0]&.zero?
- errors.add(:staged_rollout_config, :zero_rollout)
- end
-
- if staged_rollout_config.sort != staged_rollout_config
- errors.add(:staged_rollout_config, :increasing_order)
- end
-
- if staged_rollout_config.any? { |value| value > FULL_ROLLOUT_VALUE }
- errors.add(:staged_rollout_config, :max_100)
- end
- end
-
- def non_prod_build_channel
- if production_channel?
- errors.add(:build_artifact_channel, :prod_channel_in_review_not_allowed)
- end
- end
-end
diff --git a/app/models/deployment_run.rb b/app/models/deployment_run.rb
deleted file mode 100644
index fcbcbe3de..000000000
--- a/app/models/deployment_run.rb
+++ /dev/null
@@ -1,578 +0,0 @@
-# == Schema Information
-#
-# Table name: deployment_runs
-#
-# id :uuid not null, primary key
-# failure_reason :string
-# initial_rollout_percentage :decimal(8, 5)
-# scheduled_at :datetime not null
-# status :string
-# created_at :datetime not null
-# updated_at :datetime not null
-# deployment_id :uuid not null, indexed => [step_run_id]
-# step_run_id :uuid not null, indexed => [deployment_id], indexed
-#
-class DeploymentRun < ApplicationRecord
- has_paper_trail
- include AASM
- include Passportable
- include Loggable
- include Displayable
- using RefinedArray
- using RefinedString
-
- belongs_to :step_run, inverse_of: :deployment_runs
- belongs_to :deployment, inverse_of: :deployment_runs
- has_one :staged_rollout, dependent: :destroy
- has_one :external_release, dependent: :destroy
- has_many :release_health_metrics, dependent: :destroy, inverse_of: :deployment_run
- has_many :release_health_events, dependent: :destroy, inverse_of: :deployment_run
-
- validates :deployment_id, uniqueness: {scope: :step_run_id}
-
- delegate :step,
- :release,
- :release_platform_run,
- :commit,
- :build_number,
- :build_artifact,
- :build_version,
- :build_display_name,
- to: :step_run
- delegate :deployment_number,
- :notify!,
- :integration,
- :deployment_channel,
- :deployment_channel_name,
- :external?,
- :google_play_store_integration?,
- :slack_integration?,
- :app_store_integration?,
- :app_store?,
- :test_flight?,
- :store?,
- :send_notes?,
- :release_notes?,
- :build_notes?,
- :staged_rollout?,
- :staged_rollout_config,
- :google_firebase_integration?,
- :production_channel?,
- :release_platform,
- :internal_channel?,
- :one_percent_beta_release?,
- to: :deployment
- delegate :train, :app, to: :release
- delegate :release_version, :release_metadatum, :platform, to: :release_platform_run
- delegate :release_health_rules, to: :release_platform
-
- STAMPABLE_REASONS = %w[
- created
- release_failed
- prepare_release_failed
- inflight_release_replaced
- submitted_for_review
- resubmitted_for_review
- review_approved
- release_started
- released
- review_failed
- skipped
- ]
-
- STATES = {
- created: "created",
- started: "started",
- preparing_release: "preparing_release",
- prepared_release: "prepared_release",
- submitted_for_review: "submitted_for_review",
- failed_prepare_release: "failed_prepare_release",
- uploading: "uploading",
- uploaded: "uploaded",
- ready_to_release: "ready_to_release",
- rollout_started: "rollout_started",
- released: "released",
- review_failed: "review_failed",
- failed_with_action_required: "failed_with_action_required",
- failed: "failed"
- }
-
- JOB_FREQUENCY = {
- "Crashlytics" => 30.minutes,
- "Bugsnag" => 5.minutes
- }
-
- READY_STATES = [STATES[:rollout_started], STATES[:ready_to_release], STATES[:released]]
- STORE_SUBMISSION_STATES = READY_STATES + [STATES[:submitted_for_review], STATES[:review_failed]]
- FAILED_STATES = [STATES[:failed], STATES[:failed_prepare_release], STATES[:failed_with_action_required]]
-
- enum :status, STATES
- enum :failure_reason, {
- developer_rejected: "developer_rejected",
- invalid_release: "invalid_release",
- unknown_failure: "unknown_failure"
- }.merge(
- *[
- Installations::Apple::AppStoreConnect::Error.reasons,
- Installations::Google::PlayDeveloper::Error.reasons,
- Installations::Google::Firebase::Error.reasons,
- Installations::Google::Firebase::OpError.reasons
- ].map(&:zip_map_self)
- )
-
- aasm safe_state_machine_params do
- state :created, initial: true, before_enter: -> { step_run.startable_deployment?(deployment) }
- state(*STATES.keys)
-
- event :dispatch, after_commit: :kickoff! do
- after { step_run.start_deploy! if first? }
- transitions from: :created, to: :started
- end
-
- event :start_prepare_release, after_commit: ->(args = {force: false}) { Deployments::AppStoreConnect::PrepareForReleaseJob.perform_async(id, args.fetch(:force, false)) } do
- transitions from: [:started, :failed_prepare_release], to: :preparing_release do
- guard { |_| app_store? }
- end
- end
-
- event :prepare_release, guard: :app_store? do
- transitions from: :preparing_release, to: :prepared_release
- end
-
- event :fail_prepare_release, before: :set_reason, after_commit: -> { event_stamp!(reason: :prepare_release_failed, kind: :error, data: stamp_data) } do
- transitions from: :preparing_release, to: :failed_prepare_release do
- guard { |_| app_store? }
- end
- end
-
- event :submit_for_review, after_commit: ->(args = {resubmission: false}) { after_submission(args.fetch(:resubmission, false)) } do
- transitions from: [:started, :prepared_release, :review_failed], to: :submitted_for_review
- end
-
- event :start_upload, after: :get_upload_status do
- transitions from: :started, to: :uploading
- end
-
- event :upload, after_commit: -> { Deployments::ReleaseJob.perform_later(id) } do
- transitions from: [:started, :uploading], to: :uploaded
- end
-
- event :ready_to_release, after_commit: :mark_reviewed do
- transitions from: [:submitted_for_review, :review_failed], to: :ready_to_release
- end
-
- event :engage_release, after_commit: :on_release_started do
- transitions from: [:uploaded, :ready_to_release], to: :rollout_started
- end
-
- event :fail_review, after_commit: :after_review_failure do
- transitions from: :submitted_for_review, to: :review_failed
- end
-
- event :fail_with_sync_option, before: :set_reason do
- transitions from: [:started, :prepared_release, :uploading, :uploaded, :submitted_for_review, :ready_to_release, :rollout_started, :failed_prepare_release, :failed_with_action_required], to: :failed_with_action_required
- after { step_run.fail_deployment_with_sync_option! }
- end
-
- event :skip, after_commit: -> { event_stamp!(reason: :skipped, kind: :notice, data: stamp_data) } do
- transitions from: :failed_with_action_required, to: :released
- after { step_run.finish_deployment!(deployment) }
- end
-
- event :dispatch_fail, before: :set_reason, after_commit: :release_failed do
- transitions from: [:started, :prepared_release, :uploading, :uploaded, :submitted_for_review, :ready_to_release, :rollout_started, :preparing_release, :failed_prepare_release], to: :failed
- after { step_run.fail_deployment!(deployment) }
- end
-
- event :complete, after_commit: :release_success do
- after { step_run.finish_deployment!(deployment) }
- transitions from: [:created, :uploaded, :started, :submitted_for_review, :rollout_started, :ready_to_release, :review_failed], to: :released
- end
- end
-
- scope :for_ids, ->(ids) { includes(deployment: :integration).where(id: ids) }
- scope :matching_runs_for, ->(integration) { includes(:deployment).where(deployments: {integration: integration}) }
- scope :has_begun, -> { where.not(status: :created) }
- scope :failed, -> { where(status: FAILED_STATES) }
- scope :not_failed, -> { where.not(status: [:failed, :failed_prepare_release]) }
- scope :ready, -> { where(status: READY_STATES) }
-
- after_commit -> { create_stamp!(data: stamp_data) }, on: :create
-
- UnknownStoreError = Class.new(StandardError)
-
- def self.reached_production
- ready.includes(:step_run, :deployment).select(&:production_channel?)
- end
-
- def self.reached_submission
- where(status: STORE_SUBMISSION_STATES).includes([:staged_rollout, {step_run: [:commit], deployment: [:integration]}]).select(&:production_channel?)
- end
-
- def failure?
- status.in?(FAILED_STATES)
- end
-
- def check_release_health
- return unless latest_health_data&.fresh?
- latest_health_data.check_release_health
- end
-
- def show_health?
- return true if latest_health_data&.fresh?
- false
- end
-
- def unhealthy?
- !healthy?
- end
-
- def deployment_notes
- return step_run.build_notes.truncate(ReleaseMetadata::NOTES_MAX_LENGTH) if build_notes?
- release_metadatum.release_notes if release_notes?
- end
-
- def healthy?
- return true if release_health_rules.blank?
- return true if release_health_events.blank?
-
- release_health_rules.all? do |rule|
- event = release_health_events.where(release_health_rule: rule).last
- event.blank? || event.healthy?
- end
- end
-
- def fetch_health_data!
- return if app.monitoring_provider.blank?
- return unless production_channel?
-
- release_data = app.monitoring_provider.find_release(platform, build_version, build_number)
- return if release_data.blank?
-
- release_health_metrics.create(fetched_at: Time.current, **release_data)
- end
-
- def latest_health_data
- release_health_metrics.order(fetched_at: :desc).first
- end
-
- def staged_rollout_events
- return [] unless staged_rollout?
-
- staged_rollout.passports.where(reason: [:started, :increased, :fully_released]).order(:event_timestamp).map do |p|
- {
- timestamp: p.event_timestamp,
- rollout_percentage: (p.reason == "fully_released") ? "100%" : p.metadata["rollout_percentage"]
- }
- end
- end
-
- def rollout_percentage_at(day)
- return 100.0 unless staged_rollout
- last_event = staged_rollout
- .passports
- .where(reason: [:started, :increased, :fully_released])
- .where("DATE_TRUNC('day', event_timestamp) <= ?", day)
- .order(:event_timestamp)
- .last
- return 0.0 unless last_event
- return 100.0 if last_event.reason == "fully_released"
- last_event.metadata["rollout_percentage"].safe_float
- end
-
- def submitted_at
- return unless production_channel?
-
- if google_play_store_integration?
- release_started_at
- elsif app_store_integration?
- passports.where(reason: :submitted_for_review).last&.event_timestamp
- end
- end
-
- def release_started_at
- return unless production_channel?
-
- passport = passports.where(reason: :release_started).last
-
- return passport.event_timestamp if passport
-
- # NOTE: closest timestamp for releases finished before the above passport was added
- return staged_rollout.created_at if staged_rollout
- passports.where(reason: :released).last&.event_timestamp
- end
-
- def first?
- step_run.deployment_runs.first == self
- end
-
- def after_submission(resubmission = false)
- notify!("Submitted for review!", :submit_for_review, notification_params.merge(resubmission:))
- if resubmission
- event_stamp!(reason: :resubmitted_for_review, kind: :notice, data: stamp_data)
- else
- event_stamp!(reason: :submitted_for_review, kind: :notice, data: stamp_data)
- end
- Deployments::AppStoreConnect::UpdateExternalReleaseJob.perform_async(id)
- end
-
- def after_review_failure
- notify!("Review failed", :review_failed, notification_params)
- event_stamp!(reason: :review_failed, kind: :error, data: stamp_data)
- end
-
- def get_upload_status(args)
- Deployments::GoogleFirebase::UpdateUploadStatusJob.perform_async(id, args&.fetch(:op_name))
- end
-
- def kickoff!
- return complete! if external?
- return Deployments::SlackJob.perform_later(id) if slack_integration?
- return Deployments::AppStoreConnect::Release.kickoff!(self) if app_store_integration?
- return Deployments::GooglePlayStore::Release.kickoff!(self) if google_play_store_integration?
- Deployments::GoogleFirebase::Release.kickoff!(self) if google_firebase_integration?
- end
-
- def start_release!
- release_platform_run.with_lock do
- return unless release_startable?
-
- if google_play_store_integration?
- Deployments::GooglePlayStore::Release.start_release!(self)
- elsif app_store_integration?
- Deployments::AppStoreConnect::Release.start_release!(self)
- elsif google_firebase_integration?
- Deployments::GoogleFirebase::Release.start_release!(self)
- else
- raise UnknownStoreError
- end
- end
- end
-
- def on_fully_release!
- return unless store?
-
- release_platform_run.with_lock do
- return unless rolloutable?
-
- if google_play_store_integration?
- result = Deployments::GooglePlayStore::Release.release_to_all!(self)
- elsif app_store_integration?
- result = Deployments::AppStoreConnect::Release.complete_phased_release!(self)
- else
- raise UnknownStoreError
- end
-
- yield result
- end
- end
-
- def on_release(rollout_value:)
- return unless store? && google_play_store_integration?
-
- release_platform_run.with_lock do
- return unless controllable_rollout?
-
- yield Deployments::GooglePlayStore::Release.release_with(self, rollout_value:)
- end
- end
-
- def on_halt_release!
- return unless store?
-
- release_platform_run.with_lock do
- return unless rolloutable?
-
- if google_play_store_integration?
- result = Deployments::GooglePlayStore::Release.halt_release!(self)
- elsif app_store_integration?
- result = Deployments::AppStoreConnect::Release.halt_phased_release!(self)
- else
- raise UnknownStoreError
- end
-
- yield result
- end
- end
-
- def on_pause_release!
- return unless store? && app_store_integration?
-
- release_platform_run.with_lock do
- return unless automatic_rollout?
-
- yield Deployments::AppStoreConnect::Release.pause_phased_release!(self)
- end
- end
-
- def on_resume_release!
- return unless store?
-
- release_platform_run.with_lock do
- return unless rolloutable?
-
- if google_play_store_integration?
- result = Deployments::GooglePlayStore::Release.release_with(self, rollout_value: staged_rollout.last_rollout_percentage)
- elsif app_store_integration?
- result = Deployments::AppStoreConnect::Release.resume_phased_release!(self)
- else
- raise UnknownStoreError
- end
-
- yield result
- end
- end
-
- def promotable?
- step_run.active? && store? && (uploaded? || rollout_started?)
- end
-
- def release_startable?
- step_run.active? && may_engage_release?
- end
-
- def rolloutable?
- step.release? &&
- promotable? &&
- deployment.staged_rollout? &&
- rollout_started?
- end
-
- def controllable_rollout?
- rolloutable? && deployment.controllable_rollout?
- end
-
- def automatic_rollout?
- rolloutable? && !deployment.controllable_rollout?
- end
-
- def app_store_release?
- step.release? && step_run.active? && deployment.app_store?
- end
-
- def test_flight_release?
- step_run.active? && deployment.test_flight?
- end
-
- def reviewable?
- app_store_release? && prepared_release?
- end
-
- def releasable?
- app_store_release? && may_engage_release?
- end
-
- def rollout_percentage
- return unless store?
- return staged_rollout.last_rollout_percentage if staged_rollout? && staged_rollout.present?
- return BigDecimal("1") if one_percent_beta_release?
- initial_rollout_percentage || Deployment::FULL_ROLLOUT_VALUE if deployment.controllable_rollout?
- end
-
- def has_uploaded?
- uploaded? || failed? || released?
- end
-
- ## slack
- #
- def push_to_slack!
- return unless slack_integration?
-
- with_lock do
- return if released?
- provider.deploy!(deployment_channel, notification_params)
- complete!
- end
- end
-
- def provider
- integration&.providable
- end
-
- def fail_with_error(error)
- elog(error)
- if error.is_a?(Installations::Error)
- if error.reason == :app_review_rejected
- fail_with_sync_option!(reason: error.reason)
- else
- dispatch_fail!(reason: error.reason)
- end
- else
- dispatch_fail!
- end
- end
-
- def notification_params
- deployment
- .notification_params
- .merge(step_run.notification_params)
- .merge(
- {
- project_link: external_link,
- deep_link: provider&.deep_link(external_release&.external_id, release_platform.platform)
- }
- )
- end
-
- def production_release_happened?
- return false unless production_channel?
- status.in?(READY_STATES) && (staged_rollout.blank? || staged_rollout.active?)
- end
-
- def production_release_submitted?
- production_channel? && status.in?(STORE_SUBMISSION_STATES)
- end
-
- def external_link
- external_release&.external_link.presence || deployment.project_link
- end
-
- private
-
- def on_release_started
- event_stamp!(reason: :release_started, kind: :notice, data: stamp_data)
- ReleasePlatformRuns::CreateTagJob.perform_later(release_platform_run.id) if production_channel? && train.tag_all_store_releases?
- Releases::FetchHealthMetricsJob.perform_later(id, JOB_FREQUENCY[app.monitoring_provider.display]) if app.monitoring_provider.present?
- end
-
- def mark_reviewed
- external_release.update(reviewed_at: Time.current)
- event_stamp!(reason: :review_approved, kind: :success, data: stamp_data)
- notify!("Review approved!", :review_approved, notification_params)
- end
-
- def set_reason(args = nil)
- self.failure_reason = args&.fetch(:reason, :unknown_failure)
- end
-
- def release_failed
- event_stamp!(reason: :release_failed, kind: :error, data: stamp_data)
- notify!("Deployment failed", :deployment_failed, notification_params)
- end
-
- def release_success
- if external_release
- now = Time.current
- external_release.update(released_at: now, reviewed_at: external_release.reviewed_at.presence || now)
- end
-
- event_stamp!(reason: :released, kind: :success, data: stamp_data)
- return if external?
-
- train.notify_with_snippet!("Deployment was successful!",
- :deployment_finished,
- notification_params,
- step_run.build_notes,
- "Changes since the last release:")
- end
-
- def stamp_data
- {
- version: build_version,
- chan: deployment_channel_name,
- provider: integration&.providable&.display,
- file: build_artifact&.get_filename,
- failure_reason: (display_attr(:failure_reason) if failure_reason.present?)
- }
- end
-end
diff --git a/app/models/external_build.rb b/app/models/external_build.rb
index 2f75887fc..a8ee65127 100644
--- a/app/models/external_build.rb
+++ b/app/models/external_build.rb
@@ -2,19 +2,19 @@
#
# Table name: external_builds
#
-# id :uuid not null, primary key
-# metadata :jsonb not null
-# created_at :datetime not null
-# updated_at :datetime not null
-# build_id :uuid indexed
-# step_run_id :uuid indexed
+# id :uuid not null, primary key
+# metadata :jsonb not null
+# created_at :datetime not null
+# updated_at :datetime not null
+# build_id :uuid indexed
#
class ExternalBuild < ApplicationRecord
has_paper_trail
METADATA_SCHEMA = Rails.root.join("config/schema/external_build_metadata.json")
- belongs_to :step_run, inverse_of: :external_build, optional: true
+ self.ignored_columns += ["step_run_id"]
+
belongs_to :build, inverse_of: :external_build, optional: true
# rubocop:disable Rails/SkipsModelValidations
@@ -22,7 +22,7 @@ def update_or_insert!(new_metadata)
validate_metadata_schema(new_metadata)
return self if errors.present?
- unique_by = step_run.present? ? [:step_run_id] : [:build_id]
+ unique_by = [:build_id]
ExternalBuild.upsert_all(
[attributes_for_upsert(new_metadata)],
@@ -34,7 +34,6 @@ def update_or_insert!(new_metadata)
def attributes_for_upsert(new_metadata)
{metadata: new_metadata.index_by { |item| item[:identifier] },
- step_run_id: step_run_id,
build_id: build_id}
end
diff --git a/app/models/external_release.rb b/app/models/external_release.rb
deleted file mode 100644
index 096ac8d32..000000000
--- a/app/models/external_release.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-# == Schema Information
-#
-# Table name: external_releases
-#
-# id :uuid not null, primary key
-# added_at :datetime
-# build_number :string
-# external_link :string
-# name :string
-# released_at :datetime
-# reviewed_at :datetime
-# size_in_bytes :integer
-# status :string
-# created_at :datetime not null
-# updated_at :datetime not null
-# deployment_run_id :uuid not null, indexed
-# external_id :string
-#
-class ExternalRelease < ApplicationRecord
- belongs_to :deployment_run
- delegate :app, :app_store_integration?, :build_version, to: :deployment_run
-
- def self.minimum_required
- column_names.map(&:to_sym).filter { |name| name.in? [:name, :status, :build_number, :external_id, :added_at] }
- end
-
- def review_time
- ActiveSupport::Duration.build(reviewed_at - added_at)
- end
-end
diff --git a/app/models/github_integration.rb b/app/models/github_integration.rb
index efc66daed..d83cf262e 100644
--- a/app/models/github_integration.rb
+++ b/app/models/github_integration.rb
@@ -19,8 +19,8 @@ class GithubIntegration < ApplicationRecord
validates :installation_id, presence: true
delegate :code_repository_name, :code_repo_namespace, :code_repo_name_only, to: :app_config
- delegate :app, to: :integration
- delegate :organization, to: :app
+ delegate :integrable, to: :integration
+ delegate :organization, to: :integrable
delegate :cache, to: Rails
BASE_INSTALLATION_URL =
@@ -367,7 +367,7 @@ def update_webhook!(id, url_params)
end
def app_config
- app.config
+ integrable.config
end
def events_url(params)
@@ -379,6 +379,6 @@ def events_url(params)
end
def workflows_cache_key
- "app/#{app.id}/github_integration/#{id}/workflows"
+ "app/#{integrable.id}/github_integration/#{id}/workflows"
end
end
diff --git a/app/models/gitlab_integration.rb b/app/models/gitlab_integration.rb
index a42938d5a..16ba43ce7 100644
--- a/app/models/gitlab_integration.rb
+++ b/app/models/gitlab_integration.rb
@@ -321,7 +321,7 @@ def set_tokens(tokens)
end
def app_config
- integration.app.config
+ integration.integrable.config
end
def redirect_uri
diff --git a/app/models/google_play_store_integration.rb b/app/models/google_play_store_integration.rb
index 6273c9f01..2245a08b3 100644
--- a/app/models/google_play_store_integration.rb
+++ b/app/models/google_play_store_integration.rb
@@ -16,8 +16,8 @@ class GooglePlayStoreIntegration < ApplicationRecord
include Displayable
include Loggable
- delegate :app, to: :integration, allow_nil: true
- delegate :refresh_external_app, to: :app
+ delegate :integrable, to: :integration, allow_nil: true
+ delegate :refresh_external_app, :bundle_identifier, to: :integrable, allow_nil: true
validate :correct_key, on: :create
@@ -50,7 +50,7 @@ def access_key
end
def installation
- Installations::Google::PlayDeveloper::Api.new(app.bundle_identifier, access_key)
+ Installations::Google::PlayDeveloper::Api.new(bundle_identifier, access_key)
end
def create_draft_release(channel, build_number, version, release_notes, retry_on_review_fail: false)
@@ -104,7 +104,7 @@ def further_setup?
end
def draft_check
- app&.set_draft_status!
+ integrable&.set_draft_status!
end
def draft_check?
@@ -126,7 +126,7 @@ def to_s
end
def connection_data
- "Bundle Identifier: #{app.bundle_identifier}"
+ "Bundle Identifier: #{bundle_identifier}"
end
def channels
diff --git a/app/models/integration.rb b/app/models/integration.rb
index 7c8666836..eb83c4fd0 100644
--- a/app/models/integration.rb
+++ b/app/models/integration.rb
@@ -11,7 +11,6 @@
# status :string indexed => [integrable_id, category, providable_type]
# created_at :datetime not null
# updated_at :datetime not null
-# app_id :uuid indexed
# integrable_id :uuid indexed => [category, providable_type, status]
# providable_id :uuid
#
@@ -21,7 +20,8 @@ class Integration < ApplicationRecord
using RefinedString
include Discard::Model
- # self.ignored_columns += %w[app_id]
+ self.ignored_columns += %w[app_id]
+
belongs_to :app, optional: true
PROVIDER_TYPES = %w[GithubIntegration GitlabIntegration SlackIntegration AppStoreIntegration GooglePlayStoreIntegration BitriseIntegration GoogleFirebaseIntegration BugsnagIntegration BitbucketIntegration CrashlyticsIntegration]
diff --git a/app/models/notification_setting.rb b/app/models/notification_setting.rb
index 9463dd3b2..2a0ce20ae 100644
--- a/app/models/notification_setting.rb
+++ b/app/models/notification_setting.rb
@@ -18,26 +18,13 @@ class NotificationSetting < ApplicationRecord
belongs_to :train
- # NOTE: These will be removed after Product v2 is released
- DEPRECATED_KINDS = {
- deployment_finished: "deployment_finished",
- deployment_failed: "deployment_failed",
- step_started: "step_started",
- build_available: "build_available",
- step_failed: "step_failed",
- submit_for_review: "submit_for_review",
- review_approved: "review_approved",
- review_failed: "review_failed",
- staged_rollout_updated: "staged_rollout_updated",
- staged_rollout_paused: "staged_rollout_paused",
- staged_rollout_resumed: "staged_rollout_resumed",
- staged_rollout_halted: "staged_rollout_halted",
- staged_rollout_completed: "staged_rollout_completed",
- staged_rollout_fully_released: "staged_rollout_fully_released"
- }
-
- # Product v2 notifications
- V2_KINDS = {
+ enum :kind, {
+ release_started: "release_started",
+ release_stopped: "release_stopped",
+ release_ended: "release_ended",
+ release_scheduled: "release_scheduled",
+ release_health_events: "release_health_events",
+ backmerge_failed: "backmerge_failed",
build_available_v2: "build_available_v2",
internal_release_finished: "internal_release_finished",
internal_release_failed: "internal_release_failed",
@@ -60,15 +47,6 @@ class NotificationSetting < ApplicationRecord
workflow_run_unavailable: "workflow_run_unavailable"
}
- enum :kind, {
- release_started: "release_started",
- release_stopped: "release_stopped",
- release_ended: "release_ended",
- release_scheduled: "release_scheduled",
- release_health_events: "release_health_events",
- backmerge_failed: "backmerge_failed"
- }.merge(DEPRECATED_KINDS).merge(V2_KINDS)
-
scope :active, -> { where(active: true) }
delegate :app, to: :train
delegate :notification_provider, to: :app
diff --git a/app/models/play_store_submission.rb b/app/models/play_store_submission.rb
index c62bd3a59..6fccbe045 100644
--- a/app/models/play_store_submission.rb
+++ b/app/models/play_store_submission.rb
@@ -80,7 +80,7 @@ class PlayStoreSubmission < StoreSubmission
transitions from: :preparing, to: :prepared
end
- # TODO: [V2] [post-alpha] This is currently not used, should be hooked up as an action from the user
+ # TODO: This is currently not used, should be hooked up as an action from the user
event :reject do
after { set_rejected_at! }
transitions from: :prepared, to: :review_failed
diff --git a/app/models/production_release.rb b/app/models/production_release.rb
index 6035960cf..9273754b6 100644
--- a/app/models/production_release.rb
+++ b/app/models/production_release.rb
@@ -163,6 +163,11 @@ def show_health?
false
end
+ def check_release_health
+ return unless latest_health_data&.fresh?
+ latest_health_data.check_release_health
+ end
+
def conf = Config::ReleaseStep.from_json(config)
def production? = true
diff --git a/app/models/release.rb b/app/models/release.rb
index a9cf14150..1130cf072 100644
--- a/app/models/release.rb
+++ b/app/models/release.rb
@@ -32,7 +32,6 @@ class Release < ApplicationRecord
include Versionable
include Displayable
include Linkable
- include ActionView::Helpers::DateHelper
using RefinedString
@@ -108,8 +107,6 @@ class Release < ApplicationRecord
has_many :release_metadata, through: :release_platform_runs
has_many :all_commits, dependent: :destroy, inverse_of: :release, class_name: "Commit"
has_many :pull_requests, dependent: :destroy, inverse_of: :release
- has_many :step_runs, through: :release_platform_runs
- has_many :deployment_runs, through: :step_runs
has_many :builds, through: :release_platform_runs
has_many :build_queues, dependent: :destroy
has_one :active_build_queue, -> { active }, class_name: "BuildQueue", inverse_of: :release, dependent: :destroy
@@ -166,14 +163,12 @@ class Release < ApplicationRecord
before_create :set_internal_notes
after_create :create_platform_runs!
after_create :create_build_queue!, if: -> { train.build_queue_enabled? }
- after_commit -> { Releases::PreReleaseJob.perform_later(id) }, on: :create
- after_commit -> { Releases::FetchCommitLogJob.perform_later(id) }, on: :create
after_commit -> { create_stamp!(data: {version: original_release_version}) }, on: :create
attr_accessor :has_major_bump, :hotfix_platform, :custom_version
friendly_id :human_slug, use: :slugged
- delegate :versioning_strategy, :patch_version_bump_only, :product_v2?, to: :train
+ delegate :versioning_strategy, :patch_version_bump_only, to: :train
delegate :app, :vcs_provider, :release_platforms, :notify!, :continuous_backmerge?, :approvals_enabled?, to: :train
delegate :platform, :organization, to: :app
@@ -192,13 +187,7 @@ def index_score
return if hotfix?
return unless finished?
- reldex_params =
- if is_v2?
- Queries::ReldexParameters.call(self)
- else
- Computations::Release::ReldexParameters.call(self)
- end
-
+ reldex_params = Queries::ReldexParameters.call(self)
train.release_index&.score(**reldex_params)
end
@@ -254,13 +243,6 @@ def duration
ActiveSupport::Duration.build(completed_at - scheduled_at)
end
- def all_store_step_runs
- deployment_runs
- .reached_production
- .map(&:step_run)
- .sort_by(&:updated_at)
- end
-
def unmerged_commits
all_commits.where(backmerge_failure: true)
end
@@ -387,14 +369,6 @@ def pull_requests_url(open = false)
train.vcs_provider&.pull_requests_url(branch_name, open:)
end
- def metadata_editable?
- release_platform_runs.any?(&:metadata_editable?)
- end
-
- def release_step_started?
- release_platform_runs.any?(&:release_step_started?)
- end
-
class PreReleaseUnfinishedError < StandardError; end
def close_pre_release_prs
@@ -411,25 +385,10 @@ def close_pre_release_prs
end
end
- def patch_fix?
- return false unless committable?
- release_platform_runs.any?(&:patch_fix?)
- end
-
def ready_to_be_finalized?
release_platform_runs.all? { |prun| prun.finished? || prun.stopped? }
end
- def finalize_phase_metadata
- {
- total_run_time: distance_of_time_in_words(created_at, completed_at),
- release_tag: tag_name,
- release_tag_url: tag_url,
- store_url: (app.store_link unless app.cross_platform?),
- final_artifact_url: (release_platform_runs.first&.final_build_artifact&.download_url unless app.cross_platform?)
- }
- end
-
def live_release_link
return if Rails.env.test?
release_url(self, link_params)
@@ -442,7 +401,9 @@ def notification_params
release_branch_url: branch_url,
release_url: live_release_link,
release_version: release_version,
- is_release_unhealthy: unhealthy?
+ is_release_unhealthy: unhealthy?,
+ release_completed_at: completed_at,
+ release_started_at: scheduled_at
}
)
end
@@ -583,13 +544,13 @@ def create_platform_runs!
end
def set_version
- if train.freeze_version? && custom_version.blank?
- self.original_release_version = train.version_current
+ if custom_version.present?
+ self.original_release_version = custom_version
return
end
- if custom_version.present?
- self.original_release_version = custom_version
+ if train.freeze_version?
+ self.original_release_version = train.version_current
return
end
diff --git a/app/models/release_health_event.rb b/app/models/release_health_event.rb
index 03f0f429b..17feecb51 100644
--- a/app/models/release_health_event.rb
+++ b/app/models/release_health_event.rb
@@ -9,7 +9,6 @@
# notification_triggered :boolean default(FALSE)
# created_at :datetime not null
# updated_at :datetime not null
-# deployment_run_id :uuid indexed => [release_health_rule_id, release_health_metric_id], indexed
# production_release_id :uuid indexed => [release_health_rule_id, release_health_metric_id], indexed
# release_health_metric_id :uuid not null, indexed => [deployment_run_id, release_health_rule_id], indexed => [production_release_id, release_health_rule_id], indexed
# release_health_rule_id :uuid not null, indexed => [deployment_run_id, release_health_metric_id], indexed => [production_release_id, release_health_metric_id], indexed
@@ -18,34 +17,30 @@ class ReleaseHealthEvent < ApplicationRecord
include Displayable
self.implicit_order_column = :event_timestamp
+ self.ignored_columns += ["deployment_run_id"]
enum :health_status, {healthy: "healthy", unhealthy: "unhealthy"}
- belongs_to :deployment_run, optional: true
- belongs_to :production_release, optional: true
+ belongs_to :production_release
belongs_to :release_health_rule
belongs_to :release_health_metric
scope :for_rule, ->(rule) { where(release_health_rule: rule) }
- delegate :notify!, to: :parent
+ delegate :notify!, to: :production_release
after_create_commit :notify_health_rule_triggered
private
- def parent
- deployment_run || production_release
- end
-
def notify_health_rule_triggered
return if previous_event.blank? && healthy?
- return if healthy? && parent.unhealthy?
+ return if healthy? && production_release.unhealthy?
notify!("One of the release health rules has been triggered", :release_health_events, notification_params)
end
def notification_params
- parent.notification_params.merge(
+ production_release.notification_params.merge(
{
release_health_rule_filters: rule_filters,
release_health_rule_triggers: rule_triggers
@@ -62,6 +57,6 @@ def rule_triggers
end
def previous_event
- parent.release_health_events.for_rule(release_health_rule).where.not(id:).reorder("event_timestamp").last
+ production_release.release_health_events.for_rule(release_health_rule).where.not(id:).reorder("event_timestamp").last
end
end
diff --git a/app/models/release_health_metric.rb b/app/models/release_health_metric.rb
index 430670ae3..6ee2b3313 100644
--- a/app/models/release_health_metric.rb
+++ b/app/models/release_health_metric.rb
@@ -14,18 +14,16 @@
# total_sessions_in_last_day :bigint
# created_at :datetime not null
# updated_at :datetime not null
-# deployment_run_id :uuid indexed
# external_release_id :string
# production_release_id :uuid indexed
#
class ReleaseHealthMetric < ApplicationRecord
- belongs_to :deployment_run, optional: true
- belongs_to :production_release, optional: true
- has_many :release_health_events, dependent: :nullify
+ self.ignored_columns += ["deployment_run_id"]
- validate :at_least_one_parent
+ belongs_to :production_release
+ has_many :release_health_events, dependent: :nullify
- delegate :release_health_rules, to: :parent
+ delegate :release_health_rules, to: :production_release
after_create_commit :check_release_health
@@ -58,7 +56,7 @@ def adoption_rate
end
def staged_rollout
- parent.rollout_percentage
+ production_release.rollout_percentage
end
def check_release_health
@@ -90,14 +88,6 @@ def metric_healthy?(metric_name)
private
- def at_least_one_parent
- errors.add(:base, "Release health metrics must have at least one of production_release or deployment_run") if parent.blank?
- end
-
- def parent
- deployment_run || production_release
- end
-
def create_health_event(release_health_rule)
return unless release_health_rule.actionable?(self)
@@ -106,11 +96,11 @@ def create_health_event(release_health_rule)
current_status = is_healthy ? ReleaseHealthEvent.health_statuses[:healthy] : ReleaseHealthEvent.health_statuses[:unhealthy]
return if last_event.present? && last_event.health_status == current_status
- release_health_events.create(deployment_run:, production_release:, release_health_rule:, health_status: current_status, event_timestamp: Time.current)
+ release_health_events.create(production_release:, release_health_rule:, health_status: current_status, event_timestamp: Time.current)
end
def last_event_for(rule)
- parent.release_health_events.where(release_health_rule: rule).last
+ production_release.release_health_events.where(release_health_rule: rule).last
end
def healthy_for_triggers?(rule, metric_name)
diff --git a/app/models/release_metadata.rb b/app/models/release_metadata.rb
index 2c98a5492..896677e88 100644
--- a/app/models/release_metadata.rb
+++ b/app/models/release_metadata.rb
@@ -20,7 +20,6 @@ class ReleaseMetadata < ApplicationRecord
belongs_to :release, inverse_of: :release_metadata
belongs_to :release_platform_run, inverse_of: :release_metadata, optional: true
- NOTES_MAX_LENGTH = 4000 # TODO [V2]: remove this and use the platform-specific notes max length
IOS_NOTES_MAX_LENGTH = 4000
ANDROID_NOTES_MAX_LENGTH = 500
# NOTE: Refer to https://www.regular-expressions.info/unicode.html for supporting more unicode characters
diff --git a/app/models/release_platform.rb b/app/models/release_platform.rb
index e54d1add8..2b9d46d9c 100644
--- a/app/models/release_platform.rb
+++ b/app/models/release_platform.rb
@@ -2,23 +2,14 @@
#
# Table name: release_platforms
#
-# id :uuid not null, primary key
-# branching_strategy :string
-# description :string
-# name :string not null
-# platform :string
-# release_backmerge_branch :string
-# release_branch :string
-# slug :string
-# status :string
-# version_current :string
-# version_seeded_with :string
-# working_branch :string
-# created_at :datetime not null
-# updated_at :datetime not null
-# app_id :uuid not null, indexed
-# train_id :uuid
-# vcs_webhook_id :string
+# id :uuid not null, primary key
+# name :string not null
+# platform :string
+# slug :string
+# created_at :datetime not null
+# updated_at :datetime not null
+# app_id :uuid not null, indexed
+# train_id :uuid
#
class ReleasePlatform < ApplicationRecord
@@ -27,8 +18,8 @@ class ReleasePlatform < ApplicationRecord
extend FriendlyId
include Displayable
- # self.ignored_columns += %w[branching_strategy description release_backmerge_branch release_branch version_current version_seeded_with working_branch vcs_webhook_id status]
- self.ignored_columns += %w[config]
+ self.ignored_columns += %w[branching_strategy description release_backmerge_branch release_branch version_current version_seeded_with working_branch vcs_webhook_id status config]
+
NATURAL_ORDER = Arel.sql("CASE WHEN platform = 'android' THEN 1 WHEN platform = 'ios' THEN 2 ELSE 3 END")
DEFAULT_PROD_RELEASE_CONFIG = {
android: {
@@ -59,9 +50,6 @@ class ReleasePlatform < ApplicationRecord
has_many :release_health_rules, -> { kept }, dependent: :destroy, inverse_of: :release_platform
has_many :all_release_health_rules, dependent: :destroy, inverse_of: :release_platform, class_name: "ReleaseHealthRule"
has_many :release_platform_runs, inverse_of: :release_platform, dependent: :destroy
- has_many :steps, -> { kept.order(:step_number) }, inverse_of: :release_platform, dependent: :destroy
- has_many :all_steps, -> { order(:step_number) }, class_name: "Step", inverse_of: :release_platform, dependent: :destroy
- has_many :deployments, through: :steps
scope :sequential, -> { order(NATURAL_ORDER) }
@@ -74,6 +62,7 @@ class ReleasePlatform < ApplicationRecord
delegate :integrations, :ci_cd_provider, to: :train
delegate :ready?, :default_locale, to: :app
+ delegate :has_restricted_public_channels?, to: :platform_config
def self.allowed_platforms
{
@@ -82,47 +71,10 @@ def self.allowed_platforms
}.invert
end
- def active_steps_for(release)
- # no release
- return steps unless release
- return steps if release.active?
-
- # historical release only
- all_steps
- .where(created_at: ..release.end_time)
- .where("discarded_at IS NULL OR discarded_at >= ?", release.end_time)
- end
-
- def has_release_step?
- steps.release.any?
- end
-
- alias_method :startable?, :has_release_step?
-
- def has_production_deployment?
- release_step&.has_production_deployment?
- end
-
- def has_review_steps?
- steps.review.exists?
- end
-
- def release_step
- steps.release.first
- end
-
def display_name
name&.parameterize
end
- def ordered_steps_until(step_number)
- steps.where(step_number: ..step_number).order(:step_number)
- end
-
- def valid_steps?
- steps.release.size == 1
- end
-
def store_provider
if ios?
app.ios_store_provider
@@ -137,12 +89,6 @@ def production_ready?
store_provider.present?
end
- def build_channel_integrations
- integrations
- .build_channel
- .where(providable_type: Integration::ALLOWED_INTEGRATIONS_FOR_APP[platform][:build_channel])
- end
-
def active_locales
app.latest_external_apps[platform.to_sym]&.active_locales
end
@@ -155,6 +101,8 @@ def production_submission_type
DEFAULT_PROD_RELEASE_CONFIG[platform.to_sym][:submissions].first[:submission_type] if production_ready?
end
+ private
+
# setup production if the store integrations are added
# otherwise use the first build channel integration and setup beta releases
def set_default_config
diff --git a/app/models/release_platform_run.rb b/app/models/release_platform_run.rb
index d35ea12b3..e8c316523 100644
--- a/app/models/release_platform_run.rb
+++ b/app/models/release_platform_run.rb
@@ -51,11 +51,6 @@ class ReleasePlatformRun < ApplicationRecord
has_many :production_store_rollouts, -> { production }, class_name: "StoreRollout", dependent: :destroy, inverse_of: :release_platform_run
has_many :production_store_submissions, -> { production }, class_name: "StoreSubmission", dependent: :destroy, inverse_of: :release_platform_run
- # NOTE: deprecated after v2
- has_many :step_runs, dependent: :destroy, inverse_of: :release_platform_run
- has_many :deployment_runs, through: :step_runs
- has_many :running_steps, through: :step_runs, source: :step
-
scope :sequential, -> { order("release_platform_runs.created_at ASC") }
scope :have_not_submitted_production, -> { on_track.reject(&:production_release_submitted?) }
@@ -70,19 +65,15 @@ class ReleasePlatformRun < ApplicationRecord
enum :status, STATES
- before_create :set_config, if: -> { release.is_v2? }
+ before_create :set_config
after_create :set_default_release_metadata
scope :pending_release, -> { where.not(status: [:finished, :stopped]) }
delegate :all_commits, :original_release_version, :hotfix?, :versioning_strategy, :organization, :release_branch, to: :release
- delegate :steps, :train, :app, :platform, :active_locales, :store_provider, :ios?, :android?, :default_locale, :ci_cd_provider, to: :release_platform
+ delegate :train, :app, :platform, :active_locales, :store_provider, :ios?, :android?, :default_locale, :ci_cd_provider, to: :release_platform
def external_builds
- if release.is_v2?
- ExternalBuild.where(build_id: builds.select(:id))
- else
- ExternalBuild.where(step_run_id: step_runs.select(:id))
- end
+ ExternalBuild.where(build_id: builds.select(:id))
end
def start!
@@ -131,7 +122,7 @@ def latest_beta_release(finished: false)
(finished ? beta_releases.finished : beta_releases).order(created_at: :desc).first
end
- # TODO: [V2] eager loading here is too expensive
+ # TODO: eager loading here is too expensive
def latest_internal_release(finished: false)
(finished ? internal_releases.finished : internal_releases)
.includes(:commit, :store_submissions, triggered_workflow_run: {build: [:commit, :artifact]}, release_platform_run: [:release])
@@ -159,7 +150,7 @@ def older_beta_releases
beta_releases.order(created_at: :desc).offset(1)
end
- # TODO: [V2] eager loading here is too expensive
+ # TODO: eager loading here is too expensive
def older_internal_releases
internal_releases
.order(created_at: :desc)
@@ -200,7 +191,7 @@ def next_build_sequence_number
end
def check_release_health
- deployment_runs.each(&:check_release_health)
+ production_releases.each(&:check_release_health)
end
def release_metadatum
@@ -212,23 +203,14 @@ def default_release_metadata
end
def show_health?
- if release.is_v2?
- latest_production_release&.show_health?
- else
- deployment_runs.any?(&:show_health?)
- end
+ latest_production_release&.show_health?
end
def unhealthy?
- if release.is_v2?
- latest_production_release&.unhealthy?
- else
- latest_store_release&.unhealthy?
- end
+ latest_production_release&.unhealthy?
end
def failure?
- return step_runs.last&.failure? unless release.is_v2?
return false if latest_production_release.present?
pre_prod_releases.reorder(created_at: :desc).first&.failure?
@@ -275,8 +257,6 @@ def corrected_release_version
train.hotfix_release.next_version if train.hotfix_release&.version_ahead?(self)
end
- # TODO: [V2] this is a workaround to handle drifted cross-platform releases
- # Figure out of a way to deprecate last_commit from rpr and rely on release instead
def update_last_commit!(commit)
return if commit.blank?
return if last_commit&.commit_hash == commit.commit_hash
@@ -314,113 +294,18 @@ def newest_release_version
upcoming.release_version
end
- def metadata_editable?
- on_track? && !started_store_release?
- end
-
def production_release_in_pre_review?
return unless active?
return if active_production_release.present? && inflight_production_release.blank?
inflight_production_release.blank? || inflight_production_release.store_submission.pre_review?
end
- alias_method :metadata_editable_v2?, :production_release_in_pre_review?
-
- # FIXME: move to release and change it for proper movement UI
- def overall_movement_status
- steps.to_h do |step|
- run = last_commit&.run_for(step, self)
- [step, run.present? ? run.status_summary : {not_started: true}]
- end
- end
-
- def manually_startable_step?(step)
- return false if train.inactive?
- return false unless on_track?
- return false if last_commit.blank?
- return false if ongoing_release_step?(step) && train.hotfix_release.present?
- return true if (hotfix? || patch_fix?) && last_commit.run_for(step, self).blank?
- return false if upcoming_release_step?(step)
- return true if step.first? && step_runs_for(step).empty?
- return false if step.first?
-
- (next_step == step) && previous_step_run_for(step).success?
- end
-
- def step_start_blocked?(step)
- return false if train.inactive?
- return false unless on_track?
- return false if last_commit.blank?
- return true if train.hotfix_release.present? && train.hotfix_release != release && step.release?
-
- (next_step == step) && previous_step_run_for(step)&.success? && upcoming_release_step?(step)
- end
+ alias_method :metadata_editable?, :production_release_in_pre_review?
def temporary_unblock_upcoming?
Flipper.enabled?(:temporary_unblock_upcoming, self)
end
- def upcoming_release_step?(step)
- step.release? && release.upcoming? && !temporary_unblock_upcoming?
- end
-
- def ongoing_release_step?(step)
- step.release? && release.ongoing?
- end
-
- def step_runs_for(step)
- step_runs.where(step:)
- end
-
- def previous_step_run_for(step)
- last_run_for(step.previous)
- end
-
- def finalizable?
- on_track? && finished_steps?
- end
-
- def next_step
- return steps.first if step_runs.empty? || last_commit.blank?
- return steps.first if last_commit.step_runs_for(self).empty?
- last_commit.step_runs_for(self).joins(:step).order(:step_number).last.step.next
- end
-
- def running_step?
- step_runs.on_track.exists?
- end
-
- def last_run_for(step)
- return if last_commit.blank?
- last_commit.step_runs_for(self).where(step: step).sequential.last
- end
-
- def current_step_number
- return if steps.blank?
- return steps.minimum(:step_number) if running_steps.blank?
- running_steps.order(:step_number).last.step_number
- end
-
- def finished_steps?
- return false if release_platform.release_step.blank?
- return false if last_commit.blank?
-
- last_commit.run_for(release_platform.release_step, self)&.success?
- end
-
- def last_good_step_run
- step_runs
- .where(status: StepRun.statuses[:success])
- .joins(:step)
- .order(step_number: :desc, updated_at: :desc)
- .first
- end
-
- def final_build_artifact
- return unless finished?
- last_good_step_run&.build_artifact
- end
-
def tag_url
train.vcs_provider&.tag_url(tag_name)
end
@@ -449,54 +334,7 @@ def create_tag!(commit, input_tag_name = base_tag_name)
# Patch fix commit: no bump required
# --
def version_bump_required?
- if release.is_v2?
- latest_production_release&.version_bump_required?
- else
- store_release = latest_deployed_store_release
- store_release&.status&.in?(DeploymentRun::READY_STATES) && store_release.step_run.basic_build_version == release_version
- end
- end
-
- def patch_fix?
- on_track? && in_store_resubmission?
- end
-
- def release_step_started?
- step_runs_for(release_platform.release_step).present?
- end
-
- def production_release_happened?
- step_runs
- .includes(:deployment_runs)
- .where(step: release_platform.release_step)
- .not_failed
- .any?(&:production_release_happened?)
- end
-
- def production_release_submitted?
- step_runs
- .includes(:deployment_runs)
- .where(step: release_platform.release_step)
- .not_failed
- .any?(&:production_release_submitted?)
- end
-
- def commit_applied?(commit)
- step_runs.exists?(commit: commit)
- end
-
- def previous_successful_run_before(step_run)
- step_runs
- .where(step: step_run.step)
- .where(scheduled_at: ..step_run.scheduled_at)
- .where.not(id: step_run.id)
- .success
- .order(scheduled_at: :asc)
- .last
- end
-
- def commits_between(older_step_run, newer_step_run)
- all_commits.between(older_step_run, newer_step_run)
+ latest_production_release&.version_bump_required?
end
def notification_params
@@ -509,14 +347,6 @@ def notification_params
)
end
- def store_releases
- deployment_runs.reached_production.sort_by(&:scheduled_at).reverse
- end
-
- def store_submitted_releases
- deployment_runs.reached_submission.sort_by(&:scheduled_at).reverse
- end
-
def block_play_store_submissions!
update!(play_store_blocked: true)
end
@@ -549,32 +379,6 @@ def base_tag_name
"v#{release_version}-#{platform}"
end
- def started_store_release?
- latest_store_release.present?
- end
-
- def latest_store_release
- last_run_for(release_platform.release_step)
- &.deployment_runs
- &.not_failed
- &.find { |dr| dr.deployment.production_channel? }
- end
-
- def latest_deployed_store_release
- last_successful_run_for(release_platform.release_step)
- &.deployment_runs
- &.not_failed
- &.find { |dr| dr.deployment.production_channel? }
- end
-
- def last_successful_run_for(step)
- step_runs
- .where(step: step)
- .not_failed
- .order(scheduled_at: :asc)
- .last
- end
-
def set_config
self.config = release_platform.platform_config.as_json
end
diff --git a/app/models/slack_integration.rb b/app/models/slack_integration.rb
index 253c43a75..f9771bb11 100644
--- a/app/models/slack_integration.rb
+++ b/app/models/slack_integration.rb
@@ -19,7 +19,7 @@ class SlackIntegration < ApplicationRecord
include Loggable
include Rails.application.routes.url_helpers
- delegate :app, to: :integration
+ delegate :integrable, to: :integration
delegate :cache, to: Rails
attr_accessor :code
@@ -105,14 +105,8 @@ def channels
.map { |c| c.slice(:id, :name, :is_private) }
end
- def build_channels(with_production:)
- cache
- .fetch(channels_cache_key, expires_in: CACHE_EXPIRY) { get_all_channels }
- .map { |c| c.slice(:id, :name) }
- end
-
def channels_cache_key
- "app/#{app.id}/slack_integration/#{id}/channels"
+ "app/#{integrable.id}/slack_integration/#{id}/channels"
end
def notify!(channel, message, type, params, file_id = nil, file_title = nil)
@@ -142,10 +136,6 @@ def upload_file!(file, file_name)
elog(e)
end
- def deploy!(channel, params)
- notify!(channel, DEPLOY_MESSAGE, :deployment_finished, params)
- end
-
def notifier(type, params)
Notifiers::Slack::Builder.build(type, **params)
end
diff --git a/app/models/staged_rollout.rb b/app/models/staged_rollout.rb
deleted file mode 100644
index 24f20a37f..000000000
--- a/app/models/staged_rollout.rb
+++ /dev/null
@@ -1,221 +0,0 @@
-# == Schema Information
-#
-# Table name: staged_rollouts
-#
-# id :uuid not null, primary key
-# config :decimal(8, 5) default([]), is an Array
-# current_stage :integer
-# status :string
-# created_at :datetime not null
-# updated_at :datetime not null
-# deployment_run_id :uuid not null, indexed
-#
-class StagedRollout < ApplicationRecord
- has_paper_trail
- include AASM
- include Loggable
- include Passportable
-
- belongs_to :deployment_run
-
- validates :current_stage, numericality: {greater_than_or_equal_to: 0, allow_nil: true}
-
- delegate :notify!, :platform, to: :deployment_run
-
- STAMPABLE_REASONS = %w[
- started
- paused
- failed
- failed_before_any_rollout
- resumed
- increased
- completed
- halted
- fully_released
- ]
-
- STATES = {
- created: "created",
- started: "started",
- paused: "paused",
- failed: "failed",
- completed: "completed",
- stopped: "stopped",
- fully_released: "fully_released"
- }
-
- enum :status, STATES
- aasm safe_state_machine_params do
- state :created, initial: true, before_enter: -> { deployment_run.rolloutable? }
- state(*STATES.keys)
-
- event :start, guard: -> { deployment_run.rolloutable? }, after_commit: -> { event_stamp!(reason: :started, kind: :notice, data: stamp_data) } do
- transitions from: :created, to: :started
- end
-
- event :pause, guard: -> { deployment_run.automatic_rollout? }, after_commit: -> { event_stamp!(reason: :paused, kind: :notice, data: stamp_data) } do
- transitions from: :started, to: :paused
- end
-
- event :resume, after_commit: -> { event_stamp!(reason: :resumed, kind: :success, data: stamp_data) } do
- transitions from: :paused, to: :started, guard: -> { deployment_run.automatic_rollout? }
- transitions from: :stopped, to: :started, guard: -> { deployment_run.controllable_rollout? }
- end
-
- event :fail, after_commit: -> { fail_stamp } do
- transitions from: [:started, :failed, :created], to: :failed
- end
-
- event :retry, guard: -> { deployment_run.rolloutable? } do
- transitions from: :failed, to: :started
- end
-
- event :halt, guard: -> { deployment_run.rolloutable? }, after_commit: -> { event_stamp!(reason: :halted, kind: :notice, data: stamp_data) } do
- transitions from: [:started, :paused, :failed], to: :stopped
- end
-
- event :complete, after_commit: -> { event_stamp!(reason: :completed, kind: :success, data: stamp_data) } do
- after { notify!("Staged rollout was completed!", :staged_rollout_completed, notification_params) }
- after { deployment_run.complete! }
- transitions from: [:failed, :started, :paused], to: :completed
- end
-
- event :full_rollout, after_commit: -> { event_stamp!(reason: :fully_released, kind: :success, data: {rollout_percentage: "%.2f" % config[current_stage]}) } do
- after { deployment_run.complete! }
- transitions from: [:failed, :started], to: :fully_released
- end
- end
-
- def active?
- %w[started completed fully_released].include?(status)
- end
-
- def display_current_stage
- return 0 if created?
- (current_stage || 0).succ
- end
-
- def update_stage(stage, finish_rollout: false)
- return if stage == current_stage && !finish_rollout
-
- update!(current_stage: stage)
-
- if created?
- start!
- else
- event_stamp!(reason: :increased, kind: :notice, data: stamp_data)
- end
-
- retry! if failed?
- return complete! if finish_rollout && finished?
- notify!("Staged rollout was updated!", :staged_rollout_updated, notification_params)
- end
-
- def last_rollout_percentage
- return Deployment::FULL_ROLLOUT_VALUE if fully_released?
- return if created? || current_stage.nil?
- return config.last if finished?
- config[current_stage]
- end
-
- def next_rollout_percentage
- return config.first if created?
- config[current_stage.succ]
- end
-
- def finished?
- next_rollout_percentage.nil?
- end
-
- def next_stage
- created? ? 0 : current_stage.succ
- end
-
- def move_to_next_stage!
- return if completed? || fully_released?
-
- deployment_run.on_release(rollout_value: next_rollout_percentage) do |result|
- if result.ok?
- update_stage(next_stage, finish_rollout: true)
- else
- fail!
- elog(result.error)
- end
- end
- end
-
- def halt_release!
- return if created?
- return if completed? || fully_released?
-
- deployment_run.on_halt_release! do |result|
- if result.ok?
- halt!
- notify!("Release was halted!", :staged_rollout_halted, notification_params)
- else
- elog(result.error)
- end
- end
- end
-
- def fully_release!
- return if created? || completed? || stopped?
-
- deployment_run.on_fully_release! do |result|
- if result.ok?
- full_rollout!
- notify!("Staged rollout was accelerated to a full rollout!", :staged_rollout_fully_released, notification_params)
- else
- elog(result.error)
- end
- end
- end
-
- def pause_release!
- return unless started?
-
- deployment_run.on_pause_release! do |result|
- if result.ok?
- pause!
- notify!("Staged rollout was paused!", :staged_rollout_paused, notification_params)
- else
- elog(result.error)
- end
- end
- end
-
- def resume_release!
- return if (deployment_run.automatic_rollout? && !paused?) || (deployment_run.controllable_rollout? && !stopped?)
-
- deployment_run.on_resume_release! do |result|
- if result.ok?
- unless completed?
- resume!
- notify!("Staged rollout was resumed!", :staged_rollout_resumed, notification_params)
- end
- else
- elog(result.error)
- end
- end
- end
-
- def notification_params
- deployment_run.notification_params.merge(stamp_data)
- end
-
- private
-
- def fail_stamp
- if last_rollout_percentage
- event_stamp!(reason: :failed, kind: :error, data: stamp_data)
- else
- event_stamp!(reason: :failed_before_any_rollout, kind: :error, data: stamp_data)
- end
- end
-
- def stamp_data
- data = {current_stage: display_current_stage, is_fully_released: fully_released?}
- data[:rollout_percentage] = "%.2f" % config[current_stage] if current_stage.present?
- data
- end
-end
diff --git a/app/models/step.rb b/app/models/step.rb
deleted file mode 100644
index 3d565a0e4..000000000
--- a/app/models/step.rb
+++ /dev/null
@@ -1,193 +0,0 @@
-# == Schema Information
-#
-# Table name: steps
-#
-# id :uuid not null, primary key
-# auto_deploy :boolean default(TRUE)
-# build_artifact_name_pattern :string
-# ci_cd_channel :jsonb not null, indexed => [release_platform_id]
-# description :string not null
-# discarded_at :datetime indexed
-# kind :string
-# name :string not null
-# release_suffix :string
-# slug :string
-# status :string not null
-# step_number :integer default(0), not null, indexed => [release_platform_id]
-# created_at :datetime not null
-# updated_at :datetime not null
-# app_variant_id :uuid
-# integration_id :uuid indexed
-# release_platform_id :uuid not null, indexed => [ci_cd_channel], indexed, indexed => [step_number]
-#
-class Step < ApplicationRecord
- has_paper_trail
- extend FriendlyId
- include Discard::Model
-
- self.implicit_order_column = :step_number
-
- belongs_to :release_platform, inverse_of: :steps
- belongs_to :app_variant, inverse_of: :steps, optional: true
- belongs_to :integration, optional: true
- has_many :step_runs, inverse_of: :step, dependent: :destroy
- has_many :deployments, -> { kept.sequential }, inverse_of: :step, dependent: :destroy
- has_many :all_deployments, -> { sequential }, class_name: "Deployment", inverse_of: :step, dependent: :destroy
- has_many :deployment_runs, through: :deployments
- validates :ci_cd_channel, presence: true, uniqueness: {scope: :release_platform_id, conditions: -> { kept }, message: "you have already used this in another step of this train!"}
- validates :release_suffix, format: {with: /\A[a-zA-Z\-_]+\z/, message: "only allows letters and underscore"}, if: -> { release_suffix.present? }
- validates :deployments, presence: true, on: :create
- validate :unique_deployments, on: :create
- validate :unique_store_deployments_per_train, on: :create
- validate :auto_deployment_allowed, on: :create
-
- after_initialize :set_default_status, if: :new_record?
- before_validation :set_step_number, if: :new_record?
- before_save -> { self.build_artifact_name_pattern = build_artifact_name_pattern.downcase }, if: -> { build_artifact_name_pattern.present? }
- after_create :set_ci_cd_provider
-
- enum :status, {
- active: "active",
- inactive: "inactive"
- }
-
- enum :kind, {
- review: "review",
- release: "release"
- }
-
- friendly_id :name, use: :slugged
- normalizes :name, with: ->(name) { name.squish }
- normalizes :build_artifact_name_pattern, with: ->(pattern) { pattern.squish }
- accepts_nested_attributes_for :deployments, allow_destroy: false, reject_if: :reject_deployments?
-
- delegate :app, :train, to: :release_platform
- delegate :android?, to: :app
- delegate :notify!, to: :train
-
- def ci_cd_provider
- integration.providable
- end
-
- def set_ci_cd_provider
- update(integration: train.ci_cd_provider.integration)
- end
-
- def active_deployments_for(release, step_run = nil)
- # no release
- return deployments unless release
-
- # ongoing release
- return step_run.deployment_runs.map(&:deployment) if release.end_time.blank? && step_run&.success?
- return deployments if release.end_time.blank?
-
- # historical release only
- all_deployments
- .where(created_at: ..release.end_time)
- .where("discarded_at IS NULL OR discarded_at >= ?", release.end_time)
- end
-
- def suffixable?
- release_suffix.present? && release_platform.android?
- end
-
- def set_step_number
- all_steps = release_platform.all_steps
-
- if review?
- self.step_number = all_steps.review.maximum(:step_number).to_i + 1
- release_platform.release_step&.update!(step_number: step_number.succ)
- else
- self.step_number = all_steps.maximum(:step_number).to_i + 1
- end
- end
-
- def set_default_status
- self.status = Step.statuses[:active]
- end
-
- def manual_trigger_only?
- release? && train.manual_release? && release_platform.has_review_steps?
- end
-
- def first?
- release_platform.steps.minimum(:step_number).to_i == step_number
- end
-
- def last?
- release_platform.steps.maximum(:step_number).to_i == step_number
- end
-
- def next
- release_platform.steps.where("step_number > ?", step_number)&.first
- end
-
- def previous
- release_platform.steps.where(step_number: ...step_number).last
- end
-
- def notification_params
- train.notification_params.merge(
- {
- step_type: kind.titleize,
- step_name: name
- }
- )
- end
-
- def has_production_deployment?
- deployments.any?(&:production_channel?)
- end
-
- def workflow_id
- ci_cd_channel["id"]
- end
-
- def workflow_name
- ci_cd_channel["name"]
- end
-
- def replicate(new_release_platform)
- new_step = dup
- new_step.release_platform = new_release_platform
- deployments.each { |deployment| deployment.replicate(new_step) }
- new_step.save!
- end
-
- def has_uploadables?
- deployments.any?(&:uploadable?)
- end
-
- def has_findables?
- deployments.any?(&:findable?)
- end
-
- private
-
- def reject_deployments?(attributes)
- attributes["build_artifact_channel"].blank? || !attributes["build_artifact_channel"].is_a?(Hash)
- end
-
- def unique_deployments
- duplicates =
- deployments
- .group_by { |deployment| deployment.values_at(:build_artifact_channel, :integration_id, :step_id) }
- .values
- .detect { |arr| arr.size > 1 }
-
- errors.add(:deployments, "should be designed to have unique providers and channels") if duplicates
- end
-
- def unique_store_deployments_per_train
- duplicates =
- deployments
- .filter(&:store?)
- .any? { |deployment| release_platform.deployments.exists?(build_artifact_channel: deployment.build_artifact_channel, integration: deployment.integration) }
-
- errors.add(:deployments, "cannot have repeated store configurations across steps in the same train") if duplicates
- end
-
- def auto_deployment_allowed
- errors.add(:auto_deploy, "cannot turn off auto deployment for review step") if review? && !auto_deploy?
- end
-end
diff --git a/app/models/step_run.rb b/app/models/step_run.rb
deleted file mode 100644
index cdb692934..000000000
--- a/app/models/step_run.rb
+++ /dev/null
@@ -1,519 +0,0 @@
-# == Schema Information
-#
-# Table name: step_runs
-#
-# id :uuid not null, primary key
-# approval_status :string default("pending"), not null
-# build_notes_raw :text default([]), is an Array
-# build_number :string indexed => [build_version]
-# build_version :string not null, indexed => [build_number]
-# ci_link :string
-# ci_ref :string
-# scheduled_at :datetime not null
-# sign_required :boolean default(TRUE)
-# status :string not null
-# created_at :datetime not null
-# updated_at :datetime not null
-# commit_id :uuid not null, indexed, indexed => [step_id]
-# release_platform_run_id :uuid not null, indexed
-# slack_file_id :string
-# step_id :uuid not null, indexed, indexed => [commit_id]
-#
-class StepRun < ApplicationRecord
- has_paper_trail
- include AASM
- include Passportable
-
- self.ignored_columns += ["initial_rollout_percentage"]
- self.implicit_order_column = :scheduled_at
-
- BASE_WAIT_TIME = 10.seconds
-
- belongs_to :step, inverse_of: :step_runs
- belongs_to :release_platform_run
- belongs_to :commit, inverse_of: :step_runs
- has_one :build_artifact, inverse_of: :step_run, dependent: :destroy
- has_one :external_build, inverse_of: :step_run, dependent: :destroy
- has_many :deployment_runs, -> { includes(:deployment).merge(Deployment.sequential) }, inverse_of: :step_run, dependent: :destroy
- has_many :deployments, through: :step
- has_many :running_deployments, through: :deployment_runs, source: :deployment
-
- validates :step_id, uniqueness: {scope: :commit_id}
- after_create_commit :handle_post_create_tasks
-
- STAMPABLE_REASONS = %w[
- created
- ci_triggered
- ci_retriggered
- ci_workflow_unavailable
- ci_finished
- ci_workflow_failed
- ci_workflow_halted
- build_available
- build_unavailable
- build_not_found_in_store
- build_found_in_store
- deployment_restarted
- finished
- failed_with_action_required
- ]
-
- # TODO: deprecate this
- STAMPABLE_REASONS.concat(["status_changed"])
-
- STATES = {
- on_track: "on_track",
- ci_workflow_triggered: "ci_workflow_triggered",
- ci_workflow_unavailable: "ci_workflow_unavailable",
- ci_workflow_started: "ci_workflow_started",
- ci_workflow_failed: "ci_workflow_failed",
- ci_workflow_halted: "ci_workflow_halted",
- build_ready: "build_ready",
- build_found_in_store: "build_found_in_store",
- build_not_found_in_store: "build_not_found_in_store",
- build_available: "build_available",
- build_unavailable: "build_unavailable",
- deployment_started: "deployment_started",
- deployment_failed: "deployment_failed",
- success: "success",
- cancelling: "cancelling",
- cancelled: "cancelled",
- cancelled_before_start: "cancelled_before_start",
- failed_with_action_required: "failed_with_action_required",
- deployment_restarted: "deployment_restarted"
- }
-
- END_STATES = STATES.slice(
- :ci_workflow_unavailable,
- :ci_workflow_failed,
- :ci_workflow_halted,
- :build_unavailable,
- :build_not_found_in_store,
- :deployment_failed,
- :success,
- :cancelled,
- :cancelled_before_start
- ).keys
-
- WORKFLOW_NOT_STARTED = [:on_track]
- WORKFLOW_IN_PROGRESS = [:ci_workflow_triggered, :ci_workflow_started]
- WORKFLOW_IMMUTABLE = STATES.keys - END_STATES - WORKFLOW_IN_PROGRESS - WORKFLOW_NOT_STARTED
- FAILED_STATES = %w[ci_workflow_failed ci_workflow_halted build_not_found_in_store build_unavailable deployment_failed failed_with_action_required cancelled_before_start]
-
- enum :status, STATES
-
- aasm safe_state_machine_params do
- state :on_track, initial: true
- state(*STATES.keys)
-
- event :trigger_ci, after_commit: :after_trigger_ci do
- transitions from: :on_track, to: :ci_workflow_triggered
- end
-
- event :ci_start, after_commit: -> { WorkflowProcessors::WorkflowRunJob.perform_later(id) } do
- transitions from: [:ci_workflow_triggered], to: :ci_workflow_started
- end
-
- event(:ci_unavailable, after_commit: -> { notify_on_failure!("Could not find the CI workflow!") }) do
- transitions from: [:on_track, :ci_workflow_triggered], to: :ci_workflow_unavailable
- end
-
- event(:fail_ci, after_commit: -> { notify_on_failure!("CI workflow failed!") }) do
- transitions from: :ci_workflow_started, to: :ci_workflow_failed
- end
-
- event(:cancel_ci, after_commit: -> { notify_on_failure!("CI workflow was halted!") }) do
- transitions from: :ci_workflow_started, to: :ci_workflow_halted
- end
-
- event(:retry_ci, after_commit: :after_retrigger_ci) do
- before :retry_workflow_run
- transitions from: [:ci_workflow_failed, :ci_workflow_halted], to: :ci_workflow_started
- end
-
- event(:finish_ci, after_commit: :after_finish_ci) { transitions from: :ci_workflow_started, to: :build_ready }
- event(:build_found, after_commit: :trigger_deployment) { transitions from: :build_ready, to: :build_found_in_store }
-
- event(:upload_artifact, after_commit: :after_artifact_uploaded) do
- before { add_build_artifact(artifacts_url) }
- transitions from: :build_ready, to: :build_available
- end
-
- event(:build_not_found, after_commit: -> { notify_on_failure!("Build not found in store!") }) do
- transitions from: :build_ready, to: :build_not_found_in_store
- end
- event(:build_upload_failed) { transitions from: :build_ready, to: :build_unavailable }
- event(:start_deploy) { transitions from: [:build_available, :build_found_in_store, :build_ready], to: :deployment_started }
- event(:restart_deploy, after_commit: :resume_deployments) do
- transitions from: [:failed_with_action_required], to: :deployment_restarted
- end
-
- event(:fail_deploy) do
- transitions from: [:deployment_started, :deployment_restarted], to: :deployment_failed
- end
-
- event :fail_deployment_with_sync_option, after_commit: :after_manual_submission_required do
- transitions from: [:deployment_started, :deployment_restarted], to: :failed_with_action_required
- end
-
- event(:finish, after_commit: :finalize_release) do
- transitions from: [:deployment_started, :deployment_restarted], to: :success
- end
-
- event(:cancel, after_commit: -> { Releases::CancelWorkflowRunJob.perform_later(id) }) do
- transitions from: WORKFLOW_IMMUTABLE, to: :cancelled
- transitions from: WORKFLOW_IN_PROGRESS, to: :cancelling
- transitions from: WORKFLOW_NOT_STARTED, to: :cancelled_before_start
- end
- end
-
- enum :approval_status, {pending: "pending", approved: "approved", rejected: "rejected"}, prefix: "approval"
-
- attr_accessor :current_user
- attr_accessor :artifacts_url
-
- delegate :release_platform, :release, :platform, to: :release_platform_run
- delegate :release_branch, :release_version, to: :release
- delegate :train, :store_provider, to: :release_platform
- delegate :app, :notify!, to: :train
- delegate :organization, to: :app
- delegate :commit_hash, to: :commit
- delegate :download_url, to: :build_artifact, allow_nil: true
- delegate :ci_cd_provider, :workflow_id, :workflow_name, :step_number, :build_artifact_name_pattern, :has_uploadables?, :has_findables?, :name, :app_variant, to: :step
- scope :not_failed, -> { where.not(status: FAILED_STATES) }
- scope :failed, -> { where(status: FAILED_STATES) }
- scope :sequential, -> { order("step_runs.scheduled_at ASC") }
-
- def failure?
- status.in?(FAILED_STATES) || last_deployment_run&.failure?
- end
-
- def basic_build_version
- build_version.split("-").first
- end
-
- def after_manual_submission_required
- event_stamp!(reason: :failed_with_action_required, kind: :error, data: stamp_data)
- notify_on_failure!("manual submission required!")
- end
-
- def build_size
- build_artifact&.file_size_in_mb
- end
-
- # TODO: move these explicit state checks to use a constant, perhaps END_STATES
- def active?
- release_platform_run.on_track? && !cancelled? && !success? && !status.in?(FAILED_STATES)
- end
-
- def find_build
- store_provider.find_build(build_number)
- end
-
- def get_workflow_run
- ci_cd_provider.get_workflow_run(ci_ref)
- end
-
- def get_build_artifact(artifacts_url)
- ci_cd_provider.get_artifact(artifacts_url, build_artifact_name_pattern)
- end
-
- def fetching_build?
- may_finish_ci? && build_artifact.blank?
- end
-
- def build_artifact_available?
- build_artifact.present?
- end
-
- def startable_deployment?(deployment)
- return false unless active?
- return true if deployment.first? && deployment_runs.empty?
-
- next_deployment == deployment
- end
-
- def manually_startable_deployment?(deployment)
- return false if deployment.first?
- return false if step.review?
- startable_deployment?(deployment) && (last_deployment_run&.released? || release_platform_run.patch_fix? || release.hotfix?)
- end
-
- def deployment_start_blocked?(deployment)
- release.upcoming? && deployment.production_channel? && !release_platform_run.temporary_unblock_upcoming?
- end
-
- def last_deployment_run
- deployment_runs.last
- end
-
- def last_run_for(deployment)
- deployment_runs.where(deployment: deployment).last
- end
-
- def next_deployment
- return step.deployments.first if deployment_runs.empty?
- last_deployment_run.deployment.next
- end
-
- def similar_deployment_runs_for(deployment_run)
- deployment_runs
- .where.not(id: deployment_run)
- .matching_runs_for(deployment_run.integration)
- .has_begun
- end
-
- def in_progress?
- on_track? || ci_workflow_triggered? || ci_workflow_started? || build_ready? || deployment_started? || deployment_restarted?
- end
-
- def blocked?
- ci_workflow_failed? || ci_workflow_halted? || failed_with_action_required?
- end
-
- def failed?
- build_unavailable? || ci_workflow_unavailable? || deployment_failed?
- end
-
- def done?
- success?
- end
-
- def status_summary
- {
- in_progress: in_progress?,
- done: done?,
- failed: failed?
- }
- end
-
- def first_deployment
- step.deployments.order(deployment_number: :asc).first
- end
-
- def finished_deployments?
- deployment_runs.released.size == step.deployments.size
- end
-
- def finish_deployment!(deployment)
- return finish! if finished_deployments? || deployment.next.blank?
- return if deployment.next.production_channel?
- return unless step.auto_deploy?
-
- # trigger the next deployment if available
- trigger_deployment(deployment.next)
- end
-
- def fail_deployment!(deployment)
- return if deployment.next
-
- fail_deploy!
- end
-
- def trigger_deployment(deployment = first_deployment)
- Triggers::Deployment.call(step_run: self, deployment: deployment)
- end
-
- def resume_deployments
- event_stamp!(reason: :deployment_restarted, kind: :notice, data: stamp_data)
- failed_deployment_run = deployment_runs.failed_with_action_required.sole
- failed_deployment_run.skip!
- end
-
- def notification_params
- step.notification_params
- .merge(release_platform_run.notification_params)
- .merge(
- {
- ci_link: ci_link,
- build_number: build_number,
- commit_sha: commit.short_sha,
- commit_message: commit.message,
- commit_url: commit.url,
- artifact_download_link: build_artifact&.download_url,
- build_notes: build_notes,
- manual_submission_required: status == STATES[:failed_with_action_required]
- }
- )
- end
-
- def production_release_happened?
- deployment_runs
- .not_failed
- .any?(&:production_release_happened?)
- end
-
- def production_release_submitted?
- deployment_runs
- .not_failed
- .any?(&:production_release_submitted?)
- end
-
- def relevant_changes
- previous_step_run = release_platform_run.previous_successful_run_before(self)
-
- changes_since_last_release = release.release_changelog&.commit_messages(organization.merge_only_build_notes?)
- changes_since_last_run = release_platform_run
- .commits_between(previous_step_run, self)
- .commit_messages(organization.merge_only_build_notes?)
-
- return changes_since_last_run if previous_step_run.present?
-
- (changes_since_last_run || []) + (changes_since_last_release || [])
- end
-
- def build_notes
- build_notes_raw
- .map { |str| str&.strip }
- .flat_map { |line| train.compact_build_notes? ? line.split("\n").first : line.split("\n") }
- .map { |line| line.gsub(/\p{Emoji_Presentation}\s*/, "") }
- .map { |line| line.gsub('"', "\\\"") }
- .reject { |line| line =~ /\AMerge|\ACo-authored-by|\A---------/ }
- .compact_blank
- .uniq
- .map { |str| "ā¢ #{str}" }
- .join("\n").presence || "Nothing new"
- end
-
- def cancel_ci_workflow!
- ci_cd_provider.cancel_workflow_run!(ci_ref)
- end
-
- def workflow_found?
- ci_ref.present?
- end
-
- def find_and_update_workflow_run
- return if workflow_found?
- find_workflow_run.then { |wr| update_ci_metadata!(wr) }
- end
-
- def trigger_ci_worfklow_run!
- trigger_workflow_run
- trigger_ci!
- end
-
- def release_info
- slice(:build_version, :build_number, :updated_at, :platform)
- end
-
- def sync_store_status!
- return unless failed_with_action_required?
- restart_deploy! if store_provider.build_present_in_public_track?(build_number)
- end
-
- def build_display_name
- "#{build_version} (#{build_number})"
- end
-
- private
-
- def handle_post_create_tasks
- populate_build_notes
- # FIXME: solve this correctly, we rely on wait time to ensure steps are triggered in correct order
- Releases::TriggerWorkflowRunJob.set(wait: BASE_WAIT_TIME * step_number).perform_later(id)
- create_stamp!(data: stamp_data)
- end
-
- def populate_build_notes
- return if build_notes_raw.present?
- update(build_notes_raw: relevant_changes)
- end
-
- def previous_step_run
- release_platform_run
- .step_runs_for(step)
- .where(scheduled_at: ...scheduled_at)
- .where.not(id: id)
- .order(:scheduled_at)
- .last
- end
-
- def find_workflow_run
- ci_cd_provider.find_workflow_run(workflow_id, release_branch, commit_hash)
- end
-
- def update_ci_metadata!(workflow_run)
- return if workflow_run.try(:[], :ci_ref).blank?
- update!(ci_ref: workflow_run[:ci_ref], ci_link: workflow_run[:ci_link])
- end
-
- def trigger_workflow_run(retrigger: false)
- update_build_number! unless retrigger
-
- deploy_action_enabled = organization.deploy_action_enabled? || app.deploy_action_enabled? || train.deploy_action_enabled?
-
- ci_cd_provider
- .trigger_workflow_run!(workflow_id, release_branch, workflow_inputs, commit_hash, deploy_action_enabled)
- .then { |wr| update_ci_metadata!(wr) }
- end
-
- def retry_workflow_run
- return ci_cd_provider.retry_workflow_run!(ci_ref) if ci_cd_provider.workflow_retriable?
- trigger_workflow_run(retrigger: true)
- end
-
- def update_build_number!
- update!(build_number: app.bump_build_number!)
- end
-
- def workflow_inputs
- data = {version_code: build_number, build_version: build_version}
- data[:build_notes] = build_notes if organization.build_notes_in_workflow?
- data
- end
-
- def add_build_artifact(url)
- return if build_artifact.present?
-
- # FIXME: this should be passed along from the CI workflow metadata
- generated_at = Time.current
-
- get_build_artifact(url).with_open do |artifact_stream|
- build_build_artifact(generated_at: generated_at).save_file!(artifact_stream)
- artifact_stream.file.rewind
- self.slack_file_id = train.upload_file_for_notifications!(artifact_stream.file, build_artifact.get_filename)
- end
- end
-
- def stamp_data
- {
- name: step.name,
- sha: commit.short_sha,
- workflow_name:,
- version: build_version
- }
- end
-
- def notify_on_failure!(message)
- notify!(message, :step_failed, notification_params.merge(step_fail_reason: message))
- end
-
- def after_trigger_ci
- Releases::FindWorkflowRun.perform_async(id)
- event_stamp!(reason: :ci_triggered, kind: :notice, data: stamp_data)
- notify!("Step has been triggered!", :step_started, notification_params)
- Releases::CancelStepRun.perform_later(previous_step_run.id) if previous_step_run&.may_cancel?
- end
-
- def after_retrigger_ci
- WorkflowProcessors::WorkflowRunJob.perform_later(id)
- event_stamp!(reason: :ci_retriggered, kind: :notice, data: stamp_data)
- end
-
- def after_artifact_uploaded
- notify!("A new build is available!", :build_available, notification_params, slack_file_id, build_display_name) if slack_file_id
- trigger_deployment
- end
-
- def after_finish_ci
- return Releases::FindBuildJob.perform_async(id) if has_findables?
- return Releases::UploadArtifact.perform_async(id, artifacts_url) if has_uploadables?
- trigger_deployment
- end
-
- def finalize_release
- event_stamp!(reason: :finished, kind: :success, data: stamp_data)
- Coordinators::FinishPlatformRun.call(release_platform_run) if release_platform_run.finalizable?
- end
-end
diff --git a/app/models/train.rb b/app/models/train.rb
index 232265e7a..7afe96c22 100644
--- a/app/models/train.rb
+++ b/app/models/train.rb
@@ -13,7 +13,6 @@
# description :string
# freeze_version :boolean default(FALSE)
# kickoff_at :datetime
-# manual_release :boolean default(FALSE)
# name :string not null
# notification_channel :jsonb
# patch_version_bump_only :boolean default(FALSE), not null
@@ -42,10 +41,11 @@ class Train < ApplicationRecord
using RefinedString
extend FriendlyId
include Rails.application.routes.url_helpers
- include Notifiable
include Versionable
include Loggable
+ self.ignored_columns += ["manual_release"]
+
BRANCHING_STRATEGIES = {
almost_trunk: "Almost Trunk",
release_backmerge: "Release with Backmerge",
@@ -55,13 +55,9 @@ class Train < ApplicationRecord
belongs_to :app
has_many :releases, -> { sequential }, inverse_of: :train, dependent: :destroy
has_many :active_runs, -> { pending_release.includes(:all_commits) }, class_name: "Release", inverse_of: :train, dependent: :destroy
- has_many :deployment_runs, through: :releases
- has_many :external_releases, through: :deployment_runs
has_many :release_platforms, -> { sequential }, dependent: :destroy, inverse_of: :train
has_many :release_platform_runs, -> { sequential }, through: :releases
has_many :integrations, through: :app
- has_many :steps, through: :release_platforms
- has_many :deployments, through: :steps
has_many :scheduled_releases, dependent: :destroy
has_many :notification_settings, inverse_of: :train, dependent: :destroy
has_one :release_index, dependent: :destroy
@@ -95,7 +91,6 @@ class Train < ApplicationRecord
validate :build_queue_config
validate :backmerge_config
validate :tag_release_config
- validate :valid_train_configuration, on: :activate_context
validate :working_branch_presence, on: :create
validate :ci_cd_workflows_presence, on: :create
validates :name, format: {with: /\A[a-zA-Z0-9\s_\/-]+\z/, message: :invalid}
@@ -146,6 +141,7 @@ def one_percent_beta_release?
Flipper.enabled?(:one_percent_beta_release, self)
end
+ # TODO: remove this after full removal of v2, it is used only for one-off rake tasks
def product_v2?
Flipper.enabled?(:product_v2, self)
end
@@ -323,13 +319,6 @@ def cancel_scheduled_releases!
scheduled_releases.pending&.delete_all
end
- # TODO [V2]: Remove this method
- def startable?
- return false unless app.ready?
- return true if product_v2?
- release_platforms.all?(&:startable?)
- end
-
def activatable?
automatic? && startable? && !active?
end
@@ -338,23 +327,11 @@ def deactivatable?
automatic? && active? && active_runs.none?
end
- def manually_startable?
- startable? && !inactive?
- end
-
def upcoming_release_startable?
- if product_v2?
- manually_startable? &&
- ongoing_release.present? &&
- ongoing_release.production_release_started? &&
- upcoming_release.blank?
- else
- manually_startable? &&
- release_platforms.any?(&:has_production_deployment?) &&
- ongoing_release.present? &&
- (release_platforms.all?(&:has_review_steps?) || ongoing_release.production_release_happened?) &&
- upcoming_release.blank?
- end
+ !inactive? &&
+ ongoing_release.present? &&
+ ongoing_release.production_release_started? &&
+ upcoming_release.blank?
end
def continuous_backmerge?
@@ -365,10 +342,6 @@ def branching_strategy_name
BRANCHING_STRATEGIES[branching_strategy.to_sym]
end
- def build_channel_integrations
- integrations.build_channel
- end
-
def active_release_for?(branch_name)
active_runs.exists?(branch_name: branch_name)
end
@@ -430,8 +403,7 @@ def notification_params
train_current_version: version_current,
train_next_version: next_version,
train_url: train_link,
- working_branch:,
- is_v2: product_v2?
+ working_branch:
}
)
end
@@ -445,36 +417,25 @@ def schedule_editable?
end
def hotfixable?
- return false unless startable?
return false unless has_production_deployment?
return false if hotfix_release.present?
return false if hotfix_from.blank?
return true if ongoing_release.blank?
- if product_v2?
- !ongoing_release.production_release_active?
- else
- !ongoing_release.production_release_happened?
- end
+ !ongoing_release.production_release_active?
end
def devops_report
- return Queries::DevopsReport.new(self) if product_v2?
- Charts::DevopsReport.new(self)
+ Queries::DevopsReport.new(self)
end
def has_production_deployment?
- if product_v2?
- release_platforms.any? { |rp| rp.platform_config.production_release? }
- else
- release_platforms.any?(&:has_production_deployment?)
- end
+ release_platforms.any? { |rp| rp.platform_config.production_release? }
end
def has_restricted_public_channels?
return false if app.ios?
-
- deployments.any? { |d| GooglePlayStoreIntegration::PUBLIC_CHANNELS.include?(d.deployment_channel) }
+ release_platforms.any(&:has_restricted_public_channels?)
end
def stop_failed_ongoing_release!
@@ -563,12 +524,6 @@ def ensure_deletable
errors.add(:trains, "cannot delete a train if there are releases made from it!") if releases.present?
end
- def valid_train_configuration
- unless release_platforms.all?(&:valid_steps?)
- errors.add(:train, "there should be one release step for all platforms")
- end
- end
-
def valid_schedule
if kickoff_at.present? || repeat_duration.present?
errors.add(:repeat_duration, "invalid schedule, provide both kickoff and period for repeat") unless kickoff_at.present? && repeat_duration.present?
diff --git a/app/presenters/devops_report_presenter.rb b/app/presenters/devops_report_presenter.rb
index cad703e50..0b39640e3 100644
--- a/app/presenters/devops_report_presenter.rb
+++ b/app/presenters/devops_report_presenter.rb
@@ -70,32 +70,26 @@ class DevopsReportPresenter < SimpleDelegator
}
def duration
- return v1_formatter(:mobile_devops, :duration) if v1?
formatter(:duration)
end
def frequency
- return v1_formatter(:mobile_devops, :frequency) if v1?
formatter(:frequency)
end
def time_in_review
- return v1_formatter(:mobile_devops, :time_in_review) if v1?
formatter(:time_in_review)
end
def patch_fixes
- return v1_formatter(:mobile_devops, :hotfixes) if v1?
formatter(:patch_fixes)
end
def hotfixes
- return nil if v1?
formatter(:hotfixes)
end
def time_in_phases
- return v1_formatter(:mobile_devops, :time_in_phases) if v1?
chart_data = formatter(:time_in_phases)
# The data is in the following format:
# {
@@ -113,7 +107,6 @@ def time_in_phases
end
def reldex_scores
- return v1_formatter(:mobile_devops, :reldex_scores) if v1?
formatter(:reldex_scores, {
y_annotations: [
{y: 0..train.release_index.tolerable_range.min, text: "Mediocre", color: "mediocre"},
@@ -123,24 +116,20 @@ def reldex_scores
end
def stability_contributors
- return v1_formatter(:operational_efficiency, :stability_contributors) if v1?
formatter(:stability_contributors)
end
def contributors
- return v1_formatter(:operational_efficiency, :contributors) if v1?
formatter(:contributors)
end
def team_stability_contributors
- return v1_formatter(:operational_efficiency, :team_stability_contributors) if v1?
formatter(:team_stability_contributors, {
colors: team_colors
})
end
def team_contributors
- return v1_formatter(:operational_efficiency, :team_contributors) if v1?
formatter(:team_contributors, {
colors: team_colors
})
@@ -150,18 +139,10 @@ def team_colors
@team_colors ||= organization.team_colors
end
- def v1_formatter(parent, key)
- all[parent][key]
- end
-
def formatter(key, params = {})
return if all.blank?
FORMATTING_DATA[key].merge(data: all[key]).merge(params)
end
- def v1?
- !train.product_v2?
- end
-
delegate :present?, to: :all
end
diff --git a/app/presenters/release_presenter.rb b/app/presenters/release_presenter.rb
index ebfbbb009..33d2b374f 100644
--- a/app/presenters/release_presenter.rb
+++ b/app/presenters/release_presenter.rb
@@ -45,8 +45,7 @@ def release_status
end
memoize def breakdown
- return Queries::ReleaseBreakdown.new(id) if is_v2?
- Queries::ReleaseSummary.all(id)
+ Queries::ReleaseBreakdown.new(id)
end
memoize def platform_runs
@@ -57,12 +56,7 @@ def release_status
release_version
end
- def reldex
- return breakdown.reldex if is_v2?
- breakdown&.fetch(:reldex, nil)
- end
-
- delegate :team_release_commits, :team_stability_commits, to: :breakdown
+ delegate :team_release_commits, :team_stability_commits, :reldex, to: :breakdown
def hotfix_badge
if hotfix?
diff --git a/app/views/app_configs/notification.html+turbo_frame.erb b/app/views/app_configs/notification.html+turbo_frame.erb
deleted file mode 100644
index e145187d2..000000000
--- a/app/views/app_configs/notification.html+turbo_frame.erb
+++ /dev/null
@@ -1,21 +0,0 @@
-<%= render V2::EnhancedTurboFrameComponent.new("#{@integration_category}_config") do %>
- <% if @app.notifications_set_up? %>
- <%= render V2::FormComponent.new(model: [@app, @config], url: app_app_config_path(@app), method: :patch) do |f| %>
- <% f.with_section(heading: "Select Channel") do |section| %>
- <% section.with_description do %>
- This will be your base notification channel. Later, when you create trains, you can add more granularity.
- <% end %>
-
- <%= render partial: "shared/notifications_form",
- locals: { form: f.F,
- app: @app,
- channels: @notification_channels,
- current: @config.notification_channel } %>
- <% end %>
-
- <% f.with_action do %>
- <%= f.F.authz_submit "Update", "plus.svg", size: :xs %>
- <% end %>
- <% end %>
- <% end %>
-<% end %>
diff --git a/app/views/apps/_setup_progress.html.erb b/app/views/apps/_setup_progress.html.erb
index fb2d3c2b2..3d0af36a7 100644
--- a/app/views/apps/_setup_progress.html.erb
+++ b/app/views/apps/_setup_progress.html.erb
@@ -9,7 +9,7 @@
-
- <%= commits_count %> commit(s) in the queue.
- These will be applied in <%= time_in_words(build_queue.scheduled_at) %> or after <%= build_queue.build_queue_size %> commits.
-
-
- <% if commits.present? %>
-
- <% if release.committable? %>
- <%= authz_button_to :blue, "Apply commits", apply_release_build_queue_path(release, build_queue), class: "btn-xs", data: { turbo_confirm: "This will trigger steps for the HEAD of the queue, are you sure?" } %>
- <% else %>
- <%= authz_button_to :disabled, "Apply commits", apply_release_build_queue_path(release, build_queue), class: "btn-xs" %>
- <% end %>
-
- <% end %>
-
- <% if commits.present? %>
-
-
-
-
-
commits
-
-
-
-
- <% commits.each_with_index do |commit, index| %>
-
- <% else %>
- <% if release.unmerged_commits.exists? %>
-
-
Unmerged Commits
-
-
- You have <%= release.unmerged_commits.size %> commit(s) that were not automatically merged by Tramline.
- You can see the current diff between the branches <%= link_to_external "here", release.compare_url, class: "underline" %>
- .
- Please ensure that those changes have been manually merged back on <%= release.train.working_branch %> before completing the finalize phase.
-
- <% end %>
-
-
- <% if release.manually_startable_step?(step) %>
- <%= authz_button_to :blue,
- "Move to this step",
- start_release_step_run_path(release, step),
- { class: "btn-xs mb-2" } %>
- <% end %>
-
- <% if release.step_start_blocked?(step) %>
-
- <%= authz_button_to :disabled,
- "Move to this step",
- start_release_step_run_path(release, step),
- { class: "btn-xs mb-1" } %>
-
- You cannot start this release step until the <%= blocked_step_release_link(release.release) %> is
- finished.
-
-
- <% end %>
-
- <% if step_run&.may_retry_ci? && release.on_track? %>
- <%= authz_button_to :blue,
- "Retry CI Workflow",
- retry_ci_workflow_release_step_run_path(release, step_run),
- method: :patch,
- data: { turbo_method: :patch,
- turbo_confirm: "This will re-run the CI workflow. Are you sure?" },
- class: "btn-xs mb-2" %>
- <% end %>
-
- <% if step_run&.failed_with_action_required? && release.on_track? %>
-
- <%= render "shared/note_box", type: :error, message: "Due to a previous rejection, new changes cannot be submitted to the store from Tramline. Please submit the current build (#{step_run.build_number}) for review manually from the Google Play Console by creating a release in a public track (eg. Closed testing, Open testing). Once that is done, you can sync the store status with Tramline and move forward with the release train." %>
- <%= authz_button_to :blue,
- "Sync store status",
- sync_store_status_release_step_run_path(release, step_run),
- method: :patch,
- data: { turbo_method: :patch,
- turbo_confirm: "Please ensure that you have manually submitted the build for review by creating a release in at least one public (alpha, beta, production) channel. Continue?" },
- class: "btn-xs mb-2" %>
-
diff --git a/app/views/steps/_form.html.erb b/app/views/steps/_form.html.erb
deleted file mode 100644
index 30c9205cf..000000000
--- a/app/views/steps/_form.html.erb
+++ /dev/null
@@ -1,65 +0,0 @@
-<% form.with_section(heading: "What should we call it?") do |section| %>
- <% section.with_description do %>
- A name that defines this part of the release process.
- <% end %>
-
-
- This is appended to the version name of the app, as follows:
-
-
-
- <% end %>
-
-<% end %>
-
-<% form.with_section(heading: "How do we build your app?") do |section| %>
- <% section.with_description do %>
- The generated build artifact will be moved across the configured <%= Deployment.display.downcase %> channels below.
- <% end %>
-
-
- This CI workflow should generate a valid build artifact (aab/apk/ipa).
-
-
-
-
- <%= section.F.labeled_text_field :build_artifact_name_pattern, "Build Artifact Name (Optional)" %>
-
-
- If your CI workflow generates multiple artifacts, provide a name to choose the correct build artifact
- (aab/apk/ipa) among the files generated.
-
- When left blank, Tramline will choose the largest file generated as the build artifact.
-
-
- To understand more about build artifact
- selection, <%= link_to_external "check out the docs.", "https://docs.tramline.app/integrations/ci-cd/#build-artifact-selection", class: "underline" %>
-
-
-
-
- <% if @app.variants.exists? %>
-
<%= section.F.labeled_select :app_variant_id, "Pick an app variant", options_for_select(@app.variant_options, step.app_variant&.id) %>
- <% end %>
-
-<% end %>
diff --git a/app/views/steps/_new_deployments.html.erb b/app/views/steps/_new_deployments.html.erb
deleted file mode 100644
index 87962fdeb..000000000
--- a/app/views/steps/_new_deployments.html.erb
+++ /dev/null
@@ -1,83 +0,0 @@
-<% form.with_section(heading: "How should we distribute?") do |section| %>
- <% section.with_description do %>
- <%= "#{Deployment.display.downcase.pluralize.titleize} are run in the specified order, you can drag them around to change the order." %>
-
Refresh your channels, if you can't find them in the list.
- <% if app.slack_build_channel_provider.present? %>
-
<%= section.F.labeled_text_field :release_branch, "Release Branch",
placeholder: "eg., production",
@@ -191,7 +190,7 @@
<%= f.with_advanced_section(heading: "Continuous Backmerge",
html_options: { hidden: true,
- data: { branching_selector_target: "backmerge" } }) do |section| %>
+ data: { domain__branching_selector_target: "backmerge" } }) do |section| %>
<% section.with_description do %>
Enable backmerging of release branch commits into the working branch as they arrive.
By default, merge happens at the end of the release.
@@ -204,20 +203,6 @@
html_options: { disabled: train.backmerge_disabled? }) %>
<% end %>
- <% if @train.persisted? && !@train.product_v2? %>
- <%= f.with_advanced_section(heading: "Manual Release Trigger") do |section| %>
- <% section.with_description do %>
- Always require a manual trigger for the release step of the train.
- By default, the release step will be auto-triggered if it has run before in the release.
- <% end %>
-
- <%= render V2::Form::SwitchComponent.new(form: section.F,
- field_name: :manual_release,
- on_label: "Manual Release enabled",
- off_label: "Manual Release disabled") %>
- <% end %>
- <% end %>
-
<%= f.with_advanced_section(heading: "Compact Build Notes") do |section| %>
<% section.with_description do %>
Compact the build notes by picking only the first line of commit messages.
diff --git a/app/views/trains/_new_step_button.html.erb b/app/views/trains/_new_step_button.html.erb
deleted file mode 100644
index 65fdd2a5a..000000000
--- a/app/views/trains/_new_step_button.html.erb
+++ /dev/null
@@ -1,15 +0,0 @@
-<% if train.active_runs.exists? %>
-