arm-ka-docs/scripts/test_prepare_contour_snapshot.sh
Andrey Epifancev 0574ba402f 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>
2026-10-16 14:47:35 +04:00

155 lines
8.9 KiB
Bash
Executable File
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.

#!/usr/bin/env bash
# Тест prepare-contour-snapshot.sh на синтетическом репозитории arm-core: артефакты агента вырезаются на любой глубине
# (в т.ч. во вложенном web/), спецификации и код остаются. Запуск: make test-snapshot (или напрямую из корня docs).
set -euo pipefail
SCRIPT="$(cd "$(dirname "$0")" && pwd)/prepare-contour-snapshot.sh"
WORK=$(mktemp -d)
trap 'rm -rf "$WORK"' EXIT
git init -q --bare "$WORK/origin.git"
git clone -q "$WORK/origin.git" "$WORK/src/arm-core" 2>/dev/null
R="$WORK/src/arm-core"
put() { mkdir -p "$(dirname "$R/$1")"; printf '%s\n' "${2:-x}" > "$R/$1"; }
# платформенные файлы, без которых проверка снимка падает
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 \
api/contracts/schema.json; do put "$p"; done
# корень: артефакты агента и локальная сборка
for p in .claude/skills/a/SKILL.md CLAUDE.md openspec/config.yaml docs/superpowers/plans/p.md Dockerfile .dockerignore; do
put "$p" "/Users/someone/projects"
done
put openspec/specs/osv/spec.md
# вложенный фронт (как web/ из arm-web): те же артефакты на второй глубине
for p in web/.claude/commands/opsx/apply.md web/.codegraph/.gitignore web/CLAUDE.md web/openspec/config.yaml \
web/docs/superpowers/plans/2026-10-08-demo.md; do
put "$p" "/Users/someone/projects"
done
put web/openspec/specs/osv-ui/spec.md
put web/openspec/changes/archive/2026-10-08-demo/design.md
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/ остаётся
git -C "$R" add -A
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" fetch -q origin
# проверка сборки web/ в тесте отключена (нет сети); её включённый режим проверяется ниже заглушкой npm
SNAPSHOT_SKIP_WEB_BUILD=1 "$SCRIPT" "$R" "$WORK/out" origin/main >/dev/null
D="$WORK/out/arm-core"
fail=0
for p in .claude CLAUDE.md openspec/config.yaml docs Dockerfile .dockerignore \
web/.claude web/.codegraph web/CLAUDE.md web/openspec/config.yaml web/docs/superpowers; do
if [ -e "$D/$p" ]; then echo "FAIL: в снимке остался $p" >&2; fail=1; fi
done
for p in openspec/specs/osv/spec.md web/openspec/specs/osv-ui/spec.md web/openspec/changes/archive/2026-10-08-demo/design.md \
web/src/App.tsx web/docs/notes.md SNAPSHOT.md; do
if [ ! -e "$D/$p" ]; then echo "FAIL: из снимка пропал $p" >&2; fail=1; fi
done
[ "$fail" = 0 ] || exit 1
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, при падении снимка нет, у сервисов без фронта не вызывается"