Repository navigation
build(make): align make docs with CI, stop re-bootstrapping, run mypy once - #831
Conversation
Every Make target depends on bootstrap, which ran `pip install --upgrade pip` and `pip install hatch` each time, and `make check` ran `hatch run lint` (already style plus typecheck) and then `make typecheck` again. With stub pip and Hatch programs, one `make check-all` made 12 pip calls and two type-check runs. bootstrap now creates `.venv` only while it is missing and installs Hatch only while `.venv/bin/hatch` is not an executable regular file; it never upgrades pip. `check` is `lint`, so the type check runs once. `PYTHON` (default python3) selects the interpreter that creates `.venv`, and `make install-ci PYTHON=<interpreter>` installs `.[dev]` into exactly that interpreter, never through Hatch, and refuses Python older than 3.11. `check-all` and the security targets are unchanged. tests/test_make_contract.py asserts the expansion with `make -n` and runs the real recipes against stub programs on a PATH that holds nothing else. Refs #811 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
`make docs` ran `mkdocs build`, while the CI docs gate runs `mkdocs build --strict`, so a broken link built with a warning and exit 0. Its CI branch also hid pip's output and errors (`> /dev/null 2>&1`, `;` between the steps) and installed `mkdocstrings[python]<1.0`, which resolves to a mkdocstrings that cannot render api.md (`Highlight.__init__() missing 1 required positional argument: 'md'`), so `CI=1 make docs` never built the docs at all. Both branches now run `mkdocs build --strict`. The CI branch installs `-e ".[docs]"`, the extra the CI gate installs, so the dependency pins live only in pyproject.toml, and it stops with pip's own error when the install fails instead of building with whatever was already installed. No target upgrades pip any more, and a test reads the Makefile to keep it that way. Refs #811 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
`make doctor` could not fail for a missing Hatch or func (`|| echo`): it failed only by accident, when `.venv/bin/python` did not exist, and its `hatch env run pip list` creates the Hatch environment when it is missing, so the report wrote. doctor now installs and writes nothing. It requires a Python 3.11 or newer on PATH and the `.venv/bin/hatch` this Makefile runs, each counted only as an executable regular file, and exits non-zero with a `MISSING` line when either is absent. func and azurite are listed as optional, because only a local Functions host needs them. The old output (Python version, pip list, func version, pre-commit hook state) is gone; nothing in the repository parses it. Refs #811 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…ingle type check The command tables still described `make check` as lint plus typecheck, `make docs` as a plain MkDocs build and `make doctor` as an environment dump. They now say what each target does after this series, and the development guide gains two short paragraphs: when `make install` acts and what `PYTHON` selects, and what `make doctor` requires, what it treats as optional and the one thing it cannot see (a Python 3.11 that only Hatch manages). AGENTS.md and CONTRIBUTING.md are policy-bearing files and stay untouched; nothing they say becomes wrong. Refs #811 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
The contract file had grown to 369 lines over the series. The helpers now share one `make` runner, the stub interpreter only answers the version probes the Makefile runs, related cases are merged and the three-line given/when/then comments are gone in favor of blank-line blocks. The virtual-environment case now reads the expansion of `make -n bootstrap` instead of stubbing `-m venv`, and also asserts that `python3` stays the default interpreter. Every one of 43 one-line mutations of the Makefile (bootstrap, check, check-all, docs, doctor and install-ci) still turns at least one test red. Refs #811 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
|
R3 authorization for head The maintainer delegated the merge decision to an Oracle review. The Oracle (AI) reviewed this change at head |
Summary
Every Make target depends on
bootstrap, which ranpip install --upgrade pipandpip install hatcheach time, andmake checkranhatch run lint(alreadystyleplustypecheck) and thenmake typecheckagain. With stubpipand Hatch programs onorigin/main(9127b17), onemake checkmade 6 pip calls and onemake check-allmade 12, and each ran the type-check script twice.make doctorexited 0 with Hatch andfuncmissing (|| echo); it failed only by accident, with exit 2, when.venv/bin/pythondid not exist.make docsranmkdocs buildwithout the--strictthe CI docs gate uses, so a broken link built with a warning and exit 0. ItsCIbranch also hid pip's output and errors (> /dev/null 2>&1,;between the steps) and pinnedmkdocstrings[python]<1.0, which cannot renderapi.md(Highlight.__init__() missing 1 required positional argument: 'md'), soCI=1 make docsnever built the docs at all.After this PR a
make check-allwith Hatch present makes 0 pip calls and runs the type check once; from nothing it makes one (pip install hatch).make doctoris a read-only check that fails when a required tool is missing,make docsbuilds like the CI docs gate (mkdocs build --strictwith thedocsextra) in both branches, andmake install-ci PYTHON=<interpreter>installs into the interpreter you choose.Makefile, tests and two docs pages only: no workflow,
src/,tools/,scripts/or policy file changes, andcheck-alland thesecurity-*targets are untouched. This adapts the Makefile fix already merged in azure-functions-doctor-python#636 and azure-functions-scaffold-python#462 to this repository's targets.Changes
Makefilebootstrap: creates.venvonly while it is missing and installs Hatch only while$(HATCH)is not an executable regular file (-xalone also accepts a directory). It never upgrades pip, and--disable-pip-version-check --quietkeeps pip's "run--upgrade pip" notice off the first install. It prints nothing when there is nothing to do (it printed two lines per target).PYTHON ?= python3replacesPYTHON := $(VENV_DIR)/bin/python, whose only user was the olddoctor. It is the interpreter that creates.venv(a hard-codedpython3before, same default) and the oneinstall-ciinstalls into. Hatch still provisions its own default environment (Python 3.11).check: lint(waslintthentypecheck):hatch run lintisstylefollowed bytypecheck.check-allis not edited; it runs the type check once becausecheckdoes.install-ci: refuses an interpreter older than Python 3.11, then$(PYTHON) -m pip install -e ".[dev]", the extra the workflows install inline today. It has noensure-hatch, so it never goes through Hatch's default environment (pinned to Python 3.11) and a CI matrix cell keeps its own interpreter.docs:mkdocs build --strictin both branches. TheCIbranch installs-e ".[docs]", the extra the CI docs gate installs, instead of three pins that had drifted (mkdocstrings[python]<1.0against the extra's<2.0), joins its steps with&&and no longer hides pip's output or errors. The local branch joinsensure-hatchand the build with&&. Thepip install --upgrade pipline is gone.doctor: read-only. A Python 3.11 or newer (the first ofpython3.14...python3.11,python3onPATHthat passes the version probe) and$(HATCH)are required: each printsok ...orMISSING ... (required...), and any MISSING makes the target fail (recipeexit 1, make exits 2).funcandazuriteare listedokormissing ... (optional ...)and never affect the status, because only a local Functions host needs them. A tool counts only ifcommand -vresolves it to an executable regular file (onehavehelper), because dash's barecommand -vaccepts any existing path and-xalone is also true for a searchable directory. The old output (Python version,pip list,func --version, pre-commit hook state) is gone; nothing in the repository parses it (grep).tests/test_make_contract.py(new, 21 cases):make -nexpansions plus the real recipes run in a scratch directory whosePATHholds only stub programs that log their arguments, so nothing is installed, no network is used and no real tool can run. Hatch scripts are expanded recursively, because the second type check hid inside Hatch'slintscript, not in make's output. It also asserts thatcheck-allstill contains each workflow lint and the Bandit evidence step exactly once.docs/development.md,docs/contributing.md: the command tables saidmake checkis "lint + typecheck",make docs"Build the MkDocs site" andmake doctor"Show environment diagnostic info". Updated, plusinstall-ciand two short paragraphs in the development guide (whenmake installacts and whatPYTHONselects; whatmake doctorrequires and the one thing it cannot see, a Python 3.11 that only Hatch manages).Net diff: 4 files, +344/-27 (the test file +279, the Makefile +46/-22, the two docs pages +19/-5). Nothing under
src examples demo scripts toolschanges.Behavior changes to know about:
make doctor's output and exit status change (above). Run beforemake installit fails on.venv/bin/hatchand saysmake installcreates it; that is the Hatch this Makefile runs, not one onPATH..venv's pip is no longer upgraded; it keeps the version bundled with the interpreter (22.0.2 forpython33.10.12 here, 24.0 for 3.11.15). Hatch installs with both.make docsfails on any MkDocs warning, and itsCIbranch now installs thedocsextra (needs the build backend and network, like the CI docs gate).PYTHONselects the interpreter that creates.venv.Verification
Environment unless stated: Linux, GNU Make 4.3,
/bin/sh= dash, Python 3.11.15 for the test runs,python3= 3.10.12 (pip 22.0.2), Hatch 1.18.1, ruff 0.17.0, mypy 2.4.0, MkDocs 1.6.1. "main" is unmodifiedorigin/main9127b17; the fresh clones are of this branch at297a0c4.Stub-pip call counts.
make VENV_DIR=<stub>on a scratch copy of the Makefile with a fake venv whosepip,hatchandpython3 -m venvonly append their arguments to a log (nothing real runs;CI=1soinstallskips the pre-commit hook):make ...check--upgrade pip, 3install hatch)hatch run lint+hatch run typecheck)checkinstall ... hatch)checkpython3 -m venv-m venvcheck-allcheck-allcheck-all-m venv-m venvlinttestinstallmake -n check:--upgrade pip3 times on main, 0 here;make -n check-all: 6 and 0. Heremake -n checkis the bootstrap guard, onehatch run lintand the finalecho.make doctor..venv.venv/bin/python: not foundMISSING .venv/bin/hatch (required; make install creates it);okforpython3.14,func,azurite.venv/bin/pythonpresent, Hatch andfuncmissing (PATH=/usr/bin:/bin)MISSING <venv>/bin/hatch (required; make install creates it);funcandazuritelistedmissing ... (optional ...)make installpip list,func --versionand the hook stateoklines (Python, Hatch,func,azurite); exit 0.venv/bin/hatchMISSING, andbootstrapinstalls Hatch (stub pip: oneinstall ... hatchcall)The
bootstrapanddoctorrecipe text taken frommake -nwas run under dash, bash,bash --posixand busyboxshfor an executable file, a directory, a non-executable file and a missing Hatch path: identical results in all four shells (only the executable file isokand not reinstalled; the other three areMISSINGand triggerpip install hatch).shellcheck0.8.0 (-s sh,-s dash,-s bash) is clean on thebootstrap,doctor,install-cianddocsrecipes.make docs. A broken link ([a broken link](does-not-exist.md)appended todocs/faq.md) as the only change:CI=1, fresh Python 3.11 venv, clean treeTypeError: Highlight.__init__() missing 1 required positional argument: 'md'(themkdocstrings[python]<1.0pin)CI=1, fresh venv, broken linkWithout the link both local runs exit 0. The
pip installfailure path is covered bytest_docs_shows_a_failed_install_and_does_not_build_after_it(stub pip prints an error and exits 1: make exits non-zero, the error is on stderr, MkDocs never runs).Contract tests.
pytest tests/test_make_contract.py: 21 passed on Python 3.11.15, 3.12.13, 3.13.13 and 3.14.4. The same file against main'sMakefile: 17 failed, 4 passed (the 4 are properties main already has:check-allkeeps every gate, a failed MkDocs build fails the target in both branches,doctorwrites nothing). One mutated copy of the Makefile per case, each run against the tests: baseline green, and all 43 mutations turn at least one test red:--upgrade pip; Hatch reinstalled every time; a directory counts as Hatch (-xonly); a non-executable file counts (-fonly);python3hard-coded (ignoresPYTHON);.venvrecreated every time; the oldPYTHON := .venv/bin/pythonrestored.checkalso runstypecheck;checkdropslint;checkalso runs the tests;check-alldrops security, the workflow lints, the tests orcheck;check-allrunschecktwice.--strictremoved (local or CI); a failed build swallowed with|| true(local or CI); a failed dependency install ignored (;for&&); pip's output and errors swallowed;--upgrade piprestored; the CI branch pins packages instead of thedocsextra.exit 0; anycommand -vhit counts; a directory counts; a non-executable file counts; a missing optional tool fails;doctordepends onensure-hatch; the version probe removed, inverted or lowered to 3.10; a missing Python or Hatch does not fail; Hatch looked up onPATH; onlypython3scanned;doctorwrites a file.PYTHON; no version guard; guard inverted; installs without thedevextra; depends onensure-hatch.Quality gates.
ruff check src tests;ruff format --check src testsmypy src tests(strict, frompyproject.toml);make security-typespython tools/lint_release_workflows.py;tools/lint_workflow_pins.py;tools/lint_hatch_matrix.py;git diff --check origin/main..HEADmake security(the CI invocation,SECURITY_PYTHON= the dev interpreter)bandit: PASS {"files": 49, "loc": 8902, "errors": 0, "severity.LOW": 13, "confidence.MEDIUM": 2, "confidence.HIGH": 11, "blocking": 0, "stale_exceptions": 0}: the 13 line-pinned LOW findings of #817 still match, because nothing undersrc examples demo scripts toolschangedmkdocs build --strictpytest tests(defaultaddopts, coverage gate), dev-only env without the companion package1526 passed, 30 skipped, 22 deselected, coverage 95.14% (gate 95%); main's 1505 plus the 21 new tests; the 30 skips are the existing companion-dependent onesFresh clones of this branch (
git clone --branch build/make-docs-strict-bootstrap-once,HEAD=297a0c4, no.venv, no Hatch environment):python3is 3.10.12)make doctorMISSING .venv/bin/hatch (required; make install creates it), exit 2make installmake doctor;make installa second timeoklines; exit 0 in 1 s with no "Creating" or "Installing" linemake test1526 passed, 30 skipped, 22 deselected, coverage 95.14% in 23 smake checkwith this Makefile, then with main's Makefiletypecheckrun:src testsand the security tools) against 4 on mainmake check-all1526 passedat 95.14%,bandit: PASSwith"blocking": 0, "stale_exceptions": 0make docs; the same with a broken link, this Makefile / main'smake install PYTHON=python3.11.venv/bin/pythonis 3.11.15, pip 24.0 (un-upgraded), Hatch 1.18.1make doctor;make checkmake install-ci PYTHON=<Python 3.11 venv>/bin/pythonazure_functions_openapi0.30.1 imports from the clone'ssrcin that interpretermake install-ci PYTHON=python3(3.10.12);PYTHON=python3.99install-ci needs Python 3.11 or newer, got 3.10.12, exit 2, before pip runs;python3.99: No such file or directory, exit 2Not run: macOS or BSD make (the contract module skips itself on Windows);
make demo*,make e2e-localandmake e2e-azure;publish-pypi.yml(tag or manual dispatch only, and it callsmake installin two jobs).docs.ymlandmaintenance.ymlalso callmake installbut do not run on pull requests, so they were dispatched by hand on this branch (see the CI section).What remains of #811
Refs, notCloses: this PR covers the Makefile items below; the rest of #811's checklist is separate work.make installwithout a per-run pip or Hatch upgrademake install-ci PYTHON=<interpreter>installs into exactly that interpretermake doctorread-only, non-zero when Python >= 3.11 or Hatch is missing, reportsfunc/azuritemake check: mypy oncecheckislint(ruff, format check, one type check); the Bandit scope and the workflow lints stay incheck-all, which is unchangedmake docsequals the CI docs gate (mkdocs build --strict)make docsmake integration,make verifyNot in this PR
AGENTS.mdandCONTRIBUTING.mdare policy-bearing files (R3 indocs/review-evidence-policy.md), so they are untouched. Nothing they say becomes wrong (they namemake install,make check-allandmake docs); a follow-up can adddoctorandinstall-cito their command lists together with the maintainer's authorization record.make install-cior the newdoctoryet. When one adoptsinstall-ci, note thattools/lint_hatch_matrix.pymatches\bmake\s+install\b, which is also a prefix ofmake install-ci, so it would count it as Hatch-routed.SECURITY_PYTHON ?= hatch run pythonresolveshatchfromPATH, not$(HATCH), so aftermake installalonemake security(and thereforemake check-all) fails withhatch: not foundunless Hatch is also onPATH. The same happens on main. The security targets are out of scope here.maintenance.ymlrunshatch run lintand thenhatch run typecheck, so mypy still runs twice in that scheduled job (visible in the dispatch run below). That is a workflow edit and is left for its own change.PATH, somake doctorreports it as missing;make installstill works (Hatch provisions it).Type of Change
Checklist
make check-alland all checks pass (including tests and at least 95% coverage): exit 0 in a fresh clone of the branch at297a0c4, coverage 95.14%Related Issues
Refs #811
Review Evidence
Makefile,tests/and twodocs/pages only; no workflow,permissions:, secrets, release/publishing, Swagger UI, CLI-import or policy-bearing file (AGENTS.md,CONTRIBUTING.md, the PR template,docs/agent-playbook.md,docs/review-evidence-policy.md,docs/release_process.md,docs/security.md,DESIGN.md,SECURITY.md) changesCI on the head commit
297a0c41a7347f435babe38761bd2567e9cd7cbd(the head of this PR;mainstill9127b17): 30 check-runs, all pages fetched (one page,total_count30): 29 success, 1 skipped (dependabot-auto-merge, by design), 0 failed. The five required contexts (bandit,semgrep,Analyze,PR title,ci-required) succeeded on this exact SHA.Test (Py 3.11, af latest):1556 passed, 22 deselected, coverage 95.14% (gate 95%); the 3.12, 3.13 and 3.14 cells:1556 passed. Theaf 1.21.0,af 1.24.0andMinimum dependenciescells (no companion package):1526 passed, 30 skipped. All of them include the 21 new tests.bandit:bandit: PASS {"files": 49, "loc": 8902, "errors": 0, ... "blocking": 0, "stale_exceptions": 0}on the PR head.make installis not on any pull-request path, so I dispatched the two workflows that call it on this branch's head.Docs (strict build check)(run 40118127178,workflow_dispatch): the log shows "Creating virtual environment...", "Installing Hatch...", the Hatch environment created, thenhatch run mkdocs build --strictand "Documentation built"; success.Maintenance Checks(run 40118128903): the same bootstrap lines,hatch run lint, andmake securitywithbandit: PASS ... "blocking": 0, "stale_exceptions": 0; success. Both appear among the 30 check-runs above (docs-check,maintenance).