diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4251584..a2589c9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -493,5 +493,28 @@ jobs: exit "${status}" + # The release builds a checksum manifest and verifies it from inside dist/. A path-prefix mistake + # there is invisible until a tag is already cut and immutable, so the round trip is exercised + # here instead of being discovered during a release. + - name: Checksum manifest round-trips the way the release verifies it + run: | + work="$(mktemp -d)" + cp -r pyproject.toml src README.md LICENSE "${work}/" + cd "${work}" + + uv build --out-dir dist + ( cd dist && sha256sum -- *.whl *.tar.gz > SHA256SUMS ) + ( cd dist && sha256sum --check SHA256SUMS ) + + # And it must still fail on tampering, or it is decoration. + printf 'tampered' >> dist/*.whl + + if ( cd dist && sha256sum --check --status SHA256SUMS ); then + echo '::error::the checksum manifest did not detect a modified artefact' + exit 1 + fi + + echo 'Checksum manifest round-trips and detects tampering' + - name: This repository satisfies its own strict preset run: ruff check --no-cache src/ && ruff format --no-cache --check src/ diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8b87558..ca204d6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -249,7 +249,13 @@ jobs: run: | uv build --out-dir dist ls -l dist - sha256sum dist/* | tee dist/SHA256SUMS + + # Generated from INSIDE dist/ so the manifest holds bare filenames. The publish job verifies + # it after `cd dist`, so a `dist/`-prefixed path there resolves to `dist/dist/...` and fails + # to open — which is exactly how the first 0.1.1 attempt broke. Patterns are explicit rather + # than `*` so SHA256SUMS can never checksum itself. + ( cd dist && sha256sum -- *.whl *.tar.gz > SHA256SUMS ) + cat dist/SHA256SUMS # The wheel and the sdist must contain the same presets that ship in the other two packages. - name: The built wheel carries the presets @@ -463,9 +469,16 @@ jobs: run: | npm config set '//npm.pkg.github.com/:_authToken' "${GH_PACKAGES_TOKEN}" - # --provenance is deliberately omitted. GitHub Packages neither displays nor verifies npm - # provenance statements, and the same bytes already carry one on npmjs plus a GitHub artefact - # attestation. Generating a second statement here would add a claim nobody can check. + # --provenance is deliberately omitted, and this job is granted no `id-token` permission. + # GitHub Packages neither displays nor verifies npm provenance statements, and the same bytes + # already carry one on npmjs plus a GitHub artefact attestation, so a second statement here + # would be a claim nobody can check. + # + # Note that omitting the flag is not sufficient on its own: `publishConfig.provenance: true` + # in package.json is baked into the tarball and makes npm attempt provenance on EVERY + # publish of it, which failed this job with "requires write access to the id-token + # permission". The declaration was removed from package.json; the npmjs job passes + # --provenance explicitly instead, so intent is per-registry rather than per-tarball. npm publish ./dist/*.tgz \ --@matchory:registry=https://npm.pkg.github.com \ --tag '${{ needs.verify.outputs.dist_tag }}' diff --git a/CHANGELOG.md b/CHANGELOG.md index d752524..a0601cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,9 +22,29 @@ even though nothing about the interface moved. Nothing yet. -## [0.1.1] — 2026-07-30 +## [0.1.2] — 2026-07-30 -First published release. +First release published to every registry. + +`0.1.1` reached npmjs, with provenance, but its PyPI and GitHub Packages jobs failed on two workflow +bugs, so it exists on npmjs alone. Both are fixed here: + +- The checksum manifest was generated as `sha256sum dist/*`, which embeds the `dist/` prefix, while the + publish job verifies it after `cd dist` — so every path resolved to `dist/dist/...` and could not be + opened. It is now generated from inside `dist/` with bare filenames, and CI round-trips it the way the + release does, including asserting that it still detects a tampered artefact. +- `publishConfig.provenance: true` is baked into the published tarball, so npm attempted provenance on + the GitHub Packages publish too and failed for want of an `id-token` permission that job is + deliberately not granted. The declaration is gone; the npmjs job passes `--provenance` explicitly, so + the intent is per-registry rather than per-tarball. + +Version tags cannot be moved or deleted in this repository, so `0.1.1` stays on npmjs as a superseded +version rather than being retracted. PyPI has no `0.1.1`; the three ecosystems are aligned again from +`0.1.2` onward. + +## [0.1.1] — 2026-07-30 [npmjs only] + +Published to npmjs only; see `0.1.2`. `v0.1.0` was tagged but never published. Its release run failed in the first job, because the workflow used `git cat-file` to check that the tag was annotated and `actions/checkout` materialises a tag ref as @@ -102,5 +122,6 @@ ecosystems, so a malicious version would run in our CI and on developer machines Composer has no artefact provenance mechanism, so the PHP package's integrity rests on repository protection and signed tags instead. -[Unreleased]: https://github.com/matchory/coding-style/compare/v0.1.1...HEAD +[Unreleased]: https://github.com/matchory/coding-style/compare/v0.1.2...HEAD +[0.1.2]: https://github.com/matchory/coding-style/compare/v0.1.1...v0.1.2 [0.1.1]: https://github.com/matchory/coding-style/releases/tag/v0.1.1 diff --git a/js/package.json b/js/package.json index 6a9a5c7..a9ca136 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "@matchory/coding-style", - "version": "0.1.1", + "version": "0.1.2", "description": "Shared code style configuration for Matchory projects: oxlint, oxfmt, ESLint and TypeScript presets.", "license": "MIT", "private": false, @@ -12,7 +12,6 @@ }, "publishConfig": { "access": "public", - "provenance": true, "registry": "https://registry.npmjs.org" }, "exports": { diff --git a/pyproject.toml b/pyproject.toml index c64f33f..4139161 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,7 +12,7 @@ [project] name = "matchory-coding-style" -version = "0.1.1" +version = "0.1.2" description = "Shared code style configuration for Matchory projects: ruff presets and the canonical .editorconfig." readme = "README.md" requires-python = ">=3.11" diff --git a/src/matchory_coding_style/__init__.py b/src/matchory_coding_style/__init__.py index d9cd88b..6ba0c60 100644 --- a/src/matchory_coding_style/__init__.py +++ b/src/matchory_coding_style/__init__.py @@ -7,4 +7,4 @@ __all__ = ["__version__"] -__version__ = "0.1.1" +__version__ = "0.1.2"