fastlane 2.196.0 → 2.212.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (211) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +1 -1
  3. data/README.md +88 -81
  4. data/cert/lib/cert/runner.rb +19 -8
  5. data/deliver/lib/assets/ScreenshotsHelp +29 -6
  6. data/deliver/lib/deliver/app_screenshot.rb +30 -4
  7. data/deliver/lib/deliver/app_screenshot_iterator.rb +1 -1
  8. data/deliver/lib/deliver/options.rb +6 -2
  9. data/deliver/lib/deliver/runner.rb +88 -24
  10. data/deliver/lib/deliver/submit_for_review.rb +25 -3
  11. data/deliver/lib/deliver/upload_price_tier.rb +3 -1
  12. data/deliver/lib/deliver/upload_screenshots.rb +2 -2
  13. data/fastlane/lib/assets/AppfileTemplate +1 -1
  14. data/fastlane/lib/assets/AppfileTemplate.swift +1 -1
  15. data/fastlane/lib/fastlane/actions/badge.rb +1 -1
  16. data/fastlane/lib/fastlane/actions/changelog_from_git_commits.rb +1 -1
  17. data/fastlane/lib/fastlane/actions/danger.rb +14 -0
  18. data/fastlane/lib/fastlane/actions/docs/build_app.md +5 -5
  19. data/fastlane/lib/fastlane/actions/docs/capture_android_screenshots.md +19 -2
  20. data/fastlane/lib/fastlane/actions/docs/frame_screenshots.md +1 -1
  21. data/fastlane/lib/fastlane/actions/docs/run_tests.md +1 -1
  22. data/fastlane/lib/fastlane/actions/docs/upload_to_app_store.md.erb +1 -1
  23. data/fastlane/lib/fastlane/actions/download_dsyms.rb +62 -46
  24. data/fastlane/lib/fastlane/actions/ensure_git_status_clean.rb +15 -4
  25. data/fastlane/lib/fastlane/actions/ensure_xcode_version.rb +1 -1
  26. data/fastlane/lib/fastlane/actions/get_push_certificate.rb +1 -1
  27. data/fastlane/lib/fastlane/actions/get_version_number.rb +8 -3
  28. data/fastlane/lib/fastlane/actions/git_commit.rb +4 -6
  29. data/fastlane/lib/fastlane/actions/import_certificate.rb +1 -1
  30. data/fastlane/lib/fastlane/actions/notarize.rb +29 -11
  31. data/fastlane/lib/fastlane/actions/pod_lib_lint.rb +1 -1
  32. data/fastlane/lib/fastlane/actions/pod_push.rb +19 -1
  33. data/fastlane/lib/fastlane/actions/read_podspec.rb +1 -1
  34. data/fastlane/lib/fastlane/actions/run_tests.rb +19 -9
  35. data/fastlane/lib/fastlane/actions/set_github_release.rb +11 -5
  36. data/fastlane/lib/fastlane/actions/setup_ci.rb +13 -4
  37. data/fastlane/lib/fastlane/actions/trainer.rb +49 -0
  38. data/fastlane/lib/fastlane/actions/update_code_signing_settings.rb +31 -4
  39. data/fastlane/lib/fastlane/actions/update_info_plist.rb +1 -1
  40. data/fastlane/lib/fastlane/actions/update_project_provisioning.rb +10 -1
  41. data/fastlane/lib/fastlane/actions/upload_symbols_to_sentry.rb +1 -1
  42. data/fastlane/lib/fastlane/actions/verify_build.rb +1 -1
  43. data/fastlane/lib/fastlane/actions/xcode_install.rb +5 -1
  44. data/fastlane/lib/fastlane/actions/xcode_select.rb +1 -1
  45. data/fastlane/lib/fastlane/actions/xcodebuild.rb +8 -2
  46. data/fastlane/lib/fastlane/actions/xcodes.rb +152 -0
  47. data/fastlane/lib/fastlane/actions/xcov.rb +5 -0
  48. data/fastlane/lib/fastlane/actions/xcversion.rb +17 -7
  49. data/fastlane/lib/fastlane/cli_tools_distributor.rb +5 -0
  50. data/fastlane/lib/fastlane/commands_generator.rb +2 -1
  51. data/fastlane/lib/fastlane/documentation/docs_generator.rb +17 -12
  52. data/fastlane/lib/fastlane/fast_file.rb +18 -5
  53. data/fastlane/lib/fastlane/features.rb +3 -0
  54. data/fastlane/lib/fastlane/helper/xcodebuild_formatter_helper.rb +9 -0
  55. data/fastlane/lib/fastlane/helper/xcodes_helper.rb +28 -0
  56. data/fastlane/lib/fastlane/helper/xcversion_helper.rb +0 -9
  57. data/fastlane/lib/fastlane/lane_manager.rb +1 -1
  58. data/fastlane/lib/fastlane/plugins/template/%gem_name%.gemspec.erb +1 -1
  59. data/fastlane/lib/fastlane/plugins/template/.rubocop.yml +5 -1
  60. data/fastlane/lib/fastlane/setup/setup_ios.rb +1 -1
  61. data/fastlane/lib/fastlane/swift_fastlane_api_generator.rb +1 -1
  62. data/fastlane/lib/fastlane/swift_lane_manager.rb +11 -3
  63. data/fastlane/lib/fastlane/swift_runner_upgrader.rb +54 -1
  64. data/fastlane/lib/fastlane/tools.rb +18 -1
  65. data/fastlane/lib/fastlane/version.rb +1 -1
  66. data/fastlane/swift/Actions.swift +1 -1
  67. data/fastlane/swift/Appfile.swift +2 -2
  68. data/fastlane/swift/ArgumentProcessor.swift +1 -1
  69. data/fastlane/swift/Atomic.swift +150 -0
  70. data/fastlane/swift/ControlCommand.swift +1 -1
  71. data/fastlane/swift/Deliverfile.swift +2 -2
  72. data/fastlane/swift/DeliverfileProtocol.swift +8 -4
  73. data/fastlane/swift/Fastlane.swift +570 -239
  74. data/fastlane/swift/FastlaneSwiftRunner/FastlaneSwiftRunner.xcodeproj/project.pbxproj +30 -20
  75. data/fastlane/swift/FastlaneSwiftRunner/FastlaneSwiftRunner.xcodeproj/xcshareddata/xcschemes/FastlaneRunner.xcscheme +1 -1
  76. data/fastlane/swift/Gymfile.swift +2 -2
  77. data/fastlane/swift/GymfileProtocol.swift +20 -8
  78. data/fastlane/swift/LaneFileProtocol.swift +2 -2
  79. data/fastlane/swift/MainProcess.swift +3 -3
  80. data/fastlane/swift/Matchfile.swift +2 -2
  81. data/fastlane/swift/MatchfileProtocol.swift +21 -5
  82. data/fastlane/swift/OptionalConfigValue.swift +1 -1
  83. data/fastlane/swift/Plugins.swift +1 -1
  84. data/fastlane/swift/Precheckfile.swift +2 -2
  85. data/fastlane/swift/PrecheckfileProtocol.swift +3 -3
  86. data/fastlane/swift/RubyCommand.swift +1 -1
  87. data/fastlane/swift/RubyCommandable.swift +1 -1
  88. data/fastlane/swift/Runner.swift +13 -9
  89. data/fastlane/swift/RunnerArgument.swift +1 -1
  90. data/fastlane/swift/Scanfile.swift +2 -2
  91. data/fastlane/swift/ScanfileProtocol.swift +31 -11
  92. data/fastlane/swift/Screengrabfile.swift +2 -2
  93. data/fastlane/swift/ScreengrabfileProtocol.swift +3 -3
  94. data/fastlane/swift/Snapshotfile.swift +2 -2
  95. data/fastlane/swift/SnapshotfileProtocol.swift +12 -8
  96. data/fastlane/swift/SocketClient.swift +9 -5
  97. data/fastlane/swift/SocketClientDelegateProtocol.swift +2 -2
  98. data/fastlane/swift/SocketResponse.swift +1 -1
  99. data/fastlane/swift/formatting/Brewfile.lock.json +46 -23
  100. data/fastlane/swift/main.swift +1 -1
  101. data/fastlane/swift/upgrade_manifest.json +1 -1
  102. data/fastlane_core/README.md +1 -0
  103. data/fastlane_core/lib/fastlane_core/cert_checker.rb +79 -17
  104. data/fastlane_core/lib/fastlane_core/device_manager.rb +5 -1
  105. data/fastlane_core/lib/fastlane_core/ipa_file_analyser.rb +10 -5
  106. data/fastlane_core/lib/fastlane_core/itunes_transporter.rb +409 -26
  107. data/fastlane_core/lib/fastlane_core/keychain_importer.rb +1 -0
  108. data/fastlane_core/lib/fastlane_core/project.rb +19 -2
  109. data/fastlane_core/lib/fastlane_core/ui/fastlane_runner.rb +7 -0
  110. data/fastlane_core/lib/fastlane_core/ui/implementations/shell.rb +4 -2
  111. data/frameit/lib/frameit/device.rb +1 -1
  112. data/frameit/lib/frameit/device_types.rb +9 -0
  113. data/frameit/lib/frameit/editor.rb +16 -18
  114. data/frameit/lib/frameit/frame_downloader.rb +1 -1
  115. data/frameit/lib/frameit/trim_box.rb +6 -0
  116. data/gym/lib/gym/generators/build_command_generator.rb +70 -23
  117. data/gym/lib/gym/options.rb +30 -5
  118. data/match/lib/match/change_password.rb +2 -0
  119. data/match/lib/match/commands_generator.rb +2 -1
  120. data/match/lib/match/encryption/openssl.rb +1 -1
  121. data/match/lib/match/encryption.rb +3 -0
  122. data/match/lib/match/generator.rb +1 -0
  123. data/match/lib/match/importer.rb +10 -1
  124. data/match/lib/match/migrate.rb +4 -3
  125. data/match/lib/match/module.rb +54 -2
  126. data/match/lib/match/nuke.rb +114 -47
  127. data/match/lib/match/options.rb +22 -1
  128. data/match/lib/match/runner.rb +25 -6
  129. data/match/lib/match/setup.rb +1 -1
  130. data/match/lib/match/spaceship_ensure.rb +5 -2
  131. data/match/lib/match/storage/gitlab/client.rb +102 -0
  132. data/match/lib/match/storage/gitlab/secure_file.rb +65 -0
  133. data/match/lib/match/storage/gitlab_secure_files.rb +182 -0
  134. data/match/lib/match/storage/google_cloud_storage.rb +7 -6
  135. data/match/lib/match/storage/s3_storage.rb +3 -3
  136. data/match/lib/match/storage.rb +4 -0
  137. data/match/lib/match/table_printer.rb +2 -1
  138. data/match/lib/match/utils.rb +15 -2
  139. data/pem/lib/pem/manager.rb +30 -7
  140. data/pem/lib/pem/options.rb +9 -0
  141. data/pilot/lib/pilot/build_manager.rb +34 -14
  142. data/pilot/lib/pilot/options.rb +6 -1
  143. data/scan/lib/scan/detect_values.rb +6 -0
  144. data/scan/lib/scan/error_handler.rb +9 -0
  145. data/scan/lib/scan/options.rb +49 -9
  146. data/scan/lib/scan/runner.rb +171 -25
  147. data/scan/lib/scan/test_command_generator.rb +65 -5
  148. data/sigh/lib/sigh/download_all.rb +14 -2
  149. data/sigh/lib/sigh/module.rb +3 -1
  150. data/sigh/lib/sigh/options.rb +5 -0
  151. data/sigh/lib/sigh/runner.rb +12 -2
  152. data/snapshot/lib/assets/SnapshotHelper.swift +3 -3
  153. data/snapshot/lib/snapshot/latest_os_version.rb +2 -5
  154. data/snapshot/lib/snapshot/options.rb +24 -8
  155. data/snapshot/lib/snapshot/reports_generator.rb +1 -0
  156. data/snapshot/lib/snapshot/simulator_launchers/simulator_launcher_base.rb +10 -3
  157. data/snapshot/lib/snapshot/test_command_generator.rb +37 -2
  158. data/spaceship/lib/spaceship/client.rb +71 -40
  159. data/spaceship/lib/spaceship/commands_generator.rb +1 -1
  160. data/spaceship/lib/spaceship/connect_api/api_client.rb +10 -5
  161. data/spaceship/lib/spaceship/connect_api/models/actor.rb +26 -0
  162. data/spaceship/lib/spaceship/connect_api/models/app.rb +52 -6
  163. data/spaceship/lib/spaceship/connect_api/models/app_info.rb +1 -0
  164. data/spaceship/lib/spaceship/connect_api/models/app_info_localization.rb +5 -0
  165. data/spaceship/lib/spaceship/connect_api/models/app_screenshot_set.rb +7 -0
  166. data/spaceship/lib/spaceship/connect_api/models/app_store_version.rb +1 -1
  167. data/spaceship/lib/spaceship/connect_api/models/app_store_version_localization.rb +27 -10
  168. data/spaceship/lib/spaceship/connect_api/models/build.rb +4 -2
  169. data/spaceship/lib/spaceship/connect_api/models/build_bundle.rb +68 -0
  170. data/spaceship/lib/spaceship/connect_api/models/build_bundle_file_sizes.rb +34 -0
  171. data/spaceship/lib/spaceship/connect_api/models/build_delivery.rb +2 -1
  172. data/spaceship/lib/spaceship/connect_api/models/certificate.rb +4 -0
  173. data/spaceship/lib/spaceship/connect_api/models/device.rb +47 -4
  174. data/spaceship/lib/spaceship/connect_api/models/profile.rb +4 -0
  175. data/spaceship/lib/spaceship/connect_api/models/resolution_center_message.rb +29 -0
  176. data/spaceship/lib/spaceship/connect_api/models/resolution_center_thread.rb +67 -0
  177. data/spaceship/lib/spaceship/connect_api/models/review_rejection.rb +19 -0
  178. data/spaceship/lib/spaceship/connect_api/models/review_submission.rb +86 -0
  179. data/spaceship/lib/spaceship/connect_api/models/review_submission_item.rb +40 -0
  180. data/spaceship/lib/spaceship/connect_api/models/user.rb +5 -0
  181. data/spaceship/lib/spaceship/connect_api/provisioning/provisioning.rb +19 -0
  182. data/spaceship/lib/spaceship/connect_api/response.rb +23 -6
  183. data/spaceship/lib/spaceship/connect_api/testflight/testflight.rb +33 -2
  184. data/spaceship/lib/spaceship/connect_api/token.rb +5 -2
  185. data/spaceship/lib/spaceship/connect_api/tunes/tunes.rb +124 -8
  186. data/spaceship/lib/spaceship/connect_api.rb +9 -0
  187. data/spaceship/lib/spaceship/errors.rb +34 -0
  188. data/spaceship/lib/spaceship/globals.rb +9 -0
  189. data/spaceship/lib/spaceship/hashcash.rb +52 -0
  190. data/spaceship/lib/spaceship/portal/certificate.rb +4 -3
  191. data/spaceship/lib/spaceship/spaceauth_runner.rb +1 -1
  192. data/spaceship/lib/spaceship/tunes/app_ratings.rb +6 -6
  193. data/spaceship/lib/spaceship/tunes/iap_families.rb +1 -1
  194. data/spaceship/lib/spaceship/tunes/tunes.rb +0 -1
  195. data/spaceship/lib/spaceship/tunes/tunes_client.rb +79 -21
  196. data/spaceship/lib/spaceship/two_step_or_factor_client.rb +11 -3
  197. data/spaceship/lib/spaceship.rb +1 -0
  198. data/supply/lib/supply/client.rb +2 -7
  199. data/supply/lib/supply/options.rb +8 -0
  200. data/supply/lib/supply/uploader.rb +6 -2
  201. data/trainer/lib/assets/junit.xml.erb +28 -0
  202. data/trainer/lib/trainer/commands_generator.rb +51 -0
  203. data/trainer/lib/trainer/junit_generator.rb +31 -0
  204. data/trainer/lib/trainer/module.rb +10 -0
  205. data/trainer/lib/trainer/options.rb +66 -0
  206. data/trainer/lib/trainer/test_parser.rb +398 -0
  207. data/trainer/lib/trainer/xcresult.rb +403 -0
  208. data/trainer/lib/trainer.rb +7 -0
  209. metadata +49 -24
  210. data/spaceship/lib/spaceship/connect_api/testflight/.testflight.rb.swp +0 -0
  211. data/spaceship/lib/spaceship/tunes/user_detail.rb +0 -15
@@ -0,0 +1,182 @@
1
+ require 'fastlane_core/command_executor'
2
+ require 'fastlane_core/configuration/configuration'
3
+ require 'net/http/post/multipart'
4
+
5
+ require_relative './gitlab/client'
6
+ require_relative './gitlab/secure_file'
7
+
8
+ require_relative '../options'
9
+ require_relative '../module'
10
+ require_relative '../spaceship_ensure'
11
+ require_relative './interface'
12
+
13
+ module Match
14
+ module Storage
15
+ # Store the code signing identities in GitLab Secure Files
16
+ class GitLabSecureFiles < Interface
17
+ attr_reader :gitlab_client
18
+ attr_reader :project_id
19
+ attr_reader :readonly
20
+ attr_reader :username
21
+ attr_reader :team_id
22
+ attr_reader :team_name
23
+ attr_reader :api_key_path
24
+ attr_reader :api_key
25
+
26
+ def self.configure(params)
27
+ api_v4_url = params[:api_v4_url] || ENV['CI_API_V4_URL'] || 'https://gitlab.com/api/v4'
28
+ project_id = params[:gitlab_project] || ENV['GITLAB_PROJECT'] || ENV['CI_PROJECT_ID']
29
+ job_token = params[:job_token] || ENV['CI_JOB_TOKEN']
30
+ private_token = params[:private_token] || ENV['PRIVATE_TOKEN']
31
+
32
+ if params[:git_url].to_s.length > 0
33
+ UI.important("Looks like you still define a `git_url` somewhere, even though")
34
+ UI.important("you use GitLab Secure Files. You can remove the `git_url`")
35
+ UI.important("from your Matchfile and Fastfile")
36
+ UI.message("The above is just a warning, fastlane will continue as usual now...")
37
+ end
38
+
39
+ return self.new(
40
+ api_v4_url: api_v4_url,
41
+ project_id: project_id,
42
+ job_token: job_token,
43
+ private_token: private_token,
44
+ readonly: params[:readonly],
45
+ username: params[:username],
46
+ team_id: params[:team_id],
47
+ team_name: params[:team_name],
48
+ api_key_path: params[:api_key_path],
49
+ api_key: params[:api_key]
50
+ )
51
+ end
52
+
53
+ def initialize(api_v4_url: nil,
54
+ project_id: nil,
55
+ job_token: nil,
56
+ private_token: nil,
57
+ readonly: nil,
58
+ username: nil,
59
+ team_id: nil,
60
+ team_name: nil,
61
+ api_key_path: nil,
62
+ api_key: nil)
63
+
64
+ @readonly = readonly
65
+ @username = username
66
+ @team_id = team_id
67
+ @team_name = team_name
68
+ @api_key_path = api_key_path
69
+ @api_key = api_key
70
+
71
+ @job_token = job_token
72
+ @private_token = private_token
73
+ @api_v4_url = api_v4_url
74
+ @project_id = project_id
75
+ @gitlab_client = GitLab::Client.new(job_token: job_token, private_token: private_token, project_id: project_id, api_v4_url: api_v4_url)
76
+
77
+ UI.message("Initializing match for GitLab project #{@project_id}")
78
+ end
79
+
80
+ # To make debugging easier, we have a custom exception here
81
+ def prefixed_working_directory
82
+ # We fall back to "*", which means certificates and profiles
83
+ # from all teams that use this bucket would be installed. This is not ideal, but
84
+ # unless the user provides a `team_id`, we can't know which one to use
85
+ # This only happens if `readonly` is activated, and no `team_id` was provided
86
+ @_folder_prefix ||= currently_used_team_id
87
+ if @_folder_prefix.nil?
88
+ # We use a `@_folder_prefix` variable, to keep state between multiple calls of this
89
+ # method, as the value won't change. This way the warning is only printed once
90
+ UI.important("Looks like you run `match` in `readonly` mode, and didn't provide a `team_id`. This will still work, however it is recommended to provide a `team_id` in your Appfile or Matchfile")
91
+ @_folder_prefix = "*"
92
+ end
93
+ return File.join(working_directory, @_folder_prefix)
94
+ end
95
+
96
+ def download
97
+ # Check if we already have a functional working_directory
98
+ return if @working_directory
99
+
100
+ # No existing working directory, creating a new one now
101
+ self.working_directory = Dir.mktmpdir
102
+
103
+ @gitlab_client.files.each do |secure_file|
104
+ secure_file.download(self.working_directory)
105
+ end
106
+
107
+ UI.verbose("Successfully downloaded all Secure Files from GitLab to #{self.working_directory}")
108
+ end
109
+
110
+ def currently_used_team_id
111
+ if self.readonly
112
+ # In readonly mode, we still want to see if the user provided a team_id
113
+ # see `prefixed_working_directory` comments for more details
114
+ return self.team_id
115
+ else
116
+ UI.user_error!("The `team_id` option is required. fastlane cannot automatically determine portal team id via the App Store Connect API (yet)") if self.team_id.to_s.empty?
117
+
118
+ spaceship = SpaceshipEnsure.new(self.username, self.team_id, self.team_name, api_token)
119
+ return spaceship.team_id
120
+ end
121
+ end
122
+
123
+ def api_token
124
+ api_token = Spaceship::ConnectAPI::Token.from(hash: self.api_key, filepath: self.api_key_path)
125
+ api_token ||= Spaceship::ConnectAPI.token
126
+ return api_token
127
+ end
128
+
129
+ # Returns a short string describing + identifing the current
130
+ # storage backend. This will be printed when nuking a storage
131
+ def human_readable_description
132
+ "GitLab Secure Files Storage [#{self.project_id}]"
133
+ end
134
+
135
+ def upload_files(files_to_upload: [], custom_message: nil)
136
+ # `files_to_upload` is an array of files that need to be uploaded to GitLab Secure Files
137
+ # Those doesn't mean they're new, it might just be they're changed
138
+ # Either way, we'll upload them using the same technique
139
+
140
+ files_to_upload.each do |current_file|
141
+ # Go from
142
+ # "/var/folders/px/bz2kts9n69g8crgv4jpjh6b40000gn/T/d20181026-96528-1av4gge/profiles/development/Development_me.mobileprovision"
143
+ # to
144
+ # "profiles/development/Development_me.mobileprovision"
145
+ #
146
+
147
+ # We also remove the trailing `/`
148
+ target_file = current_file.gsub(self.working_directory + "/", "")
149
+ UI.verbose("Uploading '#{target_file}' to GitLab Secure Files...")
150
+ @gitlab_client.upload_file(current_file, target_file)
151
+ end
152
+ end
153
+
154
+ def delete_files(files_to_delete: [], custom_message: nil)
155
+ files_to_delete.each do |current_file|
156
+ target_path = current_file.gsub(self.working_directory + "/", "")
157
+
158
+ secure_file = @gitlab_client.find_file_by_name(target_path)
159
+ UI.message("Deleting '#{target_path}' from GitLab Secure Files...")
160
+ secure_file.delete
161
+ end
162
+ end
163
+
164
+ def skip_docs
165
+ true
166
+ end
167
+
168
+ def list_files(file_name: "", file_ext: "")
169
+ Dir[File.join(working_directory, self.team_id, "**", file_name, "*.#{file_ext}")]
170
+ end
171
+
172
+ # Implement this for the `fastlane match init` command
173
+ # This method must return the content of the Matchfile
174
+ # that should be generated
175
+ def generate_matchfile_content(template: nil)
176
+ project = UI.input("What is your GitLab Project (i.e. gitlab-org/gitlab): ")
177
+
178
+ return "gitlab_project(\"#{project}\")"
179
+ end
180
+ end
181
+ end
182
+ end
@@ -48,7 +48,8 @@ module Match
48
48
  team_id: params[:team_id],
49
49
  team_name: params[:team_name],
50
50
  api_key_path: params[:api_key_path],
51
- api_key: params[:api_key]
51
+ api_key: params[:api_key],
52
+ skip_google_cloud_account_confirmation: params[:skip_google_cloud_account_confirmation]
52
53
  )
53
54
  end
54
55
 
@@ -62,7 +63,8 @@ module Match
62
63
  team_id: nil,
63
64
  team_name: nil,
64
65
  api_key_path: nil,
65
- api_key: nil)
66
+ api_key: nil,
67
+ skip_google_cloud_account_confirmation: nil)
66
68
  @type = type if type
67
69
  @platform = platform if platform
68
70
  @google_cloud_project_id = google_cloud_project_id if google_cloud_project_id
@@ -76,8 +78,7 @@ module Match
76
78
  @api_key_path = api_key_path
77
79
  @api_key = api_key
78
80
 
79
- @google_cloud_keys_file = ensure_keys_file_exists(google_cloud_keys_file, google_cloud_project_id)
80
-
81
+ @google_cloud_keys_file = ensure_keys_file_exists(google_cloud_keys_file, google_cloud_project_id, skip_google_cloud_account_confirmation)
81
82
  if self.google_cloud_keys_file.to_s.length > 0
82
83
  # Extract the Project ID from the `JSON` file
83
84
  # so the user doesn't have to provide it manually
@@ -223,7 +224,7 @@ module Match
223
224
 
224
225
  # This method will make sure the keys file exists
225
226
  # If it's missing, it will help the user set things up
226
- def ensure_keys_file_exists(google_cloud_keys_file, google_cloud_project_id)
227
+ def ensure_keys_file_exists(google_cloud_keys_file, google_cloud_project_id, skip_google_cloud_account_confirmation)
227
228
  if google_cloud_keys_file && File.exist?(google_cloud_keys_file)
228
229
  return google_cloud_keys_file
229
230
  end
@@ -251,7 +252,7 @@ module Match
251
252
  # we can continue and ask the user if they want to use a keys file.
252
253
  end
253
254
 
254
- if application_default_keys && UI.confirm("Do you want to use this system's Google Cloud application default keys?")
255
+ if application_default_keys && (skip_google_cloud_account_confirmation || UI.confirm("Do you want to use this system's Google Cloud application default keys?"))
255
256
  return nil
256
257
  end
257
258
  end
@@ -168,10 +168,10 @@ module Match
168
168
  private
169
169
 
170
170
  def s3_object_path(file_name)
171
- santized = sanitize_file_name(file_name)
172
- return santized if santized.start_with?(s3_object_prefix)
171
+ sanitized = sanitize_file_name(file_name)
172
+ return sanitized if sanitized.start_with?(s3_object_prefix)
173
173
 
174
- s3_object_prefix + santized
174
+ s3_object_prefix + sanitized
175
175
  end
176
176
 
177
177
  def strip_s3_object_prefix(object_path)
@@ -2,6 +2,7 @@ require_relative 'storage/interface'
2
2
  require_relative 'storage/git_storage'
3
3
  require_relative 'storage/google_cloud_storage'
4
4
  require_relative 'storage/s3_storage'
5
+ require_relative 'storage/gitlab_secure_files'
5
6
 
6
7
  module Match
7
8
  module Storage
@@ -16,6 +17,9 @@ module Match
16
17
  },
17
18
  "s3" => lambda { |params|
18
19
  return Storage::S3Storage.configure(params)
20
+ },
21
+ "gitlab_secure_files" => lambda { |params|
22
+ return Storage::GitLabSecureFiles.configure(params)
19
23
  }
20
24
  }
21
25
  end
@@ -33,7 +33,8 @@ module Match
33
33
  Utils.environment_variable_name(app_identifier: app_identifier, type: type, platform: platform) => "Profile UUID",
34
34
  Utils.environment_variable_name_profile_name(app_identifier: app_identifier, type: type, platform: platform) => "Profile Name",
35
35
  Utils.environment_variable_name_profile_path(app_identifier: app_identifier, type: type, platform: platform) => "Profile Path",
36
- Utils.environment_variable_name_team_id(app_identifier: app_identifier, type: type, platform: platform) => "Development Team ID"
36
+ Utils.environment_variable_name_team_id(app_identifier: app_identifier, type: type, platform: platform) => "Development Team ID",
37
+ Utils.environment_variable_name_certificate_name(app_identifier: app_identifier, type: type, platform: platform) => "Certificate Name"
37
38
  }.each do |env_key, name|
38
39
  rows << [name, env_key, ENV[env_key]]
39
40
  end
@@ -31,8 +31,21 @@ module Match
31
31
  (base_environment_variable_name(app_identifier: app_identifier, type: type, platform: platform) + ["profile-path"]).join("_")
32
32
  end
33
33
 
34
- def self.get_cert_info(cer_certificate_path)
35
- cert = OpenSSL::X509::Certificate.new(File.binread(cer_certificate_path))
34
+ def self.environment_variable_name_certificate_name(app_identifier: nil, type: nil, platform: :ios)
35
+ (base_environment_variable_name(app_identifier: app_identifier, type: type, platform: platform) + ["certificate-name"]).join("_")
36
+ end
37
+
38
+ def self.get_cert_info(cer_certificate)
39
+ # can receive a certificate path or the file data
40
+ begin
41
+ if File.exist?(cer_certificate)
42
+ cer_certificate = File.binread(cer_certificate)
43
+ end
44
+ rescue ArgumentError
45
+ # cert strings have null bytes; suppressing output
46
+ end
47
+
48
+ cert = OpenSSL::X509::Certificate.new(cer_certificate)
36
49
 
37
50
  # openssl output:
38
51
  # subject= /UID={User ID}/CN={Certificate Name}/OU={Certificate User}/O={Organisation}/C={Country}
@@ -18,7 +18,13 @@ module PEM
18
18
 
19
19
  if existing_certificate
20
20
  remaining_days = (existing_certificate.expires - Time.now) / 60 / 60 / 24
21
- UI.message("Existing push notification profile for '#{existing_certificate.owner_name}' is valid for #{remaining_days.round} more days.")
21
+
22
+ display_platform = ''
23
+ unless PEM.config[:website_push]
24
+ display_platform = "#{PEM.config[:platform]} "
25
+ end
26
+
27
+ UI.message("Existing #{display_platform}push notification profile for '#{existing_certificate.owner_name}' is valid for #{remaining_days.round} more days.")
22
28
  if remaining_days > PEM.config[:active_days_limit]
23
29
  if PEM.config[:force]
24
30
  UI.success("You already have an existing push certificate, but a new one will be created since the --force option has been set.")
@@ -59,7 +65,7 @@ module PEM
59
65
 
60
66
  x509_certificate = cert.download
61
67
 
62
- filename_base = PEM.config[:pem_name] || "#{certificate_type}_#{PEM.config[:app_identifier]}"
68
+ filename_base = PEM.config[:pem_name] || "#{certificate_type}_#{PEM.config[:app_identifier]}_#{PEM.config[:platform]}"
63
69
  filename_base = File.basename(filename_base, ".pem") # strip off the .pem if it was provided.
64
70
 
65
71
  output_path = File.expand_path(PEM.config[:output_path])
@@ -75,7 +81,7 @@ module PEM
75
81
  p12_cert_path = File.join(output_path, "#{filename_base}.p12")
76
82
  p12_password = PEM.config[:p12_password] == "" ? nil : PEM.config[:p12_password]
77
83
  p12 = OpenSSL::PKCS12.create(p12_password, certificate_type, pkey, x509_certificate)
78
- File.write(p12_cert_path, p12.to_der)
84
+ File.write(p12_cert_path, p12.to_der.force_encoding("UTF-8"))
79
85
  UI.message("p12 certificate: ".green + Pathname.new(p12_cert_path).realpath.to_s)
80
86
  end
81
87
 
@@ -86,12 +92,29 @@ module PEM
86
92
  end
87
93
 
88
94
  def certificate
89
- if PEM.config[:development]
90
- Spaceship.certificate.development_push
91
- elsif PEM.config[:website_push]
95
+ if PEM.config[:website_push]
92
96
  Spaceship.certificate.website_push
93
97
  else
94
- Spaceship.certificate.production_push
98
+ platform = PEM.config[:platform]
99
+ UI.user_error!('platform parameter is unspecified.') unless platform
100
+
101
+ case platform
102
+ when 'ios'
103
+ if PEM.config[:development]
104
+ Spaceship.certificate.development_push
105
+ else
106
+ Spaceship.certificate.production_push
107
+ end
108
+ when 'macos'
109
+ if PEM.config[:development]
110
+ Spaceship.certificate.mac_development_push
111
+ else
112
+ Spaceship.certificate.mac_production_push
113
+ end
114
+ else
115
+ UI.user_error!("Unsupported platform '#{platform}'. Supported platforms for development and production certificates are 'ios' & 'macos'")
116
+ end
117
+
95
118
  end
96
119
  end
97
120
 
@@ -1,5 +1,6 @@
1
1
  require 'fastlane_core/configuration/config_item'
2
2
  require 'credentials_manager/appfile_config'
3
+ require 'fastlane/helper/lane_helper'
3
4
 
4
5
  require_relative 'module'
5
6
 
@@ -10,6 +11,14 @@ module PEM
10
11
  user ||= CredentialsManager::AppfileConfig.try_fetch_value(:apple_id)
11
12
 
12
13
  [
14
+ FastlaneCore::ConfigItem.new(key: :platform,
15
+ description: "Set certificate's platform. Used for creation of production & development certificates. Supported platforms: ios, macos",
16
+ short_option: "-m",
17
+ env_name: "PEM_PLATFORM",
18
+ default_value: "ios",
19
+ verify_block: proc do |value|
20
+ UI.user_error!("The platform can only be ios or macos") unless ['ios', 'macos'].include?(value)
21
+ end),
13
22
  FastlaneCore::ConfigItem.new(key: :development,
14
23
  env_name: "PEM_DEVELOPMENT",
15
24
  description: "Renew the development push certificate instead of the production one",
@@ -18,6 +18,11 @@ module Pilot
18
18
 
19
19
  UI.user_error!("No ipa or pkg file given") if config[:ipa].nil? && config[:pkg].nil?
20
20
 
21
+ if config[:ipa] && config[:pkg]
22
+ UI.important("WARNING: Both `ipa` and `pkg` options are defined either explicitly or with default_value (build found in directory)")
23
+ UI.important("Uploading `ipa` is preferred by default. Set `app_platform` to `osx` to force uploading `pkg`")
24
+ end
25
+
21
26
  check_for_changelog_or_whats_new!(options)
22
27
 
23
28
  UI.success("Ready to upload new build to TestFlight (App: #{fetch_app_id})...")
@@ -25,24 +30,29 @@ module Pilot
25
30
  dir = Dir.mktmpdir
26
31
 
27
32
  platform = fetch_app_platform
28
- if options[:ipa]
33
+ ipa_path = options[:ipa]
34
+ if ipa_path && platform != 'osx'
35
+ asset_path = ipa_path
29
36
  package_path = FastlaneCore::IpaUploadPackageBuilder.new.generate(app_id: fetch_app_id,
30
- ipa_path: options[:ipa],
37
+ ipa_path: ipa_path,
31
38
  package_path: dir,
32
39
  platform: platform)
33
40
  else
41
+ pkg_path = options[:pkg]
42
+ asset_path = pkg_path
34
43
  package_path = FastlaneCore::PkgUploadPackageBuilder.new.generate(app_id: fetch_app_id,
35
- pkg_path: options[:pkg],
44
+ pkg_path: pkg_path,
36
45
  package_path: dir,
37
46
  platform: platform)
38
47
  end
39
48
 
40
49
  transporter = transporter_for_selected_team(options)
41
- result = transporter.upload(package_path: package_path)
50
+ result = transporter.upload(package_path: package_path, asset_path: asset_path, platform: platform)
42
51
 
43
52
  unless result
44
53
  transporter_errors = transporter.displayable_errors
45
- UI.user_error!("Error uploading ipa file: \n #{transporter_errors}")
54
+ file_type = platform == "osx" ? "pkg" : "ipa"
55
+ UI.user_error!("Error uploading #{file_type} file: \n #{transporter_errors}")
46
56
  end
47
57
 
48
58
  UI.success("Successfully uploaded the new binary to App Store Connect")
@@ -98,10 +108,10 @@ module Pilot
98
108
 
99
109
  def wait_for_build_processing_to_be_complete(return_when_build_appears = false)
100
110
  platform = fetch_app_platform
101
- if config[:ipa]
111
+ if config[:ipa] && platform != "osx" && !config[:distribute_only]
102
112
  app_version = FastlaneCore::IpaFileAnalyser.fetch_app_version(config[:ipa])
103
113
  app_build = FastlaneCore::IpaFileAnalyser.fetch_app_build(config[:ipa])
104
- elsif config[:pkg]
114
+ elsif config[:pkg] && !config[:distribute_only]
105
115
  app_version = FastlaneCore::PkgFileAnalyser.fetch_app_version(config[:pkg])
106
116
  app_build = FastlaneCore::PkgFileAnalyser.fetch_app_build(config[:pkg])
107
117
  else
@@ -147,7 +157,7 @@ module Pilot
147
157
  end
148
158
  end
149
159
  platform = Spaceship::ConnectAPI::Platform.map(fetch_app_platform)
150
- build ||= Spaceship::ConnectAPI::Build.all(app_id: app.id, version: app_version, build_number: build_number, sort: "-uploadedDate", platform: platform, limit: 1).first
160
+ build ||= Spaceship::ConnectAPI::Build.all(app_id: app.id, version: app_version, build_number: build_number, sort: "-uploadedDate", platform: platform, limit: Spaceship::ConnectAPI::Platform.size).first
151
161
  end
152
162
 
153
163
  # Verify the build has all the includes that we need
@@ -379,24 +389,34 @@ module Pilot
379
389
  def transporter_for_selected_team(options)
380
390
  # Use JWT auth
381
391
  api_token = Spaceship::ConnectAPI.token
392
+ api_key = if options[:api_key].nil? && !api_token.nil?
393
+ # Load api key info if user set api_key_path, not api_key
394
+ { key_id: api_token.key_id, issuer_id: api_token.issuer_id, key: api_token.key_raw }
395
+ elsif !options[:api_key].nil?
396
+ api_key = options[:api_key].transform_keys(&:to_sym).dup
397
+ # key is still base 64 style if api_key is loaded from option
398
+ api_key[:key] = Base64.decode64(api_key[:key]) if api_key[:is_key_content_base64]
399
+ api_key
400
+ end
401
+
382
402
  unless api_token.nil?
383
403
  api_token.refresh! if api_token.expired?
384
- return FastlaneCore::ItunesTransporter.new(nil, nil, false, nil, api_token.text)
404
+ return FastlaneCore::ItunesTransporter.new(nil, nil, false, nil, api_token.text, altool_compatible_command: true, api_key: api_key)
385
405
  end
386
406
 
387
407
  # Otherwise use username and password
388
408
  tunes_client = Spaceship::ConnectAPI.client ? Spaceship::ConnectAPI.client.tunes_client : nil
389
409
 
390
- generic_transporter = FastlaneCore::ItunesTransporter.new(options[:username], nil, false, options[:itc_provider])
410
+ generic_transporter = FastlaneCore::ItunesTransporter.new(options[:username], nil, false, options[:itc_provider], altool_compatible_command: true, api_key: api_key)
391
411
  return generic_transporter if options[:itc_provider] || tunes_client.nil?
392
412
  return generic_transporter unless tunes_client.teams.count > 1
393
413
 
394
414
  begin
395
- team = tunes_client.teams.find { |t| t['contentProvider']['contentProviderId'].to_s == tunes_client.team_id }
396
- name = team['contentProvider']['name']
415
+ team = tunes_client.teams.find { |t| t['providerId'].to_s == tunes_client.team_id }
416
+ name = team['name']
397
417
  provider_id = generic_transporter.provider_ids[name]
398
418
  UI.verbose("Inferred provider id #{provider_id} for team #{name}.")
399
- return FastlaneCore::ItunesTransporter.new(options[:username], nil, false, provider_id)
419
+ return FastlaneCore::ItunesTransporter.new(options[:username], nil, false, provider_id, altool_compatible_command: true, api_key: api_key)
400
420
  rescue => ex
401
421
  STDERR.puts(ex.to_s)
402
422
  UI.verbose("Couldn't infer a provider short name for team with id #{tunes_client.team_id} automatically: #{ex}. Proceeding without provider short name.")
@@ -410,7 +430,7 @@ module Pilot
410
430
  # This is where we could add a check to see if encryption is required and has been updated
411
431
  uploaded_build = set_export_compliance_if_needed(uploaded_build, options)
412
432
 
413
- if options[:groups] || options[:distribute_external]
433
+ if options[:submit_beta_review] && (options[:groups] || options[:distribute_external])
414
434
  if uploaded_build.ready_for_beta_submission?
415
435
  uploaded_build.post_beta_app_review_submission
416
436
  else
@@ -324,7 +324,12 @@ module Pilot
324
324
  env_name: "PILOT_REJECT_PREVIOUS_BUILD",
325
325
  description: "Expire previous if it's 'waiting for review'",
326
326
  is_string: false,
327
- default_value: false)
327
+ default_value: false),
328
+ FastlaneCore::ConfigItem.new(key: :submit_beta_review,
329
+ env_name: "PILOT_DISTRIBUTE_EXTERNAL",
330
+ description: "Send the build for a beta review",
331
+ type: Boolean,
332
+ default_value: true)
328
333
  ]
329
334
  end
330
335
  end
@@ -209,6 +209,12 @@ module Scan
209
209
 
210
210
  def self.detect_destination
211
211
  if Scan.config[:destination]
212
+ # No need to show below warnings message(s) for xcode13+, because
213
+ # Apple recommended to have destination in all xcodebuild commands
214
+ # otherwise, Apple will generate warnings in console logs
215
+ # see: https://github.com/fastlane/fastlane/issues/19579
216
+ return if Helper.xcode_at_least?("13.0")
217
+
212
218
  UI.important("It's not recommended to set the `destination` value directly")
213
219
  UI.important("Instead use the other options available in `fastlane scan --help`")
214
220
  UI.important("Using your value '#{Scan.config[:destination]}' for now")
@@ -27,6 +27,15 @@ module Scan
27
27
  print("If you are using zshell or another shell, make sure to edit the correct bash file.")
28
28
  print("For more information visit this stackoverflow answer:")
29
29
  print("https://stackoverflow.com/a/17031697/445598")
30
+ when /Testing failed on/
31
+ # This is important because xcbeautify and raw output will print:
32
+ # Testing failed on 'iPhone 13 Pro Max'
33
+ # when multiple devices are use.
34
+ # xcpretty hides this output so its not an issue with xcpretty.
35
+ # We need to catch "Testing failed on" before "Test failed"
36
+ # so that an error isn't raised.
37
+ # Raising an error prevents trainer from processing the xcresult
38
+ return
30
39
  when /Testing failed/
31
40
  UI.build_failure!("Error building the application. #{details}")
32
41
  when /Executed/, /Failing tests:/