ukiryu 0.1.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 +7 -0
- data/.github/workflows/test.yml +143 -0
- data/.gitignore +8 -0
- data/.gitmodules +3 -0
- data/Gemfile +17 -0
- data/README.adoc +295 -0
- data/Rakefile +8 -0
- data/lib/ukiryu/cli.rb +348 -0
- data/lib/ukiryu/errors.rb +30 -0
- data/lib/ukiryu/executor.rb +716 -0
- data/lib/ukiryu/platform.rb +93 -0
- data/lib/ukiryu/registry.rb +246 -0
- data/lib/ukiryu/schema_validator.rb +103 -0
- data/lib/ukiryu/shell/base.rb +79 -0
- data/lib/ukiryu/shell/bash.rb +60 -0
- data/lib/ukiryu/shell/cmd.rb +75 -0
- data/lib/ukiryu/shell/fish.rb +16 -0
- data/lib/ukiryu/shell/powershell.rb +60 -0
- data/lib/ukiryu/shell/sh.rb +16 -0
- data/lib/ukiryu/shell/zsh.rb +16 -0
- data/lib/ukiryu/shell.rb +164 -0
- data/lib/ukiryu/tool.rb +439 -0
- data/lib/ukiryu/type.rb +254 -0
- data/lib/ukiryu/version.rb +5 -0
- data/lib/ukiryu.rb +54 -0
- data/ukiryu.gemspec +33 -0
- metadata +72 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 9a829b8c3437032c87c7dc360fec0aacd18a2ce7fe627b82defbb972e00047c2
|
|
4
|
+
data.tar.gz: 811407bd87c8e7665e78a970506d1591584a8a51ff95d6426333dd57b0749339
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 03a7575c15d808461881baebe51ffaca79f868f93ef4e073e026ab2385f88a0540e20168bcfe2299edcec0a68a7e0a5074cd01f60aaee786267665eb56a568cd
|
|
7
|
+
data.tar.gz: 15d32df885540d695c0a439acc03e4c1d144056ca5f7fa481c12934237b61350bab4b2e83c2ee4f184c3bb1f9dd905a3085d283b81f625cf4073cbc4d520f488
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
name: Test Tool Profiles
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ main, master, develop ]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [ main, master, develop ]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
name: Test on ${{ matrix.os }}
|
|
12
|
+
runs-on: ${{ matrix.os }}
|
|
13
|
+
strategy:
|
|
14
|
+
fail-fast: false
|
|
15
|
+
matrix:
|
|
16
|
+
os: [ubuntu-latest, macos-latest]
|
|
17
|
+
ruby: ['3.3']
|
|
18
|
+
|
|
19
|
+
steps:
|
|
20
|
+
- name: Checkout code
|
|
21
|
+
uses: actions/checkout@v4
|
|
22
|
+
|
|
23
|
+
- name: Set up Ruby
|
|
24
|
+
uses: ruby/setup-ruby@v1
|
|
25
|
+
with:
|
|
26
|
+
ruby-version: ${{ matrix.ruby }}
|
|
27
|
+
bundler-cache: true
|
|
28
|
+
working-directory: ./ukiryu
|
|
29
|
+
|
|
30
|
+
- name: Install system dependencies (Ubuntu)
|
|
31
|
+
if: matrix.os == 'ubuntu-latest'
|
|
32
|
+
run: |
|
|
33
|
+
sudo apt-get update
|
|
34
|
+
sudo apt-get install -y imagemagick ffmpeg ghostscript
|
|
35
|
+
|
|
36
|
+
- name: Install system dependencies (macOS)
|
|
37
|
+
if: matrix.os == 'macos-latest'
|
|
38
|
+
run: |
|
|
39
|
+
brew install imagemagick ffmpeg ghostscript
|
|
40
|
+
|
|
41
|
+
- name: Show installed tool versions
|
|
42
|
+
run: |
|
|
43
|
+
echo "=== Installed Tool Versions ==="
|
|
44
|
+
echo -n "ImageMagick: " && magick -version | head -1 || echo "Not installed"
|
|
45
|
+
echo -n "FFmpeg: " && ffmpeg -version | head -1 || echo "Not installed"
|
|
46
|
+
echo -n "Ghostscript: " && gs --version || echo "Not installed"
|
|
47
|
+
echo -n "Pandoc: " && pandoc --version | head -1 || echo "Not installed"
|
|
48
|
+
echo -n "jpegoptim: " && jpegoptim --version 2>&1 | head -1 || echo "Not installed"
|
|
49
|
+
echo -n "optipng: " && optipng -v 2>&1 | head -1 || echo "Not installed"
|
|
50
|
+
|
|
51
|
+
- name: Run tests
|
|
52
|
+
working-directory: ./ukiryu
|
|
53
|
+
run: bundle exec rspec --format documentation
|
|
54
|
+
|
|
55
|
+
- name: Run all tests with coverage
|
|
56
|
+
working-directory: ./ukiryu
|
|
57
|
+
run: bundle exec rspec
|
|
58
|
+
|
|
59
|
+
schema-validation:
|
|
60
|
+
name: Validate Tool Profiles Schema
|
|
61
|
+
runs-on: ubuntu-latest
|
|
62
|
+
|
|
63
|
+
steps:
|
|
64
|
+
- name: Checkout code
|
|
65
|
+
uses: actions/checkout@v4
|
|
66
|
+
|
|
67
|
+
- name: Set up Ruby
|
|
68
|
+
uses: ruby/setup-ruby@v1
|
|
69
|
+
with:
|
|
70
|
+
ruby-version: '3.3'
|
|
71
|
+
bundler-cache: true
|
|
72
|
+
working-directory: ./ukiryu
|
|
73
|
+
|
|
74
|
+
- name: Validate all tool profiles
|
|
75
|
+
working-directory: ./ukiryu
|
|
76
|
+
run: |
|
|
77
|
+
ruby -r ./lib/ukiryu.rb -r ./lib/ukiryu/schema_validator.rb <<-'RUBY'
|
|
78
|
+
require 'yaml'
|
|
79
|
+
require 'json-schema'
|
|
80
|
+
|
|
81
|
+
schema_path = File.expand_path('../../schema/tool-profile.schema.yaml', __dir__)
|
|
82
|
+
schema = JSON::Schema.parse(File.read(schema_path))
|
|
83
|
+
|
|
84
|
+
# Find all tool profiles
|
|
85
|
+
profiles = Dir.glob('../../register/tools/**/*.yaml').sort
|
|
86
|
+
|
|
87
|
+
puts "Validating #{profiles.count} tool profiles..."
|
|
88
|
+
errors = []
|
|
89
|
+
|
|
90
|
+
profiles.each do |profile_path|
|
|
91
|
+
profile_name = profile_path.sub('../../register/tools/', '')
|
|
92
|
+
begin
|
|
93
|
+
profile = YAML.safe_load(File.read(profile_path))
|
|
94
|
+
schema.validate(profile)
|
|
95
|
+
puts " ✓ #{profile_name}"
|
|
96
|
+
rescue => e
|
|
97
|
+
errors << "#{profile_name}: #{e.message}"
|
|
98
|
+
puts " ✗ #{profile_name}: #{e.message}"
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
if errors.any?
|
|
103
|
+
puts "\n#{errors.count} validation errors:"
|
|
104
|
+
errors.each { |err| puts " - #{err}" }
|
|
105
|
+
exit 1
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
puts "\n✓ All #{profiles.count} tool profiles are valid!"
|
|
109
|
+
RUBY
|
|
110
|
+
|
|
111
|
+
# Optional: Test on Windows with native Windows tools
|
|
112
|
+
test-windows:
|
|
113
|
+
name: Test on Windows
|
|
114
|
+
runs-on: windows-latest
|
|
115
|
+
if: github.event_name == 'push' || (github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'test-windows'))
|
|
116
|
+
|
|
117
|
+
steps:
|
|
118
|
+
- name: Checkout code
|
|
119
|
+
uses: actions/checkout@v4
|
|
120
|
+
|
|
121
|
+
- name: Set up Ruby
|
|
122
|
+
uses: ruby/setup-ruby@v1
|
|
123
|
+
with:
|
|
124
|
+
ruby-version: '3.3'
|
|
125
|
+
bundler-cache: true
|
|
126
|
+
working-directory: ./ukiryu
|
|
127
|
+
|
|
128
|
+
- name: Install Chocolatey packages
|
|
129
|
+
run: |
|
|
130
|
+
choco install imagemagick ffmpeg ghostscript -y
|
|
131
|
+
|
|
132
|
+
- name: Show installed tool versions
|
|
133
|
+
shell: pwsh
|
|
134
|
+
run: |
|
|
135
|
+
Write-Host "=== Installed Tool Versions ==="
|
|
136
|
+
Write-Host "ImageMagick: $(magick -version | Select-Object -First 1)"
|
|
137
|
+
Write-Host "FFmpeg: $(ffmpeg -version | Select-Object -First 1)"
|
|
138
|
+
Write-Host "Ghostscript: $(gs --version)"
|
|
139
|
+
|
|
140
|
+
- name: Run tests (skip unavailable tools)
|
|
141
|
+
working-directory: ./ukiryu
|
|
142
|
+
run: bundle exec rspec --format documentation
|
|
143
|
+
continue-on-error: true
|
data/.gitignore
ADDED
data/.gitmodules
ADDED
data/Gemfile
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
source "https://rubygems.org"
|
|
4
|
+
|
|
5
|
+
# Specify your gem's dependencies in ukiryu.gemspec
|
|
6
|
+
gemspec
|
|
7
|
+
|
|
8
|
+
# Development dependencies
|
|
9
|
+
gem "rake"
|
|
10
|
+
gem "rspec"
|
|
11
|
+
gem "rubocop"
|
|
12
|
+
gem "rubocop-performance"
|
|
13
|
+
gem "rubocop-rake"
|
|
14
|
+
gem "rubocop-rspec"
|
|
15
|
+
|
|
16
|
+
# For optional YAML schema validation in registry
|
|
17
|
+
gem "json-schema", "~> 3.0"
|
data/README.adoc
ADDED
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
= Ukiryu
|
|
2
|
+
|
|
3
|
+
Platform-adaptive command execution framework using declarative YAML tool
|
|
4
|
+
profiles.
|
|
5
|
+
|
|
6
|
+
image:https://img.shields.io/gem/v/ukiryu.svg[RubyGems Version]
|
|
7
|
+
image:https://img.shields.io/github/license/ukiryu/ukiryu.svg[License]
|
|
8
|
+
image:https://github.com/ukiryu/ukiryu/actions/workflows/test.yml/badge.svg["Build", link="https://github.com/ukiryu/ukiryu/actions/workflows/test.yml"]
|
|
9
|
+
|
|
10
|
+
== Purpose
|
|
11
|
+
|
|
12
|
+
Ukiryu provides a Ruby framework for executing external commands with:
|
|
13
|
+
|
|
14
|
+
* Declarative YAML tool profiles
|
|
15
|
+
* Platform-specific command formatting (macOS, Linux, Windows)
|
|
16
|
+
* Shell-specific syntax (bash, zsh, fish, PowerShell, cmd)
|
|
17
|
+
* Version-aware tool detection
|
|
18
|
+
* Fully object-oriented result handling
|
|
19
|
+
|
|
20
|
+
== Features
|
|
21
|
+
|
|
22
|
+
=== Tool Registry
|
|
23
|
+
|
|
24
|
+
Load tool definitions from YAML profiles:
|
|
25
|
+
|
|
26
|
+
[source,ruby]
|
|
27
|
+
----
|
|
28
|
+
Ukiryu::Registry.default_registry_path = 'path/to/register'
|
|
29
|
+
tool = Ukiryu::Tool.get(:imagemagick)
|
|
30
|
+
----
|
|
31
|
+
|
|
32
|
+
=== Platform Detection
|
|
33
|
+
|
|
34
|
+
Automatic platform and shell detection:
|
|
35
|
+
|
|
36
|
+
[source,ruby]
|
|
37
|
+
----
|
|
38
|
+
Ukiryu::Platform.detect # => :macos, :linux, or :windows
|
|
39
|
+
Ukiryu::Shell.detect # => :bash, :zsh, :powershell, etc.
|
|
40
|
+
----
|
|
41
|
+
|
|
42
|
+
=== Version Awareness
|
|
43
|
+
|
|
44
|
+
Handle different tool versions with specific profiles:
|
|
45
|
+
|
|
46
|
+
[source,ruby]
|
|
47
|
+
----
|
|
48
|
+
tool = Ukiryu::Tool.get(:inkscape)
|
|
49
|
+
# Automatically selects 1.0.yaml or 0.92.yaml based on detected version
|
|
50
|
+
----
|
|
51
|
+
|
|
52
|
+
=== OOP Result Handling
|
|
53
|
+
|
|
54
|
+
Rich, object-oriented execution results:
|
|
55
|
+
|
|
56
|
+
[source,ruby]
|
|
57
|
+
----
|
|
58
|
+
result = tool.execute(:convert, { inputs: ['input.png'], output: 'output.jpg' })
|
|
59
|
+
|
|
60
|
+
result.command_info.executable # => "/opt/homebrew/bin/magick"
|
|
61
|
+
result.output.stdout # => ""
|
|
62
|
+
result.output.success? # => true
|
|
63
|
+
result.metadata.duration # => 0.5
|
|
64
|
+
result.metadata.formatted_duration # => "500ms"
|
|
65
|
+
----
|
|
66
|
+
|
|
67
|
+
== Architecture
|
|
68
|
+
|
|
69
|
+
=== Class Hierarchy
|
|
70
|
+
|
|
71
|
+
[source]
|
|
72
|
+
----
|
|
73
|
+
Ukiryu
|
|
74
|
+
├── Executor
|
|
75
|
+
│ ├── CommandInfo (command details)
|
|
76
|
+
│ ├── Output (stdout/stderr with utilities)
|
|
77
|
+
│ ├── ExecutionMetadata(timing information)
|
|
78
|
+
│ └── Result (composes above three)
|
|
79
|
+
├── Tool (YAML profile loader and executor)
|
|
80
|
+
├── Registry (profile discovery and loading)
|
|
81
|
+
├── Platform (platform detection and utilities)
|
|
82
|
+
├── Shell
|
|
83
|
+
│ ├── Base (abstract shell interface)
|
|
84
|
+
│ ├── Bash
|
|
85
|
+
│ ├── Zsh
|
|
86
|
+
│ ├── Fish
|
|
87
|
+
│ ├── Sh
|
|
88
|
+
│ ├── PowerShell
|
|
89
|
+
│ └── Cmd
|
|
90
|
+
└── Errors (custom exception hierarchy)
|
|
91
|
+
----
|
|
92
|
+
|
|
93
|
+
=== Data Flow
|
|
94
|
+
|
|
95
|
+
[source]
|
|
96
|
+
----
|
|
97
|
+
┌─────────────────┐ ┌─────────────────┐ ┌──────────────────┐
|
|
98
|
+
│ YAML Profile │─────►│ Tool Class │─────►│ Executor │
|
|
99
|
+
│ (declarative) │ │ (loader & │ │ (platform & │
|
|
100
|
+
│ │ │ executor) │ │ shell aware) │
|
|
101
|
+
└─────────────────┘ └─────────────────┘ └────────┬─────────┘
|
|
102
|
+
│
|
|
103
|
+
▼
|
|
104
|
+
┌─────────────┐
|
|
105
|
+
│ Result │
|
|
106
|
+
│ (OOP with │
|
|
107
|
+
│ CommandInfo│
|
|
108
|
+
│ + Output │
|
|
109
|
+
│ + Metadata)│
|
|
110
|
+
└─────────────┘
|
|
111
|
+
----
|
|
112
|
+
|
|
113
|
+
== Installation
|
|
114
|
+
|
|
115
|
+
Add this line to your application's Gemfile:
|
|
116
|
+
|
|
117
|
+
[source,ruby]
|
|
118
|
+
----
|
|
119
|
+
gem 'ukiryu'
|
|
120
|
+
----
|
|
121
|
+
|
|
122
|
+
And then execute:
|
|
123
|
+
|
|
124
|
+
[source,shell]
|
|
125
|
+
----
|
|
126
|
+
bundle install
|
|
127
|
+
----
|
|
128
|
+
|
|
129
|
+
Or install it yourself as:
|
|
130
|
+
|
|
131
|
+
[source,shell]
|
|
132
|
+
----
|
|
133
|
+
gem install ukiryu
|
|
134
|
+
----
|
|
135
|
+
|
|
136
|
+
== Usage
|
|
137
|
+
|
|
138
|
+
=== Basic Command Execution
|
|
139
|
+
|
|
140
|
+
[source,ruby]
|
|
141
|
+
----
|
|
142
|
+
require 'ukiryu'
|
|
143
|
+
|
|
144
|
+
# Set registry path to tool profiles
|
|
145
|
+
Ukiryu::Registry.default_registry_path = 'path/to/register'
|
|
146
|
+
|
|
147
|
+
# Get a tool
|
|
148
|
+
tool = Ukiryu::Tool.get(:imagemagick)
|
|
149
|
+
|
|
150
|
+
# Execute a command
|
|
151
|
+
result = tool.execute(:convert, {
|
|
152
|
+
inputs: ['input.png'],
|
|
153
|
+
output: 'output.jpg',
|
|
154
|
+
resize: '50x50',
|
|
155
|
+
quality: 85
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
# Check result
|
|
159
|
+
if result.success?
|
|
160
|
+
puts "Converted in #{result.metadata.formatted_duration}"
|
|
161
|
+
else
|
|
162
|
+
puts "Error: #{result.output.stderr}"
|
|
163
|
+
end
|
|
164
|
+
----
|
|
165
|
+
|
|
166
|
+
=== Accessing Result Details
|
|
167
|
+
|
|
168
|
+
[source,ruby]
|
|
169
|
+
----
|
|
170
|
+
result = tool.execute(:identify, { input: ['test.png'] })
|
|
171
|
+
|
|
172
|
+
# Command information
|
|
173
|
+
result.command_info.executable # Full path to executable
|
|
174
|
+
result.command_info.arguments # Array of arguments
|
|
175
|
+
result.command_info.full_command # Complete command string
|
|
176
|
+
result.command_info.shell # Shell type used
|
|
177
|
+
|
|
178
|
+
# Output
|
|
179
|
+
result.output.stdout # Stripped stdout
|
|
180
|
+
result.output.stderr # Stripped stderr
|
|
181
|
+
result.output.stdout_lines # Array of lines
|
|
182
|
+
result.output.stdout_contains?(/PNG/)
|
|
183
|
+
|
|
184
|
+
# Metadata
|
|
185
|
+
result.metadata.started_at # Time object
|
|
186
|
+
result.metadata.finished_at # Time object
|
|
187
|
+
result.metadata.duration # Float (seconds)
|
|
188
|
+
result.metadata.timed_out? # Boolean
|
|
189
|
+
----
|
|
190
|
+
|
|
191
|
+
=== Error Handling
|
|
192
|
+
|
|
193
|
+
[source,ruby]
|
|
194
|
+
----
|
|
195
|
+
begin
|
|
196
|
+
result = tool.execute(:convert, { inputs: ['nonexistent.png'], output: 'out.jpg' })
|
|
197
|
+
rescue Ukiryu::ExecutionError => e
|
|
198
|
+
puts "Command failed: #{e.message}"
|
|
199
|
+
rescue Ukiryu::TimeoutError => e
|
|
200
|
+
puts "Command timed out: #{e.message}"
|
|
201
|
+
end
|
|
202
|
+
----
|
|
203
|
+
|
|
204
|
+
=== Tool Availability
|
|
205
|
+
|
|
206
|
+
[source,ruby]
|
|
207
|
+
----
|
|
208
|
+
tool = Ukiryu::Tool.get(:ffmpeg)
|
|
209
|
+
|
|
210
|
+
if tool.available?
|
|
211
|
+
puts "FFmpeg #{tool.version} found at #{tool.executable}"
|
|
212
|
+
else
|
|
213
|
+
puts "FFmpeg not installed"
|
|
214
|
+
end
|
|
215
|
+
----
|
|
216
|
+
|
|
217
|
+
== Tool Profiles
|
|
218
|
+
|
|
219
|
+
Tool profiles are defined in separate https://github.com/ukiryu/register[Ukiryu Register] repository.
|
|
220
|
+
|
|
221
|
+
Example profile structure:
|
|
222
|
+
|
|
223
|
+
[source,yaml]
|
|
224
|
+
----
|
|
225
|
+
name: imagemagick
|
|
226
|
+
version: "7.1"
|
|
227
|
+
|
|
228
|
+
version_detection:
|
|
229
|
+
command: "--version"
|
|
230
|
+
pattern: "(\\d+\\.\\d+)"
|
|
231
|
+
|
|
232
|
+
profiles:
|
|
233
|
+
- name: unix
|
|
234
|
+
platforms: [macos, linux]
|
|
235
|
+
shells: [bash, zsh, fish, sh]
|
|
236
|
+
commands:
|
|
237
|
+
convert:
|
|
238
|
+
arguments:
|
|
239
|
+
- name: inputs
|
|
240
|
+
type: file
|
|
241
|
+
variadic: true
|
|
242
|
+
position: last
|
|
243
|
+
options:
|
|
244
|
+
- name: resize
|
|
245
|
+
cli: "-resize"
|
|
246
|
+
flags:
|
|
247
|
+
- name: strip
|
|
248
|
+
cli: "-strip"
|
|
249
|
+
----
|
|
250
|
+
|
|
251
|
+
== Development
|
|
252
|
+
|
|
253
|
+
=== Running Tests
|
|
254
|
+
|
|
255
|
+
[source,shell]
|
|
256
|
+
----
|
|
257
|
+
bundle exec rspec
|
|
258
|
+
----
|
|
259
|
+
|
|
260
|
+
=== Schema Validation
|
|
261
|
+
|
|
262
|
+
[source,shell]
|
|
263
|
+
----
|
|
264
|
+
bundle exec rspec spec/ukiryu/schema_validator_spec.rb
|
|
265
|
+
----
|
|
266
|
+
|
|
267
|
+
=== GitHub Actions
|
|
268
|
+
|
|
269
|
+
Tests run on:
|
|
270
|
+
* Ubuntu latest
|
|
271
|
+
* macOS latest
|
|
272
|
+
|
|
273
|
+
== Contributing
|
|
274
|
+
|
|
275
|
+
1. Fork the repository
|
|
276
|
+
2. Create your feature branch
|
|
277
|
+
3. Write tests for your changes
|
|
278
|
+
4. Ensure all tests pass
|
|
279
|
+
5. Submit a pull request
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
== License
|
|
283
|
+
|
|
284
|
+
The content is available as open source under the terms of the Ribose BSD
|
|
285
|
+
2-Clause License.
|
|
286
|
+
|
|
287
|
+
== Copyright
|
|
288
|
+
|
|
289
|
+
Copyright Ribose.
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
== Related Repositories
|
|
293
|
+
|
|
294
|
+
* https://github.com/ukiryu/register[ukiryu/register] - Tool profile registry
|
|
295
|
+
* https://github.com/ukiryu/schema[ukiryu/schema] - Profile validation schema
|