fastlane 2.132.0 → 2.135.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +74 -74
  3. data/fastlane/lib/fastlane/action.rb +1 -1
  4. data/fastlane/lib/fastlane/actions/actions_helper.rb +1 -1
  5. data/fastlane/lib/fastlane/actions/carthage.rb +7 -0
  6. data/fastlane/lib/fastlane/actions/cocoapods.rb +24 -2
  7. data/fastlane/lib/fastlane/actions/deploygate.rb +1 -1
  8. data/fastlane/lib/fastlane/actions/docs/capture_ios_screenshots.md +1 -1
  9. data/fastlane/lib/fastlane/actions/docs/sync_code_signing.md +4 -2
  10. data/fastlane/lib/fastlane/actions/docs/upload_to_play_store.md +26 -2
  11. data/fastlane/lib/fastlane/actions/download_dsyms.rb +26 -3
  12. data/fastlane/lib/fastlane/actions/download_from_play_store.rb +1 -1
  13. data/fastlane/lib/fastlane/actions/get_version_number.rb +10 -4
  14. data/fastlane/lib/fastlane/actions/google_play_track_version_codes.rb +5 -1
  15. data/fastlane/lib/fastlane/actions/sonar.rb +16 -0
  16. data/fastlane/lib/fastlane/actions/testfairy.rb +1 -1
  17. data/fastlane/lib/fastlane/actions/update_fastlane.rb +9 -49
  18. data/fastlane/lib/fastlane/actions/update_keychain_access_groups.rb +94 -0
  19. data/fastlane/lib/fastlane/environment_printer.rb +9 -3
  20. data/fastlane/lib/fastlane/fast_file.rb +3 -2
  21. data/fastlane/lib/fastlane/lane_manager.rb +1 -1
  22. data/fastlane/lib/fastlane/plugins/plugin_manager.rb +12 -2
  23. data/fastlane/lib/fastlane/plugins/template/.rubocop.yml +1 -0
  24. data/fastlane/lib/fastlane/runner.rb +2 -2
  25. data/fastlane/lib/fastlane/swift_fastlane_api_generator.rb +10 -2
  26. data/fastlane/lib/fastlane/swift_fastlane_function.rb +72 -3
  27. data/fastlane/lib/fastlane/version.rb +1 -1
  28. data/fastlane/swift/Deliverfile.swift +1 -1
  29. data/fastlane/swift/DeliverfileProtocol.swift +121 -1
  30. data/fastlane/swift/Fastlane.swift +3900 -16
  31. data/fastlane/swift/Gymfile.swift +1 -1
  32. data/fastlane/swift/GymfileProtocol.swift +81 -1
  33. data/fastlane/swift/Matchfile.swift +1 -1
  34. data/fastlane/swift/MatchfileProtocol.swift +60 -2
  35. data/fastlane/swift/Precheckfile.swift +1 -1
  36. data/fastlane/swift/PrecheckfileProtocol.swift +15 -2
  37. data/fastlane/swift/Scanfile.swift +1 -1
  38. data/fastlane/swift/ScanfileProtocol.swift +109 -1
  39. data/fastlane/swift/Screengrabfile.swift +1 -1
  40. data/fastlane/swift/ScreengrabfileProtocol.swift +39 -2
  41. data/fastlane/swift/Snapshotfile.swift +1 -1
  42. data/fastlane/swift/SnapshotfileProtocol.swift +69 -1
  43. data/fastlane_core/lib/fastlane_core/configuration/commander_generator.rb +3 -3
  44. data/fastlane_core/lib/fastlane_core/configuration/configuration.rb +1 -1
  45. data/fastlane_core/lib/fastlane_core/device_manager.rb +1 -1
  46. data/fastlane_core/lib/fastlane_core/swag.rb +1 -1
  47. data/fastlane_core/lib/fastlane_core/ui/fastlane_runner.rb +1 -1
  48. data/match/lib/match/importer.rb +1 -1
  49. data/match/lib/match/storage/google_cloud_storage.rb +3 -0
  50. data/scan/lib/scan/error_handler.rb +9 -4
  51. data/scan/lib/scan/runner.rb +1 -1
  52. data/sigh/lib/assets/resign.sh +2 -2
  53. data/snapshot/lib/snapshot/simulator_launchers/simulator_launcher.rb +1 -1
  54. data/spaceship/lib/spaceship/client.rb +2 -2
  55. data/supply/lib/supply.rb +23 -0
  56. data/supply/lib/supply/.options.rb.swp +0 -0
  57. data/supply/lib/supply/.uploader.rb.swp +0 -0
  58. data/supply/lib/supply/client.rb +101 -55
  59. data/supply/lib/supply/options.rb +50 -14
  60. data/supply/lib/supply/release_listing.rb +18 -0
  61. data/supply/lib/supply/setup.rb +42 -34
  62. data/supply/lib/supply/uploader.rb +171 -93
  63. metadata +36 -41
  64. data/fastlane/lib/fastlane/actions/.hockey.rb.swp +0 -0
  65. data/fastlane/lib/fastlane/actions/.slack.rb.swp +0 -0
  66. data/fastlane/lib/fastlane/actions/.update_project_provisioning.rb.swp +0 -0
  67. data/fastlane/swift/FastlaneSwiftRunner/FastlaneSwiftRunner.xcodeproj/project.xcworkspace/xcuserdata/josh.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  68. data/gym/lib/gym/.runner.rb.swp +0 -0
  69. data/pilot/lib/pilot/.manager.rb.swp +0 -0
  70. data/spaceship/lib/spaceship/connect_api/.DS_Store +0 -0
  71. data/spaceship/lib/spaceship/portal/.certificate.rb.swp +0 -0
@@ -5,7 +5,6 @@ module Supply
5
5
  class Options
6
6
  # rubocop:disable Metrics/PerceivedComplexity
7
7
  def self.available_options
8
- default_tracks = %w(production beta alpha internal rollout)
9
8
  @options ||= [
10
9
  FastlaneCore::ConfigItem.new(key: :package_name,
11
10
  env_name: "SUPPLY_PACKAGE_NAME",
@@ -14,11 +13,37 @@ module Supply
14
13
  code_gen_sensitive: true,
15
14
  default_value: CredentialsManager::AppfileConfig.try_fetch_value(:package_name),
16
15
  default_value_dynamic: true),
16
+ FastlaneCore::ConfigItem.new(key: :version_name,
17
+ env_name: "SUPPLY_VERSION_NAME",
18
+ short_option: "-n",
19
+ optional: true,
20
+ description: "Version name (used when uploading new apks/aabs) - defaults to 'versionName' in build.gradle or AndroidManifest.xml",
21
+ default_value: CredentialsManager::AppfileConfig.try_fetch_value(:version_name),
22
+ default_value_dynamic: true),
23
+ FastlaneCore::ConfigItem.new(key: :version_code,
24
+ env_name: "SUPPLY_VERSION_CODE",
25
+ short_option: "-C",
26
+ optional: true,
27
+ type: Integer,
28
+ description: "Version code (used when updating rollout or promoting specific versions)",
29
+ default_value: CredentialsManager::AppfileConfig.try_fetch_value(:version_code),
30
+ default_value_dynamic: true),
31
+ FastlaneCore::ConfigItem.new(key: :release_status,
32
+ env_name: "SUPPLY_RELEASE_STATUS",
33
+ short_option: "-e",
34
+ optional: true,
35
+ description: "Release status (used when uploading new apks/aabs) - valid values are #{Supply::ReleaseStatus::ALL.join(', ')}",
36
+ default_value: Supply::ReleaseStatus::COMPLETED,
37
+ default_value_dynamic: true,
38
+ verify_block: proc do |value|
39
+ UI.user_error!("Value must be one of '#{Supply::RELEASE_STATUS}'") unless Supply::ReleaseStatus::ALL.include?(value)
40
+ end),
17
41
  FastlaneCore::ConfigItem.new(key: :track,
18
42
  short_option: "-a",
19
43
  env_name: "SUPPLY_TRACK",
20
- description: "The track of the application to use. The default available tracks are: #{default_tracks.join(', ')}",
21
- default_value: 'production'),
44
+ description: "The track of the application to use. The default available tracks are: #{Supply::Tracks::DEFAULTS.join(', ')}",
45
+ default_value: Supply::Tracks::DEFAULT,
46
+ type: String),
22
47
  FastlaneCore::ConfigItem.new(key: :rollout,
23
48
  short_option: "-r",
24
49
  description: "The percentage of the user fraction when uploading to the rollout track",
@@ -104,6 +129,7 @@ module Supply
104
129
  FastlaneCore::ConfigItem.new(key: :apk_paths,
105
130
  env_name: "SUPPLY_APK_PATHS",
106
131
  conflicting_options: [:apk, :aab, :aab_paths],
132
+ code_gen_sensitive: true,
107
133
  optional: true,
108
134
  type: Array,
109
135
  description: "An array of paths to APK files to upload",
@@ -131,6 +157,7 @@ module Supply
131
157
  FastlaneCore::ConfigItem.new(key: :aab_paths,
132
158
  env_name: "SUPPLY_AAB_PATHS",
133
159
  conflicting_options: [:apk, :apk_paths, :aab],
160
+ code_gen_sensitive: true,
134
161
  optional: true,
135
162
  type: Array,
136
163
  description: "An array of paths to AAB files to upload",
@@ -146,41 +173,47 @@ module Supply
146
173
  env_name: "SUPPLY_SKIP_UPLOAD_APK",
147
174
  optional: true,
148
175
  description: "Whether to skip uploading APK",
149
- is_string: false,
176
+ type: Boolean,
150
177
  default_value: false),
151
178
  FastlaneCore::ConfigItem.new(key: :skip_upload_aab,
152
179
  env_name: "SUPPLY_SKIP_UPLOAD_AAB",
153
180
  optional: true,
154
181
  description: "Whether to skip uploading AAB",
155
- is_string: false,
182
+ type: Boolean,
156
183
  default_value: false),
157
184
  FastlaneCore::ConfigItem.new(key: :skip_upload_metadata,
158
185
  env_name: "SUPPLY_SKIP_UPLOAD_METADATA",
159
186
  optional: true,
160
- description: "Whether to skip uploading metadata",
161
- is_string: false,
187
+ description: "Whether to skip uploading metadata, changelogs not included",
188
+ type: Boolean,
189
+ default_value: false),
190
+ FastlaneCore::ConfigItem.new(key: :skip_upload_changelogs,
191
+ env_name: "SUPPLY_SKIP_UPLOAD_CHANGELOGS",
192
+ optional: true,
193
+ description: "Whether to skip uploading changelogs",
194
+ type: Boolean,
162
195
  default_value: false),
163
196
  FastlaneCore::ConfigItem.new(key: :skip_upload_images,
164
197
  env_name: "SUPPLY_SKIP_UPLOAD_IMAGES",
165
198
  optional: true,
166
199
  description: "Whether to skip uploading images, screenshots not included",
167
- is_string: false,
200
+ type: Boolean,
168
201
  default_value: false),
169
202
  FastlaneCore::ConfigItem.new(key: :skip_upload_screenshots,
170
203
  env_name: "SUPPLY_SKIP_UPLOAD_SCREENSHOTS",
171
204
  optional: true,
172
205
  description: "Whether to skip uploading SCREENSHOTS",
173
- is_string: false,
206
+ type: Boolean,
174
207
  default_value: false),
175
208
  FastlaneCore::ConfigItem.new(key: :track_promote_to,
176
209
  env_name: "SUPPLY_TRACK_PROMOTE_TO",
177
210
  optional: true,
178
- description: "The track to promote to. The default available tracks are: #{default_tracks.join(', ')}"),
211
+ description: "The track to promote to. The default available tracks are: #{Supply::Tracks::DEFAULTS.join(', ')}"),
179
212
  FastlaneCore::ConfigItem.new(key: :validate_only,
180
213
  env_name: "SUPPLY_VALIDATE_ONLY",
181
214
  optional: true,
182
215
  description: "Only validate changes with Google Play rather than actually publish",
183
- is_string: false,
216
+ type: Boolean,
184
217
  default_value: false),
185
218
  FastlaneCore::ConfigItem.new(key: :mapping,
186
219
  env_name: "SUPPLY_MAPPING",
@@ -215,7 +248,8 @@ module Supply
215
248
  env_name: "SUPPLY_CHECK_SUPERSEDED_TRACKS",
216
249
  optional: true,
217
250
  description: "Check the other tracks for superseded versions and disable them",
218
- is_string: false,
251
+ deprecated: "Google Play does this automatically now",
252
+ type: Boolean,
219
253
  default_value: false),
220
254
  FastlaneCore::ConfigItem.new(key: :timeout,
221
255
  env_name: "SUPPLY_TIMEOUT",
@@ -227,16 +261,18 @@ module Supply
227
261
  env_name: "SUPPLY_DEACTIVATE_ON_PROMOTE",
228
262
  optional: true,
229
263
  description: "When promoting to a new track, deactivate the binary in the origin track",
230
- is_string: false,
264
+ deprecated: "Google Play does this automatically now",
265
+ type: Boolean,
231
266
  default_value: true),
232
267
  FastlaneCore::ConfigItem.new(key: :version_codes_to_retain,
233
268
  optional: true,
234
269
  type: Array,
235
270
  description: "An array of version codes to retain when publishing a new APK",
236
271
  verify_block: proc do |version_codes|
272
+ version_codes = version_codes.map(&:to_i)
237
273
  UI.user_error!("Could not evaluate array from '#{version_codes}'") unless version_codes.kind_of?(Array)
238
274
  version_codes.each do |version_code|
239
- UI.user_error!("Version code '#{version_code}' is not an integer") unless version_code.kind_of?(Integer)
275
+ UI.user_error!("Version code '#{version_code}' is not an integer") if version_code == 0
240
276
  end
241
277
  end),
242
278
  FastlaneCore::ConfigItem.new(key: :obb_main_references_version,
@@ -0,0 +1,18 @@
1
+ module Supply
2
+ class ReleaseListing
3
+ attr_accessor :track
4
+ attr_accessor :version
5
+ attr_accessor :versioncodes
6
+ attr_accessor :language
7
+ attr_accessor :release_notes
8
+
9
+ # Initializes the release listing with the current listing if available
10
+ def initialize(track, version, versioncodes, language, text)
11
+ self.track = track
12
+ self.version = version
13
+ self.versioncodes = versioncodes
14
+ self.language = language
15
+ self.release_notes = text
16
+ end
17
+ end
18
+ end
@@ -12,16 +12,22 @@ module Supply
12
12
 
13
13
  client.listings.each do |listing|
14
14
  store_metadata(listing)
15
- create_screenshots_folder(listing)
16
15
  download_images(listing)
17
16
  end
18
17
 
19
- client.apks_version_codes.each do |apk_version_code|
20
- client.apk_listings(apk_version_code).each do |apk_listing|
21
- store_apk_listing(apk_listing)
18
+ if Supply.config[:version_name].to_s == ""
19
+ latest_version = client.latest_version(Supply.config[:track])
20
+ if latest_version
21
+ Supply.config[:version_name] = latest_version.name
22
+ else
23
+ UI.user_error!("Could not find the latest version to download metadata, images, and screenshots from")
22
24
  end
23
25
  end
24
26
 
27
+ client.release_listings(Supply.config[:version_name]).each do |release_listing|
28
+ store_release_listing(release_listing)
29
+ end
30
+
25
31
  client.abort_current_edit
26
32
 
27
33
  UI.success("✅ Successfully stored metadata in '#{metadata_path}'")
@@ -43,25 +49,40 @@ module Supply
43
49
  def download_images(listing)
44
50
  UI.message("🖼️ Downloading images (#{listing.language})")
45
51
 
46
- # We cannot download existing screenshots as they are compressed
47
- # But we can at least download the images
48
52
  require 'net/http'
49
53
 
50
- IMAGES_TYPES.each do |image_type|
51
- if ['featureGraphic'].include?(image_type)
52
- # we don't get all files in full resolution :(
53
- UI.message("📵 Due to a limitation of the Google Play API, there is no way for `supply` to download your existing feature graphic. Please copy your feature graphic to `metadata/android/#{listing.language}/images/featureGraphic.png`")
54
- next
55
- end
54
+ allowed_imagetypes = [Supply::IMAGES_TYPES, Supply::SCREENSHOT_TYPES].flatten
56
55
 
56
+ allowed_imagetypes.each do |image_type|
57
57
  begin
58
+ path = File.join(metadata_path, listing.language, IMAGES_FOLDER_NAME, image_type)
59
+
60
+ p = Pathname.new(path)
61
+ if IMAGES_TYPES.include?(image_type) # IMAGE_TYPES are stored in locale/images location
62
+ FileUtils.mkdir_p(p.dirname.to_s)
63
+ else # SCREENSHOT_TYPES go under their respective folders.
64
+ FileUtils.mkdir_p(p.to_s)
65
+ end
66
+
58
67
  UI.message("Downloading `#{image_type}` for #{listing.language}...")
59
68
 
60
- url = client.fetch_images(image_type: image_type, language: listing.language).last
61
- next unless url
69
+ urls = client.fetch_images(image_type: image_type, language: listing.language)
70
+ next if urls.nil? || urls.empty?
71
+
72
+ image_counter = 1 # Used to prefix the downloaded files, so order is preserved.
73
+ urls.each do |url|
74
+ if IMAGES_TYPES.include?(image_type) # IMAGE_TYPES are stored in locale/images
75
+ file_path = "#{path}.#{FastImage.type(url)}"
76
+ else # SCREENSHOT_TYPES are stored in locale/images/<screensho_types>
77
+ file_path = File.join(path, "#{image_counter}_#{listing.language}.#{FastImage.type(url)}")
78
+ end
79
+
80
+ File.binwrite(file_path, Net::HTTP.get(URI.parse(url)))
81
+
82
+ UI.message("\tDownloaded - #{file_path}")
62
83
 
63
- path = File.join(metadata_path, listing.language, IMAGES_FOLDER_NAME, "#{image_type}.png")
64
- File.write(path, Net::HTTP.get(URI.parse(url)))
84
+ image_counter += 1
85
+ end
65
86
  rescue => ex
66
87
  UI.error(ex.to_s)
67
88
  UI.error("Error downloading '#{image_type}' for #{listing.language}...")
@@ -69,30 +90,17 @@ module Supply
69
90
  end
70
91
  end
71
92
 
72
- def create_screenshots_folder(listing)
73
- UI.message("📱 Downloading screenshots (#{listing.language})")
74
-
75
- containing = File.join(metadata_path, listing.language)
76
-
77
- FileUtils.mkdir_p(File.join(containing, IMAGES_FOLDER_NAME))
78
- Supply::SCREENSHOT_TYPES.each do |screenshot_type|
79
- FileUtils.mkdir_p(File.join(containing, IMAGES_FOLDER_NAME, screenshot_type))
80
- end
81
-
82
- UI.message("📵 Due to a limitation of the Google Play API, there is no way for `supply` to download your existing screenshots. Please copy your screenshots into `metadata/android/#{listing.language}/images/`")
83
- end
84
-
85
- def store_apk_listing(apk_listing)
86
- UI.message("🔨 Downloading changelogs (#{apk_listing.language}, #{apk_listing.apk_version_code})")
93
+ def store_release_listing(release_listing)
94
+ UI.message("🔨 Downloading changelogs (#{release_listing.language}, #{release_listing.version})")
87
95
 
88
- containing = File.join(metadata_path, apk_listing.language, CHANGELOGS_FOLDER_NAME)
96
+ containing = File.join(metadata_path, release_listing.language, CHANGELOGS_FOLDER_NAME)
89
97
  unless File.exist?(containing)
90
98
  FileUtils.mkdir_p(containing)
91
99
  end
92
100
 
93
- path = File.join(containing, "#{apk_listing.apk_version_code}.txt")
101
+ path = File.join(containing, "#{release_listing.version}.txt")
94
102
  UI.message("Writing to #{path}...")
95
- File.write(path, apk_listing.recent_changes)
103
+ File.write(path, release_listing.release_notes)
96
104
  end
97
105
 
98
106
  private
@@ -1,4 +1,5 @@
1
1
  module Supply
2
+ # rubocop:disable Metrics/ClassLength
2
3
  class Uploader
3
4
  def perform_upload
4
5
  FastlaneCore::PrintTable.print_values(config: Supply.config, hide_keys: [:issuer], mask_keys: [:json_key_data], title: "Summary for supply #{Fastlane::VERSION}")
@@ -7,22 +8,6 @@ module Supply
7
8
 
8
9
  verify_config!
9
10
 
10
- if metadata_path
11
- UI.user_error!("Could not find folder #{metadata_path}") unless File.directory?(metadata_path)
12
-
13
- all_languages.each do |language|
14
- next if language.start_with?('.') # e.g. . or .. or hidden folders
15
- UI.message("Preparing to upload for language '#{language}'...")
16
-
17
- listing = client.listing_for_language(language)
18
-
19
- upload_metadata(language, listing) unless Supply.config[:skip_upload_metadata]
20
- upload_images(language) unless Supply.config[:skip_upload_images]
21
- upload_screenshots(language) unless Supply.config[:skip_upload_screenshots]
22
- upload_changelogs(language) unless Supply.config[:skip_upload_metadata]
23
- end
24
- end
25
-
26
11
  apk_version_codes = []
27
12
  apk_version_codes.concat(upload_apks) unless Supply.config[:skip_upload_apk]
28
13
  apk_version_codes.concat(upload_bundles) unless Supply.config[:skip_upload_aab]
@@ -34,19 +19,102 @@ module Supply
34
19
  # Updating a track with empty version codes can completely clear out a track
35
20
  update_track(apk_version_codes) unless apk_version_codes.empty?
36
21
 
22
+ if !Supply.config[:rollout].nil? && Supply.config[:track].to_s != ""
23
+ update_rollout
24
+ end
25
+
37
26
  promote_track if Supply.config[:track_promote_to]
38
27
 
39
28
  if Supply.config[:validate_only]
40
- UI.message("Validating all changes with Google Play...")
29
+ UI.message("Validating all track and release changes with Google Play...")
30
+ client.validate_current_edit!
31
+ UI.success("Successfully validated the upload to Google Play")
32
+ else
33
+ UI.message("Uploading all track and release changes to Google Play...")
34
+ client.commit_current_edit!
35
+ UI.success("Successfully finished the upload to Google Play")
36
+ end
37
+
38
+ perform_upload_meta(apk_version_codes)
39
+ end
40
+
41
+ def perform_upload_meta(version_codes)
42
+ client.begin_edit(package_name: Supply.config[:package_name])
43
+
44
+ if (!Supply.config[:skip_upload_metadata] || !Supply.config[:skip_upload_images] || !Supply.config[:skip_upload_changelogs] || !Supply.config[:skip_upload_screenshots]) && metadata_path
45
+ version_codes = [Supply.config[:version_code]] if version_codes.empty?
46
+ version_codes.each do |version_code|
47
+ UI.user_error!("Could not find folder #{metadata_path}") unless File.directory?(metadata_path)
48
+
49
+ track, release = fetch_track_and_release!(Supply.config[:track], version_code)
50
+ UI.user_error!("Unable to find the requested track - '#{Supply.config[:track]}'") unless track
51
+ UI.user_error!("Could not find release for version code '#{version_code}' to update changelog") unless release
52
+
53
+ release_notes = []
54
+ all_languages.each do |language|
55
+ next if language.start_with?('.') # e.g. . or .. or hidden folders
56
+ UI.message("Preparing to upload for language '#{language}'...")
57
+
58
+ listing = client.listing_for_language(language)
59
+
60
+ upload_metadata(language, listing) unless Supply.config[:skip_upload_metadata]
61
+ upload_images(language) unless Supply.config[:skip_upload_images]
62
+ upload_screenshots(language) unless Supply.config[:skip_upload_screenshots]
63
+ release_notes << upload_changelog(language, release.name) unless Supply.config[:skip_upload_changelogs]
64
+ end
65
+
66
+ upload_changelogs(release_notes, release, track) unless release_notes.empty?
67
+ end
68
+ end
69
+
70
+ if Supply.config[:validate_only]
71
+ UI.message("Validating all meta changes with Google Play...")
41
72
  client.validate_current_edit!
42
73
  UI.success("Successfully validated the upload to Google Play")
43
74
  else
44
- UI.message("Uploading all changes to Google Play...")
75
+ UI.message("Uploading all meta changes to Google Play...")
45
76
  client.commit_current_edit!
46
77
  UI.success("Successfully finished the upload to Google Play")
47
78
  end
48
79
  end
49
80
 
81
+ def fetch_track_and_release!(track, version_code, status = nil)
82
+ tracks = client.tracks(track)
83
+ return nil, nil if tracks.empty?
84
+
85
+ track = tracks.first
86
+ releases = track.releases
87
+ releases = releases.select { |r| r.status == status } if status
88
+ releases = releases.select { |r| r.version_codes.map(&:to_s).include?(version_code.to_s) }
89
+
90
+ if releases.size > 1
91
+ UI.user_error!("More than one release found in this track. Please specify with the :version_code option to select a release.")
92
+ end
93
+
94
+ return track, releases.first
95
+ end
96
+
97
+ def update_rollout
98
+ track, release = fetch_track_and_release!(Supply.config[:track], Supply.config[:version_code], Supply::ReleaseStatus::IN_PROGRESS)
99
+ version_code = release.version_codes.first
100
+
101
+ UI.message("Updating #{version_code}'s rollout to '#{Supply.config[:rollout]}' on track '#{Supply.config[:track]}'...")
102
+ UI.user_error!("Unable to find the requested track - '#{Supply.config[:track]}'") unless track
103
+
104
+ if track && release
105
+ completed = Supply.config[:rollout].to_f == 1
106
+ release.user_fraction = completed ? nil : Supply.config[:rollout]
107
+ release.status = Supply::ReleaseStatus::COMPLETED if completed
108
+
109
+ # Deleted other version codes if completed because only allowed on completed version in a release
110
+ track.releases.delete_if { |r| !(r.version_codes || []).map(&:to_s).include?(version_code) } if completed
111
+ else
112
+ UI.user_error!("Unable to find version to rollout in track '#{Supply.config[:track]}'")
113
+ end
114
+
115
+ client.update_track(Supply.config[:track], track)
116
+ end
117
+
50
118
  def verify_config!
51
119
  unless metadata_path || Supply.config[:apk] || Supply.config[:apk_paths] || Supply.config[:aab] || Supply.config[:aab_paths] || (Supply.config[:track] && Supply.config[:track_promote_to])
52
120
  UI.user_error!("No local metadata, apks, aab, or track to promote were found, make sure to run `fastlane supply init` to setup supply")
@@ -60,32 +128,73 @@ module Supply
60
128
  if could_upload_apk && could_upload_aab
61
129
  UI.user_error!("Cannot provide both apk(s) and aab - use `skip_upload_apk`, `skip_upload_aab`, or make sure to remove any existing .apk or .aab files that are no longer needed")
62
130
  end
131
+
132
+ if Supply.config[:release_status] == Supply::ReleaseStatus::DRAFT && Supply.config[:rollout]
133
+ UI.user_error!(%(Cannot specify rollout percentage when the release status is set to 'draft'))
134
+ end
135
+
136
+ unless Supply.config[:version_codes_to_retain].nil?
137
+ Supply.config[:version_codes_to_retain] = Supply.config[:version_codes_to_retain].map(&:to_i)
138
+ end
63
139
  end
64
140
 
65
141
  def promote_track
66
- version_codes = client.track_version_codes(Supply.config[:track])
67
- # the actual value passed for the rollout argument does not matter because it will be ignored by the Google Play API
68
- # but it has to be between 0.0 and 1.0 to pass the validity check. So we are passing the default value 0.1
69
- client.update_track(Supply.config[:track], 0.1, nil) if Supply.config[:deactivate_on_promote]
70
- client.update_track(Supply.config[:track_promote_to], Supply.config[:rollout] || 0.1, version_codes)
71
- end
142
+ track_from = client.tracks(Supply.config[:track]).first
143
+ unless track_from
144
+ UI.user_error!("Cannot promote from track '#{Supply.config[:track]}' - track doesn't exist")
145
+ end
146
+
147
+ releases = track_from.releases
148
+ if Supply.config[:version_code].to_s != ""
149
+ releases = releases.select do |release|
150
+ release.version_codes.include?(Supply.config[:version_code].to_s)
151
+ end
152
+ end
72
153
 
73
- def upload_changelogs(language)
74
- client.apks_version_codes.each do |apk_version_code|
75
- upload_changelog(language, apk_version_code)
154
+ if releases.size == 0
155
+ UI.user_error!("Track '#{Supply.config[:track]}' doesn't have any releases")
156
+ elsif releases.size > 1
157
+ UI.user_error!("Track '#{Supply.config[:track]}' has more than one release - use :version_code to filter the release to promote")
76
158
  end
77
- client.aab_version_codes.each do |aab_version_code|
78
- upload_changelog(language, aab_version_code)
159
+
160
+ release = track_from.releases.first
161
+ track_to = client.tracks(Supply.config[:track_promote_to]).first
162
+
163
+ if track_to
164
+ # Its okay to set releases to an array containing the newest release
165
+ # Google Play will keep previous releases there this release is a partial rollout
166
+ track_to.releases = [release]
167
+ else
168
+ track_to = AndroidPublisher::Track.new(
169
+ track: Supply.config[:track_promote_to],
170
+ releases: [release]
171
+ )
79
172
  end
173
+
174
+ client.update_track(Supply.config[:track_promote_to], track_to)
80
175
  end
81
176
 
82
- def upload_changelog(language, version_code)
83
- path = File.join(metadata_path, language, Supply::CHANGELOGS_FOLDER_NAME, "#{version_code}.txt")
177
+ def upload_changelog(language, version_name)
178
+ UI.user_error!("Cannot find changelog because no version name given - please specify :version_name") unless version_name
179
+
180
+ path = File.join(Supply.config[:metadata_path], language, Supply::CHANGELOGS_FOLDER_NAME, "#{version_name}.txt")
181
+ changelog_text = ''
84
182
  if File.exist?(path)
85
- UI.message("Updating changelog for code version '#{version_code}' and language '#{language}'...")
86
- apk_listing = ApkListing.new(File.read(path, encoding: 'UTF-8'), language, version_code)
87
- client.update_apk_listing_for_language(apk_listing)
183
+ UI.message("Updating changelog for '#{version_name}' and language '#{language}'...")
184
+ changelog_text = File.read(path, encoding: 'UTF-8')
185
+ else
186
+ UI.message("Could not find changelog for '#{version_name}' and language '#{language}' at path #{path}...")
88
187
  end
188
+
189
+ AndroidPublisher::LocalizedText.new({
190
+ language: language,
191
+ text: changelog_text
192
+ })
193
+ end
194
+
195
+ def upload_changelogs(release_notes, release, track)
196
+ release.release_notes = release_notes
197
+ client.upload_changelogs(track, Supply.config[:track])
89
198
  end
90
199
 
91
200
  def upload_metadata(language, listing)
@@ -163,14 +272,6 @@ module Supply
163
272
  aab_paths.each do |aab_path|
164
273
  UI.message("Preparing aab at path '#{aab_path}' for upload...")
165
274
  bundle_version_code = client.upload_bundle(aab_path)
166
-
167
- if metadata_path
168
- all_languages.each do |language|
169
- next if language.start_with?('.') # e.g. . or .. or hidden folders
170
- upload_changelog(language, bundle_version_code)
171
- end
172
- end
173
-
174
275
  aab_version_codes.push(bundle_version_code)
175
276
  end
176
277
 
@@ -188,6 +289,7 @@ module Supply
188
289
  # @return [Integer] The apk version code returned after uploading, or nil if there was a problem
189
290
  def upload_binary_data(apk_path)
190
291
  apk_version_code = nil
292
+
191
293
  if apk_path
192
294
  UI.message("Preparing apk at path '#{apk_path}' for upload...")
193
295
  apk_version_code = client.upload_apk(apk_path)
@@ -208,16 +310,11 @@ module Supply
208
310
  end
209
311
 
210
312
  upload_obbs(apk_path, apk_version_code)
211
-
212
- if metadata_path
213
- all_languages.each do |language|
214
- next if language.start_with?('.') # e.g. . or .. or hidden folders
215
- upload_changelog(language, apk_version_code)
216
- end
217
- end
218
313
  else
219
314
  UI.message("No apk file found, you can pass the path to your apk using the `apk` option")
220
315
  end
316
+
317
+ UI.message("\tVersion Code: #{apk_version_code}")
221
318
  apk_version_code
222
319
  end
223
320
 
@@ -230,55 +327,35 @@ module Supply
230
327
  end
231
328
 
232
329
  def update_track(apk_version_codes)
233
- UI.message("Updating track '#{Supply.config[:track]}'...")
234
- check_superseded_tracks(apk_version_codes) if Supply.config[:check_superseded_tracks]
235
-
236
- if Supply.config[:track].eql?("rollout")
237
- client.update_track(Supply.config[:track], Supply.config[:rollout] || 0.1, apk_version_codes)
238
- else
239
- client.update_track(Supply.config[:track], 1.0, apk_version_codes)
240
- end
241
- end
330
+ return if apk_version_codes.empty?
242
331
 
243
- # Remove any version codes that is:
244
- # - Lesser than the greatest of any later (i.e. production) track
245
- # - Or lesser than the currently being uploaded if it's in an earlier (i.e. alpha) track
246
- def check_superseded_tracks(apk_version_codes)
247
- UI.message("Checking superseded tracks, uploading '#{apk_version_codes}' to '#{Supply.config[:track]}'...")
248
- max_apk_version_code = apk_version_codes.max
249
- max_tracks_version_code = nil
250
-
251
- tracks = ["production", "rollout", "beta", "alpha", "internal"]
252
- config_track_index = tracks.index(Supply.config[:track])
253
-
254
- # Custom "closed" tracks are now allowed (https://support.google.com/googleplay/android-developer/answer/3131213)
255
- # Custom tracks have an equal level with alpha (alpha is considered a closed track as well)
256
- # If a track index is not found, we will assume is a custom track so an alpha index is given
257
- config_track_index = tracks.index("alpha") unless config_track_index
258
-
259
- tracks.each_index do |track_index|
260
- track = tracks[track_index]
261
- track_version_codes = client.track_version_codes(track).sort
262
- UI.verbose("Found '#{track_version_codes}' on track '#{track}'")
263
-
264
- next if track_index.eql?(config_track_index)
265
- next if track_version_codes.empty?
266
-
267
- if max_tracks_version_code.nil?
268
- max_tracks_version_code = track_version_codes.max
269
- end
332
+ UI.message("Updating track '#{Supply.config[:track]}'...")
270
333
 
271
- removed_version_codes = track_version_codes.take_while do |v|
272
- v < max_tracks_version_code || (v < max_apk_version_code && track_index > config_track_index)
273
- end
334
+ track_release = AndroidPublisher::TrackRelease.new(
335
+ name: Supply.config[:version_name],
336
+ status: Supply.config[:release_status],
337
+ version_codes: apk_version_codes
338
+ )
274
339
 
275
- next if removed_version_codes.empty?
340
+ if Supply.config[:rollout]
341
+ track_release.status = Supply::ReleaseStatus::IN_PROGRESS
342
+ track_release.user_fraction = Supply.config[:rollout].to_f
343
+ end
276
344
 
277
- keep_version_codes = track_version_codes - removed_version_codes
278
- max_tracks_version_code = keep_version_codes[0] unless keep_version_codes.empty?
279
- client.update_track(track, 1.0, keep_version_codes)
280
- UI.message("Superseded track '#{track}', removed '#{removed_version_codes}'")
345
+ tracks = client.tracks(Supply.config[:track])
346
+ track = tracks.first
347
+ if track
348
+ # Its okay to set releases to an array containing the newest release
349
+ # Google Play will keep previous releases there this release is a partial rollout
350
+ track.releases = [track_release]
351
+ else
352
+ track = AndroidPublisher::Track.new(
353
+ track: Supply.config[:track],
354
+ releases: [track_release]
355
+ )
281
356
  end
357
+
358
+ client.update_track(Supply.config[:track], track)
282
359
  end
283
360
 
284
361
  # returns only language directories from metadata_path
@@ -346,4 +423,5 @@ module Supply
346
423
  end
347
424
  end
348
425
  end
426
+ # rubocop:enable Metrics/ClassLength
349
427
  end