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
data/exe/ukiryu
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Add lib directory to load path
|
|
5
|
+
$LOAD_PATH.unshift File.expand_path('../lib', __dir__)
|
|
6
|
+
|
|
7
|
+
# Require thor first before ukiryu to ensure proper loading
|
|
8
|
+
require 'thor'
|
|
9
|
+
require 'ukiryu'
|
|
10
|
+
|
|
11
|
+
Ukiryu::Cli.start(ARGV)
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ukiryu
|
|
4
|
+
module Action
|
|
5
|
+
# Abstract base class for action classes
|
|
6
|
+
#
|
|
7
|
+
# Action classes represent executable commands and provide a fluent
|
|
8
|
+
# interface for building and executing commands.
|
|
9
|
+
#
|
|
10
|
+
# This is an alternative pattern to using options objects directly.
|
|
11
|
+
#
|
|
12
|
+
# @abstract
|
|
13
|
+
class Base
|
|
14
|
+
class << self
|
|
15
|
+
# Get the command name
|
|
16
|
+
#
|
|
17
|
+
# @return [Symbol] the command name
|
|
18
|
+
attr_reader :command_name
|
|
19
|
+
|
|
20
|
+
# Get the command definition
|
|
21
|
+
#
|
|
22
|
+
# @return [Hash] the command definition
|
|
23
|
+
attr_reader :command_def
|
|
24
|
+
|
|
25
|
+
# Get the associated tool class
|
|
26
|
+
#
|
|
27
|
+
# @return [Class] the tool class
|
|
28
|
+
attr_reader :tool_class
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Create a new action
|
|
32
|
+
#
|
|
33
|
+
# @param tool_instance [Tools::Base] the tool instance to use
|
|
34
|
+
def initialize(tool_instance = nil)
|
|
35
|
+
@tool = tool_instance || self.class.tool_class.new
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Execute this action with options
|
|
39
|
+
#
|
|
40
|
+
# @param options [Hash, Options::Base] the options to use
|
|
41
|
+
# @return [Response::Base] the execution response
|
|
42
|
+
def run(options = {})
|
|
43
|
+
# If options is a hash, create an options object
|
|
44
|
+
if options.is_a?(Hash)
|
|
45
|
+
options_class = @tool.options_class_for(self.class.command_name)
|
|
46
|
+
options_obj = options_class.new
|
|
47
|
+
options_obj.set(options)
|
|
48
|
+
options = options_obj
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Execute with the tool
|
|
52
|
+
@tool.execute(self.class.command_name, options)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Create an options object for this action
|
|
56
|
+
#
|
|
57
|
+
# @return [Options::Base] a new options object
|
|
58
|
+
def options
|
|
59
|
+
@tool.options_class_for(self.class.command_name).new
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Get the command name
|
|
63
|
+
#
|
|
64
|
+
# @return [Symbol] the command name
|
|
65
|
+
def command_name
|
|
66
|
+
self.class.command_name
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Get the command definition
|
|
70
|
+
#
|
|
71
|
+
# @return [Hash] the command definition
|
|
72
|
+
def command_def
|
|
73
|
+
self.class.command_def
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
data/lib/ukiryu/cache.rb
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ukiryu
|
|
4
|
+
# Bounded LRU cache with expiration support
|
|
5
|
+
#
|
|
6
|
+
# This cache provides:
|
|
7
|
+
# - Maximum size limit (evicts oldest entries when limit reached)
|
|
8
|
+
# - Time-to-live (TTL) expiration for entries
|
|
9
|
+
# - Thread-safe operations
|
|
10
|
+
#
|
|
11
|
+
# @example
|
|
12
|
+
# cache = Cache.new(max_size: 100, ttl: 300)
|
|
13
|
+
# cache[:tool] = tool_instance
|
|
14
|
+
# value = cache[:tool]
|
|
15
|
+
#
|
|
16
|
+
class Cache
|
|
17
|
+
# Cache entry with expiration tracking
|
|
18
|
+
#
|
|
19
|
+
# @api private
|
|
20
|
+
class Entry
|
|
21
|
+
attr_reader :value, :created_at, :accessed_at
|
|
22
|
+
|
|
23
|
+
def initialize(value)
|
|
24
|
+
@value = value
|
|
25
|
+
@created_at = Time.now
|
|
26
|
+
@accessed_at = @created_at
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def touch!
|
|
30
|
+
@accessed_at = Time.now
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def expired?(ttl)
|
|
34
|
+
return false unless ttl
|
|
35
|
+
|
|
36
|
+
(Time.now - @created_at) > ttl
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Initialize a new cache
|
|
41
|
+
#
|
|
42
|
+
# @param max_size [Integer] maximum number of entries (default: 100)
|
|
43
|
+
# @param ttl [Integer, nil] time-to-live in seconds (nil = no expiration)
|
|
44
|
+
# @option options [Boolean] :thread_safe enable thread-safe operations (default: true)
|
|
45
|
+
def initialize(max_size: 100, ttl: nil, thread_safe: true)
|
|
46
|
+
@max_size = max_size
|
|
47
|
+
@ttl = ttl
|
|
48
|
+
@thread_safe = thread_safe
|
|
49
|
+
@data = {}
|
|
50
|
+
@mutex = Mutex.new if thread_safe
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Get a value from the cache
|
|
54
|
+
#
|
|
55
|
+
# @param key [Object] the cache key
|
|
56
|
+
# @return [Object, nil] the cached value or nil if not found/expired
|
|
57
|
+
def [](key)
|
|
58
|
+
synchronize do
|
|
59
|
+
entry = @data[key]
|
|
60
|
+
return nil unless entry
|
|
61
|
+
|
|
62
|
+
# Check expiration
|
|
63
|
+
if entry.expired?(@ttl)
|
|
64
|
+
@data.delete(key)
|
|
65
|
+
return nil
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Update access time for LRU
|
|
69
|
+
entry.touch!
|
|
70
|
+
entry.value
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Set a value in the cache
|
|
75
|
+
#
|
|
76
|
+
# @param key [Object] the cache key
|
|
77
|
+
# @param value [Object] the value to cache
|
|
78
|
+
# @return [Object] the cached value
|
|
79
|
+
def []=(key, value)
|
|
80
|
+
synchronize do
|
|
81
|
+
# Evict oldest entry if at capacity
|
|
82
|
+
evict_if_needed
|
|
83
|
+
|
|
84
|
+
@data[key] = Entry.new(value)
|
|
85
|
+
value
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Check if a key exists in the cache (and is not expired)
|
|
90
|
+
#
|
|
91
|
+
# @param key [Object] the cache key
|
|
92
|
+
# @return [Boolean] true if key exists and is not expired
|
|
93
|
+
def key?(key)
|
|
94
|
+
synchronize do
|
|
95
|
+
entry = @data[key]
|
|
96
|
+
return false unless entry
|
|
97
|
+
|
|
98
|
+
if entry.expired?(@ttl)
|
|
99
|
+
@data.delete(key)
|
|
100
|
+
return false
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
true
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Delete a key from the cache
|
|
108
|
+
#
|
|
109
|
+
# @param key [Object] the cache key
|
|
110
|
+
# @return [Object, nil] the deleted value or nil if not found
|
|
111
|
+
def delete(key)
|
|
112
|
+
synchronize do
|
|
113
|
+
entry = @data.delete(key)
|
|
114
|
+
entry&.value
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Clear all entries from the cache
|
|
119
|
+
#
|
|
120
|
+
# @return [void]
|
|
121
|
+
def clear
|
|
122
|
+
synchronize do
|
|
123
|
+
@data.clear
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Get the current number of entries
|
|
128
|
+
#
|
|
129
|
+
# @return [Integer] number of entries
|
|
130
|
+
def size
|
|
131
|
+
@data.size
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Check if the cache is empty
|
|
135
|
+
#
|
|
136
|
+
# @return [Boolean] true if cache is empty
|
|
137
|
+
def empty?
|
|
138
|
+
@data.empty?
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Get all keys (excluding expired entries)
|
|
142
|
+
#
|
|
143
|
+
# @return [Array<Object>] array of keys
|
|
144
|
+
def keys
|
|
145
|
+
synchronize do
|
|
146
|
+
cleanup_expired
|
|
147
|
+
@data.keys
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Get cache statistics
|
|
152
|
+
#
|
|
153
|
+
# @return [Hash] statistics about the cache
|
|
154
|
+
def stats
|
|
155
|
+
synchronize do
|
|
156
|
+
cleanup_expired
|
|
157
|
+
{
|
|
158
|
+
size: @data.size,
|
|
159
|
+
max_size: @max_size,
|
|
160
|
+
ttl: @ttl,
|
|
161
|
+
utilization: @data.size.to_f / @max_size
|
|
162
|
+
}
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
private
|
|
167
|
+
|
|
168
|
+
# Synchronize block if thread-safe mode is enabled
|
|
169
|
+
#
|
|
170
|
+
# @api private
|
|
171
|
+
def synchronize(&block)
|
|
172
|
+
if @thread_safe
|
|
173
|
+
@mutex.synchronize(&block)
|
|
174
|
+
else
|
|
175
|
+
yield
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# Evict oldest entries if cache is at capacity
|
|
180
|
+
#
|
|
181
|
+
# @api private
|
|
182
|
+
def evict_if_needed
|
|
183
|
+
return if @data.size < @max_size
|
|
184
|
+
|
|
185
|
+
# Find and remove the least recently used entry
|
|
186
|
+
lru_key = @data.min_by { |_, v| v.accessed_at }&.first
|
|
187
|
+
@data.delete(lru_key) if lru_key
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# Remove expired entries
|
|
191
|
+
#
|
|
192
|
+
# @api private
|
|
193
|
+
def cleanup_expired
|
|
194
|
+
return unless @ttl
|
|
195
|
+
|
|
196
|
+
@data.delete_if { |_, entry| entry.expired?(@ttl) }
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
end
|