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>
155 lines
8.9 KiB
Bash
Executable File
155 lines
8.9 KiB
Bash
Executable File
#!/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, при падении снимка нет, у сервисов без фронта не вызывается"
|