diff --git a/.forgejo/workflows/build.yml b/.forgejo/workflows/build.yml index f5744e8..ed26c89 100644 --- a/.forgejo/workflows/build.yml +++ b/.forgejo/workflows/build.yml @@ -70,3 +70,37 @@ jobs: - name: Log out if: always() run: docker logout "$REGISTRY" + + deploy: + # Auto-deploy to racknerd2 after a successful build. Deploys the exact + # images just built (pinned to this commit's SHA). For manual/on-demand + # deploys of an arbitrary tag (e.g. rollback), use the separate + # "Deploy to racknerd2" workflow (deploy.yml). + needs: build + runs-on: docker + env: + DEPLOY_HOST: 100.96.217.250 + DEPLOY_DIR: /opt/archnest + steps: + - name: Install SSH client + run: | + apt-get update + apt-get install -y --no-install-recommends openssh-client + + - name: Write deploy key + run: | + install -m 700 -d ~/.ssh + printf '%s\n' "${{ secrets.RACKNERD2_SSH_KEY }}" > ~/.ssh/id_deploy + chmod 600 ~/.ssh/id_deploy + + - name: Pull this build's images and restart stack + run: | + ssh -i ~/.ssh/id_deploy -o StrictHostKeyChecking=accept-new \ + root@"$DEPLOY_HOST" \ + "cd $DEPLOY_DIR && ARCHNEST_TAG='${{ github.sha }}' docker compose pull && ARCHNEST_TAG='${{ github.sha }}' docker compose up -d --remove-orphans" + + - name: Health check (backend /api/health via mesh) + run: | + ssh -i ~/.ssh/id_deploy -o StrictHostKeyChecking=accept-new \ + root@"$DEPLOY_HOST" \ + "for i in \$(seq 1 30); do curl -fsS http://$DEPLOY_HOST:8080/api/health && echo OK && exit 0; sleep 2; done; echo 'health check failed'; cd $DEPLOY_DIR && docker compose logs --tail=50; exit 1" diff --git a/deploy/README.md b/deploy/README.md index 4ae523c..cb1da28 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -6,11 +6,17 @@ over the NetBird mesh. racknerd2 only pulls and runs — it never builds (1.9 Gi RAM). ``` -push to main / manual ─► [build.yml] build + push images ─► registry.snsnetlabs.com/sam/{archnest,archnest-backend} +push to main ─► [build.yml] + job: build ─► build + push images ─► registry.snsnetlabs.com/sam/{archnest,archnest-backend} + job: deploy ─► (needs build) ssh racknerd2 ─► compose pull + up -d (this build's SHA) ─► /api/health │ - manual dispatch ─► [deploy.yml] ssh racknerd2 ─► docker compose pull && up -d + manual dispatch (any tag / rollback) ─► [deploy.yml] ssh racknerd2 ─► compose pull && up -d ``` +Every push to `main` auto-builds and auto-deploys to racknerd2. `deploy.yml` +stays as a manual `workflow_dispatch` for deploying/rolling back to an arbitrary +tag without rebuilding. + ## Images | Image | From | Tags | @@ -65,16 +71,19 @@ ufw, so this is what keeps the app off the public interface. Validate at ## Running it -1. **Build**: push to `main`, or run **Build & Push Images** manually - (Actions tab → Run workflow). -2. **Deploy**: run **Deploy to racknerd2** manually, entering the tag - (`latest` or a specific commit SHA). It pulls, restarts, and health-checks - `/api/health`. +- **Automatic**: push to `main` → `build.yml` builds + pushes both images, then + its `deploy` job (needs `build`) pulls this commit's SHA onto racknerd2, + restarts the stack, and health-checks `/api/health`. Fully hands-off. +- **Manual build**: run **Build & Push Images** from the Actions tab (also + triggers the auto-deploy job). +- **Manual deploy / rollback**: run **Deploy to racknerd2**, entering any tag + (`latest` or a specific commit SHA) to deploy without rebuilding. ## Notes / ceilings -- `ponytail:` deploy is manual (workflow_dispatch), not auto-on-merge — this is - a validation host, so deploys are deliberate. Wire `build.yml` → `deploy.yml` - with `needs:` later if auto-deploy-to-validation is wanted. +- Auto-deploy targets racknerd2 (the validation host) on every push to `main`, + pinned to the built commit's SHA. If you later add a prod host, gate + prod deploys behind a manual approval or a tag/release trigger rather than + every push. - Single-arch (amd64) only — both the runner host and racknerd2 are amd64, so no buildx/multi-platform is needed.