Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
076ea19
馃悰 fix(release): check generated changelog
gaborbernat Oct 19, 2026
c878e06
馃┕ fix(release): validate the full release path
gaborbernat Oct 19, 2026
cec7b6e
馃悰 fix(release): match maintenance workflow name
gaborbernat Oct 19, 2026
c42c4e5
馃悰 fix(ci): rehearse dispatched lock updates
gaborbernat Oct 19, 2026
28284ff
馃懛 ci(release): share checked release Actions
gaborbernat Oct 20, 2026
6b59d61
馃懛 ci(release): reuse tagged notes guard
gaborbernat Oct 20, 2026
4d6df62
馃敡 build: pin corrected release notes
gaborbernat Oct 20, 2026
7438212
馃敡 build: pin checked release Actions
gaborbernat Oct 20, 2026
52786ee
馃悰 fix(ci): use the published Action SHA
gaborbernat Oct 20, 2026
1ef0249
馃И test(ci): rehearse shared publisher
gaborbernat Oct 20, 2026
04bd3ab
馃И test(ci): compare matching release fixtures
gaborbernat Oct 20, 2026
48cd62a
馃И test(ci): check publisher failure paths
gaborbernat Oct 20, 2026
ab5ebfb
馃И test(ci): include uncredited tag preview
gaborbernat Oct 20, 2026
c0a7333
馃И test(ci): rehearse manual PR dispatch
gaborbernat Oct 20, 2026
8f0cb6c
鈾伙笍 refactor(ci): share tagged release build
gaborbernat Oct 20, 2026
aec46f7
馃И test(ci): pin corrected publisher
gaborbernat Oct 20, 2026
ca1fa66
鈾伙笍 refactor(ci): reuse publisher check
gaborbernat Oct 20, 2026
eb90655
馃И test(ci): cover release rejection paths
gaborbernat Oct 20, 2026
29abee7
馃敡 build: rehearse corrected fragment credits
gaborbernat Oct 20, 2026
d742b52
馃И test(ci): cover numeric release notes
gaborbernat Oct 20, 2026
3d451ce
馃敀 Pin shared release validation head
gaborbernat Oct 20, 2026
063e6e2
馃敀 Pin author-credit rehearsal
gaborbernat Oct 20, 2026
e373a13
馃敀 Pin current release Actions
gaborbernat Oct 20, 2026
8c4680b
鈾伙笍 refactor(release): share PR upkeep
gaborbernat Oct 20, 2026
7eb213a
build(release): update shared workflow pin
gaborbernat Oct 20, 2026
1377444
test(release): rehearse lock refresh on PRs
gaborbernat Oct 20, 2026
9242773
build(release): align shared Action revision
gaborbernat Oct 20, 2026
a5ddf1e
build(deps): update shared release actions
gaborbernat Oct 20, 2026
d0deb0d
fix: pin validated shared workflow
gaborbernat Oct 20, 2026
3b6c194
fix: pin validated shared workflow
gaborbernat Oct 20, 2026
2bc47d9
build: validate shared workflow update
gaborbernat Oct 20, 2026
ab0a76b
馃悰 fix(release): hide CalVer bump
gaborbernat Oct 20, 2026
d7d5137
馃敡 build(deps): lock all project groups
gaborbernat Oct 20, 2026
d8642e0
馃懛 ci: remove temporary release jobs
gaborbernat Oct 20, 2026
56aee1e
馃敡 chore: ignore uv lock files
gaborbernat Oct 20, 2026
c917f58
馃悰 fix(readme): link PyPI files
gaborbernat Oct 20, 2026
61fe511
馃悰 fix(meta): correct package metadata
gaborbernat Oct 20, 2026
2ff79ff
docs: keep release history user-facing
gaborbernat Oct 21, 2026
84d5964
ci: refresh shared release action pin
gaborbernat Oct 21, 2026
5db1f70
馃懛 ci: scope bot label token to job
gaborbernat Oct 21, 2026
883d740
馃懛 ci: isolate release App key from tools
gaborbernat Oct 21, 2026
c777e76
馃懛 ci(release): isolate GitHub writes
gaborbernat Oct 21, 2026
83e39d9
馃敡 build: freeze release dependencies
gaborbernat Oct 21, 2026
721b639
馃摑 docs: record Python 3.11 support
gaborbernat Oct 21, 2026
1f3de81
馃懛 ci(release): share preparation workflow
gaborbernat Oct 21, 2026
cb49233
馃懛 ci: narrow bot label permissions
gaborbernat Oct 21, 2026
19ef952
馃懛 ci(release): verify retry artifacts
gaborbernat Oct 21, 2026
8011be8
fix: gate hook updates in release checks
gaborbernat Oct 21, 2026
cc72c33
馃懛 ci: use named shared Action steps
gaborbernat Oct 21, 2026
1155e4f
馃敡 chore(release): use live workflow
gaborbernat Oct 22, 2026
af670e1
馃懛 ci(release): use published tooling
gaborbernat Oct 22, 2026
9bf6daf
馃懛 ci: adopt shared release controls
gaborbernat Oct 22, 2026
757de08
馃悰 fix(release): lock caller build backends
gaborbernat Oct 22, 2026
ce99b33
馃悰 fix(release): pin corrected shared actions
gaborbernat Oct 22, 2026
6c191e3
馃悰 fix(release): pin portable shared actions
gaborbernat Oct 22, 2026
23c9429
馃懛 ci: narrow release tool installs
gaborbernat Oct 22, 2026
6516af0
馃悰 fix(changelog): align category aliases
gaborbernat Oct 22, 2026
e3699c9
馃懛 ci: adopt shared release pipeline
gaborbernat Oct 23, 2026
799a4e9
馃懛 ci: run news checks separately
gaborbernat Oct 23, 2026
84c4431
馃懛 ci: align release jobs with shared actions
gaborbernat Oct 23, 2026
2522585
馃敡 build(release): pin current tools
gaborbernat Oct 23, 2026
9fe4a46
鈾伙笍 refactor(ci): drop duplicate PATH steps
gaborbernat Oct 23, 2026
d2483a9
馃悰 fix(release): reuse selected previous tag
gaborbernat Oct 23, 2026
ce8c1d5
build: adopt released workflow actions
gaborbernat Oct 23, 2026
5ca5526
馃敡 build: adopt release tooling 2.0.0
gaborbernat Oct 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .github/dependabot.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,16 @@ updates:
- "*"
cooldown:
default-days: 7
- package-ecosystem: "pre-commit"
directory: "/"
labels:
- dependencies
- skip news
schedule:
interval: "weekly"
day: "tuesday"
allow:
- dependency-name: mdformat-gfm
- dependency-name: mdformat-ruff
cooldown:
default-days: 7
54 changes: 31 additions & 23 deletions .github/workflows/check.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ on:
branches: ["main"]
tags-ignore: ["**"]
pull_request:
types: [opened, synchronize, reopened, labeled, unlabeled]
types: [opened, synchronize, reopened]
schedule:
- cron: "0 8 * * *"
concurrency:
group: check-${{ github.ref }}
cancel-in-progress: true
cancel-in-progress: false
permissions:
contents: read
jobs:
Expand All @@ -34,9 +34,10 @@ jobs:
with:
fetch-depth: 0
persist-credentials: false
- name: Install the latest version of uv
- name: Set up uv
uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
with:
version: latest-known
python-version: "3.15"
enable-cache: false
cache-dependency-glob: "pyproject.toml"
Expand All @@ -45,39 +46,46 @@ jobs:
run: uv tool install --python-preference only-managed --python 3.15 tox --with tox-uv
- name: Install Python
if: startsWith(matrix.env, '3.') && matrix.env != '3.15'
run: uv python install --python-preference only-managed ${{ matrix.env }}
env:
MATRIX_ENV: ${{ matrix.env }}
run: uv python install --python-preference only-managed "$MATRIX_ENV"
- name: Setup test suite
run: tox run -vv --notest --skip-missing-interpreters false -e ${{ matrix.env }}
env:
MATRIX_ENV: ${{ matrix.env }}
run: tox run -vv --notest --skip-missing-interpreters false -e "$MATRIX_ENV"
- name: Run test suite
run: tox run --skip-pkg-install -e ${{ matrix.env }}
run: tox run --skip-pkg-install -e "$MATRIX_ENV"
env:
MATRIX_ENV: ${{ matrix.env }}
PYTEST_ADDOPTS: "-vv --durations=5"
changelog:
if: github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'skip news')
runs-on: ubuntu-24.04
hook-cooldown:
name: Hook update cooldown
if: github.event_name == 'pull_request'
runs-on: ubuntu-26.04
permissions:
actions: read
contents: read
pull-requests: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: tox-dev/github-workflows/check-hook-update-cooldown@e2d5e308194a31710039f3237b7157431c0e38b5 # v2.0.0
with:
fetch-depth: 0
persist-credentials: false
- uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
with:
python-version: "3.15"
enable-cache: false
- run: uvx towncrier check --compare-with "origin/${GITHUB_BASE_REF}"
release-app-client-id: ${{ vars.RELEASE_APP_CLIENT_ID }}
env:
GH_TOKEN: ${{ github.token }}
gate:
name: Required checks pass
if: always()
needs: [test, changelog]
runs-on: ubuntu-24.04
if: ${{ !cancelled() }} # codespell:ignore cancelled
needs: [test, hook-cooldown]
runs-on: ubuntu-26.04
steps:
- env:
NEEDS_JSON: ${{ toJSON(needs) }}
EVENT_NAME: ${{ github.event_name }}
run: |
jq -e '
jq -e --arg event "$EVENT_NAME" '
all(
to_entries[];
.value.result == "success" or
(.key == "changelog" and .value.result == "skipped")
((.key == "hook-cooldown") and .value.result == "skipped" and $event != "pull_request")
)
' <<< "$NEEDS_JSON"
' <<<"$NEEDS_JSON"
30 changes: 30 additions & 0 deletions .github/workflows/news.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: Changelog
on:
pull_request:
types: [opened, reopened, synchronize, labeled, unlabeled]
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
cancel-in-progress: true
permissions:
contents: read
jobs:
changelog:
name: News fragment
if: github.event_name == 'pull_request'
runs-on: ubuntu-26.04
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
persist-credentials: false
- uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
with:
version: latest-known
python-version: "3.15"
enable-cache: false
- name: Install locked release tools
uses: tox-dev/github-workflows/install-locked-tools@e2d5e308194a31710039f3237b7157431c0e38b5 # v2.0.0
with:
groups: release-news
venv: ${{ runner.temp }}/release-tools
- uses: tox-dev/github-workflows/validate-news@e2d5e308194a31710039f3237b7157431c0e38b5 # v2.0.0
119 changes: 19 additions & 100 deletions .github/workflows/prepare-release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,105 +2,24 @@ name: Prepare release
on:
workflow_dispatch:
inputs:
version:
description: Version to release, without a v prefix
bump:
description: Version bump type
required: true
permissions: {}
concurrency:
group: prepare-release
cancel-in-progress: false
type: choice
options:
- auto
- major
- minor
- patch
default: auto
permissions:
contents: read
pull-requests: read
jobs:
assemble:
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-26.04
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
persist-credentials: false
- uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
with:
python-version: "3.15"
enable-cache: false
- name: Build release patch
env:
VERSION: ${{ inputs.version }}
run: |
set -euo pipefail
if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "::error::expected a major.minor.patch version"
exit 1
fi
latest=$(git tag --list '[0-9]*' | sort -V | tail -n 1)
if [[ "$(printf '%s\n' "$latest" "$VERSION" | sort -V | tail -n 1)" != "$VERSION" || "$latest" == "$VERSION" ]]; then
echo "::error::$VERSION must be newer than $latest"
exit 1
fi
if ! compgen -G 'changelog.d/*.md' > /dev/null; then
echo "::error::no changelog fragments to release"
exit 1
fi
uvx towncrier build --yes --version "$VERSION"
git diff HEAD --binary > "$RUNNER_TEMP/release.patch"
test -s "$RUNNER_TEMP/release.patch"
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: release-patch
path: ${{ runner.temp }}/release.patch
if-no-files-found: error
push:
needs: assemble
runs-on: ubuntu-26.04
environment: release-auth
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
persist-credentials: false
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: release-patch
path: ${{ runner.temp }}/release-patch
- name: Validate release patch
env:
VERSION: ${{ inputs.version }}
run: |
set -euo pipefail
git fetch origin main
git reset --hard origin/main
git apply --check "$RUNNER_TEMP/release-patch/release.patch"
git apply --index "$RUNNER_TEMP/release-patch/release.patch"
while IFS= read -r path; do
case "$path" in
CHANGELOG.md|changelog.d/*.md) ;;
*) echo "::error::unexpected release path: $path"; exit 1 ;;
esac
done < <(git diff --cached --name-only)
grep -Fq "## $VERSION (" CHANGELOG.md
git diff --cached --check
- id: token
uses: actions/create-github-app-token@64ab729a6294000f5aa4a4bc0b73d1f32bc45510 # v3.3.0
with:
client-id: ${{ vars.RELEASE_APP_CLIENT_ID }}
private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
owner: ${{ github.repository_owner }}
repositories: ${{ github.event.repository.name }}
permission-contents: write
- name: Commit and tag the release
env:
VERSION: ${{ inputs.version }}
GH_TOKEN: ${{ steps.token.outputs.token }}
APP_SLUG: ${{ steps.token.outputs.app-slug }}
run: |
set -euo pipefail
app_user_id=$(gh api "/users/${APP_SLUG}[bot]" --jq .id)
git config user.name "${APP_SLUG}[bot]"
git config user.email "${app_user_id}+${APP_SLUG}[bot]@users.noreply.github.com"
git commit -m "chore: prepare $VERSION"
git tag "$VERSION"
gh auth setup-git --hostname github.com --force
git push --atomic origin HEAD:main "refs/tags/$VERSION"
prepare:
name: Prepare Towncrier release
uses: tox-dev/github-workflows/.github/workflows/prepare-towncrier-release.yaml@e2d5e308194a31710039f3237b7157431c0e38b5 # v2.0.0
with:
bump: ${{ inputs.bump }}
secrets:
RELEASE_APP_PRIVATE_KEY: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
18 changes: 18 additions & 0 deletions .github/workflows/recheck-hook-cooldown.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: Recheck hook cooldown
on:
schedule:
- cron: "17 7 * * *"
workflow_dispatch:
permissions: {}
jobs:
recheck:
runs-on: ubuntu-26.04
permissions:
actions: write
contents: read
pull-requests: read
steps:
- uses: tox-dev/github-workflows/recheck-hook-update-cooldown@e2d5e308194a31710039f3237b7157431c0e38b5 # v2.0.0
with:
workflow: check.yaml
job-name: Hook update cooldown
Loading
Loading