fastlane-plugin-appcenter 1.6.0 → 1.9.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 +4 -4
- data/README.md +32 -6
- data/lib/fastlane/plugin/appcenter/actions/appcenter_fetch_version_number.rb +84 -0
- data/lib/fastlane/plugin/appcenter/actions/appcenter_upload_action.rb +142 -31
- data/lib/fastlane/plugin/appcenter/helper/appcenter_helper.rb +396 -95
- data/lib/fastlane/plugin/appcenter/version.rb +1 -1
- metadata +18 -3
@@ -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
|
-
|
10
|
-
|
11
|
-
|
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
|
-
|
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 ||
|
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
|
-
|
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
|
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.
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
177
|
-
|
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("#{
|
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 #{
|
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("#{
|
238
|
+
UI.error("#{log_type} upload aborted")
|
190
239
|
false
|
191
240
|
end
|
192
241
|
end
|
193
242
|
|
194
|
-
#
|
195
|
-
#
|
196
|
-
#
|
197
|
-
def self.
|
198
|
-
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)
|
199
248
|
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
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
|
-
|
214
|
-
|
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
|
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
|
-
|
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.
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
349
|
-
|
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(
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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(
|
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
|
-
|
707
|
+
url = "/v0.1/apps/#{owner_name}/#{app_name}/distribution_groups"
|
483
708
|
|
484
|
-
|
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
|
-
|
736
|
+
url = "/v0.1/apps/#{owner_name}/#{app_name}/distribution_groups/#{ERB::Util.url_encode(distribution_group)}/devices/download_devices_list"
|
509
737
|
|
510
|
-
|
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
|
-
|
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
|