Reference

CI / CD Workflows

The full suite of GitHub Actions workflows — from quality gates on the develop branch through automated publishing to production and weekly backups.

Pipeline Overview

Content flows through two distinct environments:

BranchWorkflowEnvironmentTrigger
develop CI (ci.yml) Local CRC instance self-hosted runner Push with .md changes
main CD (cd.yml) Remote production instance GitHub runner PR merged to main
Any Backup (bookstack-backup.yml) Both instances Weekly cron (Sunday 02:00 UTC)
Any Claude Autofix (claude-autofix.yml) GitHub runner CI failure or @claude PR comment

CI Pipeline (ci.yml)

Runs on every push to develop that modifies *.md, .cspell.json, or .markdownlint.json. Can also be triggered manually via workflow_dispatch with an option to sync all files (not just changed ones).

Jobs (in order)

  1. 1

    Secret Scan ubuntu-latest

    Runs Gitleaks across the full git history to detect hardcoded credentials. Blocks all subsequent jobs on failure.

  2. 2

    Markdown Lint ubuntu-latest

    Runs markdownlint-cli2 against all *.md files using the rules in .markdownlint.json.

  3. 3

    Tests ubuntu-latest

    Runs pytest tests/ -v --tb=short with Python 3.12. Tests cover the sync helper functions and content integrity of all cocktail/whiskey pages. Requires markdown lint to pass first.

  4. 4

    Spell Check ubuntu-latest

    Runs cspell in warning (non-blocking) mode against all *.md files using the config in .cspell.json. Inline annotations appear in the PR diff.

  5. 5

    Sync to Local BookStack self-hosted

    Runs bookstack-sync.py on the self-hosted CRC runner. Detects changed files automatically (or syncs all when triggered manually). BookStack URL is resolved live from the CRC route.

  6. 6

    Raise Pull Request ubuntu-latest

    Creates a develop → main PR automatically after a successful sync. PR title includes today's date. Skipped if a PR already exists. Only runs on push events (not manual triggers).

CD Pipeline (cd.yml)

Triggered when a PR is merged into main. Syncs the same changed files (or all files if triggered manually) to the remote production BookStack instance.

Jobs

  1. 1

    Collect changed files

    Uses tj-actions/changed-files to find which *.md files changed in the merge commit, or collects all files when sync_all is true.

  2. 2

    Plan sync

    Runs bookstack-sync.py --plan and appends the Terraform-style output to the GitHub Actions step summary so you can review it in the workflow run.

  3. 3

    Sync to remote BookStack

    Runs the actual sync using the BOOKSTACK_URL, BOOKSTACK_TOKEN_ID, and BOOKSTACK_TOKEN_SECRET secrets for the production instance.

â„šī¸ Only runs on merged PRs The job has an if guard: github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch'. Closing a PR without merging will not trigger a sync.

Backup Workflow (bookstack-backup.yml)

Runs every Sunday at 02:00 UTC and backs up both the local and remote BookStack instances in parallel jobs. Can also be triggered manually with a configurable retention period.

What gets backed up?

The backup script (.github/scripts/bookstack-backup.py) paginates through GET /api/books and exports every book as a markdown ZIP via GET /api/books/{id}/export/markdown. All ZIPs are bundled into a single bookstack-backup-YYYY-MM-DD.tar.gz archive.

Storage

Archives are stored as GitHub Actions artifacts with a default retention of 90 days. You can override this when triggering manually.

Artifact name patternRunnerSecrets used
bookstack-backup-remote-{run_id}-{attempt} ubuntu-latest BOOKSTACK_URL, BOOKSTACK_TOKEN_ID, BOOKSTACK_TOKEN_SECRET
bookstack-backup-local-{run_id}-{attempt} self-hosted LOCAL_BOOKSTACK_URL, LOCAL_BOOKSTACK_TOKEN_ID, LOCAL_BOOKSTACK_TOKEN_SECRET
âš ī¸ Backup failure behaviour If any book fails to export, the entire archive is deleted and the artifact upload step fails with if-no-files-found: error. This ensures a partial backup is never silently accepted.

Image Generation Workflows

Two workflows automatically generate placeholder images for content pages that are missing them:

WorkflowTriggerScript
generate-images.yml Push to develop with changes to cocktails/*.md generate-cocktail-images.py
generate-whiskey-images.yml Push to develop with changes to whiskeys/*.md generate-whiskey-images.py

Each workflow checks for Markdown files that have no corresponding image in <dir>/images/. If any are missing, it runs the generation script (which uses Pillow) and commits the generated images back to the branch with the commit message chore: auto-generate <type> images [skip ci].

Claude Autofix (claude-autofix.yml)

Integrates Claude Code into the workflow for two scenarios:

Manual: @claude mention

Mention @claude in any PR comment or review comment. Claude will read the conversation and act on the request — useful for asking Claude to fix a lint error, rewrite a section, or explain a problem.

Automatic: CI failure

When the CI workflow completes with a failure, this workflow automatically:

  1. Finds the open PR for the failed branch.
  2. Collects the names of failed jobs and steps.
  3. Invokes Claude Code with the failure context.

Claude will investigate the failure (Markdown lint errors, spell-check warnings, Python syntax errors) and commit a fix directly to the branch.

â„šī¸ Required secret Both Claude jobs require an ANTHROPIC_API_KEY repository secret. See Required Secrets below.

Required GitHub Actions Secrets

SecretUsed byDescription
LOCAL_BOOKSTACK_TOKEN_ID CI sync API token ID for the local CRC BookStack instance
LOCAL_BOOKSTACK_TOKEN_SECRET CI sync API token secret for the local CRC BookStack instance
LOCAL_BOOKSTACK_BOOK_ID CI sync Default book ID on the local instance
LOCAL_BOOKSTACK_COCKTAILS_BOOK_ID CI sync Cocktails book ID on the local instance
LOCAL_BOOKSTACK_WHISKEY_BOOK_ID CI sync Whiskey book ID on the local instance
LOCAL_BOOKSTACK_URL Backup (local) Base URL of the local BookStack instance for backup (if URL can't be resolved from CRC route)
BOOKSTACK_URL CD sync, Backup (remote) Base URL of the remote production BookStack instance
BOOKSTACK_TOKEN_ID CD sync, Backup (remote) API token ID for the remote production instance
BOOKSTACK_TOKEN_SECRET CD sync, Backup (remote) API token secret for the remote production instance
BOOKSTACK_BOOK_ID CD sync Default book ID on the remote production instance
BOOKSTACK_COCKTAILS_BOOK_ID CD sync Cocktails book ID on the remote production instance
BOOKSTACK_WHISKEY_BOOK_ID CD sync Whiskey book ID on the remote production instance
ANTHROPIC_API_KEY Claude Autofix Anthropic API key for Claude Code Action
GITHUB_TOKEN All workflows Automatically provided by GitHub Actions — no setup needed

Runner Requirements

GitHub-hosted runner (ubuntu-latest)

Used for: secret scan, lint, tests, spell check, CD sync, backup (remote), Claude Autofix, PR creation. No special setup required.

Self-hosted runner

Used for: CI sync (step 5), backup (local), image generation. The runner must be:

💡 Setting up a self-hosted runner Go to your repository → Settings → Actions → Runners → New self-hosted runner and follow the instructions. The runner registers itself and waits for jobs.