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 +4 -4
- data/README.md +32 -6
- data/lib/fastlane/plugin/appcenter/actions/appcenter_fetch_version_number.rb +84 -0
- data/lib/fastlane/plugin/appcenter/actions/appcenter_upload_action.rb +142 -31
- data/lib/fastlane/plugin/appcenter/helper/appcenter_helper.rb +396 -95
- data/lib/fastlane/plugin/appcenter/version.rb +1 -1
- metadata +18 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 89d582118585c4f375abd0b893ad9648180e33d19308d20c881aea0262356a82
|
4
|
+
data.tar.gz: 57a447973defb5be52cee9ed03a9d635e442cc30b523152b23d0754b63638c14
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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
|
39
|
-
|
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['
|
181
|
-
|
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
|
-
|
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
|
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 =
|
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
|
-
|
237
|
-
|
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? ?
|
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.
|
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
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
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",
|