Skip to content

Verify published release evidence #3

Verify published release evidence

Verify published release evidence #3

Workflow file for this run

name: Release
on:
push:
tags:
- "v*.*.*"
workflow_dispatch:
inputs:
publish_to_pypi:
description: "Publish dist artifacts to PyPI through Trusted Publishing. Requires the pypi environment and PyPI publisher to already exist."
required: true
default: false
type: boolean
trusted_publisher_configured:
description: "Set to yes only after PyPI has a Trusted Publisher for this repo, workflow, and pypi environment."
required: true
default: "no"
type: choice
options:
- "no"
- "yes"
permissions:
contents: read
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false
jobs:
test:
name: Test Python ${{ matrix.python-version }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.11", "3.13"]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: pip
cache-dependency-path: |
requirements.txt
requirements-dev.txt
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install -r requirements-dev.txt
- name: Run tests
run: python -m pytest test_secure_vault.py test_release_workflow.py -v
build-release:
name: Build, attest, and upload release assets
runs-on: ubuntu-latest
needs: test
permissions:
contents: write
id-token: write
attestations: write
outputs:
release_id: ${{ steps.release-id.outputs.release_id }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.13"
cache: pip
cache-dependency-path: |
requirements.txt
requirements-dev.txt
- name: Install build dependencies
run: |
python -m pip install --upgrade pip
python -m pip install -r requirements-dev.txt
- name: Run release tests
run: python -m pytest test_secure_vault.py test_release_workflow.py -v
- name: Clean generated release outputs
run: rm -rf dist release-artifacts
- name: Build distributions
run: python -m build
- name: Generate release transparency artifacts
run: |
if [[ "${GITHUB_REF}" == refs/tags/v* ]]; then
python tools/generate_release_artifacts.py --require-tag --require-clean
else
python tools/generate_release_artifacts.py
fi
- name: Resolve release artifact directory
id: release-id
run: |
tag="$(git describe --tags --exact-match HEAD 2>/dev/null || true)"
if [[ -n "${tag}" ]]; then
release_id="${tag}"
else
version="$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml', 'rb'))['project']['version'])")"
release_id="${version}-$(git rev-parse --short HEAD)"
fi
test -d "release-artifacts/${release_id}"
echo "release_id=${release_id}" >> "${GITHUB_OUTPUT}"
- name: Upload distribution artifact
uses: actions/upload-artifact@v4
with:
name: python-dist
path: dist/*
if-no-files-found: error
- name: Upload transparency artifact
uses: actions/upload-artifact@v4
with:
name: release-transparency
path: release-artifacts/${{ steps.release-id.outputs.release_id }}/*
if-no-files-found: error
- name: Attest release artifacts
uses: actions/attest@v4
with:
subject-path: |
${{ github.workspace }}/dist/*
${{ github.workspace }}/release-artifacts/${{ steps.release-id.outputs.release_id }}/*
- name: Publish GitHub release assets
if: startsWith(github.ref, 'refs/tags/v')
env:
GH_TOKEN: ${{ github.token }}
RELEASE_ID: ${{ steps.release-id.outputs.release_id }}
run: |
notes="$(mktemp)"
cat > "${notes}" <<'EOF'
This release publishes Python distribution files plus SBOM, checksum, and local provenance artifacts for reviewability.
AES Secure Vault remains an educational authenticated-encryption tool. These release artifacts and GitHub attestations are supply-chain transparency evidence, not production cryptography certification.
EOF
assets=(dist/* release-artifacts/${RELEASE_ID}/*)
if gh release view "${GITHUB_REF_NAME}" >/dev/null 2>&1; then
gh release upload "${GITHUB_REF_NAME}" "${assets[@]}" --clobber
else
gh release create "${GITHUB_REF_NAME}" "${assets[@]}" --title "AES Secure Vault ${GITHUB_REF_NAME}" --notes-file "${notes}"
fi
- name: Verify assets as published
if: startsWith(github.ref, 'refs/tags/v')
env:
GH_TOKEN: ${{ github.token }}
run: |
python tools/verify_published_release.py \
--repository "${GITHUB_REPOSITORY}" \
--tag "${GITHUB_REF_NAME}" \
--expected-commit "${GITHUB_SHA}" \
--report published-release-verification.json
- name: Upload published verification result
if: startsWith(github.ref, 'refs/tags/v') && always()
uses: actions/upload-artifact@v4
with:
name: published-release-verification-${{ github.ref_name }}
path: published-release-verification.json
if-no-files-found: warn
pypi-preflight:
name: PyPI Trusted Publishing preflight
runs-on: ubuntu-latest
needs: build-release
if: github.event_name == 'workflow_dispatch' && inputs.publish_to_pypi == true
permissions:
contents: read
actions: read
steps:
- name: Stop unless this is a tagged release
run: |
if [[ "${GITHUB_REF}" != refs/tags/v* ]]; then
echo "::error::PyPI publishing is allowed only from a v* tag. Re-run this workflow from the release tag."
exit 1
fi
- name: Stop unless PyPI Trusted Publisher setup is confirmed
run: |
if [[ "${{ inputs.trusted_publisher_configured }}" != "yes" ]]; then
echo "::error::Configure the PyPI Trusted Publisher first, then re-run with trusted_publisher_configured=yes."
exit 1
fi
- name: Verify GitHub pypi environment exists
env:
GH_TOKEN: ${{ github.token }}
run: |
curl --fail --silent --show-error \
-H "Authorization: Bearer ${GH_TOKEN}" \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"https://api.github.com/repos/${GITHUB_REPOSITORY}/environments/pypi" >/dev/null
pypi-publish:
name: Publish to PyPI
runs-on: ubuntu-latest
needs: [build-release, pypi-preflight]
if: github.event_name == 'workflow_dispatch' && inputs.publish_to_pypi == true
environment:
name: pypi
url: https://pypi.org/project/aes-secure-vault/
permissions:
contents: read
id-token: write
steps:
- name: Download distribution artifact
uses: actions/download-artifact@v4
with:
name: python-dist
path: dist
- name: Publish package distributions to PyPI
uses: pypa/gh-action-pypi-publish@release/v1