ukiryu 0.1.1 → 0.1.3
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/release.yml +58 -14
- data/.gitignore +3 -0
- data/.rubocop_todo.yml +170 -79
- data/Gemfile +1 -1
- data/README.adoc +1603 -576
- data/docs/.gitignore +1 -0
- data/docs/Gemfile +10 -0
- data/docs/INDEX.adoc +261 -0
- data/docs/_config.yml +180 -0
- data/docs/advanced/custom-tool-classes.adoc +581 -0
- data/docs/advanced/index.adoc +20 -0
- data/docs/features/configuration.adoc +657 -0
- data/docs/features/index.adoc +31 -0
- data/docs/features/platform-support.adoc +488 -0
- data/docs/getting-started/core-concepts.adoc +666 -0
- data/docs/getting-started/index.adoc +36 -0
- data/docs/getting-started/installation.adoc +216 -0
- data/docs/getting-started/quick-start.adoc +258 -0
- data/docs/guides/env-var-sets.adoc +388 -0
- data/docs/guides/index.adoc +20 -0
- data/docs/interfaces/cli.adoc +609 -0
- data/docs/interfaces/index.adoc +153 -0
- data/docs/interfaces/ruby-api.adoc +538 -0
- data/docs/lychee.toml +49 -0
- data/docs/reference/configuration-options.adoc +720 -0
- data/docs/reference/error-codes.adoc +634 -0
- data/docs/reference/index.adoc +20 -0
- data/docs/reference/ruby-api.adoc +1217 -0
- data/docs/understanding/index.adoc +20 -0
- data/lib/ukiryu/cli.rb +43 -58
- data/lib/ukiryu/cli_commands/base_command.rb +16 -27
- data/lib/ukiryu/cli_commands/cache_command.rb +100 -0
- data/lib/ukiryu/cli_commands/commands_command.rb +8 -8
- data/lib/ukiryu/cli_commands/commands_command.rb.fixed +1 -1
- data/lib/ukiryu/cli_commands/config_command.rb +49 -7
- data/lib/ukiryu/cli_commands/definitions_command.rb +254 -0
- data/lib/ukiryu/cli_commands/describe_command.rb +13 -7
- data/lib/ukiryu/cli_commands/describe_command.rb.fixed +1 -1
- data/lib/ukiryu/cli_commands/docs_command.rb +148 -0
- data/lib/ukiryu/cli_commands/exec_inline_command.rb.fixed +1 -1
- data/lib/ukiryu/cli_commands/extract_command.rb +2 -2
- data/lib/ukiryu/cli_commands/info_command.rb +7 -7
- data/lib/ukiryu/cli_commands/lint_command.rb +167 -0
- data/lib/ukiryu/cli_commands/list_command.rb +6 -6
- data/lib/ukiryu/cli_commands/opts_command.rb +2 -2
- data/lib/ukiryu/cli_commands/opts_command.rb.fixed +1 -1
- data/lib/ukiryu/cli_commands/register_command.rb +144 -0
- data/lib/ukiryu/cli_commands/resolve_command.rb +124 -0
- data/lib/ukiryu/cli_commands/run_command.rb +38 -14
- data/lib/ukiryu/cli_commands/run_file_command.rb +2 -2
- data/lib/ukiryu/cli_commands/system_command.rb +50 -32
- data/lib/ukiryu/cli_commands/validate_command.rb +452 -51
- data/lib/ukiryu/cli_commands/which_command.rb +5 -5
- data/lib/ukiryu/command_builder.rb +81 -23
- data/lib/ukiryu/config/env_provider.rb +3 -3
- data/lib/ukiryu/config/env_schema.rb +6 -6
- data/lib/ukiryu/config.rb +11 -11
- data/lib/ukiryu/definition/definition_cache.rb +238 -0
- data/lib/ukiryu/definition/definition_composer.rb +257 -0
- data/lib/ukiryu/definition/definition_linter.rb +460 -0
- data/lib/ukiryu/definition/definition_validator.rb +320 -0
- data/lib/ukiryu/definition/discovery.rb +239 -0
- data/lib/ukiryu/definition/documentation_generator.rb +429 -0
- data/lib/ukiryu/definition/lint_issue.rb +168 -0
- data/lib/ukiryu/definition/loader.rb +139 -0
- data/lib/ukiryu/definition/metadata.rb +159 -0
- data/lib/ukiryu/definition/source.rb +87 -0
- data/lib/ukiryu/definition/sources/file.rb +138 -0
- data/lib/ukiryu/definition/sources/string.rb +88 -0
- data/lib/ukiryu/definition/validation_result.rb +158 -0
- data/lib/ukiryu/definition/version_resolver.rb +194 -0
- data/lib/ukiryu/definition.rb +40 -0
- data/lib/ukiryu/errors.rb +6 -0
- data/lib/ukiryu/execution_context.rb +11 -11
- data/lib/ukiryu/executor.rb +6 -0
- data/lib/ukiryu/extractors/extractor.rb +6 -5
- data/lib/ukiryu/extractors/help_parser.rb +13 -19
- data/lib/ukiryu/logger.rb +3 -1
- data/lib/ukiryu/models/command_definition.rb +3 -3
- data/lib/ukiryu/models/command_info.rb +1 -1
- data/lib/ukiryu/models/components.rb +1 -3
- data/lib/ukiryu/models/env_var_definition.rb +11 -3
- data/lib/ukiryu/models/flag_definition.rb +15 -0
- data/lib/ukiryu/models/option_definition.rb +7 -7
- data/lib/ukiryu/models/platform_profile.rb +6 -3
- data/lib/ukiryu/models/routing.rb +1 -1
- data/lib/ukiryu/models/tool_definition.rb +2 -4
- data/lib/ukiryu/models/tool_metadata.rb +6 -6
- data/lib/ukiryu/models/validation_result.rb +1 -1
- data/lib/ukiryu/models/version_compatibility.rb +6 -3
- data/lib/ukiryu/models/version_detection.rb +10 -1
- data/lib/ukiryu/{registry.rb → register.rb} +54 -38
- data/lib/ukiryu/register_auto_manager.rb +268 -0
- data/lib/ukiryu/schema_validator.rb +31 -10
- data/lib/ukiryu/shell/base.rb +18 -0
- data/lib/ukiryu/shell/bash.rb +19 -1
- data/lib/ukiryu/shell/cmd.rb +11 -1
- data/lib/ukiryu/shell/powershell.rb +11 -1
- data/lib/ukiryu/shell.rb +1 -1
- data/lib/ukiryu/tool.rb +107 -95
- data/lib/ukiryu/tool_index.rb +22 -22
- data/lib/ukiryu/tools/base.rb +12 -25
- data/lib/ukiryu/tools/generator.rb +7 -7
- data/lib/ukiryu/tools.rb +3 -3
- data/lib/ukiryu/type.rb +20 -5
- data/lib/ukiryu/version.rb +1 -1
- data/lib/ukiryu/version_detector.rb +21 -2
- data/lib/ukiryu.rb +6 -3
- data/ukiryu-proposal.md +41 -41
- data/ukiryu.gemspec +1 -0
- metadata +64 -8
- data/.gitmodules +0 -3
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../models/tool_definition'
|
|
4
|
+
|
|
5
|
+
module Ukiryu
|
|
6
|
+
module Definition
|
|
7
|
+
# Compose tool definitions from multiple sources
|
|
8
|
+
#
|
|
9
|
+
# This class handles merging and composing tool definitions,
|
|
10
|
+
# allowing for profile inheritance and command mixins.
|
|
11
|
+
class DefinitionComposer
|
|
12
|
+
# Merge strategies
|
|
13
|
+
MERGE_STRATEGIES = %i[override replace prepend append].freeze
|
|
14
|
+
|
|
15
|
+
# Compose a definition with its includes/inherits
|
|
16
|
+
#
|
|
17
|
+
# @param definition [Models::ToolDefinition] the base definition
|
|
18
|
+
# @param loader [Object] the loader to use for loading dependencies
|
|
19
|
+
# @return [Models::ToolDefinition] the composed definition
|
|
20
|
+
def self.compose(definition, loader: nil)
|
|
21
|
+
@loader = loader || Loader
|
|
22
|
+
|
|
23
|
+
# Process inherits first (base definitions)
|
|
24
|
+
definition = process_inherits(definition) if definition.respond_to?(:inherits) && definition.inherits
|
|
25
|
+
|
|
26
|
+
# Process includes (additions)
|
|
27
|
+
definition = process_includes(definition) if definition.respond_to?(:includes) && definition.includes
|
|
28
|
+
|
|
29
|
+
definition
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Process inherits clauses
|
|
33
|
+
#
|
|
34
|
+
# @param definition [Models::ToolDefinition] the definition
|
|
35
|
+
# @return [Models::ToolDefinition] the merged definition
|
|
36
|
+
def self.process_inherits(definition)
|
|
37
|
+
inherits = definition.inherits || []
|
|
38
|
+
return definition if inherits.empty?
|
|
39
|
+
|
|
40
|
+
# Load base definitions and merge them
|
|
41
|
+
inherits.reverse.reduce(definition) do |current, inherit_spec|
|
|
42
|
+
base_def = load_base_definition(inherit_spec)
|
|
43
|
+
next current unless base_def
|
|
44
|
+
|
|
45
|
+
merge_definitions(base_def, current, strategy: :replace)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Process includes clauses
|
|
50
|
+
#
|
|
51
|
+
# @param definition [Models::ToolDefinition] the definition
|
|
52
|
+
# @return [Models::ToolDefinition] the merged definition
|
|
53
|
+
def self.process_includes(definition)
|
|
54
|
+
includes = definition.includes || []
|
|
55
|
+
return definition if includes.empty?
|
|
56
|
+
|
|
57
|
+
# Load included definitions and merge them
|
|
58
|
+
includes.reduce(definition) do |current, include_spec|
|
|
59
|
+
include_def = load_base_definition(include_spec)
|
|
60
|
+
next current unless include_def
|
|
61
|
+
|
|
62
|
+
merge_definitions(current, include_def, strategy: :append)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Load a base definition from a spec
|
|
67
|
+
#
|
|
68
|
+
# @param spec [Hash, String] the specification
|
|
69
|
+
# @return [Models::ToolDefinition, nil] the loaded definition
|
|
70
|
+
def self.load_base_definition(spec)
|
|
71
|
+
case spec
|
|
72
|
+
when String
|
|
73
|
+
# Just a tool name, find latest version
|
|
74
|
+
metadata = Discovery.find(spec)
|
|
75
|
+
metadata&.load_definition
|
|
76
|
+
when Hash
|
|
77
|
+
tool = spec[:tool] || spec['tool']
|
|
78
|
+
version = spec[:version] || spec['version']
|
|
79
|
+
|
|
80
|
+
metadata = if version
|
|
81
|
+
Discovery.find(tool, version)
|
|
82
|
+
else
|
|
83
|
+
Discovery.find(tool)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
metadata&.load_definition
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Merge two definitions
|
|
91
|
+
#
|
|
92
|
+
# @param base [Models::ToolDefinition] the base definition
|
|
93
|
+
# @param addition [Models::ToolDefinition] the definition to merge in
|
|
94
|
+
# @param strategy [Symbol] the merge strategy
|
|
95
|
+
# @return [Models::ToolDefinition] the merged definition
|
|
96
|
+
def self.merge_definitions(base, addition, strategy: :override)
|
|
97
|
+
# Create a merged definition by deep copying and merging
|
|
98
|
+
merged = base.dup
|
|
99
|
+
|
|
100
|
+
# Merge profiles
|
|
101
|
+
if addition.profiles
|
|
102
|
+
merged.profiles ||= []
|
|
103
|
+
|
|
104
|
+
case strategy
|
|
105
|
+
when :replace
|
|
106
|
+
merged.profiles = addition.profiles.dup
|
|
107
|
+
when :override
|
|
108
|
+
# Merge by profile name, addition overrides base
|
|
109
|
+
merged.profiles = merge_profiles_by_name(base.profiles || [], addition.profiles)
|
|
110
|
+
when :append
|
|
111
|
+
# Append addition profiles
|
|
112
|
+
merged.profiles = (base.profiles || []) + addition.profiles
|
|
113
|
+
when :prepend
|
|
114
|
+
# Prepend addition profiles
|
|
115
|
+
merged.profiles = addition.profiles + (base.profiles || [])
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Merge commands if profiles have them
|
|
120
|
+
merged.profiles&.each do |profile|
|
|
121
|
+
# Find corresponding profile in addition
|
|
122
|
+
addition_profile = find_profile(addition.profiles, profile.name)
|
|
123
|
+
next unless addition_profile
|
|
124
|
+
|
|
125
|
+
# Merge commands
|
|
126
|
+
next unless addition_profile.commands
|
|
127
|
+
|
|
128
|
+
profile.commands ||= []
|
|
129
|
+
case strategy
|
|
130
|
+
when :replace
|
|
131
|
+
profile.commands = addition_profile.commands.dup
|
|
132
|
+
when :override
|
|
133
|
+
profile.commands = merge_commands_by_name(profile.commands, addition_profile.commands)
|
|
134
|
+
when :append
|
|
135
|
+
profile.commands = profile.commands + addition_profile.commands
|
|
136
|
+
when :prepend
|
|
137
|
+
profile.commands = addition_profile.commands + profile.commands
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
merged
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Merge profiles by name
|
|
145
|
+
#
|
|
146
|
+
# @param base_profiles [Array] base profiles
|
|
147
|
+
# @param addition_profiles [Array] profiles to merge
|
|
148
|
+
# @return [Array] merged profiles
|
|
149
|
+
def self.merge_profiles_by_name(base_profiles, addition_profiles)
|
|
150
|
+
merged = base_profiles.dup
|
|
151
|
+
|
|
152
|
+
addition_profiles.each do |addition_profile|
|
|
153
|
+
existing_index = merged.find_index { |p| p.name == addition_profile.name }
|
|
154
|
+
|
|
155
|
+
if existing_index
|
|
156
|
+
# Merge the profile content
|
|
157
|
+
existing = merged[existing_index]
|
|
158
|
+
merged[existing_index] = merge_profile_content(existing, addition_profile)
|
|
159
|
+
else
|
|
160
|
+
# Add new profile
|
|
161
|
+
merged << addition_profile.dup
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
merged
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Merge the content of two profiles
|
|
169
|
+
#
|
|
170
|
+
# @param base [Object] the base profile
|
|
171
|
+
# @param addition [Object] the profile to merge
|
|
172
|
+
# @return [Object] merged profile
|
|
173
|
+
def self.merge_profile_content(base, addition)
|
|
174
|
+
# Create a new profile that merges both
|
|
175
|
+
merged = base.dup
|
|
176
|
+
|
|
177
|
+
# Merge commands
|
|
178
|
+
merged.commands = merge_commands_by_name(base.commands || [], addition.commands) if addition.commands
|
|
179
|
+
|
|
180
|
+
# Merge other arrays if they exist
|
|
181
|
+
%i[environment options flags arguments].each do |attr|
|
|
182
|
+
if addition.respond_to?(attr) && addition.send(attr)
|
|
183
|
+
base_items = base.respond_to?(attr) ? base.send(attr) : []
|
|
184
|
+
merged.send("#{attr}=", base_items + addition.send(attr))
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# Override scalars
|
|
189
|
+
%i[description option_style].each do |attr|
|
|
190
|
+
merged.send("#{attr}=", addition.send(attr)) if addition.respond_to?(attr) && !addition.send(attr).nil?
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
merged
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# Merge commands by name
|
|
197
|
+
#
|
|
198
|
+
# @param base_commands [Array] base commands
|
|
199
|
+
# @param addition_commands [Array] commands to merge
|
|
200
|
+
# @return [Array] merged commands
|
|
201
|
+
def self.merge_commands_by_name(base_commands, addition_commands)
|
|
202
|
+
merged = base_commands.dup
|
|
203
|
+
|
|
204
|
+
addition_commands.each do |addition_command|
|
|
205
|
+
existing_index = merged.find_index { |c| c.name == addition_command.name }
|
|
206
|
+
|
|
207
|
+
if existing_index
|
|
208
|
+
# Override existing command
|
|
209
|
+
merged[existing_index] = addition_command.dup
|
|
210
|
+
else
|
|
211
|
+
# Add new command
|
|
212
|
+
merged << addition_command.dup
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
merged
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
# Find a profile by name
|
|
220
|
+
#
|
|
221
|
+
# @param profiles [Array] the profiles array
|
|
222
|
+
# @param name [String] the profile name
|
|
223
|
+
# @return [Object, nil] the profile or nil
|
|
224
|
+
def self.find_profile(profiles, name)
|
|
225
|
+
return nil unless profiles
|
|
226
|
+
|
|
227
|
+
profiles.find { |p| p.name == name }
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
# Validate that a definition can be composed
|
|
231
|
+
#
|
|
232
|
+
# @param definition [Models::ToolDefinition] the definition to validate
|
|
233
|
+
# @return [Array<String>] array of validation errors (empty if valid)
|
|
234
|
+
def self.validate_composable(definition)
|
|
235
|
+
errors = []
|
|
236
|
+
|
|
237
|
+
# Check inherits
|
|
238
|
+
if definition.respond_to?(:inherits) && definition.inherits
|
|
239
|
+
definition.inherits.each do |inherit_spec|
|
|
240
|
+
base_def = load_base_definition(inherit_spec)
|
|
241
|
+
errors << "Cannot inherit from '#{inherit_spec}': definition not found" unless base_def
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
# Check includes
|
|
246
|
+
if definition.respond_to?(:includes) && definition.includes
|
|
247
|
+
definition.includes.each do |include_spec|
|
|
248
|
+
include_def = load_base_definition(include_spec)
|
|
249
|
+
errors << "Cannot include '#{include_spec}': definition not found" unless include_def
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
errors
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
end
|