From bfe5a0faa4e8deea02666de36bc43385dfb87753 Mon Sep 17 00:00:00 2001 From: ops Date: Mon, 1 Jun 2026 23:44:09 +0000 Subject: [PATCH] clean starter (phase 5) --- .env.example | 41 ++++++++++ .forgejo/workflows/deploy.yml | 21 +++++ .gitignore | 32 ++++++++ README.md | 140 +++++++++++++++++++++++++++++++++- 4 files changed, 232 insertions(+), 2 deletions(-) create mode 100644 .env.example create mode 100644 .forgejo/workflows/deploy.yml create mode 100644 .gitignore diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..777cf74 --- /dev/null +++ b/.env.example @@ -0,0 +1,41 @@ +# Скопируй этот файл в .env (без .example) и заполни. +# .env ДОЛЖЕН быть закоммичен в git — это конфиг деплоя, не секреты. +# (Секреты — отдельно, через Forgejo Actions secrets, см. RUNTIME_KEYS ниже.) + +# Домен сайта. Обязательно. Платформа выпустит SSL (Let's Encrypt) и направит +# домен на твой сайт. Заранее пропиши A-запись домена на IP сервера платформы. +DOMAIN=mysite.com + +# Тип деплоя. По умолчанию авто-детект: +# static — фронт (Vite/CRA/Next static export). bun build → раздаётся как +# статика напрямую (без контейнера). Самый быстрый. Дефолт, если в +# корне НЕТ Dockerfile. +# docker — свой Dockerfile в корне: собирается образ и поднимается контейнер. +# Дефолт, если Dockerfile ЕСТЬ. +# docker-db — как docker + персистентная SQLite. Платформа монтирует named-volume +# в /data и прокидывает DATABASE_URL=file:/data/app.db. Переживает +# редеплои. Миграции — твой entrypoint (drizzle/prisma). +# DEPLOY=static + +# Папка со сборкой для static. Default: dist (Vite). CRA → build. Next export → out. +# BUILD_DIR=dist + +# Порт, который слушает твой контейнер (только docker/docker-db). Default: 80. +# APP_PORT=3000 + +# Поведение www. По умолчанию auto — подключим www. ТОЛЬКО если у него есть +# A-запись на наш IP. false — привязать лишь основной домен. +# WWW=false + +# Kill-switch. Default true. Поставь false и push — контейнер/сайт удаляется, домен +# отдаёт 404. Убери строку (или true) и push — поднимается обратно. Данные (volume, +# секреты) сохраняются. +# ENABLED=false + +# Какие секреты прокинуть в контейнер (docker/docker-db). Список имён через запятую. +# Сами значения заведи в Forgejo: репа → Settings → Actions → Secrets → Add Secret, +# имя секрета = имя переменной окружения. Платформа возьмёт только перечисленные ключи. +# RUNTIME_KEYS=DATABASE_URL,JWT_SECRET +# → контейнер получит process.env.DATABASE_URL и process.env.JWT_SECRET. +# Для DEPLOY=docker-db переменную DATABASE_URL НЕ указывай — она ставится автоматически. +# RUNTIME_KEYS= diff --git a/.forgejo/workflows/deploy.yml b/.forgejo/workflows/deploy.yml new file mode 100644 index 0000000..33c1feb --- /dev/null +++ b/.forgejo/workflows/deploy.yml @@ -0,0 +1,21 @@ +name: deploy +on: + push: + branches: [main] +jobs: + deploy: + runs-on: docker-cli + container: + image: docker:24.0.7 + volumes: + - /srv/sites:/srv/sites + - /srv/platform/caddy/sites:/srv/platform/caddy/sites + steps: + - uses: https://git.154.83.149.72.nip.io/platform/ci-templates@main + with: + repo: ${{ github.repository }} + ref: ${{ github.ref_name }} + sha: ${{ github.sha }} + token: ${{ github.token }} + secrets_json: ${{ toJSON(secrets) }} + vars_json: ${{ toJSON(vars) }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7aa9e60 --- /dev/null +++ b/.gitignore @@ -0,0 +1,32 @@ +# dependencies +node_modules +.pnp +.pnp.js + +# build output +dist +dist-ssr +build +*.local + +# logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + +# editor +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +# IMPORTANT: .env МУСТ БЫТЬ ЗАКОММИЧЕН — это конфиг деплоя (DOMAIN и т.д.). +# Локальные секреты держи в .env.local (он игнорится через *.local выше). diff --git a/README.md b/README.md index e9f7719..f999057 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,139 @@ -# starter +# Deploy (Forgejo Actions) -Fork me: starter for the Forgejo Actions deploy flow \ No newline at end of file +Push в `main` → сайт на твоём домене с SSL. Один файл `.env`, один пуш. + +> Это новый флоу на **Forgejo Actions**. Старый GitLab-флоу (`.gitlab-ci.yml`) +> ещё жив до завершения миграции, но новые проекты заводи здесь. + +--- + +## TL;DR + +1. **Fork** этого репо (`templates/starter`) в Forgejo. +2. Положи свой код в корень. +3. Создай `.env` (см. `.env.example`), минимум: + ```env + DOMAIN=mysite.com + ``` +4. Пропиши A-запись домена на IP сервера платформы. +5. `git push` в `main`. Через ~30–90 сек сайт открыт на `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` — она ставится автоматически. + +--- + +## Скорость билда — bun + +Платформа собирает фронт через **bun** (install в 10–30× быстрее 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`). + +--- + +## Секреты (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 | +| `ENABLED` | `true` | `false` — выключить сайт | +| `RUNTIME_KEYS`| — | docker: список секретов в контейнер | + +--- + +## Не открывай сайт в браузере, пока workflow не зелёный + +Пока деплой идёт, по домену ещё может не быть валидного серта. Браузер может +закешировать "сломанный SSL". Дождись зелёного run в **Actions**, потом открывай +(или открой в инкогнито).