img2threejs / scripts /replay_build.sh
Mike0021's picture
Deploy verified img2threejs Docker Space
39ff632 verified
Raw
History Blame Contribute Delete
5.02 kB
#!/usr/bin/env bash
# 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"