fastlane-plugin-appcenter 1.4.0 → 1.5.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
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