fastlane 2.115.0.beta.20190119200019 → 2.115.0.beta.20190120200101
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/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
|