fastlane-plugin-pgyersws 0.3.5

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: edc1b6a7f6942167bf1cdce64cccb9fe0b84ba85ff10e2b4a2ed58477bd4f554
4
+ data.tar.gz: 347172bc15c543ce38098f6c4cfdcab5b560131e720bef85aef8f7bea7eed410
5
+ SHA512:
6
+ metadata.gz: 48774d857a74bfe5873b809337588397925b2c98451b788071ad69dff18d6144c4b47a018b4684778c607221cefd9bae8401959348c8c9f435fb9bf2ce55ddcb
7
+ data.tar.gz: 189d5b3ab41e26b71632573013a4322885138ae81b0710f301dae0f0af39c703be5162553f0d02ecec77fc5715e1ee3a608c0c57cb63395a2373966443b0ac0c
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 rexshi <rexshi@pgyer.com>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,170 @@
1
+ # pgyer plugin
2
+
3
+ This fork is published as `fastlane-plugin-pgyersws` to preserve compatibility with `fastlane 2.238.0` while keeping the `pgyer(...)` action name unchanged.
4
+
5
+ [![fastlane Plugin Badge](https://rawcdn.githack.com/fastlane/fastlane/master/fastlane/assets/plugin-badge.svg)](https://rubygems.org/gems/fastlane-plugin-pgyersws)
6
+ ## Important Notifications
7
+
8
+ - Since pgyer has migrated the API domain name, Please update the plugin to the latest version (>= 0.2.6) to avoid uploading failures.
9
+
10
+ ## Getting Started
11
+
12
+ This project is a [fastlane](https://github.com/fastlane/fastlane) plugin. To get started with `fastlane-plugin-pgyersws`, add it to your project by running:
13
+
14
+ ```bash
15
+ fastlane add_plugin pgyersws
16
+ ```
17
+
18
+ ## About pgyer
19
+
20
+ This pluginin allow you distribute app automatically to [pgyer beta testing service](https://www.pgyer.com) in fastlane workflow.
21
+
22
+ ## How to update pgyer plugin to the latest version
23
+
24
+ **Due to the adjustment of the API interface, please ensure that the plugin version is at least `0.2.4`**
25
+
26
+ Plan A: update all plguins (Recommended)
27
+
28
+
29
+ ```bash
30
+ fastlane update_plugins
31
+ ```
32
+
33
+ Plan B: update pgyer plugin only
34
+
35
+ modify `fastlane/Pluginfile`, update the following line:
36
+
37
+ ```ruby
38
+ gem 'fastlane-plugin-pgyersws', ">= 0.3.5"
39
+ ```
40
+
41
+ and run `bundle install` at the root of your fastlane project to update gemfile.lock
42
+
43
+ ## Plugin avaliable options
44
+
45
+ please visit [https://github.com/shishirui/fastlane-plugin-pgyer/blob/master/lib/fastlane/plugin/pgyer/actions/pgyer_action.rb#L135-L205](https://github.com/shishirui/fastlane-plugin-pgyer/blob/master/lib/fastlane/plugin/pgyer/actions/pgyer_action.rb#L135-L205) to know the options.
46
+
47
+
48
+ ## Example
49
+
50
+ Check out the [example `Fastfile`](fastlane/Fastfile) to see how to use this plugin. Try it by cloning the repo, running `fastlane install_plugins` and `bundle exec fastlane test`.
51
+
52
+ Just specify the `api_key` associated with your pgyer account.
53
+
54
+ ```
55
+ lane :beta do
56
+ gym
57
+ pgyer(api_key: "7f15xxxxxxxxxxxxxxxxxx141")
58
+ end
59
+ ```
60
+
61
+ You can also set a password to protect the App from being downloaded publicly:
62
+
63
+ ```
64
+ lane :beta do
65
+ gym
66
+ pgyer(api_key: "7f15xxxxxxxxxxxxxxxxxx141", password: "123456", install_type: "2")
67
+ end
68
+ ```
69
+
70
+ Set a version update description for App:
71
+ acd88a1a52f0818dc7923150c4d86f08
72
+ ```
73
+ lane :beta do
74
+ gym
75
+ pgyer(api_key: "7f15xxxxxxxxxxxxxxxxxx141", update_description: "update by fastlane")
76
+ end
77
+ ```
78
+
79
+
80
+ If the upload is successful, you will get information about the app after it is uploaded, which is returned from the API interface app/buildinfo . You can pass it to other plugins, or export it to the terminal for use by other scripts:
81
+
82
+ ```ruby
83
+ lane :beta do
84
+ gym
85
+ answer = pgyer(api_key: "xxxxxx")
86
+ puts answer
87
+ # terminal outputs like this if uploaded successfully
88
+ # {"buildKey"=>"xxxx", "buildType"=>"2", "buildIsFirst"=>"0", "buildIsLastest"=>"1", "buildFileKey"=>"xxx.apk", "buildFileName"=>"", "buildFileSize"=>"111111", "buildName"=>"testApk", "buildVersion"=>"0.11.0", "buildVersionNo"=>"13", "buildBuildVersion"=>"10", "buildIdentifier"=>"com.pgyer.testapk", "buildIcon"=>"xxxx", "buildDescription"=>"", "buildUpdateDescription"=>"", "buildScreenshots"=>"", "buildShortcutUrl"=>"xxxxxxx", "buildCreated"=>"2023-04-04 11:33:24", "buildUpdated"=>"2023-04-04 11:33:24", "buildQRCodeURL"=>"https://www.pgyer.com/app/qrcodeHistory/xxxxxx", "fastlaneAddedWholeVisitUrl"=>"https://www.pgyer.com/xxxxxx"}
89
+ puts "url = #{answer["fastlaneAddedWholeVisitUrl"]}"
90
+
91
+ # terminal outputs like this if uploaded successfully
92
+ # url = https://www.pgyer.com/xxxxxx
93
+
94
+ # More information please visit https://www.pgyer.com/doc/view/api#fastUploadApp to check API "https://www.pgyer.com/apiv2/app/buildInfo"
95
+
96
+ end
97
+ ```
98
+
99
+
100
+ HAP file upload
101
+
102
+ pgyer supports hap file to upload, use params hap to specify the absolute file path.
103
+
104
+ ```ruby
105
+ lane :beta do
106
+ gym
107
+ pgyer(api_key: "7f15xxxxxxxxxxxxxxxxxx141", hap: "xxxx")
108
+ end
109
+ ```
110
+
111
+ or in bash
112
+ ```bash
113
+ fastlane run pgyer api_key:7f15xxxxxxxxxxxxxxxxxx141 hap:xxx.hap
114
+ ```
115
+
116
+
117
+
118
+ And more params
119
+
120
+ ```
121
+
122
+ password: Set password to protect app.
123
+
124
+ update_description: Set update description for app.
125
+
126
+ install_type: Set install type for app (1=public, 2=password, 3=invite), Please set as a string.
127
+
128
+ install_date: Set install type for app (1=Set valid time, 2=Long-term effective, other=Do not modify the last setting), Please set as a string.
129
+
130
+ install_start_date: The value is a string of characters, for example, 2018-01-01.
131
+
132
+ install_end_date: The value is a string of characters, such as 2018-12-31.
133
+
134
+ channel: Need to update the specified channel of the download short link, can specify only one channel, string type, such as: ABCD. Specifies channel uploads. If you do not have one, do not use this parameter.
135
+
136
+ user_download_file_name: The name of the file downloaded by the user, the string type, such as: test.apk.
137
+
138
+ save_uploaded_info_json: (true or false, default to false) Whether to save the information returned by the API interface to a json file.
139
+
140
+
141
+
142
+ ```
143
+ ## Run tests for this plugin
144
+
145
+ To run both the tests, and code style validation, run
146
+
147
+ ```
148
+ rake
149
+ ```
150
+
151
+ To automatically fix many of the styling issues, use
152
+ ```
153
+ rubocop -a
154
+ ```
155
+
156
+ ## Issues and Feedback
157
+
158
+ For any other issues and feedback about this plugin, please submit it to this repository.
159
+
160
+ ## Troubleshooting
161
+
162
+ If you have trouble using plugins, check out the [Plugins Troubleshooting](https://docs.fastlane.tools/plugins/plugins-troubleshooting/) guide.
163
+
164
+ ## Using `fastlane` Plugins
165
+
166
+ For more information about how the `fastlane` plugin system works, check out the [Plugins documentation](https://docs.fastlane.tools/plugins/create-plugin/).
167
+
168
+ ## About `fastlane`
169
+
170
+ `fastlane` is the easiest way to automate beta deployments and releases for your iOS and Android apps. To learn more, check out [fastlane.tools](https://fastlane.tools).
@@ -0,0 +1,507 @@
1
+ require "faraday"
2
+ require "faraday/multipart"
3
+ require "faraday/response/json"
4
+ require "timeout"
5
+
6
+ module Fastlane
7
+ module Actions
8
+ class PgyerAction < Action
9
+ def self.run(params)
10
+ UI.message("The pgyer plugin is working.")
11
+
12
+ api_key = params[:api_key]
13
+
14
+ build_file = [
15
+ params[:ipa],
16
+ params[:apk],
17
+ params[:hap],
18
+ ].detect { |e| !e.to_s.empty? }
19
+
20
+ if build_file.nil?
21
+ UI.user_error!("You have to provide a build file")
22
+ end
23
+
24
+ type = get_type(params)
25
+
26
+ UI.message "build_file: #{build_file}, type: #{type}"
27
+
28
+ install_type = params[:install_type]
29
+ if install_type.nil?
30
+ install_type = "1"
31
+ end
32
+
33
+ password = params[:password]
34
+ if password.nil?
35
+ password = ""
36
+ end
37
+
38
+ request_params = {
39
+ "_api_key" => api_key,
40
+ "buildType" => type,
41
+ "buildInstallType" => install_type,
42
+ "buildPassword" => password,
43
+ }
44
+ request_params["oversea"] = params[:oversea] unless params[:oversea].nil?
45
+
46
+ update_description = params[:update_description]
47
+
48
+ if update_description != nil
49
+ request_params["buildUpdateDescription"] = update_description
50
+ end
51
+
52
+ install_date = params[:install_date]
53
+
54
+ if install_date != nil
55
+ if install_date == "1"
56
+ request_params["buildInstallDate"] = install_date
57
+ install_start_date = params[:install_start_date]
58
+ request_params["buildInstallStartDate"] = install_start_date
59
+ install_end_date = params[:install_end_date]
60
+ request_params["buildInstallEndDate"] = install_end_date
61
+ elsif install_date == "2"
62
+ request_params["buildInstallDate"] = install_date
63
+ end
64
+ end
65
+
66
+ channel = params[:channel]
67
+ if channel != nil
68
+ request_params["buildChannelShortcut"] = channel
69
+ end
70
+
71
+ # start upload
72
+ conn_options = {
73
+ request: {
74
+ timeout: 1000,
75
+ open_timeout: 300,
76
+ },
77
+ }
78
+
79
+ # 选择可用的 API 域名
80
+ use_doh = params[:doh] == true
81
+ api_host, pgyer_client, info = self.select_available_api_host(conn_options, request_params, use_doh)
82
+
83
+ if info["code"] != 0
84
+ UI.user_error!("Get token is failed, info: #{info}")
85
+ end
86
+
87
+ key = info["data"]["key"]
88
+
89
+ endpoint = info["data"]["endpoint"]
90
+
91
+ request_params = info["data"]["params"]
92
+
93
+ if key.nil? || endpoint.nil? || request_params.nil?
94
+ UI.user_error!("Get token is failed")
95
+ end
96
+ content_type = type == "android" ? "application/vnd.android.package-archive" : "application/octet-stream"
97
+
98
+ if !params[:user_download_file_name].nil?
99
+ request_params["x-cos-meta-file-name"] = params[:user_download_file_name]
100
+ end
101
+ request_params["file"] = Faraday::UploadIO.new(build_file, content_type)
102
+
103
+ UI.message "Start upload #{build_file} to pgyer..."
104
+
105
+ UI.message "Upload endpoint: #{endpoint}"
106
+
107
+ UI.message "Upload request_params: #{request_params}"
108
+
109
+
110
+
111
+ response = pgyer_client.post endpoint, request_params
112
+
113
+ if response.status != 204
114
+ UI.user_error!("PGYER Plugin Upload Error: #{response.body}")
115
+ end
116
+
117
+ # 如果使用 DoH,需要传递 DoH 相关信息
118
+ doh_info = nil
119
+ if use_doh && api_host.include?("www.pgyer.com")
120
+ domain = "www.pgyer.com"
121
+ ip_address = resolve_domain_via_doh(domain)
122
+ if ip_address
123
+ doh_info = { domain: domain, ip_address: ip_address }
124
+ end
125
+ end
126
+ answer = self.checkPublishStatus(pgyer_client, api_host, api_key, key, doh_info)
127
+
128
+ if params[:save_uploaded_info_json]
129
+ File.open("pgyer-fastlane-uploaded-app-info.json", "w") do |f|
130
+ f.write(answer.to_json)
131
+ end
132
+ end
133
+ answer
134
+ end
135
+
136
+ def self.description
137
+ "distribute app to pgyer beta testing service"
138
+ end
139
+
140
+ def self.authors
141
+ ["rexshi"]
142
+ end
143
+
144
+ def self.return_value
145
+ # If your method provides a return value, you can describe here what it does
146
+ end
147
+
148
+ def self.details
149
+ # Optional:
150
+ "distribute app to pgyer beta testing service"
151
+ end
152
+
153
+ def self.available_options
154
+ [
155
+ FastlaneCore::ConfigItem.new(key: :api_key,
156
+ env_name: "PGYER_API_KEY",
157
+ description: "api_key in your pgyer account",
158
+ optional: false,
159
+ type: String),
160
+ FastlaneCore::ConfigItem.new(key: :apk,
161
+ env_name: "PGYER_APK",
162
+ description: "Path to your APK file",
163
+ default_value: Actions.lane_context[SharedValues::GRADLE_APK_OUTPUT_PATH],
164
+ optional: true,
165
+ verify_block: proc do |value|
166
+ UI.user_error!("Couldn't find apk file at path '#{value}'") unless File.exist?(value)
167
+ end,
168
+ conflicting_options: [:ipa, :hap],
169
+ conflict_block: proc do |value|
170
+ UI.user_error!("You can't use 'apk' and '#{value.key}' options in one run")
171
+ end),
172
+ FastlaneCore::ConfigItem.new(key: :ipa,
173
+ env_name: "PGYER_IPA",
174
+ description: "Path to your IPA file. Optional if you use the _gym_ or _xcodebuild_ action. For Mac zip the .app. For Android provide path to .apk file",
175
+ default_value: Actions.lane_context[SharedValues::IPA_OUTPUT_PATH],
176
+ optional: true,
177
+ verify_block: proc do |value|
178
+ UI.user_error!("Couldn't find ipa file at path '#{value}'") unless File.exist?(value)
179
+ end,
180
+ conflicting_options: [:apk, :hap],
181
+ conflict_block: proc do |value|
182
+ UI.user_error!("You can't use 'ipa' and '#{value.key}' options in one run")
183
+ end),
184
+ FastlaneCore::ConfigItem.new(key: :hap,
185
+ env_name: "PGYER_HAP",
186
+ description: "Path to your HAP file",
187
+ default_value: Actions.lane_context[:HVIGOR_HAP_OUTPUT_PATH],
188
+ optional: true,
189
+ verify_block: proc do |value|
190
+ UI.user_error!("Couldn't find hap file at path '#{value}'") unless File.exist?(value)
191
+ end,
192
+ conflicting_options: [:apk, :ipa],
193
+ conflict_block: proc do |value|
194
+ UI.user_error!("You can't use 'hap' and '#{value.key}' options in one run")
195
+ end),
196
+ FastlaneCore::ConfigItem.new(key: :password,
197
+ env_name: "PGYER_PASSWORD",
198
+ description: "Set password to protect app",
199
+ optional: true,
200
+ type: String),
201
+
202
+ FastlaneCore::ConfigItem.new(key: :user_download_file_name,
203
+ env_name: "USER_DOWNLOAD_FILE_NAME",
204
+ description: "Rename the file name that user downloaded from pgyer",
205
+ optional: true,
206
+ type: String),
207
+
208
+ FastlaneCore::ConfigItem.new(key: :update_description,
209
+ env_name: "PGYER_UPDATE_DESCRIPTION",
210
+ description: "Set update description for app",
211
+ optional: true,
212
+ type: String),
213
+
214
+ FastlaneCore::ConfigItem.new(key: :save_uploaded_info_json,
215
+ env_name: "PGYER_SAVE_UPLOADED_INFO_JSON",
216
+ description: "Save uploaded info json to file named pgyer-fastlane-uploaded-app-info.json",
217
+ optional: true,
218
+ default_value: false,
219
+ type: Boolean),
220
+
221
+ FastlaneCore::ConfigItem.new(key: :install_type,
222
+ env_name: "PGYER_INSTALL_TYPE",
223
+ description: "Set install type for app (1=public, 2=password, 3=invite). Please set as a string",
224
+ optional: true,
225
+ type: String),
226
+ FastlaneCore::ConfigItem.new(key: :install_date,
227
+ env_name: "PGYER_INSTALL_DATE",
228
+ description: "Set install type for app (1=Set valid time, 2=Long-term effective, other=Do not modify the last setting). Please set as a string",
229
+ optional: true,
230
+ type: String),
231
+ FastlaneCore::ConfigItem.new(key: :install_start_date,
232
+ env_name: "PGYER_INSTALL_START_DATE",
233
+ description: "The value is a string of characters, for example, 2018-01-01",
234
+ optional: true,
235
+ type: String),
236
+
237
+ FastlaneCore::ConfigItem.new(key: :install_end_date,
238
+ env_name: "PGYER_INSTALL_END_DATE",
239
+ description: "The value is a string of characters, such as 2018-12-31",
240
+ optional: true,
241
+ type: String),
242
+
243
+ FastlaneCore::ConfigItem.new(key: :oversea,
244
+ env_name: "PGYER_OVERSEA",
245
+ description: "Whether to use overseas acceleration. 1 for overseas accelerated upload, 0 for domestic accelerated upload, not filled in for automatic judgment based on IP",
246
+ optional: true,
247
+ type: Numeric),
248
+ FastlaneCore::ConfigItem.new(key: :channel,
249
+ env_name: "PGYER_SPECIFIED_CHANNEL",
250
+ description: "Need to update the specified channel of the download short link, can specify only one channel, string type, such as: ABCD",
251
+ optional: true,
252
+ type: String),
253
+ FastlaneCore::ConfigItem.new(key: :doh,
254
+ env_name: "PGYER_DOH",
255
+ description: "Use DNS over HTTPS (DoH) to resolve domain names, bypassing local DNS. Experimental feature",
256
+ optional: true,
257
+ default_value: false,
258
+ type: Boolean),
259
+ ]
260
+ end
261
+
262
+ def self.is_supported?(platform)
263
+ # Adjust this if your plugin only works for a particular platform (iOS vs. Android, for example)
264
+ # See: https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Platforms.md
265
+ #
266
+ [:ios, :mac, :android].include?(platform)
267
+ true
268
+ end
269
+
270
+ private
271
+
272
+ def self.select_available_api_host(conn_options, request_params, use_doh = false)
273
+ # 如果启用 DoH,直接使用 DoH 解析 www.pgyer.com,跳过三个域名测试
274
+ if use_doh
275
+ return select_api_host_via_doh(conn_options, request_params)
276
+ end
277
+
278
+ # 尝试多个域名,防止 DNS 劫持
279
+ api_hosts = [
280
+ "https://www.pgyer.com/apiv2/app",
281
+ "https://www.xcxwo.com/apiv2/app",
282
+ "https://www.pgyerapp.com/apiv2/app",
283
+ ]
284
+
285
+ api_host = nil
286
+ pgyer_client = nil
287
+ info = nil
288
+
289
+ api_hosts.each_with_index do |host, index|
290
+ UI.message "尝试使用域名 #{index + 1}/#{api_hosts.length}: #{host}"
291
+
292
+ begin
293
+ # 为域名检测设置 5 秒超时
294
+ test_conn_options = {
295
+ request: {
296
+ timeout: 5,
297
+ open_timeout: 5,
298
+ },
299
+ }
300
+ test_client = create_faraday_client(test_conn_options, nil)
301
+
302
+ # 使用超时控制,5 秒后如果还没响应就放弃
303
+ test_response = nil
304
+ test_info = nil
305
+
306
+ begin
307
+ Timeout.timeout(5) do
308
+ test_response = test_client.post "#{host}/getCOSToken", request_params
309
+ test_info = test_response.body
310
+ end
311
+ rescue Timeout::Error
312
+ UI.message "域名 #{host} 请求超时(5秒),切换到下一个域名"
313
+ next
314
+ end
315
+
316
+ # 检查响应是否有效:如果返回了有效的 JSON 且包含 code 字段,说明域名解析正常
317
+ if test_info && test_info.is_a?(Hash) && test_info.key?("code")
318
+ if test_info["code"] == 0
319
+ # API 调用成功,使用该域名
320
+ # 使用原始的超时设置重新创建客户端(用于后续上传)
321
+ api_host = host
322
+ pgyer_client = create_faraday_client(conn_options, nil)
323
+ info = test_info
324
+ UI.success "成功使用域名: #{host}"
325
+ break
326
+ else
327
+ # API 返回了业务错误,但域名解析正常
328
+ # 保存第一个可用的域名作为备选,继续尝试其他域名看是否有返回 code == 0 的
329
+ if api_host.nil?
330
+ api_host = host
331
+ # 使用原始的超时设置重新创建客户端
332
+ pgyer_client = create_faraday_client(conn_options, nil)
333
+ info = test_info
334
+ UI.message "域名 #{host} 解析正常,但 API 返回错误,保存为备选: #{test_info}"
335
+ else
336
+ UI.message "域名 #{host} 可用,但 API 返回错误: #{test_info}"
337
+ end
338
+ end
339
+ else
340
+ # 响应格式不正确,可能是 DNS 劫持
341
+ UI.message "域名 #{host} 返回的响应格式不正确,可能遭遇 DNS 劫持"
342
+ end
343
+ rescue => e
344
+ UI.message "域名 #{host} 测试失败(可能是 DNS 劫持或超时): #{e.message}"
345
+ next
346
+ end
347
+ end
348
+
349
+ if api_host.nil? || pgyer_client.nil? || info.nil?
350
+ UI.user_error!("所有域名都无法使用,可能都遭遇了 DNS 劫持。尝试的域名: #{api_hosts.join(', ')}")
351
+ end
352
+
353
+ return api_host, pgyer_client, info
354
+ end
355
+
356
+ # DoH 模式:使用 DoH 解析 www.pgyer.com(实验性功能)
357
+ def self.select_api_host_via_doh(conn_options, request_params)
358
+ UI.message "启用 DoH 模式,使用 DoH 解析 www.pgyer.com"
359
+
360
+ host = "https://www.pgyer.com/apiv2/app"
361
+ domain = "www.pgyer.com"
362
+
363
+ # 通过 DoH 解析域名
364
+ ip_address = resolve_domain_via_doh(domain)
365
+
366
+ if ip_address.nil?
367
+ UI.user_error!("DoH 解析失败,无法获取 www.pgyer.com 的 IP 地址")
368
+ end
369
+
370
+ UI.message "DoH 解析成功: #{domain} -> #{ip_address}"
371
+
372
+ # 使用 IP 地址替换域名,但保留 Host header
373
+ actual_url = host.gsub(domain, ip_address)
374
+ host_header = domain
375
+
376
+ begin
377
+ test_client = create_faraday_client(conn_options, host_header)
378
+
379
+ # 设置 Host header
380
+ request_headers = { "Host" => host_header }
381
+
382
+ test_response = test_client.post "#{actual_url}/getCOSToken", request_params, request_headers
383
+ test_info = test_response.body
384
+
385
+ if test_info && test_info.is_a?(Hash) && test_info.key?("code")
386
+ if test_info["code"] == 0
387
+ UI.success "DoH 模式:成功使用域名 #{host}"
388
+ return host, test_client, test_info
389
+ else
390
+ UI.user_error!("DoH 模式:API 返回错误: #{test_info}")
391
+ end
392
+ else
393
+ UI.user_error!("DoH 模式:返回的响应格式不正确: #{test_info}")
394
+ end
395
+ rescue => e
396
+ UI.user_error!("DoH 模式:请求失败: #{e.message}")
397
+ end
398
+ end
399
+
400
+ # DoH 相关方法(实验性功能)
401
+ def self.resolve_domain_via_doh(domain)
402
+ # 使用阿里云公共 DoH 服务
403
+ doh_url = "https://dns.alidns.com/resolve"
404
+
405
+ begin
406
+ doh_client = Faraday.new do |c|
407
+ c.request :url_encoded
408
+ c.response :json
409
+ c.adapter :net_http
410
+ end
411
+
412
+ response = doh_client.get(doh_url, { name: domain, type: "A" })
413
+ result = response.body
414
+
415
+ if result && result.is_a?(Hash) && result["Answer"]
416
+ # 获取第一个 A 记录
417
+ a_record = result["Answer"].find { |r| r["type"] == 1 }
418
+ if a_record && a_record["data"]
419
+ return a_record["data"]
420
+ end
421
+ end
422
+ rescue => e
423
+ UI.message "DoH 解析出错: #{e.message}"
424
+ end
425
+
426
+ return nil
427
+ end
428
+
429
+ def self.extract_domain_from_url(url)
430
+ # 从 URL 中提取域名
431
+ # 例如: https://www.pgyer.com/apiv2/app -> www.pgyer.com
432
+ uri = URI.parse(url)
433
+ return uri.host
434
+ end
435
+
436
+ def self.create_faraday_client(conn_options, host_header = nil)
437
+ # 创建基础的 Faraday 客户端
438
+ # Host header 将在请求时通过 headers 参数设置
439
+ # DoH 模式下禁用 SSL 证书验证(因为使用 IP 地址,证书绑定域名)
440
+ final_options = conn_options.dup
441
+
442
+ Faraday.new(nil, final_options) do |c|
443
+ c.request :multipart
444
+ c.request :url_encoded
445
+ c.response :json, content_type: /\bjson$/
446
+ c.adapter :net_http do |http|
447
+ if host_header
448
+ # DoH 模式:使用 IP 地址,禁用 SSL 证书验证
449
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
450
+ UI.message "DoH 模式:已禁用 SSL 证书验证(实验性功能)"
451
+ else
452
+ # 普通模式:使用默认 SSL 验证
453
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
454
+ end
455
+ end
456
+ end
457
+ end
458
+
459
+ def self.get_type(params)
460
+ type = params[:ipa].nil? ? "android" : "ios"
461
+
462
+ if !params[:hap].nil?
463
+ type = "hap"
464
+ end
465
+
466
+ type
467
+ end
468
+
469
+ def self.checkPublishStatus(client, api_host, api_key, buildKey, doh_info = nil)
470
+ # URL 保持使用域名(用于显示)
471
+ url = "#{api_host}/buildInfo"
472
+ UI.message "checkPublishStatus url: #{url}"
473
+
474
+ # 如果使用 DoH,实际请求时使用 IP 地址,但设置 Host header
475
+ actual_url = api_host
476
+ request_headers = {}
477
+
478
+ if doh_info && doh_info[:ip_address]
479
+ # 实际连接使用 IP 地址
480
+ actual_url = api_host.gsub(doh_info[:domain], doh_info[:ip_address])
481
+ # 设置 Host header 为原始域名
482
+ request_headers["Host"] = doh_info[:domain]
483
+ UI.message "DoH 模式:实际连接使用 IP 地址 #{doh_info[:ip_address]},Host header: #{doh_info[:domain]}"
484
+ end
485
+
486
+ response = client.post "#{actual_url}/buildInfo", { :_api_key => api_key, :buildKey => buildKey }, request_headers
487
+ info = response.body
488
+ code = info["code"]
489
+ if code == 0
490
+ UI.success "Upload success. BuildInfo is #{info["data"]}."
491
+ shortUrl = info["data"]["buildShortcutUrl"]
492
+ if shortUrl.nil? || shortUrl == ""
493
+ shortUrl = info["data"]["buildKey"]
494
+ end
495
+ info["data"]["fastlaneAddedWholeVisitUrl"] = "https://www.pgyer.com/#{shortUrl}"
496
+ UI.success "Upload success. Visit this URL to see: #{info["data"]["fastlaneAddedWholeVisitUrl"]}"
497
+ return info["data"]
498
+ elsif code == 1246 || code == 1247
499
+ sleep 3
500
+ self.checkPublishStatus(client, api_host, api_key, buildKey, doh_info)
501
+ else
502
+ UI.user_error!("PGYER Plugin Published Error: #{info} buildKey: #{buildKey}")
503
+ end
504
+ end
505
+ end
506
+ end
507
+ end
@@ -0,0 +1,16 @@
1
+ require "fastlane_core/ui/ui"
2
+
3
+ module Fastlane
4
+ UI = FastlaneCore::UI unless Fastlane.const_defined?("UI")
5
+
6
+ module Helper
7
+ class PgyerHelper
8
+ # class methods that you define here become available in your action
9
+ # as `Helper::PgyerHelper.your_method`
10
+ #
11
+ def self.show_message
12
+ UI.message("Hello from the pgyer plugin helper!")
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1 @@
1
+ require "fastlane/plugin/pgyer"
@@ -0,0 +1,5 @@
1
+ module Fastlane
2
+ module Pgyer
3
+ VERSION = "0.3.5"
4
+ end
5
+ end
@@ -0,0 +1,16 @@
1
+ require 'fastlane/plugin/pgyer/version'
2
+
3
+ module Fastlane
4
+ module Pgyer
5
+ # Return all .rb files inside the "actions" and "helper" directory
6
+ def self.all_classes
7
+ Dir[File.expand_path('**/{actions,helper}/*.rb', File.dirname(__FILE__))]
8
+ end
9
+ end
10
+ end
11
+
12
+ # By default we want to import all available actions and helpers
13
+ # A plugin can contain any number of actions and plugins
14
+ Fastlane::Pgyer.all_classes.each do |current|
15
+ require current
16
+ end
@@ -0,0 +1,11 @@
1
+ require "fastlane/plugin/pgyer"
2
+
3
+ module Fastlane
4
+ module Pgyersws
5
+ VERSION = Fastlane::Pgyer::VERSION
6
+
7
+ def self.all_classes
8
+ Fastlane::Pgyer.all_classes
9
+ end
10
+ end
11
+ end
metadata ADDED
@@ -0,0 +1,226 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fastlane-plugin-pgyersws
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.5
5
+ platform: ruby
6
+ authors:
7
+ - rexshi
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: faraday
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '1.10'
19
+ - - "<"
20
+ - !ruby/object:Gem::Version
21
+ version: '3.0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ version: '1.10'
29
+ - - "<"
30
+ - !ruby/object:Gem::Version
31
+ version: '3.0'
32
+ - !ruby/object:Gem::Dependency
33
+ name: faraday-multipart
34
+ requirement: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - ">="
37
+ - !ruby/object:Gem::Version
38
+ version: '1.0'
39
+ - - "<"
40
+ - !ruby/object:Gem::Version
41
+ version: '3.0'
42
+ type: :runtime
43
+ prerelease: false
44
+ version_requirements: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '1.0'
49
+ - - "<"
50
+ - !ruby/object:Gem::Version
51
+ version: '3.0'
52
+ - !ruby/object:Gem::Dependency
53
+ name: bundler
54
+ requirement: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: '0'
59
+ type: :development
60
+ prerelease: false
61
+ version_requirements: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ - !ruby/object:Gem::Dependency
67
+ name: fastlane
68
+ requirement: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: 2.208.0
73
+ type: :development
74
+ prerelease: false
75
+ version_requirements: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: 2.208.0
80
+ - !ruby/object:Gem::Dependency
81
+ name: pry
82
+ requirement: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ type: :development
88
+ prerelease: false
89
+ version_requirements: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: rake
96
+ requirement: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ type: :development
102
+ prerelease: false
103
+ version_requirements: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ - !ruby/object:Gem::Dependency
109
+ name: rspec
110
+ requirement: !ruby/object:Gem::Requirement
111
+ requirements:
112
+ - - ">="
113
+ - !ruby/object:Gem::Version
114
+ version: '0'
115
+ type: :development
116
+ prerelease: false
117
+ version_requirements: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ - !ruby/object:Gem::Dependency
123
+ name: rspec_junit_formatter
124
+ requirement: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ version: '0'
129
+ type: :development
130
+ prerelease: false
131
+ version_requirements: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - ">="
134
+ - !ruby/object:Gem::Version
135
+ version: '0'
136
+ - !ruby/object:Gem::Dependency
137
+ name: rubocop
138
+ requirement: !ruby/object:Gem::Requirement
139
+ requirements:
140
+ - - '='
141
+ - !ruby/object:Gem::Version
142
+ version: 1.12.1
143
+ type: :development
144
+ prerelease: false
145
+ version_requirements: !ruby/object:Gem::Requirement
146
+ requirements:
147
+ - - '='
148
+ - !ruby/object:Gem::Version
149
+ version: 1.12.1
150
+ - !ruby/object:Gem::Dependency
151
+ name: rubocop-performance
152
+ requirement: !ruby/object:Gem::Requirement
153
+ requirements:
154
+ - - ">="
155
+ - !ruby/object:Gem::Version
156
+ version: '0'
157
+ type: :development
158
+ prerelease: false
159
+ version_requirements: !ruby/object:Gem::Requirement
160
+ requirements:
161
+ - - ">="
162
+ - !ruby/object:Gem::Version
163
+ version: '0'
164
+ - !ruby/object:Gem::Dependency
165
+ name: rubocop-require_tools
166
+ requirement: !ruby/object:Gem::Requirement
167
+ requirements:
168
+ - - ">="
169
+ - !ruby/object:Gem::Version
170
+ version: '0'
171
+ type: :development
172
+ prerelease: false
173
+ version_requirements: !ruby/object:Gem::Requirement
174
+ requirements:
175
+ - - ">="
176
+ - !ruby/object:Gem::Version
177
+ version: '0'
178
+ - !ruby/object:Gem::Dependency
179
+ name: simplecov
180
+ requirement: !ruby/object:Gem::Requirement
181
+ requirements:
182
+ - - ">="
183
+ - !ruby/object:Gem::Version
184
+ version: '0'
185
+ type: :development
186
+ prerelease: false
187
+ version_requirements: !ruby/object:Gem::Requirement
188
+ requirements:
189
+ - - ">="
190
+ - !ruby/object:Gem::Version
191
+ version: '0'
192
+ email: rexshi@pgyer.com
193
+ executables: []
194
+ extensions: []
195
+ extra_rdoc_files: []
196
+ files:
197
+ - LICENSE
198
+ - README.md
199
+ - lib/fastlane/plugin/pgyer.rb
200
+ - lib/fastlane/plugin/pgyer/actions/pgyer_action.rb
201
+ - lib/fastlane/plugin/pgyer/helper/pgyer_helper.rb
202
+ - lib/fastlane/plugin/pgyer/sws.rb
203
+ - lib/fastlane/plugin/pgyer/version.rb
204
+ - lib/fastlane/plugin/pgyersws.rb
205
+ homepage: https://github.com/shishirui/fastlane-plugin-pgyer
206
+ licenses:
207
+ - MIT
208
+ metadata: {}
209
+ rdoc_options: []
210
+ require_paths:
211
+ - lib
212
+ required_ruby_version: !ruby/object:Gem::Requirement
213
+ requirements:
214
+ - - ">="
215
+ - !ruby/object:Gem::Version
216
+ version: '2.6'
217
+ required_rubygems_version: !ruby/object:Gem::Requirement
218
+ requirements:
219
+ - - ">="
220
+ - !ruby/object:Gem::Version
221
+ version: '0'
222
+ requirements: []
223
+ rubygems_version: 4.0.14
224
+ specification_version: 4
225
+ summary: distribute app to pgyer beta testing service
226
+ test_files: []