fastlane-plugin-appcenter 0.1.7 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: cfaf06714cd6c125dd4fb5b199e9d519fcf3fbaa
4
- data.tar.gz: 0dd75092e7436bdf0e10276f2c1bc6924ad39b3b
2
+ SHA256:
3
+ metadata.gz: b7aee7355c0b29ee7bf79054cf8a1036b59ccf37adb847b9dc0dc62cf421f25f
4
+ data.tar.gz: bb7c314a2e16662fd9aef3d820fb6e31e097f7ccff7807ab2d4e990b8c0d8e72
5
5
  SHA512:
6
- metadata.gz: bf78aae4984955537de575c92b0ca2d524f7d15372b92aceb67ea95f4e1c02ce38fc74c1a26309bfbf773354de1d34f7d706efbc01787e28cf5a4a2e06bcb68f
7
- data.tar.gz: 541f2b17ce6d55cb681b26d7b5d6915c9c7fe7ae8374acb2c9b5ad288e5524b8b182b5528bdcac1646ddfd59a27b746b45031b7f2268121baad3d56db52d5bdc
6
+ metadata.gz: 2c24839b70a4b7366b8966a57adabf13b66e6f04e94c494f1f851d49e7ca0620cbca701c217af7990abf1f1591b16ca46950c37a0ad2ad4072c4b34d195e7e4c
7
+ data.tar.gz: 5667213b8132cc43957cd3dc5124b579104e5c3cac9eaeeb012440a3c54bfe13b3618a38646edc31f7b99debe0a2b5f2b748fa649eb2e890d3dec9866f548f5d
data/README.md CHANGED
@@ -38,7 +38,11 @@ The action parameters `api_token` and `owner_name` can also be omitted when thei
38
38
  - `APPCENTER_DISTRIBUTE_DSYM` - Path to your symbols file. For iOS provide path to app.dSYM.zip
39
39
  - `APPCENTER_DISTRIBUTE_UPLOAD_DSYM_ONLY` - Flag to upload only the dSYM file to App Center
40
40
  - `APPCENTER_DISTRIBUTE_GROUP` - Comma separated list of Distribution Group names
41
+ - `APPCENTER_DISTRIBUTE_MANDATORY_UPDATE` - Require users to update to this release
42
+ - `APPCENTER_DISTRIBUTE_NOTIFY_TESTERS` - Send email notification about release
41
43
  - `APPCENTER_DISTRIBUTE_RELEASE_NOTES` - Release notes
44
+ - `APPCENTER_DISTRIBUTE_RELEASE_NOTES_CLIPPING` - Clip release notes if its length is more then 5000, `true` by default
45
+ - `APPCENTER_DISTRIBUTE_RELEASE_NOTES_LINK` - Additional release notes link
42
46
 
43
47
  ## Example
44
48
 
@@ -1,262 +1,10 @@
1
- # rubocop:disable Metrics/ClassLength
2
1
  module Fastlane
3
2
  module Actions
4
- module SharedValues
5
- APPCENTER_DOWNLOAD_LINK = :APPCENTER_DOWNLOAD_LINK
6
- APPCENTER_BUILD_INFORMATION = :APPCENTER_BUILD_INFORMATION
7
- end
8
-
9
3
  module Constants
10
4
  MAX_RELEASE_NOTES_LENGTH = 5000
11
5
  end
12
6
 
13
7
  class AppcenterUploadAction < Action
14
- # create request
15
- def self.connection(upload_url = false, dsym = false)
16
- require 'faraday'
17
- require 'faraday_middleware'
18
-
19
- options = {
20
- url: upload_url ? upload_url : "https://api.appcenter.ms"
21
- }
22
-
23
- Faraday.new(options) do |builder|
24
- if upload_url
25
- builder.request :multipart unless dsym
26
- builder.request :url_encoded unless dsym
27
- else
28
- builder.request :json
29
- end
30
- builder.response :json, content_type: /\bjson$/
31
- builder.use FaradayMiddleware::FollowRedirects
32
- builder.adapter :net_http
33
- end
34
- end
35
-
36
- # creates new release upload
37
- # returns:
38
- # upload_id
39
- # upload_url
40
- def self.create_release_upload(api_token, owner_name, app_name)
41
- connection = self.connection
42
-
43
- response = connection.post do |req|
44
- req.url("/v0.1/apps/#{owner_name}/#{app_name}/release_uploads")
45
- req.headers['X-API-Token'] = api_token
46
- req.headers['internal-request-source'] = "fastlane"
47
- req.body = {}
48
- end
49
-
50
- case response.status
51
- when 200...300
52
- UI.message("DEBUG: #{JSON.pretty_generate(response.body)}\n") if ENV['DEBUG']
53
- response.body
54
- when 401
55
- UI.user_error!("Auth Error, provided invalid token")
56
- false
57
- when 404
58
- UI.error("Not found, invalid owner or application name")
59
- false
60
- else
61
- UI.error("Error #{response.status}: #{response.body}")
62
- false
63
- end
64
- end
65
-
66
- # creates new dSYM upload in appcenter
67
- # returns:
68
- # symbol_upload_id
69
- # upload_url
70
- # expiration_date
71
- def self.create_dsym_upload(api_token, owner_name, app_name)
72
- connection = self.connection
73
-
74
- response = connection.post do |req|
75
- req.url("/v0.1/apps/#{owner_name}/#{app_name}/symbol_uploads")
76
- req.headers['X-API-Token'] = api_token
77
- req.headers['internal-request-source'] = "fastlane"
78
- req.body = {
79
- symbol_type: 'Apple'
80
- }
81
- end
82
-
83
- case response.status
84
- when 200...300
85
- UI.message("DEBUG: #{JSON.pretty_generate(response.body)}\n") if ENV['DEBUG']
86
- response.body
87
- when 401
88
- UI.user_error!("Auth Error, provided invalid token")
89
- false
90
- when 404
91
- UI.error("Not found, invalid owner or application name")
92
- false
93
- else
94
- UI.error("Error #{response.status}: #{response.body}")
95
- false
96
- end
97
- end
98
-
99
- # committs or aborts dsym upload
100
- def self.update_dsym_upload(api_token, owner_name, app_name, symbol_upload_id, status)
101
- connection = self.connection
102
-
103
- response = connection.patch do |req|
104
- req.url("/v0.1/apps/#{owner_name}/#{app_name}/symbol_uploads/#{symbol_upload_id}")
105
- req.headers['X-API-Token'] = api_token
106
- req.headers['internal-request-source'] = "fastlane"
107
- req.body = {
108
- "status" => status
109
- }
110
- end
111
-
112
- case response.status
113
- when 200...300
114
- UI.message("DEBUG: #{JSON.pretty_generate(response.body)}\n") if ENV['DEBUG']
115
- response.body
116
- else
117
- UI.error("Error #{response.status}: #{response.body}")
118
- false
119
- end
120
- end
121
-
122
- # upload dSYM files to specified upload url
123
- # if succeed, then commits the upload
124
- # otherwise aborts
125
- def self.upload_dsym(api_token, owner_name, app_name, dsym, symbol_upload_id, upload_url)
126
- connection = self.connection(upload_url, true)
127
-
128
- response = connection.put do |req|
129
- req.headers['x-ms-blob-type'] = "BlockBlob"
130
- req.headers['Content-Length'] = File.size(dsym).to_s
131
- req.headers['internal-request-source'] = "fastlane"
132
- req.body = Faraday::UploadIO.new(dsym, 'application/octet-stream') if dsym && File.exist?(dsym)
133
- end
134
-
135
- case response.status
136
- when 200...300
137
- self.update_dsym_upload(api_token, owner_name, app_name, symbol_upload_id, 'committed')
138
- UI.success("dSYM uploaded")
139
- else
140
- UI.error("Error uploading dSYM #{response.status}: #{response.body}")
141
- self.update_dsym_upload(api_token, owner_name, app_name, symbol_upload_id, 'aborted')
142
- UI.error("dSYM upload aborted")
143
- false
144
- end
145
- end
146
-
147
- # upload binary for specified upload_url
148
- # if succeed, then commits the release
149
- # otherwise aborts
150
- def self.upload_build(api_token, owner_name, app_name, file, upload_id, upload_url)
151
- connection = self.connection(upload_url)
152
-
153
- options = {}
154
- options[:upload_id] = upload_id
155
- # ipa field is used both for .apk and .ipa files
156
- options[:ipa] = Faraday::UploadIO.new(file, 'application/octet-stream') if file && File.exist?(file)
157
-
158
- response = connection.post do |req|
159
- req.headers['internal-request-source'] = "fastlane"
160
- req.body = options
161
- end
162
-
163
- case response.status
164
- when 200...300
165
- UI.message("Binary uploaded")
166
- self.update_release_upload(api_token, owner_name, app_name, upload_id, 'committed')
167
- else
168
- UI.error("Error uploading binary #{response.status}: #{response.body}")
169
- self.update_release_upload(api_token, owner_name, app_name, upload_id, 'aborted')
170
- UI.error("Release aborted")
171
- false
172
- end
173
- end
174
-
175
- # Commits or aborts the upload process for a release
176
- def self.update_release_upload(api_token, owner_name, app_name, upload_id, status)
177
- connection = self.connection
178
-
179
- response = connection.patch do |req|
180
- req.url("/v0.1/apps/#{owner_name}/#{app_name}/release_uploads/#{upload_id}")
181
- req.headers['X-API-Token'] = api_token
182
- req.headers['internal-request-source'] = "fastlane"
183
- req.body = {
184
- "status" => status
185
- }
186
- end
187
-
188
- case response.status
189
- when 200...300
190
- UI.message("DEBUG: #{JSON.pretty_generate(response.body)}\n") if ENV['DEBUG']
191
- response.body
192
- else
193
- UI.error("Error #{response.status}: #{response.body}")
194
- false
195
- end
196
- end
197
-
198
- # get existing release
199
- def self.get_release(api_token, release_url)
200
- connection = self.connection
201
- response = connection.get do |req|
202
- req.url("/#{release_url}")
203
- req.headers['X-API-Token'] = api_token
204
- req.headers['internal-request-source'] = "fastlane"
205
- end
206
-
207
- case response.status
208
- when 200...300
209
- release = response.body
210
- UI.message("DEBUG: #{JSON.pretty_generate(release)}") if ENV['DEBUG']
211
- release
212
- when 404
213
- UI.error("Not found, invalid release url")
214
- false
215
- else
216
- UI.error("Error fetching information about release #{response.status}: #{response.body}")
217
- false
218
- end
219
- end
220
-
221
- # add release to distribution group
222
- def self.add_to_group(api_token, release_url, group_name, release_notes = '')
223
- connection = self.connection
224
-
225
- response = connection.patch do |req|
226
- req.url("/#{release_url}")
227
- req.headers['X-API-Token'] = api_token
228
- req.headers['internal-request-source'] = "fastlane"
229
- req.body = {
230
- "distribution_group_name" => group_name,
231
- "release_notes" => release_notes
232
- }
233
- end
234
-
235
- case response.status
236
- when 200...300
237
- # get full release info
238
- release = self.get_release(api_token, release_url)
239
- return false unless release
240
- download_url = release['download_url']
241
-
242
- UI.message("DEBUG: #{JSON.pretty_generate(release)}") if ENV['DEBUG']
243
-
244
- Actions.lane_context[SharedValues::APPCENTER_DOWNLOAD_LINK] = download_url
245
- Actions.lane_context[SharedValues::APPCENTER_BUILD_INFORMATION] = release
246
-
247
- UI.message("Public Download URL: #{download_url}") if download_url
248
- UI.success("Release #{release['short_version']} was successfully distributed to group \"#{group_name}\"")
249
-
250
- release
251
- when 404
252
- UI.error("Not found, invalid distribution group name")
253
- false
254
- else
255
- UI.error("Error adding to group #{response.status}: #{response.body}")
256
- false
257
- end
258
- end
259
-
260
8
  # run whole upload process for dSYM files
261
9
  def self.run_dsym_upload(params)
262
10
  values = params.values
@@ -289,14 +37,14 @@ module Fastlane
289
37
  values[:dsym_path] = dsym_path
290
38
 
291
39
  UI.message("Starting dSYM upload...")
292
- dsym_upload_details = self.create_dsym_upload(api_token, owner_name, app_name)
40
+ dsym_upload_details = Helper::AppcenterHelper.create_dsym_upload(api_token, owner_name, app_name)
293
41
 
294
42
  if dsym_upload_details
295
43
  symbol_upload_id = dsym_upload_details['symbol_upload_id']
296
44
  upload_url = dsym_upload_details['upload_url']
297
45
 
298
46
  UI.message("Uploading dSYM...")
299
- self.upload_dsym(api_token, owner_name, app_name, dsym_path, symbol_upload_id, upload_url)
47
+ Helper::AppcenterHelper.upload_dsym(api_token, owner_name, app_name, dsym_path, symbol_upload_id, upload_url)
300
48
  end
301
49
  end
302
50
  end
@@ -308,6 +56,8 @@ module Fastlane
308
56
  owner_name = params[:owner_name]
309
57
  app_name = params[:app_name]
310
58
  group = params[:group]
59
+ mandatory_update = params[:mandatory_update]
60
+ notify_testers = params[:notify_testers]
311
61
  release_notes = params[:release_notes]
312
62
  should_clip = params[:should_clip]
313
63
  release_notes_link = params[:release_notes_link]
@@ -333,48 +83,39 @@ module Fastlane
333
83
  UI.user_error!("No Distribute Group given, pass using `group: 'group name'`") unless group && !group.empty?
334
84
 
335
85
  UI.message("Starting release upload...")
336
- upload_details = self.create_release_upload(api_token, owner_name, app_name)
86
+ upload_details = Helper::AppcenterHelper.create_release_upload(api_token, owner_name, app_name)
337
87
  if upload_details
338
88
  upload_id = upload_details['upload_id']
339
89
  upload_url = upload_details['upload_url']
340
90
 
341
91
  UI.message("Uploading release binary...")
342
- uploaded = self.upload_build(api_token, owner_name, app_name, file, upload_id, upload_url)
92
+ uploaded = Helper::AppcenterHelper.upload_build(api_token, owner_name, app_name, file, upload_id, upload_url)
343
93
 
344
94
  if uploaded
345
- release_url = uploaded['release_url']
346
- UI.message("Release committed")
95
+ release_id = uploaded['release_id']
96
+ UI.message("Release '#{release_id}' committed")
97
+
98
+ Helper::AppcenterHelper.update_release(api_token, owner_name, app_name, release_id, release_notes)
99
+
347
100
  groups = group.split(',')
348
101
  groups.each do |group_name|
349
- self.add_to_group(api_token, release_url, group_name, release_notes)
102
+ group = Helper::AppcenterHelper.get_group(api_token, owner_name, app_name, group_name)
103
+ if group
104
+ group_id = group['id']
105
+ distributed_release = Helper::AppcenterHelper.add_to_group(api_token, owner_name, app_name, release_id, group_id, mandatory_update, notify_testers)
106
+ if distributed_release
107
+ UI.success("Release #{distributed_release['short_version']} was successfully distributed to group \"#{group_name}\"")
108
+ else
109
+ UI.error("Release '#{release_id}' was not found")
110
+ end
111
+ else
112
+ UI.error("Group '#{group_name}' was not found")
113
+ end
350
114
  end
351
115
  end
352
116
  end
353
117
  end
354
118
 
355
- # returns true if app exists, false in case of 404 and error otherwise
356
- def self.get_app(api_token, owner_name, app_name)
357
- connection = self.connection
358
-
359
- response = connection.get do |req|
360
- req.url("/v0.1/apps/#{owner_name}/#{app_name}")
361
- req.headers['X-API-Token'] = api_token
362
- req.headers['internal-request-source'] = "fastlane"
363
- end
364
-
365
- case response.status
366
- when 200...300
367
- UI.message("DEBUG: #{JSON.pretty_generate(response.body)}\n") if ENV['DEBUG']
368
- true
369
- when 404
370
- UI.message("DEBUG: #{JSON.pretty_generate(response.body)}\n") if ENV['DEBUG']
371
- false
372
- else
373
- UI.error("Error #{response.status}: #{response.body}")
374
- false
375
- end
376
- end
377
-
378
119
  # checks app existance, if ther is no such - creates it
379
120
  def self.get_or_create_app(params)
380
121
  api_token = params[:api_token]
@@ -386,37 +127,14 @@ module Fastlane
386
127
  "iOS" => ['Objective-C-Swift', 'React-Native', 'Xamarin']
387
128
  }
388
129
 
389
- if self.get_app(api_token, owner_name, app_name)
130
+ if Helper::AppcenterHelper.get_app(api_token, owner_name, app_name)
390
131
  true
391
132
  else
392
133
  if Helper.test? || UI.confirm("App with name #{app_name} not found, create one?")
393
- connection = self.connection
394
-
395
134
  os = Helper.test? ? "Android" : UI.select("Select OS", ["Android", "iOS"])
396
135
  platform = Helper.test? ? "Java" : UI.select("Select Platform", platforms[os])
397
136
 
398
- response = connection.post do |req|
399
- req.url("/v0.1/apps")
400
- req.headers['X-API-Token'] = api_token
401
- req.headers['internal-request-source'] = "fastlane"
402
- req.body = {
403
- "display_name" => app_name,
404
- "name" => app_name,
405
- "os" => os,
406
- "platform" => platform
407
- }
408
- end
409
-
410
- case response.status
411
- when 200...300
412
- created = response.body
413
- UI.message("DEBUG: #{JSON.pretty_generate(created)}") if ENV['DEBUG']
414
- UI.success("Created #{os}/#{platform} app with name \"#{created['name']}\"")
415
- true
416
- else
417
- UI.error("Error creating app #{response.status}: #{response.body}")
418
- false
419
- end
137
+ Helper::AppcenterHelper.create_app(api_token, owner_name, app_name, os, platform)
420
138
  else
421
139
  UI.error("Lane aborted")
422
140
  false
@@ -446,9 +164,7 @@ module Fastlane
446
164
  end
447
165
 
448
166
  def self.details
449
- [
450
- "Symbols will also be uploaded automatically if a `app.dSYM.zip` file is found next to `app.ipa`. In case it is located in a different place you can specify the path explicitly in `:dsym` parameter."
451
- ]
167
+ "Symbols will also be uploaded automatically if a `app.dSYM.zip` file is found next to `app.ipa`. In case it is located in a different place you can specify the path explicitly in `:dsym` parameter."
452
168
  end
453
169
 
454
170
  def self.available_options
@@ -536,6 +252,20 @@ module Fastlane
536
252
  optional: true,
537
253
  type: String),
538
254
 
255
+ FastlaneCore::ConfigItem.new(key: :mandatory_update,
256
+ env_name: "APPCENTER_DISTRIBUTE_MANDATORY_UPDATE",
257
+ description: "Require users to update to this release",
258
+ optional: true,
259
+ is_string: false,
260
+ default_value: false),
261
+
262
+ FastlaneCore::ConfigItem.new(key: :notify_testers,
263
+ env_name: "APPCENTER_DISTRIBUTE_NOTIFY_TESTERS",
264
+ description: "Send email notification about release",
265
+ optional: true,
266
+ is_string: false,
267
+ default_value: false),
268
+
539
269
  FastlaneCore::ConfigItem.new(key: :release_notes,
540
270
  env_name: "APPCENTER_DISTRIBUTE_RELEASE_NOTES",
541
271
  description: "Release notes",
@@ -545,7 +275,7 @@ module Fastlane
545
275
 
546
276
  FastlaneCore::ConfigItem.new(key: :should_clip,
547
277
  env_name: "APPCENTER_DISTRIBUTE_RELEASE_NOTES_CLIPPING",
548
- description: "Clip release notes if its lenght is more then #{Constants::MAX_RELEASE_NOTES_LENGTH}, true by default",
278
+ description: "Clip release notes if its length is more then #{Constants::MAX_RELEASE_NOTES_LENGTH}, true by default",
549
279
  optional: true,
550
280
  is_string: false,
551
281
  default_value: true),
@@ -593,4 +323,3 @@ module Fastlane
593
323
  end
594
324
  end
595
325
  end
596
- # rubocop:enable Metrics/ClassLength
@@ -1,11 +1,370 @@
1
1
  module Fastlane
2
2
  module Helper
3
+ module SharedValues
4
+ APPCENTER_DOWNLOAD_LINK = :APPCENTER_DOWNLOAD_LINK
5
+ APPCENTER_BUILD_INFORMATION = :APPCENTER_BUILD_INFORMATION
6
+ end
7
+
3
8
  class AppcenterHelper
4
- # class methods that you define here become available in your action
5
- # as `Helper::AppcenterHelper.your_method`
6
- #
7
- def self.show_message
8
- UI.message("Hello from the appcenter plugin helper!")
9
+
10
+ # create request
11
+ def self.connection(upload_url = false, dsym = false)
12
+ require 'faraday'
13
+ require 'faraday_middleware'
14
+
15
+ options = {
16
+ url: upload_url ? upload_url : "https://api.appcenter.ms"
17
+ }
18
+
19
+ Faraday.new(options) do |builder|
20
+ if upload_url
21
+ builder.request :multipart unless dsym
22
+ builder.request :url_encoded unless dsym
23
+ else
24
+ builder.request :json
25
+ end
26
+ builder.response :json, content_type: /\bjson$/
27
+ builder.use FaradayMiddleware::FollowRedirects
28
+ builder.adapter :net_http
29
+ end
30
+ end
31
+
32
+ # creates new release upload
33
+ # returns:
34
+ # upload_id
35
+ # upload_url
36
+ def self.create_release_upload(api_token, owner_name, app_name)
37
+ connection = self.connection
38
+
39
+ response = connection.post do |req|
40
+ req.url("/v0.1/apps/#{owner_name}/#{app_name}/release_uploads")
41
+ req.headers['X-API-Token'] = api_token
42
+ req.headers['internal-request-source'] = "fastlane"
43
+ req.body = {}
44
+ end
45
+
46
+ case response.status
47
+ when 200...300
48
+ UI.message("DEBUG: #{JSON.pretty_generate(response.body)}\n") if ENV['DEBUG']
49
+ response.body
50
+ when 401
51
+ UI.user_error!("Auth Error, provided invalid token")
52
+ false
53
+ when 404
54
+ UI.error("Not found, invalid owner or application name")
55
+ false
56
+ else
57
+ UI.error("Error #{response.status}: #{response.body}")
58
+ false
59
+ end
60
+ end
61
+
62
+ # creates new dSYM upload in appcenter
63
+ # returns:
64
+ # symbol_upload_id
65
+ # upload_url
66
+ # expiration_date
67
+ def self.create_dsym_upload(api_token, owner_name, app_name)
68
+ connection = self.connection
69
+
70
+ response = connection.post do |req|
71
+ req.url("/v0.1/apps/#{owner_name}/#{app_name}/symbol_uploads")
72
+ req.headers['X-API-Token'] = api_token
73
+ req.headers['internal-request-source'] = "fastlane"
74
+ req.body = {
75
+ symbol_type: 'Apple'
76
+ }
77
+ end
78
+
79
+ case response.status
80
+ when 200...300
81
+ UI.message("DEBUG: #{JSON.pretty_generate(response.body)}\n") if ENV['DEBUG']
82
+ response.body
83
+ when 401
84
+ UI.user_error!("Auth Error, provided invalid token")
85
+ false
86
+ when 404
87
+ UI.error("Not found, invalid owner or application name")
88
+ false
89
+ else
90
+ UI.error("Error #{response.status}: #{response.body}")
91
+ false
92
+ end
93
+ end
94
+
95
+ # committs or aborts dsym upload
96
+ def self.update_dsym_upload(api_token, owner_name, app_name, symbol_upload_id, status)
97
+ connection = self.connection
98
+
99
+ response = connection.patch do |req|
100
+ req.url("/v0.1/apps/#{owner_name}/#{app_name}/symbol_uploads/#{symbol_upload_id}")
101
+ req.headers['X-API-Token'] = api_token
102
+ req.headers['internal-request-source'] = "fastlane"
103
+ req.body = {
104
+ "status" => status
105
+ }
106
+ end
107
+
108
+ case response.status
109
+ when 200...300
110
+ UI.message("DEBUG: #{JSON.pretty_generate(response.body)}\n") if ENV['DEBUG']
111
+ response.body
112
+ else
113
+ UI.error("Error #{response.status}: #{response.body}")
114
+ false
115
+ end
116
+ end
117
+
118
+ # upload dSYM files to specified upload url
119
+ # if succeed, then commits the upload
120
+ # otherwise aborts
121
+ def self.upload_dsym(api_token, owner_name, app_name, dsym, symbol_upload_id, upload_url)
122
+ connection = self.connection(upload_url, true)
123
+
124
+ response = connection.put do |req|
125
+ req.headers['x-ms-blob-type'] = "BlockBlob"
126
+ req.headers['Content-Length'] = File.size(dsym).to_s
127
+ req.headers['internal-request-source'] = "fastlane"
128
+ req.body = Faraday::UploadIO.new(dsym, 'application/octet-stream') if dsym && File.exist?(dsym)
129
+ end
130
+
131
+ case response.status
132
+ when 200...300
133
+ self.update_dsym_upload(api_token, owner_name, app_name, symbol_upload_id, 'committed')
134
+ UI.success("dSYM uploaded")
135
+ else
136
+ UI.error("Error uploading dSYM #{response.status}: #{response.body}")
137
+ self.update_dsym_upload(api_token, owner_name, app_name, symbol_upload_id, 'aborted')
138
+ UI.error("dSYM upload aborted")
139
+ false
140
+ end
141
+ end
142
+
143
+ # upload binary for specified upload_url
144
+ # if succeed, then commits the release
145
+ # otherwise aborts
146
+ def self.upload_build(api_token, owner_name, app_name, file, upload_id, upload_url)
147
+ connection = self.connection(upload_url)
148
+
149
+ options = {}
150
+ options[:upload_id] = upload_id
151
+ # ipa field is used both for .apk and .ipa files
152
+ options[:ipa] = Faraday::UploadIO.new(file, 'application/octet-stream') if file && File.exist?(file)
153
+
154
+ response = connection.post do |req|
155
+ req.headers['internal-request-source'] = "fastlane"
156
+ req.body = options
157
+ end
158
+
159
+ case response.status
160
+ when 200...300
161
+ UI.message("Binary uploaded")
162
+ self.update_release_upload(api_token, owner_name, app_name, upload_id, 'committed')
163
+ else
164
+ UI.error("Error uploading binary #{response.status}: #{response.body}")
165
+ self.update_release_upload(api_token, owner_name, app_name, upload_id, 'aborted')
166
+ UI.error("Release aborted")
167
+ false
168
+ end
169
+ end
170
+
171
+ # Commits or aborts the upload process for a release
172
+ def self.update_release_upload(api_token, owner_name, app_name, upload_id, status)
173
+ connection = self.connection
174
+
175
+ response = connection.patch do |req|
176
+ req.url("/v0.1/apps/#{owner_name}/#{app_name}/release_uploads/#{upload_id}")
177
+ req.headers['X-API-Token'] = api_token
178
+ req.headers['internal-request-source'] = "fastlane"
179
+ req.body = {
180
+ "status" => status
181
+ }
182
+ end
183
+
184
+ case response.status
185
+ when 200...300
186
+ UI.message("DEBUG: #{JSON.pretty_generate(response.body)}\n") if ENV['DEBUG']
187
+ response.body
188
+ else
189
+ UI.error("Error #{response.status}: #{response.body}")
190
+ false
191
+ end
192
+ end
193
+
194
+ # get existing release
195
+ def self.get_release(api_token, owner_name, app_name, release_id)
196
+ connection = self.connection
197
+ response = connection.get do |req|
198
+ req.url("/v0.1/apps/#{owner_name}/#{app_name}/releases/#{release_id}")
199
+ req.headers['X-API-Token'] = api_token
200
+ req.headers['internal-request-source'] = "fastlane"
201
+ end
202
+
203
+ case response.status
204
+ when 200...300
205
+ release = response.body
206
+ UI.message("DEBUG: #{JSON.pretty_generate(release)}") if ENV['DEBUG']
207
+ release
208
+ when 404
209
+ UI.error("Not found, invalid release url")
210
+ false
211
+ else
212
+ UI.error("Error fetching information about release #{response.status}: #{response.body}")
213
+ false
214
+ end
215
+ end
216
+
217
+ # get distribution group
218
+ def self.get_group(api_token, owner_name, app_name, group_name)
219
+ connection = self.connection
220
+
221
+ response = connection.get do |req|
222
+ req.url("/v0.1/apps/#{owner_name}/#{app_name}/distribution_groups/#{group_name}")
223
+ req.headers['X-API-Token'] = api_token
224
+ req.headers['internal-request-source'] = "fastlane"
225
+ end
226
+
227
+ case response.status
228
+ when 200...300
229
+ group = response.body
230
+ UI.message("DEBUG: received group #{JSON.pretty_generate(group)}") if ENV['DEBUG']
231
+ group
232
+ when 404
233
+ UI.error("Not found, invalid distribution group name")
234
+ false
235
+ else
236
+ UI.error("Error getting group #{response.status}: #{response.body}")
237
+ false
238
+ end
239
+ end
240
+
241
+ # add release to destination
242
+ def self.update_release(api_token, owner_name, app_name, release_id, release_notes = '')
243
+ connection = self.connection
244
+
245
+ response = connection.put do |req|
246
+ req.url("/v0.1/apps/#{owner_name}/#{app_name}/releases/#{release_id}")
247
+ req.headers['X-API-Token'] = api_token
248
+ req.headers['internal-request-source'] = "fastlane"
249
+ req.body = {
250
+ "release_notes" => release_notes
251
+ }
252
+ end
253
+
254
+ case response.status
255
+ when 200...300
256
+ # get full release info
257
+ release = self.get_release(api_token, owner_name, app_name, release_id)
258
+ return false unless release
259
+ download_url = release['download_url']
260
+
261
+ UI.message("DEBUG: #{JSON.pretty_generate(release)}") if ENV['DEBUG']
262
+
263
+ Actions.lane_context[SharedValues::APPCENTER_DOWNLOAD_LINK] = download_url
264
+ Actions.lane_context[SharedValues::APPCENTER_BUILD_INFORMATION] = release
265
+
266
+ UI.message("Release #{release['short_version']} was successfully updated")
267
+
268
+ release
269
+ when 404
270
+ UI.error("Not found, invalid release id")
271
+ false
272
+ else
273
+ UI.error("Error adding updating release #{response.status}: #{response.body}")
274
+ false
275
+ end
276
+ end
277
+
278
+ # add release to distribution group
279
+ def self.add_to_group(api_token, owner_name, app_name, release_id, group_id, mandatory_update = false, notify_testers = false)
280
+ connection = self.connection
281
+
282
+ UI.message("DEBUG: getting #{release_id}") if ENV['DEBUG']
283
+
284
+ response = connection.post do |req|
285
+ req.url("/v0.1/apps/#{owner_name}/#{app_name}/releases/#{release_id}/groups")
286
+ req.headers['X-API-Token'] = api_token
287
+ req.headers['internal-request-source'] = "fastlane"
288
+ req.body = {
289
+ "id" => group_id,
290
+ "mandatory_update" => mandatory_update,
291
+ "notify_testers" => notify_testers
292
+ }
293
+ end
294
+
295
+ case response.status
296
+ when 200...300
297
+ # get full release info
298
+ release = self.get_release(api_token, owner_name, app_name, release_id)
299
+ return false unless release
300
+ download_url = release['download_url']
301
+
302
+ UI.message("DEBUG: received release #{JSON.pretty_generate(release)}") if ENV['DEBUG']
303
+
304
+ Actions.lane_context[SharedValues::APPCENTER_DOWNLOAD_LINK] = download_url
305
+ Actions.lane_context[SharedValues::APPCENTER_BUILD_INFORMATION] = release
306
+
307
+ UI.message("Public Download URL: #{download_url}") if download_url
308
+
309
+ release
310
+ when 404
311
+ UI.error("Not found, invalid distribution group name")
312
+ false
313
+ else
314
+ UI.error("Error adding to group #{response.status}: #{response.body}")
315
+ false
316
+ end
317
+ end
318
+
319
+ # returns true if app exists, false in case of 404 and error otherwise
320
+ def self.get_app(api_token, owner_name, app_name)
321
+ connection = self.connection
322
+
323
+ response = connection.get do |req|
324
+ req.url("/v0.1/apps/#{owner_name}/#{app_name}")
325
+ req.headers['X-API-Token'] = api_token
326
+ req.headers['internal-request-source'] = "fastlane"
327
+ end
328
+
329
+ case response.status
330
+ when 200...300
331
+ UI.message("DEBUG: #{JSON.pretty_generate(response.body)}\n") if ENV['DEBUG']
332
+ true
333
+ when 404
334
+ UI.message("DEBUG: #{JSON.pretty_generate(response.body)}\n") if ENV['DEBUG']
335
+ false
336
+ else
337
+ UI.error("Error #{response.status}: #{response.body}")
338
+ false
339
+ end
340
+ end
341
+
342
+ # returns true if app exists, false in case of 404 and error otherwise
343
+ def self.create_app(api_token, owner_name, app_name, os, platform)
344
+ connection = self.connection
345
+
346
+ response = connection.post do |req|
347
+ req.url("/v0.1/apps")
348
+ req.headers['X-API-Token'] = api_token
349
+ req.headers['internal-request-source'] = "fastlane"
350
+ req.body = {
351
+ "display_name" => app_name,
352
+ "name" => app_name,
353
+ "os" => os,
354
+ "platform" => platform
355
+ }
356
+ end
357
+
358
+ case response.status
359
+ when 200...300
360
+ created = response.body
361
+ UI.message("DEBUG: #{JSON.pretty_generate(created)}") if ENV['DEBUG']
362
+ UI.success("Created #{os}/#{platform} app with name \"#{created['name']}\"")
363
+ true
364
+ else
365
+ UI.error("Error creating app #{response.status}: #{response.body}")
366
+ false
367
+ end
9
368
  end
10
369
  end
11
370
  end
@@ -1,5 +1,5 @@
1
1
  module Fastlane
2
2
  module Appcenter
3
- VERSION = "0.1.7"
3
+ VERSION = "0.2.0"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fastlane-plugin-appcenter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.7
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Microsoft Corporation
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-12-12 00:00:00.000000000 Z
11
+ date: 2019-04-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -126,7 +126,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
126
126
  version: '0'
127
127
  requirements: []
128
128
  rubyforge_project:
129
- rubygems_version: 2.6.12
129
+ rubygems_version: 2.7.6
130
130
  signing_key:
131
131
  specification_version: 4
132
132
  summary: Fastlane plugin for App Center