File size: 5,024 Bytes
39ff632
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#!/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"