feat(scripts): снимок для контура — во web/ только зависимости сборки (#112)
prepare-contour-snapshot.sh урезает web/package.json arm-core по разрешённому списку (devDependencies: @rsbuild/core, @rsbuild/plugin-react, tailwindcss, daisyui; optional: raisa-*; scripts: build, start, preview), сокращает web-check до npm install + npm run build, пишет убранное в SNAPSHOT.md и сам собирает урезанный web/ во временной копии: не собралось — снимка нет. Гайд: раздел 3.2 и правило «web/package.json из контура обратно не переносить». Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
7c7018a0c2
commit
0574ba402f
@ -78,14 +78,47 @@ docs/scripts/prepare-contour-snapshot.sh scan-gateway /tmp/contour
|
|||||||
(переменная `PYTHON`, по умолчанию `python3.11`); удаляет цель `docker-build` и её упоминания в `check-full`, `.PHONY`,
|
(переменная `PYTHON`, по умолчанию `python3.11`); удаляет цель `docker-build` и её упоминания в `check-full`, `.PHONY`,
|
||||||
`help`.
|
`help`.
|
||||||
4. Заменяет ссылки на внешний Gitea на `RSHB/<repo>#<n>`.
|
4. Заменяет ссылки на внешний Gitea на `RSHB/<repo>#<n>`.
|
||||||
5. Пишет `SNAPSHOT.md` (источник, хеш, дата, что вырезано).
|
5. Только arm-core: урезает `web/package.json` до зависимостей сборки (раздел 3.2) и правит цель `web-check`.
|
||||||
6. Проверяет: нет `.claude`/`.codegraph`/`CLAUDE.md`/`openspec/config.yaml`/планов ни на какой глубине, нет `Dockerfile`, нет локальных путей (`/Users/…`, `/home/…`), нет `uv` и
|
6. Пишет `SNAPSHOT.md` (источник, хеш, дата, что вырезано и какие пакеты/скрипты фронта убраны).
|
||||||
|
7. Только arm-core: собирает урезанный `web/` (`npm install` + `npm run build` во временной копии); не собралось — снимок
|
||||||
|
не выдаётся (код 1, «пакет, нужный сборке, не в разрешённом списке»). Для тестов без сети проверку отключает
|
||||||
|
`SNAPSHOT_SKIP_WEB_BUILD=1`.
|
||||||
|
8. Проверяет: нет `.claude`/`.codegraph`/`CLAUDE.md`/`openspec/config.yaml`/планов ни на какой глубине, нет `Dockerfile`, нет локальных путей (`/Users/…`, `/home/…`), нет `uv` и
|
||||||
`git.aepif.ru`, на месте платформенные файлы (`raisa_config.yml`, `setup/`, `run_server.py`, `requirements.txt`,
|
`git.aepif.ru`, на месте платформенные файлы (`raisa_config.yml`, `setup/`, `run_server.py`, `requirements.txt`,
|
||||||
`.env.test`, `contracts/`). Любая проблема — код возврата 1 и список.
|
`.env.test`, `contracts/`). Любая проблема — код возврата 1 и список.
|
||||||
|
|
||||||
Тест скрипта — `make test-snapshot` (синтетический репозиторий с артефактами агента в корне и в `web/`); проверка
|
Тест скрипта — `make test-snapshot` (синтетический репозиторий с артефактами агента в корне и в `web/`); проверка
|
||||||
отставания `web/` и остановка переноса — в `make test-sync-web`.
|
отставания `web/` и остановка переноса — в `make test-sync-web`.
|
||||||
|
|
||||||
|
### 3.2. Урезанный `web/package.json` в снимке arm-core
|
||||||
|
|
||||||
|
В контуре фронт только собирается (`npm install` через Nexus + `rsbuild build`). Зеркало npm банка не содержит свежих
|
||||||
|
версий инструментов разработки (уже ломались на vitest — в зеркале ≤3.1.4; затем eslint, typescript-eslint, prettier из
|
||||||
|
arm-web #29), поэтому снимок оставляет только нужное сборке (решение владельца 16.10.2026, AURORA-1635, issue docs #112).
|
||||||
|
Разработка и проверки фронта (`vitest`, `eslint`, `prettier`, `biome`, `tsc`) идут снаружи, где npm доступен.
|
||||||
|
|
||||||
|
Разрешённый список — массивы `WEB_DEV_ALLOW`, `WEB_OPTIONAL_KEEP_PREFIX`, `WEB_SCRIPTS_KEEP` в начале
|
||||||
|
`prepare-contour-snapshot.sh`:
|
||||||
|
|
||||||
|
| Раздел `package.json` | Остаётся | Убирается |
|
||||||
|
|---|---|---|
|
||||||
|
| `dependencies` | всё | — |
|
||||||
|
| `devDependencies` | `@rsbuild/core`, `@rsbuild/plugin-react`, `tailwindcss`, `daisyui` (`rsbuild.config.ts`, `postcss.config.cjs`, `tailwind.config.js`) | всё остальное: eslint и плагины, prettier, biome, typescript, `@types/*`, vitest, globals |
|
||||||
|
| `optionalDependencies` | `raisa-*` | инструменты разработки (`@biomejs/cli-linux-x64-musl`) |
|
||||||
|
| `scripts` | `build`, `start`, `preview` | `test`, `lint`, `format` и любые новые |
|
||||||
|
|
||||||
|
`make web-check` в снимке — только `npm install` и `npm run build` (без `biome` и `tsc`); `check-full` ссылается на него как
|
||||||
|
раньше. Конфиги инструментов (`biome.json`, `eslint.config.js`, `tsconfig.json`) в `web/` остаются, сборка их не читает.
|
||||||
|
Что убрано в конкретном снимке — в `SNAPSHOT.md`.
|
||||||
|
|
||||||
|
Защита от обратной ошибки: скрипт сам собирает урезанный `web/` во временной копии (снимок остаётся без
|
||||||
|
`node_modules`/`dist`/`package-lock.json`). Если сборка не прошла, значит ей понадобился пакет вне списка: добавьте его в
|
||||||
|
`WEB_DEV_ALLOW` и повторите снимок.
|
||||||
|
|
||||||
|
**Правило обратного потока:** `web/package.json` из контура наружу не переносить — он урезан (нет eslint, prettier, vitest,
|
||||||
|
typescript и скриптов `test`/`lint`/`format`); источник истины — `arm-web`, а `arm-core/web` синхронизируется из него
|
||||||
|
(раздел 3.1). Правки зависимостей в контуре возвращаются в arm-web вручную, только по существу, а не файлом целиком.
|
||||||
|
|
||||||
### 3.1. Синхронизация `arm-core/web` с `arm-web`: `scripts/sync-web.sh`
|
### 3.1. Синхронизация `arm-core/web` с `arm-web`: `scripts/sync-web.sh`
|
||||||
|
|
||||||
Фронт ведётся в двух местах: отдельный репозиторий `RSHB/arm-web` (ветка `test`, прототип и демо-стенд) и встроенный
|
Фронт ведётся в двух местах: отдельный репозиторий `RSHB/arm-web` (ветка `test`, прототип и демо-стенд) и встроенный
|
||||||
@ -156,7 +189,7 @@ make check # ruff, mypy, pytest без интеграционных т
|
|||||||
compose из `docs` либо Docker. Интеграционные тесты против PostgreSQL/NATS/MinIO прогоняются снаружи перед каждым снимком;
|
compose из `docs` либо Docker. Интеграционные тесты против PostgreSQL/NATS/MinIO прогоняются снаружи перед каждым снимком;
|
||||||
пайплайну платформы отдаётся `make check`.
|
пайплайну платформы отдаётся `make check`.
|
||||||
|
|
||||||
Фронт arm-core: `make web-check` (`npm install` через `.npmrc` Nexus, biome, tsc, `npm run build`). В контуре
|
Фронт arm-core: `make web-check` (в снимке — `npm install` через `.npmrc` Nexus и `npm run build`; biome и tsc убраны вместе с пакетами, раздел 3.2). В контуре
|
||||||
`raisa-fastapi-protected-web` и `raisa-ui` ставятся штатно, снаружи они в `optionalDependencies` и пропускаются.
|
`raisa-fastapi-protected-web` и `raisa-ui` ставятся штатно, снаружи они в `optionalDependencies` и пропускаются.
|
||||||
`web/package-lock.json` снаружи не коммитится (OQ-63) — в контуре lock-файл появится при первом `npm install`, решение о его
|
`web/package-lock.json` снаружи не коммитится (OQ-63) — в контуре lock-файл появится при первом `npm install`, решение о его
|
||||||
коммите за контуром.
|
коммите за контуром.
|
||||||
|
|||||||
@ -17,6 +17,15 @@
|
|||||||
# issue. Без терминала вопрос не задаётся: нужен флаг --web-as-is, иначе код 3. Расхождение (прямые правки web/ в arm-core) —
|
# issue. Без терминала вопрос не задаётся: нужен флаг --web-as-is, иначе код 3. Расхождение (прямые правки web/ в arm-core) —
|
||||||
# всегда стоп. arm-web ищется рядом с репозиторием (../arm-web) или в $ARM_WEB; если его нет — предупреждение, снимок делается.
|
# всегда стоп. arm-web ищется рядом с репозиторием (../arm-web) или в $ARM_WEB; если его нет — предупреждение, снимок делается.
|
||||||
# $SYNC_WEB_ARGS — дополнительные аргументы sync-web.sh (для теста: --dry-run --wt-root …).
|
# $SYNC_WEB_ARGS — дополнительные аргументы sync-web.sh (для теста: --dry-run --wt-root …).
|
||||||
|
# $SNAPSHOT_SKIP_WEB_BUILD=1 — не проверять сборку web/ (для тестов без сети); по умолчанию проверка включена.
|
||||||
|
#
|
||||||
|
# Встроенный фронт arm-core в контуре только собирается (npm install через Nexus + rsbuild build), а зеркало npm банка не
|
||||||
|
# содержит свежих версий инструментов разработки. Поэтому в снимке web/package.json урезается по РАЗРЕШЁННОМУ списку
|
||||||
|
# (решение владельца 16.10.2026); всё остальное — eslint, prettier, biome, typescript, vitest, @types/* — разработка
|
||||||
|
# и проверки идут снаружи. Список — единственное место, которое правят, если сборке понадобится ещё пакет.
|
||||||
|
WEB_DEV_ALLOW=(@rsbuild/core @rsbuild/plugin-react tailwindcss daisyui) # devDependencies, нужные сборке
|
||||||
|
WEB_OPTIONAL_KEEP_PREFIX="raisa-" # optionalDependencies: платформенные пакеты
|
||||||
|
WEB_SCRIPTS_KEEP=(build start preview) # scripts, работающие без инструментов разработки
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
usage() { echo "usage: $0 [--web-as-is] <repo-dir> <out-dir> [ref]" >&2; exit 2; }
|
usage() { echo "usage: $0 [--web-as-is] <repo-dir> <out-dir> [ref]" >&2; exit 2; }
|
||||||
@ -139,6 +148,17 @@ while i < len(lines):
|
|||||||
if i < len(lines) and lines[i] == "" and out and out[-1] == "":
|
if i < len(lines) and lines[i] == "" and out and out[-1] == "":
|
||||||
i += 1
|
i += 1
|
||||||
continue
|
continue
|
||||||
|
# web-check в контуре: только npm install и npm run build (biome/tsc убраны вместе с пакетами)
|
||||||
|
if line.startswith("web-check:"):
|
||||||
|
out.append(line)
|
||||||
|
i += 1
|
||||||
|
while i < len(lines) and lines[i].startswith("\t"):
|
||||||
|
if "npx " not in lines[i]:
|
||||||
|
out.append(lines[i])
|
||||||
|
i += 1
|
||||||
|
continue
|
||||||
|
if line.startswith("# Фронт:"):
|
||||||
|
line = line.replace("biome, типы, ", "")
|
||||||
if line.startswith("check-full:"):
|
if line.startswith("check-full:"):
|
||||||
line = re.sub(r"\s+docker-build\b", "", line)
|
line = re.sub(r"\s+docker-build\b", "", line)
|
||||||
if line.startswith("\t$(MAKE) docker-build"):
|
if line.startswith("\t$(MAKE) docker-build"):
|
||||||
@ -157,6 +177,44 @@ if not venv_patched:
|
|||||||
path.write_text("\n".join(out) + "\n", encoding="utf-8")
|
path.write_text("\n".join(out) + "\n", encoding="utf-8")
|
||||||
PY
|
PY
|
||||||
|
|
||||||
|
# --- 2a. arm-core: web/package.json только с зависимостями сборки ---------------------------------------------------------
|
||||||
|
WEB_REMOVED=""
|
||||||
|
if [ -f "$DEST/web/package.json" ]; then
|
||||||
|
WEB_REMOVED=$(python3 -I - "$DEST/web/package.json" "$WEB_OPTIONAL_KEEP_PREFIX" "${WEB_DEV_ALLOW[*]}" "${WEB_SCRIPTS_KEEP[*]}" <<'PY'
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
path = Path(sys.argv[1])
|
||||||
|
optional_prefix = sys.argv[2]
|
||||||
|
dev_allow = sys.argv[3].split()
|
||||||
|
scripts_keep = sys.argv[4].split()
|
||||||
|
pkg = json.loads(path.read_text(encoding="utf-8"))
|
||||||
|
|
||||||
|
missing = [d for d in dev_allow if d not in pkg.get("devDependencies", {})]
|
||||||
|
if missing:
|
||||||
|
sys.exit(f"web/package.json: в devDependencies нет {', '.join(missing)} из разрешённого списка — скрипт устарел относительно репозитория")
|
||||||
|
|
||||||
|
dev = pkg.get("devDependencies", {})
|
||||||
|
opt = pkg.get("optionalDependencies", {})
|
||||||
|
scripts = pkg.get("scripts", {})
|
||||||
|
removed_dev = [n for n in dev if n not in dev_allow]
|
||||||
|
removed_opt = [n for n in opt if not n.startswith(optional_prefix)]
|
||||||
|
removed_scripts = [n for n in scripts if n not in scripts_keep]
|
||||||
|
|
||||||
|
pkg["devDependencies"] = {n: v for n, v in dev.items() if n in dev_allow}
|
||||||
|
if "optionalDependencies" in pkg:
|
||||||
|
pkg["optionalDependencies"] = {n: v for n, v in opt.items() if n.startswith(optional_prefix)}
|
||||||
|
pkg["scripts"] = {n: v for n, v in scripts.items() if n in scripts_keep}
|
||||||
|
path.write_text(json.dumps(pkg, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
||||||
|
|
||||||
|
print("devDependencies: " + (", ".join(removed_dev) or "—"))
|
||||||
|
print("optionalDependencies: " + (", ".join(removed_opt) or "—"))
|
||||||
|
print("scripts: " + (", ".join(removed_scripts) or "—"))
|
||||||
|
PY
|
||||||
|
)
|
||||||
|
fi
|
||||||
|
|
||||||
# --- 3. Ссылки на внешний Gitea → имя репозитория и номер ---------------------------------------------------------
|
# --- 3. Ссылки на внешний Gitea → имя репозитория и номер ---------------------------------------------------------
|
||||||
# В openspec-архивах и README встречаются ссылки вида https://git.aepif.ru/RSHB/<repo>/issues/5 и pulls/7.
|
# В openspec-архивах и README встречаются ссылки вида https://git.aepif.ru/RSHB/<repo>/issues/5 и pulls/7.
|
||||||
while IFS= read -r file; do
|
while IFS= read -r file; do
|
||||||
@ -189,6 +247,17 @@ cat > "$DEST/SNAPSHOT.md" <<EOF
|
|||||||
|
|
||||||
Разработка ведётся снаружи; правки в контуре возвращаются в источник вручную (см. dev/contour-transfer-guide.md в docs).
|
Разработка ведётся снаружи; правки в контуре возвращаются в источник вручную (см. dev/contour-transfer-guide.md в docs).
|
||||||
EOF
|
EOF
|
||||||
|
if [ -n "$WEB_REMOVED" ]; then
|
||||||
|
{
|
||||||
|
printf '\nВстроенный фронт `web/`: `package.json` урезан до зависимостей сборки (в контуре фронт только собирается: '
|
||||||
|
printf '`npm install` через Nexus + `rsbuild build`; зеркало npm банка не содержит свежих инструментов разработки).\n'
|
||||||
|
printf 'Оставлены `dependencies` целиком, devDependencies `%s`, optionalDependencies `%s*`, scripts `%s`.\n' \
|
||||||
|
"${WEB_DEV_ALLOW[*]}" "$WEB_OPTIONAL_KEEP_PREFIX" "${WEB_SCRIPTS_KEEP[*]}"
|
||||||
|
printf 'Убрано:\n\n'
|
||||||
|
printf '%s\n' "$WEB_REMOVED" | sed 's/^/- /'
|
||||||
|
printf '\nЦель `make web-check` — `npm install` и `npm run build`. `web/package.json` из контура обратно не переносить: он урезан.\n'
|
||||||
|
} >> "$DEST/SNAPSHOT.md"
|
||||||
|
fi
|
||||||
if [ -n "$WEB_LAG" ]; then
|
if [ -n "$WEB_LAG" ]; then
|
||||||
printf '\nВстроенный фронт \`web/\` на момент снимка %s.\n' "$WEB_LAG" >> "$DEST/SNAPSHOT.md"
|
printf '\nВстроенный фронт \`web/\` на момент снимка %s.\n' "$WEB_LAG" >> "$DEST/SNAPSHOT.md"
|
||||||
fi
|
fi
|
||||||
@ -210,6 +279,23 @@ fi
|
|||||||
for p in raisa_config.yml setup/setup.sh setup/pip.conf api/run_server.py api/requirements.txt api/.env.test api/contracts; do
|
for p in raisa_config.yml setup/setup.sh setup/pip.conf api/run_server.py api/requirements.txt api/.env.test api/contracts; do
|
||||||
if [ ! -e "$DEST/$p" ]; then echo "ошибка: платформенный файл $p отсутствует" >&2; fail=1; fi
|
if [ ! -e "$DEST/$p" ]; then echo "ошибка: платформенный файл $p отсутствует" >&2; fail=1; fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
# Сборка урезанного web/ собирается в ВРЕМЕННОЙ копии: снимок остаётся без node_modules/dist/package-lock.json, а упавшая
|
||||||
|
# проверка не оставляет полуготовых файлов. Нужен npm и доступ к реестру (в контуре — Nexus, снаружи — публичный npm;
|
||||||
|
# raisa-* опциональны и снаружи пропускаются). Падение сборки почти всегда значит: пакет, нужный сборке, не в WEB_DEV_ALLOW.
|
||||||
|
if [ -f "$DEST/web/package.json" ] && [ "${SNAPSHOT_SKIP_WEB_BUILD:-0}" != 1 ]; then
|
||||||
|
echo "проверка сборки урезанного web/ (npm install + npm run build)…" >&2
|
||||||
|
BUILD_TMP=$(mktemp -d)
|
||||||
|
cp -R "$DEST/web" "$BUILD_TMP/web"
|
||||||
|
if ! { (cd "$BUILD_TMP/web" && npm install --no-audit --no-fund && npm run build) >"$BUILD_TMP/build.log" 2>&1; }; then
|
||||||
|
tail -n 30 "$BUILD_TMP/build.log" >&2
|
||||||
|
rm -rf "$BUILD_TMP" "$DEST"
|
||||||
|
echo "ошибка: сборка урезанного web/ не прошла — пакет, нужный сборке, не в разрешённом списке (WEB_DEV_ALLOW в $(basename "$0"))." >&2
|
||||||
|
echo "снимок не выдан." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
rm -rf "$BUILD_TMP"
|
||||||
|
fi
|
||||||
[ "$fail" = 0 ] || exit 1
|
[ "$fail" = 0 ] || exit 1
|
||||||
|
|
||||||
echo "снимок готов: $DEST"
|
echo "снимок готов: $DEST"
|
||||||
|
|||||||
@ -14,7 +14,7 @@ R="$WORK/src/arm-core"
|
|||||||
put() { mkdir -p "$(dirname "$R/$1")"; printf '%s\n' "${2:-x}" > "$R/$1"; }
|
put() { mkdir -p "$(dirname "$R/$1")"; printf '%s\n' "${2:-x}" > "$R/$1"; }
|
||||||
|
|
||||||
# платформенные файлы, без которых проверка снимка падает
|
# платформенные файлы, без которых проверка снимка падает
|
||||||
printf 'venv:\n\tuv venv api/.venv\n' > "$R/Makefile"
|
printf 'venv:\n\tuv venv api/.venv\n\n# Фронт: biome, типы, сборка\nweb-check:\n\tcd web && npm install\n\tcd web && npx biome check src\n\tcd web && npx tsc --noEmit\n\tcd web && npm run build\n\ncheck-full: check web-check\n' > "$R/Makefile"
|
||||||
for p in raisa_config.yml setup/setup.sh setup/pip.conf api/run_server.py api/requirements.txt api/.env.test \
|
for p in raisa_config.yml setup/setup.sh setup/pip.conf api/run_server.py api/requirements.txt api/.env.test \
|
||||||
api/contracts/schema.json; do put "$p"; done
|
api/contracts/schema.json; do put "$p"; done
|
||||||
# корень: артефакты агента и локальная сборка
|
# корень: артефакты агента и локальная сборка
|
||||||
@ -30,6 +30,27 @@ done
|
|||||||
put web/openspec/specs/osv-ui/spec.md
|
put web/openspec/specs/osv-ui/spec.md
|
||||||
put web/openspec/changes/archive/2026-10-08-demo/design.md
|
put web/openspec/changes/archive/2026-10-08-demo/design.md
|
||||||
put web/src/App.tsx
|
put web/src/App.tsx
|
||||||
|
cat > "$R/web/package.json" <<'JSON'
|
||||||
|
{
|
||||||
|
"name": "frontend",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"build": "rsbuild build",
|
||||||
|
"test": "vitest run",
|
||||||
|
"format": "eslint --fix src && prettier --write src",
|
||||||
|
"lint": "eslint src && prettier --check src",
|
||||||
|
"start": "rsbuild dev --open",
|
||||||
|
"preview": "rsbuild preview"
|
||||||
|
},
|
||||||
|
"dependencies": {"antd": "^6.5.0", "react": "^18.3.1"},
|
||||||
|
"devDependencies": {
|
||||||
|
"@biomejs/biome": "1.9.4", "@rsbuild/core": "1.6.7", "@rsbuild/plugin-react": "^1.1.0", "@types/react": "^18.3.1",
|
||||||
|
"daisyui": "^4.12.10", "eslint": "9.17.0", "prettier": "3.3.3", "tailwindcss": "^3.4.13", "typescript": "^5.7.3",
|
||||||
|
"vitest": "3.1.4"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {"@biomejs/cli-linux-x64-musl": "^1.9.4", "raisa-ui": "^1.0.0"}
|
||||||
|
}
|
||||||
|
JSON
|
||||||
put web/docs/notes.md # прочее содержимое вложенного docs/ остаётся
|
put web/docs/notes.md # прочее содержимое вложенного docs/ остаётся
|
||||||
|
|
||||||
git -C "$R" add -A
|
git -C "$R" add -A
|
||||||
@ -37,7 +58,8 @@ git -C "$R" -c user.name=t -c user.email=t@t commit -qm init
|
|||||||
git -C "$R" push -q origin HEAD:main 2>/dev/null
|
git -C "$R" push -q origin HEAD:main 2>/dev/null
|
||||||
git -C "$R" fetch -q origin
|
git -C "$R" fetch -q origin
|
||||||
|
|
||||||
"$SCRIPT" "$R" "$WORK/out" origin/main >/dev/null
|
# проверка сборки web/ в тесте отключена (нет сети); её включённый режим проверяется ниже заглушкой npm
|
||||||
|
SNAPSHOT_SKIP_WEB_BUILD=1 "$SCRIPT" "$R" "$WORK/out" origin/main >/dev/null
|
||||||
D="$WORK/out/arm-core"
|
D="$WORK/out/arm-core"
|
||||||
|
|
||||||
fail=0
|
fail=0
|
||||||
@ -51,3 +73,82 @@ for p in openspec/specs/osv/spec.md web/openspec/specs/osv-ui/spec.md web/opensp
|
|||||||
done
|
done
|
||||||
[ "$fail" = 0 ] || exit 1
|
[ "$fail" = 0 ] || exit 1
|
||||||
echo "ok: prepare-contour-snapshot.sh вырезает артефакты агента на любой глубине"
|
echo "ok: prepare-contour-snapshot.sh вырезает артефакты агента на любой глубине"
|
||||||
|
|
||||||
|
# --- web/package.json урезан до зависимостей сборки -------------------------------------------------------------------
|
||||||
|
python3 -I - "$D/web/package.json" <<'PY' || fail=1
|
||||||
|
import json, sys
|
||||||
|
pk = json.load(open(sys.argv[1], encoding="utf-8"))
|
||||||
|
errs = []
|
||||||
|
if sorted(pk["devDependencies"]) != ["@rsbuild/core", "@rsbuild/plugin-react", "daisyui", "tailwindcss"]:
|
||||||
|
errs.append(f"devDependencies: {sorted(pk['devDependencies'])}")
|
||||||
|
if pk["devDependencies"]["@rsbuild/core"] != "1.6.7":
|
||||||
|
errs.append("версии devDependencies должны сохраниться")
|
||||||
|
if pk["dependencies"] != {"antd": "^6.5.0", "react": "^18.3.1"}:
|
||||||
|
errs.append("dependencies изменились")
|
||||||
|
if pk["optionalDependencies"] != {"raisa-ui": "^1.0.0"}:
|
||||||
|
errs.append(f"optionalDependencies: {pk['optionalDependencies']}")
|
||||||
|
if sorted(pk["scripts"]) != ["build", "preview", "start"]:
|
||||||
|
errs.append(f"scripts: {sorted(pk['scripts'])}")
|
||||||
|
if errs:
|
||||||
|
print("FAIL:", *errs, sep="\n ", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
PY
|
||||||
|
# Makefile: web-check без biome/tsc, check-full на месте
|
||||||
|
if grep -Eq "biome|tsc" "$D/Makefile"; then echo "FAIL: в Makefile снимка остались biome/tsc" >&2; fail=1; fi
|
||||||
|
grep -q "cd web && npm install" "$D/Makefile" && grep -q "cd web && npm run build" "$D/Makefile" \
|
||||||
|
|| { echo "FAIL: web-check потерял npm install/build" >&2; fail=1; }
|
||||||
|
grep -q "^check-full:.*web-check" "$D/Makefile" || { echo "FAIL: check-full не ссылается на web-check" >&2; fail=1; }
|
||||||
|
# SNAPSHOT.md перечисляет убранное
|
||||||
|
for w in "@biomejs/biome" "eslint" "prettier" "typescript" "vitest" "@types/react" "@biomejs/cli-linux-x64-musl" \
|
||||||
|
"test" "lint" "format"; do
|
||||||
|
grep -q "$w" "$D/SNAPSHOT.md" || { echo "FAIL: SNAPSHOT.md не упоминает убранное: $w" >&2; fail=1; }
|
||||||
|
done
|
||||||
|
[ "$fail" = 0 ] || exit 1
|
||||||
|
echo "ok: web/package.json, Makefile и SNAPSHOT.md урезаны под сборку"
|
||||||
|
|
||||||
|
# --- проверка сборки web/ (включена по умолчанию): заглушка npm в PATH -----------------------------------------------
|
||||||
|
STUB="$WORK/stub"; mkdir -p "$STUB"
|
||||||
|
cat > "$STUB/npm" <<'SH'
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# заглушка: install создаёт node_modules, build — dist; NPM_STUB_FAIL=build роняет сборку
|
||||||
|
echo "$*" >> "$NPM_STUB_LOG"
|
||||||
|
case "$1" in
|
||||||
|
install) mkdir -p node_modules; echo "$PWD" >> "$NPM_STUB_LOG.cwd" ;;
|
||||||
|
run) if [ "${NPM_STUB_FAIL:-}" = build ]; then echo "Module not found: eslint" >&2; exit 1; fi; mkdir -p dist ;;
|
||||||
|
esac
|
||||||
|
SH
|
||||||
|
chmod +x "$STUB/npm"
|
||||||
|
export NPM_STUB_LOG="$WORK/npm.log"
|
||||||
|
|
||||||
|
# сборка проходит: снимок выдан, node_modules/dist в него не попали, исходный каталог вывода чист
|
||||||
|
PATH="$STUB:$PATH" "$SCRIPT" "$R" "$WORK/out-ok" origin/main >/dev/null
|
||||||
|
O="$WORK/out-ok/arm-core"
|
||||||
|
grep -q "^install" "$NPM_STUB_LOG" && grep -q "^run build" "$NPM_STUB_LOG" \
|
||||||
|
|| { echo "FAIL: проверка сборки не вызвала npm install и npm run build" >&2; fail=1; }
|
||||||
|
for p in web/node_modules web/dist web/package-lock.json; do
|
||||||
|
[ ! -e "$O/$p" ] || { echo "FAIL: в снимке остался $p" >&2; fail=1; }
|
||||||
|
done
|
||||||
|
[ -e "$O/web/package.json" ] || { echo "FAIL: после проверки сборки пропал web/package.json" >&2; fail=1; }
|
||||||
|
|
||||||
|
# сборка падает: код 1, понятное сообщение, снимка нет
|
||||||
|
rc=0
|
||||||
|
PATH="$STUB:$PATH" NPM_STUB_FAIL=build "$SCRIPT" "$R" "$WORK/out-bad" origin/main >"$WORK/bad.out" 2>&1 || rc=$?
|
||||||
|
[ "$rc" = 1 ] || { echo "FAIL: при падении сборки ожидался код 1, получен $rc" >&2; fail=1; }
|
||||||
|
grep -q "не в разрешённом списке" "$WORK/bad.out" || { echo "FAIL: нет сообщения про разрешённый список" >&2; fail=1; }
|
||||||
|
[ ! -e "$WORK/out-bad/arm-core" ] || { echo "FAIL: при падении сборки снимок выдан" >&2; fail=1; }
|
||||||
|
|
||||||
|
# tabular-parser и scan-gateway фронта не имеют: npm не вызывается
|
||||||
|
: > "$NPM_STUB_LOG"
|
||||||
|
git init -q --bare "$WORK/tp-origin.git"
|
||||||
|
git clone -q "$WORK/tp-origin.git" "$WORK/src/tabular-parser" 2>/dev/null
|
||||||
|
T="$WORK/src/tabular-parser"
|
||||||
|
printf 'venv:\n\tuv venv api/.venv\n' > "$T/Makefile"
|
||||||
|
for p in raisa_config.yml setup/setup.sh setup/pip.conf api/run_server.py api/requirements.txt api/.env.test api/contracts/s.json; do
|
||||||
|
mkdir -p "$(dirname "$T/$p")"; echo x > "$T/$p"
|
||||||
|
done
|
||||||
|
git -C "$T" add -A; git -C "$T" -c user.name=t -c user.email=t@t commit -qm init
|
||||||
|
git -C "$T" push -q origin HEAD:main 2>/dev/null; git -C "$T" fetch -q origin
|
||||||
|
PATH="$STUB:$PATH" "$SCRIPT" "$T" "$WORK/out-tp" origin/main >/dev/null
|
||||||
|
[ ! -s "$NPM_STUB_LOG" ] || { echo "FAIL: для tabular-parser вызван npm" >&2; fail=1; }
|
||||||
|
[ "$fail" = 0 ] || exit 1
|
||||||
|
echo "ok: проверка сборки web/ — выдаёт снимок без node_modules/dist, при падении снимка нет, у сервисов без фронта не вызывается"
|
||||||
|
|||||||
@ -25,7 +25,7 @@ git clone -q "$WORK/arm-web.git" "$WEB" 2>/dev/null
|
|||||||
git -C "$WEB" checkout -q -b test
|
git -C "$WEB" checkout -q -b test
|
||||||
put "$WEB/src/a.ts" "export const a = 1;"
|
put "$WEB/src/a.ts" "export const a = 1;"
|
||||||
put "$WEB/README.md" "arm-web v1"
|
put "$WEB/README.md" "arm-web v1"
|
||||||
put "$WEB/package.json" '{"name":"web"}'
|
put "$WEB/package.json" '{"name":"web","devDependencies":{"@rsbuild/core":"1","@rsbuild/plugin-react":"1","tailwindcss":"3","daisyui":"4"}}'
|
||||||
for p in CLAUDE.md .claude/skills/s/SKILL.md openspec/config.yaml openspec/specs/ui/spec.md docs/superpowers/plans/p.md \
|
for p in CLAUDE.md .claude/skills/s/SKILL.md openspec/config.yaml openspec/specs/ui/spec.md docs/superpowers/plans/p.md \
|
||||||
scripts/demo-prepare.sh Makefile ARCHITECTURE.md .gitignore; do put "$WEB/$p" "/Users/someone/arm-web"; done
|
scripts/demo-prepare.sh Makefile ARCHITECTURE.md .gitignore; do put "$WEB/$p" "/Users/someone/arm-web"; done
|
||||||
commit_push "$WEB" "web v1" test
|
commit_push "$WEB" "web v1" test
|
||||||
@ -38,7 +38,7 @@ git clone -q "$WORK/arm-core.git" "$CORE" 2>/dev/null
|
|||||||
git -C "$CORE" checkout -q -b main
|
git -C "$CORE" checkout -q -b main
|
||||||
put "$CORE/web/src/a.ts" "export const a = 1;"
|
put "$CORE/web/src/a.ts" "export const a = 1;"
|
||||||
put "$CORE/web/README.md" "arm-web v1"
|
put "$CORE/web/README.md" "arm-web v1"
|
||||||
put "$CORE/web/package.json" '{"name":"web"}'
|
put "$CORE/web/package.json" '{"name":"web","devDependencies":{"@rsbuild/core":"1","@rsbuild/plugin-react":"1","tailwindcss":"3","daisyui":"4"}}'
|
||||||
printf 'venv:\n\tuv venv api/.venv\n' > "$CORE/Makefile"
|
printf 'venv:\n\tuv venv api/.venv\n' > "$CORE/Makefile"
|
||||||
for p in raisa_config.yml setup/setup.sh setup/pip.conf api/run_server.py api/requirements.txt api/.env.test \
|
for p in raisa_config.yml setup/setup.sh setup/pip.conf api/run_server.py api/requirements.txt api/.env.test \
|
||||||
api/contracts/schema.json; do put "$CORE/$p"; done
|
api/contracts/schema.json; do put "$CORE/$p"; done
|
||||||
@ -86,6 +86,8 @@ check "$([ "$rc" != 0 ] && echo 0 || echo 1)" "dry-run повторно: отк
|
|||||||
git -C "$CORE" worktree remove --force "$WT"; git -C "$CORE" branch -q -D "tmp/web-sync-$HEAD7"
|
git -C "$CORE" worktree remove --force "$WT"; git -C "$CORE" branch -q -D "tmp/web-sync-$HEAD7"
|
||||||
|
|
||||||
# --- 4. пайплайн переноса: prepare-contour-snapshot.sh видит отставание ---------------------------------------------
|
# --- 4. пайплайн переноса: prepare-contour-snapshot.sh видит отставание ---------------------------------------------
|
||||||
|
# сети в тесте нет: проверку сборки web/ в снимке отключаем (она покрыта test_prepare_contour_snapshot.sh)
|
||||||
|
export SNAPSHOT_SKIP_WEB_BUILD=1
|
||||||
export ARM_WEB="$WEB" SYNC_WEB_ARGS="--dry-run --wt-root $WORK/wt"
|
export ARM_WEB="$WEB" SYNC_WEB_ARGS="--dry-run --wt-root $WORK/wt"
|
||||||
rc=0; out=$("$SNAPSHOT" "$CORE" "$WORK/out1" origin/main </dev/null 2>&1) || rc=$?
|
rc=0; out=$("$SNAPSHOT" "$CORE" "$WORK/out1" origin/main </dev/null 2>&1) || rc=$?
|
||||||
check "$([ "$rc" = 3 ] && echo 0 || echo 1)" "пайплайн без терминала: перенос остановлен, rc=3 (rc=$rc)"
|
check "$([ "$rc" = 3 ] && echo 0 || echo 1)" "пайплайн без терминала: перенос остановлен, rc=3 (rc=$rc)"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user