Skip to content

Commit c8b5a3e

Browse files
authored
Allow VCS integrations to be disconnected (#747)
1 parent 86e3611 commit c8b5a3e

24 files changed

+139
-85
lines changed

.dockerignore

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
# Ignore all logfiles and tempfiles.
2020
/log/*
2121
/tmp/*
22+
!/tmp/caching-dev.txt
2223
!/log/.keep
2324
!/tmp/.keep
2425

app/components/integration_card_component.html.erb

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
<% if connected? %>
2121
<div class="flex justify-between w-full items-baseline">
2222
<%= render BadgeComponent.new(text: "Connected", status: :success) %>
23-
<% if ci_cd? %>
23+
<% if disconnectable_categories? %>
2424
<%= render ButtonComponent.new(
2525
scheme: :danger,
2626
options: app_integration_path(@app, integration),

app/components/integration_card_component.rb

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ def initialize(app, integration, category)
1717
end
1818

1919
attr_reader :integration
20-
delegate :connected?, :disconnected?, :providable, :connection_data, :providable_type, :ci_cd?, to: :integration, allow_nil: true
20+
delegate :connected?, :disconnected?, :providable, :connection_data, :providable_type, :disconnectable_categories?, to: :integration, allow_nil: true
2121
alias_method :provider, :providable
2222
delegate :creatable?, :connectable?, to: :provider
2323

@@ -70,6 +70,6 @@ def reusable_integrations_form_partial(existing_integrations)
7070
end
7171

7272
def disconnectable?
73-
integration.disconnectable? && ci_cd?
73+
integration.disconnectable? && disconnectable_categories?
7474
end
7575
end

app/components/live_release/finalize_component.rb

+2-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,8 @@ def subtitle
7979
end
8080

8181
def tag_link
82-
link = release.tag_url || release.app.config.code_repo_url
82+
link = release.tag_url || release.app.config&.code_repo_url
83+
return NOT_AVAILABLE if link.blank?
8384
link_to_external train.vcs_provider.display, link, class: "underline"
8485
end
8586
end

app/components/release_list_component.html.erb

+4-10
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<% if train.hotfixable? %>
33
<% container.with_action do %>
44
<%= render ModalComponent.new(title: "Start a hotfix release",
5-
subtitle: "This will be against your last successful release #{hotfix_from.release_version}") do |modal| %>
5+
subtitle: "This will be against your last successful release #{hotfix_from.release_version}") do |modal| %>
66
<% button = modal.with_button(scheme: :light,
77
type: :action,
88
size: :xxs,
@@ -62,6 +62,7 @@
6262
label: "Configure",
6363
authz: false,
6464
options: edit_app_train_path(app, train),
65+
disabled: !app.ready?,
6566
type: :link) do |b|
6667
b.with_icon("cog.svg")
6768
end %>
@@ -111,15 +112,8 @@
111112
<% end %>
112113

113114
<% container.with_body do %>
114-
<% unless app.ready? %>
115-
<%= render AlertComponent.new(kind: :banner,
116-
type: :notice,
117-
title: "App is not ready",
118-
info: { label: "Configure", link: app_integrations_path(app) },
119-
full_screen: false) do %>
120-
Please finish configuring the required integrations before you can start creating releases.
121-
<% end %>
122-
<% end %>
115+
<%= render partial: "shared/app_not_ready", locals: { app: } %>
116+
123117
<% if train.automatic? %>
124118
<%= render ScheduledTrainComponent.new(train) %>
125119
<% end %>

app/components/release_list_component.rb

+8
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,14 @@ def ios_enabled?
123123
end
124124

125125
def no_release_empty_state
126+
unless app.ready?
127+
return {
128+
title: "App is not ready",
129+
text: "There are required integrations that need to be configured before you can start creating releases.",
130+
content: render(ButtonComponent.new(scheme: :light, type: :link, label: "Configure Integration", options: app_integrations_path(app), size: :xxs, authz: false))
131+
}
132+
end
133+
126134
if train.automatic?
127135
if train.activatable?
128136
{

app/controllers/config/release_platforms_controller.rb

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ class Config::ReleasePlatformsController < SignedInApplicationController
88
before_action :set_release_platform, only: %i[edit update]
99
before_action :set_config, only: %i[edit update]
1010
before_action :set_train_config_tabs, only: %i[edit update]
11+
before_action :ensure_app_ready, only: %i[edit update]
1112
before_action :set_ci_actions, only: %i[edit update]
1213
before_action :set_submission_types, only: %i[edit update]
1314
around_action :set_time_zone
@@ -43,7 +44,7 @@ def set_config
4344
end
4445

4546
def set_ci_actions
46-
@ci_actions = @app.config.ci_cd_workflows
47+
@ci_actions = @train.workflows || []
4748
end
4849

4950
def set_submission_types

app/controllers/notification_settings_controller.rb

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ class NotificationSettingsController < SignedInApplicationController
55

66
before_action :require_write_access!, only: %i[update]
77
before_action :set_train, only: %i[index update edit]
8+
before_action :ensure_app_ready, only: %i[index edit update]
89
before_action :set_notification_setting, only: %i[update edit]
910
around_action :set_time_zone
1011

app/controllers/release_health_rules_controller.rb

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ class ReleaseHealthRulesController < SignedInApplicationController
22
before_action :require_write_access!, only: %i[create destroy]
33
before_action :set_train, only: %i[create destroy]
44
before_action :set_release_platform, only: %i[create destroy]
5+
before_action :ensure_app_ready, only: %i[create destroy]
56

67
def create
78
@rule = @release_platform.release_health_rules.new(rule_params)

app/controllers/release_indices_controller.rb

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ class ReleaseIndicesController < SignedInApplicationController
55
before_action :set_train, only: %i[edit update]
66
before_action :set_app_from_train, only: %i[edit update]
77
before_action :set_train_config_tabs, only: %i[edit update]
8+
before_action :ensure_app_ready, only: %i[edit update]
89

910
def edit
1011
@release_index = @train.release_index

app/controllers/signed_in_application_controller.rb

+8-2
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ class SignedInApplicationController < ApplicationController
3939

4040
private
4141

42+
def ensure_app_ready
43+
unless @app.ready?
44+
redirect_to app_path(@app), flash: {error: "App is not ready (not fully configured)."}
45+
end
46+
end
47+
4248
def set_page_name
4349
user_facing_actions = %w[new index show edit]
4450

@@ -185,11 +191,11 @@ def new_app
185191
end
186192

187193
def vcs_provider_logo
188-
@vcs_provider_logo ||= "integrations/logo_#{@app.vcs_provider}.png"
194+
@vcs_provider_logo ||= "integrations/logo_#{@app.vcs_provider.presence || "deprecated"}.png"
189195
end
190196

191197
def ci_cd_provider_logo
192-
@ci_cd_provider_logo ||= "integrations/logo_#{@app.ci_cd_provider}.png"
198+
@ci_cd_provider_logo ||= "integrations/logo_#{@app.ci_cd_provider || "deprecated"}.png"
193199
end
194200

195201
def teams_supported?

app/controllers/trains_controller.rb

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ class TrainsController < SignedInApplicationController
77
before_action :require_write_access!, only: %i[new create update destroy activate deactivate]
88
before_action :set_train, only: %i[edit update destroy activate deactivate rules]
99
before_action :set_train_config_tabs, only: %i[edit update rules destroy activate deactivate]
10+
before_action :ensure_app_ready, only: %i[new create edit update activate deactivate rules]
1011
before_action :validate_integration_status, only: %i[new create]
1112
before_action :set_notification_channels, only: %i[new create edit update]
1213
around_action :set_time_zone

app/models/app_config.rb

+14-13
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,10 @@
66
# bitbucket_workspace :string
77
# bugsnag_android_config :jsonb
88
# bugsnag_ios_config :jsonb
9-
# ci_cd_workflows :jsonb
109
# code_repository :json
1110
# firebase_android_config :jsonb
1211
# firebase_ios_config :jsonb
1312
# jira_config :jsonb not null
14-
# notification_channel :json
1513
# created_at :datetime not null
1614
# updated_at :datetime not null
1715
# app_id :uuid not null, indexed
@@ -22,7 +20,7 @@ class AppConfig < ApplicationRecord
2220
include AppConfigurable
2321

2422
PLATFORM_AWARE_CONFIG_SCHEMA = Rails.root.join("config/schema/platform_aware_integration_config.json")
25-
self.ignored_columns += %w[bugsnag_project_id firebase_crashlytics_android_config firebase_crashlytics_ios_config notification_channel]
23+
self.ignored_columns += %w[bugsnag_project_id firebase_crashlytics_android_config firebase_crashlytics_ios_config notification_channel ci_cd_workflows]
2624

2725
belongs_to :app
2826
has_many :variants, class_name: "AppVariant", dependent: :destroy
@@ -47,28 +45,27 @@ def ready?
4745
end
4846

4947
def code_repository_name
50-
return if code_repository.blank?
51-
code_repository["full_name"]
48+
code_repository&.fetch("full_name", nil)
5249
end
5350

5451
def code_repo_url
55-
code_repository["repo_url"]
52+
code_repository&.fetch("repo_url", nil)
5653
end
5754

5855
def code_repo_namespace
59-
code_repository["namespace"]
56+
code_repository&.fetch("namespace", nil)
6057
end
6158

6259
def code_repo_name_only
63-
code_repository["name"]
60+
code_repository&.fetch("name", nil)
6461
end
6562

6663
def bitrise_project
6764
bitrise_project_id&.fetch("id", nil)
6865
end
6966

7067
def further_setup_by_category?
71-
integrations = app.integrations
68+
integrations = app.integrations.connected
7269
categories = {}.with_indifferent_access
7370

7471
if integrations.version_control.present?
@@ -131,10 +128,14 @@ def ci_cd_workflows
131128
super&.map(&:with_indifferent_access)
132129
end
133130

134-
def set_ci_cd_workflows(workflows)
135-
return if code_repository.nil?
136-
return if app.ci_cd_provider.blank?
137-
update(ci_cd_workflows: workflows)
131+
def disconnect!(integration)
132+
if integration.version_control?
133+
self.code_repository = nil
134+
elsif integration.ci_cd?
135+
self.bitrise_project_id = nil
136+
end
137+
138+
save!
138139
end
139140

140141
private

app/models/app_variant.rb

+4
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ def display_text
3333

3434
def config = self
3535

36+
def disconnect!(_)
37+
raise NotImplementedError
38+
end
39+
3640
private
3741

3842
def duplicate_bundle_identifier

app/models/google_play_store_integration.rb

+19-9
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ class GooglePlayStoreIntegration < ApplicationRecord
1616
include Displayable
1717
include Loggable
1818

19+
delegate :cache, to: Rails
1920
delegate :integrable, to: :integration, allow_nil: true
2021
delegate :refresh_external_app, :bundle_identifier, to: :integrable, allow_nil: true
2122

@@ -26,6 +27,7 @@ class GooglePlayStoreIntegration < ApplicationRecord
2627
after_create_commit :draft_check
2728
after_create_commit :refresh_external_app
2829

30+
CACHE_EXPIRY = 1.month
2931
LOCK_ACQUISITION_FAILURE_REASON = :lock_acquisition_failed
3032
LOCK_ACQUISITION_FAILURE_MSG = "Failed to acquire an internal lock to make Play Store calls"
3133
LOCK_NAME_PREFIX = "google_play_store_edit_"
@@ -150,15 +152,6 @@ def connection_data
150152
"Bundle Identifier: #{bundle_identifier}"
151153
end
152154

153-
def channels
154-
default_channels = CHANNELS.map(&:with_indifferent_access)
155-
channel_data.each do |chan|
156-
next if default_channels.pluck(:id).map(&:to_s).include?(chan[:name])
157-
default_channels << {id: chan[:name], name: "Closed testing - #{chan[:name]}", is_production: false}.with_indifferent_access
158-
end
159-
default_channels
160-
end
161-
162155
def pick_default_beta_channel
163156
BETA_CHANNEL
164157
end
@@ -229,6 +222,8 @@ def build_in_progress?(channel, build_number)
229222
response.present? && GooglePlayStoreIntegration::IN_PROGRESS_STORE_STATUS.include?(response[:status])
230223
end
231224

225+
private
226+
232227
def project_id
233228
JSON.parse(json_key)["project_id"]&.split("-")&.third
234229
end
@@ -276,4 +271,19 @@ def api_lock_params(priority: :high)
276271
raise ArgumentError, "Invalid priority: #{priority}"
277272
end
278273
end
274+
275+
def channels
276+
default_channels = CHANNELS.map(&:with_indifferent_access)
277+
cache.fetch(tracks_cache_key, expires_in: CACHE_EXPIRY) do
278+
channel_data&.each do |chan|
279+
next if default_channels.pluck(:id).map(&:to_s).include?(chan[:name])
280+
default_channels << {id: chan[:name], name: "Closed testing - #{chan[:name]}", is_production: false}.with_indifferent_access
281+
end
282+
end
283+
default_channels
284+
end
285+
286+
def tracks_cache_key
287+
"google_play_store_integration/#{id}/tracks"
288+
end
279289
end

app/models/integration.rb

+12-1
Original file line numberDiff line numberDiff line change
@@ -229,9 +229,20 @@ def disconnectable?
229229
app.active_runs.none?
230230
end
231231

232+
def disconnectable_categories?
233+
ci_cd? || version_control?
234+
end
235+
232236
def disconnect
233237
return unless disconnectable?
234-
update(status: :disconnected, discarded_at: Time.current)
238+
transaction do
239+
integrable.config.disconnect!(self)
240+
update!(status: :disconnected, discarded_at: Time.current)
241+
true
242+
end
243+
rescue ActiveRecord::RecordInvalid => e
244+
errors.add(:base, e.message)
245+
false
235246
end
236247

237248
def set_metadata!

app/models/release_platform.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ def set_default_config
109109
return if Rails.env.test?
110110
return if platform_config.present?
111111

112-
rc_ci_cd_channel = app.config.ci_cd_workflows.first
112+
rc_ci_cd_channel = train.workflows.first || {}
113113
base_config_map = {
114114
release_platform: self,
115115
workflows: {

app/models/train.rb

+4-5
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ class Train < ApplicationRecord
106106
after_initialize :set_backmerge_config, if: :persisted?
107107
after_initialize :set_notifications_config, if: :persisted?
108108
before_validation :set_version_seeded_with, if: :new_record?
109-
before_create :set_ci_cd_workflows
109+
before_create :fetch_ci_cd_workflows
110110
before_create :set_current_version
111111
before_create :set_default_status
112112
after_create :create_release_platforms
@@ -157,7 +157,7 @@ def temporarily_allow_workflow_errors?
157157
end
158158

159159
def workflows
160-
@workflows ||= ci_cd_provider.workflows(working_branch)
160+
@workflows ||= ci_cd_provider&.workflows(working_branch)
161161
end
162162

163163
def version_ahead?(release)
@@ -454,9 +454,8 @@ def stop_failed_ongoing_release!
454454
ongoing_release.stop!
455455
end
456456

457-
def set_ci_cd_workflows
458-
config.set_ci_cd_workflows(workflows)
459-
end
457+
# call workflows to memoize and cache the result
458+
def fetch_ci_cd_workflows = workflows
460459

461460
def last_finished_release
462461
releases.where(status: "finished").reorder(completed_at: :desc).first

app/views/config/release_platforms/_form.html.erb

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
<%# locals: (app:, train:, platform:, platform_config:, platform_name:, platform_label:, ci_actions:, submission_types:) %>
22

3+
<% if @edit_allowed %>
4+
<%= render AlertComponent.new(kind: :banner, type: :notice, title: "Updates will be applied on a new release", full_screen: false) do %>
5+
The submissions settings can be edited while there are active releases. But they will only be applied on the next release.
6+
<% end %>
7+
<% end %>
8+
39
<%= render FormComponent.new(model: [app, train, platform, platform_config],
410
url: app_train_platform_submission_config_path(app, train, platform_name)) do |f| %>
511
<% f.with_section(heading: "Configure RC (Release Candidate) Workflow") do |section| %>
@@ -183,6 +189,6 @@
183189
<% end %>
184190

185191
<% f.with_action do %>
186-
<%= f.F.authz_submit "Save", "archive.svg" %>
192+
<%= f.F.authz_submit "Save", "archive.svg", disabled: !@app.ready? %>
187193
<% end %>
188194
<% end %>

0 commit comments

Comments
 (0)