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 +4 -4
- data/{snapshot/lib → fastlane/lib/fastlane}/.DS_Store +0 -0
- data/fastlane/lib/fastlane/actions/app_store_build_number.rb +20 -5
- data/fastlane/lib/fastlane/actions/download_dsyms.rb +3 -5
- data/fastlane/lib/fastlane/actions/latest_testflight_build_number.rb +10 -0
- data/fastlane/lib/fastlane/setup/setup_ios.rb +5 -0
- data/fastlane/lib/fastlane/version.rb +1 -1
- data/pilot/lib/pilot/build_manager.rb +2 -4
- data/pilot/lib/pilot/commands_generator.rb +1 -0
- data/pilot/lib/pilot/options.rb +1 -0
- data/pilot/lib/pilot/tester_exporter.rb +8 -6
- data/pilot/lib/pilot/tester_manager.rb +23 -83
- data/pilot/lib/pilot/tester_util.rb +0 -16
- data/{deliver → precheck}/lib/.DS_Store +0 -0
- data/spaceship/lib/spaceship/test_flight/client.rb +14 -4
- data/spaceship/lib/spaceship/test_flight/group.rb +2 -10
- data/spaceship/lib/spaceship/test_flight/tester.rb +18 -1
- data/spaceship/lib/spaceship/tunes/application.rb +5 -5
- data/spaceship/lib/spaceship/tunes/sandbox_tester.rb +72 -0
- data/spaceship/lib/spaceship/tunes/tunes.rb +1 -1
- data/spaceship/lib/spaceship/tunes/tunes_client.rb +0 -121
- metadata +8 -8
- data/spaceship/lib/spaceship/tunes/tester.rb +0 -283
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6ec7b1127c00a1e221eb4d72105724ff0f4c0b37
|
|
4
|
+
data.tar.gz: d4d307e021d7548af03a580fc677bea6af828912
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0832577ea0780bf5c9b65e4aa5ca7225b8730376d03dbb1d4a440e9c1f209ec79a7661f29721f37a46746d04bea18cfbc5c7d1caea30e4a2d45913beefe11d85
|
|
7
|
+
data.tar.gz: 3b499a83814e5d15f8e052d8eaa75dba15e6cc6ce787f35931ed02c2d5ee0a6e163bfb121de21d2435c46b0408ca022cb088028b38806dca978f2253346039a6
|
|
Binary file
|
|
@@ -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.
|
|
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
|
-
|
|
42
|
-
build_nr
|
|
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!("
|
|
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
|
|
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.
|
|
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
|
|
@@ -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 #", "
|
|
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.
|
|
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
|
data/pilot/lib/pilot/options.rb
CHANGED
|
@@ -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
|
-
|
|
15
|
+
|
|
16
|
+
testers = Spaceship::TestFlight::Tester.all(app_id: app.apple_id)
|
|
16
17
|
else
|
|
17
|
-
testers = Spaceship::
|
|
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', '
|
|
24
|
+
csv << ['First', 'Last', 'Email', 'Groups', 'Installed Version', 'Install Date']
|
|
24
25
|
|
|
25
26
|
testers.each do |tester|
|
|
26
|
-
group_names = tester.
|
|
27
|
-
|
|
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,
|
|
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
|
-
|
|
22
|
-
|
|
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?
|
|
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
|
-
|
|
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::
|
|
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
|
|
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::
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
175
|
+
tester_groups
|
|
221
176
|
# Testers returned by the query made in the context of an app do not contain
|
|
222
|
-
# the
|
|
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.
|
|
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=
|
|
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
|
-
|
|
168
|
+
|
|
163
169
|
break if result.count == 0
|
|
164
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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/
|
|
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.
|
|
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-
|
|
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.
|
|
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.
|
|
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.
|
|
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
|