fastlane 2.65.0 → 2.66.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e6b2a78c9b6e3e48a30f9433cfda1c683e6a3ce6
4
- data.tar.gz: d6efe4c63a4b99dbc604001a97be4b5b1c5991c6
3
+ metadata.gz: 6ec7b1127c00a1e221eb4d72105724ff0f4c0b37
4
+ data.tar.gz: d4d307e021d7548af03a580fc677bea6af828912
5
5
  SHA512:
6
- metadata.gz: a71be3e1dc14441c66ca144644c91ef2b672745e7ff273e47a605ff24c3093ec7785c4c83c2b25b85fe782e2cabdd5e9d5140b5afe2fe0dc6324172f61af492c
7
- data.tar.gz: 89a89b2265e85b4e2c3edc6826628d22e0df9aa79561a71576e7a848ae4601155495615556225cb5059a848c785f6aa39119ce0719008028bb2cd84fedd715c9
6
+ metadata.gz: 0832577ea0780bf5c9b65e4aa5ca7225b8730376d03dbb1d4a440e9c1f209ec79a7661f29721f37a46746d04bea18cfbc5c7d1caea30e4a2d45913beefe11d85
7
+ data.tar.gz: 3b499a83814e5d15f8e052d8eaa75dba15e6cc6ce787f35931ed02c2d5ee0a6e163bfb121de21d2435c46b0408ca022cb088028b38806dca978f2253346039a6
@@ -13,6 +13,8 @@ module Fastlane
13
13
  Spaceship::Tunes.select_team
14
14
  UI.message("Login successful")
15
15
 
16
+ platform = params[:platform]
17
+
16
18
  app = Spaceship::Tunes::Application.find(params[:app_identifier])
17
19
  if params[:live]
18
20
  UI.message("Fetching the latest build number for live-version")
@@ -22,7 +24,7 @@ module Fastlane
22
24
  unless version_number
23
25
  # Automatically fetch the latest version in testflight
24
26
  begin
25
- testflight_version = app.build_trains.keys.last
27
+ testflight_version = app.all_build_train_numbers(platform: platform).sort_by { |v| Gem::Version.new(v) }.last
26
28
  rescue
27
29
  testflight_version = params[:version]
28
30
  end
@@ -38,14 +40,17 @@ module Fastlane
38
40
  UI.message("Fetching the latest build number for version #{version_number}")
39
41
 
40
42
  begin
41
- train = app.build_trains[version_number]
42
- build_nr = train.builds.map(&:build_version).map(&:to_i).sort.last
43
+ build_nr = app.all_builds_for_train(train: version_number, platform: platform).map(&:build_version).map(&:to_i).sort.last
44
+ if build_nr.nil? && params[:initial_build_number]
45
+ UI.message("Could not find a build on iTC. Using supplied 'initial_build_number' option")
46
+ build_nr = params[:initial_build_number]
47
+ end
43
48
  rescue
44
- UI.user_error!("could not find a build on iTC - and 'initial_build_number' option is not set") unless params[:initial_build_number]
49
+ UI.user_error!("Could not find a build on iTC - and 'initial_build_number' option is not set") unless params[:initial_build_number]
45
50
  build_nr = params[:initial_build_number]
46
51
  end
47
52
  end
48
- UI.message("Latest upload is build number: #{build_nr}")
53
+ UI.message("Latest upload for version #{version_number} is build: #{build_nr}")
49
54
  Actions.lane_context[SharedValues::LATEST_BUILD_NUMBER] = build_nr
50
55
  end
51
56
 
@@ -97,6 +102,16 @@ module Fastlane
97
102
  env_name: "LATEST_VERSION",
98
103
  description: "The version number whose latest build number we want",
99
104
  optional: true),
105
+ FastlaneCore::ConfigItem.new(key: :platform,
106
+ short_option: "-j",
107
+ env_name: "APPSTORE_PLATFORM",
108
+ description: "The platform to use (optional)",
109
+ optional: true,
110
+ is_string: true,
111
+ default_value: "ios",
112
+ verify_block: proc do |value|
113
+ UI.user_error!("The platform can only be ios, appletvos, or osx") unless %('ios', 'appletvos', 'osx').include? value
114
+ end),
100
115
  FastlaneCore::ConfigItem.new(key: :team_name,
101
116
  short_option: "-e",
102
117
  env_name: "LATEST_TESTFLIGHT_BUILD_NUMBER_TEAM_NAME",
@@ -47,9 +47,6 @@ module Fastlane
47
47
  UI.message(message.join(" "))
48
48
 
49
49
  # Loop through all app versions and download their dSYM
50
- # This will need to change with the new build trains endpoints
51
- # can use app.build_trains(platform: platform).versions to check for the right version
52
- # then iterate through the builds
53
50
  app.all_build_train_numbers(platform: platform).each do |train_number|
54
51
  if version && version != train_number
55
52
  next
@@ -60,7 +57,7 @@ module Fastlane
60
57
  end
61
58
 
62
59
  begin
63
- download_url = build.details.dsym_url
60
+ download_url = build.dsym_url
64
61
  rescue Spaceship::TunesClient::ITunesConnectError => ex
65
62
  UI.error("Error accessing dSYM file for build\n\n#{build}\n\nException: #{ex}")
66
63
  end
@@ -162,7 +159,8 @@ module Fastlane
162
159
  short_option: "-p",
163
160
  env_name: "DOWNLOAD_DSYMS_PLATFORM",
164
161
  description: "The app platform for dSYMs you wish to download",
165
- optional: true),
162
+ optional: true,
163
+ default_value: :ios),
166
164
  FastlaneCore::ConfigItem.new(key: :version,
167
165
  short_option: "-v",
168
166
  env_name: "DOWNLOAD_DSYMS_VERSION",
@@ -53,6 +53,16 @@ module Fastlane
53
53
  env_name: "LATEST_VERSION",
54
54
  description: "The version number whose latest build number we want",
55
55
  optional: true),
56
+ FastlaneCore::ConfigItem.new(key: :platform,
57
+ short_option: "-j",
58
+ env_name: "APPSTORE_PLATFORM",
59
+ description: "The platform to use (optional)",
60
+ optional: true,
61
+ is_string: true,
62
+ default_value: "ios",
63
+ verify_block: proc do |value|
64
+ UI.user_error!("The platform can only be ios, or appletvos") unless %('ios', 'appletvos').include? value
65
+ end),
56
66
  FastlaneCore::ConfigItem.new(key: :initial_build_number,
57
67
  env_name: "INITIAL_BUILD_NUMBER",
58
68
  description: "sets the build number to given value if no build is in current train",
@@ -34,6 +34,9 @@ module Fastlane
34
34
  detect_if_app_is_available
35
35
  end
36
36
  print_config_table
37
+ if self.project.schemes.count > 1
38
+ UI.important("Note: If the values above are incorrect, it is possible the wrong scheme was selected")
39
+ end
37
40
  if UI.confirm("Please confirm the above values")
38
41
  default_setup
39
42
  else
@@ -137,6 +140,8 @@ module Fastlane
137
140
  config = {}
138
141
  FastlaneCore::Project.detect_projects(config)
139
142
  self.project = FastlaneCore::Project.new(config)
143
+ self.project.select_scheme(preferred_to_include: self.project.project_name)
144
+
140
145
  self.app_identifier = self.project.default_app_identifier # These two vars need to be accessed in order to be set
141
146
  self.app_name = self.project.default_app_name # They are set as a side effect, this could/should be changed down the road
142
147
  end
@@ -1,5 +1,5 @@
1
1
  module Fastlane
2
- VERSION = '2.65.0'.freeze
2
+ VERSION = '2.66.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
@@ -98,7 +98,7 @@ module Pilot
98
98
 
99
99
  puts Terminal::Table.new(
100
100
  title: "#{app.name} Builds".green,
101
- headings: ["Version #", "Build #", "Testing", "Installs", "Sessions"],
101
+ headings: ["Version #", "Build #", "Installs"],
102
102
  rows: FastlaneCore::PrintTable.transform_output(rows)
103
103
  )
104
104
  end
@@ -119,9 +119,7 @@ module Pilot
119
119
  def describe_build(build)
120
120
  row = [build.train_version,
121
121
  build.build_version,
122
- build.testing_status,
123
- build.install_count,
124
- build.session_count]
122
+ build.install_count]
125
123
 
126
124
  return row
127
125
  end
@@ -104,6 +104,7 @@ module Pilot
104
104
 
105
105
  c.action do |args, options|
106
106
  config = create_config(options)
107
+ UI.user_error!("You must include an `app_identifier` to list testers") unless config[:app_identifier]
107
108
  Pilot::TesterManager.new.list_testers(config)
108
109
  end
109
110
  end
@@ -24,6 +24,7 @@ module Pilot
24
24
  env_name: "PILOT_PLATFORM",
25
25
  description: "The platform to use (optional)",
26
26
  optional: true,
27
+ default_value: 'ios',
27
28
  verify_block: proc do |value|
28
29
  UI.user_error!("The platform can only be ios, appletvos, or osx") unless ['ios', 'appletvos', 'osx'].include? value
29
30
  end),
@@ -12,22 +12,24 @@ module Pilot
12
12
  app_filter = (config[:apple_id] || config[:app_identifier])
13
13
  if app_filter
14
14
  app = Spaceship::Application.find(app_filter)
15
- testers = Spaceship::Tunes::Tester::External.all_by_app(app.apple_id)
15
+
16
+ testers = Spaceship::TestFlight::Tester.all(app_id: app.apple_id)
16
17
  else
17
- testers = Spaceship::Tunes::Tester::External.all
18
+ testers = Spaceship::TestFlight::Tester.all
18
19
  end
19
20
 
20
21
  file = config[:testers_file_path]
21
22
 
22
23
  CSV.open(file, "w") do |csv|
23
- csv << ['First', 'Last', 'Email', 'Groups', 'Devices', 'Installed Version', 'Install Date']
24
+ csv << ['First', 'Last', 'Email', 'Groups', 'Installed Version', 'Install Date']
24
25
 
25
26
  testers.each do |tester|
26
- group_names = tester.groups_list(';') || ""
27
- install_version = tester.latest_build || ""
27
+ group_names = tester.groups.join(";") || ""
28
+ latest_install_info = tester.latest_install_info
29
+ install_version = latest_install_info["latestInstalledShortVersion"] || ""
28
30
  pretty_date = tester.pretty_install_date || ""
29
31
 
30
- csv << [tester.first_name, tester.last_name, tester.email, group_names, tester.devices.count, install_version, pretty_date]
32
+ csv << [tester.first_name, tester.last_name, tester.email, group_names, install_version, pretty_date]
31
33
  end
32
34
 
33
35
  UI.success("Successfully exported CSV to #{file}")
@@ -9,6 +9,9 @@ module Pilot
9
9
  app = find_app(app_filter: config[:apple_id] || config[:app_identifier])
10
10
  UI.user_error!("You must provide either a Apple ID for the app (with the `:apple_id` option) or app identifier (with the `:app_identifier` option)") unless app
11
11
 
12
+ groups_param = config[:groups]
13
+ UI.user_error!("You must provide 1 or more groups (with the `:groups` option)") unless groups_param
14
+
12
15
  tester = find_app_tester(email: config[:email], app: app)
13
16
  tester ||= create_tester(
14
17
  email: config[:email],
@@ -17,18 +20,10 @@ module Pilot
17
20
  app: app
18
21
  )
19
22
  begin
23
+ # Groups are now required
20
24
  groups = Spaceship::TestFlight::Group.add_tester_to_groups!(tester: tester, app: app, groups: config[:groups])
21
- if tester.kind_of?(Spaceship::Tunes::Tester::Internal)
22
- UI.success("Successfully added tester to app #{app.name}")
23
- else
24
- # tester was added to the group(s) in the above add_tester_to_groups() call, now we need to let the user know which group(s)
25
- if config[:groups]
26
- group_names = groups.map(&:name).join(", ")
27
- UI.success("Successfully added tester to group(s): #{group_names} in app: #{app.name}")
28
- else
29
- UI.success("Successfully added tester to the default tester group in app: #{app.name}")
30
- end
31
- end
25
+ group_names = groups.map(&:name).join(", ")
26
+ UI.success("Successfully added tester to group(s): #{group_names} in app: #{app.name}")
32
27
  rescue => ex
33
28
  UI.error("Could not add #{tester.email} to app: #{app.name}")
34
29
  raise ex
@@ -66,7 +61,7 @@ module Pilot
66
61
  begin
67
62
  # If no groups are passed to options, remove the tester from the app-level,
68
63
  # otherwise remove the tester from the groups specified.
69
- if config[:groups].nil? && tester.kind_of?(Spaceship::Tunes::Tester::External)
64
+ if config[:groups].nil?
70
65
  test_flight_testers = Spaceship::TestFlight::Tester.search(app_id: app.apple_id, text: tester.email, is_email_exact_match: true)
71
66
 
72
67
  if test_flight_testers.length > 1
@@ -95,7 +90,7 @@ module Pilot
95
90
  if app_filter
96
91
  list_testers_by_app(app_filter)
97
92
  else
98
- list_testers_global
93
+ UI.user_error!("You must include an `app_identifier` to `list_testers`")
99
94
  end
100
95
  end
101
96
 
@@ -112,16 +107,15 @@ module Pilot
112
107
 
113
108
  def find_app_tester(email: nil, app: nil)
114
109
  current_user = find_current_user
110
+ app_apple_id = app.nil? ? nil : app.apple_id
115
111
 
116
112
  if current_user.admin?
117
- tester = Spaceship::Tunes::Tester::Internal.find(email)
118
- tester ||= Spaceship::Tunes::Tester::External.find(email)
113
+ tester = Spaceship::TestFlight::Tester.find(app_id: app_apple_id, email: email)
119
114
  elsif current_user.app_manager?
120
- unless app
115
+ unless app_apple_id
121
116
  UI.user_error!("Account #{current_user.email_address} is only an 'App Manager' and therefore you must also define what app this tester (#{email}) should be added to")
122
117
  end
123
- tester = Spaceship::Tunes::Tester::Internal.find_by_app(app.apple_id, email)
124
- tester ||= Spaceship::Tunes::Tester::External.find_by_app(app.apple_id, email)
118
+ tester = Spaceship::TestFlight::Tester.find(app_id: app_apple_id, email: email)
125
119
  else
126
120
  UI.user_error!("Account #{current_user.email_address} doesn't have a role that is allowed to administer app testers, current roles: #{current_user.roles}")
127
121
  tester = nil
@@ -147,24 +141,17 @@ module Pilot
147
141
 
148
142
  def create_tester(email: nil, first_name: nil, last_name: nil, app: nil)
149
143
  current_user = find_current_user
150
- if current_user.admin?
151
- tester = Spaceship::Tunes::Tester::External.create!(email: email,
152
- first_name: first_name,
153
- last_name: last_name)
154
- UI.success("Successfully added tester: #{email} to your account")
155
- elsif current_user.app_manager?
156
-
144
+ if current_user.admin? || current_user.app_manager?
157
145
  Spaceship::TestFlight::Tester.create_app_level_tester(app_id: app.apple_id,
158
- first_name: first_name,
159
- last_name: last_name,
146
+ first_name: first_name || '',
147
+ last_name: last_name || '',
160
148
  email: email)
161
- tester = Spaceship::Tunes::Tester::External.find_by_app(app.apple_id, email)
149
+
162
150
  UI.success("Successfully added tester: #{email} to app: #{app.name}")
151
+ return Spaceship::TestFlight::Tester.find(app_id: app.apple_id, email: email)
163
152
  else
164
153
  UI.user_error!("Current account doesn't have permission to create a tester")
165
154
  end
166
-
167
- return tester
168
155
  rescue => ex
169
156
  UI.error("Could not create tester #{email}")
170
157
  raise ex
@@ -173,53 +160,21 @@ module Pilot
173
160
  def list_testers_by_app(app_filter)
174
161
  app = Spaceship::Application.find(app_filter)
175
162
  UI.user_error!("Couldn't find app with '#{app_filter}'") unless app
176
-
177
- int_testers = Spaceship::Tunes::Tester::Internal.all_by_app(app.apple_id)
178
- ext_testers = Spaceship::Tunes::Tester::External.all_by_app(app.apple_id)
179
-
180
- list_by_app(int_testers, "Internal Testers")
181
- puts ""
182
- list_by_app(ext_testers, "External Testers")
183
- end
184
-
185
- def list_testers_global
186
- begin
187
- int_testers = Spaceship::Tunes::Tester::Internal.all
188
- ext_testers = Spaceship::Tunes::Tester::External.all
189
- rescue Spaceship::Client::InsufficientPermissions
190
- UI.user_error!("You don't have the permission to list the testers of your whole team. Please provide an app identifier to list all testers of a specific application.")
191
- end
192
-
193
- list_global(int_testers, "Internal Testers")
194
- puts ""
195
- list_global(ext_testers, "External Testers")
196
- end
197
-
198
- def list_global(all_testers, title)
199
- headers = ["First", "Last", "Email", "Groups", "Devices", "Latest Version", "Latest Install Date"]
200
- list(all_testers, "#{title} (#{all_testers.count})", headers) do |tester|
201
- [
202
- tester.first_name,
203
- tester.last_name,
204
- tester.email,
205
- tester.groups_list,
206
- tester.devices.count,
207
- tester.latest_build,
208
- tester.pretty_install_date
209
- ]
210
- end
163
+ testers = Spaceship::TestFlight::Tester.all(app_id: app.apple_id)
164
+ list_by_app(testers, "All Testers")
211
165
  end
212
166
 
213
167
  def list_by_app(all_testers, title)
214
168
  headers = ["First", "Last", "Email", "Groups"]
215
169
  list(all_testers, "#{title} (#{all_testers.count})", headers) do |tester|
170
+ tester_groups = tester.groups.nil? ? nil : tester.groups.join(";")
216
171
  [
217
172
  tester.first_name,
218
173
  tester.last_name,
219
174
  tester.email,
220
- tester.groups_list
175
+ tester_groups
221
176
  # Testers returned by the query made in the context of an app do not contain
222
- # the devices, version, or install date information
177
+ # the version, or install date information
223
178
  ]
224
179
  end
225
180
  end
@@ -245,7 +200,7 @@ module Pilot
245
200
  rows << ["Email", tester.email]
246
201
 
247
202
  if tester.groups.to_s.length > 0
248
- rows << ["Groups", tester.groups_list]
203
+ rows << ["Groups", tester.groups.join(";")]
249
204
  end
250
205
 
251
206
  if tester.latest_install_date
@@ -253,21 +208,6 @@ module Pilot
253
208
  rows << ["Latest Install Date", tester.pretty_install_date]
254
209
  end
255
210
 
256
- if tester.devices.to_s.length == 0
257
- rows << ["Devices", "No devices"]
258
- else
259
- rows << ["#{tester.devices.count} Devices", ""]
260
- tester.devices.each do |device|
261
- current = "\u2022 #{device['model']}, iOS #{device['osVersion']}"
262
-
263
- if rows.last[1].length == 0
264
- rows.last[1] = current
265
- else
266
- rows << ["", current]
267
- end
268
- end
269
- end
270
-
271
211
  puts Terminal::Table.new(
272
212
  title: tester.email.green,
273
213
  rows: FastlaneCore::PrintTable.transform_output(rows)
@@ -1,16 +0,0 @@
1
- require "spaceship/base"
2
- require "spaceship/tunes/tunes_base"
3
- require "spaceship/tunes/tester"
4
-
5
- module Spaceship
6
- module Tunes
7
- # monkey patched
8
- # move this to spaceship
9
- class Tester < TunesBase
10
- def pretty_install_date
11
- return nil unless latest_install_date
12
- Time.at((latest_install_date / 1000)).strftime("%m/%d/%y %H:%M")
13
- end
14
- end
15
- end
16
- end
Binary file
@@ -153,17 +153,27 @@ module Spaceship::TestFlight
153
153
  page_size = 40 # that's enforced by the iTC servers
154
154
  offset = nil
155
155
  resulting_array = []
156
+ initial_url = "providers/#{team_id}/apps/#{app_id}/testers?limit=#{page_size}&sort=email&order=asc"
157
+ response = request(:get, initial_url)
158
+ result = Array(handle_response(response))
159
+ resulting_array += result
160
+ return resulting_array if result.count == 0
161
+ offset = result.last['id']
156
162
 
157
163
  loop do
158
- url = "providers/#{team_id}/apps/#{app_id}/testers?limit=#{page_size}&sort=email&order=asc"
164
+ url = "providers/#{team_id}/apps/#{app_id}/testers?offset=added,#{offset}&limit=#{page_size}&sort=status,default&order=asc"
159
165
  url += "&offset=#{offset}" if offset
160
166
  response = request(:get, url)
161
167
  result = Array(handle_response(response))
162
- resulting_array += result
168
+
163
169
  break if result.count == 0
164
- offset = "#{result.last['email']}%2C#{result.last['id']}"
170
+ # If there are no more, the last response seems to be repeated
171
+ break if offset == result.last['id']
172
+
173
+ resulting_array += result
174
+ offset = result.last['id']
165
175
  end
166
- return resulting_array
176
+ return resulting_array.uniq
167
177
  end
168
178
 
169
179
  def delete_tester_from_app(app_id: nil, tester_id: nil)
@@ -71,19 +71,11 @@ module Spaceship::TestFlight
71
71
  end
72
72
 
73
73
  def self.add_tester_to_groups!(tester: nil, app: nil, groups: nil)
74
- if tester.kind_of?(Spaceship::Tunes::Tester::Internal)
75
- self.internal_group(app_id: app.apple_id).add_tester!(tester)
76
- else
77
- self.perform_for_groups_in_app(app: app, groups: groups) { |group| group.add_tester!(tester) }
78
- end
74
+ self.perform_for_groups_in_app(app: app, groups: groups) { |group| group.add_tester!(tester) }
79
75
  end
80
76
 
81
77
  def self.remove_tester_from_groups!(tester: nil, app: nil, groups: nil)
82
- if tester.kind_of?(Spaceship::Tunes::Tester::Internal)
83
- self.internal_group(app_id: app.apple_id).remove_tester!(tester)
84
- else
85
- self.perform_for_groups_in_app(app: app, groups: groups) { |group| group.remove_tester!(tester) }
86
- end
78
+ self.perform_for_groups_in_app(app: app, groups: groups) { |group| group.remove_tester!(tester) }
87
79
  end
88
80
 
89
81
  def default_external_group?
@@ -41,9 +41,13 @@ module Spaceship::TestFlight
41
41
  # }
42
42
  attr_accessor :latest_install_info
43
43
 
44
+ attr_accessor :latest_install_date
45
+
44
46
  # @return (Integer) Number of sessions
45
47
  attr_accessor :session_count
46
48
 
49
+ attr_accessor :groups
50
+
47
51
  attr_mapping(
48
52
  'id' => :tester_id,
49
53
  'email' => :email,
@@ -52,9 +56,22 @@ module Spaceship::TestFlight
52
56
  'latestInstallInfo' => :latest_install_info,
53
57
  'sessionCount' => :session_count,
54
58
  'firstName' => :first_name,
55
- 'lastName' => :last_name
59
+ 'lastName' => :last_name,
60
+ 'groups' => :groups
56
61
  )
57
62
 
63
+ def latest_install_date
64
+ return nil unless latest_install_info
65
+ latest_installed_date = latest_install_info["latestInstalledDate"]
66
+ return latest_installed_date
67
+ end
68
+
69
+ def pretty_install_date
70
+ return nil unless latest_installed_date
71
+
72
+ Time.at((latest_installed_date / 1000)).strftime("%m/%d/%y %H:%M")
73
+ end
74
+
58
75
  # @return (Array) Returns all beta testers available for this account
59
76
  def self.all(app_id: nil)
60
77
  client.testers_for_app(app_id: app_id).map { |data| self.new(data) }
@@ -261,13 +261,13 @@ module Spaceship
261
261
  # TestFlight: A reference to all the build trains
262
262
  # @return [Hash] a hash, the version number and platform being the key
263
263
  def build_trains(platform: nil)
264
- TestFlight::BuildTrains.all(app_id: self.apple_id, platform: platform)
264
+ TestFlight::BuildTrains.all(app_id: self.apple_id, platform: platform || self.platform)
265
265
  end
266
266
 
267
267
  # The numbers of all build trains that were uploaded
268
268
  # @return [Array] An array of train version numbers
269
269
  def all_build_train_numbers(platform: nil)
270
- return self.build_trains.versions
270
+ return self.build_trains(platform: platform || self.platform).versions
271
271
  end
272
272
 
273
273
  # Receive the build details for a specific build
@@ -275,19 +275,19 @@ module Spaceship
275
275
  # which might happen if you don't use TestFlight
276
276
  # This is used to receive dSYM files from Apple
277
277
  def all_builds_for_train(train: nil, platform: nil)
278
- return TestFlight::Build.builds_for_train(app_id: self.apple_id, platform: platform, train_version: train)
278
+ return TestFlight::Build.builds_for_train(app_id: self.apple_id, platform: platform || self.platform, train_version: train)
279
279
  end
280
280
 
281
281
  # @return [Array] This will return an array of *all* processing builds
282
282
  # this include pre-processing or standard processing
283
283
  def all_processing_builds(platform: nil)
284
- return TestFlight::Build.all_processing_builds(app_id: self.apple_id, platform: platform)
284
+ return TestFlight::Build.all_processing_builds(app_id: self.apple_id, platform: platform || self.platform)
285
285
  end
286
286
 
287
287
  # Get all builds that are already processed for all build trains
288
288
  # You can either use the return value (array) or pass a block
289
289
  def builds(platform: nil)
290
- all = TestFlight::Build.all(app_id: self.apple_id, platform: platform)
290
+ all = TestFlight::Build.all(app_id: self.apple_id, platform: platform || self.platform)
291
291
  return all unless block_given?
292
292
  all.each { |build| yield(build) }
293
293
  end
@@ -0,0 +1,72 @@
1
+ module Spaceship
2
+ module Tunes
3
+ class SandboxTester < TunesBase
4
+ # @return (String) The email of this sandbox tester
5
+ # @example
6
+ # "tester@spaceship.com"
7
+ attr_accessor :email
8
+
9
+ # @return (String) The first name of this tester
10
+ # @example
11
+ # "Cary"
12
+ attr_accessor :first_name
13
+
14
+ # @return (String) The last name of this tester
15
+ # @example
16
+ # "Bennett"
17
+ attr_accessor :last_name
18
+
19
+ # @return (String) The two-letter country code of this tester
20
+ # @example
21
+ # "US"
22
+ attr_accessor :country
23
+
24
+ attr_mapping(
25
+ 'emailAddress.value' => :email,
26
+ 'firstName.value' => :first_name,
27
+ 'lastName.value' => :last_name,
28
+ 'storeFront.value' => :country
29
+ )
30
+
31
+ def self.url
32
+ {
33
+ index: "ra/users/iap",
34
+ create: "ra/users/iap/add",
35
+ delete: "ra/users/iap/delete"
36
+ }
37
+ end
38
+
39
+ def self.all
40
+ client.sandbox_testers(self).map { |tester| self.new(tester) }
41
+ end
42
+
43
+ def self.create!(email: nil, password: nil, first_name: 'Test', last_name: 'Test', country: 'US')
44
+ data = client.create_sandbox_tester!(
45
+ tester_class: self,
46
+ email: email,
47
+ password: password,
48
+ first_name: first_name,
49
+ last_name: last_name,
50
+ country: country
51
+ )
52
+ self.new(data)
53
+ end
54
+
55
+ def self.delete!(emails)
56
+ client.delete_sandbox_testers!(self, emails)
57
+ end
58
+
59
+ def self.delete_all!
60
+ delete!(self.all.map(&:email))
61
+ end
62
+
63
+ #####################################################
64
+ # @!group Subclasses
65
+ #####################################################
66
+ # Delete current tester
67
+ def delete!
68
+ client.delete_tester!(self)
69
+ end
70
+ end
71
+ end
72
+ end
@@ -22,7 +22,7 @@ require 'spaceship/tunes/build_details'
22
22
  require 'spaceship/tunes/build_train'
23
23
  require 'spaceship/tunes/device_type'
24
24
  require 'spaceship/tunes/app_trailer'
25
- require 'spaceship/tunes/tester'
25
+ require 'spaceship/tunes/sandbox_tester'
26
26
  require 'spaceship/tunes/app_details'
27
27
  require 'spaceship/tunes/pricing_tier'
28
28
  require 'spaceship/tunes/recovery_device'
@@ -1172,127 +1172,6 @@ module Spaceship
1172
1172
  handle_itc_response(r.body)
1173
1173
  end
1174
1174
 
1175
- #####################################################
1176
- # @!group Testers
1177
- #####################################################
1178
- def testers(tester)
1179
- url = tester.url[:index]
1180
- r = request(:get, url)
1181
- parse_response(r, 'data')['testers']
1182
- end
1183
-
1184
- def testers_by_app(tester, app_id)
1185
- url = tester.url(app_id)[:index_by_app]
1186
- r = request(:get, url)
1187
- parse_response(r, 'data')['users']
1188
- end
1189
-
1190
- # Returns a list of available testing groups
1191
- # e.g.
1192
- # {"b6f65dbd-c845-4d91-bc39-0b661d608970" => "Boarding",
1193
- # "70402368-9deb-409f-9a26-bb3f215dfee3" => "Automatic"}
1194
- def groups
1195
- return @cached_groups if @cached_groups
1196
- r = request(:get, '/WebObjects/iTunesConnect.woa/ra/users/pre/ext')
1197
- @cached_groups = parse_response(r, 'data')['groups']
1198
- end
1199
-
1200
- def create_tester!(tester: nil, email: nil, first_name: nil, last_name: nil, groups: nil)
1201
- url = tester.url[:create]
1202
- raise "Action not provided for this tester type." unless url
1203
-
1204
- tester_data = {
1205
- emailAddress: {
1206
- value: email
1207
- },
1208
- firstName: {
1209
- value: first_name || ""
1210
- },
1211
- lastName: {
1212
- value: last_name || ""
1213
- },
1214
- testing: {
1215
- value: true
1216
- }
1217
- }
1218
-
1219
- if groups
1220
- tester_data[:groups] = groups.map do |group_name_or_group_id|
1221
- if self.groups.value?(group_name_or_group_id)
1222
- # This is an existing group, let's use that, the user specified the group name
1223
- group_name = group_name_or_group_id
1224
- group_id = self.groups.key(group_name_or_group_id)
1225
- elsif self.groups.key?(group_name_or_group_id)
1226
- # This is an existing group, let's use that, the user specified the group ID
1227
- group_name = self.groups[group_name_or_group_id]
1228
- group_id = group_name_or_group_id
1229
- else
1230
- group_name = group_name_or_group_id
1231
- group_id = nil # this is expected by the iTC API
1232
- end
1233
-
1234
- {
1235
- "id" => group_id,
1236
- "name" => {
1237
- "value" => group_name
1238
- }
1239
- }
1240
- end
1241
- end
1242
-
1243
- data = { testers: [tester_data] }
1244
-
1245
- r = request(:post) do |req|
1246
- req.url url
1247
- req.body = data.to_json
1248
- req.headers['Content-Type'] = 'application/json'
1249
- end
1250
-
1251
- data = parse_response(r, 'data')['testers']
1252
- handle_itc_response(data) || data[0]
1253
- end
1254
-
1255
- def delete_tester!(tester)
1256
- url = tester.class.url[:delete]
1257
- raise "Action not provided for this tester type." unless url
1258
-
1259
- data = [
1260
- {
1261
- emailAddress: {
1262
- value: tester.email
1263
- },
1264
- firstName: {
1265
- value: tester.first_name
1266
- },
1267
- lastName: {
1268
- value: tester.last_name
1269
- },
1270
- testing: {
1271
- value: false
1272
- },
1273
- userName: tester.email,
1274
- testerId: tester.tester_id
1275
- }
1276
- ]
1277
-
1278
- r = request(:post) do |req|
1279
- req.url url
1280
- req.body = data.to_json
1281
- req.headers['Content-Type'] = 'application/json'
1282
- end
1283
-
1284
- data = parse_response(r, 'data')['testers']
1285
- handle_itc_response(data) || data[0]
1286
- end
1287
-
1288
- def add_tester_to_app!(tester, app_id)
1289
- update_tester_from_app!(tester, app_id, true)
1290
- end
1291
-
1292
- def remove_tester_from_app!(tester, app_id)
1293
- update_tester_from_app!(tester, app_id, false)
1294
- end
1295
-
1296
1175
  #####################################################
1297
1176
  # @!group Sandbox Testers
1298
1177
  #####################################################
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fastlane
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.65.0
4
+ version: 2.66.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Felix Krause
@@ -15,7 +15,7 @@ authors:
15
15
  autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
- date: 2017-11-20 00:00:00.000000000 Z
18
+ date: 2017-11-21 00:00:00.000000000 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: slack-notifier
@@ -225,14 +225,14 @@ dependencies:
225
225
  requirements:
226
226
  - - "~>"
227
227
  - !ruby/object:Gem::Version
228
- version: 0.5.0
228
+ version: 0.6.2
229
229
  type: :runtime
230
230
  prerelease: false
231
231
  version_requirements: !ruby/object:Gem::Requirement
232
232
  requirements:
233
233
  - - "~>"
234
234
  - !ruby/object:Gem::Version
235
- version: 0.5.0
235
+ version: 0.6.2
236
236
  - !ruby/object:Gem::Dependency
237
237
  name: babosa
238
238
  requirement: !ruby/object:Gem::Requirement
@@ -808,7 +808,6 @@ files:
808
808
  - credentials_manager/lib/credentials_manager/appfile_config.rb
809
809
  - credentials_manager/lib/credentials_manager/cli.rb
810
810
  - deliver/README.md
811
- - deliver/lib/.DS_Store
812
811
  - deliver/lib/assets/DeliverfileDefault
813
812
  - deliver/lib/assets/ScreenshotsHelp
814
813
  - deliver/lib/assets/summary.html.erb
@@ -846,6 +845,7 @@ files:
846
845
  - fastlane/lib/assets/s3_plist_template.erb
847
846
  - fastlane/lib/assets/s3_version_template.erb
848
847
  - fastlane/lib/fastlane.rb
848
+ - fastlane/lib/fastlane/.DS_Store
849
849
  - fastlane/lib/fastlane/action.rb
850
850
  - fastlane/lib/fastlane/action_collector.rb
851
851
  - fastlane/lib/fastlane/actions/README.md
@@ -1294,6 +1294,7 @@ files:
1294
1294
  - pilot/lib/pilot/tester_manager.rb
1295
1295
  - pilot/lib/pilot/tester_util.rb
1296
1296
  - precheck/README.md
1297
+ - precheck/lib/.DS_Store
1297
1298
  - precheck/lib/assets/PrecheckfileTemplate
1298
1299
  - precheck/lib/precheck.rb
1299
1300
  - precheck/lib/precheck/commands_generator.rb
@@ -1363,7 +1364,6 @@ files:
1363
1364
  - sigh/lib/sigh/resign.rb
1364
1365
  - sigh/lib/sigh/runner.rb
1365
1366
  - snapshot/README.md
1366
- - snapshot/lib/.DS_Store
1367
1367
  - snapshot/lib/assets/SnapfileTemplate
1368
1368
  - snapshot/lib/assets/SnapshotHelper.swift
1369
1369
  - snapshot/lib/assets/SnapshotHelperXcode8.swift
@@ -1477,9 +1477,9 @@ files:
1477
1477
  - spaceship/lib/spaceship/tunes/members.rb
1478
1478
  - spaceship/lib/spaceship/tunes/pricing_tier.rb
1479
1479
  - spaceship/lib/spaceship/tunes/recovery_device.rb
1480
+ - spaceship/lib/spaceship/tunes/sandbox_tester.rb
1480
1481
  - spaceship/lib/spaceship/tunes/spaceship.rb
1481
1482
  - spaceship/lib/spaceship/tunes/territory.rb
1482
- - spaceship/lib/spaceship/tunes/tester.rb
1483
1483
  - spaceship/lib/spaceship/tunes/transit_app_file.rb
1484
1484
  - spaceship/lib/spaceship/tunes/tunes.rb
1485
1485
  - spaceship/lib/spaceship/tunes/tunes_base.rb
@@ -1536,7 +1536,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
1536
1536
  version: '0'
1537
1537
  requirements: []
1538
1538
  rubyforge_project:
1539
- rubygems_version: 2.6.8
1539
+ rubygems_version: 2.2.5
1540
1540
  signing_key:
1541
1541
  specification_version: 4
1542
1542
  summary: The easiest way to automate beta deployments and releases for your iOS and
@@ -1,283 +0,0 @@
1
- module Spaceship
2
- module Tunes
3
- class Tester < TunesBase
4
- # @return (String) The identifier of this tester, provided by iTunes Connect
5
- # @example
6
- # "60f858b4-60a8-428a-963a-f943a3d68d17"
7
- attr_accessor :tester_id
8
-
9
- # @return (String) The email of this tester
10
- # @example
11
- # "tester@spaceship.com"
12
- attr_accessor :email
13
-
14
- # @return (String) The first name of this tester
15
- # @example
16
- # "Cary"
17
- attr_accessor :first_name
18
-
19
- # @return (String) The last name of this tester
20
- # @example
21
- # "Bennett"
22
- attr_accessor :last_name
23
-
24
- # @return (Array) an array of associated groups
25
- # @example
26
- # [{
27
- # "id": "e031e048-4f0f-4c1e-8d8a-a5341a267986",
28
- # "name": {
29
- # "value": "My App Testers"
30
- # }
31
- # }]
32
- attr_accessor :groups
33
-
34
- # @return (Array) An array of registered devices for this user
35
- # @example
36
- # [{
37
- # "model": "iPhone 6",
38
- # "os": "iOS",
39
- # "osVersion": "8.3",
40
- # "name": null
41
- # }]
42
- attr_accessor :devices
43
-
44
- # Information about the most recent beta install
45
- # @return [Integer] The ID of the most recently installed app
46
- attr_accessor :latest_install_app_id
47
-
48
- # @return [Integer] Date of the last install of this tester
49
- # Number of seconds since 1970
50
- attr_accessor :latest_install_date
51
-
52
- # @return [Integer] The build number of the last installed build
53
- attr_accessor :latest_installed_build_number
54
-
55
- # @return [Integer] The version number of the last installed build
56
- attr_accessor :latest_installed_version_number
57
-
58
- # @return [String] The full version of the last installed build
59
- attr_accessor :latest_build
60
-
61
- attr_mapping(
62
- 'testerId' => :tester_id,
63
- 'emailAddress.value' => :email,
64
- 'firstName.value' => :first_name,
65
- 'lastName.value' => :last_name,
66
- 'groups' => :groups,
67
- 'devices' => :devices,
68
- 'latestInstalledAppAdamId' => :latest_install_app_id,
69
- 'latestInstallByPlatform.ios.installDate' => :latest_install_date,
70
- 'latestInstallByPlatform.ios.shortVersion' => :latest_installed_version_number,
71
- 'latestInstallByPlatform.ios.version' => :latest_installed_build_number,
72
- 'latestBuild' => :latest_build
73
- )
74
-
75
- class << self
76
- # @return (Hash) All urls for the ITC used for web requests
77
- def url
78
- raise "You have to use a subclass: Internal or External"
79
- end
80
-
81
- # @return (Array) Returns all beta testers available for this account
82
- def all
83
- client.testers(self).map { |tester| self.factory(tester) }
84
- end
85
-
86
- # @return (Spaceship::Tunes::Tester) Returns the tester matching the parameter
87
- # as either the Tester id or email
88
- # @param identifier (String) (required): Value used to filter the tester, case insensitive
89
- def find(identifier)
90
- all.find do |tester|
91
- (tester.tester_id.to_s.casecmp(identifier.to_s).zero? or tester.email.to_s.casecmp(identifier.to_s).zero?)
92
- end
93
- end
94
-
95
- def groups
96
- client.groups
97
- end
98
-
99
- # Create new tester in iTunes Connect
100
- # @param email (String) (required): The email of the new tester
101
- # @param first_name (String) (optional): The first name of the new tester
102
- # @param last_name (String) (optional): The last name of the new tester
103
- # @param groups (Array) (option): Names/IDs of existing groups for the new tester
104
- # @example
105
- # Spaceship::Tunes::Tester.external.create!(email: "tester@mathiascarignani.com", first_name: "Cary", last_name: "Bennett", groups: ["Testers"])
106
- # @return (Tester): The newly created tester
107
- def create!(email: nil, first_name: nil, last_name: nil, groups: nil)
108
- client.create_tester!(tester: self,
109
- email: email,
110
- first_name: first_name,
111
- last_name: last_name,
112
- groups: groups)
113
- # The response of the client request is a hash but doesn't contain all of the necessary data (like testerId, etc)
114
- # use #find to get an instance of Tester with all of the data.
115
- find(email)
116
- end
117
-
118
- #####################################################
119
- # @!group App
120
- #####################################################
121
-
122
- # @return (Array) Returns all beta testers available for this account filtered by app
123
- # @param app_id (String) (required): The app id to filter the testers
124
- def all_by_app(app_id)
125
- client.testers_by_app(self, app_id).map { |tester| self.factory(tester) }
126
- end
127
-
128
- # @return (Spaceship::Tunes::Tester) Returns the tester matching the parameter
129
- # as either the Tester id or email
130
- # @param app_id (String) (required): The app id to filter the testers
131
- # @param identifier (String) (required): Value used to filter the tester, case insensitive
132
- def find_by_app(app_id, identifier)
133
- all_by_app(app_id).find do |tester|
134
- (tester.tester_id.to_s.casecmp(identifier.to_s).zero? or tester.email.to_s.casecmp(identifier.to_s).zero?)
135
- end
136
- end
137
-
138
- # Add all testers to the app received
139
- # @param app_id (String) (required): The app id to filter the testers
140
- def add_all_to_app!(app_id)
141
- all.each do |tester|
142
- begin
143
- tester.add_to_app!(app_id)
144
- rescue => ex
145
- if ex.to_s.include? "testerEmailExistsInternal" or ex.to_s.include? "duplicate.email"
146
- # That's a non-relevant error message by iTC
147
- # ignore that
148
- else
149
- raise ex
150
- end
151
- end
152
- end
153
- end
154
- end
155
-
156
- def setup
157
- self.devices ||= [] # by default, an empty array instead of nil
158
- end
159
-
160
- #####################################################
161
- # @!group Subclasses
162
- #####################################################
163
- class External < Tester
164
- def self.url(app_id = nil)
165
- {
166
- index: "ra/users/pre/ext",
167
- index_by_app: "ra/user/externalTesters/#{app_id}/",
168
- create: "ra/users/pre/create",
169
- delete: "ra/users/pre/ext/delete",
170
- update_by_app: "ra/user/externalTesters/#{app_id}/"
171
- }
172
- end
173
- end
174
-
175
- class Internal < Tester
176
- def self.url(app_id = nil)
177
- {
178
- index: "ra/users/pre/int",
179
- index_by_app: "ra/user/internalTesters/#{app_id}/",
180
- create: nil,
181
- delete: nil,
182
- update_by_app: "ra/user/internalTesters/#{app_id}/"
183
- }
184
- end
185
- end
186
-
187
- # Delete current tester
188
- def delete!
189
- client.delete_tester!(self)
190
- end
191
-
192
- #####################################################
193
- # @!group App
194
- #####################################################
195
-
196
- # Add current tester to list of the app testers
197
- # @param app_id (String) (required): The id of the application to which want to modify the list
198
- def add_to_app!(app_id)
199
- raise "`[tester].add_to_app!` got removed from spaceship as the TestFlight API changed, please use `app.default_external_group.add_tester!(tester)` instead"
200
- end
201
-
202
- # Remove current tester from list of the app testers
203
- # @param app_id (String) (required): The id of the application to which want to modify the list
204
- def remove_from_app!(app_id)
205
- raise "`[tester].remove_from_app!` got removed from spaceship as the TestFlight API changed, please use `app.default_external_group.remove_tester!(tester)` instead"
206
- end
207
-
208
- #####################################################
209
- # @!group Helpers
210
- #####################################################
211
-
212
- # Return a list of the Tester's group, if any
213
- # @return
214
- def groups_list(separator = ', ')
215
- if groups
216
- group_names = groups.map { |group| group["name"]["value"] }
217
- group_names.join(separator)
218
- end
219
- end
220
- end
221
-
222
- class SandboxTester < TunesBase
223
- # @return (String) The email of this tester
224
- # @example
225
- # "tester@spaceship.com"
226
- attr_accessor :email
227
-
228
- # @return (String) The first name of this tester
229
- # @example
230
- # "Cary"
231
- attr_accessor :first_name
232
-
233
- # @return (String) The last name of this tester
234
- # @example
235
- # "Bennett"
236
- attr_accessor :last_name
237
-
238
- # @return (String) The two-letter country code of this tester
239
- # @example
240
- # "US"
241
- attr_accessor :country
242
-
243
- attr_mapping(
244
- 'emailAddress.value' => :email,
245
- 'firstName.value' => :first_name,
246
- 'lastName.value' => :last_name,
247
- 'storeFront.value' => :country
248
- )
249
-
250
- def self.url
251
- {
252
- index: "ra/users/iap",
253
- create: "ra/users/iap/add",
254
- delete: "ra/users/iap/delete"
255
- }
256
- end
257
-
258
- def self.all
259
- client.sandbox_testers(self).map { |tester| self.new(tester) }
260
- end
261
-
262
- def self.create!(email: nil, password: nil, first_name: 'Test', last_name: 'Test', country: 'US')
263
- data = client.create_sandbox_tester!(
264
- tester_class: self,
265
- email: email,
266
- password: password,
267
- first_name: first_name,
268
- last_name: last_name,
269
- country: country
270
- )
271
- self.new(data)
272
- end
273
-
274
- def self.delete!(emails)
275
- client.delete_sandbox_testers!(self, emails)
276
- end
277
-
278
- def self.delete_all!
279
- delete!(self.all.map(&:email))
280
- end
281
- end
282
- end
283
- end