name: 🚀 Release # Manual only — run this deliberately after build and sign are confirmed good. # Provide the exact version tag and the sign.yml run ID to pull artifacts from. on: workflow_dispatch: inputs: version: description: 'Release version tag (e.g. v1.2.4) — must not already exist' required: true type: string sign_run_id: description: 'sign.yml run ID to pull signatures and PDFs from' required: true type: string prerelease: description: 'Mark as pre-release?' required: false default: false type: boolean permissions: contents: write # create releases and tags actions: read # download artifacts from other runs jobs: release: name: Publish GitHub Release runs-on: ubuntu-latest steps: - name: 🛠️ Checkout (for tags and pgp/) uses: actions/checkout@v4 with: fetch-depth: 0 sparse-checkout: pgp # ------------------------------------------------------------------ # # Download artifacts from the specified sign run # ------------------------------------------------------------------ # - name: 📥 Download signatures artifact uses: actions/download-artifact@v4 with: name: signatures path: release/ run-id: ${{ inputs.sign_run_id }} github-token: ${{ secrets.GITHUB_TOKEN }} - name: 📥 Download signed PDFs artifact uses: actions/download-artifact@v4 with: name: pdfs-signed path: release/ run-id: ${{ inputs.sign_run_id }} github-token: ${{ secrets.GITHUB_TOKEN }} - name: 📋 List release assets run: ls -lh release/ # ------------------------------------------------------------------ # # Read hashes for the release body # ------------------------------------------------------------------ # - name: "#️⃣ Read hashes" id: hashes run: | read_hash() { cat "release/$1" 2>/dev/null || echo "(not built)"; } echo "light_sha256=$(read_hash thgtoa.pdf.sha256)" >> $GITHUB_OUTPUT echo "dark_sha256=$(read_hash thgtoa-dark.pdf.sha256)" >> $GITHUB_OUTPUT echo "light_b2=$(read_hash thgtoa.pdf.b2)" >> $GITHUB_OUTPUT echo "dark_b2=$(read_hash thgtoa-dark.pdf.b2)" >> $GITHUB_OUTPUT # ------------------------------------------------------------------ # # VirusTotal # ------------------------------------------------------------------ # - name: 🦠 Upload PDFs to VirusTotal id: vt uses: crazy-max/ghaction-virustotal@v5 with: vt_api_key: ${{ secrets.VT_API_KEY }} files: | release/thgtoa.pdf release/thgtoa-dark.pdf - name: 🔗 Build VT report URLs id: vt_urls run: | light_hash=$(cat release/thgtoa.pdf.sha256 2>/dev/null || echo "") dark_hash=$(cat release/thgtoa-dark.pdf.sha256 2>/dev/null || echo "") if [ -n "$light_hash" ]; then echo "light_vt=https://www.virustotal.com/gui/file/${light_hash}" >> $GITHUB_OUTPUT else echo "light_vt=(not built)" >> $GITHUB_OUTPUT fi if [ -n "$dark_hash" ]; then echo "dark_vt=https://www.virustotal.com/gui/file/${dark_hash}" >> $GITHUB_OUTPUT else echo "dark_vt=(not built)" >> $GITHUB_OUTPUT fi # ------------------------------------------------------------------ # # Validate explicit version input — refuse to auto-increment or # overwrite an existing tag # ------------------------------------------------------------------ # - name: 🏷️ Validate release tag id: tag run: | git fetch --tags --quiet VERSION="${{ inputs.version }}" # Enforce vX.Y.Z format if ! echo "$VERSION" | grep -qE '^v[0-9]+\.[0-9]+\.[0-9]+$'; then echo "::error::Version '$VERSION' is not valid semver. Use format: v1.2.3" exit 1 fi # Refuse to overwrite an existing tag if git tag --list | grep -qx "$VERSION"; then echo "::error::Tag '$VERSION' already exists. Bump the version." exit 1 fi echo "tag=$VERSION" >> $GITHUB_OUTPUT echo "name=$VERSION" >> $GITHUB_OUTPUT echo "Tag: $VERSION" # ------------------------------------------------------------------ # # Create GitHub Release # ------------------------------------------------------------------ # - name: 🚀 Create GitHub Release uses: softprops/action-gh-release@v2 with: tag_name: ${{ steps.tag.outputs.tag }} name: ${{ steps.tag.outputs.name }} prerelease: ${{ inputs.prerelease || false }} draft: false fail_on_unmatched_files: false body: | ## 📖 The Hitchhiker's Guide to Online Anonymity Built from [`${{ github.sha }}`](${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }}) on `${{ github.ref_name }}`. --- ### 📄 Release assets | File | Description | |------|-------------| | `thgtoa.pdf` | Light mode PDF | | `thgtoa-dark.pdf` | Dark mode PDF (hacker theme) | | `sha256sums.txt` | SHA-256 checksums (both files) | | `b2sums.txt` | BLAKE2b checksums (both files) | | `thgtoa.pdf.sha256` | SHA-256 — light PDF | | `thgtoa-dark.pdf.sha256` | SHA-256 — dark PDF | | `thgtoa.pdf.b2` | BLAKE2b — light PDF | | `thgtoa-dark.pdf.b2` | BLAKE2b — dark PDF | | `*.sig` | GPG detached signatures (ASCII armor) | --- ### #️⃣ Hashes **thgtoa.pdf** (light) ```text SHA-256 ${{ steps.hashes.outputs.light_sha256 }} BLAKE2b ${{ steps.hashes.outputs.light_b2 }} ``` **thgtoa-dark.pdf** (dark) ```text SHA-256 ${{ steps.hashes.outputs.dark_sha256 }} BLAKE2b ${{ steps.hashes.outputs.dark_b2 }} ``` --- ### 🔏 Verifying GPG signatures ```bash # Import the release signing key gpg --import pgp/anonymousplanet-release.asc # Verify PDFs gpg --verify thgtoa.pdf.sig thgtoa.pdf gpg --verify thgtoa-dark.pdf.sig thgtoa-dark.pdf # Verify hash files gpg --verify sha256sums.txt.sig sha256sums.txt gpg --verify b2sums.txt.sig b2sums.txt ``` --- ### 🦠 VirusTotal scans | File | Report | |------|--------| | `thgtoa.pdf` | ${{ steps.vt_urls.outputs.light_vt }} | | `thgtoa-dark.pdf` | ${{ steps.vt_urls.outputs.dark_vt }} | files: | release/thgtoa.pdf release/thgtoa-dark.pdf release/sha256sums.txt release/b2sums.txt release/thgtoa.pdf.sha256 release/thgtoa-dark.pdf.sha256 release/thgtoa.pdf.b2 release/thgtoa-dark.pdf.b2 release/thgtoa.pdf.sig release/thgtoa-dark.pdf.sig release/sha256sums.txt.sig release/b2sums.txt.sig