Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
167 commits
Select commit Hold shift + click to select a range
0aa0cc7
🐛 fix(release): verify PR credits in notes
gaborbernat Oct 19, 2026
653c0cf
👷 ci: give Action tests PR read access
gaborbernat Oct 19, 2026
f0c484a
✨ feat(build): share Python release build
gaborbernat Oct 20, 2026
47a08c1
✨ feat(notes): share tagged note rendering
gaborbernat Oct 20, 2026
662d014
🔧 build: pin corrected release notes
gaborbernat Oct 20, 2026
d65cd55
👷 ci: rehearse tagged release workflow
gaborbernat Oct 20, 2026
eebfb35
🐛 fix(ci): scope release metadata access
gaborbernat Oct 20, 2026
0bd7b05
✨ feat(ci): share Python release validation
gaborbernat Oct 20, 2026
8a331bf
🐛 fix(ci): isolate pilot release artifacts
gaborbernat Oct 20, 2026
846a7da
🐛 fix(ci): match release rehearsal fixture
gaborbernat Oct 20, 2026
6fa0cc9
🧪 test(ci): reject invalid release inputs
gaborbernat Oct 20, 2026
ee98371
🧪 test(ci): preview tag notes without metadata
gaborbernat Oct 20, 2026
f239f59
✨ feat(ci): rehearse release from PR branch
gaborbernat Oct 20, 2026
e66230b
🐛 fix(ci): validate tag notes before build
gaborbernat Oct 20, 2026
6a27fa0
🐛 fix(ci): keep dispatch PR number explicit
gaborbernat Oct 20, 2026
bffa1a0
🧪 test(ci): reject stale tags and patch paths
gaborbernat Oct 20, 2026
031a000
🔧 build: test numbered fragment credits
gaborbernat Oct 20, 2026
24ee797
🧪 test(ci): cover numbered release fragments
gaborbernat Oct 20, 2026
23263fd
🧪 test(ci): reuse the tagged notes step
gaborbernat Oct 20, 2026
e3317a0
🔒 Reject release push without App token
gaborbernat Oct 20, 2026
057646c
🧪 Test missing release App key
gaborbernat Oct 20, 2026
fb051c6
🧪 Check bot credits in release notes
gaborbernat Oct 20, 2026
b5eca8e
🧪 Rehearse bot and shared credits
gaborbernat Oct 20, 2026
7d50b45
✨ feat(news): share PR news policy
gaborbernat Oct 20, 2026
ed3c13d
🐛 fix(news): count covered shell paths
gaborbernat Oct 20, 2026
91ac273
✨ feat(tools): share lock updater
gaborbernat Oct 20, 2026
99789bb
🐛 fix(news): read bot files as an array
gaborbernat Oct 20, 2026
2b8045f
🐛 fix(tools): retain caller PR wording
gaborbernat Oct 20, 2026
d5089c6
✨ feat(news): support legacy fragment paths
gaborbernat Oct 20, 2026
56f832a
✨ feat(release): rehearse RST changelogs
gaborbernat Oct 20, 2026
be1ec2c
🐛 fix(release): create fragment directory
gaborbernat Oct 20, 2026
84d39dc
test(release): cover RST fragment fixture
gaborbernat Oct 20, 2026
e6bd0e1
test(news): cover RST bot fragments
gaborbernat Oct 20, 2026
d1ba52b
feat(release): expose read-only lock preview
gaborbernat Oct 20, 2026
b504807
feat(release): validate RST issue links
gaborbernat Oct 20, 2026
cc2680d
fix(release): use matching Action revision
gaborbernat Oct 20, 2026
6f744fa
fix: format RST release notes with prek
gaborbernat Oct 20, 2026
7d9f41c
style: format RST action test
gaborbernat Oct 20, 2026
5e8bf7d
fix: pin validated release creator
gaborbernat Oct 20, 2026
60edc05
fix: validate RST release headings
gaborbernat Oct 20, 2026
8425555
fix: set identity for release rehearsal
gaborbernat Oct 20, 2026
bbcc22f
🐛 fix(news): accept numbered entries
gaborbernat Oct 20, 2026
65ca974
✨ feat(locks): refresh full project lock
gaborbernat Oct 20, 2026
8b2f837
🐛 fix(locks): reject uv lock changes
gaborbernat Oct 20, 2026
b33daf8
✨ feat(release): support prefixed tags
gaborbernat Oct 21, 2026
144f8e6
fix: validate linked RST release sections
gaborbernat Oct 21, 2026
621cdd6
✨ feat(ci): dispatch exact-head rehearsals
gaborbernat Oct 21, 2026
66a76e6
📝 docs: point rehearsal at review branch
gaborbernat Oct 21, 2026
d28e126
📝 docs: describe reusable rehearsal ref
gaborbernat Oct 21, 2026
4c06886
test: reject duplicate fragment links
gaborbernat Oct 21, 2026
e7a0700
👷 ci: split release workflow steps
gaborbernat Oct 21, 2026
a0b11f4
👷 ci: isolate release credentials
gaborbernat Oct 21, 2026
c72de07
🧪 test: cover invalid patch check mode
gaborbernat Oct 21, 2026
3a162d8
🐛 fix(release): strip v from changelog heading
gaborbernat Oct 21, 2026
5e11f22
✨ feat(release): share preparation job graph
gaborbernat Oct 21, 2026
dd527a2
👷 ci: expose package paths to rehearsal
gaborbernat Oct 21, 2026
2963888
🐛 fix(ci): reuse numbered rehearsal fragments
gaborbernat Oct 21, 2026
68ef6d0
🧪 test(release): rehearse JetBrains tags
gaborbernat Oct 21, 2026
6765d77
test(release): validate RST headings
gaborbernat Oct 21, 2026
db15ff3
test(release): rehearse Action tags
gaborbernat Oct 21, 2026
5aabff9
feat(news): label bots after PR checks
gaborbernat Oct 21, 2026
71d3c16
feat(notes): render package histories
gaborbernat Oct 21, 2026
72de095
test(release): sign local Action tags
gaborbernat Oct 21, 2026
28517d3
Refresh hook pins weekly
gaborbernat Oct 21, 2026
89e630c
Allow original PR numbers in fragments
gaborbernat Oct 21, 2026
7afb8fc
Harden release builds and lock refresh
gaborbernat Oct 21, 2026
5cf5a91
🧪 test(release): rehearse Quotaline locally
gaborbernat Oct 21, 2026
a1e1b8e
test(release): rehearse tox pipeline
Oct 21, 2026
657f432
Pin release-creator render fixes
gaborbernat Oct 21, 2026
6a3ee86
Verify signed ZIP in JetBrains rehearsal
gaborbernat Oct 21, 2026
d08dd30
Limit lock exceptions to reviewed uploads
gaborbernat Oct 21, 2026
7efdc4a
test(release): rehearse turbohtml native build
gaborbernat Oct 21, 2026
cd227ce
test(release): hash native build dependencies
gaborbernat Oct 21, 2026
ec26113
🧪 test(release): rehearse toml-fmt packages
gaborbernat Oct 21, 2026
4417ce2
🧪 test(release): rehearse weekly locks
gaborbernat Oct 21, 2026
d880023
✨ feat(release): reconcile GitHub releases
gaborbernat Oct 21, 2026
3bfcf2c
🐛 fix(ci): pass dry-run secret fixture
gaborbernat Oct 21, 2026
a250ca3
👷 ci(release): reconcile publication retries
gaborbernat Oct 21, 2026
3553fb1
🐛 fix(release): verify artifact partitions
gaborbernat Oct 21, 2026
d30ee73
🐛 fix(release): isolate the GitHub writer
gaborbernat Oct 21, 2026
6b0b2bf
🐛 fix(deps): cool frozen hook updates
gaborbernat Oct 21, 2026
6a57fba
✨ feat(marketplace): verify signed ZIP upload
gaborbernat Oct 21, 2026
16fb799
🐛 fix(locks): expire package cutoff floors
gaborbernat Oct 21, 2026
fb3063a
🐛 fix(release): require exact tag ref
gaborbernat Oct 21, 2026
e8d4290
🐛 fix(validate): forward tool group
gaborbernat Oct 21, 2026
11b186e
Install Rust checks in Quotaline rehearsal
gaborbernat Oct 21, 2026
03e2fb0
test: scope shell coverage to Action steps
gaborbernat Oct 21, 2026
d5a69a4
test: check exact Action tag refs
gaborbernat Oct 21, 2026
64c2da9
test: match attributed Quotaline note
gaborbernat Oct 21, 2026
983b5c7
🐛 fix(release): accept RST category headings
gaborbernat Oct 21, 2026
560223e
🧪 test(ci): report uncovered Action lines
gaborbernat Oct 21, 2026
0bba9c7
✨ feat(jetbrains): isolate plugin signing
gaborbernat Oct 21, 2026
1b12b7f
👷 ci: drop blanket shell coverage gate
gaborbernat Oct 21, 2026
d25a938
🧪 test(rst): reject real conflict markers
gaborbernat Oct 21, 2026
1100be8
✨ feat(ci): gate young bot hook updates
gaborbernat Oct 21, 2026
81cd413
✨ feat(release): reconcile follow-up PRs
gaborbernat Oct 21, 2026
ce26796
📝 docs: define retry and cooldown callers
gaborbernat Oct 21, 2026
3fc2aa3
🐛 fix(release): verify App-authored retry
gaborbernat Oct 21, 2026
89b4c94
👷 ci: finalize shared release checks
gaborbernat Oct 21, 2026
605a3c5
📝 docs: describe new release Actions
gaborbernat Oct 21, 2026
32b7e72
🧪 test: give PR retry tests a unique module
gaborbernat Oct 21, 2026
8dcf033
🐛 fix(ci): render cutoff floor cleanly
gaborbernat Oct 21, 2026
e00d086
fix: Restrict release writer paths
gaborbernat Oct 21, 2026
8db87d4
fix: Rehearse Python releases on PRs
gaborbernat Oct 21, 2026
0058b07
🐛 fix(release): preserve prefixed headings
gaborbernat Oct 21, 2026
9a4e76e
♻️ refactor(actions): expose writer steps
gaborbernat Oct 21, 2026
a32ce09
🔧 chore(release): drop PR rehearsals
gaborbernat Oct 22, 2026
aef333b
📝 docs(release): remove rehearsal claims
gaborbernat Oct 22, 2026
c8f36b3
🐛 fix(release): harden shared actions
gaborbernat Oct 22, 2026
2b4ed19
🐛 fix(lock): allow new release-creator
gaborbernat Oct 22, 2026
24e7c78
🐛 fix(release): secure cooldown and tool installs
gaborbernat Oct 22, 2026
5ac4a83
🐛 fix(release): reject closed post-release PRs
gaborbernat Oct 22, 2026
b9f9b75
🐛 fix(lock): use caller build hashes
gaborbernat Oct 22, 2026
33e08dd
🐛 fix(hooks): keep cooldown after force push
gaborbernat Oct 22, 2026
4edd8f8
🐛 fix(release): preserve formatter default
gaborbernat Oct 22, 2026
961281c
🐛 fix(lock): allow reviewed 0.5.1 upload
gaborbernat Oct 22, 2026
a040f8e
🐛 fix(lock): honor pinned tools on runners
gaborbernat Oct 22, 2026
2eb1334
🐛 fix(actions): resolve tools across runners
gaborbernat Oct 22, 2026
33db207
🐛 fix(actions): run validators by environment
gaborbernat Oct 22, 2026
dbf40c5
🎨 style(actions): wrap long commands
gaborbernat Oct 22, 2026
0fb57de
🐛 fix(release): harden shared actions
gaborbernat Oct 22, 2026
c709f15
🐛 fix(ci): use uv-managed Python on runners
gaborbernat Oct 22, 2026
0edeb02
🐛 fix(test): include Towncrier in job tools
gaborbernat Oct 22, 2026
bab4598
🧪 test(release): isolate PR author lookup
gaborbernat Oct 22, 2026
198d058
⚡ perf(release): narrow PyPI tool group
gaborbernat Oct 22, 2026
85dfe42
👷 ci: cancel superseded maintenance runs
gaborbernat Oct 22, 2026
27485a7
📝 docs: shorten PyPI workflow reference
gaborbernat Oct 22, 2026
6000bc0
🧪 test(release): exercise PyPI group input
gaborbernat Oct 22, 2026
9c25cb0
📝 docs: describe narrow updater group
gaborbernat Oct 22, 2026
4e89972
♻️ refactor(test): type Action inputs
gaborbernat Oct 22, 2026
2d37831
📝 docs: clarify release job dependencies
gaborbernat Oct 22, 2026
fea018e
♻️ refactor(test): keep bash command local
gaborbernat Oct 22, 2026
4a27eb7
📝 docs: define remaining Action inputs
gaborbernat Oct 22, 2026
7a9bd03
📝 docs: split long workflow explanations
gaborbernat Oct 22, 2026
fcba1cc
⚡ perf(ci): run shell checks separately
gaborbernat Oct 22, 2026
70cfff4
🐛 fix(jetbrains): upload from safe path
gaborbernat Oct 22, 2026
c57e6d3
🧪 test(towncrier): cover first RST release
gaborbernat Oct 22, 2026
eacb5bf
🐛 fix(release): retry failed release reads
gaborbernat Oct 22, 2026
e928649
🐛 fix(hooks): cool App update pull requests
gaborbernat Oct 22, 2026
7d52531
👷 ci: check all shell blocks and reruns
gaborbernat Oct 22, 2026
c3f9d59
🐛 fix(build): use commit time for archives
gaborbernat Oct 22, 2026
c3adc22
🐛 fix(prepare): check Git diff failures
gaborbernat Oct 22, 2026
e0687e3
🐛 fix(marketplace): guard signer outputs
gaborbernat Oct 22, 2026
5a77025
🐛 fix(release): check tag ancestry and labels
gaborbernat Oct 22, 2026
f8e2d68
🐛 fix(lock): combine backend build pins
gaborbernat Oct 22, 2026
adb4be2
🐛 fix(hooks): recheck mature bot updates
gaborbernat Oct 22, 2026
ba7d2db
🐛 fix(lock): merge pins with managed Python
gaborbernat Oct 22, 2026
da8c222
⚡ perf(hooks): filter old workflow runs
gaborbernat Oct 22, 2026
1beb48a
🧪 test(build): compare repeat artifacts
gaborbernat Oct 22, 2026
fdbb364
⚡ perf(ci): reuse hooks and shallow fetches
gaborbernat Oct 22, 2026
d719ab2
🐛 Fix prepare artifact handoff and news checkout
gaborbernat Oct 22, 2026
7803089
🔒 Verify prepared commit identity
gaborbernat Oct 22, 2026
ac3adfe
🧪 Align Action tests with declared inputs
gaborbernat Oct 22, 2026
ce0c6de
feat(release): share Nab-backed release actions
gaborbernat Oct 23, 2026
1a6a095
🐛 fix(ci): align release validation
gaborbernat Oct 23, 2026
e4432d5
🐛 fix(actions): close reviewed gaps
gaborbernat Oct 23, 2026
cb26da5
🧪 test(release): model squash PR lookup
gaborbernat Oct 23, 2026
0f92337
💄 style(shell): format inline Actions code
gaborbernat Oct 23, 2026
253474c
💄 style(news): match pinned shell formatter
gaborbernat Oct 23, 2026
dfef54b
🐛 fix(ci): trust runner TLS roots
gaborbernat Oct 23, 2026
8269cd8
🐛 fix(release): trust runner TLS roots
gaborbernat Oct 23, 2026
7148732
🐛 fix(tests): use installed build backend
gaborbernat Oct 23, 2026
b29d250
🐛 fix(tests): isolate build TLS settings
gaborbernat Oct 23, 2026
6657e53
🐛 fix(release): allow local tool source
gaborbernat Oct 23, 2026
c549001
🐛 fix(pypi): pass local tool source
gaborbernat Oct 23, 2026
553df96
🔧 build(release): lock creator 0.6.0
gaborbernat Oct 23, 2026
4061205
🧪 test(release): use runner trust store
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
10 changes: 10 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,13 @@ updates:
labels:
- dependencies
- skip news
- package-ecosystem: github-actions
directory: /check-hook-update-cooldown
cooldown:
default-days: 7
schedule:
interval: weekly
day: tuesday
labels:
- dependencies
- skip news
312 changes: 140 additions & 172 deletions .github/workflows/check.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,71 +3,135 @@ on:
push:
branches: [main]
pull_request:
types: [opened, reopened, synchronize, labeled, unlabeled]
workflow_dispatch:
permissions:
contents: read
concurrency:
group: check-${{ github.ref }}-${{ github.run_attempt > 1 && github.run_id || 'current' }}
cancel-in-progress: ${{ github.run_attempt == 1 }}
jobs:
check:
runs-on: ubuntu-26.04
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
fetch-depth: 1
persist-credentials: false
- uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.cache/prek
key: prek-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('.pre-commit-config.yaml') }}
- uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
with:
version: latest-known
python-version: "3.15"
enable-cache: false
- name: Install locked tools
run: |
uv venv --python 3.15 .venv
uv pip sync --preview-features pylock --python .venv/bin/python pylock.toml
echo "$PWD/.venv/bin" >>"$GITHUB_PATH"
enable-cache: true
cache-dependency-glob: |
pylock.toml
pyproject.toml
- uses: $/install-locked-tools
with:
groups: release-check
venv: .release-tools
- name: Add locked tools to PATH
run: echo "$PWD/.release-tools/bin" >>"$GITHUB_PATH"
- name: Check repository formatting
run: prek run --all-files
- name: Check Python tests
run: |
ruff format --check .
ruff check . --output-format=concise
ty check tests
- name: Measure Action shell coverage
run: |
sudo apt-get update
sudo apt-get install -y kcov
KCOV_BIN=kcov KCOV_OUTPUT="$RUNNER_TEMP/shell-coverage" pytest -q
kcov --merge "$RUNNER_TEMP/shell-coverage-merged" "$RUNNER_TEMP/shell-coverage"/*
python - <<'PY'
import json
import os
from pathlib import Path

import yaml

report = next(Path(os.environ["RUNNER_TEMP"], "shell-coverage-merged").rglob("codecov.json"))
coverage = json.loads(report.read_text())["coverage"]
expected = {
f"{path.parent.name}-{index}.sh"
for path in Path().glob("*/action.yml")
for index, _ in enumerate(yaml.safe_load(path.read_text())["runs"]["steps"])
}
if set(coverage) != expected or any(not lines or not all(lines.values()) for lines in coverage.values()):
raise SystemExit(f"Action shell coverage is below 100%: {coverage}")
PY
shell:
runs-on: ubuntu-26.04
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 1
persist-credentials: false
- uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.cache/prek
key: prek-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('.pre-commit-config.yaml') }}
- uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
with:
version: latest-known
python-version: "3.15"
enable-cache: true
cache-dependency-glob: |
pylock.toml
pyproject.toml
- uses: $/install-locked-tools
with:
groups: release-shell
venv: .release-tools
- name: Add locked tools to PATH
run: echo "$PWD/.release-tools/bin" >>"$GITHUB_PATH"
- name: Check inline shell
run: |
set -euo pipefail
script_dir=./inline-shell-check
mkdir "$script_dir"
trap 'rm -rf "$script_dir"' EXIT
for file in .github/workflows/*.yaml */action.yml; do
yq -rj '[.. | objects | select(has("run")) | .run] | .[] | . + "\u0000"' "$file" \
>"$RUNNER_TEMP/run-blocks"
index=0
while IFS= read -r -d '' script; do
echo "$file: run block $index"
printf '#!/usr/bin/env bash\n%s\n' "${script%$'\n'}" >"$RUNNER_TEMP/inline-shell.sh"
prek run shellcheck --files "$RUNNER_TEMP/inline-shell.sh"
prek run shfmt --files "$RUNNER_TEMP/inline-shell.sh"
script_file="$script_dir/run-${file//\//-}-$index.sh"
printf '#!/usr/bin/env bash\n%s\n' "${script%$'\n'}" >"$script_file"
index=$((index + 1))
done <"$RUNNER_TEMP/run-blocks"
done
prek run shellcheck --files "$script_dir"/*.sh
prek run shfmt --files "$script_dir"/*.sh
test:
runs-on: ubuntu-26.04
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 1
persist-credentials: false
- uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
with:
version: latest-known
python-version: "3.15"
enable-cache: true
cache-dependency-glob: |
pylock.toml
pyproject.toml
- uses: $/install-locked-tools
with:
groups: release-news release-shell
venv: .release-tools
- name: Add locked tools to PATH
run: echo "$PWD/.release-tools/bin" >>"$GITHUB_PATH"
- name: Install current test tools
run: |
uv venv --python 3.15 .venv
uv pip install --python .venv/bin/python --group test
- name: Run Action tests
run: .venv/bin/pytest -q -n auto
type:
runs-on: ubuntu-26.04
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
with:
version: latest-known
python-version: "3.15"
enable-cache: true
cache-dependency-glob: |
pylock.toml
pyproject.toml
- name: Check Python types
run: uvx --with tox-uv tox run -e type
changelog:
if: github.event_name == 'pull_request'
runs-on: ubuntu-26.04
Expand All @@ -78,161 +142,65 @@ jobs:
persist-credentials: false
- uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
with:
version: latest-known
python-version: "3.15"
enable-cache: false
- name: Require release notes
env:
SKIP_NEWS: ${{ contains(github.event.pull_request.labels.*.name, 'skip news') }}
BOT_AUTHOR: ${{ github.event.pull_request.user.type == 'Bot' }}
run: |
set -euo pipefail
if git diff --name-only "origin/${GITHUB_BASE_REF}...HEAD" -- 'changelog.d/*.md' | grep -q .; then
uv run --no-default-groups --group release towncrier check --compare-with "origin/${GITHUB_BASE_REF}"
elif [[ "$SKIP_NEWS" != true && "$BOT_AUTHOR" != true ]]; then
echo '::error::add a changelog fragment or apply the skip news label'
exit 1
fi
release-dry-run:
enable-cache: true
cache-dependency-glob: |
pylock.toml
pyproject.toml
- uses: $/install-locked-tools
with:
groups: release-news
venv: ${{ runner.temp }}/release-tools
- name: Add locked tools to PATH
run: echo "$RUNNER_TEMP/release-tools/bin" >>"$GITHUB_PATH"
- uses: $/validate-news
hook-cooldown:
if: github.event_name == 'pull_request'
runs-on: ubuntu-26.04
permissions:
actions: read
contents: read
pull-requests: read
steps:
- uses: $/check-hook-update-cooldown
release-tools-preview:
if: github.event_name == 'pull_request'
runs-on: ubuntu-26.04
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
persist-credentials: false
ref: ${{ github.event.pull_request.head.sha }}
- 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
run: |
uv venv --python 3.15 .venv
uv pip sync --preview-features pylock --python .venv/bin/python pylock.toml
echo "$PWD/.venv/bin" >>"$GITHUB_PATH"
- name: Rehearse a squash merge
id: fixture
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
set -euo pipefail
git checkout -B release-validation "$BASE_SHA"
git merge --squash "$HEAD_SHA"
printf 'Validate the release pipeline on this pull request.\n' \
>"changelog.d/+validation-${PR_NUMBER}.bugfix.md"
git add -A
git -c user.name=release-validation \
-c user.email=release-validation@users.noreply.github.com \
commit -m "Validate release pipeline (#${PR_NUMBER})"
git init --bare "$RUNNER_TEMP/release-origin.git"
git remote set-url origin "$RUNNER_TEMP/release-origin.git"
git push origin HEAD:main
echo "sha=$(git rev-parse HEAD)" >>"$GITHUB_OUTPUT"
- name: Dirty the release checkout
run: touch untracked-release-file
- id: dirty_prepare
name: Reject a dirty release checkout
uses: $/prepare-towncrier
continue-on-error: true
with:
changelog: CHANGELOG.md
format: markdown
patch: ${{ runner.temp }}/release.patch
- name: Check dirty checkout rejection
env:
OUTCOME: ${{ steps.dirty_prepare.outcome }}
run: |
test "$OUTCOME" = failure
rm untracked-release-file
- id: prepare
uses: $/prepare-towncrier
with:
changelog: CHANGELOG.md
format: markdown
patch: ${{ runner.temp }}/release.patch
- name: Restore the source commit
run: git reset --hard HEAD
- id: stale_validate
name: Reject a stale release branch
uses: $/validate-release-patch
continue-on-error: true
with:
version: ${{ steps.prepare.outputs.version }}
patch: ${{ runner.temp }}/release.patch
changelog: CHANGELOG.md
fragments: changelog.d
expected-sha: "0000000000000000000000000000000000000000"
- name: Check stale branch rejection
env:
OUTCOME: ${{ steps.stale_validate.outcome }}
run: test "$OUTCOME" = failure
- uses: $/validate-release-patch
with:
version: ${{ steps.prepare.outputs.version }}
patch: ${{ runner.temp }}/release.patch
changelog: CHANGELOG.md
fragments: changelog.d
expected-sha: ${{ steps.fixture.outputs.sha }}
- id: invalid_tag
name: Reject an invalid release tag
uses: $/commit-and-tag
continue-on-error: true
with:
version: bad tag
token: ${{ github.token }}
app-slug: github-actions
message: invalid release
- name: Check invalid tag rejection
env:
OUTCOME: ${{ steps.invalid_tag.outcome }}
run: test "$OUTCOME" = failure
- uses: $/commit-and-tag
with:
version: ${{ steps.prepare.outputs.version }}
token: ${{ github.token }}
app-slug: github-actions
message: release ${{ steps.prepare.outputs.version }}
- name: Render tagged notes
env:
VERSION: ${{ steps.prepare.outputs.version }}
run: |
set -euo pipefail
test -n "$(git ls-remote origin "refs/tags/$VERSION")"
previous=$(git tag --list | release-creator tags "$VERSION" --previous)
args=()
if [[ -n "$previous" ]]; then
args+=(--previous "$previous")
fi
release-creator notes --changelog CHANGELOG.md --tag "$VERSION" \
--repository "$GITHUB_REPOSITORY" "${args[@]}" \
>"$RUNNER_TEMP/release-notes.md"
test -s "$RUNNER_TEMP/release-notes.md"
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: release-dry-run
path: |
${{ runner.temp }}/release.patch
${{ runner.temp }}/release-notes.md
if-no-files-found: error
enable-cache: true
cache-dependency-glob: |
pylock.toml
pyproject.toml
- uses: $/refresh-release-lock
with:
lock-groups: >-
build lock-tools release-notes release-news release-prepare release-validate release-check release-attestations release-shell release-hooks release-hook-check
mode: check
gate:
name: Required checks pass
if: always()
needs: [check, changelog, release-dry-run]
name: ${{ github.event_name == 'pull_request' && 'Required checks pass' || 'Required checks pass (non-PR)' }}
if: ${{ !cancelled() }}
needs: [check, shell, test, type, changelog, hook-cooldown, release-tools-preview]
runs-on: ubuntu-26.04
steps:
- env:
NEEDS_JSON: ${{ toJSON(needs) }}
EVENT_NAME: ${{ github.event_name }}
run: |
jq -e '
all(
to_entries[];
.value.result == "success" or
(.key == "changelog" and .value.result == "skipped") or
(.key == "release-dry-run" and .value.result == "skipped")
($ENV.EVENT_NAME != "pull_request" and .value.result == "skipped" and
(.key == "changelog" or .key == "hook-cooldown" or .key == "release-tools-preview"))
)
' <<<"$NEEDS_JSON"
Loading
Loading