fastlane 2.232.2 → 2.233.1

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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +102 -102
  3. data/credentials_manager/lib/credentials_manager/appfile_config.rb +4 -0
  4. data/deliver/lib/deliver/options.rb +23 -0
  5. data/deliver/lib/deliver/runner.rb +17 -12
  6. data/deliver/lib/deliver/sync_app_previews.rb +204 -0
  7. data/fastlane/lib/fastlane/actions/app_store_connect_api_key.rb +5 -1
  8. data/fastlane/lib/fastlane/actions/docs/upload_to_app_store.md.erb +20 -4
  9. data/fastlane/lib/fastlane/actions/docs/upload_to_testflight.md +3 -0
  10. data/fastlane/lib/fastlane/actions/resign.rb +13 -2
  11. data/fastlane/lib/fastlane/actions/swiftlint.rb +8 -1
  12. data/fastlane/lib/fastlane/actions/upload_to_app_store.rb +1 -1
  13. data/fastlane/lib/fastlane/helper/s3_client_helper.rb +5 -2
  14. data/fastlane/lib/fastlane/version.rb +1 -1
  15. data/fastlane/swift/Deliverfile.swift +1 -1
  16. data/fastlane/swift/DeliverfileProtocol.swift +29 -1
  17. data/fastlane/swift/Fastlane.swift +105 -9
  18. data/fastlane/swift/Gymfile.swift +1 -1
  19. data/fastlane/swift/GymfileProtocol.swift +8 -1
  20. data/fastlane/swift/Matchfile.swift +1 -1
  21. data/fastlane/swift/MatchfileProtocol.swift +8 -1
  22. data/fastlane/swift/Precheckfile.swift +1 -1
  23. data/fastlane/swift/PrecheckfileProtocol.swift +1 -1
  24. data/fastlane/swift/Scanfile.swift +1 -1
  25. data/fastlane/swift/ScanfileProtocol.swift +1 -1
  26. data/fastlane/swift/Screengrabfile.swift +1 -1
  27. data/fastlane/swift/ScreengrabfileProtocol.swift +1 -1
  28. data/fastlane/swift/Snapshotfile.swift +1 -1
  29. data/fastlane/swift/SnapshotfileProtocol.swift +1 -1
  30. data/fastlane_core/lib/fastlane_core/itunes_transporter.rb +48 -17
  31. data/fastlane_core/lib/fastlane_core/video_utils.rb +202 -0
  32. data/frameit/lib/frameit/device_types.rb +2 -2
  33. data/gym/lib/gym/generators/build_command_generator.rb +2 -1
  34. data/gym/lib/gym/options.rb +5 -0
  35. data/match/lib/match/generator.rb +3 -1
  36. data/match/lib/match/options.rb +5 -0
  37. data/match/lib/match/runner.rb +12 -7
  38. data/match/lib/match/storage/s3_storage.rb +4 -1
  39. data/match/lib/match/storage.rb +1 -0
  40. data/pilot/lib/pilot/build_manager.rb +4 -12
  41. data/pilot/lib/pilot/options.rb +4 -0
  42. data/precheck/lib/precheck/rules/rules_data/curse_word_hashes/README.md +54 -0
  43. data/precheck/lib/precheck/rules/rules_data/curse_word_hashes/en_us.txt +2 -1
  44. data/scan/lib/scan/detect_values.rb +11 -3
  45. data/sigh/lib/assets/resign.sh +17 -5
  46. data/sigh/lib/sigh/commands_generator.rb +1 -0
  47. data/sigh/lib/sigh/manager.rb +6 -6
  48. data/sigh/lib/sigh/resign.rb +9 -6
  49. data/spaceship/lib/spaceship/connect_api/models/app_preview_set.rb +54 -17
  50. data/spaceship/lib/spaceship/connect_api/models/app_store_version_localization.rb +1 -2
  51. data/spaceship/lib/spaceship/connect_api/tunes/tunes.rb +2 -2
  52. data/trainer/lib/trainer/legacy_xcresult.rb +27 -20
  53. data/trainer/lib/trainer/xcresult/test_suite.rb +4 -1
  54. metadata +24 -21
@@ -398,7 +398,7 @@ module Pilot
398
398
 
399
399
  # If App Store Connect API token, use token.
400
400
  # If api_key is specified and it is an Individual API Key, don't use token but use username.
401
- # If itc_provider was explicitly specified, use it.
401
+ # If itc_provider or provider_public_id was explicitly specified, use it.
402
402
  # If there are multiple teams, infer the provider from the selected team name.
403
403
  # If there are fewer than two teams, don't infer the provider.
404
404
  def transporter_for_selected_team(options)
@@ -414,24 +414,16 @@ module Pilot
414
414
  api_key
415
415
  end
416
416
 
417
- # Currently no kind of transporters accept an Individual API Key. Use username and app-specific password instead.
418
- # See https://github.com/fastlane/fastlane/issues/22115
419
- is_individual_key = !api_key.nil? && api_key[:issuer_id].nil?
420
- if is_individual_key
421
- api_key = nil
422
- api_token = nil
423
- end
424
-
425
417
  unless api_token.nil?
426
418
  api_token.refresh! if api_token.expired?
427
- return FastlaneCore::ItunesTransporter.new(nil, nil, false, nil, api_token.text, altool_compatible_command: true, api_key: api_key)
419
+ return FastlaneCore::ItunesTransporter.new(nil, nil, false, nil, api_token.text, altool_compatible_command: true, api_key: api_key, provider_public_id: nil)
428
420
  end
429
421
 
430
422
  # Otherwise use username and password
431
423
  tunes_client = Spaceship::ConnectAPI.client ? Spaceship::ConnectAPI.client.tunes_client : nil
432
424
 
433
- generic_transporter = FastlaneCore::ItunesTransporter.new(options[:username], nil, false, options[:itc_provider], altool_compatible_command: true, api_key: api_key)
434
- return generic_transporter if options[:itc_provider] || tunes_client.nil?
425
+ generic_transporter = FastlaneCore::ItunesTransporter.new(options[:username], nil, false, options[:itc_provider], altool_compatible_command: true, api_key: api_key, provider_public_id: options[:provider_public_id])
426
+ return generic_transporter if options[:itc_provider] || options[:provider_public_id] || tunes_client.nil?
435
427
  return generic_transporter unless tunes_client.teams.count > 1
436
428
 
437
429
  begin
@@ -293,6 +293,10 @@ module Pilot
293
293
  env_name: "PILOT_ITC_PROVIDER",
294
294
  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 `pathToXcode.app/Contents/Applications/Application\\ Loader.app/Contents/itms/bin/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",
295
295
  optional: true),
296
+ FastlaneCore::ConfigItem.new(key: :provider_public_id,
297
+ env_name: "PILOT_PROVIDER_PUBLIC_ID",
298
+ description: "The provider public ID to be used with altool (--provider-public-id). This value will override the automatically detected provider value for altool uploads. Required after Xcode 26 when your account is associated with multiple providers and using username/app-password authentication",
299
+ optional: true),
296
300
  # rubocop:enable Layout/LineLength
297
301
 
298
302
  # waiting and uploaded build
@@ -0,0 +1,54 @@
1
+ # Updating Curse Word Hashes for `fastlane precheck`
2
+
3
+ ## What does `precheck/rules/curse_words_rule` do?
4
+
5
+ It uses the metadata fetched from the App Store by _precheck_ and reviews it word by word, failing if it matches one of the committed terms.
6
+
7
+ ## What Terms to Hash
8
+
9
+ According to "Want to improve precheck's rules?" in [precheck's docs](https://docs.fastlane.tools/actions/precheck/), phrases flagged by a reviewer in your recent App Store Rejection.
10
+
11
+ ## Format
12
+
13
+ These phrases are committed in repo under https://github.com/fastlane/fastlane/tree/master/precheck/lib/precheck/rules/rules_data/curse_word_hashes as sha 256 digest (one way hash).
14
+
15
+ ### Before Generating
16
+
17
+ Make sure the term you are adding is:
18
+ 1. A single word phrase
19
+ 2. In all lowercase letters
20
+ 3. Void of punctuation
21
+
22
+ This criteria mimics the transformations performed on the input data that is getting checked in https://github.com/fastlane/fastlane/tree/master/precheck/lib/precheck/rules/curse_words_rule.rb
23
+
24
+ ### Generating a New Hash
25
+
26
+ ```rb
27
+ irb(main):001:0> require 'digest'
28
+ => true
29
+ irb(main):002:0> new_term_to_add = "oneword"
30
+ => "oneword"
31
+ irb(main):003:0> Digest::SHA256.hexdigest(new_term_to_add)
32
+ => "31be3624bc03aa68bc050cce316dc80cfe1ace3d0f58fa5f5b20c9e781c44a07"
33
+ irb(main):004:0>
34
+ ```
35
+
36
+ Append this to the end of the file (with a newline afterward).
37
+
38
+ ## How to Test Your Newly Added Term
39
+
40
+ ### Hack at the unit tests to include your phrase
41
+
42
+ Update the tests in https://github.com/fastlane/fastlane/blob/master/precheck/spec/rules/curse_words_rule_spec.rb to include your new phrase.
43
+
44
+ ```diff
45
+ let(:happy_item) { TextItemToCheck.new("tacos are really delicious, seriously, I can't even", :description, "description") }
46
+ - let(:curse_item) { TextItemToCheck.new("please excuse the use of 'shit' in this description", :description, "description") }
47
+ + let(:curse_item) { TextItemToCheck.new("please excuse the use of 'oneword' in this description", :description, "description") }
48
+
49
+ it "passes for non-curse item" do
50
+ ```
51
+
52
+ ### Update your App's listing and run pre-check
53
+
54
+ Add your term to your App's description or keywords and run `bundle exec fastlane precheck` to ensure that it fails the check.
@@ -346,4 +346,5 @@ bc8059fe9118fcf7b2c20a0bf9fdd8111942f2cb4754b45fc0b16916ab679ddb
346
346
  9f02f229004cc97cbe3e41ad839a73dab5f68d4c296dd64055317cf90dd11a1a
347
347
  dab70d211b90bf5e931b5626bc558850752e107daf995bf9e8cb1a08ca7309bf
348
348
  4efb200187aa257858d45bc72b8e590bac33db176aba6576cdd6623655a5a74f
349
- 0be9b885f4a35a18f6af6b1ac0acd7fa4b19993999c3fbf66dd1e3e0c4c753c8
349
+ 0be9b885f4a35a18f6af6b1ac0acd7fa4b19993999c3fbf66dd1e3e0c4c753c8
350
+ c6603565c5159fbe846a53e991829d452a1546d41150c0d3c73ddbd7f476ee0d
@@ -111,7 +111,9 @@ module Scan
111
111
 
112
112
  def self.default_os_version(os_type)
113
113
  @os_versions ||= {}
114
- @os_versions[os_type] ||= begin
114
+ return @os_versions[os_type] if @os_versions.key?(os_type)
115
+
116
+ @os_versions[os_type] = begin
115
117
  UI.crash!("Unknown platform: #{os_type}") unless PLATFORMS.key?(os_type)
116
118
  platform = PLATFORMS[os_type]
117
119
 
@@ -128,7 +130,8 @@ module Scan
128
130
  sdks_output, status = Open3.capture2('xcodebuild -showsdks -json')
129
131
  sdk_version = begin
130
132
  raise status unless status.success?
131
- JSON.parse(sdks_output).find { |e| e['platform'] == platform[:simulator] }['sdkVersion']
133
+ entry = JSON.parse(sdks_output).find { |e| e['platform'] == platform[:simulator] }
134
+ entry['productVersion'] || entry['sdkVersion']
132
135
  rescue StandardError => e
133
136
  UI.error(e)
134
137
  UI.error("xcodebuild CLI broken, please run `xcodebuild` and make sure it works")
@@ -147,7 +150,12 @@ module Scan
147
150
  end
148
151
 
149
152
  # Get OS version corresponding to build
150
- Gem::Version.new(FastlaneCore::DeviceManager.runtime_build_os_versions[runtime_build])
153
+ os_version = FastlaneCore::DeviceManager.runtime_build_os_versions[runtime_build]
154
+ unless os_version
155
+ UI.important("Runtime build '#{runtime_build}' not found in installed runtimes, falling back to SDK version '#{sdk_version}'")
156
+ os_version = sdk_version
157
+ end
158
+ Gem::Version.new(os_version)
151
159
  end
152
160
  end
153
161
  end
@@ -152,6 +152,7 @@ usage() {
152
152
  echo -e "\t --use-app-entitlements\t\tExtract app bundle codesigning entitlements and combine with entitlements from new provisioning profile." >&2
153
153
  echo -e "\t\t\t\t\t\t\tCan't use together with '-e, --entitlements' option." >&2
154
154
  echo -e "\t--keychain-path path\t\t\tSpecify the path to a keychain that /usr/bin/codesign should use." >&2
155
+ echo -e "\t--pagesize size\t\t\t\tSpecify the page size (in bytes) for codesign. Must be a power of two." >&2
155
156
  echo -e "\t-v, --verbose\t\t\t\tVerbose output." >&2
156
157
  echo -e "\t-h, --help\t\t\t\tDisplay help message." >&2
157
158
  exit 2
@@ -171,6 +172,7 @@ VERSION_NUMBER=""
171
172
  SHORT_VERSION=
172
173
  BUNDLE_VERSION=
173
174
  KEYCHAIN_PATH=
175
+ PAGESIZE=
174
176
  RAW_PROVISIONS=()
175
177
  PROVISIONS_BY_ID=()
176
178
  DEFAULT_PROVISION=""
@@ -227,6 +229,10 @@ while [ "$1" != "" ]; do
227
229
  shift
228
230
  KEYCHAIN_PATH="$1"
229
231
  ;;
232
+ --pagesize )
233
+ shift
234
+ PAGESIZE="$1"
235
+ ;;
230
236
  -v | --verbose )
231
237
  VERBOSE="--verbose"
232
238
  ;;
@@ -248,6 +254,11 @@ if [ -n "$KEYCHAIN_PATH" ]; then
248
254
  KEYCHAIN_FLAG="--keychain $KEYCHAIN_PATH"
249
255
  fi
250
256
 
257
+ PAGESIZE_ARGS=()
258
+ if [ -n "$PAGESIZE" ]; then
259
+ PAGESIZE_ARGS=(--pagesize "$PAGESIZE")
260
+ fi
261
+
251
262
  # Log the options
252
263
  for provision in "${RAW_PROVISIONS[@]}"; do
253
264
  if [[ "$provision" =~ .+=.+ ]]; then
@@ -267,6 +278,7 @@ log "Certificate: '$CERTIFICATE'"
267
278
  [[ -n "${SHORT_VERSION}" ]] && log "Specified short version to use: '$SHORT_VERSION'"
268
279
  [[ -n "${BUNDLE_VERSION}" ]] && log "Specified bundle version to use: '$BUNDLE_VERSION'"
269
280
  [[ -n "${KEYCHAIN_FLAG}" ]] && log "Specified keychain to use: '$KEYCHAIN_PATH'"
281
+ [[ -n "${PAGESIZE}" ]] && log "Specified page size: '$PAGESIZE'"
270
282
  [[ -n "${NEW_FILE}" ]] && log "Output file name: '$NEW_FILE'"
271
283
  [[ -n "${USE_APP_ENTITLEMENTS}" ]] && log "Extract app entitlements: YES"
272
284
 
@@ -550,7 +562,7 @@ function resign {
550
562
  do
551
563
  if [[ "$assetpack" == *.assetpack ]]; then
552
564
  rm -rf "$assetpack"/_CodeSignature
553
- /usr/bin/codesign ${VERBOSE} --generate-entitlement-der "${KEYCHAIN_FLAG}" -f -s "$CERTIFICATE" "$assetpack"
565
+ /usr/bin/codesign ${VERBOSE} "${PAGESIZE_ARGS[@]}" --generate-entitlement-der "${KEYCHAIN_FLAG}" -f -s "$CERTIFICATE" "$assetpack"
554
566
  checkStatus
555
567
  else
556
568
  log "Ignoring non-assetpack: $assetpack"
@@ -572,7 +584,7 @@ function resign {
572
584
  log "Resigning '$framework'"
573
585
  # Must not quote KEYCHAIN_FLAG because it needs to be unwrapped and passed to codesign with spaces
574
586
  # shellcheck disable=SC2086
575
- /usr/bin/codesign ${VERBOSE} --generate-entitlement-der ${KEYCHAIN_FLAG} -f -s "$CERTIFICATE" "$framework"
587
+ /usr/bin/codesign ${VERBOSE} "${PAGESIZE_ARGS[@]}" --generate-entitlement-der ${KEYCHAIN_FLAG} -f -s "$CERTIFICATE" "$framework"
576
588
  checkStatus
577
589
  else
578
590
  log "Ignoring non-framework: $framework"
@@ -626,7 +638,7 @@ function resign {
626
638
  log "Creating an archived-expanded-entitlements.xcent file for Xcode 9 builds or earlier"
627
639
  cp -f "$ENTITLEMENTS" "$APP_PATH/archived-expanded-entitlements.xcent"
628
640
  fi
629
- /usr/bin/codesign ${VERBOSE} --generate-entitlement-der -f -s "$CERTIFICATE" --entitlements "$ENTITLEMENTS" "$APP_PATH"
641
+ /usr/bin/codesign ${VERBOSE} "${PAGESIZE_ARGS[@]}" --generate-entitlement-der -f -s "$CERTIFICATE" --entitlements "$ENTITLEMENTS" "$APP_PATH"
630
642
  checkStatus
631
643
  elif [[ -n "${USE_APP_ENTITLEMENTS}" ]]; then
632
644
  # Extract entitlements from provisioning profile and from the app binary
@@ -871,7 +883,7 @@ function resign {
871
883
  log "Creating an archived-expanded-entitlements.xcent file for Xcode 9 builds or earlier"
872
884
  cp -f "$PATCHED_ENTITLEMENTS" "$APP_PATH/archived-expanded-entitlements.xcent"
873
885
  fi
874
- /usr/bin/codesign ${VERBOSE} --generate-entitlement-der -f -s "$CERTIFICATE" --entitlements "$PATCHED_ENTITLEMENTS" "$APP_PATH"
886
+ /usr/bin/codesign ${VERBOSE} "${PAGESIZE_ARGS[@]}" --generate-entitlement-der -f -s "$CERTIFICATE" --entitlements "$PATCHED_ENTITLEMENTS" "$APP_PATH"
875
887
  checkStatus
876
888
  else
877
889
  log "Extracting entitlements from provisioning profile"
@@ -885,7 +897,7 @@ function resign {
885
897
  fi
886
898
  # Must not quote KEYCHAIN_FLAG because it needs to be unwrapped and passed to codesign with spaces
887
899
  # shellcheck disable=SC2086
888
- /usr/bin/codesign ${VERBOSE} --generate-entitlement-der ${KEYCHAIN_FLAG} -f -s "$CERTIFICATE" --entitlements "$TEMP_DIR/newEntitlements" "$APP_PATH"
900
+ /usr/bin/codesign ${VERBOSE} "${PAGESIZE_ARGS[@]}" --generate-entitlement-der ${KEYCHAIN_FLAG} -f -s "$CERTIFICATE" --entitlements "$TEMP_DIR/newEntitlements" "$APP_PATH"
889
901
  checkStatus
890
902
  fi
891
903
 
@@ -114,6 +114,7 @@ module Sigh
114
114
  c.option('--use_app_entitlements', 'Extract app bundle codesigning entitlements and combine with entitlements from new provisioning profile.')
115
115
  c.option('-g', '--new_bundle_id STRING', String, 'New application bundle ID (CFBundleIdentifier)')
116
116
  c.option('--keychain_path STRING', String, 'Path to the keychain that /usr/bin/codesign should use')
117
+ c.option('--page_size STRING', String, 'Page size in bytes for codesign --pagesize (power of two)')
117
118
 
118
119
  c.action do |args, options|
119
120
  Sigh::Resign.new.run(options, args)
@@ -4,7 +4,7 @@ require_relative 'runner'
4
4
 
5
5
  module Sigh
6
6
  class Manager
7
- def self.start
7
+ def self.start(keychain_path: nil)
8
8
  path = Sigh::Runner.new.run
9
9
 
10
10
  return nil unless path
@@ -23,7 +23,7 @@ module Sigh
23
23
  # in case it already exists
24
24
  end
25
25
 
26
- install_profile(output) unless Sigh.config[:skip_install]
26
+ install_profile(output, keychain_path) unless Sigh.config[:skip_install]
27
27
 
28
28
  puts(output.green)
29
29
 
@@ -35,13 +35,13 @@ module Sigh
35
35
  DownloadAll.new.download_all(download_xcode_profiles: download_xcode_profiles)
36
36
  end
37
37
 
38
- def self.install_profile(profile)
39
- uuid = FastlaneCore::ProvisioningProfile.uuid(profile)
40
- name = FastlaneCore::ProvisioningProfile.name(profile)
38
+ def self.install_profile(profile, keychain_path = nil)
39
+ uuid = FastlaneCore::ProvisioningProfile.uuid(profile, keychain_path)
40
+ name = FastlaneCore::ProvisioningProfile.name(profile, keychain_path)
41
41
  ENV["SIGH_UDID"] = ENV["SIGH_UUID"] = uuid if uuid
42
42
  ENV["SIGH_NAME"] = name if name
43
43
 
44
- FastlaneCore::ProvisioningProfile.install(profile)
44
+ FastlaneCore::ProvisioningProfile.install(profile, keychain_path)
45
45
  end
46
46
  end
47
47
  end
@@ -8,18 +8,18 @@ module Sigh
8
8
  class Resign
9
9
  def run(options, args)
10
10
  # get the command line inputs and parse those into the vars we need...
11
- ipa, signing_identity, provisioning_profiles, entitlements, version, display_name, short_version, bundle_version, new_bundle_id, use_app_entitlements, keychain_path = get_inputs(options, args)
11
+ ipa, signing_identity, provisioning_profiles, entitlements, version, display_name, short_version, bundle_version, new_bundle_id, use_app_entitlements, keychain_path, page_size = get_inputs(options, args)
12
12
  # ... then invoke our programmatic interface with these vars
13
- unless resign(ipa, signing_identity, provisioning_profiles, entitlements, version, display_name, short_version, bundle_version, new_bundle_id, use_app_entitlements, keychain_path)
13
+ unless resign(ipa, signing_identity, provisioning_profiles, entitlements, version, display_name, short_version, bundle_version, new_bundle_id, use_app_entitlements, keychain_path, page_size)
14
14
  UI.user_error!("Failed to re-sign .ipa")
15
15
  end
16
16
  end
17
17
 
18
- def self.resign(ipa, signing_identity, provisioning_profiles, entitlements, version, display_name, short_version, bundle_version, new_bundle_id, use_app_entitlements, keychain_path)
19
- self.new.resign(ipa, signing_identity, provisioning_profiles, entitlements, version, display_name, short_version, bundle_version, new_bundle_id, use_app_entitlements, keychain_path)
18
+ def self.resign(ipa, signing_identity, provisioning_profiles, entitlements, version, display_name, short_version, bundle_version, new_bundle_id, use_app_entitlements, keychain_path, pagesize = nil)
19
+ self.new.resign(ipa, signing_identity, provisioning_profiles, entitlements, version, display_name, short_version, bundle_version, new_bundle_id, use_app_entitlements, keychain_path, pagesize)
20
20
  end
21
21
 
22
- def resign(ipa, signing_identity, provisioning_profiles, entitlements, version, display_name, short_version, bundle_version, new_bundle_id, use_app_entitlements, keychain_path)
22
+ def resign(ipa, signing_identity, provisioning_profiles, entitlements, version, display_name, short_version, bundle_version, new_bundle_id, use_app_entitlements, keychain_path, pagesize = nil)
23
23
  resign_path = find_resign_path
24
24
 
25
25
  if keychain_path
@@ -53,6 +53,7 @@ module Sigh
53
53
  bundle_id = "-b '#{new_bundle_id}'" if new_bundle_id
54
54
  use_app_entitlements_flag = "--use-app-entitlements" if use_app_entitlements
55
55
  specific_keychain = "--keychain-path #{keychain_path.shellescape}" if keychain_path
56
+ pagesize_option = "--pagesize #{pagesize}" if pagesize
56
57
 
57
58
  command = [
58
59
  resign_path.shellescape,
@@ -68,6 +69,7 @@ module Sigh
68
69
  verbose,
69
70
  bundle_id,
70
71
  specific_keychain,
72
+ pagesize_option,
71
73
  ipa.shellescape # Output path must always be last argument
72
74
  ].join(' ')
73
75
 
@@ -97,12 +99,13 @@ module Sigh
97
99
  new_bundle_id = options.new_bundle_id || nil
98
100
  use_app_entitlements = options.use_app_entitlements || nil
99
101
  keychain_path = options.keychain_path || nil
102
+ page_size = options.page_size || nil
100
103
 
101
104
  if options.provisioning_name
102
105
  UI.important("The provisioning_name (-n) option is not applicable to resign. You should use provisioning_profile (-p) instead")
103
106
  end
104
107
 
105
- return ipa, signing_identity, provisioning_profiles, entitlements, version, display_name, short_version, bundle_version, new_bundle_id, use_app_entitlements, keychain_path
108
+ return ipa, signing_identity, provisioning_profiles, entitlements, version, display_name, short_version, bundle_version, new_bundle_id, use_app_entitlements, keychain_path, page_size
106
109
  end
107
110
 
108
111
  def find_resign_path
@@ -11,35 +11,37 @@ module Spaceship
11
11
  attr_accessor :app_previews
12
12
 
13
13
  module PreviewType
14
- IPHONE_35 = "IPHONE_35"
15
- IPHONE_40 = "IPHONE_40"
16
- IPHONE_47 = "IPHONE_47"
17
- IPHONE_55 = "IPHONE_55"
18
- IPHONE_58 = "IPHONE_58"
19
- IPHONE_65 = "IPHONE_65"
20
- IPHONE_67 = "IPHONE_67"
21
-
22
- IPAD_97 = "IPAD_97"
23
- IPAD_105 = "IPAD_105"
24
- IPAD_PRO_3GEN_11 = "IPAD_PRO_3GEN_11"
25
- IPAD_PRO_129 = "IPAD_PRO_129"
26
- IPAD_PRO_3GEN_129 = "IPAD_PRO_3GEN_129"
14
+ # https://developer.apple.com/documentation/appstoreconnectapi/previewtype
15
+ IPHONE_35 = "IPHONE_35" # not supported anymore
16
+ IPHONE_40 = "IPHONE_40" # 4"
17
+ IPHONE_47 = "IPHONE_47" # 4.7"
18
+ IPHONE_55 = "IPHONE_55" # 5.5"
19
+ IPHONE_58 = "IPHONE_58" # 6.1"
20
+ IPHONE_61 = "IPHONE_61" # 6.3"
21
+ IPHONE_65 = "IPHONE_65" # 6.5"
22
+ IPHONE_67 = "IPHONE_67" # 6.9"
23
+
24
+ IPAD_97 = "IPAD_97" # 9.7"
25
+ IPAD_105 = "IPAD_105" # 10.5"
26
+ IPAD_PRO_129 = "IPAD_PRO_129" # 12.9"
27
+ IPAD_PRO_3GEN_11 = "IPAD_PRO_3GEN_11" # 11"
28
+ IPAD_PRO_3GEN_129 = "IPAD_PRO_3GEN_129" # 13"
27
29
 
28
30
  DESKTOP = "DESKTOP"
29
31
 
30
32
  ALL = [
31
- IPHONE_35,
32
33
  IPHONE_40,
33
34
  IPHONE_47,
34
35
  IPHONE_55,
35
36
  IPHONE_58,
37
+ IPHONE_61,
36
38
  IPHONE_65,
37
39
  IPHONE_67,
38
40
 
39
41
  IPAD_97,
40
42
  IPAD_105,
41
- IPAD_PRO_3GEN_11,
42
43
  IPAD_PRO_129,
44
+ IPAD_PRO_3GEN_11,
43
45
  IPAD_PRO_3GEN_129,
44
46
 
45
47
  DESKTOP
@@ -60,9 +62,9 @@ module Spaceship
60
62
  # API
61
63
  #
62
64
 
63
- def self.all(client: nil, filter: {}, includes: nil, limit: nil, sort: nil)
65
+ def self.all(client: nil, app_store_version_localization_id: nil, filter: {}, includes: nil, limit: nil, sort: nil)
64
66
  client ||= Spaceship::ConnectAPI
65
- resp = client.get_app_preview_sets(filter: filter, includes: includes, limit: limit, sort: sort)
67
+ resp = client.get_app_preview_sets(app_store_version_localization_id: app_store_version_localization_id, filter: filter, includes: includes, limit: limit, sort: sort)
66
68
  return resp.to_models
67
69
  end
68
70
 
@@ -106,6 +108,41 @@ module Spaceship
106
108
 
107
109
  return client.get_app_preview_set(app_preview_set_id: id, includes: "appPreviews").first
108
110
  end
111
+
112
+ # Validate video resolution (portrait canonical sizes) for provided preview_type.
113
+ # Returns true if the resolution matches any accepted pair.
114
+ def self.validate_video_resolution(width, height, preview_type)
115
+ return false unless width && height
116
+ if width > height
117
+ width, height = height, width
118
+ end
119
+ # for a list of valid resolutions, look for "Accepted resolutions" at https://developer.apple.com/help/app-store-connect/reference/app-information/app-preview-specifications
120
+ # resolutions below are sorted by display inch from biggest to smallest (see top of the module)
121
+ canonical = {
122
+ # iPhone
123
+ PreviewType::IPHONE_67 => [[886, 1920]],
124
+ PreviewType::IPHONE_65 => [[886, 1920]],
125
+ PreviewType::IPHONE_61 => [[886, 1920]],
126
+ PreviewType::IPHONE_58 => [[886, 1920]],
127
+ PreviewType::IPHONE_55 => [[1080, 1920]],
128
+ PreviewType::IPHONE_47 => [[750, 1334]],
129
+ PreviewType::IPHONE_40 => [[1080, 1920]],
130
+
131
+ # iPad
132
+ PreviewType::IPAD_PRO_3GEN_129 => [[1200, 1600]],
133
+ PreviewType::IPAD_PRO_3GEN_11 => [[1200, 1600]],
134
+ PreviewType::IPAD_PRO_129 => [[1200, 1600], [900, 1200]],
135
+ PreviewType::IPAD_105 => [[1200, 1600]],
136
+ PreviewType::IPAD_97 => [[900, 1200]],
137
+ }
138
+
139
+ pairs = canonical[preview_type] || []
140
+ pairs.any? { |(canon_width, canon_height)| width == canon_width && height == canon_height }
141
+ end
142
+
143
+ def self.preview_type_from_filename(name, preview_types = PreviewType::ALL)
144
+ preview_types.find { |type| name.to_s.upcase.include?(type) }
145
+ end
109
146
  end
110
147
  end
111
148
  end
@@ -78,8 +78,7 @@ module Spaceship
78
78
  def get_app_preview_sets(client: nil, filter: {}, includes: "appPreviews", limit: nil, sort: nil)
79
79
  client ||= Spaceship::ConnectAPI
80
80
  filter ||= {}
81
- filter["appStoreVersionLocalization"] = id
82
- return Spaceship::ConnectAPI::AppPreviewSet.all(client: client, filter: filter, includes: includes, limit: limit, sort: sort)
81
+ return Spaceship::ConnectAPI::AppPreviewSet.all(client: client, app_store_version_localization_id: id, filter: filter, includes: includes, limit: limit, sort: sort)
83
82
  rescue => error
84
83
  raise Spaceship::AppStoreAppPreviewError.new(@locale, error)
85
84
  end
@@ -367,9 +367,9 @@ module Spaceship
367
367
  # appPreviewSets
368
368
  #
369
369
 
370
- def get_app_preview_sets(filter: {}, includes: nil, limit: nil, sort: nil)
370
+ def get_app_preview_sets(app_store_version_localization_id: nil, filter: {}, includes: nil, limit: nil, sort: nil)
371
371
  params = tunes_request_client.build_params(filter: filter, includes: includes, limit: limit, sort: sort)
372
- tunes_request_client.get("#{Version::V1}/appPreviewSets", params)
372
+ tunes_request_client.get("#{Version::V1}/appStoreVersionLocalizations/#{app_store_version_localization_id}/appPreviewSets", params)
373
373
  end
374
374
 
375
375
  def get_app_preview_set(app_preview_set_id: nil, filter: {}, includes: nil, limit: nil, sort: nil)
@@ -502,26 +502,7 @@ module Trainer
502
502
 
503
503
  # Remove retry attempts from the count and test rows
504
504
  if output_remove_retry_attempts
505
- test_rows = test_rows.reject do |test_row|
506
- remove = false
507
-
508
- identifier = test_row[:identifier]
509
- info = tests_by_identifier[identifier]
510
-
511
- # Remove if this row is a retry and is a failure
512
- if info[:retry_count] > 0
513
- remove = !(test_row[:failures] || []).empty?
514
- end
515
-
516
- # Remove all failure and retry count if test did eventually pass
517
- if remove
518
- info[:failure_count] -= 1
519
- info[:retry_count] -= 1
520
- tests_by_identifier[identifier] = info
521
- end
522
-
523
- remove
524
- end
505
+ test_rows, tests_by_identifier = remove_retry_attempts(test_rows, tests_by_identifier)
525
506
  end
526
507
 
527
508
  row = {
@@ -549,6 +530,32 @@ module Trainer
549
530
  rows
550
531
  end
551
532
 
533
+ def remove_retry_attempts(test_rows, tests_by_identifier)
534
+ # Group test rows by identifier and keep only the last one for each identifier
535
+ test_rows_by_identifier = test_rows.group_by { |row| row[:identifier] }
536
+
537
+ # Update counts for removed retry attempts
538
+ test_rows_by_identifier.each do |identifier, rows|
539
+ rows_to_remove = rows[0...-1]
540
+
541
+ info = tests_by_identifier[identifier]
542
+ rows_to_remove.each do |row|
543
+ if !(row[:failures] || []).empty?
544
+ info[:failure_count] -= 1
545
+ elsif row[:skipped] == true
546
+ info[:skip_count] -= 1
547
+ end
548
+ info[:retry_count] -= 1
549
+ end
550
+ tests_by_identifier[identifier] = info
551
+ end
552
+
553
+ # Keep only the last row for each identifier
554
+ filtered_test_rows = test_rows_by_identifier.values.map(&:last)
555
+
556
+ [filtered_test_rows, tests_by_identifier]
557
+ end
558
+
552
559
  def test_summaries_to_configuration_names(test_summaries)
553
560
  summary_to_name = {}
554
561
  test_summaries.each do |summary|
@@ -106,7 +106,10 @@ module Trainer
106
106
  # Add test cases
107
107
  @test_cases.each do |test_case|
108
108
  runs = test_case.to_xml_nodes
109
- runs = runs.last(1) if output_remove_retry_attempts
109
+ # Remove retry attempts: keep only the final run (whether it passes, fails, or is skipped)
110
+ if output_remove_retry_attempts
111
+ runs = runs.last(1)
112
+ end
110
113
  runs.each { |node| testsuite.add_element(node) }
111
114
  end
112
115
 
metadata CHANGED
@@ -1,40 +1,40 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fastlane
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.232.2
4
+ version: 2.233.1
5
5
  platform: ruby
6
6
  authors:
7
- - Jorge Revuelta H
8
- - Danielle Tomlinson
9
- - Fumiya Nakamura
10
- - Jérôme Lacoste
11
- - Roger Oba
12
- - Aaron Brager
13
- - Maksym Grebenets
14
- - Helmut Januschka
15
- - Manu Wallner
16
- - Olivier Halligon
17
- - Daniel Jankowski
18
7
  - Satoshi Namai
8
+ - Łukasz Grabowski
9
+ - Manu Wallner
19
10
  - Connor Tumbleson
11
+ - Helmut Januschka
12
+ - Stefan Natchev
13
+ - Maksym Grebenets
20
14
  - Felix Krause
21
- - Iulian Onofrei
15
+ - Andrew McBurney
16
+ - Luka Mirosevic
22
17
  - Josh Holtz
18
+ - Jorge Revuelta H
23
19
  - Jan Piotrowski
20
+ - Iulian Onofrei
21
+ - Fumiya Nakamura
24
22
  - Joshua Liebowitz
23
+ - Daniel Jankowski
24
+ - Danielle Tomlinson
25
25
  - Kohki Miki
26
- - Łukasz Grabowski
27
- - Andrew McBurney
28
- - Stefan Natchev
29
- - Manish Rathi
30
26
  - Jimmy Dee
31
27
  - Max Ott
28
+ - Manish Rathi
29
+ - Aaron Brager
30
+ - Olivier Halligon
31
+ - Roger Oba
32
32
  - Matthew Ellis
33
- - Luka Mirosevic
33
+ - Jérôme Lacoste
34
34
  autorequire:
35
35
  bindir: bin
36
36
  cert_chain: []
37
- date: 2026-02-27 00:00:00.000000000 Z
37
+ date: 2026-04-29 00:00:00.000000000 Z
38
38
  dependencies:
39
39
  - !ruby/object:Gem::Dependency
40
40
  name: addressable
@@ -300,14 +300,14 @@ dependencies:
300
300
  requirements:
301
301
  - - ">="
302
302
  - !ruby/object:Gem::Version
303
- version: 1.0.0
303
+ version: 1.1.0
304
304
  type: :runtime
305
305
  prerelease: false
306
306
  version_requirements: !ruby/object:Gem::Requirement
307
307
  requirements:
308
308
  - - ">="
309
309
  - !ruby/object:Gem::Version
310
- version: 1.0.0
310
+ version: 1.1.0
311
311
  - !ruby/object:Gem::Dependency
312
312
  name: gh_inspector
313
313
  requirement: !ruby/object:Gem::Requirement
@@ -905,6 +905,7 @@ files:
905
905
  - deliver/lib/deliver/screenshot_comparable.rb
906
906
  - deliver/lib/deliver/setup.rb
907
907
  - deliver/lib/deliver/submit_for_review.rb
908
+ - deliver/lib/deliver/sync_app_previews.rb
908
909
  - deliver/lib/deliver/sync_screenshots.rb
909
910
  - deliver/lib/deliver/upload_metadata.rb
910
911
  - deliver/lib/deliver/upload_price_tier.rb
@@ -1368,6 +1369,7 @@ files:
1368
1369
  - fastlane_core/lib/fastlane_core/ui/ui.rb
1369
1370
  - fastlane_core/lib/fastlane_core/update_checker/changelog.rb
1370
1371
  - fastlane_core/lib/fastlane_core/update_checker/update_checker.rb
1372
+ - fastlane_core/lib/fastlane_core/video_utils.rb
1371
1373
  - frameit/README.md
1372
1374
  - frameit/lib/assets/empty.png
1373
1375
  - frameit/lib/frameit.rb
@@ -1477,6 +1479,7 @@ files:
1477
1479
  - precheck/lib/precheck/rules/negative_apple_sentiment_rule.rb
1478
1480
  - precheck/lib/precheck/rules/other_platforms_rule.rb
1479
1481
  - precheck/lib/precheck/rules/placeholder_words_rule.rb
1482
+ - precheck/lib/precheck/rules/rules_data/curse_word_hashes/README.md
1480
1483
  - precheck/lib/precheck/rules/rules_data/curse_word_hashes/en_us.txt
1481
1484
  - precheck/lib/precheck/rules/test_words_rule.rb
1482
1485
  - precheck/lib/precheck/rules/unreachable_urls_rule.rb