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,429 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ukiryu
|
|
4
|
+
module Definition
|
|
5
|
+
# Generate documentation from tool definitions
|
|
6
|
+
#
|
|
7
|
+
# This class generates human-readable documentation from tool definitions
|
|
8
|
+
# in various formats (Markdown, AsciiDoc).
|
|
9
|
+
class DocumentationGenerator
|
|
10
|
+
# Supported formats
|
|
11
|
+
FORMATS = %i[markdown asciidoc md].freeze
|
|
12
|
+
|
|
13
|
+
class << self
|
|
14
|
+
# Generate documentation for a definition
|
|
15
|
+
#
|
|
16
|
+
# @param definition [Hash] the definition
|
|
17
|
+
# @param format [Symbol] output format (:markdown, :asciidoc)
|
|
18
|
+
# @return [String] generated documentation
|
|
19
|
+
def generate(definition, format: :markdown)
|
|
20
|
+
format = normalize_format(format)
|
|
21
|
+
|
|
22
|
+
case format
|
|
23
|
+
when :markdown
|
|
24
|
+
generate_markdown(definition)
|
|
25
|
+
when :asciidoc
|
|
26
|
+
generate_asciidoc(definition)
|
|
27
|
+
else
|
|
28
|
+
raise ArgumentError, "Unsupported format: #{format}"
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Generate documentation to a file
|
|
33
|
+
#
|
|
34
|
+
# @param definition [Hash] the definition
|
|
35
|
+
# @param file_path [String] output file path
|
|
36
|
+
# @param format [Symbol] output format
|
|
37
|
+
def generate_to_file(definition, file_path, format: :markdown)
|
|
38
|
+
content = generate(definition, format: format)
|
|
39
|
+
File.write(file_path, content)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Generate documentation for a specific command
|
|
43
|
+
#
|
|
44
|
+
# @param command_name [String] the command name
|
|
45
|
+
# @param command_def [Hash] the command definition
|
|
46
|
+
# @param format [Symbol] output format
|
|
47
|
+
# @return [String] generated command documentation
|
|
48
|
+
def generate_command_docs(command_name, command_def, format: :markdown)
|
|
49
|
+
format = normalize_format(format)
|
|
50
|
+
|
|
51
|
+
case format
|
|
52
|
+
when :markdown
|
|
53
|
+
generate_command_markdown(command_name, command_def)
|
|
54
|
+
when :asciidoc
|
|
55
|
+
generate_command_asciidoc(command_name, command_def)
|
|
56
|
+
else
|
|
57
|
+
raise ArgumentError, "Unsupported format: #{format}"
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
private
|
|
62
|
+
|
|
63
|
+
# Normalize format
|
|
64
|
+
#
|
|
65
|
+
# @param format [Symbol] the format
|
|
66
|
+
# @return [Symbol] normalized format
|
|
67
|
+
def normalize_format(format)
|
|
68
|
+
return :markdown if format == :md
|
|
69
|
+
|
|
70
|
+
format
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Generate Markdown documentation
|
|
74
|
+
#
|
|
75
|
+
# @param definition [Hash] the definition
|
|
76
|
+
# @return [String] Markdown documentation
|
|
77
|
+
def generate_markdown(definition)
|
|
78
|
+
output = []
|
|
79
|
+
tool_name = definition[:name] || 'Unknown'
|
|
80
|
+
version = definition[:version]
|
|
81
|
+
|
|
82
|
+
# Title
|
|
83
|
+
output << "# #{tool_name}"
|
|
84
|
+
output << '' if version
|
|
85
|
+
output << "Version: #{version}" if version
|
|
86
|
+
output << ''
|
|
87
|
+
|
|
88
|
+
# Description
|
|
89
|
+
if definition[:description]
|
|
90
|
+
output << definition[:description]
|
|
91
|
+
output << ''
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Metadata
|
|
95
|
+
output << '## Overview'
|
|
96
|
+
output << ''
|
|
97
|
+
output << '| Property | Value |'
|
|
98
|
+
output << '|----------|-------|'
|
|
99
|
+
output << "| Name | `#{tool_name}` |"
|
|
100
|
+
output << "| Version | `#{version}` |" if version
|
|
101
|
+
output << "| Homepage | #{definition[:homepage]} |" if definition[:homepage]
|
|
102
|
+
output << ''
|
|
103
|
+
|
|
104
|
+
# Platforms
|
|
105
|
+
platforms = extract_platforms(definition)
|
|
106
|
+
if platforms.any?
|
|
107
|
+
output << '### Supported Platforms'
|
|
108
|
+
output << ''
|
|
109
|
+
platforms.each do |platform|
|
|
110
|
+
shells = extract_shells_for_platform(definition, platform)
|
|
111
|
+
output << "- **#{platform.to_s.capitalize}**: #{shells.join(', ')}"
|
|
112
|
+
end
|
|
113
|
+
output << ''
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Installation
|
|
117
|
+
if definition[:install]
|
|
118
|
+
output << '## Installation'
|
|
119
|
+
output << ''
|
|
120
|
+
output << render_installation_markdown(definition[:install])
|
|
121
|
+
output << ''
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Commands
|
|
125
|
+
commands = extract_commands(definition)
|
|
126
|
+
if commands.any?
|
|
127
|
+
output << '## Commands'
|
|
128
|
+
output << ''
|
|
129
|
+
commands.each do |cmd_name, cmd_def|
|
|
130
|
+
output << generate_command_markdown(cmd_name, cmd_def)
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Options reference
|
|
135
|
+
if commands.any?
|
|
136
|
+
output << '## Options Reference'
|
|
137
|
+
output << ''
|
|
138
|
+
commands.each do |cmd_name, cmd_def|
|
|
139
|
+
options = cmd_def[:options] || []
|
|
140
|
+
flags = cmd_def[:flags] || []
|
|
141
|
+
|
|
142
|
+
next unless options.any? || flags.any?
|
|
143
|
+
|
|
144
|
+
output << "### `#{cmd_name}`"
|
|
145
|
+
output << ''
|
|
146
|
+
|
|
147
|
+
if options.any?
|
|
148
|
+
output << '#### Options'
|
|
149
|
+
output << ''
|
|
150
|
+
options.each do |opt|
|
|
151
|
+
output << render_option_markdown(opt)
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
if flags.any?
|
|
156
|
+
output << '#### Flags'
|
|
157
|
+
output << ''
|
|
158
|
+
flags.each do |flag|
|
|
159
|
+
output << render_flag_markdown(flag)
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
output << ''
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
output.join("\n")
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Generate command documentation in Markdown
|
|
171
|
+
#
|
|
172
|
+
# @param command_name [String] the command name
|
|
173
|
+
# @param command_def [Hash] the command definition
|
|
174
|
+
# @return [String] command documentation
|
|
175
|
+
def generate_command_markdown(command_name, command_def)
|
|
176
|
+
output = []
|
|
177
|
+
output << "### `#{command_name}`"
|
|
178
|
+
output << ''
|
|
179
|
+
|
|
180
|
+
if command_def[:description]
|
|
181
|
+
output << command_def[:description]
|
|
182
|
+
output << ''
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# Arguments
|
|
186
|
+
if command_def[:arguments]
|
|
187
|
+
output << '#### Arguments'
|
|
188
|
+
output << ''
|
|
189
|
+
command_def[:arguments].each do |arg|
|
|
190
|
+
output << render_argument_markdown(arg)
|
|
191
|
+
end
|
|
192
|
+
output << ''
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
output.join("\n")
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# Render option in Markdown
|
|
199
|
+
#
|
|
200
|
+
# @param opt [Hash] the option
|
|
201
|
+
# @return [String] rendered option
|
|
202
|
+
def render_option_markdown(opt)
|
|
203
|
+
cli = opt[:cli] || opt[:name]
|
|
204
|
+
required = opt[:required] ? '**(required)**' : '(optional)'
|
|
205
|
+
desc = opt[:description] || ''
|
|
206
|
+
|
|
207
|
+
output = "- `#{cli}` #{required}"
|
|
208
|
+
output << " - #{desc}" if desc
|
|
209
|
+
output << "\n\n Type: `#{opt[:type]}`" if opt[:type]
|
|
210
|
+
output
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# Render flag in Markdown
|
|
214
|
+
#
|
|
215
|
+
# @param flag [Hash] the flag
|
|
216
|
+
# @return [String] rendered flag
|
|
217
|
+
def render_flag_markdown(flag)
|
|
218
|
+
cli = flag[:cli] || flag[:name]
|
|
219
|
+
desc = flag[:description] || ''
|
|
220
|
+
|
|
221
|
+
output = "- `#{cli}`"
|
|
222
|
+
output << " - #{desc}" if desc
|
|
223
|
+
output
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
# Render argument in Markdown
|
|
227
|
+
#
|
|
228
|
+
# @param arg [Hash] the argument
|
|
229
|
+
# @return [String] rendered argument
|
|
230
|
+
def render_argument_markdown(arg)
|
|
231
|
+
name = arg[:name]
|
|
232
|
+
type = arg[:type] || 'any'
|
|
233
|
+
desc = arg[:description] || ''
|
|
234
|
+
required = arg[:required] ? '**required**' : 'optional'
|
|
235
|
+
|
|
236
|
+
output = "- **`#{name}`** (#{type}, #{required})"
|
|
237
|
+
output << " - #{desc}" if desc
|
|
238
|
+
output
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
# Render installation instructions in Markdown
|
|
242
|
+
#
|
|
243
|
+
# @param install [Hash] installation instructions
|
|
244
|
+
# @return [String] rendered installation
|
|
245
|
+
def render_installation_markdown(install)
|
|
246
|
+
output = []
|
|
247
|
+
|
|
248
|
+
if install[:macos]
|
|
249
|
+
output << '#### macOS'
|
|
250
|
+
output << '```bash'
|
|
251
|
+
output << install[:macos]
|
|
252
|
+
output << '```'
|
|
253
|
+
output << ''
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
if install[:linux]
|
|
257
|
+
output << '#### Linux'
|
|
258
|
+
output << '```bash'
|
|
259
|
+
output << install[:linux]
|
|
260
|
+
output << '```'
|
|
261
|
+
output << ''
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
if install[:windows]
|
|
265
|
+
output << '#### Windows'
|
|
266
|
+
output << '```powershell'
|
|
267
|
+
output << install[:windows]
|
|
268
|
+
output << '```'
|
|
269
|
+
output << ''
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
output.join("\n")
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
# Generate AsciiDoc documentation
|
|
276
|
+
#
|
|
277
|
+
# @param definition [Hash] the definition
|
|
278
|
+
# @return [String] AsciiDoc documentation
|
|
279
|
+
def generate_asciidoc(definition)
|
|
280
|
+
output = []
|
|
281
|
+
tool_name = definition[:name] || 'Unknown'
|
|
282
|
+
version = definition[:version]
|
|
283
|
+
|
|
284
|
+
# Title
|
|
285
|
+
output << "= #{tool_name}"
|
|
286
|
+
output << '' if version
|
|
287
|
+
output << ':toc:' if definition[:profiles] # Only add TOC if there's content
|
|
288
|
+
output << ''
|
|
289
|
+
|
|
290
|
+
if version
|
|
291
|
+
output << "Version: #{version}"
|
|
292
|
+
output << ''
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
# Description
|
|
296
|
+
if definition[:description]
|
|
297
|
+
output << definition[:description]
|
|
298
|
+
output << ''
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
# Overview
|
|
302
|
+
output << '== Overview'
|
|
303
|
+
output << ''
|
|
304
|
+
output << '[cols="1,2"]'
|
|
305
|
+
output << '|==='
|
|
306
|
+
output << '| Property | Value'
|
|
307
|
+
output << "| Name | `#{tool_name}`"
|
|
308
|
+
output << "| Version | `#{version}`" if version
|
|
309
|
+
output << "| Homepage | #{definition[:homepage]}" if definition[:homepage]
|
|
310
|
+
output << '|==='
|
|
311
|
+
output << ''
|
|
312
|
+
|
|
313
|
+
# Platforms
|
|
314
|
+
platforms = extract_platforms(definition)
|
|
315
|
+
if platforms.any?
|
|
316
|
+
output << '=== Supported Platforms'
|
|
317
|
+
output << ''
|
|
318
|
+
platforms.each do |platform|
|
|
319
|
+
shells = extract_shells_for_platform(definition, platform)
|
|
320
|
+
output << "* *#{platform.to_s.capitalize}*: #{shells.join(', ')}"
|
|
321
|
+
end
|
|
322
|
+
output << ''
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
# Commands
|
|
326
|
+
commands = extract_commands(definition)
|
|
327
|
+
if commands.any?
|
|
328
|
+
output << '== Commands'
|
|
329
|
+
output << ''
|
|
330
|
+
commands.each do |cmd_name, cmd_def|
|
|
331
|
+
output << generate_command_asciidoc(cmd_name, cmd_def)
|
|
332
|
+
end
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
output.join("\n")
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
# Generate command documentation in AsciiDoc
|
|
339
|
+
#
|
|
340
|
+
# @param command_name [String] the command name
|
|
341
|
+
# @param command_def [Hash] the command definition
|
|
342
|
+
# @return [String] command documentation
|
|
343
|
+
def generate_command_asciidoc(command_name, command_def)
|
|
344
|
+
output = []
|
|
345
|
+
output << "=== `#{command_name}`"
|
|
346
|
+
output << ''
|
|
347
|
+
|
|
348
|
+
if command_def[:description]
|
|
349
|
+
output << command_def[:description]
|
|
350
|
+
output << ''
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
if command_def[:arguments]
|
|
354
|
+
output << '==== Arguments'
|
|
355
|
+
output << ''
|
|
356
|
+
command_def[:arguments].each do |arg|
|
|
357
|
+
name = arg[:name]
|
|
358
|
+
type = arg[:type] || 'any'
|
|
359
|
+
desc = arg[:description] || ''
|
|
360
|
+
required = arg[:required] ? '*required*' : 'optional'
|
|
361
|
+
|
|
362
|
+
output << "* **`#{name}`** (#{type}, #{required})"
|
|
363
|
+
output << ": #{desc}" if desc
|
|
364
|
+
end
|
|
365
|
+
output << ''
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
output.join("\n")
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
# Extract all platforms from definition
|
|
372
|
+
#
|
|
373
|
+
# @param definition [Hash] the definition
|
|
374
|
+
# @return [Array<Symbol>] platforms
|
|
375
|
+
def extract_platforms(definition)
|
|
376
|
+
platforms = Set.new
|
|
377
|
+
return platforms.to_a unless definition[:profiles]
|
|
378
|
+
|
|
379
|
+
definition[:profiles].each do |profile|
|
|
380
|
+
next unless profile[:platforms]
|
|
381
|
+
|
|
382
|
+
profile[:platforms].each { |p| platforms.add(p) }
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
platforms.to_a
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
# Extract shells for a specific platform
|
|
389
|
+
#
|
|
390
|
+
# @param definition [Hash] the definition
|
|
391
|
+
# @param platform [Symbol] the platform
|
|
392
|
+
# @return [Array<Symbol>] shells
|
|
393
|
+
def extract_shells_for_platform(definition, platform)
|
|
394
|
+
shells = Set.new
|
|
395
|
+
return shells.to_a unless definition[:profiles]
|
|
396
|
+
|
|
397
|
+
definition[:profiles].each do |profile|
|
|
398
|
+
next unless profile[:platforms]&.include?(platform)
|
|
399
|
+
next unless profile[:shells]
|
|
400
|
+
|
|
401
|
+
profile[:shells].each { |s| shells.add(s) }
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
shells.to_a.sort
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
# Extract all commands from definition
|
|
408
|
+
#
|
|
409
|
+
# @param definition [Hash] the definition
|
|
410
|
+
# @return [Hash] commands
|
|
411
|
+
def extract_commands(definition)
|
|
412
|
+
commands = {}
|
|
413
|
+
|
|
414
|
+
return commands unless definition[:profiles]
|
|
415
|
+
|
|
416
|
+
definition[:profiles].each do |profile|
|
|
417
|
+
next unless profile[:commands]
|
|
418
|
+
|
|
419
|
+
profile[:commands].each do |name, cmd_def|
|
|
420
|
+
commands[name] = cmd_def unless commands.key?(name)
|
|
421
|
+
end
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
commands
|
|
425
|
+
end
|
|
426
|
+
end
|
|
427
|
+
end
|
|
428
|
+
end
|
|
429
|
+
end
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ukiryu
|
|
4
|
+
module Definition
|
|
5
|
+
# A linting issue found in a definition
|
|
6
|
+
#
|
|
7
|
+
# This class represents a single linting issue with severity,
|
|
8
|
+
# message, location, and optional suggestion.
|
|
9
|
+
class LintIssue
|
|
10
|
+
# Severity levels
|
|
11
|
+
SEVERITY_ERROR = :error
|
|
12
|
+
SEVERITY_WARNING = :warning
|
|
13
|
+
SEVERITY_INFO = :info
|
|
14
|
+
SEVERITY_STYLE = :style
|
|
15
|
+
|
|
16
|
+
attr_reader :severity, :message, :location, :suggestion, :rule_id
|
|
17
|
+
|
|
18
|
+
def initialize(severity:, message:, location: nil, suggestion: nil, rule_id: nil)
|
|
19
|
+
@severity = severity
|
|
20
|
+
@message = message
|
|
21
|
+
@location = location
|
|
22
|
+
@suggestion = suggestion
|
|
23
|
+
@rule_id = rule_id
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Check if this is an error
|
|
27
|
+
#
|
|
28
|
+
# @return [Boolean] true if severity is error
|
|
29
|
+
def error?
|
|
30
|
+
@severity == SEVERITY_ERROR
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Check if this is a warning
|
|
34
|
+
#
|
|
35
|
+
# @return [Boolean] true if severity is warning
|
|
36
|
+
def warning?
|
|
37
|
+
@severity == SEVERITY_WARNING
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Check if this is info
|
|
41
|
+
#
|
|
42
|
+
# @return [Boolean] true if severity is info
|
|
43
|
+
def info?
|
|
44
|
+
@severity == SEVERITY_INFO
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Check if this is style
|
|
48
|
+
#
|
|
49
|
+
# @return [Boolean] true if severity is style
|
|
50
|
+
def style?
|
|
51
|
+
@severity == SEVERITY_STYLE
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Check if this issue has a suggestion
|
|
55
|
+
#
|
|
56
|
+
# @return [Boolean] true if suggestion is present
|
|
57
|
+
def has_suggestion?
|
|
58
|
+
!@suggestion.nil? && !@suggestion.empty?
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Check if this issue has a location
|
|
62
|
+
#
|
|
63
|
+
# @return [Boolean] true if location is present
|
|
64
|
+
def has_location?
|
|
65
|
+
!@location.nil? && !@location.empty?
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Get severity as a readable string
|
|
69
|
+
#
|
|
70
|
+
# @return [String] severity string
|
|
71
|
+
def severity_string
|
|
72
|
+
@severity.to_s.upcase
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Convert to hash
|
|
76
|
+
#
|
|
77
|
+
# @return [Hash] hash representation
|
|
78
|
+
def to_h
|
|
79
|
+
{
|
|
80
|
+
severity: @severity,
|
|
81
|
+
severity_string: severity_string,
|
|
82
|
+
message: @message,
|
|
83
|
+
location: @location,
|
|
84
|
+
suggestion: @suggestion,
|
|
85
|
+
rule_id: @rule_id
|
|
86
|
+
}
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Format as string
|
|
90
|
+
#
|
|
91
|
+
# @return [String] formatted issue
|
|
92
|
+
def to_s
|
|
93
|
+
output = "[#{severity_string}] #{@message}"
|
|
94
|
+
output += " (at #{@location})" if has_location?
|
|
95
|
+
output += "\n Suggestion: #{@suggestion}" if has_suggestion?
|
|
96
|
+
output
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Create an error issue
|
|
100
|
+
#
|
|
101
|
+
# @param message [String] error message
|
|
102
|
+
# @param location [String, nil] issue location
|
|
103
|
+
# @param suggestion [String, nil] fix suggestion
|
|
104
|
+
# @param rule_id [String, nil] rule identifier
|
|
105
|
+
# @return [LintIssue] error issue
|
|
106
|
+
def self.error(message, location: nil, suggestion: nil, rule_id: nil)
|
|
107
|
+
new(
|
|
108
|
+
severity: SEVERITY_ERROR,
|
|
109
|
+
message: message,
|
|
110
|
+
location: location,
|
|
111
|
+
suggestion: suggestion,
|
|
112
|
+
rule_id: rule_id
|
|
113
|
+
)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Create a warning issue
|
|
117
|
+
#
|
|
118
|
+
# @param message [String] warning message
|
|
119
|
+
# @param location [String, nil] issue location
|
|
120
|
+
# @param suggestion [String, nil] fix suggestion
|
|
121
|
+
# @param rule_id [String, nil] rule identifier
|
|
122
|
+
# @return [LintIssue] warning issue
|
|
123
|
+
def self.warning(message, location: nil, suggestion: nil, rule_id: nil)
|
|
124
|
+
new(
|
|
125
|
+
severity: SEVERITY_WARNING,
|
|
126
|
+
message: message,
|
|
127
|
+
location: location,
|
|
128
|
+
suggestion: suggestion,
|
|
129
|
+
rule_id: rule_id
|
|
130
|
+
)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Create an info issue
|
|
134
|
+
#
|
|
135
|
+
# @param message [String] info message
|
|
136
|
+
# @param location [String, nil] issue location
|
|
137
|
+
# @param suggestion [String, nil] fix suggestion
|
|
138
|
+
# @param rule_id [String, nil] rule identifier
|
|
139
|
+
# @return [LintIssue] info issue
|
|
140
|
+
def self.info(message, location: nil, suggestion: nil, rule_id: nil)
|
|
141
|
+
new(
|
|
142
|
+
severity: SEVERITY_INFO,
|
|
143
|
+
message: message,
|
|
144
|
+
location: location,
|
|
145
|
+
suggestion: suggestion,
|
|
146
|
+
rule_id: rule_id
|
|
147
|
+
)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# Create a style issue
|
|
151
|
+
#
|
|
152
|
+
# @param message [String] style message
|
|
153
|
+
# @param location [String, nil] issue location
|
|
154
|
+
# @param suggestion [String, nil] fix suggestion
|
|
155
|
+
# @param rule_id [String, nil] rule identifier
|
|
156
|
+
# @return [LintIssue] style issue
|
|
157
|
+
def self.style(message, location: nil, suggestion: nil, rule_id: nil)
|
|
158
|
+
new(
|
|
159
|
+
severity: SEVERITY_STYLE,
|
|
160
|
+
message: message,
|
|
161
|
+
location: location,
|
|
162
|
+
suggestion: suggestion,
|
|
163
|
+
rule_id: rule_id
|
|
164
|
+
)
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|