fastlane-plugin-appcenter 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 48df8a17edb752b7e11b38125b5e205971e9e2be
4
+ data.tar.gz: 7a0672dd1a6250ce6225a42c23cb30ac74e82177
5
+ SHA512:
6
+ metadata.gz: 29d4b4696c8cacdee4e2ad44ea52b806a77ff60fabee8ef4da0516688016f855caa2895a367348cdc8fffb9c750603b89098c45e77d295dcd7fd5295a0859b26
7
+ data.tar.gz: 69e9c5a5bf119ad4afbced987b4522384ab9c56469e52d3c49d043d5d00e3bc3508ed6bbe955877830ae00512e930376165f73950b2ef252124bbf26328dec54
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) Microsoft Corporation. All rights reserved.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE
data/README.md ADDED
@@ -0,0 +1,84 @@
1
+ # appcenter plugin
2
+
3
+ [![fastlane Plugin Badge](https://rawcdn.githack.com/fastlane/fastlane/master/fastlane/assets/plugin-badge.svg)](https://rubygems.org/gems/fastlane-plugin-appcenter)
4
+ [![Gem Version](https://badge.fury.io/rb/fastlane-plugin-appcenter.svg)](https://badge.fury.io/rb/fastlane-plugin-appcenter)
5
+ [![Build Status](https://travis-ci.org/Microsoft/fastlane-plugin-appcenter.svg?branch=master)](https://travis-ci.org/Microsoft/fastlane-plugin-appcenter)
6
+
7
+ ## Getting Started
8
+
9
+ This project is a [_fastlane_](https://github.com/fastlane/fastlane) plugin. To get started with `fastlane-plugin-appcenter`, add it to your project by running:
10
+
11
+ ```bash
12
+ fastlane add_plugin appcenter
13
+ ```
14
+
15
+ ## About appcenter
16
+
17
+ Plugin for [App Center](https://appcenter.ms). Provides `appcenter_upload` action for [release distribution](https://docs.microsoft.com/en-us/appcenter/distribution/uploading) and [dSYM uploads](https://docs.microsoft.com/en-us/appcenter/crashes/ios)
18
+
19
+ ## Usage
20
+
21
+ Obtain an [API token](https://docs.microsoft.com/en-us/appcenter/api-docs/). API Token is used for authentication for all App Center API calls.
22
+
23
+ ```ruby
24
+ appcenter_upload(
25
+ api_token: "<appcenter token>",
26
+ owner_name: "<your appcenter account name>",
27
+ app_name: "<your app name>",
28
+ apk: "<path to android build binary>"
29
+ )
30
+ ```
31
+
32
+ ## Example
33
+
34
+ Check out the [example `Fastfile`](fastlane/Fastfile) to see how to use this plugin. Try it by cloning the repo, running `fastlane install_plugins` and `bundle exec fastlane test`.
35
+
36
+ Sample uses `.env` for setting private variables like API token, owner name, .etc. You need to replace it in `Fastfile` by your own values.
37
+
38
+ There are three examples in `test` lane:
39
+ - upload release for android with minimum required parameters
40
+ - upload release for ios with all set parameters
41
+ - upload only dSYM file for ios
42
+
43
+ ## Run tests for this plugin
44
+
45
+ To run both the tests, and code style validation, run
46
+
47
+ ```
48
+ rake
49
+ ```
50
+
51
+ To automatically fix many of the styling issues, use
52
+ ```
53
+ rubocop -a
54
+ ```
55
+
56
+ ## Issues and Feedback
57
+
58
+ For any other issues and feedback about this plugin, please submit it to this repository.
59
+
60
+ ## Troubleshooting
61
+
62
+ If you have trouble using plugins, check out the [Plugins Troubleshooting](https://docs.fastlane.tools/plugins/plugins-troubleshooting/) guide.
63
+
64
+ ## Using _fastlane_ Plugins
65
+
66
+ For more information about how the `fastlane` plugin system works, check out the [Plugins documentation](https://docs.fastlane.tools/plugins/create-plugin/).
67
+
68
+ ## About _fastlane_
69
+
70
+ _fastlane_ is the easiest way to automate beta deployments and releases for your iOS and Android apps. To learn more, check out [fastlane.tools](https://fastlane.tools).
71
+
72
+ ## Contributing
73
+
74
+ This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
75
+
76
+ ## Contact
77
+
78
+ ### Intercom
79
+
80
+ If you have further questions, want to provide feedback or you are running into issues, log in to the [App Center](https://appcenter.ms) portal and use the blue Intercom button on the bottom right to start a conversation with us.
81
+
82
+ ### Twitter
83
+
84
+ We're on Twitter as [@appcenter](https://www.twitter.com/vsappcenter).
@@ -0,0 +1,16 @@
1
+ require 'fastlane/plugin/appcenter/version'
2
+
3
+ module Fastlane
4
+ module Appcenter
5
+ # Return all .rb files inside the "actions" and "helper" directory
6
+ def self.all_classes
7
+ Dir[File.expand_path('**/{actions,helper}/*.rb', File.dirname(__FILE__))]
8
+ end
9
+ end
10
+ end
11
+
12
+ # By default we want to import all available actions and helpers
13
+ # A plugin can contain any number of actions and plugins
14
+ Fastlane::Appcenter.all_classes.each do |current|
15
+ require current
16
+ end
@@ -0,0 +1,535 @@
1
+ # rubocop:disable Metrics/ClassLength
2
+ module Fastlane
3
+ module Actions
4
+ module SharedValues
5
+ APPCENTER_DOWNLOAD_LINK = :APPCENTER_DOWNLOAD_LINK
6
+ APPCENTER_BUILD_INFORMATION = :APPCENTER_BUILD_INFORMATION
7
+ end
8
+
9
+ class AppcenterUploadAction < Action
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
+ # add release to distribution group
195
+ def self.add_to_group(api_token, release_url, group_name, release_notes = '')
196
+ connection = self.connection
197
+
198
+ response = connection.patch do |req|
199
+ req.url("/#{release_url}")
200
+ req.headers['X-API-Token'] = api_token
201
+ req.headers['internal-request-source'] = "fastlane"
202
+ req.body = {
203
+ "distribution_group_name" => group_name,
204
+ "release_notes" => release_notes
205
+ }
206
+ end
207
+
208
+ case response.status
209
+ when 200...300
210
+ release = response.body
211
+ download_url = release['download_url']
212
+
213
+ UI.message("DEBUG: #{JSON.pretty_generate(release)}") if ENV['DEBUG']
214
+
215
+ Actions.lane_context[SharedValues::APPCENTER_DOWNLOAD_LINK] = download_url
216
+ Actions.lane_context[SharedValues::APPCENTER_BUILD_INFORMATION] = release
217
+
218
+ UI.message("Public Download URL: #{download_url}") if download_url
219
+ UI.success("Release #{release['short_version']} was successfully distributed")
220
+
221
+ release
222
+ when 404
223
+ UI.error("Not found, invalid distribution group name")
224
+ false
225
+ else
226
+ UI.error("Error adding to group #{response.status}: #{response.body}")
227
+ false
228
+ end
229
+ end
230
+
231
+ # run whole upload process for dSYM files
232
+ def self.run_dsym_upload(params)
233
+ values = params.values
234
+ api_token = params[:api_token]
235
+ owner_name = params[:owner_name]
236
+ app_name = params[:app_name]
237
+ file = params[:ipa]
238
+ dsym = params[:dsym]
239
+
240
+ dsym_path = nil
241
+ if dsym
242
+ # we can use dsym parameter only if build file is ipa
243
+ dsym_path = dsym if !file || File.extname(file) == '.ipa'
244
+ else
245
+ # if dsym is note set, but build is ipa - check default path
246
+ if file && File.exist?(file) && File.extname(file) == '.ipa'
247
+ dsym_path = file.to_s.gsub('.ipa', '.dSYM.zip')
248
+ UI.message("dSYM is found")
249
+ end
250
+ end
251
+
252
+ # if we provided valid dsym path, or <ipa_path>.dSYM.zip was found, start dSYM upload
253
+ if dsym_path && File.exist?(dsym_path)
254
+ if File.directory?(dsym_path)
255
+ UI.message("dSYM path is folder, zipping...")
256
+ dsym_path = Actions::ZipAction.run(path: dsym, output_path: dsym + ".zip")
257
+ UI.message("dSYM files zipped")
258
+ end
259
+
260
+ values[:dsym_path] = dsym_path
261
+
262
+ UI.message("Starting dSYM upload...")
263
+ dsym_upload_details = self.create_dsym_upload(api_token, owner_name, app_name)
264
+
265
+ if dsym_upload_details
266
+ symbol_upload_id = dsym_upload_details['symbol_upload_id']
267
+ upload_url = dsym_upload_details['upload_url']
268
+
269
+ UI.message("Uploading dSYM...")
270
+ self.upload_dsym(api_token, owner_name, app_name, dsym_path, symbol_upload_id, upload_url)
271
+ end
272
+ end
273
+ end
274
+
275
+ # run whole upload process for release
276
+ def self.run_release_upload(params)
277
+ api_token = params[:api_token]
278
+ owner_name = params[:owner_name]
279
+ app_name = params[:app_name]
280
+ group = params[:group]
281
+
282
+ file = [
283
+ params[:ipa],
284
+ params[:apk]
285
+ ].detect { |e| !e.to_s.empty? }
286
+
287
+ UI.user_error!("Couldn't find build file at path '#{file}'") unless file && File.exist?(file)
288
+ UI.user_error!("No Distribute Group given, pass using `group: 'group name'`") unless group && !group.empty?
289
+
290
+ UI.message("Starting release upload...")
291
+ upload_details = self.create_release_upload(api_token, owner_name, app_name)
292
+ if upload_details
293
+ upload_id = upload_details['upload_id']
294
+ upload_url = upload_details['upload_url']
295
+
296
+ UI.message("Uploading release binary...")
297
+ uploaded = self.upload_build(api_token, owner_name, app_name, file, upload_id, upload_url)
298
+
299
+ if uploaded
300
+ release_url = uploaded['release_url']
301
+ UI.message("Release committed")
302
+ self.add_to_group(api_token, release_url, group, params[:release_notes])
303
+ end
304
+ end
305
+ end
306
+
307
+ # returns true if app exists, false in case of 404 and error otherwise
308
+ def self.get_app(api_token, owner_name, app_name)
309
+ connection = self.connection
310
+
311
+ response = connection.get do |req|
312
+ req.url("/v0.1/apps/#{owner_name}/#{app_name}")
313
+ req.headers['X-API-Token'] = api_token
314
+ req.headers['internal-request-source'] = "fastlane"
315
+ end
316
+
317
+ case response.status
318
+ when 200...300
319
+ UI.message("DEBUG: #{JSON.pretty_generate(response.body)}\n") if ENV['DEBUG']
320
+ true
321
+ when 404
322
+ UI.message("DEBUG: #{JSON.pretty_generate(response.body)}\n") if ENV['DEBUG']
323
+ false
324
+ else
325
+ UI.error("Error #{response.status}: #{response.body}")
326
+ false
327
+ end
328
+ end
329
+
330
+ # checks app existance, if ther is no such - creates it
331
+ def self.get_or_create_app(params)
332
+ api_token = params[:api_token]
333
+ owner_name = params[:owner_name]
334
+ app_name = params[:app_name]
335
+
336
+ platforms = {
337
+ "Android" => ['Java', 'React-Native', 'Xamarin'],
338
+ "iOS" => ['Objective-C-Swift', 'React-Native', 'Xamarin']
339
+ }
340
+
341
+ if self.get_app(api_token, owner_name, app_name)
342
+ true
343
+ else
344
+ if Helper.test? || UI.confirm("App with name #{app_name} not found, create one?")
345
+ connection = self.connection
346
+
347
+ os = Helper.test? ? "Android" : UI.select("Select OS", ["Android", "iOS"])
348
+ platform = Helper.test? ? "Java" : UI.select("Select Platform", platforms[os])
349
+
350
+ response = connection.post do |req|
351
+ req.url("/v0.1/apps")
352
+ req.headers['X-API-Token'] = api_token
353
+ req.headers['internal-request-source'] = "fastlane"
354
+ req.body = {
355
+ "display_name" => app_name,
356
+ "name" => app_name,
357
+ "os" => os,
358
+ "platform" => platform
359
+ }
360
+ end
361
+
362
+ case response.status
363
+ when 200...300
364
+ created = response.body
365
+ UI.message("DEBUG: #{JSON.pretty_generate(created)}") if ENV['DEBUG']
366
+ UI.success("Created #{os}/#{platform} app with name \"#{created['name']}\"")
367
+ true
368
+ else
369
+ UI.error("Error creating app #{response.status}: #{response.body}")
370
+ false
371
+ end
372
+ else
373
+ UI.error("Lane aborted")
374
+ false
375
+ end
376
+ end
377
+ end
378
+
379
+ def self.run(params)
380
+ values = params.values
381
+ upload_dsym_only = params[:upload_dsym_only]
382
+
383
+ # if app found or successfully created
384
+ if self.get_or_create_app(params)
385
+ self.run_release_upload(params) unless upload_dsym_only
386
+ self.run_dsym_upload(params)
387
+ end
388
+
389
+ return values if Helper.test?
390
+ end
391
+
392
+ def self.description
393
+ "Distribute new release to App Center"
394
+ end
395
+
396
+ def self.authors
397
+ ["Microsoft"]
398
+ end
399
+
400
+ def self.details
401
+ [
402
+ "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."
403
+ ]
404
+ end
405
+
406
+ def self.available_options
407
+ [
408
+ FastlaneCore::ConfigItem.new(key: :api_token,
409
+ env_name: "APPCENTER_API_TOKEN",
410
+ description: "API Token for App Center",
411
+ optional: false,
412
+ type: String,
413
+ verify_block: proc do |value|
414
+ UI.user_error!("No API token for App Center given, pass using `api_token: 'token'`") unless value && !value.empty?
415
+ end),
416
+
417
+ FastlaneCore::ConfigItem.new(key: :owner_name,
418
+ env_name: "APPCENTER_OWNER_NAME",
419
+ description: "Owner name",
420
+ optional: false,
421
+ type: String,
422
+ verify_block: proc do |value|
423
+ UI.user_error!("No Owner name for App Center given, pass using `owner_name: 'name'`") unless value && !value.empty?
424
+ end),
425
+
426
+ FastlaneCore::ConfigItem.new(key: :app_name,
427
+ env_name: "APPCENTER_APP_NAME",
428
+ description: "App name. If there is no app with such name, you will be prompted to create one",
429
+ optional: false,
430
+ type: String,
431
+ verify_block: proc do |value|
432
+ UI.user_error!("No App name given, pass using `app_name: 'app name'`") unless value && !value.empty?
433
+ end),
434
+
435
+ FastlaneCore::ConfigItem.new(key: :apk,
436
+ env_name: "APPCENTER_DISTRIBUTE_APK",
437
+ description: "Build release path for android build",
438
+ default_value: Actions.lane_context[SharedValues::GRADLE_APK_OUTPUT_PATH],
439
+ optional: true,
440
+ type: String,
441
+ conflicting_options: [:ipa],
442
+ conflict_block: proc do |value|
443
+ UI.user_error!("You can't use 'apk' and '#{value.key}' options in one run")
444
+ end,
445
+ verify_block: proc do |value|
446
+ accepted_formats = [".apk"]
447
+ UI.user_error!("Only \".apk\" formats are allowed, you provided \"#{File.extname(value)}\"") unless accepted_formats.include? File.extname(value)
448
+ end),
449
+
450
+ FastlaneCore::ConfigItem.new(key: :ipa,
451
+ env_name: "APPCENTER_DISTRIBUTE_IPA",
452
+ description: "Build release path for ios build",
453
+ default_value: Actions.lane_context[SharedValues::IPA_OUTPUT_PATH],
454
+ optional: true,
455
+ type: String,
456
+ conflicting_options: [:apk],
457
+ conflict_block: proc do |value|
458
+ UI.user_error!("You can't use 'ipa' and '#{value.key}' options in one run")
459
+ end,
460
+ verify_block: proc do |value|
461
+ accepted_formats = [".ipa"]
462
+ UI.user_error!("Only \".ipa\" formats are allowed, you provided \"#{File.extname(value)}\"") unless accepted_formats.include? File.extname(value)
463
+ end),
464
+
465
+ FastlaneCore::ConfigItem.new(key: :dsym,
466
+ env_name: "APPCENTER_DISTRIBUTE_DSYM",
467
+ description: "Path to your symbols file. For iOS provide path to app.dSYM.zip",
468
+ default_value: Actions.lane_context[SharedValues::DSYM_OUTPUT_PATH],
469
+ optional: true,
470
+ type: String,
471
+ verify_block: proc do |value|
472
+ if value
473
+ UI.user_error!("Couldn't find dSYM file at path '#{value}'") unless File.exist?(value)
474
+ end
475
+ end),
476
+
477
+ FastlaneCore::ConfigItem.new(key: :upload_dsym_only,
478
+ env_name: "APPCENTER_DISTRIBUTE_UPLOAD_DSYM_ONLY",
479
+ description: "Flag to upload only the dSYM file to App Center",
480
+ optional: true,
481
+ is_string: false,
482
+ default_value: false),
483
+
484
+ FastlaneCore::ConfigItem.new(key: :group,
485
+ env_name: "APPCENTER_DISTRIBUTE_GROUP",
486
+ description: "Distribute group name",
487
+ default_value: "Collaborators",
488
+ optional: true,
489
+ type: String),
490
+
491
+ FastlaneCore::ConfigItem.new(key: :release_notes,
492
+ env_name: "APPCENTER_DISTRIBUTE_RELEASE_NOTES",
493
+ description: "Release notes",
494
+ default_value: Actions.lane_context[SharedValues::FL_CHANGELOG] || "No changelog given",
495
+ optional: true,
496
+ type: String)
497
+ ]
498
+ end
499
+
500
+ def self.output
501
+ [
502
+ ['APPCENTER_DOWNLOAD_LINK', 'The newly generated download link for this build'],
503
+ ['APPCENTER_BUILD_INFORMATION', 'contains all keys/values from the App Center API']
504
+ ]
505
+ end
506
+
507
+ def self.is_supported?(platform)
508
+ true
509
+ end
510
+
511
+ def self.example_code
512
+ [
513
+ 'appcenter_upload(
514
+ api_token: "...",
515
+ owner_name: "appcenter_owner",
516
+ app_name: "testing_app",
517
+ apk: "./app-release.apk",
518
+ group: "Testers",
519
+ release_notes: "release notes"
520
+ )',
521
+ 'appcenter_upload(
522
+ api_token: "...",
523
+ owner_name: "appcenter_owner",
524
+ app_name: "testing_app",
525
+ apk: "./app-release.ipa",
526
+ group: "Testers",
527
+ dsym: "./app.dSYM.zip",
528
+ release_notes: "release notes"
529
+ )'
530
+ ]
531
+ end
532
+ end
533
+ end
534
+ end
535
+ # rubocop:enable Metrics/ClassLength
@@ -0,0 +1,12 @@
1
+ module Fastlane
2
+ module Helper
3
+ 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
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,5 @@
1
+ module Fastlane
2
+ module Appcenter
3
+ VERSION = "0.1.4"
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,133 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fastlane-plugin-appcenter
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.4
5
+ platform: ruby
6
+ authors:
7
+ - Microsoft Corporation
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-11-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: fastlane
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 2.29.0
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 2.29.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: pry
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description:
98
+ email:
99
+ executables: []
100
+ extensions: []
101
+ extra_rdoc_files: []
102
+ files:
103
+ - LICENSE
104
+ - README.md
105
+ - lib/fastlane/plugin/appcenter.rb
106
+ - lib/fastlane/plugin/appcenter/actions/appcenter_upload_action.rb
107
+ - lib/fastlane/plugin/appcenter/helper/appcenter_helper.rb
108
+ - lib/fastlane/plugin/appcenter/version.rb
109
+ homepage: https://github.com/Microsoft/fastlane-plugin-appcenter
110
+ licenses:
111
+ - MIT
112
+ metadata: {}
113
+ post_install_message:
114
+ rdoc_options: []
115
+ require_paths:
116
+ - lib
117
+ required_ruby_version: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ required_rubygems_version: !ruby/object:Gem::Requirement
123
+ requirements:
124
+ - - ">="
125
+ - !ruby/object:Gem::Version
126
+ version: '0'
127
+ requirements: []
128
+ rubyforge_project:
129
+ rubygems_version: 2.6.12
130
+ signing_key:
131
+ specification_version: 4
132
+ summary: Fastlane plugin for App Center
133
+ test_files: []