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
127 changes: 93 additions & 34 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,50 +1,114 @@
name: Publish release
name: Start release

on:
# The pypi job checks the dispatch, run identity, and main ancestry before consuming the artifact.
workflow_run: # zizmor: ignore[dangerous-triggers]
workflows: [Start release]
types: [completed]
branches: [main]
# Keep publishing in the dispatch that tested the sdist to avoid trusting artifacts from another run.
workflow_dispatch:
inputs:
version:
description: Version to publish from main, without the leading v
required: true
type: string

permissions: {}

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

env:
# Explicit installs use the Python archive hashes embedded in the pinned uv binary.
UV_PYTHON_DOWNLOADS: manual
UV_MANAGED_PYTHON: 'true'

jobs:
ci:
if: github.repository == 'astral-experiments/astral-html-python' && github.ref == 'refs/heads/main'
permissions:
contents: read
uses: $/.github/workflows/ci.yml

prepare:
name: Prepare the tested source distribution
needs: ci
runs-on: ubuntu-latest
permissions:
contents: read
outputs:
artifact-id: ${{ steps.upload.outputs.artifact-id }}
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: sdist-and-abi3
path: dist
- uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
with:
version: '0.13.6'
checksum: f29c3e94ffabd7e6f3ba521dba169a9b7ec12100bf3257087f0f24f6123f3645
enable-cache: false
- run: uv python install 3.14.10
- name: Select the tested sdist and its changelog entry
env:
VERSION: ${{ inputs.version }}
run: |
sdists=(dist/*.tar.gz)
if [[ ${#sdists[@]} != 1 || ${sdists[0]} != "dist/astral_html-$VERSION.tar.gz" ]]; then
echo "::error::Requested version $VERSION does not match the built sdist"
exit 1
fi
if git show-ref --verify --quiet "refs/tags/v$VERSION"; then
test "$(git rev-parse "refs/tags/v$VERSION^{commit}")" = "$GITHUB_SHA"
fi
mkdir release
awk -v heading="## $VERSION" '
/^## / { selected = ($0 == heading); next }
selected { print }
' CHANGELOG.md > release/notes.md
if ! grep -q '[^[:space:]]' release/notes.md; then
echo "::error::Missing changelog entry for $VERSION"
exit 1
fi
mv "${sdists[0]}" release/
printf 'v%s\n' "$VERSION" > release/tag
uv run --locked --no-build --only-group dev --python 3.14.10 python - <<'PY' > release/prerelease
import os
from packaging.version import Version

print(str(Version(os.environ["VERSION"]).is_prerelease).lower())
PY
- id: upload
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
# Each prepare attempt uploads its own bundle. Retried publishers use its immutable ID.
name: release-${{ github.run_attempt }}
path: release/
if-no-files-found: error

pypi:
# workflow_run has publishing privileges. Only consume a successful manual run in this repository.
if: >-
github.repository == 'astral-experiments/astral-html-python' &&
github.event.workflow_run.head_repository.full_name == github.repository &&
github.event.workflow_run.path == '.github/workflows/start-release.yml' &&
github.event.workflow_run.event == 'workflow_dispatch' &&
github.event.workflow_run.conclusion == 'success'
name: Publish to PyPI
needs: prepare
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/project/astral-html/
permissions:
actions: read
contents: read
id-token: write
id-token: write # Exchange the job's OIDC token for a short-lived PyPI credential.
steps:
- name: Verify the source commit is on main
env:
GH_TOKEN: ${{ github.token }}
RELEASE_SHA: ${{ github.event.workflow_run.head_sha }}
# workflow_run reports a short branch name, which a same-named tag could also have.
# Main may have been force-pushed since dispatch or while awaiting environment approval.
run: |
main_sha=$(gh api "repos/$GITHUB_REPOSITORY/git/ref/heads/main" --jq .object.sha)
gh api "repos/$GITHUB_REPOSITORY/compare/$RELEASE_SHA...$main_sha" --jq .status | grep -Ex 'ahead|identical'
gh api "repos/$GITHUB_REPOSITORY/compare/$GITHUB_SHA...$main_sha" --jq .status | grep -Ex 'ahead|identical'
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: release-${{ github.event.workflow_run.run_attempt }}
artifact-ids: ${{ needs.prepare.outputs.artifact-id }}
path: release
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ github.token }}
- uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
with:
version: '0.13.6'
Expand All @@ -53,30 +117,25 @@ jobs:
- run: uv publish --trusted-publishing always --check-url https://pypi.org/simple release/*.tar.gz

github:
needs: pypi
name: Create the GitHub release
needs: [prepare, pypi]
runs-on: ubuntu-latest
permissions:
actions: read
contents: write
contents: write # Create the version tag and its GitHub release.
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
# workflow_run's GITHUB_SHA can be newer than the commit CI tested.
ref: ${{ github.event.workflow_run.head_sha }}
persist-credentials: false
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: release-${{ github.event.workflow_run.run_attempt }}
artifact-ids: ${{ needs.prepare.outputs.artifact-id }}
path: release
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ github.token }}
- name: Tag the published commit and release its changelog entry
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
run: |
tag=$(cat release/tag)
gh auth setup-git
git push origin "HEAD:refs/tags/$tag"
# A retry can reuse the published commit's tag, but must never move an existing tag.
gh api "repos/$GH_REPO/git/refs" --method POST -f ref="refs/tags/$tag" -f sha="$GITHUB_SHA" --silent ||
test "$(gh api "repos/$GH_REPO/git/ref/tags/$tag" --jq .object.sha)" = "$GITHUB_SHA"
if gh release view "$tag" --json isDraft --jq '.isDraft == false' | grep -qx true; then
exit 0
fi
Expand Down
76 changes: 0 additions & 76 deletions .github/workflows/start-release.yml

This file was deleted.

Loading