fastlane-plugin-appcenter 1.6.0 → 1.9.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: 172c940fc1d03ac4d3951cffc2c0f1c1d1b78ee25967f464d0a1058f12d7c7b9
4
- data.tar.gz: f6835efe4d035061a6c8834e27c355817061835c9be067fe2090b2ae9dd3b65a
3
+ metadata.gz: 89d582118585c4f375abd0b893ad9648180e33d19308d20c881aea0262356a82
4
+ data.tar.gz: 57a447973defb5be52cee9ed03a9d635e442cc30b523152b23d0754b63638c14
5
5
  SHA512:
6
- metadata.gz: 9060632241323fe3a7cbc412b77cbbe457819277176e06a00fdbf155ef72c01aa19453a626214003b211bee5a9388fd4ce509b97a823489e0d9022674b14e908
7
- data.tar.gz: 68fce7a6a0755c2fe71363e7f85d1f88d44f3303d7a94876c15d57d810033021ad410461fb399b91fee82aae4710fe1740a033967c2576245495f12360dd2a48
6
+ metadata.gz: 3c9c355551542046daffa8383aee7521d9185b76cc4351e494e6645de638a982af60812bb14982ef171707716dce5761d87e98dd9455a2548bde66d3157237db
7
+ data.tar.gz: c9a7690e05d9251cae36125ab72669bf11733b2fbf25c8d0f15544fe741b9f09e525d924524eb4daccc7056bdd290c06bc68df5f6a72d64558fc225fb4597544
data/README.md CHANGED
@@ -21,6 +21,8 @@ With [App Center](https://appcenter.ms) you can continuously build, test, releas
21
21
 
22
22
  `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.
23
23
 
24
+ `appcenter_fetch_version_number` allows you to obtain the latest version number (short or full) for an app. This is useful for tasks such as getting the latest version of an app so that an increment action can take place on CI, or checking that an upload has been successful.
25
+
24
26
  ## Usage
25
27
 
26
28
  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.
@@ -29,7 +31,6 @@ To get started, first, [obtain an API token](https://appcenter.ms/settings/apito
29
31
  appcenter_fetch_devices(
30
32
  api_token: "<appcenter token>",
31
33
  owner_name: "<appcenter account name of the owner of the app (username or organization URL name)>",
32
- owner_type: "user", # Default is user - set to organization for appcenter organizations
33
34
  app_name: "<appcenter app name>",
34
35
  destinations: "*", # Default is 'Collaborators', use '*' for all distribution groups
35
36
  devices_file: "devices.txt" # Default. If you customize, the extension must be .txt
@@ -47,6 +48,20 @@ appcenter_upload(
47
48
  )
48
49
  ```
49
50
 
51
+ ```ruby
52
+ appcenter_fetch_version_number(
53
+ api_token: "<appcenter token>",
54
+ owner_name: "<appcenter account name of the owner of the app (username or organization URL name)>",
55
+ app_name: "<appcenter app name (as seen in app URL)>"
56
+ )
57
+ ```
58
+
59
+ The `appcenter_fetch_version_number` returns a hash that contains the id, the version number, and the build number. The version corresponds to the `short_version` and the build number to the `version` known by App Center for a given release:
60
+ ```ruby
61
+ {"id"=>1, "version"=>"1.0.0", "build_number"=>"1.0.0.1234"} # iOS apps contain the full version plus build number due to the way that Apple use CFBundleVersion for this value
62
+ {"id"=>588, "version"=>"1.2.0", "build_number"=>"1615"}
63
+ ```
64
+
50
65
  ### Help
51
66
 
52
67
  Once installed, information and help for an action can be printed out with this command:
@@ -74,7 +89,6 @@ Here is the list of all existing parameters:
74
89
  | Key & Env Var | Description |
75
90
  |-----------------|--------------------|
76
91
  | `api_token` <br/> `APPCENTER_API_TOKEN` | API Token for App Center |
77
- | `owner_type` <br/> `APPCENTER_OWNER_TYPE` | Owner type, either 'user' or 'organization' (default: `user`) |
78
92
  | `owner_name` <br/> `APPCENTER_OWNER_NAME` | Owner name, as found in the App's URL in App Center |
79
93
  | `destinations` <br/> `APPCENTER_DISTRIBUTE_DESTINATIONS` | Comma separated list of distribution group names. Default is 'Collaborators', use '*' for all distribution groups |
80
94
  | `devices_file` <br/> `FL_REGISTER_DEVICES_FILE` | File to save the devices list to. Same environment variable as _fastlane_'s `register_devices` action |
@@ -88,14 +102,15 @@ Here is the list of all existing parameters:
88
102
  | `owner_name` <br/> `APPCENTER_OWNER_NAME` | Owner name as found in the App's URL in App Center |
89
103
  | `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 |
90
104
  | `app_display_name` <br/> `APPCENTER_APP_DISPLAY_NAME` | App display name to use when creating a new app |
91
- | `app_os` <br/> `APPCENTER_APP_OS` | App OS. Used for new app creation, if app 'app_name' was not found |
105
+ | `app_os` <br/> `APPCENTER_APP_OS` | App OS can be Android, iOS, macOS, Windows, Custom. Used for new app creation, if app 'app_name' was not found |
92
106
  | `app_platform` <br/> `APPCENTER_APP_PLATFORM` | App Platform. Used for new app creation, if app 'app_name' was not found |
93
107
  | `file` <br/> `APPCENTER_DISTRIBUTE_FILE` | File path to the release build to publish |
108
+ | `upload_build_only` <br/> `APPCENTER_DISTRIBUTE_UPLOAD_BUILD_ONLY` | Flag to upload only the build to App Center. Skips uploading symbols or mapping (default: `false`) |
94
109
  | `dsym` <br/> `APPCENTER_DISTRIBUTE_DSYM` | Path to your symbols file. For iOS provide path to app.dSYM.zip |
95
110
  | `upload_dsym_only` <br/> `APPCENTER_DISTRIBUTE_UPLOAD_DSYM_ONLY` | Flag to upload only the dSYM file to App Center (default: `false`) |
96
111
  | `mapping` <br/> `APPCENTER_DISTRIBUTE_ANDROID_MAPPING` | Path to your Android mapping.txt |
97
112
  | `upload_mapping_only` <br/> `APPCENTER_DISTRIBUTE_UPLOAD_ANDROID_MAPPING_ONLY` | Flag to upload only the mapping.txt file to App Center (default: `false`) |
98
- | `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`) |
113
+ | `destinations` <br/> `APPCENTER_DISTRIBUTE_DESTINATIONS` | Comma separated list of destination names, use '*' for all distribution groups if destination type is 'group'. Both distribution groups and stores are supported. All names are required to be of the same destination type (default: `Collaborators`) |
99
114
  | `destination_type` <br/> `APPCENTER_DISTRIBUTE_DESTINATION_TYPE` | Destination type of distribution destination. 'group' and 'store' are supported (default: `group`) |
100
115
  | `mandatory_update` <br/> `APPCENTER_DISTRIBUTE_MANDATORY_UPDATE` | Require users to update to this release. Ignored if destination type is 'store' (default: `false`) |
101
116
  | `notify_testers` <br/> `APPCENTER_DISTRIBUTE_NOTIFY_TESTERS` | Send email notification about release. Ignored if destination type is 'store' (default: `false`) |
@@ -104,10 +119,17 @@ Here is the list of all existing parameters:
104
119
  | `release_notes_link` <br/> `APPCENTER_DISTRIBUTE_RELEASE_NOTES_LINK` | Additional release notes link |
105
120
  | `build_number` <br/> `APPCENTER_DISTRIBUTE_BUILD_NUMBER` | The build number, required for macOS .pkg and .dmg builds, as well as Android ProGuard `mapping.txt` when using `upload_mapping_only` |
106
121
  | `version` <br/> `APPCENTER_DISTRIBUTE_VERSION` | The build version, required for .pkg, .dmg, .zip and .msi builds, as well as Android ProGuard `mapping.txt` when using `upload_mapping_only` |
107
- | `timeout` <br/> `APPCENTER_DISTRIBUTE_TIMEOUT` | Request timeout in seconds |
122
+ | `timeout` <br/> `APPCENTER_DISTRIBUTE_TIMEOUT` | Request timeout in seconds applied to individual HTTP requests. Some commands use multiple HTTP requests, large file uploads are also split in multiple HTTP requests |
108
123
  | `dsa_signature` <br/> `APPCENTER_DISTRIBUTE_DSA_SIGNATURE` | DSA signature of the macOS or Windows release for Sparkle update feed |
109
124
  | `strict` <br/> `APPCENTER_STRICT_MODE` | Strict mode, set to 'true' to fail early in case a potential error was detected |
110
125
 
126
+ #### `appcenter_fetch_version_number`
127
+
128
+ | Key & Env Var | Description |
129
+ |-----------------|--------------------|
130
+ | `api_token` <br/> `APPCENTER_API_TOKEN` | API Token for App Center |
131
+ | `owner_name` <br/> `APPCENTER_OWNER_NAME` | Owner name, as found in the App's URL in App Center |
132
+ | `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 |
111
133
 
112
134
  ## Example
113
135
 
@@ -155,6 +177,10 @@ _fastlane_ is the easiest way to automate beta deployments and releases for your
155
177
 
156
178
  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.
157
179
 
180
+ ## Security
181
+
182
+ Check out [SECURITY.md](SECURITY.md) for any security concern with this project.
183
+
158
184
  ## Contact
159
185
 
160
- We're on Twitter as [@vsappcenter](https://www.twitter.com/vsappcenter). Additionally you can reach out to us on the [App Center](https://appcenter.ms/apps) portal by using the blue Intercom button on the bottom right to start a conversation.
186
+ We're on Twitter as [@vsappcenter](https://www.twitter.com/vsappcenter). Additionally you can reach out to us on the [App Center](https://appcenter.ms/apps) portal. Open the "?" menu on the top right corner of screen, then use "Contact support" to file a support ticket. Our support team is there to answer your questions and help you solve your problems.
@@ -0,0 +1,84 @@
1
+ require 'json'
2
+ require 'net/http'
3
+ require 'fastlane_core/ui/ui'
4
+
5
+ module Fastlane
6
+ UI = FastlaneCore::UI unless Fastlane.const_defined?("UI")
7
+
8
+ module Actions
9
+ class AppcenterFetchVersionNumberAction < Action
10
+ def self.description
11
+ "Fetches the latest version number of an app from App Center"
12
+ end
13
+
14
+ def self.authors
15
+ ["jspargo", "ShopKeep"]
16
+ end
17
+
18
+ def self.run(params)
19
+ api_token = params[:api_token]
20
+ app_name = params[:app_name]
21
+ owner_name = params[:owner_name]
22
+
23
+ releases = Helper::AppcenterHelper.fetch_releases(
24
+ api_token: api_token,
25
+ owner_name: owner_name,
26
+ app_name: app_name
27
+ )
28
+
29
+ UI.abort_with_message!("No versions found for '#{app_name}' owned by #{owner_name}") unless releases
30
+ sorted_release = releases.sort_by { |release| release['id'] }.reverse!
31
+ latest_release = sorted_release.first
32
+
33
+ if latest_release.nil?
34
+ UI.user_error!("This app has no releases yet")
35
+ return nil
36
+ end
37
+
38
+ return {
39
+ "id" => latest_release['id'],
40
+ "version" => latest_release['short_version'],
41
+ "build_number" => latest_release['version']
42
+ }
43
+ end
44
+
45
+ def self.available_options
46
+ [
47
+ FastlaneCore::ConfigItem.new(key: :api_token,
48
+ env_name: "APPCENTER_API_TOKEN",
49
+ description: "API Token for App Center Access",
50
+ verify_block: proc do |value|
51
+ UI.user_error!("No API token for App Center given, pass using `api_token: 'token'`") unless value && !value.empty?
52
+ end),
53
+ FastlaneCore::ConfigItem.new(key: :owner_name,
54
+ env_name: "APPCENTER_OWNER_NAME",
55
+ description: "Name of the owner of the application on App Center",
56
+ verify_block: proc do |value|
57
+ UI.user_error!("No owner name for App Center given, pass using `owner_name: 'owner name'`") unless value && !value.empty?
58
+ end),
59
+ FastlaneCore::ConfigItem.new(key: :app_name,
60
+ env_name: "APPCENTER_APP_NAME",
61
+ description: "Name of the application on App Center",
62
+ verify_block: proc do |value|
63
+ UI.user_error!("No app name for App Center given, pass using `app_name: 'app name'`") unless value && !value.empty?
64
+ end)
65
+ ]
66
+ end
67
+
68
+ def self.is_supported?(platform)
69
+ [:ios, :android].include?(platform)
70
+ end
71
+
72
+ def self.get_apps(api_token)
73
+ host_uri = URI.parse('https://api.appcenter.ms')
74
+ http = Net::HTTP.new(host_uri.host, host_uri.port)
75
+ http.use_ssl = true
76
+ apps_request = Net::HTTP::Get.new("/v0.1/apps")
77
+ apps_request['X-API-Token'] = api_token
78
+ apps_response = http.request(apps_request)
79
+ return [] unless apps_response.kind_of?(Net::HTTPOK)
80
+ return JSON.parse(apps_response.body)
81
+ end
82
+ end
83
+ end
84
+ end
@@ -9,6 +9,23 @@ module Fastlane
9
9
  windows: %w(.appx .appxbundle .appxupload .msix .msixbundle .msixupload .zip .msi),
10
10
  custom: %w(.zip)
11
11
  }
12
+ CONTENT_TYPES = {
13
+ apk: "application/vnd.android.package-archive",
14
+ aab: "application/vnd.android.package-archive",
15
+ msi: "application/x-msi",
16
+ plist: "application/xml",
17
+ aetx: "application/c-x509-ca-cert",
18
+ cer: "application/pkix-cert",
19
+ xap: "application/x-silverlight-app",
20
+ appx: "application/x-appx",
21
+ appxbundle: "application/x-appxbundle",
22
+ appxupload: "application/x-appxupload",
23
+ appxsym: "application/x-appxupload",
24
+ msix: "application/x-msix",
25
+ msixbundle: "application/x-msixbundle",
26
+ msixupload: "application/x-msixupload",
27
+ msixsym: "application/x-msixupload",
28
+ }
12
29
  ALL_SUPPORTED_EXTENSIONS = SUPPORTED_EXTENSIONS.values.flatten.sort!.uniq!
13
30
  STORE_ONLY_EXTENSIONS = %w(.aab)
14
31
  STORE_SUPPORTED_EXTENSIONS = %w(.aab .apk .ipa)
@@ -22,21 +39,29 @@ module Fastlane
22
39
  end
23
40
 
24
41
  class AppcenterUploadAction < Action
42
+ def self.is_apple_build(file)
43
+ return false unless file
44
+
45
+ file_ext = Helper::AppcenterHelper.file_extname_full(file)
46
+ ((Constants::SUPPORTED_EXTENSIONS[:ios] + Constants::SUPPORTED_EXTENSIONS[:mac])).include? file_ext
47
+ end
48
+
25
49
  # run whole upload process for dSYM files
26
50
  def self.run_dsym_upload(params)
27
51
  values = params.values
28
52
  api_token = params[:api_token]
29
53
  owner_name = params[:owner_name]
30
54
  app_name = params[:app_name]
31
- file = params[:ipa]
55
+ file = params[:file] || params[:ipa]
32
56
  dsym = params[:dsym]
33
57
  build_number = params[:build_number]
34
58
  version = params[:version]
35
59
 
36
60
  dsym_path = nil
37
61
  if dsym
38
- # we can use dsym parameter only if build file is ipa
39
- dsym_path = dsym if !file || File.extname(file) == '.ipa'
62
+ # we can use dsym parameter for all apple builds
63
+ self.optional_error("dsym parameter can only be used with Apple builds (ios, mac)") unless !file || self.is_apple_build(file)
64
+ dsym_path = dsym
40
65
  else
41
66
  # if dsym is not set, but build is ipa - check default path
42
67
  if file && File.exist?(file) && File.extname(file) == '.ipa'
@@ -132,10 +157,10 @@ module Fastlane
132
157
  end
133
158
 
134
159
  file = [
160
+ params[:file],
135
161
  params[:ipa],
136
162
  params[:apk],
137
163
  params[:aab],
138
- params[:file]
139
164
  ].detect { |e| !e.to_s.empty? }
140
165
 
141
166
  UI.user_error!("Couldn't find build file at path '#{file}'") unless file && File.exist?(file)
@@ -145,6 +170,7 @@ module Fastlane
145
170
  self.optional_error("Can't distribute #{file_ext} to groups, please use `destination_type: 'store'`") if Constants::STORE_ONLY_EXTENSIONS.include? file_ext
146
171
  else
147
172
  self.optional_error("Can't distribute #{file_ext} to stores, please use `destination_type: 'group'`") unless Constants::STORE_SUPPORTED_EXTENSIONS.include? file_ext
173
+ UI.user_error!("The combination of `destinations: '*'` and `destination_type: 'store'` is invalid, please use `destination_type: 'group'` or explicitly specify the destinations") if destinations == "*"
148
174
  end
149
175
 
150
176
  release_upload_body = nil
@@ -171,27 +197,57 @@ module Fastlane
171
197
  File.delete zip_file
172
198
  end
173
199
  UI.message("Creating zip archive: #{zip_file}")
174
- file = Actions::ZipAction.run(path: file, output_path: zip_file)
200
+ file = Actions::ZipAction.run(path: file, output_path: zip_file, symlinks: true)
175
201
  end
176
202
 
177
203
  UI.message("Starting release upload...")
178
204
  upload_details = Helper::AppcenterHelper.create_release_upload(api_token, owner_name, app_name, release_upload_body)
179
205
  if upload_details
180
- upload_id = upload_details['upload_id']
181
- upload_url = upload_details['upload_url']
206
+ upload_id = upload_details['id']
207
+
208
+ UI.message("Setting Metadata...")
209
+ content_type = Constants::CONTENT_TYPES[File.extname(file)&.delete('.').downcase.to_sym] || "application/octet-stream"
210
+ set_metadata_url = "#{upload_details['upload_domain']}/upload/set_metadata/#{upload_details['package_asset_id']}?file_name=#{File.basename(file)}&file_size=#{File.size(file)}&token=#{upload_details['url_encoded_token']}&content_type=#{content_type}"
211
+ chunk_size = Helper::AppcenterHelper.set_release_upload_metadata(set_metadata_url, api_token, owner_name, app_name, upload_id, timeout)
212
+ UI.abort_with_message!("Upload aborted") unless chunk_size
182
213
 
183
214
  UI.message("Uploading release binary...")
184
- uploaded = Helper::AppcenterHelper.upload_build(api_token, owner_name, app_name, file, upload_id, upload_url, timeout)
215
+ upload_url = "#{upload_details['upload_domain']}/upload/upload_chunk/#{upload_details['package_asset_id']}?token=#{upload_details['url_encoded_token']}"
216
+ uploaded = Helper::AppcenterHelper.upload_build(api_token, owner_name, app_name, file, upload_id, upload_url, content_type, chunk_size, timeout)
217
+ UI.abort_with_message!("Upload aborted") unless uploaded
218
+
219
+ UI.message("Finishing release...")
220
+ finish_url = "#{upload_details['upload_domain']}/upload/finished/#{upload_details['package_asset_id']}?token=#{upload_details['url_encoded_token']}"
221
+ finished = Helper::AppcenterHelper.finish_release_upload(finish_url, api_token, owner_name, app_name, upload_id, timeout)
222
+ UI.abort_with_message!("Upload aborted") unless finished
223
+
224
+ UI.message("Waiting for release to be ready...")
225
+ release_status_url = "v0.1/apps/#{owner_name}/#{app_name}/uploads/releases/#{upload_id}"
226
+ release_id = Helper::AppcenterHelper.poll_for_release_id(api_token, release_status_url)
185
227
 
186
- if uploaded
187
- release_id = uploaded['release_id']
228
+ if release_id.is_a? Integer
188
229
  release_url = Helper::AppcenterHelper.get_release_url(owner_type, owner_name, app_name, release_id)
189
230
  UI.message("Release '#{release_id}' committed: #{release_url}")
190
231
 
191
232
  release = Helper::AppcenterHelper.update_release(api_token, owner_name, app_name, release_id, release_notes)
192
233
  Helper::AppcenterHelper.update_release_metadata(api_token, owner_name, app_name, release_id, dsa_signature)
193
234
 
194
- destinations_array = destinations.split(',')
235
+ destinations_array = []
236
+ if destinations == '*'
237
+ UI.message("Looking up all distribution groups for #{owner_name}/#{app_name}")
238
+ distribution_groups = Helper::AppcenterHelper.fetch_distribution_groups(
239
+ api_token: api_token,
240
+ owner_name: owner_name,
241
+ app_name: app_name
242
+ )
243
+
244
+ UI.abort_with_message!("Failed to list distribution groups for #{owner_name}/#{app_name}") unless distribution_groups
245
+
246
+ destinations_array = distribution_groups.map {|h| h['name'] }
247
+ else
248
+ destinations_array = destinations.split(',').map(&:strip)
249
+ end
250
+
195
251
  destinations_array.each do |destination_name|
196
252
  destination = Helper::AppcenterHelper.get_destination(api_token, owner_name, app_name, destination_type, destination_name)
197
253
  if destination
@@ -228,13 +284,20 @@ module Fastlane
228
284
  app_platform = params[:app_platform]
229
285
 
230
286
  platforms = {
231
- Android: %w[Java React-Native Xamarin],
232
- iOS: %w[Objective-C-Swift React-Native Xamarin],
233
- macOS: %w[Objective-C-Swift]
287
+ Android: %w[Java React-Native Xamarin Unity],
288
+ iOS: %w[Objective-C-Swift React-Native Xamarin Unity],
289
+ macOS: %w[Objective-C-Swift],
290
+ Windows: %w[UWP WPF WinForms Unity],
291
+ Custom: %w[Custom]
234
292
  }
235
293
 
236
- if Helper::AppcenterHelper.get_app(api_token, owner_name, app_name)
237
- return true
294
+ begin
295
+ if Helper::AppcenterHelper.get_app(api_token, owner_name, app_name)
296
+ return true
297
+ end
298
+ rescue URI::InvalidURIError
299
+ UI.user_error!("Provided app_name: '#{app_name}' is not in a valid format. Please ensure no special characters or spaces in the app_name.")
300
+ return false
238
301
  end
239
302
 
240
303
  should_create_app = !app_display_name.to_s.empty? || !app_os.to_s.empty? || !app_platform.to_s.empty?
@@ -242,9 +305,9 @@ module Fastlane
242
305
  if Helper.test? || should_create_app || UI.confirm("App with name #{app_name} not found, create one?")
243
306
  app_display_name = app_name if app_display_name.to_s.empty?
244
307
  os = app_os.to_s.empty? && (Helper.test? ? "Android" : UI.select("Select OS", platforms.keys)) || app_os.to_s
245
- platform = app_platform.to_s.empty? && (Helper.test? ? "Java" : app_platform.to_s) || app_platform.to_s
308
+ platform = app_platform.to_s.empty? && (Helper.test? ? platforms[os.to_sym][0] : app_platform.to_s) || app_platform.to_s
246
309
  if platform.to_s.empty?
247
- platform = platforms[os].length == 1 ? platforms[os][0] : UI.select("Select Platform", platforms[os])
310
+ platform = platforms[os.to_sym].length == 1 ? platforms[os.to_sym][0] : UI.select("Select Platform", platforms[os.to_sym])
248
311
  end
249
312
 
250
313
  Helper::AppcenterHelper.create_app(api_token, owner_type, owner_name, app_name, app_display_name, os, platform)
@@ -254,8 +317,33 @@ module Fastlane
254
317
  end
255
318
  end
256
319
 
320
+ def self.add_app_to_distribution_group_if_needed(params)
321
+ return unless params[:destination_type] == 'group' && params[:owner_type] == 'organization' && params[:destinations] != '*'
322
+
323
+ app_distribution_groups = Helper::AppcenterHelper.fetch_distribution_groups(
324
+ api_token: params[:api_token],
325
+ owner_name: params[:owner_name],
326
+ app_name: params[:app_name]
327
+ )
328
+
329
+ group_names = app_distribution_groups.map { |g| g['name'] }
330
+ destination_names = params[:destinations].split(',').map(&:strip)
331
+
332
+ destination_names.each do |destination_name|
333
+ unless group_names.include? destination_name
334
+ Helper::AppcenterHelper.add_new_app_to_distribution_group(
335
+ api_token: params[:api_token],
336
+ owner_name: params[:owner_name],
337
+ app_name: params[:app_name],
338
+ destination_name: destination_name
339
+ )
340
+ end
341
+ end
342
+ end
343
+
257
344
  def self.run(params)
258
345
  values = params.values
346
+ upload_build_only = params[:upload_build_only]
259
347
  upload_dsym_only = params[:upload_dsym_only]
260
348
  upload_mapping_only = params[:upload_mapping_only]
261
349
 
@@ -263,12 +351,12 @@ module Fastlane
263
351
 
264
352
  # if app found or successfully created
265
353
  if self.get_or_create_app(params)
354
+ self.add_app_to_distribution_group_if_needed(params)
266
355
  release = self.run_release_upload(params) unless upload_dsym_only || upload_mapping_only
267
356
  params[:version] = release['short_version'] if release
268
357
  params[:build_number] = release['version'] if release
269
-
270
- self.run_dsym_upload(params) unless upload_mapping_only
271
- self.run_mapping_upload(params) unless upload_dsym_only
358
+ self.run_dsym_upload(params) unless upload_mapping_only || upload_build_only
359
+ self.run_mapping_upload(params) unless upload_dsym_only || upload_build_only
272
360
  end
273
361
 
274
362
  return values if Helper.test?
@@ -337,7 +425,7 @@ module Fastlane
337
425
 
338
426
  FastlaneCore::ConfigItem.new(key: :app_os,
339
427
  env_name: "APPCENTER_APP_OS",
340
- description: "App OS. Used for new app creation, if app 'app_name' was not found",
428
+ description: "App OS can be Android, iOS, macOS, Windows, Custom. Used for new app creation, if app 'app_name' was not found",
341
429
  optional: true,
342
430
  type: String),
343
431
 
@@ -407,15 +495,28 @@ module Fastlane
407
495
  end,
408
496
  verify_block: proc do |value|
409
497
  platform = Actions.lane_context[SharedValues::PLATFORM_NAME]
410
- accepted_formats = Constants::SUPPORTED_EXTENSIONS[platform.to_sym] if platform
411
- unless accepted_formats
412
- UI.important("Unknown platform '#{platform}', consider using one of: #{Constants::SUPPORTED_EXTENSIONS.keys}")
413
- accepted_formats = Constants::ALL_SUPPORTED_EXTENSIONS
498
+ if platform
499
+ accepted_formats = Constants::SUPPORTED_EXTENSIONS[platform.to_sym]
500
+ unless accepted_formats
501
+ UI.important("Unknown platform '#{platform}', Supported are #{Constants::SUPPORTED_EXTENSIONS.keys}")
502
+ accepted_formats = Constants::ALL_SUPPORTED_EXTENSIONS
503
+ end
504
+ file_ext = Helper::AppcenterHelper.file_extname_full(value)
505
+ self.optional_error("Extension not supported: '#{file_ext}'. Supported formats for platform '#{platform}': #{accepted_formats.join ' '}") unless accepted_formats.include? file_ext
414
506
  end
415
- file_ext = Helper::AppcenterHelper.file_extname_full(value)
416
- self.optional_error("Extension not supported: '#{file_ext}'. Supported formats for platform '#{platform}': #{accepted_formats.join ' '}") unless accepted_formats.include? file_ext
417
507
  end),
418
508
 
509
+ FastlaneCore::ConfigItem.new(key: :upload_build_only,
510
+ env_name: "APPCENTER_DISTRIBUTE_UPLOAD_BUILD_ONLY",
511
+ description: "Flag to upload only the build to App Center. Skips uploading symbols or mapping",
512
+ optional: true,
513
+ is_string: false,
514
+ default_value: false,
515
+ conflicting_options: [:upload_dsym_only, :upload_mapping_only],
516
+ conflict_block: proc do |value|
517
+ UI.user_error!("You can't use 'upload_build_only' and '#{value.key}' options in one run")
518
+ end),
519
+
419
520
  FastlaneCore::ConfigItem.new(key: :dsym,
420
521
  env_name: "APPCENTER_DISTRIBUTE_DSYM",
421
522
  description: "Path to your symbols file. For iOS provide path to app.dSYM.zip",
@@ -440,6 +541,7 @@ module Fastlane
440
541
  FastlaneCore::ConfigItem.new(key: :mapping,
441
542
  env_name: "APPCENTER_DISTRIBUTE_ANDROID_MAPPING",
442
543
  description: "Path to your Android mapping.txt",
544
+ default_value: (defined? SharedValues::GRADLE_MAPPING_TXT_OUTPUT_PATH) && Actions.lane_context[SharedValues::GRADLE_MAPPING_TXT_OUTPUT_PATH] || nil,
443
545
  optional: true,
444
546
  type: String,
445
547
  verify_block: proc do |value|
@@ -469,12 +571,11 @@ module Fastlane
469
571
 
470
572
  FastlaneCore::ConfigItem.new(key: :destinations,
471
573
  env_name: "APPCENTER_DISTRIBUTE_DESTINATIONS",
472
- 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",
574
+ description: "Comma separated list of destination names, use '*' for all distribution groups if destination type is 'group'. Both distribution groups and stores are supported. All names are required to be of the same destination type",
473
575
  default_value: Actions.lane_context[SharedValues::APPCENTER_DISTRIBUTE_DESTINATIONS] || "Collaborators",
474
576
  optional: true,
475
577
  type: String),
476
578
 
477
-
478
579
  FastlaneCore::ConfigItem.new(key: :destination_type,
479
580
  env_name: "APPCENTER_DISTRIBUTE_DESTINATION_TYPE",
480
581
  description: "Destination type of distribution destination. 'group' and 'store' are supported",
@@ -533,7 +634,7 @@ module Fastlane
533
634
 
534
635
  FastlaneCore::ConfigItem.new(key: :timeout,
535
636
  env_name: "APPCENTER_DISTRIBUTE_TIMEOUT",
536
- description: "Request timeout in seconds",
637
+ description: "Request timeout in seconds applied to individual HTTP requests. Some commands use multiple HTTP requests, large file uploads are also split in multiple HTTP requests",
537
638
  optional: true,
538
639
  type: Integer),
539
640
 
@@ -588,6 +689,16 @@ module Fastlane
588
689
  release_notes: "release notes",
589
690
  notify_testers: false
590
691
  )',
692
+ 'appcenter_upload(
693
+ api_token: "...",
694
+ owner_name: "appcenter_owner",
695
+ app_name: "testing_ios_app",
696
+ file: "./app-release.ipa",
697
+ destinations: "*",
698
+ destination_type: "group",
699
+ release_notes: "release notes",
700
+ notify_testers: false
701
+ )',
591
702
  'appcenter_upload(
592
703
  api_token: "...",
593
704
  owner_name: "appcenter_owner",