fastlane-plugin-appcenter 1.4.0 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f420746739b204f6bfd6e9e5dd23f841028009e63ed49adf45107b053eeb24ac
4
- data.tar.gz: '07896d07c221682028e2c59166c8d248a2ede8d953a3da408f734509eb10ae3a'
3
+ metadata.gz: 39cd3b20f7c98ea0ac0068fdcdb5e8528b763e747efba88d4c6f8da48f902d6d
4
+ data.tar.gz: ca7d95d7ed15b4b5aef398dbfc7d83ef318cf7bb976d8551733950b25bf80fec
5
5
  SHA512:
6
- metadata.gz: 71b84cb05df74504d82d8aaf83626c999f69fdc06481fc072baada8aa29d24fd9e13652359f84a8cca5417b64f3457b5f33dd12d1a769e388e9aa430b0befb7e
7
- data.tar.gz: 6e85fa4da98dfbba7a4147aad34aed090dc1e69224495be770587149dd0efb27747ac5b4ed4b7d1570b24656a610477dab359edbaaedcd6a05fde8d5ef50a452
6
+ metadata.gz: c4f6ae8f7f2b77e0546aa65897362f64959be25b3475c364a85cd3371da31ea3b8b9a8dbc7d77232bdc69172ba34faf61354b5e9b6ea24838e8cab6d14ee418c
7
+ data.tar.gz: de52913eb094b8592428f60835691873e5ad306b5885de6a1bf7b048cdbfcddc69067cc60806bf1306bee40ed5b9aba5e1d5911fca3315859a024c5a57d924c8
data/README.md CHANGED
@@ -13,47 +13,103 @@ fastlane add_plugin appcenter
13
13
  ```
14
14
 
15
15
  ## About App Center
16
- With [App Center](https://appcenter.ms) you can continuously build, test, release, and monitor your apps. This plugin provides an `appcenter_upload` action which allows you to upload and [release distribute](https://docs.microsoft.com/en-us/appcenter/distribution/uploading) apps to your testers on App Center as well as to upload .dSYM files to [collect detailed crash reports](https://docs.microsoft.com/en-us/appcenter/crashes/ios) in App Center.
16
+ With [App Center](https://appcenter.ms) you can continuously build, test, release, and monitor your apps. This plugin provides a set of actions to interact with App Center.
17
+
18
+ `appcenter_fetch_devices` allows you to obtain the list of iOS devices to distribute an app to (useful for automatic provisioning of testers' devices).
19
+
20
+ `appcenter_upload` allows you to upload and [distribute](https://docs.microsoft.com/en-us/appcenter/distribution/uploading) apps to your testers on App Center as well as to upload .dSYM files to [collect detailed crash reports](https://docs.microsoft.com/en-us/appcenter/crashes/ios) in App Center.
17
21
 
18
22
  ## Usage
19
23
 
20
24
  To get started, first, [obtain an API token](https://appcenter.ms/settings/apitokens) in App Center. The API Token is used to authenticate with the App Center API in each call.
21
25
 
22
26
  ```ruby
23
- appcenter_upload(
27
+ appcenter_fetch_devices(
24
28
  api_token: "<appcenter token>",
25
29
  owner_name: "<appcenter account name of the owner of the app (username or organization URL name)>",
26
30
  app_name: "<appcenter app name>",
27
- apk: "<path to android build binary>",
31
+ destinations: "*", # Default is 'Collaborators', use '*' for all distribution groups
32
+ devices_file: "devices.txt" # Default. If you customize, the extension must be .txt
33
+ )
34
+ ```
35
+
36
+ ```ruby
37
+ appcenter_upload(
38
+ api_token: "<appcenter token>",
39
+ owner_name: "<appcenter owner name of the app (as seen in app URL)>",
40
+ app_name: "<appcenter app name (as seen in app URL)>",
41
+ file: "<path to android build binary>",
28
42
  notify_testers: true # Set to false if you don't want to notify testers of your new release (default: `false`)
29
43
  )
30
44
  ```
31
45
 
32
- The action parameters `api_token` and `owner_name` can also be omitted when their values are [set as environment variables](https://docs.fastlane.tools/advanced/#environment-variables). Below a list of all available environment variables:
33
-
34
- - `APPCENTER_API_TOKEN` - API Token for App Center
35
- - `APPCENTER_OWNER_TYPE` - Owner type - `user` or `organization` (default value is `user`)
36
- - `APPCENTER_OWNER_NAME` - Owner name
37
- - `APPCENTER_APP_NAME` - App name. If there is no app with such name, you will be prompted to create one
38
- - `APPCENTER_DISTRIBUTE_APK` - Build release path for android build
39
- - `APPCENTER_DISTRIBUTE_AAB` - Build release path for android app bundle build
40
- - `APPCENTER_DISTRIBUTE_IPA` - Build release path for ios build
41
- - `APPCENTER_DISTRIBUTE_DSYM` - Path to your symbols (app.dSYM.zip) file
42
- - `APPCENTER_DISTRIBUTE_UPLOAD_DSYM_ONLY` - Flag to upload only the dSYM file to App Center
43
- - `APPCENTER_DISTRIBUTE_ANDROID_MAPPING` - Path to your Android mapping.txt file
44
- - `APPCENTER_DISTRIBUTE_UPLOAD_ANDROID_MAPPING_ONLY` - Flag to upload only the mapping file to App Center
45
- - `APPCENTER_DISTRIBUTE_DESTINATIONS` - Comma separated list of destination names. Both distribution groups and stores are supported. All names are required to be of the same destination type. Default is `Collaborators`.
46
- - `APPCENTER_DISTRIBUTE_DESTINATION_TYPE` - Destination type of distribution destination. `group` and `store` are supported. Default is `group`
47
- - `APPCENTER_DISTRIBUTE_MANDATORY_UPDATE` - Require users to update to this release
48
- - `APPCENTER_DISTRIBUTE_NOTIFY_TESTERS` - Send email notification about release (default: `false`)
49
- - `APPCENTER_DISTRIBUTE_RELEASE_NOTES` - Release notes
50
- - `APPCENTER_DISTRIBUTE_RELEASE_NOTES_CLIPPING` - Clip release notes if its length is more then 5000, `true` by default
51
- - `APPCENTER_DISTRIBUTE_RELEASE_NOTES_LINK` - Additional release notes link
52
- - `APPCENTER_DISTRIBUTE_TIMEOUT` - Sets the request timeout in seconds. Used when uploading builds to App Center.
46
+ ### Help
47
+
48
+ Once installed, information and help for an action can be printed out with this command:
49
+
50
+ ```bash
51
+ fastlane action appcenter_upload # or any action included with this plugin
52
+ ```
53
+
54
+ ### A note on App Name
55
+
56
+ The `app_name` and `owner_name` as set in the Fastfile come from the app's URL in App Center, in the below form:
57
+ ```
58
+ https://appcenter.ms/users/{owner_name}/apps/{app_name}
59
+ ```
60
+ They should not be confused with the displayed name on App Center pages, which is called `app_display_name ` instead.
61
+
62
+ ### Parameters
63
+
64
+ The action parameters `api_token`, `owner_name`, `app_name`, and others can also be omitted when their values are [set as environment variables](https://docs.fastlane.tools/advanced/#environment-variables). By default, `appcenter_upload` will use the same `api_token`, `owner_name`, and `app_name` you used in `appcenter_fetch_devices`.
65
+
66
+ Here is the list of all existing parameters:
67
+
68
+ #### `appcenter_fetch_devices`
69
+
70
+ | Key & Env Var | Description |
71
+ |-----------------|--------------------|
72
+ | `api_token` <br/> `APPCENTER_API_TOKEN` | API Token for App Center |
73
+ | `owner_type` <br/> `APPCENTER_OWNER_TYPE` | Owner type, either 'user' or 'organization' (default: `user`) |
74
+ | `owner_name` <br/> `APPCENTER_OWNER_NAME` | Owner name, as found in the App's URL in App Center |
75
+ | `destinations` <br/> `APPCENTER_DISTRIBUTE_DESTINATIONS` | Comma separated list of distribution group names. Default is 'Collaborators', use '*' for all distribution groups |
76
+ | `devices_file` <br/> `FL_REGISTER_DEVICES_FILE` | File to save the devices list to. Same environment variable as _fastlane_'s `register_devices` action |
77
+
78
+ #### `appcenter_upload`
79
+
80
+ | Key & Env Var | Description |
81
+ |-----------------|--------------------|
82
+ | `api_token` <br/> `APPCENTER_API_TOKEN` | API Token for App Center |
83
+ | `owner_type` <br/> `APPCENTER_OWNER_TYPE` | Owner type, either 'user' or 'organization' (default: `user`) |
84
+ | `owner_name` <br/> `APPCENTER_OWNER_NAME` | Owner name, as found in the App's URL in App Center |
85
+ | `app_name` <br/> `APPCENTER_APP_NAME` | App name as found in the App's URL in App Center, if there is no app with such name, you will be prompted to create one |
86
+ | `app_display_name` <br/> `APPCENTER_APP_DISPLAY_NAME` | App display name to use when creating a new app |
87
+ | `app_os` <br/> `APPCENTER_APP_OS` | App OS. Used for new app creation, if app 'app_name' was not found |
88
+ | `app_platform` <br/> `APPCENTER_APP_PLATFORM` | App Platform. Used for new app creation, if app 'app_name' was not found |
89
+ | `apk` <br/> `APPCENTER_DISTRIBUTE_APK` | Build release path for android build |
90
+ | `aab` <br/> `APPCENTER_DISTRIBUTE_AAB` | Build release path for android app bundle build |
91
+ | `ipa` <br/> `APPCENTER_DISTRIBUTE_IPA` | Build release path for iOS builds |
92
+ | `file` <br/> `APPCENTER_DISTRIBUTE_FILE` | Build release path for generic builds (.aab, .app, .app.zip, .apk, .dmg, .ipa, .pkg) |
93
+ | `dsym` <br/> `APPCENTER_DISTRIBUTE_DSYM` | Path to your symbols file. For iOS provide path to app.dSYM.zip |
94
+ | `upload_dsym_only` <br/> `APPCENTER_DISTRIBUTE_UPLOAD_DSYM_ONLY` | Flag to upload only the dSYM file to App Center (default: `false`) |
95
+ | `mapping` <br/> `APPCENTER_DISTRIBUTE_ANDROID_MAPPING` | Path to your Android mapping.txt |
96
+ | `upload_mapping_only` <br/> `APPCENTER_DISTRIBUTE_UPLOAD_ANDROID_MAPPING_ONLY` | Flag to upload only the mapping.txt file to App Center (default: `false`) |
97
+ | `destinations` <br/> `APPCENTER_DISTRIBUTE_DESTINATIONS` | Comma separated list of destination names. Both distribution groups and stores are supported. All names are required to be of the same destination type (default: `Collaborators`) |
98
+ | `destination_type` <br/> `APPCENTER_DISTRIBUTE_DESTINATION_TYPE` | Destination type of distribution destination. 'group' and 'store' are supported (default: `group`) |
99
+ | `mandatory_update` <br/> `APPCENTER_DISTRIBUTE_MANDATORY_UPDATE` | Require users to update to this release. Ignored if destination type is 'store' (default: `false`) |
100
+ | `notify_testers` <br/> `APPCENTER_DISTRIBUTE_NOTIFY_TESTERS` | Send email notification about release. Ignored if destination type is 'store' (default: `false`) |
101
+ | `release_notes` <br/> `APPCENTER_DISTRIBUTE_RELEASE_NOTES` | Release notes (default: `No changelog given`) |
102
+ | `should_clip` <br/> `APPCENTER_DISTRIBUTE_RELEASE_NOTES_CLIPPING` | Clip release notes if its length is more then 5000, true by default (default: `true`) |
103
+ | `release_notes_link` <br/> `APPCENTER_DISTRIBUTE_RELEASE_NOTES_LINK` | Additional release notes link |
104
+ | `build_number` <br/> `APPCENTER_DISTRIBUTE_BUILD_NUMBER` | The build number, required for Android ProGuard mapping files, as well as macOS .pkg and .dmg builds |
105
+ | `version` <br/> `APPCENTER_DISTRIBUTE_VERSION` | The build version, required for Android ProGuard mapping files, as well as macOS .pkg and .dmg builds |
106
+ | `timeout` <br/> `APPCENTER_DISTRIBUTE_TIMEOUT` | Request timeout in seconds |
107
+ | `dsa_signature` <br/> `APPCENTER_DISTRIBUTE_DSA_SIGNATURE` | DSA signature of the macOS or Windows releases for Sparkle update feed |
108
+
53
109
 
54
110
  ## Example
55
111
 
56
- 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`.
112
+ Check out this [example `Fastfile`](fastlane/Fastfile) to see how to use the `appcenter_upload` action. Try it by cloning the repo, running `fastlane install_plugins` and `bundle exec fastlane test`.
57
113
 
58
114
  Sample uses `.env` for setting private variables like API token, owner name, .etc. You need to replace it in `Fastfile` by your own values.
59
115
 
@@ -62,6 +118,8 @@ There are three examples in `test` lane:
62
118
  - upload release for ios with all set parameters
63
119
  - upload only dSYM file for ios
64
120
 
121
+ Check out this [example `Fastfile`](fastlane/fetch_devices/Fastfile) for a full example of fetching devices, registering them with Apple, provisioning the devices, and signing an app.
122
+
65
123
  ## Run tests for this plugin
66
124
 
67
125
  To run both the tests, and code style validation, run
@@ -0,0 +1,138 @@
1
+ require 'fastlane/action'
2
+ require 'csv'
3
+ require 'faraday'
4
+
5
+ module Fastlane
6
+ module Actions
7
+ module SharedValues
8
+ APPCENTER_API_TOKEN = :APPCENTER_API_TOKEN
9
+ APPCENTER_OWNER_NAME = :APPCENTER_OWNER_NAME
10
+ APPCENTER_APP_NAME = :APPCENTER_APP_NAME
11
+ APPCENTER_DISTRIBUTE_DESTINATIONS = :APPCENTER_DISTRIBUTE_DESTINATIONS
12
+ end
13
+
14
+ class AppcenterFetchDevicesAction < Action
15
+ def self.run(params)
16
+ api_token = params[:api_token]
17
+ owner_name = params[:owner_name]
18
+ app_name = params[:app_name]
19
+ destinations = params[:destinations]
20
+
21
+ Actions.lane_context[SharedValues::APPCENTER_API_TOKEN] = api_token
22
+ Actions.lane_context[SharedValues::APPCENTER_OWNER_NAME] = owner_name
23
+ Actions.lane_context[SharedValues::APPCENTER_APP_NAME] = app_name
24
+
25
+ group_names = []
26
+ if destinations == '*'
27
+ UI.message("Looking up all distribution groups for #{owner_name}/#{app_name}")
28
+ distribution_groups = Helper::AppcenterHelper.fetch_distribution_groups(
29
+ api_token: api_token,
30
+ owner_name: owner_name,
31
+ app_name: app_name
32
+ )
33
+ UI.abort_with_message!("Failed to list distribution groups for #{owner_name}/#{app_name}") unless distribution_groups
34
+ distribution_groups.each do |group|
35
+ group_names << group['name']
36
+ end
37
+ else
38
+ group_names += destinations.split(',').map(&:strip)
39
+ end
40
+
41
+ Actions.lane_context[SharedValues::APPCENTER_DISTRIBUTE_DESTINATIONS] = group_names.join(',')
42
+
43
+ devices = []
44
+ group_names.each do |group_name|
45
+ group_devices = Helper::AppcenterHelper.fetch_devices(
46
+ api_token: api_token,
47
+ owner_name: owner_name,
48
+ app_name: app_name,
49
+ distribution_group: group_name
50
+ )
51
+ UI.abort_with_message!("Failed to get devices for group '#{group_name}'") unless group_devices
52
+ devices << group_devices
53
+ end
54
+
55
+ self.write_devices(devices: devices, devices_file: params[:devices_file])
56
+ end
57
+
58
+ def self.write_devices(devices:, devices_file:)
59
+ CSV.open(devices_file, 'w',
60
+ write_headers: true,
61
+ headers: ['Device ID', 'Device Name'],
62
+ col_sep: "\t") do |csv|
63
+
64
+ devices.each do |device|
65
+ CSV.parse(device, { col_sep: "\t", headers: true }) do |row|
66
+ csv << row
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ def self.description
73
+ "Fetches a list of devices from App Center to distribute an iOS app to"
74
+ end
75
+
76
+ def self.authors
77
+ ["benkane"]
78
+ end
79
+
80
+ def self.return_value
81
+ "CSV file formatted for multi-device registration with Apple"
82
+ end
83
+
84
+ def self.details
85
+ "List is a tab-delimited CSV file containing every device from specified distribution groups for an app in App Center. " +
86
+ "Especially useful when combined with register_devices and match to automatically register and provision devices with Apple. " +
87
+ "By default, only the Collaborators group will be included, use `destination: '*'` to match all groups."
88
+ end
89
+
90
+ def self.available_options
91
+ [
92
+ FastlaneCore::ConfigItem.new(key: :api_token,
93
+ env_name: "APPCENTER_API_TOKEN",
94
+ sensitive: true,
95
+ description: "API Token for App Center",
96
+ optional: false,
97
+ type: String,
98
+ verify_block: proc do |value|
99
+ UI.user_error!("No API token for App Center given, pass using `api_token: 'token'`") unless value && !value.empty?
100
+ end),
101
+ FastlaneCore::ConfigItem.new(key: :owner_name,
102
+ env_name: "APPCENTER_OWNER_NAME",
103
+ description: "Owner name",
104
+ optional: false,
105
+ type: String,
106
+ verify_block: proc do |value|
107
+ UI.user_error!("No Owner name for App Center given, pass using `owner_name: 'name'`") unless value && !value.empty?
108
+ end),
109
+ FastlaneCore::ConfigItem.new(key: :app_name,
110
+ env_name: "APPCENTER_APP_NAME",
111
+ description: "App name",
112
+ optional: false,
113
+ type: String,
114
+ verify_block: proc do |value|
115
+ UI.user_error!("No App name given, pass using `app_name: 'app name'`") unless value && !value.empty?
116
+ end),
117
+ FastlaneCore::ConfigItem.new(key: :devices_file,
118
+ env_name: "FL_REGISTER_DEVICES_FILE",
119
+ description: "File to save devices to",
120
+ type: String,
121
+ default_value: "devices.txt",
122
+ verify_block: proc do |value|
123
+ UI.important("Important: Devices file is #{value}. If you plan to upload this file to Apple Developer Center, the file must have the .txt extension") unless value && value.end_with?('.txt')
124
+ end),
125
+ FastlaneCore::ConfigItem.new(key: :destinations,
126
+ env_name: "APPCENTER_DISTRIBUTE_DESTINATIONS",
127
+ description: "Comma separated list of distribution group names. Default is 'Collaborators', use '*' for all distribution groups",
128
+ default_value: "Collaborators",
129
+ type: String)
130
+ ]
131
+ end
132
+
133
+ def self.is_supported?(platform)
134
+ [:ios].include?(platform)
135
+ end
136
+ end
137
+ end
138
+ end
@@ -94,6 +94,7 @@ module Fastlane
94
94
  values = params.values
95
95
  api_token = params[:api_token]
96
96
  owner_name = params[:owner_name]
97
+ owner_type = params[:owner_type]
97
98
  app_name = params[:app_name]
98
99
  destinations = params[:destinations]
99
100
  destination_type = params[:destination_type]
@@ -105,6 +106,7 @@ module Fastlane
105
106
  timeout = params[:timeout]
106
107
  build_number = params[:build_number]
107
108
  version = params[:version]
109
+ dsa_signature = params[:dsa_signature]
108
110
 
109
111
  if release_notes.length >= Constants::MAX_RELEASE_NOTES_LENGTH
110
112
  unless should_clip
@@ -166,9 +168,11 @@ module Fastlane
166
168
 
167
169
  if uploaded
168
170
  release_id = uploaded['release_id']
169
- UI.message("Release '#{release_id}' committed")
171
+ release_url = Helper::AppcenterHelper.get_release_url(owner_type, owner_name, app_name, release_id)
172
+ UI.message("Release '#{release_id}' committed: #{release_url}")
170
173
 
171
174
  Helper::AppcenterHelper.update_release(api_token, owner_name, app_name, release_id, release_notes)
175
+ Helper::AppcenterHelper.update_release_metadata(api_token, owner_name, app_name, release_id, dsa_signature)
172
176
 
173
177
  destinations_array = destinations.split(',')
174
178
  destinations_array.each do |destination_name|
@@ -177,14 +181,17 @@ module Fastlane
177
181
  destination_id = destination['id']
178
182
  distributed_release = Helper::AppcenterHelper.add_to_destination(api_token, owner_name, app_name, release_id, destination_type, destination_id, mandatory_update, notify_testers)
179
183
  if distributed_release
180
- UI.success("Release #{distributed_release['short_version']} was successfully distributed to #{destination_type} \"#{destination_name}\"")
184
+ UI.success("Release '#{release_id}' (#{distributed_release['short_version']}) was successfully distributed to #{destination_type} \"#{destination_name}\"")
181
185
  else
182
- UI.error("Release '#{release_id}' was not found")
186
+ UI.error("Release '#{release_id}' was not found for destination '#{destination_name}'")
183
187
  end
184
188
  else
185
189
  UI.error("#{destination_type} '#{destination_name}' was not found")
186
190
  end
187
191
  end
192
+
193
+ safe_download_url = Helper::AppcenterHelper.get_install_url(owner_type, owner_name, app_name)
194
+ UI.message("Release '#{release_id}' is available for download at: #{safe_download_url}")
188
195
  else
189
196
  UI.user_error!("Failed to upload release")
190
197
  end
@@ -260,6 +267,7 @@ module Fastlane
260
267
  FastlaneCore::ConfigItem.new(key: :api_token,
261
268
  env_name: "APPCENTER_API_TOKEN",
262
269
  description: "API Token for App Center",
270
+ default_value: Actions.lane_context[SharedValues::APPCENTER_API_TOKEN],
263
271
  optional: false,
264
272
  type: String,
265
273
  verify_block: proc do |value|
@@ -268,7 +276,7 @@ module Fastlane
268
276
 
269
277
  FastlaneCore::ConfigItem.new(key: :owner_type,
270
278
  env_name: "APPCENTER_OWNER_TYPE",
271
- description: "Owner type",
279
+ description: "Owner type, either 'user' or 'organization'",
272
280
  optional: true,
273
281
  default_value: "user",
274
282
  type: String,
@@ -279,7 +287,8 @@ module Fastlane
279
287
 
280
288
  FastlaneCore::ConfigItem.new(key: :owner_name,
281
289
  env_name: "APPCENTER_OWNER_NAME",
282
- description: "Owner name",
290
+ description: "Owner name as found in the App's URL in App Center",
291
+ default_value: Actions.lane_context[SharedValues::APPCENTER_OWNER_NAME],
283
292
  optional: false,
284
293
  type: String,
285
294
  verify_block: proc do |value|
@@ -288,7 +297,8 @@ module Fastlane
288
297
 
289
298
  FastlaneCore::ConfigItem.new(key: :app_name,
290
299
  env_name: "APPCENTER_APP_NAME",
291
- description: "App name. If there is no app with such name, you will be prompted to create one",
300
+ description: "App name as found in the App's URL in App Center. If there is no app with such name, you will be prompted to create one",
301
+ default_value: Actions.lane_context[SharedValues::APPCENTER_APP_NAME],
292
302
  optional: false,
293
303
  type: String,
294
304
  verify_block: proc do |value|
@@ -303,13 +313,13 @@ module Fastlane
303
313
 
304
314
  FastlaneCore::ConfigItem.new(key: :app_os,
305
315
  env_name: "APPCENTER_APP_OS",
306
- description: "App OS. Used for new app creation, if app with 'app_name' name was not found",
316
+ description: "App OS. Used for new app creation, if app 'app_name' was not found",
307
317
  optional: true,
308
318
  type: String),
309
319
 
310
320
  FastlaneCore::ConfigItem.new(key: :app_platform,
311
321
  env_name: "APPCENTER_APP_PLATFORM",
312
- description: "App Platform. Used for new app creation, if app with 'app_name' name was not found",
322
+ description: "App Platform. Used for new app creation, if app 'app_name' was not found",
313
323
  optional: true,
314
324
  type: String),
315
325
 
@@ -428,7 +438,7 @@ module Fastlane
428
438
  FastlaneCore::ConfigItem.new(key: :destinations,
429
439
  env_name: "APPCENTER_DISTRIBUTE_DESTINATIONS",
430
440
  description: "Comma separated list of destination names. Both distribution groups and stores are supported. All names are required to be of the same destination type",
431
- default_value: "Collaborators",
441
+ default_value: Actions.lane_context[SharedValues::APPCENTER_DISTRIBUTE_DESTINATIONS] || "Collaborators",
432
442
  optional: true,
433
443
  type: String),
434
444
 
@@ -494,6 +504,12 @@ module Fastlane
494
504
  description: "Request timeout in seconds",
495
505
  optional: true,
496
506
  type: Integer),
507
+
508
+ FastlaneCore::ConfigItem.new(key: :dsa_signature,
509
+ env_name: "APPCENTER_DISTRIBUTE_DSA_SIGNATURE",
510
+ description: "DSA signature of the macOS or Windows releases for Sparkle update feed",
511
+ optional: true,
512
+ type: String)
497
513
  ]
498
514
  end
499
515
 
@@ -16,12 +16,12 @@ module Fastlane
16
16
  end
17
17
 
18
18
  # create request
19
- def self.connection(upload_url = false, dsym = false)
19
+ def self.connection(upload_url = nil, dsym = false, csv = false)
20
20
  require 'faraday'
21
21
  require 'faraday_middleware'
22
22
 
23
23
  options = {
24
- url: upload_url ? upload_url : ENV.fetch('APPCENTER_UPLOAD_URL', "https://api.appcenter.ms")
24
+ url: upload_url || ENV.fetch('APPCENTER_UPLOAD_URL', "https://api.appcenter.ms")
25
25
  }
26
26
 
27
27
  Faraday.new(options) do |builder|
@@ -31,7 +31,7 @@ module Fastlane
31
31
  else
32
32
  builder.request :json
33
33
  end
34
- builder.response :json, content_type: /\bjson$/
34
+ builder.response :json, content_type: /\bjson$/ unless csv
35
35
  builder.use FaradayMiddleware::FollowRedirects
36
36
  builder.adapter :net_http
37
37
  end
@@ -151,6 +151,9 @@ module Fastlane
151
151
  when 200...300
152
152
  UI.message("DEBUG: #{JSON.pretty_generate(response.body)}\n") if ENV['DEBUG']
153
153
  response.body
154
+ when 401
155
+ UI.user_error!("Auth Error, provided invalid token")
156
+ false
154
157
  else
155
158
  UI.error("Error #{response.status}: #{response.body}")
156
159
  false
@@ -177,6 +180,9 @@ module Fastlane
177
180
  when 200...300
178
181
  self.update_symbol_upload(api_token, owner_name, app_name, symbol_upload_id, 'committed')
179
182
  UI.success("#{logType} uploaded")
183
+ when 401
184
+ UI.user_error!("Auth Error, provided invalid token")
185
+ false
180
186
  else
181
187
  UI.error("Error uploading #{logType} #{response.status}: #{response.body}")
182
188
  self.update_symbol_upload(api_token, owner_name, app_name, symbol_upload_id, 'aborted')
@@ -206,6 +212,9 @@ module Fastlane
206
212
  when 200...300
207
213
  UI.message("Binary uploaded")
208
214
  self.update_release_upload(api_token, owner_name, app_name, upload_id, 'committed')
215
+ when 401
216
+ UI.user_error!("Auth Error, provided invalid token")
217
+ false
209
218
  else
210
219
  UI.error("Error uploading binary #{response.status}: #{response.body}")
211
220
  self.update_release_upload(api_token, owner_name, app_name, upload_id, 'aborted')
@@ -230,6 +239,9 @@ module Fastlane
230
239
  when 200...300
231
240
  UI.message("DEBUG: #{JSON.pretty_generate(response.body)}\n") if ENV['DEBUG']
232
241
  response.body
242
+ when 401
243
+ UI.user_error!("Auth Error, provided invalid token")
244
+ false
233
245
  when 500...600
234
246
  UI.crash!("Internal Service Error, please try again later")
235
247
  else
@@ -254,6 +266,9 @@ module Fastlane
254
266
  when 404
255
267
  UI.error("Not found, invalid release url")
256
268
  false
269
+ when 401
270
+ UI.user_error!("Auth Error, provided invalid token")
271
+ false
257
272
  else
258
273
  UI.error("Error fetching information about release #{response.status}: #{response.body}")
259
274
  false
@@ -277,6 +292,9 @@ module Fastlane
277
292
  when 404
278
293
  UI.error("Not found, invalid distribution #{destination_type} name")
279
294
  false
295
+ when 401
296
+ UI.user_error!("Auth Error, provided invalid token")
297
+ false
280
298
  else
281
299
  UI.error("Error getting #{destination_type} #{response.status}: #{response.body}")
282
300
  false
@@ -291,7 +309,7 @@ module Fastlane
291
309
  req.headers['X-API-Token'] = api_token
292
310
  req.headers['internal-request-source'] = "fastlane"
293
311
  req.body = {
294
- "release_notes" => release_notes
312
+ release_notes: release_notes
295
313
  }
296
314
  end
297
315
 
@@ -300,6 +318,7 @@ module Fastlane
300
318
  # get full release info
301
319
  release = self.get_release(api_token, owner_name, app_name, release_id)
302
320
  return false unless release
321
+
303
322
  download_url = release['download_url']
304
323
 
305
324
  UI.message("DEBUG: #{JSON.pretty_generate(release)}") if ENV['DEBUG']
@@ -307,18 +326,53 @@ module Fastlane
307
326
  Actions.lane_context[Fastlane::Actions::SharedValues::APPCENTER_DOWNLOAD_LINK] = download_url
308
327
  Actions.lane_context[Fastlane::Actions::SharedValues::APPCENTER_BUILD_INFORMATION] = release
309
328
 
310
- UI.message("Release #{release['short_version']} was successfully updated")
329
+ UI.message("Release '#{release_id}' (#{release['short_version']}) was successfully updated")
311
330
 
312
331
  release
313
332
  when 404
314
333
  UI.error("Not found, invalid release id")
315
334
  false
335
+ when 401
336
+ UI.user_error!("Auth Error, provided invalid token")
337
+ false
316
338
  else
317
339
  UI.error("Error adding updating release #{response.status}: #{response.body}")
318
340
  false
319
341
  end
320
342
  end
321
343
 
344
+ # updates release metadata
345
+ def self.update_release_metadata(api_token, owner_name, app_name, release_id, dsa_signature)
346
+ return if dsa_signature.to_s == ''
347
+
348
+ release_metadata = {
349
+ dsa_signature: dsa_signature
350
+ }
351
+
352
+ connection = self.connection
353
+ response = connection.patch("v0.1/apps/#{owner_name}/#{app_name}/releases/#{release_id}") do |req|
354
+ req.headers['X-API-Token'] = api_token
355
+ req.headers['internal-request-source'] = "fastlane"
356
+ req.body = {
357
+ metadata: release_metadata
358
+ }
359
+ end
360
+
361
+ case response.status
362
+ when 200...300
363
+ UI.message("Release Metadata was successfully updated for release '#{release_id}'")
364
+ when 404
365
+ UI.error("Not found, invalid release id")
366
+ false
367
+ when 401
368
+ UI.user_error!("Auth Error, provided invalid token")
369
+ false
370
+ else
371
+ UI.error("Error adding updating release metadata #{response.status}: #{response.body}")
372
+ false
373
+ end
374
+ end
375
+
322
376
  # add release to distribution group or store
323
377
  def self.add_to_destination(api_token, owner_name, app_name, release_id, destination_type, destination_id, mandatory_update = false, notify_testers = false)
324
378
  connection = self.connection
@@ -342,6 +396,7 @@ module Fastlane
342
396
  # get full release info
343
397
  release = self.get_release(api_token, owner_name, app_name, release_id)
344
398
  return false unless release
399
+
345
400
  download_url = release['download_url']
346
401
 
347
402
  UI.message("DEBUG: received release #{JSON.pretty_generate(release)}") if ENV['DEBUG']
@@ -349,12 +404,15 @@ module Fastlane
349
404
  Actions.lane_context[Fastlane::Actions::SharedValues::APPCENTER_DOWNLOAD_LINK] = download_url
350
405
  Actions.lane_context[Fastlane::Actions::SharedValues::APPCENTER_BUILD_INFORMATION] = release
351
406
 
352
- UI.message("Public Download URL: #{download_url}") if download_url
407
+ UI.message("Release '#{release_id}' (#{release['short_version']}) was successfully distributed'")
353
408
 
354
409
  release
355
410
  when 404
356
411
  UI.error("Not found, invalid distribution #{destination_type} name")
357
412
  false
413
+ when 401
414
+ UI.user_error!("Auth Error, provided invalid token")
415
+ false
358
416
  else
359
417
  UI.error("Error adding to #{destination_type} #{response.status}: #{response.body}")
360
418
  false
@@ -377,6 +435,9 @@ module Fastlane
377
435
  when 404
378
436
  UI.message("DEBUG: #{JSON.pretty_generate(response.body)}\n") if ENV['DEBUG']
379
437
  false
438
+ when 401
439
+ UI.user_error!("Auth Error, provided invalid token")
440
+ false
380
441
  else
381
442
  UI.error("Error getting app #{owner_name}/#{app_name}, #{response.status}: #{response.body}")
382
443
  false
@@ -406,11 +467,78 @@ module Fastlane
406
467
  UI.message("DEBUG: #{JSON.pretty_generate(created)}") if ENV['DEBUG']
407
468
  UI.success("Created #{os}/#{platform} app with name \"#{created['name']}\" and display name \"#{created['display_name']}\" for #{owner_type} \"#{owner_name}\"")
408
469
  true
470
+ when 401
471
+ UI.user_error!("Auth Error, provided invalid token")
472
+ false
409
473
  else
410
474
  UI.error("Error creating app #{response.status}: #{response.body}")
411
475
  false
412
476
  end
413
477
  end
478
+
479
+ def self.fetch_distribution_groups(api_token:, owner_name:, app_name:)
480
+ connection = self.connection
481
+
482
+ endpoint = "/v0.1/apps/#{owner_name}/#{app_name}/distribution_groups"
483
+
484
+ response = connection.get(endpoint) do |req|
485
+ req.headers['X-API-Token'] = api_token
486
+ req.headers['internal-request-source'] = "fastlane"
487
+ end
488
+
489
+ case response.status
490
+ when 200...300
491
+ UI.message("DEBUG: #{JSON.pretty_generate(response.body)}\n") if ENV['DEBUG']
492
+ response.body
493
+ when 401
494
+ UI.user_error!("Auth Error, provided invalid token")
495
+ false
496
+ when 404
497
+ UI.error("Not found, invalid owner or application name")
498
+ false
499
+ else
500
+ UI.error("Error #{response.status}: #{response.body}")
501
+ false
502
+ end
503
+ end
504
+
505
+ def self.fetch_devices(api_token:, owner_name:, app_name:, distribution_group:)
506
+ connection = self.connection(nil, false, true)
507
+
508
+ endpoint = "/v0.1/apps/#{owner_name}/#{app_name}/distribution_groups/#{ERB::Util.url_encode(distribution_group)}/devices/download_devices_list"
509
+
510
+ response = connection.get(endpoint) do |req|
511
+ req.headers['X-API-Token'] = api_token
512
+ req.headers['internal-request-source'] = "fastlane"
513
+ end
514
+
515
+ case response.status
516
+ when 200...300
517
+ UI.message("DEBUG: #{response.body.inspect}") if ENV['DEBUG']
518
+ response.body
519
+ when 401
520
+ UI.user_error!("Auth Error, provided invalid token")
521
+ false
522
+ when 404
523
+ UI.error("Not found, invalid owner, application or distribution group name")
524
+ false
525
+ else
526
+ UI.error("Error #{response.status}: #{response.body}")
527
+ false
528
+ end
529
+ end
530
+
531
+ # Note: This does not support testing environment (INT)
532
+ def self.get_release_url(owner_type, owner_name, app_name, release_id)
533
+ owner_path = owner_type == "user" ? "users/#{owner_name}" : "orgs/#{owner_name}"
534
+ return "https://appcenter.ms/#{owner_path}/apps/#{app_name}/distribute/releases/#{release_id}"
535
+ end
536
+
537
+ # Note: This does not support testing environment (INT)
538
+ def self.get_install_url(owner_type, owner_name, app_name)
539
+ owner_path = owner_type == "user" ? "users/#{owner_name}" : "orgs/#{owner_name}"
540
+ return "https://install.appcenter.ms/#{owner_path}/apps/#{app_name}"
541
+ end
414
542
  end
415
543
  end
416
544
  end
@@ -1,5 +1,5 @@
1
1
  module Fastlane
2
2
  module Appcenter
3
- VERSION = "1.4.0"
3
+ VERSION = "1.5.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: 1.4.0
4
+ version: 1.5.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: 2019-08-30 00:00:00.000000000 Z
11
+ date: 2019-10-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -103,6 +103,7 @@ files:
103
103
  - LICENSE
104
104
  - README.md
105
105
  - lib/fastlane/plugin/appcenter.rb
106
+ - lib/fastlane/plugin/appcenter/actions/appcenter_fetch_devices_action.rb
106
107
  - lib/fastlane/plugin/appcenter/actions/appcenter_upload_action.rb
107
108
  - lib/fastlane/plugin/appcenter/helper/appcenter_helper.rb
108
109
  - lib/fastlane/plugin/appcenter/version.rb