ukiryu 0.1.6 → 0.2.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 +4 -4
- data/lib/ukiryu/cache.rb +6 -0
- data/lib/ukiryu/cache_registry.rb +64 -0
- data/lib/ukiryu/cli_commands/base_command.rb +6 -5
- data/lib/ukiryu/cli_commands/config_command.rb +7 -10
- data/lib/ukiryu/cli_commands/register_command.rb +27 -18
- data/lib/ukiryu/cli_commands/validate_command.rb +2 -2
- data/lib/ukiryu/command_builder.rb +83 -50
- data/lib/ukiryu/config.rb +13 -2
- data/lib/ukiryu/debug.rb +20 -9
- data/lib/ukiryu/definition/loader.rb +3 -3
- data/lib/ukiryu/errors.rb +37 -37
- data/lib/ukiryu/executable_locator.rb +40 -16
- data/lib/ukiryu/extractors/base_extractor.rb +2 -1
- data/lib/ukiryu/extractors/help_parser.rb +3 -0
- data/lib/ukiryu/logger.rb +51 -0
- data/lib/ukiryu/models/implementation_index.rb +2 -1
- data/lib/ukiryu/models/implementation_version.rb +18 -1
- data/lib/ukiryu/models/interface.rb +2 -1
- data/lib/ukiryu/models/run_environment.rb +0 -2
- data/lib/ukiryu/models/semantic_version.rb +174 -0
- data/lib/ukiryu/models/stage_metrics.rb +0 -1
- data/lib/ukiryu/register.rb +473 -232
- data/lib/ukiryu/shell/powershell.rb +209 -89
- data/lib/ukiryu/shell/sh.rb +4 -1
- data/lib/ukiryu/shell.rb +60 -2
- data/lib/ukiryu/tool/command_resolution.rb +2 -1
- data/lib/ukiryu/tool/executable_discovery.rb +14 -15
- data/lib/ukiryu/tool/loader.rb +543 -0
- data/lib/ukiryu/tool/version_detection.rb +1 -3
- data/lib/ukiryu/tool.rb +79 -87
- data/lib/ukiryu/tool_index.rb +127 -62
- data/lib/ukiryu/tools/base.rb +4 -2
- data/lib/ukiryu/type.rb +26 -15
- data/lib/ukiryu/version.rb +1 -1
- data/lib/ukiryu.rb +1 -1
- data/spec/fixtures/profiles/ghostscript_10.0.yaml +50 -0
- data/spec/fixtures/register/tools/ghostscript/default/10.0.yaml +6 -0
- data/spec/spec_helper.rb +10 -6
- data/spec/support/tool_helper.rb +2 -0
- data/spec/ukiryu/definition/loader_spec.rb +2 -2
- data/spec/ukiryu/executor_spec.rb +6 -3
- data/spec/ukiryu/models/execution_report_spec.rb +3 -2
- data/spec/ukiryu/models/semantic_version_spec.rb +284 -0
- data/spec/ukiryu/shell/powershell_integration_spec.rb +165 -0
- data/spec/ukiryu/shell/powershell_real_command_spec.rb +143 -0
- data/spec/ukiryu/shell/powershell_spec.rb +286 -51
- data/spec/ukiryu/tool/loader_spec.rb +148 -0
- data/spec/ukiryu/tool_index_spec.rb +110 -18
- data/spec/ukiryu/tools/ghostscript_spec.rb +242 -0
- data/spec/ukiryu/tools/imagemagick_spec.rb +2 -1
- data/spec/ukiryu/tools/inkscape_spec.rb +4 -2
- metadata +14 -2
- data/lib/ukiryu/register_auto_manager.rb +0 -342
|
@@ -1,342 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'git'
|
|
4
|
-
require 'fileutils'
|
|
5
|
-
require 'pathname'
|
|
6
|
-
|
|
7
|
-
module Ukiryu
|
|
8
|
-
# Manages automatic register cloning and updates
|
|
9
|
-
#
|
|
10
|
-
# This class handles:
|
|
11
|
-
# - Auto-cloning the register repository to ~/.ukiryu/register
|
|
12
|
-
# - Detecting development mode (local submodule)
|
|
13
|
-
# - Validating register integrity
|
|
14
|
-
# - Providing register path to the Register class
|
|
15
|
-
#
|
|
16
|
-
# @api private
|
|
17
|
-
class RegisterAutoManager
|
|
18
|
-
# GitHub repository URL for the register
|
|
19
|
-
REGISTER_URL = 'https://github.com/ukiryu/register'
|
|
20
|
-
|
|
21
|
-
# Default local directory for the register
|
|
22
|
-
DEFAULT_DIR = '~/.ukiryu/register'
|
|
23
|
-
|
|
24
|
-
class << self
|
|
25
|
-
# Get the register path, ensuring it exists
|
|
26
|
-
#
|
|
27
|
-
# Checks in order:
|
|
28
|
-
# 1. Environment variable UKIRYU_REGISTER
|
|
29
|
-
# 2. Development register (../../register relative to gem lib)
|
|
30
|
-
# 3. User's local clone (~/.ukiryu/register)
|
|
31
|
-
#
|
|
32
|
-
# @return [String, nil] the register path, or nil if unavailable
|
|
33
|
-
def register_path
|
|
34
|
-
# Debug logging
|
|
35
|
-
if ENV['UKIRYU_DEBUG_EXECUTABLE']
|
|
36
|
-
warn '[UKIRYU DEBUG RegisterAutoManager] Checking register_path...'
|
|
37
|
-
warn "[UKIRYU DEBUG RegisterAutoManager] ENV['UKIRYU_REGISTER'] = #{ENV['UKIRYU_REGISTER'].inspect}"
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
# 1. Environment variable has highest priority
|
|
41
|
-
env_path = ENV['UKIRYU_REGISTER']
|
|
42
|
-
if env_path && Dir.exist?(env_path)
|
|
43
|
-
warn "[UKIRU DEBUG RegisterAutoManager] Using ENV register: #{env_path}" if ENV['UKIRYU_DEBUG_EXECUTABLE']
|
|
44
|
-
return env_path
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
warn "[UKIRYU DEBUG RegisterAutoManager] ENV path doesn't exist or not set" if ENV['UKIRYU_DEBUG_EXECUTABLE']
|
|
48
|
-
|
|
49
|
-
# 2. Check development register (../../../register relative to this file)
|
|
50
|
-
# Use Pathname for reliable path calculation
|
|
51
|
-
this_file = Pathname.new(__FILE__).realpath
|
|
52
|
-
dev_path = this_file.parent.parent.parent.parent + 'register'
|
|
53
|
-
if dev_path.exist?
|
|
54
|
-
warn "[UKIRYU DEBUG RegisterAutoManager] Using DEV register: #{dev_path}" if ENV['UKIRYU_DEBUG_EXECUTABLE']
|
|
55
|
-
return dev_path.to_s
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
warn "[UKIRYU DEBUG RegisterAutoManager] DEV register doesn't exist" if ENV['UKIRYU_DEBUG_EXECUTABLE']
|
|
59
|
-
|
|
60
|
-
# 3. Use user's local clone, create if needed
|
|
61
|
-
ensure_user_clone
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
# Check if the register exists and is valid
|
|
65
|
-
#
|
|
66
|
-
# @return [Boolean] true if register exists and is valid
|
|
67
|
-
def register_exists?
|
|
68
|
-
path = resolve_register_path
|
|
69
|
-
return false unless path
|
|
70
|
-
|
|
71
|
-
Dir.exist?(path) && validate_register_integrity(path)
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
# Update or re-clone the register
|
|
75
|
-
#
|
|
76
|
-
# @param force [Boolean] if true, re-clone even if register exists
|
|
77
|
-
# @return [Boolean] true if successful
|
|
78
|
-
# @raise [RegisterError] if update fails
|
|
79
|
-
def update_register(force: false)
|
|
80
|
-
if force
|
|
81
|
-
force_reclone
|
|
82
|
-
else
|
|
83
|
-
update_existing_clone
|
|
84
|
-
end
|
|
85
|
-
true
|
|
86
|
-
rescue Git::GitExecuteError => e
|
|
87
|
-
raise RegisterError, "Failed to update register: #{e.message}"
|
|
88
|
-
rescue StandardError => e
|
|
89
|
-
raise RegisterError, "Register update failed: #{e.message}"
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
# Get register information
|
|
93
|
-
#
|
|
94
|
-
# @return [Hash] register information
|
|
95
|
-
def register_info
|
|
96
|
-
path = resolve_register_path
|
|
97
|
-
return { status: :not_found } unless path
|
|
98
|
-
|
|
99
|
-
return { status: :not_cloned, path: expand_path(DEFAULT_DIR) } unless Dir.exist?(path)
|
|
100
|
-
|
|
101
|
-
return { status: :invalid, path: path } unless validate_register_integrity(path)
|
|
102
|
-
|
|
103
|
-
info = {
|
|
104
|
-
status: :ok,
|
|
105
|
-
path: path,
|
|
106
|
-
source: detect_source(path)
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
# Add git info if available
|
|
110
|
-
git_dir = File.join(path, '.git')
|
|
111
|
-
if Dir.exist?(git_dir)
|
|
112
|
-
begin
|
|
113
|
-
# Suppress stderr from git commands using GIT_REDIRECT_STDERR
|
|
114
|
-
# This prevents "fatal: not a git repository" errors from polluting output
|
|
115
|
-
# Redirect stderr to /dev/null at the git subprocess level
|
|
116
|
-
null_dev = RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ ? 'NUL' : '/dev/null'
|
|
117
|
-
old_git_redirect = ENV['GIT_REDIRECT_STDERR']
|
|
118
|
-
ENV['GIT_REDIRECT_STDERR'] = null_dev
|
|
119
|
-
|
|
120
|
-
g = Git.open(path)
|
|
121
|
-
info[:branch] = g.current_branch
|
|
122
|
-
log = g.log(1).to_a
|
|
123
|
-
info[:commit] = log.first.sha[0..7]
|
|
124
|
-
info[:last_update] = Time.at(log.first.date.to_i)
|
|
125
|
-
|
|
126
|
-
# Restore original GIT_REDIRECT_STDERR
|
|
127
|
-
if old_git_redirect
|
|
128
|
-
ENV['GIT_REDIRECT_STDERR'] = old_git_redirect
|
|
129
|
-
else
|
|
130
|
-
ENV.delete('GIT_REDIRECT_STDERR')
|
|
131
|
-
end
|
|
132
|
-
rescue Git::GitExecuteError, IOError, Errno::ENOENT
|
|
133
|
-
# Git info not available, but register is valid
|
|
134
|
-
# Ensure GIT_REDIRECT_STDERR is restored
|
|
135
|
-
if old_git_redirect
|
|
136
|
-
ENV['GIT_REDIRECT_STDERR'] = old_git_redirect
|
|
137
|
-
else
|
|
138
|
-
ENV.delete('GIT_REDIRECT_STDERR')
|
|
139
|
-
end
|
|
140
|
-
end
|
|
141
|
-
end
|
|
142
|
-
|
|
143
|
-
# Count available tools
|
|
144
|
-
tools_dir = File.join(path, 'tools')
|
|
145
|
-
if Dir.exist?(tools_dir)
|
|
146
|
-
info[:tools_count] = Dir.glob(File.join(tools_dir, '*')).select do |d|
|
|
147
|
-
File.directory?(d)
|
|
148
|
-
end.count
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
info
|
|
152
|
-
end
|
|
153
|
-
|
|
154
|
-
private
|
|
155
|
-
|
|
156
|
-
# Ensure the user's local clone exists
|
|
157
|
-
#
|
|
158
|
-
# @return [String, nil] the register path, or nil if unavailable
|
|
159
|
-
def ensure_user_clone
|
|
160
|
-
expanded_path = expand_path(DEFAULT_DIR)
|
|
161
|
-
|
|
162
|
-
# If already exists and valid, return it
|
|
163
|
-
if Dir.exist?(expanded_path)
|
|
164
|
-
return expanded_path if validate_register_integrity(expanded_path)
|
|
165
|
-
|
|
166
|
-
# Exists but invalid, re-clone
|
|
167
|
-
force_reclone
|
|
168
|
-
|
|
169
|
-
return expanded_path
|
|
170
|
-
end
|
|
171
|
-
|
|
172
|
-
# Doesn't exist, clone it
|
|
173
|
-
clone_register(expanded_path)
|
|
174
|
-
expanded_path
|
|
175
|
-
rescue RegisterError
|
|
176
|
-
# Re-raise with context
|
|
177
|
-
raise
|
|
178
|
-
rescue StandardError => e
|
|
179
|
-
raise RegisterError, "Failed to setup register at #{expanded_path}: #{e.message}"
|
|
180
|
-
end
|
|
181
|
-
|
|
182
|
-
# Clone the register repository
|
|
183
|
-
#
|
|
184
|
-
# @param target_path [String] where to clone
|
|
185
|
-
# @raise [RegisterError] if clone fails
|
|
186
|
-
def clone_register(target_path)
|
|
187
|
-
parent_dir = File.dirname(target_path)
|
|
188
|
-
|
|
189
|
-
# Create parent directory if needed
|
|
190
|
-
FileUtils.mkdir_p(parent_dir) unless Dir.exist?(parent_dir)
|
|
191
|
-
|
|
192
|
-
# Check if git is available
|
|
193
|
-
unless git_available?
|
|
194
|
-
raise RegisterError, <<~ERROR
|
|
195
|
-
Git is required but not found in PATH.
|
|
196
|
-
|
|
197
|
-
To fix this:
|
|
198
|
-
1. Install git from https://git-scm.com
|
|
199
|
-
2. Or set UKIRYU_REGISTER to use a local register path
|
|
200
|
-
|
|
201
|
-
Example:
|
|
202
|
-
export UKIRYU_REGISTER=/path/to/register
|
|
203
|
-
ERROR
|
|
204
|
-
end
|
|
205
|
-
|
|
206
|
-
# Perform the clone
|
|
207
|
-
print "Cloning register from #{REGISTER_URL}..." if $stdout.tty?
|
|
208
|
-
Git.clone(REGISTER_URL, target_path, quiet: true)
|
|
209
|
-
puts 'done' if $stdout.tty?
|
|
210
|
-
|
|
211
|
-
# Validate the clone
|
|
212
|
-
unless validate_register_integrity(target_path)
|
|
213
|
-
FileUtils.rm_rf(target_path)
|
|
214
|
-
raise RegisterError, 'Register clone validation failed. Please try again or set UKIRYU_REGISTER.'
|
|
215
|
-
end
|
|
216
|
-
rescue Git::GitExecuteError => e
|
|
217
|
-
raise RegisterError, <<~ERROR
|
|
218
|
-
Failed to clone register from #{REGISTER_URL}: #{e.message}
|
|
219
|
-
|
|
220
|
-
To fix this:
|
|
221
|
-
1. Check your internet connection
|
|
222
|
-
2. Manually clone: git clone #{REGISTER_URL} #{target_path}
|
|
223
|
-
3. Or set UKIRYU_REGISTER to use a local register path
|
|
224
|
-
|
|
225
|
-
Example:
|
|
226
|
-
export UKIRYU_REGISTER=/path/to/register
|
|
227
|
-
ERROR
|
|
228
|
-
end
|
|
229
|
-
|
|
230
|
-
# Update existing register clone
|
|
231
|
-
#
|
|
232
|
-
# @raise [RegisterError] if update fails
|
|
233
|
-
def update_existing_clone
|
|
234
|
-
path = expand_path(DEFAULT_DIR)
|
|
235
|
-
|
|
236
|
-
return clone_register(path) unless Dir.exist?(path)
|
|
237
|
-
|
|
238
|
-
begin
|
|
239
|
-
print 'Updating register...' if $stdout.tty?
|
|
240
|
-
# Suppress stderr from git commands using GIT_REDIRECT_STDERR
|
|
241
|
-
null_dev = RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ ? 'NUL' : '/dev/null'
|
|
242
|
-
old_git_redirect = ENV['GIT_REDIRECT_STDERR']
|
|
243
|
-
ENV['GIT_REDIRECT_STDERR'] = null_dev
|
|
244
|
-
|
|
245
|
-
g = Git.open(path)
|
|
246
|
-
g.pull
|
|
247
|
-
puts 'done' if $stdout.tty?
|
|
248
|
-
|
|
249
|
-
# Restore original GIT_REDIRECT_STDERR
|
|
250
|
-
if old_git_redirect
|
|
251
|
-
ENV['GIT_REDIRECT_STDERR'] = old_git_redirect
|
|
252
|
-
else
|
|
253
|
-
ENV.delete('GIT_REDIRECT_STDERR')
|
|
254
|
-
end
|
|
255
|
-
rescue Git::GitExecuteError => e
|
|
256
|
-
# Ensure GIT_REDIRECT_STDERR is restored
|
|
257
|
-
if defined?(old_git_redirect)
|
|
258
|
-
if old_git_redirect
|
|
259
|
-
ENV['GIT_REDIRECT_STDERR'] = old_git_redirect
|
|
260
|
-
else
|
|
261
|
-
ENV.delete('GIT_REDIRECT_STDERR')
|
|
262
|
-
end
|
|
263
|
-
end
|
|
264
|
-
raise RegisterError, "Failed to update register: #{e.message}"
|
|
265
|
-
end
|
|
266
|
-
end
|
|
267
|
-
|
|
268
|
-
# Force re-clone the register
|
|
269
|
-
#
|
|
270
|
-
# @raise [RegisterError] if re-clone fails
|
|
271
|
-
def force_reclone
|
|
272
|
-
path = expand_path(DEFAULT_DIR)
|
|
273
|
-
FileUtils.rm_rf(path) if Dir.exist?(path)
|
|
274
|
-
clone_register(path)
|
|
275
|
-
end
|
|
276
|
-
|
|
277
|
-
# Validate register integrity
|
|
278
|
-
#
|
|
279
|
-
# @param path [String] path to check
|
|
280
|
-
# @return [Boolean] true if valid
|
|
281
|
-
def validate_register_integrity(path)
|
|
282
|
-
return false unless path
|
|
283
|
-
|
|
284
|
-
# Check for tools/ directory
|
|
285
|
-
tools_dir = File.join(path, 'tools')
|
|
286
|
-
return false unless Dir.exist?(tools_dir)
|
|
287
|
-
|
|
288
|
-
# Check for at least one tool definition
|
|
289
|
-
# This confirms it's a valid register structure
|
|
290
|
-
Dir.glob(File.join(tools_dir, '*', '*.yaml')).any?
|
|
291
|
-
end
|
|
292
|
-
|
|
293
|
-
# Resolve the register path without auto-creating
|
|
294
|
-
#
|
|
295
|
-
# @return [String, nil] current register path or nil
|
|
296
|
-
def resolve_register_path
|
|
297
|
-
# Check environment variable
|
|
298
|
-
env_path = ENV['UKIRYU_REGISTER']
|
|
299
|
-
return env_path if env_path && Dir.exist?(env_path)
|
|
300
|
-
|
|
301
|
-
# Check development register (../../../register relative to this file)
|
|
302
|
-
# Use Pathname for reliable path calculation
|
|
303
|
-
this_file = Pathname.new(__FILE__).realpath
|
|
304
|
-
dev_path = this_file.parent.parent.parent.parent + 'register'
|
|
305
|
-
return dev_path.to_s if dev_path.exist?
|
|
306
|
-
|
|
307
|
-
# Check user clone
|
|
308
|
-
expanded = expand_path(DEFAULT_DIR)
|
|
309
|
-
Dir.exist?(expanded) ? expanded : nil
|
|
310
|
-
end
|
|
311
|
-
|
|
312
|
-
# Detect the source of the register
|
|
313
|
-
#
|
|
314
|
-
# @param path [String] register path
|
|
315
|
-
# @return [Symbol] :env or :user
|
|
316
|
-
def detect_source(path)
|
|
317
|
-
env_path = ENV['UKIRYU_REGISTER']
|
|
318
|
-
return :env if env_path && path == File.expand_path(env_path)
|
|
319
|
-
|
|
320
|
-
:user
|
|
321
|
-
end
|
|
322
|
-
|
|
323
|
-
# Check if git is available
|
|
324
|
-
#
|
|
325
|
-
# @return [Boolean] true if git binary is available
|
|
326
|
-
def git_available?
|
|
327
|
-
system('git --version > /dev/null 2>&1')
|
|
328
|
-
end
|
|
329
|
-
|
|
330
|
-
# Expand a path with ~ support
|
|
331
|
-
#
|
|
332
|
-
# @param path [String] path to expand
|
|
333
|
-
# @return [String] expanded path
|
|
334
|
-
def expand_path(path)
|
|
335
|
-
File.expand_path(path)
|
|
336
|
-
end
|
|
337
|
-
end
|
|
338
|
-
|
|
339
|
-
# Register-specific error
|
|
340
|
-
class RegisterError < StandardError; end
|
|
341
|
-
end
|
|
342
|
-
end
|