taski 0.8.0 → 0.8.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f7677c0f3995a7c61b5850fb25c82c60626287584e5c7f918939ededc045e9ea
4
- data.tar.gz: d470969c88a4df51503487aacdf2c6261b65e6ed75ee3c7cc2ff744dfd750365
3
+ metadata.gz: 1ecbd73a50d1f39207625cda94e9f90a90ce212d270a70ed49cd94705bb30a5c
4
+ data.tar.gz: d5efd7fcf8c5c0d5bdfacf147e302375e63d41acd00f428924e87ba3a4e54ac0
5
5
  SHA512:
6
- metadata.gz: 5f93d9e7d10f7104851266f0252ec4365d445a90d3a25b885f289df5d8caafbe9f9250ff9236e4cca0791749100c83c46735a8e98e5c1b675662199ba7a0e547
7
- data.tar.gz: eb4fb9f806cdd5097e0d3321d4cda9e3df183d4850c4aa8161ba09b71666444166f05b5e795b330227076d85f7410adbe2fc4f1e2b19c66d007ff1632d354f97
6
+ metadata.gz: e263e9c974f17ff46f545adc3cf90cddf66cbfe273c783d07f036a2d3fc4c7f8ec4759c1a94ff72ab0b302d8c4fae50a4654cda5b4b17d9f218ae897ba67572a
7
+ data.tar.gz: 9bf8d6b053ee781ef9ba2535926ef663be9ed45d88249d59a5325d7cbd600468ae5bacc69e506ae5bc7f2819707b9e4451a0faf0aa377dbbfb33fe04a7d9199b
data/CHANGELOG.md CHANGED
@@ -7,6 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.8.2] - 2026-01-26
11
+
12
+ ### Fixed
13
+ - Queue `Taski.message` output until progress display stops to prevent interleaved output ([#133](https://github.com/ahogappa/taski/pull/133))
14
+ - Correct task count display in SimpleProgressDisplay ([#132](https://github.com/ahogappa/taski/pull/132))
15
+
16
+ ### Changed
17
+ - Consolidate examples from 15 to 8 files for better maintainability ([#131](https://github.com/ahogappa/taski/pull/131))
18
+
19
+ ## [0.8.1] - 2026-01-26
20
+
21
+ ### Added
22
+ - `Taski.message` API for user-facing output during task execution ([#129](https://github.com/ahogappa/taski/pull/129))
23
+
24
+ ### Fixed
25
+ - Count unselected section candidates as completed in SimpleProgressDisplay ([#128](https://github.com/ahogappa/taski/pull/128))
26
+ - Prioritize environment variable over code settings for progress_mode ([#127](https://github.com/ahogappa/taski/pull/127))
27
+
10
28
  ## [0.8.0] - 2026-01-23
11
29
 
12
30
  ### Added
data/README.md CHANGED
@@ -262,6 +262,34 @@ end
262
262
 
263
263
  When `TaskAbortException` is raised, no new tasks will start. Already running tasks will complete, then execution stops.
264
264
 
265
+ ### Lifecycle Management
266
+
267
+ Define `clean` methods for resource cleanup. Clean runs in reverse dependency order:
268
+
269
+ ```ruby
270
+ class DatabaseSetup < Taski::Task
271
+ exports :connection
272
+
273
+ def run
274
+ @connection = connect_to_database
275
+ end
276
+
277
+ def clean
278
+ @connection&.close
279
+ end
280
+ end
281
+
282
+ # Run then clean in one call
283
+ DatabaseSetup.run_and_clean
284
+
285
+ # Or separately
286
+ DatabaseSetup.run
287
+ # ... do work ...
288
+ DatabaseSetup.clean
289
+ ```
290
+
291
+ See [docs/guide.md](docs/guide.md#lifecycle-management) for details.
292
+
265
293
  ### Progress Display
266
294
 
267
295
  Tree-based progress visualization is enabled by default:
@@ -281,14 +309,27 @@ WebServer (Task)
281
309
  ✓ [5/5] All tasks completed (1234ms)
282
310
  ```
283
311
 
312
+ **Plain mode** provides text output without escape codes (for CI/logs):
313
+
314
+ ```
315
+ [START] DatabaseSetup
316
+ [DONE] DatabaseSetup (45.2ms)
317
+ [START] WebServer
318
+ [DONE] WebServer (120.5ms)
319
+ [TASKI] Completed: 2/2 tasks (165ms)
320
+ ```
321
+
284
322
  **Configuration:**
285
323
 
286
324
  ```ruby
287
325
  # Via API
288
- Taski.progress_mode = :simple # or :tree (default)
326
+ Taski.progress_mode = :tree # Tree display (default)
327
+ Taski.progress_mode = :simple # Single-line display
328
+ Taski.progress_mode = :plain # Plain text (CI/logs)
289
329
 
290
330
  # Via environment variable
291
331
  TASKI_PROGRESS_MODE=simple ruby your_script.rb
332
+ TASKI_PROGRESS_MODE=plain ruby your_script.rb
292
333
  ```
293
334
 
294
335
  To disable: `TASKI_PROGRESS_DISABLE=1 ruby your_script.rb`
data/docs/GUIDE.md CHANGED
@@ -324,20 +324,34 @@ On failure:
324
324
  ✗ [3/5] DeployTask failed: Connection refused
325
325
  ```
326
326
 
327
+ #### Plain Mode
328
+
329
+ Plain text output without escape codes, designed for CI/logs:
330
+
331
+ ```
332
+ [START] DatabaseSetup
333
+ [DONE] DatabaseSetup (45.2ms)
334
+ [START] WebServer
335
+ [DONE] WebServer (120.5ms)
336
+ [TASKI] Completed: 2/2 tasks (165ms)
337
+ ```
338
+
327
339
  ### Configuring Progress Mode
328
340
 
329
341
  **Via API:**
330
342
 
331
343
  ```ruby
332
- Taski.progress_mode = :simple # Use simple mode
333
- Taski.progress_mode = :tree # Use tree mode (default)
344
+ Taski.progress_mode = :tree # Tree display (default)
345
+ Taski.progress_mode = :simple # Single-line display
346
+ Taski.progress_mode = :plain # Plain text (CI/logs)
334
347
  ```
335
348
 
336
349
  **Via environment variable:**
337
350
 
338
351
  ```bash
339
- TASKI_PROGRESS_MODE=simple ruby your_script.rb
340
352
  TASKI_PROGRESS_MODE=tree ruby your_script.rb
353
+ TASKI_PROGRESS_MODE=simple ruby your_script.rb
354
+ TASKI_PROGRESS_MODE=plain ruby your_script.rb
341
355
  ```
342
356
 
343
357
  ### Disabling Progress Display
@@ -363,7 +377,7 @@ ruby build.rb > build.log 2>&1
363
377
  | Variable | Purpose |
364
378
  |----------|---------|
365
379
  | `TASKI_PROGRESS_DISABLE=1` | Disable progress display |
366
- | `TASKI_PROGRESS_MODE=simple\|tree` | Set progress display mode (default: tree) |
380
+ | `TASKI_PROGRESS_MODE=tree\|simple\|plain` | Set progress display mode (default: tree) |
367
381
  | `TASKI_DEBUG=1` | Enable debug output |
368
382
 
369
383
  ### Dependency Tree Visualization
data/examples/README.md CHANGED
@@ -67,78 +67,67 @@ TASKI_PROGRESS_DISABLE=1 ruby examples/reexecution_demo.rb
67
67
 
68
68
  ---
69
69
 
70
- ### 5. data_pipeline_demo.rb - Real-World Pipeline
70
+ ### 5. clean_demo.rb - Lifecycle Management
71
71
 
72
- A realistic ETL pipeline with parallel data fetching.
73
-
74
- ```bash
75
- ruby examples/data_pipeline_demo.rb
76
- ```
77
-
78
- **Covers:**
79
- - Multiple data sources in parallel
80
- - Data transformation stages
81
- - Aggregation and reporting
82
-
83
- ---
84
-
85
- ### 6. parallel_progress_demo.rb - Progress Display
86
-
87
- Real-time progress visualization during parallel execution.
72
+ Demonstrates resource cleanup with clean methods.
88
73
 
89
74
  ```bash
90
- ruby examples/parallel_progress_demo.rb
75
+ ruby examples/clean_demo.rb
91
76
  ```
92
77
 
93
78
  **Covers:**
94
- - Parallel task execution
95
- - Progress display with spinners
96
- - Execution timing
79
+ - Defining `clean` methods for resource cleanup
80
+ - Reverse dependency order execution
81
+ - `run_and_clean` combined operation
97
82
 
98
83
  ---
99
84
 
100
- ### 7. clean_demo.rb - Lifecycle Management
85
+ ### 6. group_demo.rb - Task Output Grouping
101
86
 
102
- Demonstrates resource cleanup with clean methods.
87
+ Organize task output into logical phases with groups.
103
88
 
104
89
  ```bash
105
- ruby examples/clean_demo.rb
90
+ ruby examples/group_demo.rb
106
91
  ```
107
92
 
108
93
  **Covers:**
109
- - Defining `clean` methods for resource cleanup
110
- - Reverse dependency order execution
111
- - `run_and_clean` combined operation
94
+ - `group("label") { ... }` for organizing output
95
+ - Groups displayed as children in progress tree
96
+ - Multiple groups within a single task
112
97
 
113
98
  ---
114
99
 
115
- ### 8. system_call_demo.rb - Subprocess Output
100
+ ### 7. message_demo.rb - User-Facing Messages
116
101
 
117
- Capture subprocess output in progress display.
102
+ Output messages that bypass the progress display capture.
118
103
 
119
104
  ```bash
120
- ruby examples/system_call_demo.rb
105
+ ruby examples/message_demo.rb
121
106
  ```
122
107
 
123
108
  **Covers:**
124
- - `system()` output capture
125
- - Streaming output display
126
- - Parallel subprocess execution
109
+ - `Taski.message(text)` for user-facing output
110
+ - Messages queued during progress and shown after completion
111
+ - Difference between `puts` (captured) and `Taski.message` (bypassed)
127
112
 
128
113
  ---
129
114
 
130
- ### 9. nested_section_demo.rb - Nested Sections
115
+ ### 8. progress_demo.rb - Progress Display Modes
131
116
 
132
- Sections that depend on other tasks for implementation selection.
117
+ Real-time progress visualization during parallel execution.
133
118
 
134
119
  ```bash
135
- TASKI_PROGRESS_DISABLE=1 ruby examples/nested_section_demo.rb
120
+ ruby examples/progress_demo.rb # Tree mode
121
+ TASKI_PROGRESS_MODE=simple ruby examples/progress_demo.rb # Simple mode
122
+ TASKI_PROGRESS_DISABLE=1 ruby examples/progress_demo.rb # Disabled
136
123
  ```
137
124
 
138
125
  **Covers:**
139
- - Section inside Section
140
- - Dynamic implementation selection
141
- - Complex dependency hierarchies
126
+ - Tree progress display (default)
127
+ - Simple one-line progress display
128
+ - Parallel task execution
129
+ - Task output capture and streaming
130
+ - system() output integration
142
131
 
143
132
  ---
144
133
 
@@ -148,13 +137,12 @@ TASKI_PROGRESS_DISABLE=1 ruby examples/nested_section_demo.rb
148
137
  |---------|---------|------------|
149
138
  | quick_start | Exports API | Basic |
150
139
  | section_demo | Section API | Intermediate |
151
- | args_demo | Args API | Intermediate |
140
+ | args_demo | Args/Env API | Intermediate |
152
141
  | reexecution_demo | Scope-Based Execution | Intermediate |
153
- | data_pipeline_demo | ETL Pipeline | Advanced |
154
- | parallel_progress_demo | Progress Display | Advanced |
155
142
  | clean_demo | Lifecycle Management | Intermediate |
156
- | system_call_demo | Subprocess Output | Advanced |
157
- | nested_section_demo | Nested Sections | Advanced |
143
+ | group_demo | Output Grouping | Intermediate |
144
+ | message_demo | User Messages | Basic |
145
+ | progress_demo | Progress Display | Advanced |
158
146
 
159
147
  ## Running All Examples
160
148
 
@@ -163,7 +151,7 @@ TASKI_PROGRESS_DISABLE=1 ruby examples/nested_section_demo.rb
163
151
  for f in examples/*.rb; do echo "=== $f ===" && ruby "$f" && echo; done
164
152
 
165
153
  # Disable progress display if needed
166
- TASKI_PROGRESS_DISABLE=1 ruby examples/parallel_progress_demo.rb
154
+ TASKI_PROGRESS_DISABLE=1 ruby examples/progress_demo.rb
167
155
  ```
168
156
 
169
157
  ## Next Steps
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Demonstrates Taski.message API
5
+ #
6
+ # Taski.message outputs text to the user without being captured by TaskOutputRouter.
7
+ # Messages are queued during progress display and shown after task completion.
8
+ #
9
+ # Usage:
10
+ # ruby examples/message_demo.rb
11
+ # TASKI_FORCE_PROGRESS=1 ruby examples/message_demo.rb # Force progress display
12
+
13
+ require_relative "../lib/taski"
14
+
15
+ class ProcessDataTask < Taski::Task
16
+ exports :processed_count
17
+
18
+ def run
19
+ puts "Starting data processing..." # Captured by TaskOutputRouter
20
+
21
+ # Simulate processing
22
+ 5.times do |i|
23
+ puts "Processing batch #{i + 1}/5..." # Captured
24
+ sleep 0.3
25
+ end
26
+
27
+ @processed_count = 42
28
+
29
+ # These messages bypass TaskOutputRouter and appear after execution
30
+ Taski.message("Created: /tmp/output.txt")
31
+ Taski.message("Summary: #{@processed_count} items processed successfully")
32
+ end
33
+ end
34
+
35
+ class GenerateReportTask < Taski::Task
36
+ exports :report_path
37
+
38
+ def run
39
+ # Dependency: ProcessDataTask will be executed first
40
+ count = ProcessDataTask.processed_count
41
+ puts "Generating report for #{count} items..." # Captured
42
+
43
+ sleep 0.5
44
+
45
+ @report_path = "/tmp/report.pdf"
46
+
47
+ Taski.message("Report available at: #{@report_path}")
48
+ end
49
+ end
50
+
51
+ puts "=== Taski.message Demo ==="
52
+ puts
53
+
54
+ GenerateReportTask.run
55
+
56
+ puts
57
+ puts "=== Done ==="
@@ -0,0 +1,154 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Taski Progress Display Demo
5
+ #
6
+ # This example demonstrates the progress display modes:
7
+ # - Tree mode (default): Shows task hierarchy with real-time updates
8
+ # - Simple mode: One-line spinner with current task name
9
+ #
10
+ # Run:
11
+ # ruby examples/progress_demo.rb # Tree mode (default)
12
+ # TASKI_PROGRESS_MODE=simple ruby examples/progress_demo.rb # Simple mode
13
+ # TASKI_PROGRESS_DISABLE=1 ruby examples/progress_demo.rb # No progress
14
+ #
15
+ # Covers:
16
+ # - Parallel task execution with progress display
17
+ # - Tree vs simple progress modes
18
+ # - Task output capture and display
19
+ # - system() output streaming
20
+
21
+ require_relative "../lib/taski"
22
+
23
+ # Configuration section with multiple implementations
24
+ class DatabaseSection < Taski::Section
25
+ interfaces :connection_string
26
+
27
+ def impl
28
+ (ENV["USE_PROD_DB"] == "1") ? ProductionDB : DevelopmentDB
29
+ end
30
+
31
+ class ProductionDB < Taski::Task
32
+ def run
33
+ puts "Connecting to production database..."
34
+ sleep(0.4)
35
+ @connection_string = "postgresql://prod-server:5432/myapp"
36
+ end
37
+ end
38
+
39
+ class DevelopmentDB < Taski::Task
40
+ def run
41
+ puts "Connecting to development database..."
42
+ sleep(0.3)
43
+ @connection_string = "postgresql://localhost:5432/myapp_dev"
44
+ end
45
+ end
46
+ end
47
+
48
+ # Parallel download tasks (executed concurrently)
49
+ class DownloadLayer1 < Taski::Task
50
+ exports :layer1_data
51
+
52
+ def run
53
+ puts "Downloading base image..."
54
+ sleep(0.8)
55
+ puts "Base image complete"
56
+ @layer1_data = "Layer 1 (base)"
57
+ end
58
+ end
59
+
60
+ class DownloadLayer2 < Taski::Task
61
+ exports :layer2_data
62
+
63
+ def run
64
+ puts "Downloading dependencies..."
65
+ sleep(1.2)
66
+ puts "Dependencies complete"
67
+ @layer2_data = "Layer 2 (deps)"
68
+ end
69
+ end
70
+
71
+ class DownloadLayer3 < Taski::Task
72
+ exports :layer3_data
73
+
74
+ def run
75
+ puts "Downloading application..."
76
+ sleep(0.4)
77
+ puts "Application complete"
78
+ @layer3_data = "Layer 3 (app)"
79
+ end
80
+ end
81
+
82
+ # Task that depends on all downloads (waits for parallel completion)
83
+ class ExtractLayers < Taski::Task
84
+ exports :extracted_data
85
+
86
+ def run
87
+ layer1 = DownloadLayer1.layer1_data
88
+ layer2 = DownloadLayer2.layer2_data
89
+ layer3 = DownloadLayer3.layer3_data
90
+
91
+ puts "Extracting layers..."
92
+ sleep(0.3)
93
+ @extracted_data = [layer1, layer2, layer3]
94
+ end
95
+ end
96
+
97
+ # Task demonstrating system() output capture
98
+ class RunSystemCommand < Taski::Task
99
+ exports :command_result
100
+
101
+ def run
102
+ puts "Running system command..."
103
+ @command_result = system("echo 'Step 1: Preparing...' && sleep 0.2 && echo 'Step 2: Processing...' && sleep 0.2 && echo 'Step 3: Done'")
104
+ end
105
+ end
106
+
107
+ # Final task combining all dependencies
108
+ class BuildApplication < Taski::Task
109
+ exports :result
110
+
111
+ def run
112
+ db = DatabaseSection.connection_string
113
+ layers = ExtractLayers.extracted_data
114
+ RunSystemCommand.command_result
115
+
116
+ puts "Building application..."
117
+ sleep(0.3)
118
+ puts "Finalizing build..."
119
+ sleep(0.2)
120
+
121
+ @result = {
122
+ database: db,
123
+ layers: layers,
124
+ status: "success"
125
+ }
126
+ end
127
+ end
128
+
129
+ # Main execution
130
+ puts "Taski Progress Display Demo"
131
+ puts "=" * 50
132
+ puts "Progress mode: #{Taski.progress_mode || "tree (default)"}"
133
+ puts
134
+
135
+ puts "Task Tree Structure:"
136
+ puts "-" * 50
137
+ puts BuildApplication.tree
138
+ puts "-" * 50
139
+ puts
140
+
141
+ # Reset for fresh execution
142
+ BuildApplication.reset!
143
+
144
+ # Execute (progress display is automatic)
145
+ result = BuildApplication.result
146
+
147
+ puts
148
+ puts "=" * 50
149
+ puts "Execution completed!"
150
+ puts "Result: #{result.inspect}"
151
+ puts
152
+ puts "Try different modes:"
153
+ puts " TASKI_PROGRESS_MODE=simple ruby examples/progress_demo.rb"
154
+ puts " TASKI_PROGRESS_DISABLE=1 ruby examples/progress_demo.rb"
@@ -85,6 +85,7 @@ module Taski
85
85
  @root_task_class = nil
86
86
  @output_capture = nil
87
87
  @start_time = nil
88
+ @message_queue = []
88
89
  end
89
90
 
90
91
  # Set the output capture for getting task output
@@ -199,6 +200,14 @@ module Taski
199
200
  return unless should_stop
200
201
 
201
202
  on_stop
203
+ flush_queued_messages
204
+ end
205
+
206
+ # Queue a message to be displayed after progress display stops.
207
+ # Thread-safe for concurrent task execution.
208
+ # @param text [String] The message text to queue
209
+ def queue_message(text)
210
+ @monitor.synchronize { @message_queue << text }
202
211
  end
203
212
 
204
213
  protected
@@ -283,6 +292,13 @@ module Taski
283
292
 
284
293
  private
285
294
 
295
+ # Flush all queued messages to output.
296
+ # Called when progress display stops.
297
+ def flush_queued_messages
298
+ messages = @monitor.synchronize { @message_queue.dup.tap { @message_queue.clear } }
299
+ messages.each { |msg| @output.puts(msg) }
300
+ end
301
+
286
302
  # Apply state transition to TaskProgress
287
303
  def apply_state_transition(progress, state, duration, error)
288
304
  case state
@@ -89,6 +89,14 @@ module Taski
89
89
  @monitor.synchronize { !@output_capture.nil? }
90
90
  end
91
91
 
92
+ # Get the original stdout before output capture was set up.
93
+ # Thread-safe accessor.
94
+ #
95
+ # @return [IO, nil] The original stdout or nil if not captured
96
+ def original_stdout
97
+ @monitor.synchronize { @original_stdout }
98
+ end
99
+
92
100
  # Set up output capture for inline progress display.
93
101
  # Creates TaskOutputRouter and replaces $stdout.
94
102
  # Should only be called when progress display is active and not already set up.
@@ -97,7 +105,7 @@ module Taski
97
105
  def setup_output_capture(output_io)
98
106
  @monitor.synchronize do
99
107
  @original_stdout = output_io
100
- @output_capture = TaskOutputRouter.new(@original_stdout)
108
+ @output_capture = TaskOutputRouter.new(@original_stdout, self)
101
109
  @output_capture.start_polling
102
110
  $stdout = @output_capture
103
111
  end
@@ -295,8 +303,7 @@ module Taski
295
303
  dispatch(:start)
296
304
  end
297
305
 
298
- ##
299
- # Notify registered observers that execution has stopped.
306
+ # Notify observers to stop.
300
307
  def notify_stop
301
308
  dispatch(:stop)
302
309
  end
@@ -34,6 +34,7 @@ module Taski
34
34
  @spinner_index = 0
35
35
  @renderer_thread = nil
36
36
  @running = false
37
+ @section_candidates = {} # section_class => [candidate_classes]
37
38
  end
38
39
 
39
40
  protected
@@ -44,9 +45,23 @@ module Taski
44
45
  end
45
46
 
46
47
  # Template method: Called when a section impl is registered
47
- def on_section_impl_registered(_section_class, impl_class)
48
+ def on_section_impl_registered(section_class, impl_class)
48
49
  @tasks[impl_class] ||= TaskProgress.new
49
50
  @tasks[impl_class].is_impl_candidate = false
51
+
52
+ # Mark the section itself as completed (it's represented by its impl)
53
+ if @tasks[section_class]
54
+ @tasks[section_class].run_state = :completed
55
+ end
56
+
57
+ # Mark unselected candidates as completed (skipped)
58
+ candidates = @section_candidates[section_class] || []
59
+ candidates.each do |candidate|
60
+ next if candidate == impl_class
61
+ progress = @tasks[candidate]
62
+ next unless progress
63
+ progress.run_state = :completed
64
+ end
50
65
  end
51
66
 
52
67
  # Template method: Determine if display should activate
@@ -83,6 +98,23 @@ module Taski
83
98
  # Use TreeProgressDisplay's static method for tree building
84
99
  tree = TreeProgressDisplay.build_tree_node(@root_task_class)
85
100
  register_tasks_from_tree(tree)
101
+ collect_section_candidates(tree)
102
+ end
103
+
104
+ def collect_section_candidates(node)
105
+ return unless node
106
+
107
+ task_class = node[:task_class]
108
+
109
+ # If this is a section, collect its implementation candidates
110
+ if node[:is_section]
111
+ candidates = node[:children]
112
+ .select { |c| c[:is_impl_candidate] }
113
+ .map { |c| c[:task_class] }
114
+ @section_candidates[task_class] = candidates unless candidates.empty?
115
+ end
116
+
117
+ node[:children].each { |child| collect_section_candidates(child) }
86
118
  end
87
119
 
88
120
  def render_live
@@ -121,7 +153,8 @@ module Taski
121
153
  def build_status_line
122
154
  running_tasks = @tasks.select { |_, p| p.run_state == :running }
123
155
  cleaning_tasks = @tasks.select { |_, p| p.clean_state == :cleaning }
124
- completed = @tasks.values.count { |p| p.run_state == :completed }
156
+ # Count both completed and failed tasks as "done"
157
+ done = @tasks.values.count { |p| p.run_state == :completed || p.run_state == :failed }
125
158
  failed = @tasks.values.count { |p| p.run_state == :failed }
126
159
  total = @tasks.size
127
160
 
@@ -147,7 +180,7 @@ module Taski
147
180
  # Get last output message if available
148
181
  output_suffix = build_output_suffix(running_tasks.keys.first || cleaning_tasks.keys.first)
149
182
 
150
- parts = ["#{status_icon} [#{completed}/#{total}]"]
183
+ parts = ["#{status_icon} [#{done}/#{total}]"]
151
184
  parts << task_names if task_names && !task_names.empty?
152
185
  parts << "|" << output_suffix if output_suffix
153
186
 
@@ -22,9 +22,10 @@ module Taski
22
22
  READ_BUFFER_SIZE = 4096
23
23
  MAX_RECENT_LINES = 30 # Maximum number of recent lines to keep per task
24
24
 
25
- def initialize(original_stdout)
25
+ def initialize(original_stdout, execution_context = nil)
26
26
  super()
27
27
  @original = original_stdout
28
+ @execution_context = execution_context
28
29
  @pipes = {} # task_class => TaskOutputPipe
29
30
  @thread_map = {} # Thread => task_class
30
31
  @recent_lines = {} # task_class => Array<String>
data/lib/taski/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Taski
4
- VERSION = "0.8.0"
4
+ VERSION = "0.8.2"
5
5
  end