diff --git a/.gitattributes b/.gitattributes index 3560973e..b27055ec 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,32 +1,32 @@ -# THIS REPOSITORY'S OWN INSTALLER COMPARES ITS BYTES, so the bytes have to be -# the same bytes on every platform. +# THE BYTES A CLONE SEES ARE THE BYTES THIS ROOT'S CHECKS DESCRIBE. # -# `openRepoTools --install` decides between `installed at`, `updated at` and -# `already installed … (unchanged)` with `cmp -s`, and -# `tests/test_openrepotools_command.py` compares an installed copy to this -# checkout's file BYTE FOR BYTE. Git for Windows' installer default is -# `core.autocrlf=true`; a checkout of THIS repository made under it turns every -# `\n` in `openRepoTools`, `park`, `resume` and `status` into `\r\n`, and every one -# of those comparisons is about line endings rather than about content — a -# second `--install` reporting three files `updated at` on a machine where -# nothing changed, and a colleague whose only sin was cloning on a stock -# Windows laptop watching the suite fail on lines nobody touched. CI never sees -# it: the Linux and macOS jobs check out LF by default and the Windows job pins -# `core.autocrlf=false`. +# `contracts/shape-pin.yaml` records a sha256 of every shape file copied here, +# and `scripts/validate-pins.py` recomputes each one from the bytes on disk. +# Git for Windows' installer default is `core.autocrlf=true`; a clone made under +# it writes CRLF into the worktree, and `make validate` then goes red on every +# pinned file for a colleague who did nothing but clone this repository +# (openRepoShape #51, ruled by Brett Heap on 2026-09-05; the shape's own +# statement of the rule is `shape/.gitattributes`, pinned and left in place). # -# The same rule for a second reason. `tests/test_park_resume_commands.py` -# asserts that `park`, `resume` and `status` carry the shared estate resolver -# BYTE FOR BYTE — that is what makes the deliberate duplication safe — and a CRLF -# checkout would either break the claim or hide a real drift behind it. +# Before the split into three repositories this file carried the same rule for +# a second reason: `openRepoTools --install` and its tests compare installed +# copies with the checkout BYTE FOR BYTE. Those files are the code leg's now, +# and a superproject's attributes never reach into a submodule's worktree, so +# that statement moved with them, word for word, to `code/.gitattributes`; the +# documents in `spec/` carry their own copy of the rule too. # -# NO PATH IS LISTED BY NAME. Every file this repository ships is text — bash, -# Python, YAML, Markdown, a workflow — so there is nothing for git's binary -# heuristic to misjudge, and `text=auto` leaves that detection where it -# belongs: with git. The pinned `upstream/openRepoShape` is a gitlink and -# carries its own copy of this rule. -# -# openRepoShape #63 wrote this rule for the standard's own root, and its #51 -# (2026-09-05) wrote it for every scaffolded project; this is the same rule -# retold for the repository that ships the two commands. +# NO PATH IS LISTED BY NAME in the rule below: every file here is text, so +# `text=auto` leaves binary detection with git. The block after the rule is +# the split's (openRepoShape #471), and it stays last: the later line wins. * text=auto eol=lf +# openRepoShape (#471): the pinned shape copies below check out with LF everywhere, as contracts/shape-pin.yaml records their bytes +/scripts/validate-pins.py text eol=lf +/scripts/validate-manifest.py text eol=lf +/scripts/bootstrap.py text eol=lf +/Makefile text eol=lf +/.github/workflows/validate.yml text eol=lf +/AGENTS-shape.md text eol=lf +/scripts/repo_shape.py text eol=lf +/scripts/validate-repository-naming.py text eol=lf +/contracts/repository-naming.yaml text eol=lf diff --git a/.github/release-build-requirements.txt b/.github/release-build-requirements.txt deleted file mode 100644 index 2713ff24..00000000 --- a/.github/release-build-requirements.txt +++ /dev/null @@ -1,152 +0,0 @@ -# THE RELEASE BUILD'S OWN TOOLS, LOCKED BY HASH (opensoft/openRepoTools#228, -# research R11). -# -# COPIED FROM openRepoShape v1.1.0: every line below this header is the lock -# at that repository's tag v1.1.0 (`190133d`), byte for byte. Only this -# header is rewritten, for this repository. -# -# `.github/workflows/release.yml`'s `build` job installs exactly the files -# named here, with `--require-hashes --only-binary :all:`, into a virtual -# environment of its own, and builds the wheel with -# `python -m build --wheel --no-isolation` in it. So every program that -# touches the wheel before it is uploaded, the `build` frontend and the -# `hatchling` backend, is a file this list names by its digest, and so is -# each of hatchling's own requirements: packaging, pathspec, pluggy, -# trove-classifiers, and tomli below Python 3.11. `pyproject.toml` pins -# hatchling alone, so a build that installs it the ordinary way, in pip's -# isolated build environment, resolves those five on the day it runs. -# -# `.github/workflows/tests.yml`'s `package` job reads one entry, tomli's: it -# copies the requirement line and every `--hash` line under it into a file -# under `$RUNNER_TEMP`, and installs that by hash, wheels only, before -# pytest. The job holds no digest of its own, but the pipx lock, -# `.github/release-pipx-requirements.txt`, carries this same `tomli` entry, -# byte for byte, and the job installs that lock after this file's `tomli` -# (step 7 after step 4), so its tests run on the pipx lock's `tomli`. The two -# entries must move together, re-resolved in one act. -# The job's own two builds are `pip wheel`, in pip's isolated build -# environment, which this lock does not reach. -# -# `hatchling==1.27.0` IS `pyproject.toml`'s OWN PIN, and must stay so: with -# `--no-isolation`, `build` refuses a backend that does not satisfy -# `[build-system] requires`, and `tests/test_release_workflow.py` holds the -# two equal. -# -# UNIVERSAL: one file for every Python from 3.9, the oldest the package -# supports, on every system. The markers choose what pip installs on each -# Python, which is why tomli's entry serves the `package` job's 3.9. -# -# RESOLVED, NOT WRITTEN BY HAND, by openRepoShape with uv 0.12.18, from PyPI -# as it stood at 2026-10-01T00:00:00Z. The command below, with that date as -# ``, gives every line under this header again (checked with uv -# 0.12.18 on 2026-10-18). To change it, run the command with a newer date, -# put this header back above the output, rewritten for that date, and commit -# the diff as its own act: -# -# printf 'build\nhatchling==1.27.0\n' \ -# | uv pip compile --universal --python-version 3.9 --generate-hashes \ -# --no-header --index-url https://pypi.org/simple --only-binary :all: \ -# --exclude-newer - -o .github/release-build-requirements.txt - -build==1.4.4 ; python_full_version < '3.10' \ - --hash=sha256:8c3f48a6090b39edec1a273d2d57949aaf13723b01e02f9d518396887519f64d \ - --hash=sha256:f832ae053061f3fb524af812dc94b8b84bac6880cd587630e3b5d91a6a9c1703 -build==1.6.1 ; python_full_version >= '3.10' \ - --hash=sha256:51cc11666391ab6f092070437ac747002ff46f3e4113a3622177ee6b488bfc53 \ - --hash=sha256:ecd351a4be9d35a9eaaba244a7687143c9c7d4aea6ac964e7e7ddab20cbcf4e7 -colorama==0.4.6 ; os_name == 'nt' \ - --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \ - --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6 - # via build -hatchling==1.27.0 \ - --hash=sha256:971c296d9819abb3811112fc52c7a9751c8d381898f36533bb16f9791e941fd6 \ - --hash=sha256:d3a2f3567c4f926ea39849cdf924c7e99e6686c9c8e288ae1037c8fa2a5d937b -importlib-metadata==8.7.1 ; python_full_version < '3.10' \ - --hash=sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb \ - --hash=sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151 - # via build -importlib-metadata==9.0.1 ; python_full_version >= '3.10' and python_full_version < '3.10.2' \ - --hash=sha256:ab830580bc0ef3db61ce8fae716389e5462b67e033018bab6d8f80ef17172f99 \ - --hash=sha256:bba5600596a7e21f3eef53281cf28d6a5195634d2f2b78ff9501a3272c6eaab0 - # via build -packaging==26.3 \ - --hash=sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79 \ - --hash=sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c - # via - # build - # hatchling -pathspec==1.1.1 \ - --hash=sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a \ - --hash=sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189 - # via hatchling -pluggy==1.6.0 \ - --hash=sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3 \ - --hash=sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746 - # via hatchling -pyproject-hooks==1.3.3 \ - --hash=sha256:5fc53fdac9f7bd63fbcdc868fb5f90b4784d78a53a3d3388cd738b807441a20b \ - --hash=sha256:defda19b854fa0d3bd4f76ea4ddcba8abd7dcfcdd585a6690ade050744fc5f43 - # via build -tomli==2.4.1 ; python_full_version < '3.11' \ - --hash=sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853 \ - --hash=sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe \ - --hash=sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5 \ - --hash=sha256:1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d \ - --hash=sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd \ - --hash=sha256:2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26 \ - --hash=sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54 \ - --hash=sha256:3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6 \ - --hash=sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c \ - --hash=sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a \ - --hash=sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd \ - --hash=sha256:504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f \ - --hash=sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5 \ - --hash=sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9 \ - --hash=sha256:559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662 \ - --hash=sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9 \ - --hash=sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1 \ - --hash=sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585 \ - --hash=sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e \ - --hash=sha256:7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c \ - --hash=sha256:734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41 \ - --hash=sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f \ - --hash=sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085 \ - --hash=sha256:7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15 \ - --hash=sha256:88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7 \ - --hash=sha256:8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c \ - --hash=sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36 \ - --hash=sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076 \ - --hash=sha256:a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac \ - --hash=sha256:b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8 \ - --hash=sha256:b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232 \ - --hash=sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece \ - --hash=sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a \ - --hash=sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897 \ - --hash=sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d \ - --hash=sha256:d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4 \ - --hash=sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917 \ - --hash=sha256:eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396 \ - --hash=sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a \ - --hash=sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc \ - --hash=sha256:ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba \ - --hash=sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f \ - --hash=sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257 \ - --hash=sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30 \ - --hash=sha256:fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf \ - --hash=sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9 \ - --hash=sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049 - # via - # build - # hatchling -trove-classifiers==2026.9.21.13 \ - --hash=sha256:0a9ebc8d4e2f3e8a22848c5258033035bec17a3012ac3fea16dbaa764489eb71 \ - --hash=sha256:8b1ff4f9c191b1040b71c37f1e445ab99732911e3cd91de52838453a854d7a17 - # via hatchling -zipp==3.23.1 ; python_full_version < '3.10' \ - --hash=sha256:0b3596c50a5c700c9cb40ba8d86d9f2cc4807e9bedb06bcdf7fac85633e444dc \ - --hash=sha256:32120e378d32cd9714ad503c1d024619063ec28aad2248dc6672ad13edfa5110 - # via importlib-metadata -zipp==4.1.0 ; python_full_version >= '3.10' and python_full_version < '3.10.2' \ - --hash=sha256:25ad4e16390cd314347dd8f1de67a2ac538ae658ed4ab9db16029c07c188e97f \ - --hash=sha256:4cb57381f544315db7688e976e922a2b18cdb513d21cc194eb42232ba2a3e602 - # via importlib-metadata diff --git a/.github/release-pipx-requirements.txt b/.github/release-pipx-requirements.txt deleted file mode 100644 index 7f6c9b1d..00000000 --- a/.github/release-pipx-requirements.txt +++ /dev/null @@ -1,132 +0,0 @@ -# THE pipx THAT INSTALLS THE WHEEL IN CI, LOCKED BY HASH -# (opensoft/openRepoTools#228, research R11). -# -# COPIED FROM openRepoShape v1.1.0: every line below this header is the lock -# at that repository's tag v1.1.0 (`190133d`), byte for byte. Only this -# header is rewritten, for this repository. -# -# Two jobs install exactly the files named here, by hash and wheels only, -# then install the openrepotools wheel with that pipx, as a person does: -# `.github/workflows/release.yml`'s `install` job, the install check, on each -# of its three legs (ubuntu and macOS on Python 3.x, and ubuntu on 3.9), and -# `.github/workflows/tests.yml`'s `package` job, on 3.9, with `PIPX_HOME` -# and `PIPX_BIN_DIR` under `$RUNNER_TEMP`. -# -# UNIVERSAL, for Linux, macOS and Windows, though no job here runs on -# Windows: pipx 1.11.1 below Python 3.10, the newest pipx that installs on -# 3.9 (the package's oldest Python), and 1.17.8 from 3.10, the newest on -# PyPI at the date below. `tests/test_release_workflow.py` holds those two -# versions, and `tests/package_layout.py`'s fixtures mirror their layouts, -# so a resolution that moves pipx must move both with it. pipx then installs -# pip into its own shared environment, which no lock here reaches; the -# install check's `PIP_INDEX_URL` holds every pip it runs to PyPI. -# -# RESOLVED, NOT WRITTEN BY HAND, by openRepoShape with uv 0.12.18, from PyPI -# as it stood at 2026-10-01T00:00:00Z. The command below, with that date as -# ``, gives every line under this header again (checked with uv -# 0.12.18 on 2026-10-18). To change it, run the command with a newer date, -# put this header back above the output, rewritten for that date, and commit -# the diff as its own act: -# -# printf 'pipx\n' \ -# | uv pip compile --universal --python-version 3.9 --generate-hashes \ -# --no-header --index-url https://pypi.org/simple --only-binary :all: \ -# --exclude-newer - -o .github/release-pipx-requirements.txt - -argcomplete==3.7.0 ; python_full_version < '3.10' \ - --hash=sha256:afde224f753f874807b1dc1414e883ab8fe0cda9c04807b6047dcb8e1ac23913 \ - --hash=sha256:d8f0f22d2a8a7caa383be1e22b6caf1ecaf0ebd10d8f83cc125e36540c95830c - # via pipx -argcomplete==3.7.2 ; python_full_version >= '3.10' \ - --hash=sha256:6029205678bdd9c1c728a155f5f9ecf5812393f969eef58807641a2bc2aa5b19 \ - --hash=sha256:aad8b69a0b9969edb62db0d1752354c0d50717b10e0cbb00e2a958381b9fc6b9 - # via pipx -click==8.1.8 ; python_full_version < '3.10' \ - --hash=sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2 \ - --hash=sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a - # via userpath -click==8.5.0 ; python_full_version >= '3.10' \ - --hash=sha256:255bc9599cf7748b4b1a446ccc735421bd08a2ae529a8b88597d3de5664ee360 \ - --hash=sha256:ba0d2089de75ea0310e2dde03160e6ca10009947fb95a182f9b54021bb272e34 - # via userpath -colorama==0.4.6 ; sys_platform == 'win32' \ - --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \ - --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6 - # via - # click - # pipx -filelock==4.0.7 ; python_full_version >= '3.10' \ - --hash=sha256:a93c4d93269b339a6af4848342c7e940d0f9928ad95eff64764699e5f1bf8a6a \ - --hash=sha256:da5915714a70b55d167fdc7e251ad91302b0a36816fb574dfafae8f4f2c9bb21 - # via pipx -packaging==26.3 \ - --hash=sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79 \ - --hash=sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c - # via pipx -pipx==1.11.1 ; python_full_version < '3.10' \ - --hash=sha256:e57354c3308b0abb75106d3b73a0d1ffa5c25f24f29e9f4a45e575776dbab85c \ - --hash=sha256:f854fbf01c179db4331adcf6e1b95836b524100135b5d35822497a47a3857e89 -pipx==1.17.8 ; python_full_version >= '3.10' \ - --hash=sha256:19387d1ef29f9192dfb084f22a68f5f663c2e80c6c814bef193b3203a4fc2dc3 \ - --hash=sha256:90810ac9a12a5141364294a1abc24bcc8fab4572d18a6dae165895512e336966 -platformdirs==4.4.0 ; python_full_version < '3.10' \ - --hash=sha256:abd01743f24e5287cd7a5db3752faf1a2d65353f38ec26d98e25a6db65958c85 \ - --hash=sha256:ca753cf4d81dc309bc67b0ea38fd15dc97bc30ce419a7f58d13eb3bf14c4febf - # via pipx -platformdirs==4.12.2 ; python_full_version >= '3.10' \ - --hash=sha256:29dbf06d96c500bc6bdbce75fb0a14d63279c93b1842f97e72a135b33e856983 \ - --hash=sha256:eab5f70271a490ef74618bb314fbb86e3c7e82fa3b9c922c2ea0e0a1a155d329 - # via pipx -tomli==2.4.1 ; python_full_version < '3.11' \ - --hash=sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853 \ - --hash=sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe \ - --hash=sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5 \ - --hash=sha256:1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d \ - --hash=sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd \ - --hash=sha256:2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26 \ - --hash=sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54 \ - --hash=sha256:3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6 \ - --hash=sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c \ - --hash=sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a \ - --hash=sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd \ - --hash=sha256:504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f \ - --hash=sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5 \ - --hash=sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9 \ - --hash=sha256:559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662 \ - --hash=sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9 \ - --hash=sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1 \ - --hash=sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585 \ - --hash=sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e \ - --hash=sha256:7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c \ - --hash=sha256:734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41 \ - --hash=sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f \ - --hash=sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085 \ - --hash=sha256:7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15 \ - --hash=sha256:88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7 \ - --hash=sha256:8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c \ - --hash=sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36 \ - --hash=sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076 \ - --hash=sha256:a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac \ - --hash=sha256:b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8 \ - --hash=sha256:b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232 \ - --hash=sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece \ - --hash=sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a \ - --hash=sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897 \ - --hash=sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d \ - --hash=sha256:d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4 \ - --hash=sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917 \ - --hash=sha256:eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396 \ - --hash=sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a \ - --hash=sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc \ - --hash=sha256:ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba \ - --hash=sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f \ - --hash=sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257 \ - --hash=sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30 \ - --hash=sha256:fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf \ - --hash=sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9 \ - --hash=sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049 - # via pipx -userpath==1.9.2 \ - --hash=sha256:2cbf01a23d655a1ff8fc166dfb78da1b641d1ceabf0fe5f970767d380b14e89d \ - --hash=sha256:6c52288dab069257cc831846d15d48133522455d4677ee69a9781f11dbefd815 - # via pipx diff --git a/.github/workflows/composed.yml b/.github/workflows/composed.yml new file mode 100644 index 00000000..35005a2f --- /dev/null +++ b/.github/workflows/composed.yml @@ -0,0 +1,121 @@ +name: composed + +# THE EXACT-PIN INTEGRATION GATE (opensoft/openRepoTools#186, T010 of +# `specs/004-migrate-to-triad/tasks.md`; FR-008). `validate` beside this file +# says the three records of each leg agree — gitlink, pin, digest. This says +# the code those records name PASSES ITS OWN SUITE, composed with the spec leg +# and the dependency it was pinned beside: the whole arrangement at the exact +# commits this root records, and nothing allowed to skip its way to green. +# +# WHY A SIBLING AND NOT A JOB IN `validate.yml`: that file is a COPY of +# openRepoShape's, digest-pinned in `contracts/shape-pin.yaml`. An edit to it +# is drift `scripts/validate-pins.py` and `status` both report. This file is +# this project's own, and it references no leg by `/@`, so the +# lockstep rule has nothing in it to move. +# +# LINUX ONLY, on purpose. The platform matrix — macOS's bash 3.2 parse and its +# `ready`-label suite, Windows and the no-submodule skip path — is the code +# leg's own `tests` workflow, and it stays there. What only this root can run +# is the COMPOSED suite: `OPENREPOTOOLS_COMPOSED=1` turns every absent root +# into a refusal and every check that would have skipped for want of one into +# a failure, so a green run here is a claim about all three legs at once. +# +# IT REFUSES ABSENT CONTEXT TWICE OVER. The step below fails on any missing +# leg, pin or dependency file before a test runs; and the suite's own +# `tests/conftest.py` refuses a composed session (pytest's exit 4) when the +# assembly or spec root is absent, the dependency is not initialized, or the +# code or spec leg is not at the commit this root records for it. A leg this +# job cannot fetch — a private leg with no credential — fails the checkout, +# which is the same refusal: composed acceptance with a leg missing is the one +# thing this job must never report. The legs are fetched over https, as +# `.gitmodules` names them; nothing here allows a `file://` transport. + +on: + pull_request: + push: + branches: [main] + workflow_dispatch: + +jobs: + exact-pins: + # THIS UBUNTU RUNNER IS ON 24.04: `ubuntu-latest` became Ubuntu 26.04 on + # 2026-10-21. There the `[host]` case of the TERM-ignoring kill test, which + # this job's suite runs, fails. Pinned until 26.04 support is decided. + runs-on: ubuntu-24.04 + steps: + # PINNED BY COMMIT, the same two actions the code leg pins. RECURSIVE: + # the legs, and the dependency nested in the code leg. + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + with: + submodules: recursive + fetch-depth: 1 + persist-credentials: false + - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 + with: + python-version: '3.x' + + - name: every context the composed run claims is here + run: | + set -euo pipefail + missing=0 + for need in project.yaml contracts/code-pin.yaml contracts/spec-pin.yaml \ + code/openRepoTools code/tests/run.sh code/tests/conftest.py \ + code/contracts/openreposhape-pin.yaml \ + code/upstream/openRepoShape/scripts/repo_shape.py \ + code/upstream/openRepoShape/templates/workspace-root/README.md; do + if [ ! -f "$need" ]; then + echo "::error::the composed run needs $need, and this checkout has none" + missing=1 + fi + done + if [ ! -d spec/openspec ] && [ ! -d spec/specs ] && [ ! -d spec/docs ]; then + echo "::error::the spec leg is not checked out at spec/" + missing=1 + fi + git submodule status --recursive + exit "$missing" + + # The same two the `validate` workflow runs, here as the gate's FIRST + # half: a suite that passes at a gitlink the pin does not name passes + # at the wrong commit. + - name: the manifest and the lockstep pins + run: | + python3 scripts/validate-manifest.py + python3 scripts/validate-pins.py + + # ZSH, SO THE SUITE'S ZSH CASES RUN (opensoft/openRepoTools#186, T014 of + # `specs/004-migrate-to-triad/tasks.md`). On this runner a composed run + # skips 66 conditional cases: 60 that need zsh, 1 byte-count case and 5 + # wheel cases. With zsh installed the 60 run, and the 6 skips left are the + # byte-count case and the five wheel cases, as in rehearsal D's composed + # run on a bench that has zsh; `-rs` below still lists every skip. Not + # pinned, as `python-version: '3.x'` above is not: the step prints the + # zsh it installed. + - name: install zsh, so the suite's zsh cases run + run: | + sudo apt-get update + sudo apt-get install -y --no-install-recommends zsh + command -v zsh + zsh --version + + - name: install the test runner + run: python3 -m pip install --disable-pip-version-check pytest==8.3.4 + + - name: git identity for the fixture commits (env, not global config) + run: | + { + echo "GIT_AUTHOR_NAME=openRepoTools CI" + echo "GIT_AUTHOR_EMAIL=ci@openrepotools.invalid" + echo "GIT_COMMITTER_NAME=openRepoTools CI" + echo "GIT_COMMITTER_EMAIL=ci@openrepotools.invalid" + } >> "$GITHUB_ENV" + + # THE CODE LEG'S OWN ENTRY POINT, serialized as it is everywhere: the + # roots are named, not inferred, and `-rs` prints any skip that remains + # (a platform one; none for want of a root can reach this far). + - name: the code leg's suite, composed, at the exact pins + env: + OPENREPOTOOLS_COMPOSED: '1' + OPENREPOTOOLS_ASSEMBLY_ROOT: ${{ github.workspace }} + OPENREPOTOOLS_SPEC_ROOT: ${{ github.workspace }}/spec + run: bash code/tests/run.sh -rs diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index 092ddc05..00000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,1736 +0,0 @@ -name: release - -# --------------------------------------------------------------------------- -# PUBLISH THE openrepotools WHEEL (#228): TestPyPI, AN INSTALL CHECK ON THREE -# LEGS, THEN PyPI, IN ONE DISPATCHED RUN. Adapted step for step from -# openRepoShape v1.1.0's own release.yml (`190133d`, the commit -# `upstream/openRepoShape` is pinned at), whose checks are taken over as they -# are (FR-028) except where design.md Decision 10 names a difference: -# - a pre-release `X.Y.ZrcN` is accepted, for TestPyPI alone; PyPI takes a -# final `X.Y.Z` and nothing else; -# - the repository guard (FR-048, WHERE RELEASES COME FROM below) is new -# here, because the model has none; -# - the stamp has a fifth field, REPOSITORY, the run's `$GITHUB_REPOSITORY`; -# - the digest's definition is held to the literal `sorted-ls-tree-r-v1` -# that `--version` prints and to the pin's `digest_definition`, where the -# model compares it with its package's own constant; -# - the payload's blobs and modes are checked under -# `openrepotools/payload/`, against the commit's own paths; -# - the install check runs on three legs, ubuntu and macOS on Python 3.x -# and ubuntu on 3.9, with no Windows leg, checking `--version` and an -# offline `--install` beside pipx's link, again under macOS's -# `/bin/bash` 3.2, and compiling the payload's two Python commands on -# 3.9; -# - no step reads the release notes (WHAT THIS WORKFLOW NEVER READS). -# It also holds more than the model where FR-029 and contracts/workflows.md -# ask for it: the gate names `tests.yml`'s four jobs, the build checks out -# full history with the submodule, and the ref step fetches the tag again -# without `--depth`. -# -# FOUR JOBS, EACH GATED ON THE ONE BEFORE IT: -# build the version and the repository guard; the tag, the branch, the -# clean checkout, both environments, `tests.yml`'s four jobs on -# this commit, and the digest; then the wheel, built twice from -# two `git archive` extractions of the commit, stamped, compared -# byte for byte, checked against the commit's own files, and its -# sha256 recorded. -# testpypi the guard again; uploads those bytes to TestPyPI, in the -# `testpypi` environment, then requires TestPyPI to serve -# exactly that digest. -# install on ubuntu, macOS and Python 3.9: pipx installs TestPyPI's -# file, taken by the recorded digest (on a branch, the build's -# own wheel), and the one command it places is run. -# pypi the guard again; uploads the same bytes to PyPI, in the `pypi` -# environment, which waits for Brett Heap's approval after the -# install check, then requires PyPI to serve exactly the -# recorded digest. -# -# DISPATCH ONLY. Nothing here runs on a push, a tag or a pull request: an -# upload is an act Brett Heap approves. The version is the tag's: `v1.1.0` -# gives `1.1.0`, which the build writes into the stamp, -# `openrepotools/_release.py`, with the tag, the commit, the digest and the -# repository. A dispatch on a tag refuses unless that tag is `v`, -# annotated, and pointing at the run's own commit, and unless that commit is -# on `main` or on the version's release branch, `release/v.`. -# -# RE-RUN ONLY THE FAILED JOBS ("Re-run failed jobs"), never the whole run -# and never a second dispatch of a version already uploaded. A re-run of a -# failed job reuses the build's artifact and its recorded digest, and each -# upload job requires the index to hold no file of the version but the -# verified wheel at its digest (FR-031), so a re-run after an upload that -# went through uploads nothing new. A second dispatch builds again, and its -# wheel is the same file only while the locks, the runner's Python and the -# commit all stay the same; the index keeps the first file under its name -# for good, and the upload jobs refuse any other. -# -# NO PyPI TOKEN AND NO STORED SECRET, ANYWHERE. Both uploads use trusted -# publishing (OIDC): `id-token: write` is granted to the two upload jobs and -# to no other job. The one other credential is GitHub's automatic -# GITHUB_TOKEN, read-only in every job, which `gh` uses to read this -# repository's own API. -# -# EVERY ACTION IS PINNED BY COMMIT, with its release tag beside it, as -# `tests.yml` pins them: checkout and setup-python at that file's own -# commits, the artifact actions at the model's. One limit carried over from -# the model, where it was accepted: pypa/gh-action-pypi-publish is a -# container action whose image is pulled by a registry tag, so its commit -# pins the action's files and not all the code an upload runs. The digest -# checks before and after each upload bound what that could change. -# -# RELEASING: THE PROCEDURE (FR-034 to FR-040). Brett Heap decides every -# tag, every release-branch cut and every upload, and approves each -# deployment. The release owner, lane openRepoTools-3, carries them out on -# his word: it tags, dispatches and creates the GitHub Release, and no other -# lane does. For each release: -# 1. THE COMMIT'S CI FIRST. A tag waits for its commit's `tests.yml` runs: -# all of them finished, and the newest green, in a push or dispatch run -# (the build's gate counts no `pull_request` run). On `main` the push -# run that landed the commit does that once it has finished green; on -# `release/vX.Y` the branch's own push run does, and for a commit that -# reached the branch by fast-forward the newer of main's run and the -# branch's decides (FR-039). Otherwise, BEFORE TAGGING, dispatch -# `tests.yml` on a branch that points at the release commit, and wait -# for it: -# git push origin :refs/heads/ -# gh workflow run tests.yml --ref -R -# 2. THE TAG, ANNOTATED, on Brett Heap's word, with the message `v1.0.0`'s -# has (FR-036), `