adsedare 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/adsedare.gemspec +1 -1
- data/lib/adsedare/capabilities.rb +43 -38
- data/lib/adsedare/export_options.rb +90 -0
- data/lib/adsedare/keychain.rb +131 -0
- data/lib/adsedare/version.rb +1 -1
- data/lib/adsedare/xcodeproj.rb +76 -0
- data/lib/adsedare.rb +22 -287
- data/lib/appstoreconnect.rb +65 -65
- data/lib/starship/2fa_provider.rb +4 -4
- data/lib/starship/auth_helper.rb +87 -88
- data/lib/starship.rb +60 -60
- metadata +4 -1
data/lib/adsedare.rb
CHANGED
@@ -8,8 +8,11 @@ require "plist"
|
|
8
8
|
|
9
9
|
require_relative "adsedare/version"
|
10
10
|
require_relative "adsedare/capabilities"
|
11
|
-
require_relative "
|
11
|
+
require_relative "adsedare/keychain"
|
12
|
+
require_relative "adsedare/xcodeproj"
|
13
|
+
require_relative "adsedare/export_options"
|
12
14
|
|
15
|
+
require_relative "logging"
|
13
16
|
require_relative "starship"
|
14
17
|
require_relative "appstoreconnect"
|
15
18
|
|
@@ -22,20 +25,20 @@ module Adsedare
|
|
22
25
|
def renew_profiles(project_path = nil, certificate_id = nil, team_id = nil)
|
23
26
|
raise "Project path is not set" unless project_path
|
24
27
|
raise "Certificate ID is not set" unless certificate_id
|
25
|
-
|
28
|
+
|
26
29
|
project = Xcodeproj::Project.open(project_path)
|
27
30
|
project_dir = File.dirname(project_path)
|
28
|
-
|
31
|
+
|
29
32
|
bundle_entitlements = {}
|
30
33
|
|
31
34
|
project.targets.each do |target|
|
32
35
|
target.build_configurations.each do |config|
|
33
36
|
bundle_identifier = config.build_settings["PRODUCT_BUNDLE_IDENTIFIER"]
|
34
37
|
entitlements_path = config.build_settings["CODE_SIGN_ENTITLEMENTS"]
|
35
|
-
|
38
|
+
|
36
39
|
# If team_id is not set, use the first one from the project
|
37
40
|
team_id ||= config.build_settings["DEVELOPMENT_TEAM"]
|
38
|
-
|
41
|
+
|
39
42
|
if entitlements_path
|
40
43
|
full_entitlements_path = File.join(project_dir, entitlements_path)
|
41
44
|
bundle_entitlements[bundle_identifier] = full_entitlements_path
|
@@ -51,10 +54,10 @@ module Adsedare
|
|
51
54
|
unless bundle_id
|
52
55
|
logger.warn "Bundle '#{bundle_identifier}' is missing in Apple Developer portal. Will create."
|
53
56
|
bundle_id = Starship::Client.create_bundle(
|
54
|
-
bundle_identifier,
|
57
|
+
bundle_identifier,
|
55
58
|
team_id,
|
56
59
|
# You cannot create bundle without this capability
|
57
|
-
[
|
60
|
+
[SimpleCapability.new("IN_APP_PURCHASE").to_bundle_capability(nil, nil)]
|
58
61
|
)["data"]["id"]
|
59
62
|
bundle_by_identifier[bundle_identifier] = bundle_id
|
60
63
|
logger.info "Bundle '#{bundle_identifier}' created with ID '#{bundle_id}'"
|
@@ -88,7 +91,7 @@ module Adsedare
|
|
88
91
|
|
89
92
|
def install_profiles(project_path = nil)
|
90
93
|
raise "Project path is not set" unless project_path
|
91
|
-
|
94
|
+
|
92
95
|
project = Xcodeproj::Project.open(project_path)
|
93
96
|
|
94
97
|
project_bundles = project.targets.map do |target|
|
@@ -116,11 +119,11 @@ module Adsedare
|
|
116
119
|
next
|
117
120
|
end
|
118
121
|
|
119
|
-
logger.info "Bundle '#{bundle_identifier}' resolved to Bundle ID '#{bundle_id[
|
122
|
+
logger.info "Bundle '#{bundle_identifier}' resolved to Bundle ID '#{bundle_id["id"]}'"
|
120
123
|
|
121
124
|
profiles = bundle_id["relationships"]["profiles"]["data"]
|
122
125
|
unless profiles
|
123
|
-
logger.warn "Profile for Bundle ID '#{bundle_id[
|
126
|
+
logger.warn "Profile for Bundle ID '#{bundle_id["id"]}' is missing in App Store Connect. Skipping."
|
124
127
|
next
|
125
128
|
end
|
126
129
|
|
@@ -136,11 +139,11 @@ module Adsedare
|
|
136
139
|
end
|
137
140
|
|
138
141
|
unless ad_hoc_profile
|
139
|
-
logger.warn "Profile for Bundle ID '#{bundle_id[
|
142
|
+
logger.warn "Profile for Bundle ID '#{bundle_id["id"]}' is missing in App Store Connect. Skipping."
|
140
143
|
next
|
141
144
|
end
|
142
145
|
|
143
|
-
logger.info "Profile for Bundle ID '#{bundle_id[
|
146
|
+
logger.info "Profile for Bundle ID '#{bundle_id["id"]}' resolved to Profile '#{ad_hoc_profile["attributes"]["name"]}'"
|
144
147
|
|
145
148
|
uuid = ad_hoc_profile["attributes"]["uuid"]
|
146
149
|
profile_content = Base64.decode64(ad_hoc_profile["attributes"]["profileContent"])
|
@@ -149,275 +152,8 @@ module Adsedare
|
|
149
152
|
FileUtils.mkdir_p(File.dirname(profile_path))
|
150
153
|
File.write(profile_path, profile_content)
|
151
154
|
|
152
|
-
logger.info "Profile '#{ad_hoc_profile[
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
|
-
def create_keychain(keychain_path = nil, keychain_password = nil, make_default = true)
|
157
|
-
raise "Keychain path is not set" unless keychain_path
|
158
|
-
raise "Keychain password is not set" unless keychain_password
|
159
|
-
|
160
|
-
keychain_path = File.expand_path(keychain_path)
|
161
|
-
|
162
|
-
logger.info "Creating keychain at '#{keychain_path}'"
|
163
|
-
|
164
|
-
FileUtils.mkdir_p(File.dirname(keychain_path))
|
165
|
-
status = system("security create-keychain -p #{keychain_password} #{keychain_path}")
|
166
|
-
unless status
|
167
|
-
logger.error "Failed to create keychain at '#{keychain_path}'"
|
168
|
-
return
|
169
|
-
end
|
170
|
-
|
171
|
-
apple_certs = [
|
172
|
-
"AppleWWDRCAG2.cer",
|
173
|
-
"AppleWWDRCAG3.cer",
|
174
|
-
"AppleWWDRCAG4.cer",
|
175
|
-
"AppleWWDRCAG5.cer",
|
176
|
-
"AppleWWDRCAG6.cer",
|
177
|
-
"AppleWWDRCAG7.cer",
|
178
|
-
"AppleWWDRCAG8.cer",
|
179
|
-
"DeveloperIDG2CA.cer"
|
180
|
-
]
|
181
|
-
|
182
|
-
apple_certs.each do |cert|
|
183
|
-
logger.info "Downloading certificate '#{cert}'"
|
184
|
-
|
185
|
-
response = Faraday.get(
|
186
|
-
"https://www.apple.com/certificateauthority/#{cert}"
|
187
|
-
)
|
188
|
-
unless response.status == 200
|
189
|
-
logger.error "Failed to download certificate '#{cert}'"
|
190
|
-
next
|
191
|
-
end
|
192
|
-
|
193
|
-
file = Tempfile.new(cert)
|
194
|
-
file.write(response.body)
|
195
|
-
file.close
|
196
|
-
|
197
|
-
install_certificate(file.path, keychain_path)
|
198
|
-
|
199
|
-
file.unlink
|
200
|
-
end
|
201
|
-
|
202
|
-
logger.info "Downloading certificate 'AppleWWDRCA.cer'"
|
203
|
-
response = Faraday.get(
|
204
|
-
"https://developer.apple.com/certificationauthority/AppleWWDRCA.cer"
|
205
|
-
)
|
206
|
-
unless response.status == 200
|
207
|
-
logger.error "Failed to download certificate 'AppleWWDRCA.cer'"
|
208
|
-
else
|
209
|
-
file = Tempfile.new("AppleWWDRCA.cer")
|
210
|
-
file.write(response.body)
|
211
|
-
file.close
|
212
|
-
|
213
|
-
install_certificate(file.path, keychain_path)
|
214
|
-
|
215
|
-
file.unlink
|
216
|
-
end
|
217
|
-
|
218
|
-
ad_hoc_certificate = ENV["AD_HOC_CERTIFICATE"]
|
219
|
-
ad_hoc_private_key = ENV["AD_HOC_PRIVATE_KEY"]
|
220
|
-
ad_hoc_key_password = ENV["AD_HOC_KEY_PASSWORD"]
|
221
|
-
|
222
|
-
unless ad_hoc_certificate || ad_hoc_private_key || ad_hoc_key_password
|
223
|
-
logger.warn "AD_HOC_CERTIFICATE, AD_HOC_PRIVATE_KEY, or AD_HOC_KEY_PASSWORD is not set"
|
224
|
-
return
|
225
|
-
end
|
226
|
-
|
227
|
-
install_certificate(ad_hoc_private_key, keychain_path, ad_hoc_key_password, "priv")
|
228
|
-
install_certificate(ad_hoc_certificate, keychain_path, "", "cert")
|
229
|
-
|
230
|
-
if make_default
|
231
|
-
status = system("security default-keychain -d user -s #{keychain_path}")
|
232
|
-
unless status
|
233
|
-
logger.warn "Failed to set default keychain"
|
234
|
-
return
|
235
|
-
end
|
236
|
-
end
|
237
|
-
|
238
|
-
status = system("security set-keychain-settings #{keychain_path}")
|
239
|
-
unless status
|
240
|
-
logger.error "Failed to set keychain settings"
|
241
|
-
return
|
242
|
-
end
|
243
|
-
|
244
|
-
status = system("security set-key-partition-list -S apple-tool:,apple: -k #{keychain_password} #{keychain_path}")
|
245
|
-
unless status
|
246
|
-
logger.error "Failed to set keychain partition list"
|
247
|
-
return
|
248
|
-
end
|
249
|
-
|
250
|
-
status = system("security unlock-keychain -p #{keychain_password} #{keychain_path}")
|
251
|
-
unless status
|
252
|
-
logger.error "Failed to unlock keychain"
|
253
|
-
return
|
254
|
-
end
|
255
|
-
|
256
|
-
logger.info "Keychain created at '#{keychain_path}'"
|
257
|
-
end
|
258
|
-
|
259
|
-
def make_export_options(project_path = nil, export_path = nil, team_id = nil, options = {})
|
260
|
-
raise "Project path is not set" unless project_path
|
261
|
-
raise "Export path is not set" unless export_path
|
262
|
-
|
263
|
-
project = Xcodeproj::Project.open(project_path)
|
264
|
-
export_options = {
|
265
|
-
"method" => "ad-hoc",
|
266
|
-
"destination" => "export",
|
267
|
-
"signingStyle" => "manual",
|
268
|
-
"provisioningProfiles" => {}
|
269
|
-
}.merge(options)
|
270
|
-
|
271
|
-
project_bundles = []
|
272
|
-
|
273
|
-
project.targets.each do |target|
|
274
|
-
target.build_configurations.each do |config|
|
275
|
-
team_id ||= config.build_settings["DEVELOPMENT_TEAM"]
|
276
|
-
project_bundles << config.build_settings["PRODUCT_BUNDLE_IDENTIFIER"]
|
277
|
-
end
|
278
|
-
end
|
279
|
-
|
280
|
-
export_options["teamID"] = team_id
|
281
|
-
|
282
|
-
logger.info "Fetching bundles with profiles for team ID '#{team_id}'"
|
283
|
-
|
284
|
-
bundles_with_profiles = AppStoreConnect::Client.get_bundles_with_profiles(project_bundles)
|
285
|
-
bundle_by_identifier = {}
|
286
|
-
profiles_by_id = {}
|
287
|
-
|
288
|
-
bundles_with_profiles["data"].each do |bundle_id|
|
289
|
-
bundle_by_identifier[bundle_id["attributes"]["identifier"]] = bundle_id
|
290
|
-
end
|
291
|
-
|
292
|
-
bundles_with_profiles["included"].each do |profile|
|
293
|
-
profiles_by_id[profile["id"]] = profile
|
294
|
-
end
|
295
|
-
|
296
|
-
project_bundles.each do |bundle_identifier|
|
297
|
-
bundle_id = bundle_by_identifier[bundle_identifier]
|
298
|
-
unless bundle_id
|
299
|
-
logger.warn "Bundle '#{bundle_identifier}' is missing in App Store Connect. Skipping."
|
300
|
-
next
|
301
|
-
end
|
302
|
-
|
303
|
-
logger.info "Bundle '#{bundle_identifier}' resolved to Bundle ID '#{bundle_id['id']}'"
|
304
|
-
|
305
|
-
profiles = bundle_id["relationships"]["profiles"]["data"]
|
306
|
-
unless profiles
|
307
|
-
logger.warn "Profile for Bundle ID '#{bundle_id['id']}' is missing in App Store Connect. Skipping."
|
308
|
-
next
|
309
|
-
end
|
310
|
-
|
311
|
-
ad_hoc_profile = nil
|
312
|
-
profiles.each do |profile|
|
313
|
-
profile_id = profile["id"]
|
314
|
-
profile = profiles_by_id[profile_id]
|
315
|
-
|
316
|
-
if profile["attributes"]["profileType"] == "IOS_APP_ADHOC" && profile["attributes"]["profileState"] == "ACTIVE"
|
317
|
-
ad_hoc_profile = profile
|
318
|
-
break
|
319
|
-
end
|
320
|
-
end
|
321
|
-
|
322
|
-
unless ad_hoc_profile
|
323
|
-
logger.warn "Profile for Bundle ID '#{bundle_id['id']}' is missing in App Store Connect. Skipping."
|
324
|
-
next
|
325
|
-
end
|
326
|
-
|
327
|
-
logger.info "Profile for Bundle ID '#{bundle_id['id']}' resolved to Profile '#{ad_hoc_profile['attributes']['name']}'"
|
328
|
-
|
329
|
-
profile_name = ad_hoc_profile["attributes"]["name"]
|
330
|
-
|
331
|
-
export_options["provisioningProfiles"][bundle_identifier] = profile_name
|
332
|
-
end
|
333
|
-
|
334
|
-
options_plist = Plist::Emit.dump(export_options)
|
335
|
-
File.write(export_path, options_plist)
|
336
|
-
|
337
|
-
return export_options
|
338
|
-
end
|
339
|
-
|
340
|
-
def patch_project(project_path, team_id = nil)
|
341
|
-
raise "Project path is not set" unless project_path
|
342
|
-
|
343
|
-
project = Xcodeproj::Project.open(project_path)
|
344
|
-
|
345
|
-
project_bundles = project.targets.map do |target|
|
346
|
-
target.build_configurations.map do |config|
|
347
|
-
config.build_settings["PRODUCT_BUNDLE_IDENTIFIER"]
|
348
|
-
end
|
349
|
-
end.flatten.uniq
|
350
|
-
|
351
|
-
bundles_with_profiles = AppStoreConnect::Client.get_bundles_with_profiles(project_bundles)
|
352
|
-
bundle_by_identifier = {}
|
353
|
-
profiles_by_id = {}
|
354
|
-
|
355
|
-
bundles_with_profiles["data"].each do |bundle_id|
|
356
|
-
bundle_by_identifier[bundle_id["attributes"]["identifier"]] = bundle_id
|
357
|
-
end
|
358
|
-
|
359
|
-
bundles_with_profiles["included"].each do |profile|
|
360
|
-
profiles_by_id[profile["id"]] = profile
|
361
|
-
end
|
362
|
-
|
363
|
-
project.targets.each do |target|
|
364
|
-
target.build_configurations.each do |config|
|
365
|
-
bundle_identifier = config.build_settings["PRODUCT_BUNDLE_IDENTIFIER"]
|
366
|
-
bundle_id = bundle_by_identifier[bundle_identifier]
|
367
|
-
unless bundle_id
|
368
|
-
logger.warn "Bundle '#{bundle_identifier}' is missing in App Store Connect. Skipping."
|
369
|
-
next
|
370
|
-
end
|
371
|
-
|
372
|
-
logger.info "Bundle '#{bundle_identifier}' resolved to Bundle ID '#{bundle_id['id']}'"
|
373
|
-
|
374
|
-
profiles = bundle_id["relationships"]["profiles"]["data"]
|
375
|
-
unless profiles
|
376
|
-
logger.warn "Profile for Bundle ID '#{bundle_id['id']}' is missing in App Store Connect. Skipping."
|
377
|
-
next
|
378
|
-
end
|
379
|
-
|
380
|
-
ad_hoc_profile = nil
|
381
|
-
profiles.each do |profile|
|
382
|
-
profile_id = profile["id"]
|
383
|
-
profile = profiles_by_id[profile_id]
|
384
|
-
|
385
|
-
if profile["attributes"]["profileType"] == "IOS_APP_ADHOC" && profile["attributes"]["profileState"] == "ACTIVE"
|
386
|
-
ad_hoc_profile = profile
|
387
|
-
break
|
388
|
-
end
|
389
|
-
end
|
390
|
-
|
391
|
-
unless ad_hoc_profile
|
392
|
-
logger.warn "Profile for Bundle ID '#{bundle_id['id']}' is missing in App Store Connect. Skipping."
|
393
|
-
next
|
394
|
-
end
|
395
|
-
|
396
|
-
config.build_settings["CODE_SIGN_IDENTITY"] = "iPhone Distribution"
|
397
|
-
config.build_settings["CODE_SIGN_STYLE"] = "Manual"
|
398
|
-
if team_id
|
399
|
-
config.build_settings["DEVELOPMENT_TEAM"] = team_id
|
400
|
-
end
|
401
|
-
config.build_settings["PROVISIONING_PROFILE_SPECIFIER"] = ad_hoc_profile["attributes"]["name"]
|
402
|
-
end
|
155
|
+
logger.info "Profile '#{ad_hoc_profile["attributes"]["name"]}' installed to '#{profile_path}'"
|
403
156
|
end
|
404
|
-
|
405
|
-
project.save
|
406
|
-
end
|
407
|
-
|
408
|
-
private
|
409
|
-
|
410
|
-
def install_certificate(certificate_path, keychain_path, certificate_password = "", certificate_type = "cert")
|
411
|
-
certificate_name = File.basename(certificate_path)
|
412
|
-
logger.info "Installing certificate '#{certificate_name}' to keychain '#{keychain_path}'"
|
413
|
-
|
414
|
-
status = system("security import #{certificate_path} -k #{keychain_path} -t #{certificate_type} -A -P #{certificate_password} -T /usr/bin/codesign -T /usr/bin/security -T /usr/bin/productbuild")
|
415
|
-
unless status
|
416
|
-
logger.error "Failed to install certificate '#{certificate_name}' to keychain '#{keychain_path}'"
|
417
|
-
return
|
418
|
-
end
|
419
|
-
|
420
|
-
logger.info "Certificate '#{certificate_name}' installed to keychain '#{keychain_path}'"
|
421
157
|
end
|
422
158
|
|
423
159
|
def get_devices(team_id)
|
@@ -474,7 +210,7 @@ module Adsedare
|
|
474
210
|
|
475
211
|
if need_update
|
476
212
|
logger.warn "Profile '#{profile["provisioningProfile"]["name"]}' is missing one or more devices."
|
477
|
-
|
213
|
+
|
478
214
|
Starship::Client.regen_provisioning_profile(profile, team_id, deviceIds)
|
479
215
|
|
480
216
|
logger.info "Profile '#{profile["provisioningProfile"]["name"]}' updated."
|
@@ -490,7 +226,7 @@ module Adsedare
|
|
490
226
|
logger.info "Checking capabilities for bundle '#{bundle_identifier}'"
|
491
227
|
|
492
228
|
capabilities = parse_entitlements(entitlements_path)
|
493
|
-
|
229
|
+
|
494
230
|
need_update = false
|
495
231
|
|
496
232
|
capabilities.each do |capability|
|
@@ -502,11 +238,10 @@ module Adsedare
|
|
502
238
|
|
503
239
|
if need_update
|
504
240
|
logger.warn "Bundle '#{bundle_identifier}' is missing one or more capabilities."
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
241
|
+
# You can't remove IN_APP_PURCHASE capability for some reason
|
242
|
+
new_capabilities = capabilities + [SimpleCapability.new("IN_APP_PURCHASE")]
|
243
|
+
new_capabilities = new_capabilities.map { |capability| capability.to_bundle_capability(bundle_info, team_id) }
|
244
|
+
|
510
245
|
Starship::Client.patch_bundle(bundle_info, team_id, new_capabilities)
|
511
246
|
|
512
247
|
logger.info "Bundle '#{bundle_identifier}' capabilities updated."
|
data/lib/appstoreconnect.rb
CHANGED
@@ -7,80 +7,80 @@ require "jwt"
|
|
7
7
|
module AppStoreConnect
|
8
8
|
class Client
|
9
9
|
BASE_URL = "https://api.appstoreconnect.apple.com/v1"
|
10
|
-
|
10
|
+
|
11
11
|
class << self
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
if response.status == 200
|
25
|
-
JSON.parse(response.body)
|
26
|
-
else
|
27
|
-
puts response.body
|
28
|
-
raise "Failed to get bundle IDs: #{response.status}"
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
private
|
12
|
+
def get_bundles_with_profiles(bundle_identifiers)
|
13
|
+
response = request(
|
14
|
+
BASE_URL + "/bundleIds",
|
15
|
+
method: :get,
|
16
|
+
params: {
|
17
|
+
"fields[bundleIds]" => "name,platform,identifier,profiles",
|
18
|
+
"filter[identifier]" => bundle_identifiers.join(","),
|
19
|
+
"fields[profiles]" => "name,profileType,profileState,profileContent,uuid",
|
20
|
+
"include" => "profiles",
|
21
|
+
},
|
22
|
+
)
|
33
23
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
24
|
+
if response.status == 200
|
25
|
+
JSON.parse(response.body)
|
26
|
+
else
|
27
|
+
puts response.body
|
28
|
+
raise "Failed to get bundle IDs: #{response.status}"
|
29
|
+
end
|
30
|
+
end
|
40
31
|
|
41
|
-
|
42
|
-
default_headers = default_headers.merge(headers)
|
43
|
-
end
|
32
|
+
private
|
44
33
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
Faraday.put(endpoint, body, default_headers)
|
52
|
-
when :delete
|
53
|
-
Faraday.delete(endpoint, default_headers)
|
54
|
-
when :patch
|
55
|
-
Faraday.patch(endpoint, body, default_headers)
|
56
|
-
end
|
34
|
+
def request(endpoint, method: :get, params: nil, body: nil, headers: nil)
|
35
|
+
default_headers = {
|
36
|
+
"Authorization" => "Bearer #{jwt_token}",
|
37
|
+
"Content-Type" => "application/json",
|
38
|
+
"Accept" => "application/json",
|
39
|
+
}
|
57
40
|
|
58
|
-
|
41
|
+
if headers
|
42
|
+
default_headers = default_headers.merge(headers)
|
59
43
|
end
|
60
44
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
45
|
+
response = case method
|
46
|
+
when :get
|
47
|
+
Faraday.get(endpoint, params, default_headers)
|
48
|
+
when :post
|
49
|
+
Faraday.post(endpoint, body, default_headers)
|
50
|
+
when :put
|
51
|
+
Faraday.put(endpoint, body, default_headers)
|
52
|
+
when :delete
|
53
|
+
Faraday.delete(endpoint, default_headers)
|
54
|
+
when :patch
|
55
|
+
Faraday.patch(endpoint, body, default_headers)
|
56
|
+
end
|
65
57
|
|
66
|
-
|
67
|
-
|
68
|
-
{
|
69
|
-
iss: issuer_id,
|
70
|
-
exp: Time.now.to_i + 20 * 60,
|
71
|
-
aud: "appstoreconnect-v1",
|
72
|
-
},
|
73
|
-
private_key,
|
74
|
-
"ES256",
|
75
|
-
header_fields = {
|
76
|
-
alg: "ES256",
|
77
|
-
kid: key_id,
|
78
|
-
typ: "JWT"
|
79
|
-
}
|
80
|
-
)
|
58
|
+
return response
|
59
|
+
end
|
81
60
|
|
82
|
-
|
83
|
-
|
61
|
+
def jwt_token
|
62
|
+
key_id = ENV["APPSTORE_CONNECT_KEY_ID"]
|
63
|
+
key = ENV["APPSTORE_CONNECT_KEY"]
|
64
|
+
issuer_id = ENV["APPSTORE_CONNECT_ISSUER_ID"]
|
65
|
+
|
66
|
+
private_key = OpenSSL::PKey.read(key)
|
67
|
+
token = JWT.encode(
|
68
|
+
{
|
69
|
+
iss: issuer_id,
|
70
|
+
exp: Time.now.to_i + 20 * 60,
|
71
|
+
aud: "appstoreconnect-v1",
|
72
|
+
},
|
73
|
+
private_key,
|
74
|
+
"ES256",
|
75
|
+
header_fields = {
|
76
|
+
alg: "ES256",
|
77
|
+
kid: key_id,
|
78
|
+
typ: "JWT",
|
79
|
+
}
|
80
|
+
)
|
81
|
+
|
82
|
+
return token
|
83
|
+
end
|
84
84
|
end
|
85
85
|
end
|
86
86
|
end
|
@@ -6,7 +6,7 @@ module Starship
|
|
6
6
|
class TwoFactorProvider
|
7
7
|
def initialize
|
8
8
|
end
|
9
|
-
|
9
|
+
|
10
10
|
# Get the 2FA code
|
11
11
|
# @param session_id [String] The session ID from Apple
|
12
12
|
# @param scnt [String] The scnt value from Apple
|
@@ -14,7 +14,7 @@ module Starship
|
|
14
14
|
def get_code(session_id, scnt)
|
15
15
|
raise NotImplementedError, "Subclasses must implement get_code"
|
16
16
|
end
|
17
|
-
|
17
|
+
|
18
18
|
# Verify if this provider can handle the given 2FA type
|
19
19
|
# @param type [String] The 2FA type (sms, voice, etc.)
|
20
20
|
# @return [Boolean] Whether this provider can handle the given type
|
@@ -22,7 +22,7 @@ module Starship
|
|
22
22
|
raise NotImplementedError, "Subclasses must implement can_handle?"
|
23
23
|
end
|
24
24
|
end
|
25
|
-
|
25
|
+
|
26
26
|
# Manual 2FA provider that prompts the user for a code
|
27
27
|
class ManualTwoFactorProvider < TwoFactorProvider
|
28
28
|
include Logging
|
@@ -32,7 +32,7 @@ module Starship
|
|
32
32
|
code = gets.chomp.strip
|
33
33
|
code
|
34
34
|
end
|
35
|
-
|
35
|
+
|
36
36
|
def can_handle?(type)
|
37
37
|
true
|
38
38
|
end
|