pindo 5.13.12 → 5.13.13

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 (50) hide show
  1. checksums.yaml +4 -4
  2. data/lib/pindo/base/funlog.rb +62 -5
  3. data/lib/pindo/base/git_handler.rb +83 -22
  4. data/lib/pindo/base/output_sink.rb +69 -0
  5. data/lib/pindo/command/android/autobuild.rb +57 -8
  6. data/lib/pindo/command/appstore/autobuild.rb +10 -1
  7. data/lib/pindo/command/ios/autobuild.rb +59 -7
  8. data/lib/pindo/command/jps/media.rb +164 -13
  9. data/lib/pindo/command/jps/upload.rb +14 -9
  10. data/lib/pindo/command/unity/autobuild.rb +64 -10
  11. data/lib/pindo/command/utils/tag.rb +9 -1
  12. data/lib/pindo/command/web/autobuild.rb +59 -10
  13. data/lib/pindo/module/android/android_build_helper.rb +6 -7
  14. data/lib/pindo/module/build/git_repo_helper.rb +29 -25
  15. data/lib/pindo/module/pgyer/pgyerhelper.rb +174 -77
  16. data/lib/pindo/module/task/core/concurrent_execution_strategy.rb +237 -0
  17. data/lib/pindo/module/task/core/dependency_checker.rb +123 -0
  18. data/lib/pindo/module/task/core/execution_strategy.rb +61 -0
  19. data/lib/pindo/module/task/core/resource_lock_manager.rb +190 -0
  20. data/lib/pindo/module/task/core/serial_execution_strategy.rb +60 -0
  21. data/lib/pindo/module/task/core/task_executor.rb +131 -0
  22. data/lib/pindo/module/task/core/task_queue.rb +221 -0
  23. data/lib/pindo/module/task/model/build/android_build_dev_task.rb +1 -1
  24. data/lib/pindo/module/task/model/build/android_build_task.rb +6 -2
  25. data/lib/pindo/module/task/model/build/ios_build_dev_task.rb +2 -3
  26. data/lib/pindo/module/task/model/build/ios_build_task.rb +6 -0
  27. data/lib/pindo/module/task/model/build_task.rb +22 -0
  28. data/lib/pindo/module/task/model/git/git_commit_task.rb +11 -2
  29. data/lib/pindo/module/task/model/git_task.rb +6 -0
  30. data/lib/pindo/module/task/model/jps/jps_message_task.rb +9 -11
  31. data/lib/pindo/module/task/model/jps/jps_upload_media_task.rb +204 -103
  32. data/lib/pindo/module/task/model/jps_task.rb +0 -1
  33. data/lib/pindo/module/task/model/unity_task.rb +38 -2
  34. data/lib/pindo/module/task/output/multi_line_output_manager.rb +380 -0
  35. data/lib/pindo/module/task/output/multi_line_task_display.rb +185 -0
  36. data/lib/pindo/module/task/output/stdout_redirector.rb +95 -0
  37. data/lib/pindo/module/task/pindo_task.rb +133 -9
  38. data/lib/pindo/module/task/task_manager.rb +98 -268
  39. data/lib/pindo/module/task/task_reporter.rb +135 -0
  40. data/lib/pindo/module/task/task_resources/resource_instance.rb +90 -0
  41. data/lib/pindo/module/task/task_resources/resource_registry.rb +105 -0
  42. data/lib/pindo/module/task/task_resources/resource_type.rb +59 -0
  43. data/lib/pindo/module/task/task_resources/types/directory_based_resource.rb +63 -0
  44. data/lib/pindo/module/task/task_resources/types/global_exclusive_resource.rb +33 -0
  45. data/lib/pindo/module/task/task_resources/types/global_shared_resource.rb +34 -0
  46. data/lib/pindo/module/xcode/xcode_build_helper.rb +26 -8
  47. data/lib/pindo/options/groups/jps_options.rb +10 -0
  48. data/lib/pindo/options/groups/task_options.rb +39 -0
  49. data/lib/pindo/version.rb +3 -2
  50. metadata +20 -1
@@ -10,10 +10,10 @@ module Pindo
10
10
  #
11
11
  # 支持两种模式:
12
12
  # 1. 指定模式:传入 file_paths 和 git_commit_id
13
- # 2. 自动模式:只传入 project_path,自动查找 JPSMedia/ 目录和 git HEAD 信息
13
+ # 2. 自动模式:只传入 upload_path,自动查找 JPSMedia/ 目录和 git HEAD 信息
14
14
  class JPSUploadMediaTask < JPSTask
15
15
  attr_reader :file_paths, :git_commit_id, :git_commit_time, :git_commit_desc
16
- attr_reader :workflow_id, :project_path
16
+ attr_reader :upload_path
17
17
 
18
18
  # 支持的媒体文件扩展名
19
19
  MEDIA_EXTENSIONS = %w[png jpg jpeg gif bmp webp mp4 mov avi mkv webm].freeze
@@ -25,20 +25,20 @@ module Pindo
25
25
 
26
26
  # 初始化 Media 上传任务
27
27
  # @param file_paths [Array<String>] 要上传的文件路径列表(可选,为空时自动查找)
28
+ # @param upload_path [String] 项目路径(必需,用于查找 git 仓库和 JPSMedia 目录)
28
29
  # @param options [Hash] 选项
29
30
  # @option options [String] :git_commit_id Git commit SHA(可选,为空时自动获取 HEAD)
30
31
  # @option options [String] :git_commit_time Git commit 时间(可选)
31
32
  # @option options [String] :git_commit_desc Git commit 描述(可选)
32
- # @option options [Integer] :workflow_id 工作流ID(可选,用于筛选 commit_log)
33
- # @option options [String] :project_name 项目名称(可选)
34
- # @option options [String] :project_path 项目路径(必需,用于查找 git 仓库和 JPSMedia 目录)
35
- def initialize(file_paths, options = {})
33
+ # @option options [Hash] :app_info_obj JPS 应用信息对象(可选,继承自基类)
34
+ # @option options [Hash] :workflow_info 工作流信息(可选,继承自基类)
35
+ # @option options [String] :project_name 项目名称(可选,继承自基类)
36
+ def initialize(file_paths, upload_path, options = {})
36
37
  @file_paths = file_paths || [] # 要上传的文件路径列表
38
+ @upload_path = upload_path # 项目路径
37
39
  @git_commit_id = options[:git_commit_id] # Git commit SHA
38
40
  @git_commit_time = options[:git_commit_time] # Git commit 时间
39
41
  @git_commit_desc = options[:git_commit_desc] # Git commit 描述
40
- @workflow_id = options[:workflow_id] # 工作流ID
41
- @project_path = options[:project_path] # 项目路径
42
42
 
43
43
  # 设置任务优先级为 LOW,确保在其他任务之后执行
44
44
  options[:priority] ||= TaskPriority::LOW
@@ -49,12 +49,14 @@ module Pindo
49
49
  end
50
50
 
51
51
  def validate
52
- # 验证 project_path(必需参数)
53
- unless @project_path && !@project_path.empty? && Dir.exist?(@project_path)
54
- @error = "缺少必需参数: project_path(项目路径)或路径不存在"
52
+ # 验证 upload_path(必需参数)
53
+ unless @upload_path && !@upload_path.empty? && Dir.exist?(@upload_path)
54
+ @error = "缺少必需参数: upload_path(项目路径)或路径不存在"
55
55
  return false
56
56
  end
57
57
 
58
+ # workflow_info 可以延迟获取,不在这里验证
59
+
58
60
  true
59
61
  end
60
62
 
@@ -88,14 +90,14 @@ module Pindo
88
90
 
89
91
  def do_work
90
92
  # 1. 查找 git 仓库根目录
91
- git_root = find_git_root(@project_path)
93
+ git_root = find_git_root(@upload_path)
92
94
  unless git_root
93
- raise "未找到 git 仓库,请确保 project_path 在 git 仓库内"
95
+ raise "未找到 git 仓库,请确保 upload_path 在 git 仓库内"
94
96
  end
95
97
 
96
- # 2. 如果没有提供 git_commit_id,自动获取 HEAD 信息
98
+ # 2. 如果没有提供 git_commit_id,获取最新的符合规范的 commit
97
99
  if @git_commit_id.nil? || @git_commit_id.empty?
98
- git_info = get_git_head_info(git_root)
100
+ git_info = Pindo::GitHandler.get_latest_conventional_commit(project_dir: git_root)
99
101
  @git_commit_id = git_info[:commit_id]
100
102
  @git_commit_time = git_info[:commit_time]
101
103
  @git_commit_desc = git_info[:commit_desc]
@@ -107,13 +109,11 @@ module Pindo
107
109
  end
108
110
 
109
111
  # 3. 打印 git commit 信息
110
- puts
111
- puts " ===== Git Commit 信息 ====="
112
- puts " Commit ID: #{@git_commit_id}"
113
- puts " Commit Time: #{@git_commit_time}" if @git_commit_time
114
- puts " Commit Desc: #{@git_commit_desc}" if @git_commit_desc
115
- puts " " + "=" * 28
116
- puts
112
+ puts ""
113
+ puts " 📝 Commit: #{@git_commit_desc}" if @git_commit_desc
114
+ puts " ID: #{@git_commit_id[0..7]}"
115
+ puts " Time: #{@git_commit_time}" if @git_commit_time
116
+ puts ""
117
117
 
118
118
  # 4. 处理文件路径
119
119
  if @file_paths.nil? || @file_paths.empty?
@@ -124,11 +124,17 @@ module Pindo
124
124
  @file_paths = expand_file_paths(@file_paths)
125
125
  end
126
126
 
127
- # 过滤存在的文件
127
+ # 5. 自动压缩文件(如果有文件需要上传)
128
+ if @file_paths.any?
129
+ @file_paths = compress_media_files(@file_paths, git_root)
130
+ end
131
+
132
+ # 6. 过滤存在的文件
128
133
  existing_files = @file_paths.select { |f| File.exist?(f) }
129
134
  if existing_files.empty?
130
- puts " 没有找到有效的 media 文件"
135
+ puts " 没有找到有效的 media 文件"
131
136
  puts " 提示: 请在项目根目录创建 JPSMedia/ 目录并放入要上传的图片或视频文件"
137
+ puts ""
132
138
  return {
133
139
  success: false,
134
140
  success_urls: [],
@@ -141,23 +147,23 @@ module Pindo
141
147
  }
142
148
  end
143
149
 
144
- puts " 准备上传 #{existing_files.size} 个文件"
145
- existing_files.each_with_index do |file, index|
146
- puts " #{index + 1}. #{File.basename(file)}"
147
- end
148
- puts
149
-
150
- # 5. 确保 PgyerHelper 已登录
150
+ # 7. 确保 PgyerHelper 已登录
151
151
  pgyer_helper = PgyerHelper.share_instace
152
152
  unless pgyer_helper.login
153
153
  raise "JPS 登录失败"
154
154
  end
155
155
 
156
- # 6. 调用 PgyerHelper start_media_upload 方法
156
+ # 8. workflow_info 提取 workflow_id
157
+ workflow_id = @workflow_info&.dig(:workflow_id) || @workflow_info&.dig('id')
158
+ unless workflow_id
159
+ raise "缺少 workflow_info,无法获取 workflow_id"
160
+ end
161
+
162
+ # 9. 调用 PgyerHelper 的 start_media_upload 方法
157
163
  upload_result = pgyer_helper.start_media_upload(
158
164
  file_paths: existing_files,
159
165
  git_commit_id: @git_commit_id,
160
- workflow_id: @workflow_id
166
+ workflow_id: workflow_id
161
167
  )
162
168
 
163
169
  # 7. 判断是否成功
@@ -200,43 +206,14 @@ module Pindo
200
206
  nil
201
207
  end
202
208
 
203
- # 获取 git HEAD 信息
204
- # @param git_root [String] git 仓库根目录
205
- # @return [Hash] 包含 :commit_id, :commit_time, :commit_desc
206
- def get_git_head_info(git_root)
207
- result = {
208
- commit_id: nil,
209
- commit_time: nil,
210
- commit_desc: nil
211
- }
212
-
213
- Dir.chdir(git_root) do
214
- # 获取 HEAD commit id
215
- commit_id = `git rev-parse HEAD 2>/dev/null`.strip
216
- result[:commit_id] = commit_id unless commit_id.empty?
217
-
218
- # 获取 commit time (ISO 8601 格式)
219
- commit_time = `git log -1 --format=%ci 2>/dev/null`.strip
220
- result[:commit_time] = commit_time unless commit_time.empty?
221
-
222
- # 获取 commit message (第一行)
223
- commit_desc = `git log -1 --format=%s 2>/dev/null`.strip
224
- result[:commit_desc] = commit_desc unless commit_desc.empty?
225
- end
226
-
227
- result
228
- end
229
-
230
209
  # 展开文件路径(支持通配符和目录)
231
- # 只查找创建时间或更新时间在 3 小时以内的文件
232
- # 超过时间的文件自动移动到 Backup_日期 子目录
210
+ # 自动过滤 3 小时以内的文件,超过 3 小时的文件归档
233
211
  # @param paths [Array<String>] 原始路径列表
234
- # @return [Array<String>] 展开后的文件路径列表
212
+ # @return [Array<String>] 展开后的文件路径列表(3 小时以内)
235
213
  def expand_file_paths(paths)
236
214
  return [] if paths.nil? || paths.empty?
237
215
 
238
216
  expanded = []
239
- time_threshold = Time.now - (3 * 60 * 60) # 3 小时前
240
217
 
241
218
  paths.each do |path|
242
219
  if path.include?('*')
@@ -257,29 +234,8 @@ module Pindo
257
234
  # 去重并过滤有效文件
258
235
  valid_files = expanded.uniq.select { |f| File.file?(f) }
259
236
 
260
- # 分离:3 小时以内的文件 和 超时的文件
261
- recent_files = []
262
- expired_files = []
263
-
264
- valid_files.each do |f|
265
- mtime = File.mtime(f)
266
- ctime = File.ctime(f)
267
- # 取创建时间和更新时间中较新的一个
268
- latest_time = [mtime, ctime].max
269
-
270
- if latest_time >= time_threshold
271
- recent_files << f
272
- else
273
- expired_files << f
274
- end
275
- end
276
-
277
- # 移动超时文件到备份目录
278
- if expired_files.any?
279
- move_expired_files_to_backup(expired_files)
280
- end
281
-
282
- recent_files
237
+ # 应用时间过滤(3 小时以内)并自动归档超时文件
238
+ filter_files_by_time(valid_files)
283
239
  end
284
240
 
285
241
  # 将超时文件移动到备份目录
@@ -287,10 +243,10 @@ module Pindo
287
243
  def move_expired_files_to_backup(files)
288
244
  return if files.empty?
289
245
 
290
- # 备份目录名:Backup_YYYY-MM-DD
291
- backup_dir_name = "Backup_#{Time.now.strftime('%Y-%m-%d')}"
246
+ # 备份目录名:backup_YYYY-MM-DD_HHMMSS
247
+ backup_dir_name = "backup_#{Time.now.strftime('%Y-%m-%d_%H%M%S')}"
292
248
 
293
- puts " 移动 #{files.size} 个超过 3 小时的文件到备份目录..."
249
+ puts " 📦 归档 #{files.size} 个超时文件 JPSMedia/#{backup_dir_name}/"
294
250
 
295
251
  files.each do |file_path|
296
252
  begin
@@ -305,41 +261,186 @@ module Pindo
305
261
  file_name = File.basename(file_path)
306
262
  dest_path = File.join(backup_dir, file_name)
307
263
 
308
- # 如果目标文件已存在,添加时间戳避免覆盖
264
+ # 如果目标文件已存在,添加序号避免覆盖
309
265
  if File.exist?(dest_path)
310
266
  ext = File.extname(file_name)
311
267
  base = File.basename(file_name, ext)
312
- timestamp = Time.now.strftime('%H%M%S')
313
- dest_path = File.join(backup_dir, "#{base}_#{timestamp}#{ext}")
268
+ counter = 1
269
+ while File.exist?(dest_path)
270
+ dest_path = File.join(backup_dir, "#{base}_#{counter}#{ext}")
271
+ counter += 1
272
+ end
314
273
  end
315
274
 
316
275
  FileUtils.mv(file_path, dest_path)
317
- puts " 已移动: #{file_name}"
276
+ puts " #{file_name}"
318
277
  rescue => e
319
- Funlog.instance.warning("移动文件失败: #{file_path} - #{e.message}")
278
+ puts " #{file_name} - #{e.message}"
320
279
  end
321
280
  end
322
281
  end
323
282
 
324
- # 在 JPSMedia 目录下查找 media 文件
283
+ # 在 JPSMedia 目录下查找 media 文件(仅查找根目录,不递归子目录)
284
+ # 自动过滤 3 小时以内的文件,超过 3 小时的文件归档
325
285
  # @param git_root [String] git 仓库根目录
326
- # @return [Array<String>] 找到的文件路径列表
286
+ # @return [Array<String>] 找到的文件路径列表(3 小时以内)
327
287
  def find_media_files(git_root)
328
288
  jps_media_dir = File.join(git_root, 'JPSMedia')
329
289
 
330
290
  unless File.directory?(jps_media_dir)
331
- puts " 未找到 JPSMedia/ 目录: #{jps_media_dir}"
291
+ puts " 未找到 JPSMedia/ 目录: #{jps_media_dir}"
332
292
  return []
333
293
  end
334
294
 
335
- puts " #{jps_media_dir} 目录查找 media 文件..."
295
+ puts " 🔍 查找 JPSMedia/ 目录下的媒体文件(3 小时以内)..."
336
296
 
337
- # 使用 glob 查找所有支持的媒体文件
338
- pattern = File.join(jps_media_dir, "**", "*.{#{MEDIA_EXTENSIONS.join(',')}}")
339
- files = Dir.glob(pattern, File::FNM_CASEFOLD)
297
+ # 只查找 JPSMedia/ 目录下的文件(不递归子目录)
298
+ pattern = File.join(jps_media_dir, "*.{#{MEDIA_EXTENSIONS.join(',')}}")
299
+ all_files = Dir.glob(pattern, File::FNM_CASEFOLD)
340
300
 
341
301
  # 过滤有效文件并去重
342
- files.uniq.select { |f| File.file?(f) }
302
+ valid_files = all_files.uniq.select { |f| File.file?(f) }
303
+
304
+ if valid_files.empty?
305
+ puts " ⚠️ 目录为空或没有支持的媒体文件"
306
+ return []
307
+ end
308
+
309
+ # 应用时间过滤(3 小时以内)并自动归档超时文件
310
+ filter_files_by_time(valid_files)
311
+ end
312
+
313
+ # 压缩媒体文件(批量压缩)
314
+ # @param files [Array<String>] 要压缩的文件列表
315
+ # @param git_root [String] git 仓库根目录
316
+ # @return [Array<String>] 压缩后的文件路径列表
317
+ def compress_media_files(files, git_root)
318
+ return [] if files.nil? || files.empty?
319
+
320
+ # 创建临时压缩输出目录: JPSMedia/compress_cli_YYYY-MM-DD_HHMMSS
321
+ # 放在 JPSMedia 目录下,避免污染 git 根目录
322
+ compress_dir_name = "compress_cli_#{Time.now.strftime('%Y-%m-%d_%H%M%S')}"
323
+ jps_media_dir = File.join(git_root, "JPSMedia")
324
+ compress_dir = File.join(jps_media_dir, compress_dir_name)
325
+
326
+ FileUtils.mkdir_p(compress_dir) unless Dir.exist?(compress_dir)
327
+
328
+ puts ""
329
+ puts " 📁 待压缩文件(共 #{files.size} 个)"
330
+ files.each_with_index do |file, index|
331
+ size_str = format_file_size(File.size(file))
332
+ file_name = File.basename(file)
333
+ puts " #{index + 1}. #{file_name} (#{size_str})"
334
+ end
335
+ puts ""
336
+
337
+ # MacCompressCliTool 路径
338
+ pindo_dir = File.expand_path(Pindoconfig.instance.pindo_dir)
339
+ compress_tool = File.join(pindo_dir, "pindo_common_config", "MacCompressCliTool")
340
+
341
+ # 检查压缩工具是否存在
342
+ unless File.exist?(compress_tool)
343
+ raise "未找到压缩工具: #{compress_tool}"
344
+ end
345
+
346
+ # 计算原始文件总大小
347
+ original_total_size = files.sum { |f| File.size(f) }
348
+
349
+ # 构建文件列表参数: -f file1.mp4,file2.png,file3.mov
350
+ file_list = files.map { |f| "\"#{f}\"" }.join(',')
351
+
352
+ # 调用压缩工具: compress-cli -s -f file1,file2,file3 -o output_dir/
353
+ # -s: 静默模式
354
+ # -f: 文件列表
355
+ # -o: 输出目录
356
+ # 使用默认参数: 分辨率等级 3 (1280x720), 质量等级 3
357
+ cmd = "#{compress_tool} -s -f #{file_list} -o \"#{compress_dir}\""
358
+
359
+ Funlog.instance.fancyinfo_start("正在压缩 #{files.size} 个文件...")
360
+ output = `#{cmd} 2>&1`
361
+ exit_code = $?.exitstatus
362
+
363
+ if exit_code != 0
364
+ Funlog.instance.fancyinfo_error("压缩失败")
365
+ puts " 错误信息: #{output.strip}" if output && !output.strip.empty?
366
+ raise "媒体文件批量压缩失败: #{output.strip}"
367
+ end
368
+
369
+ # 从临时目录获取所有压缩后的媒体文件
370
+ compressed_files = get_media_files_from_dir(compress_dir)
371
+
372
+ if compressed_files.empty?
373
+ Funlog.instance.fancyinfo_error("压缩失败:未找到压缩结果")
374
+ raise "压缩失败:未在输出目录中找到压缩结果"
375
+ end
376
+
377
+ # 计算压缩后总大小
378
+ compressed_total_size = compressed_files.sum { |f| File.size(f) }
379
+ total_ratio = ((1 - compressed_total_size.to_f / original_total_size) * 100).round(1)
380
+
381
+ # 显示压缩统计
382
+ Funlog.instance.fancyinfo_success("压缩完成:#{format_file_size(original_total_size)} → #{format_file_size(compressed_total_size)}(减少 #{total_ratio}%)")
383
+ puts ""
384
+
385
+ compressed_files
386
+ end
387
+
388
+ # 从目录获取所有媒体文件
389
+ # @param dir [String] 目录路径
390
+ # @return [Array<String>] 媒体文件路径列表
391
+ def get_media_files_from_dir(dir)
392
+ return [] unless Dir.exist?(dir)
393
+
394
+ # 使用 MEDIA_EXTENSIONS 常量构建匹配模式
395
+ pattern = File.join(dir, "*.{#{MEDIA_EXTENSIONS.join(',')}}")
396
+ Dir.glob(pattern).select { |f| File.file?(f) }.sort
397
+ end
398
+
399
+ # 格式化文件大小
400
+ # @param size [Integer] 字节数
401
+ # @return [String] 格式化后的大小
402
+ def format_file_size(size)
403
+ if size < 1024
404
+ "#{size}B"
405
+ elsif size < 1024 * 1024
406
+ "#{(size / 1024.0).round(1)}KB"
407
+ else
408
+ "#{(size / 1024.0 / 1024.0).round(1)}MB"
409
+ end
410
+ end
411
+
412
+ # 按时间过滤文件(3 小时以内)
413
+ # 超过 3 小时的文件自动移动到备份目录
414
+ # @param files [Array<String>] 文件列表
415
+ # @return [Array<String>] 3 小时以内的文件列表
416
+ def filter_files_by_time(files)
417
+ return [] if files.nil? || files.empty?
418
+
419
+ time_threshold = Time.now - (3 * 60 * 60) # 3 小时前
420
+
421
+ # 分离:3 小时以内的文件 和 超时的文件
422
+ recent_files = []
423
+ expired_files = []
424
+
425
+ files.each do |f|
426
+ mtime = File.mtime(f)
427
+ ctime = File.ctime(f)
428
+ # 取创建时间和更新时间中较新的一个
429
+ latest_time = [mtime, ctime].max
430
+
431
+ if latest_time >= time_threshold
432
+ recent_files << f
433
+ else
434
+ expired_files << f
435
+ end
436
+ end
437
+
438
+ # 移动超时文件到备份目录
439
+ if expired_files.any?
440
+ move_expired_files_to_backup(expired_files)
441
+ end
442
+
443
+ recent_files
343
444
  end
344
445
 
345
446
  end
@@ -1,5 +1,4 @@
1
1
  require 'pindo/module/task/pindo_task'
2
- require 'pindo/module/pgyer/pgyerhelper'
3
2
 
4
3
  module Pindo
5
4
  module TaskSystem
@@ -1,6 +1,7 @@
1
1
  require 'pindo/module/task/pindo_task'
2
2
  require 'pindo/module/unity/unity_helper'
3
3
  require 'pindo/module/unity/unity_proc_helper'
4
+ require 'pindo/base/git_handler'
4
5
 
5
6
  module Pindo
6
7
  module TaskSystem
@@ -10,12 +11,22 @@ module Pindo
10
11
  attr_reader :project_path, :unity_root_path
11
12
 
12
13
  def initialize(name, options = {})
13
- @project_path = options[:project_path] ? File.expand_path(options[:project_path]) : nil
14
- @unity_root_path = options[:unity_root_path] || @project_path
14
+ @project_path = options[:project_path] ? normalize_path(options[:project_path]) : nil
15
+ @unity_root_path = options[:unity_root_path] ? normalize_path(options[:unity_root_path]) : @project_path
15
16
 
16
17
  super(name, options)
17
18
  end
18
19
 
20
+ # 规范化路径辅助方法
21
+ def normalize_path(path)
22
+ return nil if path.nil?
23
+ begin
24
+ File.realpath(File.expand_path(path))
25
+ rescue
26
+ File.expand_path(path)
27
+ end
28
+ end
29
+
19
30
  # Unity 任务类型
20
31
  def self.task_type
21
32
  :unity
@@ -59,6 +70,17 @@ module Pindo
59
70
  @unity_helper ||= Pindo::Unity::UnityHelper.share_instance
60
71
  end
61
72
 
73
+ # 声明所需资源(锁住项目目录,防止并发执行)
74
+ def required_resources
75
+ # 1. Unity 资源:锁定 Unity 项目目录
76
+ # 2. Build 资源:锁定构建目录(git 仓库或项目目录)
77
+ build_lock_dir = get_build_lock_directory
78
+ (super || []) + [
79
+ { type: :unity, directory: @unity_root_path },
80
+ { type: :build, directory: build_lock_dir }
81
+ ]
82
+ end
83
+
62
84
  # 重试前清理 Unity 进程
63
85
  def before_retry
64
86
  super
@@ -67,6 +89,20 @@ module Pindo
67
89
 
68
90
  protected
69
91
 
92
+ # 获取 build 资源锁定的目录
93
+ # 优先使用 git 根目录(如果在 git 仓库内),否则使用 Unity 根目录
94
+ # 这样可以确保同一 git 仓库下的所有构建任务串行执行
95
+ def get_build_lock_directory
96
+ return @unity_root_path unless @unity_root_path
97
+
98
+ begin
99
+ git_root = Pindo::GitHandler.git_root_directory(local_repo_dir: @unity_root_path)
100
+ git_root || @unity_root_path
101
+ rescue
102
+ @unity_root_path
103
+ end
104
+ end
105
+
70
106
  # 检查 Unity 项目是否有效
71
107
  def valid_unity_project?
72
108
  # 检查是否存在 Assets 和 ProjectSettings 目录