#!/usr/bin/env python3 """Setup helper for PDF workflow configuration. This script helps you configure the necessary GitHub Secrets for the automated PDF build, signing, and VirusTotal scanning workflows. Usage: python scripts/setup_workflow.py Requirements: - Python 3.8+ - GPG installed (for key export) - Access to GitHub repository settings What it does: 1. Validates your GPG key setup 2. Exports the public key for verification 3. Provides instructions for adding secrets to GitHub """ # The Hitchhiker's Guide to Online Anonymity © 2026 by Anonymous Planet is licensed under Creative # Commons Attribution-NonCommercial 4.0 International from __future__ import annotations import subprocess import sys from pathlib import Path # Computes the root directory of the repository based on the current file’s location def repo_root() -> Path: return Path(__file__).resolve().parent.parent # Determines whether the GPG command-line tool is available on the current system def check_gpg_installed() -> bool: """Check if GPG is installed and accessible.""" try: result = subprocess.run( ["gpg", "--version"], capture_output=True, text=True, timeout=10, ) return result.returncode == 0 except (FileNotFoundError, subprocess.TimeoutExpired): return False # Checks whether the GPG tool is installed and accessible on the system # Export public keys, parse, and read selection of keys from the GPG keyring def list_gpg_keys() -> list[dict]: """List all GPG keys in the keyring.""" try: result = subprocess.run( ["gpg", "--list-keys", "--with-colons"], capture_output=True, text=True, check=True, ) keys = [] current_key = {} for line in result.stdout.split('\n'): if line.startswith('pub:'): if current_key: keys.append(current_key) parts = line.split(':') current_key = { 'type': parts[1], 'key_id': parts[4], 'fingerprint': parts[9] if len(parts) > 9 else None, 'created': parts[5], 'expires': parts[6], 'uid': None, } elif line.startswith('uid:'): parts = line.split(':') current_key['uid'] = parts[9] if len(parts) > 9 else None if current_key: keys.append(current_key) return keys except subprocess.CalledProcessError as e: print(f"Error listing GPG keys: {e}") return [] def export_public_key(key_id: str, output_file: Path | None = None) -> str | None: """Export a public key in ASCII armor format.""" try: result = subprocess.run( ["gpg", "--armor", "--export", key_id], capture_output=True, text=True, check=True, ) if output_file: output_file.write_text(result.stdout) print(f"✓ Public key exported to {output_file}") return result.stdout except subprocess.CalledProcessError as e: print(f"Error exporting public key: {e}") return None # Exporting the given private key in ASCII armor format (requires passphrase) def export_private_key(key_id: str, output_file: Path | None = None) -> str | None: """Export a private key in ASCII armor format (requires passphrase).""" try: # This will prompt for passphrase interactively result = subprocess.run( ["gpg", "--armor", "--export-secret-keys", key_id], capture_output=True, text=True, check=True, ) if output_file: output_file.write_text(result.stdout) print(f"✓ Private key exported to {output_file}") return result.stdout # return None if export fails (e.g. wrong passphrase, key not found) except subprocess.CalledProcessError as e: print(f"Error exporting private key: {e}") return None # Validate that the selected GPG key has signing capability def validate_gpg_key(key_id: str) -> bool: """Validate that a GPG key has signing capability.""" try: result = subprocess.run( ["gpg", "--list-keys", "--with-colons", key_id], capture_output=True, text=True, check=True, ) # Check for 's' (signing) in the pub line for line in result.stdout.split('\n'): if line.startswith('pub:'): flags = line.split(':')[1] return 's' in flags return False except subprocess.CalledProcessError: return False # Print instructions for configuring GitHub Secrets def print_setup_instructions(): """Print instructions for configuring GitHub Secrets.""" print("\n" + "="*70) print("GITHUB SECRETS SETUP INSTRUCTIONS") print("="*70) print(""" To enable the automated PDF workflow, you need to add three secrets to your GitHub repository: 1. GPG_PRIVATE_KEY - Your GPG private key in ASCII armor format - Used to sign PDFs and hash files - IMPORTANT: Keep this secret! Never commit it publicly 2. GPG_PASSPHRASE - The passphrase for your GPG private key - Required to unlock the private key for signing 3. VT_API_KEY (optional but recommended) - VirusTotal API key for malware scanning TROUBLESHOOTING: - If GPG signing fails: Check that your key has signing capability ('s' flag) - If passphrase is wrong: Verify you're using the correct passphrase - If VT scan fails: Ensure API key is valid and within rate limits """) def main() -> int: print("\n" + "="*70) print("PDF WORKFLOW SETUP HELPER") print("="*70) # Check GPG installation if not check_gpg_installed(): print("⚠ WARNING: GPG is not installed or not in PATH") print("Please install GPG before continuing:") print(" - Linux: sudo apt install gnupg") print("\nContinuing anyway...") # List available keys print("\n🔑 Available GPG Keys:") print("-" * 70) keys = list_gpg_keys() if not keys: print("No GPG keys found in your keyring.") print("Generate a key with: gpg --full-generate-key") return 1 for i, key in enumerate(keys, 1): status = "✓" if validate_gpg_key(key['key_id']) else "✗" print(f"\n{i}. {status} Key ID: {key['key_id']}") print(f" Fingerprint: {key.get('fingerprint', 'N/A')}") print(f" UID: {key.get('uid', 'Unknown')}") print(f" Created: {key.get('created', 'Unknown')}") if key.get('expires'): print(f" Expires: {key['expires']}") # Ask user to select key print("\n" + "-" * 70) try: choice = input("\nEnter the number of the key you want to use (1-{}): ".format(len(keys))) selected_index = int(choice) - 1 if not (0 <= selected_index < len(keys)): print("Invalid selection!") return 1 except ValueError: print("Invalid input! Please enter a number.") return 1 selected_key = keys[selected_index] # Validate key has signing capability if not validate_gpg_key(selected_key['key_id']): print(f"\n⚠ WARNING: Selected key does not have signing capability!") print("You need a key with 's' (signing) flag for PDF signatures.") confirm = input("Continue anyway? (y/N): ") if confirm.lower() != 'y': return 1 # Export public key print(f"\n📤 Exporting public key for {selected_key['uid']}...") public_key_file = repo_root() / "pgp" / "workflow-public.asc" public_key = export_public_key(selected_key['key_id'], public_key_file) if not public_key: print("Failed to export public key!") return 1 # Show public key info print("\n✓ Public Key Information:") print("-" * 70) for line in public_key.split('\n')[:5]: print(line) print("...") # Instructions for private key export print("\n🔐 Private Key Export:") print("-" * 70) print(""" To get your private key for the GPG_PRIVATE_KEY secret: 1. Run this command (you'll be prompted for passphrase): gpg --armor --export-secret-keys {} > workflow-private.asc 2. Copy the ENTIRE output including BEGIN and END lines 3. Add it to GitHub Secrets as 'GPG_PRIVATE_KEY' ⚠ IMPORTANT: Keep your private key secure! Never commit it publicly. """.format(selected_key['key_id'])) # Print setup instructions print_setup_instructions() print("\n" + "="*70) print("SETUP COMPLETE!") print("="*70) print(f"\nPublic key saved to: {public_key_file}") print("Next steps:") print("1. Export your private key (see instructions above)") print("2. Add all three secrets to GitHub repository settings") print("3. Test the workflow by triggering a manual build") print("\nFor more information, see: docs/guide/dev-workflow.md\n") return 0 if __name__ == "__main__": raise SystemExit(main())