Skip to content

Pins the release verify job's actions and names the partial-upload remedy - #12

Merged
johnnyt merged 1 commit into
mainfrom
uxpy-9q0-pin-release-verify
Oct 21, 2026
Merged

johnnyt merged 1 commit into
mainfrom
uxpy-9q0-pin-release-verify

Conversation

@johnnyt

@johnnyt johnnyt commented Oct 21, 2026

Copy link
Copy Markdown
Member

Bead: uxpy-9q0

What changes

  • Every action in the release workflow's verify job is pinned by commit SHA, with its version in a trailing comment, the way the publish job already was. verify checks out the source the build reads, installs the toolchain that builds it and stores the files publish uploads, so a moved major tag there could change what is published without ever reaching a token. Each pin is the version the major tag verify used pointed to when this was written (no move to a newer major):

    Action Was Pinned to Version
    actions/checkout v4 11d5960a326750d5838078e36cf38b85af677262 v4.4.0 (the same pin publish carries)
    jdx/mise-action v4 c2a87611a18de5b3828c5652fe268e992400cb5c v4.3.0
    actions/upload-artifact v7 cf430e030ddbb5b0abf93d22962f4752f3646cd9 v7.0.2

    How each SHA was resolved: gh api repos/<owner>/<repo>/git/ref/tags/<tag> for the major tag and for each release tag of that major, following any annotated tag object through git/tags/<sha> to its commit (the three major tags and the three release tags named in the table all point at commits directly). The major tag and the named release tag resolve to the same commit in each row. The newest of the three releases, upload-artifact v7.0.2, was published 2026-10-07, two weeks before this change.

  • The header's pinning paragraph now says both jobs are pinned and why; ci.yml keeps its major tags (it holds only contents: read and nothing it builds is published), and is not touched here.

  • The header names the partial-upload remedy: a version PyPI holds with only some of its files is never completed (every re-run stops at the "already on PyPI" check, and nothing is uploaded by hand); the remedy is a new patch version through a new release prep and its tag; whether to yank the incomplete version is the maintainer's choice in the PyPI console, and the workflow never yanks or deletes anything.

  • The address step reads the project name from pyproject.toml: scripts/release_guard.py gains an address subcommand (address()), which runs the same check as version and prints https://pypi.org/project/<name>/<version>/ with both values from pyproject.toml. The step no longer writes uxid into the URL.

  • The guard script is under the coverage measurement: the gate's gate:test task and [tool.coverage.run] now measure scripts/ as well as the package (--cov=uxid --cov=scripts). Total coverage stays above the 90 percent floor (99.17 percent on this head; scripts/release_guard.py 97 percent, scripts/check_corpus.py 98 percent). Measuring the scripts was chosen over a header sentence saying they are unmeasured because their tests already exist and the measurement costs nothing; CLAUDE.md's Build and Test paragraph says what is measured.

No changelog fragment: changelog.d/README.md exempts the release workflow, tests and the gate.

Tests

Three new tests in tests/test_release_guard.py cover address (the name and version come from pyproject.toml; a tag naming another version refuses with nothing on standard output; the default pyproject.toml is the repository's). Each was sabotaged against its own mutation of address() or the default path and went red on an assertion, and the file was restored byte for byte.

Checks run

  • mise run gate green on this head.
  • actionlint 1.7.7 with shellcheck 0.10.0 (both through mise) over release.yml and ci.yml: no findings.
  • Nothing was tagged, no workflow was run and nothing was uploaded.

Review

Release workflow change: the conductor dispatches the cold review.

The verify job checks out the source, installs the toolchain and stores
the files the publish job uploads, so a moved major tag on any of its
actions could change what is published without reaching a token. Each
is now pinned to a full commit SHA with its version in a comment, the
way the publish job already was, at the version the major tag pointed
to: checkout v4.4.0, mise-action v4.3.0, upload-artifact v7.0.2.

The header now says what to do about a version PyPI holds with only
some of its files: a new patch version through a new release prep,
with any yank left to the maintainer in the PyPI console.

The address step reads the project name from pyproject.toml through a
new address subcommand of the guard script, and the gate's coverage
measurement now covers scripts/ as well as the package.

Refs: uxpy-9q0
@johnnyt
johnnyt merged commit 1545d37 into main Oct 21, 2026
5 checks passed
@johnnyt
johnnyt deleted the uxpy-9q0-pin-release-verify branch October 21, 2026 12:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant