fastlane-plugin-sapfire 1.2.1 → 1.3.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 +4 -4
- data/README.md +5 -0
- data/lib/fastlane/plugin/sapfire/actions/associate_ms_store_action.rb +49 -23
- data/lib/fastlane/plugin/sapfire/actions/upload_ms_store_action.rb +8 -20
- data/lib/fastlane/plugin/sapfire/actions/upload_package_flight_action.rb +233 -0
- data/lib/fastlane/plugin/sapfire/helper/azure_blob_helper.rb +143 -0
- data/lib/fastlane/plugin/sapfire/helper/ms_devcenter_helper.rb +92 -128
- data/lib/fastlane/plugin/sapfire/version.rb +1 -1
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 226d306173179880376ba2856ece97544e8d0fbeb1b19878a1188c77b2ebe753
|
4
|
+
data.tar.gz: ccee066ce7a4f4bad9e64e9b7939615098984439f4f8fbdf2a7cc6b27533d66b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 792614cad248073d030c735ba6a048426382eed3a758c9bddf8292e2cd76895c7b9a4adfc469475ceff4f3c94389ced6c33b4886e98fb2f0ce1258fb9309e414
|
7
|
+
data.tar.gz: 2daccfbafee00c0d48864506744cec183e413d6e58bd40183ef30559b2330191ee5d7ba7ae185e6ae466458fd7ff9bef64f5b21156721c326ce91adbfc6c5b1d
|
data/README.md
CHANGED
@@ -90,6 +90,11 @@ Here is the list of all available actions. Read the documentation on each one by
|
|
90
90
|
|--------------------------------------------------------|-----------------------------------------------------------------------------|--------------------:|
|
91
91
|
| [app_certification](docs/actions/app_certification.md) | Runs Windows App Certification Kit to ensure your app is safe and efficient | windows |
|
92
92
|
|
93
|
+
#### 🐛 Beta
|
94
|
+
|
95
|
+
| Argument | Description | Supported platforms |
|
96
|
+
|----------------------------------------------------------------|------------------------------------------------------------------------|--------------------:|
|
97
|
+
| [upload_package_flight](docs/actions/upload_package_flight.md) | Uploads new binary to Microsoft Partner Center as a new package flight | windows |
|
93
98
|
|
94
99
|
#### 📦 Releasing
|
95
100
|
|
@@ -11,6 +11,9 @@ module Fastlane
|
|
11
11
|
@vsapi_host = ""
|
12
12
|
@vsapi_endpoint = ""
|
13
13
|
|
14
|
+
VS_API_FW_LINK = "2264307".freeze
|
15
|
+
DEV_CENTER_FW_LINK = "2263650".freeze
|
16
|
+
VS_CLIENT_ID = "04f0c124-f2bc-4f59-8241-bf6df9866bbd".freeze
|
14
17
|
XML_NAME = "Package.StoreAssociation.xml".freeze
|
15
18
|
|
16
19
|
def self.run(params)
|
@@ -19,8 +22,9 @@ module Fastlane
|
|
19
22
|
begin
|
20
23
|
UI.message("Creating #{XML_NAME}...")
|
21
24
|
|
22
|
-
|
23
|
-
|
25
|
+
dev_center_url = acquire_dev_center_location
|
26
|
+
acquire_vs_api_location
|
27
|
+
acquire_authorization_token(dev_center_url)
|
24
28
|
ms_developer_info = developer_info
|
25
29
|
ms_app_info = app_info(params[:app_id])
|
26
30
|
create_xml(params[:manifest], ms_developer_info, ms_app_info)
|
@@ -116,14 +120,20 @@ module Fastlane
|
|
116
120
|
|
117
121
|
if response.status == 200
|
118
122
|
data = JSON.parse(response.body)
|
123
|
+
failure_code = data["FailureCode"]
|
124
|
+
failure_reason = data["FailureReason"]
|
125
|
+
|
126
|
+
UI.user_error!("Request returned the error.\nFailure code: #{failure_code}\nFailure reason: #{failure_reason}") if
|
127
|
+
(!failure_code.nil? && failure_code != 0) || !failure_reason.nil?
|
128
|
+
|
119
129
|
developer_info = DeveloperInfo.new(data["PublisherDisplayName"], data["Publisher"])
|
120
130
|
|
121
|
-
UI.
|
131
|
+
UI.success("Developer info was obtained")
|
122
132
|
|
123
133
|
return developer_info
|
124
134
|
end
|
125
135
|
|
126
|
-
UI.user_error!("Request
|
136
|
+
UI.user_error!("Request completed with non successful status: #{response.status}")
|
127
137
|
rescue StandardError => ex
|
128
138
|
UI.user_error!("Developer info request failed: #{ex}")
|
129
139
|
end
|
@@ -147,30 +157,34 @@ module Fastlane
|
|
147
157
|
|
148
158
|
if response.status == 200
|
149
159
|
data = JSON.parse(response.body)
|
160
|
+
failure_code = data["FailureCode"]
|
161
|
+
failure_reason = data["FailureReason"]
|
162
|
+
|
163
|
+
UI.user_error!("Request returned the error.\nFailure code: #{failure_code}\nFailure reason: #{failure_reason}") if
|
164
|
+
(!failure_code.nil? && failure_code != 0) || !failure_reason.nil?
|
165
|
+
|
150
166
|
product = data["Products"].find { |x| x["LandingUrl"].include?(app_id) }
|
151
167
|
app_info = AppInfo.new(product["MainPackageIdentityName"], product["ReservedNames"])
|
152
168
|
|
153
|
-
UI.
|
169
|
+
UI.success("Application info was obtained")
|
154
170
|
|
155
171
|
return app_info
|
156
172
|
end
|
157
173
|
|
158
|
-
UI.user_error!("Request
|
174
|
+
UI.user_error!("Request completed with non successful status: #{response.status}")
|
159
175
|
rescue StandardError => ex
|
160
176
|
UI.user_error!("Application info request failed: #{ex}")
|
161
177
|
end
|
162
178
|
end
|
163
179
|
|
164
|
-
def self.acquire_authorization_token
|
180
|
+
def self.acquire_authorization_token(resource)
|
165
181
|
UI.message("Acquiring authorization token ...")
|
166
182
|
|
167
183
|
ms_credentials = Helper.ms_credentials
|
168
184
|
body = {
|
169
|
-
client_id:
|
170
|
-
client_secret: ms_credentials.client_secret,
|
171
|
-
client_info: 1,
|
185
|
+
client_id: VS_CLIENT_ID,
|
172
186
|
grant_type: "password",
|
173
|
-
scope: "
|
187
|
+
scope: "#{resource}/.default",
|
174
188
|
username: ms_credentials.username,
|
175
189
|
password: ms_credentials.password
|
176
190
|
}
|
@@ -188,7 +202,7 @@ module Fastlane
|
|
188
202
|
|
189
203
|
if response.status == 200
|
190
204
|
@token = data["access_token"]
|
191
|
-
UI.
|
205
|
+
UI.success("Authorization token was obtained")
|
192
206
|
|
193
207
|
return
|
194
208
|
end
|
@@ -202,9 +216,28 @@ module Fastlane
|
|
202
216
|
end
|
203
217
|
end
|
204
218
|
|
205
|
-
def self.
|
219
|
+
def self.acquire_dev_center_location
|
220
|
+
UI.message("Acquiring Dev Center location ...")
|
221
|
+
location = acquire_fw_url(DEV_CENTER_FW_LINK)
|
222
|
+
UI.success("URL was obtained: #{location}")
|
223
|
+
|
224
|
+
location
|
225
|
+
end
|
226
|
+
|
227
|
+
def self.acquire_vs_api_location
|
228
|
+
UI.message("Acquiring VS API location ...")
|
229
|
+
|
230
|
+
location = acquire_fw_url(VS_API_FW_LINK)
|
231
|
+
uri = URI(location)
|
232
|
+
@vsapi_host = "#{uri.scheme}://#{uri.host}"
|
233
|
+
@vsapi_endpoint = uri.path
|
234
|
+
|
235
|
+
UI.success("URL was obtained: #{location}")
|
236
|
+
end
|
237
|
+
|
238
|
+
def self.acquire_fw_url(link_id)
|
206
239
|
query = {
|
207
|
-
LinkId:
|
240
|
+
LinkId: link_id
|
208
241
|
}
|
209
242
|
connection = Faraday.new("https://go.microsoft.com")
|
210
243
|
|
@@ -214,17 +247,10 @@ module Fastlane
|
|
214
247
|
if response.status == 302
|
215
248
|
raise "'Location' header isn't presented" unless response.headers.include?("Location")
|
216
249
|
|
217
|
-
|
218
|
-
uri = URI(location)
|
219
|
-
@vsapi_host = "#{uri.scheme}://#{uri.host}"
|
220
|
-
@vsapi_endpoint = uri.path
|
221
|
-
|
222
|
-
UI.message("VS API endpoint: #{location}")
|
223
|
-
|
224
|
-
return
|
250
|
+
return response.headers["Location"]
|
225
251
|
end
|
226
252
|
|
227
|
-
UI.user_error!("Request
|
253
|
+
UI.user_error!("Request completed with non successful status: #{response.status}")
|
228
254
|
rescue StandardError => ex
|
229
255
|
UI.user_error!("Failed to get VS API endpoint location: #{ex}")
|
230
256
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
require "fastlane/action"
|
2
2
|
require_relative "../helper/ms_credentials"
|
3
3
|
require_relative "../helper/ms_devcenter_helper"
|
4
|
+
require_relative "../helper/azure_blob_helper"
|
4
5
|
require_relative "../msbuild/options"
|
5
6
|
|
6
7
|
module Fastlane
|
@@ -22,7 +23,8 @@ module Fastlane
|
|
22
23
|
UI.message("Authorization token was obtained")
|
23
24
|
|
24
25
|
UI.message("Creating submission for app #{app_id} ...")
|
25
|
-
|
26
|
+
app_info = get_app_info(app_id, auth_token, timeout)
|
27
|
+
pending_submission = app_info["pendingApplicationSubmission"]
|
26
28
|
|
27
29
|
unless pending_submission.nil?
|
28
30
|
submission_id = pending_submission["id"]
|
@@ -46,11 +48,11 @@ module Fastlane
|
|
46
48
|
UI.message("Submission #{submission_id} created")
|
47
49
|
|
48
50
|
UI.message("Prepare ZIP blob for upload ...")
|
49
|
-
zip_path = create_blob_zip(File.expand_path(path))
|
51
|
+
zip_path = Helper::AzureBlobHelper.create_blob_zip(File.expand_path(path))
|
50
52
|
UI.success("Blob is ready")
|
51
53
|
|
52
54
|
UI.message("Uploading ZIP blob ...")
|
53
|
-
Helper::
|
55
|
+
Helper::AzureBlobHelper.upload_blob(submission_obj["fileUploadUrl"], zip_path, timeout)
|
54
56
|
UI.success("ZIP blob uploaded successfully")
|
55
57
|
|
56
58
|
submission_obj = prepare_empty_submission(submission_obj)
|
@@ -61,11 +63,11 @@ module Fastlane
|
|
61
63
|
UI.message("Updated successfully")
|
62
64
|
|
63
65
|
UI.message("Committing ...")
|
64
|
-
Helper::MsDevCenterHelper.commit_submission(app_id, submission_id, auth_token, timeout)
|
66
|
+
Helper::MsDevCenterHelper.commit_submission(app_id, nil, submission_id, auth_token, timeout)
|
65
67
|
|
66
68
|
if params.values.include?(:skip_waiting_for_build_processing) &&
|
67
69
|
[true].include?(params[:skip_waiting_for_build_processing])
|
68
|
-
UI.success("Submission passed, but build processing were skipped. Check the Dev Center page.")
|
70
|
+
UI.success("Submission passed, but build processing were skipped. Check the Dev Center page to get an actual status.")
|
69
71
|
return
|
70
72
|
end
|
71
73
|
|
@@ -73,7 +75,7 @@ module Fastlane
|
|
73
75
|
data = nil
|
74
76
|
until status
|
75
77
|
UI.message("Waiting for the submission to change the status - this may take a few minutes")
|
76
|
-
data = Helper::MsDevCenterHelper.get_submission_status(app_id, submission_id, auth_token, timeout)
|
78
|
+
data = Helper::MsDevCenterHelper.get_submission_status(app_id, nil, submission_id, auth_token, timeout)
|
77
79
|
status = data["status"] != "CommitStarted"
|
78
80
|
sleep(30) unless status
|
79
81
|
end
|
@@ -182,20 +184,6 @@ module Fastlane
|
|
182
184
|
:production
|
183
185
|
end
|
184
186
|
|
185
|
-
def self.create_blob_zip(package_path)
|
186
|
-
zip_path = File.join(File.dirname(package_path), "blob.zip")
|
187
|
-
File.delete(zip_path) if File.exist?(zip_path)
|
188
|
-
|
189
|
-
Zip::File.open(zip_path, create: true) do |file|
|
190
|
-
file.add(File.basename(package_path), package_path)
|
191
|
-
|
192
|
-
screenshot_path = File.join(Helper::SapfireHelper.root_plugin_location, "assets", "ms_example_screenshot.png")
|
193
|
-
file.add("ms_example_screenshot.png", File.expand_path(screenshot_path))
|
194
|
-
end
|
195
|
-
|
196
|
-
zip_path
|
197
|
-
end
|
198
|
-
|
199
187
|
def self.add_package_to_submission(submission_obj, file_name)
|
200
188
|
check_submission(submission_obj)
|
201
189
|
UI.user_error!("Package file name can't be null or empty") if file_name.nil? || file_name.empty?
|
@@ -0,0 +1,233 @@
|
|
1
|
+
require "fastlane/action"
|
2
|
+
require_relative "../helper/ms_credentials"
|
3
|
+
require_relative "../helper/ms_devcenter_helper"
|
4
|
+
require_relative "../helper/azure_blob_helper"
|
5
|
+
require_relative "../msbuild/options"
|
6
|
+
|
7
|
+
module Fastlane
|
8
|
+
module Actions
|
9
|
+
class UploadPackageFlightAction < Action
|
10
|
+
DEFAULT_TIMEOUT = 300
|
11
|
+
|
12
|
+
def self.run(params)
|
13
|
+
ms_credentials = Helper.ms_credentials
|
14
|
+
app_id = params[:app_id]
|
15
|
+
flight_name = params[:name]
|
16
|
+
group_ids = params[:group_ids]
|
17
|
+
path = params[:path]
|
18
|
+
timeout = params.values.include?(:timeout) && params[:timeout].positive? ? params[:timeout] : DEFAULT_TIMEOUT
|
19
|
+
|
20
|
+
UI.message("Acquiring authorization token for DevCenter ...")
|
21
|
+
auth_token = Helper::MsDevCenterHelper.acquire_authorization_token(ms_credentials.tenant_id,
|
22
|
+
ms_credentials.client_id,
|
23
|
+
ms_credentials.client_secret,
|
24
|
+
timeout)
|
25
|
+
UI.message("Authorization token was obtained")
|
26
|
+
UI.message("Creating package flight for app #{app_id} ...")
|
27
|
+
|
28
|
+
flight_obj = Helper::MsDevCenterHelper.create_flight(app_id, flight_name, group_ids, auth_token, timeout)
|
29
|
+
flight_id = flight_obj["flightId"]
|
30
|
+
flight_name = flight_obj["friendlyName"]
|
31
|
+
submission_id = flight_obj["pendingFlightSubmission"]["id"]
|
32
|
+
submission_obj = Helper::MsDevCenterHelper.get_submission(app_id, flight_id, submission_id, auth_token, timeout)
|
33
|
+
UI.message("Flight #{flight_name} (ID: #{flight_id}) created")
|
34
|
+
|
35
|
+
UI.message("Prepare ZIP blob for upload ...")
|
36
|
+
zip_path = Helper::AzureBlobHelper.create_blob_zip(File.expand_path(path))
|
37
|
+
UI.success("Blob is ready")
|
38
|
+
|
39
|
+
UI.message("Uploading ZIP blob ...")
|
40
|
+
Helper::AzureBlobHelper.upload_blob(submission_obj["fileUploadUrl"], zip_path, timeout)
|
41
|
+
UI.success("ZIP blob uploaded successfully")
|
42
|
+
|
43
|
+
publish_immediate = params.values.include?(:publish_immediate) && [true].include?(params[:publish_immediate])
|
44
|
+
submission_obj = prepare_empty_submission(submission_obj, publish_immediate)
|
45
|
+
submission_obj = add_package_to_submission(submission_obj, File.basename(path))
|
46
|
+
|
47
|
+
UI.message("Updating submission data ...")
|
48
|
+
Helper::MsDevCenterHelper.update_submission(app_id, submission_obj, auth_token, timeout)
|
49
|
+
UI.message("Updated successfully")
|
50
|
+
|
51
|
+
UI.message("Committing ...")
|
52
|
+
Helper::MsDevCenterHelper.commit_submission(app_id, flight_id, submission_id, auth_token, timeout)
|
53
|
+
|
54
|
+
if params.values.include?(:skip_waiting_for_build_processing) &&
|
55
|
+
[true].include?(params[:skip_waiting_for_build_processing])
|
56
|
+
UI.success("Submission passed, but build processing were skipped. Check the Dev Center page to get an actual status.")
|
57
|
+
return
|
58
|
+
end
|
59
|
+
|
60
|
+
status = false
|
61
|
+
data = nil
|
62
|
+
until status
|
63
|
+
UI.message("Waiting for the submission to change the status - this may take a few minutes")
|
64
|
+
data = Helper::MsDevCenterHelper.get_submission_status(app_id, flight_id, submission_id, auth_token, timeout)
|
65
|
+
status = data["status"] != "CommitStarted"
|
66
|
+
sleep(30) unless status
|
67
|
+
end
|
68
|
+
|
69
|
+
if data["status"] == "CommitFailed"
|
70
|
+
errors = data["statusDetails"]["errors"]
|
71
|
+
if errors.length == 1 && errors[0]["code"] == "InvalidState"
|
72
|
+
UI.important(
|
73
|
+
[
|
74
|
+
"All submission operations passed correctly, but there are some things that you need to proceed using DevCenter.",
|
75
|
+
"Message: #{errors[0]["details"]}"
|
76
|
+
].join("\n")
|
77
|
+
)
|
78
|
+
return
|
79
|
+
end
|
80
|
+
|
81
|
+
errors.each do |error|
|
82
|
+
UI.error("Error code: #{error["code"]}\nMessage: #{error["details"]}")
|
83
|
+
end
|
84
|
+
|
85
|
+
UI.user_error!("Submission failed")
|
86
|
+
end
|
87
|
+
|
88
|
+
UI.success("Submission passed. Check the DevCenter page.")
|
89
|
+
end
|
90
|
+
|
91
|
+
def self.description
|
92
|
+
"Creates a new package flight submission in Microsoft Partner Center and uploads new binary to it"
|
93
|
+
end
|
94
|
+
|
95
|
+
def self.authors
|
96
|
+
["CheeryLee"]
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.is_supported?(platform)
|
100
|
+
[:windows].include?(platform)
|
101
|
+
end
|
102
|
+
|
103
|
+
def self.output
|
104
|
+
[
|
105
|
+
["SF_PUSHING_TIMEOUT", "The timeout for pushing to a server in seconds"],
|
106
|
+
["SF_APP_ID", "The Microsoft Store ID of an application"],
|
107
|
+
["SF_FLIGHT_NAME", "An optional name that would be used as a flight name"],
|
108
|
+
["SF_PACKAGE", "The file path to the package to be uploaded"]
|
109
|
+
]
|
110
|
+
end
|
111
|
+
|
112
|
+
def self.available_options
|
113
|
+
[
|
114
|
+
FastlaneCore::ConfigItem.new(
|
115
|
+
key: :timeout,
|
116
|
+
env_name: "SF_PUSHING_TIMEOUT",
|
117
|
+
description: "The timeout for pushing to a server in seconds",
|
118
|
+
optional: true,
|
119
|
+
default_value: 0,
|
120
|
+
type: Integer
|
121
|
+
),
|
122
|
+
FastlaneCore::ConfigItem.new(
|
123
|
+
key: :app_id,
|
124
|
+
env_name: "SF_APP_ID",
|
125
|
+
description: "The Microsoft Store ID of an application",
|
126
|
+
optional: false,
|
127
|
+
type: String,
|
128
|
+
verify_block: proc do |value|
|
129
|
+
UI.user_error!("The Microsoft Store ID can't be empty") unless value && !value.empty?
|
130
|
+
end
|
131
|
+
),
|
132
|
+
FastlaneCore::ConfigItem.new(
|
133
|
+
key: :skip_waiting_for_build_processing,
|
134
|
+
description: "If set to true, the action will only commit the submission and skip the remaining build validation",
|
135
|
+
optional: true,
|
136
|
+
default_value: false,
|
137
|
+
type: Fastlane::Boolean
|
138
|
+
),
|
139
|
+
FastlaneCore::ConfigItem.new(
|
140
|
+
key: :publish_immediate,
|
141
|
+
description: "If set to true, the submission will be published automatically once the validation passes",
|
142
|
+
optional: true,
|
143
|
+
default_value: false,
|
144
|
+
type: Fastlane::Boolean
|
145
|
+
),
|
146
|
+
FastlaneCore::ConfigItem.new(
|
147
|
+
key: :name,
|
148
|
+
env_name: "SF_FLIGHT_NAME",
|
149
|
+
description: "An optional name that would be used as a flight name",
|
150
|
+
optional: true,
|
151
|
+
default_value: "",
|
152
|
+
type: String
|
153
|
+
),
|
154
|
+
FastlaneCore::ConfigItem.new(
|
155
|
+
key: :group_ids,
|
156
|
+
description: "A list of tester groups who will get a new package",
|
157
|
+
optional: false,
|
158
|
+
type: Array,
|
159
|
+
verify_block: proc do |array|
|
160
|
+
UI.user_error!("List of tester groups can't be empty") if array.empty?
|
161
|
+
|
162
|
+
array.each do |value|
|
163
|
+
UI.user_error!("Tester group ID must be a string and can't be null or empty") if
|
164
|
+
!value.is_a?(String) || value.nil? || value.empty?
|
165
|
+
end
|
166
|
+
end
|
167
|
+
),
|
168
|
+
FastlaneCore::ConfigItem.new(
|
169
|
+
key: :path,
|
170
|
+
env_name: "SF_PACKAGE",
|
171
|
+
description: "The file path to the package to be uploaded",
|
172
|
+
optional: false,
|
173
|
+
type: String,
|
174
|
+
verify_block: proc do |value|
|
175
|
+
UI.user_error!("Path to UWP package is invalid") unless value && !value.empty?
|
176
|
+
|
177
|
+
format_valid = false
|
178
|
+
Msbuild::Options::PACKAGE_FORMATS.each do |extension|
|
179
|
+
if value.end_with?(extension)
|
180
|
+
format_valid = true
|
181
|
+
break
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
UI.user_error!("The provided path doesn't point to UWP file") unless
|
186
|
+
File.exist?(File.expand_path(value)) && format_valid
|
187
|
+
end
|
188
|
+
)
|
189
|
+
]
|
190
|
+
end
|
191
|
+
|
192
|
+
def self.category
|
193
|
+
:beta
|
194
|
+
end
|
195
|
+
|
196
|
+
def self.add_package_to_submission(submission_obj, file_name)
|
197
|
+
check_submission(submission_obj)
|
198
|
+
UI.user_error!("Package file name can't be null or empty") if file_name.nil? || file_name.empty?
|
199
|
+
|
200
|
+
key = "flightPackages"
|
201
|
+
package = {
|
202
|
+
"fileName": file_name,
|
203
|
+
"fileStatus": "PendingUpload",
|
204
|
+
"minimumDirectXVersion": "None",
|
205
|
+
"minimumSystemRam": "None"
|
206
|
+
}
|
207
|
+
|
208
|
+
if submission_obj[key].empty?
|
209
|
+
submission_obj[key] = []
|
210
|
+
else
|
211
|
+
submission_obj[key].each do |existing_package|
|
212
|
+
existing_package["fileStatus"] = "PendingDelete"
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
submission_obj[key].append(package)
|
217
|
+
submission_obj
|
218
|
+
end
|
219
|
+
|
220
|
+
def self.prepare_empty_submission(submission_obj, publish_immediate)
|
221
|
+
check_submission(submission_obj)
|
222
|
+
submission_obj["targetPublishMode"] = publish_immediate ? "Immediate" : "Manual"
|
223
|
+
submission_obj
|
224
|
+
end
|
225
|
+
|
226
|
+
def self.check_submission(submission_obj)
|
227
|
+
UI.user_error!("Submission data object need to be provided") if submission_obj.nil?
|
228
|
+
end
|
229
|
+
|
230
|
+
private_constant(:DEFAULT_TIMEOUT)
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
module Fastlane
|
2
|
+
module Helper
|
3
|
+
class AzureBlobHelper
|
4
|
+
FILE_CHUNK_SIZE = 26_214_400
|
5
|
+
UPLOAD_RETRIES = 3
|
6
|
+
|
7
|
+
def self.create_blob_zip(package_path)
|
8
|
+
zip_path = File.join(File.dirname(package_path), "blob.zip")
|
9
|
+
File.delete(zip_path) if File.exist?(zip_path)
|
10
|
+
|
11
|
+
Zip::File.open(zip_path, create: true) do |file|
|
12
|
+
file.add(File.basename(package_path), package_path)
|
13
|
+
|
14
|
+
screenshot_path = File.join(Helper::SapfireHelper.root_plugin_location, "assets", "ms_example_screenshot.png")
|
15
|
+
file.add("ms_example_screenshot.png", File.expand_path(screenshot_path))
|
16
|
+
end
|
17
|
+
|
18
|
+
zip_path
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.upload_blob(url, zip_path, timeout = 0)
|
22
|
+
UI.user_error!("File upload URL need to be provided") if !url.is_a?(String) || url.nil? || url.empty?
|
23
|
+
UI.user_error!("File path is invalid") if !zip_path.is_a?(String) || zip_path.nil? || zip_path.empty?
|
24
|
+
|
25
|
+
expand_path = File.expand_path(zip_path)
|
26
|
+
UI.user_error!("The provided path doesn't point to ZIP file") unless File.exist?(expand_path) && zip_path.end_with?(".zip")
|
27
|
+
|
28
|
+
File.open(expand_path) do |file|
|
29
|
+
block_list = []
|
30
|
+
chunks_count = (file.size.to_f / FILE_CHUNK_SIZE).ceil
|
31
|
+
current_chunk = 1
|
32
|
+
|
33
|
+
until file.eof?
|
34
|
+
bytes = file.read(FILE_CHUNK_SIZE)
|
35
|
+
id = SecureRandom.uuid.delete("-")
|
36
|
+
block_list.append(id)
|
37
|
+
retry_count = 0
|
38
|
+
result = false
|
39
|
+
|
40
|
+
UI.message("Upload chunk [#{current_chunk} / #{chunks_count}]")
|
41
|
+
|
42
|
+
while !result && retry_count < UPLOAD_RETRIES
|
43
|
+
result = upload_block(url, bytes, id, timeout)
|
44
|
+
retry_count += 1
|
45
|
+
end
|
46
|
+
|
47
|
+
UI.user_error!("Uploading failed: some chunks have not been uploaded") unless result
|
48
|
+
current_chunk += 1
|
49
|
+
end
|
50
|
+
|
51
|
+
result = upload_block_list(url, block_list, timeout)
|
52
|
+
UI.user_error!("Uploading failed: block list hasn't been uploaded") unless result
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.upload_block(url, bytes, id, timeout = 0)
|
57
|
+
headers = {
|
58
|
+
"Content-Length": bytes.length.to_s
|
59
|
+
}
|
60
|
+
|
61
|
+
url_data = parse_upload_url(url)
|
62
|
+
url_data[:query]["comp"] = "block"
|
63
|
+
url_data[:query]["blockid"] = id
|
64
|
+
connection = Faraday.new(url_data[:host])
|
65
|
+
|
66
|
+
begin
|
67
|
+
response = connection.put(url_data[:path]) do |req|
|
68
|
+
req.headers = headers
|
69
|
+
req.params = url_data[:query]
|
70
|
+
req.body = bytes
|
71
|
+
req.options.timeout = timeout if timeout.positive?
|
72
|
+
end
|
73
|
+
|
74
|
+
return true if response.status == 201
|
75
|
+
|
76
|
+
error = response.body.to_s
|
77
|
+
UI.error("Upload request failed.\nCode: #{response.status}\nError: #{error}")
|
78
|
+
rescue StandardError => ex
|
79
|
+
UI.error("Upload request failed: #{ex}")
|
80
|
+
end
|
81
|
+
|
82
|
+
false
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.upload_block_list(url, list, timeout = 0)
|
86
|
+
document = REXML::Document.new
|
87
|
+
document.xml_decl.version = "1.0"
|
88
|
+
document.xml_decl.encoding = "utf-8"
|
89
|
+
block_list = document.add_element("BlockList")
|
90
|
+
|
91
|
+
list.each do |block|
|
92
|
+
block_list.add_element("Latest").text = block
|
93
|
+
end
|
94
|
+
|
95
|
+
url_data = parse_upload_url(url)
|
96
|
+
url_data[:query]["comp"] = "blocklist"
|
97
|
+
connection = Faraday.new(url_data[:host])
|
98
|
+
|
99
|
+
begin
|
100
|
+
response = connection.put(url_data[:path]) do |req|
|
101
|
+
req.params = url_data[:query]
|
102
|
+
req.body = document.to_s
|
103
|
+
req.options.timeout = timeout if timeout.positive?
|
104
|
+
end
|
105
|
+
|
106
|
+
return true if response.status == 201
|
107
|
+
|
108
|
+
error = response.body.to_s
|
109
|
+
UI.error("Upload block list request failed.\nCode: #{response.status}\nError: #{error}")
|
110
|
+
rescue StandardError => ex
|
111
|
+
UI.error("Upload block list request failed: #{ex}")
|
112
|
+
end
|
113
|
+
|
114
|
+
false
|
115
|
+
end
|
116
|
+
|
117
|
+
def self.parse_upload_url(url)
|
118
|
+
url = URI.parse(url)
|
119
|
+
query_parts = {}
|
120
|
+
url.query.split("&").each do |x|
|
121
|
+
parts = x.split("=")
|
122
|
+
query_parts[parts[0]] = CGI.unescape(parts[1])
|
123
|
+
end
|
124
|
+
|
125
|
+
{
|
126
|
+
host: "https://#{url.host}",
|
127
|
+
path: url.path,
|
128
|
+
query: query_parts
|
129
|
+
}
|
130
|
+
end
|
131
|
+
|
132
|
+
public_class_method(:create_blob_zip)
|
133
|
+
public_class_method(:upload_blob)
|
134
|
+
|
135
|
+
private_class_method(:upload_block)
|
136
|
+
private_class_method(:upload_block_list)
|
137
|
+
private_class_method(:parse_upload_url)
|
138
|
+
|
139
|
+
private_constant(:FILE_CHUNK_SIZE)
|
140
|
+
private_constant(:UPLOAD_RETRIES)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
@@ -7,112 +7,42 @@ module Fastlane
|
|
7
7
|
REQUEST_HEADERS = {
|
8
8
|
"Accept": "application/json"
|
9
9
|
}.freeze
|
10
|
-
FILE_CHUNK_SIZE = 26_214_400
|
11
|
-
UPLOAD_RETRIES = 3
|
12
10
|
|
13
|
-
def self.
|
14
|
-
|
15
|
-
UI.user_error!("File path is invalid") if !zip_path.is_a?(String) || zip_path.nil? || zip_path.empty?
|
16
|
-
|
17
|
-
expand_path = File.expand_path(zip_path)
|
18
|
-
UI.user_error!("The provided path doesn't point to ZIP file") unless File.exist?(expand_path) && zip_path.end_with?(".zip")
|
19
|
-
|
20
|
-
File.open(expand_path) do |file|
|
21
|
-
block_list = []
|
22
|
-
chunks_count = (file.size.to_f / FILE_CHUNK_SIZE).ceil
|
23
|
-
current_chunk = 1
|
24
|
-
|
25
|
-
until file.eof?
|
26
|
-
bytes = file.read(FILE_CHUNK_SIZE)
|
27
|
-
id = SecureRandom.uuid.delete("-")
|
28
|
-
block_list.append(id)
|
29
|
-
retry_count = 0
|
30
|
-
result = false
|
31
|
-
|
32
|
-
UI.message("Upload chunk [#{current_chunk} / #{chunks_count}]")
|
33
|
-
|
34
|
-
while !result && retry_count < UPLOAD_RETRIES
|
35
|
-
result = upload_block(url, bytes, id, timeout)
|
36
|
-
retry_count += 1
|
37
|
-
end
|
38
|
-
|
39
|
-
UI.user_error!("Uploading failed: some chunks have not been uploaded") unless result
|
40
|
-
current_chunk += 1
|
41
|
-
end
|
42
|
-
|
43
|
-
result = upload_block_list(url, block_list, timeout)
|
44
|
-
UI.user_error!("Uploading failed: block list hasn't been uploaded") unless result
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
def self.upload_block(url, bytes, id, timeout = 0)
|
49
|
-
headers = {
|
50
|
-
"Content-Length": bytes.length.to_s
|
51
|
-
}
|
52
|
-
|
53
|
-
url_data = parse_upload_url(url)
|
54
|
-
url_data[:query]["comp"] = "block"
|
55
|
-
url_data[:query]["blockid"] = id
|
56
|
-
connection = Faraday.new(url_data[:host])
|
57
|
-
|
58
|
-
begin
|
59
|
-
response = connection.put(url_data[:path]) do |req|
|
60
|
-
req.headers = headers
|
61
|
-
req.params = url_data[:query]
|
62
|
-
req.body = bytes
|
63
|
-
req.options.timeout = timeout if timeout.positive?
|
64
|
-
end
|
65
|
-
|
66
|
-
return true if response.status == 201
|
67
|
-
|
68
|
-
error = response.body.to_s
|
69
|
-
UI.error("Upload request failed.\nCode: #{response.status}\nError: #{error}")
|
70
|
-
rescue StandardError => ex
|
71
|
-
UI.error("Upload request failed: #{ex}")
|
72
|
-
end
|
73
|
-
|
74
|
-
false
|
75
|
-
end
|
76
|
-
|
77
|
-
def self.upload_block_list(url, list, timeout = 0)
|
78
|
-
document = REXML::Document.new
|
79
|
-
document.xml_decl.version = "1.0"
|
80
|
-
document.xml_decl.encoding = "utf-8"
|
81
|
-
block_list = document.add_element("BlockList")
|
82
|
-
|
83
|
-
list.each do |block|
|
84
|
-
block_list.add_element("Latest").text = block
|
85
|
-
end
|
11
|
+
def self.get_app_info(app_id, auth_token, timeout = 0)
|
12
|
+
check_app_id(app_id)
|
86
13
|
|
87
|
-
|
88
|
-
|
89
|
-
connection = Faraday.new(url_data[:host])
|
14
|
+
connection = Faraday.new(HOST)
|
15
|
+
url = build_url_root(app_id)
|
90
16
|
|
91
17
|
begin
|
92
|
-
response = connection.
|
93
|
-
req.
|
94
|
-
req.body = document.to_s
|
18
|
+
response = connection.get(url) do |req|
|
19
|
+
req.headers = build_headers(auth_token)
|
95
20
|
req.options.timeout = timeout if timeout.positive?
|
96
21
|
end
|
22
|
+
data = JSON.parse(response.body)
|
97
23
|
|
98
|
-
return
|
24
|
+
return data if response.status == 200
|
99
25
|
|
100
|
-
error
|
101
|
-
UI.error("Upload block list request failed.\nCode: #{response.status}\nError: #{error}")
|
26
|
+
UI.user_error!("Getting app request returned the error.\nCode: #{response.status}")
|
102
27
|
rescue StandardError => ex
|
103
|
-
UI.
|
28
|
+
UI.user_error!("Getting app info process failed: #{ex}")
|
104
29
|
end
|
105
|
-
|
106
|
-
false
|
107
30
|
end
|
108
31
|
|
109
|
-
def self.
|
32
|
+
def self.get_submission(app_id, flight_id, submission_id, auth_token, timeout = 0)
|
110
33
|
check_app_id(app_id)
|
34
|
+
check_submission_id(submission_id)
|
35
|
+
|
36
|
+
is_flight = !flight_id.nil? && !flight_id.empty?
|
37
|
+
check_flight_id(flight_id) if is_flight
|
111
38
|
|
112
39
|
connection = Faraday.new(HOST)
|
40
|
+
url = build_url_root(app_id)
|
41
|
+
url += "/flights/#{flight_id}" if is_flight
|
42
|
+
url += "/submissions/#{submission_id}"
|
113
43
|
|
114
44
|
begin
|
115
|
-
response = connection.get(
|
45
|
+
response = connection.get(url) do |req|
|
116
46
|
req.headers = build_headers(auth_token)
|
117
47
|
req.options.timeout = timeout if timeout.positive?
|
118
48
|
end
|
@@ -120,9 +50,12 @@ module Fastlane
|
|
120
50
|
|
121
51
|
return data if response.status == 200
|
122
52
|
|
123
|
-
|
53
|
+
code = data["code"]
|
54
|
+
message = data["message"]
|
55
|
+
|
56
|
+
UI.user_error!("Getting flight submission request returned the error.\nCode: #{response.status} #{code}.\nDescription: #{message}")
|
124
57
|
rescue StandardError => ex
|
125
|
-
UI.user_error!("Getting
|
58
|
+
UI.user_error!("Getting flight submission process failed: #{ex}")
|
126
59
|
end
|
127
60
|
end
|
128
61
|
|
@@ -130,9 +63,10 @@ module Fastlane
|
|
130
63
|
check_app_id(app_id)
|
131
64
|
|
132
65
|
connection = Faraday.new(HOST)
|
66
|
+
url = "#{build_url_root(app_id)}/submissions"
|
133
67
|
|
134
68
|
begin
|
135
|
-
response = connection.post(
|
69
|
+
response = connection.post(url) do |req|
|
136
70
|
req.headers = build_headers(auth_token)
|
137
71
|
req.options.timeout = timeout if timeout.positive?
|
138
72
|
end
|
@@ -153,11 +87,15 @@ module Fastlane
|
|
153
87
|
check_app_id(app_id)
|
154
88
|
UI.user_error!("Submission data object need to be provided") if submission_obj.nil?
|
155
89
|
|
156
|
-
submission_id = submission_obj["id"]
|
157
90
|
connection = Faraday.new(HOST)
|
91
|
+
flight_id = submission_obj["flightId"]
|
92
|
+
submission_id = submission_obj["id"]
|
93
|
+
url = build_url_root(app_id)
|
94
|
+
url += "/flights/#{flight_id}" if !flight_id.nil? && !flight_id.empty?
|
95
|
+
url += "/submissions/#{submission_id}"
|
158
96
|
|
159
97
|
begin
|
160
|
-
response = connection.put(
|
98
|
+
response = connection.put(url) do |req|
|
161
99
|
req.headers = build_headers(auth_token)
|
162
100
|
req.body = submission_obj.to_json
|
163
101
|
req.options.timeout = timeout if timeout.positive?
|
@@ -172,14 +110,20 @@ module Fastlane
|
|
172
110
|
end
|
173
111
|
end
|
174
112
|
|
175
|
-
def self.commit_submission(app_id, submission_id, auth_token, timeout = 0)
|
113
|
+
def self.commit_submission(app_id, flight_id, submission_id, auth_token, timeout = 0)
|
176
114
|
check_app_id(app_id)
|
177
115
|
check_submission_id(submission_id)
|
178
116
|
|
117
|
+
is_flight = !flight_id.nil? && !flight_id.empty?
|
118
|
+
check_flight_id(flight_id) if is_flight
|
119
|
+
|
179
120
|
connection = Faraday.new(HOST)
|
121
|
+
url = build_url_root(app_id)
|
122
|
+
url += "/flights/#{flight_id}" if is_flight
|
123
|
+
url += "/submissions/#{submission_id}/commit"
|
180
124
|
|
181
125
|
begin
|
182
|
-
response = connection.post(
|
126
|
+
response = connection.post(url) do |req|
|
183
127
|
req.headers = build_headers(auth_token)
|
184
128
|
req.options.timeout = timeout if timeout.positive?
|
185
129
|
end
|
@@ -195,9 +139,10 @@ module Fastlane
|
|
195
139
|
check_submission_id(submission_id)
|
196
140
|
|
197
141
|
connection = Faraday.new(HOST)
|
142
|
+
url = "#{build_url_root(app_id)}/submissions/#{submission_id}"
|
198
143
|
|
199
144
|
begin
|
200
|
-
response = connection.delete(
|
145
|
+
response = connection.delete(url) do |req|
|
201
146
|
req.headers = build_headers(auth_token)
|
202
147
|
req.options.timeout = timeout if timeout.positive?
|
203
148
|
end
|
@@ -208,11 +153,12 @@ module Fastlane
|
|
208
153
|
end
|
209
154
|
end
|
210
155
|
|
211
|
-
def self.get_submission_status(app_id, submission_id, auth_token, timeout = 0)
|
156
|
+
def self.get_submission_status(app_id, flight_id, submission_id, auth_token, timeout = 0)
|
212
157
|
check_app_id(app_id)
|
158
|
+
check_flight_id(flight_id) if !flight_id.nil? && !flight_id.empty?
|
213
159
|
check_submission_id(submission_id)
|
214
160
|
|
215
|
-
response = get_submission_status_internal(app_id, submission_id, auth_token, timeout)
|
161
|
+
response = get_submission_status_internal(app_id, flight_id, submission_id, auth_token, timeout)
|
216
162
|
|
217
163
|
# Sometimes MS can return internal server error code (500) that is not directly related to uploading process.
|
218
164
|
# Once it happens, retry 3 times until we'll get a success response.
|
@@ -221,7 +167,7 @@ module Fastlane
|
|
221
167
|
|
222
168
|
until server_error_500_retry_counter < 2
|
223
169
|
server_error_500_retry_counter += 1
|
224
|
-
response = get_submission_status_internal(app_id, submission_id, auth_token, timeout)
|
170
|
+
response = get_submission_status_internal(app_id, flight_id, submission_id, auth_token, timeout)
|
225
171
|
break if response.nil? || response[:status] == 200
|
226
172
|
end
|
227
173
|
end
|
@@ -231,11 +177,14 @@ module Fastlane
|
|
231
177
|
UI.user_error!("Submission status obtaining request returned the error.\nCode: #{response[:status]}")
|
232
178
|
end
|
233
179
|
|
234
|
-
def self.get_submission_status_internal(app_id, submission_id, auth_token, timeout = 0)
|
180
|
+
def self.get_submission_status_internal(app_id, flight_id, submission_id, auth_token, timeout = 0)
|
235
181
|
connection = Faraday.new(HOST)
|
182
|
+
url = build_url_root(app_id)
|
183
|
+
url += "/flights/#{flight_id}/" if !flight_id.nil? && !flight_id.empty?
|
184
|
+
url += "/submissions/#{submission_id}/status"
|
236
185
|
|
237
186
|
begin
|
238
|
-
response = connection.get(
|
187
|
+
response = connection.get(url) do |req|
|
239
188
|
req.headers = build_headers(auth_token)
|
240
189
|
req.options.timeout = timeout if timeout.positive?
|
241
190
|
end
|
@@ -251,6 +200,36 @@ module Fastlane
|
|
251
200
|
end
|
252
201
|
end
|
253
202
|
|
203
|
+
def self.create_flight(app_id, friendly_name, group_ids, auth_token, timeout = 0)
|
204
|
+
check_app_id(app_id)
|
205
|
+
|
206
|
+
friendly_name = !friendly_name.nil? && !friendly_name.empty? ? friendly_name : "Fastlane Sapfire Flight"
|
207
|
+
body = {
|
208
|
+
friendlyName: friendly_name,
|
209
|
+
groupIds: group_ids
|
210
|
+
}
|
211
|
+
connection = Faraday.new(HOST)
|
212
|
+
url = "#{build_url_root(app_id)}/flights"
|
213
|
+
|
214
|
+
begin
|
215
|
+
response = connection.post(url) do |req|
|
216
|
+
req.headers = build_headers(auth_token)
|
217
|
+
req.body = body.to_json
|
218
|
+
req.options.timeout = timeout if timeout.positive?
|
219
|
+
end
|
220
|
+
data = JSON.parse(response.body)
|
221
|
+
|
222
|
+
return data if response.status == 201
|
223
|
+
|
224
|
+
code = data["code"]
|
225
|
+
message = data["message"]
|
226
|
+
|
227
|
+
UI.user_error!("Creating flight request returned the error.\nCode: #{response.status} #{code}.\nDescription: #{message}")
|
228
|
+
rescue StandardError => ex
|
229
|
+
UI.user_error!("Creating flight process failed: #{ex}")
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
254
233
|
def self.acquire_authorization_token(tenant_id, client_id, client_secret, timeout = 0)
|
255
234
|
body = {
|
256
235
|
client_id: client_id,
|
@@ -282,10 +261,8 @@ module Fastlane
|
|
282
261
|
end
|
283
262
|
end
|
284
263
|
|
285
|
-
def self.
|
286
|
-
|
287
|
-
app_info = get_app_info(app_id, auth_token, timeout)
|
288
|
-
app_info["pendingApplicationSubmission"]
|
264
|
+
def self.build_url_root(app_id)
|
265
|
+
"/#{API_VERSION}/#{API_ROOT}/#{app_id}"
|
289
266
|
end
|
290
267
|
|
291
268
|
def self.build_headers(auth_token)
|
@@ -295,21 +272,6 @@ module Fastlane
|
|
295
272
|
}.merge(REQUEST_HEADERS)
|
296
273
|
end
|
297
274
|
|
298
|
-
def self.parse_upload_url(url)
|
299
|
-
url = URI.parse(url)
|
300
|
-
query_parts = {}
|
301
|
-
url.query.split("&").each do |x|
|
302
|
-
parts = x.split("=")
|
303
|
-
query_parts[parts[0]] = CGI.unescape(parts[1])
|
304
|
-
end
|
305
|
-
|
306
|
-
{
|
307
|
-
host: "https://#{url.host}",
|
308
|
-
path: url.path,
|
309
|
-
query: query_parts
|
310
|
-
}
|
311
|
-
end
|
312
|
-
|
313
275
|
def self.check_app_id(id)
|
314
276
|
UI.user_error!("App ID need to be provided") if !id.is_a?(String) || id.nil? || id.empty?
|
315
277
|
end
|
@@ -318,30 +280,32 @@ module Fastlane
|
|
318
280
|
UI.user_error!("Submission ID need to be provided") if !id.is_a?(String) || id.nil? || id.empty?
|
319
281
|
end
|
320
282
|
|
321
|
-
|
283
|
+
def self.check_flight_id(id)
|
284
|
+
UI.user_error!("Flight ID need to be provided") if !id.is_a?(String) || id.nil? || id.empty?
|
285
|
+
end
|
286
|
+
|
287
|
+
|
322
288
|
public_class_method(:get_app_info)
|
289
|
+
public_class_method(:get_submission)
|
323
290
|
public_class_method(:create_submission)
|
324
291
|
public_class_method(:update_submission)
|
325
292
|
public_class_method(:commit_submission)
|
326
293
|
public_class_method(:remove_submission)
|
327
294
|
public_class_method(:get_submission_status)
|
295
|
+
public_class_method(:create_flight)
|
328
296
|
public_class_method(:acquire_authorization_token)
|
329
|
-
public_class_method(:non_published_submission)
|
330
297
|
|
331
|
-
private_class_method(:
|
332
|
-
private_class_method(:upload_block_list)
|
298
|
+
private_class_method(:build_url_root)
|
333
299
|
private_class_method(:build_headers)
|
334
|
-
private_class_method(:parse_upload_url)
|
335
300
|
private_class_method(:check_app_id)
|
336
301
|
private_class_method(:check_submission_id)
|
302
|
+
private_class_method(:check_flight_id)
|
337
303
|
private_class_method(:get_submission_status_internal)
|
338
304
|
|
339
305
|
private_constant(:HOST)
|
340
306
|
private_constant(:API_VERSION)
|
341
307
|
private_constant(:API_ROOT)
|
342
308
|
private_constant(:REQUEST_HEADERS)
|
343
|
-
private_constant(:FILE_CHUNK_SIZE)
|
344
|
-
private_constant(:UPLOAD_RETRIES)
|
345
309
|
end
|
346
310
|
end
|
347
311
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fastlane-plugin-sapfire
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- CheeryLee
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2025-06-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|
@@ -144,7 +144,9 @@ files:
|
|
144
144
|
- lib/fastlane/plugin/sapfire/actions/update_uwp_signing_settings_action.rb
|
145
145
|
- lib/fastlane/plugin/sapfire/actions/upload_ms_store_action.rb
|
146
146
|
- lib/fastlane/plugin/sapfire/actions/upload_nuget_action.rb
|
147
|
+
- lib/fastlane/plugin/sapfire/actions/upload_package_flight_action.rb
|
147
148
|
- lib/fastlane/plugin/sapfire/actions_base/msbuild_action_base.rb
|
149
|
+
- lib/fastlane/plugin/sapfire/helper/azure_blob_helper.rb
|
148
150
|
- lib/fastlane/plugin/sapfire/helper/ms_credentials.rb
|
149
151
|
- lib/fastlane/plugin/sapfire/helper/ms_devcenter_helper.rb
|
150
152
|
- lib/fastlane/plugin/sapfire/helper/sapfire_helper.rb
|
@@ -176,7 +178,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
176
178
|
- !ruby/object:Gem::Version
|
177
179
|
version: '0'
|
178
180
|
requirements: []
|
179
|
-
rubygems_version: 3.
|
181
|
+
rubygems_version: 3.4.10
|
180
182
|
signing_key:
|
181
183
|
specification_version: 4
|
182
184
|
summary: A bunch of fastlane actions to work with MSBuild, NuGet and Microsoft Store
|