Spaces:
Running
Running
| # replay_build.sh — native replay of every Dockerfile build step. | |
| # | |
| # This script replays the Dockerfile stages natively as a fast, independent | |
| # check. The final verification ladder still performs the real Docker build. | |
| set -euo pipefail | |
| ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" | |
| cd "$ROOT" | |
| WORK="$(mktemp -d /tmp/i2t-build-replay-XXXXXX)" | |
| trap 'rm -rf "$WORK"' EXIT | |
| echo "==> [static] COPY sources exist in build context" | |
| python3 - "$ROOT" <<'PY' | |
| import pathlib, shlex, sys | |
| root = pathlib.Path(sys.argv[1]) | |
| missing = [] | |
| for raw in (root / "Dockerfile").read_text(encoding="utf-8").splitlines(): | |
| if not raw.startswith("COPY "): | |
| continue | |
| words = shlex.split(raw) | |
| if any(word.startswith("--from=") for word in words[1:]): | |
| continue | |
| operands = [word for word in words[1:] if not word.startswith("--")] | |
| for source in operands[:-1]: | |
| path = root / source.rstrip("/") | |
| if not path.exists(): | |
| missing.append(source) | |
| if missing: | |
| for source in missing: | |
| print(f" MISSING: {source}") | |
| raise SystemExit(1) | |
| print(" all COPY sources present") | |
| PY | |
| echo "==> [static] .dockerignore sanity" | |
| python3 - "$ROOT" <<'PY' | |
| import fnmatch, pathlib, sys | |
| root = pathlib.Path(sys.argv[1]) | |
| patterns = [l.strip() for l in (root/".dockerignore").read_text().splitlines() | |
| if l.strip() and not l.startswith("#")] | |
| def ignored(rel): | |
| for pat in patterns: | |
| p = pat.rstrip("/") | |
| if fnmatch.fnmatch(rel, p) or fnmatch.fnmatch(rel, p+"/*") or rel == p \ | |
| or any(fnmatch.fnmatch(part, p) for part in rel.split("/")): | |
| return True | |
| return False | |
| must_be_ignored = ["upstream-src", ".venv", "node_modules", "rollout.jsonl", ".workflow"] | |
| must_ship = ["Dockerfile", "README.md", "requirements.txt", "package.json", | |
| "app/main.py", "forge/stage3_build/generate_threejs_factory.py", | |
| "tests/fixtures/canned_spec.json", "scripts/node_smoke.mjs"] | |
| ok = True | |
| for rel in must_be_ignored: | |
| if not ignored(rel): | |
| print(f" NOT IGNORED (should be): {rel}"); ok = False | |
| for rel in must_ship: | |
| if ignored(rel): | |
| print(f" IGNORED (must ship): {rel}"); ok = False | |
| print(" .dockerignore ok" if ok else " .dockerignore BROKEN") | |
| sys.exit(0 if ok else 1) | |
| PY | |
| echo "==> [stage 1: fixture] regenerate fixture factory via real pipeline" | |
| python3 scripts/build_fixture_factory.py "$WORK/factory_fixture.ts" | |
| echo "==> [stage 2: nodesmoke] npm ci (fresh dir) + bundle + node smoke" | |
| mkdir -p "$WORK/node" | |
| cp package.json package-lock.json "$WORK/node/" | |
| (cd "$WORK/node" && npm ci --omit=dev --no-audit --no-fund >/dev/null) | |
| cp "$WORK/factory_fixture.ts" "$WORK/node/factory.ts" | |
| mkdir -p "$WORK/node/app/static" "$WORK/node/scripts" | |
| cp app/static/viewer-core.js "$WORK/node/app/static/" | |
| cp scripts/node_smoke.mjs "$WORK/node/scripts/" | |
| (cd "$WORK/node" && \ | |
| factory_export=$(grep -oE 'export function create[A-Za-z0-9]+Model' factory.ts | head -1 | awk '{print $3}') && \ | |
| pascal=$(echo "$factory_export" | sed -E 's/^create//; s/Model$//') && \ | |
| printf 'export { %s as makeModel, create%sLookDevLights as makeLights } from "./factory.ts";\nexport { mountViewer } from "%s";\n' \ | |
| "$factory_export" "$pascal" "$WORK/node/app/static/viewer-core.js" > entry.js && \ | |
| node_modules/.bin/esbuild entry.js --bundle --format=esm --target=es2022 --minify --outfile=model.bundle.js && \ | |
| node scripts/node_smoke.mjs "$WORK/node/model.bundle.js") | |
| echo "==> [stage 3: runtime] pip install -r requirements.txt (fresh venv)" | |
| python3 -m venv "$WORK/venv" | |
| "$WORK/venv/bin/pip" install -q --disable-pip-version-check -r requirements.txt | |
| echo "==> [runtime] boot app as the image would (python -m app.main), probe health/UI/503" | |
| RUNS_DIR="$WORK/runs" PORT=17861 env -u LLM_API_KEY -u LLM_MODEL -u LLM_BASE_URL \ | |
| -u ANTHROPIC_API_KEY -u ANTHROPIC_AUTH_TOKEN -u ANTHROPIC_BASE_URL -u ANTHROPIC_MODEL \ | |
| "$WORK/venv/bin/python" -m app.main > "$WORK/server.log" 2>&1 & | |
| SERVER_PID=$! | |
| trap 'kill $SERVER_PID 2>/dev/null || true; rm -rf "$WORK"' EXIT | |
| curl --fail --silent --show-error \ | |
| --retry 30 --retry-all-errors --retry-connrefused --retry-delay 1 \ | |
| --connect-timeout 2 --max-time 60 \ | |
| http://127.0.0.1:17861/health > "$WORK/health.json" | |
| grep -q '"status":"ok"' "$WORK/health.json" && echo " /health ok" | |
| grep -q '"llm_configured":false' "$WORK/health.json" && echo " llm_configured=false ok" | |
| curl -fsS http://127.0.0.1:17861/ | grep -q img2threejs && echo " / ok" | |
| code=$(curl -s -o "$WORK/job.json" -w '%{http_code}' \ | |
| -F "file=@tests/fixtures/ref.png;type=image/png" http://127.0.0.1:17861/api/jobs) | |
| [ "$code" = "503" ] && grep -q llm_not_configured "$WORK/job.json" && echo " honest 503 ok" | |
| ! grep -qiE 'sculptRuntime|makeModel' "$WORK/job.json" && echo " no fabrication ok" | |
| kill $SERVER_PID 2>/dev/null || true | |
| echo "==> [runtime] pytest suite inside the fresh venv" | |
| "$WORK/venv/bin/python" -m pytest tests/ -q 2>&1 | tail -2 | |
| echo | |
| echo "BUILD REPLAY: ALL STAGES OK" | |