fastlane 2.206.1 → 2.208.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +96 -96
  3. data/deliver/lib/deliver/runner.rb +31 -35
  4. data/deliver/lib/deliver/upload_price_tier.rb +3 -1
  5. data/deliver/lib/deliver/upload_screenshots.rb +1 -1
  6. data/fastlane/lib/fastlane/actions/changelog_from_git_commits.rb +1 -1
  7. data/fastlane/lib/fastlane/actions/import_certificate.rb +1 -1
  8. data/fastlane/lib/fastlane/actions/update_info_plist.rb +1 -1
  9. data/fastlane/lib/fastlane/actions/upload_symbols_to_sentry.rb +1 -1
  10. data/fastlane/lib/fastlane/actions/xcodebuild.rb +8 -2
  11. data/fastlane/lib/fastlane/cli_tools_distributor.rb +1 -16
  12. data/fastlane/lib/fastlane/lane_manager.rb +1 -1
  13. data/fastlane/lib/fastlane/plugins/template/%gem_name%.gemspec.erb +1 -1
  14. data/fastlane/lib/fastlane/plugins/template/.rubocop.yml +5 -1
  15. data/fastlane/lib/fastlane/setup/setup_ios.rb +1 -1
  16. data/fastlane/lib/fastlane/tools.rb +16 -0
  17. data/fastlane/lib/fastlane/version.rb +1 -1
  18. data/fastlane/swift/Deliverfile.swift +1 -1
  19. data/fastlane/swift/DeliverfileProtocol.swift +1 -1
  20. data/fastlane/swift/Fastlane.swift +18 -6
  21. data/fastlane/swift/FastlaneSwiftRunner/FastlaneSwiftRunner.xcodeproj/project.xcworkspace/xcuserdata/joshholtz.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  22. data/fastlane/swift/Gymfile.swift +1 -1
  23. data/fastlane/swift/GymfileProtocol.swift +1 -1
  24. data/fastlane/swift/MainProcess.swift +2 -2
  25. data/fastlane/swift/Matchfile.swift +1 -1
  26. data/fastlane/swift/MatchfileProtocol.swift +5 -1
  27. data/fastlane/swift/Precheckfile.swift +1 -1
  28. data/fastlane/swift/PrecheckfileProtocol.swift +1 -1
  29. data/fastlane/swift/Scanfile.swift +1 -1
  30. data/fastlane/swift/ScanfileProtocol.swift +1 -1
  31. data/fastlane/swift/Screengrabfile.swift +1 -1
  32. data/fastlane/swift/ScreengrabfileProtocol.swift +1 -1
  33. data/fastlane/swift/Snapshotfile.swift +1 -1
  34. data/fastlane/swift/SnapshotfileProtocol.swift +2 -2
  35. data/fastlane/swift/SocketClient.swift +1 -1
  36. data/fastlane/swift/formatting/Brewfile.lock.json +21 -16
  37. data/fastlane_core/lib/fastlane_core/cert_checker.rb +7 -11
  38. data/fastlane_core/lib/fastlane_core/keychain_importer.rb +1 -0
  39. data/fastlane_core/lib/fastlane_core/project.rb +19 -2
  40. data/frameit/lib/frameit/device_types.rb +2 -0
  41. data/match/lib/match/encryption.rb +3 -0
  42. data/match/lib/match/importer.rb +1 -0
  43. data/match/lib/match/module.rb +53 -1
  44. data/match/lib/match/nuke.rb +3 -40
  45. data/match/lib/match/options.rb +6 -0
  46. data/match/lib/match/runner.rb +11 -1
  47. data/match/lib/match/setup.rb +1 -1
  48. data/match/lib/match/spaceship_ensure.rb +4 -2
  49. data/match/lib/match/storage/gitlab/client.rb +102 -0
  50. data/match/lib/match/storage/gitlab/secure_file.rb +65 -0
  51. data/match/lib/match/storage/gitlab_secure_files.rb +182 -0
  52. data/match/lib/match/storage.rb +4 -0
  53. data/match/lib/match/table_printer.rb +2 -1
  54. data/match/lib/match/utils.rb +10 -2
  55. data/pem/lib/pem/manager.rb +1 -1
  56. data/scan/lib/scan/detect_values.rb +6 -0
  57. data/sigh/lib/sigh/download_all.rb +14 -2
  58. data/sigh/lib/sigh/module.rb +3 -1
  59. data/sigh/lib/sigh/runner.rb +7 -0
  60. data/snapshot/lib/snapshot/options.rb +1 -1
  61. data/snapshot/lib/snapshot/reports_generator.rb +1 -0
  62. data/snapshot/lib/snapshot/simulator_launchers/simulator_launcher_base.rb +4 -1
  63. data/spaceship/lib/spaceship/connect_api/models/app.rb +4 -2
  64. data/spaceship/lib/spaceship/connect_api/models/profile.rb +4 -0
  65. data/spaceship/lib/spaceship/connect_api/response.rb +10 -6
  66. data/spaceship/lib/spaceship/connect_api/tunes/tunes.rb +18 -8
  67. data/spaceship/lib/spaceship/tunes/.tunes_client.rb.swp +0 -0
  68. data/spaceship/lib/spaceship/tunes/tunes_client.rb +63 -2
  69. data/trainer/lib/trainer/junit_generator.rb +1 -1
  70. metadata +24 -21
  71. data/spaceship/lib/spaceship/connect_api/.response.rb.swp +0 -0
  72. data/spaceship/lib/spaceship/connect_api/models/.app.rb.swp +0 -0
@@ -54,6 +54,7 @@ module Match
54
54
  s3_secret_access_key: params[:s3_secret_access_key],
55
55
  s3_bucket: params[:s3_bucket],
56
56
  s3_object_prefix: params[:s3_object_prefix],
57
+ gitlab_project: params[:gitlab_project],
57
58
  readonly: params[:readonly],
58
59
  username: params[:readonly] ? nil : params[:username], # only pass username if not readonly
59
60
  team_id: params[:team_id],
@@ -286,7 +287,10 @@ module Match
286
287
  FileUtils.cp(profile, params[:output_path])
287
288
  end
288
289
 
289
- if spaceship && !spaceship.profile_exists(username: params[:username], uuid: uuid, platform: params[:platform])
290
+ if spaceship && !spaceship.profile_exists(type: prov_type,
291
+ username: params[:username],
292
+ uuid: uuid,
293
+ platform: params[:platform])
290
294
  # This profile is invalid, let's remove the local file and generate a new one
291
295
  File.delete(profile)
292
296
  # This method will be called again, no need to modify `files_to_commit`
@@ -305,6 +309,12 @@ module Match
305
309
  platform: params[:platform]),
306
310
  parsed["TeamIdentifier"].first)
307
311
 
312
+ cert_info = Utils.get_cert_info(parsed["DeveloperCertificates"].first.string).to_h
313
+ Utils.fill_environment(Utils.environment_variable_name_certificate_name(app_identifier: app_identifier,
314
+ type: prov_type,
315
+ platform: params[:platform]),
316
+ cert_info["Common Name"])
317
+
308
318
  Utils.fill_environment(Utils.environment_variable_name_profile_name(app_identifier: app_identifier,
309
319
  type: prov_type,
310
320
  platform: params[:platform]),
@@ -34,7 +34,7 @@ module Match
34
34
  end
35
35
 
36
36
  def storage_options
37
- return ["git", "google_cloud", "s3"]
37
+ return ["git", "google_cloud", "s3", "gitlab_secure_files"]
38
38
  end
39
39
  end
40
40
  end
@@ -74,10 +74,12 @@ module Match
74
74
  UI.user_error!("To reset the certificates of your Apple account, you can use the `fastlane match nuke` feature, more information on https://docs.fastlane.tools/actions/match/")
75
75
  end
76
76
 
77
- def profile_exists(username: nil, uuid: nil, platform: nil)
77
+ def profile_exists(type: nil, username: nil, uuid: nil, platform: nil)
78
78
  # App Store Connect API does not allow filter of profile by platform or uuid (as of 2020-07-30)
79
79
  # Need to fetch all profiles and search for uuid on client side
80
- found = Spaceship::ConnectAPI::Profile.all.find do |profile|
80
+ # But we can filter provisioning profiles based on their type (this, in general way faster than getting all profiles)
81
+ filter = { profileType: Match.profile_types(type).join(",") } if type
82
+ found = Spaceship::ConnectAPI::Profile.all(filter: filter).find do |profile|
81
83
  profile.uuid == uuid
82
84
  end
83
85
 
@@ -0,0 +1,102 @@
1
+ require 'net/http/post/multipart'
2
+ require 'securerandom'
3
+
4
+ require_relative '../../module'
5
+ require_relative './secure_file'
6
+
7
+ module Match
8
+ module Storage
9
+ class GitLab
10
+ class Client
11
+ def initialize(api_v4_url:, project_id:, job_token: nil, private_token: nil)
12
+ @job_token = job_token
13
+ @private_token = private_token
14
+ @api_v4_url = api_v4_url
15
+ @project_id = project_id
16
+
17
+ UI.important("JOB_TOKEN and PRIVATE_TOKEN both defined, using JOB_TOKEN to execute this job.") if @job_token && @private_token
18
+ end
19
+
20
+ def base_url
21
+ return "#{@api_v4_url}/projects/#{CGI.escape(@project_id)}/secure_files"
22
+ end
23
+
24
+ def authentication_key
25
+ if @job_token
26
+ return "JOB-TOKEN"
27
+ elsif @private_token
28
+ return "PRIVATE-TOKEN"
29
+ end
30
+ end
31
+
32
+ def authentication_value
33
+ if @job_token
34
+ return @job_token
35
+ elsif @private_token
36
+ return @private_token
37
+ end
38
+ end
39
+
40
+ def files
41
+ @files ||= begin
42
+ url = URI.parse(base_url)
43
+
44
+ request = Net::HTTP::Get.new(url.request_uri)
45
+
46
+ res = execute_request(url, request)
47
+
48
+ data = []
49
+
50
+ JSON.parse(res.body).each do |file|
51
+ data << SecureFile.new(client: self, file: file)
52
+ end
53
+
54
+ data
55
+ end
56
+ end
57
+
58
+ def find_file_by_name(name)
59
+ files.select { |secure_file| secure_file.file.name == name }.first
60
+ end
61
+
62
+ def upload_file(current_file, target_file)
63
+ url = URI.parse(base_url)
64
+
65
+ File.open(current_file) do |file|
66
+ request = Net::HTTP::Post::Multipart.new(
67
+ url.path,
68
+ "file" => UploadIO.new(file, "application/octet-stream"),
69
+ "name" => target_file
70
+ )
71
+
72
+ response = execute_request(url, request)
73
+
74
+ log_upload_error(response, target_file) if response.code != "201"
75
+ end
76
+ end
77
+
78
+ def log_upload_error(response, target_file)
79
+ begin
80
+ response_body = JSON.parse(response.body)
81
+ rescue JSON::ParserError
82
+ response_body = response.body
83
+ end
84
+
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")
87
+ else
88
+ UI.error("Upload error for #{target_file}: #{response_body}")
89
+ end
90
+ end
91
+
92
+ def execute_request(url, request)
93
+ request[authentication_key] = authentication_value
94
+
95
+ http = Net::HTTP.new(url.host, url.port)
96
+ http.use_ssl = url.instance_of?(URI::HTTPS)
97
+ http.request(request)
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,65 @@
1
+ require 'open-uri'
2
+
3
+ require_relative '../../module'
4
+
5
+ module Match
6
+ module Storage
7
+ class GitLab
8
+ class SecureFile
9
+ attr_reader :client, :file
10
+
11
+ def initialize(file:, client:)
12
+ @file = OpenStruct.new(file)
13
+ @client = client
14
+ end
15
+
16
+ def file_url
17
+ "#{@client.base_url}/#{@file.id}"
18
+ end
19
+
20
+ def create_subfolders(working_directory)
21
+ FileUtils.mkdir_p("#{working_directory}/#{destination_file_path}")
22
+ end
23
+
24
+ def destination_file_path
25
+ filename = @file.name.split('/').last
26
+
27
+ @file.name.gsub(filename, '').gsub(%r{^/}, '')
28
+ end
29
+
30
+ def valid_checksum?(file)
31
+ Digest::SHA256.hexdigest(File.read(file)) == @file.checksum
32
+ end
33
+
34
+ def download(working_directory)
35
+ url = URI("#{file_url}/download")
36
+
37
+ begin
38
+ destination_file = "#{working_directory}/#{@file.name}"
39
+
40
+ create_subfolders(working_directory)
41
+ File.open(destination_file, "wb") do |saved_file|
42
+ URI.open(url, "rb", { @client.authentication_key => @client.authentication_value }) do |data|
43
+ saved_file.write(data.read)
44
+ end
45
+
46
+ FileUtils.chmod('u=rw,go-r', destination_file)
47
+ end
48
+
49
+ UI.crash!("Checksum validation failed for #{@file.name}") unless valid_checksum?(destination_file)
50
+ rescue OpenURI::HTTPError => msg
51
+ UI.error("Unable to download #{@file.name} - #{msg}")
52
+ end
53
+ end
54
+
55
+ def delete
56
+ url = URI(file_url)
57
+
58
+ request = Net::HTTP::Delete.new(url.request_uri)
59
+
60
+ @client.execute_request(url, request)
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -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
@@ -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,16 @@ 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
+ if File.exist?(cer_certificate)
41
+ cer_certificate = File.binread(cer_certificate)
42
+ end
43
+ cert = OpenSSL::X509::Certificate.new(cer_certificate)
36
44
 
37
45
  # openssl output:
38
46
  # subject= /UID={User ID}/CN={Certificate Name}/OU={Certificate User}/O={Organisation}/C={Country}
@@ -81,7 +81,7 @@ module PEM
81
81
  p12_cert_path = File.join(output_path, "#{filename_base}.p12")
82
82
  p12_password = PEM.config[:p12_password] == "" ? nil : PEM.config[:p12_password]
83
83
  p12 = OpenSSL::PKCS12.create(p12_password, certificate_type, pkey, x509_certificate)
84
- File.write(p12_cert_path, p12.to_der)
84
+ File.write(p12_cert_path, p12.to_der.force_encoding("UTF-8"))
85
85
  UI.message("p12 certificate: ".green + Pathname.new(p12_cert_path).realpath.to_s)
86
86
  end
87
87
 
@@ -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")
@@ -40,12 +40,26 @@ module Sigh
40
40
  Spaceship::ConnectAPI::Profile::ProfileType::MAC_APP_DEVELOPMENT,
41
41
  Spaceship::ConnectAPI::Profile::ProfileType::MAC_APP_DIRECT
42
42
  ]
43
+
44
+ # As of 2022-06-25, only available with Apple ID auth
45
+ if Spaceship::ConnectAPI.token
46
+ UI.important("Skipping #{Spaceship::ConnectAPI::Profile::ProfileType::MAC_APP_INHOUSE}... only available with Apple ID auth")
47
+ else
48
+ profile_types << Spaceship::ConnectAPI::Profile::ProfileType::MAC_APP_INHOUSE
49
+ end
43
50
  when 'catalyst'
44
51
  profile_types = [
45
52
  Spaceship::ConnectAPI::Profile::ProfileType::MAC_CATALYST_APP_STORE,
46
53
  Spaceship::ConnectAPI::Profile::ProfileType::MAC_CATALYST_APP_DEVELOPMENT,
47
54
  Spaceship::ConnectAPI::Profile::ProfileType::MAC_CATALYST_APP_DIRECT
48
55
  ]
56
+
57
+ # As of 2022-06-25, only available with Apple ID auth
58
+ if Spaceship::ConnectAPI.token
59
+ UI.important("Skipping #{Spaceship::ConnectAPI::Profile::ProfileType::MAC_CATALYST_APP_INHOUSE}... only available with Apple ID auth")
60
+ else
61
+ profile_types << Spaceship::ConnectAPI::Profile::ProfileType::MAC_CATALYST_APP_INHOUSE
62
+ end
49
63
  when 'tvos'
50
64
  profile_types = [
51
65
  Spaceship::ConnectAPI::Profile::ProfileType::TVOS_APP_STORE,
@@ -55,8 +69,6 @@ module Sigh
55
69
  ]
56
70
  end
57
71
 
58
- # Filtering on 'profileType' seems to be undocumented as of 2020-07-30
59
- # but works on both web session and official API
60
72
  profiles = Spaceship::ConnectAPI::Profile.all(filter: { profileType: profile_types.join(",") }, includes: "bundleId")
61
73
  download_profiles(profiles)
62
74
  end
@@ -24,7 +24,9 @@ module Sigh
24
24
  Spaceship::ConnectAPI::Profile::ProfileType::TVOS_APP_ADHOC
25
25
  "AdHoc"
26
26
  when Spaceship::ConnectAPI::Profile::ProfileType::IOS_APP_INHOUSE,
27
- Spaceship::ConnectAPI::Profile::ProfileType::TVOS_APP_INHOUSE
27
+ Spaceship::ConnectAPI::Profile::ProfileType::TVOS_APP_INHOUSE,
28
+ Spaceship::ConnectAPI::Profile::ProfileType::MAC_APP_INHOUSE,
29
+ Spaceship::ConnectAPI::Profile::ProfileType::MAC_CATALYST_APP_INHOUSE
28
30
  "InHouse"
29
31
  when Spaceship::ConnectAPI::Profile::ProfileType::MAC_APP_DIRECT,
30
32
  Spaceship::ConnectAPI::Profile::ProfileType::MAC_CATALYST_APP_DIRECT
@@ -82,10 +82,12 @@ module Sigh
82
82
  @profile_type = Spaceship::ConnectAPI::Profile::ProfileType::TVOS_APP_DEVELOPMENT if Sigh.config[:development]
83
83
  when "macos"
84
84
  @profile_type = Spaceship::ConnectAPI::Profile::ProfileType::MAC_APP_STORE
85
+ @profile_type = Spaceship::ConnectAPI::Profile::ProfileType::MAC_APP_INHOUSE if Spaceship::ConnectAPI.client.in_house?
85
86
  @profile_type = Spaceship::ConnectAPI::Profile::ProfileType::MAC_APP_DEVELOPMENT if Sigh.config[:development]
86
87
  @profile_type = Spaceship::ConnectAPI::Profile::ProfileType::MAC_APP_DIRECT if Sigh.config[:developer_id]
87
88
  when "catalyst"
88
89
  @profile_type = Spaceship::ConnectAPI::Profile::ProfileType::MAC_CATALYST_APP_STORE
90
+ @profile_type = Spaceship::ConnectAPI::Profile::ProfileType::MAC_CATALYST_APP_INHOUSE if Spaceship::ConnectAPI.client.in_house?
89
91
  @profile_type = Spaceship::ConnectAPI::Profile::ProfileType::MAC_CATALYST_APP_DEVELOPMENT if Sigh.config[:development]
90
92
  @profile_type = Spaceship::ConnectAPI::Profile::ProfileType::MAC_CATALYST_APP_DIRECT if Sigh.config[:developer_id]
91
93
  end
@@ -254,6 +256,11 @@ module Sigh
254
256
  Spaceship::ConnectAPI::Certificate::CertificateType::DEVELOPER_ID_APPLICATION,
255
257
  Spaceship::ConnectAPI::Certificate::CertificateType::DEVELOPER_ID_APPLICATION_G2
256
258
  ]
259
+ elsif profile_type == Spaceship::ConnectAPI::Profile::ProfileType::MAC_APP_INHOUSE || profile_type == Spaceship::ConnectAPI::Profile::ProfileType::MAC_CATALYST_APP_INHOUSE
260
+ # Enterprise accounts don't have access to Apple Distribution certificates
261
+ types = [
262
+ Spaceship::ConnectAPI::Certificate::CertificateType::MAC_APP_DISTRIBUTION
263
+ ]
257
264
  else
258
265
  types = [
259
266
  Spaceship::ConnectAPI::Certificate::CertificateType::DISTRIBUTION,
@@ -133,7 +133,7 @@ module Snapshot
133
133
  is_string: false),
134
134
  FastlaneCore::ConfigItem.new(key: :override_status_bar_arguments,
135
135
  env_name: 'SNAPSHOT_OVERRIDE_STATUS_BAR_ARGUMENTS',
136
- description: "Fully customize the status bar by setting each option here. See `xcrun simctl status_bar --help`",
136
+ description: "Fully customize the status bar by setting each option here. Requires `override_status_bar` to be set to `true`. See `xcrun simctl status_bar --help`",
137
137
  optional: true,
138
138
  type: String),
139
139
  FastlaneCore::ConfigItem.new(key: :localize_simulator,
@@ -132,6 +132,7 @@ module Snapshot
132
132
  'iPad Pro (12.9-inch)' => 'iPad Pro (12.9-inch)',
133
133
  'iPad Pro (12.9 inch)' => 'iPad Pro (12.9-inch)', # iOS 10.3.1 simulator
134
134
  'iPad Pro' => 'iPad Pro (12.9-inch)', # iOS 9.3 simulator
135
+ 'iPod touch (7th generation)' => 'iPod touch (7th generation)',
135
136
  'Apple TV 1080p' => 'Apple TV',
136
137
  'Apple TV 4K (at 1080p)' => 'Apple TV 4K (at 1080p)',
137
138
  'Apple TV 4K' => 'Apple TV 4K',
@@ -135,7 +135,10 @@ module Snapshot
135
135
  if arguments.nil? || arguments.empty?
136
136
  # The time needs to be passed as ISO8601 so the simulator formats it correctly
137
137
  time = Time.new(2007, 1, 9, 9, 41, 0)
138
- arguments = "--time #{time.iso8601} --dataNetwork wifi --wifiMode active --wifiBars 3 --cellularMode active --cellularBars 4 --batteryState charged --batteryLevel 100"
138
+
139
+ # If you don't override the operator name, you'll get "Carrier" in the status bar on no-notch devices such as iPhone 8. Pass an empty string to blank it out.
140
+
141
+ arguments = "--time #{time.iso8601} --dataNetwork wifi --wifiMode active --wifiBars 3 --cellularMode active --operatorName '' --cellularBars 4 --batteryState charged --batteryLevel 100"
139
142
  end
140
143
 
141
144
  Helper.backticks("xcrun simctl status_bar #{device_udid} override #{arguments} &> /dev/null")
@@ -101,10 +101,12 @@ module Spaceship
101
101
  return client.get_app(app_id: app_id, includes: includes).first
102
102
  end
103
103
 
104
- def update(client: nil, attributes: nil, app_price_tier_id: nil, territory_ids: nil)
104
+ # Updates app attributes, price tier and availability of an app in territories
105
+ # Check Tunes patch_app method for explanation how to use territory_ids parameter with allow_removing_from_sale to remove app from sale
106
+ def update(client: nil, attributes: nil, app_price_tier_id: nil, territory_ids: nil, allow_removing_from_sale: false)
105
107
  client ||= Spaceship::ConnectAPI
106
108
  attributes = reverse_attr_mapping(attributes)
107
- return client.patch_app(app_id: id, attributes: attributes, app_price_tier_id: app_price_tier_id, territory_ids: territory_ids)
109
+ return client.patch_app(app_id: id, attributes: attributes, app_price_tier_id: app_price_tier_id, territory_ids: territory_ids, allow_removing_from_sale: allow_removing_from_sale)
108
110
  end
109
111
 
110
112
  #
@@ -51,6 +51,10 @@ module Spaceship
51
51
  MAC_CATALYST_APP_DEVELOPMENT = "MAC_CATALYST_APP_DEVELOPMENT"
52
52
  MAC_CATALYST_APP_STORE = "MAC_CATALYST_APP_STORE"
53
53
  MAC_CATALYST_APP_DIRECT = "MAC_CATALYST_APP_DIRECT"
54
+
55
+ # As of 2022-06-25, only available with Apple ID auth
56
+ MAC_APP_INHOUSE = "MAC_APP_INHOUSE"
57
+ MAC_CATALYST_APP_INHOUSE = "MAC_CATALYST_APP_INHOUSE"
54
58
  end
55
59
 
56
60
  def self.type
@@ -22,13 +22,17 @@ module Spaceship
22
22
  return links["next"]
23
23
  end
24
24
 
25
- def next_page
25
+ def next_page(&block)
26
26
  url = next_url
27
27
  return nil if url.nil?
28
- return client.get(url)
28
+ if block_given?
29
+ return yield(url)
30
+ else
31
+ return client.get(url)
32
+ end
29
33
  end
30
34
 
31
- def next_pages(count: 1)
35
+ def next_pages(count: 1, &block)
32
36
  if !count.nil? && count < 0
33
37
  count = 0
34
38
  end
@@ -38,7 +42,7 @@ module Spaceship
38
42
 
39
43
  resp = self
40
44
  loop do
41
- resp = resp.next_page
45
+ resp = resp.next_page(&block)
42
46
  break if resp.nil?
43
47
  responses << resp
44
48
  counter += 1
@@ -49,8 +53,8 @@ module Spaceship
49
53
  return responses
50
54
  end
51
55
 
52
- def all_pages
53
- return next_pages(count: nil)
56
+ def all_pages(&block)
57
+ return next_pages(count: nil, &block)
54
58
  end
55
59
 
56
60
  def to_models