Files
thgtoa/scripts/setup_workflow.py
T
nopeitsnothing 4fd0413f4b 8/8 chore(scripts): minor cleanup to setup_workflow.py
Some whitespace failed checks

Signed-off-by: nopeitsnothing <no@anonymousplanet.org>
2026-05-22 16:28:43 -04:00

292 lines
9.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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 files 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())