fastlane-plugin-appcenter 1.6.0 → 1.9.0

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