fastlane-plugin-appcenter 1.7.0 → 1.11.0

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.
@@ -1,7 +1,22 @@
1
+ class File
2
+ def each_chunk(chunk_size)
3
+ yield read(chunk_size) until eof?
4
+ end
5
+ end
6
+
1
7
  module Fastlane
2
8
  module Helper
3
9
  class AppcenterHelper
4
10
 
11
+ # Time to wait between 2 status polls in seconds
12
+ RELEASE_UPLOAD_STATUS_POLL_INTERVAL = 1
13
+
14
+ # Maximum number of retries for a request
15
+ MAX_REQUEST_RETRIES = 2
16
+
17
+ # Delay between retries in seconds
18
+ REQUEST_RETRY_INTERVAL = 5
19
+
5
20
  # basic utility method to check file types that App Center will accept,
6
21
  # accounting for file types that can and should be zip-compressed
7
22
  # before they are uploaded
@@ -18,10 +33,16 @@ module Fastlane
18
33
  require 'faraday'
19
34
  require 'faraday_middleware'
20
35
 
36
+ default_api_url = "https://api.appcenter.ms"
37
+ if ENV['APPCENTER_ENV']&.upcase == 'INT'
38
+ default_api_url = "https://api-gateway-core-integration.dev.avalanch.es"
39
+ end
21
40
  options = {
22
- url: upload_url || ENV.fetch('APPCENTER_UPLOAD_URL', "https://api.appcenter.ms")
41
+ url: upload_url || default_api_url
23
42
  }
24
43
 
44
+ UI.message("DEBUG: BASE URL #{options[:url]}") if ENV['DEBUG']
45
+
25
46
  Faraday.new(options) do |builder|
26
47
  if upload_url
27
48
  builder.request :multipart unless dsym
@@ -41,16 +62,20 @@ module Fastlane
41
62
  # upload_url
42
63
  def self.create_release_upload(api_token, owner_name, app_name, body)
43
64
  connection = self.connection
65
+ url = "v0.1/apps/#{owner_name}/#{app_name}/uploads/releases"
66
+ body ||= {}
44
67
 
45
- response = connection.post("v0.1/apps/#{owner_name}/#{app_name}/release_uploads") do |req|
68
+ UI.message("DEBUG: POST #{url}") if ENV['DEBUG']
69
+ UI.message("DEBUG: POST body: #{JSON.pretty_generate(body)}\n") if ENV['DEBUG']
70
+ response = connection.post(url) do |req|
46
71
  req.headers['X-API-Token'] = api_token
47
72
  req.headers['internal-request-source'] = "fastlane"
48
- req.body = body.nil? && {} || body
73
+ req.body = body
49
74
  end
50
75
 
76
+ UI.message("DEBUG: #{response.status} #{JSON.pretty_generate(response.body)}\n") if ENV['DEBUG']
51
77
  case response.status
52
78
  when 200...300
53
- UI.message("DEBUG: #{JSON.pretty_generate(response.body)}\n") if ENV['DEBUG']
54
79
  response.body
55
80
  when 401
56
81
  UI.user_error!("Auth Error, provided invalid token")
@@ -59,7 +84,7 @@ module Fastlane
59
84
  UI.error("Not found, invalid owner or application name")
60
85
  false
61
86
  when 500...600
62
- UI.crash!("Internal Service Error, please try again later")
87
+ UI.abort_with_message!("Internal Service Error, please try again later")
63
88
  else
64
89
  UI.error("Error #{response.status}: #{response.body}")
65
90
  false
@@ -74,20 +99,27 @@ module Fastlane
74
99
  def self.create_mapping_upload(api_token, owner_name, app_name, file_name, build_number, version)
75
100
  connection = self.connection
76
101
 
77
- response = connection.post("v0.1/apps/#{owner_name}/#{app_name}/symbol_uploads") do |req|
102
+ url = "v0.1/apps/#{owner_name}/#{app_name}/symbol_uploads"
103
+ body = {
104
+ symbol_type: "AndroidProguard",
105
+ file_name: file_name,
106
+ build: build_number,
107
+ version: version,
108
+ }
109
+
110
+ UI.message("DEBUG: POST #{url}") if ENV['DEBUG']
111
+ UI.message("DEBUG: POST body #{JSON.pretty_generate(body)}\n") if ENV['DEBUG']
112
+
113
+ response = connection.post(url) do |req|
78
114
  req.headers['X-API-Token'] = api_token
79
115
  req.headers['internal-request-source'] = "fastlane"
80
- req.body = {
81
- symbol_type: "AndroidProguard",
82
- file_name: file_name,
83
- build: build_number,
84
- version: version,
85
- }
116
+ req.body = body
86
117
  end
87
118
 
119
+ UI.message("DEBUG: #{response.status} #{JSON.pretty_generate(response.body)}\n") if ENV['DEBUG']
120
+
88
121
  case response.status
89
122
  when 200...300
90
- UI.message("DEBUG: #{JSON.pretty_generate(response.body)}\n") if ENV['DEBUG']
91
123
  response.body
92
124
  when 401
93
125
  UI.user_error!("Auth Error, provided invalid token")
@@ -109,17 +141,24 @@ module Fastlane
109
141
  def self.create_dsym_upload(api_token, owner_name, app_name)
110
142
  connection = self.connection
111
143
 
112
- response = connection.post("v0.1/apps/#{owner_name}/#{app_name}/symbol_uploads") do |req|
144
+ url = "v0.1/apps/#{owner_name}/#{app_name}/symbol_uploads"
145
+ body = {
146
+ symbol_type: 'Apple'
147
+ }
148
+
149
+ UI.message("DEBUG: POST #{url}") if ENV['DEBUG']
150
+ UI.message("DEBUG: POST body #{JSON.pretty_generate(body)}\n") if ENV['DEBUG']
151
+
152
+ response = connection.post(url) do |req|
113
153
  req.headers['X-API-Token'] = api_token
114
154
  req.headers['internal-request-source'] = "fastlane"
115
- req.body = {
116
- symbol_type: 'Apple'
117
- }
155
+ req.body = body
118
156
  end
119
157
 
158
+ UI.message("DEBUG: #{response.status} #{JSON.pretty_generate(response.body)}\n") if ENV['DEBUG']
159
+
120
160
  case response.status
121
161
  when 200...300
122
- UI.message("DEBUG: #{JSON.pretty_generate(response.body)}\n") if ENV['DEBUG']
123
162
  response.body
124
163
  when 401
125
164
  UI.user_error!("Auth Error, provided invalid token")
@@ -137,17 +176,24 @@ module Fastlane
137
176
  def self.update_symbol_upload(api_token, owner_name, app_name, symbol_upload_id, status)
138
177
  connection = self.connection
139
178
 
140
- response = connection.patch("v0.1/apps/#{owner_name}/#{app_name}/symbol_uploads/#{symbol_upload_id}") do |req|
179
+ url = "v0.1/apps/#{owner_name}/#{app_name}/symbol_uploads/#{symbol_upload_id}"
180
+ body = {
181
+ status: status
182
+ }
183
+
184
+ UI.message("DEBUG: PATCH #{url}") if ENV['DEBUG']
185
+ UI.message("DEBUG: PATCH body #{JSON.pretty_generate(body)}\n") if ENV['DEBUG']
186
+
187
+ response = connection.patch(url) do |req|
141
188
  req.headers['X-API-Token'] = api_token
142
189
  req.headers['internal-request-source'] = "fastlane"
143
- req.body = {
144
- "status" => status
145
- }
190
+ req.body = body
146
191
  end
147
192
 
193
+ UI.message("DEBUG: #{response.status} #{JSON.pretty_generate(response.body)}\n") if ENV['DEBUG']
194
+
148
195
  case response.status
149
196
  when 200...300
150
- UI.message("DEBUG: #{JSON.pretty_generate(response.body)}\n") if ENV['DEBUG']
151
197
  response.body
152
198
  when 401
153
199
  UI.user_error!("Auth Error, provided invalid token")
@@ -164,6 +210,9 @@ module Fastlane
164
210
  def self.upload_symbol(api_token, owner_name, app_name, symbol, symbol_type, symbol_upload_id, upload_url)
165
211
  connection = self.connection(upload_url, true)
166
212
 
213
+ UI.message("DEBUG: PUT #{upload_url}") if ENV['DEBUG']
214
+ UI.message("DEBUG: PUT body <data>\n") if ENV['DEBUG']
215
+
167
216
  response = connection.put do |req|
168
217
  req.headers['x-ms-blob-type'] = "BlockBlob"
169
218
  req.headers['Content-Length'] = File.size(symbol).to_s
@@ -171,77 +220,183 @@ module Fastlane
171
220
  req.body = Faraday::UploadIO.new(symbol, 'application/octet-stream') if symbol && File.exist?(symbol)
172
221
  end
173
222
 
174
- logType = "dSYM" if (symbol_type == "Apple")
175
- logType = "mapping" if (symbol_type == "Android")
223
+ UI.message("DEBUG: #{response.status} #{JSON.pretty_generate(response.body)}\n") if ENV['DEBUG']
224
+
225
+ log_type = "dSYM" if symbol_type == "Apple"
226
+ log_type = "mapping" if symbol_type == "Android"
176
227
 
177
228
  case response.status
178
229
  when 200...300
179
230
  self.update_symbol_upload(api_token, owner_name, app_name, symbol_upload_id, 'committed')
180
- UI.success("#{logType} uploaded")
231
+ UI.success("#{log_type} uploaded")
181
232
  when 401
182
233
  UI.user_error!("Auth Error, provided invalid token")
183
234
  false
184
235
  else
185
- UI.error("Error uploading #{logType} #{response.status}: #{response.body}")
236
+ UI.error("Error uploading #{log_type} #{response.status}: #{response.body}")
186
237
  self.update_symbol_upload(api_token, owner_name, app_name, symbol_upload_id, 'aborted')
187
- UI.error("#{logType} upload aborted")
238
+ UI.error("#{log_type} upload aborted")
188
239
  false
189
240
  end
190
241
  end
191
242
 
192
- # upload binary for specified upload_url
193
- # if succeed, then commits the release
194
- # otherwise aborts
195
- def self.upload_build(api_token, owner_name, app_name, file, upload_id, upload_url, timeout)
196
- connection = self.connection(upload_url)
243
+ # sets metadata for new upload in App Center
244
+ # returns:
245
+ # chunk size
246
+ def self.set_release_upload_metadata(set_metadata_url, api_token, owner_name, app_name, upload_id, timeout)
247
+ connection = self.connection(set_metadata_url)
197
248
 
198
- options = {}
199
- options[:upload_id] = upload_id
200
- # ipa field is used for .apk, .aab and .ipa files
201
- options[:ipa] = Faraday::UploadIO.new(file, 'application/octet-stream') if file && File.exist?(file)
249
+ UI.message("DEBUG: POST #{set_metadata_url}") if ENV['DEBUG']
250
+ UI.message("DEBUG: POST body <data>\n") if ENV['DEBUG']
251
+ response = connection.post do |req|
252
+ req.options.timeout = timeout
253
+ req.headers['internal-request-source'] = "fastlane"
254
+ end
255
+ UI.message("DEBUG: #{response.status} #{JSON.pretty_generate(response.body)}\n") if ENV['DEBUG']
202
256
 
257
+ case response.status
258
+ when 200...300
259
+ chunk_size = response.body['chunk_size']
260
+ unless chunk_size.is_a? Integer
261
+ UI.error("Set metadata didn't return chunk size: #{response.status}: #{response.body}")
262
+ false
263
+ else
264
+ UI.message("Metadata set")
265
+ chunk_size
266
+ end
267
+ when 401
268
+ UI.user_error!("Auth Error, provided invalid token")
269
+ false
270
+ else
271
+ UI.error("Error setting metadata: #{response.status}: #{response.body}")
272
+ false
273
+ end
274
+ end
275
+
276
+ # Verifies a successful upload to App Center
277
+ # returns:
278
+ # successful upload response body.
279
+ def self.finish_release_upload(finish_url, api_token, owner_name, app_name, upload_id, timeout)
280
+ connection = self.connection(finish_url)
281
+
282
+ UI.message("DEBUG: POST #{finish_url}") if ENV['DEBUG']
203
283
  response = connection.post do |req|
204
284
  req.options.timeout = timeout
205
285
  req.headers['internal-request-source'] = "fastlane"
206
- req.body = options
207
286
  end
287
+ UI.message("DEBUG: #{response.status} #{JSON.pretty_generate(response.body)}\n") if ENV['DEBUG']
208
288
 
209
289
  case response.status
210
290
  when 200...300
211
- UI.message("Binary uploaded")
212
- self.update_release_upload(api_token, owner_name, app_name, upload_id, 'committed')
291
+ if response.body['error'] == false
292
+ UI.message("Upload finished")
293
+ self.update_release_upload(api_token, owner_name, app_name, upload_id, 'uploadFinished')
294
+ else
295
+ UI.error("Error finishing upload: #{response.body['message']}")
296
+ false
297
+ end
213
298
  when 401
214
299
  UI.user_error!("Auth Error, provided invalid token")
215
300
  false
216
301
  else
217
- UI.error("Error uploading binary #{response.status}: #{response.body}")
218
- self.update_release_upload(api_token, owner_name, app_name, upload_id, 'aborted')
219
- UI.error("Release aborted")
302
+ UI.error("Error finishing upload: #{response.status}: #{response.body}")
220
303
  false
221
304
  end
222
305
  end
223
306
 
307
+ # upload binary for specified upload_url
308
+ # if succeed, then commits the release
309
+ # otherwise aborts
310
+ def self.upload_build(api_token, owner_name, app_name, file, upload_id, upload_url, content_type, chunk_size, timeout)
311
+ block_number = 1
312
+
313
+ File.open(file).each_chunk(chunk_size) do |chunk|
314
+ upload_chunk_url = "#{upload_url}&block_number=#{block_number}"
315
+ retries = 0
316
+
317
+ while retries <= MAX_REQUEST_RETRIES
318
+ begin
319
+ connection = self.connection(upload_chunk_url, true)
320
+
321
+ UI.message("DEBUG: POST #{upload_chunk_url}") if ENV['DEBUG']
322
+ UI.message("DEBUG: POST body <data>\n") if ENV['DEBUG']
323
+ response = connection.post do |req|
324
+ req.options.timeout = timeout
325
+ req.headers['internal-request-source'] = "fastlane"
326
+ req.headers['Content-Length'] = chunk.length.to_s
327
+ req.headers['Content-Type'] = 'application/octet-stream'
328
+ req.body = chunk
329
+ end
330
+ UI.message("DEBUG: #{response.status} #{JSON.pretty_generate(response.body)}\n") if ENV['DEBUG']
331
+ status = response.status
332
+ message = response.body
333
+ rescue Faraday::Error => e
334
+
335
+ # Low level HTTP errors, we will retry them
336
+ status = 0
337
+ message = e.message
338
+ end
339
+
340
+ case status
341
+ when 200...300
342
+ if response.body['error'] == false
343
+ UI.message("Chunk uploaded")
344
+ block_number += 1
345
+ break
346
+ else
347
+ UI.error("Error uploading binary #{response.body['message']}")
348
+ return false
349
+ end
350
+ when 401
351
+ UI.user_error!("Auth Error, provided invalid token")
352
+ return false
353
+ when 400...407, 409...428, 430...499
354
+ UI.user_error!("Client error: #{response.status}: #{response.body}")
355
+ return false
356
+ else
357
+ if retries < MAX_REQUEST_RETRIES
358
+ UI.message("DEBUG: Retryable error uploading binary #{status}: #{message}")
359
+ retries += 1
360
+ sleep(REQUEST_RETRY_INTERVAL)
361
+ else
362
+ UI.error("Error uploading binary #{status}: #{message}")
363
+ return false
364
+ end
365
+ end
366
+ end
367
+ end
368
+ UI.message("Binary uploaded")
369
+ end
370
+
224
371
  # Commits or aborts the upload process for a release
225
372
  def self.update_release_upload(api_token, owner_name, app_name, upload_id, status)
226
373
  connection = self.connection
227
374
 
228
- response = connection.patch("v0.1/apps/#{owner_name}/#{app_name}/release_uploads/#{upload_id}") do |req|
375
+ url = "v0.1/apps/#{owner_name}/#{app_name}/uploads/releases/#{upload_id}"
376
+ body = {
377
+ upload_status: status,
378
+ id: upload_id
379
+ }
380
+
381
+ UI.message("DEBUG: PATCH #{url}") if ENV['DEBUG']
382
+ UI.message("DEBUG: PATCH body #{JSON.pretty_generate(body)}\n") if ENV['DEBUG']
383
+
384
+ response = connection.patch(url) do |req|
229
385
  req.headers['X-API-Token'] = api_token
230
386
  req.headers['internal-request-source'] = "fastlane"
231
- req.body = {
232
- "status" => status
233
- }
387
+ req.body = body
234
388
  end
235
389
 
390
+ UI.message("DEBUG: #{response.status} #{JSON.pretty_generate(response.body)}\n") if ENV['DEBUG']
391
+
236
392
  case response.status
237
393
  when 200...300
238
- UI.message("DEBUG: #{JSON.pretty_generate(response.body)}\n") if ENV['DEBUG']
239
394
  response.body
240
395
  when 401
241
396
  UI.user_error!("Auth Error, provided invalid token")
242
397
  false
243
398
  when 500...600
244
- UI.crash!("Internal Service Error, please try again later")
399
+ UI.abort_with_message!("Internal Service Error, please try again later")
245
400
  else
246
401
  UI.error("Error #{response.status}: #{response.body}")
247
402
  false
@@ -251,15 +406,21 @@ module Fastlane
251
406
  # get existing release
252
407
  def self.get_release(api_token, owner_name, app_name, release_id)
253
408
  connection = self.connection
254
- response = connection.get("v0.1/apps/#{owner_name}/#{app_name}/releases/#{release_id}") do |req|
409
+
410
+ url = "v0.1/apps/#{owner_name}/#{app_name}/releases/#{release_id}"
411
+
412
+ UI.message("DEBUG: GET #{url}") if ENV['DEBUG']
413
+
414
+ response = connection.get(url) do |req|
255
415
  req.headers['X-API-Token'] = api_token
256
416
  req.headers['internal-request-source'] = "fastlane"
257
417
  end
258
418
 
419
+ UI.message("DEBUG: #{response.status} #{JSON.pretty_generate(response.body)}\n") if ENV['DEBUG']
420
+
259
421
  case response.status
260
422
  when 200...300
261
423
  release = response.body
262
- UI.message("DEBUG: #{JSON.pretty_generate(release)}") if ENV['DEBUG']
263
424
  release
264
425
  when 404
265
426
  UI.error("Not found, invalid release url")
@@ -273,19 +434,58 @@ module Fastlane
273
434
  end
274
435
  end
275
436
 
437
+ # Polls the upload for a release id. When a release is uploaded, we have to check
438
+ # for a successful extraction before we can continue.
439
+ # returns:
440
+ # release_distinct_id
441
+ def self.poll_for_release_id(api_token, url)
442
+ connection = self.connection
443
+
444
+ while true
445
+ UI.message("DEBUG: GET #{url}") if ENV['DEBUG']
446
+ response = connection.get(url) do |req|
447
+ req.headers['X-API-Token'] = api_token
448
+ req.headers['internal-request-source'] = "fastlane"
449
+ end
450
+
451
+ UI.message("DEBUG: #{response.status} #{JSON.pretty_generate(response.body)}\n") if ENV['DEBUG']
452
+
453
+ case response.status
454
+ when 200...300
455
+ case response.body['upload_status']
456
+ when "readyToBePublished"
457
+ return response.body['release_distinct_id']
458
+ when "error"
459
+ UI.error("Error fetching release: #{response.body['error_details']}")
460
+ return false
461
+ else
462
+ sleep(RELEASE_UPLOAD_STATUS_POLL_INTERVAL)
463
+ end
464
+ else
465
+ UI.error("Error fetching information about release #{response.status}: #{response.body}")
466
+ return false
467
+ end
468
+ end
469
+ end
470
+
276
471
  # get distribution group or store
277
472
  def self.get_destination(api_token, owner_name, app_name, destination_type, destination_name)
278
473
  connection = self.connection
279
474
 
280
- response = connection.get("v0.1/apps/#{owner_name}/#{app_name}/distribution_#{destination_type}s/#{ERB::Util.url_encode(destination_name)}") do |req|
475
+ url = "v0.1/apps/#{owner_name}/#{app_name}/distribution_#{destination_type}s/#{ERB::Util.url_encode(destination_name)}"
476
+
477
+ UI.message("DEBUG: GET #{url}") if ENV['DEBUG']
478
+
479
+ response = connection.get(url) do |req|
281
480
  req.headers['X-API-Token'] = api_token
282
481
  req.headers['internal-request-source'] = "fastlane"
283
482
  end
284
483
 
484
+ UI.message("DEBUG: #{response.status} #{JSON.pretty_generate(response.body)}\n") if ENV['DEBUG']
485
+
285
486
  case response.status
286
487
  when 200...300
287
488
  destination = response.body
288
- UI.message("DEBUG: received #{destination_type} #{JSON.pretty_generate(destination)}") if ENV['DEBUG']
289
489
  destination
290
490
  when 404
291
491
  UI.error("Not found, invalid distribution #{destination_type} name")
@@ -303,14 +503,22 @@ module Fastlane
303
503
  def self.update_release(api_token, owner_name, app_name, release_id, release_notes = '')
304
504
  connection = self.connection
305
505
 
306
- response = connection.put("v0.1/apps/#{owner_name}/#{app_name}/releases/#{release_id}") do |req|
506
+ url = "v0.1/apps/#{owner_name}/#{app_name}/releases/#{release_id}"
507
+ body = {
508
+ release_notes: release_notes
509
+ }
510
+
511
+ UI.message("DEBUG: PUT #{url}") if ENV['DEBUG']
512
+ UI.message("DEBUG: PUT body #{JSON.pretty_generate(body)}\n") if ENV['DEBUG']
513
+
514
+ response = connection.put(url) do |req|
307
515
  req.headers['X-API-Token'] = api_token
308
516
  req.headers['internal-request-source'] = "fastlane"
309
- req.body = {
310
- release_notes: release_notes
311
- }
517
+ req.body = body
312
518
  end
313
519
 
520
+ UI.message("DEBUG: #{response.status} #{JSON.pretty_generate(response.body)}\n") if ENV['DEBUG']
521
+
314
522
  case response.status
315
523
  when 200...300
316
524
  # get full release info
@@ -319,8 +527,6 @@ module Fastlane
319
527
 
320
528
  download_url = release['download_url']
321
529
 
322
- UI.message("DEBUG: #{JSON.pretty_generate(release)}") if ENV['DEBUG']
323
-
324
530
  Actions.lane_context[Fastlane::Actions::SharedValues::APPCENTER_DOWNLOAD_LINK] = download_url
325
531
  Actions.lane_context[Fastlane::Actions::SharedValues::APPCENTER_BUILD_INFORMATION] = release
326
532
 
@@ -340,22 +546,33 @@ module Fastlane
340
546
  end
341
547
 
342
548
  # updates release metadata
343
- def self.update_release_metadata(api_token, owner_name, app_name, release_id, dsa_signature)
344
- return if dsa_signature.to_s == ''
549
+ def self.update_release_metadata(api_token, owner_name, app_name, release_id, dsa_signature = '', ed_signature = '')
550
+ return if dsa_signature.to_s == '' && ed_signature.to_s == ''
345
551
 
346
- release_metadata = {
347
- dsa_signature: dsa_signature
552
+ url = "v0.1/apps/#{owner_name}/#{app_name}/releases/#{release_id}"
553
+ body = {
554
+ metadata: {}
348
555
  }
556
+
557
+ if dsa_signature.to_s != ''
558
+ body[:metadata]["dsa_signature"] = dsa_signature
559
+ end
560
+ if ed_signature.to_s != ''
561
+ body[:metadata]["ed_signature"] = ed_signature
562
+ end
563
+
564
+ UI.message("DEBUG: PATCH #{url}") if ENV['DEBUG']
565
+ UI.message("DEBUG: PATCH body #{JSON.pretty_generate(body)}\n") if ENV['DEBUG']
349
566
 
350
567
  connection = self.connection
351
- response = connection.patch("v0.1/apps/#{owner_name}/#{app_name}/releases/#{release_id}") do |req|
568
+ response = connection.patch(url) do |req|
352
569
  req.headers['X-API-Token'] = api_token
353
570
  req.headers['internal-request-source'] = "fastlane"
354
- req.body = {
355
- metadata: release_metadata
356
- }
571
+ req.body = body
357
572
  end
358
573
 
574
+ UI.message("DEBUG: #{response.status} #{JSON.pretty_generate(response.body)}\n") if ENV['DEBUG']
575
+
359
576
  case response.status
360
577
  when 200...300
361
578
  UI.message("Release Metadata was successfully updated for release '#{release_id}'")
@@ -370,25 +587,32 @@ module Fastlane
370
587
  false
371
588
  end
372
589
  end
373
-
590
+
374
591
  # add release to distribution group or store
375
592
  def self.add_to_destination(api_token, owner_name, app_name, release_id, destination_type, destination_id, mandatory_update = false, notify_testers = false)
376
593
  connection = self.connection
377
594
 
378
- UI.message("DEBUG: getting #{release_id}") if ENV['DEBUG']
595
+ url = "v0.1/apps/#{owner_name}/#{app_name}/releases/#{release_id}/#{destination_type}s"
596
+ body = {
597
+ id: destination_id
598
+ }
379
599
 
380
- body = { "id" => destination_id }
381
600
  if destination_type == "group"
382
601
  body["mandatory_update"] = mandatory_update
383
602
  body["notify_testers"] = notify_testers
384
603
  end
385
604
 
386
- response = connection.post("v0.1/apps/#{owner_name}/#{app_name}/releases/#{release_id}/#{destination_type}s") do |req|
605
+ UI.message("DEBUG: POST #{url}") if ENV['DEBUG']
606
+ UI.message("DEBUG: POST body #{JSON.pretty_generate(body)}\n") if ENV['DEBUG']
607
+
608
+ response = connection.post(url) do |req|
387
609
  req.headers['X-API-Token'] = api_token
388
610
  req.headers['internal-request-source'] = "fastlane"
389
611
  req.body = body
390
612
  end
391
613
 
614
+ UI.message("DEBUG: #{response.status} #{JSON.pretty_generate(response.body)}\n") if ENV['DEBUG']
615
+
392
616
  case response.status
393
617
  when 200...300
394
618
  # get full release info
@@ -397,8 +621,6 @@ module Fastlane
397
621
 
398
622
  download_url = release['download_url']
399
623
 
400
- UI.message("DEBUG: received release #{JSON.pretty_generate(release)}") if ENV['DEBUG']
401
-
402
624
  Actions.lane_context[Fastlane::Actions::SharedValues::APPCENTER_DOWNLOAD_LINK] = download_url
403
625
  Actions.lane_context[Fastlane::Actions::SharedValues::APPCENTER_BUILD_INFORMATION] = release
404
626
 
@@ -421,11 +643,17 @@ module Fastlane
421
643
  def self.get_app(api_token, owner_name, app_name)
422
644
  connection = self.connection
423
645
 
424
- response = connection.get("v0.1/apps/#{owner_name}/#{app_name}") do |req|
646
+ url = "v0.1/apps/#{owner_name}/#{app_name}"
647
+
648
+ UI.message("DEBUG: GET #{url}") if ENV['DEBUG']
649
+
650
+ response = connection.get(url) do |req|
425
651
  req.headers['X-API-Token'] = api_token
426
652
  req.headers['internal-request-source'] = "fastlane"
427
653
  end
428
654
 
655
+ UI.message("DEBUG: #{response.status} #{JSON.pretty_generate(response.body)}\n") if ENV['DEBUG']
656
+
429
657
  case response.status
430
658
  when 200...300
431
659
  UI.message("DEBUG: #{JSON.pretty_generate(response.body)}\n") if ENV['DEBUG']
@@ -446,23 +674,28 @@ module Fastlane
446
674
  def self.create_app(api_token, owner_type, owner_name, app_name, app_display_name, os, platform)
447
675
  connection = self.connection
448
676
 
449
- endpoint = owner_type == "user" ? "v0.1/apps" : "v0.1/orgs/#{owner_name}/apps"
677
+ url = owner_type == "user" ? "v0.1/apps" : "v0.1/orgs/#{owner_name}/apps"
678
+ body = {
679
+ display_name: app_display_name,
680
+ name: app_name,
681
+ os: os,
682
+ platform: platform
683
+ }
684
+
685
+ UI.message("DEBUG: POST #{url}") if ENV['DEBUG']
686
+ UI.message("DEBUG: POST body #{JSON.pretty_generate(body)}\n") if ENV['DEBUG']
450
687
 
451
- response = connection.post(endpoint) do |req|
688
+ response = connection.post(url) do |req|
452
689
  req.headers['X-API-Token'] = api_token
453
690
  req.headers['internal-request-source'] = "fastlane"
454
- req.body = {
455
- "display_name" => app_display_name,
456
- "name" => app_name,
457
- "os" => os,
458
- "platform" => platform
459
- }
691
+ req.body = body
460
692
  end
461
693
 
694
+ UI.message("DEBUG: #{response.status} #{JSON.pretty_generate(response.body)}\n") if ENV['DEBUG']
695
+
462
696
  case response.status
463
697
  when 200...300
464
698
  created = response.body
465
- UI.message("DEBUG: #{JSON.pretty_generate(created)}") if ENV['DEBUG']
466
699
  UI.success("Created #{os}/#{platform} app with name \"#{created['name']}\" and display name \"#{created['display_name']}\" for #{owner_type} \"#{owner_name}\"")
467
700
  true
468
701
  when 401
@@ -477,16 +710,19 @@ module Fastlane
477
710
  def self.fetch_distribution_groups(api_token:, owner_name:, app_name:)
478
711
  connection = self.connection
479
712
 
480
- endpoint = "/v0.1/apps/#{owner_name}/#{app_name}/distribution_groups"
713
+ url = "/v0.1/apps/#{owner_name}/#{app_name}/distribution_groups"
481
714
 
482
- response = connection.get(endpoint) do |req|
715
+ UI.message("DEBUG: GET #{url}") if ENV['DEBUG']
716
+
717
+ response = connection.get(url) do |req|
483
718
  req.headers['X-API-Token'] = api_token
484
719
  req.headers['internal-request-source'] = "fastlane"
485
720
  end
486
721
 
722
+ UI.message("DEBUG: #{response.status} #{JSON.pretty_generate(response.body)}\n") if ENV['DEBUG']
723
+
487
724
  case response.status
488
725
  when 200...300
489
- UI.message("DEBUG: #{JSON.pretty_generate(response.body)}\n") if ENV['DEBUG']
490
726
  response.body
491
727
  when 401
492
728
  UI.user_error!("Auth Error, provided invalid token")
@@ -503,16 +739,19 @@ module Fastlane
503
739
  def self.fetch_devices(api_token:, owner_name:, app_name:, distribution_group:)
504
740
  connection = self.connection(nil, false, true)
505
741
 
506
- endpoint = "/v0.1/apps/#{owner_name}/#{app_name}/distribution_groups/#{ERB::Util.url_encode(distribution_group)}/devices/download_devices_list"
742
+ url = "/v0.1/apps/#{owner_name}/#{app_name}/distribution_groups/#{ERB::Util.url_encode(distribution_group)}/devices/download_devices_list"
743
+
744
+ UI.message("DEBUG: GET #{url}") if ENV['DEBUG']
507
745
 
508
- response = connection.get(endpoint) do |req|
746
+ response = connection.get(url) do |req|
509
747
  req.headers['X-API-Token'] = api_token
510
748
  req.headers['internal-request-source'] = "fastlane"
511
749
  end
512
750
 
751
+ UI.message("DEBUG: #{response.status} #{JSON.pretty_generate(response.body)}\n") if ENV['DEBUG']
752
+
513
753
  case response.status
514
754
  when 200...300
515
- UI.message("DEBUG: #{response.body.inspect}") if ENV['DEBUG']
516
755
  response.body
517
756
  when 401
518
757
  UI.user_error!("Auth Error, provided invalid token")
@@ -526,17 +765,87 @@ module Fastlane
526
765
  end
527
766
  end
528
767
 
529
- # Note: This does not support testing environment (INT)
768
+ def self.fetch_releases(api_token:, owner_name:, app_name:)
769
+ connection = self.connection(nil, false, true)
770
+
771
+ url = "/v0.1/apps/#{owner_name}/#{app_name}/releases"
772
+
773
+ UI.message("DEBUG: GET #{url}") if ENV['DEBUG']
774
+
775
+ response = connection.get(url) do |req|
776
+ req.headers['X-API-Token'] = api_token
777
+ req.headers['internal-request-source'] = "fastlane"
778
+ end
779
+
780
+ UI.message("DEBUG: #{response.status} #{JSON.pretty_generate(response.body)}\n") if ENV['DEBUG']
781
+
782
+ case response.status
783
+ when 200...300
784
+ JSON.parse(response.body)
785
+ when 401
786
+ UI.user_error!("Auth Error, provided invalid token")
787
+ false
788
+ when 404
789
+ UI.error("Not found, invalid owner or application name")
790
+ false
791
+ else
792
+ UI.error("Error #{response.status}: #{response.body}")
793
+ false
794
+ end
795
+ end
796
+
530
797
  def self.get_release_url(owner_type, owner_name, app_name, release_id)
531
798
  owner_path = owner_type == "user" ? "users/#{owner_name}" : "orgs/#{owner_name}"
799
+ if ENV['APPCENTER_ENV']&.upcase == 'INT'
800
+ return "https://portal-server-core-integration.dev.avalanch.es/#{owner_path}/apps/#{app_name}/distribute/releases/#{release_id}"
801
+ end
802
+
532
803
  return "https://appcenter.ms/#{owner_path}/apps/#{app_name}/distribute/releases/#{release_id}"
533
804
  end
534
805
 
535
- # Note: This does not support testing environment (INT)
536
806
  def self.get_install_url(owner_type, owner_name, app_name)
537
807
  owner_path = owner_type == "user" ? "users/#{owner_name}" : "orgs/#{owner_name}"
808
+ if ENV['APPCENTER_ENV']&.upcase == 'INT'
809
+ return "https://install.portal-server-core-integration.dev.avalanch.es/#{owner_path}/apps/#{app_name}"
810
+ end
811
+
538
812
  return "https://install.appcenter.ms/#{owner_path}/apps/#{app_name}"
539
813
  end
814
+
815
+ # add new created app to existing distribution group
816
+ def self.add_new_app_to_distribution_group(api_token:, owner_name:, app_name:, destination_name:)
817
+ url = URI.escape("/v0.1/orgs/#{owner_name}/distribution_groups/#{destination_name}/apps")
818
+ body = {
819
+ apps: [
820
+ { name: app_name }
821
+ ]
822
+ }
823
+
824
+ UI.message("DEBUG: POST #{url}") if ENV['DEBUG']
825
+ UI.message("DEBUG: POST body #{JSON.pretty_generate(body)}\n") if ENV['DEBUG']
826
+
827
+ response = connection.post(url) do |req|
828
+ req.headers['X-API-Token'] = api_token
829
+ req.headers['internal-request-source'] = "fastlane"
830
+ req.body = body
831
+ end
832
+
833
+ UI.message("DEBUG: #{response.status} #{JSON.pretty_generate(response.body)}\n") if ENV['DEBUG']
834
+
835
+ case response.status
836
+ when 200...300
837
+ created = response.body
838
+ UI.success("Added new app #{app_name} to distribution group #{destination_name}")
839
+ when 401
840
+ UI.user_error!("Auth Error, provided invalid token")
841
+ when 404
842
+ UI.error("Not found, invalid distribution group name #{destination_name}")
843
+ when 409
844
+ UI.success("App already added to distribution group #{destination_name}")
845
+ else
846
+ UI.error("Error adding app to distribution group #{response.status}: #{response.body}")
847
+ end
848
+ end
540
849
  end
541
850
  end
542
851
  end