chore(deploy): bake commit version into image and add scripts/deploy.sh
This commit is contained in:
@@ -27,8 +27,5 @@ Thumbs.db
|
|||||||
# --- Playwright test artifacts ---
|
# --- Playwright test artifacts ---
|
||||||
.playwright-mcp/
|
.playwright-mcp/
|
||||||
|
|
||||||
# --- Generated version data ---
|
|
||||||
public/version.json
|
|
||||||
|
|
||||||
# --- Internal audit (not for commit) ---
|
# --- Internal audit (not for commit) ---
|
||||||
SECURITY_AUDIT.md
|
SECURITY_AUDIT.md
|
||||||
|
|||||||
@@ -150,6 +150,7 @@ curl -H "X-Admin-Token: $ADMIN_PASSWORD" http://localhost:3003/api/groups
|
|||||||
| `public/admin.js` | Shared frontend logic |
|
| `public/admin.js` | Shared frontend logic |
|
||||||
| `scripts/backup.sh` | Host-level backup script |
|
| `scripts/backup.sh` | Host-level backup script |
|
||||||
| `scripts/restore.sh` | Host-level restore script |
|
| `scripts/restore.sh` | Host-level restore script |
|
||||||
|
| `scripts/deploy.sh` | Deploy script (pull master, build image with commit version, restart app) |
|
||||||
| `start-tailscale.sh` | Tailscale container entrypoint |
|
| `start-tailscale.sh` | Tailscale container entrypoint |
|
||||||
| `.env.example` | Env var template |
|
| `.env.example` | Env var template |
|
||||||
|
|
||||||
@@ -172,6 +173,9 @@ curl -H "X-Admin-Token: $ADMIN_PASSWORD" http://localhost:3003/api/groups
|
|||||||
# Full rebuild
|
# Full rebuild
|
||||||
docker compose down && docker compose up -d --build
|
docker compose down && docker compose up -d --build
|
||||||
|
|
||||||
|
# Обновление на сервере (pull master + сборка образа с версией коммита + перезапуск app)
|
||||||
|
./scripts/deploy.sh
|
||||||
|
|
||||||
# App logs
|
# App logs
|
||||||
docker compose logs -f app
|
docker compose logs -f app
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ COPY package.json package-lock.json* ./
|
|||||||
RUN npm install --omit=dev
|
RUN npm install --omit=dev
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN mkdir -p uploads certs
|
RUN mkdir -p uploads certs
|
||||||
|
ARG GIT_COMMIT=""
|
||||||
|
ARG GIT_COMMIT_DATE=""
|
||||||
|
RUN node -e "require('fs').writeFileSync('public/version.json', JSON.stringify({ full: process.env.GIT_COMMIT || '', short: (process.env.GIT_COMMIT || '').slice(0, 7), date: process.env.GIT_COMMIT_DATE || '' }, null, 2))"
|
||||||
RUN set -e; \
|
RUN set -e; \
|
||||||
V="v$(cut -d. -f1-2 /etc/alpine-release)"; \
|
V="v$(cut -d. -f1-2 /etc/alpine-release)"; \
|
||||||
try_repo() { \
|
try_repo() { \
|
||||||
|
|||||||
@@ -60,6 +60,33 @@ docker compose down # остановка (данные сохраняю
|
|||||||
> Приложение **не запустится** без `ADMIN_PASSWORD` (защита от пароля по умолчанию).
|
> Приложение **не запустится** без `ADMIN_PASSWORD` (защита от пароля по умолчанию).
|
||||||
> `DB_PASSWORD` задаёт пароль пользователя `app` в PostgreSQL.
|
> `DB_PASSWORD` задаёт пароль пользователя `app` в PostgreSQL.
|
||||||
|
|
||||||
|
## Обновление на сервере (деплой)
|
||||||
|
|
||||||
|
Код приложения находится внутри образа: bind-монтируется только `uploads/`. Поэтому после `git pull` нужна **пересборка образа** — `docker compose up -d` без `--build` и `docker compose restart` новый `server.js` и статику не подхватят.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./scripts/deploy.sh # ветка master (либо $DEPLOY_BRANCH, либо первый аргумент)
|
||||||
|
```
|
||||||
|
|
||||||
|
Скрипт проверяет рабочую копию, обновляет ветку (`fetch` + `checkout` + `pull --ff-only`), собирает образ с версией коммита, перезапускает `app`, затем сверяет `server.js` в контейнере с рабочей копией и печатает `/version.json`.
|
||||||
|
|
||||||
|
Вручную то же самое:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git checkout master && git pull --ff-only origin master
|
||||||
|
docker compose build --build-arg GIT_COMMIT=$(git rev-parse HEAD) --build-arg GIT_COMMIT_DATE=$(git log -1 --format=%cI) app
|
||||||
|
docker compose up -d app
|
||||||
|
```
|
||||||
|
|
||||||
|
Проверка:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -sk https://127.0.0.1:3443/version.json # версия собранного коммита
|
||||||
|
docker compose exec app md5sum /app/server.js # совпадает с md5sum server.js
|
||||||
|
```
|
||||||
|
|
||||||
|
Версия сборки записывается в `public/version.json` внутри образа из аргументов `GIT_COMMIT` / `GIT_COMMIT_DATE` (`build.args` в `docker-compose.yml`, подставляет `scripts/deploy.sh`) и показывается в сайдбаре админки. Файл в git не хранится (`.gitignore`), локально его обновляет хук: `cp scripts/post-commit.sh .git/hooks/post-commit`. Если `/version.json` отдаёт 404 — образ собран без аргументов версии, нужна пересборка.
|
||||||
|
|
||||||
## Конфигурация
|
## Конфигурация
|
||||||
|
|
||||||
Переменные окружения (`.env`):
|
Переменные окружения (`.env`):
|
||||||
|
|||||||
@@ -21,6 +21,9 @@ services:
|
|||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
network: host
|
network: host
|
||||||
|
args:
|
||||||
|
GIT_COMMIT: ${GIT_COMMIT:-}
|
||||||
|
GIT_COMMIT_DATE: ${GIT_COMMIT_DATE:-}
|
||||||
extra_hosts:
|
extra_hosts:
|
||||||
- "host.docker.internal:host-gateway"
|
- "host.docker.internal:host-gateway"
|
||||||
expose:
|
expose:
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"full": "a95af7daa76d37fbb1c4f1e6dcf19175df0a66e0",
|
||||||
|
"short": "a95af7d",
|
||||||
|
"date": "2026-09-23T22:23:38+03:00"
|
||||||
|
}
|
||||||
Executable
+49
@@ -0,0 +1,49 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Обновление кода на сервере: pull ветки, сборка образа с версией коммита, перезапуск app.
|
||||||
|
# Использование: ./scripts/deploy.sh [ветка] (по умолчанию master либо $DEPLOY_BRANCH)
|
||||||
|
set -euo pipefail
|
||||||
|
cd "$(dirname "$0")/.."
|
||||||
|
|
||||||
|
BRANCH="${1:-${DEPLOY_BRANCH:-master}}"
|
||||||
|
|
||||||
|
if docker compose version >/dev/null 2>&1; then
|
||||||
|
DC="docker compose"
|
||||||
|
else
|
||||||
|
DC="docker-compose"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ">> Рабочая копия"
|
||||||
|
if [ -n "$(git status --porcelain --untracked-files=no)" ]; then
|
||||||
|
git status --short
|
||||||
|
echo "Есть незакоммиченные изменения. Задайте DEPLOY_ALLOW_DIRTY=1, чтобы продолжить."
|
||||||
|
[ "${DEPLOY_ALLOW_DIRTY:-0}" = "1" ] || exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ">> Обновление ветки $BRANCH"
|
||||||
|
git fetch origin --prune
|
||||||
|
git checkout "$BRANCH"
|
||||||
|
git pull --ff-only origin "$BRANCH"
|
||||||
|
|
||||||
|
GIT_COMMIT="$(git rev-parse HEAD)"
|
||||||
|
GIT_COMMIT_DATE="$(git log -1 --format=%cI)"
|
||||||
|
export GIT_COMMIT GIT_COMMIT_DATE
|
||||||
|
echo ">> Сборка: $GIT_COMMIT ($GIT_COMMIT_DATE)"
|
||||||
|
|
||||||
|
$DC build --build-arg "GIT_COMMIT=$GIT_COMMIT" --build-arg "GIT_COMMIT_DATE=$GIT_COMMIT_DATE" app
|
||||||
|
$DC up -d app
|
||||||
|
|
||||||
|
echo ">> Проверка"
|
||||||
|
$DC exec -T app cat /app/public/version.json
|
||||||
|
HOST_MD5="$(md5sum server.js | cut -d' ' -f1)"
|
||||||
|
APP_MD5="$($DC exec -T app md5sum /app/server.js | tr -d '\r' | cut -d' ' -f1)"
|
||||||
|
if [ "$HOST_MD5" = "$APP_MD5" ]; then
|
||||||
|
echo "server.js в контейнере совпадает с рабочей копией: $APP_MD5"
|
||||||
|
else
|
||||||
|
echo "ВНИМАНИЕ: server.js в контейнере ($APP_MD5) не совпадает с рабочей копией ($HOST_MD5)"
|
||||||
|
fi
|
||||||
|
if command -v curl >/dev/null 2>&1; then
|
||||||
|
echo ">> /version.json"
|
||||||
|
curl -sk https://127.0.0.1:3443/version.json || true
|
||||||
|
echo
|
||||||
|
fi
|
||||||
|
$DC ps
|
||||||
Reference in New Issue
Block a user