pindo 5.18.17 → 5.19.1

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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/lib/pindo/base/git_handler.rb +44 -83
  3. data/lib/pindo/command/appstore/adhocbuild.rb +13 -2
  4. data/lib/pindo/command/appstore/autobuild.rb +2 -1
  5. data/lib/pindo/command/appstore/iap.rb +58 -54
  6. data/lib/pindo/command/repo/create.rb +1 -1
  7. data/lib/pindo/command/repo/search.rb +1 -1
  8. data/lib/pindo/command/utils/installskills.rb +2 -2
  9. data/lib/pindo/command/utils/renewproj.rb +0 -1
  10. data/lib/pindo/config/build_info_manager.rb +31 -0
  11. data/lib/pindo/config/ios_config_parser.rb +212 -140
  12. data/lib/pindo/module/android/android_build_helper.rb +39 -16
  13. data/lib/pindo/module/android/gradle_helper.rb +8 -14
  14. data/lib/pindo/module/appstore/appstore_in_app_purchase.rb +80 -56
  15. data/lib/pindo/module/cert/cert_helper.rb +2 -2
  16. data/lib/pindo/module/pgyer/pgyerhelper.rb +18 -20
  17. data/lib/pindo/module/task/model/build/android_build_dev_task.rb +1 -37
  18. data/lib/pindo/module/task/model/build/android_build_task.rb +40 -0
  19. data/lib/pindo/module/task/model/build/ios_build_adhoc_task.rb +9 -35
  20. data/lib/pindo/module/task/model/build/ios_build_appstore_task.rb +5 -5
  21. data/lib/pindo/module/task/model/build/ios_build_dev_task.rb +14 -4
  22. data/lib/pindo/module/task/model/build_task.rb +2 -0
  23. data/lib/pindo/module/task/model/git/git_commit_task.rb +11 -1
  24. data/lib/pindo/module/task/model/git/git_tag_task.rb +92 -77
  25. data/lib/pindo/module/task/model/jps/jps_workflow_message_task.rb +8 -10
  26. data/lib/pindo/module/task/model/unity/unity_export_task.rb +44 -0
  27. data/lib/pindo/module/task/pindo_task.rb +2 -2
  28. data/lib/pindo/module/task/task_manager.rb +14 -6
  29. data/lib/pindo/module/task/task_reporter.rb +45 -0
  30. data/lib/pindo/module/unity/nuget_helper.rb +5 -20
  31. data/lib/pindo/module/utils/git_repo_helper.rb +69 -36
  32. data/lib/pindo/module/xcode/ipa_resign_helper.rb +33 -62
  33. data/lib/pindo/module/xcode/res/xcode_res_handler.rb +25 -4
  34. data/lib/pindo/module/xcode/xcode_build_config.rb +7 -1
  35. data/lib/pindo/module/xcode/xcode_build_helper.rb +36 -4
  36. data/lib/pindo/module/xcode/xcode_res_helper.rb +8 -0
  37. data/lib/pindo/options/helpers/bundleid_selector.rb +23 -6
  38. data/lib/pindo/version.rb +1 -1
  39. metadata +2 -2
@@ -186,9 +186,9 @@ module Pindo
186
186
  begin
187
187
  response = appstore_client.create_in_app_purchase(
188
188
  name:purchase_item["reference_name"],
189
- product_id:purchase_item["product_id"],
190
- in_app_purchase_type:purchase_item["in_app_purchase_type"],
191
- review_note:purchase_item["review_note"],
189
+ productId:purchase_item["product_id"],
190
+ inAppPurchaseType:purchase_item["in_app_purchase_type"],
191
+ reviewNote:purchase_item["review_note"],
192
192
  relationships: {
193
193
  app:{
194
194
  data:{
@@ -228,7 +228,7 @@ module Pindo
228
228
  begin
229
229
  response = appstore_client.update_in_app_purchase(
230
230
  name:purchase_item["reference_name"],
231
- review_note:purchase_item["review_note"],
231
+ reviewNote:purchase_item["review_note"],
232
232
  id:in_app_purchase_id
233
233
  )
234
234
  rescue => err
@@ -255,22 +255,34 @@ module Pindo
255
255
  end
256
256
  end
257
257
 
258
- response = appstore_client.modify_in_app_purchase_territory_availablity(
259
- available_in_new_territories:true,
260
- relationships: {
261
- inAppPurchase:{
262
- data:{
263
- id: in_app_purchase_id,
264
- type: 'inAppPurchases'
265
- }
266
- },
267
- availableTerritories:{
268
- data:availableTerritories_data
269
- }
258
+ body = {
259
+ data: {
260
+ type: "inAppPurchaseAvailabilities",
261
+ attributes: {
262
+ availableInNewTerritories: true
263
+ },
264
+ relationships: {
265
+ inAppPurchase: {
266
+ data: {
267
+ id: in_app_purchase_id,
268
+ type: 'inAppPurchases'
270
269
  }
271
- )
270
+ },
271
+ availableTerritories: {
272
+ data: availableTerritories_data
273
+ }
274
+ }
275
+ }
276
+ }
272
277
 
273
- # puts JSON.pretty_generate(response)
278
+ begin
279
+ client = Spaceship::ConnectAPI
280
+ client.tunes_request_client.post("#{Spaceship::ConnectAPI::Tunes::API::Version::V1}/inAppPurchaseAvailabilities", body)
281
+ puts " Territory Availability 设置成功"
282
+ rescue => err
283
+ puts err
284
+ puts " 设置Territory Availability失败!!!"
285
+ end
274
286
 
275
287
  end
276
288
 
@@ -396,7 +408,7 @@ module Pindo
396
408
  if !price_id.nil?
397
409
  purchase_response = appstore_client.create_in_app_purchase_price_schedule(
398
410
  relationships: {
399
- manual_prices: {
411
+ manualPrices: {
400
412
  data: [
401
413
  {
402
414
  type: 'inAppPurchasePrices',
@@ -404,7 +416,7 @@ module Pindo
404
416
  }
405
417
  ]
406
418
  },
407
- in_app_purchase: {
419
+ inAppPurchase: {
408
420
  data: {
409
421
  type: 'inAppPurchases',
410
422
  id: in_app_purchase_id
@@ -488,8 +500,8 @@ module Pindo
488
500
 
489
501
  puts " 正在上传...."
490
502
  response_iap_screenshot = appstore_client.create_in_app_purchase_review_screenshot(
491
- file_name:filename,
492
- file_size: filesize,
503
+ fileName:filename,
504
+ fileSize: filesize,
493
505
  relationships: {
494
506
  inAppPurchaseV2:{
495
507
  data:{
@@ -519,7 +531,7 @@ module Pindo
519
531
  in_app_purchase = appstore_client.update_in_app_purchase_app_store_review_screenshot(
520
532
  id:upload_id,
521
533
  uploaded: true,
522
- source_file_checksum:checksum
534
+ sourceFileChecksum:checksum
523
535
  )
524
536
  puts " 上传完成!!"
525
537
 
@@ -632,7 +644,7 @@ module Pindo
632
644
 
633
645
  begin
634
646
  group_response = appstore_client.create_subscription_group(
635
- reference_name:subscription_group_item["group_reference_name"],
647
+ referenceName:subscription_group_item["group_reference_name"],
636
648
  relationships: {
637
649
  app:{
638
650
  data:{
@@ -684,7 +696,7 @@ module Pindo
684
696
  response = appstore_client.update_subscription_group_localization(
685
697
  id:locale_item["locale_id"],
686
698
  name:locale_item["group_display_name"],
687
- custom_app_name:locale_item["custom_app_name"]
699
+ customAppName:locale_item["custom_app_name"]
688
700
  )
689
701
  # puts JSON.pretty_generate(response)
690
702
 
@@ -693,7 +705,7 @@ module Pindo
693
705
  response = appstore_client.create_subscription_group_localization(
694
706
  name:locale_item["group_display_name"],
695
707
  locale:locale,
696
- custom_app_name:locale_item["custom_app_name"],
708
+ customAppName:locale_item["custom_app_name"],
697
709
  relationships: {
698
710
  subscriptionGroup:{
699
711
  data:{
@@ -881,11 +893,11 @@ module Pindo
881
893
  begin
882
894
  response = appstore_client.create_subscription(
883
895
  name:subscription_item["reference_name"],
884
- product_id:subscription_item["product_id"],
885
- family_sharable:false,
886
- review_note:subscription_item["review_note"],
887
- subscription_period:subscription_item["subscription_duration"],
888
- group_level:1,
896
+ productId:subscription_item["product_id"],
897
+ familySharable:false,
898
+ reviewNote:subscription_item["review_note"],
899
+ subscriptionPeriod:subscription_item["subscription_duration"],
900
+ groupLevel:1,
889
901
  relationships: {
890
902
  group:{
891
903
  data:{
@@ -928,9 +940,9 @@ module Pindo
928
940
  response = appstore_client.update_subscription(
929
941
  id:subscription_id,
930
942
  name:subscription_item["reference_name"],
931
- family_sharable:false,
932
- review_note:subscription_item["review_note"],
933
- group_level:1
943
+ familySharable:false,
944
+ reviewNote:subscription_item["review_note"],
945
+ groupLevel:1
934
946
  )
935
947
  # puts JSON.pretty_generate(response)
936
948
 
@@ -962,7 +974,7 @@ module Pindo
962
974
  puts " 设置订阅的周期: #{subscription_item["subscription_duration"]}"
963
975
  response = appstore_client.update_subscription(
964
976
  id:subscription_id,
965
- subscription_period:subscription_item["subscription_duration"]
977
+ subscriptionPeriod:subscription_item["subscription_duration"]
966
978
  )
967
979
  # puts JSON.pretty_generate(response)
968
980
 
@@ -998,22 +1010,34 @@ module Pindo
998
1010
  availableTerritories_data << new_territory_item
999
1011
  end
1000
1012
  end
1001
- response = appstore_client.modify_subscription_territory_availability(
1002
- available_in_new_territories:true,
1003
- relationships: {
1004
- subscription:{
1005
- data:{
1006
- id: subscription_id,
1007
- type: 'subscriptions'
1008
- }
1009
- },
1010
- availableTerritories:{
1011
- data:availableTerritories_data
1012
- }
1013
+ body = {
1014
+ data: {
1015
+ type: "subscriptionAvailabilities",
1016
+ attributes: {
1017
+ availableInNewTerritories: true
1018
+ },
1019
+ relationships: {
1020
+ subscription: {
1021
+ data: {
1022
+ id: subscription_id,
1023
+ type: 'subscriptions'
1013
1024
  }
1014
- )
1025
+ },
1026
+ availableTerritories: {
1027
+ data: availableTerritories_data
1028
+ }
1029
+ }
1030
+ }
1031
+ }
1015
1032
 
1016
- # puts JSON.pretty_generate(response)
1033
+ begin
1034
+ client = Spaceship::ConnectAPI
1035
+ client.tunes_request_client.post("#{Spaceship::ConnectAPI::Tunes::API::Version::V1}/subscriptionAvailabilities", body)
1036
+ puts " Territory Availability 设置成功"
1037
+ rescue => err
1038
+ puts err
1039
+ puts " 设置订阅的Territory Availability失败!!!"
1040
+ end
1017
1041
  end
1018
1042
 
1019
1043
 
@@ -1624,8 +1648,8 @@ module Pindo
1624
1648
 
1625
1649
  response = appstore_client.create_subscription_introductory_offer(
1626
1650
  duration:"THREE_DAYS",
1627
- number_of_periods:1,
1628
- offer_mode:'FREE_TRIAL',
1651
+ numberOfPeriods:1,
1652
+ offerMode:'FREE_TRIAL',
1629
1653
  relationships: {
1630
1654
  subscription: {
1631
1655
  data: {
@@ -1688,7 +1712,7 @@ module Pindo
1688
1712
  if !price_point_id.nil?
1689
1713
 
1690
1714
  respose_price = appstore_client.create_subscription_price(
1691
- preserve_current_price:true,
1715
+ preserveCurrentPrice:true,
1692
1716
  relationships: {
1693
1717
  subscription: {
1694
1718
  data: {
@@ -1733,7 +1757,7 @@ module Pindo
1733
1757
  puts " " + index_item.to_s + ".更新价格 地区:" + territory_id+ " 价格:" + price_local_item[:attributes][:customer_price]
1734
1758
 
1735
1759
  respose_price = appstore_client.create_subscription_price(
1736
- preserve_current_price:true,
1760
+ preserveCurrentPrice:true,
1737
1761
  relationships: {
1738
1762
  subscription: {
1739
1763
  data: {
@@ -1814,8 +1838,8 @@ module Pindo
1814
1838
 
1815
1839
  puts " 正在上传...."
1816
1840
  response_iap_screenshot = appstore_client.create_subscription_review_screenshot(
1817
- file_name:filename,
1818
- file_size: filesize,
1841
+ fileName:filename,
1842
+ fileSize: filesize,
1819
1843
  relationships: {
1820
1844
  subscription:{
1821
1845
  data:{
@@ -1848,7 +1872,7 @@ module Pindo
1848
1872
  in_app_purchase = appstore_client.update_subscription_review_screenshot(
1849
1873
  id:upload_id,
1850
1874
  uploaded: true,
1851
- source_file_checksum:checksum
1875
+ sourceFileChecksum:checksum
1852
1876
  )
1853
1877
  puts " 上传完成!!"
1854
1878
  # puts JSON.pretty_generate(in_app_purchase)
@@ -165,9 +165,9 @@ module Pindo
165
165
  puts "找到 #{identity_ids.size} 个证书:"
166
166
  puts identity_ids
167
167
 
168
- # 删除证书
168
+ # 删除证书(数组参数形式,避免 identity_id 拼接进 shell)
169
169
  identity_ids.each do |identity_id|
170
- system "security delete-certificate -Z #{identity_id}"
170
+ system("security", "delete-certificate", "-Z", identity_id)
171
171
  end
172
172
 
173
173
  # 清理 Provisioning Profiles
@@ -710,14 +710,10 @@ module Pindo
710
710
  FileUtils.rm_rf(ipa_file_upload)
711
711
  end
712
712
 
713
- current_dir = Dir.pwd
714
713
  if File.exist?(ipa_base_dir)
715
- Dir.chdir(ipa_base_dir)
716
714
  base_name = File.basename(mac_app_path)
717
- command = "zip -qry \"#{ipa_file_upload}\" \"#{base_name}\""
718
- puts command
719
- system command
720
- Dir.chdir(current_dir)
715
+ # 数组参数 + chdir,避免 shell 注入与 cwd 污染
716
+ system("zip", "-qry", ipa_file_upload, base_name, chdir: ipa_base_dir)
721
717
  end
722
718
  end
723
719
  if !ipa_file_upload.nil? &&
@@ -738,19 +734,18 @@ module Pindo
738
734
  rescue StandardError => e
739
735
 
740
736
  end
741
- current_project_dir = Dir.pwd
742
737
  web_build_name = web_build_name.downcase.strip.gsub(/[\s\-_]/, '')
743
738
  web_res_zip_fullname = File.join(web_zip_dir, web_build_name + "_" + web_build_version + ".zip")
744
739
  if File.exist?(web_res_zip_fullname)
745
740
  FileUtils.rm_rf(web_res_zip_fullname)
746
741
  end
747
742
  Zip::File.open(web_res_zip_fullname, Zip::File::CREATE) do |zipfile|
748
- Dir.chdir web_res_path
749
- Dir.glob("**/*").reject {|fn| File.directory?(fn) }.each do |file|
750
- zipfile.add(file.sub(web_res_path + '/', ''), file)
743
+ Dir.chdir(web_res_path) do
744
+ Dir.glob("**/*").reject {|fn| File.directory?(fn) }.each do |file|
745
+ zipfile.add(file.sub(web_res_path + '/', ''), file)
746
+ end
751
747
  end
752
748
  end
753
- Dir.chdir(current_project_dir)
754
749
  ipa_file_upload = web_res_zip_fullname
755
750
  end
756
751
  if !ipa_file_upload.nil? &&
@@ -767,12 +762,11 @@ module Pindo
767
762
  FileUtils.rm_rf(windows_zip_fullname)
768
763
  end
769
764
 
770
- current_project_dir = Dir.pwd
771
765
  Zip::File.open(windows_zip_fullname, Zip::File::CREATE) do |zipfile|
772
- Dir.chdir windows_build_dir
773
- zipfile.add(windows_exe_name, windows_exe_name)
766
+ Dir.chdir(windows_build_dir) do
767
+ zipfile.add(windows_exe_name, windows_exe_name)
768
+ end
774
769
  end
775
- Dir.chdir(current_project_dir)
776
770
  ipa_file_upload = windows_zip_fullname
777
771
  end
778
772
  unless !ipa_file_upload.nil? && File.exist?(ipa_file_upload)
@@ -800,14 +794,14 @@ module Pindo
800
794
  end
801
795
 
802
796
  Zip::File.open(server_zipfile_name, Zip::File::CREATE) do |zipfile|
803
- Dir.chdir server_file_directory
804
- Dir.glob("**/*").reject {|fn| File.directory?(fn) }.each do |file|
805
- zipfile.add(file.sub(server_file_directory + '/', ''), file)
797
+ Dir.chdir(server_file_directory) do
798
+ Dir.glob("**/*").reject {|fn| File.directory?(fn) }.each do |file|
799
+ zipfile.add(file.sub(server_file_directory + '/', ''), file)
800
+ end
806
801
  end
807
802
  end
808
803
 
809
804
  addtach_file = server_zipfile_name
810
- Dir.chdir current_project_dir
811
805
  end
812
806
 
813
807
  puts
@@ -1539,6 +1533,7 @@ module Pindo
1539
1533
  if git_cliff_installed
1540
1534
  temp_dir = Dir.pwd
1541
1535
  Dir.chdir(current_git_root_path)
1536
+ begin
1542
1537
 
1543
1538
  # 检查 HEAD 是否有 tag
1544
1539
  head_tag = `git describe --exact-match --tags HEAD 2>/dev/null`.strip
@@ -1605,7 +1600,10 @@ module Pindo
1605
1600
  description = "版本更新"
1606
1601
  end
1607
1602
 
1608
- Dir.chdir(temp_dir)
1603
+ ensure
1604
+ # ensure 保证异常路径也还原 cwd,避免污染全局工作目录
1605
+ Dir.chdir(temp_dir)
1606
+ end
1609
1607
  else
1610
1608
  # git-cliff 未安装,提示用户并退出
1611
1609
  puts "\n\e[31m错误: git-cliff 未安装\e[0m"
@@ -58,43 +58,7 @@ module Pindo
58
58
  build_android_project
59
59
  end
60
60
 
61
- def find_output
62
- # 搜索 APK 和 AAB 文件
63
- search_paths = []
64
- search_paths.concat(TaskConfig::BUILD_OUTPUT_PATTERNS[:apk].map { |p| File.join(@project_path, p) })
65
- search_paths.concat(TaskConfig::BUILD_OUTPUT_PATTERNS[:aab].map { |p| File.join(@project_path, p) })
66
-
67
- package_files = []
68
- search_paths.each do |pattern|
69
- package_files.concat(Dir.glob(pattern))
70
- end
71
-
72
- # 过滤测试包和未签名包
73
- package_files.reject! do |f|
74
- basename = File.basename(f).downcase
75
- TaskConfig::EXCLUDED_PATTERNS.any? { |pattern| basename.include?(pattern) }
76
- end
77
-
78
- if package_files.any?
79
- # 优先返回 AAB,其次 APK
80
- aab_files = package_files.select { |f| f.end_with?(".aab") }
81
- apk_files = package_files.select { |f| f.end_with?(".apk") }
82
-
83
- latest_package = if aab_files.any?
84
- aab_files.max_by { |f| File.mtime(f) }
85
- elsif apk_files.any?
86
- apk_files.max_by { |f| File.mtime(f) }
87
- else
88
- package_files.max_by { |f| File.mtime(f) }
89
- end
90
-
91
- puts " 找到 Android 包文件: #{latest_package}"
92
- latest_package
93
- else
94
- puts " 警告: 未找到 APK/AAB 文件"
95
- nil
96
- end
97
- end
61
+ # find_output 已上移到 AndroidBuildTask 基类(dev/adhoc/gplay 共用)
98
62
 
99
63
  private
100
64
 
@@ -1,4 +1,5 @@
1
1
  require_relative '../build_task'
2
+ require_relative '../../task_config'
2
3
 
3
4
  module Pindo
4
5
  module TaskSystem
@@ -12,6 +13,45 @@ module Pindo
12
13
  def required_resources
13
14
  (super || []) + [{ type: :gradle, directory: @project_path }]
14
15
  end
16
+
17
+ # 查找构建产物(APK/AAB),三个子类(dev/adhoc/gplay)共用同一套 glob 逻辑
18
+ # 搜索路径与过滤规则与 pindo jps upload 查找 Android 包保持一致;优先 AAB,其次 APK
19
+ def find_output
20
+ search_paths = []
21
+ search_paths.concat(TaskConfig::BUILD_OUTPUT_PATTERNS[:apk].map { |p| File.join(@project_path, p) })
22
+ search_paths.concat(TaskConfig::BUILD_OUTPUT_PATTERNS[:aab].map { |p| File.join(@project_path, p) })
23
+
24
+ package_files = []
25
+ search_paths.each do |pattern|
26
+ package_files.concat(Dir.glob(pattern))
27
+ end
28
+
29
+ # 过滤测试包和未签名包
30
+ package_files.reject! do |f|
31
+ basename = File.basename(f).downcase
32
+ TaskConfig::EXCLUDED_PATTERNS.any? { |pattern| basename.include?(pattern) }
33
+ end
34
+
35
+ if package_files.any?
36
+ # 优先返回 AAB,其次 APK
37
+ aab_files = package_files.select { |f| f.end_with?(".aab") }
38
+ apk_files = package_files.select { |f| f.end_with?(".apk") }
39
+
40
+ latest_package = if aab_files.any?
41
+ aab_files.max_by { |f| File.mtime(f) }
42
+ elsif apk_files.any?
43
+ apk_files.max_by { |f| File.mtime(f) }
44
+ else
45
+ package_files.max_by { |f| File.mtime(f) }
46
+ end
47
+
48
+ puts " 找到 Android 包文件: #{latest_package}"
49
+ latest_package
50
+ else
51
+ # 构建已执行但产物缺失,视为失败,不静默返回 nil
52
+ raise Informative, "构建完成但未找到 APK/AAB 文件"
53
+ end
54
+ end
15
55
  end
16
56
  end
17
57
  end
@@ -41,7 +41,7 @@ module Pindo
41
41
 
42
42
  #准备构建
43
43
  def prepare_build
44
- Dir.chdir(@project_path)
44
+ # Dir.chdir(@project_path) # Removed for thread safety(下游均传入绝对路径)
45
45
 
46
46
  # 0. 清理 Firebase Shell Script
47
47
  cleanup_firebase_shell
@@ -50,36 +50,10 @@ module Pindo
50
50
  load_ios_config
51
51
 
52
52
  # 1.5 获取 AdHoc 配置目录并合并配置
53
- adhoc_config_dir = nil
54
- begin
55
- config_parser = Pindo::IosConfigParser.instance
56
- config_json = config_parser.config_json
57
-
58
- # 根据 app_type 获取 AdHoc 配置仓库
59
- if config_json && config_json['project_info'] && config_json['project_info']['app_type']
60
- require 'pindo/config/build_info_manager'
61
-
62
- build_info_manager = Pindo::BuildInfoManager.share_instance
63
- adhoc_repo_name = build_info_manager.get_deploy_repo_with_modul_name(
64
- module_name: config_json['project_info']['app_type']
65
- )
66
-
67
- if adhoc_repo_name.nil? || adhoc_repo_name.empty?
68
- raise Informative, "config.json app_type is error!!!"
69
- end
70
-
71
- # 克隆 AdHoc 配置仓库
72
- adhoc_config_dir = Pindo::GitHandler.clong_buildconfig_repo(repo_name: adhoc_repo_name)
73
-
74
- # 合并 AdHoc 配置
75
- config_parser.modify_config_with_adhoc(adhoc_config_dir: adhoc_config_dir)
76
- else
77
- puts "配置中缺少 app_type,跳过 AdHoc 配置合并"
78
- end
79
- rescue => e
80
- puts "获取 AdHoc 配置失败: #{e.message}"
81
- adhoc_config_dir = nil
82
- end
53
+ # 复用 BuildInfoManager(命令层在导出前可能已合并过,幂等短路直接返回缓存目录)
54
+ # 合并失败 fail-fast:抛 Informative 由 TaskManager 标记任务失败
55
+ require 'pindo/config/build_info_manager'
56
+ adhoc_config_dir = Pindo::BuildInfoManager.share_instance.merge_adhoc_config_into_parser
83
57
 
84
58
  # 2. 自动增加版本号
85
59
  auto_increase_buildnumber
@@ -124,8 +98,8 @@ module Pindo
124
98
  puts " 找到 IPA 文件: #{latest_ipa}"
125
99
  latest_ipa
126
100
  else
127
- puts " 警告: 未找到 IPA 文件"
128
- nil
101
+ # 构建已执行但产物缺失,视为失败,不静默返回 nil
102
+ raise Informative, "构建完成但未找到 IPA 文件 (#{build_path})"
129
103
  end
130
104
  end
131
105
 
@@ -315,7 +289,7 @@ module Pindo
315
289
  puts " 处理 Quark/Swark..."
316
290
 
317
291
  require 'pindo/module/xcode/xcode_swark_helper'
318
- Dir.chdir(@project_path)
292
+ # Dir.chdir(@project_path) # Removed for thread safety(下游均传入绝对路径)
319
293
 
320
294
  if xcode_build_type.include?("quark")
321
295
  Pindo::XcodeSwarkHelper.quark_run(
@@ -403,7 +377,7 @@ module Pindo
403
377
 
404
378
  # 编译 iOS 工程
405
379
  def build_ios_project
406
- Dir.chdir(@project_path)
380
+ # Dir.chdir(@project_path) # Removed for thread safety(下游均传入绝对路径)
407
381
 
408
382
  # 使用 XcodeBuildHelper 进行构建
409
383
  ipa_file = Pindo::XcodeBuildHelper.build_project(
@@ -40,7 +40,7 @@ module Pindo
40
40
 
41
41
  #准备构建
42
42
  def prepare_build
43
- Dir.chdir(@project_path)
43
+ # Dir.chdir(@project_path) # Removed for thread safety(下游均传入绝对路径)
44
44
 
45
45
  # 0. 清理 Firebase Shell Script
46
46
  cleanup_firebase_shell
@@ -81,8 +81,8 @@ module Pindo
81
81
  puts " 找到 IPA 文件: #{latest_ipa}"
82
82
  latest_ipa
83
83
  else
84
- puts " 警告: 未找到 IPA 文件"
85
- nil
84
+ # 构建已执行但产物缺失,视为失败,不静默返回 nil
85
+ raise Informative, "构建完成但未找到 IPA 文件 (#{build_path})"
86
86
  end
87
87
  end
88
88
 
@@ -262,7 +262,7 @@ module Pindo
262
262
  puts " 处理 Quark/Swark..."
263
263
 
264
264
  require 'pindo/module/xcode/xcode_swark_helper'
265
- Dir.chdir(@project_path)
265
+ # Dir.chdir(@project_path) # Removed for thread safety(下游均传入绝对路径)
266
266
 
267
267
  if xcode_build_type.include?("quark")
268
268
  Pindo::XcodeSwarkHelper.quark_run(
@@ -350,7 +350,7 @@ module Pindo
350
350
 
351
351
  # 编译 iOS 工程
352
352
  def build_ios_project
353
- Dir.chdir(@project_path)
353
+ # Dir.chdir(@project_path) # Removed for thread safety(下游均传入绝对路径)
354
354
 
355
355
  # 使用 XcodeBuildHelper 进行构建
356
356
  ipa_file = Pindo::XcodeBuildHelper.build_project(
@@ -78,11 +78,21 @@ module Pindo
78
78
  latest_output = all_outputs.max_by { |f| File.mtime(f) }
79
79
  output_type = latest_output.end_with?('.ipa') ? 'IPA' : 'APP'
80
80
  puts " 找到 #{output_type} 文件: #{latest_output}"
81
- latest_output
82
- else
83
- puts " 警告: 未找到 IPA 或 APP 文件"
84
- nil
81
+ return latest_output
82
+ end
83
+
84
+ # 命令行工具产物为裸可执行文件(build 根目录下无扩展名的普通文件)
85
+ tool_outputs = Dir.glob(File.join(build_dir, "*")).select do |f|
86
+ File.file?(f) && File.extname(f).empty?
85
87
  end
88
+ if tool_outputs.any?
89
+ latest_output = tool_outputs.max_by { |f| File.mtime(f) }
90
+ puts " 找到命令行工具可执行文件: #{latest_output}"
91
+ return latest_output
92
+ end
93
+
94
+ # 构建已执行但产物缺失,视为失败,不静默返回 nil
95
+ raise Informative, "构建完成但未找到 IPA / APP / 可执行文件 (#{build_dir})"
86
96
  end
87
97
 
88
98
  # 处理 CocoaPods 依赖(Dev 构建)
@@ -180,6 +180,8 @@ module Pindo
180
180
  execute_build
181
181
 
182
182
  # 查找输出文件
183
+ # 注意:是否「找不到产物即失败」由各子类 find_output 自行决定(已实现的子类会 raise;
184
+ # 未实现的 TODO 桩 AndroidBuildAdhocTask/AndroidBuildGPlayTask 仍返回 nil,保持原行为)
183
185
  @output_path = find_output
184
186
 
185
187
  {
@@ -13,7 +13,7 @@ module Pindo
13
13
  :git_commit
14
14
  end
15
15
 
16
- # 重试配置:失败后重试 1
16
+ # 重试配置:失败后重试 2 次(共 3 次尝试)
17
17
  def self.default_retry_count
18
18
  2
19
19
  end
@@ -69,6 +69,16 @@ module Pindo
69
69
  root_dir = @git_root_path
70
70
  git_repo_helper = Pindo::GitRepoHelper.share_instance
71
71
 
72
+ # 0. 先拉取远程 tag,确保本地 tag 列表是最新的
73
+ # 否则本地 tag 落后于远程时(如远程被 CI/其他机器打了更高 tag),
74
+ # 后续基于本地 tag 计算的版本会偏低,导致本该新建的版本 tag 与远程已有 tag 冲突而被漏打。
75
+ # 尽力而为:网络不可达时仅告警,用本地 tag 降级计算,不阻断构建。
76
+ begin
77
+ Pindo::GitHandler.git_remote!(root_dir, %W(-C #{root_dir} fetch --tags --force origin))
78
+ rescue => e
79
+ Funlog.instance.fancyinfo_warning("拉取远程 tag 失败,将使用本地 tag 计算版本: #{e.message.lines.first&.strip}")
80
+ end
81
+
72
82
  # 1. 如果 fixed_version 为 nil,尝试从 HEAD 的 tag 获取版本
73
83
  # 必须在 check_gitignore 之前检查,因为 check_gitignore 可能创建提交导致 HEAD 移动
74
84
  if (@fixed_version.nil? || @fixed_version.empty?) &&