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:
| Branch | Workflow | Environment | Trigger |
|---|---|---|---|
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
Secret Scan ubuntu-latest
Runs Gitleaks across the full git history to detect hardcoded credentials. Blocks all subsequent jobs on failure.
-
2
Markdown Lint ubuntu-latest
Runs markdownlint-cli2 against all
*.mdfiles using the rules in.markdownlint.json. -
3
Tests ubuntu-latest
Runs
pytest tests/ -v --tb=shortwith Python 3.12. Tests cover the sync helper functions and content integrity of all cocktail/whiskey pages. Requires markdown lint to pass first. -
4
Spell Check ubuntu-latest
Runs cspell in warning (non-blocking) mode against all
*.mdfiles using the config in.cspell.json. Inline annotations appear in the PR diff. -
5
Sync to Local BookStack self-hosted
Runs
bookstack-sync.pyon 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
Raise Pull Request ubuntu-latest
Creates a
develop â mainPR 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
Collect changed files
Uses
tj-actions/changed-filesto find which*.mdfiles changed in the merge commit, or collects all files whensync_allistrue. -
2
Plan sync
Runs
bookstack-sync.py --planand appends the Terraform-style output to the GitHub Actions step summary so you can review it in the workflow run. -
3
Sync to remote BookStack
Runs the actual sync using the
BOOKSTACK_URL,BOOKSTACK_TOKEN_ID, andBOOKSTACK_TOKEN_SECRETsecrets for the production instance.
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 pattern | Runner | Secrets 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 |
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:
| Workflow | Trigger | Script |
|---|---|---|
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:
- Finds the open PR for the failed branch.
- Collects the names of failed jobs and steps.
- 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.
ANTHROPIC_API_KEY repository secret. See Required Secrets below.
Required GitHub Actions Secrets
| Secret | Used by | Description |
|---|---|---|
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:
- Running on the same machine or network where CRC is deployed
- Have
ocin its PATH and be authenticated askubeadmin(or a user with read access to thebookstack-devnamespace) - Have Python 3 available (
python3) - Have network access to the BookStack instance at
http://bookstack-bookstack-dev.apps-crc.testing