Skip to content

👷 ci(release): move releases to shared workflows - #282

Draft
gaborbernat wants to merge 1 commit into
pytest-dev:mainfrom
gaborbernat:fix/release-contract
Draft

gaborbernat wants to merge 1 commit into
pytest-dev:mainfrom
gaborbernat:fix/release-contract

Conversation

@gaborbernat

@gaborbernat gaborbernat commented Oct 20, 2026 •

Copy link
Copy Markdown
Collaborator

pytest-env's releases ran on two hand-written workflows of 89 lines each, mostly inline bash: one picked the next version, stitched fragments into CHANGELOG.md and pushed the tag, the other built, uploaded and wrote the GitHub Release. This PR hands that work to tox-dev/github-workflows 6.0.1 and release-creator 4.1.0. 🚀 Maintainers can now dispatch with auto and get the bump from the fragments, write named fragments in ten categories, and get pull request links, author credits and a New Contributors list in the release notes.

Architecture

Pull requests run the shared validate-news check as 📝 News fragment. A human PR needs a fragment or the skip news label, the check waives bot PRs, and it validates every fragment a PR adds. Following the 6.0.0 change, the fragment, Towncrier config and lock checks run only when the PR touches what they validate; the fragment requirement runs on every PR. The test workflow keeps only the test matrix behind 🚦 Required checks pass.

A manual dispatch calls the shared prepare workflow. Its first job holds no write credentials: Towncrier builds the changelog and release-creator links each fragment to its pull request, credits the commit authors and co-authors, and adds new contributors. A second job in the release-auth environment mints a token for the repository's GitHub App and pushes the release commit and tag. A weekly job, Tuesdays at 08:00 UTC, refreshes the release-tool lock through the same App.

The tag run splits into four jobs under a per-tag concurrency group. The shared build produces the distributions while a read-only job renders the GitHub Release notes from CHANGELOG.md. PyPI publishing keeps trusted publishing with attestations in the release environment. The last job compares the local files with what PyPI now serves and only then creates the GitHub Release. 🔐 That digest check stays because it guards an external store.

flowchart LR
    PR[Pull request] --> News[📝 News fragment]
    Dispatch[Manual dispatch] --> Assemble[Assemble changelog<br/>read-only]
    Assemble --> Push[Push commit and tag<br/>release App]
    Push --> Build[🏗️ Build]
    Push --> Notes[📝 Render notes]
    Build --> PyPI[🚀 Publish to PyPI]
    Notes --> PyPI
    PyPI --> GHR[🚀 Publish GitHub Release<br/>after digest check]
    classDef pr fill:#d8ecd8,stroke:#2f8132,color:#0f2a10;
    classDef write fill:#ffe3a3,stroke:#d29200,color:#3a2c00;
    classDef read fill:#cfe4ff,stroke:#1f6feb,color:#0b1f3a;
    class PR,News pr;
    class Dispatch,Assemble,Build,Notes read;
    class Push,PyPI,GHR write;
Loading

We trust people with write access, so the checks that only re-validated their own actions outside a pull request are gone: the tag-time lookup of a matching changelog heading, the check that the built files carry the tag's version, and prepare's refusals to bump to an existing tag or to release malformed or empty fragments. github-workflows 6.0.0 drops the matching shared checks (tag identity, the tag-time lock job, artifact shape, and prepare's re-validation of its own patch). PR-time checks still run.

Dependencies and packaging

The release tools install from a pylock.toml that Nab generates for every dependency group on CPython 3.15 linux_x86_64, the platform the release jobs run on, and the fix, pkg_meta and type tox environments now install from dependency groups too. The release groups require release-creator 4.1, and a temporary uploaded-prior-to exception admits 4.1.0, uploaded 2026-10-26, ahead of the seven-day cutoff; it is marked for deletion after 2026-11-02.

PyYAML 6.0.3 has no CPython 3.15 wheel, so the lock carries its sdist and a build group locks Cython and setuptools alongside the hatchling plugins. Hook updates move to Dependabot's pre-commit ecosystem on Tuesdays with a seven-day cooldown and the skip news label, hook revisions are pinned to frozen commit SHAs, and pre-commit.ci autoupdates quarterly.

CHANGELOG.md now covers all 29 PyPI versions with user-facing categories, stable anchors and human credits. PyPI lists 0.5, 0.5.1, 0.6.0 and 0.6.1, which have no tag in the repository, so their entries link the version commits. 📝 The package metadata gains an SPDX MIT license expression, import-names, the Framework :: Pytest and Topic :: Software Development :: Testing classifiers and a new summary, and the README's links to CHANGELOG.md and the new CONTRIBUTING.md now point to GitHub on PyPI.

Rollout

  • The App key and client ID are set in the release-auth environment, which deploys from main only. A pytest-dev administrator still needs to install the release App on this repository and add it as a bypass actor to the main ruleset (1973547), which today lets only the repository admin role bypass, so the prepare job can push to main.
  • Before merging, run swap_required_checks.py pytest-dev/pytest-env 282 --apply to point ruleset 1973547 at the renamed checks: Required checks pass becomes 🚦 Required checks pass and changelog becomes 📝 News fragment; pre-commit.ci - pr stays.

🛡️ CodeQL

CodeQL moves from default setup to a committed workflow on ubuntu-26.04 scanning Actions and Python with the security-extended suite and ignoring tests, which removes the ubuntu-latest migration notice default setup produces. GitHub rejects advanced-setup results while default setup is on, so the 🛡️ Analyze jobs fail until default setup is turned off for this repository; that happens right before merging (switch_codeql.sh pytest-dev/pytest-env 282 --apply).

@gaborbernat gaborbernat added the skip news PR does not need a changelog entry label Oct 20, 2026
@gaborbernat gaborbernat changed the title 🔧 build(release): lock all project groups 👷 ci(release): adopt shared release workflow Oct 21, 2026
@gaborbernat gaborbernat added documentation Improvements or additions to documentation enhancement New feature or request and removed skip news PR does not need a changelog entry labels Oct 21, 2026
@gaborbernat
gaborbernat force-pushed the fix/release-contract branch 8 times, most recently from 4eae918 to ab94427 Compare October 26, 2026 20:58
@gaborbernat gaborbernat changed the title 👷 ci(release): adopt shared release workflow 👷 ci(release): move releases to shared workflows Oct 26, 2026
@gaborbernat
gaborbernat force-pushed the fix/release-contract branch 3 times, most recently from 950f755 to e96cf71 Compare October 26, 2026 23:40
Move pytest-env to the github-workflows 6.0.1 release path with
release-creator 4.1.0. A news check validates changelog fragments on PRs,
running each check only when the PR touches its files. Preparation runs the
shared Towncrier workflow, and a weekly job refreshes the release lock.

The tag workflow builds through validate-python-release while a read-only
notes job renders the release notes. PyPI uploads through OIDC without
skip-existing, then one job verifies the published digests and creates the
GitHub Release. The workflow runs under a per-tag concurrency group.

Writers are trusted, so checks that only re-validate their actions go: the
tag re-check before the GitHub Release, the redundant pull_request condition
on the news job, and the release-validate group. Every job and step gets an
emoji name; the gate becomes "🚦 Required checks pass" and the news job
"📝 News fragment", so the ruleset needs the new check names before merge.

Nab writes pylock.toml for every group from a [tool.nab] table, with a
temporary upload cutoff exception for release-creator 4.1.0. A build group
locks the hatchling backends with Cython and setuptools, because PyYAML
has no CPython 3.15 wheel. The unused release, release-check and
release-validate groups are gone.

Dependabot's pre-commit ecosystem and pre-commit.ci's quarterly
autoupdate handle hook updates, so there is no hook cooldown check.

CONTRIBUTING explains that credits come from commit authors and
co-authors and that preparation runs release-creator contributors.

The changelog is backfilled for every PyPI version, and the package
metadata and PyPI README links are fixed.

CodeQL moves from default setup to a committed advanced workflow on
ubuntu-26.04, scanning Actions and Python with security-extended and
ignoring tests.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant