fastlane 2.236.1 → 2.238.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 +109 -102
- data/deliver/lib/deliver/options.rb +1 -1
- data/fastlane/lib/fastlane/actions/deploygate.rb +3 -2
- data/fastlane/lib/fastlane/actions/docs/upload_to_app_store.md.erb +7 -3
- data/fastlane/lib/fastlane/actions/hockey.rb +3 -2
- data/fastlane/lib/fastlane/actions/installr.rb +3 -2
- data/fastlane/lib/fastlane/actions/mailgun.rb +1 -0
- data/fastlane/lib/fastlane/actions/notarize.rb +3 -18
- data/fastlane/lib/fastlane/actions/register_devices.rb +21 -5
- data/fastlane/lib/fastlane/actions/setup_ci.rb +7 -2
- data/fastlane/lib/fastlane/actions/spm.rb +6 -0
- data/fastlane/lib/fastlane/actions/swiftlint.rb +10 -1
- data/fastlane/lib/fastlane/actions/testfairy.rb +5 -3
- data/fastlane/lib/fastlane/actions/tryouts.rb +3 -2
- data/fastlane/lib/fastlane/actions/update_project_team.rb +17 -9
- data/fastlane/lib/fastlane/actions/upload_symbols_to_crashlytics.rb +33 -7
- data/fastlane/lib/fastlane/helper/sh_helper.rb +7 -3
- data/fastlane/lib/fastlane/lane_manager.rb +0 -1
- data/fastlane/lib/fastlane/plugins/template/.github/workflows/test.yml +0 -7
- data/fastlane/lib/fastlane/plugins/template/.gitignore +0 -1
- data/fastlane/lib/fastlane/plugins/template/.rspec +0 -2
- data/fastlane/lib/fastlane/plugins/template/Gemfile.erb +0 -2
- data/fastlane/lib/fastlane/version.rb +1 -1
- data/fastlane/swift/Deliverfile.swift +1 -1
- data/fastlane/swift/DeliverfileProtocol.swift +2 -2
- data/fastlane/swift/Fastlane.swift +50 -20
- data/fastlane/swift/FastlaneSwiftRunner/FastlaneSwiftRunner.xcodeproj/project.pbxproj +1 -1
- data/fastlane/swift/Gymfile.swift +1 -1
- data/fastlane/swift/GymfileProtocol.swift +1 -1
- data/fastlane/swift/Matchfile.swift +1 -1
- data/fastlane/swift/MatchfileProtocol.swift +8 -1
- data/fastlane/swift/Precheckfile.swift +1 -1
- data/fastlane/swift/PrecheckfileProtocol.swift +1 -1
- data/fastlane/swift/Scanfile.swift +1 -1
- data/fastlane/swift/ScanfileProtocol.swift +8 -1
- data/fastlane/swift/Screengrabfile.swift +1 -1
- data/fastlane/swift/ScreengrabfileProtocol.swift +1 -1
- data/fastlane/swift/Snapshotfile.swift +1 -1
- data/fastlane/swift/SnapshotfileProtocol.swift +1 -1
- data/fastlane_core/lib/fastlane_core/analytics/analytics_event_builder.rb +31 -5
- data/fastlane_core/lib/fastlane_core/analytics/analytics_ingester_client.rb +24 -12
- data/fastlane_core/lib/fastlane_core/analytics/analytics_session.rb +22 -12
- data/fastlane_core/lib/fastlane_core/cert_checker.rb +22 -9
- data/fastlane_core/lib/fastlane_core/helper.rb +4 -3
- data/fastlane_core/lib/fastlane_core/ipa_upload_package_builder.rb +6 -2
- data/fastlane_core/lib/fastlane_core/itunes_transporter.rb +3 -1
- data/fastlane_core/lib/fastlane_core/keychain_importer.rb +1 -1
- data/fastlane_core/lib/fastlane_core/pkg_upload_package_builder.rb +1 -1
- data/fastlane_core/lib/fastlane_core/print_table.rb +0 -16
- data/fastlane_core/lib/fastlane_core/project.rb +3 -1
- data/fastlane_core/lib/fastlane_core/queue_worker.rb +10 -1
- data/fastlane_core/lib/fastlane_core/ui/errors/fastlane_shell_error.rb +6 -0
- data/fastlane_core/lib/fastlane_core/ui/fastlane_runner.rb +1 -1
- data/fastlane_core/lib/fastlane_core/video_utils.rb +4 -2
- data/gym/lib/gym/error_handler.rb +11 -1
- data/gym/lib/gym/generators/build_command_generator.rb +2 -0
- data/gym/lib/gym/module.rb +20 -2
- data/match/lib/match/options.rb +8 -0
- data/match/lib/match/runner.rb +4 -2
- data/match/lib/match/storage/git_storage.rb +18 -2
- data/pilot/lib/pilot/build_manager.rb +2 -2
- data/pilot/lib/pilot/options.rb +2 -2
- data/pilot/lib/pilot/tester_manager.rb +2 -2
- data/precheck/lib/precheck/rules/copyright_date_rule.rb +16 -10
- data/precheck/lib/precheck/rules/unreachable_urls_rule.rb +2 -2
- data/scan/lib/scan/options.rb +5 -0
- data/scan/lib/scan/runner.rb +1 -0
- data/scan/lib/scan/test_command_generator.rb +2 -0
- data/snapshot/lib/snapshot/simulator_launchers/simulator_launcher_base.rb +4 -1
- data/spaceship/lib/spaceship/client.rb +0 -1
- data/spaceship/lib/spaceship/connect_api/file_uploader.rb +1 -1
- data/spaceship/lib/spaceship/connect_api/models/age_rating_declaration.rb +6 -0
- data/spaceship/lib/spaceship/connect_api/models/beta_group.rb +5 -0
- data/spaceship/lib/spaceship/connect_api/tunes/tunes.rb +7 -2
- data/spaceship/lib/spaceship/helper/plist_middleware.rb +37 -8
- data/supply/lib/supply/uploader.rb +7 -1
- data/trainer/lib/trainer/xcresult/test_case.rb +21 -2
- data/trainer/lib/trainer/xcresult/test_plan.rb +3 -3
- data/trainer/lib/trainer/xcresult/test_suite.rb +28 -19
- metadata +74 -32
|
@@ -34,21 +34,33 @@ module FastlaneCore
|
|
|
34
34
|
|
|
35
35
|
def post_request(event)
|
|
36
36
|
connection = Faraday.new(GA_URL) do |conn|
|
|
37
|
-
|
|
37
|
+
# This request runs while fastlane is shutting down,
|
|
38
|
+
# so keep the timeouts short to not delay the user
|
|
39
|
+
conn.options.open_timeout = 5
|
|
40
|
+
conn.options.timeout = 5
|
|
38
41
|
conn.adapter(Faraday.default_adapter)
|
|
39
42
|
end
|
|
40
43
|
connection.headers[:user_agent] = 'fastlane/' + Fastlane::VERSION
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
44
|
+
|
|
45
|
+
# GA4 protocol, event parameters are sent as `ep.<name>` query parameters
|
|
46
|
+
params = {
|
|
47
|
+
v: "2", # Protocol version (GA4)
|
|
48
|
+
tid: @ga_tracking, # GA4 measurement ID
|
|
49
|
+
cid: event[:client_id], # Client ID
|
|
50
|
+
sid: event[:session_id], # Session ID
|
|
51
|
+
_ss: "1", # Session start
|
|
52
|
+
seg: "1", # Session engaged
|
|
53
|
+
# Engagement time in ms, required for events to count towards active users
|
|
54
|
+
_et: (event[:engagement_time_msec] || 100).to_s,
|
|
55
|
+
en: event[:name].to_s # Event name
|
|
56
|
+
}
|
|
57
|
+
(event[:params] || {}).each do |key, value|
|
|
58
|
+
params["ep.#{key}"] = value.to_s
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
connection.post("/g/collect") do |request|
|
|
62
|
+
request.params = params
|
|
63
|
+
end
|
|
52
64
|
end
|
|
53
65
|
end
|
|
54
66
|
end
|
|
@@ -4,18 +4,20 @@ require_relative 'analytics_event_builder'
|
|
|
4
4
|
|
|
5
5
|
module FastlaneCore
|
|
6
6
|
class AnalyticsSession
|
|
7
|
-
GA_TRACKING = "
|
|
7
|
+
GA_TRACKING = "G-94HQ3VVP0X"
|
|
8
8
|
|
|
9
9
|
private_constant :GA_TRACKING
|
|
10
10
|
attr_accessor :session_id
|
|
11
11
|
attr_accessor :client
|
|
12
12
|
|
|
13
13
|
def initialize(analytics_ingester_client: AnalyticsIngesterClient.new(GA_TRACKING))
|
|
14
|
-
|
|
15
|
-
@session_id =
|
|
14
|
+
# GA4 requires a numeric session ID for events to be attributed to a session
|
|
15
|
+
@session_id = Time.now.to_i.to_s
|
|
16
|
+
@session_start_time = Time.now
|
|
16
17
|
@client = analytics_ingester_client
|
|
17
18
|
@threads = []
|
|
18
|
-
@
|
|
19
|
+
@launch_event = nil
|
|
20
|
+
@launch_event_captured = false
|
|
19
21
|
end
|
|
20
22
|
|
|
21
23
|
def action_launched(launch_context: nil)
|
|
@@ -23,23 +25,22 @@ module FastlaneCore
|
|
|
23
25
|
show_message
|
|
24
26
|
end
|
|
25
27
|
|
|
26
|
-
if @
|
|
28
|
+
if @launch_event_captured || launch_context.p_hash.nil?
|
|
27
29
|
return
|
|
28
30
|
end
|
|
29
31
|
|
|
30
|
-
@
|
|
32
|
+
@launch_event_captured = true
|
|
31
33
|
builder = AnalyticsEventBuilder.new(
|
|
32
34
|
p_hash: launch_context.p_hash,
|
|
33
35
|
session_id: session_id,
|
|
34
36
|
action_name: nil,
|
|
35
|
-
fastlane_client_language: launch_context.fastlane_client_language
|
|
37
|
+
fastlane_client_language: launch_context.fastlane_client_language,
|
|
38
|
+
build_tool_version: launch_context.build_tool_version
|
|
36
39
|
)
|
|
37
40
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
@threads << post_thread
|
|
42
|
-
end
|
|
41
|
+
# The event is not posted until finalize_session, so that the
|
|
42
|
+
# run duration can be reported as GA4 engagement time
|
|
43
|
+
@launch_event = builder.new_event(:launch)
|
|
43
44
|
end
|
|
44
45
|
|
|
45
46
|
def action_completed(completion_context: nil)
|
|
@@ -64,6 +65,15 @@ module FastlaneCore
|
|
|
64
65
|
end
|
|
65
66
|
|
|
66
67
|
def finalize_session
|
|
68
|
+
unless @launch_event.nil?
|
|
69
|
+
@launch_event[:engagement_time_msec] = ((Time.now - @session_start_time) * 1000).round
|
|
70
|
+
post_thread = client.post_event(@launch_event)
|
|
71
|
+
unless post_thread.nil?
|
|
72
|
+
@threads << post_thread
|
|
73
|
+
end
|
|
74
|
+
@launch_event = nil
|
|
75
|
+
end
|
|
76
|
+
|
|
67
77
|
@threads.map(&:join)
|
|
68
78
|
end
|
|
69
79
|
end
|
|
@@ -43,6 +43,15 @@ WWDRCA_CERTIFICATES = [
|
|
|
43
43
|
}
|
|
44
44
|
]
|
|
45
45
|
|
|
46
|
+
# Common names of the certificates in `WWDRCA_CERTIFICATES`. The WWDR
|
|
47
|
+
# certificates (G2-G6) and the Developer ID certificates (DEV-ID-G1,
|
|
48
|
+
# DEV-ID-G2) use different common names, so each of them has to be queried
|
|
49
|
+
# separately when looking up the installed certificates
|
|
50
|
+
WWDRCA_CERTIFICATE_NAMES = [
|
|
51
|
+
'Apple Worldwide Developer Relations',
|
|
52
|
+
'Developer ID Certification Authority'
|
|
53
|
+
]
|
|
54
|
+
|
|
46
55
|
module FastlaneCore
|
|
47
56
|
# This class checks if a specific certificate is installed on the current mac
|
|
48
57
|
class CertChecker
|
|
@@ -123,18 +132,21 @@ module FastlaneCore
|
|
|
123
132
|
end
|
|
124
133
|
|
|
125
134
|
def self.installed_wwdr_certificates(keychain: nil)
|
|
126
|
-
certificate_name = "Apple Worldwide Developer Relations"
|
|
127
135
|
keychain ||= wwdr_keychain # backwards compatibility
|
|
128
136
|
|
|
129
|
-
# Find all installed WWDRCA certificates
|
|
137
|
+
# Find all installed WWDRCA certificates. A single `security
|
|
138
|
+
# find-certificate` call can only match one common name, so query the
|
|
139
|
+
# keychain once per common name used by the certificates
|
|
130
140
|
installed_certs = []
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
141
|
+
WWDRCA_CERTIFICATE_NAMES.each do |certificate_name|
|
|
142
|
+
Helper.backticks("security find-certificate -a -c '#{certificate_name}' -p #{keychain.shellescape}", print: false)
|
|
143
|
+
.lines
|
|
144
|
+
.each do |line|
|
|
145
|
+
if line.start_with?('-----BEGIN CERTIFICATE-----')
|
|
146
|
+
installed_certs << line
|
|
147
|
+
else
|
|
148
|
+
installed_certs.last << line
|
|
149
|
+
end
|
|
138
150
|
end
|
|
139
151
|
end
|
|
140
152
|
|
|
@@ -145,6 +157,7 @@ module FastlaneCore
|
|
|
145
157
|
WWDRCA_CERTIFICATES.find { |c| c[:sha256].casecmp?(sha256) }&.fetch(:alias)
|
|
146
158
|
end
|
|
147
159
|
.compact
|
|
160
|
+
.uniq
|
|
148
161
|
end
|
|
149
162
|
|
|
150
163
|
def self.install_missing_wwdr_certificates(in_keychain: nil)
|
|
@@ -91,9 +91,10 @@ module FastlaneCore
|
|
|
91
91
|
end
|
|
92
92
|
|
|
93
93
|
def self.operating_system
|
|
94
|
-
return "macOS" if
|
|
95
|
-
return "Windows" if
|
|
96
|
-
return "Linux" if
|
|
94
|
+
return "macOS" if self.mac?
|
|
95
|
+
return "Windows" if self.windows?
|
|
96
|
+
return "Linux" if self.linux?
|
|
97
|
+
return "Java" if RUBY_PLATFORM =~ /java/
|
|
97
98
|
return "Unknown"
|
|
98
99
|
end
|
|
99
100
|
|
|
@@ -14,8 +14,12 @@ module FastlaneCore
|
|
|
14
14
|
|
|
15
15
|
def generate(app_id: nil, ipa_path: nil, package_path: nil, platform: nil, app_identifier: nil, short_version: nil, bundle_version: nil)
|
|
16
16
|
unless Helper.is_mac?
|
|
17
|
-
# .itmsp packages are not supported for ipa uploads starting Transporter 4.1, for non-macOS
|
|
18
|
-
|
|
17
|
+
# .itmsp packages are not supported for ipa uploads starting Transporter 4.1, for non-macOS.
|
|
18
|
+
# Use a unique subdirectory anyway: callers pass a shared parent (deliver passes "/tmp"),
|
|
19
|
+
# and ItunesTransporter#upload runs FileUtils.rm_rf on whatever this returns.
|
|
20
|
+
self.package_path = File.join(package_path, "#{app_id}-#{SecureRandom.uuid}")
|
|
21
|
+
FileUtils.mkdir_p(self.package_path)
|
|
22
|
+
|
|
19
23
|
copy_ipa(ipa_path)
|
|
20
24
|
|
|
21
25
|
# copy any AppStoreInfo.plist file that's next to the ipa file
|
|
@@ -404,6 +404,8 @@ module FastlaneCore
|
|
|
404
404
|
private
|
|
405
405
|
|
|
406
406
|
def file_upload_option(source)
|
|
407
|
+
# altool's --upload-app and --validate-app only support the -f flag,
|
|
408
|
+
# unlike iTMSTransporter which requires -assetFile for .ipa/.pkg since 2026.
|
|
407
409
|
"-f #{source.shellescape}"
|
|
408
410
|
end
|
|
409
411
|
|
|
@@ -482,7 +484,7 @@ module FastlaneCore
|
|
|
482
484
|
'"' + Helper.transporter_path + '"',
|
|
483
485
|
'-m verify',
|
|
484
486
|
build_credential_params(username, password, jwt),
|
|
485
|
-
|
|
487
|
+
file_upload_option(source),
|
|
486
488
|
("-WONoPause true" if Helper.windows?), # Windows only: process instantly returns instead of waiting for key press
|
|
487
489
|
("-itc_provider #{provider_short_name}" if jwt.nil? && !provider_short_name.to_s.empty?)
|
|
488
490
|
].compact.join(' ')
|
|
@@ -10,7 +10,7 @@ module FastlaneCore
|
|
|
10
10
|
password_part = " -P #{certificate_password.shellescape}"
|
|
11
11
|
certificate_format_part = certificate_format.to_s.strip.empty? ? "" : " -f #{certificate_format.shellescape}"
|
|
12
12
|
|
|
13
|
-
command = "security import #{path.shellescape} -k
|
|
13
|
+
command = "security import #{path.shellescape} -k #{keychain_path.shellescape}"
|
|
14
14
|
command << password_part
|
|
15
15
|
command << certificate_format_part
|
|
16
16
|
command << " -T /usr/bin/codesign" # to not be asked for permission when running a tool like `gym` (before Sierra)
|
|
@@ -22,7 +22,7 @@ module FastlaneCore
|
|
|
22
22
|
apple_id: app_id,
|
|
23
23
|
file_size: File.size(pkg_path),
|
|
24
24
|
ipa_path: File.basename(pkg_path), # this is only the base name as the ipa is inside the package
|
|
25
|
-
md5: Digest::MD5.
|
|
25
|
+
md5: Digest::MD5.file(pkg_path).hexdigest,
|
|
26
26
|
archive_type: 'product-archive',
|
|
27
27
|
platform: platform
|
|
28
28
|
}
|
|
@@ -1,22 +1,6 @@
|
|
|
1
1
|
require_relative 'configuration/configuration'
|
|
2
2
|
require_relative 'helper'
|
|
3
3
|
|
|
4
|
-
# Monkey patch Terminal::Table until this is merged
|
|
5
|
-
# https://github.com/tj/terminal-table/pull/131
|
|
6
|
-
# solves https://github.com/fastlane/fastlane/issues/21852
|
|
7
|
-
# loads Terminal::Table first to be able to monkey patch it.
|
|
8
|
-
require 'terminal-table'
|
|
9
|
-
module Terminal
|
|
10
|
-
class Table
|
|
11
|
-
class Cell
|
|
12
|
-
def lines
|
|
13
|
-
# @value.to_s.split(/\n/)
|
|
14
|
-
@value.to_s.encode("utf-8", invalid: :replace).split(/\n/)
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
|
|
20
4
|
module FastlaneCore
|
|
21
5
|
class PrintTable
|
|
22
6
|
class << self
|
|
@@ -395,7 +395,9 @@ module FastlaneCore
|
|
|
395
395
|
xcode_at_least_13 = FastlaneCore::Helper.xcode_at_least?("13")
|
|
396
396
|
if xcode_at_least_13 && options[:destination]
|
|
397
397
|
begin
|
|
398
|
-
|
|
398
|
+
destination = options[:destination]
|
|
399
|
+
destination = destination.first if destination.kind_of?(Array)
|
|
400
|
+
destination_parameter = " " + "-destination #{destination.shellescape}"
|
|
399
401
|
rescue => ex
|
|
400
402
|
# xcodebuild command can continue without destination parameter, so
|
|
401
403
|
# we really don't care about this exception if something goes wrong with shellescape
|
|
@@ -33,17 +33,26 @@ module FastlaneCore
|
|
|
33
33
|
@queue.close
|
|
34
34
|
|
|
35
35
|
threads = []
|
|
36
|
+
results = Queue.new
|
|
36
37
|
@concurrency.times do
|
|
37
38
|
threads << Thread.new do
|
|
39
|
+
# Exceptions are re-joined (and raised) below, so this just causes duplicate raised exceptions
|
|
40
|
+
Thread.current.report_on_exception = false
|
|
41
|
+
|
|
38
42
|
job = @queue.pop
|
|
39
43
|
while job
|
|
40
|
-
@block.call(job)
|
|
44
|
+
results << @block.call(job)
|
|
41
45
|
job = @queue.pop
|
|
42
46
|
end
|
|
43
47
|
end
|
|
44
48
|
end
|
|
45
49
|
|
|
46
50
|
threads.each(&:join)
|
|
51
|
+
|
|
52
|
+
# Convert Queue to Array
|
|
53
|
+
real_results = []
|
|
54
|
+
real_results << results.pop until results.empty?
|
|
55
|
+
real_results
|
|
47
56
|
end
|
|
48
57
|
end
|
|
49
58
|
end
|
|
@@ -3,6 +3,12 @@ require_relative 'fastlane_exception'
|
|
|
3
3
|
module FastlaneCore
|
|
4
4
|
class Interface
|
|
5
5
|
class FastlaneShellError < FastlaneException
|
|
6
|
+
attr_reader :show_github_issues
|
|
7
|
+
|
|
8
|
+
def initialize(options = {})
|
|
9
|
+
@show_github_issues = options[:show_github_issues].nil? ? false : options[:show_github_issues]
|
|
10
|
+
end
|
|
11
|
+
|
|
6
12
|
def prefix
|
|
7
13
|
'[SHELL_ERROR]'
|
|
8
14
|
end
|
|
@@ -167,7 +167,7 @@ module Commander
|
|
|
167
167
|
end
|
|
168
168
|
rescue FastlaneCore::Interface::FastlaneCommonException => e # these are exceptions that we don't count as crashes
|
|
169
169
|
display_user_error!(e, e.to_s)
|
|
170
|
-
rescue FastlaneCore::Interface::FastlaneError => e # user_error!
|
|
170
|
+
rescue FastlaneCore::Interface::FastlaneError, FastlaneCore::Interface::FastlaneShellError => e # user_error! or shell_error!
|
|
171
171
|
rescue_fastlane_error(e)
|
|
172
172
|
rescue Errno::ENOENT => e
|
|
173
173
|
rescue_file_error(e)
|
|
@@ -60,7 +60,9 @@ module FastlaneCore
|
|
|
60
60
|
nil
|
|
61
61
|
end
|
|
62
62
|
|
|
63
|
-
# Reads the moov box and returns [w, h]
|
|
63
|
+
# Reads the moov box and returns [w, h] from the first trak/tkhd with non-zero dimensions, else nil.
|
|
64
|
+
# Non-video tracks (e.g. audio) carry a legitimate tkhd width/height of 0x0,
|
|
65
|
+
# so zero-sized results are skipped to keep scanning for the video track.
|
|
64
66
|
# Input: io (IO) positioned at moov contents; moov_end (Integer) absolute end offset.
|
|
65
67
|
# Output: [width, height] or nil.
|
|
66
68
|
def self.extract_resolution_from_moov(io, moov_end)
|
|
@@ -71,7 +73,7 @@ module FastlaneCore
|
|
|
71
73
|
if atom_type == "trak"
|
|
72
74
|
trak_end = io.pos + (atom_size - 8)
|
|
73
75
|
resolution = extract_resolution_from_trak(io, trak_end)
|
|
74
|
-
return resolution if resolution
|
|
76
|
+
return resolution if resolution && resolution[0] > 0 && resolution[1] > 0
|
|
75
77
|
io.seek(trak_end, IO::SEEK_SET)
|
|
76
78
|
else
|
|
77
79
|
skip(io, atom_size - 8)
|
|
@@ -151,11 +151,21 @@ module Gym
|
|
|
151
151
|
# `xcodebuild` doesn't properly mark lines as failure reason or important information
|
|
152
152
|
# so we assume that the last few lines show the error message that's relevant
|
|
153
153
|
# (at least that's what was correct during testing)
|
|
154
|
-
|
|
154
|
+
log_lines = File.read(log_path).split("\n")
|
|
155
|
+
log_content = log_lines.last(5)
|
|
155
156
|
log_content.each do |row|
|
|
156
157
|
UI.command_output(row)
|
|
157
158
|
end
|
|
158
159
|
|
|
160
|
+
# If we didn't find the error in the last 5 lines, let's search for it
|
|
161
|
+
# This is specifically for errors that might be buried by subsequent warnings
|
|
162
|
+
last_5_lines = log_content.join("\n")
|
|
163
|
+
unless last_5_lines.include?("error:")
|
|
164
|
+
UI.verbose("Error not found in last 5 lines, searching for it in the log...")
|
|
165
|
+
error_line = log_lines.reverse.find { |l| l.include?("error:") }
|
|
166
|
+
UI.command_output(error_line) if error_line
|
|
167
|
+
end
|
|
168
|
+
|
|
159
169
|
UI.message("")
|
|
160
170
|
UI.error("⬆️ Check out the few lines of raw `xcodebuild` output above for potential hints on how to solve this error")
|
|
161
171
|
UI.important("📋 For the complete and more detailed error log, check the full log at:")
|
|
@@ -119,6 +119,8 @@ module Gym
|
|
|
119
119
|
end
|
|
120
120
|
|
|
121
121
|
def pipe_xcpretty
|
|
122
|
+
UI.important("Using xcpretty can result in missing some build errors and is slower than the preferred xcbeautify. See https://docs.fastlane.tools/best-practices/xcodebuild-formatters/ for more information.")
|
|
123
|
+
|
|
122
124
|
pipe = []
|
|
123
125
|
|
|
124
126
|
formatter = Gym.config[:xcpretty_formatter]
|
data/gym/lib/gym/module.rb
CHANGED
|
@@ -56,11 +56,29 @@ module Gym
|
|
|
56
56
|
end
|
|
57
57
|
|
|
58
58
|
def building_mac_catalyst_for_ios?
|
|
59
|
-
Gym.project.supports_mac_catalyst?
|
|
59
|
+
return false unless Gym.project.supports_mac_catalyst?
|
|
60
|
+
|
|
61
|
+
# If catalyst_platform is explicitly set, use it
|
|
62
|
+
if Gym.config[:catalyst_platform]
|
|
63
|
+
return Gym.config[:catalyst_platform] == "ios"
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# If catalyst_platform is not set, use SDK to determine
|
|
67
|
+
# Mac Catalyst apps with iOS SDK build for iOS (IPA), not macOS
|
|
68
|
+
return Gym.config[:sdk] == "iphoneos" || Gym.config[:sdk] == "iphonesimulator"
|
|
60
69
|
end
|
|
61
70
|
|
|
62
71
|
def building_mac_catalyst_for_mac?
|
|
63
|
-
Gym.project.supports_mac_catalyst?
|
|
72
|
+
return false unless Gym.project.supports_mac_catalyst?
|
|
73
|
+
|
|
74
|
+
# If catalyst_platform is explicitly set, use it
|
|
75
|
+
if Gym.config[:catalyst_platform]
|
|
76
|
+
return Gym.config[:catalyst_platform] == "macos"
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# If catalyst_platform is not set, use SDK to determine
|
|
80
|
+
# Mac Catalyst apps with SDK macosx build for Mac (PKG), otherwise for iOS (IPA)
|
|
81
|
+
return Gym.config[:sdk] == "macosx"
|
|
64
82
|
end
|
|
65
83
|
|
|
66
84
|
def building_multiplatform_for_ios?
|
data/match/lib/match/options.rb
CHANGED
|
@@ -297,6 +297,14 @@ module Match
|
|
|
297
297
|
description: "Renew the provisioning profiles if the certificate count on the developer portal has changed. Works only for the 'development' provisioning profile type. Requires 'include_all_certificates' option to be 'true'",
|
|
298
298
|
type: Boolean,
|
|
299
299
|
default_value: false),
|
|
300
|
+
FastlaneCore::ConfigItem.new(key: :renew_expired_certs,
|
|
301
|
+
env_name: "MATCH_RENEW_EXPIRED_CERTS",
|
|
302
|
+
description: "Automatically renew expired certificates. Note: to renew `developer_id` and `developer_id_installer` certificates " \
|
|
303
|
+
"you must log in with the Account Holder account by using username and password; App Store Connect API key doesn't work in this case. " \
|
|
304
|
+
"The expired certificate is removed from the match storage and a new one is created, but the old certificate is not revoked on the " \
|
|
305
|
+
"Apple Developer Portal — run `fastlane match nuke` if you need to free up certificate slots",
|
|
306
|
+
type: Boolean,
|
|
307
|
+
default_value: false),
|
|
300
308
|
FastlaneCore::ConfigItem.new(key: :skip_confirmation,
|
|
301
309
|
env_name: "MATCH_SKIP_CONFIRMATION",
|
|
302
310
|
description: "Disables confirmation prompts during nuke, answering them with yes",
|
data/match/lib/match/runner.rb
CHANGED
|
@@ -85,12 +85,12 @@ module Match
|
|
|
85
85
|
end
|
|
86
86
|
|
|
87
87
|
# Certificate
|
|
88
|
-
cert_id = fetch_certificate(params: params, renew_expired_certs:
|
|
88
|
+
cert_id = fetch_certificate(params: params, renew_expired_certs: params[:renew_expired_certs])
|
|
89
89
|
|
|
90
90
|
# Mac Installer Distribution Certificate
|
|
91
91
|
additional_cert_types = params[:additional_cert_types] || []
|
|
92
92
|
cert_ids = additional_cert_types.map do |additional_cert_type|
|
|
93
|
-
fetch_certificate(params: params, renew_expired_certs:
|
|
93
|
+
fetch_certificate(params: params, renew_expired_certs: params[:renew_expired_certs], specific_cert_type: additional_cert_type)
|
|
94
94
|
end
|
|
95
95
|
|
|
96
96
|
profile_type = Sigh.profile_type_for_distribution_type(
|
|
@@ -174,6 +174,8 @@ module Match
|
|
|
174
174
|
is_cert_renewable = is_authenticated_with_login || is_cert_renewable_via_api
|
|
175
175
|
|
|
176
176
|
# Validate existing certificate first.
|
|
177
|
+
# NOTE: only a single certificate is considered here — the one matching
|
|
178
|
+
# `certificate_id`, or otherwise the last one found for this type.
|
|
177
179
|
if renew_expired_certs && is_cert_renewable && storage_has_certs && !params[:readonly]
|
|
178
180
|
cert_path = select_cert_or_key(paths: certs, certificate_id: certificate_id)
|
|
179
181
|
|
|
@@ -111,7 +111,7 @@ module Match
|
|
|
111
111
|
|
|
112
112
|
begin
|
|
113
113
|
# GIT_TERMINAL_PROMPT will fail the `git clone` command if user credentials are missing
|
|
114
|
-
Helper.with_env_values(
|
|
114
|
+
Helper.with_env_values(git_env_values) do
|
|
115
115
|
FastlaneCore::CommandExecutor.execute(command: command,
|
|
116
116
|
print_all: FastlaneCore::Globals.verbose?,
|
|
117
117
|
print_command: FastlaneCore::Globals.verbose?)
|
|
@@ -252,7 +252,7 @@ module Match
|
|
|
252
252
|
commands << git_push_command
|
|
253
253
|
|
|
254
254
|
UI.message("Pushing changes to remote git repo...")
|
|
255
|
-
Helper.with_env_values(
|
|
255
|
+
Helper.with_env_values(git_env_values) do
|
|
256
256
|
commands.each do |command|
|
|
257
257
|
FastlaneCore::CommandExecutor.execute(command: command,
|
|
258
258
|
print_all: FastlaneCore::Globals.verbose?,
|
|
@@ -263,6 +263,22 @@ module Match
|
|
|
263
263
|
UI.error("Couldn't commit or push changes back to git...")
|
|
264
264
|
UI.error(ex)
|
|
265
265
|
end
|
|
266
|
+
|
|
267
|
+
def git_env_values
|
|
268
|
+
env_values = { 'GIT_TERMINAL_PROMPT' => '0' }
|
|
269
|
+
env_values['GIT_SSH_COMMAND'] = non_interactive_git_ssh_command
|
|
270
|
+
env_values
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
def non_interactive_git_ssh_command
|
|
274
|
+
ssh_command = ENV['GIT_SSH_COMMAND']
|
|
275
|
+
|
|
276
|
+
if ssh_command.nil? || ssh_command.empty?
|
|
277
|
+
return "ssh -o BatchMode=yes"
|
|
278
|
+
end
|
|
279
|
+
return ssh_command if ssh_command.include?('BatchMode=yes')
|
|
280
|
+
"#{ssh_command} -o BatchMode=yes"
|
|
281
|
+
end
|
|
266
282
|
end
|
|
267
283
|
end
|
|
268
284
|
end
|
|
@@ -172,7 +172,7 @@ module Pilot
|
|
|
172
172
|
# Verify the build has all the includes that we need
|
|
173
173
|
# and fetch a new build if not
|
|
174
174
|
if build && (!build.app || !build.build_beta_detail || !build.pre_release_version)
|
|
175
|
-
UI.important("Build did include information for app, build beta detail and pre release version")
|
|
175
|
+
UI.important("Build did not include information for app, build beta detail and pre release version")
|
|
176
176
|
UI.important("Fetching a new build with all the information needed")
|
|
177
177
|
build = Spaceship::ConnectAPI::Build.get(build_id: build.id)
|
|
178
178
|
end
|
|
@@ -459,7 +459,7 @@ module Pilot
|
|
|
459
459
|
if options[:groups]
|
|
460
460
|
app = uploaded_build.app
|
|
461
461
|
beta_groups = app.get_beta_groups.select do |group|
|
|
462
|
-
options[:groups]
|
|
462
|
+
group.matches_identifiers?(options[:groups])
|
|
463
463
|
end
|
|
464
464
|
|
|
465
465
|
unless beta_groups.empty?
|
data/pilot/lib/pilot/options.rb
CHANGED
|
@@ -267,7 +267,7 @@ module Pilot
|
|
|
267
267
|
FastlaneCore::ConfigItem.new(key: :groups,
|
|
268
268
|
short_option: "-g",
|
|
269
269
|
env_name: "PILOT_GROUPS",
|
|
270
|
-
description: "Associate tester to one group or more by group name / group id. E.g. `-g \"Team 1\",\"
|
|
270
|
+
description: "Associate tester to one group or more by group name / group id. E.g. `-g \"Team 1\",\"a06cf5b5-95a9-4beb-88c6-f22bd6b3f7a2\"` This is required when `distribute_external` option is set to true or when we want to add a tester to one or more external testing groups ",
|
|
271
271
|
optional: true,
|
|
272
272
|
type: Array,
|
|
273
273
|
verify_block: proc do |value|
|
|
@@ -312,7 +312,7 @@ module Pilot
|
|
|
312
312
|
# rubocop:disable Layout/LineLength
|
|
313
313
|
FastlaneCore::ConfigItem.new(key: :itc_provider,
|
|
314
314
|
env_name: "PILOT_ITC_PROVIDER",
|
|
315
|
-
description: "The provider short name to be used with the iTMSTransporter to identify your team. This value will override the automatically detected provider short name. To get provider short name run `
|
|
315
|
+
description: "The provider short name to be used with the iTMSTransporter to identify your team. This value will override the automatically detected provider short name. To get provider short name run `xcrun iTMSTransporter -m provider -u 'USERNAME' -p 'PASSWORD' -account_type itunes_connect -v off`. The short names of providers should be listed in the second column",
|
|
316
316
|
optional: true),
|
|
317
317
|
FastlaneCore::ConfigItem.new(key: :provider_public_id,
|
|
318
318
|
env_name: "PILOT_PROVIDER_PUBLIC_ID",
|
|
@@ -13,7 +13,7 @@ module Pilot
|
|
|
13
13
|
UI.user_error!("You must provide 1 or more groups (with the `:groups` option)") unless groups_param
|
|
14
14
|
|
|
15
15
|
app.get_beta_groups.select do |group|
|
|
16
|
-
next unless
|
|
16
|
+
next unless group.matches_identifiers?(groups_param)
|
|
17
17
|
user = {
|
|
18
18
|
email: config[:email],
|
|
19
19
|
firstName: config[:first_name],
|
|
@@ -54,7 +54,7 @@ module Pilot
|
|
|
54
54
|
UI.success("Successfully removed tester #{tester.email} from app: #{app.name}")
|
|
55
55
|
else
|
|
56
56
|
groups = tester.beta_groups.select do |group|
|
|
57
|
-
config[:groups]
|
|
57
|
+
group.matches_identifiers?(config[:groups])
|
|
58
58
|
end
|
|
59
59
|
tester.delete_from_beta_groups(beta_groups: groups)
|
|
60
60
|
|
|
@@ -11,27 +11,33 @@ module Precheck
|
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
def self.friendly_name
|
|
14
|
-
"
|
|
14
|
+
"No incorrect or missing copyright date"
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
def self.description
|
|
18
|
-
"using a copyright
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def pass_if_empty?
|
|
22
|
-
return false
|
|
18
|
+
"using a copyright year in the future, or missing a copyright year"
|
|
23
19
|
end
|
|
24
20
|
|
|
25
21
|
def supported_fields_symbol_set
|
|
26
22
|
[:copyright].to_set
|
|
27
23
|
end
|
|
28
24
|
|
|
29
|
-
def
|
|
30
|
-
|
|
25
|
+
def rule_block
|
|
26
|
+
return lambda { |text|
|
|
27
|
+
year = copyright_year(text)
|
|
28
|
+
if year.nil?
|
|
29
|
+
RuleReturn.new(validation_state: VALIDATION_STATES[:failed], failure_data: "missing copyright year")
|
|
30
|
+
elsif year > DateTime.now.year
|
|
31
|
+
RuleReturn.new(validation_state: VALIDATION_STATES[:failed], failure_data: "copyright year is in the future: #{year}")
|
|
32
|
+
else
|
|
33
|
+
RuleReturn.new(validation_state: VALIDATION_STATES[:passed])
|
|
34
|
+
end
|
|
35
|
+
}
|
|
31
36
|
end
|
|
32
37
|
|
|
33
|
-
def
|
|
34
|
-
|
|
38
|
+
def copyright_year(text)
|
|
39
|
+
match = text.to_s.match(/\b(?:19|20)\d{2}\b/)
|
|
40
|
+
match && match[0].to_i
|
|
35
41
|
end
|
|
36
42
|
end
|
|
37
43
|
end
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
require 'addressable'
|
|
2
|
-
require '
|
|
2
|
+
require 'faraday/follow_redirects'
|
|
3
3
|
|
|
4
4
|
require_relative '../rule'
|
|
5
5
|
|
|
@@ -30,7 +30,7 @@ module Precheck
|
|
|
30
30
|
uri = Addressable::URI.parse(url)
|
|
31
31
|
uri.fragment = nil
|
|
32
32
|
request = Faraday.new(uri.normalize.to_s) do |connection|
|
|
33
|
-
connection.
|
|
33
|
+
connection.response(:follow_redirects)
|
|
34
34
|
connection.adapter(:net_http)
|
|
35
35
|
end
|
|
36
36
|
return RuleReturn.new(validation_state: Precheck::VALIDATION_STATES[:failed], failure_data: "HTTP #{request.head.status}: #{url}") unless request.head.status == 200
|
data/scan/lib/scan/options.rb
CHANGED
|
@@ -273,6 +273,11 @@ module Scan
|
|
|
273
273
|
description: "Remove retry attempts from test results table and the JUnit report (if not using xcpretty)",
|
|
274
274
|
type: Boolean,
|
|
275
275
|
default_value: false),
|
|
276
|
+
FastlaneCore::ConfigItem.new(key: :force_legacy_xcresulttool,
|
|
277
|
+
env_names: ["SCAN_FORCE_LEGACY_XCRESULTTOOL", "TRAINER_FORCE_LEGACY_XCRESULTTOOL"],
|
|
278
|
+
description: "Force the use of the '--legacy' flag for xcresulttool instead of using the new commands",
|
|
279
|
+
type: Boolean,
|
|
280
|
+
default_value: false),
|
|
276
281
|
|
|
277
282
|
# xcpretty
|
|
278
283
|
FastlaneCore::ConfigItem.new(key: :disable_xcpretty,
|
data/scan/lib/scan/runner.rb
CHANGED
|
@@ -216,6 +216,7 @@ module Scan
|
|
|
216
216
|
params = {
|
|
217
217
|
path: result_bundle_path,
|
|
218
218
|
output_remove_retry_attempts: Scan.config[:output_remove_retry_attempts],
|
|
219
|
+
force_legacy_xcresulttool: Scan.config[:force_legacy_xcresulttool],
|
|
219
220
|
silent: !FastlaneCore::Globals.verbose?
|
|
220
221
|
}
|
|
221
222
|
|