Skip to content

Commit c26929b

Browse files
committed
fix(nextflow): move stage CLIs into module bins
1 parent 662de62 commit c26929b

6 files changed

Lines changed: 135 additions & 51 deletions

File tree

modules/local/aggregate_benchmark_report_data/AGENTS.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,14 @@ Purpose
55

66
Owns
77
- `AGGREGATE_BENCHMARK_REPORT_DATA` in `main.nf`
8-
- Python aggregation logic in `bin/benchmark_report_aggregate.py`
8+
- Module-local CLI entrypoint in `bin/benchmark_report.py`
9+
- Shared aggregation logic in repo-root `bin/benchmark_report_aggregate.py`
910
- Stage-scoped tests under `tests/`
1011

12+
Run directly
13+
- `nextflow run modules/local/aggregate_benchmark_report_data/main.nf -profile docker,arm --jsonl_bundle <jsonl_bundle_dir>`
14+
- The direct `nextflow run modules/local/...` path depends on the module-local `bin/benchmark_report.py` shim/CLI; keep that invocation shape working if you refactor the stage.
15+
1116
Inputs
1217
- `jsonl_bundle/` from `normalize_benchmark_jsonl`
1318

Lines changed: 40 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,52 @@
11
#!/usr/bin/env python3
2-
"""Module-local shim for direct `nextflow run modules/local/...` execution.
2+
"""Aggregate benchmark JSONL datasets into report_data.json.
33
4-
This delegates to the repo-root benchmark_report.py so the shared CLI and
5-
assets/template lookup continue to work from the canonical implementation.
4+
This module-local entrypoint is the actual stage CLI used by
5+
`nextflow run modules/local/aggregate_benchmark_report_data/main.nf`.
66
"""
77

88
from __future__ import annotations
99

10-
import runpy
10+
import argparse
1111
import sys
1212
from pathlib import Path
1313

1414

15-
def _find_repo_root() -> Path:
16-
here = Path(__file__).resolve()
17-
for parent in here.parents:
18-
candidate = parent / "bin" / "benchmark_report.py"
19-
if candidate.exists() and candidate != here:
20-
return parent
21-
raise RuntimeError(f"Could not locate repo root from {here}")
15+
def _add_repo_bin_to_path() -> None:
16+
for parent in Path(__file__).resolve().parents:
17+
candidate = parent / "bin" / "benchmark_report_aggregate.py"
18+
if candidate.exists():
19+
sys.path.insert(0, str(parent / "bin"))
20+
return
21+
raise RuntimeError("Unable to locate the repository root for benchmark report helpers")
2222

2323

24-
REPO_ROOT = _find_repo_root()
25-
REPO_BIN = REPO_ROOT / "bin"
26-
sys.path.insert(0, str(REPO_BIN))
27-
sys.argv[0] = str(REPO_BIN / "benchmark_report.py")
28-
runpy.run_path(str(REPO_BIN / "benchmark_report.py"), run_name="__main__")
24+
_add_repo_bin_to_path()
25+
26+
from benchmark_report_aggregate import aggregate_report_data # noqa: E402
27+
28+
29+
def _build_parser() -> argparse.ArgumentParser:
30+
parser = argparse.ArgumentParser(prog="benchmark_report.py", description=__doc__)
31+
subparsers = parser.add_subparsers(dest="command", required=True)
32+
33+
p = subparsers.add_parser("aggregate-report-data", help="Aggregate JSONL into report_data.json")
34+
p.add_argument("--jsonl-dir", type=Path, required=True, help="Directory containing JSONL bundle")
35+
p.add_argument(
36+
"--output",
37+
type=Path,
38+
default=Path("report_data.json"),
39+
help="Output report_data.json path",
40+
)
41+
return parser
42+
43+
44+
def main(argv: list[str] | None = None) -> None:
45+
args = _build_parser().parse_args(argv)
46+
if args.command == "aggregate-report-data":
47+
aggregate_report_data(jsonl_dir=args.jsonl_dir, output=args.output)
48+
print(f"Report data written to {args.output}")
49+
50+
51+
if __name__ == "__main__":
52+
main()

modules/local/normalize_benchmark_jsonl/AGENTS.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,14 @@ Purpose
55

66
Owns
77
- `NORMALIZE_BENCHMARK_JSONL` in `main.nf`
8-
- Python stage logic in `bin/benchmark_report_normalize.py`
8+
- Module-local CLI entrypoint in `bin/benchmark_report.py`
9+
- Shared normalization logic in repo-root `bin/benchmark_report_normalize.py`
910
- Stage-scoped tests under `tests/`
1011

12+
Run directly
13+
- `nextflow run modules/local/normalize_benchmark_jsonl/main.nf -profile docker,arm --data_dir <dir> --benchmark_aws_cur_report <parquet-or-NO_FILE>`
14+
- The direct `nextflow run modules/local/...` path depends on the module-local `bin/benchmark_report.py` shim/CLI; keep that invocation shape working if you refactor the stage.
15+
1116
Inputs
1217
- `data_dir/` of per-run JSON payloads
1318
- optional CUR parquet
Lines changed: 40 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,52 @@
11
#!/usr/bin/env python3
2-
"""Module-local shim for direct `nextflow run modules/local/...` execution.
2+
"""Normalize raw benchmark run JSON files into JSONL datasets.
33
4-
This delegates to the repo-root benchmark_report.py so the shared CLI and
5-
assets/template lookup continue to work from the canonical implementation.
4+
This module-local entrypoint is the actual stage CLI used by
5+
`nextflow run modules/local/normalize_benchmark_jsonl/main.nf`.
66
"""
77

88
from __future__ import annotations
99

10-
import runpy
10+
import argparse
1111
import sys
1212
from pathlib import Path
1313

1414

15-
def _find_repo_root() -> Path:
16-
here = Path(__file__).resolve()
17-
for parent in here.parents:
18-
candidate = parent / "bin" / "benchmark_report.py"
19-
if candidate.exists() and candidate != here:
20-
return parent
21-
raise RuntimeError(f"Could not locate repo root from {here}")
15+
def _add_repo_bin_to_path() -> None:
16+
for parent in Path(__file__).resolve().parents:
17+
candidate = parent / "bin" / "benchmark_report_normalize.py"
18+
if candidate.exists():
19+
sys.path.insert(0, str(parent / "bin"))
20+
return
21+
raise RuntimeError("Unable to locate the repository root for benchmark report helpers")
2222

2323

24-
REPO_ROOT = _find_repo_root()
25-
REPO_BIN = REPO_ROOT / "bin"
26-
sys.path.insert(0, str(REPO_BIN))
27-
sys.argv[0] = str(REPO_BIN / "benchmark_report.py")
28-
runpy.run_path(str(REPO_BIN / "benchmark_report.py"), run_name="__main__")
24+
_add_repo_bin_to_path()
25+
26+
from benchmark_report_normalize import normalize_jsonl # noqa: E402
27+
28+
29+
def _build_parser() -> argparse.ArgumentParser:
30+
parser = argparse.ArgumentParser(prog="benchmark_report.py", description=__doc__)
31+
subparsers = parser.add_subparsers(dest="command", required=True)
32+
33+
p = subparsers.add_parser("normalize-jsonl", help="Normalize raw run JSON into JSONL files")
34+
p.add_argument("--data-dir", type=Path, required=True, help="Directory containing run JSON files")
35+
p.add_argument(
36+
"--output-dir",
37+
type=Path,
38+
default=Path("jsonl_bundle"),
39+
help="Output JSONL bundle directory",
40+
)
41+
p.add_argument("--costs", type=Path, default=None, help="Optional AWS CUR parquet file")
42+
return parser
43+
44+
45+
def main(argv: list[str] | None = None) -> None:
46+
args = _build_parser().parse_args(argv)
47+
if args.command == "normalize-jsonl":
48+
normalize_jsonl(data_dir=args.data_dir, output_dir=args.output_dir, costs_parquet=args.costs)
49+
50+
51+
if __name__ == "__main__":
52+
main()

modules/local/render_benchmark_report/AGENTS.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,14 @@ Purpose
55

66
Owns
77
- `RENDER_BENCHMARK_REPORT` in `main.nf`
8-
- Python render logic in `bin/benchmark_report_render.py`
8+
- Module-local CLI entrypoint in `bin/benchmark_report.py`
9+
- Shared render logic in repo-root `bin/benchmark_report_render.py`
910
- Stage-scoped tests under `tests/`
1011

12+
Run directly
13+
- `nextflow run modules/local/render_benchmark_report/main.nf -profile docker,arm --report_data_json <report_data.json> --brand_yml <brand.yml-or-NO_FILE> --logo_svg <logo.svg-or-NO_FILE>`
14+
- The direct `nextflow run modules/local/...` path depends on the module-local `bin/benchmark_report.py` shim/CLI; keep that invocation shape working if you refactor the stage.
15+
1116
Inputs
1217
- `report_data.json`
1318
- optional `brand.yml`
Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,49 @@
11
#!/usr/bin/env python3
2-
"""Module-local shim for direct `nextflow run modules/local/...` execution.
2+
"""Render benchmark report HTML from report_data.json.
33
4-
This delegates to the repo-root benchmark_report.py so the shared CLI and
5-
assets/template lookup continue to work from the canonical implementation.
4+
This module-local entrypoint is the actual stage CLI used by
5+
`nextflow run modules/local/render_benchmark_report/main.nf`.
66
"""
77

88
from __future__ import annotations
99

10-
import runpy
10+
import argparse
1111
import sys
1212
from pathlib import Path
1313

1414

15-
def _find_repo_root() -> Path:
16-
here = Path(__file__).resolve()
17-
for parent in here.parents:
18-
candidate = parent / "bin" / "benchmark_report.py"
19-
if candidate.exists() and candidate != here:
20-
return parent
21-
raise RuntimeError(f"Could not locate repo root from {here}")
15+
def _add_repo_bin_to_path() -> None:
16+
for parent in Path(__file__).resolve().parents:
17+
candidate = parent / "bin" / "benchmark_report_render.py"
18+
if candidate.exists():
19+
sys.path.insert(0, str(parent / "bin"))
20+
return
21+
raise RuntimeError("Unable to locate the repository root for benchmark report helpers")
2222

2323

24-
REPO_ROOT = _find_repo_root()
25-
REPO_BIN = REPO_ROOT / "bin"
26-
sys.path.insert(0, str(REPO_BIN))
27-
sys.argv[0] = str(REPO_BIN / "benchmark_report.py")
28-
runpy.run_path(str(REPO_BIN / "benchmark_report.py"), run_name="__main__")
24+
_add_repo_bin_to_path()
25+
26+
from benchmark_report_render import render_report_from_json # noqa: E402
27+
28+
29+
def _build_parser() -> argparse.ArgumentParser:
30+
parser = argparse.ArgumentParser(prog="benchmark_report.py", description=__doc__)
31+
subparsers = parser.add_subparsers(dest="command", required=True)
32+
33+
p = subparsers.add_parser("render-html", help="Render report_data.json to HTML")
34+
p.add_argument("--data", type=Path, required=True, help="report_data.json path")
35+
p.add_argument("--output", type=Path, default=Path("benchmark_report.html"), help="Output HTML file")
36+
p.add_argument("--brand", type=Path, default=None, help="Brand YAML file")
37+
p.add_argument("--logo", type=Path, default=None, help="SVG logo file")
38+
return parser
39+
40+
41+
def main(argv: list[str] | None = None) -> None:
42+
args = _build_parser().parse_args(argv)
43+
if args.command == "render-html":
44+
render_report_from_json(report_data_path=args.data, output=args.output, brand_path=args.brand, logo_path=args.logo)
45+
print(f"Report written to {args.output}")
46+
47+
48+
if __name__ == "__main__":
49+
main()

0 commit comments

Comments
 (0)