Add Soroush CI/CD (Gitea + Nexus) + self-host fonts for offline build
CI/CD / CI - API (dotnet build + engine sim) (push) Failing after 1m40s
CI/CD / CI - Web (tsc + next build) (push) Failing after 1m20s
CI/CD / Deploy - local stack (db + server + web) (push) Has been skipped

Pipeline (.gitea/workflows/ci-cd.yml), all images/packages via Nexus mirror:
- CI api-build: dotnet restore/build server/Hokm.slnx + run Hokm.Sim (rules).
- CI web-check: npm install + tsc --noEmit + next build (static export).
- deploy (self-hosted): pre-deploy pg_dump backup, rollback image tag, build,
  bring up db -> server -> web with stop+rm+up --no-deps (no force-recreate,
  no bare compose down), health-wait each, prune.

Local stack (docker-compose.yml), ports in 1500-1600 so it coexists with manual
dev on 3000/5005:  web :1500 (nginx static) -> server :1505 (.NET) -> db :1510
(postgres, named volume + backups). Dockerfiles: server (.NET, NuGet via
nuget.docker.config, binds 0.0.0.0, busybox wget healthcheck) + web (Next static
export -> nginx, NEXT_PUBLIC_* baked as build args). nginx.conf SPA fallback.

Config: server CORS is now config-driven (Cors__Origins) so the deployed web
origin is allowed without code edits. deploy/ENV_FILE.example documents the
Gitea ENV_FILE secret; DEPLOY.md covers setup/run/LAN-IP/rollback/migrations.

Fonts: switch Vazirmatn + Plus Jakarta Sans from next/font/google (build-time
Google fetch -> fails on the Iran CI runner) to self-hosted @fontsource-variable
packages. Build is offline and ~3x faster; 7 woff2 emitted into out/.

Verified locally: dotnet build slnx + Hokm.Sim (300 matches, exit 0); tsc clean;
next build clean with self-hosted fonts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
soroush.asadi
2026-06-04 19:09:31 +03:30
parent e778e8b5bd
commit 89d42184a1
15 changed files with 534 additions and 21 deletions
+85
View File
@@ -0,0 +1,85 @@
# Deploy — Barg-e Vasat (Soroush CI/CD)
CI/CD runs on **Gitea Actions** (`git.soroushasadi.com`) with all packages and
base images pulled through the **Nexus mirror** (`mirror.soroushasadi.com`).
Pushing to `main` triggers build → deploy.
## Topology
| Service | Image | Container | Host port | Notes |
|---|---|---|---|---|
| Postgres | `postgres:16-alpine` | `hokm-db` | `1510` | named volume `hokm_db_data` |
| API (.NET SignalR) | `hokm-server:latest` | `hokm-server` | `1505` → 5005 | EF Core → Postgres |
| Web (static Next → nginx) | `hokm-web:latest` | `hokm-web` | `1500` → 80 | `NEXT_PUBLIC_*` baked at build |
Ports are in **15001600** on purpose, so the deployed stack runs alongside a
manual `npm run dev` (:3000) and `dotnet run` (:5005) without colliding.
## Pipeline (`.gitea/workflows/ci-cd.yml`)
1. **CI API**: restore (Nexus NuGet) → `dotnet build server/Hokm.slnx` → run `Hokm.Sim` (engine rules validation).
2. **CI Web**: `npm install` (Nexus npm) → `tsc --noEmit``next build` (static export).
3. **Deploy** (`self-hosted`, push to `main` only): backup DB → tag rollback image → build images → bring up `db` (wait healthy) → `server` (stop+rm+up, wait healthy) → `web` (stop+rm+up, wait healthy) → prune.
Deploy follows the safety rules: pre-deploy `pg_dump` backup to `/opt/hokm-backups`,
rollback tag before replace, explicit `stop + rm + up --no-deps` (no
`--force-recreate`, no bare `docker compose down`).
## One-time setup
1. **Secret**: fill `deploy/ENV_FILE.example` and paste into the Gitea repo secret
`ENV_FILE` at `.../HokmPlay/settings/secrets`. At minimum set `JWT_KEY`
(`openssl rand -hex 32`) and `POSTGRES_PASSWORD`.
2. **Runner**: an `act_runner` registered with both labels
(`ubuntu-latest:docker://...` for CI, `self-hosted:host` for deploy) — reused
from existing Soroush projects.
3. **Push**: `git push origin main` → watch `.../HokmPlay/actions`.
## Reaching the stack
- Same machine as the deploy host: open `http://localhost:1500`.
- Different machine (browser elsewhere): set `NEXT_PUBLIC_SERVER_URL` and
`CORS_ORIGINS` in `ENV_FILE` to the host **LAN IP** (e.g.
`http://172.28.144.1:1505` / `http://172.28.144.1:1500`) and push again —
the API URL is baked into the web bundle at build time. (localhost can be
hijacked by the VPN; prefer the LAN IP.)
## Local test (no Gitea, on your machine)
```bash
cd D:\Projects\hokm
copy deploy\ENV_FILE.example .env # then edit JWT_KEY / POSTGRES_PASSWORD
docker compose build server web
docker compose up -d
# web → http://localhost:1500 api → http://localhost:1505/
docker compose logs -f server
```
Tear down (keeps the DB volume):
```bash
docker compose stop
```
## Migrations
The server auto-applies EF migrations when any exist, else `EnsureCreated()`
(current state — no migration classes yet, so the Postgres schema is created on
first boot). When you generate them:
```bash
cd server/src/Hokm.Server
$env:HOKM_DESIGN_CONN="Host=localhost;Port=1510;Database=hokm;Username=hokm;Password=<pw>"
dotnet ef migrations add Init
```
Then the next deploy runs `Database.Migrate()` automatically.
## Rollback
```bash
docker stop hokm-server && docker rm hokm-server
docker run -d --name hokm-server --network hokm_default -p 1505:5005 \
--env-file <(grep -E '^(JWT_|Database__|ConnectionStrings__|Cors__|Zarinpal__)' .env) \
hokm-server:rollback
```
(or just revert the commit and push — CI redeploys the previous code.)