From 3184181fa8ebba7a4869283287f882b190c620e8 Mon Sep 17 00:00:00 2001 From: nopeitsnothing Date: Fri, 22 May 2026 16:20:46 -0400 Subject: [PATCH] ci: refactor pipeline into independent build/sign/release/changelog workflows Signed-off-by: nopeitsnothing --- .github/workflows/build-sign-release.yml | 215 +++++++++++++++++------ 1 file changed, 164 insertions(+), 51 deletions(-) diff --git a/.github/workflows/build-sign-release.yml b/.github/workflows/build-sign-release.yml index 982d985..5c4b6b2 100644 --- a/.github/workflows/build-sign-release.yml +++ b/.github/workflows/build-sign-release.yml @@ -1,25 +1,10 @@ -name: 📖 Build & Sign PDFs +# DEPRECATED — replaced by build.yml, sign.yml, and release.yml +# This file is kept temporarily so in-flight runs are not broken. +# +# name: 📖 Build & Sign PDFs on: - workflow_dispatch: - inputs: - build_mode: - description: 'PDF build mode' - required: true - default: 'both' - type: choice - options: - - light - - dark - - both - push: - branches: - - main - paths: - - "docs/**" - - "mkdocs.yml" - - "scripts/**" - - ".github/workflows/**" + workflow_dispatch: # manual only — no automatic triggers (deprecated) permissions: contents: write @@ -38,8 +23,13 @@ jobs: with: python-version: "3.13" - - name: 📦 Install MkDocs Material - run: pip install mkdocs-material + - name: 📦 Install Python dependencies + run: pip install mkdocs-material pillow numpy + + - name: 🖼️ Install poppler (pdftoppm) and qpdf + run: | + sudo apt-get update + sudo apt-get install -y poppler-utils qpdf - name: Setup Chrome uses: browser-actions/setup-chrome@v2 @@ -49,21 +39,71 @@ jobs: install-chromedriver: true - name: 🔑 Install GPG tools - run: | - sudo apt-get update - sudo apt-get install gnupg + run: sudo apt-get install -y gnupg - - name: 🖨️ Build & Hash PDFs + # ------------------------------------------------------------------ # + # Build PDFs + # ------------------------------------------------------------------ # + - name: 🖨️ Build PDFs env: CI: true - run: | - python scripts/build_guide_pdf.py --${{ inputs.build_mode || 'both' }} - for f in ./export/*.pdf; do - echo "sha256sums: $f"; sha256sum "$f" >> export/sha256sums.txt; done - for f in ./export/*.pdf; do - echo "b2sums: $f"; b2sum "$f" >> export/b2sums.txt; done + run: python scripts/build_guide_pdf.py --${{ inputs.build_mode || 'both' }} + # ------------------------------------------------------------------ # + # Hash (SHA-256 + BLAKE2b) + # ------------------------------------------------------------------ # + - name: #️⃣ Hash PDFs + id: hashes + run: | + mkdir -p export + + sha256sum export/thgtoa.pdf | awk '{print $1}' > export/thgtoa.pdf.sha256 + sha256sum export/thgtoa-dark.pdf | awk '{print $1}' > export/thgtoa-dark.pdf.sha256 + b2sum export/thgtoa.pdf | awk '{print $1}' > export/thgtoa.pdf.b2 + b2sum export/thgtoa-dark.pdf | awk '{print $1}' > export/thgtoa-dark.pdf.b2 + + # Also write combined human-readable files + sha256sum export/thgtoa.pdf export/thgtoa-dark.pdf > export/sha256sums.txt + b2sum export/thgtoa.pdf export/thgtoa-dark.pdf > export/b2sums.txt + + # Expose hashes as step outputs for the release body + echo "light_sha256=$(cat export/thgtoa.pdf.sha256)" >> $GITHUB_OUTPUT + echo "dark_sha256=$(cat export/thgtoa-dark.pdf.sha256)" >> $GITHUB_OUTPUT + echo "light_b2=$(cat export/thgtoa.pdf.b2)" >> $GITHUB_OUTPUT + echo "dark_b2=$(cat export/thgtoa-dark.pdf.b2)" >> $GITHUB_OUTPUT + + # ------------------------------------------------------------------ # + # GPG sign (detached .sig for each PDF + each hash file) + # ------------------------------------------------------------------ # + - name: 🔏 Import GPG key + env: + GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} + run: | + echo "$GPG_PRIVATE_KEY" | gpg --batch --import + # Pre-cache the passphrase so signing doesn't prompt + echo "$GPG_PASSPHRASE" | gpg --batch --yes --passphrase-fd 0 \ + --pinentry-mode loopback --list-secret-keys + + - name: 🔏 GPG sign PDFs and hash files + env: + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} + run: | + sign() { + echo "$GPG_PASSPHRASE" | gpg --batch --yes --passphrase-fd 0 \ + --pinentry-mode loopback \ + --detach-sign --armor --output "${1}.sig" "$1" + } + sign export/thgtoa.pdf + sign export/thgtoa-dark.pdf + sign export/sha256sums.txt + sign export/b2sums.txt + + # ------------------------------------------------------------------ # + # VirusTotal + # ------------------------------------------------------------------ # - name: 🦠 Upload PDFs to VirusTotal + id: vt uses: crazy-max/ghaction-virustotal@v5 with: vt_api_key: ${{ secrets.VT_API_KEY }} @@ -71,35 +111,108 @@ jobs: export/thgtoa.pdf export/thgtoa-dark.pdf - - name: 📊 Extract VT scan results - id: vt-scan + - name: 🔗 Build VT report URLs + id: vt_urls run: | - echo "status=completed" >> $GITHUB_OUTPUT + light_hash=$(cat export/thgtoa.pdf.sha256) + dark_hash=$(cat export/thgtoa-dark.pdf.sha256) + echo "light_vt=https://www.virustotal.com/gui/file/${light_hash}" >> $GITHUB_OUTPUT + echo "dark_vt=https://www.virustotal.com/gui/file/${dark_hash}" >> $GITHUB_OUTPUT - - name: 🔗 Generate VT report links + # ------------------------------------------------------------------ # + # Create GitHub Release + # ------------------------------------------------------------------ # + - name: 🏷️ Generate release tag + id: tag run: | - # Create a markdown file with VT scan results and links - cat > export/virus-total-results.md << EOF - ## VirusTotal Scan Results + TAG="release-$(date -u +'%Y%m%d-%H%M%S')" + echo "tag=$TAG" >> $GITHUB_OUTPUT + echo "name=Release $(date -u +'%Y-%m-%d %H:%M UTC')" >> $GITHUB_OUTPUT - **Scan Date:** \$(date -u +"%Y-%m-%d %H:%M UTC") + - name: 🚀 Create GitHub Release + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ steps.tag.outputs.tag }} + name: ${{ steps.tag.outputs.name }} + body: | + ## 📖 The Hitchhiker's Guide to Online Anonymity - ### thgtoa.pdf (Light Mode) - - **VT Report:** https://www.virustotal.com/gui/file/\$(sha256sum export/thgtoa.pdf | cut -d' ' -f1) + Built from commit ${{ github.sha }} on `${{ github.ref_name }}`. - ### thgtoa-dark.pdf (Dark Mode) (currently broken) - - **VT Report:** https://www.virustotal.com/gui/file/\$(sha256sum export/thgtoa-dark.pdf | cut -d' ' -f1) + --- - --- - *Scan performed automatically by GitHub Actions* - EOF + ### 📄 Files - - name: 📤 Upload export directory as artifact + | File | Description | + |------|-------------| + | `thgtoa.pdf` | Light mode PDF | + | `thgtoa-dark.pdf` | Dark mode PDF (hacker theme) | + | `sha256sums.txt` | SHA-256 checksums | + | `b2sums.txt` | BLAKE2b checksums | + | `*.sig` | GPG detached signatures (ASCII armor) | + + --- + + ### #️⃣ Hashes + + #### thgtoa.pdf (Light) + ``` + SHA-256: ${{ steps.hashes.outputs.light_sha256 }} + BLAKE2b: ${{ steps.hashes.outputs.light_b2 }} + ``` + + #### thgtoa-dark.pdf (Dark) + ``` + SHA-256: ${{ steps.hashes.outputs.dark_sha256 }} + BLAKE2b: ${{ steps.hashes.outputs.dark_b2 }} + ``` + + --- + + ### 🔏 GPG Signatures + + Detached signatures (`.sig`) are included in the release assets. + Verify with: + ```bash + gpg --verify thgtoa.pdf.sig thgtoa.pdf + gpg --verify thgtoa-dark.pdf.sig thgtoa-dark.pdf + ``` + The signing key is published at `pgp/anonymousplanet-release.asc`. + + --- + + ### 🦠 VirusTotal Scans + + | File | Report | + |------|--------| + | `thgtoa.pdf` | ${{ steps.vt_urls.outputs.light_vt }} | + | `thgtoa-dark.pdf` | ${{ steps.vt_urls.outputs.dark_vt }} | + + files: | + export/thgtoa.pdf + export/thgtoa-dark.pdf + export/sha256sums.txt + export/b2sums.txt + export/thgtoa.pdf.sha256 + export/thgtoa-dark.pdf.sha256 + export/thgtoa.pdf.b2 + export/thgtoa-dark.pdf.b2 + export/thgtoa.pdf.sig + export/thgtoa-dark.pdf.sig + export/sha256sums.txt.sig + export/b2sums.txt.sig + draft: false + prerelease: false + fail_on_unmatched_files: true + + # ------------------------------------------------------------------ # + # Upload everything as a workflow artifact (90-day archive) + # ------------------------------------------------------------------ # + - name: 📤 Upload export as workflow artifact uses: actions/upload-artifact@v4 with: - name: upload pdf artifact - path: | - export/* + name: pdf-release-${{ steps.tag.outputs.tag }} + path: export/* if-no-files-found: error retention-days: 90 compression-level: 0