Workflow
The repo should support quick stage iteration without making production casual.
Branches
- Use short-lived branches for normal work.
- Deploying straight from a local checkout to stage is fine while developing.
- Production WordPress code is deployed only by GitHub Actions from the remote
mainbranch. - Promote to prod only after stage smoke and URL hygiene checks pass.
Stage Deploy
task deploy:wordpress:stage
task deploy:wordpress:stage -- --apply
task health:stage
task deploy:wordpress:stage is a dry-run. Add --apply to write to the server. task health:stage runs database URL hygiene only: the stage domain is protected by Cloudflare Access and cannot be smoke-tested without an authenticated service token. To deploy stage from GitHub Actions, run Deploy WordPress with target=stage.
For continuous sync while editing:
task watch:wordpress:stage
Prod Deploy
Prod WordPress code deploy is not a local workflow. Merge the tested change to main, then run the GitHub Deploy WordPress workflow with target=prod.
The local task deploy:wordpress:prod command is useful as a dry-run, but --apply is rejected unless it runs inside GitHub Actions on main.
NixOS Deploy
NixOS configuration is not part of the normal WordPress code deploy. Check it separately:
task test:nixos:check
Then dry-activate it on the disposable rehearsal host:
task deploy:nixos:rehearsal
Only switch production explicitly, after rehearsal has passed:
CONFIRM_NIXOS_PROD=prod task deploy:nixos:prod -- --apply
This should stay conservative because prod and stage live on the same server. Follow with both smoke checks:
task health:stage
task health:prod
Stage Database Refresh
task db:update-stage
task db:update-stage -- --apply
task health:stage
The refresh snapshots the stage database, replaces it with prod data, rewrites known prod domains to https://stage.piratenbande.de, and disables indexing. Run task health:stage afterwards for stage hygiene.
After refreshing, manually check plugins that write under uploads. Some may need to stay disabled on stage because uploads are read-only there.
Health Checks
Production smoke checks fetch piratenbande.de, its blog, and hosen-flicken.de. Stage runs database hygiene only because Cloudflare Access blocks unauthenticated HTTP checks. Add more checks when code starts owning more behavior, especially checkout, cart, product pages, redirects, and email rendering.
Public HTTP checks belong in health/. Read-only WordPress database checks can either live in checks/wordpress/ once there are several of them, or stay in apps/wordpress/scripts/ while they are closely tied to WordPress deployment. Mutating WordPress commands belong in apps/wordpress/scripts/. NixOS scripts live in infra/nixos/scripts/.
Keep these scripts in Bash while they are thin wrappers around CLI tools such as ssh, rsync, wp, curl, and nixos-rebuild. Shared shell code belongs in lib/. Move a check to Python or another higher-level language when it starts parsing structured data, aggregating many results, producing reports, or needing substantial tests.
Taskfile.yml is the supported entry point for scripts. It exports shared path context:
REPO_ROOTWORDPRESS_APP_ROOTNIXOS_FLAKE_ROOT
Scripts should assert the variables they need instead of rediscovering repository paths. Add a new shared variable only when at least two scripts need the same value.
Use PROD_SSH_TARGET for routine production-server operations, including WordPress deploys, database updates, and hygiene checks. Use REHEARSAL_SSH_TARGET for NixOS rehearsal deployment. Both are SSH aliases configured in .env; Taskfile passes the appropriate one explicitly to each script. In CI, DEPLOY_HOST and DEPLOY_USER are inputs only to SSH setup. The WordPress workflows use the nginx GitHub environment because stage and prod share a host; NixOS workflows select production or rehearsal. Remote WordPress roots are centralized in lib/remote.sh.
Aggregate health tasks should compose smaller checks:
task health:stage
task health:prod