fastlane-plugin-pgyer 0.3.1 → 0.3.3

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: 741284528b903fbc1a290128b51cbed6edaa428d734da99514a3e15e353d0534
4
- data.tar.gz: 6fe64045b3a0fecb3da47ed3cc8a7d3915736a0e85f47a4bcb5386f9a4a19373
3
+ metadata.gz: 76598ee4c500e69dc2628a463315f2b45ece7db0bebca38e26b65a9d8f3413cc
4
+ data.tar.gz: b2df8a60da19d648c964dbd92da17a50edd471a5343867afb6be2c56553dd721
5
5
  SHA512:
6
- metadata.gz: 94cef84dc0babdac14f62ed7d4ea53a73ff72bf788f463517743c921a427c30c8c6fc82a69ce4fbb5c0f19e922d457dedd5c4e31baed3402323b8960ae3005a7
7
- data.tar.gz: 4eeef611fdc5c90845577d22d7f1f541598313a19776c459c032ea483d3c1f4b3a41ef05700facad77f82f5193491693668488c8c7202e9db21d9d2ebc747397
6
+ metadata.gz: 60302c4a4e3c101df47ed239c8c2a012fe161a3535d70ceb4f9047b94bd1e7f312eb8eb8e5b5f0fe39deae53b43299d312d8dc0c9d87f883911fc1e2379d94e3
7
+ data.tar.gz: 27415b535fa5f9110ecf86f145aa8499c8290c2abf3bf215175e1a31da517e28f384843e7caaafce192d2fc448d0f1034a6ef6207960b447b07f59890b977883
@@ -1,5 +1,6 @@
1
1
  require "faraday"
2
2
  require "faraday_middleware"
3
+ require "timeout"
3
4
 
4
5
  module Fastlane
5
6
  module Actions
@@ -74,18 +75,9 @@ module Fastlane
74
75
  },
75
76
  }
76
77
 
77
- api_host = "https://www.pgyer.com/apiv2/app"
78
-
79
- pgyer_client = Faraday.new(nil, conn_options) do |c|
80
- c.request :multipart
81
- c.request :url_encoded
82
- c.response :json, content_type: /\bjson$/
83
- c.adapter :net_http
84
- end
85
-
86
- response = pgyer_client.post "#{api_host}/getCOSToken", request_params
87
-
88
- info = response.body
78
+ # 选择可用的 API 域名
79
+ use_doh = params[:doh] == true
80
+ api_host, pgyer_client, info = self.select_available_api_host(conn_options, request_params, use_doh)
89
81
 
90
82
  if info["code"] != 0
91
83
  UI.user_error!("Get token is failed, info: #{info}")
@@ -121,7 +113,16 @@ module Fastlane
121
113
  UI.user_error!("PGYER Plugin Upload Error: #{response.body}")
122
114
  end
123
115
 
124
- answer = self.checkPublishStatus(pgyer_client, api_host, api_key, key)
116
+ # 如果使用 DoH,需要传递 DoH 相关信息
117
+ doh_info = nil
118
+ if use_doh && api_host.include?("www.pgyer.com")
119
+ domain = "www.pgyer.com"
120
+ ip_address = resolve_domain_via_doh(domain)
121
+ if ip_address
122
+ doh_info = { domain: domain, ip_address: ip_address }
123
+ end
124
+ end
125
+ answer = self.checkPublishStatus(pgyer_client, api_host, api_key, key, doh_info)
125
126
 
126
127
  if params[:save_uploaded_info_json]
127
128
  File.open("pgyer-fastlane-uploaded-app-info.json", "w") do |f|
@@ -167,17 +168,6 @@ module Fastlane
167
168
  conflict_block: proc do |value|
168
169
  UI.user_error!("You can't use 'apk' and '#{value.key}' options in one run")
169
170
  end),
170
- FastlaneCore::ConfigItem.new(key: :hap,
171
- env_name: "PGYER_HAP",
172
- description: "Path to your HAP file",
173
- optional: true,
174
- verify_block: proc do |value|
175
- UI.user_error!("Couldn't find hap file at path '#{value}'") unless File.exist?(value)
176
- end,
177
- conflicting_options: [:ipa, :apk],
178
- conflict_block: proc do |value|
179
- UI.user_error!("You can't use 'hap' and '#{value.key}' options in one run")
180
- end),
181
171
  FastlaneCore::ConfigItem.new(key: :ipa,
182
172
  env_name: "PGYER_IPA",
183
173
  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",
@@ -192,7 +182,7 @@ module Fastlane
192
182
  end),
193
183
  FastlaneCore::ConfigItem.new(key: :hap,
194
184
  env_name: "PGYER_HAP",
195
- description: "Path to your HAP file. Optional if you use the _gym_ or _xcodebuild_ action. For Mac zip the .app. For Android provide path to .apk file",
185
+ description: "Path to your HAP file",
196
186
  default_value: Actions.lane_context[:HVIGOR_HAP_OUTPUT_PATH],
197
187
  optional: true,
198
188
  verify_block: proc do |value|
@@ -259,6 +249,12 @@ module Fastlane
259
249
  description: "Need to update the specified channel of the download short link, can specify only one channel, string type, such as: ABCD",
260
250
  optional: true,
261
251
  type: String),
252
+ FastlaneCore::ConfigItem.new(key: :doh,
253
+ env_name: "PGYER_DOH",
254
+ description: "Use DNS over HTTPS (DoH) to resolve domain names, bypassing local DNS. Experimental feature",
255
+ optional: true,
256
+ default_value: false,
257
+ type: Boolean),
262
258
  ]
263
259
  end
264
260
 
@@ -272,6 +268,193 @@ module Fastlane
272
268
 
273
269
  private
274
270
 
271
+ def self.select_available_api_host(conn_options, request_params, use_doh = false)
272
+ # 如果启用 DoH,直接使用 DoH 解析 www.pgyer.com,跳过三个域名测试
273
+ if use_doh
274
+ return select_api_host_via_doh(conn_options, request_params)
275
+ end
276
+
277
+ # 尝试多个域名,防止 DNS 劫持
278
+ api_hosts = [
279
+ "https://www.pgyer.com/apiv2/app",
280
+ "https://www.xcxwo.com/apiv2/app",
281
+ "https://www.pgyerapp.com/apiv2/app",
282
+ ]
283
+
284
+ api_host = nil
285
+ pgyer_client = nil
286
+ info = nil
287
+
288
+ api_hosts.each_with_index do |host, index|
289
+ UI.message "尝试使用域名 #{index + 1}/#{api_hosts.length}: #{host}"
290
+
291
+ begin
292
+ # 为域名检测设置 5 秒超时
293
+ test_conn_options = {
294
+ request: {
295
+ timeout: 5,
296
+ open_timeout: 5,
297
+ },
298
+ }
299
+ test_client = create_faraday_client(test_conn_options, nil)
300
+
301
+ # 使用超时控制,5 秒后如果还没响应就放弃
302
+ test_response = nil
303
+ test_info = nil
304
+
305
+ begin
306
+ Timeout.timeout(5) do
307
+ test_response = test_client.post "#{host}/getCOSToken", request_params
308
+ test_info = test_response.body
309
+ end
310
+ rescue Timeout::Error
311
+ UI.message "域名 #{host} 请求超时(5秒),切换到下一个域名"
312
+ next
313
+ end
314
+
315
+ # 检查响应是否有效:如果返回了有效的 JSON 且包含 code 字段,说明域名解析正常
316
+ if test_info && test_info.is_a?(Hash) && test_info.key?("code")
317
+ if test_info["code"] == 0
318
+ # API 调用成功,使用该域名
319
+ # 使用原始的超时设置重新创建客户端(用于后续上传)
320
+ api_host = host
321
+ pgyer_client = create_faraday_client(conn_options, nil)
322
+ info = test_info
323
+ UI.success "成功使用域名: #{host}"
324
+ break
325
+ else
326
+ # API 返回了业务错误,但域名解析正常
327
+ # 保存第一个可用的域名作为备选,继续尝试其他域名看是否有返回 code == 0 的
328
+ if api_host.nil?
329
+ api_host = host
330
+ # 使用原始的超时设置重新创建客户端
331
+ pgyer_client = create_faraday_client(conn_options, nil)
332
+ info = test_info
333
+ UI.message "域名 #{host} 解析正常,但 API 返回错误,保存为备选: #{test_info}"
334
+ else
335
+ UI.message "域名 #{host} 可用,但 API 返回错误: #{test_info}"
336
+ end
337
+ end
338
+ else
339
+ # 响应格式不正确,可能是 DNS 劫持
340
+ UI.message "域名 #{host} 返回的响应格式不正确,可能遭遇 DNS 劫持"
341
+ end
342
+ rescue => e
343
+ UI.message "域名 #{host} 测试失败(可能是 DNS 劫持或超时): #{e.message}"
344
+ next
345
+ end
346
+ end
347
+
348
+ if api_host.nil? || pgyer_client.nil? || info.nil?
349
+ UI.user_error!("所有域名都无法使用,可能都遭遇了 DNS 劫持。尝试的域名: #{api_hosts.join(', ')}")
350
+ end
351
+
352
+ return api_host, pgyer_client, info
353
+ end
354
+
355
+ # DoH 模式:使用 DoH 解析 www.pgyer.com(实验性功能)
356
+ def self.select_api_host_via_doh(conn_options, request_params)
357
+ UI.message "启用 DoH 模式,使用 DoH 解析 www.pgyer.com"
358
+
359
+ host = "https://www.pgyer.com/apiv2/app"
360
+ domain = "www.pgyer.com"
361
+
362
+ # 通过 DoH 解析域名
363
+ ip_address = resolve_domain_via_doh(domain)
364
+
365
+ if ip_address.nil?
366
+ UI.user_error!("DoH 解析失败,无法获取 www.pgyer.com 的 IP 地址")
367
+ end
368
+
369
+ UI.message "DoH 解析成功: #{domain} -> #{ip_address}"
370
+
371
+ # 使用 IP 地址替换域名,但保留 Host header
372
+ actual_url = host.gsub(domain, ip_address)
373
+ host_header = domain
374
+
375
+ begin
376
+ test_client = create_faraday_client(conn_options, host_header)
377
+
378
+ # 设置 Host header
379
+ request_headers = { "Host" => host_header }
380
+
381
+ test_response = test_client.post "#{actual_url}/getCOSToken", request_params, request_headers
382
+ test_info = test_response.body
383
+
384
+ if test_info && test_info.is_a?(Hash) && test_info.key?("code")
385
+ if test_info["code"] == 0
386
+ UI.success "DoH 模式:成功使用域名 #{host}"
387
+ return host, test_client, test_info
388
+ else
389
+ UI.user_error!("DoH 模式:API 返回错误: #{test_info}")
390
+ end
391
+ else
392
+ UI.user_error!("DoH 模式:返回的响应格式不正确: #{test_info}")
393
+ end
394
+ rescue => e
395
+ UI.user_error!("DoH 模式:请求失败: #{e.message}")
396
+ end
397
+ end
398
+
399
+ # DoH 相关方法(实验性功能)
400
+ def self.resolve_domain_via_doh(domain)
401
+ # 使用阿里云公共 DoH 服务
402
+ doh_url = "https://dns.alidns.com/resolve"
403
+
404
+ begin
405
+ doh_client = Faraday.new do |c|
406
+ c.request :url_encoded
407
+ c.response :json
408
+ c.adapter :net_http
409
+ end
410
+
411
+ response = doh_client.get(doh_url, { name: domain, type: "A" })
412
+ result = response.body
413
+
414
+ if result && result.is_a?(Hash) && result["Answer"]
415
+ # 获取第一个 A 记录
416
+ a_record = result["Answer"].find { |r| r["type"] == 1 }
417
+ if a_record && a_record["data"]
418
+ return a_record["data"]
419
+ end
420
+ end
421
+ rescue => e
422
+ UI.message "DoH 解析出错: #{e.message}"
423
+ end
424
+
425
+ return nil
426
+ end
427
+
428
+ def self.extract_domain_from_url(url)
429
+ # 从 URL 中提取域名
430
+ # 例如: https://www.pgyer.com/apiv2/app -> www.pgyer.com
431
+ uri = URI.parse(url)
432
+ return uri.host
433
+ end
434
+
435
+ def self.create_faraday_client(conn_options, host_header = nil)
436
+ # 创建基础的 Faraday 客户端
437
+ # Host header 将在请求时通过 headers 参数设置
438
+ # DoH 模式下禁用 SSL 证书验证(因为使用 IP 地址,证书绑定域名)
439
+ final_options = conn_options.dup
440
+
441
+ Faraday.new(nil, final_options) do |c|
442
+ c.request :multipart
443
+ c.request :url_encoded
444
+ c.response :json, content_type: /\bjson$/
445
+ c.adapter :net_http do |http|
446
+ if host_header
447
+ # DoH 模式:使用 IP 地址,禁用 SSL 证书验证
448
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
449
+ UI.message "DoH 模式:已禁用 SSL 证书验证(实验性功能)"
450
+ else
451
+ # 普通模式:使用默认 SSL 验证
452
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
453
+ end
454
+ end
455
+ end
456
+ end
457
+
275
458
  def self.get_type(params)
276
459
  type = params[:ipa].nil? ? "android" : "ios"
277
460
 
@@ -282,10 +465,24 @@ module Fastlane
282
465
  type
283
466
  end
284
467
 
285
- def self.checkPublishStatus(client, api_host, api_key, buildKey)
286
- url ="#{api_host}/buildInfo"
468
+ def self.checkPublishStatus(client, api_host, api_key, buildKey, doh_info = nil)
469
+ # URL 保持使用域名(用于显示)
470
+ url = "#{api_host}/buildInfo"
287
471
  UI.message "checkPublishStatus url: #{url}"
288
- response = client.post "#{api_host}/buildInfo", { :_api_key => api_key, :buildKey => buildKey }
472
+
473
+ # 如果使用 DoH,实际请求时使用 IP 地址,但设置 Host header
474
+ actual_url = api_host
475
+ request_headers = {}
476
+
477
+ if doh_info && doh_info[:ip_address]
478
+ # 实际连接使用 IP 地址
479
+ actual_url = api_host.gsub(doh_info[:domain], doh_info[:ip_address])
480
+ # 设置 Host header 为原始域名
481
+ request_headers["Host"] = doh_info[:domain]
482
+ UI.message "DoH 模式:实际连接使用 IP 地址 #{doh_info[:ip_address]},Host header: #{doh_info[:domain]}"
483
+ end
484
+
485
+ response = client.post "#{actual_url}/buildInfo", { :_api_key => api_key, :buildKey => buildKey }, request_headers
289
486
  info = response.body
290
487
  code = info["code"]
291
488
  if code == 0
@@ -299,7 +496,7 @@ module Fastlane
299
496
  return info["data"]
300
497
  elsif code == 1246 || code == 1247
301
498
  sleep 3
302
- self.checkPublishStatus(client, api_host, api_key, buildKey)
499
+ self.checkPublishStatus(client, api_host, api_key, buildKey, doh_info)
303
500
  else
304
501
  UI.user_error!("PGYER Plugin Published Error: #{info} buildKey: #{buildKey}")
305
502
  end
@@ -1,5 +1,5 @@
1
1
  module Fastlane
2
2
  module Pgyer
3
- VERSION = "0.3.1"
3
+ VERSION = "0.3.3"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fastlane-plugin-pgyer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.3.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - rexshi
@@ -178,7 +178,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
178
178
  - !ruby/object:Gem::Version
179
179
  version: '0'
180
180
  requirements: []
181
- rubygems_version: 3.7.1
181
+ rubygems_version: 3.6.9
182
182
  specification_version: 4
183
183
  summary: distribute app to pgyer beta testing service
184
184
  test_files: []