fastlane 2.125.0.beta.20190531200016 → 2.125.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +70 -70
  3. data/fastlane/lib/fastlane/actions/.slack.rb.swp +0 -0
  4. data/fastlane/lib/fastlane/actions/.update_project_provisioning.rb.swp +0 -0
  5. data/fastlane/lib/fastlane/version.rb +1 -1
  6. data/fastlane/swift/Deliverfile.swift +1 -1
  7. data/fastlane/swift/FastlaneSwiftRunner/FastlaneSwiftRunner.xcodeproj/project.xcworkspace/xcuserdata/josh.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  8. data/fastlane/swift/Gymfile.swift +1 -1
  9. data/fastlane/swift/Matchfile.swift +1 -1
  10. data/fastlane/swift/Precheckfile.swift +1 -1
  11. data/fastlane/swift/Scanfile.swift +1 -1
  12. data/fastlane/swift/Screengrabfile.swift +1 -1
  13. data/fastlane/swift/Snapshotfile.swift +1 -1
  14. data/fastlane_core/lib/fastlane_core/build_watcher.rb +31 -15
  15. data/pilot/lib/pilot/.build_manager.rb.swp +0 -0
  16. data/pilot/lib/pilot/.manager.rb.swp +0 -0
  17. data/pilot/lib/pilot/build_manager.rb +133 -101
  18. data/pilot/lib/pilot/manager.rb +9 -9
  19. data/pilot/lib/pilot/tester_exporter.rb +27 -11
  20. data/pilot/lib/pilot/tester_manager.rb +48 -98
  21. data/spaceship/lib/spaceship/connect_api.rb +18 -0
  22. data/spaceship/lib/spaceship/connect_api/.DS_Store +0 -0
  23. data/spaceship/lib/spaceship/connect_api/client.rb +297 -156
  24. data/spaceship/lib/spaceship/connect_api/models/.app.rb.swp +0 -0
  25. data/spaceship/lib/spaceship/connect_api/models/.build.rb.swp +0 -0
  26. data/spaceship/lib/spaceship/connect_api/models/.model.rb.swp +0 -0
  27. data/spaceship/lib/spaceship/connect_api/models/app.rb +97 -0
  28. data/spaceship/lib/spaceship/connect_api/models/beta_app_localization.rb +28 -0
  29. data/spaceship/lib/spaceship/connect_api/models/beta_app_review_detail.rb +32 -0
  30. data/spaceship/lib/spaceship/connect_api/models/beta_app_review_submission.rb +26 -0
  31. data/spaceship/lib/spaceship/connect_api/models/beta_build_localization.rb +20 -0
  32. data/spaceship/lib/spaceship/connect_api/models/beta_build_metric.rb +24 -0
  33. data/spaceship/lib/spaceship/connect_api/models/beta_group.rb +41 -0
  34. data/spaceship/lib/spaceship/connect_api/models/beta_tester.rb +56 -0
  35. data/spaceship/lib/spaceship/connect_api/models/beta_tester_metric.rb +43 -0
  36. data/spaceship/lib/spaceship/connect_api/models/build.rb +144 -0
  37. data/spaceship/lib/spaceship/connect_api/models/build_beta_detail.rb +56 -0
  38. data/spaceship/lib/spaceship/connect_api/models/build_delivery.rb +36 -0
  39. data/spaceship/lib/spaceship/connect_api/models/model.rb +165 -0
  40. data/spaceship/lib/spaceship/connect_api/models/pre_release_version.rb +20 -0
  41. data/spaceship/lib/spaceship/connect_api/models/user.rb +50 -0
  42. data/spaceship/lib/spaceship/connect_api/response.rb +70 -0
  43. data/spaceship/lib/spaceship/connect_api/token.rb +44 -0
  44. data/spaceship/lib/spaceship/test_flight/build_trains.rb +1 -1
  45. metadata +73 -33
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d16dbea28a1f46899ebaa7d2f6895ec924ae21de
4
- data.tar.gz: 62ddaa61146dece0f60ef76f970441c85a5590bb
3
+ metadata.gz: ca7f763873225520b689ab237c87fecd46e97c1b
4
+ data.tar.gz: d98f89b8a2a5b8aaf1b3c17ca30022454b8ba2b1
5
5
  SHA512:
6
- metadata.gz: 9c855b6784ce3170e20be759f87062e1376617459adb0c063c9bf77e35f1c4c8c2869a2a3714017c26729275148cbdf82716078a607c4c76ef8394c6de138c82
7
- data.tar.gz: bb696eb8b56835a66508c7594881b40a304a589c02fa56f1966df61226d40004e82fb2aa2ab8f2efda57d187b5394f4730e2968cbcb649456cf6462015919e4e
6
+ metadata.gz: f6f512b2280b248cb9739fd6aa700591facb595db744e0fb5d9cd0668f11bef077d2ec8837dfc0b135b8a6b056df930617e3c4aee95a267e2ba16869b2853c31
7
+ data.tar.gz: 120c4f4280951593936da20bd2b7427d769fefc778610b3362af8469f1da25a9786ec54dbb959c7f1ccf64f2b57fd98175f818e78e715cbe53d998602066dd22
data/README.md CHANGED
@@ -34,23 +34,23 @@ If the above doesn't help, please [submit an issue](https://github.com/fastlane/
34
34
  <!-- This table is regenerated and resorted on each release -->
35
35
  <table id='team'>
36
36
  <tr>
37
- <td id='manu-wallner'>
38
- <a href='https://github.com/milch'>
39
- <img src='https://github.com/milch.png?size=140'>
37
+ <td id='stefan-natchev'>
38
+ <a href='https://github.com/snatchev'>
39
+ <img src='https://github.com/snatchev.png?size=140'>
40
40
  </a>
41
- <h4 align='center'><a href='https://twitter.com/acrooow'>Manu Wallner</a></h4>
41
+ <h4 align='center'><a href='https://twitter.com/snatchev'>Stefan Natchev</a></h4>
42
42
  </td>
43
- <td id='aaron-brager'>
44
- <a href='https://github.com/getaaron'>
45
- <img src='https://github.com/getaaron.png?size=140'>
43
+ <td id='jorge-revuelta-h'>
44
+ <a href='https://github.com/minuscorp'>
45
+ <img src='https://github.com/minuscorp.png?size=140'>
46
46
  </a>
47
- <h4 align='center'><a href='https://twitter.com/getaaron'>Aaron Brager</a></h4>
47
+ <h4 align='center'><a href='https://twitter.com/minuscorp'>Jorge Revuelta H</a></h4>
48
48
  </td>
49
- <td id='helmut-januschka'>
50
- <a href='https://github.com/hjanuschka'>
51
- <img src='https://github.com/hjanuschka.png?size=140'>
49
+ <td id='maksym-grebenets'>
50
+ <a href='https://github.com/mgrebenets'>
51
+ <img src='https://github.com/mgrebenets.png?size=140'>
52
52
  </a>
53
- <h4 align='center'><a href='https://twitter.com/hjanuschka'>Helmut Januschka</a></h4>
53
+ <h4 align='center'><a href='https://twitter.com/mgrebenets'>Maksym Grebenets</a></h4>
54
54
  </td>
55
55
  <td id='jérôme-lacoste'>
56
56
  <a href='https://github.com/lacostej'>
@@ -58,63 +58,69 @@ If the above doesn't help, please [submit an issue](https://github.com/fastlane/
58
58
  </a>
59
59
  <h4 align='center'><a href='https://twitter.com/lacostej'>Jérôme Lacoste</a></h4>
60
60
  </td>
61
- <td id='luka-mirosevic'>
62
- <a href='https://github.com/lmirosevic'>
63
- <img src='https://github.com/lmirosevic.png?size=140'>
61
+ <td id='matthew-ellis'>
62
+ <a href='https://github.com/matthewellis'>
63
+ <img src='https://github.com/matthewellis.png?size=140'>
64
64
  </a>
65
- <h4 align='center'><a href='https://twitter.com/lmirosevic'>Luka Mirosevic</a></h4>
65
+ <h4 align='center'><a href='https://twitter.com/mellis1995'>Matthew Ellis</a></h4>
66
66
  </td>
67
67
  </tr>
68
68
  <tr>
69
- <td id='jorge-revuelta-h'>
70
- <a href='https://github.com/minuscorp'>
71
- <img src='https://github.com/minuscorp.png?size=140'>
69
+ <td id='luka-mirosevic'>
70
+ <a href='https://github.com/lmirosevic'>
71
+ <img src='https://github.com/lmirosevic.png?size=140'>
72
72
  </a>
73
- <h4 align='center'><a href='https://twitter.com/minuscorp'>Jorge Revuelta H</a></h4>
73
+ <h4 align='center'><a href='https://twitter.com/lmirosevic'>Luka Mirosevic</a></h4>
74
74
  </td>
75
- <td id='stefan-natchev'>
76
- <a href='https://github.com/snatchev'>
77
- <img src='https://github.com/snatchev.png?size=140'>
75
+ <td id='felix-krause'>
76
+ <a href='https://github.com/KrauseFx'>
77
+ <img src='https://github.com/KrauseFx.png?size=140'>
78
78
  </a>
79
- <h4 align='center'><a href='https://twitter.com/snatchev'>Stefan Natchev</a></h4>
79
+ <h4 align='center'><a href='https://twitter.com/KrauseFx'>Felix Krause</a></h4>
80
80
  </td>
81
- <td id='jimmy-dee'>
82
- <a href='https://github.com/jdee'>
83
- <img src='https://github.com/jdee.png?size=140'>
81
+ <td id='joshua-liebowitz'>
82
+ <a href='https://github.com/taquitos'>
83
+ <img src='https://github.com/taquitos.png?size=140'>
84
84
  </a>
85
- <h4 align='center'>Jimmy Dee</h4>
85
+ <h4 align='center'><a href='https://twitter.com/taquitos'>Joshua Liebowitz</a></h4>
86
86
  </td>
87
- <td id='matthew-ellis'>
88
- <a href='https://github.com/matthewellis'>
89
- <img src='https://github.com/matthewellis.png?size=140'>
87
+ <td id='manu-wallner'>
88
+ <a href='https://github.com/milch'>
89
+ <img src='https://github.com/milch.png?size=140'>
90
90
  </a>
91
- <h4 align='center'><a href='https://twitter.com/mellis1995'>Matthew Ellis</a></h4>
91
+ <h4 align='center'><a href='https://twitter.com/acrooow'>Manu Wallner</a></h4>
92
92
  </td>
93
- <td id='fumiya-nakamura'>
94
- <a href='https://github.com/nafu'>
95
- <img src='https://github.com/nafu.png?size=140'>
93
+ <td id='josh-holtz'>
94
+ <a href='https://github.com/joshdholtz'>
95
+ <img src='https://github.com/joshdholtz.png?size=140'>
96
96
  </a>
97
- <h4 align='center'><a href='https://twitter.com/nafu003'>Fumiya Nakamura</a></h4>
97
+ <h4 align='center'><a href='https://twitter.com/joshdholtz'>Josh Holtz</a></h4>
98
98
  </td>
99
99
  </tr>
100
100
  <tr>
101
+ <td id='jan-piotrowski'>
102
+ <a href='https://github.com/janpio'>
103
+ <img src='https://github.com/janpio.png?size=140'>
104
+ </a>
105
+ <h4 align='center'><a href='https://twitter.com/Sujan'>Jan Piotrowski</a></h4>
106
+ </td>
101
107
  <td id='andrew-mcburney'>
102
108
  <a href='https://github.com/armcburney'>
103
109
  <img src='https://github.com/armcburney.png?size=140'>
104
110
  </a>
105
111
  <h4 align='center'><a href='https://twitter.com/armcburney'>Andrew McBurney</a></h4>
106
112
  </td>
107
- <td id='olivier-halligon'>
108
- <a href='https://github.com/AliSoftware'>
109
- <img src='https://github.com/AliSoftware.png?size=140'>
113
+ <td id='fumiya-nakamura'>
114
+ <a href='https://github.com/nafu'>
115
+ <img src='https://github.com/nafu.png?size=140'>
110
116
  </a>
111
- <h4 align='center'><a href='https://twitter.com/aligatr'>Olivier Halligon</a></h4>
117
+ <h4 align='center'><a href='https://twitter.com/nafu003'>Fumiya Nakamura</a></h4>
112
118
  </td>
113
- <td id='joshua-liebowitz'>
114
- <a href='https://github.com/taquitos'>
115
- <img src='https://github.com/taquitos.png?size=140'>
119
+ <td id='kohki-miki'>
120
+ <a href='https://github.com/giginet'>
121
+ <img src='https://github.com/giginet.png?size=140'>
116
122
  </a>
117
- <h4 align='center'><a href='https://twitter.com/taquitos'>Joshua Liebowitz</a></h4>
123
+ <h4 align='center'><a href='https://twitter.com/giginet'>Kohki Miki</a></h4>
118
124
  </td>
119
125
  <td id='danielle-tomlinson'>
120
126
  <a href='https://github.com/endocrimes'>
@@ -122,43 +128,37 @@ If the above doesn't help, please [submit an issue](https://github.com/fastlane/
122
128
  </a>
123
129
  <h4 align='center'><a href='https://twitter.com/endocrimes'>Danielle Tomlinson</a></h4>
124
130
  </td>
125
- <td id='kohki-miki'>
126
- <a href='https://github.com/giginet'>
127
- <img src='https://github.com/giginet.png?size=140'>
128
- </a>
129
- <h4 align='center'><a href='https://twitter.com/giginet'>Kohki Miki</a></h4>
130
- </td>
131
131
  </tr>
132
132
  <tr>
133
- <td id='josh-holtz'>
134
- <a href='https://github.com/joshdholtz'>
135
- <img src='https://github.com/joshdholtz.png?size=140'>
133
+ <td id='olivier-halligon'>
134
+ <a href='https://github.com/AliSoftware'>
135
+ <img src='https://github.com/AliSoftware.png?size=140'>
136
136
  </a>
137
- <h4 align='center'><a href='https://twitter.com/joshdholtz'>Josh Holtz</a></h4>
137
+ <h4 align='center'><a href='https://twitter.com/aligatr'>Olivier Halligon</a></h4>
138
138
  </td>
139
- <td id='iulian-onofrei'>
140
- <a href='https://github.com/revolter'>
141
- <img src='https://github.com/revolter.png?size=140'>
139
+ <td id='jimmy-dee'>
140
+ <a href='https://github.com/jdee'>
141
+ <img src='https://github.com/jdee.png?size=140'>
142
142
  </a>
143
- <h4 align='center'><a href='https://twitter.com/Revolt666'>Iulian Onofrei</a></h4>
143
+ <h4 align='center'>Jimmy Dee</h4>
144
144
  </td>
145
- <td id='maksym-grebenets'>
146
- <a href='https://github.com/mgrebenets'>
147
- <img src='https://github.com/mgrebenets.png?size=140'>
145
+ <td id='aaron-brager'>
146
+ <a href='https://github.com/getaaron'>
147
+ <img src='https://github.com/getaaron.png?size=140'>
148
148
  </a>
149
- <h4 align='center'><a href='https://twitter.com/mgrebenets'>Maksym Grebenets</a></h4>
149
+ <h4 align='center'><a href='https://twitter.com/getaaron'>Aaron Brager</a></h4>
150
150
  </td>
151
- <td id='felix-krause'>
152
- <a href='https://github.com/KrauseFx'>
153
- <img src='https://github.com/KrauseFx.png?size=140'>
151
+ <td id='iulian-onofrei'>
152
+ <a href='https://github.com/revolter'>
153
+ <img src='https://github.com/revolter.png?size=140'>
154
154
  </a>
155
- <h4 align='center'><a href='https://twitter.com/KrauseFx'>Felix Krause</a></h4>
155
+ <h4 align='center'><a href='https://twitter.com/Revolt666'>Iulian Onofrei</a></h4>
156
156
  </td>
157
- <td id='jan-piotrowski'>
158
- <a href='https://github.com/janpio'>
159
- <img src='https://github.com/janpio.png?size=140'>
157
+ <td id='helmut-januschka'>
158
+ <a href='https://github.com/hjanuschka'>
159
+ <img src='https://github.com/hjanuschka.png?size=140'>
160
160
  </a>
161
- <h4 align='center'><a href='https://twitter.com/Sujan'>Jan Piotrowski</a></h4>
161
+ <h4 align='center'><a href='https://twitter.com/hjanuschka'>Helmut Januschka</a></h4>
162
162
  </td>
163
163
  </tr>
164
164
  </table>
@@ -1,5 +1,5 @@
1
1
  module Fastlane
2
- VERSION = '2.125.0.beta.20190531200016'.freeze
2
+ VERSION = '2.125.0'.freeze
3
3
  DESCRIPTION = "The easiest way to automate beta deployments and releases for your iOS and Android apps".freeze
4
4
  MINIMUM_XCODE_RELEASE = "7.0".freeze
5
5
  RUBOCOP_REQUIREMENT = '0.49.1'.freeze
@@ -18,4 +18,4 @@ class Deliverfile: DeliverfileProtocol {
18
18
 
19
19
 
20
20
 
21
- // Generated with fastlane 2.124.0
21
+ // Generated with fastlane 2.125.0
@@ -18,4 +18,4 @@ class Gymfile: GymfileProtocol {
18
18
 
19
19
 
20
20
 
21
- // Generated with fastlane 2.124.0
21
+ // Generated with fastlane 2.125.0
@@ -18,4 +18,4 @@ class Matchfile: MatchfileProtocol {
18
18
 
19
19
 
20
20
 
21
- // Generated with fastlane 2.124.0
21
+ // Generated with fastlane 2.125.0
@@ -18,4 +18,4 @@ class Precheckfile: PrecheckfileProtocol {
18
18
 
19
19
 
20
20
 
21
- // Generated with fastlane 2.124.0
21
+ // Generated with fastlane 2.125.0
@@ -18,4 +18,4 @@ class Scanfile: ScanfileProtocol {
18
18
 
19
19
 
20
20
 
21
- // Generated with fastlane 2.124.0
21
+ // Generated with fastlane 2.125.0
@@ -18,4 +18,4 @@ class Screengrabfile: ScreengrabfileProtocol {
18
18
 
19
19
 
20
20
 
21
- // Generated with fastlane 2.124.0
21
+ // Generated with fastlane 2.125.0
@@ -18,4 +18,4 @@ class Snapshotfile: SnapshotfileProtocol {
18
18
 
19
19
 
20
20
 
21
- // Generated with fastlane 2.124.0
21
+ // Generated with fastlane 2.125.0
@@ -1,4 +1,5 @@
1
- require 'spaceship/test_flight/build'
1
+ require 'spaceship/connect_api/models/build'
2
+ require 'spaceship/connect_api/models/build_delivery'
2
3
 
3
4
  require_relative 'ui/ui'
4
5
 
@@ -6,19 +7,29 @@ module FastlaneCore
6
7
  class BuildWatcher
7
8
  class << self
8
9
  # @return The build we waited for. This method will always return a build
9
- def wait_for_build_processing_to_be_complete(app_id: nil, platform: nil, train_version: nil, build_version: nil, poll_interval: 10, strict_build_watch: false)
10
+ def wait_for_build_processing_to_be_complete(app_id: nil, platform: nil, train_version: nil, app_version: nil, build_version: nil, poll_interval: 10, strict_build_watch: false, return_spaceship_testflight_build: true)
11
+ # Warn about train_version being removed in the future
12
+ if train_version
13
+ UI.deprecated(":train_version is no longer a used argument on FastlaneCore::BuildWatcher. Please use :app_verion instead.")
14
+ end
15
+ app_version = train_version
16
+
10
17
  # Warn about strict_build_watch being removed in the future
11
18
  if strict_build_watch
12
19
  UI.deprecated(":strict_build_watch is no longer a used argument on FastlaneCore::BuildWatcher.")
13
20
  end
14
21
 
15
22
  loop do
16
- matched_build, build_delivery = matching_build(watched_train_version: train_version, watched_build_version: build_version, app_id: app_id, platform: platform)
23
+ matched_build, build_delivery = matching_build(watched_app_version: app_version, watched_build_version: build_version, app_id: app_id)
17
24
 
18
25
  report_status(build: matched_build, build_delivery: build_delivery)
19
26
 
20
27
  if matched_build && matched_build.processed?
21
- return matched_build
28
+ if return_spaceship_testflight_build
29
+ return matched_build.to_testflight_build
30
+ else
31
+ return matched_build
32
+ end
22
33
  end
23
34
 
24
35
  sleep(poll_interval)
@@ -32,18 +43,23 @@ module FastlaneCore
32
43
  return version.instance_of?(String) ? version.split('.').map { |s| s.to_i.to_s }.join('.') : version
33
44
  end
34
45
 
35
- def matching_build(watched_train_version: nil, watched_build_version: nil, app_id: nil, platform: nil)
46
+ def matching_build(watched_app_version: nil, watched_build_version: nil, app_id: nil, platform: nil)
36
47
  # Get build deliveries (newly uploaded processing builds)
37
- client = Spaceship::ConnectAPI::Base.client
38
- truncated_watched_train_version = remove_version_leading_zeros(version: watched_train_version)
39
- truncated_watched_build_version = remove_version_leading_zeros(version: watched_build_version)
40
- build_deliveries = client.get_build_deliveries(filter: { app: app_id, cfBundleShortVersionString: truncated_watched_train_version, cfBundleVersion: truncated_watched_build_version }, limit: 1)
48
+ watched_app_version = remove_version_leading_zeros(version: watched_app_version)
49
+ watched_build_version = remove_version_leading_zeros(version: watched_build_version)
50
+
51
+ build_deliveries = Spaceship::ConnectAPI::BuildDelivery.all(app_id: app_id, version: watched_app_version, build_number: watched_build_version)
41
52
  build_delivery = build_deliveries.first
42
53
 
43
54
  # Get processed builds when no longer in build deliveries
44
- unless build_delivery
45
- matched_builds = Spaceship::TestFlight::Build.all(app_id: app_id, platform: platform)
46
- matched_build = matched_builds.find { |build| build.train_version.to_s == truncated_watched_train_version.to_s && build.build_version.to_s == truncated_watched_build_version.to_s }
55
+ if build_delivery.nil?
56
+ matched_builds = Spaceship::ConnectAPI::Build.all(
57
+ app_id: app_id,
58
+ version: watched_app_version,
59
+ build_number: watched_build_version,
60
+ includes: "app,preReleaseVersion"
61
+ )
62
+ matched_build = matched_builds.first
47
63
  end
48
64
 
49
65
  return matched_build, build_delivery
@@ -51,11 +67,11 @@ module FastlaneCore
51
67
 
52
68
  def report_status(build: nil, build_delivery: nil)
53
69
  if build_delivery
54
- UI.message("Waiting for App Store Connect to finish processing the new build (#{build_delivery['attributes']['cfBundleShortVersionString']} - #{build_delivery['attributes']['cfBundleVersion']})")
70
+ UI.message("Waiting for App Store Connect to finish processing the new build (#{build_delivery.cf_build_short_version_string} - #{build_delivery.cf_build_version})")
55
71
  elsif build && !build.processed?
56
- UI.message("Waiting for App Store Connect to finish processing the new build (#{build.train_version} - #{build.build_version})")
72
+ UI.message("Waiting for App Store Connect to finish processing the new build (#{build.app_version} - #{build.version})")
57
73
  elsif build && build.processed?
58
- UI.success("Successfully finished processing the build #{build.train_version} - #{build.build_version}")
74
+ UI.success("Successfully finished processing the build #{build.app_version} - #{build.version}")
59
75
  else
60
76
  UI.message("Build doesn't show up in the build list anymore, waiting for it to appear again (check your email for processing issues if this continues)")
61
77
  end
Binary file
@@ -8,6 +8,7 @@ require 'fastlane_core/ipa_upload_package_builder'
8
8
  require_relative 'manager'
9
9
 
10
10
  module Pilot
11
+ # rubocop:disable Metrics/ClassLength
11
12
  class BuildManager < Manager
12
13
  def upload(options)
13
14
  # Only need to login before upload if no apple_id was given
@@ -15,30 +16,22 @@ module Pilot
15
16
  should_login_in_start = options[:apple_id].nil?
16
17
  start(options, should_login: should_login_in_start)
17
18
 
18
- options[:changelog] = self.class.sanitize_changelog(options[:changelog]) if options[:changelog]
19
-
20
19
  UI.user_error!("No ipa file given") unless config[:ipa]
21
20
 
22
- if options[:changelog].nil? && options[:distribute_external] == true
23
- if UI.interactive?
24
- options[:changelog] = UI.input("No changelog provided for new build. You can provide a changelog using the `changelog` option. For now, please provide a changelog here:")
25
- else
26
- UI.user_error!("No changelog provided for new build. Please either disable `distribute_external` or provide a changelog using the `changelog` option")
27
- end
28
- end
21
+ check_for_changelog_or_whats_new!(options)
29
22
 
30
- UI.success("Ready to upload new build to TestFlight (App: #{fetch_apple_id})...")
23
+ UI.success("Ready to upload new build to TestFlight (App: #{fetch_app_id})...")
31
24
 
32
25
  dir = Dir.mktmpdir
33
26
 
34
27
  platform = fetch_app_platform
35
- package_path = FastlaneCore::IpaUploadPackageBuilder.new.generate(app_id: fetch_apple_id,
28
+ package_path = FastlaneCore::IpaUploadPackageBuilder.new.generate(app_id: fetch_app_id,
36
29
  ipa_path: options[:ipa],
37
30
  package_path: dir,
38
31
  platform: platform)
39
32
 
40
33
  transporter = transporter_for_selected_team(options)
41
- result = transporter.upload(fetch_apple_id, package_path)
34
+ result = transporter.upload(fetch_app_id, package_path)
42
35
 
43
36
  unless result
44
37
  UI.user_error!("Error uploading ipa file, for more information see above")
@@ -60,14 +53,41 @@ module Pilot
60
53
  distribute(options, build: latest_build)
61
54
  end
62
55
 
56
+ def has_changelog_or_whats_new?(options)
57
+ # Look for legacy :changelog option
58
+ has_changelog = !options[:changelog].nil?
59
+
60
+ # Look for :whats_new in :localized_build_info
61
+ unless has_changelog
62
+ infos_by_lang = options[:localized_build_info] || []
63
+ infos_by_lang.each do |k, v|
64
+ next if has_changelog
65
+ v ||= {}
66
+ has_changelog = v.key?(:whats_new) || v.key?('whats_new')
67
+ end
68
+ end
69
+
70
+ return has_changelog
71
+ end
72
+
73
+ def check_for_changelog_or_whats_new!(options)
74
+ if !has_changelog_or_whats_new?(options) && options[:distribute_external] == true
75
+ if UI.interactive?
76
+ options[:changelog] = UI.input("No changelog provided for new build. You can provide a changelog using the `changelog` option. For now, please provide a changelog here:")
77
+ else
78
+ UI.user_error!("No changelog provided for new build. Please either disable `distribute_external` or provide a changelog using the `changelog` option")
79
+ end
80
+ end
81
+ end
82
+
63
83
  def wait_for_build_processing_to_be_complete
64
84
  platform = fetch_app_platform
65
85
  app_version = FastlaneCore::IpaFileAnalyser.fetch_app_version(config[:ipa])
66
86
  app_build = FastlaneCore::IpaFileAnalyser.fetch_app_build(config[:ipa])
67
- latest_build = FastlaneCore::BuildWatcher.wait_for_build_processing_to_be_complete(app_id: app.apple_id, platform: platform, train_version: app_version, build_version: app_build, poll_interval: config[:wait_processing_interval], strict_build_watch: config[:wait_for_uploaded_build])
87
+ latest_build = FastlaneCore::BuildWatcher.wait_for_build_processing_to_be_complete(app_id: app.id, platform: platform, train_version: app_version, build_version: app_build, poll_interval: config[:wait_processing_interval], return_spaceship_testflight_build: false)
68
88
 
69
- unless latest_build.train_version == app_version && latest_build.build_version == app_build
70
- UI.important("Uploaded app #{app_version} - #{app_build}, but received build #{latest_build.train_version} - #{latest_build.build_version}.")
89
+ unless latest_build.app_version == app_version && latest_build.version == app_build
90
+ UI.important("Uploaded app #{app_version} - #{app_build}, but received build #{latest_build.app_version} - #{latest_build.version}.")
71
91
  end
72
92
 
73
93
  return latest_build
@@ -79,7 +99,18 @@ module Pilot
79
99
  config[:app_identifier] = UI.input("App Identifier: ")
80
100
  end
81
101
 
82
- build ||= Spaceship::TestFlight::Build.latest(app_id: app.apple_id, platform: fetch_app_platform)
102
+ # Get latest uploaded build if no build specified
103
+ build ||= Spaceship::ConnectAPI::Build.all(app_id: app.id, sort: "-uploadedDate", limit: 1).first
104
+
105
+ # Verify the build has all the includes that we need
106
+ # and fetch a new build if not
107
+ if build && (!build.app || !build.build_beta_detail || !build.pre_release_version)
108
+ UI.important("Build did include information for app, build beta detail and pre release version")
109
+ UI.important("Fetching a new build with all the information needed")
110
+ build = Spaceship::ConnectAPI::Build.get(build_id: build.id)
111
+ end
112
+
113
+ # Error out if no build
83
114
  if build.nil?
84
115
  UI.user_error!("No build to distribute!")
85
116
  end
@@ -94,12 +125,12 @@ module Pilot
94
125
 
95
126
  return if config[:skip_submission]
96
127
  if options[:reject_build_waiting_for_review]
97
- waiting_for_review_build = Spaceship::TestFlight::Build.all_waiting_for_review(app_id: build.app_id, platform: fetch_app_platform).first
128
+ waiting_for_review_build = build.app.get_builds(filter: { "betaAppReviewSubmission.betaReviewState" => "WAITING_FOR_REVIEW" }, includes: "betaAppReviewSubmission,preReleaseVersion").first
98
129
  unless waiting_for_review_build.nil?
99
- UI.important("Another build is already in review. Going to expire that build and submit the new one.")
100
- UI.important("Expiring build: #{waiting_for_review_build.train_version} - #{waiting_for_review_build.build_version}")
101
- waiting_for_review_build.expire!
102
- UI.success("Expired previous build: #{waiting_for_review_build.train_version} - #{waiting_for_review_build.build_version}")
130
+ UI.important("Another build is already in review. Going to remove that build and submit the new one.")
131
+ UI.important("Deleting beta app review submission for build: #{waiting_for_review_build.app_version} - #{waiting_for_review_build.version}")
132
+ waiting_for_review_build.beta_app_review_submission.delete!
133
+ UI.success("Deleted beta app review submission for previous build: #{waiting_for_review_build.app_version} - #{waiting_for_review_build.version}")
103
134
  end
104
135
  end
105
136
  distribute_build(build, options)
@@ -113,38 +144,55 @@ module Pilot
113
144
  config[:app_identifier] = UI.input("App Identifier: ")
114
145
  end
115
146
 
116
- platform = fetch_app_platform(required: false)
117
- builds = app.all_processing_builds(platform: platform) + app.builds(platform: platform)
118
- # sort by upload_date
119
- builds.sort! { |a, b| a.upload_date <=> b.upload_date }
120
- rows = builds.collect { |build| describe_build(build) }
147
+ # Get processing builds
148
+ build_deliveries = app.get_build_deliveries.map do |build_delivery|
149
+ [
150
+ build_delivery.cf_build_short_version_string,
151
+ build_delivery.cf_build_version
152
+ ]
153
+ end
154
+
155
+ # Get processed builds
156
+ builds = app.get_builds(includes: "betaBuildMetrics,preReleaseVersion", sort: "-uploadedDate").map do |build|
157
+ [
158
+ build.app_version,
159
+ build.version,
160
+ build.beta_build_metrics.map(&:install_count).reduce(:+)
161
+ ]
162
+ end
163
+
164
+ # Only show table if there are any build deliveries
165
+ unless build_deliveries.empty?
166
+ puts(Terminal::Table.new(
167
+ title: "#{app.name} Processing Builds".green,
168
+ headings: ["Version #", "Build #"],
169
+ rows: FastlaneCore::PrintTable.transform_output(build_deliveries)
170
+ ))
171
+ end
121
172
 
122
173
  puts(Terminal::Table.new(
123
174
  title: "#{app.name} Builds".green,
124
175
  headings: ["Version #", "Build #", "Installs"],
125
- rows: FastlaneCore::PrintTable.transform_output(rows)
176
+ rows: FastlaneCore::PrintTable.transform_output(builds)
126
177
  ))
127
178
  end
128
179
 
129
180
  def update_beta_app_meta(options, build)
130
- # App Store Connect API build id
131
- build_id = build.find_app_store_connect_build["id"]
132
-
133
181
  # Setting account required wth AppStore Connect API
134
- update_review_detail(build.app_id, { demo_account_required: options[:demo_account_required] })
182
+ update_review_detail(build, { demo_account_required: options[:demo_account_required] })
135
183
 
136
184
  if should_update_beta_app_review_info(options)
137
- update_review_detail(build.app_id, options[:beta_app_review_info])
185
+ update_review_detail(build, options[:beta_app_review_info])
138
186
  end
139
187
 
140
188
  if should_update_localized_app_information?(options)
141
- update_localized_app_review(build.app_id, options[:localized_app_info])
189
+ update_localized_app_review(build, options[:localized_app_info])
142
190
  elsif should_update_app_test_information?(options)
143
191
  default_info = {}
144
192
  default_info[:feedback_email] = options[:beta_app_feedback_email] if options[:beta_app_feedback_email]
145
193
  default_info[:description] = options[:beta_app_description] if options[:beta_app_description]
146
194
  begin
147
- update_localized_app_review(build.app_id, {}, default_info: default_info)
195
+ update_localized_app_review(build, {}, default_info: default_info)
148
196
  UI.success("Successfully set the beta_app_feedback_email and/or beta_app_description")
149
197
  rescue => ex
150
198
  UI.user_error!("Could not set beta_app_feedback_email and/or beta_app_description: #{ex}")
@@ -152,17 +200,17 @@ module Pilot
152
200
  end
153
201
 
154
202
  if should_update_localized_build_information?(options)
155
- update_localized_build_review(build_id, options[:localized_build_info])
203
+ update_localized_build_review(build, options[:localized_build_info])
156
204
  elsif should_update_build_information?(options)
157
205
  begin
158
- update_localized_build_review(build_id, {}, default_info: { whats_new: options[:changelog] })
206
+ update_localized_build_review(build, {}, default_info: { whats_new: options[:changelog] })
159
207
  UI.success("Successfully set the changelog for build")
160
208
  rescue => ex
161
209
  UI.user_error!("Could not set changelog: #{ex}")
162
210
  end
163
211
  end
164
212
 
165
- update_build_beta_details(build_id, {
213
+ update_build_beta_details(build, {
166
214
  auto_notify_enabled: options[:notify_external_testers]
167
215
  })
168
216
  end
@@ -242,37 +290,27 @@ module Pilot
242
290
  end
243
291
 
244
292
  def distribute_build(uploaded_build, options)
245
- UI.message("Distributing new build to testers: #{uploaded_build.train_version} - #{uploaded_build.build_version}")
293
+ UI.message("Distributing new build to testers: #{uploaded_build.app_version} - #{uploaded_build.version}")
246
294
 
247
295
  # This is where we could add a check to see if encryption is required and has been updated
248
296
  set_export_compliance_if_needed(uploaded_build, options)
249
297
 
250
298
  if options[:groups] || options[:distribute_external]
251
- begin
252
- uploaded_build.submit_for_testflight_review!
253
- rescue => ex
254
- # App Store Connect currently may 504 on this request even though it manages to get the build in
255
- # the approved state, this is a temporary workaround.
256
- raise ex unless ex.to_s.include?("504")
257
- UI.message("Submitting the build for review timed out, trying to recover.")
258
- updated_build = Spaceship::TestFlight::Build.find(app_id: uploaded_build.app_id, build_id: uploaded_build.id)
259
- raise ex unless updated_build.approved?
299
+ if uploaded_build.ready_for_beta_submission?
300
+ uploaded_build.post_beta_app_review_submission
301
+ else
302
+ UI.message("Build #{uploaded_build.app_version} - #{uploaded_build.version} already submitted for review")
260
303
  end
261
304
  end
262
305
 
263
306
  if options[:groups]
264
- client = Spaceship::ConnectAPI::Base.client
265
- beta_group_ids = client.get_beta_groups(filter: { app: uploaded_build.app_id }).select do |group|
266
- options[:groups].include?(group["attributes"]["name"])
267
- end.map do |group|
268
- group["id"]
307
+ app = uploaded_build.app
308
+ beta_groups = app.get_beta_groups.select do |group|
309
+ options[:groups].include?(group.name)
269
310
  end
270
311
 
271
- unless beta_group_ids.empty?
272
- build = uploaded_build.find_app_store_connect_build
273
- build_id = build["id"]
274
-
275
- client.add_beta_groups_to_build(build_id: build_id, beta_group_ids: beta_group_ids)
312
+ unless beta_groups.empty?
313
+ uploaded_build.add_beta_groups(beta_groups: beta_groups)
276
314
  end
277
315
  end
278
316
 
@@ -286,14 +324,12 @@ module Pilot
286
324
  end
287
325
 
288
326
  def set_export_compliance_if_needed(uploaded_build, options)
289
- build = uploaded_build.find_app_store_connect_build
290
- build_attributes = build["attributes"] || {}
291
- if build_attributes["usesNonExemptEncryption"].nil?
327
+ if uploaded_build.uses_non_exempt_encryption.nil?
292
328
  uses_non_exempt_encryption = options[:uses_non_exempt_encryption]
293
329
  attributes = { usesNonExemptEncryption: uses_non_exempt_encryption }
294
330
 
295
331
  client = Spaceship::ConnectAPI::Base.client
296
- client.patch_builds(build_id: build["id"], attributes: attributes)
332
+ client.patch_builds(build_id: uploaded_build.id, attributes: attributes)
297
333
 
298
334
  UI.important("Export compliance has been set to '#{uses_non_exempt_encryption}'. Need to wait for build to finishing processing again...")
299
335
  UI.important("Set 'ITSAppUsesNonExemptEncryption' in the 'Info.plist' to skip this step and speed up the submission")
@@ -301,7 +337,7 @@ module Pilot
301
337
  end
302
338
  end
303
339
 
304
- def update_review_detail(app_id, info)
340
+ def update_review_detail(build, info)
305
341
  info = info.collect { |k, v| [k.to_sym, v] }.to_h
306
342
 
307
343
  attributes = {}
@@ -315,10 +351,10 @@ module Pilot
315
351
  attributes[:notes] = info[:notes] if info.key?(:notes)
316
352
 
317
353
  client = Spaceship::ConnectAPI::Base.client
318
- client.patch_beta_app_review_detail(app_id: app_id, attributes: attributes)
354
+ client.patch_beta_app_review_detail(app_id: build.app.id, attributes: attributes)
319
355
  end
320
356
 
321
- def update_localized_app_review(app_id, info_by_lang, default_info: nil)
357
+ def update_localized_app_review(build, info_by_lang, default_info: nil)
322
358
  info_by_lang = info_by_lang.collect { |k, v| [k.to_sym, v] }.to_h
323
359
 
324
360
  if default_info
@@ -327,30 +363,28 @@ module Pilot
327
363
  default_info = info_by_lang.delete(:default)
328
364
  end
329
365
 
330
- # Initialize hash of lang codes
331
- langs_localization_ids = {}
366
+ # Initialize hash of lang codes with info_by_lang keys
367
+ localizations_by_lang = {}
368
+ info_by_lang.each_key do |key|
369
+ localizations_by_lang[key] = nil
370
+ end
332
371
 
333
372
  # Validate locales exist
334
- client = Spaceship::ConnectAPI::Base.client
335
- localizations = client.get_beta_app_localizations(filter: { app: app_id })
373
+ localizations = app.get_beta_app_localizations
336
374
  localizations.each do |localization|
337
- localization_id = localization["id"]
338
- attributes = localization["attributes"]
339
- locale = attributes["locale"]
340
-
341
- langs_localization_ids[locale.to_sym] = localization_id
375
+ localizations_by_lang[localization.locale.to_sym] = localization
342
376
  end
343
377
 
344
378
  # Create or update localized app review info
345
- langs_localization_ids.each do |lang_code, localization_id|
379
+ localizations_by_lang.each do |lang_code, localization|
346
380
  info = info_by_lang[lang_code]
347
381
 
348
382
  info = default_info unless info
349
- update_localized_app_review_for_lang(app_id, localization_id, lang_code, info) if info
383
+ update_localized_app_review_for_lang(app, localization, lang_code, info) if info
350
384
  end
351
385
  end
352
386
 
353
- def update_localized_app_review_for_lang(app_id, localization_id, locale, info)
387
+ def update_localized_app_review_for_lang(app, localization, locale, info)
354
388
  attributes = {}
355
389
  attributes[:feedbackEmail] = info[:feedback_email] if info.key?(:feedback_email)
356
390
  attributes[:marketingUrl] = info[:marketing_url] if info.key?(:marketing_url)
@@ -359,15 +393,15 @@ module Pilot
359
393
  attributes[:description] = info[:description] if info.key?(:description)
360
394
 
361
395
  client = Spaceship::ConnectAPI::Base.client
362
- if localization_id
363
- client.patch_beta_app_localizations(localization_id: localization_id, attributes: attributes)
396
+ if localization
397
+ client.patch_beta_app_localizations(localization_id: localization.id, attributes: attributes)
364
398
  else
365
399
  attributes[:locale] = locale if locale
366
- client.post_beta_app_localizations(app_id: app_id, attributes: attributes)
400
+ client.post_beta_app_localizations(app_id: app.id, attributes: attributes)
367
401
  end
368
402
  end
369
403
 
370
- def update_localized_build_review(build_id, info_by_lang, default_info: nil)
404
+ def update_localized_build_review(build, info_by_lang, default_info: nil)
371
405
  info_by_lang = info_by_lang.collect { |k, v| [k.to_sym, v] }.to_h
372
406
 
373
407
  if default_info
@@ -376,51 +410,49 @@ module Pilot
376
410
  default_info = info_by_lang.delete(:default)
377
411
  end
378
412
 
379
- # Initialize hash of lang codes
380
- langs_localization_ids = {}
413
+ # Initialize hash of lang codes with info_by_lang keys
414
+ localizations_by_lang = {}
415
+ info_by_lang.each_key do |key|
416
+ localizations_by_lang[key] = nil
417
+ end
381
418
 
382
419
  # Validate locales exist
383
- client = Spaceship::ConnectAPI::Base.client
384
- localizations = client.get_beta_build_localizations(filter: { build: build_id })
420
+ localizations = build.get_beta_build_localizations
385
421
  localizations.each do |localization|
386
- localization_id = localization["id"]
387
- attributes = localization["attributes"]
388
- locale = attributes["locale"]
389
-
390
- langs_localization_ids[locale.to_sym] = localization_id
422
+ localizations_by_lang[localization.locale.to_sym] = localization
391
423
  end
392
424
 
393
425
  # Create or update localized app review info
394
- langs_localization_ids.each do |lang_code, localization_id|
426
+ localizations_by_lang.each do |lang_code, localization|
395
427
  info = info_by_lang[lang_code]
396
428
 
397
429
  info = default_info unless info
398
- update_localized_build_review_for_lang(build_id, localization_id, lang_code, info) if info
430
+ update_localized_build_review_for_lang(build, localization, lang_code, info) if info
399
431
  end
400
432
  end
401
433
 
402
- def update_localized_build_review_for_lang(build_id, localization_id, locale, info)
434
+ def update_localized_build_review_for_lang(build, localization, locale, info)
403
435
  attributes = {}
404
- attributes[:whatsNew] = info[:whats_new] if info.key?(:whats_new)
436
+ attributes[:whatsNew] = self.class.sanitize_changelog(info[:whats_new]) if info.key?(:whats_new)
405
437
 
406
438
  client = Spaceship::ConnectAPI::Base.client
407
- if localization_id
408
- client.patch_beta_build_localizations(localization_id: localization_id, attributes: attributes)
439
+ if localization
440
+ client.patch_beta_build_localizations(localization_id: localization.id, attributes: attributes)
409
441
  else
410
442
  attributes[:locale] = locale if locale
411
- client.post_beta_build_localizations(build_id: build_id, attributes: attributes)
443
+ client.post_beta_build_localizations(build_id: build.id, attributes: attributes)
412
444
  end
413
445
  end
414
446
 
415
- def update_build_beta_details(build_id, info)
416
- client = Spaceship::ConnectAPI::Base.client
417
- resp = client.get_build_beta_details(filter: { build: build_id })
418
- build_beta_details_id = resp.first["id"]
447
+ def update_build_beta_details(build, info)
448
+ build_beta_detail = build.build_beta_detail
419
449
 
420
450
  attributes = {}
421
451
  attributes[:autoNotifyEnabled] = info[:auto_notify_enabled] if info.key?(:auto_notify_enabled)
422
452
 
423
- client.patch_build_beta_details(build_beta_details_id: build_beta_details_id, attributes: attributes)
453
+ client = Spaceship::ConnectAPI::Base.client
454
+ client.patch_build_beta_details(build_beta_details_id: build_beta_detail.id, attributes: attributes)
424
455
  end
425
456
  end
457
+ # rubocop:enable Metrics/ClassLength
426
458
  end