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