serialbench 0.1.2 → 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 (84) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/benchmark.yml +294 -229
  3. data/.github/workflows/rake.yml +11 -0
  4. data/.github/workflows/windows-debug.yml +171 -0
  5. data/.gitignore +32 -0
  6. data/.rubocop.yml +1 -0
  7. data/.rubocop_todo.yml +274 -0
  8. data/Gemfile +15 -1
  9. data/README.adoc +41 -0
  10. data/data/schemas/result.yml +29 -0
  11. data/docs/PLATFORM_VALIDATION_FIX.md +79 -0
  12. data/docs/SYCK_YAML_FIX.md +91 -0
  13. data/docs/WEBSITE_COMPLETION_PLAN.md +440 -0
  14. data/docs/WINDOWS_LIBXML_FIX.md +136 -0
  15. data/docs/WINDOWS_SETUP.md +122 -0
  16. data/lib/serialbench/benchmark_runner.rb +3 -3
  17. data/lib/serialbench/cli/benchmark_cli.rb +74 -1
  18. data/lib/serialbench/cli/environment_cli.rb +3 -3
  19. data/lib/serialbench/cli/resultset_cli.rb +112 -26
  20. data/lib/serialbench/cli/ruby_build_cli.rb +75 -88
  21. data/lib/serialbench/cli/validate_cli.rb +88 -0
  22. data/lib/serialbench/cli.rb +6 -2
  23. data/lib/serialbench/config_manager.rb +15 -26
  24. data/lib/serialbench/models/benchmark_config.rb +12 -0
  25. data/lib/serialbench/models/benchmark_result.rb +39 -3
  26. data/lib/serialbench/models/environment_config.rb +3 -2
  27. data/lib/serialbench/models/platform.rb +61 -6
  28. data/lib/serialbench/models/result.rb +28 -1
  29. data/lib/serialbench/models/result_set.rb +8 -0
  30. data/lib/serialbench/ruby_build_manager.rb +19 -23
  31. data/lib/serialbench/runners/asdf_runner.rb +1 -1
  32. data/lib/serialbench/runners/docker_runner.rb +2 -4
  33. data/lib/serialbench/runners/local_runner.rb +71 -0
  34. data/lib/serialbench/serializers/base_serializer.rb +1 -1
  35. data/lib/serialbench/serializers/json/rapidjson_serializer.rb +1 -5
  36. data/lib/serialbench/serializers/toml/base_toml_serializer.rb +0 -2
  37. data/lib/serialbench/serializers/toml/toml_rb_serializer.rb +1 -1
  38. data/lib/serialbench/serializers/toml/tomlib_serializer.rb +1 -1
  39. data/lib/serialbench/serializers/xml/base_xml_serializer.rb +6 -1
  40. data/lib/serialbench/serializers/xml/leptris_serializer.rb +167 -0
  41. data/lib/serialbench/serializers/xml/libxml_serializer.rb +13 -8
  42. data/lib/serialbench/serializers/xml/nokogiri_serializer.rb +11 -2
  43. data/lib/serialbench/serializers/xml/oga_serializer.rb +8 -8
  44. data/lib/serialbench/serializers/xml/ox_serializer.rb +2 -2
  45. data/lib/serialbench/serializers/xml/rexml_serializer.rb +3 -3
  46. data/lib/serialbench/serializers/yaml/base_yaml_serializer.rb +1 -3
  47. data/lib/serialbench/serializers/yaml/psych_serializer.rb +1 -5
  48. data/lib/serialbench/serializers/yaml/syck_serializer.rb +1 -1
  49. data/lib/serialbench/serializers.rb +5 -3
  50. data/lib/serialbench/site_generator.rb +234 -2
  51. data/lib/serialbench/templates/assets/css/format_based.css +1 -53
  52. data/lib/serialbench/templates/assets/css/themes.css +5 -4
  53. data/lib/serialbench/templates/assets/js/chart_helpers.js +44 -14
  54. data/lib/serialbench/templates/assets/js/dashboard.js +14 -15
  55. data/lib/serialbench/templates/format_based.liquid +480 -252
  56. data/lib/serialbench/version.rb +1 -1
  57. data/lib/serialbench/yaml_validator.rb +36 -0
  58. data/serialbench.gemspec +11 -2
  59. data/site/.gitignore +5 -0
  60. data/site/README.md +32 -0
  61. data/site/astro.config.mjs +12 -0
  62. data/site/package-lock.json +6160 -0
  63. data/site/package.json +22 -0
  64. data/site/public/favicon.svg +4 -0
  65. data/site/scripts/gen-sample-data.mjs +154 -0
  66. data/site/src/components/AvailabilityMatrix.astro +61 -0
  67. data/site/src/components/CalibratedLeaderboard.vue +134 -0
  68. data/site/src/components/CitationBox.vue +50 -0
  69. data/site/src/components/DashboardConsole.vue +193 -0
  70. data/site/src/components/FeaturesTable.astro +97 -0
  71. data/site/src/components/MemoryPanel.vue +44 -0
  72. data/site/src/components/OpsChart.vue +135 -0
  73. data/site/src/config.ts +18 -0
  74. data/site/src/layouts/Layout.astro +85 -0
  75. data/site/src/lib/channels.ts +24 -0
  76. data/site/src/lib/dashboard.ts +269 -0
  77. data/site/src/pages/index.astro +44 -0
  78. data/site/src/pages/library/[slug].astro +128 -0
  79. data/site/src/pages/methodology.astro +55 -0
  80. data/site/src/styles/global.css +103 -0
  81. data/site/tsconfig.json +5 -0
  82. metadata +58 -23
  83. data/.github/workflows/ci.yml +0 -74
  84. data/.github/workflows/docker.yml +0 -272
@@ -32,7 +32,6 @@ module Serialbench
32
32
  say "\n📋 Recent Ruby versions available:", :white
33
33
  recent_versions.each { |version| say " #{version}", :cyan }
34
34
  end
35
-
36
35
  rescue StandardError => e
37
36
  say "❌ Failed to update Ruby-Build definitions: #{e.message}", :red
38
37
  exit 1
@@ -52,42 +51,39 @@ module Serialbench
52
51
  DESC
53
52
  option :limit, type: :numeric, default: 50, desc: 'Maximum number of definitions to show'
54
53
  def list(filter = nil)
55
- begin
56
- definitions = RubyBuildManager.list_definitions(filter: filter)
57
-
58
- if definitions.empty?
59
- if filter
60
- say "No Ruby-Build definitions found matching '#{filter}'", :yellow
61
- else
62
- say 'No Ruby-Build definitions found in cache', :yellow
63
- say 'Update the cache first: serialbench ruby-build update', :white
64
- end
65
- return
66
- end
67
-
68
- # Limit results if there are many
69
- limited_definitions = definitions.first(options[:limit])
54
+ definitions = RubyBuildManager.list_definitions(filter: filter)
70
55
 
71
- say "Ruby-Build Definitions#{filter ? " (filtered by '#{filter}')" : ''}:", :green
72
- say '=' * 60, :green
73
-
74
- limited_definitions.each do |definition|
75
- say " #{definition}", :cyan
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
76
62
  end
63
+ return
64
+ end
77
65
 
78
- if definitions.length > options[:limit]
79
- remaining = definitions.length - options[:limit]
80
- say "\n... and #{remaining} more definitions", :yellow
81
- say "Use --limit to show more results", :white
82
- end
66
+ # Limit results if there are many
67
+ limited_definitions = definitions.first(options[:limit])
83
68
 
84
- say "\nTotal: #{definitions.length} definitions", :white
69
+ say "Ruby-Build Definitions#{filter ? " (filtered by '#{filter}')" : ''}:", :green
70
+ say '=' * 60, :green
85
71
 
86
- rescue StandardError => e
87
- say "❌ Failed to list Ruby-Build definitions: #{e.message}", :red
88
- say 'Try updating the cache: serialbench ruby-build update', :white
89
- exit 1
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
90
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
91
87
  end
92
88
 
93
89
  desc 'show TAG', 'Show details for a specific Ruby-Build definition'
@@ -99,21 +95,18 @@ module Serialbench
99
95
  serialbench ruby-build show 3.2.4
100
96
  DESC
101
97
  def show(tag)
102
- begin
103
- definition = RubyBuildManager.show_definition(tag)
104
-
105
- say "Ruby-Build Definition: #{tag}", :green
106
- say '=' * 40, :green
107
- say "Tag: #{definition[:tag]}", :cyan
108
- say "Available: #{definition[:available] ? '✅ Yes' : '❌ No'}", :cyan
109
- say "Source: #{definition[:source]}", :cyan
110
- say "Cache file: #{definition[:cache_file]}", :white
111
-
112
- rescue StandardError => e
113
- say "❌ #{e.message}", :red
114
- say 'Available definitions: serialbench ruby-build list', :white
115
- exit 1
116
- end
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
117
110
  end
118
111
 
119
112
  desc 'validate TAG', 'Validate a Ruby-Build tag'
@@ -125,31 +118,28 @@ module Serialbench
125
118
  serialbench ruby-build validate 3.2.4
126
119
  DESC
127
120
  def validate(tag)
128
- begin
129
- valid = RubyBuildManager.validate_tag(tag)
130
-
131
- if valid
132
- say "✅ Ruby-Build tag '#{tag}' is valid", :green
133
- else
134
- say "❌ Ruby-Build tag '#{tag}' is not valid", :red
121
+ valid = RubyBuildManager.validate_tag(tag)
135
122
 
136
- # Suggest similar tags
137
- definitions = RubyBuildManager.list_definitions
138
- similar = definitions.select { |d| d.include?(tag.split('.').first(2).join('.')) }.first(5)
123
+ if valid
124
+ say "✅ Ruby-Build tag '#{tag}' is valid", :green
125
+ else
126
+ say "❌ Ruby-Build tag '#{tag}' is not valid", :red
139
127
 
140
- if similar.any?
141
- say "\n💡 Similar available tags:", :yellow
142
- similar.each { |s| say " #{s}", :cyan }
143
- end
128
+ # Suggest similar tags
129
+ definitions = RubyBuildManager.list_definitions
130
+ similar = definitions.select { |d| d.include?(tag.split('.').first(2).join('.')) }.first(5)
144
131
 
145
- exit 1
132
+ if similar.any?
133
+ say "\n💡 Similar available tags:", :yellow
134
+ similar.each { |s| say " #{s}", :cyan }
146
135
  end
147
136
 
148
- rescue StandardError => e
149
- say "❌ Failed to validate tag: #{e.message}", :red
150
- say 'Try updating the cache: serialbench ruby-build update', :white
151
137
  exit 1
152
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
153
143
  end
154
144
 
155
145
  desc 'suggest', 'Suggest Ruby-Build tag for current Ruby version'
@@ -163,26 +153,23 @@ module Serialbench
163
153
  serialbench ruby-build suggest
164
154
  DESC
165
155
  def suggest
166
- begin
167
- current_ruby = RUBY_VERSION
168
- suggested_tag = RubyBuildManager.suggest_current_ruby_tag
156
+ current_ruby = RUBY_VERSION
157
+ suggested_tag = RubyBuildManager.suggest_current_ruby_tag
169
158
 
170
- say "Current Ruby version: #{current_ruby}", :cyan
171
- say "Suggested ruby_build_tag: #{suggested_tag}", :green
172
-
173
- # Validate the suggestion
174
- if RubyBuildManager.validate_tag(suggested_tag)
175
- say "✅ Suggested tag is valid", :green
176
- else
177
- say "⚠️ Suggested tag not found in ruby-build definitions", :yellow
178
- say "You may need to update the cache or use a different tag", :white
179
- end
159
+ say "Current Ruby version: #{current_ruby}", :cyan
160
+ say "Suggested ruby_build_tag: #{suggested_tag}", :green
180
161
 
181
- rescue StandardError => e
182
- say "❌ Failed to suggest tag: #{e.message}", :red
183
- say 'Try updating the cache: serialbench ruby-build update', :white
184
- exit 1
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
185
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
186
173
  end
187
174
 
188
175
  desc 'cache-info', 'Show information about the Ruby-Build definitions cache'
@@ -203,19 +190,19 @@ module Serialbench
203
190
  say "Location: #{RubyBuildManager::CACHE_FILE}", :cyan
204
191
  say "Definitions: #{definitions_count}", :cyan
205
192
  say "Age: #{format_cache_age(cache_age)}", :cyan
206
- say "Status: ✅ Available", :green
193
+ say 'Status: ✅ Available', :green
207
194
 
208
195
  if cache_age > 7 * 24 * 60 * 60 # 7 days
209
196
  say "\n💡 Cache is older than 7 days, consider updating:", :yellow
210
- say " serialbench ruby-build update", :white
197
+ say ' serialbench ruby-build update', :white
211
198
  end
212
199
  else
213
200
  say 'Ruby-Build Cache Information:', :green
214
201
  say '=' * 40, :green
215
202
  say "Location: #{RubyBuildManager::CACHE_FILE}", :cyan
216
- say "Status: ❌ Not found", :red
203
+ say 'Status: ❌ Not found', :red
217
204
  say "\n📥 Update the cache first:", :yellow
218
- say " serialbench ruby-build update", :white
205
+ say ' serialbench ruby-build update', :white
219
206
  end
220
207
  end
221
208
 
@@ -225,12 +212,12 @@ module Serialbench
225
212
  days = (seconds / (24 * 60 * 60)).to_i
226
213
  hours = ((seconds % (24 * 60 * 60)) / (60 * 60)).to_i
227
214
 
228
- if days > 0
215
+ if days.positive?
229
216
  "#{days} day#{'s' if days != 1}, #{hours} hour#{'s' if hours != 1}"
230
- elsif hours > 0
217
+ elsif hours.positive?
231
218
  "#{hours} hour#{'s' if hours != 1}"
232
219
  else
233
- "less than 1 hour"
220
+ 'less than 1 hour'
234
221
  end
235
222
  end
236
223
  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
@@ -6,6 +6,7 @@ require_relative 'cli/environment_cli'
6
6
  require_relative 'cli/benchmark_cli'
7
7
  require_relative 'cli/resultset_cli'
8
8
  require_relative 'cli/ruby_build_cli'
9
+ require_relative 'cli/validate_cli'
9
10
 
10
11
  module Serialbench
11
12
  # Main CLI entry point for the new object-oriented command structure
@@ -22,6 +23,9 @@ module Serialbench
22
23
  desc 'ruby_build SUBCOMMAND', 'Manage Ruby-Build definitions for validation'
23
24
  subcommand 'ruby_build', Serialbench::Cli::RubyBuildCli
24
25
 
26
+ desc 'validate SUBCOMMAND', 'Validate YAML files against schemas'
27
+ subcommand 'validate', Serialbench::Cli::ValidateCli
28
+
25
29
  desc 'version', 'Show Serialbench version'
26
30
  def self.version
27
31
  puts "Serialbench version #{Serialbench::VERSION}"
@@ -73,14 +77,14 @@ module Serialbench
73
77
  end
74
78
 
75
79
  # Handle unknown commands gracefully
76
- def method_missing(method_name, *args)
80
+ def method_missing(method_name, *_args)
77
81
  puts "Unknown command: #{method_name}"
78
82
  puts ''
79
83
  help
80
84
  exit 1
81
85
  end
82
86
 
83
- def respond_to_missing?(method_name, include_private = false)
87
+ def respond_to_missing?(_method_name, _include_private = false)
84
88
  false
85
89
  end
86
90
  end
@@ -39,16 +39,14 @@ module Serialbench
39
39
  private
40
40
 
41
41
  # Validate configuration against schema
42
- def validate_config(config_data, config_path)
42
+ def validate_config(config_data, _config_path)
43
43
  # For now, perform basic validation since we don't have a specific config schema validator
44
44
  validate_basic_config(config_data)
45
45
  end
46
46
 
47
47
  # Load the configuration schema
48
48
  def load_schema
49
- unless File.exist?(SCHEMA_PATH)
50
- raise ConfigurationError, "Configuration schema not found: #{SCHEMA_PATH}"
51
- end
49
+ raise ConfigurationError, "Configuration schema not found: #{SCHEMA_PATH}" unless File.exist?(SCHEMA_PATH)
52
50
 
53
51
  begin
54
52
  YAML.load_file(SCHEMA_PATH)
@@ -81,28 +79,22 @@ module Serialbench
81
79
 
82
80
  # Validate Docker-specific configuration
83
81
  def validate_docker_config(config)
84
- unless config.image_variants && !config.image_variants.empty?
85
- raise ConfigurationError, "Docker runtime requires 'image_variants' to be specified"
86
- end
82
+ raise ConfigurationError, "Docker runtime requires 'image_variants' to be specified" unless config.image_variants && !config.image_variants.empty?
87
83
 
88
84
  invalid_variants = config.image_variants - %w[slim alpine]
89
- unless invalid_variants.empty?
90
- raise ConfigurationError, "Invalid image variants: #{invalid_variants.join(', ')}. Valid variants: slim, alpine"
91
- end
85
+ return if invalid_variants.empty?
86
+
87
+ raise ConfigurationError, "Invalid image variants: #{invalid_variants.join(', ')}. Valid variants: slim, alpine"
92
88
  end
93
89
 
94
90
  # Validate ASDF-specific configuration
95
91
  def validate_asdf_config(config)
96
92
  # Check if ASDF is available
97
- unless command_available?('asdf')
98
- raise ConfigurationError, "ASDF is not installed or not in PATH. Please install ASDF to use asdf runtime."
99
- end
93
+ raise ConfigurationError, 'ASDF is not installed or not in PATH. Please install ASDF to use asdf runtime.' unless command_available?('asdf')
100
94
 
101
95
  # Validate Ruby version format for ASDF (should include patch version)
102
96
  config.ruby_versions.each do |version|
103
- unless version.match?(/^\d+\.\d+\.\d+$/)
104
- raise ConfigurationError, "ASDF runtime requires full version numbers (e.g., '3.2.8'), got: #{version}"
105
- end
97
+ raise ConfigurationError, "ASDF runtime requires full version numbers (e.g., '3.2.8'), got: #{version}" unless version.match?(/^\d+\.\d+\.\d+$/)
106
98
  end
107
99
  end
108
100
 
@@ -111,25 +103,22 @@ module Serialbench
111
103
  # Check required fields
112
104
  required_fields = %w[runtime ruby_versions output_dir benchmark_config]
113
105
  required_fields.each do |field|
114
- unless config_data.key?(field)
115
- raise ConfigurationError, "Missing required field: #{field}"
116
- end
106
+ raise ConfigurationError, "Missing required field: #{field}" unless config_data.key?(field)
117
107
  end
118
108
 
119
109
  # Validate runtime
120
110
  valid_runtimes = %w[docker asdf]
121
111
  unless valid_runtimes.include?(config_data['runtime'])
122
- raise ConfigurationError, "Invalid runtime: #{config_data['runtime']}. Valid runtimes: #{valid_runtimes.join(', ')}"
112
+ raise ConfigurationError,
113
+ "Invalid runtime: #{config_data['runtime']}. Valid runtimes: #{valid_runtimes.join(', ')}"
123
114
  end
124
115
 
125
116
  # Validate ruby_versions is an array
126
- unless config_data['ruby_versions'].is_a?(Array)
127
- raise ConfigurationError, "ruby_versions must be an array"
128
- end
117
+ raise ConfigurationError, 'ruby_versions must be an array' unless config_data['ruby_versions'].is_a?(Array)
129
118
 
130
- if config_data['ruby_versions'].empty?
131
- raise ConfigurationError, "ruby_versions cannot be empty"
132
- end
119
+ return unless config_data['ruby_versions'].empty?
120
+
121
+ raise ConfigurationError, 'ruby_versions cannot be empty'
133
122
  end
134
123
 
135
124
  # Check if a command is available in PATH
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'lutaml/model'
2
4
 
3
5
  module Serialbench
@@ -51,6 +53,16 @@ module Serialbench
51
53
  attribute :warmup, :integer, default: -> { 1 }
52
54
  attribute :operations, :string, collection: true, values: %w[parse generate memory streaming]
53
55
 
56
+ key_value do
57
+ map 'name', to: :name
58
+ map 'description', to: :description
59
+ map 'data_sizes', to: :data_sizes
60
+ map 'formats', to: :formats
61
+ map 'iterations', to: :iterations
62
+ map 'warmup', to: :warmup
63
+ map 'operations', to: :operations
64
+ end
65
+
54
66
  def to_file(file_path)
55
67
  File.write(file_path, to_yaml)
56
68
  end
@@ -8,16 +8,24 @@ module Serialbench
8
8
  attribute :format, :string, values: %w[xml json yaml toml]
9
9
  attribute :name, :string
10
10
  attribute :version, :string
11
- end
12
11
 
13
- class SerializerInformationCollection < Lutaml::Model::Collection
14
- instances :items, SerializerInformation
12
+ key_value do
13
+ map 'format', to: :format
14
+ map 'name', to: :name
15
+ map 'version', to: :version
16
+ end
15
17
  end
16
18
 
17
19
  class AdapterPerformance < Lutaml::Model::Serializable
18
20
  attribute :adapter, :string
19
21
  attribute :format, :string, values: %w[xml json yaml toml]
20
22
  attribute :data_size, :string, values: %w[small medium large]
23
+
24
+ key_value do
25
+ map 'adapter', to: :adapter
26
+ map 'format', to: :format
27
+ map 'data_size', to: :data_size
28
+ end
21
29
  end
22
30
 
23
31
  class IterationPerformance < AdapterPerformance
@@ -25,6 +33,16 @@ module Serialbench
25
33
  attribute :time_per_iteration, :float
26
34
  attribute :iterations_per_second, :float
27
35
  attribute :iterations_count, :integer
36
+
37
+ key_value do
38
+ map 'adapter', to: :adapter
39
+ map 'format', to: :format
40
+ map 'data_size', to: :data_size
41
+ map 'time_per_iterations', to: :time_per_iterations
42
+ map 'time_per_iteration', to: :time_per_iteration
43
+ map 'iterations_per_second', to: :iterations_per_second
44
+ map 'iterations_count', to: :iterations_count
45
+ end
28
46
  end
29
47
 
30
48
  class MemoryPerformance < AdapterPerformance
@@ -32,6 +50,16 @@ module Serialbench
32
50
  attribute :total_retained, :integer
33
51
  attribute :allocated_memory, :integer
34
52
  attribute :retained_memory, :integer
53
+
54
+ key_value do
55
+ map 'adapter', to: :adapter
56
+ map 'format', to: :format
57
+ map 'data_size', to: :data_size
58
+ map 'total_allocated', to: :total_allocated
59
+ map 'total_retained', to: :total_retained
60
+ map 'allocated_memory', to: :allocated_memory
61
+ map 'retained_memory', to: :retained_memory
62
+ end
35
63
  end
36
64
 
37
65
  class BenchmarkResult < Lutaml::Model::Serializable
@@ -40,6 +68,14 @@ module Serialbench
40
68
  attribute :generation, IterationPerformance, collection: true
41
69
  attribute :memory, MemoryPerformance, collection: true
42
70
  attribute :streaming, IterationPerformance, collection: true
71
+
72
+ key_value do
73
+ map 'serializers', to: :serializers
74
+ map 'parsing', to: :parsing
75
+ map 'generation', to: :generation
76
+ map 'memory', to: :memory
77
+ map 'streaming', to: :streaming
78
+ end
43
79
  end
44
80
  end
45
81
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'lutaml/model'
2
4
  require_relative '../ruby_build_manager'
3
5
 
@@ -43,8 +45,7 @@ module Serialbench
43
45
  attribute :name, :string
44
46
  attribute :kind, :string
45
47
  attribute :created_at, :string, default: -> { Time.now.utc.iso8601 }
46
- attribute :ruby_build_tag, :string, values:
47
- RubyBuildManager.list_definitions
48
+ attribute :ruby_build_tag, :string
48
49
  attribute :description, :string
49
50
  attribute :docker, DockerEnvConfig
50
51
  attribute :asdf, AsdfEnvConfig
@@ -15,17 +15,46 @@ module Serialbench
15
15
  class Platform < Lutaml::Model::Serializable
16
16
  attribute :platform_string, :string
17
17
  attribute :kind, :string, default: -> { 'local' }
18
- attribute :os, :string, default: -> { detect_os }
19
- attribute :arch, :string, default: -> { detect_arch }
18
+ # Fully qualified: lutaml-model 0.8+ instance-evals default
19
+ # lambdas, so bare detect_os/detect_arch (class methods) no
20
+ # longer resolve there.
21
+ attribute :os, :string, default: -> { Serialbench::Models::Platform.detect_os }
22
+ attribute :arch, :string, default: -> { Serialbench::Models::Platform.detect_arch }
20
23
  attribute :ruby_version, :string, default: -> { RUBY_VERSION }
21
24
  attribute :ruby_platform, :string, default: -> { RUBY_PLATFORM }
22
- attribute :ruby_build_tag, :string, values: RubyBuildManager.list_definitions
25
+ attribute :ruby_build_tag, :string
26
+
27
+ key_value do
28
+ map 'platform_string', to: :platform_string
29
+ map 'kind', to: :kind
30
+ map 'os', to: :os
31
+ map 'arch', to: :arch
32
+ map 'ruby_version', to: :ruby_version
33
+ map 'ruby_platform', to: :ruby_platform
34
+ map 'ruby_build_tag', to: :ruby_build_tag
35
+ end
36
+
37
+ def self.current_local(ruby_version: nil)
38
+ version = ruby_version || RUBY_VERSION
39
+
40
+ # Check for GitHub Actions runner platform
41
+ github_platform = ENV['GITHUB_RUNNER_PLATFORM']
42
+ if github_platform
43
+ os, arch = parse_github_platform(github_platform)
44
+ platform_string = "#{github_platform}-ruby-#{version}"
45
+ else
46
+ os = detect_os
47
+ arch = detect_arch
48
+ platform_string = "local-#{version}"
49
+ end
23
50
 
24
- def self.current_local
25
51
  new(
26
- platform_string: "local-#{RUBY_VERSION}",
52
+ platform_string: platform_string,
27
53
  kind: 'local',
28
- ruby_build_tag: RUBY_VERSION
54
+ os: os,
55
+ arch: arch,
56
+ ruby_version: version,
57
+ ruby_build_tag: version
29
58
  )
30
59
  end
31
60
 
@@ -54,6 +83,32 @@ module Serialbench
54
83
  'unknown'
55
84
  end
56
85
  end
86
+
87
+ def self.parse_github_platform(platform_name)
88
+ # Parse GitHub Actions runner platform names
89
+ # Examples: ubuntu-24.04, ubuntu-24.04-arm, macos-13, macos-15-intel,
90
+ # macos-14, macos-15, macos-26, windows-2022, windows-2025, windows-11-arm
91
+
92
+ case platform_name
93
+ when /^ubuntu.*-arm$/
94
+ ['linux', 'arm64']
95
+ when /^ubuntu/
96
+ ['linux', 'x86_64']
97
+ when /^macos-.*-intel$/
98
+ ['macos', 'x86_64']
99
+ when /^macos-(13)$/
100
+ ['macos', 'x86_64']
101
+ when /^macos-(14|15|26)$/
102
+ ['macos', 'arm64']
103
+ when /^windows.*-arm$/
104
+ ['windows', 'arm64']
105
+ when /^windows/
106
+ ['windows', 'x86_64']
107
+ else
108
+ # Fallback to automatic detection
109
+ [detect_os, detect_arch]
110
+ end
111
+ end
57
112
  end
58
113
  end
59
114
  end