ADR-012: именованные сборки (zip) рядом с номерными — /sol/; ^~ для служебных путей; два дефекта сборки фронтендера
This commit is contained in:
@@ -81,3 +81,29 @@ supported on this GPU` и Unity-исключение из wasm. **Это арт
|
||||
|
||||
⚠️ `publish-release.sh` содержит креды реестра (`subochev:…`) в открытом виде — файл живёт только
|
||||
внутри LXC 152 как root (`/opt/game/tools/`), в git лежит шаблон из репы ops. Не тиражировать наружу.
|
||||
|
||||
## Именованные сборки (zip от фронтендера) — рядом с номерными
|
||||
|
||||
Кроме номерных релизов из CI (`/opt/game/releases/<N>/`) на nginx можно положить **именованную**
|
||||
сборку — готовый zip от фронтендера, вне релизного цикла:
|
||||
|
||||
```bash
|
||||
# zip сначала доставить на LXC 152 (через хост проксмокса):
|
||||
scp build.zip root@192.168.76.66:/tmp/build.zip
|
||||
ssh root@192.168.76.66 "pct push 152 /tmp/build.zip /tmp/build.zip"
|
||||
# разложить под именем:
|
||||
ssh root@192.168.76.66 "pct exec 152 -- /opt/game/tools/publish-zip.sh sol /tmp/build.zip"
|
||||
```
|
||||
|
||||
Адрес: **`https://smart-updown.binom.pw/sol/`**. Активную сборку такая публикация **не трогает** —
|
||||
корень по-прежнему редиректит на текущий номер.
|
||||
|
||||
**Питфоллы именованных сборок:**
|
||||
- Служебные location обязаны иметь **`^~`** (`^~ /api`, `^~ /ws`, `^~ /solana-rpc/`). Regex-локейшн
|
||||
сборок `^/(?<build>[a-zA-Z0-9][a-zA-Z0-9_-]*)…` иначе перехватит `/api/...` как «сборку с именем
|
||||
api» — regex в nginx приоритетнее обычного префикса. Без `^~` API ломается (проверено).
|
||||
- `immutable` — **только для номерных** каталогов (`~^/[0-9]+/…`). Именованную папку могут перезалить
|
||||
тем же именем → её ассеты кэшировать нельзя.
|
||||
- Сборки фронтендера бывают на `.unityweb`-ассетах (`Build.wasm.unityweb` и т.п.) — расширение
|
||||
`unityweb` в маске кэша учтено.
|
||||
- Имя папки не должно совпадать со служебным путём (`api`, `ws`, `solana-rpc`) — скрипт это проверяет.
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
# nginx для раздачи Unity-сборок «Бинарной ракеты» — LXC 152 (game-web, 192.168.76.152)
|
||||
# Файл: /etc/nginx/sites-available/game
|
||||
#
|
||||
# Модель версионирования:
|
||||
# /opt/game/releases/<N>/ — файлы сборки N (Build/, TemplateData/, index.html)
|
||||
# Модель версионирования (два вида сборок, живут рядом):
|
||||
# /opt/game/releases/<N>/ — номерные релизы (1, 2, 3, … 29) из CI/реестра
|
||||
# /opt/game/releases/<имя>/ — именованные сборки (например `sol`), присланные zip'ом
|
||||
# /opt/game/current -> releases/<N> — активная сборка (симлинк, переключает publish-release.sh)
|
||||
#
|
||||
# https://smart-updown.binom.pw/ -> 302 на активную сборку (/29/)
|
||||
# https://smart-updown.binom.pw/29/ -> конкретная сборка 29
|
||||
# https://smart-updown.binom.pw/29/ -> номерная сборка 29
|
||||
# https://smart-updown.binom.pw/sol/ -> именованная сборка `sol`
|
||||
# https://smart-updown.binom.pw/29/Build/WebGL.data -> ассет сборки 29
|
||||
#
|
||||
# ⚠️ ПОЧЕМУ КОРЕНЬ РЕДИРЕКТИТ, А НЕ ОТДАЁТ ФАЙЛЫ:
|
||||
@@ -18,16 +20,20 @@
|
||||
# просто перестаёт использоваться, и `immutable` становится корректным.
|
||||
# Номер активной сборки пишет publish-release.sh в отдельный include-файл /etc/nginx/game-active.inc.
|
||||
#
|
||||
# ⚠️ ПОРЯДОК LOCATION. Служебные пути (/api, /ws, /solana-rpc) объявлены с префиксом `^~`,
|
||||
# потому что regex-локейшн сборок `^/(?<build>…)` иначе перехватил бы их (regex в nginx
|
||||
# приоритетнее обычного префикса). `^~` подавляет проверку regex для этого префикса.
|
||||
#
|
||||
# API и WebSocket nginx НЕ обслуживает: их забирает Traefik на VDS
|
||||
# (router smart-updown-api: PathPrefix /api,/ws -> куб 192.168.76.120).
|
||||
# Локальные location /api, /ws, /solana-rpc — страховка для запросов,
|
||||
# которые всё же дойдут до nginx (прямой заход по IP, старые сборки).
|
||||
|
||||
# Ассеты Unity внутри каталога версии неизменяемы -> immutable.
|
||||
# Всё, что НЕ под номером сборки, и index.html -> no-store.
|
||||
# Ассеты Unity внутри НОМЕРНОГО каталога неизменяемы -> immutable.
|
||||
# Всё остальное (включая именованные сборки: их папку могут перезалить) -> no-store.
|
||||
map $uri $game_cache_control {
|
||||
default "no-store";
|
||||
~^/[0-9]+/.+\.(js|wasm|data|mem|symbols|css|png|jpg|jpeg|gif|svg|woff2?)$ "public, max-age=604800, immutable";
|
||||
~^/[0-9]+/.+\.(js|wasm|data|mem|symbols|css|png|jpg|jpeg|gif|svg|woff2?|unityweb)$ "public, max-age=604800, immutable";
|
||||
}
|
||||
|
||||
server {
|
||||
@@ -51,18 +57,11 @@ server {
|
||||
# Файл переписывает publish-release.sh при --activate.
|
||||
include /etc/nginx/game-active.inc;
|
||||
|
||||
# --- версионированные сборки: /<число>/... -------------------------------
|
||||
# /29/index.html -> /opt/game/releases/29/index.html
|
||||
location ~ ^/(?<build>[0-9]+)(?<subpath>/.*)?$ {
|
||||
root /opt/game/releases;
|
||||
try_files /$build$subpath /$build$subpath/ =404;
|
||||
}
|
||||
|
||||
# --- страховка: API -------------------------------------------------------
|
||||
# --- страховка: API (^~ — приоритетнее regex-локейшна сборок) -------------
|
||||
# Нормальный /api идёт мимо nginx (Traefik VDS -> куб). Если запрос всё же
|
||||
# пришёл сюда — уходим в куб на k3s-Traefik с правильным Host, дальше
|
||||
# ingress smart-updown-api ведёт в шлюз :8895.
|
||||
location /api {
|
||||
location ^~ /api {
|
||||
proxy_pass http://192.168.76.120;
|
||||
proxy_set_header Host smart-updown.binom.pw;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
@@ -76,7 +75,7 @@ server {
|
||||
}
|
||||
|
||||
# --- страховка: WebSocket -------------------------------------------------
|
||||
location /ws {
|
||||
location ^~ /ws {
|
||||
proxy_pass http://192.168.76.120;
|
||||
proxy_set_header Host smart-updown.binom.pw;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
@@ -92,10 +91,19 @@ server {
|
||||
# --- страховка: Solana RPC -------------------------------------------------
|
||||
# /solana-rpc/ НЕ ловится PathPrefix /api на VDS, поэтому уходит в корневой
|
||||
# router, т.е. сюда. Старые сборки игры стучатся именно в этот путь.
|
||||
location /solana-rpc/ {
|
||||
location ^~ /solana-rpc/ {
|
||||
proxy_pass http://192.168.76.181:8899/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_http_version 1.1;
|
||||
proxy_read_timeout 300s;
|
||||
}
|
||||
|
||||
# --- сборки: /<N>/... и /<имя>/... ---------------------------------------
|
||||
# /29/index.html -> /opt/game/releases/29/index.html
|
||||
# /sol/index.html -> /opt/game/releases/sol/index.html
|
||||
# /29/Build/WebGL.data -> /opt/game/releases/29/Build/WebGL.data
|
||||
location ~ ^/(?<build>[a-zA-Z0-9][a-zA-Z0-9_-]*)(?<subpath>/.*)?$ {
|
||||
root /opt/game/releases;
|
||||
try_files /$build$subpath /$build$subpath/ =404;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
#!/bin/bash
|
||||
# Публикация готового zip-билда (например, присланного фронтендером) в ОТДЕЛЬНУЮ папку nginx.
|
||||
#
|
||||
# publish-zip.sh <имя-папки> <путь-к-zip>
|
||||
#
|
||||
# Имя папки может быть НЕ числовым (например `sol`) — такие сборки живут рядом с
|
||||
# номерными релизами (/opt/game/releases/<N>/) и АКТИВНУЮ сборку НЕ трогают:
|
||||
# корень продолжает редиректить на текущий номер, а эта сборка доступна по /<имя>/.
|
||||
set -euo pipefail
|
||||
|
||||
NAME="${1:?использование: publish-zip.sh <имя-папки> <путь-к-zip>}"
|
||||
ZIP="${2:?использование: publish-zip.sh <имя-папки> <путь-к-zip>}"
|
||||
DEST="/opt/game/releases/${NAME}"
|
||||
TMP="/tmp/unzip-${NAME}"
|
||||
|
||||
case "$NAME" in
|
||||
*[!a-zA-Z0-9_-]*|'') echo "имя папки: только буквы/цифры/дефис/подчёркивание, получено: $NAME" >&2; exit 1 ;;
|
||||
api|ws|solana-rpc) echo "имя '$NAME' занято служебным location — выбери другое" >&2; exit 1 ;;
|
||||
esac
|
||||
|
||||
[ -f "$ZIP" ] || { echo "нет файла $ZIP" >&2; exit 1; }
|
||||
|
||||
command -v unzip >/dev/null || DEBIAN_FRONTEND=noninteractive apt-get install -y -qq unzip >/dev/null
|
||||
|
||||
echo "=== 1/3 распаковываю ${ZIP} ==="
|
||||
rm -rf "$TMP"; mkdir -p "$TMP"
|
||||
unzip -q "$ZIP" -d "$TMP"
|
||||
|
||||
# если в архиве одна вложенная папка — поднимаем содержимое наверх
|
||||
CNT=$(find "$TMP" -mindepth 1 -maxdepth 1 | wc -l)
|
||||
if [ "$CNT" = 1 ]; then
|
||||
INNER="$(find "$TMP" -mindepth 1 -maxdepth 1)"
|
||||
if [ -d "$INNER" ]; then
|
||||
mv "$INNER"/* "$TMP"/ 2>/dev/null || true
|
||||
rmdir "$INNER" 2>/dev/null || true
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "=== 2/3 раскладываю в ${DEST} ==="
|
||||
rm -rf "$DEST"; mkdir -p "$DEST"
|
||||
cp -a "$TMP"/. "$DEST"/
|
||||
chmod -R a+rX /opt/game/releases
|
||||
rm -rf "$TMP"
|
||||
|
||||
[ -f "$DEST/index.html" ] || { echo "ВНИМАНИЕ: в сборке нет index.html — проверь архив" >&2; exit 1; }
|
||||
|
||||
echo "=== 3/3 проверка ==="
|
||||
nginx -t >/dev/null && systemctl reload nginx
|
||||
for u in "/${NAME}/" "/${NAME}/index.html"; do
|
||||
printf ' %-46s %s\n' "$u" "$(curl -s -o /dev/null -w '%{http_code}' "http://127.0.0.1${u}")"
|
||||
done
|
||||
echo "готово: https://smart-updown.binom.pw/${NAME}/"
|
||||
du -sh "$DEST"
|
||||
ls -la "$DEST"
|
||||
Reference in New Issue
Block a user