fastlane 2.213.0 → 2.215.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +94 -94
  3. data/deliver/lib/deliver/app_screenshot.rb +13 -10
  4. data/deliver/lib/deliver/submit_for_review.rb +13 -0
  5. data/deliver/lib/deliver/upload_price_tier.rb +1 -1
  6. data/fastlane/lib/assets/custom_action_template.rb +18 -12
  7. data/fastlane/lib/fastlane/actions/docs/build_app.md +2 -2
  8. data/fastlane/lib/fastlane/actions/docs/capture_android_screenshots.md +8 -8
  9. data/fastlane/lib/fastlane/actions/docs/check_app_store_metadata.md +3 -3
  10. data/fastlane/lib/fastlane/actions/docs/get_push_certificate.md +2 -2
  11. data/fastlane/lib/fastlane/actions/docs/sync_code_signing.md +6 -4
  12. data/fastlane/lib/fastlane/actions/docs/upload_to_testflight.md +1 -1
  13. data/fastlane/lib/fastlane/actions/download_universal_apk_from_google_play.rb +124 -0
  14. data/fastlane/lib/fastlane/actions/ensure_xcode_version.rb +4 -0
  15. data/fastlane/lib/fastlane/helper/xcodes_helper.rb +0 -3
  16. data/fastlane/lib/fastlane/new_action.rb +1 -1
  17. data/fastlane/lib/fastlane/plugins/plugin_info_collector.rb +3 -3
  18. data/fastlane/lib/fastlane/plugins/template/%gem_name%.gemspec.erb +1 -2
  19. data/fastlane/lib/fastlane/plugins/template/.rubocop.yml +5 -1
  20. data/fastlane/lib/fastlane/plugins/template/lib/fastlane/plugin/%plugin_name%/helper/%plugin_name%_helper.rb.erb +1 -1
  21. data/fastlane/lib/fastlane/version.rb +2 -2
  22. data/fastlane/swift/Deliverfile.swift +1 -1
  23. data/fastlane/swift/DeliverfileProtocol.swift +1 -1
  24. data/fastlane/swift/Fastlane.swift +79 -4
  25. data/fastlane/swift/Gymfile.swift +1 -1
  26. data/fastlane/swift/GymfileProtocol.swift +1 -1
  27. data/fastlane/swift/Matchfile.swift +1 -1
  28. data/fastlane/swift/MatchfileProtocol.swift +5 -1
  29. data/fastlane/swift/Precheckfile.swift +1 -1
  30. data/fastlane/swift/PrecheckfileProtocol.swift +1 -1
  31. data/fastlane/swift/Scanfile.swift +1 -1
  32. data/fastlane/swift/ScanfileProtocol.swift +1 -1
  33. data/fastlane/swift/Screengrabfile.swift +1 -1
  34. data/fastlane/swift/ScreengrabfileProtocol.swift +1 -1
  35. data/fastlane/swift/Snapshotfile.swift +1 -1
  36. data/fastlane/swift/SnapshotfileProtocol.swift +1 -1
  37. data/fastlane/swift/formatting/Brewfile.lock.json +28 -18
  38. data/fastlane_core/lib/fastlane_core/cert_checker.rb +1 -1
  39. data/frameit/lib/frameit/device_types.rb +1 -1
  40. data/gym/lib/gym/generators/build_command_generator.rb +1 -1
  41. data/match/lib/match/change_password.rb +3 -9
  42. data/match/lib/match/commands_generator.rb +3 -6
  43. data/match/lib/match/encryption.rb +1 -1
  44. data/match/lib/match/importer.rb +3 -31
  45. data/match/lib/match/migrate.rb +6 -2
  46. data/match/lib/match/nuke.rb +3 -25
  47. data/match/lib/match/options.rb +7 -0
  48. data/match/lib/match/runner.rb +5 -32
  49. data/match/lib/match/setup.rb +1 -1
  50. data/match/lib/match/storage/gitlab/client.rb +40 -14
  51. data/match/lib/match/storage/gitlab_secure_files.rb +7 -2
  52. data/match/lib/match/storage/s3_storage.rb +6 -0
  53. data/match/lib/match/storage.rb +56 -5
  54. data/scan/lib/scan/test_command_generator.rb +1 -1
  55. data/snapshot/lib/snapshot/test_command_generator.rb +1 -1
  56. data/spaceship/lib/spaceship/client.rb +0 -1
  57. data/spaceship/lib/spaceship/connect_api/api_client.rb +19 -1
  58. data/spaceship/lib/spaceship/connect_api/models/app.rb +3 -2
  59. data/spaceship/lib/spaceship/connect_api/models/app_info.rb +15 -9
  60. data/spaceship/lib/spaceship/connect_api/models/app_screenshot_set.rb +5 -0
  61. data/spaceship/lib/spaceship/connect_api/models/app_store_version.rb +1 -0
  62. data/spaceship/lib/spaceship/connect_api/models/beta_group.rb +3 -1
  63. data/spaceship/lib/spaceship/connect_api/models/user.rb +38 -0
  64. data/spaceship/lib/spaceship/connect_api/models/user_invitation.rb +5 -15
  65. data/spaceship/lib/spaceship/connect_api/testflight/testflight.rb +8 -2
  66. data/spaceship/lib/spaceship/connect_api/users/users.rb +34 -0
  67. data/supply/lib/supply/client.rb +72 -20
  68. data/supply/lib/supply/generated_universal_apk.rb +24 -0
  69. data/supply/lib/supply/image_listing.rb +15 -0
  70. data/supply/lib/supply/options.rb +5 -0
  71. data/supply/lib/supply/setup.rb +1 -1
  72. data/supply/lib/supply/uploader.rb +32 -5
  73. data/supply/lib/supply.rb +2 -0
  74. metadata +285 -274
@@ -2,45 +2,55 @@
2
2
  "entries": {
3
3
  "brew": {
4
4
  "swiftformat": {
5
- "version": "0.51.9",
5
+ "version": "0.52.3",
6
6
  "bottle": {
7
7
  "rebuild": 0,
8
8
  "root_url": "https://ghcr.io/v2/homebrew/core",
9
9
  "files": {
10
+ "arm64_sonoma": {
11
+ "cellar": ":any_skip_relocation",
12
+ "url": "https://ghcr.io/v2/homebrew/core/swiftformat/blobs/sha256:a5ea23d7a08c20b20981a23f1ebc3221ca5796408934c412c2518598e00eb50f",
13
+ "sha256": "a5ea23d7a08c20b20981a23f1ebc3221ca5796408934c412c2518598e00eb50f"
14
+ },
10
15
  "arm64_ventura": {
11
16
  "cellar": ":any_skip_relocation",
12
- "url": "https://ghcr.io/v2/homebrew/core/swiftformat/blobs/sha256:bbb01c5c7fcde4f8b17945c2c8386b099848c9cd4fda8bcfd032c2420d5d1b65",
13
- "sha256": "bbb01c5c7fcde4f8b17945c2c8386b099848c9cd4fda8bcfd032c2420d5d1b65"
17
+ "url": "https://ghcr.io/v2/homebrew/core/swiftformat/blobs/sha256:4c60e18bc44c0781d97a96c536aa8bb6e762b79008927c5e7307dcf2c69b6058",
18
+ "sha256": "4c60e18bc44c0781d97a96c536aa8bb6e762b79008927c5e7307dcf2c69b6058"
14
19
  },
15
20
  "arm64_monterey": {
16
21
  "cellar": ":any_skip_relocation",
17
- "url": "https://ghcr.io/v2/homebrew/core/swiftformat/blobs/sha256:f04596d4567e86f46e0740ea3e9d1e2e19fa1cdf137a70287342b6ce937ddf8d",
18
- "sha256": "f04596d4567e86f46e0740ea3e9d1e2e19fa1cdf137a70287342b6ce937ddf8d"
22
+ "url": "https://ghcr.io/v2/homebrew/core/swiftformat/blobs/sha256:d63d1affcfba1ea8b6bf035d4abd834bf2effe0c0cf385ba608f98341da1c837",
23
+ "sha256": "d63d1affcfba1ea8b6bf035d4abd834bf2effe0c0cf385ba608f98341da1c837"
19
24
  },
20
25
  "arm64_big_sur": {
21
26
  "cellar": ":any_skip_relocation",
22
- "url": "https://ghcr.io/v2/homebrew/core/swiftformat/blobs/sha256:d8b6f0f30662dd4c2e1eb64d614fe2765ad664e2ac10a346004f94ed6ed6cdf2",
23
- "sha256": "d8b6f0f30662dd4c2e1eb64d614fe2765ad664e2ac10a346004f94ed6ed6cdf2"
27
+ "url": "https://ghcr.io/v2/homebrew/core/swiftformat/blobs/sha256:d80ec81a9140159181b77145d2dfdcd38488b23b262ef1bd74ab7163d0cbebae",
28
+ "sha256": "d80ec81a9140159181b77145d2dfdcd38488b23b262ef1bd74ab7163d0cbebae"
29
+ },
30
+ "sonoma": {
31
+ "cellar": ":any_skip_relocation",
32
+ "url": "https://ghcr.io/v2/homebrew/core/swiftformat/blobs/sha256:1aa6efde9ecda9e98c1b852192e0230b2e84055861382660e93c50e59a80487f",
33
+ "sha256": "1aa6efde9ecda9e98c1b852192e0230b2e84055861382660e93c50e59a80487f"
24
34
  },
25
35
  "ventura": {
26
36
  "cellar": ":any_skip_relocation",
27
- "url": "https://ghcr.io/v2/homebrew/core/swiftformat/blobs/sha256:dc2be8c829dc26595577f8b5daa071324f341c4f144cc46bf0a47520d44fc76e",
28
- "sha256": "dc2be8c829dc26595577f8b5daa071324f341c4f144cc46bf0a47520d44fc76e"
37
+ "url": "https://ghcr.io/v2/homebrew/core/swiftformat/blobs/sha256:274da364741558f1f52780e9a92354db212a803cf5d84c2ca9ff632d94ada47d",
38
+ "sha256": "274da364741558f1f52780e9a92354db212a803cf5d84c2ca9ff632d94ada47d"
29
39
  },
30
40
  "monterey": {
31
41
  "cellar": ":any_skip_relocation",
32
- "url": "https://ghcr.io/v2/homebrew/core/swiftformat/blobs/sha256:a88b992a2316fa06dd13944c6bb16d00522de0cc21aae17ede07e65d700c6800",
33
- "sha256": "a88b992a2316fa06dd13944c6bb16d00522de0cc21aae17ede07e65d700c6800"
42
+ "url": "https://ghcr.io/v2/homebrew/core/swiftformat/blobs/sha256:b53d82e6f2d42bf1e6fa799983fdfae934b483bc916c34436184fcb7e407f896",
43
+ "sha256": "b53d82e6f2d42bf1e6fa799983fdfae934b483bc916c34436184fcb7e407f896"
34
44
  },
35
45
  "big_sur": {
36
46
  "cellar": ":any_skip_relocation",
37
- "url": "https://ghcr.io/v2/homebrew/core/swiftformat/blobs/sha256:ba2cf56b5845bb33265a3e32737c5defa6e310987f785e1dfe6e7dc114a12555",
38
- "sha256": "ba2cf56b5845bb33265a3e32737c5defa6e310987f785e1dfe6e7dc114a12555"
47
+ "url": "https://ghcr.io/v2/homebrew/core/swiftformat/blobs/sha256:9bd117c9e2f92bde0f59a4106748a56e57687a1716627ef601e63b15a383ea35",
48
+ "sha256": "9bd117c9e2f92bde0f59a4106748a56e57687a1716627ef601e63b15a383ea35"
39
49
  },
40
50
  "x86_64_linux": {
41
51
  "cellar": "/home/linuxbrew/.linuxbrew/Cellar",
42
- "url": "https://ghcr.io/v2/homebrew/core/swiftformat/blobs/sha256:d1f82ed96ee9f8f239e9396bff00a0feac937fe12840d05fb6d5c01fe9161f03",
43
- "sha256": "d1f82ed96ee9f8f239e9396bff00a0feac937fe12840d05fb6d5c01fe9161f03"
52
+ "url": "https://ghcr.io/v2/homebrew/core/swiftformat/blobs/sha256:384fee82f88b4bf8b30cfe9cec946ebd9127a4741b28e06cec42fc0d9b70c0f6",
53
+ "sha256": "384fee82f88b4bf8b30cfe9cec946ebd9127a4741b28e06cec42fc0d9b70c0f6"
44
54
  }
45
55
  }
46
56
  }
@@ -74,12 +84,12 @@
74
84
  "macOS": "12.5"
75
85
  },
76
86
  "ventura": {
77
- "HOMEBREW_VERSION": "4.0.18-59-gf7b3225",
87
+ "HOMEBREW_VERSION": "4.1.11-36-g184efd9",
78
88
  "HOMEBREW_PREFIX": "/opt/homebrew",
79
89
  "Homebrew/homebrew-core": "api",
80
90
  "CLT": "",
81
- "Xcode": "14.1",
82
- "macOS": "13.2.1"
91
+ "Xcode": "14.3",
92
+ "macOS": "13.4"
83
93
  }
84
94
  }
85
95
  }
@@ -146,7 +146,7 @@ module FastlaneCore
146
146
 
147
147
  def self.install_wwdr_certificate(cert_alias)
148
148
  url = WWDRCA_CERTIFICATES.find { |c| c[:alias] == cert_alias }.fetch(:url)
149
- file = Tempfile.new(File.basename(url))
149
+ file = Tempfile.new([File.basename(url, ".cer"), ".cer"])
150
150
  filename = file.path
151
151
  keychain = wwdr_keychain
152
152
  keychain = "-k #{keychain.shellescape}" unless keychain.empty?
@@ -120,7 +120,7 @@ module Frameit
120
120
  IPHONE_8_PLUS ||= Frameit::Device.new("iphone-8-plus", "Apple iPhone 8 Plus", 6, [[1242, 2208], [2208, 1242]], 401, Color::SPACE_GRAY)
121
121
  IPHONE_X ||= Frameit::Device.new("iphone-X", "Apple iPhone X", 7, [[1125, 2436], [2436, 1125]], 458, Color::SPACE_GRAY, Platform::IOS, Deliver::AppScreenshot::ScreenSize::IOS_58, :use_legacy_iphonex)
122
122
  IPHONE_XS ||= Frameit::Device.new("iphone-XS", "Apple iPhone XS", 8, [[1125, 2436], [2436, 1125]], 458, Color::SPACE_GRAY, Platform::IOS, Deliver::AppScreenshot::ScreenSize::IOS_58, :use_legacy_iphonexs)
123
- IPHONE_XR ||= Frameit::Device.new("iphone-XR", "Apple iPhone XR", 8, [[828, 1792], [1792, 828]], 326, Color::SPACE_GRAY, Platform::IOS, Deliver::AppScreenshot::ScreenSize::IOS_61, :use_legacy_iphonexr)
123
+ IPHONE_XR ||= Frameit::Device.new("iphone-XR", "Apple iPhone XR", 8, [[828, 1792], [1792, 828]], 326, Color::SPACE_GRAY, Platform::IOS, Deliver::AppScreenshot::ScreenSize::IOS_65, :use_legacy_iphonexsmax)
124
124
  IPHONE_XS_MAX ||= Frameit::Device.new("iphone-XS-Max", "Apple iPhone XS Max", 8, [[1242, 2688], [2688, 1242]], 458, Color::SPACE_GRAY, Platform::IOS, Deliver::AppScreenshot::ScreenSize::IOS_65, :use_legacy_iphonexsmax)
125
125
  IPHONE_11 ||= Frameit::Device.new("iphone-11", "Apple iPhone 11", 9, [[828, 1792], [1792, 828]], 326, Color::BLACK, Platform::IOS)
126
126
  IPHONE_11_PRO ||= Frameit::Device.new("iphone-11-pro", "Apple iPhone 11 Pro", 9, [[1125, 2436], [2436, 1125]], 458, Color::SPACE_GRAY, Platform::IOS)
@@ -80,7 +80,7 @@ module Gym
80
80
  if Gym.config[:disable_xcpretty] || formatter == ''
81
81
  UI.verbose("Not using an xcodebuild formatter")
82
82
  elsif !options.empty?
83
- UI.important("Detected legacy xcpretty being used so formatting wth xcpretty")
83
+ UI.important("Detected legacy xcpretty being used, so formatting with xcpretty")
84
84
  UI.important("Option(s) used: #{options.join(', ')}")
85
85
  pipe += pipe_xcpretty
86
86
  elsif formatter == 'xcpretty'
@@ -19,19 +19,13 @@ module Match
19
19
  new_password = FastlaneCore::Helper.ask_password(message: "New passphrase for Git Repo: ", confirm: true)
20
20
 
21
21
  # Choose the right storage and encryption implementations
22
- storage = Storage.for_mode(params[:storage_mode], {
23
- git_url: params[:git_url],
24
- shallow_clone: params[:shallow_clone],
25
- skip_docs: params[:skip_docs],
26
- git_branch: params[:git_branch],
27
- git_full_name: params[:git_full_name],
28
- git_user_email: params[:git_user_email],
29
- clone_branch_directly: params[:clone_branch_directly]
30
- })
22
+ storage = Storage.from_params(params)
31
23
  storage.download
32
24
 
33
25
  encryption = Encryption.for_storage_mode(params[:storage_mode], {
34
26
  git_url: params[:git_url],
27
+ s3_bucket: params[:s3_bucket],
28
+ s3_skip_encryption: params[:s3_skip_encryption],
35
29
  working_directory: storage.working_directory
36
30
  })
37
31
  encryption.decrypt_files
@@ -118,16 +118,13 @@ module Match
118
118
  params = FastlaneCore::Configuration.create(Match::Options.available_options, options.__hash__)
119
119
  params.load_configuration_file("Matchfile")
120
120
 
121
- storage = Storage.for_mode(params[:storage_mode], {
122
- git_url: params[:git_url],
123
- shallow_clone: params[:shallow_clone],
124
- git_branch: params[:git_branch],
125
- clone_branch_directly: params[:clone_branch_directly]
126
- })
121
+ storage = Storage.from_params(params)
127
122
  storage.download
128
123
 
129
124
  encryption = Encryption.for_storage_mode(params[:storage_mode], {
130
125
  git_url: params[:git_url],
126
+ s3_bucket: params[:s3_bucket],
127
+ s3_skip_encryption: params[:s3_skip_encryption],
131
128
  working_directory: storage.working_directory
132
129
  })
133
130
  encryption.decrypt_files if encryption
@@ -17,7 +17,7 @@ module Match
17
17
  },
18
18
  "s3" => lambda { |params|
19
19
  params[:keychain_name] = params[:s3_bucket]
20
- return Encryption::OpenSSL.configure(params)
20
+ return params[:s3_skip_encryption] ? nil : Encryption::OpenSSL.configure(params)
21
21
  },
22
22
  "gitlab_secure_files" => lambda { |params|
23
23
  return nil
@@ -15,42 +15,14 @@ module Match
15
15
  profile_path = ensure_valid_file_path(profile_path, "Provisioning profile", ".mobileprovision or .provisionprofile", optional: true)
16
16
 
17
17
  # Storage
18
- storage = Storage.for_mode(params[:storage_mode], {
19
- git_url: params[:git_url],
20
- shallow_clone: params[:shallow_clone],
21
- skip_docs: params[:skip_docs],
22
- git_branch: params[:git_branch],
23
- git_full_name: params[:git_full_name],
24
- git_user_email: params[:git_user_email],
25
- git_private_key: params[:git_private_key],
26
- git_basic_authorization: params[:git_basic_authorization],
27
- git_bearer_authorization: params[:git_bearer_authorization],
28
- clone_branch_directly: params[:clone_branch_directly],
29
- type: params[:type].to_s,
30
- platform: params[:platform].to_s,
31
- google_cloud_bucket_name: params[:google_cloud_bucket_name].to_s,
32
- google_cloud_keys_file: params[:google_cloud_keys_file].to_s,
33
- google_cloud_project_id: params[:google_cloud_project_id].to_s,
34
- skip_google_cloud_account_confirmation: params[:skip_google_cloud_account_confirmation],
35
- s3_bucket: params[:s3_bucket],
36
- s3_region: params[:s3_region],
37
- s3_access_key: params[:s3_access_key],
38
- s3_secret_access_key: params[:s3_secret_access_key],
39
- s3_object_prefix: params[:s3_object_prefix],
40
- gitlab_project: params[:gitlab_project],
41
- gitlab_host: params[:gitlab_host],
42
- readonly: params[:readonly],
43
- username: params[:username],
44
- team_id: params[:team_id],
45
- team_name: params[:team_name],
46
- api_key_path: params[:api_key_path],
47
- api_key: params[:api_key]
48
- })
18
+ storage = Storage.from_params(params)
49
19
  storage.download
50
20
 
51
21
  # Encryption
52
22
  encryption = Encryption.for_storage_mode(params[:storage_mode], {
53
23
  git_url: params[:git_url],
24
+ s3_bucket: params[:s3_bucket],
25
+ s3_skip_encryption: params[:s3_skip_encryption],
54
26
  working_directory: storage.working_directory
55
27
  })
56
28
  encryption.decrypt_files if encryption
@@ -13,13 +13,15 @@ module Match
13
13
 
14
14
  # We init the Google storage client before the git client
15
15
  # to ask for all the missing inputs *before* cloning the git repo
16
- google_cloud_storage = Storage.for_mode("google_cloud", {
16
+ google_cloud_storage = Storage.from_params({
17
+ storage_mode: "google_cloud",
17
18
  google_cloud_bucket_name: params[:google_cloud_bucket_name],
18
19
  google_cloud_keys_file: params[:google_cloud_keys_file],
19
20
  google_cloud_project_id: params[:google_cloud_project_id]
20
21
  })
21
22
 
22
- git_storage = Storage.for_mode("git", {
23
+ git_storage = Storage.from_params({
24
+ storage_mode: "git",
23
25
  git_url: params[:git_url],
24
26
  shallow_clone: params[:shallow_clone],
25
27
  git_branch: params[:git_branch],
@@ -29,6 +31,8 @@ module Match
29
31
 
30
32
  encryption = Encryption.for_storage_mode(params[:storage_mode], {
31
33
  git_url: params[:git_url],
34
+ s3_bucket: params[:s3_bucket],
35
+ s3_skip_encryption: params[:s3_skip_encryption],
32
36
  working_directory: git_storage.working_directory
33
37
  })
34
38
  encryption.decrypt_files if encryption
@@ -34,36 +34,14 @@ module Match
34
34
 
35
35
  spaceship_login
36
36
 
37
- self.storage = Storage.for_mode(params[:storage_mode], {
38
- git_url: params[:git_url],
39
- shallow_clone: params[:shallow_clone],
40
- skip_docs: params[:skip_docs],
41
- git_branch: params[:git_branch],
42
- git_full_name: params[:git_full_name],
43
- git_user_email: params[:git_user_email],
44
-
45
- git_private_key: params[:git_private_key],
46
- git_basic_authorization: params[:git_basic_authorization],
47
- git_bearer_authorization: params[:git_bearer_authorization],
48
-
49
- clone_branch_directly: params[:clone_branch_directly],
50
- google_cloud_bucket_name: params[:google_cloud_bucket_name].to_s,
51
- google_cloud_keys_file: params[:google_cloud_keys_file].to_s,
52
- google_cloud_project_id: params[:google_cloud_project_id].to_s,
53
- s3_region: params[:s3_region].to_s,
54
- s3_access_key: params[:s3_access_key].to_s,
55
- s3_secret_access_key: params[:s3_secret_access_key].to_s,
56
- s3_bucket: params[:s3_bucket].to_s,
57
- s3_object_prefix: params[:s3_object_prefix].to_s,
58
- gitlab_project: params[:gitlab_project],
59
- gitlab_host: params[:gitlab_host],
60
- team_id: params[:team_id] || Spaceship::ConnectAPI.client.portal_team_id
61
- })
37
+ self.storage = Storage.from_params(params)
62
38
  self.storage.download
63
39
 
64
40
  # After the download was complete
65
41
  self.encryption = Encryption.for_storage_mode(params[:storage_mode], {
66
42
  git_url: params[:git_url],
43
+ s3_bucket: params[:s3_bucket],
44
+ s3_skip_encryption: params[:s3_skip_encryption],
67
45
  working_directory: storage.working_directory
68
46
  })
69
47
  self.encryption.decrypt_files if self.encryption
@@ -4,6 +4,7 @@ require 'credentials_manager/appfile_config'
4
4
  require_relative 'module'
5
5
 
6
6
  module Match
7
+ # rubocop:disable Metrics/ClassLength
7
8
  class Options
8
9
  # This is match specific, as users can append storage specific options
9
10
  def self.append_option(option)
@@ -222,6 +223,11 @@ module Match
222
223
  env_name: "MATCH_S3_OBJECT_PREFIX",
223
224
  description: "Prefix to be used on all objects uploaded to S3",
224
225
  optional: true),
226
+ FastlaneCore::ConfigItem.new(key: :s3_skip_encryption,
227
+ env_name: "MATCH_S3_SKIP_ENCRYPTION",
228
+ description: "Skip encryption of all objects uploaded to S3. WARNING: only enable this on S3 buckets with sufficiently restricted permissions and server-side encryption enabled. See https://docs.aws.amazon.com/AmazonS3/latest/userguide/UsingEncryption.html",
229
+ type: Boolean,
230
+ default_value: false),
225
231
 
226
232
  # Storage: GitLab Secure Files
227
233
  FastlaneCore::ConfigItem.new(key: :gitlab_project,
@@ -349,4 +355,5 @@ module Match
349
355
  ]
350
356
  end
351
357
  end
358
+ # rubocop:enable Metrics/ClassLength
352
359
  end
@@ -31,43 +31,16 @@ module Match
31
31
  update_optional_values_depending_on_storage_type(params)
32
32
 
33
33
  # Choose the right storage and encryption implementations
34
- self.storage = Storage.for_mode(params[:storage_mode], {
35
- git_url: params[:git_url],
36
- shallow_clone: params[:shallow_clone],
37
- skip_docs: params[:skip_docs],
38
- git_branch: params[:git_branch],
39
- git_full_name: params[:git_full_name],
40
- git_user_email: params[:git_user_email],
41
- clone_branch_directly: params[:clone_branch_directly],
42
- git_basic_authorization: params[:git_basic_authorization],
43
- git_bearer_authorization: params[:git_bearer_authorization],
44
- git_private_key: params[:git_private_key],
45
- type: params[:type].to_s,
46
- generate_apple_certs: params[:generate_apple_certs],
47
- platform: params[:platform].to_s,
48
- google_cloud_bucket_name: params[:google_cloud_bucket_name].to_s,
49
- google_cloud_keys_file: params[:google_cloud_keys_file].to_s,
50
- google_cloud_project_id: params[:google_cloud_project_id].to_s,
51
- skip_google_cloud_account_confirmation: params[:skip_google_cloud_account_confirmation],
52
- s3_region: params[:s3_region],
53
- s3_access_key: params[:s3_access_key],
54
- s3_secret_access_key: params[:s3_secret_access_key],
55
- s3_bucket: params[:s3_bucket],
56
- s3_object_prefix: params[:s3_object_prefix],
57
- gitlab_project: params[:gitlab_project],
58
- gitlab_host: params[:gitlab_host],
59
- readonly: params[:readonly],
60
- username: params[:readonly] ? nil : params[:username], # only pass username if not readonly
61
- team_id: params[:team_id],
62
- team_name: params[:team_name],
63
- api_key_path: params[:api_key_path],
64
- api_key: params[:api_key]
65
- })
34
+ storage_params = params
35
+ storage_params[:username] = params[:readonly] ? nil : params[:username] # only pass username if not readonly
36
+ self.storage = Storage.from_params(storage_params)
66
37
  storage.download
67
38
 
68
39
  # Init the encryption only after the `storage.download` was called to have the right working directory
69
40
  encryption = Encryption.for_storage_mode(params[:storage_mode], {
70
41
  git_url: params[:git_url],
42
+ s3_bucket: params[:s3_bucket],
43
+ s3_skip_encryption: params[:s3_skip_encryption],
71
44
  working_directory: storage.working_directory
72
45
  })
73
46
  encryption.decrypt_files if encryption
@@ -15,7 +15,7 @@ module Match
15
15
  self.storage_options
16
16
  )
17
17
 
18
- storage = Storage.for_mode(storage_mode, {})
18
+ storage = Storage.from_params({ storage_mode: storage_mode })
19
19
 
20
20
  specific_content = storage.generate_matchfile_content
21
21
  UI.crash!("Looks like `generate_matchfile_content` was `nil` for `#{storage_mode}`") if specific_content.nil?
@@ -40,6 +40,8 @@ module Match
40
40
  def files
41
41
  @files ||= begin
42
42
  url = URI.parse(base_url)
43
+ # 100 is maximum number of Secure files available on Gitlab https://docs.gitlab.com/ee/api/secure_files.html
44
+ url.query = [url.query, "per_page=100"].compact.join('&')
43
45
 
44
46
  request = Net::HTTP::Get.new(url.request_uri)
45
47
 
@@ -69,32 +71,56 @@ module Match
69
71
  "name" => target_file
70
72
  )
71
73
 
72
- response = execute_request(url, request)
74
+ execute_request(url, request, target_file)
75
+ end
76
+ end
77
+
78
+ def execute_request(url, request, target_file = nil)
79
+ request[authentication_key] = authentication_value
80
+
81
+ http = Net::HTTP.new(url.host, url.port)
82
+ http.use_ssl = url.instance_of?(URI::HTTPS)
83
+
84
+ begin
85
+ response = http.request(request)
86
+ rescue Errno::ECONNREFUSED, SocketError => message
87
+ UI.user_error!("GitLab connection error: #{message}")
88
+ end
73
89
 
74
- log_upload_error(response, target_file) if response.code != "201"
90
+ unless response.kind_of?(Net::HTTPSuccess)
91
+ handle_response_error(response, target_file)
75
92
  end
93
+
94
+ response
76
95
  end
77
96
 
78
- def log_upload_error(response, target_file)
97
+ def handle_response_error(response, target_file = nil)
98
+ error_prefix = "GitLab storage error:"
99
+ file_debug_string = "File: #{target_file}" unless target_file.nil?
100
+ api_debug_string = "API: #{@api_v4_url}"
101
+ debug_info = "(#{[file_debug_string, api_debug_string].compact.join(', ')})"
102
+
79
103
  begin
80
- response_body = JSON.parse(response.body)
104
+ parsed = JSON.parse(response.body)
81
105
  rescue JSON::ParserError
82
- response_body = response.body
83
106
  end
84
107
 
85
- if response_body["message"] && (response_body["message"]["name"] == ["has already been taken"])
86
- UI.error("#{target_file} already exists in GitLab project #{@project_id}, file not uploaded")
108
+ if parsed && parsed["message"] && (parsed["message"]["name"] == ["has already been taken"])
109
+ error_handler = :error
110
+ error = "#{target_file} already exists in GitLab project #{@project_id}, file not uploaded"
87
111
  else
88
- UI.error("Upload error for #{target_file}: #{response_body}")
112
+ error_handler = :user_error!
113
+ error = "#{response.code}: #{response.body}"
89
114
  end
90
- end
115
+ error_message = [error_prefix, error, debug_info].join(' ')
91
116
 
92
- def execute_request(url, request)
93
- request[authentication_key] = authentication_value
117
+ UI.send(error_handler, error_message)
118
+ end
94
119
 
95
- http = Net::HTTP.new(url.host, url.port)
96
- http.use_ssl = url.instance_of?(URI::HTTPS)
97
- http.request(request)
120
+ def prompt_for_access_token
121
+ unless authentication_key
122
+ @private_token = UI.input("Please supply a GitLab personal or project access token: ")
123
+ end
98
124
  end
99
125
  end
100
126
  end
@@ -47,7 +47,8 @@ module Match
47
47
  team_id: params[:team_id],
48
48
  team_name: params[:team_name],
49
49
  api_key_path: params[:api_key_path],
50
- api_key: params[:api_key]
50
+ api_key: params[:api_key],
51
+ gitlab_host: params[:gitlab_host]
51
52
  )
52
53
  end
53
54
 
@@ -60,7 +61,8 @@ module Match
60
61
  team_id: nil,
61
62
  team_name: nil,
62
63
  api_key_path: nil,
63
- api_key: nil)
64
+ api_key: nil,
65
+ gitlab_host: nil)
64
66
 
65
67
  @readonly = readonly
66
68
  @username = username
@@ -68,6 +70,7 @@ module Match
68
70
  @team_name = team_name
69
71
  @api_key_path = api_key_path
70
72
  @api_key = api_key
73
+ @gitlab_host = gitlab_host
71
74
 
72
75
  @job_token = job_token
73
76
  @private_token = private_token
@@ -95,6 +98,8 @@ module Match
95
98
  end
96
99
 
97
100
  def download
101
+ gitlab_client.prompt_for_access_token
102
+
98
103
  # Check if we already have a functional working_directory
99
104
  return if @working_directory
100
105
 
@@ -102,6 +102,12 @@ module Match
102
102
  self.working_directory = Dir.mktmpdir
103
103
 
104
104
  s3_client.find_bucket!(s3_bucket).objects(prefix: s3_object_prefix).each do |object|
105
+
106
+ # Prevent download if the file path is a directory.
107
+ # We need to check if string ends with "/" instead of using `File.directory?` because
108
+ # the string represent a remote location, not a local file in disk.
109
+ next if object.key.end_with?("/")
110
+
105
111
  file_path = strip_s3_object_prefix(object.key) # :s3_object_prefix:team_id/path/to/file
106
112
 
107
113
  # strip s3_prefix from file_path
@@ -10,16 +10,66 @@ module Match
10
10
  def backends
11
11
  @backends ||= {
12
12
  "git" => lambda { |params|
13
- return Storage::GitStorage.configure(params)
13
+ return Storage::GitStorage.configure({
14
+ type: params[:type],
15
+ platform: params[:platform],
16
+ git_url: params[:git_url],
17
+ shallow_clone: params[:shallow_clone],
18
+ skip_docs: params[:skip_docs],
19
+ git_branch: params[:git_branch],
20
+ git_full_name: params[:git_full_name],
21
+ git_user_email: params[:git_user_email],
22
+ clone_branch_directly: params[:clone_branch_directly],
23
+ git_basic_authorization: params[:git_basic_authorization],
24
+ git_bearer_authorization: params[:git_bearer_authorization],
25
+ git_private_key: params[:git_private_key]
26
+ })
14
27
  },
15
28
  "google_cloud" => lambda { |params|
16
- return Storage::GoogleCloudStorage.configure(params)
29
+ return Storage::GoogleCloudStorage.configure({
30
+ type: params[:type],
31
+ platform: params[:platform],
32
+ google_cloud_bucket_name: params[:google_cloud_bucket_name],
33
+ google_cloud_keys_file: params[:google_cloud_keys_file],
34
+ google_cloud_project_id: params[:google_cloud_project_id],
35
+ readonly: params[:readonly],
36
+ username: params[:username],
37
+ team_id: params[:team_id],
38
+ team_name: params[:team_name],
39
+ api_key_path: params[:api_key_path],
40
+ api_key: params[:api_key],
41
+ skip_google_cloud_account_confirmation: params[:skip_google_cloud_account_confirmation]
42
+ })
17
43
  },
18
44
  "s3" => lambda { |params|
19
- return Storage::S3Storage.configure(params)
45
+ return Storage::S3Storage.configure({
46
+ s3_region: params[:s3_region],
47
+ s3_access_key: params[:s3_access_key],
48
+ s3_secret_access_key: params[:s3_secret_access_key],
49
+ s3_bucket: params[:s3_bucket],
50
+ s3_object_prefix: params[:s3_object_prefix],
51
+ readonly: params[:readonly],
52
+ username: params[:username],
53
+ team_id: params[:team_id],
54
+ team_name: params[:team_name],
55
+ api_key_path: params[:api_key_path],
56
+ api_key: params[:api_key]
57
+ })
20
58
  },
21
59
  "gitlab_secure_files" => lambda { |params|
22
- return Storage::GitLabSecureFiles.configure(params)
60
+ return Storage::GitLabSecureFiles.configure({
61
+ gitlab_host: params[:gitlab_host],
62
+ gitlab_project: params[:gitlab_project],
63
+ git_url: params[:git_url], # enables warning about unnecessary git_url
64
+ job_token: params[:job_token],
65
+ private_token: params[:private_token],
66
+ readonly: params[:readonly],
67
+ username: params[:username],
68
+ team_id: params[:team_id],
69
+ team_name: params[:team_name],
70
+ api_key_path: params[:api_key_path],
71
+ api_key: params[:api_key]
72
+ })
23
73
  }
24
74
  }
25
75
  end
@@ -39,7 +89,8 @@ module Match
39
89
  end
40
90
  end
41
91
 
42
- def for_mode(storage_mode, params)
92
+ def from_params(params)
93
+ storage_mode = params[:storage_mode]
43
94
  configurator = backends[storage_mode.to_s]
44
95
  return configurator.call(params) if configurator
45
96
 
@@ -132,7 +132,7 @@ module Scan
132
132
  if formatter == ''
133
133
  UI.verbose("Not using an xcodebuild formatter")
134
134
  elsif !options.empty?
135
- UI.important("Detected legacy xcpretty being used so formatting wth xcpretty")
135
+ UI.important("Detected legacy xcpretty being used, so formatting with xcpretty")
136
136
  UI.important("Option(s) used: #{options.join(', ')}")
137
137
  pipe << pipe_xcpretty
138
138
  elsif formatter == 'xcpretty'
@@ -34,7 +34,7 @@ module Snapshot
34
34
  if Snapshot.config[:disable_xcpretty] || formatter == ''
35
35
  UI.verbose("Not using an xcodebuild formatter")
36
36
  elsif !options.empty?
37
- UI.important("Detected legacy xcpretty being used so formatting wth xcpretty")
37
+ UI.important("Detected legacy xcpretty being used, so formatting with xcpretty")
38
38
  UI.important("Option(s) used: #{options.join(', ')}")
39
39
  pipe += pipe_xcpretty
40
40
  elsif formatter == 'xcpretty'
@@ -533,7 +533,6 @@ module Spaceship
533
533
 
534
534
  if try_upgrade_2fa_later(response)
535
535
  store_cookie
536
- fetch_olympus_session
537
536
  return true
538
537
  end
539
538