fastlane 2.189.0 → 2.193.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +92 -92
  3. data/deliver/lib/deliver/app_screenshot.rb +2 -1
  4. data/deliver/lib/deliver/app_screenshot_iterator.rb +2 -2
  5. data/deliver/lib/deliver/loader.rb +1 -1
  6. data/deliver/lib/deliver/options.rb +6 -0
  7. data/deliver/lib/deliver/runner.rb +9 -1
  8. data/deliver/lib/deliver/screenshot_comparable.rb +62 -0
  9. data/deliver/lib/deliver/sync_screenshots.rb +200 -0
  10. data/fastlane/lib/assets/completions/completion.bash +4 -1
  11. data/fastlane/lib/assets/completions/completion.zsh +6 -5
  12. data/fastlane/lib/fastlane/actions/app_store_connect_api_key.rb +1 -1
  13. data/fastlane/lib/fastlane/actions/bundle_install.rb +13 -1
  14. data/fastlane/lib/fastlane/actions/clean_cocoapods_cache.rb +25 -1
  15. data/fastlane/lib/fastlane/actions/create_xcframework.rb +97 -17
  16. data/fastlane/lib/fastlane/actions/docs/capture_android_screenshots.md +2 -2
  17. data/fastlane/lib/fastlane/actions/docs/upload_to_app_store.md.erb +2 -2
  18. data/fastlane/lib/fastlane/actions/get_provisioning_profile.rb +1 -1
  19. data/fastlane/lib/fastlane/actions/notarize.rb +77 -1
  20. data/fastlane/lib/fastlane/actions/push_git_tags.rb +1 -1
  21. data/fastlane/lib/fastlane/actions/sync_code_signing.rb +1 -1
  22. data/fastlane/lib/fastlane/actions/upload_to_testflight.rb +3 -1
  23. data/fastlane/lib/fastlane/actions/zip.rb +86 -21
  24. data/fastlane/lib/fastlane/features.rb +3 -0
  25. data/fastlane/lib/fastlane/version.rb +1 -1
  26. data/fastlane/swift/Deliverfile.swift +1 -1
  27. data/fastlane/swift/DeliverfileProtocol.swift +5 -1
  28. data/fastlane/swift/Fastlane.swift +121 -25
  29. data/fastlane/swift/Gymfile.swift +1 -1
  30. data/fastlane/swift/GymfileProtocol.swift +1 -1
  31. data/fastlane/swift/Matchfile.swift +1 -1
  32. data/fastlane/swift/MatchfileProtocol.swift +1 -1
  33. data/fastlane/swift/Precheckfile.swift +1 -1
  34. data/fastlane/swift/PrecheckfileProtocol.swift +1 -1
  35. data/fastlane/swift/Scanfile.swift +1 -1
  36. data/fastlane/swift/ScanfileProtocol.swift +1 -1
  37. data/fastlane/swift/Screengrabfile.swift +1 -1
  38. data/fastlane/swift/ScreengrabfileProtocol.swift +1 -1
  39. data/fastlane/swift/Snapshotfile.swift +1 -1
  40. data/fastlane/swift/SnapshotfileProtocol.swift +1 -1
  41. data/fastlane/swift/formatting/Brewfile.lock.json +9 -9
  42. data/fastlane_core/lib/fastlane_core/build_watcher.rb +25 -6
  43. data/fastlane_core/lib/fastlane_core/keychain_importer.rb +11 -4
  44. data/fastlane_core/lib/fastlane_core/ui/disable_colors.rb +1 -0
  45. data/fastlane_core/lib/fastlane_core/ui/fastlane_runner.rb +41 -0
  46. data/match/lib/match/runner.rb +5 -5
  47. data/match/lib/match/storage/.git_storage.rb.swp +0 -0
  48. data/match/lib/match/storage/.interface.rb.swp +0 -0
  49. data/pilot/lib/pilot/build_manager.rb +14 -4
  50. data/pilot/lib/pilot/manager.rb +3 -1
  51. data/pilot/lib/pilot/options.rb +20 -1
  52. data/produce/lib/produce/commands_generator.rb +28 -0
  53. data/produce/lib/produce/service.rb +16 -1
  54. data/scan/lib/scan/xcpretty_reporter_options_generator.rb +1 -1
  55. data/sigh/lib/sigh/options.rb +2 -1
  56. data/spaceship/lib/spaceship/client.rb +6 -0
  57. data/spaceship/lib/spaceship/connect_api/api_client.rb +15 -1
  58. data/spaceship/lib/spaceship/connect_api/models/app.rb +9 -1
  59. data/spaceship/lib/spaceship/connect_api/models/build.rb +4 -0
  60. data/spaceship/lib/spaceship/connect_api/models/build_beta_detail.rb +4 -0
  61. data/spaceship/lib/spaceship/connect_api/models/capabilities.rb +27 -0
  62. data/spaceship/lib/spaceship/connect_api/models/user.rb +17 -3
  63. data/spaceship/lib/spaceship/connect_api/models/user_invitation.rb +26 -5
  64. data/spaceship/lib/spaceship/connect_api/provisioning/provisioning.rb +5 -0
  65. data/spaceship/lib/spaceship/connect_api/testflight/.testflight.rb.swp +0 -0
  66. data/spaceship/lib/spaceship/connect_api/testflight/client.rb +3 -0
  67. data/spaceship/lib/spaceship/connect_api/testflight/testflight.rb +46 -5
  68. data/spaceship/lib/spaceship/connect_api/token.rb +4 -1
  69. data/spaceship/lib/spaceship/connect_api/tunes/client.rb +3 -0
  70. data/spaceship/lib/spaceship/connect_api/users/client.rb +3 -0
  71. data/spaceship/lib/spaceship/connect_api/users/users.rb +58 -3
  72. data/spaceship/lib/spaceship/connect_api.rb +1 -0
  73. data/spaceship/lib/spaceship/tunes/tunes_client.rb +3 -0
  74. data/supply/lib/supply/client.rb +38 -5
  75. data/supply/lib/supply/options.rb +7 -0
  76. data/supply/lib/supply/uploader.rb +1 -1
  77. metadata +39 -19
@@ -4,6 +4,7 @@ _fastlane_complete() {
4
4
  COMPREPLY=()
5
5
  local word="${COMP_WORDS[COMP_CWORD]}"
6
6
  local completions=""
7
+ local file
7
8
 
8
9
  # look for Fastfile either in this directory or fastlane/ then grab the lane names
9
10
  if [[ -e "Fastfile" ]]; then
@@ -12,10 +13,12 @@ _fastlane_complete() {
12
13
  file="fastlane/Fastfile"
13
14
  elif [[ -e ".fastlane/Fastfile" ]]; then
14
15
  file=".fastlane/Fastfile"
16
+ else
17
+ return 1
15
18
  fi
16
19
 
17
20
  # parse 'beta' out of 'lane :beta do', etc
18
- completions=$(grep "^\s*lane \:" $file | awk -F ':' '{print $2}' | awk -F ' ' '{print $1}')
21
+ completions="$(sed -En 's/^[ ]*lane +:([^ ]+).*$/\1/p' "$file")"
19
22
  completions="$completions update_fastlane"
20
23
 
21
24
  COMPREPLY=( $(compgen -W "$completions" -- "$word") )
@@ -1,7 +1,7 @@
1
1
  #!/bin/zsh
2
2
 
3
3
  _fastlane_complete() {
4
- local word completions
4
+ local word completions file
5
5
  word="$1"
6
6
 
7
7
  # look for Fastfile either in this directory or fastlane/ then grab the lane names
@@ -11,13 +11,14 @@ _fastlane_complete() {
11
11
  file="fastlane/Fastfile"
12
12
  elif [[ -e ".fastlane/Fastfile" ]] then
13
13
  file=".fastlane/Fastfile"
14
+ else
15
+ return 1
14
16
  fi
15
17
 
16
18
  # parse 'beta' out of 'lane :beta do', etc
17
- completions=`cat $file | grep "^\s*lane \:" | awk -F ':' '{print $2}' | awk -F ' ' '{print $1}'`
18
- completions="$completions
19
- update_fastlane"
19
+ completions="$(sed -En 's/^[ ]*lane +:([^ ]+).*$/\1/p' "$file")"
20
+ completions="$completions update_fastlane"
20
21
 
21
- reply=( "${(ps:\n:)completions}" )
22
+ reply=( "${=completions}" )
22
23
  }
23
24
 
@@ -80,7 +80,7 @@ module Fastlane
80
80
  env_name: "APP_STORE_CONNECT_API_KEY_DURATION",
81
81
  description: "The token session duration",
82
82
  optional: true,
83
- default_value: Spaceship::ConnectAPI::Token::MAX_TOKEN_DURATION,
83
+ default_value: Spaceship::ConnectAPI::Token::DEFAULT_TOKEN_DURATION,
84
84
  type: Integer,
85
85
  verify_block: proc do |value|
86
86
  UI.user_error!("The duration can't be more than 1200 (20 minutes) and the value entered was '#{value}'") unless value <= 1200
@@ -24,6 +24,8 @@ module Fastlane
24
24
  cmd << "--trust-policy" if params[:trust_policy]
25
25
  cmd << "--without #{params[:without]}" if params[:without]
26
26
  cmd << "--with #{params[:with]}" if params[:with]
27
+ cmd << "--frozen" if params[:frozen]
28
+ cmd << "--redownload" if params[:redownload]
27
29
 
28
30
  return sh(cmd.join(' '))
29
31
  else
@@ -146,7 +148,17 @@ module Fastlane
146
148
  FastlaneCore::ConfigItem.new(key: :with,
147
149
  env_name: "FL_BUNDLE_INSTALL_WITH",
148
150
  description: "Include gems that are part of the specified named group",
149
- optional: true)
151
+ optional: true),
152
+ FastlaneCore::ConfigItem.new(key: :frozen,
153
+ env_name: "FL_BUNDLE_INSTALL_FROZEN",
154
+ description: "Don't allow the Gemfile.lock to be updated after install",
155
+ type: Boolean,
156
+ default_value: false),
157
+ FastlaneCore::ConfigItem.new(key: :redownload,
158
+ env_name: "FL_BUNDLE_INSTALL_REDOWNLOAD",
159
+ description: "Force download every gem, even if the required versions are already available locally",
160
+ type: Boolean,
161
+ default_value: false)
150
162
  ]
151
163
  end
152
164
  end
@@ -7,6 +7,10 @@ module Fastlane
7
7
  cmd = ['pod cache clean']
8
8
 
9
9
  cmd << params[:name].to_s if params[:name]
10
+ cmd << '--no-ansi' if params[:no_ansi]
11
+ cmd << '--verbose' if params[:verbose]
12
+ cmd << '--silent' if params[:silent]
13
+ cmd << '--allow-root' if params[:allow_root]
10
14
  cmd << '--all'
11
15
 
12
16
  Actions.sh(cmd.join(' '))
@@ -24,7 +28,27 @@ module Fastlane
24
28
  optional: true,
25
29
  verify_block: proc do |value|
26
30
  UI.user_error!("You must specify pod name which should be removed from cache") if value.to_s.empty?
27
- end)
31
+ end),
32
+ FastlaneCore::ConfigItem.new(key: :no_ansi,
33
+ env_name: "FL_CLEAN_COCOAPODS_CACHE_NO_ANSI",
34
+ description: "Show output without ANSI codes",
35
+ type: Boolean,
36
+ default_value: false),
37
+ FastlaneCore::ConfigItem.new(key: :verbose,
38
+ env_name: "FL_CLEAN_COCOAPODS_CACHE_VERBOSE",
39
+ description: "Show more debugging information",
40
+ type: Boolean,
41
+ default_value: false),
42
+ FastlaneCore::ConfigItem.new(key: :silent,
43
+ env_name: "FL_CLEAN_COCOAPODS_CACHE_SILENT",
44
+ description: "Show nothing",
45
+ type: Boolean,
46
+ default_value: false),
47
+ FastlaneCore::ConfigItem.new(key: :allow_root,
48
+ env_name: "FL_CLEAN_COCOAPODS_CACHE_ALLOW_ROOT",
49
+ description: "Allows CocoaPods to run as root",
50
+ type: Boolean,
51
+ default_value: false)
28
52
  ]
29
53
  end
30
54
 
@@ -5,12 +5,19 @@ module Fastlane
5
5
  end
6
6
 
7
7
  class CreateXcframeworkAction < Action
8
+ PARAMETERS_TO_OPTIONS = { headers: '-headers', dsyms: '-debug-symbols' }
9
+
8
10
  def self.run(params)
9
- UI.user_error!("Please provide either :frameworks or :libraries to be packaged into the xcframework") unless params[:frameworks] || params[:libraries]
11
+ artifacts = normalized_artifact_info(params[:frameworks], [:dsyms]) ||
12
+ normalized_artifact_info(params[:frameworks_with_dsyms], [:dsyms]) ||
13
+ normalized_artifact_info(params[:libraries], [:headers, :dsyms]) ||
14
+ normalized_artifact_info(params[:libraries_with_headers_or_dsyms], [:headers, :dsyms])
15
+
16
+ UI.user_error!("Please provide either :frameworks, :frameworks_with_dsyms, :libraries or :libraries_with_headers_or_dsyms to be packaged into the xcframework") unless artifacts
10
17
 
18
+ artifacts_type = params[:frameworks] || params[:frameworks_with_dsyms] ? '-framework' : '-library'
11
19
  create_command = ['xcodebuild', '-create-xcframework']
12
- create_command << params[:frameworks].map { |framework| ['-framework', "\"#{framework}\""] }.flatten if params[:frameworks]
13
- create_command << params[:libraries].map { |library, headers| ['-library', "\"#{library}\""] + (headers.empty? ? [] : ['-headers', "\"#{headers}\""]) } if params[:libraries]
20
+ create_command << artifacts.map { |artifact, artifact_info| [artifacts_type, "\"#{artifact}\""] + artifact_info_as_options(artifact_info) }.flatten
14
21
  create_command << ['-output', "\"#{params[:output]}\""]
15
22
  create_command << ['-allow-internal-distribution'] if params[:allow_internal_distribution]
16
23
 
@@ -24,6 +31,32 @@ module Fastlane
24
31
  sh(create_command)
25
32
  end
26
33
 
34
+ def self.normalized_artifact_info(artifacts_with_info, valid_info)
35
+ case artifacts_with_info
36
+ when Array
37
+ artifacts_with_info.map { |artifact| [artifact, {}] }.to_h
38
+ when Hash
39
+ # Convert keys of artifact info to symbols ('dsyms' to :dsyms) and only keep keys we are interested in
40
+ # For example with valid_info = [:dsyms]
41
+ # { 'FrameworkA.framework' => { 'dsyms' => 'FrameworkA.framework.dSYM', 'foo' => bar } }
42
+ # gets converted to
43
+ # { 'FrameworkA.framework' => { dsyms: 'FrameworkA.framework.dSYM' } }
44
+ artifacts_with_info.transform_values { |artifact_info| artifact_info.transform_keys(&:to_sym).slice(*valid_info) }
45
+ else
46
+ artifacts_with_info
47
+ end
48
+ end
49
+
50
+ def self.artifact_info_as_options(artifact_info)
51
+ artifact_info.map { |type, file| [PARAMETERS_TO_OPTIONS[type], "\"#{file}\""] }.flatten
52
+ end
53
+
54
+ def self.check_artifact_info(artifact_info)
55
+ UI.user_error!("Headers and dSYMs information should be a hash") unless artifact_info.kind_of?(Hash)
56
+ UI.user_error!("#{artifact_info[:headers]} doesn't exist or is not a directory") if artifact_info[:headers] && !File.directory?(artifact_info[:headers])
57
+ UI.user_error!("#{artifact_info[:dsyms]} doesn't seem to be a dSYM archive") if artifact_info[:dsyms] && !File.directory?(artifact_info[:dsyms])
58
+ end
59
+
27
60
  #####################################################
28
61
  # @!group Documentation
29
62
  #####################################################
@@ -37,13 +70,31 @@ module Fastlane
37
70
  Utility for packaging multiple build configurations of a given library
38
71
  or framework into a single xcframework.
39
72
 
40
- If you want to package several frameworks just provide an array containing
41
- the list of frameworks to be packaged using the :frameworks parameter.
73
+ If you want to package several frameworks just provide one of:
74
+
75
+ * An array containing the list of frameworks using the :frameworks parameter
76
+ (if they have no associated dSYMs):
77
+ ['FrameworkA.framework', 'FrameworkB.framework']
78
+
79
+ * A hash containing the list of frameworks with their dSYMs using the
80
+ :frameworks_with_dsyms parameter:
81
+ {
82
+ 'FrameworkA.framework' => {},
83
+ 'FrameworkB.framework' => { dsyms: 'FrameworkB.framework.dSYM' }
84
+ }
42
85
 
43
- If you want to package several libraries with their corresponding headers
44
- provide a hash containing the library as the key and the directory containing
45
- its headers as the value (or an empty string if there are no headers associated
46
- with the provided library).
86
+ If you want to package several libraries just provide one of:
87
+
88
+ * An array containing the list of libraries using the :libraries parameter
89
+ (if they have no associated headers or dSYMs):
90
+ ['LibraryA.so', 'LibraryB.so']
91
+
92
+ * A hash containing the list of libraries with their headers and dSYMs
93
+ using the :libraries_with_headers_or_dsyms parameter:
94
+ {
95
+ 'LibraryA.so' => { dsyms: 'libraryA.so.dSYM' },
96
+ 'LibraryB.so' => { headers: 'headers' }
97
+ }
47
98
 
48
99
  Finally specify the location of the xcframework to be generated using the :output
49
100
  parameter.
@@ -54,27 +105,54 @@ module Fastlane
54
105
  [
55
106
  FastlaneCore::ConfigItem.new(key: :frameworks,
56
107
  env_name: "FL_CREATE_XCFRAMEWORK_FRAMEWORKS",
57
- description: "Frameworks to add to the target xcframework",
108
+ description: "Frameworks (without dSYMs) to add to the target xcframework",
58
109
  type: Array,
59
110
  optional: true,
60
- conflicting_options: [:libraries],
111
+ conflicting_options: [:frameworks_with_dsyms, :libraries, :libraries_with_headers_or_dsyms],
61
112
  verify_block: proc do |value|
62
- value.each do |framework|
113
+ normalized_artifact_info(value, [:dsyms]).each do |framework, framework_info|
63
114
  UI.user_error!("#{framework} doesn't end with '.framework'. Is this really a framework?") unless framework.end_with?('.framework')
64
115
  UI.user_error!("Couldn't find framework at #{framework}") unless File.exist?(framework)
65
116
  UI.user_error!("#{framework} doesn't seem to be a framework") unless File.directory?(framework)
117
+ check_artifact_info(framework_info)
118
+ end
119
+ end),
120
+ FastlaneCore::ConfigItem.new(key: :frameworks_with_dsyms,
121
+ env_name: "FL_CREATE_XCFRAMEWORK_FRAMEWORKS_WITH_DSYMS",
122
+ description: "Frameworks (with dSYMs) to add to the target xcframework",
123
+ type: Hash,
124
+ optional: true,
125
+ conflicting_options: [:frameworks, :libraries, :libraries_with_headers_or_dsyms],
126
+ verify_block: proc do |value|
127
+ normalized_artifact_info(value, [:dsyms]).each do |framework, framework_info|
128
+ UI.user_error!("#{framework} doesn't end with '.framework'. Is this really a framework?") unless framework.end_with?('.framework')
129
+ UI.user_error!("Couldn't find framework at #{framework}") unless File.exist?(framework)
130
+ UI.user_error!("#{framework} doesn't seem to be a framework") unless File.directory?(framework)
131
+ check_artifact_info(framework_info)
66
132
  end
67
133
  end),
68
134
  FastlaneCore::ConfigItem.new(key: :libraries,
69
135
  env_name: "FL_CREATE_XCFRAMEWORK_LIBRARIES",
70
- description: "Libraries to add to the target xcframework, with their corresponding headers",
136
+ description: "Libraries (without headers or dSYMs) to add to the target xcframework",
137
+ type: Array,
138
+ optional: true,
139
+ conflicting_options: [:frameworks, :frameworks_with_dsyms, :libraries_with_headers_or_dsyms],
140
+ verify_block: proc do |value|
141
+ normalized_artifact_info(value, [:headers, :dsyms]).each do |library, library_info|
142
+ UI.user_error!("Couldn't find library at #{library}") unless File.exist?(library)
143
+ check_artifact_info(library_info)
144
+ end
145
+ end),
146
+ FastlaneCore::ConfigItem.new(key: :libraries_with_headers_or_dsyms,
147
+ env_name: "FL_CREATE_XCFRAMEWORK_LIBRARIES_WITH_HEADERS_OR_DSYMS",
148
+ description: "Libraries (with headers or dSYMs) to add to the target xcframework",
71
149
  type: Hash,
72
150
  optional: true,
73
- conflicting_options: [:frameworks],
151
+ conflicting_options: [:frameworks, :frameworks_with_dsyms, :libraries],
74
152
  verify_block: proc do |value|
75
- value.each do |library, headers|
153
+ normalized_artifact_info(value, [:headers, :dsyms]).each do |library, library_info|
76
154
  UI.user_error!("Couldn't find library at #{library}") unless File.exist?(library)
77
- UI.user_error!("#{headers} doesn't exist or is not a directory") unless headers.empty? || File.directory?(headers)
155
+ check_artifact_info(library_info)
78
156
  end
79
157
  end),
80
158
  FastlaneCore::ConfigItem.new(key: :output,
@@ -103,7 +181,9 @@ module Fastlane
103
181
  def self.example_code
104
182
  [
105
183
  "create_xcframework(frameworks: ['FrameworkA.framework', 'FrameworkB.framework'], output: 'UniversalFramework.xcframework')",
106
- "create_xcframework(libraries: { 'LibraryA.so' => '', 'LibraryB.so' => 'LibraryBHeaders'}, output: 'UniversalFramework.xcframework')"
184
+ "create_xcframework(frameworks_with_dsyms: {'FrameworkA.framework' => {}, 'FrameworkB.framework' => { dsyms: 'FrameworkB.framework.dSYM' } }, output: 'UniversalFramework.xcframework')",
185
+ "create_xcframework(libraries: ['LibraryA.so', 'LibraryB.so'], output: 'UniversalFramework.xcframework')",
186
+ "create_xcframework(libraries_with_headers_or_dsyms: { 'LibraryA.so' => { dsyms: 'libraryA.so.dSYM' }, 'LibraryB.so' => { headers: 'LibraryBHeaders' } }, output: 'UniversalFramework.xcframework')"
107
187
  ]
108
188
  end
109
189
 
@@ -247,7 +247,7 @@ new CleanStatusBar()
247
247
 
248
248
  # Advanced _screengrab_
249
249
 
250
- <details>
250
+ <details markdown="1">
251
251
  <summary>Launch Arguments</summary>
252
252
 
253
253
  You can provide additional arguments to your test cases on launch. These strings will be available in your tests through `InstrumentationRegistry.getArguments()`.
@@ -277,7 +277,7 @@ if (extras != null) {
277
277
  ```
278
278
  </details>
279
279
 
280
- <details>
280
+ <details markdown="1">
281
281
  <summary>Detecting screengrab at runtime</summary>
282
282
 
283
283
  For some apps, it is helpful to know when _screengrab_ is running so that you can display specific data for your screenshots. For iOS fastlane users, this is much like "FASTLANE_SNAPSHOT". In order to do this, you'll need to have at least two product flavors of your app.
@@ -104,7 +104,7 @@ deliver(
104
104
 
105
105
  ## More options
106
106
 
107
- <details>
107
+ <details markdown="1">
108
108
  <summary>View all available options and their valid values</summary>
109
109
 
110
110
  ## Available options
@@ -497,7 +497,7 @@ Key | Editable While Live | Directory | Filename | Deprecated Filename
497
497
 
498
498
  ## Reference
499
499
 
500
- <details>
500
+ <details markdown="1">
501
501
  <summary>View all available categories, etc.</summary>
502
502
 
503
503
  ### Available Categories
@@ -64,7 +64,7 @@ module Fastlane
64
64
  ['SIGH_PROFILE_PATHS', 'Paths in which certificates, key and profile are exported'],
65
65
  ['SIGH_UUID', 'UUID (Universally Unique IDentifier) of a provisioning profile'],
66
66
  ['SIGH_NAME', 'The name of the profile'],
67
- ['SIGH_PROFILE_TYPE', 'The profile type, can be appstore, adhoc, development, enterprise']
67
+ ['SIGH_PROFILE_TYPE', 'The profile type, can be app-store, ad-hoc, development, enterprise, developer-id, can be used in `build_app` as a default value for `export_method`']
68
68
  ]
69
69
  end
70
70
 
@@ -10,6 +10,8 @@ module Fastlane
10
10
  verbose = params[:verbose]
11
11
  api_key_path = params[:api_key_path]
12
12
 
13
+ use_notarytool = params[:use_notarytool]
14
+
13
15
  # Compress and read bundle identifier only for .app bundle.
14
16
  compressed_package_path = nil
15
17
  if File.extname(package_path) == '.app'
@@ -30,6 +32,74 @@ module Fastlane
30
32
 
31
33
  UI.user_error!('Could not read bundle identifier, provide as a parameter') unless bundle_id
32
34
 
35
+ if use_notarytool
36
+ notarytool(params, package_path, bundle_id, try_early_stapling, print_log, verbose, api_key_path, compressed_package_path)
37
+ else
38
+ altool(params, package_path, bundle_id, try_early_stapling, print_log, verbose, api_key_path, compressed_package_path)
39
+ end
40
+ end
41
+
42
+ def self.notarytool(params, package_path, bundle_id, try_early_stapling, print_log, verbose, api_key_path, compressed_package_path)
43
+ temp_file = nil
44
+
45
+ # Create authorization part of command with either API Key or Apple ID
46
+ auth_parts = []
47
+ if api_key_path
48
+ api_key = Spaceship::ConnectAPI::Token.from_json_file(api_key_path)
49
+
50
+ # Writes key contents to temporary file for command
51
+ require 'tempfile'
52
+ temp_file = Tempfile.new
53
+ api_key.write_key_to_file(temp_file.path)
54
+
55
+ auth_parts << "--key #{temp_file.path}"
56
+ auth_parts << "--key-id #{api_key.key_id}"
57
+ auth_parts << "--issuer #{api_key.issuer_id}"
58
+ else
59
+ auth_parts << "--apple-id #{params[:username]}"
60
+ auth_parts << "--password #{ENV['FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD']}"
61
+ auth_parts << "--team-id #{params[:asc_provider]}"
62
+ end
63
+
64
+ # Submits package and waits for processing using `xcrun notarytool submit --wait`
65
+ submit_parts = [
66
+ "xcrun notarytool submit",
67
+ (compressed_package_path || package_path).shellescape,
68
+ "--output-format json",
69
+ "--wait"
70
+ ] + auth_parts
71
+
72
+ submit_command = submit_parts.join(' ')
73
+ submit_response = Actions.sh(
74
+ submit_command,
75
+ log: verbose,
76
+ error_callback: lambda { |msg|
77
+ UI.error("Error polling for notarization info: #{msg}")
78
+ }
79
+ )
80
+
81
+ notarization_info = JSON.parse(submit_response)
82
+
83
+ # Staple
84
+ case notarization_info['status']
85
+ when 'Accepted'
86
+ submission_id = notarization_info["id"]
87
+ UI.success("Successfully uploaded package to notarization service with request identifier #{submission_id}")
88
+
89
+ UI.message('Stapling package')
90
+ self.staple(package_path, verbose)
91
+
92
+ UI.success("Successfully notarized and stapled package")
93
+ when 'Invalid'
94
+ UI.user_error!("Could not notarize package with message '#{notarization_info['statusSummary']}'")
95
+ else
96
+ UI.crash!("Could not notarize package with status '#{notarization_info['status']}'")
97
+ end
98
+ ensure
99
+ temp_file.delete if temp_file
100
+ end
101
+
102
+ def self.altool(params, package_path, bundle_id, try_early_stapling, print_log, verbose, api_key_path, compressed_package_path)
33
103
  UI.message('Uploading package to notarization service, might take a while')
34
104
 
35
105
  notarization_upload_command = "xcrun altool --notarize-app -t osx -f \"#{compressed_package_path || package_path}\" --primary-bundle-id #{bundle_id} --output-format xml"
@@ -125,7 +195,7 @@ module Fastlane
125
195
 
126
196
  def self.staple(package_path, verbose)
127
197
  Actions.sh(
128
- "xcrun stapler staple \"#{package_path}\"",
198
+ "xcrun stapler staple #{package_path.shellescape}",
129
199
  log: verbose
130
200
  )
131
201
  end
@@ -180,6 +250,12 @@ module Fastlane
180
250
  verify_block: proc do |value|
181
251
  UI.user_error!("Could not find package at '#{value}'") unless File.exist?(value)
182
252
  end),
253
+ FastlaneCore::ConfigItem.new(key: :use_notarytool,
254
+ env_name: 'FL_NOTARIZE_USE_NOTARYTOOL',
255
+ description: 'Whether to `xcrun notarytool` or `xcrun altool`',
256
+ default_value: Helper.mac? && Helper.xcode_at_least?("13.0"), # Notary tool added in Xcode 13
257
+ default_value_dynamic: true,
258
+ type: Boolean),
183
259
  FastlaneCore::ConfigItem.new(key: :try_early_stapling,
184
260
  env_name: 'FL_NOTARIZE_TRY_EARLY_STAPLING',
185
261
  description: 'Whether to try early stapling while the notarization request is in progress',
@@ -9,7 +9,7 @@ module Fastlane
9
9
  ]
10
10
 
11
11
  if params[:tag]
12
- command << "refs/tags/#{params[:tag]}"
12
+ command << "refs/tags/#{params[:tag].shellescape}"
13
13
  else
14
14
  command << '--tags'
15
15
  end