fastlane 2.115.0.beta.20190119200019 → 2.115.0.beta.20190120200101
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/fastlane/lib/fastlane/fast_file.rb +9 -5
- data/fastlane/lib/fastlane/version.rb +1 -1
- data/fastlane_core/lib/fastlane_core/helper.rb +16 -0
- data/fastlane_core/lib/fastlane_core/provisioning_profile.rb +9 -3
- data/match/lib/match/encryption/openssl.rb +1 -1
- data/match/lib/match/options.rb +4 -0
- data/match/lib/match/runner.rb +37 -15
- data/match/lib/match/storage/git_storage.rb +12 -8
- data/match/lib/match/utils.rb +2 -2
- data/scan/lib/scan/options.rb +20 -0
- data/scan/lib/scan/slack_poster.rb +4 -2
- data/sigh/lib/sigh/runner.rb +10 -7
- data/spaceship/lib/spaceship/client.rb +2 -2
- data/spaceship/lib/spaceship/tunes/tunes.rb +0 -1
- data/spaceship/lib/spaceship/tunes/tunes_client.rb +7 -10
- data/spaceship/lib/spaceship/two_step_or_factor_client.rb +104 -45
- metadata +14 -15
- data/spaceship/lib/spaceship/tunes/recovery_device.rb +0 -65
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ca7d2255218f2b7f48d1b95b3d48abee6957abd2
|
4
|
+
data.tar.gz: a61e4ceef9c859240a85d52a4046c8aab11ad10b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3e69aacb4427d3d84b4f348ecf78cb0eb83e3944e770f9d17673e25765b0ae4cfebb71ef258efd9cee63f79057fb0a88bb1de1aadc5efd5e3470606f03791226
|
7
|
+
data.tar.gz: 3a43f626ddbf582c1b5666c544a103d2ffead5b9879db53194de284033323d75a08f082ae51afc264968754ceb282a146a82238610a995caad73d92ceb739a88
|
@@ -254,7 +254,9 @@ module Fastlane
|
|
254
254
|
branch_option = "--branch #{branch}" if branch != 'HEAD'
|
255
255
|
|
256
256
|
UI.message("Cloning remote git repo...")
|
257
|
-
|
257
|
+
Helper.with_env_values('GIT_TERMINAL_PROMPT' => '0') do
|
258
|
+
Actions.sh("git clone #{url.shellescape} #{clone_folder.shellescape} --depth 1 -n #{branch_option}")
|
259
|
+
end
|
258
260
|
|
259
261
|
unless version.nil?
|
260
262
|
req = Gem::Requirement.new(version)
|
@@ -263,14 +265,14 @@ module Fastlane
|
|
263
265
|
UI.user_error!("No tag found matching #{version.inspect}") if checkout_param.nil?
|
264
266
|
end
|
265
267
|
|
266
|
-
Actions.sh("cd
|
268
|
+
Actions.sh("cd #{clone_folder.shellescape} && git checkout #{checkout_param.shellescape} #{path.shellescape}")
|
267
269
|
|
268
270
|
# We also want to check out all the local actions of this fastlane setup
|
269
271
|
containing = path.split(File::SEPARATOR)[0..-2]
|
270
272
|
containing = "." if containing.count == 0
|
271
273
|
actions_folder = File.join(containing, "actions")
|
272
274
|
begin
|
273
|
-
Actions.sh("cd
|
275
|
+
Actions.sh("cd #{clone_folder.shellescape} && git checkout #{checkout_param.shellescape} #{actions_folder.shellescape}")
|
274
276
|
rescue
|
275
277
|
# We don't care about a failure here, as local actions are optional
|
276
278
|
end
|
@@ -290,10 +292,12 @@ module Fastlane
|
|
290
292
|
|
291
293
|
def fetch_remote_tags(folder: nil)
|
292
294
|
UI.message("Fetching remote git tags...")
|
293
|
-
|
295
|
+
Helper.with_env_values('GIT_TERMINAL_PROMPT' => '0') do
|
296
|
+
Actions.sh("cd #{folder.shellescape} && git fetch --all --tags -q")
|
297
|
+
end
|
294
298
|
|
295
299
|
# Fetch all possible tags
|
296
|
-
git_tags_string = Actions.sh("cd
|
300
|
+
git_tags_string = Actions.sh("cd #{folder.shellescape} && git tag -l")
|
297
301
|
git_tags = git_tags_string.split("\n")
|
298
302
|
|
299
303
|
# Sort tags based on their version number
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module Fastlane
|
2
|
-
VERSION = '2.115.0.beta.
|
2
|
+
VERSION = '2.115.0.beta.20190120200101'.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
|
@@ -299,6 +299,22 @@ module FastlaneCore
|
|
299
299
|
Helper.backticks(command, print: print)
|
300
300
|
end
|
301
301
|
|
302
|
+
# Executes the provided block after adjusting the ENV to have the
|
303
|
+
# provided keys and values set as defined in hash. After the block
|
304
|
+
# completes, restores the ENV to its previous state.
|
305
|
+
def self.with_env_values(hash, &block)
|
306
|
+
old_vals = ENV.select { |k, v| hash.include?(k) }
|
307
|
+
hash.each do |k, v|
|
308
|
+
ENV[k] = hash[k]
|
309
|
+
end
|
310
|
+
yield
|
311
|
+
ensure
|
312
|
+
hash.each do |k, v|
|
313
|
+
ENV.delete(k) unless old_vals.include?(k)
|
314
|
+
ENV[k] = old_vals[k]
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
302
318
|
# loading indicator
|
303
319
|
#
|
304
320
|
|
@@ -91,10 +91,16 @@ module FastlaneCore
|
|
91
91
|
err = "#{dir}/cms.err"
|
92
92
|
# we want to prevent the error output to mix up with the standard output because of
|
93
93
|
# /dev/null: https://github.com/fastlane/fastlane/issues/6387
|
94
|
-
if
|
95
|
-
|
94
|
+
if Helper.mac?
|
95
|
+
if keychain_path.nil?
|
96
|
+
decoded = `security cms -D -i "#{path}" 2> #{err}`
|
97
|
+
else
|
98
|
+
decoded = `security cms -D -i "#{path}" -k "#{keychain_path.shellescape}" 2> #{err}`
|
99
|
+
end
|
96
100
|
else
|
97
|
-
|
101
|
+
# `security` only works on Mac, fallback to `openssl`
|
102
|
+
# via https://stackoverflow.com/a/14379814/252627
|
103
|
+
decoded = `openssl smime -inform der -verify -noverify -in #{path} 2> #{err}`
|
98
104
|
end
|
99
105
|
UI.error("Failure to decode #{path}. Exit: #{$?.exitstatus}: #{File.read(err)}") if $?.exitstatus != 0
|
100
106
|
decoded
|
@@ -116,7 +116,7 @@ module Match
|
|
116
116
|
def encrypt_specific_file(path: nil, password: nil)
|
117
117
|
UI.user_error!("No password supplied") if password.to_s.strip.length == 0
|
118
118
|
|
119
|
-
data_to_encrypt = File.
|
119
|
+
data_to_encrypt = File.binread(path)
|
120
120
|
salt = SecureRandom.random_bytes(8)
|
121
121
|
|
122
122
|
# The :: is important, as there is a name clash
|
data/match/lib/match/options.rb
CHANGED
@@ -174,6 +174,10 @@ module Match
|
|
174
174
|
description: "The name of provisioning profile template. If the developer account has provisioning profile templates (aka: custom entitlements), the template name can be found by inspecting the Entitlements drop-down while creating/editing a provisioning profile (e.g. \"Apple Pay Pass Suppression Development\")",
|
175
175
|
optional: true,
|
176
176
|
default_value: nil),
|
177
|
+
FastlaneCore::ConfigItem.new(key: :output_path,
|
178
|
+
env_name: "MATCH_OUTPUT_PATH",
|
179
|
+
description: "Path in which to export certificates, key and profile",
|
180
|
+
optional: true),
|
177
181
|
|
178
182
|
# other
|
179
183
|
FastlaneCore::ConfigItem.new(key: :verbose,
|
data/match/lib/match/runner.rb
CHANGED
@@ -25,6 +25,8 @@ module Match
|
|
25
25
|
def run(params)
|
26
26
|
self.files_to_commit = []
|
27
27
|
|
28
|
+
FileUtils.mkdir_p(params[:output_path]) if params[:output_path]
|
29
|
+
|
28
30
|
FastlaneCore::PrintTable.print_values(config: params,
|
29
31
|
title: "Summary for match #{Fastlane::VERSION}")
|
30
32
|
|
@@ -166,21 +168,31 @@ module Match
|
|
166
168
|
self.files_to_commit << private_key_path
|
167
169
|
else
|
168
170
|
cert_path = certs.last
|
169
|
-
UI.message("Installing certificate...")
|
170
171
|
|
171
|
-
|
172
|
-
|
173
|
-
|
172
|
+
if Helper.mac?
|
173
|
+
UI.message("Installing certificate...")
|
174
|
+
|
175
|
+
# Only looking for cert in "custom" (non login.keychain) keychain
|
176
|
+
# Doing this for backwards compatability
|
177
|
+
keychain_name = params[:keychain_name] == "login.keychain" ? nil : params[:keychain_name]
|
178
|
+
|
179
|
+
if FastlaneCore::CertChecker.installed?(cert_path, in_keychain: keychain_name)
|
180
|
+
UI.verbose("Certificate '#{File.basename(cert_path)}' is already installed on this machine")
|
181
|
+
else
|
182
|
+
Utils.import(cert_path, params[:keychain_name], password: params[:keychain_password])
|
183
|
+
end
|
174
184
|
|
175
|
-
|
176
|
-
|
185
|
+
# Import the private key
|
186
|
+
# there seems to be no good way to check if it's already installed - so just install it
|
187
|
+
Utils.import(keys.last, params[:keychain_name], password: params[:keychain_password])
|
177
188
|
else
|
178
|
-
|
189
|
+
UI.message("Skipping installation of certificate as it would not work on this operating system.")
|
179
190
|
end
|
180
191
|
|
181
|
-
|
182
|
-
|
183
|
-
|
192
|
+
if params[:output_path]
|
193
|
+
FileUtils.cp(cert_path, params[:output_path])
|
194
|
+
FileUtils.cp(keys.last, params[:output_path])
|
195
|
+
end
|
184
196
|
|
185
197
|
# Get and print info of certificate
|
186
198
|
info = Utils.get_cert_info(cert_path)
|
@@ -202,7 +214,9 @@ module Match
|
|
202
214
|
profile_name = names.join("_").gsub("*", '\*') # this is important, as it shouldn't be a wildcard
|
203
215
|
base_dir = File.join(prefixed_working_directory(working_directory), "profiles", prov_type.to_s)
|
204
216
|
profiles = Dir[File.join(base_dir, "#{profile_name}.mobileprovision")]
|
205
|
-
|
217
|
+
if Helper.mac?
|
218
|
+
keychain_path = FastlaneCore::Helper.keychain_path(params[:keychain_name]) unless params[:keychain_name].nil?
|
219
|
+
end
|
206
220
|
|
207
221
|
# Install the provisioning profiles
|
208
222
|
profile = profiles.last
|
@@ -220,11 +234,13 @@ module Match
|
|
220
234
|
|
221
235
|
if profile.nil? || params[:force]
|
222
236
|
if params[:readonly]
|
223
|
-
all_profiles = Dir.entries(base_dir).reject { |f| f.start_with?(".") }
|
224
237
|
UI.error("No matching provisioning profiles found for '#{profile_name}'")
|
225
238
|
UI.error("A new one cannot be created because you enabled `readonly`")
|
226
|
-
|
227
|
-
|
239
|
+
if Dir.exist?(base_dir) # folder for `prov_type` does not exist on first match use for that type
|
240
|
+
all_profiles = Dir.entries(base_dir).reject { |f| f.start_with?(".") }
|
241
|
+
UI.error("Provisioning profiles in your repo for type `#{prov_type}`:")
|
242
|
+
all_profiles.each { |p| UI.error("- '#{p}'") }
|
243
|
+
end
|
228
244
|
UI.error("If you are certain that a profile should exist, double-check the recent changes to your match repository")
|
229
245
|
UI.user_error!("No matching provisioning profiles found and can not create a new one because you enabled `readonly`. Check the output above for more information.")
|
230
246
|
end
|
@@ -236,10 +252,16 @@ module Match
|
|
236
252
|
self.files_to_commit << profile
|
237
253
|
end
|
238
254
|
|
239
|
-
|
255
|
+
if Helper.mac?
|
256
|
+
installed_profile = FastlaneCore::ProvisioningProfile.install(profile, keychain_path)
|
257
|
+
end
|
240
258
|
parsed = FastlaneCore::ProvisioningProfile.parse(profile, keychain_path)
|
241
259
|
uuid = parsed["UUID"]
|
242
260
|
|
261
|
+
if params[:output_path]
|
262
|
+
FileUtils.cp(profile, params[:output_path])
|
263
|
+
end
|
264
|
+
|
243
265
|
if spaceship && !spaceship.profile_exists(username: params[:username], uuid: uuid)
|
244
266
|
# This profile is invalid, let's remove the local file and generate a new one
|
245
267
|
File.delete(profile)
|
@@ -60,7 +60,7 @@ module Match
|
|
60
60
|
# No existing working directory, creating a new one now
|
61
61
|
self.working_directory = Dir.mktmpdir
|
62
62
|
|
63
|
-
command = "git clone
|
63
|
+
command = "git clone #{self.git_url.shellescape} #{self.working_directory.shellescape}"
|
64
64
|
if self.shallow_clone
|
65
65
|
command << " --depth 1 --no-single-branch"
|
66
66
|
elsif self.clone_branch_directly
|
@@ -74,9 +74,11 @@ module Match
|
|
74
74
|
|
75
75
|
begin
|
76
76
|
# GIT_TERMINAL_PROMPT will fail the `git clone` command if user credentials are missing
|
77
|
-
|
78
|
-
|
79
|
-
|
77
|
+
Helper.with_env_values('GIT_TERMINAL_PROMPT' => '0') do
|
78
|
+
FastlaneCore::CommandExecutor.execute(command: command,
|
79
|
+
print_all: FastlaneCore::Globals.verbose?,
|
80
|
+
print_command: FastlaneCore::Globals.verbose?)
|
81
|
+
end
|
80
82
|
rescue
|
81
83
|
UI.error("Error cloning certificates repo, please make sure you have read access to the repository you want to use")
|
82
84
|
if self.branch && self.clone_branch_directly
|
@@ -193,13 +195,15 @@ module Match
|
|
193
195
|
def git_push(commands: [], commit_message: nil)
|
194
196
|
commit_message ||= generate_commit_message
|
195
197
|
commands << "git commit -m #{commit_message.shellescape}"
|
196
|
-
commands << "
|
198
|
+
commands << "git push origin #{self.branch.shellescape}"
|
197
199
|
|
198
200
|
UI.message("Pushing changes to remote git repo...")
|
199
|
-
|
200
|
-
|
201
|
+
Helper.with_env_values('GIT_TERMINAL_PROMPT' => '0') do
|
202
|
+
commands.each do |command|
|
203
|
+
FastlaneCore::CommandExecutor.execute(command: command,
|
201
204
|
print_all: FastlaneCore::Globals.verbose?,
|
202
|
-
|
205
|
+
print_command: FastlaneCore::Globals.verbose?)
|
206
|
+
end
|
203
207
|
end
|
204
208
|
|
205
209
|
self.clear_changes
|
data/match/lib/match/utils.rb
CHANGED
@@ -32,7 +32,7 @@ module Match
|
|
32
32
|
end
|
33
33
|
|
34
34
|
def self.get_cert_info(cer_certificate_path)
|
35
|
-
cert = OpenSSL::X509::Certificate.new(File.
|
35
|
+
cert = OpenSSL::X509::Certificate.new(File.binread(cer_certificate_path))
|
36
36
|
|
37
37
|
# openssl output:
|
38
38
|
# subject= /UID={User ID}/CN={Certificate Name}/OU={Certificate User}/O={Organisation}/C={Country}
|
@@ -54,7 +54,7 @@ module Match
|
|
54
54
|
.push([openssl_keys_to_readable_keys.fetch("notBefore"), cert.not_before])
|
55
55
|
.push([openssl_keys_to_readable_keys.fetch("notAfter"), cert.not_after])
|
56
56
|
rescue => ex
|
57
|
-
UI.error(ex)
|
57
|
+
UI.error("get_cert_info: #{ex}")
|
58
58
|
return {}
|
59
59
|
end
|
60
60
|
|
data/scan/lib/scan/options.rb
CHANGED
@@ -3,6 +3,7 @@ require 'credentials_manager/appfile_config'
|
|
3
3
|
require_relative 'module'
|
4
4
|
|
5
5
|
module Scan
|
6
|
+
# rubocop:disable Metrics/ClassLength
|
6
7
|
class Options
|
7
8
|
def self.verify_type(item_name, acceptable_types, value)
|
8
9
|
type_ok = [Array, String].any? { |type| value.kind_of?(type) }
|
@@ -322,6 +323,24 @@ module Scan
|
|
322
323
|
env_name: "SCAN_SLACK_MESSAGE",
|
323
324
|
description: "The message included with each message posted to slack",
|
324
325
|
optional: true),
|
326
|
+
FastlaneCore::ConfigItem.new(key: :slack_use_webhook_configured_username_and_icon,
|
327
|
+
env_name: "SCAN_SLACK_USE_WEBHOOK_CONFIGURED_USERNAME_AND_ICON",
|
328
|
+
description: "Use webhook's default username and icon settings? (true/false)",
|
329
|
+
default_value: false,
|
330
|
+
type: Boolean,
|
331
|
+
optional: true),
|
332
|
+
FastlaneCore::ConfigItem.new(key: :slack_username,
|
333
|
+
env_name: "SCAN_SLACK_USERNAME",
|
334
|
+
description: "Overrides the webhook's username property if slack_use_webhook_configured_username_and_icon is false",
|
335
|
+
default_value: "fastlane",
|
336
|
+
is_string: true,
|
337
|
+
optional: true),
|
338
|
+
FastlaneCore::ConfigItem.new(key: :slack_icon_url,
|
339
|
+
env_name: "SCAN_SLACK_ICON_URL",
|
340
|
+
description: "Overrides the webhook's image property if slack_use_webhook_configured_username_and_icon is false",
|
341
|
+
default_value: "https://s3-eu-west-1.amazonaws.com/fastlane.tools/fastlane.png",
|
342
|
+
is_string: true,
|
343
|
+
optional: true),
|
325
344
|
FastlaneCore::ConfigItem.new(key: :skip_slack,
|
326
345
|
description: "Don't publish to slack, even when an URL is given",
|
327
346
|
is_string: false,
|
@@ -349,4 +368,5 @@ module Scan
|
|
349
368
|
]
|
350
369
|
end
|
351
370
|
end
|
371
|
+
# rubocop:enable Metrics/ClassLength
|
352
372
|
end
|
@@ -15,6 +15,8 @@ module Scan
|
|
15
15
|
channel = ('#' + channel) unless ['#', '@'].include?(channel[0]) # send message to channel by default
|
16
16
|
end
|
17
17
|
|
18
|
+
username = Scan.config[:slack_use_webhook_configured_username_and_icon] ? nil : Scan.config[:slack_username]
|
19
|
+
icon_url = Scan.config[:slack_use_webhook_configured_username_and_icon] ? nil : Scan.config[:slack_icon_url]
|
18
20
|
fields = []
|
19
21
|
|
20
22
|
if results[:build_errors]
|
@@ -46,8 +48,8 @@ module Scan
|
|
46
48
|
channel: channel,
|
47
49
|
slack_url: Scan.config[:slack_url].to_s,
|
48
50
|
success: results[:build_errors].to_i == 0 && results[:failures].to_i == 0,
|
49
|
-
username:
|
50
|
-
icon_url:
|
51
|
+
username: username,
|
52
|
+
icon_url: icon_url,
|
51
53
|
payload: {},
|
52
54
|
attachment_properties: {
|
53
55
|
fields: fields
|
data/sigh/lib/sigh/runner.rb
CHANGED
@@ -199,13 +199,16 @@ module Sigh
|
|
199
199
|
true
|
200
200
|
end
|
201
201
|
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
202
|
+
# verify certificates
|
203
|
+
if Helper.mac?
|
204
|
+
unless Sigh.config[:skip_certificate_verification]
|
205
|
+
certificates = certificates.find_all do |c|
|
206
|
+
file = Tempfile.new('cert')
|
207
|
+
file.write(c.download_raw)
|
208
|
+
file.close
|
209
|
+
|
210
|
+
FastlaneCore::CertChecker.installed?(file.path)
|
211
|
+
end
|
209
212
|
end
|
210
213
|
end
|
211
214
|
|
@@ -221,7 +221,7 @@ module Spaceship
|
|
221
221
|
end
|
222
222
|
|
223
223
|
if ENV["DEBUG"]
|
224
|
-
puts("To run
|
224
|
+
puts("To run spaceship through a local proxy, use SPACESHIP_DEBUG")
|
225
225
|
end
|
226
226
|
end
|
227
227
|
end
|
@@ -394,7 +394,7 @@ module Spaceship
|
|
394
394
|
end
|
395
395
|
|
396
396
|
# This method is used for both the Apple Dev Portal and App Store Connect
|
397
|
-
# This will also handle 2 step verification
|
397
|
+
# This will also handle 2 step verification and 2 factor authentication
|
398
398
|
#
|
399
399
|
# It is called in `send_login_request` of sub classes (which the method `login`, above, transferred over to via `do_login`)
|
400
400
|
def send_shared_login_request(user, password)
|
@@ -25,7 +25,6 @@ require_relative 'app_trailer'
|
|
25
25
|
require_relative 'sandbox_tester'
|
26
26
|
require_relative 'app_details'
|
27
27
|
require_relative 'pricing_tier'
|
28
|
-
require_relative 'recovery_device'
|
29
28
|
require_relative 'territory'
|
30
29
|
require_relative 'availability'
|
31
30
|
require_relative 'members'
|
@@ -163,14 +163,13 @@ module Spaceship
|
|
163
163
|
return unless raw.kind_of?(Hash)
|
164
164
|
|
165
165
|
data = raw['data'] || raw # sometimes it's with data, sometimes it isn't
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
errors_in_version_info = fetch_errors_in_data(data_section: data, sub_section_name: "versionInfo", keys: error_keys_to_check)
|
166
|
+
|
167
|
+
error_keys = ["sectionErrorKeys", "validationErrors", "serviceErrors"]
|
168
|
+
info_keys = ["sectionInfoKeys", "sectionWarningKeys"]
|
169
|
+
error_and_info_keys_to_check = error_keys + info_keys
|
170
|
+
|
171
|
+
errors_in_data = fetch_errors_in_data(data_section: data, keys: error_and_info_keys_to_check)
|
172
|
+
errors_in_version_info = fetch_errors_in_data(data_section: data, sub_section_name: "versionInfo", keys: error_and_info_keys_to_check)
|
174
173
|
|
175
174
|
# If we have any errors or "info" we need to treat them as warnings or errors
|
176
175
|
if errors_in_data.count == 0 && errors_in_version_info.count == 0
|
@@ -205,7 +204,6 @@ module Spaceship
|
|
205
204
|
errors = handle_response_hash.call(data)
|
206
205
|
|
207
206
|
# Search at data level, as well as "versionInfo" level for errors
|
208
|
-
error_keys = ["sectionErrorKeys", "validationErrors"]
|
209
207
|
errors_in_data = fetch_errors_in_data(data_section: data, keys: error_keys)
|
210
208
|
errors_in_version_info = fetch_errors_in_data(data_section: data, sub_section_name: "versionInfo", keys: error_keys)
|
211
209
|
|
@@ -234,7 +232,6 @@ module Spaceship
|
|
234
232
|
end
|
235
233
|
|
236
234
|
# Search at data level, as well as "versionInfo" level for info and warnings
|
237
|
-
info_keys = ["sectionInfoKeys", "sectionWarningKeys"]
|
238
235
|
info_in_data = fetch_errors_in_data(data_section: data, keys: info_keys)
|
239
236
|
info_in_version_info = fetch_errors_in_data(data_section: data, sub_section_name: "versionInfo", keys: info_keys)
|
240
237
|
|
@@ -1,19 +1,14 @@
|
|
1
1
|
require_relative 'globals'
|
2
2
|
require_relative 'tunes/tunes_client'
|
3
|
-
require_relative 'tunes/recovery_device'
|
4
3
|
|
5
4
|
module Spaceship
|
6
5
|
class Client
|
7
6
|
def handle_two_step_or_factor(response)
|
7
|
+
# extract `x-apple-id-session-id` and `scnt` from response, to be used by `update_request_headers`
|
8
8
|
@x_apple_id_session_id = response["x-apple-id-session-id"]
|
9
9
|
@scnt = response["scnt"]
|
10
10
|
|
11
|
-
|
12
|
-
puts("Two-step Verification (4 digits code) or Two-factor Authentication (6 digits code) is enabled for account '#{self.user}'")
|
13
|
-
puts("More information about Two-step Verification (4 digits code): https://support.apple.com/en-us/HT204152")
|
14
|
-
puts("More information about Two-factor Authentication (6 digits code): https://support.apple.com/en-us/HT204915")
|
15
|
-
puts("")
|
16
|
-
|
11
|
+
# get authentication options
|
17
12
|
r = request(:get) do |req|
|
18
13
|
req.url("https://idmsa.apple.com/appleauth/auth")
|
19
14
|
update_request_headers(req)
|
@@ -28,35 +23,28 @@ module Spaceship
|
|
28
23
|
end
|
29
24
|
end
|
30
25
|
|
31
|
-
def handle_two_step(
|
32
|
-
if
|
26
|
+
def handle_two_step(response)
|
27
|
+
if response.body.fetch("securityCode", {})["tooManyCodesLock"].to_s.length > 0
|
33
28
|
raise Tunes::Error.new, "Too many verification codes have been sent. Enter the last code you received, use one of your devices, or try again later."
|
34
29
|
end
|
35
30
|
|
36
|
-
old_client = (begin
|
37
|
-
Tunes::RecoveryDevice.client
|
38
|
-
rescue
|
39
|
-
nil # since client might be nil, which raises an exception
|
40
|
-
end)
|
41
|
-
Tunes::RecoveryDevice.client = self # temporary set it as it's required by the factory method
|
42
|
-
devices = r.body["trustedDevices"].collect do |current|
|
43
|
-
Tunes::RecoveryDevice.factory(current)
|
44
|
-
end
|
45
|
-
Tunes::RecoveryDevice.client = old_client
|
46
|
-
|
47
31
|
puts("Two-step Verification (4 digits code) is enabled for account '#{self.user}'")
|
48
|
-
puts("
|
49
|
-
|
50
|
-
|
32
|
+
puts("More information about Two-step Verification: https://support.apple.com/en-us/HT204152")
|
33
|
+
puts("")
|
34
|
+
|
35
|
+
puts("Please select a trusted device to verify your identity")
|
36
|
+
available = response.body["trustedDevices"].collect do |current|
|
37
|
+
"#{current['name']}\t#{current['modelName'] || 'SMS'}\t(#{current['id']})"
|
51
38
|
end
|
52
39
|
result = choose(*available)
|
40
|
+
|
53
41
|
device_id = result.match(/.*\t.*\t\((.*)\)/)[1]
|
54
|
-
|
42
|
+
handle_two_step_for_device(device_id)
|
55
43
|
end
|
56
44
|
|
57
45
|
# this is extracted into its own method so it can be called multiple times (see end)
|
58
|
-
def
|
59
|
-
# Request
|
46
|
+
def handle_two_step_for_device(device_id)
|
47
|
+
# Request token to device
|
60
48
|
r = request(:put) do |req|
|
61
49
|
req.url("https://idmsa.apple.com/appleauth/auth/verify/device/#{device_id}/securitycode")
|
62
50
|
update_request_headers(req)
|
@@ -70,7 +58,7 @@ module Spaceship
|
|
70
58
|
code = ask("Please enter the 4 digit code: ")
|
71
59
|
puts("Requesting session...")
|
72
60
|
|
73
|
-
# Send token
|
61
|
+
# Send token to server to get a valid session
|
74
62
|
r = request(:post) do |req|
|
75
63
|
req.url("https://idmsa.apple.com/appleauth/auth/verify/device/#{device_id}/securitycode")
|
76
64
|
req.headers['Content-Type'] = 'application/json'
|
@@ -101,7 +89,7 @@ module Spaceship
|
|
101
89
|
# }
|
102
90
|
if ex.to_s.include?("verification code") # to have a nicer output
|
103
91
|
puts("Error: Incorrect verification code")
|
104
|
-
return
|
92
|
+
return handle_two_step_for_device(device_id)
|
105
93
|
end
|
106
94
|
|
107
95
|
raise ex
|
@@ -112,29 +100,100 @@ module Spaceship
|
|
112
100
|
return true
|
113
101
|
end
|
114
102
|
|
115
|
-
def handle_two_factor(response)
|
116
|
-
|
117
|
-
|
103
|
+
def handle_two_factor(response, depth = 0)
|
104
|
+
if depth == 0
|
105
|
+
puts("Two-factor Authentication (6 digits code) is enabled for account '#{self.user}'")
|
106
|
+
puts("More information about Two-factor Authentication: https://support.apple.com/en-us/HT204915")
|
107
|
+
puts("")
|
108
|
+
|
109
|
+
two_factor_url = "https://github.com/fastlane/fastlane/tree/master/spaceship#2-step-verification"
|
110
|
+
puts("If you're running this in a non-interactive session (e.g. server or CI)")
|
111
|
+
puts("check out #{two_factor_url}")
|
112
|
+
end
|
118
113
|
|
119
|
-
|
120
|
-
puts("check out #{two_factor_url}")
|
114
|
+
# "verification code" has already be pushed to devices
|
121
115
|
|
122
116
|
security_code = response.body["securityCode"]
|
123
|
-
# securityCode
|
124
|
-
#
|
125
|
-
#
|
126
|
-
#
|
127
|
-
#
|
117
|
+
# "securityCode": {
|
118
|
+
# "length": 6,
|
119
|
+
# "tooManyCodesSent": false,
|
120
|
+
# "tooManyCodesValidated": false,
|
121
|
+
# "securityCodeLocked": false
|
122
|
+
# },
|
128
123
|
code_length = security_code["length"]
|
129
|
-
|
124
|
+
|
125
|
+
puts("")
|
126
|
+
puts("(Input `sms` to escape this prompt and select a trusted phone number to send the code as a text message)")
|
127
|
+
code_type = 'trusteddevice'
|
128
|
+
code = ask("Please enter the #{code_length} digit code:")
|
129
|
+
body = { "securityCode" => { "code" => code.to_s } }.to_json
|
130
|
+
|
131
|
+
if code == 'sms'
|
132
|
+
code_type = 'phone'
|
133
|
+
body = request_two_factor_code_from_phone(response.body["trustedPhoneNumbers"], code_length)
|
134
|
+
end
|
135
|
+
|
130
136
|
puts("Requesting session...")
|
131
137
|
|
132
|
-
# Send
|
138
|
+
# Send "verification code" back to server to get a valid session
|
133
139
|
r = request(:post) do |req|
|
134
|
-
req.url("https://idmsa.apple.com/appleauth/auth/verify/
|
140
|
+
req.url("https://idmsa.apple.com/appleauth/auth/verify/#{code_type}/securitycode")
|
135
141
|
req.headers['Content-Type'] = 'application/json'
|
136
|
-
req.body =
|
142
|
+
req.body = body
|
143
|
+
update_request_headers(req)
|
144
|
+
end
|
145
|
+
|
146
|
+
begin
|
147
|
+
# we use `Spaceship::TunesClient.new.handle_itc_response`
|
148
|
+
# since this might be from the Dev Portal, but for 2 factor
|
149
|
+
Spaceship::TunesClient.new.handle_itc_response(r.body) # this will fail if the code is invalid
|
150
|
+
rescue => ex
|
151
|
+
# If the code was entered wrong
|
152
|
+
# {
|
153
|
+
# "service_errors": [{
|
154
|
+
# "code": "-21669",
|
155
|
+
# "title": "Incorrect Verification Code",
|
156
|
+
# "message": "Incorrect verification code."
|
157
|
+
# }],
|
158
|
+
# "hasError": true
|
159
|
+
# }
|
137
160
|
|
161
|
+
if ex.to_s.include?("verification code") # to have a nicer output
|
162
|
+
puts("Error: Incorrect verification code")
|
163
|
+
depth += 1
|
164
|
+
return handle_two_factor(response, depth)
|
165
|
+
end
|
166
|
+
|
167
|
+
raise ex
|
168
|
+
end
|
169
|
+
|
170
|
+
store_session
|
171
|
+
|
172
|
+
return true
|
173
|
+
end
|
174
|
+
|
175
|
+
def get_id_for_number(phone_numbers, result)
|
176
|
+
phone_numbers.each do |phone|
|
177
|
+
phone_id = phone['id']
|
178
|
+
return phone_id if phone['numberWithDialCode'] == result
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def request_two_factor_code_from_phone(phone_numbers, code_length)
|
183
|
+
puts("Please select a trusted phone number to send code to:")
|
184
|
+
|
185
|
+
available = phone_numbers.collect do |current|
|
186
|
+
current['numberWithDialCode']
|
187
|
+
end
|
188
|
+
result = choose(*available)
|
189
|
+
|
190
|
+
phone_id = get_id_for_number(phone_numbers, result)
|
191
|
+
|
192
|
+
# Request code
|
193
|
+
r = request(:put) do |req|
|
194
|
+
req.url("https://idmsa.apple.com/appleauth/auth/verify/phone")
|
195
|
+
req.headers['Content-Type'] = 'application/json'
|
196
|
+
req.body = { "phoneNumber" => { "id" => phone_id }, "mode" => "sms" }.to_json
|
138
197
|
update_request_headers(req)
|
139
198
|
end
|
140
199
|
|
@@ -142,9 +201,10 @@ module Spaceship
|
|
142
201
|
# since this might be from the Dev Portal, but for 2 step
|
143
202
|
Spaceship::TunesClient.new.handle_itc_response(r.body)
|
144
203
|
|
145
|
-
|
204
|
+
puts("Successfully requested text message")
|
146
205
|
|
147
|
-
|
206
|
+
code = ask("Please enter the #{code_length} digit code you received at #{result}:")
|
207
|
+
{ "securityCode" => { "code" => code.to_s }, "phoneNumber" => { "id" => phone_id }, "mode" => "sms" }.to_json
|
148
208
|
end
|
149
209
|
|
150
210
|
def store_session
|
@@ -162,7 +222,6 @@ module Spaceship
|
|
162
222
|
|
163
223
|
request(:get) do |req|
|
164
224
|
req.url("https://idmsa.apple.com/appleauth/auth/2sv/trust")
|
165
|
-
|
166
225
|
update_request_headers(req)
|
167
226
|
end
|
168
227
|
# This request will fail if the user isn't added to a team on iTC
|
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.115.0.beta.
|
4
|
+
version: 2.115.0.beta.20190120200101
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Danielle Tomlinson
|
@@ -27,7 +27,7 @@ authors:
|
|
27
27
|
autorequire:
|
28
28
|
bindir: bin
|
29
29
|
cert_chain: []
|
30
|
-
date: 2019-01-
|
30
|
+
date: 2019-01-20 00:00:00.000000000 Z
|
31
31
|
dependencies:
|
32
32
|
- !ruby/object:Gem::Dependency
|
33
33
|
name: slack-notifier
|
@@ -1657,7 +1657,6 @@ files:
|
|
1657
1657
|
- spaceship/lib/spaceship/tunes/members.rb
|
1658
1658
|
- spaceship/lib/spaceship/tunes/pricing_info.rb
|
1659
1659
|
- spaceship/lib/spaceship/tunes/pricing_tier.rb
|
1660
|
-
- spaceship/lib/spaceship/tunes/recovery_device.rb
|
1661
1660
|
- spaceship/lib/spaceship/tunes/sandbox_tester.rb
|
1662
1661
|
- spaceship/lib/spaceship/tunes/spaceship.rb
|
1663
1662
|
- spaceship/lib/spaceship/tunes/territory.rb
|
@@ -1688,24 +1687,24 @@ metadata:
|
|
1688
1687
|
post_install_message:
|
1689
1688
|
rdoc_options: []
|
1690
1689
|
require_paths:
|
1691
|
-
-
|
1692
|
-
-
|
1693
|
-
-
|
1690
|
+
- supply/lib
|
1691
|
+
- precheck/lib
|
1692
|
+
- pem/lib
|
1694
1693
|
- fastlane/lib
|
1694
|
+
- spaceship/lib
|
1695
1695
|
- snapshot/lib
|
1696
|
-
- pem/lib
|
1697
|
-
- gym/lib
|
1698
|
-
- credentials_manager/lib
|
1699
|
-
- screengrab/lib
|
1700
|
-
- pilot/lib
|
1701
1696
|
- match/lib
|
1697
|
+
- pilot/lib
|
1702
1698
|
- sigh/lib
|
1703
|
-
-
|
1704
|
-
- supply/lib
|
1705
|
-
- deliver/lib
|
1706
|
-
- precheck/lib
|
1699
|
+
- screengrab/lib
|
1707
1700
|
- frameit/lib
|
1701
|
+
- fastlane_core/lib
|
1702
|
+
- cert/lib
|
1703
|
+
- credentials_manager/lib
|
1708
1704
|
- produce/lib
|
1705
|
+
- gym/lib
|
1706
|
+
- deliver/lib
|
1707
|
+
- scan/lib
|
1709
1708
|
required_ruby_version: !ruby/object:Gem::Requirement
|
1710
1709
|
requirements:
|
1711
1710
|
- - ">="
|
@@ -1,65 +0,0 @@
|
|
1
|
-
require_relative 'tunes_base'
|
2
|
-
|
3
|
-
module Spaceship
|
4
|
-
module Tunes
|
5
|
-
class RecoveryDevice < TunesBase
|
6
|
-
# @return (String) ID provided by Apple
|
7
|
-
# @example
|
8
|
-
# "1801231651"
|
9
|
-
attr_accessor :device_id
|
10
|
-
|
11
|
-
# @return (String) The name of the device
|
12
|
-
# @example
|
13
|
-
# "Felix Krause's iPhone 6"
|
14
|
-
attr_accessor :name
|
15
|
-
|
16
|
-
# @return (Bool) This device looks suspicious [add emoji here]
|
17
|
-
# this will probably always be true, otherwise the device
|
18
|
-
# doesn't show up
|
19
|
-
# @example
|
20
|
-
# true
|
21
|
-
attr_accessor :trusted
|
22
|
-
|
23
|
-
# @return (Bool)
|
24
|
-
# @example
|
25
|
-
# true
|
26
|
-
attr_accessor :status
|
27
|
-
|
28
|
-
# @return (String) Remote URL to an image representing this device
|
29
|
-
# This shows the attention to detail by Apple <3
|
30
|
-
# @example
|
31
|
-
# "https://appleid.cdn-apple.com/static/deviceImages-5.0/iPhone/iPhone8,1-e4e7e8-dadcdb/online-sourcelist__3x.png"
|
32
|
-
# @example
|
33
|
-
# "https://appleid.cdn-apple.com/appleauth/static/bin/cb2613252489/images/sms@3x.png"
|
34
|
-
attr_accessor :device_image
|
35
|
-
|
36
|
-
# @return (String)
|
37
|
-
# @example
|
38
|
-
# "iPad Air"
|
39
|
-
# @example
|
40
|
-
# nil # e.g. when it's a phone number
|
41
|
-
attr_accessor :model_name
|
42
|
-
|
43
|
-
# @return (String)
|
44
|
-
# @example
|
45
|
-
# "79"
|
46
|
-
attr_accessor :last_two_digits
|
47
|
-
|
48
|
-
# @return (Number)
|
49
|
-
# @example
|
50
|
-
# 1446488271926
|
51
|
-
attr_accessor :update_date
|
52
|
-
|
53
|
-
attr_mapping(
|
54
|
-
'id' => :device_id,
|
55
|
-
'name' => :name,
|
56
|
-
'trusted' => :trusted,
|
57
|
-
'status' => :status,
|
58
|
-
'imageLocation3x' => :device_image,
|
59
|
-
'modelName' => :model_name,
|
60
|
-
'lastTwoDigits' => :last_two_digits,
|
61
|
-
'updateDate' => :update_date
|
62
|
-
)
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|