fastlane 2.54.0.beta.20170822010003 → 2.54.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/deliver/lib/deliver/loader.rb +29 -3
- data/deliver/lib/deliver/upload_metadata.rb +18 -0
- data/fastlane/lib/fastlane/actions/opt_out_crash_reporting.rb +1 -1
- data/fastlane/lib/fastlane/version.rb +1 -1
- data/fastlane_core/lib/fastlane_core.rb +1 -0
- data/fastlane_core/lib/fastlane_core/crash_reporter/crash_reporter.rb +1 -1
- data/fastlane_core/lib/fastlane_core/device_manager.rb +12 -12
- data/fastlane_core/lib/fastlane_core/test_parser.rb +145 -0
- data/fastlane_core/lib/fastlane_core/ui/fastlane_runner.rb +1 -1
- data/pilot/lib/pilot/tester_manager.rb +8 -1
- data/sigh/lib/sigh/runner.rb +27 -18
- data/snapshot/README.md +3 -1
- data/snapshot/lib/assets/SnapshotHelper.swift +57 -35
- data/snapshot/lib/assets/SnapshotHelperXcode8.swift +173 -0
- data/snapshot/lib/snapshot.rb +6 -0
- data/snapshot/lib/snapshot/collector.rb +37 -11
- data/snapshot/lib/snapshot/detect_values.rb +4 -1
- data/snapshot/lib/snapshot/fixes/simulator_zoom_fix.rb +1 -1
- data/snapshot/lib/snapshot/options.rb +5 -0
- data/snapshot/lib/snapshot/reports_generator.rb +34 -3
- data/snapshot/lib/snapshot/runner.rb +30 -215
- data/snapshot/lib/snapshot/setup.rb +9 -3
- data/snapshot/lib/snapshot/simulator_launchers/launcher_configuration.rb +53 -0
- data/snapshot/lib/snapshot/simulator_launchers/simulator_launcher.rb +203 -0
- data/snapshot/lib/snapshot/simulator_launchers/simulator_launcher_base.rb +129 -0
- data/snapshot/lib/snapshot/simulator_launchers/simulator_launcher_xcode_8.rb +110 -0
- data/snapshot/lib/snapshot/test_command_generator.rb +39 -107
- data/snapshot/lib/snapshot/test_command_generator_base.rb +94 -0
- data/snapshot/lib/snapshot/test_command_generator_xcode_8.rb +65 -0
- data/snapshot/lib/snapshot/update.rb +4 -2
- data/spaceship/lib/spaceship/portal/provisioning_profile.rb +5 -2
- data/spaceship/lib/spaceship/test_flight/client.rb +8 -0
- data/spaceship/lib/spaceship/test_flight/tester.rb +20 -4
- metadata +26 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4b0e5057dd038fe51f2566e6e003e9bb6ffea30f
|
4
|
+
data.tar.gz: 21be6de01c31164ab160e45685a9983123341ed4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4aec5f5ee4a8183d8b2a296822e9afb8c04e296c1068114b60d46dc25b085ad5ff42bb5a59581e6b15a1e8ea4c99ee5da5cffd5f15daf3a5ff2c6f602551525c
|
7
|
+
data.tar.gz: 5e9a703ed1d1834d6d2e5e98f042552775dab51ace991a739faf7f16683ff264c4ee0c6775099c4f3cc45afb29d40397da6bf5619ae75df6c82092fa201472f1
|
data/README.md
CHANGED
@@ -161,7 +161,7 @@ You can easily opt-out of metrics collection by adding `opt_out_usage` at the to
|
|
161
161
|
|
162
162
|
## Crash Reporting
|
163
163
|
|
164
|
-
In order to continuously improve stability, _fastlane_ will record crash reports with sanitized
|
164
|
+
In order to continuously improve stability, _fastlane_ will record crash reports with sanitized stack traces. Sanitization removes personal information from the stack trace and error message (including home directories, _fastlane_ path, gem paths, environment variables, and parameters).
|
165
165
|
|
166
166
|
You can easily opt-out of crash reporting by adding `opt_out_crash_reporting` at the top of your `Fastfile` or by setting the environment variable `FASTLANE_OPT_OUT_CRASH_REPORTING`. Just like metrics mentioned above, participating helps us provide the best possible support for _fastlane_, so we hope you'll consider it a plus! :heavy_plus_sign:
|
167
167
|
|
@@ -7,12 +7,38 @@ module Deliver
|
|
7
7
|
APPLE_TV_DIR_NAME = "appleTV".freeze
|
8
8
|
IMESSAGE_DIR_NAME = "iMessage".freeze
|
9
9
|
DEFAULT_DIR_NAME = "default".freeze
|
10
|
-
|
10
|
+
|
11
|
+
SPECIAL_DIR_NAMES = [APPLE_TV_DIR_NAME, IMESSAGE_DIR_NAME, DEFAULT_DIR_NAME].freeze
|
12
|
+
|
13
|
+
EXCEPTION_DIRECTORIES = UploadMetadata::ALL_META_SUB_DIRS.map(&:downcase).freeze
|
11
14
|
|
12
15
|
def self.language_folders(root)
|
13
|
-
Dir.glob(File.join(root, '*'))
|
14
|
-
|
16
|
+
folders = Dir.glob(File.join(root, '*'))
|
17
|
+
|
18
|
+
if Helper.is_test?
|
19
|
+
available_languages = FastlaneCore::Languages::ALL_LANGUAGES
|
20
|
+
else
|
21
|
+
available_languages = Spaceship::Tunes.client.available_languages.sort
|
22
|
+
end
|
23
|
+
|
24
|
+
allowed_directory_names = (available_languages + SPECIAL_DIR_NAMES).map(&:downcase).freeze
|
25
|
+
|
26
|
+
selected_folders = folders.select do |path|
|
27
|
+
File.directory?(path) && allowed_directory_names.include?(File.basename(path).downcase)
|
15
28
|
end.sort
|
29
|
+
|
30
|
+
# Gets list of folders that are not supported languages
|
31
|
+
rejected_folders = folders.select do |path|
|
32
|
+
normalized_path = File.basename(path).downcase
|
33
|
+
File.directory?(path) && !allowed_directory_names.include?(normalized_path) && !EXCEPTION_DIRECTORIES.include?(normalized_path)
|
34
|
+
end.sort
|
35
|
+
|
36
|
+
unless rejected_folders.empty?
|
37
|
+
rejected_folders = rejected_folders.map { |path| File.basename(path) }
|
38
|
+
UI.user_error! "Unsupport directory name(s) for screenshots/metadata: #{rejected_folders.join(', ')}\n\nValid directory names are: #{allowed_directory_names}"
|
39
|
+
end
|
40
|
+
|
41
|
+
selected_folders
|
16
42
|
end
|
17
43
|
end
|
18
44
|
end
|
@@ -55,6 +55,8 @@ module Deliver
|
|
55
55
|
# Directory name it contains review information
|
56
56
|
REVIEW_INFORMATION_DIR = "review_information"
|
57
57
|
|
58
|
+
ALL_META_SUB_DIRS = [TRADE_REPRESENTATIVE_CONTACT_INFORMATION_DIR, REVIEW_INFORMATION_DIR]
|
59
|
+
|
58
60
|
# rubocop:disable Metrics/PerceivedComplexity
|
59
61
|
|
60
62
|
# Make sure to call `load_from_filesystem` before calling upload
|
@@ -146,6 +148,22 @@ module Deliver
|
|
146
148
|
# Build a complete list of the required languages
|
147
149
|
enabled_languages = detect_languages(options)
|
148
150
|
|
151
|
+
# Get all languages used in existing settings
|
152
|
+
(LOCALISED_VERSION_VALUES + LOCALISED_APP_VALUES).each do |key|
|
153
|
+
current = options[key]
|
154
|
+
next unless current && current.kind_of?(Hash)
|
155
|
+
current.each do |language, value|
|
156
|
+
enabled_languages << language unless enabled_languages.include?(language)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
# Check folder list (an empty folder signifies a language is required)
|
161
|
+
Loader.language_folders(options[:metadata_path]).each do |lang_folder|
|
162
|
+
next unless File.directory?(lang_folder) # We don't want to read txt as they are non localised
|
163
|
+
language = File.basename(lang_folder)
|
164
|
+
enabled_languages << language unless enabled_languages.include?(language)
|
165
|
+
end
|
166
|
+
|
149
167
|
return unless enabled_languages.include?("default")
|
150
168
|
UI.message("Detected languages: " + enabled_languages.to_s)
|
151
169
|
|
@@ -13,7 +13,7 @@ module Fastlane
|
|
13
13
|
def self.details
|
14
14
|
[
|
15
15
|
"By default, fastlane will send a report when it crashes",
|
16
|
-
"The
|
16
|
+
"The stack trace is sanitized so no personal information is sent.",
|
17
17
|
"Learn more at https://github.com/fastlane/fastlane#crash-reporting",
|
18
18
|
"Add `opt_out_crash_reporting` at the top of your Fastfile to disable crash reporting"
|
19
19
|
].join(' ')
|
@@ -39,6 +39,7 @@ require 'fastlane_core/ui/errors/fastlane_error'
|
|
39
39
|
require 'fastlane_core/ui/errors/fastlane_crash'
|
40
40
|
require 'fastlane_core/ui/errors/fastlane_shell_error'
|
41
41
|
require 'fastlane_core/ui/errors/fastlane_common_error'
|
42
|
+
require 'fastlane_core/test_parser'
|
42
43
|
|
43
44
|
# Third Party code
|
44
45
|
require 'colored'
|
@@ -55,7 +55,7 @@ module FastlaneCore
|
|
55
55
|
|
56
56
|
def show_message
|
57
57
|
UI.message("Sending crash report...")
|
58
|
-
UI.message("The
|
58
|
+
UI.message("The stack trace is sanitized so no personal information is sent.")
|
59
59
|
UI.message("To see what we are sending, look here: #{crash_report_path}")
|
60
60
|
UI.message("Learn more at https://github.com/fastlane/fastlane#crash-reporting")
|
61
61
|
UI.message("You can disable crash reporting by adding `opt_out_crash_reporting` at the top of your Fastfile")
|
@@ -25,22 +25,22 @@ module FastlaneCore
|
|
25
25
|
end
|
26
26
|
|
27
27
|
output.split(/\n/).each do |line|
|
28
|
+
next if line =~ /unavailable/
|
28
29
|
next if line =~ /^== /
|
29
30
|
if line =~ /^-- /
|
30
31
|
(os_type, os_version) = line.gsub(/-- (.*) --/, '\1').split
|
31
32
|
else
|
32
|
-
# iPad 2 (0EDE6AFC-3767-425A-9658-AAA30A60F212) (Shutdown)
|
33
|
-
# iPad Air 2 (4F3B8059-03FD-4D72-99C0-6E9BBEE2A9CE) (Shutdown) (unavailable, device type profile not found)
|
34
|
-
if line.include?("inch)")
|
35
|
-
# For Xcode 8, where sometimes we have the # of inches in ()
|
36
|
-
# iPad Pro (12.9 inch) (CEF11EB3-79DF-43CB-896A-0F33916C8BDE) (Shutdown)
|
37
|
-
match = line.match(/\s+([^\(]+ \(.*inch\)) \(([-0-9A-F]+)\) \(([^\(]+)\)(.*unavailable.*)?/)
|
38
|
-
else
|
39
|
-
match = line.match(/\s+([^\(]+) \(([-0-9A-F]+)\) \(([^\(]+)\)(.*unavailable.*)?/)
|
40
|
-
end
|
41
33
|
|
42
|
-
|
43
|
-
|
34
|
+
# " iPad (5th generation) (852A5796-63C3-4641-9825-65EBDC5C4259) (Shutdown)"
|
35
|
+
# This line will turn the above string into
|
36
|
+
# ["iPad", "(5th generation)", "(852A5796-63C3-4641-9825-65EBDC5C4259)", "(Shutdown)"]
|
37
|
+
matches = line.strip.scan(/(.*?) (\(.*?\))/).flatten.reject(&:empty?)
|
38
|
+
state = matches.pop.to_s.delete('(').delete(')')
|
39
|
+
udid = matches.pop.to_s.delete('(').delete(')')
|
40
|
+
name = matches.join(' ')
|
41
|
+
|
42
|
+
if matches.count && (os_type == requested_os_type || requested_os_type == "")
|
43
|
+
@devices << Device.new(name: name, os_type: os_type, os_version: os_version, udid: udid, state: state, is_simulator: true)
|
44
44
|
end
|
45
45
|
end
|
46
46
|
end
|
@@ -254,7 +254,7 @@ module FastlaneCore
|
|
254
254
|
|
255
255
|
logarchive_dst = Shellwords.escape(File.join(logs_destination_dir, "system_logs-#{log_identity}.logarchive"))
|
256
256
|
FileUtils.rm_rf(logarchive_dst)
|
257
|
-
FileUtils.mkdir_p(logarchive_dst)
|
257
|
+
FileUtils.mkdir_p(File.expand_path("..", logarchive_dst))
|
258
258
|
command = "xcrun simctl spawn #{device.udid} log collect --output #{logarchive_dst} 2>/dev/null"
|
259
259
|
FastlaneCore::CommandExecutor.execute(command: command, print_all: false, print_command: true)
|
260
260
|
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
module FastlaneCore
|
2
|
+
class TestParser
|
3
|
+
attr_accessor :data
|
4
|
+
|
5
|
+
attr_accessor :file_content
|
6
|
+
|
7
|
+
attr_accessor :raw_json
|
8
|
+
|
9
|
+
# Returns a hash with the path being the key, and the value
|
10
|
+
# defining if the tests were successful
|
11
|
+
def self.auto_convert(config)
|
12
|
+
FastlaneCore::PrintTable.print_values(config: config,
|
13
|
+
title: "Summary for trainer #{Trainer::VERSION}")
|
14
|
+
|
15
|
+
containing_dir = config[:path]
|
16
|
+
files = Dir["#{containing_dir}/**/Logs/Test/*TestSummaries.plist"]
|
17
|
+
files += Dir["#{containing_dir}/Test/*TestSummaries.plist"]
|
18
|
+
files += Dir["#{containing_dir}/*TestSummaries.plist"]
|
19
|
+
files += Dir[containing_dir] if containing_dir.end_with?(".plist") # if it's the exact path to a plist file
|
20
|
+
|
21
|
+
if files.empty?
|
22
|
+
UI.user_error!("No test result files found in directory '#{containing_dir}', make sure the file name ends with 'TestSummaries.plist'")
|
23
|
+
end
|
24
|
+
|
25
|
+
return_hash = {}
|
26
|
+
files.each do |path|
|
27
|
+
if config[:output_directory]
|
28
|
+
FileUtils.mkdir_p(config[:output_directory])
|
29
|
+
filename = File.basename(path).gsub(".plist", config[:extension])
|
30
|
+
to_path = File.join(config[:output_directory], filename)
|
31
|
+
else
|
32
|
+
to_path = path.gsub(".plist", config[:extension])
|
33
|
+
end
|
34
|
+
|
35
|
+
tp = Trainer::TestParser.new(path)
|
36
|
+
File.write(to_path, tp.to_junit)
|
37
|
+
puts "Successfully generated '#{to_path}'"
|
38
|
+
|
39
|
+
return_hash[to_path] = tp.tests_successful?
|
40
|
+
end
|
41
|
+
return_hash
|
42
|
+
end
|
43
|
+
|
44
|
+
def initialize(path)
|
45
|
+
path = File.expand_path(path)
|
46
|
+
UI.user_error!("File not found at path '#{path}'") unless File.exist?(path)
|
47
|
+
|
48
|
+
self.file_content = File.read(path)
|
49
|
+
self.raw_json = Plist.parse_xml(self.file_content)
|
50
|
+
return if self.raw_json["FormatVersion"].to_s.length.zero? # maybe that's a useless plist file
|
51
|
+
|
52
|
+
ensure_file_valid!
|
53
|
+
parse_content
|
54
|
+
end
|
55
|
+
|
56
|
+
# Returns the JUnit report as String
|
57
|
+
def to_junit
|
58
|
+
JunitGenerator.new(self.data).generate
|
59
|
+
end
|
60
|
+
|
61
|
+
# @return [Bool] were all tests successful? Is false if at least one test failed
|
62
|
+
def tests_successful?
|
63
|
+
self.data.collect { |a| a[:number_of_failures] }.all?(&:zero?)
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def ensure_file_valid!
|
69
|
+
format_version = self.raw_json["FormatVersion"]
|
70
|
+
supported_versions = ["1.1", "1.2"]
|
71
|
+
UI.user_error!("Format version '#{format_version}' is not supported, must be #{supported_versions.join(', ')}") unless supported_versions.include?(format_version)
|
72
|
+
end
|
73
|
+
|
74
|
+
# Converts the raw plist test structure into something that's easier to enumerate
|
75
|
+
def unfold_tests(data)
|
76
|
+
# `data` looks like this
|
77
|
+
# => [{"Subtests"=>
|
78
|
+
# [{"Subtests"=>
|
79
|
+
# [{"Subtests"=>
|
80
|
+
# [{"Duration"=>0.4,
|
81
|
+
# "TestIdentifier"=>"Unit/testExample()",
|
82
|
+
# "TestName"=>"testExample()",
|
83
|
+
# "TestObjectClass"=>"IDESchemeActionTestSummary",
|
84
|
+
# "TestStatus"=>"Success",
|
85
|
+
# "TestSummaryGUID"=>"4A24BFED-03E6-4FBE-BC5E-2D80023C06B4"},
|
86
|
+
# {"FailureSummaries"=>
|
87
|
+
# [{"FileName"=>"/Users/krausefx/Developer/themoji/Unit/Unit.swift",
|
88
|
+
# "LineNumber"=>34,
|
89
|
+
# "Message"=>"XCTAssertTrue failed - ",
|
90
|
+
# "PerformanceFailure"=>false}],
|
91
|
+
# "TestIdentifier"=>"Unit/testExample2()",
|
92
|
+
|
93
|
+
tests = []
|
94
|
+
data.each do |current_hash|
|
95
|
+
if current_hash["Subtests"]
|
96
|
+
tests += unfold_tests(current_hash["Subtests"])
|
97
|
+
end
|
98
|
+
if current_hash["TestStatus"]
|
99
|
+
tests << current_hash
|
100
|
+
end
|
101
|
+
end
|
102
|
+
return tests
|
103
|
+
end
|
104
|
+
|
105
|
+
# Convert the Hashes and Arrays in something more useful
|
106
|
+
def parse_content
|
107
|
+
self.data = self.raw_json["TestableSummaries"].collect do |testable_summary|
|
108
|
+
summary_row = {
|
109
|
+
project_path: testable_summary["ProjectPath"],
|
110
|
+
target_name: testable_summary["TargetName"],
|
111
|
+
test_name: testable_summary["TestName"],
|
112
|
+
duration: testable_summary["Tests"].map { |current_test| current_test["Duration"] }.inject(:+),
|
113
|
+
tests: unfold_tests(testable_summary["Tests"]).collect do |current_test|
|
114
|
+
current_row = {
|
115
|
+
identifier: current_test["TestIdentifier"],
|
116
|
+
test_group: current_test["TestIdentifier"].split("/")[0..-2].join("."),
|
117
|
+
name: current_test["TestName"],
|
118
|
+
object_class: current_test["TestObjectClass"],
|
119
|
+
status: current_test["TestStatus"],
|
120
|
+
guid: current_test["TestSummaryGUID"],
|
121
|
+
duration: current_test["Duration"]
|
122
|
+
}
|
123
|
+
if current_test["FailureSummaries"]
|
124
|
+
current_row[:failures] = current_test["FailureSummaries"].collect do |current_failure|
|
125
|
+
{
|
126
|
+
file_name: current_failure['FileName'],
|
127
|
+
line_number: current_failure['LineNumber'],
|
128
|
+
message: current_failure['Message'],
|
129
|
+
performance_failure: current_failure['PerformanceFailure'],
|
130
|
+
failure_message: "#{current_failure['Message']} (#{current_failure['FileName']}:#{current_failure['LineNumber']})"
|
131
|
+
}
|
132
|
+
end
|
133
|
+
end
|
134
|
+
current_row
|
135
|
+
end
|
136
|
+
}
|
137
|
+
summary_row[:number_of_tests] = summary_row[:tests].count
|
138
|
+
summary_row[:number_of_failures] = summary_row[:tests].find_all { |a| (a[:failures] || []).count > 0 }.count
|
139
|
+
summary_row
|
140
|
+
end
|
141
|
+
self.data.first[:run_destination_name] = self.raw_json["RunDestination"]["Name"]
|
142
|
+
return self.data
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -125,7 +125,7 @@ module Commander
|
|
125
125
|
end
|
126
126
|
|
127
127
|
def rescue_file_error(e)
|
128
|
-
# We're also printing the new-lines, as otherwise the message is not very visible in-between the error and the
|
128
|
+
# We're also printing the new-lines, as otherwise the message is not very visible in-between the error and the stack trace
|
129
129
|
puts ""
|
130
130
|
FastlaneCore::UI.important("Error accessing file, this might be due to fastlane's directory handling")
|
131
131
|
FastlaneCore::UI.important("Check out https://docs.fastlane.tools/advanced/#directory-behavior for more details")
|
@@ -67,7 +67,14 @@ module Pilot
|
|
67
67
|
# If no groups are passed to options, remove the tester from the app-level,
|
68
68
|
# otherwise remove the tester from the groups specified.
|
69
69
|
if config[:groups].nil? && tester.kind_of?(Spaceship::Tunes::Tester::External)
|
70
|
-
|
70
|
+
test_flight_testers = Spaceship::TestFlight::Tester.search(app_id: app.apple_id, text: tester.email, is_email_exact_match: true)
|
71
|
+
|
72
|
+
if test_flight_testers.length > 1
|
73
|
+
UI.user_error!("Could not remove #{tester.email} from app: #{app.name}, reason: too many matches: #{test_flight_testers}")
|
74
|
+
elsif test_flight_testers.length == 0
|
75
|
+
UI.user_error!("Could not remove #{tester.email} from app: #{app.name}, reason: unable to find tester on app")
|
76
|
+
end
|
77
|
+
test_flight_tester = test_flight_testers.first
|
71
78
|
test_flight_tester.remove_from_app!(app_id: app.apple_id)
|
72
79
|
UI.success("Successfully removed tester, #{test_flight_tester.email}, from app: #{app.name}")
|
73
80
|
else
|
data/sigh/lib/sigh/runner.rb
CHANGED
@@ -61,7 +61,9 @@ module Sigh
|
|
61
61
|
# Fetches a profile matching the user's search requirements
|
62
62
|
def fetch_profiles
|
63
63
|
UI.message "Fetching profiles..."
|
64
|
-
results = profile_type.find_by_bundle_id(Sigh.config[:app_identifier],
|
64
|
+
results = profile_type.find_by_bundle_id(bundle_id: Sigh.config[:app_identifier],
|
65
|
+
mac: Sigh.config[:platform].to_s == 'macos',
|
66
|
+
sub_platform: Sigh.config[:platform].to_s == 'tvos' ? 'tvOS' : nil)
|
65
67
|
results = results.find_all do |current_profile|
|
66
68
|
if current_profile.valid? || Sigh.config[:force]
|
67
69
|
true
|
@@ -72,28 +74,13 @@ module Sigh
|
|
72
74
|
end
|
73
75
|
|
74
76
|
# Take the provisioning profile name into account
|
75
|
-
if Sigh.config[:provisioning_name].to_s.length > 0
|
76
|
-
filtered = results.select { |p| p.name.strip == Sigh.config[:provisioning_name].strip }
|
77
|
-
if Sigh.config[:ignore_profiles_with_different_name]
|
78
|
-
results = filtered
|
79
|
-
elsif (filtered || []).count > 0
|
80
|
-
results = filtered
|
81
|
-
end
|
82
|
-
end
|
77
|
+
results = filter_profiles_by_name(results) if Sigh.config[:provisioning_name].to_s.length > 0
|
83
78
|
|
84
79
|
# Since September 20, 2016 spaceship doesn't distinguish between AdHoc and AppStore profiles
|
85
80
|
# any more, since it requires an additional request
|
86
81
|
# Instead we only call is_adhoc? on the matching profiles to speed up spaceship
|
87
82
|
|
88
|
-
results = results
|
89
|
-
if profile_type == Spaceship.provisioning_profile.ad_hoc
|
90
|
-
current_profile.is_adhoc?
|
91
|
-
elsif profile_type == Spaceship.provisioning_profile.app_store
|
92
|
-
!current_profile.is_adhoc?
|
93
|
-
else
|
94
|
-
true
|
95
|
-
end
|
96
|
-
end
|
83
|
+
results = filter_profiles_for_adhoc_or_app_store(results)
|
97
84
|
|
98
85
|
return results if Sigh.config[:skip_certificate_verification]
|
99
86
|
|
@@ -154,6 +141,28 @@ module Sigh
|
|
154
141
|
profile
|
155
142
|
end
|
156
143
|
|
144
|
+
def filter_profiles_by_name(profiles)
|
145
|
+
filtered = profiles.select { |p| p.name.strip == Sigh.config[:provisioning_name].strip }
|
146
|
+
if Sigh.config[:ignore_profiles_with_different_name]
|
147
|
+
profiles = filtered
|
148
|
+
elsif (filtered || []).count > 0
|
149
|
+
profiles = filtered
|
150
|
+
end
|
151
|
+
profiles
|
152
|
+
end
|
153
|
+
|
154
|
+
def filter_profiles_for_adhoc_or_app_store(profiles)
|
155
|
+
profiles.find_all do |current_profile|
|
156
|
+
if profile_type == Spaceship.provisioning_profile.ad_hoc
|
157
|
+
current_profile.is_adhoc?
|
158
|
+
elsif profile_type == Spaceship.provisioning_profile.app_store
|
159
|
+
!current_profile.is_adhoc?
|
160
|
+
else
|
161
|
+
true
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
157
166
|
def certificates_for_profile_and_platform
|
158
167
|
case Sigh.config[:platform].to_s
|
159
168
|
when 'ios', 'tvos'
|
data/snapshot/README.md
CHANGED
@@ -79,6 +79,7 @@ Get in contact with the developer on Twitter: [@FastlaneTools](https://twitter.c
|
|
79
79
|
|
80
80
|
# Features
|
81
81
|
- Create hundreds of screenshots in multiple languages on all simulators
|
82
|
+
- Take screenshots in multiple device simulators concurrently to cut down execution time (Xcode 9 only)
|
82
83
|
- Configure it once, store the configuration in git
|
83
84
|
- Do something else, while the computer takes the screenshots for you
|
84
85
|
- Integrates with [`fastlane`](https://fastlane.tools) and [`deliver`](https://github.com/fastlane/fastlane/tree/master/deliver)
|
@@ -133,6 +134,7 @@ Here a few links to get started:
|
|
133
134
|
- Create a new UI Test target in your Xcode project ([top part of this article](https://krausefx.com/blog/run-xcode-7-ui-tests-from-the-command-line))
|
134
135
|
- Run `fastlane snapshot init` in your project folder
|
135
136
|
- Add the ./SnapshotHelper.swift to your UI Test target (You can move the file anywhere you want)
|
137
|
+
- **Note:** if you're using Xcode 8, add the ./SnapshotHelperXcode8.swift to your UI Test target
|
136
138
|
- (Objective C only) add the bridging header to your test class.
|
137
139
|
- `#import "MYUITests-Swift.h"`
|
138
140
|
- The bridging header is named after your test target with -Swift.h appended.
|
@@ -371,7 +373,7 @@ If you want to add frames around the screenshots and even put a title on top, ch
|
|
371
373
|
|
372
374
|
## Available language codes
|
373
375
|
```ruby
|
374
|
-
ALL_LANGUAGES = ["da", "de-DE", "el", "en-AU", "en-CA", "en-GB", "en-US", "es-ES", "es-MX", "fi", "fr-CA", "fr-FR", "id", "it", "ja", "ko", "ms", "nl", "no", "pt-BR", "pt-PT", "ru", "sv", "th", "tr", "vi", "zh-Hans", "zh-Hant"]
|
376
|
+
ALL_LANGUAGES = ["da", "de-DE", "el", "en-AU", "en-CA", "en-GB", "en-US", "es-ES", "es-MX", "fi", "fr-CA", "fr-FR", "id", "it", "ja", "ko", "ms", "nl-NL", "no", "pt-BR", "pt-PT", "ru", "sv", "th", "tr", "vi", "zh-Hans", "zh-Hant"]
|
375
377
|
```
|
376
378
|
|
377
379
|
To get more information about language and locale codes please read [Internationalization and Localization Guide](https://developer.apple.com/library/ios/documentation/MacOSX/Conceptual/BPInternational/LanguageandLocaleIDs/LanguageandLocaleIDs.html).
|
@@ -7,7 +7,7 @@
|
|
7
7
|
//
|
8
8
|
|
9
9
|
// -----------------------------------------------------
|
10
|
-
// IMPORTANT: When modifying this file, make sure to
|
10
|
+
// IMPORTANT: When modifying this file, make sure to
|
11
11
|
// increment the version number at the very
|
12
12
|
// bottom of the file to notify users about
|
13
13
|
// the new SnapshotHelper.swift
|
@@ -32,20 +32,48 @@ func snapshot(_ name: String, waitForLoadingIndicator: Bool = true) {
|
|
32
32
|
Snapshot.snapshot(name, waitForLoadingIndicator: waitForLoadingIndicator)
|
33
33
|
}
|
34
34
|
|
35
|
+
enum SnapshotError: Error, CustomDebugStringConvertible {
|
36
|
+
case cannotDetectUser
|
37
|
+
case cannotFindHomeDirectory
|
38
|
+
case cannotFindSimulatorHomeDirectory
|
39
|
+
case cannotAccessSimulatorHomeDirectory(String)
|
40
|
+
|
41
|
+
var debugDescription: String {
|
42
|
+
switch self {
|
43
|
+
case .cannotDetectUser:
|
44
|
+
return "Couldn't find Snapshot configuration files - can't detect current user "
|
45
|
+
case .cannotFindHomeDirectory:
|
46
|
+
return "Couldn't find Snapshot configuration files - can't detect `Users` dir"
|
47
|
+
case .cannotFindSimulatorHomeDirectory:
|
48
|
+
return "Couldn't find simulator home location. Please, check SIMULATOR_HOST_HOME env variable."
|
49
|
+
case .cannotAccessSimulatorHomeDirectory(let simulatorHostHome):
|
50
|
+
return "Can't prepare environment. Simulator home location is inaccessible. Does \(simulatorHostHome) exist?"
|
51
|
+
}
|
52
|
+
}
|
53
|
+
}
|
54
|
+
|
35
55
|
open class Snapshot: NSObject {
|
56
|
+
static var app: XCUIApplication!
|
57
|
+
static var cacheDirectory: URL!
|
58
|
+
static var screenshotsDirectory: URL? {
|
59
|
+
return cacheDirectory.appendingPathComponent("screenshots", isDirectory: true)
|
60
|
+
}
|
36
61
|
|
37
62
|
open class func setupSnapshot(_ app: XCUIApplication) {
|
38
|
-
|
39
|
-
|
40
|
-
|
63
|
+
do {
|
64
|
+
let cacheDir = try pathPrefix()
|
65
|
+
Snapshot.cacheDirectory = cacheDir
|
66
|
+
Snapshot.app = app
|
67
|
+
setLanguage(app)
|
68
|
+
setLocale(app)
|
69
|
+
setLaunchArguments(app)
|
70
|
+
} catch let error {
|
71
|
+
print(error)
|
72
|
+
}
|
41
73
|
}
|
42
74
|
|
43
75
|
class func setLanguage(_ app: XCUIApplication) {
|
44
|
-
|
45
|
-
return
|
46
|
-
}
|
47
|
-
|
48
|
-
let path = prefix.appendingPathComponent("language.txt")
|
76
|
+
let path = cacheDirectory.appendingPathComponent("language.txt")
|
49
77
|
|
50
78
|
do {
|
51
79
|
let trimCharacterSet = CharacterSet.whitespacesAndNewlines
|
@@ -57,11 +85,7 @@ open class Snapshot: NSObject {
|
|
57
85
|
}
|
58
86
|
|
59
87
|
class func setLocale(_ app: XCUIApplication) {
|
60
|
-
|
61
|
-
return
|
62
|
-
}
|
63
|
-
|
64
|
-
let path = prefix.appendingPathComponent("locale.txt")
|
88
|
+
let path = cacheDirectory.appendingPathComponent("locale.txt")
|
65
89
|
|
66
90
|
do {
|
67
91
|
let trimCharacterSet = CharacterSet.whitespacesAndNewlines
|
@@ -76,11 +100,7 @@ open class Snapshot: NSObject {
|
|
76
100
|
}
|
77
101
|
|
78
102
|
class func setLaunchArguments(_ app: XCUIApplication) {
|
79
|
-
|
80
|
-
return
|
81
|
-
}
|
82
|
-
|
83
|
-
let path = prefix.appendingPathComponent("snapshot-launch_arguments.txt")
|
103
|
+
let path = cacheDirectory.appendingPathComponent("snapshot-launch_arguments.txt")
|
84
104
|
app.launchArguments += ["-FASTLANE_SNAPSHOT", "YES", "-ui_testing"]
|
85
105
|
|
86
106
|
do {
|
@@ -105,12 +125,18 @@ open class Snapshot: NSObject {
|
|
105
125
|
|
106
126
|
sleep(1) // Waiting for the animation to be finished (kind of)
|
107
127
|
|
108
|
-
#if os(
|
109
|
-
XCUIApplication().childrenMatchingType(.Browser).count
|
110
|
-
#elseif os(OSX)
|
128
|
+
#if os(OSX)
|
111
129
|
XCUIApplication().typeKey(XCUIKeyboardKeySecondaryFn, modifierFlags: [])
|
112
130
|
#else
|
113
|
-
|
131
|
+
let screenshot = app.windows.firstMatch.screenshot()
|
132
|
+
guard let simulator = ProcessInfo().environment["SIMULATOR_DEVICE_NAME"], let screenshotsDir = screenshotsDirectory else { return }
|
133
|
+
let path = screenshotsDir.appendingPathComponent("\(simulator)-\(name).png")
|
134
|
+
do {
|
135
|
+
try screenshot.pngRepresentation.write(to: path)
|
136
|
+
} catch let error {
|
137
|
+
print("Problem writing screenshot: \(name) to \(path)")
|
138
|
+
print(error)
|
139
|
+
}
|
114
140
|
#endif
|
115
141
|
}
|
116
142
|
|
@@ -127,30 +153,26 @@ open class Snapshot: NSObject {
|
|
127
153
|
}
|
128
154
|
}
|
129
155
|
|
130
|
-
class func pathPrefix() -> URL? {
|
156
|
+
class func pathPrefix() throws -> URL? {
|
131
157
|
let homeDir: URL
|
132
|
-
//on OSX config is stored in /Users/<username>/Library
|
133
|
-
//and on iOS/tvOS/WatchOS it's in simulator's home dir
|
158
|
+
// on OSX config is stored in /Users/<username>/Library
|
159
|
+
// and on iOS/tvOS/WatchOS it's in simulator's home dir
|
134
160
|
#if os(OSX)
|
135
161
|
guard let user = ProcessInfo().environment["USER"] else {
|
136
|
-
|
137
|
-
return nil
|
162
|
+
throw SnapshotError.cannotDetectUser
|
138
163
|
}
|
139
164
|
|
140
165
|
guard let usersDir = FileManager.default.urls(for: .userDirectory, in: .localDomainMask).first else {
|
141
|
-
|
142
|
-
return nil
|
166
|
+
throw SnapshotError.cannotFindHomeDirectory
|
143
167
|
}
|
144
168
|
|
145
169
|
homeDir = usersDir.appendingPathComponent(user)
|
146
170
|
#else
|
147
171
|
guard let simulatorHostHome = ProcessInfo().environment["SIMULATOR_HOST_HOME"] else {
|
148
|
-
|
149
|
-
return nil
|
172
|
+
throw SnapshotError.cannotFindSimulatorHomeDirectory
|
150
173
|
}
|
151
174
|
guard let homeDirUrl = URL(string: simulatorHostHome) else {
|
152
|
-
|
153
|
-
return nil
|
175
|
+
throw SnapshotError.cannotAccessSimulatorHomeDirectory(simulatorHostHome)
|
154
176
|
}
|
155
177
|
homeDir = URL(fileURLWithPath: homeDirUrl.path)
|
156
178
|
#endif
|
@@ -170,4 +192,4 @@ extension XCUIElement {
|
|
170
192
|
|
171
193
|
// Please don't remove the lines below
|
172
194
|
// They are used to detect outdated configuration files
|
173
|
-
// SnapshotHelperVersion [1.
|
195
|
+
// SnapshotHelperVersion [1.5]
|