CI / CD¶
Status
Workflow written, not yet wired. The pipeline exists at .github/workflows/deploy.yml; it activates once the repo is pushed to GitHub. Today we deploy locally by running docker build + docker restart fruitplug-web-dev.
Target pipeline¶
.github/workflows/deploy.yml on every push to main:
flowchart LR
Push([git push main]) --> Build[docker buildx<br/>apps/web/Dockerfile]
Build --> GHCR[ghcr.io/<org>/<br/>fruitplug-web:sha<br/>+ :latest]
GHCR --> SSH[SSH to pwa-host]
SSH --> Pull[docker compose pull<br/>systemctl restart]
Pull --> Smoke[curl /healthz<br/>30×4s retries]
Smoke --> Done([deployed])
Required GitHub secrets (when wired)¶
| Secret | Purpose |
|---|---|
SSH_HOST |
pwa-host hostname or IP |
SSH_USER |
Deploy user (sudoer or owns /opt/fruitplug) |
SSH_PORT |
Optional; defaults to 22 |
SSH_PRIVATE_KEY |
PEM contents of the deploy key |
BASIC_AUTH |
user:password for Caddy basic-auth smoke test (if re-enabled) |
GHCR_TOKEN is not needed — the pipeline uses the automatic GITHUB_TOKEN scoped to the repo.
Deploy script on the server¶
/opt/fruitplug/infra/deploy.sh:
#!/usr/bin/env bash
set -euo pipefail
cd /opt/fruitplug
docker compose -f infra/docker-compose.prod.yml pull
systemctl restart fruitplug-web
for i in {1..30}; do
if curl -sf http://127.0.0.1:3000/healthz >/dev/null; then
echo "ok"
exit 0
fi
sleep 2
done
exit 1
Today's deploy flow (manual)¶
Until CI is wired, we deploy the PWA by editing locally, which:
- Hits the dev container's hot-reload loop in seconds (for iteration)
- Or:
docker build -f apps/web/Dockerfile -t ghcr.io/fruitplug/fruitplug-web:local .+docker restart(for prod-build testing)
For fruitplug-api on A2:
This uploads the latest plugin files via SFTP and activates the plugin. The plugin header's Version should bump on significant changes so WP triggers Migrations::migrate().
Rollback (post-CI)¶
# On the server
docker pull ghcr.io/fruitplug/fruitplug-web:<previous-sha>
docker tag ghcr.io/fruitplug/fruitplug-web:<previous-sha> ghcr.io/fruitplug/fruitplug-web:latest
systemctl restart fruitplug-web
GHCR retains the previous ~10 tags by default.
Preview environments (future)¶
When the repo is public/private on GitHub, add a preview.yml workflow that:
- Builds per-PR image
- Deploys to Fly.io machine with the PR number in the URL (e.g.
pr-42.fruitplug.co.uk) - Auto-teardowns on PR close
Not urgent for Phase 1 — nice-to-have for team scaling later.