# InboxIntel — Git Workflow & Environment Strategy The single source of truth for how we branch, commit, version, and promote code across environments. Optimised for a **solo developer on Windows 11 with a future Linux production server**. Kept deliberately lightweight — every rule here earns its place. --- ## 1. Branch strategy A trimmed **GitHub Flow + a long-lived `develop`** model. Two permanent branches, short-lived branches off `develop`. | Branch | Lives for | Purpose | Deploys to | |-----------------|-----------|------------------------------------------------------|------------| | `main` | forever | Always releasable. Every commit is tagged & shippable | production | | `develop` | forever | Integration branch. What staging runs | staging | | `feature/*` | hours–days| One feature or refactor | dev (local)| | `fix/*` | hours | Non-urgent bug fix | dev (local)| | `hotfix/*` | minutes–hrs| Urgent prod fix, branched from `main` | prod (fast)| | `release/x.y.0` | optional | Only if a release needs stabilisation before tagging | staging | **Why this shape (not full GitFlow):** a solo dev doesn't need GitFlow's ceremony (separate release managers, parallel release trains). But keeping `develop` separate from `main` gives one thing that pure trunk-based can't: a **staging environment that always mirrors `develop`** while `main` stays clean and tag-perfect for production. `release/*` exists only when you want to freeze features and stabilise — skip it for routine work. ### Normal flow ``` main ──────●────────────────────●────────── (tagged: v1.2.0, v1.3.0) \ / develop ──●──●──●──●──●──●──●──●──────────── (staging) \ / \ / feature/x ●─● \ / fix/y ●───● ``` 1. `git switch develop && git pull` 2. `git switch -c feature/sender-policy` 3. Commit in small conventional commits. 4. Push, open a PR **into `develop`** (Gitea). CI must be green. 5. Squash-merge. Delete the branch. 6. When `develop` is ready to ship → PR `develop → main`, tag, deploy. --- ## 2. Commit conventions — Conventional Commits Format: `type(scope): short imperative summary` **Types:** `feat`, `fix`, `docs`, `refactor`, `test`, `chore`, `ci`, `perf`, `build`, `style`, `security`. Rules: - Summary ≤ 72 chars, imperative mood ("add", not "added"). - One logical change per commit. - Body explains **why**, not what (the diff shows what). - Breaking change → add `!` (`feat!:`) and a `BREAKING CHANGE:` footer. Examples (matching this repo's history): ``` feat(ui): F1+F2 — component primitives + rebuilt app shell fix(security): systemic IDOR safeguard via EF global query filters ci: add Gitea Actions build + test pipeline ``` Why: conventional commits drive **automatic semver bumps** and a generated CHANGELOG, and make `git log` scannable. `feat` → minor, `fix` → patch, `BREAKING CHANGE` → major. --- ## 3. Pull request / merge flow (Gitea) Even solo, PRs are worth it: they run CI, give a diff review checkpoint, and build a paper trail. - **Target:** `feature/*` and `fix/*` → `develop`. `develop`/`hotfix/*` → `main`. - **Gate:** the `CI` workflow (backend build+test, frontend build) must pass. - **Merge style:** **squash-merge** feature branches (one clean commit on `develop`). **Merge commit** for `develop → main` (preserves the integration history and makes the release boundary visible). - **Branch protection (Gitea → Settings → Branches):** protect `main` and `develop`; require status checks to pass; disallow force-push. --- ## 4. Versioning — Semantic Versioning (`MAJOR.MINOR.PATCH`) - **MAJOR** — breaking API/behaviour change. - **MINOR** — backward-compatible feature. - **PATCH** — backward-compatible fix. The current version lives in [`VERSION`](../VERSION) and is the one place bumped per release. Pre-1.0 while scaffolding: stay on `0.x` (minor = features, patch = fixes; anything may change). --- ## 5. Tagging & releases Tags are cut **only on `main`**, annotated, prefixed `v`: ```bash git switch main && git pull # bump VERSION + CHANGELOG in a release commit, then: git tag -a v1.3.0 -m "v1.3.0 — sender policy + staging overlay" git push origin main --tags ``` - Tag == the exact commit deployed to production == the rollback target. - The tag message summarises the release; details live in `CHANGELOG.md`. --- ## 6. Hotfix process For a bug already in production: ```bash git switch main && git pull git switch -c hotfix/oauth-callback-500 # fix + test git commit -m "fix(security): guard null OAuth state on callback" # PR hotfix/* -> main, CI green, merge git switch main && git pull git tag -a v1.3.1 -m "v1.3.1 hotfix — OAuth callback" && git push origin main --tags # deploy the tag, THEN back-merge so develop doesn't lose the fix: git switch develop && git merge main && git push ``` The **back-merge to `develop`** is the step people forget — without it the next release silently reintroduces the bug. --- ## 7. Environments | Env | Host | Runtime | Config source | Purpose | |-------------|-------------------------|--------------------------------------|--------------------------|--------------------| | Development | Windows 11 (native) | `dotnet run` + `vite` (hot reload) | `appsettings.Development.json` + user-secrets | fast iteration, debugging | | Staging | Windows 11 (Docker) | Linux containers, `ASPNETCORE_ENVIRONMENT=Production` | `deploy/.env.staging` + `docker-compose.staging.yml` | production rehearsal | | Production | Linux server (Docker) | identical Linux containers | `deploy/.env` on the server | live | **Parity principle:** dev is fast (native, Windows) and *not* production-shaped — that's fine, it's for inner-loop speed. **Staging is the parity gate**: it runs the *same Linux images* Docker builds for production, so "works in staging" genuinely predicts "works in prod". The only staging↔prod differences are the dev banner, capped sync, ports, and volume namespace — see `docker-compose.staging.yml`. ### Commands ```powershell # DEV (native, hot reload) — two terminals dotnet run --project src/InboxIntel.Api # http://localhost:5080 cd frontend; npm run dev # http://localhost:5173 # STAGING (production-shaped, on Windows via Docker) ./deploy/up.ps1 -Staging # http://localhost:18081 ./deploy/down.ps1 -Staging # PRODUCTION-shaped run locally (smoke test the real config) ./deploy/up.ps1 # http://localhost:8081 ``` Dev and staging use **different ports and different volumes**, so they can run at the same time and never share a database. --- ## 8. Git hooks Installed via `core.hooksPath` (run once per clone): ```powershell ./scripts/install-hooks.ps1 ``` - **pre-commit** (fast): blocks committed `.env`/secrets, verifies `dotnet format`. - **pre-push** (thorough): `dotnet build -c Release` + `dotnet test` + frontend build — the same gate CI runs, caught before the push. Emergency bypass: `--no-verify`. CI still enforces the gate server-side, so a bypassed push can still be rejected by branch protection.