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
@@ -5,6 +5,9 @@ module Pindo
5
5
  # 任务取消异常
6
6
  class TaskCancelledException < StandardError; end
7
7
 
8
+ # 资源锁超时异常
9
+ class ResourceLockTimeout < StandardError; end
10
+
8
11
  # 任务状态
9
12
  module TaskStatus
10
13
  PENDING = :pending # 待执行
@@ -32,13 +35,47 @@ module Pindo
32
35
  # PindoTask 基类(简化版,所有任务在主线程中执行)
33
36
  class PindoTask
34
37
  attr_accessor :id, :name, :status, :error, :result
35
- attr_reader :type, :task_key, :priority, :dependencies, :data_dependencies
38
+ attr_reader :type, :task_key, :priority, :dependencies
36
39
  attr_accessor :context, :metadata
37
40
  attr_reader :created_at, :started_at, :finished_at
38
41
  attr_accessor :retry_count # 剩余重试次数
39
42
  attr_reader :retry_mode, :retry_delay, :max_retry_count # max_retry_count: 初始最大重试次数
40
43
  attr_accessor :callbacks_setup # 标记回调是否已经设置
41
44
  attr_accessor :task_manager # TaskManager 实例(依赖注入)
45
+ attr_accessor :skip_count # 因资源不足被跳过的次数
46
+
47
+ # 获取任务需要的资源(子类覆盖以声明所需资源)
48
+ # @return [Array<Hash>] 资源规格数组
49
+ #
50
+ # 资源规格格式:
51
+ # { type: :xcode, directory: "/path/to/project" } # 基于目录的资源
52
+ # { type: :keychain } # 全局资源
53
+ #
54
+ # @example 基于目录的资源(相同目录互斥)
55
+ # class IOSBuildTask < PindoTask
56
+ # def initialize(name:, project_path:)
57
+ # super(name)
58
+ # @project_path = project_path
59
+ # end
60
+ #
61
+ # def required_resources
62
+ # [
63
+ # { type: :xcode, directory: @project_path },
64
+ # { type: :git, directory: @project_path },
65
+ # { type: :keychain }
66
+ # ]
67
+ # end
68
+ # end
69
+ #
70
+ # @example 全局资源
71
+ # class UploadTask < PindoTask
72
+ # def required_resources
73
+ # [{ type: :network }]
74
+ # end
75
+ # end
76
+ def required_resources
77
+ [] # 默认不需要资源
78
+ end
42
79
 
43
80
  def initialize(name, options = {})
44
81
  @id = SecureRandom.uuid
@@ -48,7 +85,6 @@ module Pindo
48
85
  @priority = options[:priority] || TaskPriority::HIGH
49
86
  @status = TaskStatus::PENDING
50
87
  @dependencies = options[:dependencies] || []
51
- @data_dependencies = options[:data_dependencies] || []
52
88
  @context = options[:context] || {}
53
89
  @metadata = options[:metadata] || {}
54
90
  @error = nil
@@ -69,6 +105,7 @@ module Pindo
69
105
  @retry_count = options[:retry_count] || self.class.default_retry_count
70
106
  @max_retry_count = @retry_count # 保存初始最大重试次数
71
107
  @retry_delay = options[:retry_delay] || self.class.default_retry_delay
108
+ @skip_count = 0 # 记录因资源不足被跳过的次数(用于解决饥饿问题)
72
109
  end
73
110
 
74
111
  # 子类必须实现的方法
@@ -196,17 +233,17 @@ module Pindo
196
233
  dep_task.data_param
197
234
  end
198
235
 
199
- # 获取所有数据依赖任务的数据参数
236
+ # 获取所有依赖任务的数据参数
200
237
  # @return [Array<Hash>] 数据参数数组
201
238
  def get_all_data_params
202
- @data_dependencies.map { |task_id| get_data_param(task_id) }.compact
239
+ @dependencies.map { |task_id| get_data_param(task_id) }.compact
203
240
  end
204
241
 
205
- # 根据 task_key 获取数据依赖任务的数据参数
242
+ # 根据 task_key 获取依赖任务的数据参数
206
243
  # @param task_key [Symbol] 任务键
207
244
  # @return [Hash, nil] 第一个匹配的任务数据参数
208
245
  def get_data_param_by_key(task_key)
209
- @data_dependencies.each do |task_id|
246
+ @dependencies.each do |task_id|
210
247
  param = get_data_param(task_id)
211
248
  return param if param && param[:task_key] == task_key
212
249
  end
@@ -220,11 +257,98 @@ module Pindo
220
257
  get_all_data_params.select { |param| param[:task_key] == task_key }
221
258
  end
222
259
 
223
- # 获取主数据参数(第一个数据依赖任务的参数)
260
+ # 获取主数据参数(第一个依赖任务的参数)
224
261
  # @return [Hash, nil] 主数据参数
225
262
  def primary_data_param
226
- return nil if @data_dependencies.empty?
227
- get_data_param(@data_dependencies.first)
263
+ return nil if @dependencies.empty?
264
+ get_data_param(@dependencies.first)
265
+ end
266
+
267
+ # ========== 动态资源管理 ==========
268
+
269
+ # 临时锁定资源(块语法,推荐使用)
270
+ # 在代码块执行期间锁定资源,自动释放
271
+ #
272
+ # @param resource_spec [Hash, Array<Hash>] 资源规格
273
+ # @param timeout [Integer] 超时时间(秒)
274
+ # @yield 持有资源时执行的代码块
275
+ # @raise [ResourceLockTimeout] 如果无法获取资源
276
+ # @raise [RuntimeError] 如果任务未运行或未提供代码块
277
+ #
278
+ # @example 单个资源
279
+ # with_resource({ type: :unity, directory: @path }) do
280
+ # build_unity
281
+ # end
282
+ #
283
+ # @example 多个资源
284
+ # with_resources([
285
+ # { type: :xcode, directory: @path },
286
+ # { type: :keychain }
287
+ # ]) do
288
+ # sign_ipa
289
+ # end
290
+ def with_resource(resource_spec, timeout: 30)
291
+ raise ArgumentError, "Block required" unless block_given?
292
+ raise RuntimeError, "Task not running (task_manager is nil)" unless @task_manager
293
+
294
+ resource_manager = @task_manager.resource_lock_manager
295
+ resource_specs = [resource_spec].flatten
296
+
297
+ # 阻塞式获取资源
298
+ success = resource_manager.acquire_blocking(resource_specs, @id, timeout: timeout)
299
+
300
+ unless success
301
+ raise ResourceLockTimeout, "无法获取资源: #{resource_specs.inspect}(超时#{timeout}秒)"
302
+ end
303
+
304
+ begin
305
+ yield # 执行代码块
306
+ ensure
307
+ # 自动释放临时资源
308
+ resource_manager.release_partial(resource_specs, @id)
309
+ end
310
+ end
311
+
312
+ # 批量临时锁定资源(with_resource 的别名)
313
+ # @param resource_specs [Array<Hash>] 资源规格数组
314
+ # @param timeout [Integer] 超时时间(秒)
315
+ # @yield 持有资源时执行的代码块
316
+ def with_resources(resource_specs, timeout: 30, &block)
317
+ with_resource(resource_specs, timeout: timeout, &block)
318
+ end
319
+
320
+ # 提前释放资源
321
+ # 释放 required_resources 中声明的资源,允许其他任务使用
322
+ #
323
+ # @param resource_spec [Hash, Array<Hash>] 要释放的资源规格
324
+ # @raise [RuntimeError] 如果任务未运行
325
+ #
326
+ # @example 释放单个资源
327
+ # def do_work
328
+ # git_pull
329
+ # release_resource({ type: :git, directory: @path })
330
+ #
331
+ # xcode_build # Git 已释放,其他任务可以使用
332
+ # end
333
+ #
334
+ # @example 释放多个资源
335
+ # release_resources([
336
+ # { type: :git, directory: @path },
337
+ # { type: :xcode, directory: @path }
338
+ # ])
339
+ def release_resource(resource_spec)
340
+ raise RuntimeError, "Task not running (task_manager is nil)" unless @task_manager
341
+
342
+ resource_manager = @task_manager.resource_lock_manager
343
+ resource_specs = [resource_spec].flatten
344
+
345
+ resource_manager.release_partial(resource_specs, @id)
346
+ end
347
+
348
+ # 批量释放资源(release_resource 的别名)
349
+ # @param resource_specs [Array<Hash>] 资源规格数组
350
+ def release_resources(resource_specs)
351
+ release_resource(resource_specs)
228
352
  end
229
353
 
230
354
  # ========== 回调方法 ==========
@@ -1,82 +1,124 @@
1
1
  require 'singleton'
2
2
  require 'pindo/base/funlog'
3
+ require_relative 'core/task_queue'
4
+ require_relative 'core/dependency_checker'
5
+ require_relative 'core/task_executor'
6
+ require_relative 'core/execution_strategy'
7
+ require_relative 'core/resource_lock_manager'
8
+ require_relative 'task_reporter'
9
+ require_relative 'output/multi_line_output_manager'
3
10
 
4
11
  module Pindo
5
12
  module TaskSystem
6
- # 简化版任务管理器
7
- # 所有任务在主线程中按顺序执行
13
+ # TaskManager - 任务管理器
14
+ #
15
+ # 职责:
16
+ # - 提供公共 API
17
+ # - 组合各个模块(队列、依赖检查、执行器、报告、资源锁)
18
+ # - 创建执行策略
8
19
  class TaskManager
9
20
  include Singleton
10
21
 
22
+ attr_reader :queue, :dependency_checker, :reporter, :resource_lock_manager
23
+
11
24
  def initialize
12
- @pending_queue = [] # 待执行队列
13
- @completed_tasks = [] # 已完成的任务
14
- @current_task = nil # 当前正在执行的任务
25
+ @queue = TaskQueue.new # 队列管理
26
+ @resource_lock_manager = ResourceLockManager.new # 资源锁管理
27
+ @dependency_checker = DependencyChecker.new(@queue, @resource_lock_manager) # 依赖检查
28
+ @reporter = TaskReporter.new(@queue) # 报告输出
29
+ @output_manager = nil # 输出管理器
30
+ end
31
+
32
+ # ==================== 公共 API ====================
33
+
34
+ # 获取执行器(延迟初始化)
35
+ # @return [TaskExecutor] 任务执行器
36
+ def executor
37
+ @executor ||= TaskExecutor.new(@queue, @reporter, @resource_lock_manager, @output_manager)
38
+ end
39
+
40
+ # 启用输出管理系统
41
+ # @param options [Hash] 配置选项
42
+ def enable_output_management(options = {})
43
+ @output_manager = MultiLineOutputManager.new(
44
+ log_dir: options[:log_dir] || './pindo_logs',
45
+ max_lines_per_task: options[:max_lines_per_task] || 5,
46
+ max_recent_completed: options[:max_recent_completed] || 3,
47
+ auto_adjust: options.fetch(:auto_adjust, true)
48
+ )
49
+ # 更新执行器的输出管理器(如果已初始化)
50
+ @executor.output_manager = @output_manager if @executor
51
+ end
52
+
53
+ # 禁用输出管理系统
54
+ def disable_output_management
55
+ @output_manager = nil
56
+ @executor.output_manager = nil if @executor
15
57
  end
16
58
 
17
59
  # 添加任务
60
+ # @param task [PindoTask] 任务对象
61
+ # @param options [Hash] 选项
62
+ # @return [String] 任务 ID
18
63
  def add_task(task, options = {})
19
64
  raise ArgumentError, "Task must be a PindoTask" unless task.is_a?(PindoTask)
20
65
 
21
- # 验证任务
22
66
  unless task.validate
23
67
  raise ArgumentError, "Task validation failed: #{task.name}"
24
68
  end
25
69
 
26
- # 处理依赖
27
70
  if options[:wait_for]
28
71
  task.dependencies.concat(Array(options[:wait_for]))
29
72
  end
30
73
 
31
- @pending_queue << task
32
- # 按优先级排序
33
- @pending_queue.sort_by! { |t| -t.priority }
34
-
35
- task.id
74
+ @queue.add(task, sort_by_priority: true)
36
75
  end
37
76
 
38
77
  # 批量添加任务
78
+ # @param tasks [Array<PindoTask>] 任务数组
79
+ # @return [Array<String>] 任务 ID 数组
39
80
  def add_tasks(tasks)
40
81
  tasks.map { |task| add_task(task) }
41
82
  end
42
83
 
43
- # 开始执行(在主线程中顺序执行所有任务)
44
- def start
45
- # 输出任务执行计划
46
- print_execution_plan
47
-
48
- # 主循环:按顺序执行所有任务
49
- while @pending_queue.any?
50
- task = get_next_executable_task
84
+ # 开始执行任务
85
+ # @param options [Hash] 执行选项
86
+ # @option options [Symbol] :mode 执行模式 (:serial, :concurrent)
87
+ # @option options [Boolean] :concurrent 快捷参数
88
+ # @option options [Integer] :max_workers 最大工作线程数
89
+ def start(options = {})
90
+ mode = parse_execution_mode(options)
91
+ strategy = ExecutionStrategy.create(mode, options)
51
92
 
52
- unless task
53
- # 检查是否所有任务都因依赖问题无法执行
54
- if @pending_queue.all? { |t| check_dependencies(t) != :ready }
55
- Funlog.warning("所有剩余任务都因依赖问题无法执行")
56
- break
57
- end
58
-
59
- # 没有可执行的任务,短暂休眠后重试
60
- sleep(0.1)
61
- next
93
+ # 如果配置了输出管理器,注册所有任务
94
+ if @output_manager
95
+ @queue.pending_snapshot.each do |task|
96
+ @output_manager.register_task(task)
62
97
  end
63
-
64
- # 执行任务
65
- execute_task(task)
66
98
  end
67
99
 
100
+ # 输出任务执行计划
101
+ @reporter.print_execution_plan(strategy)
102
+
103
+ # 执行策略
104
+ strategy.execute(self)
105
+
68
106
  # 输出执行摘要
69
- print_execution_summary
107
+ @reporter.print_execution_summary
70
108
  end
71
109
 
72
110
  # 获取执行报告
111
+ # @return [Hash] 执行报告
73
112
  def execution_report
113
+ pending = @queue.pending_snapshot
114
+ completed = @queue.completed_snapshot
115
+
74
116
  {
75
- pending: @pending_queue.count,
76
- completed: @completed_tasks.count,
77
- success: @completed_tasks.count { |t| t.status == TaskStatus::SUCCESS },
78
- failed: @completed_tasks.count { |t| t.status == TaskStatus::FAILED },
79
- tasks: (@pending_queue + @completed_tasks).map do |task|
117
+ pending: pending.count,
118
+ completed: completed.count,
119
+ success: completed.count { |t| t.status == TaskStatus::SUCCESS },
120
+ failed: completed.count { |t| t.status == TaskStatus::FAILED },
121
+ tasks: (pending + completed).map do |task|
80
122
  {
81
123
  id: task.id,
82
124
  name: task.name,
@@ -89,260 +131,48 @@ module Pindo
89
131
  }
90
132
  end
91
133
 
92
- # 清空所有队列(用于重置)
134
+ # 清空所有队列
93
135
  def clear_all
94
- @pending_queue.clear
95
- @completed_tasks.clear
96
- @current_task = nil
136
+ @queue.clear_all
97
137
  end
98
138
 
99
139
  # 获取任务状态
140
+ # @param task_id [String] 任务 ID
141
+ # @return [Symbol, nil] 任务状态
100
142
  def task_status(task_id)
101
143
  task = find_task(task_id)
102
144
  task&.status
103
145
  end
104
146
 
105
147
  # 取消任务
148
+ # @param task_id [String] 任务 ID
106
149
  def cancel_task(task_id)
107
150
  task = find_task(task_id)
108
151
  task&.cancel
109
152
  end
110
153
 
111
- # 查找任务(公共方法,供 PindoTask 获取依赖任务使用)
154
+ # 查找任务
112
155
  # @param task_id [String] 任务 ID
113
156
  # @return [PindoTask, nil] 任务对象
114
157
  def find_task(task_id)
115
- all_tasks = @pending_queue + @completed_tasks
116
- all_tasks << @current_task if @current_task
117
- all_tasks.find { |t| t.id == task_id }
158
+ @queue.find(task_id)
118
159
  end
119
160
 
120
161
  private
121
162
 
122
- # 获取下一个可执行的任务
123
- def get_next_executable_task
124
- task = @pending_queue.find { |t| can_execute?(t) }
125
- @pending_queue.delete(task) if task
126
- task
127
- end
163
+ # ==================== 辅助方法(私有)====================
128
164
 
129
- # 判断任务是否可以执行
130
- def can_execute?(task)
131
- # 检查依赖状态
132
- dependency_check = check_dependencies(task)
133
-
134
- if dependency_check == :failed
135
- # 依赖失败,标记任务为失败
136
- mark_task_failed(task, "依赖任务失败")
137
- return false
138
- elsif dependency_check == :cancelled
139
- # 依赖被取消,标记任务为取消
140
- mark_task_cancelled(task, "依赖任务被取消")
141
- return false
142
- elsif dependency_check == :waiting
143
- return false # 依赖未完成,继续等待
144
- end
145
-
146
- # 依赖已满足,可以执行
147
- true
148
- end
149
-
150
- # 检查依赖状态
151
- def check_dependencies(task)
152
- return :ready if task.dependencies.empty?
153
-
154
- task.dependencies.each do |dep_id|
155
- dep_task = find_task(dep_id)
156
-
157
- # 依赖任务不存在
158
- return :failed unless dep_task
159
-
160
- # 检查依赖任务状态
161
- case dep_task.status
162
- when TaskStatus::SUCCESS
163
- next # 这个依赖已完成
164
- when TaskStatus::FAILED
165
- return :failed # 依赖失败
166
- when TaskStatus::CANCELLED
167
- return :cancelled # 依赖被取消
168
- else
169
- return :waiting # 依赖未完成
170
- end
171
- end
172
-
173
- :ready # 所有依赖都成功完成
174
- end
175
-
176
- # 执行任务
177
- def execute_task(task)
178
- @current_task = task
179
-
180
- # 注入 TaskManager 实例(依赖注入)
181
- task.task_manager = self
182
-
183
- # 设置任务进度回调
184
- setup_task_callbacks(task)
185
-
186
- # 显示任务开始信息
187
- print_task_header(task)
188
-
189
- begin
190
- # 在主线程中执行任务
191
- task.do_task
192
-
193
- # 任务成功
194
- print_task_success(task)
195
-
196
- rescue => e
197
- # 任务失败
198
- print_task_failure(task, e)
199
- end
200
-
201
- # 将任务移到完成队列
202
- @completed_tasks << task
203
- @current_task = nil
204
-
205
- # 任务完成后输出分隔线
206
- print_task_footer
207
- end
208
-
209
- # 设置任务回调
210
- def setup_task_callbacks(task)
211
- # 只在第一次设置回调
212
- return if task.callbacks_setup
213
-
214
- # 主线程执行不需要进度回调
215
-
216
- task.callbacks_setup = true
217
- end
218
-
219
- # 标记任务为失败
220
- def mark_task_failed(task, reason)
221
- @pending_queue.delete(task)
222
- task.status = TaskStatus::FAILED
223
- task.error = RuntimeError.new(reason)
224
- @completed_tasks << task
225
-
226
- Funlog.error("任务 #{task.name} 因#{reason}而被标记为失败")
227
- end
228
-
229
- # 标记任务为取消
230
- def mark_task_cancelled(task, reason)
231
- @pending_queue.delete(task)
232
- task.status = TaskStatus::CANCELLED
233
- task.error = RuntimeError.new(reason)
234
- @completed_tasks << task
235
-
236
- Funlog.warning("任务 #{task.name} 因#{reason}而被取消")
237
- end
238
-
239
- # 输出任务执行计划
240
- def print_execution_plan
241
- # 按类型分组统计
242
- tasks_by_type = @pending_queue.group_by(&:type)
243
-
244
- puts "\n"
245
- puts "\e[34m" + "=" * 60
246
- puts " 任务执行计划"
247
- puts "=" * 60
248
-
249
- tasks_by_type.each do |type, tasks|
250
- # 直接从任务类获取显示名称
251
- type_name = tasks.first&.class&.task_type_name || type.to_s.capitalize
252
- puts "\n #{type_name}: #{tasks.count} 个任务"
253
-
254
- # 显示该类型下的每个任务名称
255
- tasks.each do |task|
256
- puts " - #{task.name}"
257
- end
258
- end
259
-
260
- puts "\n 总计: #{@pending_queue.count} 个任务"
261
- puts "=" * 60 + "\e[0m"
262
- puts "\n"
263
- end
264
-
265
- # 输出任务执行头部
266
- def print_task_header(task)
267
- puts "\n"
268
- puts "\e[34m" + "*" * 60
269
- puts " ▶ #{task.name}"
270
- puts "*" * 60 + "\e[0m"
271
- end
272
-
273
- # 输出任务成功信息
274
- def print_task_success(task)
275
- time_str = task.execution_time ? task.execution_time.round(2) : 0
276
- puts "\n"
277
- puts "\e[34m" + " ✓ 任务完成: #{task.name} (耗时: #{time_str}秒)" + "\e[0m"
278
- end
279
-
280
- # 输出任务失败信息
281
- def print_task_failure(task, error)
282
- puts "\n"
283
- Funlog.error("任务失败: #{task.name}")
284
- Funlog.error("错误信息: #{error.message}")
285
-
286
- # 打印堆栈信息用于调试(仅在 PINDO_DEBUG 模式下)
287
- if ENV['PINDO_DEBUG'] && error.backtrace && !error.backtrace.empty?
288
- puts "\n堆栈信息:"
289
- error.backtrace.first(10).each do |line|
290
- puts " #{line}"
291
- end
292
- end
293
- end
294
-
295
- # 输出任务底部分隔线
296
- def print_task_footer
297
- puts "\e[34m" + "*" * 60 + "\e[0m"
298
- puts ""
299
- end
300
-
301
- # 输出执行摘要
302
- def print_execution_summary
303
- success_count = @completed_tasks.count { |t| t.status == TaskStatus::SUCCESS }
304
- failed_count = @completed_tasks.count { |t| t.status == TaskStatus::FAILED }
305
- cancelled_count = @completed_tasks.count { |t| t.status == TaskStatus::CANCELLED }
306
-
307
- # 获取失败和取消的任务
308
- failed_tasks = @completed_tasks.select { |t| t.status == TaskStatus::FAILED }
309
- cancelled_tasks = @completed_tasks.select { |t| t.status == TaskStatus::CANCELLED }
310
-
311
- # 计算总耗时
312
- total_time = @completed_tasks.map(&:execution_time).compact.sum
313
- minutes = (total_time / 60).to_i
314
- seconds = (total_time % 60).to_i
315
-
316
- puts "\n"
317
- puts "\e[34m" + "=" * 60
318
- puts "\e[34m" + " 任务执行完成"
319
- puts "\e[34m" + "=" * 60
320
-
321
- # 显示统计信息
322
- puts "\e[34m" + " 成功: #{success_count} 个任务" + "\e[0m" if success_count > 0
323
-
324
- if failed_count > 0
325
- puts "\e[31m" + " 失败: #{failed_count} 个任务" + "\e[0m"
326
- failed_tasks.each do |task|
327
- puts "\e[31m" + " - #{task.name}" + "\e[0m"
328
- end
329
- end
330
-
331
- if cancelled_count > 0
332
- puts "\e[33m" + " 取消: #{cancelled_count} 个任务" + "\e[0m"
333
- cancelled_tasks.each do |task|
334
- puts "\e[33m" + " - #{task.name}" + "\e[0m"
335
- end
336
- end
337
-
338
- if minutes > 0
339
- puts "\e[34m" + " 总耗时: #{minutes}分#{seconds}秒" + "\e[0m"
165
+ # 解析执行模式
166
+ # @param options [Hash] 选项
167
+ # @return [Symbol] 执行模式
168
+ def parse_execution_mode(options)
169
+ if options[:mode]
170
+ options[:mode]
171
+ elsif options.key?(:concurrent)
172
+ options[:concurrent] ? :concurrent : :serial
340
173
  else
341
- puts "\e[34m" + " 总耗时: #{seconds}秒" + "\e[0m"
174
+ :serial # 默认串行
342
175
  end
343
-
344
- puts "\e[34m" + "=" * 60 + "\e[0m"
345
- puts "\n"
346
176
  end
347
177
  end
348
178
  end