name: ๐Ÿ” Sign PDFs # Can be triggered: # 1. Automatically after build.yml completes on main # 2. Manually, pointing at a specific build run to pull PDFs from on: workflow_dispatch: inputs: build_run_id: description: 'build.yml run ID to download PDFs from' required: true type: string # Download artifacts from other runs + commit export/ files back to the repo permissions: actions: read contents: write jobs: sign: name: Hash & Sign PDFs runs-on: ubuntu-latest outputs: light_sha256: ${{ steps.hashes.outputs.light_sha256 }} dark_sha256: ${{ steps.hashes.outputs.dark_sha256 }} light_b2: ${{ steps.hashes.outputs.light_b2 }} dark_b2: ${{ steps.hashes.outputs.dark_b2 }} steps: - name: ๐Ÿ› ๏ธ Checkout (for pgp/ key reference only) uses: actions/checkout@v4 with: sparse-checkout: pgp - name: ๐Ÿ“ฅ Download PDF artifacts uses: actions/download-artifact@v4 with: name: pdfs path: export/ run-id: ${{ inputs.build_run_id }} github-token: ${{ secrets.GITHUB_TOKEN }} - name: ๐Ÿ“‹ List downloaded files run: ls -lh export/ # Hash - extensions match export/ conventions: .sha256, .b2sum - name: "#๏ธโƒฃ Hash PDFs" id: hashes run: | cd export for f in thgtoa.pdf thgtoa-dark.pdf; do [ -f "$f" ] || continue sha256sum "$f" | awk '{print $1}' > "${f}.sha256" b2sum "$f" | awk '{print $1}' > "${f}.b2sum" done # Combined summary files sha256sum thgtoa.pdf thgtoa-dark.pdf 2>/dev/null > sha256sums.txt || \ sha256sum thgtoa.pdf 2>/dev/null > sha256sums.txt b2sum thgtoa.pdf thgtoa-dark.pdf 2>/dev/null > b2sums.txt || \ b2sum thgtoa.pdf 2>/dev/null > b2sums.txt light_sha256=$(cat thgtoa.pdf.sha256 2>/dev/null || echo "") dark_sha256=$(cat thgtoa-dark.pdf.sha256 2>/dev/null || echo "") light_b2=$(cat thgtoa.pdf.b2sum 2>/dev/null || echo "") dark_b2=$(cat thgtoa-dark.pdf.b2sum 2>/dev/null || echo "") echo "light_sha256=$light_sha256" >> $GITHUB_OUTPUT echo "dark_sha256=$dark_sha256" >> $GITHUB_OUTPUT echo "light_b2=$light_b2" >> $GITHUB_OUTPUT echo "dark_b2=$dark_b2" >> $GITHUB_OUTPUT echo "--- SHA-256 ---" cat sha256sums.txt echo "--- BLAKE2b ---" cat b2sums.txt # GPG sign โ€” detached ASCII-armor signatures use .asc extension - name: ๐Ÿ”‘ Install GPG run: | sudo apt-get update -qq sudo apt-get install -y gnupg - name: ๐Ÿ” Import GPG signing key env: GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} run: | echo "$GPG_PRIVATE_KEY" | gpg --batch --import echo "$GPG_PASSPHRASE" | gpg --batch --yes --passphrase-fd 0 \ --pinentry-mode loopback --list-secret-keys - name: ๐Ÿ” Sign PDFs and hash files env: GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} run: | sign() { local file="$1" [ -f "$file" ] || return 0 echo "$GPG_PASSPHRASE" | gpg --batch --yes --passphrase-fd 0 \ --pinentry-mode loopback \ --detach-sign --armor --output "${file}.asc" "$file" echo "Signed: $file โ†’ ${file}.asc" } sign export/thgtoa.pdf sign export/thgtoa-dark.pdf sign export/sha256sums.txt sign export/b2sums.txt # Commit export/ back to main - name: ๐Ÿ“ฆ Checkout full repo for commit uses: actions/checkout@v4 with: ref: main fetch-depth: 0 path: repo - name: ๐Ÿ“‚ Copy export files into repo run: cp -v export/* repo/export/ - name: ๐Ÿ” Configure SSH commit signing run: | mkdir -p ~/.ssh echo "${{ secrets.ACTIONS_SSH_SIGNING_KEY }}" > ~/.ssh/signing_key chmod 600 ~/.ssh/signing_key git config --global gpg.format ssh git config --global user.signingKey ~/.ssh/signing_key git config --global commit.gpgSign true git config --global user.name "github-actions[bot]" git config --global user.email "github-actions[bot]@users.noreply.github.com" # If no change in git diff, do nothing - name: ๐Ÿ“ค Commit and push export/ to main working-directory: repo run: | git add export/ if git diff --cached --quiet; then echo "Nothing to commit โ€” export/ is already up to date." else git commit -S -m "chore(export): update PDFs, hashes and signatures [skip ci]" git push origin main fi # Upload artifacts for release.yml and verify job to consume - name: ๐Ÿ“ค Upload signatures artifact uses: actions/upload-artifact@v4 with: name: signatures path: | export/sha256sums.txt export/b2sums.txt export/thgtoa.pdf.sha256 export/thgtoa-dark.pdf.sha256 export/thgtoa.pdf.b2sum export/thgtoa-dark.pdf.b2sum export/thgtoa.pdf.asc export/thgtoa-dark.pdf.asc export/sha256sums.txt.asc export/b2sums.txt.asc if-no-files-found: error retention-days: 90 compression-level: 0 - name: ๐Ÿ“ค Upload signed PDFs artifact uses: actions/upload-artifact@v4 with: name: pdfs-signed path: | export/thgtoa.pdf export/thgtoa-dark.pdf if-no-files-found: warn retention-days: 90 compression-level: 0 # Verify โ€” runs after sign, surfaces results as a job summary verify: name: Verify hashes & signatures runs-on: ubuntu-latest needs: sign # Always run so the summary is visible even if sign partially failed if: always() steps: - name: ๐Ÿ› ๏ธ Checkout scripts and public key uses: actions/checkout@v4 with: sparse-checkout: | scripts/verify_pdf.py pgp - name: ๐Ÿ“ฅ Download signatures artifact uses: actions/download-artifact@v4 with: name: signatures path: export/ github-token: ${{ secrets.GITHUB_TOKEN }} - name: ๐Ÿ“ฅ Download signed PDFs artifact uses: actions/download-artifact@v4 with: name: pdfs-signed path: export/ github-token: ${{ secrets.GITHUB_TOKEN }} - name: ๐Ÿ”‘ Install GPG and import public key run: | sudo apt-get update -qq sudo apt-get install -y gnupg gpg --import pgp/anonymousplanet-release.asc - name: ๐Ÿ Set up Python uses: actions/setup-python@v5 with: python-version: "3.13" - name: ๐Ÿ” Run verify_pdf.py id: verify run: | # Capture output and exit code separately so we can write the # summary regardless of whether verification passed or failed. set +e output=$(python scripts/verify_pdf.py --hashes --signatures --export-dir export 2>&1) exit_code=$? set -e echo "exit_code=$exit_code" >> $GITHUB_OUTPUT # โ”€โ”€ Job summary โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ { if [ "$exit_code" -eq 0 ]; then echo "## โœ… Verification passed" else echo "## โŒ Verification failed" fi echo "" echo "**Run:** [\`${{ github.run_id }}\`](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})  ยท  **Commit:** [\`${GITHUB_SHA::7}\`](${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }})  ยท  **By:** \`${{ github.actor }}\`" echo "" echo "### Script output" echo '```' echo "$output" echo '```' echo "" echo "### Hashes" echo '```' echo "โ”€โ”€ SHA-256 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€" cat export/sha256sums.txt 2>/dev/null || echo "(not found)" echo "" echo "โ”€โ”€ BLAKE2b โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€" cat export/b2sums.txt 2>/dev/null || echo "(not found)" echo '```' } >> $GITHUB_STEP_SUMMARY # Propagate failure so the job is marked red if verification fails exit $exit_code