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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/lib/ukiryu/cache.rb +6 -0
  3. data/lib/ukiryu/cache_registry.rb +64 -0
  4. data/lib/ukiryu/cli_commands/base_command.rb +6 -5
  5. data/lib/ukiryu/cli_commands/config_command.rb +7 -10
  6. data/lib/ukiryu/cli_commands/register_command.rb +27 -18
  7. data/lib/ukiryu/cli_commands/validate_command.rb +2 -2
  8. data/lib/ukiryu/command_builder.rb +83 -50
  9. data/lib/ukiryu/config.rb +13 -2
  10. data/lib/ukiryu/debug.rb +20 -9
  11. data/lib/ukiryu/definition/loader.rb +3 -3
  12. data/lib/ukiryu/errors.rb +37 -37
  13. data/lib/ukiryu/executable_locator.rb +40 -16
  14. data/lib/ukiryu/extractors/base_extractor.rb +2 -1
  15. data/lib/ukiryu/extractors/help_parser.rb +3 -0
  16. data/lib/ukiryu/logger.rb +51 -0
  17. data/lib/ukiryu/models/implementation_index.rb +2 -1
  18. data/lib/ukiryu/models/implementation_version.rb +18 -1
  19. data/lib/ukiryu/models/interface.rb +2 -1
  20. data/lib/ukiryu/models/run_environment.rb +0 -2
  21. data/lib/ukiryu/models/semantic_version.rb +174 -0
  22. data/lib/ukiryu/models/stage_metrics.rb +0 -1
  23. data/lib/ukiryu/register.rb +473 -232
  24. data/lib/ukiryu/shell/powershell.rb +209 -89
  25. data/lib/ukiryu/shell/sh.rb +4 -1
  26. data/lib/ukiryu/shell.rb +60 -2
  27. data/lib/ukiryu/tool/command_resolution.rb +2 -1
  28. data/lib/ukiryu/tool/executable_discovery.rb +14 -15
  29. data/lib/ukiryu/tool/loader.rb +543 -0
  30. data/lib/ukiryu/tool/version_detection.rb +1 -3
  31. data/lib/ukiryu/tool.rb +79 -87
  32. data/lib/ukiryu/tool_index.rb +127 -62
  33. data/lib/ukiryu/tools/base.rb +4 -2
  34. data/lib/ukiryu/type.rb +26 -15
  35. data/lib/ukiryu/version.rb +1 -1
  36. data/lib/ukiryu.rb +1 -1
  37. data/spec/fixtures/profiles/ghostscript_10.0.yaml +50 -0
  38. data/spec/fixtures/register/tools/ghostscript/default/10.0.yaml +6 -0
  39. data/spec/spec_helper.rb +10 -6
  40. data/spec/support/tool_helper.rb +2 -0
  41. data/spec/ukiryu/definition/loader_spec.rb +2 -2
  42. data/spec/ukiryu/executor_spec.rb +6 -3
  43. data/spec/ukiryu/models/execution_report_spec.rb +3 -2
  44. data/spec/ukiryu/models/semantic_version_spec.rb +284 -0
  45. data/spec/ukiryu/shell/powershell_integration_spec.rb +165 -0
  46. data/spec/ukiryu/shell/powershell_real_command_spec.rb +143 -0
  47. data/spec/ukiryu/shell/powershell_spec.rb +286 -51
  48. data/spec/ukiryu/tool/loader_spec.rb +148 -0
  49. data/spec/ukiryu/tool_index_spec.rb +110 -18
  50. data/spec/ukiryu/tools/ghostscript_spec.rb +242 -0
  51. data/spec/ukiryu/tools/imagemagick_spec.rb +2 -1
  52. data/spec/ukiryu/tools/inkscape_spec.rb +4 -2
  53. metadata +14 -2
  54. 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