pindo 5.17.4 → 5.18.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.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/lib/pindo/base/git_handler.rb +120 -38
  3. data/lib/pindo/command/android/autobuild.rb +92 -31
  4. data/lib/pindo/command/appstore/adhocbuild.rb +1 -1
  5. data/lib/pindo/command/appstore/autobuild.rb +1 -1
  6. data/lib/pindo/command/appstore/autoresign.rb +1 -1
  7. data/lib/pindo/command/appstore/updateid.rb +229 -0
  8. data/lib/pindo/command/appstore.rb +1 -0
  9. data/lib/pindo/command/ios/autobuild.rb +70 -33
  10. data/lib/pindo/command/ios/podpush.rb +1 -1
  11. data/lib/pindo/command/unity/autobuild.rb +38 -18
  12. data/lib/pindo/command/utils/allcopyconfig.rb +144 -0
  13. data/lib/pindo/command/utils/copyconfig.rb +207 -0
  14. data/lib/pindo/command/utils/icon.rb +2 -2
  15. data/lib/pindo/command/utils/renewbundleid.rb +199 -0
  16. data/lib/pindo/command/utils/renewcert.rb +56 -54
  17. data/lib/pindo/command/utils.rb +3 -0
  18. data/lib/pindo/command/web/autobuild.rb +10 -8
  19. data/lib/pindo/config/build_info_manager.rb +1 -3
  20. data/lib/pindo/module/android/android_build_helper.rb +198 -33
  21. data/lib/pindo/module/android/android_config_helper.rb +305 -88
  22. data/lib/pindo/module/android/android_project_helper.rb +124 -14
  23. data/lib/pindo/module/android/android_res_helper.rb +349 -51
  24. data/lib/pindo/module/android/keystore_helper.rb +611 -295
  25. data/lib/pindo/module/android/workflow_gradle_injector.rb +702 -0
  26. data/lib/pindo/module/appselect.rb +4 -4
  27. data/lib/pindo/module/appstore/bundleid_helper.rb +204 -14
  28. data/lib/pindo/module/build/build_helper.rb +76 -10
  29. data/lib/pindo/module/build/git_repo_helper.rb +4 -4
  30. data/lib/pindo/module/cert/mode/base_cert_operator.rb +12 -6
  31. data/lib/pindo/module/pgyer/pgyerhelper.rb +124 -39
  32. data/lib/pindo/module/task/model/build/android_build_dev_task.rb +64 -6
  33. data/lib/pindo/module/task/model/git/git_commit_task.rb +70 -54
  34. data/lib/pindo/module/task/model/git/git_tag_task.rb +13 -9
  35. data/lib/pindo/module/task/model/jps/jps_upload_task.rb +110 -3
  36. data/lib/pindo/module/task/model/unity/unity_export_task.rb +2 -1
  37. data/lib/pindo/module/task/model/unity/unity_update_task.rb +2 -1
  38. data/lib/pindo/module/task/model/unity/unity_yoo_asset_task.rb +2 -1
  39. data/lib/pindo/module/task/model/unity_task.rb +2 -1
  40. data/lib/pindo/module/unity/unity_helper.rb +13 -10
  41. data/lib/pindo/module/unity/unity_proc_helper.rb +27 -2
  42. data/lib/pindo/module/xcode/applovin_xcode_helper.rb +6 -2
  43. data/lib/pindo/module/xcode/res/xcode_res_constant.rb +72 -0
  44. data/lib/pindo/module/xcode/res/xcode_res_handler.rb +3 -3
  45. data/lib/pindo/module/xcode/xcode_build_config.rb +46 -17
  46. data/lib/pindo/module/xcode/xcode_build_helper.rb +186 -25
  47. data/lib/pindo/module/xcode/xcode_project_helper.rb +1 -1
  48. data/lib/pindo/module/xcode/xcode_res_helper.rb +32 -16
  49. data/lib/pindo/options/groups/build_options.rb +5 -5
  50. data/lib/pindo/options/groups/git_options.rb +7 -5
  51. data/lib/pindo/options/groups/unity_options.rb +11 -0
  52. data/lib/pindo/options/helpers/bundleid_selector.rb +25 -0
  53. data/lib/pindo/options/helpers/git_constants.rb +7 -6
  54. data/lib/pindo/version.rb +3 -3
  55. metadata +12 -7
@@ -15,7 +15,7 @@ module Pindo
15
15
  end
16
16
 
17
17
  # 初始化上传任务
18
- # @param file_type [String] 文件类型:'ipa' | 'apk' | 'html' | 'mac'
18
+ # @param file_type [String] 文件类型:'ipa' | 'apk' | 'html' | 'mac' | 'exe'
19
19
  # @param upload_path [String] 搜索文件的路径
20
20
  # @param upload_file [String] 要上传的文件(nil 表示自动查找)
21
21
  # @param options [Hash] 选项
@@ -24,7 +24,7 @@ module Pindo
24
24
  # @option options [String] :project_name 项目名称(可选)
25
25
  # @option options [String] :upload_desc 上传描述(可选)
26
26
  def initialize(file_type, upload_path, upload_file, options = {})
27
- @file_type = file_type # 'ipa' | 'apk' | 'html' | 'mac'
27
+ @file_type = file_type # 'ipa' | 'apk' | 'html' | 'mac' | 'exe'
28
28
  @upload_path = upload_path # 搜索文件的路径
29
29
  @upload_file = upload_file # 要上传的文件(nil 表示自动查找)
30
30
  @upload_desc = options[:upload_desc] # 上传描述
@@ -41,6 +41,8 @@ module Pindo
41
41
  "上传 WebGL包"
42
42
  when 'mac'
43
43
  "上传 macOS App包"
44
+ when 'exe'
45
+ "上传 Windows EXE包"
44
46
  else
45
47
  "上传 #{file_type.upcase}"
46
48
  end
@@ -109,7 +111,10 @@ module Pindo
109
111
  )
110
112
  end
111
113
 
112
- # 3. 上传到 JPS
114
+ # 3. 自动解析上传描述(未手动指定时)
115
+ @upload_desc = resolve_upload_desc(file_to_upload) if @upload_desc.nil?
116
+
117
+ # 4. 上传到 JPS
113
118
  app_version_info = upload_to_jps(file_to_upload)
114
119
 
115
120
  {
@@ -149,11 +154,113 @@ module Pindo
149
154
  File.join(@upload_path, "**", "*.html")
150
155
  when 'mac'
151
156
  File.join(@upload_path, "**", "*.app")
157
+ when 'exe'
158
+ File.join(@upload_path, "**", "*.exe")
152
159
  else
153
160
  raise "不支持的文件类型: #{@file_type}"
154
161
  end
155
162
  end
156
163
 
164
+ # 根据依赖任务类型或 IPA 描述文件类型自动推断上传描述
165
+ # 规则:
166
+ # 依赖 IosBuildAdhocTask → "Commit id: <commit_id>"
167
+ # 依赖 IosBuildAppStoreTask → "重签名"
168
+ # 依赖其他 IosBuildTask 子类 → nil(不传描述)
169
+ # 直接上传 IPA(无构建依赖) → 检测描述文件类型后同上
170
+ def resolve_upload_desc(file_path)
171
+ # 路径1:检查任务依赖
172
+ if @dependencies && !@dependencies.empty?
173
+ @dependencies.each do |dep_id|
174
+ dep_task = get_dependency_task(dep_id)
175
+ next unless dep_task
176
+
177
+ class_name = dep_task.class.name || ''
178
+ is_ios_build = dep_task.class.ancestors.any? do |a|
179
+ a.name == 'Pindo::TaskSystem::IosBuildTask'
180
+ end
181
+ next unless is_ios_build
182
+
183
+ if class_name == 'Pindo::TaskSystem::IosBuildAdhocTask'
184
+ commit_id = get_current_commit_id
185
+ return commit_id ? "Commit id: #{commit_id}" : nil
186
+ elsif class_name == 'Pindo::TaskSystem::IosBuildAppStoreTask'
187
+ return "重签名"
188
+ else
189
+ return nil
190
+ end
191
+ end
192
+ end
193
+
194
+ # 路径2:直接上传 IPA 时,检测 Xcode 工程描述文件类型
195
+ if @file_type == 'ipa'
196
+ profile_type = detect_xcode_profile_type
197
+ case profile_type
198
+ when :adhoc
199
+ commit_id = get_current_commit_id
200
+ return commit_id ? "Commit id: #{commit_id}" : nil
201
+ when :appstore
202
+ return "重签名"
203
+ end
204
+ end
205
+
206
+ nil
207
+ end
208
+
209
+ # 获取当前 git commit id(从 upload_path 所在的 git 仓库)
210
+ def get_current_commit_id
211
+ dir = @upload_path || Dir.pwd
212
+ commit_id = `git -C "#{dir}" rev-parse HEAD 2>/dev/null`.strip
213
+ commit_id.empty? ? nil : commit_id
214
+ rescue
215
+ nil
216
+ end
217
+
218
+ # 检测当前目录下 Xcode 工程的描述文件类型
219
+ # 通过读取主 target Release 配置的 PROVISIONING_PROFILE_SPECIFIER 判断
220
+ # @return [Symbol, nil] :adhoc / :appstore / :development / nil
221
+ def detect_xcode_profile_type
222
+ require 'xcodeproj'
223
+
224
+ # 从 upload_path 向上查找包含 .xcodeproj 的目录
225
+ search_dir = File.expand_path(@upload_path || Dir.pwd)
226
+ project_fullname = nil
227
+ loop do
228
+ candidates = Dir.glob(File.join(search_dir, '*.xcodeproj'))
229
+ if candidates.any?
230
+ project_fullname = candidates.max_by { |f| File.mtime(f) }
231
+ break
232
+ end
233
+ parent = File.dirname(search_dir)
234
+ break if parent == search_dir # 已到文件系统根目录
235
+ search_dir = parent
236
+ end
237
+
238
+ return nil unless project_fullname && File.exist?(project_fullname)
239
+
240
+ project_obj = Xcodeproj::Project.open(project_fullname)
241
+ main_target = project_obj.targets.select do |t|
242
+ t.respond_to?(:product_type) &&
243
+ t.product_type.include?(Xcodeproj::Constants::PRODUCT_TYPE_UTI[:application])
244
+ end.first
245
+ return nil unless main_target
246
+
247
+ # 优先取 Release 配置,找不到则取第一个
248
+ release_config = main_target.build_configurations.find { |c| c.name == 'Release' } ||
249
+ main_target.build_configurations.first
250
+ return nil unless release_config
251
+
252
+ specifier = release_config.build_settings['PROVISIONING_PROFILE_SPECIFIER'].to_s.downcase
253
+ if specifier.include?('adhoc')
254
+ :adhoc
255
+ elsif specifier.include?('development')
256
+ :development
257
+ else
258
+ :appstore
259
+ end
260
+ rescue
261
+ nil
262
+ end
263
+
157
264
  # 延迟获取 JPS 配置(上传任务专用)
158
265
  # @return [Array] 返回 [app_info_obj, workflow_info]
159
266
  def fetch_jps_config(working_directory: Dir.pwd, package_type: nil)
@@ -205,7 +205,8 @@ module Pindo
205
205
  platform: platform,
206
206
  isLibrary: @is_library_mode,
207
207
  indexNo: @index_count,
208
- deployMode: @deploy_mode
208
+ deployMode: @deploy_mode,
209
+ auto_kill: @auto_kill
209
210
  )
210
211
  end
211
212
  end
@@ -81,7 +81,8 @@ module Pindo
81
81
  # 调用 UnityHelper 的 force_update_libraries 方法
82
82
  result = unity_helper.force_update_libraries(
83
83
  unity_exe_full_path: @unity_exe_path,
84
- project_path: @unity_root_path
84
+ project_path: @unity_root_path,
85
+ auto_kill: @auto_kill
85
86
  )
86
87
 
87
88
  # 返回成功结果
@@ -100,7 +100,8 @@ module Pindo
100
100
  result = unity_helper.build_yoo_asset(
101
101
  unity_exe_full_path: @unity_exe_path,
102
102
  project_path: @unity_root_path,
103
- platform: platform_name
103
+ platform: platform_name,
104
+ auto_kill: @auto_kill
104
105
  )
105
106
 
106
107
  # 如果跳过(YooAsset 不存在),输出警告并显示命令
@@ -8,11 +8,12 @@ module Pindo
8
8
  # Unity 任务基类
9
9
  # 所有 Unity 相关任务的父类,提供通用的 Unity 操作和配置
10
10
  class UnityTask < PindoTask
11
- attr_reader :project_path, :unity_root_path
11
+ attr_reader :project_path, :unity_root_path, :auto_kill
12
12
 
13
13
  def initialize(name, options = {})
14
14
  @project_path = options[:project_path] ? normalize_path(options[:project_path]) : nil
15
15
  @unity_root_path = options[:unity_root_path] ? normalize_path(options[:unity_root_path]) : @project_path
16
+ @auto_kill = options[:auto_kill] || false
16
17
 
17
18
  super(name, options)
18
19
  end
@@ -46,11 +46,11 @@ module Pindo
46
46
  # ============================================
47
47
 
48
48
  # 检查 GoodUnityBuild 版本是否满足要求
49
- def check_goodunitybuild_version(unity_exe_full_path, project_path)
49
+ def check_goodunitybuild_version(unity_exe_full_path, project_path, auto_kill: false)
50
50
  Funlog.instance.fancyinfo_start("检查 GoodUnityBuild 版本")
51
51
 
52
52
  # 检查是否有Unity进程在运行
53
- UnityProcHelper.check_unity_processes(unity_exe_full_path: unity_exe_full_path, project_path: project_path)
53
+ UnityProcHelper.check_unity_processes(unity_exe_full_path: unity_exe_full_path, project_path: project_path, auto_kill: auto_kill)
54
54
 
55
55
  # 执行 Unity 命令获取库版本
56
56
  result = UnityCommandHelper.execute_unity_command(
@@ -112,10 +112,10 @@ module Pindo
112
112
  Funlog.warning("GoodUnityBuild 库版本检查失败: #{e.message}")
113
113
  end
114
114
 
115
- def export_project(unity_exe_full_path:nil, project_path:nil, platform: nil, isLibrary: false, indexNo: nil, deployMode: nil)
115
+ def export_project(unity_exe_full_path:nil, project_path:nil, platform: nil, isLibrary: false, indexNo: nil, deployMode: nil, auto_kill: false)
116
116
 
117
117
  # 检查是否有Unity进程在运行,传入Unity路径和项目路径以精确匹配
118
- UnityProcHelper.check_unity_processes(unity_exe_full_path: unity_exe_full_path, project_path: project_path)
118
+ UnityProcHelper.check_unity_processes(unity_exe_full_path: unity_exe_full_path, project_path: project_path, auto_kill: auto_kill)
119
119
 
120
120
  additional_args = {}
121
121
  additional_args[:platform] = platform if platform
@@ -201,8 +201,9 @@ module Pindo
201
201
  # @param unity_exe_full_path [String] Unity 可执行文件路径
202
202
  # @param project_path [String] Unity 项目路径
203
203
  # @param platform [String] 目标平台 ('Android', 'iOS' 等)
204
+ # @param auto_kill [Boolean] 是否自动关闭进程(不询问用户)
204
205
  # @return [Hash] 执行结果,如果跳过则返回 { skipped: true }
205
- def build_yoo_asset(unity_exe_full_path:nil, project_path:nil, platform: nil)
206
+ def build_yoo_asset(unity_exe_full_path:nil, project_path:nil, platform: nil, auto_kill: false)
206
207
  # 检查项目中是否存在 YooAsset
207
208
  unless yoo_asset_exists?(project_path)
208
209
  return { skipped: true, reason: 'YooAsset not found' }
@@ -211,7 +212,7 @@ module Pindo
211
212
  puts "检测到 YooAsset,开始构建资源..."
212
213
 
213
214
  # 检查是否有Unity进程在运行
214
- UnityProcHelper.check_unity_processes(unity_exe_full_path: unity_exe_full_path, project_path: project_path)
215
+ UnityProcHelper.check_unity_processes(unity_exe_full_path: unity_exe_full_path, project_path: project_path, auto_kill: auto_kill)
215
216
 
216
217
  additional_args = {}
217
218
  additional_args[:platform] = platform if platform
@@ -244,10 +245,11 @@ module Pindo
244
245
  # 强制更新必备库(NugetForUnity)
245
246
  # @param unity_exe_full_path [String] Unity 可执行文件路径
246
247
  # @param project_path [String] Unity 项目路径
248
+ # @param auto_kill [Boolean] 是否自动关闭进程(不询问用户)
247
249
  # @return [Hash] 执行结果
248
- def force_update_libraries(unity_exe_full_path:nil, project_path:nil)
250
+ def force_update_libraries(unity_exe_full_path:nil, project_path:nil, auto_kill: false)
249
251
  # 检查是否有Unity进程在运行
250
- UnityProcHelper.check_unity_processes(unity_exe_full_path: unity_exe_full_path, project_path: project_path)
252
+ UnityProcHelper.check_unity_processes(unity_exe_full_path: unity_exe_full_path, project_path: project_path, auto_kill: auto_kill)
251
253
 
252
254
  # 使用 NugetForUnity.Joy.JoyTools.CheckForceUpdate 强制更新库
253
255
  result = UnityCommandHelper.execute_unity_command(
@@ -278,8 +280,9 @@ module Pindo
278
280
  # @param unity_exe_full_path [String] Unity 可执行文件路径
279
281
  # @param project_path [String] Unity 项目路径
280
282
  # @param deploy_mode [String] 部署模式 ('dev', 'adhoc', 'release')
283
+ # @param auto_kill [Boolean] 是否自动关闭进程(不询问用户)
281
284
  # @return [Hash] 执行结果
282
- def config_build_mode(unity_exe_full_path:nil, project_path:nil, deploy_mode: 'dev')
285
+ def config_build_mode(unity_exe_full_path:nil, project_path:nil, deploy_mode: 'dev', auto_kill: false)
283
286
  # 验证 deploy_mode 参数
284
287
  valid_modes = ['dev', 'adhoc', 'release']
285
288
  unless valid_modes.include?(deploy_mode)
@@ -287,7 +290,7 @@ module Pindo
287
290
  end
288
291
 
289
292
  # 检查是否有Unity进程在运行
290
- UnityProcHelper.check_unity_processes(unity_exe_full_path: unity_exe_full_path, project_path: project_path)
293
+ UnityProcHelper.check_unity_processes(unity_exe_full_path: unity_exe_full_path, project_path: project_path, auto_kill: auto_kill)
291
294
 
292
295
  puts "配置 GoodUnityBuild 编译模式为: #{deploy_mode}"
293
296
 
@@ -5,10 +5,11 @@ module Pindo
5
5
  # 所有方法均为类方法,无需实例化
6
6
  class UnityProcHelper
7
7
 
8
- # 构建前检查 Unity 进程(交互式)
8
+ # 构建前检查 Unity 进程(交互式或自动关闭)
9
9
  # @param unity_exe_full_path [String] Unity 可执行文件路径
10
10
  # @param project_path [String] Unity 项目路径
11
- def self.check_unity_processes(unity_exe_full_path: nil, project_path: nil)
11
+ # @param auto_kill [Boolean] 是否自动关闭进程(不询问用户)
12
+ def self.check_unity_processes(unity_exe_full_path: nil, project_path: nil, auto_kill: false)
12
13
  # 如果没有提供路径参数,直接跳过Unity进程检查
13
14
  if unity_exe_full_path.nil? && project_path.nil?
14
15
  return
@@ -32,6 +33,30 @@ module Pindo
32
33
  end
33
34
  puts ""
34
35
 
36
+ # 如果指定了 auto_kill,直接关闭进程
37
+ if auto_kill
38
+ puts "自动关闭 Unity 进程..."
39
+ success_count = 0
40
+ unity_pids.each do |process_info|
41
+ if kill_unity_process(process_info[:pid])
42
+ puts "✅ 成功关闭进程 #{process_info[:pid]}"
43
+ success_count += 1
44
+ else
45
+ puts "❌ 关闭进程 #{process_info[:pid]} 失败"
46
+ end
47
+ end
48
+
49
+ if success_count > 0
50
+ puts "\n✅ 已关闭 #{success_count} 个 Unity 相关进程"
51
+ puts "等待3秒后继续编译..."
52
+ sleep(3)
53
+ else
54
+ puts "\n❌ 无法关闭 Unity 相关进程,请手动关闭后重试"
55
+ raise "Unity进程冲突:无法自动关闭 Unity 相关进程,请手动关闭后重试"
56
+ end
57
+ return
58
+ end
59
+
35
60
  # 询问用户是否要自动关闭Unity进程
36
61
  loop do
37
62
  puts "批处理模式需要关闭这些 Unity 进程以避免冲突"
@@ -4,6 +4,7 @@ require 'faraday'
4
4
  require 'xcodeproj'
5
5
  require 'pindo/base/git_handler'
6
6
  require 'pindo/config/pindoconfig'
7
+ require 'pindo/module/xcode/xcode_build_helper'
7
8
 
8
9
  module Pindo
9
10
  # Applovin Xcode 辅助类
@@ -255,9 +256,12 @@ module Pindo
255
256
  project_obj = Xcodeproj::Project.open(xcodeproj_file)
256
257
 
257
258
  project_obj.targets.each do |target|
259
+ # 跳过非 native target(如 PBXAggregateTarget)
260
+ next unless target.respond_to?(:product_type)
258
261
  if target.product_type.to_s == "com.apple.product-type.application"
259
- temp_info = target.build_configurations.first.build_settings['INFOPLIST_FILE']
260
- return File.join(project_dir, temp_info) if temp_info
262
+ raw_path = target.build_configurations.first.build_settings['INFOPLIST_FILE']
263
+ resolved = Pindo::XcodeBuildHelper.resolve_info_plist_path(project_dir, raw_path, target: target)
264
+ return resolved if resolved
261
265
  end
262
266
  end
263
267
  rescue => e
@@ -176,6 +176,78 @@ module Pindo
176
176
  # return model_data
177
177
  # end
178
178
 
179
+ def self.xcode_macos_icon_json
180
+ model_data = {
181
+ "images" => [
182
+ {
183
+ "size" => "16x16",
184
+ "idiom" => "mac",
185
+ "filename" => "app_icon_16.png",
186
+ "scale" => "1x"
187
+ },
188
+ {
189
+ "size" => "16x16",
190
+ "idiom" => "mac",
191
+ "filename" => "app_icon_32.png",
192
+ "scale" => "2x"
193
+ },
194
+ {
195
+ "size" => "32x32",
196
+ "idiom" => "mac",
197
+ "filename" => "app_icon_32.png",
198
+ "scale" => "1x"
199
+ },
200
+ {
201
+ "size" => "32x32",
202
+ "idiom" => "mac",
203
+ "filename" => "app_icon_64.png",
204
+ "scale" => "2x"
205
+ },
206
+ {
207
+ "size" => "128x128",
208
+ "idiom" => "mac",
209
+ "filename" => "app_icon_128.png",
210
+ "scale" => "1x"
211
+ },
212
+ {
213
+ "size" => "128x128",
214
+ "idiom" => "mac",
215
+ "filename" => "app_icon_256.png",
216
+ "scale" => "2x"
217
+ },
218
+ {
219
+ "size" => "256x256",
220
+ "idiom" => "mac",
221
+ "filename" => "app_icon_256.png",
222
+ "scale" => "1x"
223
+ },
224
+ {
225
+ "size" => "256x256",
226
+ "idiom" => "mac",
227
+ "filename" => "app_icon_512.png",
228
+ "scale" => "2x"
229
+ },
230
+ {
231
+ "size" => "512x512",
232
+ "idiom" => "mac",
233
+ "filename" => "app_icon_512.png",
234
+ "scale" => "1x"
235
+ },
236
+ {
237
+ "size" => "512x512",
238
+ "idiom" => "mac",
239
+ "filename" => "app_icon_1024.png",
240
+ "scale" => "2x"
241
+ }
242
+ ],
243
+ "info" => {
244
+ "author" => "xcode",
245
+ "version" => 1
246
+ }
247
+ }
248
+ return model_data
249
+ end
250
+
179
251
  def self.xcode_ios_imessage_icon_json
180
252
  model_data = {
181
253
  "images" => [
@@ -15,7 +15,7 @@ module Pindo
15
15
  def get_xcodeproj_icon_path()
16
16
 
17
17
  icon_path = nil
18
- select_target = @project_obj.targets.select { |target| target.product_type.include?(Xcodeproj::Constants::PRODUCT_TYPE_UTI[:application]) }.first
18
+ select_target = @project_obj.targets.select { |target| target.respond_to?(:product_type) && target.product_type.include?(Xcodeproj::Constants::PRODUCT_TYPE_UTI[:application]) }.first
19
19
 
20
20
  project_dir = @project_obj.project_dir
21
21
 
@@ -91,7 +91,7 @@ module Pindo
91
91
 
92
92
  def get_xcodeproj_imessage_icon_path
93
93
  icon_path = nil
94
- select_target = @project_obj.targets.select { |target| target.product_type.include?(Xcodeproj::Constants::PRODUCT_TYPE_UTI[:messages_extension]) }.first
94
+ select_target = @project_obj.targets.select { |target| target.respond_to?(:product_type) && target.product_type.include?(Xcodeproj::Constants::PRODUCT_TYPE_UTI[:messages_extension]) }.first
95
95
  project_dir = @project_obj.project_dir
96
96
  if !select_target.nil?
97
97
  file_refs = select_target.resources_build_phase.files_references.select { |file| file.display_name.include?("Assets.xcassets") } || []
@@ -170,7 +170,7 @@ module Pindo
170
170
 
171
171
  launchimg_path = nil
172
172
  assets_path = nil
173
- select_target = @project_obj.targets.select { |target| target.product_type.include?(Xcodeproj::Constants::PRODUCT_TYPE_UTI[:application]) }.first
173
+ select_target = @project_obj.targets.select { |target| target.respond_to?(:product_type) && target.product_type.include?(Xcodeproj::Constants::PRODUCT_TYPE_UTI[:application]) }.first
174
174
  project_dir = @project_obj.project_dir
175
175
  if !select_target.nil?
176
176
  assets_objs = select_target.resources_build_phase.files_references.select { |file| file.display_name.include?("Assets.xcassets") } || []
@@ -2,6 +2,7 @@ require 'fileutils'
2
2
  require 'xcodeproj'
3
3
  require 'json'
4
4
  require 'pindo/module/xcode/xcode_res_helper'
5
+ require 'pindo/module/xcode/xcode_build_helper'
5
6
  require 'pindo/module/utils/file_downloader'
6
7
  require 'open-uri'
7
8
  require_relative '../../base/funlog'
@@ -36,16 +37,22 @@ module Pindo
36
37
 
37
38
  project_obj = Xcodeproj::Project.open(project_fullname)
38
39
  project_obj.targets.each do |target|
40
+ # 跳过非 native target(如 PBXAggregateTarget)
41
+ next unless target.respond_to?(:product_type)
39
42
  if target.product_type.to_s.eql?("com.apple.product-type.application")
40
- temp_info_file = target.build_configurations.first.build_settings['INFOPLIST_FILE']
41
- if temp_info_file && !temp_info_file.empty?
42
- info_plist_path = File.join(project_dir, temp_info_file)
43
- end
44
43
  app_target = target
45
44
  break # 找到第一个application target即可
46
45
  end
47
46
  end
48
47
 
48
+ return false unless app_target
49
+
50
+ # 确保 Info.plist 存在(不存在时自动创建)
51
+ info_plist_path = Pindo::XcodeBuildHelper.ensure_info_plist_exists(
52
+ project_dir: project_dir,
53
+ target: app_target,
54
+ project_obj: project_obj
55
+ )
49
56
  return false unless info_plist_path && File.exist?(info_plist_path)
50
57
 
51
58
  # 一次性读取plist
@@ -136,18 +143,26 @@ module Pindo
136
143
  current_bundle_id = nil
137
144
 
138
145
  project_obj = Xcodeproj::Project.open(project_fullname)
146
+ app_target = nil
139
147
  project_obj.targets.each do |target|
148
+ # 跳过非 native target(如 PBXAggregateTarget)
149
+ next unless target.respond_to?(:product_type)
140
150
  if target.product_type.to_s.eql?("com.apple.product-type.application")
141
- temp_info_file = target.build_configurations.first.build_settings['INFOPLIST_FILE']
142
- if temp_info_file && !temp_info_file.empty?
143
- info_plist_path = File.join(project_dir, temp_info_file)
144
- end
151
+ app_target = target
145
152
  # 获取当前实际的 Bundle ID
146
153
  current_bundle_id = target.build_configurations.first.build_settings['PRODUCT_BUNDLE_IDENTIFIER']
147
154
  break # 找到第一个application target即可
148
155
  end
149
156
  end
150
157
 
158
+ return false unless app_target
159
+
160
+ # 确保 Info.plist 存在
161
+ info_plist_path = Pindo::XcodeBuildHelper.ensure_info_plist_exists(
162
+ project_dir: project_dir,
163
+ target: app_target,
164
+ project_obj: project_obj
165
+ )
151
166
  return false unless info_plist_path && File.exist?(info_plist_path)
152
167
  return false unless current_bundle_id && !current_bundle_id.empty?
153
168
 
@@ -205,17 +220,25 @@ module Pindo
205
220
  bundleid_scheme_name = nil
206
221
 
207
222
  project_obj = Xcodeproj::Project.open(project_fullname)
223
+ app_target = nil
208
224
  project_obj.targets.each do |target|
225
+ # 跳过非 native target(如 PBXAggregateTarget)
226
+ next unless target.respond_to?(:product_type)
209
227
  if target.product_type.to_s.eql?("com.apple.product-type.application")
210
- temp_info_file = target.build_configurations.first.build_settings['INFOPLIST_FILE']
211
- if temp_info_file && !temp_info_file.empty?
212
- info_plist_path = File.join(project_dir, temp_info_file)
213
- end
228
+ app_target = target
214
229
  bundleid_scheme_name = target.build_configurations.first.build_settings['PRODUCT_BUNDLE_IDENTIFIER']
215
230
  break # 找到第一个application target即可
216
231
  end
217
232
  end
218
233
 
234
+ return false unless app_target
235
+
236
+ # 确保 Info.plist 存在
237
+ info_plist_path = Pindo::XcodeBuildHelper.ensure_info_plist_exists(
238
+ project_dir: project_dir,
239
+ target: app_target,
240
+ project_obj: project_obj
241
+ )
219
242
  return false unless info_plist_path && File.exist?(info_plist_path)
220
243
 
221
244
  info_plist_dict = Xcodeproj::Plist.read_from_path(info_plist_path)
@@ -295,6 +318,8 @@ module Pindo
295
318
  # 更新所有application类型target的版本号
296
319
  updated_targets = []
297
320
  project.targets.each do |target|
321
+ # 跳过非 native target(如 PBXAggregateTarget)
322
+ next unless target.respond_to?(:product_type)
298
323
  # 只处理application类型的target
299
324
  if target.product_type.to_s.eql?("com.apple.product-type.application")
300
325
  target.build_configurations.each do |config|
@@ -303,9 +328,10 @@ module Pindo
303
328
  config.build_settings['CURRENT_PROJECT_VERSION'] = build_number.to_s
304
329
 
305
330
  # 兼容旧的设置方式,通过Info.plist更新
306
- if config.build_settings['INFOPLIST_FILE']
307
- info_plist_path = File.join(project_dir, config.build_settings['INFOPLIST_FILE'])
308
- if File.exist?(info_plist_path)
331
+ raw_plist_path = config.build_settings['INFOPLIST_FILE']
332
+ if raw_plist_path
333
+ info_plist_path = Pindo::XcodeBuildHelper.resolve_info_plist_path(project_dir, raw_plist_path, target: target)
334
+ if info_plist_path
309
335
  update_info_plist(info_plist_path, version, build_number.to_s)
310
336
  end
311
337
  end
@@ -384,6 +410,8 @@ module Pindo
384
410
  project_obj = Xcodeproj::Project.open(project_fullname)
385
411
 
386
412
  project_obj.targets.each do |target|
413
+ # 跳过非 native target(如 PBXAggregateTarget)
414
+ next unless target.respond_to?(:product_type)
387
415
  if target.product_type.to_s.eql?("com.apple.product-type.application")
388
416
  temp_entitlements_file = target.build_configurations.first.build_settings['CODE_SIGN_ENTITLEMENTS']
389
417
  if temp_entitlements_file && !temp_entitlements_file.empty?
@@ -468,7 +496,8 @@ module Pindo
468
496
  Funlog.instance.fancyinfo_start("正在创建 icon...")
469
497
  XcodeResHelper.create_icons(
470
498
  icon_name: icon_download_path,
471
- new_icon_dir: new_icon_dir
499
+ new_icon_dir: new_icon_dir,
500
+ proj_dir: project_dir
472
501
  )
473
502
  Funlog.instance.fancyinfo_success("创建 icon 完成!")
474
503
 
@@ -548,7 +577,7 @@ module Pindo
548
577
  new_icon_dir = File.join(project_dir, "icon_#{time}")
549
578
 
550
579
  Funlog.instance.fancyinfo_start("正在创建 icon...")
551
- Pindo::XcodeResHelper.create_icons(icon_name: icon_name, new_icon_dir: new_icon_dir)
580
+ Pindo::XcodeResHelper.create_icons(icon_name: icon_name, new_icon_dir: new_icon_dir, proj_dir: project_dir)
552
581
  Funlog.instance.fancyinfo_success("创建 icon 完成!")
553
582
 
554
583
  Funlog.instance.fancyinfo_start("正在替换 icon...")