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 main branch.
  • 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_ROOT
  • WORDPRESS_APP_ROOT
  • NIXOS_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