You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit fd765c9
Browse filesBrowse the repository at this point in the historyBrowse files
On Windows outside Git Bash, use `.\gradlew.bat` instead of `./gradlew`.
242
249
250
+
### Deploy
251
+
252
+
```bash
253
+
make deploy-prod # rebase the `production` branch onto `main` and force-push (with lease) to trigger a deploy
254
+
```
255
+
256
+
`staging` syncs to `main` automatically on every merge via `.github/workflows/sync-staging.yml` — no manual step needed. `production` only advances when you run `make deploy-prod`.
257
+
258
+
See [RUNBOOK.md](RUNBOOK.md#triggering-a-deploy) for the full deploy workflow, prerequisites, and platform setup.
259
+
243
260
## Testing
244
261
245
262
Backend tests use [Testcontainers](https://testcontainers.com/) to spin up real PostgreSQL and Redis instances — database mocking is prohibited.
Copy file name to clipboardExpand all lines: RUNBOOK.md
+63Lines changed: 63 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -10,6 +10,7 @@ Operational guide for deploying and maintaining the fullstack template in stagin
10
10
-[Environment variables](#environment-variables)
11
11
-[First-time production setup](#first-time-production-setup)
12
12
-[Pre-launch checklist](#pre-launch-checklist)
13
+
-[Triggering a deploy](#triggering-a-deploy)
13
14
-[Deploying the backend](#deploying-the-backend)
14
15
-[Deploying the web app](#deploying-the-web-app)
15
16
-[Deploying the mobile app](#deploying-the-mobile-app)
@@ -155,6 +156,68 @@ Run through this before going live with any project based on this template.
155
156
156
157
---
157
158
159
+
## Triggering a deploy
160
+
161
+
This template uses two branches as deploy triggers, kept separate from `main`:
162
+
163
+
| Branch | Advances | Trigger |
164
+
|---|---|---|
165
+
|`staging`| Automatically, on every merge to `main`| GitHub Actions (`.github/workflows/sync-staging.yml`) |
166
+
|`production`| Manually, whenever you choose |`make deploy-prod`|
167
+
168
+
Configure your hosting platforms (Vercel, Railway, Fly.io, etc.) to auto-deploy from these branches — `staging` for the staging environment, `production` for production — rather than from `main`. That way merges to `main` land safely, staging always reflects the latest `main`, and a production deploy only fires when you explicitly advance `production`.
169
+
170
+
### Staging (automatic)
171
+
172
+
`.github/workflows/sync-staging.yml` runs on every push to `main` (i.e. every merge) and calls `scripts/sync-staging.sh`, which:
173
+
174
+
1. Fetches `origin/main` and `origin/staging`.
175
+
2. Checks out (or creates) a local `staging` branch reset to `origin/staging`.
176
+
3. Rebases it onto `origin/main`.
177
+
4. Force-pushes `staging` with `--force-with-lease`.
178
+
179
+
There is no confirmation prompt — unlike production, staging is meant to always match `main`. If the rebase hits a conflict (only possible if someone committed directly to `staging`), the job aborts the rebase and fails without pushing; fix it manually with the same commands `scripts/sync-staging.sh` prints on failure, or delete stray commits from `staging` so it stops diverging from `main`.
180
+
181
+
You can also trigger the workflow manually from the **Actions** tab (`workflow_dispatch`), or run `bash scripts/sync-staging.sh` locally.
182
+
183
+
**The workflow checks out with a personal access token (PAT), not the default `GITHUB_TOKEN`.** This matters because of GitHub's built-in loop-prevention rule: pushes made with the default `GITHUB_TOKEN` do **not** trigger other `on: push` workflows. If your CD workflow deploys on `push: branches: [staging]`, a `GITHUB_TOKEN`-authenticated push here would update the `staging` ref without ever firing that deploy — it would look like the sync succeeded but nothing would actually deploy. A PAT belonging to a real account avoids that.
184
+
185
+
To set it up:
186
+
1. Generate a PAT with push access to this repo — a fine-grained token scoped to this repo with **Contents: Read and write** is enough; a classic token needs the `repo` scope.
187
+
2. Add it as a repository secret named `SYNC_STAGING_PAT` (**Settings → Secrets and variables → Actions → New repository secret**).
188
+
3. If `staging` is a protected branch, make sure the PAT's account is allowed to bypass (or is exempt from) those protection rules, otherwise the force-push will still be rejected.
189
+
190
+
The commit identity used for the sync (`git config user.name`/`user.email`) is resolved from `github.actor` — whoever triggered the workflow (typically whoever merged the PR) — not hardcoded, so this works the same for any fork of this template.
191
+
192
+
### Production (manual)
193
+
194
+
Advance `production` to match `main` with:
195
+
196
+
```bash
197
+
make deploy-prod
198
+
```
199
+
200
+
This runs `scripts/deploy-prod.sh` (macOS/Linux) or `scripts/deploy-prod.ps1` (Windows) via the `deploy-prod` Makefile target, which handles both platforms automatically. The script:
201
+
202
+
1. Aborts if the working tree is dirty — commit, stash, or discard changes first.
203
+
2. Fetches `origin/main` and `origin/production`.
204
+
3. Checks out (or creates) a local `production` branch reset to `origin/production`.
205
+
4. Rebases it onto `origin/main`.
206
+
5. Shows the commits about to ship and prompts `Continue? [y/N]` before pushing.
207
+
6. Force-pushes `production` with `--force-with-lease` (never a bare `--force`), so the push fails instead of clobbering someone else's work if `production` moved remotely since the last fetch.
208
+
209
+
Set `CONFIRM=yes` to skip the interactive prompt (e.g. from CI):
210
+
211
+
```bash
212
+
CONFIRM=yes make deploy-prod
213
+
```
214
+
215
+
If the rebase hits conflicts, the script leaves the branch mid-rebase and prints next steps — resolve the conflicts, then run `git rebase --continue` followed by `git push --force-with-lease origin production`, or `git rebase --abort` to back out. The original branch is restored automatically on both success and abort.
216
+
217
+
`make deploy-prod` only advances the branch — it does not run database migrations or build anything. Apply pending migrations against production **before** running it; see [Database migrations](#database-migrations).
218
+
219
+
---
220
+
158
221
## Deploying the backend
159
222
160
223
The backend compiles to a single binary. Choose one deployment model:
0 commit comments