starter/README.md

187 lines
8.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Deploy (Forgejo Actions)
Push в `main` → сайт на твоём домене с SSL. Один файл `.env`, один пуш.
> Флоу на **Forgejo Actions**. Один `.env` + `.forgejo/workflows/deploy.yml` в
> репе — и push. Никакого GitLab.
---
## TL;DR
1. Скопируй в **свой** репозиторий 2 файла из этого стартера:
`.forgejo/workflows/deploy.yml` (не трогать) и `.env` (из `.env.example`).
**Форкать/клонировать весь стартер не надо** — он лишь источник этих файлов;
деплой цепляется к твоей репе по самому факту наличия `deploy.yml`.
2. Заполни `.env` (см. `.env.example`), минимум:
```env
DOMAIN=mysite.com
```
3. Пропиши A-запись домена на IP сервера платформы.
4. `git push` в `main`. Через ~3090 сек сайт открыт на `https://mysite.com`.
Дальше любой push = редеплой. Файл `.forgejo/workflows/deploy.yml` трогать не нужно.
---
## Три типа проектов
Тип определяется по `.env` (`DEPLOY=...`) или авто-детектом.
### 1. static — фронт (90% проектов)
Vite / CRA / Next static export. `bun build` → статика раздаётся напрямую (без
контейнера). Деплой за секунды, редеплой = только заливка новых файлов.
```env
DOMAIN=mysite.com
# DEPLOY=static # дефолт, если нет Dockerfile
# BUILD_DIR=dist # Vite=dist (дефолт), CRA=build, Next export=out
```
- **Vite + React** — ничего настраивать, дефолт.
- **CRA** — `BUILD_DIR=build`.
- **Next.js static export** — в `next.config`: `output: 'export'`,
`images: { unoptimized: true }`, `trailingSlash: true`; `.env`: `BUILD_DIR=out`.
SSR / API routes / Server Actions не работают (только статика).
### 2. docker — фронт + бэкенд в контейнере
Положи `Dockerfile` в корень. Платформа собирает образ и поднимает контейнер,
проксирует домен на него. Контейнер должен слушать порт из `APP_PORT`.
```env
DOMAIN=mysite.com
DEPLOY=docker # или просто наличие Dockerfile
APP_PORT=3000 # порт, который слушает контейнер (default 80)
```
### 3. docker-db — + персистентная база (SQLite)
Как docker, плюс платформа монтирует named-volume в `/data` и прокидывает
`DATABASE_URL=file:/data/app.db`. Том переживает редеплои.
```env
DOMAIN=mysite.com
DEPLOY=docker-db
APP_PORT=3000
```
В коде читаешь `process.env.DATABASE_URL`. Миграции (drizzle/prisma) запускай в
своём entrypoint — платформа их не делает. **Не** указывай `DATABASE_URL` в
`RUNTIME_KEYS` — она ставится автоматически.
---
## Доп. домены — один сайт на нескольких доменах
По умолчанию сайт отдаётся на `DOMAIN` (+ `www.` авто, если у www есть A-запись на
наш IP). Нужен тот же сайт ещё на других доменах — перечисли их через запятую:
```env
DOMAIN=mysite.com
EXTRA_DOMAINS=mysite.io,app.mysite.com,promo.xyz
```
Платформа раздаёт сайт на всех доменах и на **каждый** выпускает свой SSL-серт
автоматически. От тебя — прописать A-запись каждого домена на IP платформы и
`git push`. `www.` для доп-доменов сам не добавляется — если нужен, впиши
`www.mysite.io` явным пунктом. Нет A-записи → в логе `::warning::`, серт выпустится,
как только DNS укажет сюда.
> One-liner `curl get.<domain> | sh` — особый случай, см. `INSTALL_SH` в
> `.env.example` (отдаёт один файл-скрипт с сабдомена `get.`).
---
## Скорость билда
Платформа собирает фронт через **bun** (install в 1030× быстрее npm). Закоммить
lock-файл:
```bash
bun install # создаст bun.lock
git add bun.lock && git commit -m "chore: bun lock" && git push
```
`package.json` и твои `npm run build` скрипты bun понимает as-is. Не коммить
`node_modules` / `dist` (они в `.gitignore`).
### docker-сайты — кеш слоёв и лёгкие образы
Платформа собирает с **BuildKit** и `--cache-from` предыдущего образа: если слои не
менялись, пересборка почти мгновенная. Чтобы это работало на полную:
- **Коммить lockfile** и ставь зависимости ДО копирования кода — слой install
кешируется, пока deps не менялись:
```dockerfile
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build
```
- **Cache-mount'ы** (BuildKit включён) — install/apt не качают заново между сборками,
а Next держит инкрементальный кеш компиляции:
```dockerfile
# syntax=docker/dockerfile:1.7
RUN --mount=type=cache,target=/root/.npm npm ci
RUN --mount=type=cache,target=/app/.next/cache npm run build
```
- **Next.js → лёгкий образ**: `output: 'standalone'` в `next.config` роняет образ
с ~13 ГБ до ~150250 МБ → деплой заметно быстрее (меньше экспорт + запуск).
---
## Секреты (DATABASE_URL, API-ключи)
`.env` в runtime контейнера **не попадает** (парсится только при деплое).
Секреты:
1. **Forgejo → репа → Settings → Actions → Secrets → Add Secret.** Имя секрета =
имя переменной окружения, напр. `DATABASE_URL`, `JWT_SECRET`.
2. В `.env` перечисли, какие из них прокинуть:
```env
RUNTIME_KEYS=DATABASE_URL,JWT_SECRET
```
3. В коде: `process.env.DATABASE_URL`.
Платформа берёт **только** ключи из `RUNTIME_KEYS` — ничего лишнего не утечёт.
(Только для `docker`/`docker-db`; у static-сайта runtime-секретов нет.)
---
## Выключить сайт (kill-switch)
В `.env`:
```env
ENABLED=false
```
Push → контейнер/файлы удаляются, домен отдаёт 404. Убери строку (или `true`) +
push → поднимается обратно. Том и секреты сохраняются.
(Админ может выключить проект и без коммита в репу — переменной `ENABLED=false` в
Settings → Actions → Variables.)
---
## Переменные `.env`
| Key | Default | Зачем |
|---------------|---------|-------|
| `DOMAIN` | — | Обязательно (кроме `ENABLED=false`) |
| `DEPLOY` | auto | `static` / `docker` / `docker-db` |
| `BUILD_DIR` | `dist` | static: папка сборки |
| `APP_PORT` | `80` | docker: порт контейнера |
| `WWW` | `auto` | `false` — не подключать www |
| `EXTRA_DOMAINS`| — | доп. домены через запятую (тот же сайт, свой SSL на каждый) |
| `INSTALL_SH` | — | static: отдать файл-скрипт на `get.<domain>` (`curl \| sh`) |
| `ENABLED` | `true` | `false` — выключить сайт |
| `RUNTIME_KEYS`| — | docker: список секретов в контейнер |
---
## Не открывай сайт в браузере, пока workflow не зелёный
Пока деплой идёт, по домену ещё может не быть валидного серта. Браузер может
закешировать "сломанный SSL". Дождись зелёного run в **Actions**, потом открывай
(или открой в инкогнито).