fastlane 2.232.2 → 2.233.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 +98 -98
- data/credentials_manager/lib/credentials_manager/appfile_config.rb +4 -0
- data/deliver/lib/deliver/detect_values.rb +2 -0
- data/deliver/lib/deliver/options.rb +23 -0
- data/deliver/lib/deliver/runner.rb +17 -12
- data/deliver/lib/deliver/sync_app_previews.rb +204 -0
- data/fastlane/lib/fastlane/actions/app_store_connect_api_key.rb +5 -1
- data/fastlane/lib/fastlane/actions/docs/upload_to_app_store.md.erb +20 -4
- data/fastlane/lib/fastlane/actions/docs/upload_to_testflight.md +3 -0
- data/fastlane/lib/fastlane/actions/resign.rb +13 -2
- data/fastlane/lib/fastlane/actions/swiftlint.rb +8 -1
- data/fastlane/lib/fastlane/actions/upload_to_app_store.rb +1 -1
- data/fastlane/lib/fastlane/helper/s3_client_helper.rb +5 -2
- data/fastlane/lib/fastlane/version.rb +1 -1
- data/fastlane/swift/Deliverfile.swift +1 -1
- data/fastlane/swift/DeliverfileProtocol.swift +29 -1
- data/fastlane/swift/Fastlane.swift +105 -9
- data/fastlane/swift/Gymfile.swift +1 -1
- data/fastlane/swift/GymfileProtocol.swift +8 -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 +1 -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/itunes_transporter.rb +48 -17
- data/fastlane_core/lib/fastlane_core/video_utils.rb +202 -0
- data/frameit/lib/frameit/device_types.rb +2 -2
- data/gym/lib/gym/generators/build_command_generator.rb +2 -1
- data/gym/lib/gym/options.rb +5 -0
- data/match/lib/match/generator.rb +3 -1
- data/match/lib/match/options.rb +5 -0
- data/match/lib/match/runner.rb +12 -7
- data/match/lib/match/storage/s3_storage.rb +4 -1
- data/match/lib/match/storage.rb +1 -0
- data/pilot/lib/pilot/build_manager.rb +4 -12
- data/pilot/lib/pilot/options.rb +4 -0
- data/precheck/lib/precheck/rules/rules_data/curse_word_hashes/README.md +54 -0
- data/precheck/lib/precheck/rules/rules_data/curse_word_hashes/en_us.txt +2 -1
- data/scan/lib/scan/detect_values.rb +11 -3
- data/sigh/lib/assets/resign.sh +17 -5
- data/sigh/lib/sigh/commands_generator.rb +1 -0
- data/sigh/lib/sigh/manager.rb +6 -6
- data/sigh/lib/sigh/resign.rb +9 -6
- data/spaceship/lib/spaceship/connect_api/models/app_preview_set.rb +54 -17
- data/spaceship/lib/spaceship/connect_api/models/app_store_version_localization.rb +1 -2
- data/spaceship/lib/spaceship/connect_api/tunes/tunes.rb +2 -2
- data/trainer/lib/trainer/legacy_xcresult.rb +27 -20
- data/trainer/lib/trainer/xcresult/test_suite.rb +4 -1
- metadata +25 -22
|
@@ -20,6 +20,9 @@ public protocol GymfileProtocol: AnyObject {
|
|
|
20
20
|
/// The name of the resulting ipa file
|
|
21
21
|
var outputName: String? { get }
|
|
22
22
|
|
|
23
|
+
/// App name to use in logfile name
|
|
24
|
+
var appName: String? { get }
|
|
25
|
+
|
|
23
26
|
/// The configuration to use when building the app. Defaults to 'Release'
|
|
24
27
|
var configuration: String? { get }
|
|
25
28
|
|
|
@@ -190,6 +193,10 @@ public extension GymfileProtocol {
|
|
|
190
193
|
return nil
|
|
191
194
|
}
|
|
192
195
|
|
|
196
|
+
var appName: String? {
|
|
197
|
+
return nil
|
|
198
|
+
}
|
|
199
|
+
|
|
193
200
|
var configuration: String? {
|
|
194
201
|
return nil
|
|
195
202
|
}
|
|
@@ -385,4 +392,4 @@ public extension GymfileProtocol {
|
|
|
385
392
|
|
|
386
393
|
// Please don't remove the lines below
|
|
387
394
|
// They are used to detect outdated files
|
|
388
|
-
// FastlaneRunnerAPIVersion [0.9.
|
|
395
|
+
// FastlaneRunnerAPIVersion [0.9.148]
|
|
@@ -86,6 +86,9 @@ public protocol MatchfileProtocol: AnyObject {
|
|
|
86
86
|
/// S3 secret access key
|
|
87
87
|
var s3SecretAccessKey: String? { get }
|
|
88
88
|
|
|
89
|
+
/// S3 session token
|
|
90
|
+
var s3SessionToken: String? { get }
|
|
91
|
+
|
|
89
92
|
/// Name of the S3 bucket
|
|
90
93
|
var s3Bucket: String? { get }
|
|
91
94
|
|
|
@@ -284,6 +287,10 @@ public extension MatchfileProtocol {
|
|
|
284
287
|
return nil
|
|
285
288
|
}
|
|
286
289
|
|
|
290
|
+
var s3SessionToken: String? {
|
|
291
|
+
return nil
|
|
292
|
+
}
|
|
293
|
+
|
|
287
294
|
var s3Bucket: String? {
|
|
288
295
|
return nil
|
|
289
296
|
}
|
|
@@ -399,4 +406,4 @@ public extension MatchfileProtocol {
|
|
|
399
406
|
|
|
400
407
|
// Please don't remove the lines below
|
|
401
408
|
// They are used to detect outdated files
|
|
402
|
-
// FastlaneRunnerAPIVersion [0.9.
|
|
409
|
+
// FastlaneRunnerAPIVersion [0.9.142]
|
|
@@ -43,7 +43,7 @@ module FastlaneCore
|
|
|
43
43
|
not_implemented(__method__)
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
-
def build_upload_command(username, password, source = "/tmp",
|
|
46
|
+
def build_upload_command(username, password, source = "/tmp", options = {})
|
|
47
47
|
not_implemented(__method__)
|
|
48
48
|
end
|
|
49
49
|
|
|
@@ -308,11 +308,23 @@ module FastlaneCore
|
|
|
308
308
|
if !username.nil? && !password.nil? && api_key.nil?
|
|
309
309
|
"-u #{username.shellescape} -p #{password.shellescape}"
|
|
310
310
|
elsif !api_key.nil?
|
|
311
|
-
|
|
311
|
+
# Individual API keys have no issuer_id; altool requires --apiIssuer
|
|
312
|
+
# to be present, so we pass the key_id as a placeholder and add
|
|
313
|
+
# --api-key-subject user to signal individual key JWT generation.
|
|
314
|
+
if api_key[:issuer_id]
|
|
315
|
+
"--apiKey #{api_key[:key_id]} --apiIssuer #{api_key[:issuer_id]}"
|
|
316
|
+
else
|
|
317
|
+
"--apiKey #{api_key[:key_id]} --apiIssuer #{api_key[:key_id]} --api-key-subject user"
|
|
318
|
+
end
|
|
312
319
|
end
|
|
313
320
|
end
|
|
314
321
|
|
|
315
|
-
def build_upload_command(username, password, source = "/tmp",
|
|
322
|
+
def build_upload_command(username, password, source = "/tmp", options = {})
|
|
323
|
+
provider_short_name = options.fetch(:provider_short_name, "")
|
|
324
|
+
provider_public_id = options.fetch(:provider_public_id, "")
|
|
325
|
+
jwt = options[:jwt]
|
|
326
|
+
platform = options[:platform]
|
|
327
|
+
api_key = options[:api_key]
|
|
316
328
|
use_api_key = !api_key.nil?
|
|
317
329
|
[
|
|
318
330
|
("API_PRIVATE_KEYS_DIR=#{api_key[:key_dir]}" if use_api_key),
|
|
@@ -320,6 +332,7 @@ module FastlaneCore
|
|
|
320
332
|
"--upload-app",
|
|
321
333
|
build_credential_params(username, password, jwt, api_key),
|
|
322
334
|
("--asc-provider #{provider_short_name}" unless use_api_key || provider_short_name.to_s.empty?),
|
|
335
|
+
("--provider-public-id #{provider_public_id}" unless use_api_key || provider_public_id.to_s.empty?),
|
|
323
336
|
platform_option(platform),
|
|
324
337
|
file_upload_option(source),
|
|
325
338
|
additional_upload_parameters,
|
|
@@ -342,9 +355,11 @@ module FastlaneCore
|
|
|
342
355
|
raise "This feature has not been implemented yet with altool for Xcode 14"
|
|
343
356
|
end
|
|
344
357
|
|
|
345
|
-
def build_verify_command(username, password, source = "/tmp",
|
|
346
|
-
|
|
347
|
-
|
|
358
|
+
def build_verify_command(username, password, source = "/tmp", options = {})
|
|
359
|
+
provider_short_name = options.fetch(:provider_short_name, "")
|
|
360
|
+
provider_public_id = options.fetch(:provider_public_id, "")
|
|
361
|
+
api_key = options[:api_key]
|
|
362
|
+
platform = options[:platform]
|
|
348
363
|
use_api_key = !api_key.nil?
|
|
349
364
|
[
|
|
350
365
|
("API_PRIVATE_KEYS_DIR=#{api_key[:key_dir]}" if use_api_key),
|
|
@@ -352,6 +367,7 @@ module FastlaneCore
|
|
|
352
367
|
"--validate-app",
|
|
353
368
|
build_credential_params(username, password, nil, api_key),
|
|
354
369
|
("--asc-provider #{provider_short_name}" unless use_api_key || provider_short_name.to_s.empty?),
|
|
370
|
+
("--provider-public-id #{provider_public_id}" unless use_api_key || provider_public_id.to_s.empty?),
|
|
355
371
|
platform_option(platform),
|
|
356
372
|
file_upload_option(source)
|
|
357
373
|
].compact.join(' ')
|
|
@@ -424,7 +440,10 @@ module FastlaneCore
|
|
|
424
440
|
end
|
|
425
441
|
end
|
|
426
442
|
|
|
427
|
-
def build_upload_command(username, password, source = "/tmp",
|
|
443
|
+
def build_upload_command(username, password, source = "/tmp", options = {})
|
|
444
|
+
provider_short_name = options.fetch(:provider_short_name, "")
|
|
445
|
+
jwt = options[:jwt]
|
|
446
|
+
api_key = options[:api_key]
|
|
428
447
|
[
|
|
429
448
|
'"' + Helper.transporter_path + '"',
|
|
430
449
|
"-m upload",
|
|
@@ -456,8 +475,9 @@ module FastlaneCore
|
|
|
456
475
|
].compact.join(' ')
|
|
457
476
|
end
|
|
458
477
|
|
|
459
|
-
def build_verify_command(username, password, source = "/tmp",
|
|
460
|
-
|
|
478
|
+
def build_verify_command(username, password, source = "/tmp", options = {})
|
|
479
|
+
provider_short_name = options.fetch(:provider_short_name, "")
|
|
480
|
+
jwt = options[:jwt]
|
|
461
481
|
[
|
|
462
482
|
'"' + Helper.transporter_path + '"',
|
|
463
483
|
'-m verify',
|
|
@@ -546,7 +566,10 @@ module FastlaneCore
|
|
|
546
566
|
end
|
|
547
567
|
end
|
|
548
568
|
|
|
549
|
-
def build_upload_command(username, password, source = "/tmp",
|
|
569
|
+
def build_upload_command(username, password, source = "/tmp", options = {})
|
|
570
|
+
provider_short_name = options.fetch(:provider_short_name, "")
|
|
571
|
+
jwt = options[:jwt]
|
|
572
|
+
api_key = options[:api_key]
|
|
550
573
|
credential_params = build_credential_params(username, password, jwt, api_key, is_default_itms_on_xcode_11?)
|
|
551
574
|
if is_default_itms_on_xcode_11?
|
|
552
575
|
[
|
|
@@ -582,8 +605,9 @@ module FastlaneCore
|
|
|
582
605
|
end
|
|
583
606
|
end
|
|
584
607
|
|
|
585
|
-
def build_verify_command(username, password, source = "/tmp",
|
|
586
|
-
|
|
608
|
+
def build_verify_command(username, password, source = "/tmp", options = {})
|
|
609
|
+
provider_short_name = options.fetch(:provider_short_name, "")
|
|
610
|
+
jwt = options[:jwt]
|
|
587
611
|
credential_params = build_credential_params(username, password, jwt, nil, is_default_itms_on_xcode_11?)
|
|
588
612
|
if is_default_itms_on_xcode_11?
|
|
589
613
|
[
|
|
@@ -731,7 +755,8 @@ module FastlaneCore
|
|
|
731
755
|
# see: https://github.com/fastlane/fastlane/issues/1524#issuecomment-196370628
|
|
732
756
|
# for more information about how to use the iTMSTransporter to list your provider
|
|
733
757
|
# short names
|
|
734
|
-
|
|
758
|
+
# @param provider_public_id The provider public ID to be given to altool via --provider-public-id.
|
|
759
|
+
def initialize(user = nil, password = nil, use_shell_script = false, provider_short_name = nil, jwt = nil, altool_compatible_command: false, api_key: nil, provider_public_id: nil)
|
|
735
760
|
# Xcode 6.x doesn't have the same iTMSTransporter Java setup as later Xcode versions, so
|
|
736
761
|
# we can't default to using the newer direct Java invocation strategy for those versions.
|
|
737
762
|
use_shell_script ||= Helper.is_mac? && Helper.xcode_version.start_with?('6.')
|
|
@@ -749,9 +774,11 @@ module FastlaneCore
|
|
|
749
774
|
if should_use_altool?(altool_compatible_command, use_shell_script)
|
|
750
775
|
UI.verbose("Using altool as transporter.")
|
|
751
776
|
@transporter_executor = AltoolTransporterExecutor.new
|
|
777
|
+
@provider_public_id = provider_public_id
|
|
752
778
|
else
|
|
753
779
|
UI.verbose("Using iTMSTransporter as transporter.")
|
|
754
780
|
@transporter_executor = use_shell_script ? ShellScriptTransporterExecutor.new : JavaTransporterExecutor.new
|
|
781
|
+
@provider_public_id = nil
|
|
755
782
|
end
|
|
756
783
|
|
|
757
784
|
@provider_short_name = provider_short_name
|
|
@@ -837,8 +864,10 @@ module FastlaneCore
|
|
|
837
864
|
api_key_placeholder = use_api_key ? { key_id: "YourKeyID", issuer_id: "YourIssuerID", key_dir: "YourTmpP8KeyDir" } : nil
|
|
838
865
|
api_key = @transporter_executor.prepare(original_api_key: @api_key)
|
|
839
866
|
|
|
840
|
-
|
|
841
|
-
|
|
867
|
+
upload_options = { provider_short_name: @provider_short_name, provider_public_id: @provider_public_id, jwt: @jwt, platform: platform, api_key: api_key }
|
|
868
|
+
upload_options_placeholder = { provider_short_name: @provider_short_name, provider_public_id: @provider_public_id, jwt: jwt_placeholder, platform: platform, api_key: api_key_placeholder }
|
|
869
|
+
command = @transporter_executor.build_upload_command(@user, @password, actual_dir, upload_options)
|
|
870
|
+
UI.verbose(@transporter_executor.build_upload_command(@user, password_placeholder, actual_dir, upload_options_placeholder))
|
|
842
871
|
|
|
843
872
|
begin
|
|
844
873
|
result = @transporter_executor.execute(command, ItunesTransporter.hide_transporter_output?)
|
|
@@ -898,8 +927,10 @@ module FastlaneCore
|
|
|
898
927
|
api_key_placeholder = use_api_key ? { key_id: "YourKeyID", issuer_id: "YourIssuerID", key_dir: "YourTmpP8KeyDir" } : nil
|
|
899
928
|
api_key = @transporter_executor.prepare(original_api_key: @api_key)
|
|
900
929
|
|
|
901
|
-
|
|
902
|
-
|
|
930
|
+
verify_options = { provider_short_name: @provider_short_name, provider_public_id: @provider_public_id, jwt: @jwt, platform: platform, api_key: api_key }
|
|
931
|
+
verify_options_placeholder = { provider_short_name: @provider_short_name, provider_public_id: @provider_public_id, jwt: jwt_placeholder, platform: platform, api_key: api_key_placeholder }
|
|
932
|
+
command = @transporter_executor.build_verify_command(@user, @password, actual_dir, verify_options)
|
|
933
|
+
UI.verbose(@transporter_executor.build_verify_command(@user, password_placeholder, actual_dir, verify_options_placeholder))
|
|
903
934
|
|
|
904
935
|
begin
|
|
905
936
|
result = @transporter_executor.execute(command, ItunesTransporter.hide_transporter_output?)
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
require "fastlane_core/ui/ui"
|
|
2
|
+
|
|
3
|
+
# Reference for the MP4/QuickTime container (video atoms/boxes structure): ISO/IEC 14496-12:2022 (base media file format) - defines atoms like moov/trak/tkhd
|
|
4
|
+
# - https://observablehq.com/@benjamintoofer/iso-base-media-file-format
|
|
5
|
+
# - https://developer.apple.com/documentation/quicktime-file-format
|
|
6
|
+
module FastlaneCore
|
|
7
|
+
module VideoUtils
|
|
8
|
+
# Pure Ruby MP4/QuickTime MOV container header parser, reading tkhd to get width/height.
|
|
9
|
+
# Note: This reads container atoms/boxes only; it does NOT parse the video codec bitstream.
|
|
10
|
+
# Works regardless of codec as long as standard container headers exist.
|
|
11
|
+
# Input: path (String) to a local MP4/MOV file.
|
|
12
|
+
# Output: [width, height] (Array<Integer>) normalized to portrait (w <= h), or nil on failure.
|
|
13
|
+
def self.read_video_resolution(path)
|
|
14
|
+
File.open(path, "rb") do |f|
|
|
15
|
+
file_size = f.size
|
|
16
|
+
while f.pos < file_size
|
|
17
|
+
size, type = read_atom_header(f)
|
|
18
|
+
break unless size && type
|
|
19
|
+
return nil if size < 8
|
|
20
|
+
content = size - 8
|
|
21
|
+
if type == "moov"
|
|
22
|
+
moov_end = f.pos + content
|
|
23
|
+
resolution = extract_resolution_from_moov(f, moov_end)
|
|
24
|
+
return resolution if resolution
|
|
25
|
+
else
|
|
26
|
+
skip(f, content)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
nil
|
|
31
|
+
rescue => e
|
|
32
|
+
FastlaneCore::UI.verbose("Failed to parse resolution for '#{path}': #{e.class} - #{e.message}")
|
|
33
|
+
nil
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Pure Ruby MP4/QuickTime MOV container header parser reading mvhd to get duration in seconds.
|
|
37
|
+
# Note: Uses movie header (mvhd) time scale/duration, not per-track edit lists; adequate for gating.
|
|
38
|
+
# Input: path (String) to a local MP4/MOV file.
|
|
39
|
+
# Output: duration in seconds (Float), or nil on failure.
|
|
40
|
+
def self.read_video_duration_seconds(path)
|
|
41
|
+
File.open(path, "rb") do |f|
|
|
42
|
+
file_size = f.size
|
|
43
|
+
while f.pos < file_size
|
|
44
|
+
size, type = read_atom_header(f)
|
|
45
|
+
break unless size && type
|
|
46
|
+
return nil if size < 8
|
|
47
|
+
content = size - 8
|
|
48
|
+
if type == "moov"
|
|
49
|
+
moov_end = f.pos + content
|
|
50
|
+
duration = extract_duration_from_moov(f, moov_end)
|
|
51
|
+
return duration if duration
|
|
52
|
+
else
|
|
53
|
+
skip(f, content)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
nil
|
|
58
|
+
rescue => e
|
|
59
|
+
FastlaneCore::UI.verbose("Failed to parse duration for '#{path}': #{e.class} - #{e.message}")
|
|
60
|
+
nil
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Reads the moov box and returns [w, h] if found in any trak/tkhd, else nil.
|
|
64
|
+
# Input: io (IO) positioned at moov contents; moov_end (Integer) absolute end offset.
|
|
65
|
+
# Output: [width, height] or nil.
|
|
66
|
+
def self.extract_resolution_from_moov(io, moov_end)
|
|
67
|
+
while io.pos < moov_end
|
|
68
|
+
atom_size, atom_type = read_atom_header(io)
|
|
69
|
+
break unless atom_size && atom_type
|
|
70
|
+
return nil if atom_size < 8
|
|
71
|
+
if atom_type == "trak"
|
|
72
|
+
trak_end = io.pos + (atom_size - 8)
|
|
73
|
+
resolution = extract_resolution_from_trak(io, trak_end)
|
|
74
|
+
return resolution if resolution
|
|
75
|
+
io.seek(trak_end, IO::SEEK_SET)
|
|
76
|
+
else
|
|
77
|
+
skip(io, atom_size - 8)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
nil
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Reads the moov box and returns duration in seconds if mvhd is found, else nil.
|
|
84
|
+
# Input: io (IO) positioned at moov contents; moov_end (Integer) absolute end offset.
|
|
85
|
+
# Output: Float seconds or nil.
|
|
86
|
+
def self.extract_duration_from_moov(io, moov_end)
|
|
87
|
+
while io.pos < moov_end
|
|
88
|
+
atom_size, atom_type = read_atom_header(io)
|
|
89
|
+
break unless atom_size && atom_type
|
|
90
|
+
return nil if atom_size < 8
|
|
91
|
+
if atom_type == "mvhd"
|
|
92
|
+
return parse_mvhd(io)
|
|
93
|
+
else
|
|
94
|
+
skip(io, atom_size - 8)
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
nil
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Reads a single trak box to locate tkhd and extract resolution.
|
|
101
|
+
# Input: io (IO) positioned at trak contents; trak_end (Integer) absolute end offset.
|
|
102
|
+
# Output: [width, height] or nil.
|
|
103
|
+
def self.extract_resolution_from_trak(io, trak_end)
|
|
104
|
+
while io.pos < trak_end
|
|
105
|
+
trak_size, trak_type = read_atom_header(io)
|
|
106
|
+
break unless trak_size && trak_type
|
|
107
|
+
return nil if trak_size < 8
|
|
108
|
+
if trak_type == "tkhd"
|
|
109
|
+
return parse_tkhd(io)
|
|
110
|
+
else
|
|
111
|
+
skip(io, trak_size - 8)
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
nil
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Parses mvhd box fields to read duration.
|
|
118
|
+
# Input: io (IO) positioned at the start of mvhd payload after its header.
|
|
119
|
+
# Output: Float seconds or nil.
|
|
120
|
+
def self.parse_mvhd(io)
|
|
121
|
+
version_flags = io.read(4)
|
|
122
|
+
return nil unless version_flags && version_flags.length == 4
|
|
123
|
+
version = version_flags.getbyte(0)
|
|
124
|
+
if version == 1
|
|
125
|
+
skip(io, 8) # creation_time (u64)
|
|
126
|
+
skip(io, 8) # modification_time (u64)
|
|
127
|
+
timescale = read_u32(io)
|
|
128
|
+
duration = read_u64(io) # u64
|
|
129
|
+
else
|
|
130
|
+
skip(io, 4) # creation_time (u32)
|
|
131
|
+
skip(io, 4) # modification_time (u32)
|
|
132
|
+
timescale = read_u32(io)
|
|
133
|
+
duration = read_u32(io) # u32
|
|
134
|
+
end
|
|
135
|
+
return nil unless timescale && timescale > 0
|
|
136
|
+
return nil unless duration
|
|
137
|
+
duration.to_f / timescale
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Parses tkhd box fields to read fixed-point width and height (16.16).
|
|
141
|
+
# Input: io (IO) positioned at the start of tkhd payload after its header.
|
|
142
|
+
# Output: [width, height] or nil.
|
|
143
|
+
def self.parse_tkhd(io)
|
|
144
|
+
video_flags = io.read(4)
|
|
145
|
+
return nil unless video_flags && video_flags.length == 4
|
|
146
|
+
version = video_flags.getbyte(0)
|
|
147
|
+
if version == 1
|
|
148
|
+
skip(io, 8 * 2) # creation, modification (u64)
|
|
149
|
+
skip(io, 4) # track id (u32)
|
|
150
|
+
skip(io, 4) # reserved
|
|
151
|
+
skip(io, 8) # duration (u64)
|
|
152
|
+
else
|
|
153
|
+
skip(io, 4 * 3) # creation, modification, track id (u32)
|
|
154
|
+
skip(io, 4) # reserved
|
|
155
|
+
skip(io, 4) # duration (u32)
|
|
156
|
+
end
|
|
157
|
+
skip(io, 8) # reserved
|
|
158
|
+
skip(io, 2) # layer (u16)
|
|
159
|
+
skip(io, 2) # alt group (u16)
|
|
160
|
+
skip(io, 2) # volume (u16)
|
|
161
|
+
skip(io, 2) # reserved
|
|
162
|
+
skip(io, 36) # matrix
|
|
163
|
+
# width/height (fixed point 16.16)
|
|
164
|
+
width_fixed = read_u32(io)
|
|
165
|
+
height_fixed = read_u32(io)
|
|
166
|
+
return nil unless width_fixed && height_fixed
|
|
167
|
+
width = (width_fixed >> 16)
|
|
168
|
+
height = (height_fixed >> 16)
|
|
169
|
+
[width, height]
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# Read a big-endian 32-bit unsigned integer; returns Integer or nil if insufficient data.
|
|
173
|
+
def self.read_u32(io)
|
|
174
|
+
bytes = io.read(4)
|
|
175
|
+
return nil unless bytes && bytes.length == 4
|
|
176
|
+
bytes.unpack1("N") # 32-bit unsigned, network (big-endian) byte order
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# Read a big-endian 64-bit unsigned integer; returns Integer or nil if insufficient data.
|
|
180
|
+
def self.read_u64(io)
|
|
181
|
+
bytes = io.read(8)
|
|
182
|
+
return nil unless bytes && bytes.length == 8
|
|
183
|
+
bytes.unpack1("Q>") # 64-bit unsigned, big-endian
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# Read an atom/box header: returns [size(Integer), type(String)] or nil.
|
|
187
|
+
def self.read_atom_header(io)
|
|
188
|
+
size = read_u32(io)
|
|
189
|
+
return nil unless size
|
|
190
|
+
type = io.read(4)
|
|
191
|
+
return nil unless type && type.length == 4
|
|
192
|
+
[size, type]
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# Advance the IO cursor by n bytes.
|
|
196
|
+
def self.skip(io, n)
|
|
197
|
+
io.seek(n, IO::SEEK_CUR)
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
private_class_method :extract_resolution_from_moov, :extract_duration_from_moov, :extract_resolution_from_trak, :parse_tkhd, :parse_mvhd, :read_u32, :read_u64, :read_atom_header, :skip
|
|
201
|
+
end
|
|
202
|
+
end
|
|
@@ -173,8 +173,8 @@ module Frameit
|
|
|
173
173
|
IPHONE_13_MINI ||= Device.new("iphone-13-mini", "Apple iPhone 13 Mini", 11, [[1080, 2340], [2340, 1080]], 476, Color::MIDNIGHT, Platform::IOS)
|
|
174
174
|
IPHONE_14 ||= Device.new("iphone-14", "Apple iPhone 14", 12, [[1170, 2532], [2532, 1170]], 460, Color::MIDNIGHT, Platform::IOS)
|
|
175
175
|
IPHONE_14_PLUS ||= Device.new("iphone-14-plus", "Apple iPhone 14 Plus", 12, [[1284, 2778], [2778, 1284]], 458, Color::MIDNIGHT, Platform::IOS)
|
|
176
|
-
IPHONE_14_PRO ||= Device.new("iphone-14-pro", "Apple iPhone 14 Pro", 12, [[1179, 2556], [2556, 1179]], 460, Color::
|
|
177
|
-
IPHONE_14_PRO_MAX ||= Device.new("iphone14-pro-max", "Apple iPhone 14 Pro Max", 12, [[1290, 2796], [2796, 1290]], 458, Color::
|
|
176
|
+
IPHONE_14_PRO ||= Device.new("iphone-14-pro", "Apple iPhone 14 Pro", 12, [[1179, 2556], [2556, 1179]], 460, Color::BLACK, Platform::IOS)
|
|
177
|
+
IPHONE_14_PRO_MAX ||= Device.new("iphone14-pro-max", "Apple iPhone 14 Pro Max", 12, [[1290, 2796], [2796, 1290]], 458, Color::BLACK, Platform::IOS)
|
|
178
178
|
IPAD_10_2 ||= Device.new("ipad-10-2", "Apple iPad 10.2", 1, [[1620, 2160], [2160, 1620]], 264, Color::SPACE_GRAY, Platform::IOS)
|
|
179
179
|
IPAD_AIR_2 ||= Device.new("ipad-air-2", "Apple iPad Air 2", 1, [[1536, 2048], [2048, 1536]], 264, Color::SPACE_GRAY, Platform::IOS, DEVICE_SCREEN_IDS[DisplayType::APP_IPAD_97])
|
|
180
180
|
IPAD_AIR_2019 ||= Device.new("ipad-air-2019", "Apple iPad Air (2019)", 2, [[1668, 2224], [2224, 1668]], 265, Color::SPACE_GRAY, Platform::IOS)
|
|
@@ -152,7 +152,8 @@ module Gym
|
|
|
152
152
|
end
|
|
153
153
|
|
|
154
154
|
def xcodebuild_log_path
|
|
155
|
-
|
|
155
|
+
app_name = Gym.config[:app_name] || Gym.project.app_name
|
|
156
|
+
file_name = "#{app_name}-#{Gym.config[:scheme]}.log"
|
|
156
157
|
containing = File.expand_path(Gym.config[:buildlog_path])
|
|
157
158
|
FileUtils.mkdir_p(containing)
|
|
158
159
|
|
data/gym/lib/gym/options.rb
CHANGED
|
@@ -65,6 +65,11 @@ module Gym
|
|
|
65
65
|
env_name: "GYM_OUTPUT_NAME",
|
|
66
66
|
description: "The name of the resulting ipa file",
|
|
67
67
|
optional: true),
|
|
68
|
+
FastlaneCore::ConfigItem.new(key: :app_name,
|
|
69
|
+
env_name: "GYM_APP_NAME",
|
|
70
|
+
optional: true,
|
|
71
|
+
description: "App name to use in logfile name",
|
|
72
|
+
type: String),
|
|
68
73
|
FastlaneCore::ConfigItem.new(key: :configuration,
|
|
69
74
|
short_option: "-q",
|
|
70
75
|
env_name: "GYM_CONFIGURATION",
|
|
@@ -115,7 +115,9 @@ module Match
|
|
|
115
115
|
arguments = FastlaneCore::Configuration.create(Sigh::Options.available_options, values)
|
|
116
116
|
|
|
117
117
|
Sigh.config = arguments
|
|
118
|
-
|
|
118
|
+
|
|
119
|
+
keychain_path = FastlaneCore::Helper.keychain_path(params[:keychain_name]) if Helper.mac? && params[:keychain_name]
|
|
120
|
+
path = Sigh::Manager.start(keychain_path: keychain_path)
|
|
119
121
|
return path
|
|
120
122
|
end
|
|
121
123
|
|
data/match/lib/match/options.rb
CHANGED
|
@@ -215,6 +215,11 @@ module Match
|
|
|
215
215
|
description: "S3 secret access key",
|
|
216
216
|
sensitive: true,
|
|
217
217
|
optional: true),
|
|
218
|
+
FastlaneCore::ConfigItem.new(key: :s3_session_token,
|
|
219
|
+
env_name: "MATCH_S3_SESSION_TOKEN",
|
|
220
|
+
description: "S3 session token",
|
|
221
|
+
sensitive: true,
|
|
222
|
+
optional: true),
|
|
218
223
|
FastlaneCore::ConfigItem.new(key: :s3_bucket,
|
|
219
224
|
env_name: "MATCH_S3_BUCKET",
|
|
220
225
|
description: "Name of the S3 bucket",
|
data/match/lib/match/runner.rb
CHANGED
|
@@ -160,6 +160,7 @@ module Match
|
|
|
160
160
|
|
|
161
161
|
def fetch_certificate(params: nil, renew_expired_certs: false, specific_cert_type: nil)
|
|
162
162
|
cert_type = Match.cert_type_sym(specific_cert_type || params[:type])
|
|
163
|
+
certificate_id = specific_cert_type.nil? ? params[:certificate_id] : nil
|
|
163
164
|
|
|
164
165
|
certs = Dir[File.join(prefixed_working_directory, "certs", cert_type.to_s, "*.cer")]
|
|
165
166
|
keys = Dir[File.join(prefixed_working_directory, "certs", cert_type.to_s, "*.p12")]
|
|
@@ -174,7 +175,7 @@ module Match
|
|
|
174
175
|
|
|
175
176
|
# Validate existing certificate first.
|
|
176
177
|
if renew_expired_certs && is_cert_renewable && storage_has_certs && !params[:readonly]
|
|
177
|
-
cert_path = select_cert_or_key(paths: certs)
|
|
178
|
+
cert_path = select_cert_or_key(paths: certs, certificate_id: certificate_id)
|
|
178
179
|
|
|
179
180
|
unless Utils.is_cert_valid?(cert_path)
|
|
180
181
|
UI.important("Removing invalid certificate '#{File.basename(cert_path)}'")
|
|
@@ -209,7 +210,7 @@ module Match
|
|
|
209
210
|
# Reset certificates cache since we have a new cert.
|
|
210
211
|
self.cache.reset_certificates
|
|
211
212
|
else
|
|
212
|
-
cert_path = select_cert_or_key(paths: certs)
|
|
213
|
+
cert_path = select_cert_or_key(paths: certs, certificate_id: certificate_id)
|
|
213
214
|
|
|
214
215
|
# Check validity of certificate
|
|
215
216
|
if Utils.is_cert_valid?(cert_path)
|
|
@@ -233,7 +234,7 @@ module Match
|
|
|
233
234
|
# Import the private key
|
|
234
235
|
# there seems to be no good way to check if it's already installed - so just install it
|
|
235
236
|
# Key will only be added to the partition list if it isn't already installed
|
|
236
|
-
Utils.import(select_cert_or_key(paths: keys), params[:keychain_name], password: params[:keychain_password])
|
|
237
|
+
Utils.import(select_cert_or_key(paths: keys, certificate_id: certificate_id), params[:keychain_name], password: params[:keychain_password])
|
|
237
238
|
end
|
|
238
239
|
else
|
|
239
240
|
UI.message("Skipping installation of certificate as it would not work on this operating system.")
|
|
@@ -241,7 +242,7 @@ module Match
|
|
|
241
242
|
|
|
242
243
|
if params[:output_path]
|
|
243
244
|
FileUtils.cp(cert_path, params[:output_path])
|
|
244
|
-
FileUtils.cp(select_cert_or_key(paths: keys), params[:output_path])
|
|
245
|
+
FileUtils.cp(select_cert_or_key(paths: keys, certificate_id: certificate_id), params[:output_path])
|
|
245
246
|
end
|
|
246
247
|
|
|
247
248
|
# Get and print info of certificate
|
|
@@ -253,9 +254,13 @@ module Match
|
|
|
253
254
|
end
|
|
254
255
|
|
|
255
256
|
# @return [String] Path to certificate or P12 key
|
|
256
|
-
def select_cert_or_key(paths:)
|
|
257
|
-
|
|
258
|
-
|
|
257
|
+
def select_cert_or_key(paths:, certificate_id: nil)
|
|
258
|
+
return paths.last if certificate_id.to_s.strip.empty?
|
|
259
|
+
|
|
260
|
+
cert_id_path = paths.find { |path| File.basename(path, File.extname(path)) == certificate_id }
|
|
261
|
+
return cert_id_path if cert_id_path
|
|
262
|
+
|
|
263
|
+
UI.user_error!("No certificate or key found for certificate_id '#{certificate_id}'")
|
|
259
264
|
end
|
|
260
265
|
|
|
261
266
|
# rubocop:disable Metrics/PerceivedComplexity
|
|
@@ -26,6 +26,7 @@ module Match
|
|
|
26
26
|
s3_region = params[:s3_region]
|
|
27
27
|
s3_access_key = params[:s3_access_key]
|
|
28
28
|
s3_secret_access_key = params[:s3_secret_access_key]
|
|
29
|
+
s3_session_token = params[:s3_session_token]
|
|
29
30
|
s3_bucket = params[:s3_bucket]
|
|
30
31
|
s3_object_prefix = params[:s3_object_prefix]
|
|
31
32
|
|
|
@@ -40,6 +41,7 @@ module Match
|
|
|
40
41
|
s3_region: s3_region,
|
|
41
42
|
s3_access_key: s3_access_key,
|
|
42
43
|
s3_secret_access_key: s3_secret_access_key,
|
|
44
|
+
s3_session_token: s3_session_token,
|
|
43
45
|
s3_bucket: s3_bucket,
|
|
44
46
|
s3_object_prefix: s3_object_prefix,
|
|
45
47
|
readonly: params[:readonly],
|
|
@@ -54,6 +56,7 @@ module Match
|
|
|
54
56
|
def initialize(s3_region: nil,
|
|
55
57
|
s3_access_key: nil,
|
|
56
58
|
s3_secret_access_key: nil,
|
|
59
|
+
s3_session_token: nil,
|
|
57
60
|
s3_bucket: nil,
|
|
58
61
|
s3_object_prefix: nil,
|
|
59
62
|
readonly: nil,
|
|
@@ -64,7 +67,7 @@ module Match
|
|
|
64
67
|
api_key: nil)
|
|
65
68
|
@s3_bucket = s3_bucket
|
|
66
69
|
@s3_region = s3_region
|
|
67
|
-
@s3_client = Fastlane::Helper::S3ClientHelper.new(access_key: s3_access_key, secret_access_key: s3_secret_access_key, region: s3_region)
|
|
70
|
+
@s3_client = Fastlane::Helper::S3ClientHelper.new(access_key: s3_access_key, secret_access_key: s3_secret_access_key, session_token: s3_session_token, region: s3_region)
|
|
68
71
|
@s3_object_prefix = s3_object_prefix.to_s
|
|
69
72
|
@readonly = readonly
|
|
70
73
|
@username = username
|
data/match/lib/match/storage.rb
CHANGED
|
@@ -46,6 +46,7 @@ module Match
|
|
|
46
46
|
s3_region: params[:s3_region],
|
|
47
47
|
s3_access_key: params[:s3_access_key],
|
|
48
48
|
s3_secret_access_key: params[:s3_secret_access_key],
|
|
49
|
+
s3_session_token: params[:s3_session_token],
|
|
49
50
|
s3_bucket: params[:s3_bucket],
|
|
50
51
|
s3_object_prefix: params[:s3_object_prefix],
|
|
51
52
|
readonly: params[:readonly],
|