springdocker 1.0.1__py3-none-any.whl
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.
- springdocker/__init__.py +9 -0
- springdocker/analyze.py +284 -0
- springdocker/benchmarks/__init__.py +2 -0
- springdocker/benchmarks/generate.py +104 -0
- springdocker/benchmarks/runner.py +343 -0
- springdocker/cli.py +289 -0
- springdocker/commands.py +388 -0
- springdocker/compare.py +138 -0
- springdocker/config.py +365 -0
- springdocker/dockerfile.py +332 -0
- springdocker/errors.py +16 -0
- springdocker/plugins.py +75 -0
- springdocker/project_detect.py +256 -0
- springdocker/regression.py +151 -0
- springdocker/services/__init__.py +2 -0
- springdocker/services/benchmark_service.py +124 -0
- springdocker/services/dockerfile_service.py +76 -0
- springdocker/services/project_service.py +37 -0
- springdocker-1.0.1.dist-info/METADATA +189 -0
- springdocker-1.0.1.dist-info/RECORD +23 -0
- springdocker-1.0.1.dist-info/WHEEL +5 -0
- springdocker-1.0.1.dist-info/entry_points.txt +2 -0
- springdocker-1.0.1.dist-info/top_level.txt +1 -0
springdocker/__init__.py
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""springdocker CLI package."""
|
|
2
|
+
|
|
3
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
4
|
+
|
|
5
|
+
__all__ = ["__version__"]
|
|
6
|
+
try:
|
|
7
|
+
__version__ = version("springdocker")
|
|
8
|
+
except PackageNotFoundError: # pragma: no cover - local source tree fallback
|
|
9
|
+
__version__ = "0.1.0"
|
springdocker/analyze.py
ADDED
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import csv
|
|
4
|
+
import json
|
|
5
|
+
import math
|
|
6
|
+
import statistics
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
REQUIRED_COLUMNS = {
|
|
11
|
+
"scenario",
|
|
12
|
+
"variant",
|
|
13
|
+
"build_ms",
|
|
14
|
+
"startup_ms",
|
|
15
|
+
"image_bytes",
|
|
16
|
+
"status",
|
|
17
|
+
}
|
|
18
|
+
OPTIONAL_COLUMNS = {"rss_bytes", "cpu_pct", "host", "docker_version", "run_profile"}
|
|
19
|
+
OPTIONAL_COLUMNS |= {
|
|
20
|
+
"gc_pause_ms",
|
|
21
|
+
"alloc_mb",
|
|
22
|
+
"startup_phase_boot_ms",
|
|
23
|
+
"startup_phase_context_ms",
|
|
24
|
+
"startup_phase_web_server_ms",
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass(frozen=True)
|
|
29
|
+
class VariantSummary:
|
|
30
|
+
scenario: str
|
|
31
|
+
variant: str
|
|
32
|
+
runs: int
|
|
33
|
+
build_avg_ms: float | None
|
|
34
|
+
startup_avg_ms: float | None
|
|
35
|
+
startup_p95_ms: float | None
|
|
36
|
+
image_mb_avg: float | None
|
|
37
|
+
success_rate_pct: float
|
|
38
|
+
build_stddev_ms: float | None = None
|
|
39
|
+
build_ci95_low_ms: float | None = None
|
|
40
|
+
build_ci95_high_ms: float | None = None
|
|
41
|
+
startup_p99_ms: float | None = None
|
|
42
|
+
startup_stddev_ms: float | None = None
|
|
43
|
+
startup_ci95_low_ms: float | None = None
|
|
44
|
+
startup_ci95_high_ms: float | None = None
|
|
45
|
+
gc_pause_ms_avg: float | None = None
|
|
46
|
+
alloc_mb_avg: float | None = None
|
|
47
|
+
startup_phase_boot_ms_avg: float | None = None
|
|
48
|
+
startup_phase_context_ms_avg: float | None = None
|
|
49
|
+
startup_phase_web_server_ms_avg: float | None = None
|
|
50
|
+
startup_phase_total_ms_avg: float | None = None
|
|
51
|
+
rss_mb_avg: float | None = None
|
|
52
|
+
cpu_pct_avg: float | None = None
|
|
53
|
+
host: str | None = None
|
|
54
|
+
docker_version: str | None = None
|
|
55
|
+
run_profile: str | None = None
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _to_int_or_none(value: str) -> int | None:
|
|
59
|
+
try:
|
|
60
|
+
return int(value)
|
|
61
|
+
except (TypeError, ValueError):
|
|
62
|
+
return None
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _to_float_or_none(value: str) -> float | None:
|
|
66
|
+
try:
|
|
67
|
+
return float(value)
|
|
68
|
+
except (TypeError, ValueError):
|
|
69
|
+
return None
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _p95(values: list[int]) -> float | None:
|
|
73
|
+
if not values:
|
|
74
|
+
return None
|
|
75
|
+
if len(values) == 1:
|
|
76
|
+
return float(values[0])
|
|
77
|
+
return statistics.quantiles(values, n=20)[18]
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _p99(values: list[int]) -> float | None:
|
|
81
|
+
if not values:
|
|
82
|
+
return None
|
|
83
|
+
if len(values) == 1:
|
|
84
|
+
return float(values[0])
|
|
85
|
+
return statistics.quantiles(values, n=100)[98]
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _stddev(values: list[int]) -> float | None:
|
|
89
|
+
if len(values) < 2:
|
|
90
|
+
return None
|
|
91
|
+
return statistics.stdev(values)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _ci95(values: list[int]) -> tuple[float | None, float | None]:
|
|
95
|
+
if len(values) < 2:
|
|
96
|
+
return None, None
|
|
97
|
+
mean = statistics.mean(values)
|
|
98
|
+
stddev = statistics.stdev(values)
|
|
99
|
+
margin = 1.96 * (stddev / math.sqrt(len(values)))
|
|
100
|
+
return mean - margin, mean + margin
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _mean_float(values: list[float]) -> float | None:
|
|
104
|
+
return statistics.mean(values) if values else None
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def summarize_csv(path: Path, scenario: str | None = None, variant: str | None = None) -> list[VariantSummary]:
|
|
108
|
+
with path.open(newline="", encoding="utf-8") as f:
|
|
109
|
+
reader = csv.DictReader(f)
|
|
110
|
+
fieldnames = set(reader.fieldnames or [])
|
|
111
|
+
missing = sorted(REQUIRED_COLUMNS - fieldnames)
|
|
112
|
+
if missing:
|
|
113
|
+
raise ValueError(f"CSV missing required columns: {', '.join(missing)}")
|
|
114
|
+
|
|
115
|
+
rows: list[dict[str, str]] = list(reader)
|
|
116
|
+
|
|
117
|
+
groups: dict[tuple[str, str], list[dict[str, str]]] = {}
|
|
118
|
+
for row in rows:
|
|
119
|
+
sc = row.get("scenario", "")
|
|
120
|
+
vr = row.get("variant", "")
|
|
121
|
+
if scenario and sc != scenario:
|
|
122
|
+
continue
|
|
123
|
+
if variant and vr != variant:
|
|
124
|
+
continue
|
|
125
|
+
groups.setdefault((sc, vr), []).append(row)
|
|
126
|
+
|
|
127
|
+
summaries: list[VariantSummary] = []
|
|
128
|
+
for (sc, vr), items in sorted(groups.items()):
|
|
129
|
+
build = [v for i in items if (v := _to_int_or_none(i.get("build_ms", ""))) is not None and v >= 0]
|
|
130
|
+
startup = [v for i in items if (v := _to_int_or_none(i.get("startup_ms", ""))) is not None and v >= 0]
|
|
131
|
+
image = [v for i in items if (v := _to_int_or_none(i.get("image_bytes", ""))) is not None and v >= 0]
|
|
132
|
+
rss: list[int] = []
|
|
133
|
+
for item in items:
|
|
134
|
+
rss_value = _to_int_or_none(item.get("rss_bytes", ""))
|
|
135
|
+
if rss_value is not None and rss_value >= 0:
|
|
136
|
+
rss.append(rss_value)
|
|
137
|
+
|
|
138
|
+
cpu: list[float] = []
|
|
139
|
+
for item in items:
|
|
140
|
+
cpu_value = _to_float_or_none(item.get("cpu_pct", ""))
|
|
141
|
+
if cpu_value is not None and cpu_value >= 0.0:
|
|
142
|
+
cpu.append(cpu_value)
|
|
143
|
+
|
|
144
|
+
gc_pause: list[float] = []
|
|
145
|
+
alloc: list[float] = []
|
|
146
|
+
phase_boot: list[float] = []
|
|
147
|
+
phase_context: list[float] = []
|
|
148
|
+
phase_web_server: list[float] = []
|
|
149
|
+
for item in items:
|
|
150
|
+
if (value := _to_float_or_none(item.get("gc_pause_ms", ""))) is not None and value >= 0.0:
|
|
151
|
+
gc_pause.append(value)
|
|
152
|
+
if (value := _to_float_or_none(item.get("alloc_mb", ""))) is not None and value >= 0.0:
|
|
153
|
+
alloc.append(value)
|
|
154
|
+
if (value := _to_float_or_none(item.get("startup_phase_boot_ms", ""))) is not None and value >= 0.0:
|
|
155
|
+
phase_boot.append(value)
|
|
156
|
+
if (value := _to_float_or_none(item.get("startup_phase_context_ms", ""))) is not None and value >= 0.0:
|
|
157
|
+
phase_context.append(value)
|
|
158
|
+
if (value := _to_float_or_none(item.get("startup_phase_web_server_ms", ""))) is not None and value >= 0.0:
|
|
159
|
+
phase_web_server.append(value)
|
|
160
|
+
|
|
161
|
+
ok = sum(1 for i in items if i.get("status") == "ok")
|
|
162
|
+
total = len(items)
|
|
163
|
+
first = items[0] if items else {}
|
|
164
|
+
build_ci95_low, build_ci95_high = _ci95(build)
|
|
165
|
+
startup_ci95_low, startup_ci95_high = _ci95(startup)
|
|
166
|
+
|
|
167
|
+
summaries.append(
|
|
168
|
+
VariantSummary(
|
|
169
|
+
scenario=sc,
|
|
170
|
+
variant=vr,
|
|
171
|
+
runs=total,
|
|
172
|
+
build_avg_ms=statistics.mean(build) if build else None,
|
|
173
|
+
build_stddev_ms=_stddev(build),
|
|
174
|
+
build_ci95_low_ms=build_ci95_low,
|
|
175
|
+
build_ci95_high_ms=build_ci95_high,
|
|
176
|
+
startup_avg_ms=statistics.mean(startup) if startup else None,
|
|
177
|
+
startup_p95_ms=_p95(startup),
|
|
178
|
+
startup_p99_ms=_p99(startup),
|
|
179
|
+
startup_stddev_ms=_stddev(startup),
|
|
180
|
+
startup_ci95_low_ms=startup_ci95_low,
|
|
181
|
+
startup_ci95_high_ms=startup_ci95_high,
|
|
182
|
+
gc_pause_ms_avg=_mean_float(gc_pause),
|
|
183
|
+
alloc_mb_avg=_mean_float(alloc),
|
|
184
|
+
startup_phase_boot_ms_avg=_mean_float(phase_boot),
|
|
185
|
+
startup_phase_context_ms_avg=_mean_float(phase_context),
|
|
186
|
+
startup_phase_web_server_ms_avg=_mean_float(phase_web_server),
|
|
187
|
+
startup_phase_total_ms_avg=(
|
|
188
|
+
(_mean_float(phase_boot) or 0.0)
|
|
189
|
+
+ (_mean_float(phase_context) or 0.0)
|
|
190
|
+
+ (_mean_float(phase_web_server) or 0.0)
|
|
191
|
+
if any([phase_boot, phase_context, phase_web_server])
|
|
192
|
+
else None
|
|
193
|
+
),
|
|
194
|
+
image_mb_avg=(statistics.mean(image) / (1024 * 1024)) if image else None,
|
|
195
|
+
rss_mb_avg=(statistics.mean(rss) / (1024 * 1024)) if rss else None,
|
|
196
|
+
cpu_pct_avg=statistics.mean(cpu) if cpu else None,
|
|
197
|
+
success_rate_pct=((ok / total) * 100.0) if total else 0.0,
|
|
198
|
+
host=first.get("host") or None,
|
|
199
|
+
docker_version=first.get("docker_version") or None,
|
|
200
|
+
run_profile=first.get("run_profile") or None,
|
|
201
|
+
)
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
return summaries
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def format_table(summaries: list[VariantSummary]) -> str:
|
|
208
|
+
lines = [
|
|
209
|
+
"| Scenario | Variant | Runs | Build avg (ms) | Build stddev (ms) | Build CI95 (ms) | Startup avg (ms) | Startup stddev (ms) | Startup p95 (ms) | Startup p99 (ms) | Startup CI95 (ms) | GC pause avg (ms) | Alloc avg (MB) | Boot avg (ms) | Context avg (ms) | Web server avg (ms) | Startup phase total (ms) | Image MB avg | RSS MB avg | CPU avg (%) | Success rate | Host | Docker | Profile |",
|
|
210
|
+
"|---|---|---:|---:|---:|---|---:|---:|---:|---:|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---|---|---|",
|
|
211
|
+
]
|
|
212
|
+
|
|
213
|
+
for s in summaries:
|
|
214
|
+
build_avg = f"{s.build_avg_ms:.1f}" if s.build_avg_ms is not None else "-"
|
|
215
|
+
build_stddev = f"{s.build_stddev_ms:.1f}" if s.build_stddev_ms is not None else "-"
|
|
216
|
+
build_ci95 = (
|
|
217
|
+
f"{s.build_ci95_low_ms:.1f}..{s.build_ci95_high_ms:.1f}"
|
|
218
|
+
if s.build_ci95_low_ms is not None and s.build_ci95_high_ms is not None
|
|
219
|
+
else "-"
|
|
220
|
+
)
|
|
221
|
+
startup_avg = f"{s.startup_avg_ms:.1f}" if s.startup_avg_ms is not None else "-"
|
|
222
|
+
startup_stddev = f"{s.startup_stddev_ms:.1f}" if s.startup_stddev_ms is not None else "-"
|
|
223
|
+
startup_p95 = f"{s.startup_p95_ms:.1f}" if s.startup_p95_ms is not None else "-"
|
|
224
|
+
startup_p99 = f"{s.startup_p99_ms:.1f}" if s.startup_p99_ms is not None else "-"
|
|
225
|
+
startup_ci95 = (
|
|
226
|
+
f"{s.startup_ci95_low_ms:.1f}..{s.startup_ci95_high_ms:.1f}"
|
|
227
|
+
if s.startup_ci95_low_ms is not None and s.startup_ci95_high_ms is not None
|
|
228
|
+
else "-"
|
|
229
|
+
)
|
|
230
|
+
gc_pause = f"{s.gc_pause_ms_avg:.1f}" if s.gc_pause_ms_avg is not None else "-"
|
|
231
|
+
alloc = f"{s.alloc_mb_avg:.2f}" if s.alloc_mb_avg is not None else "-"
|
|
232
|
+
boot = f"{s.startup_phase_boot_ms_avg:.1f}" if s.startup_phase_boot_ms_avg is not None else "-"
|
|
233
|
+
context = f"{s.startup_phase_context_ms_avg:.1f}" if s.startup_phase_context_ms_avg is not None else "-"
|
|
234
|
+
web_server = f"{s.startup_phase_web_server_ms_avg:.1f}" if s.startup_phase_web_server_ms_avg is not None else "-"
|
|
235
|
+
phase_total = (
|
|
236
|
+
f"{s.startup_phase_total_ms_avg:.1f}" if s.startup_phase_total_ms_avg is not None else "-"
|
|
237
|
+
)
|
|
238
|
+
image_mb = f"{s.image_mb_avg:.2f}" if s.image_mb_avg is not None else "-"
|
|
239
|
+
rss_mb = f"{s.rss_mb_avg:.2f}" if s.rss_mb_avg is not None else "-"
|
|
240
|
+
cpu_pct = f"{s.cpu_pct_avg:.1f}" if s.cpu_pct_avg is not None else "-"
|
|
241
|
+
lines.append(
|
|
242
|
+
f"| {s.scenario} | {s.variant} | {s.runs} | {build_avg} | {build_stddev} | {build_ci95} | "
|
|
243
|
+
f"{startup_avg} | {startup_stddev} | {startup_p95} | {startup_p99} | {startup_ci95} | "
|
|
244
|
+
f"{gc_pause} | {alloc} | {boot} | {context} | {web_server} | {phase_total} | "
|
|
245
|
+
f"{image_mb} | {rss_mb} | {cpu_pct} | {s.success_rate_pct:.1f}% | "
|
|
246
|
+
f"{s.host or '-'} | {s.docker_version or '-'} | {s.run_profile or '-'} |"
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
return "\n".join(lines)
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def format_json(summaries: list[VariantSummary]) -> str:
|
|
253
|
+
payload = [
|
|
254
|
+
{
|
|
255
|
+
"scenario": s.scenario,
|
|
256
|
+
"variant": s.variant,
|
|
257
|
+
"runs": s.runs,
|
|
258
|
+
"build_avg_ms": s.build_avg_ms,
|
|
259
|
+
"build_stddev_ms": s.build_stddev_ms,
|
|
260
|
+
"build_ci95_low_ms": s.build_ci95_low_ms,
|
|
261
|
+
"build_ci95_high_ms": s.build_ci95_high_ms,
|
|
262
|
+
"startup_avg_ms": s.startup_avg_ms,
|
|
263
|
+
"startup_p95_ms": s.startup_p95_ms,
|
|
264
|
+
"startup_p99_ms": s.startup_p99_ms,
|
|
265
|
+
"startup_stddev_ms": s.startup_stddev_ms,
|
|
266
|
+
"startup_ci95_low_ms": s.startup_ci95_low_ms,
|
|
267
|
+
"startup_ci95_high_ms": s.startup_ci95_high_ms,
|
|
268
|
+
"gc_pause_ms_avg": s.gc_pause_ms_avg,
|
|
269
|
+
"alloc_mb_avg": s.alloc_mb_avg,
|
|
270
|
+
"startup_phase_boot_ms_avg": s.startup_phase_boot_ms_avg,
|
|
271
|
+
"startup_phase_context_ms_avg": s.startup_phase_context_ms_avg,
|
|
272
|
+
"startup_phase_web_server_ms_avg": s.startup_phase_web_server_ms_avg,
|
|
273
|
+
"startup_phase_total_ms_avg": s.startup_phase_total_ms_avg,
|
|
274
|
+
"image_mb_avg": s.image_mb_avg,
|
|
275
|
+
"rss_mb_avg": s.rss_mb_avg,
|
|
276
|
+
"cpu_pct_avg": s.cpu_pct_avg,
|
|
277
|
+
"success_rate_pct": s.success_rate_pct,
|
|
278
|
+
"host": s.host,
|
|
279
|
+
"docker_version": s.docker_version,
|
|
280
|
+
"run_profile": s.run_profile,
|
|
281
|
+
}
|
|
282
|
+
for s in summaries
|
|
283
|
+
]
|
|
284
|
+
return json.dumps(payload, indent=2, sort_keys=True)
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from springdocker.dockerfile import DockerfileOptions, build_dockerfile
|
|
7
|
+
|
|
8
|
+
EXPECTED_CSV_HEADER = (
|
|
9
|
+
"date,scenario,variant,run,build_ms,image_bytes,startup_ms,status,notes,host,docker_version,run_profile,"
|
|
10
|
+
"gc_pause_ms,alloc_mb,startup_phase_boot_ms,startup_phase_context_ms,startup_phase_web_server_ms\n"
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass(frozen=True)
|
|
15
|
+
class ScenarioDefinition:
|
|
16
|
+
id: str
|
|
17
|
+
variants: tuple[tuple[str, DockerfileOptions], ...]
|
|
18
|
+
run_overrides: dict[str, int] | None = None
|
|
19
|
+
scenario_type: str = "standard"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def default_scenarios(build_tool: str, java_version: int) -> list[ScenarioDefinition]:
|
|
23
|
+
base = DockerfileOptions(build_tool=build_tool, java_version=java_version)
|
|
24
|
+
return [
|
|
25
|
+
ScenarioDefinition(
|
|
26
|
+
id="01-multi-stage-build-structure",
|
|
27
|
+
variants=(
|
|
28
|
+
("specialized-multi-stage", base),
|
|
29
|
+
("simple-two-stage", DockerfileOptions(build_tool=build_tool, java_version=java_version, use_buildkit_cache=False)),
|
|
30
|
+
),
|
|
31
|
+
),
|
|
32
|
+
ScenarioDefinition(
|
|
33
|
+
id="02-buildkit-gradle-cache",
|
|
34
|
+
variants=(
|
|
35
|
+
("with-buildkit-cache", base),
|
|
36
|
+
("without-buildkit-cache", DockerfileOptions(build_tool=build_tool, java_version=java_version, use_buildkit_cache=False)),
|
|
37
|
+
),
|
|
38
|
+
),
|
|
39
|
+
ScenarioDefinition(
|
|
40
|
+
id="03-custom-jre-jlink",
|
|
41
|
+
variants=(
|
|
42
|
+
("with-jlink-runtime", base),
|
|
43
|
+
("without-jlink-runtime", DockerfileOptions(build_tool=build_tool, java_version=java_version, use_jlink=False)),
|
|
44
|
+
),
|
|
45
|
+
),
|
|
46
|
+
ScenarioDefinition(
|
|
47
|
+
id="04-jep483-aot-cache",
|
|
48
|
+
variants=(
|
|
49
|
+
("with-aot-cache", base),
|
|
50
|
+
("without-aot-cache", DockerfileOptions(build_tool=build_tool, java_version=java_version, tuned_jvm_flags=False)),
|
|
51
|
+
),
|
|
52
|
+
run_overrides={"quick": 8, "full": 15},
|
|
53
|
+
),
|
|
54
|
+
ScenarioDefinition(
|
|
55
|
+
id="05-jvm-container-flags",
|
|
56
|
+
variants=(
|
|
57
|
+
("tuned-flags", base),
|
|
58
|
+
("defaults-like", DockerfileOptions(build_tool=build_tool, java_version=java_version, tuned_jvm_flags=False)),
|
|
59
|
+
),
|
|
60
|
+
),
|
|
61
|
+
ScenarioDefinition(
|
|
62
|
+
id="06-base-image-choice",
|
|
63
|
+
variants=(
|
|
64
|
+
("temurin-jre", DockerfileOptions(build_tool=build_tool, java_version=java_version, use_jlink=False)),
|
|
65
|
+
(
|
|
66
|
+
"distroless-nonroot",
|
|
67
|
+
DockerfileOptions(
|
|
68
|
+
build_tool=build_tool,
|
|
69
|
+
java_version=java_version,
|
|
70
|
+
use_jlink=False,
|
|
71
|
+
runtime_image="distroless",
|
|
72
|
+
),
|
|
73
|
+
),
|
|
74
|
+
),
|
|
75
|
+
),
|
|
76
|
+
ScenarioDefinition(
|
|
77
|
+
id="07-native-vs-jvm",
|
|
78
|
+
variants=(),
|
|
79
|
+
scenario_type="native",
|
|
80
|
+
),
|
|
81
|
+
]
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def generate_benchmark_assets(project_root: Path, build_tool: str, java_version: int) -> None:
|
|
85
|
+
bench_root = project_root / "benchmarks"
|
|
86
|
+
bench_root.mkdir(parents=True, exist_ok=True)
|
|
87
|
+
|
|
88
|
+
for scenario in default_scenarios(build_tool=build_tool, java_version=java_version):
|
|
89
|
+
scenario_dir = bench_root / scenario.id
|
|
90
|
+
variants_dir = scenario_dir / "variants"
|
|
91
|
+
results_dir = scenario_dir / "results"
|
|
92
|
+
scenario_dir.mkdir(parents=True, exist_ok=True)
|
|
93
|
+
variants_dir.mkdir(parents=True, exist_ok=True)
|
|
94
|
+
results_dir.mkdir(parents=True, exist_ok=True)
|
|
95
|
+
|
|
96
|
+
if scenario.scenario_type == "standard":
|
|
97
|
+
for name, opts in scenario.variants:
|
|
98
|
+
variant_dir = variants_dir / name
|
|
99
|
+
variant_dir.mkdir(parents=True, exist_ok=True)
|
|
100
|
+
(variant_dir / "Dockerfile").write_text(build_dockerfile(opts), encoding="utf-8")
|
|
101
|
+
|
|
102
|
+
csv = results_dir / "raw.csv"
|
|
103
|
+
if not csv.exists():
|
|
104
|
+
csv.write_text(EXPECTED_CSV_HEADER, encoding="utf-8")
|