fastlane 2.65.0 → 2.66.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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