philiprehberger-task_runner 0.4.0 → 0.6.0

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: 0edebc1ac881ac5547d09cc825898875461f22b5b13edd6c5c01b3efc9d7a13f
4
- data.tar.gz: f7172ad5068e86cb31fa0cb48f1eac41b21f239ff884f9333f52b52d33df29bb
3
+ metadata.gz: 81a304bf73bc20e70f8bb340125ed6cbc3add80df788e746909341cdbcac85a4
4
+ data.tar.gz: 46e8d6a93c2f3501142a9a4c330a8686e0d91edfd2c3bcd2674508075d563c34
5
5
  SHA512:
6
- metadata.gz: 96f98887b963b6e19cd0025b1d2621571422b21ec7c7e72f936892b4f768b0f023590da3591b575deaf8b6395c8d5b27fe3a6ff56bb0a057159f594383323a40
7
- data.tar.gz: 84271258a850cc7dbb6a3bda26db60c55b7a43b61b9323659eec0ecbe4bc83f1bfe804ade2b33922de9643418becdc8f595231fa9cf84e9663e57b85ca1a8e15
6
+ metadata.gz: 83345dc31740a6618be9d21078c0b0640ba04bb54b08d457e67fc0df37786e2c8f86b316355ffd558bf1470967e71aa54575a4f98a2b75acaf8731059b8fbf52
7
+ data.tar.gz: 304a3c5de5afd3a41e61dd9e127dc8fabecf546743a22900df0486ad95a768f7112f3713f8f4de9747080a31a0e0563a2c3d67b547a3cd6eefbc9f32b0e9ea83
data/CHANGELOG.md CHANGED
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.6.0] - 2026-04-28
11
+
12
+ ### Added
13
+ - `TaskRunner.which(cmd)` — absolute path of an executable on `PATH`, or `nil` when not found. Honors `ENV['PATHEXT']` on Windows. Raises `ArgumentError` for nil/empty input.
14
+
15
+ ## [0.5.0] - 2026-04-21
16
+
17
+ ### Added
18
+ - `TaskRunner.run?` — Boolean convenience returning true only when the command exits with status 0; swallows `TimeoutError` as `false`
19
+
10
20
  ## [0.4.0] - 2026-04-20
11
21
 
12
22
  ### Added
data/README.md CHANGED
@@ -51,6 +51,31 @@ rescue Philiprehberger::TaskRunner::CommandError => e
51
51
  end
52
52
  ```
53
53
 
54
+ ### Boolean Success Check
55
+
56
+ ```ruby
57
+ # Returns true for exit 0, false otherwise (timeouts also return false)
58
+ if Philiprehberger::TaskRunner.run?('which', 'git')
59
+ puts 'git is installed'
60
+ end
61
+ ```
62
+
63
+ ### Which
64
+
65
+ Locate an executable on `PATH` (like the `which` shell builtin). Returns the
66
+ absolute path or `nil` when not found:
67
+
68
+ ```ruby
69
+ Philiprehberger::TaskRunner.which("git")
70
+ # => "/usr/bin/git"
71
+
72
+ Philiprehberger::TaskRunner.which("definitely-not-installed")
73
+ # => nil
74
+ ```
75
+
76
+ On Windows, candidate suffixes from `ENV["PATHEXT"]` (`.COM`, `.EXE`,
77
+ `.BAT`, `.CMD`) are tried automatically.
78
+
54
79
  ### Timeout
55
80
 
56
81
  ```ruby
@@ -115,6 +140,8 @@ end
115
140
  |----------------|-------------|
116
141
  | `.run(cmd, *args, timeout:, env:, chdir:, signal:, kill_after:, stdin:)` | Run a command and return a Result |
117
142
  | `.run!(cmd, *args, **opts)` | Same as `run`, raises `CommandError` on non-zero exit |
143
+ | `.run?(cmd, *args, **opts)` | Boolean shortcut — true only when exit code is 0; timeouts return false |
144
+ | `.which(cmd)` | Absolute path of `cmd` on PATH (or `nil`); honors `PATHEXT` on Windows |
118
145
  | `CommandError#result` | The failed `Result` object |
119
146
  | `.run(cmd) { \|line\| ... }` | Run with line-by-line stdout streaming |
120
147
  | `.run(cmd) { \|line, stream\| ... }` | Run with stdout and stderr streaming |
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Philiprehberger
4
4
  module TaskRunner
5
- VERSION = '0.4.0'
5
+ VERSION = '0.6.0'
6
6
  end
7
7
  end
@@ -70,6 +70,48 @@ module Philiprehberger
70
70
  result
71
71
  end
72
72
 
73
+ # Boolean convenience — run the command and return whether it succeeded
74
+ # (exit code 0). Swallows `TimeoutError` (reported as false). Accepts the
75
+ # same arguments as {.run}.
76
+ #
77
+ # @return [Boolean] true if the command exited with status 0
78
+ def self.run?(cmd, ...)
79
+ run(cmd, ...).success?
80
+ rescue TimeoutError
81
+ false
82
+ end
83
+
84
+ # Find the absolute path of an executable on PATH.
85
+ #
86
+ # Behaves like the `which` shell builtin: walks each entry in
87
+ # `ENV['PATH']` and returns the first directory that contains an
88
+ # executable file matching `cmd`. On Windows, also tries each suffix in
89
+ # `ENV['PATHEXT']`. Returns `nil` when nothing is found.
90
+ #
91
+ # @param cmd [String] the executable name to search for
92
+ # @return [String, nil] the absolute path, or nil when not found
93
+ # @raise [ArgumentError] if `cmd` is nil or empty
94
+ def self.which(cmd)
95
+ raise ArgumentError, 'cmd cannot be nil or empty' if cmd.nil? || cmd.to_s.empty?
96
+
97
+ exts = if Gem.win_platform?
98
+ (ENV['PATHEXT'] || '.COM;.EXE;.BAT;.CMD').split(';')
99
+ else
100
+ ['']
101
+ end
102
+
103
+ ENV.fetch('PATH', '').split(File::PATH_SEPARATOR).each do |dir|
104
+ next if dir.empty?
105
+
106
+ exts.each do |ext|
107
+ candidate = File.join(dir, "#{cmd}#{ext}")
108
+ return File.expand_path(candidate) if File.file?(candidate) && File.executable?(candidate)
109
+ end
110
+ end
111
+
112
+ nil
113
+ end
114
+
73
115
  # @api private
74
116
  def self.run_capture(env_hash, full_cmd, spawn_opts, timeout, start_time, signal, kill_after, stdin_data)
75
117
  stdout_buf = +''
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: philiprehberger-task_runner
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Philip Rehberger
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-04-20 00:00:00.000000000 Z
11
+ date: 2026-04-29 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Run shell commands with captured stdout/stderr, exit code, duration measurement,
14
14
  configurable timeout, environment variables, line-by-line streaming, graceful signal