pindo 5.13.1 → 5.13.2

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 (110) hide show
  1. checksums.yaml +4 -4
  2. data/lib/pindo/base/git_handler.rb +692 -0
  3. data/lib/pindo/command/android/autobuild.rb +2 -2
  4. data/lib/pindo/command/appstore/adhocbuild.rb +258 -311
  5. data/lib/pindo/command/appstore/autobuild.rb +203 -0
  6. data/lib/pindo/command/appstore/autoresign.rb +35 -17
  7. data/lib/pindo/command/appstore/bundleid.rb +120 -0
  8. data/lib/pindo/command/appstore/cert.rb +212 -0
  9. data/lib/pindo/command/appstore/configproj.rb +81 -0
  10. data/lib/pindo/command/{deploy → appstore}/getitcinfo.rb +76 -91
  11. data/lib/pindo/command/appstore/iap.rb +788 -24
  12. data/lib/pindo/command/appstore/initconfig.rb +105 -0
  13. data/lib/pindo/command/appstore/itcapp.rb +95 -13
  14. data/lib/pindo/command/{deploy → appstore}/itcinfo.rb +90 -118
  15. data/lib/pindo/command/appstore/pem.rb +136 -0
  16. data/lib/pindo/command/appstore/pullconfig.rb +99 -0
  17. data/lib/pindo/command/appstore/quswark.rb +87 -0
  18. data/lib/pindo/command/appstore/quswauth.rb +67 -0
  19. data/lib/pindo/command/appstore/tag.rb +77 -0
  20. data/lib/pindo/command/appstore.rb +13 -1
  21. data/lib/pindo/command/env/quarkenv.rb +11 -13
  22. data/lib/pindo/command/env/swarkenv.rb +11 -16
  23. data/lib/pindo/command/ios/autobuild.rb +64 -43
  24. data/lib/pindo/command/ios/autoresign.rb +34 -19
  25. data/lib/pindo/command/ios/build.rb +9 -6
  26. data/lib/pindo/command/ios/cert.rb +27 -20
  27. data/lib/pindo/command/jps/upload.rb +3 -3
  28. data/lib/pindo/command/unity/autobuild.rb +2 -2
  29. data/lib/pindo/command/utils/clearcert.rb +2 -17
  30. data/lib/pindo/command/{deploy → utils}/fabric.rb +13 -13
  31. data/lib/pindo/command/utils/renewcert.rb +62 -38
  32. data/lib/pindo/command/utils/renewproj.rb +0 -3
  33. data/lib/pindo/command/{deploy → utils}/updateconfig.rb +6 -7
  34. data/lib/pindo/command/utils.rb +2 -0
  35. data/lib/pindo/command/web/autobuild.rb +2 -2
  36. data/lib/pindo/command.rb +30 -3
  37. data/lib/pindo/config/build_info_manager.rb +176 -0
  38. data/lib/pindo/config/ios_config_parser.rb +404 -0
  39. data/lib/pindo/module/android/android_config_helper.rb +9 -5
  40. data/lib/pindo/module/appstore/bundleid_helper.rb +349 -0
  41. data/lib/pindo/module/appstore/itcapp_helper.rb +228 -0
  42. data/lib/pindo/module/build/build_helper.rb +12 -0
  43. data/lib/pindo/module/build/swark_helper.rb +116 -77
  44. data/lib/pindo/module/cert/cert_helper.rb +74 -0
  45. data/lib/pindo/module/cert/pem_helper.rb +72 -0
  46. data/lib/pindo/module/cert/{xcodecerthelper.rb → xcode_cert_helper.rb} +208 -6
  47. data/lib/pindo/module/task/model/appstore/appstore_task.rb +18 -0
  48. data/lib/pindo/module/task/model/appstore/appstore_upload_ipa_task.rb +151 -0
  49. data/lib/pindo/module/task/model/appstore/appstore_upload_metadata_task.rb +250 -0
  50. data/lib/pindo/module/task/model/appstore/appstore_upload_screenshot_task.rb +276 -0
  51. data/lib/pindo/module/task/model/build/android_build_adhoc_task.rb +210 -0
  52. data/lib/pindo/module/task/model/build/{android_dev_build_task.rb → android_build_dev_task.rb} +2 -2
  53. data/lib/pindo/module/task/model/build/android_build_gplay_task.rb +210 -0
  54. data/lib/pindo/module/task/model/build/android_build_task.rb +13 -0
  55. data/lib/pindo/module/task/model/build/ios_build_adhoc_task.rb +197 -0
  56. data/lib/pindo/module/task/model/build/ios_build_appstore_task.rb +367 -0
  57. data/lib/pindo/module/task/model/build/{ios_dev_build_task.rb → ios_build_dev_task.rb} +37 -27
  58. data/lib/pindo/module/task/model/build/ios_build_task.rb +13 -0
  59. data/lib/pindo/module/task/model/build/{web_dev_build_task.rb → web_build_dev_task.rb} +1 -1
  60. data/lib/pindo/module/task/model/build_task.rb +15 -12
  61. data/lib/pindo/module/task/model/jps_resign_task.rb +185 -0
  62. data/lib/pindo/module/task/model/{upload_task.rb → jps_upload_task.rb} +3 -3
  63. data/lib/pindo/module/task/model/unity_export_task.rb +3 -1
  64. data/lib/pindo/module/unity/unity_helper.rb +2 -1
  65. data/lib/pindo/module/xcode/ipa_resign_helper.rb +210 -0
  66. data/lib/pindo/module/xcode/{xcodeappconfig.rb → xcode_app_config.rb} +79 -0
  67. data/lib/pindo/module/xcode/xcode_build_config.rb +152 -17
  68. data/lib/pindo/module/xcode/xcode_build_helper.rb +151 -1
  69. data/lib/pindo/module/xcode/xcode_swark_helper.rb +341 -0
  70. data/lib/pindo/options/core/global_options_state.rb +268 -0
  71. data/lib/pindo/options/core/option_configuration.rb +206 -0
  72. data/lib/pindo/options/core/option_initializer.rb +51 -0
  73. data/lib/pindo/options/core/option_item.rb +144 -0
  74. data/lib/pindo/options/core/option_value_parser.rb +54 -0
  75. data/lib/pindo/options/groups/build_options.rb +60 -0
  76. data/lib/pindo/options/groups/jps_options.rb +70 -0
  77. data/lib/pindo/options/groups/option_group.rb +73 -0
  78. data/lib/pindo/options/helpers/bundleid_selector.rb +103 -0
  79. data/lib/pindo/options/options.rb +14 -0
  80. data/lib/pindo/version.rb +1 -1
  81. metadata +49 -40
  82. data/lib/pindo/command/appstore/import.rb +0 -259
  83. data/lib/pindo/command/deploy/build.rb +0 -250
  84. data/lib/pindo/command/deploy/bundleid.rb +0 -259
  85. data/lib/pindo/command/deploy/cert.rb +0 -202
  86. data/lib/pindo/command/deploy/check.rb +0 -93
  87. data/lib/pindo/command/deploy/configproj.rb +0 -120
  88. data/lib/pindo/command/deploy/confusecode.rb +0 -262
  89. data/lib/pindo/command/deploy/confuseproj.rb +0 -122
  90. data/lib/pindo/command/deploy/iap.rb +0 -826
  91. data/lib/pindo/command/deploy/initconfig.rb +0 -138
  92. data/lib/pindo/command/deploy/itcapp.rb +0 -146
  93. data/lib/pindo/command/deploy/pem.rb +0 -55
  94. data/lib/pindo/command/deploy/pullconfig.rb +0 -56
  95. data/lib/pindo/command/deploy/pushconfig.rb +0 -93
  96. data/lib/pindo/command/deploy/quswark.rb +0 -156
  97. data/lib/pindo/command/deploy/quswauth.rb +0 -76
  98. data/lib/pindo/command/deploy/reportbug.rb +0 -145
  99. data/lib/pindo/command/deploy/resign.rb +0 -300
  100. data/lib/pindo/command/deploy/tag.rb +0 -108
  101. data/lib/pindo/command/deploy/uploadipa.rb +0 -73
  102. data/lib/pindo/command/deploy.rb +0 -42
  103. data/lib/pindo/command/dev/autobuild.rb +0 -117
  104. data/lib/pindo/command/dev/build.rb +0 -94
  105. data/lib/pindo/command/dev/debug.rb +0 -112
  106. data/lib/pindo/module/task/model/build/android_release_build_task.rb +0 -29
  107. data/lib/pindo/module/task/model/build/ios_adhoc_build_task.rb +0 -53
  108. data/lib/pindo/module/task/model/build/ios_release_build_task.rb +0 -53
  109. data/lib/pindo/options/appconfigoptions.rb +0 -24
  110. data/lib/pindo/options/deployoptions.rb +0 -372
@@ -0,0 +1,185 @@
1
+ require_relative '../pindo_task'
2
+ require_relative '../task_config'
3
+ require 'pindo/module/pgyer/pgyerhelper'
4
+
5
+ module Pindo
6
+ module TaskSystem
7
+ # JPS 重签名任务
8
+ # 从 JPS 平台下载应用并使用指定证书重签名
9
+ class JPSResignTask < PindoTask
10
+ attr_reader :app_info_obj, :version_info, :cert_id, :send_flag
11
+
12
+ def self.task_type
13
+ :jps_resign
14
+ end
15
+
16
+ # 重试配置
17
+ def self.default_retry_mode
18
+ RetryMode::DELAYED # 延迟重试
19
+ end
20
+
21
+ def self.default_retry_count
22
+ 2 # 默认可以重试 2 次
23
+ end
24
+
25
+ def self.default_retry_delay
26
+ 10 # 默认延迟 10 秒
27
+ end
28
+
29
+ # 初始化 JPS 重签名任务
30
+ # @param options [Hash] 选项
31
+ # @option options [Hash] :app_info_obj JPS 应用信息对象(可选,如为 nil 则延迟获取)
32
+ # @option options [String] :project_name 项目名称(可选)
33
+ # @option options [String] :cert_id 重签名使用的 Bundle ID(可选,nil 表示交互选择)
34
+ # @option options [Boolean] :send_flag 是否发送到测试群(默认 false,始终会发送给自己)
35
+ def initialize(options = {})
36
+ @app_info_obj = options[:app_info_obj]
37
+ @project_name = options[:project_name]
38
+ @cert_id = options[:cert_id]
39
+ @send_flag = options[:send_flag] || false
40
+ @version_info = nil
41
+
42
+ # 设置重签名任务的优先级为 LOW,确保在上传任务之后执行
43
+ options[:priority] ||= TaskPriority::LOW
44
+
45
+ super("JPS 重签名", options)
46
+ end
47
+
48
+ def validate
49
+ # app_info_obj 可以延迟获取,不在这里验证
50
+ true
51
+ end
52
+
53
+ protected
54
+
55
+ def do_work
56
+ pgyer_helper = PgyerHelper.share_instace
57
+
58
+ # 1. 如果没有 app_info_obj,延迟获取
59
+ if @app_info_obj.nil?
60
+ fetch_jps_config
61
+ end
62
+
63
+ # 2. 获取要重签名的版本
64
+ @version_info = pgyer_helper.get_versioon_history_item(
65
+ app_info_obj: @app_info_obj,
66
+ list_select_flat: false
67
+ )
68
+
69
+ if @version_info.nil?
70
+ @retry_count = 0 # 没有版本记录时不重试
71
+ raise "没有找到上传记录"
72
+ end
73
+
74
+ # 3. 获取证书列表
75
+ cert_list = pgyer_helper.get_cert_list
76
+
77
+ if cert_list.nil? || cert_list.empty?
78
+ @retry_count = 0 # 没有证书时不重试
79
+ raise "没有找到可用的证书"
80
+ end
81
+
82
+ # 4. 选择证书
83
+ cert_item = select_certificate(cert_list)
84
+
85
+ if cert_item.nil?
86
+ @retry_count = 0 # 没有匹配的证书时不重试
87
+ raise "未找到匹配的证书"
88
+ end
89
+
90
+ # 5. 执行重签名
91
+ result = pgyer_helper.resign(
92
+ package_id: @version_info["id"],
93
+ cert_id: cert_item["id"]
94
+ )
95
+
96
+ unless result
97
+ raise "重签名失败"
98
+ end
99
+
100
+ # 6. 获取重签名后的版本信息
101
+ @version_info = pgyer_helper.get_versioon_history_item(
102
+ app_info_obj: @app_info_obj,
103
+ list_select_flat: false
104
+ )
105
+
106
+ if @version_info.nil?
107
+ raise "重签名成功但无法获取新版本信息"
108
+ end
109
+
110
+ # 7. 打印版本信息
111
+ pgyer_helper.print_app_version_info(
112
+ app_info_obj: @app_info_obj,
113
+ app_version_info_obj: @version_info
114
+ )
115
+
116
+ # 8. 发送消息给自己
117
+ pgyer_helper.send_apptest_msg(
118
+ app_info_obj: @app_info_obj,
119
+ app_version_info_obj: @version_info,
120
+ receiveType: "self"
121
+ )
122
+
123
+ # 9. 如果需要发送到测试群
124
+ if @send_flag
125
+ pgyer_helper.send_apptest_msg(
126
+ app_info_obj: @app_info_obj,
127
+ app_version_info_obj: @version_info,
128
+ chatEnv: "DevTest",
129
+ receiveType: "chat"
130
+ )
131
+ end
132
+
133
+ {
134
+ success: true,
135
+ version_info: @version_info,
136
+ cert_id: cert_item["mainBundleId"]
137
+ }
138
+ end
139
+
140
+ private
141
+
142
+ # 延迟获取 JPS 配置
143
+ def fetch_jps_config
144
+ puts " 获取 JPS 配置..."
145
+
146
+ # 获取当前工作目录
147
+ working_directory = Dir.pwd
148
+
149
+ # 调用 PgyerHelper 获取配置
150
+ @app_info_obj = PgyerHelper.share_instace.prepare_upload(
151
+ working_directory: working_directory,
152
+ proj_name: @project_name
153
+ )
154
+
155
+ unless @app_info_obj
156
+ raise "无法获取 JPS 配置"
157
+ end
158
+ end
159
+
160
+ # 选择证书
161
+ def select_certificate(cert_list)
162
+ if @cert_id.nil? && cert_list.size > 0
163
+ # 交互式选择证书
164
+ bundle_id_list = cert_list.map { |cert| [cert["certType"], cert["mainBundleId"]].join("_") }
165
+ cli = HighLine.new
166
+ selected = cli.choose do |menu|
167
+ menu.prompt = "请选择重签名的证书:"
168
+ menu.choices(*bundle_id_list)
169
+ end
170
+
171
+ # 根据选择找到对应的证书
172
+ cert_list.select { |certitem|
173
+ [certitem["certType"], certitem["mainBundleId"]].join("_") == selected
174
+ }.first
175
+ else
176
+ # 根据 cert_id 查找 adhoc 证书
177
+ cert_list.select { |certitem|
178
+ certitem["mainBundleId"] == @cert_id &&
179
+ certitem["certType"].downcase.include?("adhoc")
180
+ }.first
181
+ end
182
+ end
183
+ end
184
+ end
185
+ end
@@ -4,9 +4,9 @@ require 'pindo/module/pgyer/pgyerhelper'
4
4
 
5
5
  module Pindo
6
6
  module TaskSystem
7
- # 上传任务
8
- # 上传构建产物到测试平台(JPS
9
- class UploadTask < PindoTask
7
+ # JPS 上传任务
8
+ # 上传构建产物到 JPS 测试平台
9
+ class JPSUploadTask < PindoTask
10
10
  attr_reader :file_type, :upload_path, :upload_file
11
11
 
12
12
  def self.task_type
@@ -31,6 +31,7 @@ module Pindo
31
31
  @is_library_mode = false # 不再支持库模式
32
32
  @index_count = options[:context] ? options[:context][:index_count] : nil
33
33
  @export_path = options[:export_path] # 可由参数传入,nil 表示自动检测
34
+ @deploy_mode = options[:deploy_mode] || 'dev' # 部署模式:dev/adhoc/release,默认 dev
34
35
 
35
36
  name = case platform
36
37
  when 'ios', 'ipa'
@@ -188,7 +189,8 @@ module Pindo
188
189
  project_path: @unity_project_path,
189
190
  platform: platform,
190
191
  isLibrary: @is_library_mode,
191
- indexNo: @index_count
192
+ indexNo: @index_count,
193
+ deployMode: @deploy_mode
192
194
  )
193
195
 
194
196
  # 清理 Unity 进程
@@ -422,7 +422,7 @@ module Pindo
422
422
  nil
423
423
  end
424
424
 
425
- def build_project(unity_exe_full_path:nil, project_path:nil, platform: nil, isLibrary: false, indexNo: nil)
425
+ def build_project(unity_exe_full_path:nil, project_path:nil, platform: nil, isLibrary: false, indexNo: nil, deployMode: nil)
426
426
 
427
427
  # 检查是否有Unity进程在运行,传入Unity路径和项目路径以精确匹配
428
428
  check_unity_processes(unity_exe_full_path: unity_exe_full_path, project_path: project_path)
@@ -434,6 +434,7 @@ module Pindo
434
434
  additional_args[:platform] = platform if platform
435
435
  additional_args[:buildtype] = 'library' if isLibrary
436
436
  additional_args[:indexno] = indexNo if indexNo
437
+ additional_args[:deploymode] = deployMode || 'dev' if deployMode || true # 默认为 dev
437
438
 
438
439
  # 使用GoodUnityBuild.BuildManager.BatchBuild进行构建
439
440
  result = execute_unity_command(unity_exe_full_path, project_path, additional_args)
@@ -0,0 +1,210 @@
1
+ require 'fileutils'
2
+ require 'sigh'
3
+ require 'zip'
4
+ require 'pindo/config/pindoconfig'
5
+
6
+ module Pindo
7
+ class IpaResignHelper
8
+
9
+ class << self
10
+
11
+ # 重签名 IPA 文件的主入口方法
12
+ # @param ipa_file_path [String] IPA 文件路径(可选,如果为 nil 则自动查找当前目录下的 IPA)
13
+ # @param bundle_id [String] 新的 Bundle ID
14
+ # @return [String, nil] 重签名后的 IPA 文件路径,失败返回 nil
15
+ def resign_ipa(ipa_file_path: nil, bundle_id:)
16
+ ipa_path = find_ipa_file(ipa_file_path)
17
+
18
+ if ipa_path.nil? || ipa_path.empty?
19
+ puts "未找到 IPA 文件"
20
+ return nil
21
+ end
22
+
23
+ # 准备重签名的 IPA(修改 Bundle ID)
24
+ resigned_ipa_path = prepare_resign_ipa(ipa_name: ipa_path, bundle_id: bundle_id)
25
+
26
+ if resigned_ipa_path.nil?
27
+ puts "准备重签名 IPA 失败"
28
+ return nil
29
+ end
30
+
31
+ # 使用新证书签名
32
+ sign_ipa_with_new_cert(ipa_name: resigned_ipa_path, bundle_id: bundle_id)
33
+
34
+ resigned_ipa_path
35
+ end
36
+
37
+ private
38
+
39
+ # 查找 IPA 文件
40
+ # @param ipa_file_path [String, nil] 指定的 IPA 文件路径
41
+ # @return [String, nil] IPA 文件的完整路径
42
+ def find_ipa_file(ipa_file_path)
43
+ if ipa_file_path
44
+ return File.expand_path(ipa_file_path)
45
+ end
46
+
47
+ # 自动查找当前目录下的 IPA 文件
48
+ current_dir = Dir.pwd
49
+ Dir.foreach(current_dir) do |file|
50
+ if file =~ /(.*).ipa/
51
+ return File.join(current_dir, file)
52
+ end
53
+ end
54
+
55
+ nil
56
+ end
57
+
58
+ # 准备重签名的 IPA(修改 Bundle ID)
59
+ # @param ipa_name [String] 原始 IPA 文件路径
60
+ # @param bundle_id [String] 新的 Bundle ID
61
+ # @return [String, nil] 准备好的 IPA 文件路径
62
+ def prepare_resign_ipa(ipa_name:, bundle_id:)
63
+ ipa_base_name = File.basename(ipa_name, ".ipa")
64
+ ipa_dir = File.dirname(ipa_name)
65
+
66
+ resign_ipa_name = ipa_base_name + "_resigned.ipa"
67
+ resign_ipa_full_name = File.join(ipa_dir, resign_ipa_name)
68
+ FileUtils.cp_r(ipa_name, resign_ipa_full_name)
69
+ puts resign_ipa_full_name
70
+
71
+ tmp_time = "tmp_" + Time.now.strftime('%y%m%d_%H%M%S')
72
+ tmp_dir = File.join(ipa_dir, tmp_time)
73
+ FileUtils.mkdir(tmp_dir) unless File.exist?(tmp_dir)
74
+
75
+ # 解压 IPA
76
+ command = "unzip -q \"#{resign_ipa_full_name}\" -d #{tmp_dir}"
77
+ puts command
78
+ system command
79
+
80
+ payload_path = File.join(tmp_dir, "Payload")
81
+ if File.exist?(payload_path)
82
+ FileUtils.rm_rf(resign_ipa_full_name)
83
+ end
84
+
85
+ # 查找 .app 目录
86
+ modify_content_path = ""
87
+ Dir.foreach(payload_path) do |file|
88
+ if file =~ /(.*).app/
89
+ modify_content_path = File.join(payload_path, file)
90
+ break
91
+ end
92
+ end
93
+
94
+ # 修改 Info.plist 中的 Bundle ID
95
+ modify_ipa_info_plist(modify_content_path: modify_content_path, bundle_id: bundle_id)
96
+
97
+ # 重新打包 IPA
98
+ current_dir = Dir.pwd
99
+ if File.exist?(payload_path)
100
+ Dir.chdir(tmp_dir)
101
+ command = "zip -qry \"#{resign_ipa_full_name}\" * "
102
+ puts command
103
+ system command
104
+ Dir.chdir(current_dir)
105
+ end
106
+
107
+ # 清理临时目录
108
+ FileUtils.rm_rf(tmp_dir) if File.exist?(resign_ipa_full_name)
109
+
110
+ resign_ipa_full_name
111
+ end
112
+
113
+ # 修改 IPA 中的 Info.plist 文件
114
+ # @param modify_content_path [String] .app 目录路径
115
+ # @param bundle_id [String] 新的 Bundle ID
116
+ def modify_ipa_info_plist(modify_content_path:, bundle_id:)
117
+ main_info_plist = File.join(modify_content_path, "Info.plist")
118
+ puts main_info_plist
119
+
120
+ # 确保 PlistBuddy 可用
121
+ unless File.exist?("/usr/local/bin/PlistBuddy")
122
+ command = 'ln -s /usr/libexec/PlistBuddy /usr/local/bin/PlistBuddy'
123
+ system command
124
+ end
125
+
126
+ # 读取旧的 Bundle ID
127
+ old_bundle_id_command = '/usr/local/bin/PlistBuddy -c "Print :CFBundleIdentifier" ' + main_info_plist
128
+ puts old_bundle_id_command
129
+ old_bundle_id = ""
130
+ IO.popen(old_bundle_id_command) { |f| old_bundle_id = f.gets }
131
+ puts old_bundle_id
132
+
133
+ # 修改主应用的 Bundle ID
134
+ exchange_bundleid_command = '/usr/local/bin/PlistBuddy -c "Set :CFBundleIdentifier ' + bundle_id + '" ' + main_info_plist
135
+ puts exchange_bundleid_command
136
+ system exchange_bundleid_command
137
+
138
+ # 修改插件扩展的 Bundle ID
139
+ plugin_path = File.join(modify_content_path, "PlugIns")
140
+ if File.exist?(plugin_path)
141
+ Dir.foreach(plugin_path) do |file|
142
+ next unless file =~ /(.*).appex/
143
+
144
+ appex_path = File.join(plugin_path, file)
145
+ tmp_info_plist = File.join(appex_path, "Info.plist")
146
+
147
+ old_bundle_id_command = '/usr/local/bin/PlistBuddy -c "Print :CFBundleIdentifier" ' + tmp_info_plist
148
+ puts old_bundle_id_command
149
+ old_plugin_bundle_id = ""
150
+ IO.popen(old_bundle_id_command) { |f| old_plugin_bundle_id = f.gets }
151
+ puts "old_bundle_id = #{old_plugin_bundle_id}"
152
+
153
+ # 根据插件类型修改 Bundle ID
154
+ update_plugin_bundle_id(tmp_info_plist, old_plugin_bundle_id, bundle_id)
155
+ end
156
+ end
157
+ end
158
+
159
+ # 更新插件的 Bundle ID
160
+ # @param plist_path [String] 插件的 Info.plist 路径
161
+ # @param old_bundle_id [String] 旧的 Bundle ID
162
+ # @param new_base_bundle_id [String] 新的基础 Bundle ID
163
+ def update_plugin_bundle_id(plist_path, old_bundle_id, new_base_bundle_id)
164
+ plugin_types = [
165
+ ".content", ".service", ".imessage", ".keyboard",
166
+ ".siri", ".widget", ".siriui", ".extension",
167
+ ".extensionad", ".extensionporn"
168
+ ]
169
+
170
+ plugin_types.each do |suffix|
171
+ if old_bundle_id.include?(suffix)
172
+ new_plugin_bundle_id = new_base_bundle_id + suffix
173
+ exchange_bundleid_command = '/usr/local/bin/PlistBuddy -c "Set :CFBundleIdentifier ' + new_plugin_bundle_id + '" ' + plist_path
174
+ puts exchange_bundleid_command
175
+ system exchange_bundleid_command
176
+ break
177
+ end
178
+ end
179
+ end
180
+
181
+ # 使用新证书签名 IPA
182
+ # @param ipa_name [String] IPA 文件路径
183
+ # @param bundle_id [String] Bundle ID
184
+ def sign_ipa_with_new_cert(ipa_name:, bundle_id:)
185
+ require 'pindo/config/pindoconfig'
186
+
187
+ profil_info_array = Pindo::PindoSingleConfig.instance.get_cert_info
188
+ bundle_id_signing_identity = profil_info_array.first['signing_identity']
189
+
190
+ profile_dict = build_resign_profile_dict(profil_info_array: profil_info_array)
191
+
192
+ # 调用 Sigh::Resign 进行重签名
193
+ # resign(ipa, signing_identity, provisioning_profiles, entitlements, version, display_name, short_version, bundle_version, new_bundle_id, use_app_entitlements, keychain_path)
194
+ Sigh::Resign.resign(ipa_name, bundle_id_signing_identity, profile_dict, nil, nil, nil, nil, nil, bundle_id, true, nil)
195
+ end
196
+
197
+ # 构建重签名用的 profile 字典
198
+ # @param profil_info_array [Array<Hash>] 证书信息数组
199
+ # @return [Hash] Bundle ID 到 profile 路径的映射
200
+ def build_resign_profile_dict(profil_info_array:)
201
+ resign_dict = {}
202
+ profil_info_array.each do |profil_info|
203
+ resign_dict[profil_info["bundle_id"]] = profil_info["profile_path"]
204
+ end
205
+ resign_dict
206
+ end
207
+
208
+ end
209
+ end
210
+ end
@@ -209,5 +209,84 @@ module Pindo
209
209
  end
210
210
  end
211
211
 
212
+ # 创建发布 tag
213
+ # @param project_dir [String] 项目目录
214
+ # @param config_json [Hash] 配置 JSON
215
+ # @param bundle_id [String] Bundle ID
216
+ # @param delete_tag_name [String] 可选,要删除的 tag 名称
217
+ # @return [Boolean] 是否成功
218
+ def create_release_tag(project_dir:, config_json:, bundle_id:, delete_tag_name: nil)
219
+ require 'pindo/base/git_handler'
220
+ require 'pindo/config/pindoconfig'
221
+ require_relative '../../base/funlog'
222
+
223
+ begin
224
+ pindo_dir = File.expand_path(Pindo::PindoConfig.pindo_single_config.pindo_dir)
225
+ app_config_dir = File.join(pindo_dir, bundle_id)
226
+
227
+ # 如果指定了要删除的 tag
228
+ if !delete_tag_name.nil? && !delete_tag_name.empty?
229
+ result = Pindo::GitHandler.remove_tag(local_repo_dir: project_dir, tag_name: delete_tag_name)
230
+ if result
231
+ puts
232
+ puts "代码仓库路径#{project_dir}"
233
+ puts "已删除tag : #{delete_tag_name}"
234
+ puts
235
+ puts
236
+ end
237
+
238
+ result = Pindo::GitHandler.remove_tag(local_repo_dir: app_config_dir, tag_name: delete_tag_name)
239
+ if result
240
+ puts
241
+ puts "配置仓库路径#{app_config_dir}"
242
+ puts "已删除tag : #{delete_tag_name}"
243
+ puts
244
+ puts
245
+ end
246
+ end
247
+
248
+ # 创建新的 tag
249
+ if config_json && config_json["app_info"] && config_json["app_info"]["app_version"]
250
+ temp_version = config_json["app_info"]["app_version"]
251
+ else
252
+ Funlog.instance.fancyinfo_error("无法从配置中获取 app_version")
253
+ return false
254
+ end
255
+
256
+ tag_name = "ios_release_" + temp_version
257
+ puts tag_name
258
+
259
+ # 为代码仓库创建 tag
260
+ Pindo::GitHandler.remove_tag(local_repo_dir: project_dir, tag_name: tag_name)
261
+ result = Pindo::GitHandler.add_tag(local_repo_dir: project_dir, tag_name: tag_name)
262
+ if result
263
+ puts
264
+ puts "代码仓库路径#{project_dir}"
265
+ puts "已添加tag : #{tag_name}"
266
+ puts
267
+ puts
268
+ end
269
+
270
+ # 为配置仓库创建 tag
271
+ Pindo::GitHandler.remove_tag(local_repo_dir: app_config_dir, tag_name: tag_name)
272
+ result = Pindo::GitHandler.add_tag(local_repo_dir: app_config_dir, tag_name: tag_name)
273
+
274
+ if result
275
+ puts
276
+ puts "配置仓库路径#{app_config_dir}"
277
+ puts "已添加tag : #{tag_name}"
278
+ puts
279
+ puts
280
+ end
281
+
282
+ return true
283
+
284
+ rescue StandardError => e
285
+ Funlog.instance.fancyinfo_error("创建 tag 失败: #{e.message}")
286
+ puts e.backtrace
287
+ return false
288
+ end
289
+ end
290
+
212
291
  end
213
292
  end