mirror of
https://github.com/azaion/ui.git
synced 2026-06-21 11:31:11 +00:00
[AZ-474] [AZ-480] Batch 8 - tile-split + nginx/image static checks (Phase A close)
- AZ-474 tile-split + YOLO parser + auto-zoom + indicator + malformed (FT-P-51..55, FT-N-10): 13 fast (6 it.fails for AC-1..6 + 7 controls) + 2 e2e (test.fail for FT-P-51 + FT-P-53). The split surface is QUARANTINED today (D11) — no Split-tile button, no parser, no <TileViewer>; all 6 ACs are documented drift, every it.fails paired with a control PASS pinning current behaviour. - AZ-480 prod image + nginx routing + RAM (NFT-RES-LIM-02 /03/08/09/10): 4 new static checks promoted into the per-commit profile (STC-RES02 500M cap, STC-RES03 Dockerfile final-stage nginx:alpine no Node, STC-RES09 exactly 9 /api/* location blocks, STC-RES10 prefix-strip on every route). 3 e2e (docker-no-Node probe, runtime prefix-strip, long-running RAM soak — all gated on docker availability + image build; RAM soak also on RUN_LONG_RUNNING=1). Phase A — One-time baseline setup is now COMPLETE. The todo/ directory is empty after this batch's archival. Cumulative review for batches 07-08 is the next autodev action; after that, Step 7 (Run Tests) auto-chains. Code review: PASS (0 findings). Fast: 26/26 files, 163 passed / 13 skipped. Static: 29/29 PASS (incl. 4 new STC-RES* gates). Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -370,6 +370,106 @@ if [ "$RUN_STATIC" = "true" ]; then
|
||||
return 0
|
||||
}
|
||||
|
||||
# AZ-480 NFT-RES-LIM-02 — nginx body cap is exactly 500M (one hit in the SPA
|
||||
# server block). Pinning here so a regression that loosens it (or copies a
|
||||
# second cap into a wrong block) lights up at commit time.
|
||||
static_check_nginx_body_cap() {
|
||||
if [ ! -f "$PROJECT_ROOT/nginx.conf" ]; then
|
||||
echo "nginx.conf missing" >&2
|
||||
return 1
|
||||
fi
|
||||
local hits
|
||||
hits=$(grep -cE 'client_max_body_size[[:space:]]+500M' "$PROJECT_ROOT/nginx.conf" || true)
|
||||
if [ "$hits" = "1" ]; then
|
||||
return 0
|
||||
fi
|
||||
echo "expected exactly 1 'client_max_body_size 500M' in nginx.conf, found $hits (NFT-RES-LIM-02)" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
# AZ-480 NFT-RES-LIM-03 — production image is nginx:alpine (no Node). The
|
||||
# e2e runtime probe (`docker run --rm $IMAGE which node`) is the second
|
||||
# half of this AC; this static gate prevents a Dockerfile change from
|
||||
# silently switching the final stage to a Node-based image.
|
||||
static_check_dockerfile_nginx_alpine() {
|
||||
if [ ! -f "$PROJECT_ROOT/Dockerfile" ]; then
|
||||
echo "Dockerfile missing" >&2
|
||||
return 1
|
||||
fi
|
||||
if ! grep -qE '^FROM[[:space:]]+nginx:alpine' "$PROJECT_ROOT/Dockerfile"; then
|
||||
echo "Dockerfile final stage must be 'FROM nginx:alpine' (NFT-RES-LIM-03)" >&2
|
||||
return 1
|
||||
fi
|
||||
# Reject any reference to oven/bun:* or node:* OUTSIDE of the AS build
|
||||
# stage. The build stage is allowed (it's a multi-stage build); the
|
||||
# final stage must not reference Node.
|
||||
if awk '
|
||||
/^FROM/ { stage = $0; in_final = ($0 !~ /AS[[:space:]]+build/) }
|
||||
in_final && /^FROM/ && /(node|oven\/bun)/ { exit 1 }
|
||||
' "$PROJECT_ROOT/Dockerfile"; then
|
||||
return 0
|
||||
fi
|
||||
echo "Dockerfile final stage references Node — must be nginx:alpine only (NFT-RES-LIM-03)" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
# AZ-480 NFT-RES-LIM-09 — exactly 9 nginx /api/* location blocks (one per
|
||||
# suite service: annotations, flights, admin, resource, detect, loader,
|
||||
# gps-denied-desktop, gps-denied-onboard, autopilot). The non-/api/
|
||||
# `location /` SPA fallback does NOT count.
|
||||
static_check_nginx_route_count() {
|
||||
if [ ! -f "$PROJECT_ROOT/nginx.conf" ]; then
|
||||
echo "nginx.conf missing" >&2
|
||||
return 1
|
||||
fi
|
||||
local hits
|
||||
hits=$(grep -cE '^\s*location\s+/api/' "$PROJECT_ROOT/nginx.conf" || true)
|
||||
if [ "$hits" = "9" ]; then
|
||||
return 0
|
||||
fi
|
||||
echo "expected exactly 9 nginx /api/* location blocks, found $hits (NFT-RES-LIM-09)" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
# AZ-480 NFT-RES-LIM-10 — every /api/<service>/ route strips its prefix.
|
||||
# The `proxy_pass http://<host>:<port>/` form (with trailing slash) is the
|
||||
# nginx-canonical "strip the matched location prefix" idiom; we assert
|
||||
# every /api/* location has such a proxy_pass directly underneath it.
|
||||
# Equivalent `rewrite ^/api/<S>/(.*)$ /$1 break;` would also satisfy the
|
||||
# AC but is not what nginx.conf uses today.
|
||||
static_check_nginx_prefix_strip() {
|
||||
if [ ! -f "$PROJECT_ROOT/nginx.conf" ]; then
|
||||
echo "nginx.conf missing" >&2
|
||||
return 1
|
||||
fi
|
||||
node -e '
|
||||
const fs = require("node:fs");
|
||||
const conf = fs.readFileSync("nginx.conf", "utf8");
|
||||
const lines = conf.split(/\r?\n/);
|
||||
const fails = [];
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const m = lines[i].match(/^\s*location\s+(\/api\/[^\s{]+)/);
|
||||
if (!m) continue;
|
||||
// Look ahead within this block for either:
|
||||
// proxy_pass http://...:<port>/ (note trailing slash)
|
||||
// rewrite ^/api/<S>/(.*)$ /$1 break;
|
||||
const route = m[1];
|
||||
let depth = 0, found = false;
|
||||
for (let j = i; j < lines.length; j++) {
|
||||
if (lines[j].includes("{")) depth++;
|
||||
if (lines[j].includes("}")) { depth--; if (depth === 0) break; }
|
||||
if (/proxy_pass\s+https?:\/\/[^/\s;]+(:\d+)?\/\s*;/.test(lines[j])) { found = true; break; }
|
||||
if (/rewrite\s+\^\/api\/[^/]+\/\(\.\*\)\$\s+\/\$1\s+break;/.test(lines[j])) { found = true; break; }
|
||||
}
|
||||
if (!found) fails.push(route);
|
||||
}
|
||||
if (fails.length) {
|
||||
console.error("location blocks without prefix-strip: " + fails.join(", ") + " (NFT-RES-LIM-10)");
|
||||
process.exit(1);
|
||||
}
|
||||
'
|
||||
}
|
||||
|
||||
# AZ-479 NFT-PERF-01 / NFT-RES-LIM-01 — initial JS bundle ≤ 2 MB gzipped.
|
||||
# Same threshold + measurement as scripts/run-performance-tests.sh; this
|
||||
# entry routes the gate through the static profile so every commit is
|
||||
@@ -417,6 +517,10 @@ if [ "$RUN_STATIC" = "true" ]; then
|
||||
run_static "STC-B1" "vite build succeeds" "AC-6" "n/a" static_check_vite_build
|
||||
run_static "STC-S5" "mission-planner not in dist/" "AC-31" "n/a" static_check_dist_no_mission_planner
|
||||
run_static "STC-PERF01" "initial JS bundle ≤ 2 MB gz" "NFT-PERF-01" "40" static_check_bundle_size
|
||||
run_static "STC-RES02" "nginx client_max_body_size 500M" "NFT-RES-LIM-02" "n/a" static_check_nginx_body_cap
|
||||
run_static "STC-RES03" "Dockerfile final stage nginx:alpine no Node" "NFT-RES-LIM-03" "n/a" static_check_dockerfile_nginx_alpine
|
||||
run_static "STC-RES09" "nginx exactly 9 /api/* location blocks" "NFT-RES-LIM-09" "n/a" static_check_nginx_route_count
|
||||
run_static "STC-RES10" "nginx prefix-strip on every /api/<S>/ route" "NFT-RES-LIM-10" "n/a" static_check_nginx_prefix_strip
|
||||
run_static "STC-SEC1B" "no literal OWM key in dist/" "SEC-09" "63" static_check_no_owm_key_in_dist
|
||||
|
||||
if [ "$STATIC_FAIL" = "1" ]; then
|
||||
|
||||
Reference in New Issue
Block a user