ukiryu 0.1.0 → 0.1.1
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 +4 -4
- data/.github/workflows/docs.yml +63 -0
- data/.github/workflows/links.yml +99 -0
- data/.github/workflows/rake.yml +19 -0
- data/.github/workflows/release.yml +27 -0
- data/.gitignore +18 -4
- data/.rubocop.yml +1 -0
- data/.rubocop_todo.yml +213 -0
- data/Gemfile +12 -8
- data/README.adoc +613 -0
- data/Rakefile +2 -2
- data/docs/assets/logo.svg +1 -0
- data/exe/ukiryu +11 -0
- data/lib/ukiryu/action/base.rb +77 -0
- data/lib/ukiryu/cache.rb +199 -0
- data/lib/ukiryu/cli.rb +133 -307
- data/lib/ukiryu/cli_commands/base_command.rb +155 -0
- data/lib/ukiryu/cli_commands/commands_command.rb +120 -0
- data/lib/ukiryu/cli_commands/commands_command.rb.fixed +40 -0
- data/lib/ukiryu/cli_commands/config_command.rb +249 -0
- data/lib/ukiryu/cli_commands/describe_command.rb +326 -0
- data/lib/ukiryu/cli_commands/describe_command.rb.fixed +254 -0
- data/lib/ukiryu/cli_commands/exec_inline_command.rb.fixed +180 -0
- data/lib/ukiryu/cli_commands/extract_command.rb +84 -0
- data/lib/ukiryu/cli_commands/info_command.rb +156 -0
- data/lib/ukiryu/cli_commands/list_command.rb +70 -0
- data/lib/ukiryu/cli_commands/opts_command.rb +106 -0
- data/lib/ukiryu/cli_commands/opts_command.rb.fixed +105 -0
- data/lib/ukiryu/cli_commands/response_formatter.rb +240 -0
- data/lib/ukiryu/cli_commands/run_command.rb +375 -0
- data/lib/ukiryu/cli_commands/run_file_command.rb +215 -0
- data/lib/ukiryu/cli_commands/system_command.rb +90 -0
- data/lib/ukiryu/cli_commands/validate_command.rb +87 -0
- data/lib/ukiryu/cli_commands/version_command.rb +16 -0
- data/lib/ukiryu/cli_commands/which_command.rb +166 -0
- data/lib/ukiryu/command_builder.rb +205 -0
- data/lib/ukiryu/config/env_provider.rb +64 -0
- data/lib/ukiryu/config/env_schema.rb +63 -0
- data/lib/ukiryu/config/override_resolver.rb +68 -0
- data/lib/ukiryu/config/type_converter.rb +59 -0
- data/lib/ukiryu/config.rb +249 -0
- data/lib/ukiryu/errors.rb +3 -0
- data/lib/ukiryu/executable_locator.rb +114 -0
- data/lib/ukiryu/execution/command_info.rb +64 -0
- data/lib/ukiryu/execution/metadata.rb +97 -0
- data/lib/ukiryu/execution/output.rb +144 -0
- data/lib/ukiryu/execution/result.rb +194 -0
- data/lib/ukiryu/execution.rb +15 -0
- data/lib/ukiryu/execution_context.rb +251 -0
- data/lib/ukiryu/executor.rb +76 -493
- data/lib/ukiryu/extractors/base_extractor.rb +63 -0
- data/lib/ukiryu/extractors/extractor.rb +150 -0
- data/lib/ukiryu/extractors/help_parser.rb +188 -0
- data/lib/ukiryu/extractors/native_extractor.rb +47 -0
- data/lib/ukiryu/io.rb +196 -0
- data/lib/ukiryu/logger.rb +544 -0
- data/lib/ukiryu/models/argument.rb +28 -0
- data/lib/ukiryu/models/argument_definition.rb +119 -0
- data/lib/ukiryu/models/arguments.rb +113 -0
- data/lib/ukiryu/models/command_definition.rb +176 -0
- data/lib/ukiryu/models/command_info.rb +37 -0
- data/lib/ukiryu/models/components.rb +107 -0
- data/lib/ukiryu/models/env_var_definition.rb +30 -0
- data/lib/ukiryu/models/error_response.rb +41 -0
- data/lib/ukiryu/models/execution_metadata.rb +31 -0
- data/lib/ukiryu/models/execution_report.rb +236 -0
- data/lib/ukiryu/models/exit_codes.rb +74 -0
- data/lib/ukiryu/models/flag_definition.rb +67 -0
- data/lib/ukiryu/models/option_definition.rb +102 -0
- data/lib/ukiryu/models/output_info.rb +25 -0
- data/lib/ukiryu/models/platform_profile.rb +153 -0
- data/lib/ukiryu/models/routing.rb +211 -0
- data/lib/ukiryu/models/search_paths.rb +39 -0
- data/lib/ukiryu/models/success_response.rb +85 -0
- data/lib/ukiryu/models/tool_definition.rb +145 -0
- data/lib/ukiryu/models/tool_metadata.rb +82 -0
- data/lib/ukiryu/models/validation_result.rb +80 -0
- data/lib/ukiryu/models/version_compatibility.rb +152 -0
- data/lib/ukiryu/models/version_detection.rb +39 -0
- data/lib/ukiryu/models.rb +23 -0
- data/lib/ukiryu/options/base.rb +95 -0
- data/lib/ukiryu/options_builder/formatter.rb +87 -0
- data/lib/ukiryu/options_builder/validator.rb +43 -0
- data/lib/ukiryu/options_builder.rb +311 -0
- data/lib/ukiryu/platform.rb +6 -6
- data/lib/ukiryu/registry.rb +143 -183
- data/lib/ukiryu/response/base.rb +217 -0
- data/lib/ukiryu/runtime.rb +179 -0
- data/lib/ukiryu/schema_validator.rb +8 -10
- data/lib/ukiryu/shell/bash.rb +3 -3
- data/lib/ukiryu/shell/cmd.rb +4 -4
- data/lib/ukiryu/shell/fish.rb +1 -1
- data/lib/ukiryu/shell/powershell.rb +3 -3
- data/lib/ukiryu/shell/sh.rb +1 -1
- data/lib/ukiryu/shell/zsh.rb +1 -1
- data/lib/ukiryu/shell.rb +146 -39
- data/lib/ukiryu/thor_ext.rb +208 -0
- data/lib/ukiryu/tool.rb +649 -258
- data/lib/ukiryu/tool_index.rb +224 -0
- data/lib/ukiryu/tools/base.rb +381 -0
- data/lib/ukiryu/tools/class_generator.rb +132 -0
- data/lib/ukiryu/tools/executable_finder.rb +29 -0
- data/lib/ukiryu/tools/generator.rb +154 -0
- data/lib/ukiryu/tools.rb +109 -0
- data/lib/ukiryu/type.rb +28 -43
- data/lib/ukiryu/validation/constraints.rb +281 -0
- data/lib/ukiryu/validation/validator.rb +188 -0
- data/lib/ukiryu/validation.rb +21 -0
- data/lib/ukiryu/version.rb +1 -1
- data/lib/ukiryu/version_detector.rb +51 -0
- data/lib/ukiryu.rb +31 -15
- data/ukiryu-proposal.md +2952 -0
- data/ukiryu.gemspec +18 -14
- metadata +137 -5
- data/.github/workflows/test.yml +0 -143
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ukiryu
|
|
4
|
+
module Execution
|
|
5
|
+
# Captured output from command execution
|
|
6
|
+
#
|
|
7
|
+
# Provides typed access to stdout and stderr with parsing utilities
|
|
8
|
+
class Output
|
|
9
|
+
attr_reader :raw_stdout, :raw_stderr, :exit_status
|
|
10
|
+
|
|
11
|
+
def initialize(stdout:, stderr:, exit_status:)
|
|
12
|
+
@raw_stdout = stdout
|
|
13
|
+
@raw_stderr = stderr
|
|
14
|
+
@exit_status = exit_status
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Get stdout as a string (stripped)
|
|
18
|
+
#
|
|
19
|
+
# @return [String] stripped stdout
|
|
20
|
+
def stdout
|
|
21
|
+
@raw_stdout.strip
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Get stderr as a string (stripped)
|
|
25
|
+
#
|
|
26
|
+
# @return [String] stripped stderr
|
|
27
|
+
def stderr
|
|
28
|
+
@raw_stderr.strip
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Get stdout lines as an array
|
|
32
|
+
#
|
|
33
|
+
# @return [Array<String>] stdout split by lines
|
|
34
|
+
def stdout_lines
|
|
35
|
+
@raw_stdout.split("\n")
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Get stderr lines as an array
|
|
39
|
+
#
|
|
40
|
+
# @return [Array<String>] stderr split by lines
|
|
41
|
+
def stderr_lines
|
|
42
|
+
@raw_stderr.split("\n")
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Check if stdout contains a pattern
|
|
46
|
+
#
|
|
47
|
+
# @param pattern [String, Regexp] pattern to search for
|
|
48
|
+
# @return [Boolean] true if pattern is found
|
|
49
|
+
def stdout_contains?(pattern)
|
|
50
|
+
if pattern.is_a?(Regexp)
|
|
51
|
+
@raw_stdout.match?(pattern)
|
|
52
|
+
else
|
|
53
|
+
@raw_stdout.include?(pattern.to_s)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Check if stderr contains a pattern
|
|
58
|
+
#
|
|
59
|
+
# @param pattern [String, Regexp] pattern to search for
|
|
60
|
+
# @return [Boolean] true if pattern is found
|
|
61
|
+
def stderr_contains?(pattern)
|
|
62
|
+
if pattern.is_a?(Regexp)
|
|
63
|
+
@raw_stderr.match?(pattern)
|
|
64
|
+
else
|
|
65
|
+
@raw_stderr.include?(pattern.to_s)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Check if stdout is empty
|
|
70
|
+
#
|
|
71
|
+
# @return [Boolean] true if stdout is empty
|
|
72
|
+
def stdout_empty?
|
|
73
|
+
@raw_stdout.strip.empty?
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Check if stderr is empty
|
|
77
|
+
#
|
|
78
|
+
# @return [Boolean] true if stderr is empty
|
|
79
|
+
def stderr_empty?
|
|
80
|
+
@raw_stderr.strip.empty?
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Get stdout length
|
|
84
|
+
#
|
|
85
|
+
# @return [Integer] byte length of stdout
|
|
86
|
+
def stdout_length
|
|
87
|
+
@raw_stdout.length
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Get stderr length
|
|
91
|
+
#
|
|
92
|
+
# @return [Integer] byte length of stderr
|
|
93
|
+
def stderr_length
|
|
94
|
+
@raw_stderr.length
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Check if command succeeded
|
|
98
|
+
#
|
|
99
|
+
# @return [Boolean] true if exit status is 0
|
|
100
|
+
def success?
|
|
101
|
+
@exit_status.zero?
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Check if command failed
|
|
105
|
+
#
|
|
106
|
+
# @return [Boolean] true if exit status is non-zero
|
|
107
|
+
def failure?
|
|
108
|
+
@exit_status != 0
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Convert to hash
|
|
112
|
+
#
|
|
113
|
+
# @return [Hash] output as hash
|
|
114
|
+
def to_h
|
|
115
|
+
{
|
|
116
|
+
stdout: @raw_stdout,
|
|
117
|
+
stderr: @raw_stderr,
|
|
118
|
+
exit_status: @exit_status,
|
|
119
|
+
success: success?,
|
|
120
|
+
stdout_lines: stdout_lines,
|
|
121
|
+
stderr_lines: stderr_lines
|
|
122
|
+
}
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# String representation
|
|
126
|
+
#
|
|
127
|
+
# @return [String] summary string
|
|
128
|
+
def to_s
|
|
129
|
+
if success?
|
|
130
|
+
"Success (exit: #{@exit_status}, stdout: #{stdout_length} bytes, stderr: #{stderr_length} bytes)"
|
|
131
|
+
else
|
|
132
|
+
"Failed (exit: #{@exit_status}, stdout: #{stdout_length} bytes, stderr: #{stderr_length} bytes)"
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Inspect
|
|
137
|
+
#
|
|
138
|
+
# @return [String] inspection string
|
|
139
|
+
def inspect
|
|
140
|
+
"#<Ukiryu::Execution::Output exit=#{@exit_status} success=#{success?}>"
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ukiryu
|
|
4
|
+
module Execution
|
|
5
|
+
# Result class for command execution
|
|
6
|
+
#
|
|
7
|
+
# Provides a rich, object-oriented interface to command execution results.
|
|
8
|
+
# Composes CommandInfo, Output, and ExecutionMetadata for a fully OOP design.
|
|
9
|
+
class Result
|
|
10
|
+
attr_reader :command_info, :output, :metadata
|
|
11
|
+
|
|
12
|
+
def initialize(command_info:, output:, metadata:)
|
|
13
|
+
@command_info = command_info
|
|
14
|
+
@output = output
|
|
15
|
+
@metadata = metadata
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Get the full command string
|
|
19
|
+
#
|
|
20
|
+
# @return [String] executed command
|
|
21
|
+
def command
|
|
22
|
+
@command_info.full_command
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Get the executable
|
|
26
|
+
#
|
|
27
|
+
# @return [String] executable path
|
|
28
|
+
def executable
|
|
29
|
+
@command_info.executable
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Get the executable name only
|
|
33
|
+
#
|
|
34
|
+
# @return [String] executable name
|
|
35
|
+
def executable_name
|
|
36
|
+
@command_info.executable_name
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Get raw stdout
|
|
40
|
+
#
|
|
41
|
+
# @return [String] raw stdout
|
|
42
|
+
def stdout
|
|
43
|
+
@output.raw_stdout
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Get raw stderr
|
|
47
|
+
#
|
|
48
|
+
# @return [String] raw stderr
|
|
49
|
+
def stderr
|
|
50
|
+
@output.raw_stderr
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Get exit status code
|
|
54
|
+
#
|
|
55
|
+
# @return [Integer] exit status
|
|
56
|
+
def status
|
|
57
|
+
@output.exit_status
|
|
58
|
+
end
|
|
59
|
+
alias exit_code status
|
|
60
|
+
|
|
61
|
+
# Get the exit code (alias for status)
|
|
62
|
+
#
|
|
63
|
+
# @return [Integer] exit status
|
|
64
|
+
def exit_status
|
|
65
|
+
@output.exit_status
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Get start time
|
|
69
|
+
#
|
|
70
|
+
# @return [Time] when command started
|
|
71
|
+
def started_at
|
|
72
|
+
@metadata.started_at
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Get finish time
|
|
76
|
+
#
|
|
77
|
+
# @return [Time] when command finished
|
|
78
|
+
def finished_at
|
|
79
|
+
@metadata.finished_at
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Get execution duration
|
|
83
|
+
#
|
|
84
|
+
# @return [Float, nil] duration in seconds
|
|
85
|
+
def duration
|
|
86
|
+
@metadata.duration
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Get execution duration (alias)
|
|
90
|
+
#
|
|
91
|
+
# @return [Float, nil] duration in seconds
|
|
92
|
+
def execution_time
|
|
93
|
+
@metadata.duration_seconds
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Check if the command succeeded
|
|
97
|
+
#
|
|
98
|
+
# @return [Boolean]
|
|
99
|
+
def success?
|
|
100
|
+
@output.success?
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Check if the command failed
|
|
104
|
+
#
|
|
105
|
+
# @return [Boolean]
|
|
106
|
+
def failure?
|
|
107
|
+
@output.failure?
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Get stdout as a stripped string
|
|
111
|
+
#
|
|
112
|
+
# @return [String] stripped stdout
|
|
113
|
+
def output
|
|
114
|
+
@output.stdout
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Get stderr as a stripped string
|
|
118
|
+
#
|
|
119
|
+
# @return [String] stripped stderr
|
|
120
|
+
def error_output
|
|
121
|
+
@output.stderr
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Get stdout lines
|
|
125
|
+
#
|
|
126
|
+
# @return [Array<String>] stdout split by lines
|
|
127
|
+
def stdout_lines
|
|
128
|
+
@output.stdout_lines
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Get stderr lines
|
|
132
|
+
#
|
|
133
|
+
# @return [Array<String>] stderr split by lines
|
|
134
|
+
def stderr_lines
|
|
135
|
+
@output.stderr_lines
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Check if stdout contains a pattern
|
|
139
|
+
#
|
|
140
|
+
# @param pattern [String, Regexp] pattern to search for
|
|
141
|
+
# @return [Boolean] true if pattern is found
|
|
142
|
+
def stdout_contains?(pattern)
|
|
143
|
+
@output.stdout_contains?(pattern)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Check if stderr contains a pattern
|
|
147
|
+
#
|
|
148
|
+
# @param pattern [String, Regexp] pattern to search for
|
|
149
|
+
# @return [Boolean] true if pattern is found
|
|
150
|
+
def stderr_contains?(pattern)
|
|
151
|
+
@output.stderr_contains?(pattern)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# Get a hash representation of the result
|
|
155
|
+
#
|
|
156
|
+
# @return [Hash] result data as a hash
|
|
157
|
+
def to_h
|
|
158
|
+
{
|
|
159
|
+
command: @command_info.to_h,
|
|
160
|
+
output: @output.to_h,
|
|
161
|
+
metadata: @metadata.to_h,
|
|
162
|
+
success: success?,
|
|
163
|
+
status: status
|
|
164
|
+
}
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# Get a JSON representation of the result
|
|
168
|
+
#
|
|
169
|
+
# @return [String] result data as JSON
|
|
170
|
+
def to_json(*args)
|
|
171
|
+
require 'json'
|
|
172
|
+
to_h.to_json(*args)
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# String representation of the result
|
|
176
|
+
#
|
|
177
|
+
# @return [String] summary string
|
|
178
|
+
def to_s
|
|
179
|
+
if success?
|
|
180
|
+
"Success: #{command} (#{metadata.formatted_duration})"
|
|
181
|
+
else
|
|
182
|
+
"Failed: #{command} (exit: #{status}, #{metadata.formatted_duration})"
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# Inspect
|
|
187
|
+
#
|
|
188
|
+
# @return [String] inspection string
|
|
189
|
+
def inspect
|
|
190
|
+
"#<Ukiryu::Execution::Result #{self}>"
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'execution/command_info'
|
|
4
|
+
require_relative 'execution/output'
|
|
5
|
+
require_relative 'execution/metadata'
|
|
6
|
+
require_relative 'execution/result'
|
|
7
|
+
|
|
8
|
+
module Ukiryu
|
|
9
|
+
# Execution namespace for result models
|
|
10
|
+
#
|
|
11
|
+
# This namespace contains OOP models for command execution results,
|
|
12
|
+
# providing a clean separation between execution logic and result modeling.
|
|
13
|
+
module Execution
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'runtime'
|
|
4
|
+
require_relative 'config'
|
|
5
|
+
|
|
6
|
+
module Ukiryu
|
|
7
|
+
# Simple thread-local storage for contexts
|
|
8
|
+
#
|
|
9
|
+
# @api private
|
|
10
|
+
class ExecutionContext
|
|
11
|
+
class ThreadLocal
|
|
12
|
+
def initialize
|
|
13
|
+
@key = "__ukiryu_execution_context_#{object_id}__"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def value
|
|
17
|
+
Thread.current[@key]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def value=(val)
|
|
21
|
+
Thread.current[@key] = val
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Execution context for dependency injection and execution-scoped configuration
|
|
27
|
+
#
|
|
28
|
+
# Provides a non-singleton alternative to Runtime.instance for better testability.
|
|
29
|
+
# Wraps runtime configuration and execution-specific options in a single object.
|
|
30
|
+
#
|
|
31
|
+
# @example Using ExecutionContext in production
|
|
32
|
+
# context = Ukiryu::ExecutionContext.current
|
|
33
|
+
# context.platform # => :macos
|
|
34
|
+
# context.shell # => :bash
|
|
35
|
+
# context.registry # => '/path/to/registry'
|
|
36
|
+
#
|
|
37
|
+
# @example Using ExecutionContext in tests
|
|
38
|
+
# context = Ukiryu::ExecutionContext.new(
|
|
39
|
+
# platform: :linux,
|
|
40
|
+
# shell: :zsh,
|
|
41
|
+
# registry_path: '/test/registry'
|
|
42
|
+
# )
|
|
43
|
+
# context.platform # => :linux
|
|
44
|
+
# context.shell # => :zsh
|
|
45
|
+
class ExecutionContext
|
|
46
|
+
# Thread-local storage for current context
|
|
47
|
+
@current_context = ThreadLocal.new
|
|
48
|
+
|
|
49
|
+
class << self
|
|
50
|
+
# Get the current execution context
|
|
51
|
+
#
|
|
52
|
+
# Creates a new context from the global Runtime if none is set.
|
|
53
|
+
#
|
|
54
|
+
# @return [ExecutionContext] the current context
|
|
55
|
+
def current
|
|
56
|
+
@current_context.value ||= from_runtime
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Set the current execution context
|
|
60
|
+
#
|
|
61
|
+
# @param context [ExecutionContext] the context to set
|
|
62
|
+
# @return [void]
|
|
63
|
+
def current=(context)
|
|
64
|
+
@current_context.value = context
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Execute a block with a temporary context
|
|
68
|
+
#
|
|
69
|
+
# @param context [ExecutionContext] the context to use
|
|
70
|
+
# @yield the block to execute
|
|
71
|
+
# @return [Object] the block's return value
|
|
72
|
+
def with_context(context)
|
|
73
|
+
old_context = @current_context.value
|
|
74
|
+
@current_context.value = context
|
|
75
|
+
yield
|
|
76
|
+
ensure
|
|
77
|
+
@current_context.value = old_context
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Create a context from the global Runtime
|
|
81
|
+
#
|
|
82
|
+
# @return [ExecutionContext] a new context with runtime values
|
|
83
|
+
def from_runtime
|
|
84
|
+
runtime = Runtime.instance
|
|
85
|
+
new(
|
|
86
|
+
platform: runtime.platform,
|
|
87
|
+
shell: runtime.shell,
|
|
88
|
+
registry_path: Registry.default_registry_path,
|
|
89
|
+
timeout: Config.timeout,
|
|
90
|
+
debug: Config.debug,
|
|
91
|
+
metrics: Config.metrics
|
|
92
|
+
)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Reset the current context (mainly for testing)
|
|
96
|
+
#
|
|
97
|
+
# @api private
|
|
98
|
+
def reset_current!
|
|
99
|
+
@current_context.value = nil
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Platform (:macos, :linux, :windows)
|
|
104
|
+
#
|
|
105
|
+
# @return [Symbol] the platform
|
|
106
|
+
attr_reader :platform
|
|
107
|
+
|
|
108
|
+
# Shell (:bash, :zsh, :fish, :powershell, :cmd)
|
|
109
|
+
#
|
|
110
|
+
# @return [Symbol] the shell
|
|
111
|
+
attr_reader :shell
|
|
112
|
+
|
|
113
|
+
# Registry path for tool profiles
|
|
114
|
+
#
|
|
115
|
+
# @return [String, nil] the registry path
|
|
116
|
+
attr_reader :registry_path
|
|
117
|
+
|
|
118
|
+
# Execution timeout in seconds
|
|
119
|
+
#
|
|
120
|
+
# @return [Integer, nil] the timeout
|
|
121
|
+
attr_reader :timeout
|
|
122
|
+
|
|
123
|
+
# Debug mode enabled
|
|
124
|
+
#
|
|
125
|
+
# @return [Boolean] true if debug mode is enabled
|
|
126
|
+
attr_reader :debug
|
|
127
|
+
|
|
128
|
+
# Metrics collection enabled
|
|
129
|
+
#
|
|
130
|
+
# @return [Boolean] true if metrics are enabled
|
|
131
|
+
attr_reader :metrics
|
|
132
|
+
|
|
133
|
+
# User-defined options hash
|
|
134
|
+
#
|
|
135
|
+
# @return [Hash] additional options
|
|
136
|
+
attr_reader :options
|
|
137
|
+
|
|
138
|
+
# Create a new execution context
|
|
139
|
+
#
|
|
140
|
+
# @param platform [Symbol] the platform (:macos, :linux, :windows)
|
|
141
|
+
# @param shell [Symbol] the shell (:bash, :zsh, :fish, :powershell, :cmd)
|
|
142
|
+
# @param registry_path [String, nil] the registry path
|
|
143
|
+
# @param timeout [Integer, nil] execution timeout in seconds
|
|
144
|
+
# @param debug [Boolean] debug mode flag
|
|
145
|
+
# @param metrics [Boolean] metrics collection flag
|
|
146
|
+
# @param options [Hash] additional options
|
|
147
|
+
def initialize(platform: nil,
|
|
148
|
+
shell: nil,
|
|
149
|
+
registry_path: nil,
|
|
150
|
+
timeout: nil,
|
|
151
|
+
debug: false,
|
|
152
|
+
metrics: false,
|
|
153
|
+
options: {})
|
|
154
|
+
@platform = platform
|
|
155
|
+
@shell = shell
|
|
156
|
+
@registry_path = registry_path
|
|
157
|
+
@timeout = timeout
|
|
158
|
+
@debug = debug
|
|
159
|
+
@metrics = metrics
|
|
160
|
+
@options = options
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# Get the shell class for this context
|
|
164
|
+
#
|
|
165
|
+
# @return [Class] the shell class
|
|
166
|
+
def shell_class
|
|
167
|
+
Shell.class_for(@shell)
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Check if running on a specific platform
|
|
171
|
+
#
|
|
172
|
+
# @param plat [Symbol] the platform to check
|
|
173
|
+
# @return [Boolean] true if running on the specified platform
|
|
174
|
+
def on_platform?(plat)
|
|
175
|
+
@platform == plat.to_sym
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# Check if using a specific shell
|
|
179
|
+
#
|
|
180
|
+
# @param sh [Symbol] the shell to check
|
|
181
|
+
# @return [Boolean] true if using the specified shell
|
|
182
|
+
def using_shell?(sh)
|
|
183
|
+
@shell == sh.to_sym
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# Check if running on Windows
|
|
187
|
+
#
|
|
188
|
+
# @return [Boolean] true if on Windows
|
|
189
|
+
def windows?
|
|
190
|
+
on_platform?(:windows)
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# Check if running on macOS
|
|
194
|
+
#
|
|
195
|
+
# @return [Boolean] true if on macOS
|
|
196
|
+
def macos?
|
|
197
|
+
on_platform?(:macos)
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# Check if running on Linux
|
|
201
|
+
#
|
|
202
|
+
# @return [Boolean] true if on Linux
|
|
203
|
+
def linux?
|
|
204
|
+
on_platform?(:linux)
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
# Check if using a Unix-like shell
|
|
208
|
+
#
|
|
209
|
+
# @return [Boolean] true if using bash, zsh, fish, or sh
|
|
210
|
+
def unix_shell?
|
|
211
|
+
%i[bash zsh fish sh].include?(@shell)
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# Check if using a Windows shell
|
|
215
|
+
#
|
|
216
|
+
# @return [Boolean] true if using powershell or cmd
|
|
217
|
+
def windows_shell?
|
|
218
|
+
%i[powershell cmd].include?(@shell)
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
# Create a new context with merged options
|
|
222
|
+
#
|
|
223
|
+
# @param changes [Hash] the changes to merge
|
|
224
|
+
# @return [ExecutionContext] a new context with merged values
|
|
225
|
+
def merge(changes)
|
|
226
|
+
self.class.new(
|
|
227
|
+
platform: changes.fetch(:platform, @platform),
|
|
228
|
+
shell: changes.fetch(:shell, @shell),
|
|
229
|
+
registry_path: changes.fetch(:registry_path, @registry_path),
|
|
230
|
+
timeout: changes.fetch(:timeout, @timeout),
|
|
231
|
+
debug: changes.fetch(:debug, @debug),
|
|
232
|
+
metrics: changes.fetch(:metrics, @metrics),
|
|
233
|
+
options: @options.merge(changes.fetch(:options, {}))
|
|
234
|
+
)
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
# String representation
|
|
238
|
+
#
|
|
239
|
+
# @return [String] the context as a string
|
|
240
|
+
def to_s
|
|
241
|
+
"ExecutionContext(platform=#{@platform}, shell=#{@shell}, registry=#{@registry_path})"
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
# Inspect
|
|
245
|
+
#
|
|
246
|
+
# @return [String] the inspection string
|
|
247
|
+
def inspect
|
|
248
|
+
"#<#{self.class} #{self}>"
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
end
|