serialbench 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/benchmark.yml +273 -220
- data/.github/workflows/rake.yml +26 -0
- data/.github/workflows/windows-debug.yml +171 -0
- data/.gitignore +32 -0
- data/.rubocop.yml +1 -0
- data/.rubocop_todo.yml +274 -0
- data/Gemfile +14 -1
- data/README.adoc +292 -1118
- data/Rakefile +0 -55
- data/config/benchmarks/full.yml +29 -0
- data/config/benchmarks/short.yml +26 -0
- data/config/environments/asdf-ruby-3.2.yml +8 -0
- data/config/environments/asdf-ruby-3.3.yml +8 -0
- data/config/environments/docker-ruby-3.0.yml +9 -0
- data/config/environments/docker-ruby-3.1.yml +9 -0
- data/config/environments/docker-ruby-3.2.yml +9 -0
- data/config/environments/docker-ruby-3.3.yml +9 -0
- data/config/environments/docker-ruby-3.4.yml +9 -0
- data/data/schemas/result.yml +29 -0
- data/docker/Dockerfile.alpine +33 -0
- data/docker/{Dockerfile.benchmark → Dockerfile.ubuntu} +4 -3
- data/docker/README.md +2 -2
- data/docs/PLATFORM_VALIDATION_FIX.md +79 -0
- data/docs/SYCK_YAML_FIX.md +91 -0
- data/docs/WEBSITE_COMPLETION_PLAN.md +440 -0
- data/docs/WINDOWS_LIBXML_FIX.md +136 -0
- data/docs/WINDOWS_SETUP.md +122 -0
- data/exe/serialbench +1 -1
- data/lib/serialbench/benchmark_runner.rb +261 -423
- data/lib/serialbench/cli/base_cli.rb +51 -0
- data/lib/serialbench/cli/benchmark_cli.rb +453 -0
- data/lib/serialbench/cli/environment_cli.rb +181 -0
- data/lib/serialbench/cli/resultset_cli.rb +261 -0
- data/lib/serialbench/cli/ruby_build_cli.rb +225 -0
- data/lib/serialbench/cli/validate_cli.rb +88 -0
- data/lib/serialbench/cli.rb +61 -600
- data/lib/serialbench/config_manager.rb +129 -0
- data/lib/serialbench/models/benchmark_config.rb +75 -0
- data/lib/serialbench/models/benchmark_result.rb +81 -0
- data/lib/serialbench/models/environment_config.rb +72 -0
- data/lib/serialbench/models/platform.rb +111 -0
- data/lib/serialbench/models/result.rb +80 -0
- data/lib/serialbench/models/result_set.rb +79 -0
- data/lib/serialbench/models/result_store.rb +108 -0
- data/lib/serialbench/models.rb +54 -0
- data/lib/serialbench/ruby_build_manager.rb +149 -0
- data/lib/serialbench/runners/asdf_runner.rb +296 -0
- data/lib/serialbench/runners/base.rb +32 -0
- data/lib/serialbench/runners/docker_runner.rb +140 -0
- data/lib/serialbench/runners/local_runner.rb +71 -0
- data/lib/serialbench/serializers/base_serializer.rb +9 -17
- data/lib/serialbench/serializers/json/base_json_serializer.rb +4 -4
- data/lib/serialbench/serializers/json/json_serializer.rb +0 -2
- data/lib/serialbench/serializers/json/oj_serializer.rb +0 -2
- data/lib/serialbench/serializers/json/rapidjson_serializer.rb +1 -1
- data/lib/serialbench/serializers/json/yajl_serializer.rb +0 -2
- data/lib/serialbench/serializers/toml/base_toml_serializer.rb +5 -5
- data/lib/serialbench/serializers/toml/toml_rb_serializer.rb +1 -3
- data/lib/serialbench/serializers/toml/tomlib_serializer.rb +1 -3
- data/lib/serialbench/serializers/toml/tomlrb_serializer.rb +56 -0
- data/lib/serialbench/serializers/xml/base_xml_serializer.rb +4 -9
- data/lib/serialbench/serializers/xml/libxml_serializer.rb +4 -10
- data/lib/serialbench/serializers/xml/nokogiri_serializer.rb +2 -4
- data/lib/serialbench/serializers/xml/oga_serializer.rb +4 -10
- data/lib/serialbench/serializers/xml/ox_serializer.rb +2 -4
- data/lib/serialbench/serializers/xml/rexml_serializer.rb +3 -5
- data/lib/serialbench/serializers/yaml/base_yaml_serializer.rb +5 -1
- data/lib/serialbench/serializers/yaml/psych_serializer.rb +1 -1
- data/lib/serialbench/serializers/yaml/syck_serializer.rb +60 -23
- data/lib/serialbench/serializers.rb +23 -6
- data/lib/serialbench/site_generator.rb +283 -0
- data/lib/serialbench/templates/assets/css/benchmark_report.css +535 -0
- data/lib/serialbench/templates/assets/css/format_based.css +474 -0
- data/lib/serialbench/templates/assets/css/themes.css +589 -0
- data/lib/serialbench/templates/assets/js/chart_helpers.js +411 -0
- data/lib/serialbench/templates/assets/js/dashboard.js +795 -0
- data/lib/serialbench/templates/assets/js/navigation.js +142 -0
- data/lib/serialbench/templates/base.liquid +49 -0
- data/lib/serialbench/templates/format_based.liquid +507 -0
- data/lib/serialbench/templates/partials/chart_section.liquid +4 -0
- data/lib/serialbench/version.rb +1 -1
- data/lib/serialbench/yaml_validator.rb +36 -0
- data/lib/serialbench.rb +2 -31
- data/serialbench.gemspec +15 -3
- metadata +106 -25
- data/.github/workflows/ci.yml +0 -74
- data/.github/workflows/docker.yml +0 -246
- data/config/ci.yml +0 -22
- data/config/full.yml +0 -30
- data/docker/run-benchmarks.sh +0 -356
- data/lib/serialbench/chart_generator.rb +0 -821
- data/lib/serialbench/result_formatter.rb +0 -182
- data/lib/serialbench/result_merger.rb +0 -1201
- data/lib/serialbench/serializers/xml/base_parser.rb +0 -69
- data/lib/serialbench/serializers/xml/libxml_parser.rb +0 -98
- data/lib/serialbench/serializers/xml/nokogiri_parser.rb +0 -111
- data/lib/serialbench/serializers/xml/oga_parser.rb +0 -85
- data/lib/serialbench/serializers/xml/ox_parser.rb +0 -64
- data/lib/serialbench/serializers/xml/rexml_parser.rb +0 -129
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base_cli'
|
|
4
|
+
require_relative '../models/result_set'
|
|
5
|
+
require_relative '../site_generator'
|
|
6
|
+
|
|
7
|
+
module Serialbench
|
|
8
|
+
module Cli
|
|
9
|
+
# CLI for managing benchmark resultsets (collections of runs)
|
|
10
|
+
class ResultsetCli < BaseCli
|
|
11
|
+
desc 'create NAME PATH', 'Create a new resultset'
|
|
12
|
+
long_desc <<~DESC
|
|
13
|
+
Create a new resultset (collection of benchmark runs).
|
|
14
|
+
|
|
15
|
+
NAME is required and must be unique.
|
|
16
|
+
PATH is the directory where the resultset will be created.
|
|
17
|
+
|
|
18
|
+
Examples:
|
|
19
|
+
serialbench resultset create performance-comparison results/sets/performance-comparison
|
|
20
|
+
serialbench resultset create cross-platform-test results/sets/cross-platform-test
|
|
21
|
+
DESC
|
|
22
|
+
def create(resultset_name, resultset_path)
|
|
23
|
+
Serialbench::Models::ResultStore.default
|
|
24
|
+
|
|
25
|
+
# Check if resultset already exists
|
|
26
|
+
definition_path = File.join(resultset_path, 'resultset.yaml')
|
|
27
|
+
|
|
28
|
+
if File.exist?(definition_path)
|
|
29
|
+
say "ResultSet at '#{resultset_path}' already exists", :yellow
|
|
30
|
+
return unless yes?('Create anyway with timestamp suffix? (y/n)')
|
|
31
|
+
|
|
32
|
+
resultset_path = "#{resultset_path}-#{generate_timestamp}"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Create empty resultset using the new ResultSet model
|
|
36
|
+
resultset = Serialbench::Models::ResultSet.new(
|
|
37
|
+
name: resultset_name,
|
|
38
|
+
description: "ResultSet for #{resultset_name} benchmarks",
|
|
39
|
+
created_at: Time.now.utc.iso8601,
|
|
40
|
+
updated_at: Time.now.utc.iso8601
|
|
41
|
+
)
|
|
42
|
+
resultset.save(resultset_path)
|
|
43
|
+
|
|
44
|
+
say "✅ Created resultset: #{resultset_path}", :green
|
|
45
|
+
say "Path: #{definition_path}", :cyan
|
|
46
|
+
say "Use 'serialbench resultset add-result' to add benchmark runs", :white
|
|
47
|
+
rescue StandardError => e
|
|
48
|
+
say "Error creating resultset: #{e.message}", :red
|
|
49
|
+
exit 1
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
desc 'add-result RESULTSET_PATH RESULT_PATH...', 'Add one or more runs to a resultset'
|
|
53
|
+
long_desc <<~DESC
|
|
54
|
+
Add one or more benchmark runs to a resultset.
|
|
55
|
+
|
|
56
|
+
RESULTSET_PATH is the path to the resultset directory
|
|
57
|
+
RESULT_PATH... accepts multiple result paths (supports shell expansion)
|
|
58
|
+
|
|
59
|
+
Examples:
|
|
60
|
+
# Add single result
|
|
61
|
+
serialbench resultset add-result results/sets/weekly results/runs/my-run
|
|
62
|
+
|
|
63
|
+
# Add multiple results explicitly
|
|
64
|
+
serialbench resultset add-result results/sets/weekly results/runs/run1 results/runs/run2
|
|
65
|
+
|
|
66
|
+
# Add multiple results with shell expansion
|
|
67
|
+
serialbench resultset add-result results/sets/weekly artifacts/benchmark-results-*/
|
|
68
|
+
serialbench resultset add-result results/sets/weekly results/runs/*
|
|
69
|
+
DESC
|
|
70
|
+
def add_result(resultset_path, *result_paths)
|
|
71
|
+
if result_paths.empty?
|
|
72
|
+
say '❌ Error: At least one result path must be provided', :red
|
|
73
|
+
exit 1
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
resultset = Serialbench::Models::ResultSet.load(resultset_path)
|
|
77
|
+
|
|
78
|
+
say "📦 Adding #{result_paths.size} result(s) to resultset", :cyan
|
|
79
|
+
say "ResultSet: #{resultset_path}", :white
|
|
80
|
+
say ''
|
|
81
|
+
|
|
82
|
+
added_count = 0
|
|
83
|
+
failed_count = 0
|
|
84
|
+
skipped_count = 0
|
|
85
|
+
|
|
86
|
+
result_paths.each_with_index do |result_path, index|
|
|
87
|
+
say "#{index + 1}/#{result_paths.size} Processing: #{result_path}", :cyan
|
|
88
|
+
|
|
89
|
+
# Find results.yaml in the path or subdirectories
|
|
90
|
+
results_file = if File.exist?(File.join(result_path, 'results.yaml'))
|
|
91
|
+
File.join(result_path, 'results.yaml')
|
|
92
|
+
else
|
|
93
|
+
Dir.glob(File.join(result_path, '**/results.yaml')).first
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
unless results_file
|
|
97
|
+
say ' ⚠️ No results.yaml found - skipping', :yellow
|
|
98
|
+
skipped_count += 1
|
|
99
|
+
next
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
result_dir = File.dirname(results_file)
|
|
103
|
+
|
|
104
|
+
begin
|
|
105
|
+
resultset.add_result(result_dir)
|
|
106
|
+
say ' ✅ Added successfully', :green
|
|
107
|
+
added_count += 1
|
|
108
|
+
rescue StandardError => e
|
|
109
|
+
say " ❌ Failed: #{e.message}", :red
|
|
110
|
+
failed_count += 1
|
|
111
|
+
end
|
|
112
|
+
say ''
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
resultset.save(resultset_path)
|
|
116
|
+
|
|
117
|
+
say '=' * 60, :cyan
|
|
118
|
+
say 'Summary:', :green
|
|
119
|
+
say " Total processed: #{result_paths.size}", :white
|
|
120
|
+
say " ✅ Successfully added: #{added_count}", :green
|
|
121
|
+
say " ❌ Failed: #{failed_count}", :red if failed_count > 0
|
|
122
|
+
say " ⚠️ Skipped: #{skipped_count}", :yellow if skipped_count > 0
|
|
123
|
+
say " 📊 Total results in set: #{resultset.results.count}", :cyan
|
|
124
|
+
say '=' * 60, :cyan
|
|
125
|
+
|
|
126
|
+
exit 1 if failed_count > 0 && added_count == 0
|
|
127
|
+
rescue StandardError => e
|
|
128
|
+
say "❌ Error: #{e.message}", :red
|
|
129
|
+
exit 1
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
desc 'remove-result RESULTSET_PATH RESULT_PATH', 'Remove a run from a resultset'
|
|
133
|
+
long_desc <<~DESC
|
|
134
|
+
Remove a benchmark run from a resultset.
|
|
135
|
+
|
|
136
|
+
RESULTSET_PATH must be specified explicitly
|
|
137
|
+
RESULT_PATH is the path to the run result directory
|
|
138
|
+
|
|
139
|
+
Examples:
|
|
140
|
+
serialbench resultset remove-result results/sets/performance-comparison results/runs/my-run-local-macos-arm64-ruby-3.3.8
|
|
141
|
+
serialbench resultset remove-result results/sets/cross-platform-test results/runs/my-docker-run
|
|
142
|
+
DESC
|
|
143
|
+
def remove_result(resultset_path, _result_path)
|
|
144
|
+
Serialbench::Models::ResultStore.default
|
|
145
|
+
|
|
146
|
+
# Find the resultset
|
|
147
|
+
resultset = Serialbench::Models::ResultSet.load(resultset_path)
|
|
148
|
+
if resultset.nil?
|
|
149
|
+
say "ResultSet '#{resultset_path}' not found", :red
|
|
150
|
+
exit 1
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# Remove run from resultset
|
|
154
|
+
removed = resultset.remove_run(run_identifier)
|
|
155
|
+
unless removed
|
|
156
|
+
say "Run '#{run_identifier}' not found in resultset", :yellow
|
|
157
|
+
say 'Available runs in resultset:', :white
|
|
158
|
+
resultset.runs.each do |run_info|
|
|
159
|
+
say " - #{run_info[:name]}", :white
|
|
160
|
+
end
|
|
161
|
+
return
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
resultset.save
|
|
165
|
+
|
|
166
|
+
say '✅ Removed run from resultset', :green
|
|
167
|
+
say "Run: #{run_identifier}", :cyan
|
|
168
|
+
say "ResultSet: #{resultset_path}", :cyan
|
|
169
|
+
say "Remaining runs in set: #{resultset.runs.length}", :white
|
|
170
|
+
rescue StandardError => e
|
|
171
|
+
say "Error removing run from resultset: #{e.message}", :red
|
|
172
|
+
exit 1
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
desc 'build-site RESULTSET_PATH [OUTPUT_DIR]', 'Generate HTML site for a resultset'
|
|
176
|
+
long_desc <<~DESC
|
|
177
|
+
Generate an HTML site for a resultset (comparative analysis).
|
|
178
|
+
|
|
179
|
+
RESULTSET_PATH must be specified explicitly
|
|
180
|
+
OUTPUT_DIR defaults to _site/
|
|
181
|
+
|
|
182
|
+
Examples:
|
|
183
|
+
serialbench resultset build-site results/sets/performance-comparison
|
|
184
|
+
serialbench resultset build-site results/sets/cross-platform-test output/
|
|
185
|
+
DESC
|
|
186
|
+
def build_site(resultset_path, output_dir = '_site')
|
|
187
|
+
unless Dir.exist?(resultset_path)
|
|
188
|
+
say "ResultSet directory not found: #{resultset_path}", :red
|
|
189
|
+
say "Please create a resultset first using 'serialbench resultset create'", :white
|
|
190
|
+
exit 1
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
resultset = Serialbench::Models::ResultSet.load(resultset_path)
|
|
194
|
+
|
|
195
|
+
if resultset.results.empty?
|
|
196
|
+
say "ResultSet '#{resultset_path}' contains no runs", :yellow
|
|
197
|
+
say "Use 'serialbench resultset add-result' to add runs first", :white
|
|
198
|
+
return
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
say "🏗️ Generating HTML site for resultset: #{resultset_path}", :green
|
|
202
|
+
say "Runs in set: #{resultset.results.size}", :cyan
|
|
203
|
+
|
|
204
|
+
# Use the unified site generator for resultsets
|
|
205
|
+
Serialbench::SiteGenerator.generate_for_resultset(resultset, output_dir)
|
|
206
|
+
|
|
207
|
+
say '✅ HTML site generated successfully!', :green
|
|
208
|
+
say "Site location: #{output_dir}", :cyan
|
|
209
|
+
say "Open: #{File.join(output_dir, 'index.html')}", :white
|
|
210
|
+
rescue StandardError => e
|
|
211
|
+
say "Error generating site: #{e.message}", :red
|
|
212
|
+
say "Details: #{e.backtrace.first(3).join("\n")}", :red if options[:verbose]
|
|
213
|
+
exit 1
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
desc 'list', 'List all available resultsets'
|
|
217
|
+
long_desc <<~DESC
|
|
218
|
+
List all resultsets in the results/sets/ directory.
|
|
219
|
+
|
|
220
|
+
Shows resultset names, number of runs, and timestamps.
|
|
221
|
+
DESC
|
|
222
|
+
def list
|
|
223
|
+
ensure_results_directory
|
|
224
|
+
|
|
225
|
+
begin
|
|
226
|
+
store = Serialbench::Models::ResultStore.default
|
|
227
|
+
resultsets = store.find_resultsets
|
|
228
|
+
|
|
229
|
+
if resultsets.empty?
|
|
230
|
+
say 'No resultsets found', :yellow
|
|
231
|
+
say "Use 'serialbench resultset create' to create a resultset", :white
|
|
232
|
+
return
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
say 'Available ResultSets:', :green
|
|
236
|
+
say '=' * 50, :green
|
|
237
|
+
|
|
238
|
+
resultsets.each do |resultset|
|
|
239
|
+
say "📁 #{resultset.name}", :cyan
|
|
240
|
+
say " Runs: #{resultset.runs.length}", :white
|
|
241
|
+
say " Created: #{resultset.metadata[:timestamp]}", :white
|
|
242
|
+
say " Path: #{resultset.directory}", :white
|
|
243
|
+
|
|
244
|
+
if resultset.runs.any?
|
|
245
|
+
say ' Contains:', :white
|
|
246
|
+
resultset.runs.first(3).each do |run_info|
|
|
247
|
+
say " - #{run_info[:name]}", :white
|
|
248
|
+
end
|
|
249
|
+
say " ... and #{resultset.runs.length - 3} more" if resultset.runs.length > 3
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
say ''
|
|
253
|
+
end
|
|
254
|
+
rescue StandardError => e
|
|
255
|
+
say "Error listing resultsets: #{e.message}", :red
|
|
256
|
+
exit 1
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
end
|
|
260
|
+
end
|
|
261
|
+
end
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base_cli'
|
|
4
|
+
require_relative '../ruby_build_manager'
|
|
5
|
+
|
|
6
|
+
module Serialbench
|
|
7
|
+
module Cli
|
|
8
|
+
# CLI for managing Ruby-Build definitions
|
|
9
|
+
class RubyBuildCli < BaseCli
|
|
10
|
+
desc 'update', 'Update Ruby-Build definitions from GitHub'
|
|
11
|
+
long_desc <<~DESC
|
|
12
|
+
Fetch the latest Ruby-Build definitions from the official ruby-build repository
|
|
13
|
+
and cache them locally for validation purposes.
|
|
14
|
+
|
|
15
|
+
This command is required before using any Ruby-Build validation features.
|
|
16
|
+
|
|
17
|
+
Examples:
|
|
18
|
+
serialbench ruby-build update
|
|
19
|
+
DESC
|
|
20
|
+
def update
|
|
21
|
+
say '🔄 Updating Ruby-Build definitions...', :green
|
|
22
|
+
|
|
23
|
+
begin
|
|
24
|
+
definitions = RubyBuildManager.update_definitions
|
|
25
|
+
|
|
26
|
+
say "✅ Successfully updated #{definitions.length} Ruby-Build definitions", :green
|
|
27
|
+
say "📁 Cache location: #{RubyBuildManager::CACHE_FILE}", :cyan
|
|
28
|
+
|
|
29
|
+
# Show some examples
|
|
30
|
+
recent_versions = definitions.select { |d| d.match?(/^3\.[2-4]\.\d+$/) }.last(5)
|
|
31
|
+
if recent_versions.any?
|
|
32
|
+
say "\n📋 Recent Ruby versions available:", :white
|
|
33
|
+
recent_versions.each { |version| say " #{version}", :cyan }
|
|
34
|
+
end
|
|
35
|
+
rescue StandardError => e
|
|
36
|
+
say "❌ Failed to update Ruby-Build definitions: #{e.message}", :red
|
|
37
|
+
exit 1
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
desc 'list [FILTER]', 'List available Ruby-Build definitions'
|
|
42
|
+
long_desc <<~DESC
|
|
43
|
+
List all available Ruby-Build definitions from the local cache.
|
|
44
|
+
|
|
45
|
+
Optionally filter the list by providing a filter string.
|
|
46
|
+
|
|
47
|
+
Examples:
|
|
48
|
+
serialbench ruby-build list # List all definitions
|
|
49
|
+
serialbench ruby-build list 3.3 # Filter by "3.3"
|
|
50
|
+
serialbench ruby-build list 3.2. # Filter by "3.2."
|
|
51
|
+
DESC
|
|
52
|
+
option :limit, type: :numeric, default: 50, desc: 'Maximum number of definitions to show'
|
|
53
|
+
def list(filter = nil)
|
|
54
|
+
definitions = RubyBuildManager.list_definitions(filter: filter)
|
|
55
|
+
|
|
56
|
+
if definitions.empty?
|
|
57
|
+
if filter
|
|
58
|
+
say "No Ruby-Build definitions found matching '#{filter}'", :yellow
|
|
59
|
+
else
|
|
60
|
+
say 'No Ruby-Build definitions found in cache', :yellow
|
|
61
|
+
say 'Update the cache first: serialbench ruby-build update', :white
|
|
62
|
+
end
|
|
63
|
+
return
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Limit results if there are many
|
|
67
|
+
limited_definitions = definitions.first(options[:limit])
|
|
68
|
+
|
|
69
|
+
say "Ruby-Build Definitions#{filter ? " (filtered by '#{filter}')" : ''}:", :green
|
|
70
|
+
say '=' * 60, :green
|
|
71
|
+
|
|
72
|
+
limited_definitions.each do |definition|
|
|
73
|
+
say " #{definition}", :cyan
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
if definitions.length > options[:limit]
|
|
77
|
+
remaining = definitions.length - options[:limit]
|
|
78
|
+
say "\n... and #{remaining} more definitions", :yellow
|
|
79
|
+
say 'Use --limit to show more results', :white
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
say "\nTotal: #{definitions.length} definitions", :white
|
|
83
|
+
rescue StandardError => e
|
|
84
|
+
say "❌ Failed to list Ruby-Build definitions: #{e.message}", :red
|
|
85
|
+
say 'Try updating the cache: serialbench ruby-build update', :white
|
|
86
|
+
exit 1
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
desc 'show TAG', 'Show details for a specific Ruby-Build definition'
|
|
90
|
+
long_desc <<~DESC
|
|
91
|
+
Show detailed information about a specific Ruby-Build definition.
|
|
92
|
+
|
|
93
|
+
Examples:
|
|
94
|
+
serialbench ruby-build show 3.3.8
|
|
95
|
+
serialbench ruby-build show 3.2.4
|
|
96
|
+
DESC
|
|
97
|
+
def show(tag)
|
|
98
|
+
definition = RubyBuildManager.show_definition(tag)
|
|
99
|
+
|
|
100
|
+
say "Ruby-Build Definition: #{tag}", :green
|
|
101
|
+
say '=' * 40, :green
|
|
102
|
+
say "Tag: #{definition[:tag]}", :cyan
|
|
103
|
+
say "Available: #{definition[:available] ? '✅ Yes' : '❌ No'}", :cyan
|
|
104
|
+
say "Source: #{definition[:source]}", :cyan
|
|
105
|
+
say "Cache file: #{definition[:cache_file]}", :white
|
|
106
|
+
rescue StandardError => e
|
|
107
|
+
say "❌ #{e.message}", :red
|
|
108
|
+
say 'Available definitions: serialbench ruby-build list', :white
|
|
109
|
+
exit 1
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
desc 'validate TAG', 'Validate a Ruby-Build tag'
|
|
113
|
+
long_desc <<~DESC
|
|
114
|
+
Validate whether a Ruby-Build tag exists in the cached definitions.
|
|
115
|
+
|
|
116
|
+
Examples:
|
|
117
|
+
serialbench ruby-build validate 3.3.8
|
|
118
|
+
serialbench ruby-build validate 3.2.4
|
|
119
|
+
DESC
|
|
120
|
+
def validate(tag)
|
|
121
|
+
valid = RubyBuildManager.validate_tag(tag)
|
|
122
|
+
|
|
123
|
+
if valid
|
|
124
|
+
say "✅ Ruby-Build tag '#{tag}' is valid", :green
|
|
125
|
+
else
|
|
126
|
+
say "❌ Ruby-Build tag '#{tag}' is not valid", :red
|
|
127
|
+
|
|
128
|
+
# Suggest similar tags
|
|
129
|
+
definitions = RubyBuildManager.list_definitions
|
|
130
|
+
similar = definitions.select { |d| d.include?(tag.split('.').first(2).join('.')) }.first(5)
|
|
131
|
+
|
|
132
|
+
if similar.any?
|
|
133
|
+
say "\n💡 Similar available tags:", :yellow
|
|
134
|
+
similar.each { |s| say " #{s}", :cyan }
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
exit 1
|
|
138
|
+
end
|
|
139
|
+
rescue StandardError => e
|
|
140
|
+
say "❌ Failed to validate tag: #{e.message}", :red
|
|
141
|
+
say 'Try updating the cache: serialbench ruby-build update', :white
|
|
142
|
+
exit 1
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
desc 'suggest', 'Suggest Ruby-Build tag for current Ruby version'
|
|
146
|
+
long_desc <<~DESC
|
|
147
|
+
Suggest an appropriate Ruby-Build tag based on the current Ruby version.
|
|
148
|
+
|
|
149
|
+
This is useful when creating local environments to get the correct
|
|
150
|
+
ruby_build_tag value.
|
|
151
|
+
|
|
152
|
+
Examples:
|
|
153
|
+
serialbench ruby-build suggest
|
|
154
|
+
DESC
|
|
155
|
+
def suggest
|
|
156
|
+
current_ruby = RUBY_VERSION
|
|
157
|
+
suggested_tag = RubyBuildManager.suggest_current_ruby_tag
|
|
158
|
+
|
|
159
|
+
say "Current Ruby version: #{current_ruby}", :cyan
|
|
160
|
+
say "Suggested ruby_build_tag: #{suggested_tag}", :green
|
|
161
|
+
|
|
162
|
+
# Validate the suggestion
|
|
163
|
+
if RubyBuildManager.validate_tag(suggested_tag)
|
|
164
|
+
say '✅ Suggested tag is valid', :green
|
|
165
|
+
else
|
|
166
|
+
say '⚠️ Suggested tag not found in ruby-build definitions', :yellow
|
|
167
|
+
say 'You may need to update the cache or use a different tag', :white
|
|
168
|
+
end
|
|
169
|
+
rescue StandardError => e
|
|
170
|
+
say "❌ Failed to suggest tag: #{e.message}", :red
|
|
171
|
+
say 'Try updating the cache: serialbench ruby-build update', :white
|
|
172
|
+
exit 1
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
desc 'cache-info', 'Show information about the Ruby-Build definitions cache'
|
|
176
|
+
long_desc <<~DESC
|
|
177
|
+
Display information about the local Ruby-Build definitions cache,
|
|
178
|
+
including location, age, and update status.
|
|
179
|
+
|
|
180
|
+
Examples:
|
|
181
|
+
serialbench ruby-build cache-info
|
|
182
|
+
DESC
|
|
183
|
+
def cache_info
|
|
184
|
+
if RubyBuildManager.cache_exists?
|
|
185
|
+
cache_age = RubyBuildManager.cache_age
|
|
186
|
+
definitions_count = RubyBuildManager.list_definitions.length
|
|
187
|
+
|
|
188
|
+
say 'Ruby-Build Cache Information:', :green
|
|
189
|
+
say '=' * 40, :green
|
|
190
|
+
say "Location: #{RubyBuildManager::CACHE_FILE}", :cyan
|
|
191
|
+
say "Definitions: #{definitions_count}", :cyan
|
|
192
|
+
say "Age: #{format_cache_age(cache_age)}", :cyan
|
|
193
|
+
say 'Status: ✅ Available', :green
|
|
194
|
+
|
|
195
|
+
if cache_age > 7 * 24 * 60 * 60 # 7 days
|
|
196
|
+
say "\n💡 Cache is older than 7 days, consider updating:", :yellow
|
|
197
|
+
say ' serialbench ruby-build update', :white
|
|
198
|
+
end
|
|
199
|
+
else
|
|
200
|
+
say 'Ruby-Build Cache Information:', :green
|
|
201
|
+
say '=' * 40, :green
|
|
202
|
+
say "Location: #{RubyBuildManager::CACHE_FILE}", :cyan
|
|
203
|
+
say 'Status: ❌ Not found', :red
|
|
204
|
+
say "\n📥 Update the cache first:", :yellow
|
|
205
|
+
say ' serialbench ruby-build update', :white
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
private
|
|
210
|
+
|
|
211
|
+
def format_cache_age(seconds)
|
|
212
|
+
days = (seconds / (24 * 60 * 60)).to_i
|
|
213
|
+
hours = ((seconds % (24 * 60 * 60)) / (60 * 60)).to_i
|
|
214
|
+
|
|
215
|
+
if days.positive?
|
|
216
|
+
"#{days} day#{'s' if days != 1}, #{hours} hour#{'s' if hours != 1}"
|
|
217
|
+
elsif hours.positive?
|
|
218
|
+
"#{hours} hour#{'s' if hours != 1}"
|
|
219
|
+
else
|
|
220
|
+
'less than 1 hour'
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base_cli'
|
|
4
|
+
require_relative '../yaml_validator'
|
|
5
|
+
|
|
6
|
+
module Serialbench
|
|
7
|
+
module Cli
|
|
8
|
+
# CLI for validating YAML files against schemas
|
|
9
|
+
class ValidateCli < BaseCli
|
|
10
|
+
desc 'result RESULT_FILE', 'Validate a benchmark result YAML file'
|
|
11
|
+
long_desc <<~DESC
|
|
12
|
+
Validate a benchmark result YAML file against its schema.
|
|
13
|
+
|
|
14
|
+
RESULT_FILE should be the path to a results.yaml file
|
|
15
|
+
|
|
16
|
+
Examples:
|
|
17
|
+
serialbench validate result results/runs/my-run/results.yaml
|
|
18
|
+
serialbench validate result test-artifacts/benchmark-results-ubuntu-latest-ruby-3.4/results.yaml
|
|
19
|
+
DESC
|
|
20
|
+
def result(file_path)
|
|
21
|
+
validate_file(file_path, 'result', 'Result')
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
desc 'config BENCHMARK_CONFIG', 'Validate a benchmark configuration file'
|
|
25
|
+
long_desc <<~DESC
|
|
26
|
+
Validate a benchmark configuration file against its schema.
|
|
27
|
+
|
|
28
|
+
BENCHMARK_CONFIG should be the path to a benchmark config YAML file
|
|
29
|
+
|
|
30
|
+
Examples:
|
|
31
|
+
serialbench validate config config/benchmarks/short.yml
|
|
32
|
+
serialbench validate config config/benchmarks/full.yml
|
|
33
|
+
DESC
|
|
34
|
+
def config(file_path)
|
|
35
|
+
validate_file(file_path, 'benchmark_config', 'Benchmark config')
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
desc 'environment ENV_CONFIG', 'Validate an environment configuration file'
|
|
39
|
+
long_desc <<~DESC
|
|
40
|
+
Validate an environment configuration file against its schema.
|
|
41
|
+
|
|
42
|
+
ENV_CONFIG should be the path to an environment config YAML file
|
|
43
|
+
|
|
44
|
+
Examples:
|
|
45
|
+
serialbench validate environment config/environments/local-dev.yml
|
|
46
|
+
serialbench validate environment config/environments/docker-ruby-3.4.yml
|
|
47
|
+
DESC
|
|
48
|
+
def environment(file_path)
|
|
49
|
+
validate_file(file_path, 'environment_config', 'Environment config')
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
desc 'resultset RESULTSET_FILE', 'Validate a resultset YAML file'
|
|
53
|
+
long_desc <<~DESC
|
|
54
|
+
Validate a resultset YAML file against its schema.
|
|
55
|
+
|
|
56
|
+
RESULTSET_FILE should be the path to a resultset.yaml file
|
|
57
|
+
|
|
58
|
+
Examples:
|
|
59
|
+
serialbench validate resultset results/sets/weekly-benchmark/resultset.yaml
|
|
60
|
+
serialbench validate resultset results/sets/comparison/resultset.yaml
|
|
61
|
+
DESC
|
|
62
|
+
def resultset(file_path)
|
|
63
|
+
validate_file(file_path, 'resultset', 'Resultset')
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
private
|
|
67
|
+
|
|
68
|
+
def validate_file(file_path, schema_name, friendly_name)
|
|
69
|
+
unless File.exist?(file_path)
|
|
70
|
+
say "❌ File not found: #{file_path}", :red
|
|
71
|
+
exit 1
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
say "📝 Validating #{friendly_name}: #{file_path}", :cyan
|
|
75
|
+
|
|
76
|
+
if Serialbench::YamlValidator.validate(file_path, schema_name)
|
|
77
|
+
say "✅ #{friendly_name} is valid", :green
|
|
78
|
+
else
|
|
79
|
+
say "❌ #{friendly_name} validation failed", :red
|
|
80
|
+
exit 1
|
|
81
|
+
end
|
|
82
|
+
rescue StandardError => e
|
|
83
|
+
say "❌ Error: #{e.message}", :red
|
|
84
|
+
exit 1
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|