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.
- checksums.yaml +5 -5
- data/README.md +30 -6
- data/lib/fastlane/plugin/appcenter/actions/appcenter_fetch_version_number.rb +103 -0
- data/lib/fastlane/plugin/appcenter/actions/appcenter_upload_action.rb +119 -21
- data/lib/fastlane/plugin/appcenter/helper/appcenter_helper.rb +400 -91
- data/lib/fastlane/plugin/appcenter/version.rb +1 -1
- metadata +23 -9
@@ -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 ||
|
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
|
-
|
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
|
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.
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
175
|
-
|
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("#{
|
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 #{
|
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("#{
|
238
|
+
UI.error("#{log_type} upload aborted")
|
188
239
|
false
|
189
240
|
end
|
190
241
|
end
|
191
242
|
|
192
|
-
#
|
193
|
-
#
|
194
|
-
#
|
195
|
-
def self.
|
196
|
-
connection = self.connection(
|
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
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
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
|
-
|
212
|
-
|
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
|
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
|
-
|
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.
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
347
|
-
|
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(
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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(
|
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
|
-
|
713
|
+
url = "/v0.1/apps/#{owner_name}/#{app_name}/distribution_groups"
|
481
714
|
|
482
|
-
|
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
|
-
|
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(
|
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
|
-
|
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
|