Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
df51a39
馃懛 ci(news): validate bot fragments
gaborbernat Oct 20, 2026
161c21b
馃敡 build(deps): lock all project groups
gaborbernat Oct 20, 2026
21d9fe2
馃敡 build(docs): preserve RST section anchors
gaborbernat Oct 20, 2026
13124db
馃懛 ci: share release preparation
gaborbernat Oct 20, 2026
aad56db
馃敡 chore: ignore uv lock files
gaborbernat Oct 20, 2026
401f2bf
馃敡 build(readme): render PyPI description
gaborbernat Oct 20, 2026
cbf3bcc
馃悰 fix(meta): correct package metadata
gaborbernat Oct 20, 2026
7d8c3f5
馃懛 ci: refresh shared release actions
gaborbernat Oct 21, 2026
a9ce470
馃懛 ci: scope bot label token to job
gaborbernat Oct 21, 2026
519cfff
馃懛 ci: isolate release App key from tools
gaborbernat Oct 21, 2026
8875c27
docs: restore tagged release notes
gaborbernat Oct 21, 2026
758af13
ci: isolate release writes
gaborbernat Oct 21, 2026
4b9b321
馃悰 fix(release): target repository explicitly
gaborbernat Oct 21, 2026
1adbb77
馃摑 docs: show draft release entries
gaborbernat Oct 21, 2026
9f3be75
馃悰 fix(release): forward App key to workflow
gaborbernat Oct 21, 2026
c0db540
馃懛 ci(release): resume matching GitHub releases
gaborbernat Oct 21, 2026
15be3ee
馃懛 ci(release): verify partial uploads
gaborbernat Oct 21, 2026
84b1922
馃懛 ci(release): stage GitHub draft first
gaborbernat Oct 21, 2026
04cb910
build(release): update shared validation pins
gaborbernat Oct 21, 2026
be1ee4e
ci: require hook update cooldown
gaborbernat Oct 21, 2026
35a3cd3
style: format release workflows
gaborbernat Oct 21, 2026
9445394
style: format hook configuration
gaborbernat Oct 21, 2026
cb861e8
馃懛 ci: use named shared Action steps
gaborbernat Oct 21, 2026
0d331b0
馃敡 chore(release): use live workflow
gaborbernat Oct 22, 2026
f07d763
馃懛 ci(release): use published tooling
gaborbernat Oct 22, 2026
5e49847
馃懛 ci: adopt shared release controls
gaborbernat Oct 22, 2026
f4f7cde
馃悰 fix(release): lock caller build backends
gaborbernat Oct 22, 2026
264f1ba
馃悰 fix(release): pin corrected shared actions
gaborbernat Oct 22, 2026
93d9560
馃悰 fix(release): pin portable shared actions
gaborbernat Oct 22, 2026
1f3f38e
馃懛 ci: narrow release tool installs
gaborbernat Oct 22, 2026
6acf036
馃敡 chore(format): check YAML formatting
gaborbernat Oct 22, 2026
c517aca
馃懛 ci: adopt shared release pipeline
gaborbernat Oct 23, 2026
db508d2
馃懛 ci: run news checks separately
gaborbernat Oct 23, 2026
8aedb82
馃懛 ci: tolerate existing PyPI files
gaborbernat Oct 23, 2026
2753aab
馃懛 ci: pin released release tools
gaborbernat Oct 23, 2026
c353cb7
馃懛 ci: remove duplicate release PATH setup
gaborbernat Oct 23, 2026
a141b8f
馃懛 ci: reuse previous tag for release notes
gaborbernat Oct 23, 2026
54bfec2
build: adopt released workflow actions
gaborbernat Oct 23, 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
12 changes: 12 additions & 0 deletions .github/dependabot.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,15 @@ updates:
- "*"
cooldown:
default-days: 7
- package-ecosystem: "pre-commit"
directory: "/"
labels:
- dependencies
- skip news
schedule:
interval: "weekly"
day: "tuesday"
cooldown:
default-days: 7
allow:
- dependency-name: sphinx
51 changes: 23 additions & 28 deletions .github/workflows/check.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,14 @@ 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

permissions:
contents: read

jobs:
test:
name: test ${{ matrix.env }} - ${{ matrix.os }}
Expand All @@ -35,12 +32,12 @@ jobs:
- pkg_meta
os:
- ubuntu-26.04
- windows-latest
- macos-latest
- windows-2025-vs2026
- macos-26
exclude:
- { os: macos-latest, env: "type" }
- { os: macos-latest, env: "dev" }
- { os: macos-latest, env: "pkg_meta" }
- {os: macos-26, env: "type"}
- {os: macos-26, env: "dev"}
- {os: macos-26, env: "pkg_meta"}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
Expand All @@ -49,14 +46,15 @@ jobs:
- name: Install the latest version of 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"
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Add .local/bin to Windows PATH
if: runner.os == 'Windows'
shell: bash
run: echo "$USERPROFILE/.local/bin" >> $GITHUB_PATH
run: echo "$USERPROFILE/.local/bin" >>"$GITHUB_PATH"
- name: Install tox
run: uv tool install --python-preference only-managed --python 3.15 tox --with tox-uv
- name: Install Python
Expand All @@ -72,34 +70,31 @@ jobs:
PYTEST_ADDOPTS: "-vv --durations=20"
DIFF_AGAINST: HEAD
UV_PYTHON_PREFERENCE: only-managed

changelog:
if: github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'skip news')
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
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}"

- uses: tox-dev/github-workflows/check-hook-update-cooldown@74472c1a3602ca9b66db058ea31578865e1d208b # v1.0.5
env:
GH_TOKEN: ${{ github.token }}
gate:
name: Required checks pass
if: always()
needs: [test, changelog]
if: ${{ !cancelled() }} # codespell:ignore cancelled
needs: [test, hook-update-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-update-cooldown" and .value.result == "skipped" and $event != "pull_request")
)
' <<< "$NEEDS_JSON"
' <<<"$NEEDS_JSON"
29 changes: 29 additions & 0 deletions .github/workflows/news.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
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:
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@74472c1a3602ca9b66db058ea31578865e1d208b # v1.0.5
with:
groups: release-news
venv: ${{ runner.temp }}/release-tools
- uses: tox-dev/github-workflows/validate-news@74472c1a3602ca9b66db058ea31578865e1d208b # v1.0.5
116 changes: 16 additions & 100 deletions .github/workflows/prepare-release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,108 +2,24 @@ name: Prepare release
on:
workflow_dispatch:
inputs:
version:
description: Version to release, without a v prefix
bump:
description: Version bump strategy
required: true

type: choice
options:
- auto
- major
- minor
- patch
default: auto
permissions: {}

concurrency:
group: prepare-release
cancel-in-progress: false

jobs:
assemble:
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-26.04
prepare:
uses: tox-dev/github-workflows/.github/workflows/prepare-towncrier-release.yaml@74472c1a3602ca9b66db058ea31578865e1d208b # v1.0.5
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 'docs/changelog.d/*.rst' > /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
docs/changelog.rst|docs/changelog.d/*.rst) ;;
*) echo "::error::unexpected release path: $path"; exit 1 ;;
esac
done < <(git diff --cached --name-only)
grep -Fq "v$VERSION (" docs/changelog.rst
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"
pull-requests: read
secrets:
RELEASE_APP_PRIVATE_KEY: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
with:
bump: ${{ inputs.bump }}
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@74472c1a3602ca9b66db058ea31578865e1d208b # v1.0.5
with:
workflow: check.yaml
job-name: hook-update-cooldown
Loading
Loading