Skip to content

refactor(auth): modularize paseto internals #9

refactor(auth): modularize paseto internals

refactor(auth): modularize paseto internals #9

Workflow file for this run

name: CI and Release
on:
pull_request:
push:
branches:
- master
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v6
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.14"
- name: Install dependencies
run: uv sync --python 3.14
- name: Test
run: bash scripts/tests.sh
- name: Build docs
run: uv run --python 3.14 mkdocs build --strict
- name: Build package
run: uv build
- name: Download Syft
id: syft
uses: anchore/sbom-action/download-syft@v0
- name: Generate release integrity artifacts
env:
SYFT: ${{ steps.syft.outputs.cmd }}
run: bash scripts/generate-release-integrity-artifacts.sh dist .release-integrity.env
- name: Coveralls
if: github.event_name == 'push'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: uv run --python 3.14 coveralls --service=github
release:
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
needs: validate
runs-on: ubuntu-latest
permissions:
contents: write
outputs:
commit_sha: ${{ steps.release-metadata.outputs.commit_sha }}
released: ${{ steps.release-plan.outputs.released }}
tag: ${{ steps.release-plan.outputs.tag }}
version: ${{ steps.release-plan.outputs.version }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: astral-sh/setup-uv@v6
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.14"
- name: Install dependencies
run: uv sync --python 3.14
- name: Configure git author
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
- name: Check release baseline
id: release-baseline
run: |
current_version="$(python - <<'PY'
from pathlib import Path
import tomllib
pyproject = tomllib.loads(Path("pyproject.toml").read_text())
print(pyproject["project"]["version"])
PY
)"
if ! git rev-parse "v${current_version}" >/dev/null 2>&1; then
echo "::warning::No v${current_version} tag exists yet. Create a bootstrap tag for the current project version before relying on fully automated version bumps."
echo "ready=false" >> "$GITHUB_OUTPUT"
exit 0
fi
echo "ready=true" >> "$GITHUB_OUTPUT"
- name: Plan release
if: steps.release-baseline.outputs.ready == 'true'
id: release-plan
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
current_version="$(python - <<'PY'
from pathlib import Path
import tomllib
pyproject = tomllib.loads(Path("pyproject.toml").read_text())
print(pyproject["project"]["version"])
PY
)"
next_version="$(uv run --python 3.14 semantic-release version --print)"
if [ "$next_version" = "$current_version" ]; then
echo "released=false" >> "$GITHUB_OUTPUT"
echo "version=$current_version" >> "$GITHUB_OUTPUT"
echo "tag=v$current_version" >> "$GITHUB_OUTPUT"
exit 0
fi
echo "released=true" >> "$GITHUB_OUTPUT"
echo "version=$next_version" >> "$GITHUB_OUTPUT"
echo "tag=v$next_version" >> "$GITHUB_OUTPUT"
- name: Run semantic release
if: steps.release-baseline.outputs.ready == 'true' && steps.release-plan.outputs.released == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: uv run --python 3.14 semantic-release version
- name: Capture release metadata
if: steps.release-baseline.outputs.ready == 'true' && steps.release-plan.outputs.released == 'true'
id: release-metadata
run: echo "commit_sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
publish-package:
if: needs.release.outputs.released == 'true'
needs: release
runs-on: ubuntu-latest
permissions:
contents: write
attestations: write
id-token: write
steps:
- uses: actions/checkout@v4
with:
ref: ${{ needs.release.outputs.commit_sha }}
- uses: astral-sh/setup-uv@v6
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.14"
- name: Install dependencies
run: uv sync --python 3.14
- name: Build package
run: uv build
- name: Download Syft
id: syft
uses: anchore/sbom-action/download-syft@v0
- name: Generate release integrity artifacts
env:
SYFT: ${{ steps.syft.outputs.cmd }}
run: bash scripts/generate-release-integrity-artifacts.sh dist .release-integrity.env
- name: Load release integrity metadata
id: integrity
run: |
source .release-integrity.env
{
printf 'wheel=%s\n' "$wheel"
printf 'wheel_name=%s\n' "$wheel_name"
printf 'wheel_sbom=%s\n' "$wheel_sbom"
printf 'wheel_sbom_name=%s\n' "$wheel_sbom_name"
printf 'sdist=%s\n' "$sdist"
printf 'sdist_name=%s\n' "$sdist_name"
printf 'sdist_sbom=%s\n' "$sdist_sbom"
printf 'sdist_sbom_name=%s\n' "$sdist_sbom_name"
printf 'checksums=%s\n' "$checksums"
printf 'checksums_name=%s\n' "$checksums_name"
} >> "$GITHUB_OUTPUT"
- name: Attest build provenance
id: attest-provenance
uses: actions/attest-build-provenance@v3
with:
subject-path: |
${{ steps.integrity.outputs.wheel }}
${{ steps.integrity.outputs.sdist }}
show-summary: false
- name: Attest wheel SBOM
id: attest-wheel-sbom
uses: actions/attest-sbom@v3
with:
subject-path: ${{ steps.integrity.outputs.wheel }}
sbom-path: ${{ steps.integrity.outputs.wheel_sbom }}
show-summary: false
- name: Attest source distribution SBOM
id: attest-sdist-sbom
uses: actions/attest-sbom@v3
with:
subject-path: ${{ steps.integrity.outputs.sdist }}
sbom-path: ${{ steps.integrity.outputs.sdist_sbom }}
show-summary: false
- name: Upload workflow artifact
uses: actions/upload-artifact@v4
with:
name: python-package-${{ needs.release.outputs.tag }}
path: dist/*
- name: Upload release assets
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: gh release upload "${{ needs.release.outputs.tag }}" dist/* --clobber
- name: Publish verification summary
env:
RELEASE_TAG: ${{ needs.release.outputs.tag }}
RELEASE_COMMIT_SHA: ${{ needs.release.outputs.commit_sha }}
PROVENANCE_URL: ${{ steps.attest-provenance.outputs.attestation-url }}
WHEEL_SBOM_URL: ${{ steps.attest-wheel-sbom.outputs.attestation-url }}
SDIST_SBOM_URL: ${{ steps.attest-sdist-sbom.outputs.attestation-url }}
run: |
source .release-integrity.env
wheel_sha="$(awk -v file="$wheel_name" '$2 == file { print $1 }' "$checksums")"
sdist_sha="$(awk -v file="$sdist_name" '$2 == file { print $1 }' "$checksums")"
cat > dist/release-verification.md <<EOF
<!-- release-verification:start -->
## Verification
Release commit: \`$RELEASE_COMMIT_SHA\`
| Artifact | SHA256 | SPDX SBOM | Provenance | SBOM attestation |
| --- | --- | --- | --- | --- |
| \`$wheel_name\` | \`$wheel_sha\` | \`$wheel_sbom_name\` | [build provenance]($PROVENANCE_URL) | [SBOM attestation]($WHEEL_SBOM_URL) |
| \`$sdist_name\` | \`$sdist_sha\` | \`$sdist_sbom_name\` | [build provenance]($PROVENANCE_URL) | [SBOM attestation]($SDIST_SBOM_URL) |
Release assets also include \`$checksums_name\`, \`$wheel_sbom_name\`, and \`$sdist_sbom_name\`.
\`\`\`bash
gh release verify "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY"
gh release verify-asset "$RELEASE_TAG" "./$wheel_name" --repo "$GITHUB_REPOSITORY"
gh release verify-asset "$RELEASE_TAG" "./$sdist_name" --repo "$GITHUB_REPOSITORY"
gh attestation verify "./$wheel_name" --repo "$GITHUB_REPOSITORY" --signer-workflow .github/workflows/release.yml
gh attestation verify "./$wheel_name" --repo "$GITHUB_REPOSITORY" --signer-workflow .github/workflows/release.yml --predicate-type https://spdx.dev/Document/v2.3
\`\`\`
Install from Git with an immutable ref, not \`master\`. Example:
\`\`\`bash
pip install "fastapi-paseto @ git+https://github.com/Raze-Systems/fastapi-paseto.git@$RELEASE_TAG"
\`\`\`
\`pip\` and \`uv\` record VCS origin metadata in \`direct_url.json\`, but mutable branch installs cannot be strongly verified after the fact. Prefer a signed tag or commit hash and verify the matching release artifacts.
<!-- release-verification:end -->
EOF
cat dist/release-verification.md >> "$GITHUB_STEP_SUMMARY"
- name: Update release notes with verification details
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
RELEASE_TAG: ${{ needs.release.outputs.tag }}
run: |
gh release view "$RELEASE_TAG" --json body --jq .body > dist/release-body.md
python - <<'PY'
from pathlib import Path
start_marker = "<!-- release-verification:start -->"
end_marker = "<!-- release-verification:end -->"
body_path = Path("dist/release-body.md")
section = Path("dist/release-verification.md").read_text(encoding="utf-8").strip()
body = body_path.read_text(encoding="utf-8").strip()
if start_marker in body and end_marker in body:
prefix, remainder = body.split(start_marker, 1)
_, suffix = remainder.split(end_marker, 1)
body = prefix.rstrip()
suffix = suffix.lstrip()
merged = "\n\n".join(part for part in [body, section, suffix] if part)
else:
merged = "\n\n".join(part for part in [body, section] if part)
body_path.write_text(f"{merged}\n", encoding="utf-8")
PY
gh release edit "$RELEASE_TAG" --notes-file dist/release-body.md
build-release-docs:
if: needs.release.outputs.released == 'true'
needs: release
runs-on: ubuntu-latest
permissions:
contents: read
pages: write
steps:
- uses: actions/checkout@v4
with:
ref: ${{ needs.release.outputs.commit_sha }}
- uses: astral-sh/setup-uv@v6
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.14"
- uses: actions/configure-pages@v5
- name: Install dependencies
run: uv sync --python 3.14
- name: Build docs
run: uv run --python 3.14 mkdocs build --strict
- name: Upload Pages artifact
uses: actions/upload-pages-artifact@v4
with:
path: site
deploy-release-docs:
if: needs.release.outputs.released == 'true'
needs:
- build-release-docs
- publish-package
runs-on: ubuntu-latest
permissions:
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- id: deployment
uses: actions/deploy-pages@v4