xmi 0.6.1 → 0.6.2

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.
@@ -1,238 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "json"
4
- require "open3"
5
- require "tmpdir"
6
- require "fileutils"
7
-
8
- module PerformanceHelpers
9
- # ANSI color codes for terminal output
10
- CLEAR = "\e[0m"
11
- BOLD = "\e[1m"
12
- DIM = "\e[2m"
13
- CYAN = "\e[36m"
14
- GREEN = "\e[32m"
15
- YELLOW = "\e[33m"
16
- RED = "\e[31m"
17
- GRAY = "\e[90m"
18
- MAGENTA = "\e[35m"
19
-
20
- # Terminal formatting helpers
21
- module Term
22
- extend self
23
-
24
- HL = "─"
25
- VL = "│"
26
- TL = "┌"
27
- TR = "┐"
28
- BL = "└"
29
- BR = "┘"
30
-
31
- def header(title, color: PerformanceHelpers::CYAN)
32
- width = 78
33
- line = HL * width
34
- puts
35
- puts "#{color}#{TL}#{line}#{TR}#{CLEAR}"
36
- puts "#{color}#{VL}#{CLEAR} #{BOLD}#{color}#{title}#{CLEAR}#{' ' * (width - title.length - 4)}#{color}#{VL}#{CLEAR}"
37
- puts "#{color}#{BL}#{line}#{BR}#{CLEAR}"
38
- end
39
-
40
- def sep(char: HL, width: 78)
41
- puts "#{DIM}#{char * width}#{CLEAR}"
42
- end
43
- end
44
-
45
- module Base
46
- end
47
-
48
- module Current
49
- end
50
-
51
- class << self
52
- def load_into_namespace(module_obj, file_path)
53
- content = File.read(file_path)
54
- module_obj.module_eval(content, file_path)
55
- end
56
-
57
- def ruby_exec(cmd, env: {})
58
- Open3.capture3(env, cmd)
59
- end
60
-
61
- def current_branch
62
- stdout, = ruby_exec("git rev-parse --abbrev-ref HEAD")
63
- stdout.strip
64
- end
65
-
66
- # Clone base branch into a temp dir and return its path
67
- def clone_base_repo(base, performance_dir, script)
68
- puts "#{DIM}Cloning base #{base}...#{CLEAR}"
69
- safe_ref = base.gsub(/[^0-9A-Za-z._-]/, "-")
70
- clone_dir = File.join(performance_dir, "base-#{safe_ref}")
71
- FileUtils.rm_rf(clone_dir)
72
-
73
- repo_url, = ruby_exec("git config --get remote.origin.url")
74
- repo_url = repo_url.strip
75
-
76
- stdout, stderr, status = ruby_exec("git clone --branch #{safe_ref} --single-branch #{repo_url} #{clone_dir}")
77
- raise "git clone failed: #{stderr}\n#{stdout}" unless status.success?
78
-
79
- Dir.chdir(clone_dir) do
80
- stdout, stderr, status = ruby_exec("bundle install --quiet")
81
- raise "bundle install failed: #{stderr}\n#{stdout}" unless status.success?
82
-
83
- bench_copy_dir = File.join(clone_dir, "lib", "tasks")
84
- FileUtils.mkdir_p(bench_copy_dir)
85
- bench_copy = File.join(bench_copy_dir, "benchmark_runner.rb")
86
- File.write(bench_copy, File.read(script))
87
- load_into_namespace(Base, bench_copy)
88
- end
89
- end
90
-
91
- def run_benchmarks(base_runner, current_runner, threshold, all_base,
92
- all_current)
93
- base_results = base_runner.run_benchmarks
94
- curr_results = current_runner.run_benchmarks
95
-
96
- all_base.merge!(base_results)
97
- all_current.merge!(curr_results)
98
-
99
- # Collect comparison results
100
- comparison_rows = []
101
-
102
- curr_results.each do |label, result|
103
- base_result = base_results[label]
104
- cmp = compare_metrics(label, result, base_result, threshold)
105
- comparison_rows << cmp
106
- end
107
-
108
- print_comparison_table(comparison_rows, threshold)
109
- end
110
-
111
- def print_comparison_table(comparison_rows, threshold)
112
- rows = comparison_rows.map do |cmp|
113
- {
114
- benchmark: cmp[:label],
115
- base_ips: cmp[:base_ips]&.round(1),
116
- curr_ips: cmp[:curr_ips]&.round(1),
117
- change: cmp[:change] ? "#{(cmp[:change] * 100).round(1)}%" : "N/A",
118
- status: if cmp[:base_ips].nil?
119
- "NEW"
120
- elsif cmp[:change] < -threshold
121
- "REGRESSED"
122
- else
123
- "OK"
124
- end,
125
- }
126
- end
127
-
128
- return if rows.empty?
129
-
130
- puts " #{'Benchmark'.ljust(40)} #{'Base IPS'.rjust(12)} #{'Curr IPS'.rjust(12)} #{'Change'.rjust(10)} #{'Status'.rjust(10)}"
131
- puts " #{DIM}#{'─' * 86}#{CLEAR}"
132
-
133
- rows.each do |row|
134
- status_color = case row[:status]
135
- when "REGRESSED" then RED
136
- when "NEW" then YELLOW
137
- else GREEN
138
- end
139
- row[:status] == "REGRESSED" ? RED : DIM
140
-
141
- puts " #{row[:benchmark].ljust(40)} #{format('%-12.1f',
142
- row[:base_ips] || 0)} #{format('%-12.1f',
143
- row[:curr_ips] || 0)} #{format('%-10s', row[:change]).gsub('%',
144
- '%%')} #{status_color}#{row[:status].rjust(10)}#{CLEAR}"
145
- end
146
-
147
- puts
148
- end
149
-
150
- def compare_metrics(label, curr, base, threshold)
151
- unless base
152
- return { label: label, base_ips: nil, curr_ips: nil, change: nil,
153
- regressed: false }
154
- end
155
-
156
- base_ips = base.fetch(:lower)
157
- curr_ips = curr.fetch(:upper)
158
- change = (curr_ips - base_ips) / base_ips.to_f
159
-
160
- {
161
- label: label,
162
- base_ips: base_ips,
163
- curr_ips: curr_ips,
164
- change: change,
165
- regressed: change < -threshold,
166
- }
167
- end
168
-
169
- def summary_report(current_results, base_results, base, run_time, threshold)
170
- summary = {
171
- run_time: run_time,
172
- threshold: threshold,
173
- branch: current_branch,
174
- base: base,
175
- regressions: [],
176
- new_benchmarks: [],
177
- }
178
-
179
- current_results.each do |label, metrics|
180
- base_result = base_results[label]
181
- cmp = compare_metrics(label, metrics, base_result, threshold)
182
-
183
- # Track new benchmarks that don't exist in base
184
- if base_result.nil?
185
- summary[:new_benchmarks] << label
186
- next
187
- end
188
-
189
- next unless cmp[:regressed]
190
-
191
- summary[:regressions] << {
192
- label: label,
193
- base_ips: cmp[:base_ips],
194
- curr_ips: cmp[:curr_ips],
195
- delta_fraction: cmp[:change],
196
- }
197
- end
198
-
199
- log_regressions(summary[:regressions], threshold)
200
- log_new_benchmarks(summary[:new_benchmarks])
201
- summary
202
- end
203
-
204
- def log_new_benchmarks(new_benchmarks)
205
- return if new_benchmarks.empty?
206
-
207
- puts
208
- puts "#{YELLOW}🆕 New benchmarks (not in base branch):#{CLEAR}"
209
- new_benchmarks.each do |label|
210
- puts " • #{label}"
211
- end
212
- end
213
-
214
- def log_regressions(regressions, threshold)
215
- return if regressions.empty?
216
-
217
- puts
218
- puts "#{RED}⚠️ Performance Regressions Detected#{CLEAR}"
219
- puts "#{RED} (< -#{(threshold * 100).round(2)}% IPS)#{CLEAR}"
220
- puts
221
- regressions.each do |regression|
222
- delta = regression[:delta_fraction]
223
- base_ips = regression[:base_ips]
224
- curr_ips = regression[:curr_ips]
225
-
226
- delta_str = delta ? format("%+0.2f%%", delta * 100) : "N/A"
227
- base_str = base_ips ? format("%.2f", base_ips) : "N/A"
228
- curr_str = curr_ips ? format("%.2f", curr_ips) : "N/A"
229
-
230
- puts " #{BOLD}#{regression[:label]}#{CLEAR}"
231
- puts " #{GRAY}base: #{base_str} IPS#{CLEAR}"
232
- puts " #{RED}curr: #{curr_str} IPS#{CLEAR}"
233
- puts " #{RED}change: #{delta_str}#{CLEAR}"
234
- puts
235
- end
236
- end
237
- end
238
- end