launch/README.md
2026-07-17 11:59:36 +00:00

283 lines
13 KiB
Markdown
Raw 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.

# Как запустить свой проект на платформе
Один файл `.env` + один пуш → сайт на твоём домене с автоматическим SSL.
Это единственный репозиторий, который нужно знать: тут и гайд, и файлы стартера,
и сам платформенный action.
---
## TL;DR — 4 шага
1. Скопируй в **свой** репозиторий 2 файла из папки [`starter/`](starter) этого репо:
- `.forgejo/workflows/deploy.yml`**не трогать** (тянет платформенный action по ссылке)
- `.env` — из `starter/.env.example`
Форкать/клонировать этот репо **не надо** — деплой цепляется к твоей репе по
самому факту наличия `.forgejo/workflows/deploy.yml`.
2. Заполни `.env`, минимум:
```env
DOMAIN=mysite.com
```
3. Пропиши **A-запись** домена → IP платформы **`51.161.128.94`**.
4. `git push` в дефолтную ветку. Через ~3090 сек сайт открыт на `https://mysite.com`.
Дальше любой push = редеплой. `deploy.yml` трогать не нужно никогда.
---
## Как это работает (флоу)
```
git push
Forgejo Actions запускает .forgejo/workflows/deploy.yml
│ (uses: https://git.extract.team/platform/launch@main)
platform/launch action:
├─ клонирует твою репу
├─ читает .env → DOMAIN, тип деплоя (static / docker / docker-db)
├─ билд: static → bun build | docker → docker build
├─ публикует статику в /srv/sites │ или поднимает контейнер
└─ пишет фрагмент Caddy на твой домен
Caddy автоматически выпускает SSL (Let's Encrypt) → https://mysite.com живой
```
Один репозиторий = один сайт (один `.env`, один `DOMAIN`).
---
## Тип проекта: static / docker / docker-db
Тип определяется полем `DEPLOY` в `.env` или авто-детектом (по наличию `Dockerfile`).
### 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 в static-режиме не работают.
### 2. `docker` — фронт + бэкенд в контейнере
Положи `Dockerfile` в корень. Платформа собирает образ, поднимает контейнер и
проксирует домен на него. Контейнер должен слушать порт из `APP_PORT`.
```env
DOMAIN=mysite.com
DEPLOY=docker # или просто наличие Dockerfile
APP_PORT=3000 # порт контейнера (default 80)
```
### 3. `docker-db` — docker + персистентная база (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` (ставится автоматически).
---
## Домен и SSL
- **`DOMAIN`** — обязательное поле. Заранее пропиши **A-запись** домена на IP
платформы `51.161.128.94` (у своего регистратора).
- **`www.`** подключается автоматически, если у `www.<domain>` тоже есть A-запись на
наш IP. Не нужен — поставь `WWW=false`.
- **SSL выпускается сам** (Let's Encrypt), обычно за 3060 сек после первого деплоя,
когда DNS уже указывает на наш IP. Отдельный серт на каждый домен. Ничего
настраивать не надо.
- **Не открывай сайт в браузере, пока workflow не зелёный** — пока идёт деплой,
валидного серта ещё может не быть, и браузер закеширует «сломанный SSL». Дождись
зелёного run в **Actions** (или открой в инкогнито).
### Доп. домены — один сайт на нескольких доменах
```env
DOMAIN=mysite.com
EXTRA_DOMAINS=mysite.io,app.mysite.com,promo.xyz
```
Тот же сайт на всех доменах, на **каждый** — свой авто-SSL. От тебя: A-запись
каждого домена на наш IP. `www.` для доп-доменов сам не добавляется — если нужен,
впиши `www.mysite.io` явным пунктом.
---
## Twitter / X проекта
Хочешь, чтобы в **админ-панели** рядом с сайтом показывался его твиттер — добавь в
`.env`:
```env
TWITTER_URL=https://x.com/mysite
```
Принимается полный URL, `@handle` или голый `handle` (`TWITTER=` тоже работает).
Платформа нормализует и покажет как `𝕏 @mysite`. Ни на деплой, ни на сайт не влияет —
только метаданные для админки.
---
## Монорепозиторий
Платформа деплоит **один сайт на репозиторий** (один `.env`, один `DOMAIN`). Отдельного
поля «папка приложения» нет — сборка идёт от **корня репы**. Поэтому для монорепо:
- `.env` и `package.json` (или `Dockerfile`) — **в корне** репозитория.
- **static:** корневой скрипт `build` должен собирать нужное приложение (через
workspaces / turbo / nx), а `BUILD_DIR` — указывать на его вывод:
```env
DOMAIN=mysite.com
BUILD_DIR=apps/web/dist # куда собирается целевое приложение
```
```jsonc
// package.json в корне
{ "scripts": { "build": "npm run build -w apps/web" } }
```
- **docker:** `Dockerfile` в корне, контекст сборки = весь репозиторий, так что он
спокойно `COPY apps/web …` и собирает нужный пакет. Задай `APP_PORT`.
- Нужно задеплоить **несколько** приложений из одного монорепо на **разные** домены —
сделай под каждое отдельный репозиторий (один репо = один сайт).
---
## Установочный сабдомен `curl get.<domain> | sh` (self-service)
Красивый one-liner установки — делается **сам, без админа**, для `static`-сайтов:
1. Положи скрипт в репу → `public/install.sh` (папка `public/` у Vite попадает в
`dist/` как есть → окажется в корне сайта).
2. **DNS:** A-запись `get` → IP платформы (host = `get`).
3. В `.env`: `INSTALL_SH=install.sh` → `git push`.
CI сам поднимет `get.<domain>`, выпустит SSL и будет отдавать `install.sh` как
`text/plain`. Убери `INSTALL_SH` и запушь — сабдомен снимется. В админке этот
сабдомен виден в строке «сабдомены».
---
## Секреты (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`.)
---
## Скорость билда
Фронт собирается через **bun** (install в 1030× быстрее npm). Закоммить lock:
```bash
bun install # создаст bun.lock
git add bun.lock && git commit -m "chore: bun lock" && git push
```
### docker-сайты — кеш слоёв и лёгкие образы
Сборка идёт с **BuildKit** и `--cache-from` предыдущего образа. Чтобы это работало:
- ставь зависимости ДО копирования кода (слой install кешируется):
```dockerfile
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build
```
- cache-mount'ы:
```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 МБ → деплой заметно быстрее.
---
## Выключить сайт (kill-switch)
```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 на каждый) |
| `TWITTER_URL` | — | твиттер/X проекта для админки (`@handle` / URL) |
| `INSTALL_SH` | — | static: отдать файл-скрипт на `get.<domain>` (`curl \| sh`) |
| `RUNTIME_KEYS` | — | docker: список секретов в контейнер |
| `ENABLED` | `true` | `false` — выключить сайт |
---
## Доступ к гиту (один раз)
- Форж: **https://git.extract.team**, логин — твой username.
- SSH: `git@git.extract.team:2223`. Добавь ключ: **Settings → SSH/GPG Keys**, затем в
`~/.ssh/config`:
```
Host git.extract.team
Port 2223
User git
```
Ремоут: `git remote set-url origin git@git.extract.team:<логин>/<репа>.git`
(или HTTPS — логин + пароль).
---
## Частые проблемы
- **502 / сайт не грузится сразу после пуша** — деплой ещё идёт или серт выпускается.
Дождись зелёного run в Actions.
- **`build output 'dist' not found`** — не та `BUILD_DIR` (CRA → `build`, Next export
→ `out`, монорепо → путь до вывода приложения).
- **`no .env` / `no package.json`** — оба файла должны быть в **корне** репозитория.
- **SSL не выпускается** — нет A-записи домена на `51.161.128.94` (в логе деплоя
будет `::warning::`). Пропиши DNS — серт выпустится сам.
- Застрял — пиши админу.
---
айлы для копирования — в [`starter/`](starter). Платформенный action — [`action.yml`](action.yml)._