-
Notifications
You must be signed in to change notification settings - Fork 7
Add staging environment #39
Description
Context
We need a staging environment to safely test changes before production. Staging will be gated with Caddy basic auth and use dedicated R2 buckets.
Step 1: Caddyfile — Add Conditional Basic Auth
File: deploy/ansible/roles/caddy/templates/Caddyfile.j2
Add a conditional basicauth * block. Production never defines caddy_basic_auth_enabled, so it's unaffected.
{% if caddy_basic_auth_enabled | default(false) %}
basicauth * {
{{ caddy_basic_auth_user }} {{ caddy_basic_auth_hash }}
}
{% endif %}Caddy uses bcrypt hashes natively — generate with caddy hash-password --plaintext '<password>'.
For Composer client testing, configure auth.json:
{"http-basic": {"staging.wp-packages.org": {"username": "staging", "password": "<pass>"}}}Step 2: Ansible Multi-Environment Setup
2a. Update ansible.cfg
File: deploy/ansible/ansible.cfg
Remove inventory = inventory/hosts/production.yml. Inventory will be passed explicitly via -i flag to prevent accidental production deploys.
2b. Create staging inventory example
New file: deploy/ansible/inventory/hosts/staging.example.yml
all:
hosts:
wp-packages-staging:
ansible_host: your.staging.server.ip
ansible_user: deploy
ansible_python_interpreter: /usr/bin/python32c. Create staging group_vars
New file: deploy/ansible/group_vars/staging/main.yml
Copy from production with these overrides:
app_domain: staging.wp-packages.org
app_env: staging
app_debug: "true"
go_log_level: debug
r2_cdn_public_url: "https://cdn-staging.wp-packages.org"
caddy_basic_auth_enabled: true
caddy_basic_auth_user: "staging"
caddy_basic_auth_hash: "{{ vault_caddy_basic_auth_hash }}"Everything else (timers, users, litestream version, etc.) stays the same as production.
New file: deploy/ansible/group_vars/staging/vault.example.yml
Same as production vault.example.yml plus vault_caddy_basic_auth_hash: "REPLACE_ME".
Step 3: GitHub Actions Workflow
File: .github/workflows/deploy.yml
Add an environment input (choice: staging/production, default: staging):
concurrency: ${{ inputs.environment }}-deploy— independent locks per env- Conditionally use
PROD_*orSTAGING_*secrets based on the selected environment - Pass
-i inventory/hosts/${{ inputs.environment }}.ymlexplicitly to ansible-playbook
GitHub repo setup (manual, one-time):
Add repo-level secrets: STAGING_SSH_PRIVATE_KEY, STAGING_ANSIBLE_VAULT_PASSWORD, STAGING_INVENTORY_YML_B64, STAGING_VAULT_YML_B64
Step 4: R2 Buckets (Manual in Cloudflare)
Create three staging buckets (using existing R2 API token):
wp-packages-staging— Composer package metadatawp-packages-staging-cdn— CDN assets, with custom domaincdn-staging.wp-packages.orgwp-packages-staging-backups— Litestream SQLite replicas
Step 5: DNS & Cloudflare (Manual)
staging.wp-packages.org— A record to staging server IP (proxied through Cloudflare)cdn-staging.wp-packages.org— set up via R2 custom domain settings- Generate a separate Cloudflare origin certificate for
staging.wp-packages.org - Store staging cert + key in staging vault (
vault_ssl_certificate,vault_ssl_private_key)
Step 6: Provision & Verify
- Spin up staging VPS
- Add all
STAGING_*secrets to the GitHub repo - Run workflow:
environment: staging,action: provision,ref: main - Verify:
curl https://staging.wp-packages.org→ 401 (basic auth required)curl -u staging:<pass> https://staging.wp-packages.org→ 200- R2 uploads land in staging buckets
composer require wp-plugin/akismetworks with auth.json configured
Files to Create/Modify
| File | Action |
|---|---|
deploy/ansible/roles/caddy/templates/Caddyfile.j2 |
Add conditional basicauth block |
deploy/ansible/ansible.cfg |
Remove hardcoded inventory line |
deploy/ansible/group_vars/staging/main.yml |
Create — staging config |
deploy/ansible/group_vars/staging/vault.example.yml |
Create — vault key reference |
deploy/ansible/inventory/hosts/staging.example.yml |
Create — inventory example |
.github/workflows/deploy.yml |
Add environment input, conditional secrets, per-env concurrency |
docs/operations.md |
Update for multi-environment support (inventory -i flag, staging secrets, etc.) |
Follow up after #28.