fastlane-plugin-upload_to_loadly 1.1.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 436b4865c186354b530ffe8d39b953ce5e2e1a1b5182bb4ce7916f4ac791b642
|
4
|
+
data.tar.gz: 0f6b59c543ad5630f58509ee9629416d4fd4dc4ceb4e71a8b70f98311ca414e7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7d0cf968133de79370e557a008a6580db352da368684ab984635127b12bb5dbac16926b3784aa4a68b2b883dac21ef0caa05b415d5804fca5846ede5765b547b
|
7
|
+
data.tar.gz: 07feb7b105b844a15d1f737218bd8545ec0ffa62904ee97384fb3ceb498c394bf441532f617c1f536a7cee3a0b5646203da3a27c476eb0fb50120c51b14157bb
|
@@ -1,198 +1,207 @@
|
|
1
1
|
module Fastlane
|
2
|
-
|
2
|
+
module Actions
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
4
|
+
module SharedValues
|
5
|
+
UPLOADED_FILE_LINK_TO_LOADLY = :UPLOADED_FILE_LINK_TO_LOADLY
|
6
|
+
QR_CODE_URL_TO_LOADLY = :QR_CODE_URL_TO_LOADLY
|
7
|
+
SHORTCUT_URL_TO_LOADLY = :SHORTCUT_URL_TO_LOADLY
|
8
|
+
end
|
9
|
+
|
10
|
+
class UploadToLoadlyAction < Action
|
11
|
+
|
12
|
+
UPLOAD_URL = "https://api.loadly.io/apiv2/app/upload"
|
13
|
+
LOADLY_FILE_LINK = "https://i.loadly.io"
|
14
|
+
|
15
|
+
def self.run(options)
|
16
|
+
Actions.verify_gem!('rest-client')
|
17
|
+
require 'rest-client'
|
18
|
+
require 'json'
|
19
|
+
require 'thread'
|
20
|
+
|
21
|
+
if options[:file].nil?
|
22
|
+
UI.important("File didn't come to LOADLY_plugin. Uploading is unavailable.")
|
23
|
+
return
|
24
|
+
end
|
25
|
+
|
26
|
+
if options[:api_key].nil?
|
27
|
+
UI.important("Loadly.io api_key is empty - uploading is unavailable.")
|
28
|
+
UI.important("Try to upload file by yourself. Path: #{options[:file]}")
|
29
|
+
return
|
30
|
+
end
|
31
|
+
|
32
|
+
file_size = File.size(options[:file])
|
33
|
+
file_name = File.basename(options[:file])
|
34
|
+
|
35
|
+
upload_options = {
|
36
|
+
_api_key: options[:api_key],
|
37
|
+
buildPassword: options[:build_password],
|
38
|
+
buildUpdateDescription: options[:build_description],
|
39
|
+
file: File.new(options[:file], 'rb'),
|
40
|
+
}
|
41
|
+
|
42
|
+
timeout = options[:timeout]
|
43
|
+
|
44
|
+
UI.message("📦 Preparing to upload #{file_name} (#{format_size(file_size)})")
|
45
|
+
UI.message("⏳ Initializing upload to Loadly.io...")
|
46
|
+
|
47
|
+
started_at = Time.now
|
48
|
+
spinner_chars = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']
|
49
|
+
spinner_index = 0
|
50
|
+
spinner_running = true
|
51
|
+
|
52
|
+
# Start the spinner thread
|
53
|
+
spinner_thread = Thread.new do
|
54
|
+
while spinner_running
|
55
|
+
spinner = spinner_chars[spinner_index % spinner_chars.length]
|
56
|
+
elapsed = Time.now - started_at
|
57
|
+
UI.message("#{spinner} Uploading... (#{format_time(elapsed)})")
|
58
|
+
spinner_index += 1
|
59
|
+
sleep(0.9)
|
29
60
|
end
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
upload_options = {
|
35
|
-
_api_key: options[:api_key],
|
36
|
-
buildPassword: options[:build_password],
|
37
|
-
buildUpdateDescription: options[:build_description],
|
38
|
-
file: File.new(options[:file], 'rb'),
|
39
|
-
}
|
40
|
-
|
41
|
-
timeout = options[:timeout]
|
42
|
-
|
43
|
-
UI.message("📦 Preparing to upload #{file_name} (#{format_size(file_size)})")
|
44
|
-
UI.message("⏳ Initializing upload to Loadly.io...")
|
45
|
-
|
46
|
-
started_at = Time.now
|
47
|
-
spinner_chars = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']
|
48
|
-
spinner_index = 0
|
49
|
-
|
61
|
+
end
|
62
|
+
|
63
|
+
begin
|
50
64
|
response = RestClient::Request.execute(
|
51
65
|
method: :post,
|
52
66
|
url: UPLOAD_URL,
|
53
67
|
timeout: timeout,
|
54
|
-
payload: upload_options
|
55
|
-
block_response: proc { |response|
|
56
|
-
if Time.now - started_at > spinner_index
|
57
|
-
spinner_index += 1
|
58
|
-
spinner = spinner_chars[spinner_index % spinner_chars.length]
|
59
|
-
elapsed = Time.now - started_at
|
60
|
-
UI.message("#{spinner} Uploading... (#{format_time(elapsed)})")
|
61
|
-
end
|
62
|
-
}
|
68
|
+
payload: upload_options
|
63
69
|
)
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
UI.error("❌ Upload failed!")
|
69
|
-
UI.important("Failed to upload file to loadly, because of:")
|
70
|
-
UI.important(error)
|
71
|
-
UI.important("Try to upload file by yourself. Path: #{options[:file]}")
|
72
|
-
return
|
73
|
-
end
|
74
|
-
|
70
|
+
spinner_running = false
|
71
|
+
spinner_thread.join # Wait for spinner to finish
|
72
|
+
UI.message("\r ") # Clear spinner line
|
73
|
+
|
75
74
|
data = JSON.parse(response.body)['data']
|
76
|
-
|
75
|
+
|
77
76
|
if data
|
78
77
|
download_url = "#{LOADLY_FILE_LINK}/#{data['buildKey']}"
|
79
|
-
qr_code_url =
|
80
|
-
shortcut_url = data['buildShortcutUrl']
|
81
|
-
|
78
|
+
qr_code_url = data['buildQRCodeURL']
|
79
|
+
shortcut_url = "#{LOADLY_FILE_LINK}/#{data['buildShortcutUrl']}"
|
80
|
+
|
82
81
|
Actions.lane_context[SharedValues::UPLOADED_FILE_LINK_TO_LOADLY] = download_url
|
83
82
|
Actions.lane_context[SharedValues::QR_CODE_URL_TO_LOADLY] = qr_code_url
|
84
83
|
Actions.lane_context[SharedValues::SHORTCUT_URL_TO_LOADLY] = shortcut_url
|
85
|
-
|
84
|
+
|
86
85
|
total_time = Time.now - started_at
|
87
|
-
UI.success("✅ Upload completed successfully
|
86
|
+
UI.success("✅ Upload completed successfully: #{file_name} (#{format_size(file_size)})")
|
88
87
|
UI.success("📱 Install URLs:")
|
89
88
|
UI.message(" • Download URL: #{download_url}")
|
90
89
|
UI.message(" • Shortcut URL: #{shortcut_url}")
|
91
90
|
UI.message(" • QR Code URL: #{qr_code_url}")
|
92
|
-
|
91
|
+
|
93
92
|
if !options[:callback_url].nil?
|
94
93
|
data['download_url'] = download_url
|
95
94
|
self.callback(options[:callback_url], data)
|
96
95
|
end
|
97
|
-
|
96
|
+
|
98
97
|
return
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
def self.format_size(size_in_bytes)
|
103
|
-
units = ['B', 'KB', 'MB', 'GB']
|
104
|
-
unit_index = 0
|
105
|
-
size = size_in_bytes.to_f
|
106
|
-
|
107
|
-
while size > 1024 && unit_index < units.length - 1
|
108
|
-
size /= 1024
|
109
|
-
unit_index += 1
|
110
|
-
end
|
111
|
-
|
112
|
-
"%.2f %s" % [size, units[unit_index]]
|
113
|
-
end
|
114
|
-
|
115
|
-
def self.format_time(seconds)
|
116
|
-
if seconds < 60
|
117
|
-
"#{seconds.round}s"
|
118
|
-
elsif seconds < 3600
|
119
|
-
minutes = (seconds / 60).floor
|
120
|
-
remaining_seconds = (seconds % 60).round
|
121
|
-
"#{minutes}m #{remaining_seconds}s"
|
122
98
|
else
|
123
|
-
|
124
|
-
remaining_minutes = ((seconds % 3600) / 60).floor
|
125
|
-
"#{hours}h #{remaining_minutes}m"
|
99
|
+
UI.error("❌ Upload failed! No response data received.")
|
126
100
|
end
|
101
|
+
rescue RestClient::ExceptionWithResponse => error
|
102
|
+
spinner_running = false
|
103
|
+
spinner_thread.join
|
104
|
+
UI.message("\r ") # Clear spinner line
|
105
|
+
UI.error("❌ Upload failed!")
|
106
|
+
UI.important("Failed to upload file to loadly, because of:")
|
107
|
+
UI.important(error.message)
|
108
|
+
UI.important("Try to upload file by yourself. Path: #{options[:file]}")
|
127
109
|
end
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
android_path = Actions.lane_context[SharedValues::GRADLE_APK_OUTPUT_PATH]
|
139
|
-
return platform == :ios ? ios_path : android_path
|
140
|
-
end
|
141
|
-
|
142
|
-
def self.available_options
|
143
|
-
[
|
144
|
-
FastlaneCore::ConfigItem.new(key: :api_key,
|
145
|
-
env_name: "LOADLY_API_KEY",
|
146
|
-
description: "Loadly API Key",
|
147
|
-
optional: false),
|
148
|
-
FastlaneCore::ConfigItem.new(key: :file,
|
149
|
-
env_name: "LOADLY_FILE",
|
150
|
-
description: "Path to .ipa or .apk file. Default - `IPA_OUTPUT_PATH` or `GRADLE_APK_OUTPUT_PATH` based on platform",
|
151
|
-
optional: true,
|
152
|
-
default_value: self.default_file_path),
|
153
|
-
FastlaneCore::ConfigItem.new(key: :build_password,
|
154
|
-
env_name: "LOADLY_BUILD_PASSWORD",
|
155
|
-
description: "Set the App installation password. If the password is empty, public installation is used by default",
|
156
|
-
optional: true),
|
157
|
-
FastlaneCore::ConfigItem.new(key: :build_description,
|
158
|
-
env_name: "LOADLY_BUILD_UPDATE_DESCRIPTION",
|
159
|
-
description: "Additional information to your users on this build: the comment will be displayed on the installation page",
|
160
|
-
optional: true),
|
161
|
-
FastlaneCore::ConfigItem.new(key: :callback_url,
|
162
|
-
env_name: "LOADLY_CALLBACK_URL",
|
163
|
-
description: "The URL loadly should call with the result",
|
164
|
-
optional: true),
|
165
|
-
FastlaneCore::ConfigItem.new(key: :timeout,
|
166
|
-
env_name: "LOADLY_TIMEOUT",
|
167
|
-
description: "Timeout for uploading file to Loadly. Default: 600, range: (60, 1800)",
|
168
|
-
is_string: false,
|
169
|
-
optional: true,
|
170
|
-
default_value: 600),
|
171
|
-
]
|
172
|
-
end
|
173
|
-
|
174
|
-
def self.output
|
175
|
-
[
|
176
|
-
['UPLOADED_FILE_LINK_TO_LOADLY', 'URL to uploaded .ipa or .apk file to loadly.']
|
177
|
-
]
|
178
|
-
end
|
179
|
-
|
180
|
-
def self.description
|
181
|
-
"Upload .ipa/.apk file to loadly.io"
|
110
|
+
end
|
111
|
+
|
112
|
+
def self.format_size(size_in_bytes)
|
113
|
+
units = ['B', 'KB', 'MB', 'GB']
|
114
|
+
unit_index = 0
|
115
|
+
size = size_in_bytes.to_f
|
116
|
+
|
117
|
+
while size > 1024 && unit_index < units.length - 1
|
118
|
+
size /= 1024
|
119
|
+
unit_index += 1
|
182
120
|
end
|
183
|
-
|
184
|
-
|
185
|
-
|
121
|
+
|
122
|
+
"%.2f %s" % [size, units[unit_index]]
|
123
|
+
end
|
124
|
+
|
125
|
+
def self.format_time(seconds)
|
126
|
+
if seconds < 60
|
127
|
+
"#{seconds.round}s"
|
128
|
+
elsif seconds < 3600
|
129
|
+
minutes = (seconds / 60).floor
|
130
|
+
remaining_seconds = (seconds % 60).round
|
131
|
+
"#{minutes}m #{remaining_seconds}s"
|
132
|
+
else
|
133
|
+
hours = (seconds / 3600).floor
|
134
|
+
remaining_minutes = ((seconds % 3600) / 60).floor
|
135
|
+
"#{hours}h #{remaining_minutes}m"
|
186
136
|
end
|
187
|
-
|
188
|
-
|
137
|
+
end
|
138
|
+
|
139
|
+
def self.callback(url, data)
|
140
|
+
UI.success("Performing callback to #{url}")
|
141
|
+
RestClient.post(url, data)
|
142
|
+
UI.success("Callback successfully")
|
143
|
+
end
|
144
|
+
|
145
|
+
def self.default_file_path
|
146
|
+
platform = Actions.lane_context[SharedValues::PLATFORM_NAME]
|
147
|
+
ios_path = Actions.lane_context[SharedValues::IPA_OUTPUT_PATH]
|
148
|
+
android_path = Actions.lane_context[SharedValues::GRADLE_APK_OUTPUT_PATH]
|
149
|
+
return platform == :ios ? ios_path : android_path
|
150
|
+
end
|
151
|
+
|
152
|
+
def self.available_options
|
153
|
+
[
|
154
|
+
FastlaneCore::ConfigItem.new(key: :api_key,
|
155
|
+
env_name: "LOADLY_API_KEY",
|
156
|
+
description: "Loadly API Key",
|
157
|
+
optional: false),
|
158
|
+
FastlaneCore::ConfigItem.new(key: :file,
|
159
|
+
env_name: "LOADLY_FILE",
|
160
|
+
description: "Path to .ipa or .apk file. Default - `IPA_OUTPUT_PATH` or `GRADLE_APK_OUTPUT_PATH` based on platform",
|
161
|
+
optional: true,
|
162
|
+
default_value: self.default_file_path),
|
163
|
+
FastlaneCore::ConfigItem.new(key: :build_password,
|
164
|
+
env_name: "LOADLY_BUILD_PASSWORD",
|
165
|
+
description: "Set the App installation password. If the password is empty, public installation is used by default",
|
166
|
+
optional: true),
|
167
|
+
FastlaneCore::ConfigItem.new(key: :build_description,
|
168
|
+
env_name: "LOADLY_BUILD_UPDATE_DESCRIPTION",
|
169
|
+
description: "Additional information to your users on this build: the comment will be displayed on the installation page",
|
170
|
+
optional: true),
|
171
|
+
FastlaneCore::ConfigItem.new(key: :callback_url,
|
172
|
+
env_name: "LOADLY_CALLBACK_URL",
|
173
|
+
description: "The URL loadly should call with the result",
|
174
|
+
optional: true),
|
175
|
+
FastlaneCore::ConfigItem.new(key: :timeout,
|
176
|
+
env_name: "LOADLY_TIMEOUT",
|
177
|
+
description: "Timeout for uploading file to Loadly. Default: 600, range: (60, 1800)",
|
178
|
+
is_string: false,
|
179
|
+
optional: true,
|
180
|
+
default_value: 600),
|
181
|
+
]
|
182
|
+
end
|
183
|
+
|
184
|
+
def self.output
|
185
|
+
[
|
186
|
+
['UPLOADED_FILE_LINK_TO_LOADLY', 'URL to uploaded .ipa or .apk file to loadly.']
|
187
|
+
]
|
188
|
+
end
|
189
|
+
|
190
|
+
def self.description
|
191
|
+
"Upload .ipa/.apk file to loadly.io"
|
192
|
+
end
|
193
|
+
|
194
|
+
def self.authors
|
195
|
+
["aliffazfar"]
|
196
|
+
end
|
197
|
+
|
198
|
+
def self.details
|
189
199
|
"This action upload .ipa/.apk file to https://loadly.io and return link to uploaded file."
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
end
|
200
|
+
end
|
201
|
+
|
202
|
+
def self.is_supported?(platform)
|
203
|
+
[:ios, :android].include?(platform)
|
195
204
|
end
|
196
205
|
end
|
197
206
|
end
|
198
|
-
|
207
|
+
end
|