|
| 1 | +# Local CI-equivalent targets. Mirrors the suite of GitHub Actions |
| 2 | +# workflows in .github/workflows/ so that `make check-all` matches the |
| 3 | +# checks a PR will run on CI. |
| 4 | +# |
| 5 | +# make help - list targets |
| 6 | +# |
| 7 | +# === Fast (seconds) === |
| 8 | +# make lint - black --check + flake8 (tests.yml :lint) |
| 9 | +# make format - apply black formatting in-place |
| 10 | +# make test-py - pytest with 100% coverage (tests.yml :test) |
| 11 | +# make test-js - JS GP sync test (js-sync.yml) |
| 12 | +# make test-notebook-fmt - nbformat validation (notebooks.yml :notebook-lint) |
| 13 | +# |
| 14 | +# === Slow (minutes) === |
| 15 | +# make test-notebooks - execute every notebook (notebooks.yml :mode-*) |
| 16 | +# make test-e2e - Playwright desktop+mobile (e2e.yml) |
| 17 | +# make test-lighthouse - Lighthouse CI (lighthouse.yml) |
| 18 | +# |
| 19 | +# === Aggregates === |
| 20 | +# make test - fast tests (py + js + notebook-fmt) |
| 21 | +# make check - lint + test (recommended pre-commit gate) |
| 22 | +# make check-all - lint + every test (full local CI parity) |
| 23 | +# |
| 24 | +# Most targets need extras installed: |
| 25 | +# pip install -e ".[dev]" # lint, test-py |
| 26 | +# pip install -e ".[notebooks]" # test-notebook-fmt, test-notebooks |
| 27 | +# npm ci # test-js, test-e2e, test-lighthouse |
| 28 | +# npx playwright install --with-deps chromium # test-e2e |
| 29 | + |
| 30 | +PYTHON ?= python |
| 31 | + |
| 32 | +.PHONY: help \ |
| 33 | + lint format \ |
| 34 | + test-py test-js test-notebook-fmt test-notebooks \ |
| 35 | + test-e2e test-lighthouse \ |
| 36 | + test check check-all |
| 37 | + |
| 38 | +help: |
| 39 | + @echo "Local CI-equivalent targets (see Makefile header for full list):" |
| 40 | + @echo "" |
| 41 | + @echo " Fast:" |
| 42 | + @echo " make lint - black --check + flake8" |
| 43 | + @echo " make format - apply black formatting in-place" |
| 44 | + @echo " make test-py - pytest with 100% coverage gate" |
| 45 | + @echo " make test-js - JS GP sync test" |
| 46 | + @echo " make test-notebook-fmt - nbformat validation" |
| 47 | + @echo "" |
| 48 | + @echo " Slow:" |
| 49 | + @echo " make test-notebooks - execute every notebook" |
| 50 | + @echo " make test-e2e - Playwright (desktop + mobile)" |
| 51 | + @echo " make test-lighthouse - Lighthouse CI" |
| 52 | + @echo "" |
| 53 | + @echo " Aggregates:" |
| 54 | + @echo " make test - fast tests" |
| 55 | + @echo " make check - lint + test (recommended)" |
| 56 | + @echo " make check-all - lint + every test (full CI parity)" |
| 57 | + |
| 58 | +# Tracked Python files. CI's `black --check --diff .` only sees files |
| 59 | +# in the checked-out commit, so locally we must scope to tracked files |
| 60 | +# too — otherwise an untracked work-in-progress script in the working |
| 61 | +# tree would block `make lint` even though it can't fail the PR. Falls |
| 62 | +# back from `sl files` (Sapling) to `git ls-files` (vanilla git). |
| 63 | +TRACKED_PY := $(shell (sl files 2>/dev/null || git ls-files) | grep -E '\.py$$') |
| 64 | + |
| 65 | +# --- Lint ----------------------------------------------------------- |
| 66 | +# Mirrors .github/workflows/tests.yml :lint, scoped to TRACKED_PY so |
| 67 | +# untracked working-tree files don't poison the result. Black version |
| 68 | +# is pinned via pyproject.toml's [project.optional-dependencies].dev |
| 69 | +# so the formatter output is bit-for-bit identical to CI. |
| 70 | +lint: |
| 71 | + $(PYTHON) -m black --check --diff $(TRACKED_PY) |
| 72 | + $(PYTHON) -m flake8 $(TRACKED_PY) --count --select=E9,F63,F7,F82 --show-source --statistics |
| 73 | + $(PYTHON) -m flake8 $(TRACKED_PY) --count --exit-zero --statistics |
| 74 | + |
| 75 | +format: |
| 76 | + $(PYTHON) -m black $(TRACKED_PY) |
| 77 | + |
| 78 | +# --- Python unit tests --------------------------------------------- |
| 79 | +# Mirrors .github/workflows/tests.yml :test. We drop --cov-report=xml |
| 80 | +# because we don't need the coverage.xml artefact locally. |
| 81 | +test-py: |
| 82 | + $(PYTHON) -m pytest test/ -v --tb=short --cov=boxcrete --cov-report=term-missing --cov-fail-under=100 |
| 83 | + |
| 84 | +# --- JS GP sync test ------------------------------------------------ |
| 85 | +# Mirrors .github/workflows/js-sync.yml. Verifies docs/gp.mjs predicts |
| 86 | +# the same values as the Python reference for the committed model. |
| 87 | +test-js: |
| 88 | + node test/test_js_gp.mjs |
| 89 | + |
| 90 | +# --- Notebook format validation ------------------------------------ |
| 91 | +# Mirrors .github/workflows/notebooks.yml :notebook-lint. Just validates |
| 92 | +# the JSON; does not execute the notebooks. |
| 93 | +test-notebook-fmt: |
| 94 | + $(PYTHON) -c "import nbformat, pathlib; \ |
| 95 | +nbs = sorted(pathlib.Path('notebooks').glob('*.ipynb')); \ |
| 96 | +[(nbformat.read(open(p), as_version=4), print(f'OK {p}')) for p in nbs]; \ |
| 97 | +print(f'Validated {len(nbs)} notebook(s).')" |
| 98 | + |
| 99 | +# --- Notebook execution -------------------------------------------- |
| 100 | +# Mirrors .github/workflows/notebooks.yml :mode-{dependent,independent}. |
| 101 | +# Slow: each notebook runs nbconvert with a 600s timeout. |
| 102 | +# Mode-dependent notebooks are executed once per (mode, include-cost) |
| 103 | +# combination, matching the CI matrix. |
| 104 | +# |
| 105 | +# Unlike CI (which writes --inplace and uploads the executed notebook as |
| 106 | +# an artifact), we redirect output to a temp dir so the local working |
| 107 | +# copy isn't dirtied by `make` runs. |
| 108 | +NOTEBOOK_OUT := $(CURDIR)/.notebook-runs |
| 109 | +NBCONVERT := $(PYTHON) -m jupyter nbconvert --to notebook --execute \ |
| 110 | + --output-dir=$(NOTEBOOK_OUT) \ |
| 111 | + --ExecutePreprocessor.timeout=600 \ |
| 112 | + --ExecutePreprocessor.kernel_name=python3 |
| 113 | +test-notebooks: |
| 114 | + @mkdir -p $(NOTEBOOK_OUT) |
| 115 | + BOXCRETE_OPTIMIZATION_MODE=concrete BOXCRETE_INCLUDE_COST=false \ |
| 116 | + $(NBCONVERT) --output=mode_concrete_cost_false.ipynb \ |
| 117 | + notebooks/prediction_and_optimization_tutorial.ipynb |
| 118 | + BOXCRETE_OPTIMIZATION_MODE=concrete BOXCRETE_INCLUDE_COST=true \ |
| 119 | + $(NBCONVERT) --output=mode_concrete_cost_true.ipynb \ |
| 120 | + notebooks/prediction_and_optimization_tutorial.ipynb |
| 121 | + BOXCRETE_OPTIMIZATION_MODE=mortar BOXCRETE_INCLUDE_COST=false \ |
| 122 | + $(NBCONVERT) --output=mode_mortar_cost_false.ipynb \ |
| 123 | + notebooks/prediction_and_optimization_tutorial.ipynb |
| 124 | + BOXCRETE_OPTIMIZATION_MODE=mortar BOXCRETE_INCLUDE_COST=true \ |
| 125 | + $(NBCONVERT) --output=mode_mortar_cost_true.ipynb \ |
| 126 | + notebooks/prediction_and_optimization_tutorial.ipynb |
| 127 | + @for nb in notebooks/*.ipynb; do \ |
| 128 | + case "$$nb" in \ |
| 129 | + notebooks/prediction_and_optimization_tutorial.ipynb) ;; \ |
| 130 | + *) echo "=== Executing $$nb ==="; \ |
| 131 | + $(NBCONVERT) "$$nb" ;; \ |
| 132 | + esac \ |
| 133 | + done |
| 134 | + |
| 135 | +# --- E2E (Playwright) ---------------------------------------------- |
| 136 | +# Mirrors .github/workflows/e2e.yml. Requires `npm ci` + a one-time |
| 137 | +# `npx playwright install --with-deps chromium` to set up browsers. |
| 138 | +test-e2e: |
| 139 | + npx playwright test --project=desktop |
| 140 | + npx playwright test --project=mobile |
| 141 | + |
| 142 | +# --- Lighthouse CI ------------------------------------------------- |
| 143 | +# Mirrors .github/workflows/lighthouse.yml. |
| 144 | +test-lighthouse: |
| 145 | + npx lhci autorun |
| 146 | + |
| 147 | +# --- Aggregates ---------------------------------------------------- |
| 148 | +test: test-py test-js test-notebook-fmt |
| 149 | +check: lint test |
| 150 | +check-all: lint test-py test-js test-notebook-fmt test-notebooks test-e2e test-lighthouse |
0 commit comments