Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
29 changes: 1 addition & 28 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,36 +9,9 @@ concurrency:
jobs:
check:
name: 🧹 Check formatting
runs-on: ubuntu-26.04
uses: $/.github/workflows/cooled-hooks.yaml
permissions:
contents: read
steps:
- name: 📥 Check out repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 1
persist-credentials: false
- name: 💾 Restore prek cache
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.cache/prek
key: prek-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('.pre-commit-config.yaml') }}
- name: 🐍 Install uv and Python
uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with:
version: latest-known
python-version: "3.15"
enable-cache: true
cache-suffix: ${{ github.job }}
cache-dependency-glob: pyproject.toml
- name: 📦 Install release tools
env:
EXCLUDE_NEWER: 7 days
run: |
tools/install_live_tools.sh .release-tools 'prek>=0.2'
uv run --no-project --python .release-tools python tools/expose_locked_commands.py .release-tools
- name: 🧹 Check repository formatting
run: tools/cooled_prek.sh run --all-files
shell:
name: 🧹 Check inline shell
runs-on: ubuntu-26.04
Expand Down
48 changes: 48 additions & 0 deletions .github/workflows/cooled-hooks.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: Cooled hooks
on:
workflow_call:
inputs:
exclude-newer:
description: Upload cutoff for the lock reader the shared Actions install from PyPI
type: string
default: 7 days
exclude-newer-package:
description: Newline-separated name=date cutoffs that override exclude-newer for single packages
type: string
default: ""
permissions: {}
jobs:
hooks:
name: 🧹 Hooks under the cooldown
runs-on: ubuntu-26.04
permissions:
contents: read
steps:
- name: 📥 Check out repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 1
persist-credentials: false
- name: 💾 Restore prek cache
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.cache/prek
# A changed hook or cutoff exception misses the cache, so its environment builds under the cutoff again.
key: prek-cooled-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('.pre-commit-config.yaml', 'pyproject.toml') }}
- name: 🐍 Install uv and Python
uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with:
version: latest-known
python-version: "3.15"
enable-cache: false
- name: 📦 Install locked release tools
uses: $/install-locked-tools
with:
groups: release-prepare
venv: ${{ runner.temp }}/release-tools
exclude-newer: ${{ inputs.exclude-newer }}
exclude-newer-package: ${{ inputs.exclude-newer-package }}
- name: 🧹 Run every hook under the cooldown
uses: $/cooled-prek
with:
args: run --all-files --show-diff-on-failure
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ at least the listed permissions; "App key" means it also passes the `RELEASE_APP
| Workflow | Purpose | Caller grants | Docs |
| --------------------------------- | ------------------------------------------------------------------------------ | ------------------------------------------------ | ------------------------------------------------------------------------------------------ |
| `check.yaml` | Run tox environments, one job per row, with a gate. | `contents: read` | [PR checks](docs/pr-checks.md#run-tox-environments-checkyaml) |
| `cooled-hooks.yaml` | Run every prek hook over the tree, environments built under the cutoff. | `contents: read` | [PR checks](docs/pr-checks.md#run-hooks-under-the-cooldown-cooled-hooksyaml) |
| `prepare-towncrier-release.yaml` | Build the Towncrier changelog, push the release commit and tag; or dry run. | `contents: read`, `pull-requests: read`; App key | [Prepare](docs/release-prepare.md#prepare-towncrier-releaseyaml) |
| `prepare-release-creator.yaml` | Same for multi-package or Rust repositories with a caller-assembled changelog. | `contents: read`, `pull-requests: read`; App key | [Prepare](docs/release-prepare.md#prepare-release-creatoryaml) |
| `validate-python-release.yaml` | Build a wheel and sdist from the tag against locked build constraints. | `contents: read` | [Publish](docs/release-publish.md#validate-python-releaseyaml) |
Expand Down
2 changes: 2 additions & 0 deletions changelog.d/+cooled-hooks.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
`cooled-hooks.yaml` runs every prek hook over the tree with environments built under the seven-day cutoff, so a pull
request fails when a hook would break the scheduled maintenance pull requests or release preparation.
29 changes: 29 additions & 0 deletions docs/pr-checks.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,35 @@ Check names are `<caller job name> / 🧪 <env> on <os>`; with the example's `
`🧪 Test / 🧪 3.15 on ubuntu-26.04`. The reusable gate shows up as `🧪 Test / 🚦 Required checks pass`; require the caller's
gate, not that one. The test jobs run project code, so grant them `contents: read` only.

## Run hooks under the cooldown: `cooled-hooks.yaml`

pre-commit.ci and a contributor's machine build hook environments from the newest releases. The scheduled maintenance
pull requests and release preparation build them with packages from the last seven days held back. A hook bumped to a
release younger than that, or one that rewrites files when its environment is built fresh, passes the pull request and
then fails those scheduled jobs. This workflow runs every hook over the whole tree the way they do, so the pull request
fails instead.

```yaml
hooks:
name: 🧹 Hooks under the cooldown
uses: tox-dev/github-workflows/.github/workflows/cooled-hooks.yaml@SHA # 6.7.0
permissions:
contents: read
gate:
name: 🚦 Required checks pass
if: ${{ !cancelled() }}
needs: [test, hooks]
```

| Input | Default | Meaning |
| ----------------------- | -------- | -------------------------------------------------------------------------------------- |
| `exclude-newer` | `7 days` | Upload cutoff for the lock reader that installs the caller's locked `release-prepare`. |
| `exclude-newer-package` | empty | Newline-separated `name=date` cutoffs that override `exclude-newer` for one package. |

prek comes from the caller's `release-prepare` lock group. Hook environments read cutoff exceptions from `[tool.nab]` in
`pyproject.toml`, as [`cooled-prek`](locked-tools.md#run-prek-under-the-cutoff-cooled-prek) describes. The prek cache is
keyed on `.pre-commit-config.yaml` and `pyproject.toml`, so a changed hook or exception rebuilds its environment.

## Release dry run

Add the caller's prepare workflow as a job with `dry-run: true` and list it in the gate's `needs`. The pull request then
Expand Down