ops: verify every release asset

This commit is contained in:
lingniu
2026-07-16 01:51:31 +08:00
parent c86c9d1e31
commit 3051caf327
5 changed files with 140 additions and 3 deletions

View File

@@ -0,0 +1,66 @@
#!/usr/bin/env bash
set -euo pipefail
WEB_ROOT=${1:-/opt/lingniu-vehicle-platform/current/web}
BASE_URL=${2:-http://127.0.0.1:20300}
PREVIOUS_MANIFEST=${3:-}
CURL_TIMEOUT_SEC=${CURL_TIMEOUT_SEC:-15}
BASE_URL=${BASE_URL%/}
CURRENT_MANIFEST="$WEB_ROOT/.release-assets"
response_file=$(mktemp)
trap 'rm -f "$response_file"' EXIT
fail() {
printf 'web release smoke failed: %s\n' "$*" >&2
exit 1
}
fetch_exact() {
local url_path=$1
local expected_file=$2
local label=$3
local status
test -f "$expected_file" || fail "$label is missing on disk: $expected_file"
status=$(curl --silent --show-error --max-time "$CURL_TIMEOUT_SEC" --output "$response_file" --write-out '%{http_code}' "$BASE_URL$url_path" || true)
test "$status" = 200 || fail "$label returned HTTP ${status:-000}: $url_path"
cmp -s "$expected_file" "$response_file" || fail "$label response differs from release file: $url_path"
}
verify_manifest() {
local manifest=$1
local kind=$2
local count=0
local asset
test -f "$manifest" || fail "$kind manifest is missing: $manifest"
while IFS= read -r asset || test -n "$asset"; do
asset=${asset%$'\r'}
test -n "$asset" || continue
case "$asset" in
/*|*../*|../*|*/..|..)
fail "$kind manifest contains unsafe asset path: $asset"
;;
esac
fetch_exact "/assets/$asset" "$WEB_ROOT/assets/$asset" "$kind asset"
count=$((count + 1))
done < "$manifest"
test "$count" -gt 0 || fail "$kind manifest is empty: $manifest"
printf '%s' "$count"
}
fetch_exact / "$WEB_ROOT/index.html" 'root document'
config_status=$(curl --silent --show-error --max-time "$CURL_TIMEOUT_SEC" --output "$response_file" --write-out '%{http_code}' "$BASE_URL/app-config.js" || true)
test "$config_status" = 200 || fail "app config returned HTTP ${config_status:-000}"
grep -q 'window\.__LINGNIU_APP_CONFIG__' "$response_file" || fail 'app config response does not expose the runtime configuration object'
current_count=$(verify_manifest "$CURRENT_MANIFEST" current)
compatibility_count=0
if test -n "$PREVIOUS_MANIFEST"; then
compatibility_count=$(verify_manifest "$PREVIOUS_MANIFEST" compatibility)
fi
printf 'web_release_smoke=ok current_assets=%s compatibility_assets=%s\n' "$current_count" "$compatibility_count"

View File

@@ -0,0 +1,60 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
fixture=$(mktemp -d)
server_pid=
cleanup() {
if test -n "$server_pid"; then
kill "$server_pid" 2>/dev/null || true
wait "$server_pid" 2>/dev/null || true
fi
rm -rf "$fixture"
}
trap cleanup EXIT
mkdir -p "$fixture/web/assets"
printf '<!doctype html><div id="root"></div><script src="/assets/new.js"></script>\n' > "$fixture/web/index.html"
printf 'window.__LINGNIU_APP_CONFIG__ = {};\n' > "$fixture/web/app-config.js"
printf 'console.log("new");\n' > "$fixture/web/assets/new.js"
printf 'console.log("old");\n' > "$fixture/web/assets/old.js"
printf 'new.js\n' > "$fixture/web/.release-assets"
printf 'old.js\n' > "$fixture/previous-assets"
cp -R "$fixture/web" "$fixture/served"
port=$(python3 -c 'import socket; s=socket.socket(); s.bind(("127.0.0.1", 0)); print(s.getsockname()[1]); s.close()')
python3 -m http.server "$port" --bind 127.0.0.1 --directory "$fixture/served" > "$fixture/server.log" 2>&1 &
server_pid=$!
for _ in $(seq 1 30); do
if curl --silent --fail "http://127.0.0.1:$port/" >/dev/null; then break; fi
sleep 0.1
done
success_output=$("$SCRIPT_DIR/verify-web-release.sh" "$fixture/web" "http://127.0.0.1:$port" "$fixture/previous-assets")
test "$success_output" = 'web_release_smoke=ok current_assets=1 compatibility_assets=1'
rm "$fixture/web/assets/old.js"
if "$SCRIPT_DIR/verify-web-release.sh" "$fixture/web" "http://127.0.0.1:$port" "$fixture/previous-assets" > "$fixture/missing.out" 2>&1; then
printf 'expected a missing compatibility asset to fail\n' >&2
exit 1
fi
grep -q 'compatibility asset is missing on disk' "$fixture/missing.out"
printf 'console.log("old");\n' > "$fixture/web/assets/old.js"
printf '<!doctype html><div id="root"></div>\n' > "$fixture/served/assets/old.js"
if "$SCRIPT_DIR/verify-web-release.sh" "$fixture/web" "http://127.0.0.1:$port" "$fixture/previous-assets" > "$fixture/mismatch.out" 2>&1; then
printf 'expected a fallback response to fail byte comparison\n' >&2
exit 1
fi
grep -q 'response differs from release file' "$fixture/mismatch.out"
printf 'console.log("old");\n' > "$fixture/served/assets/old.js"
printf '../secret.js\n' > "$fixture/previous-assets"
if "$SCRIPT_DIR/verify-web-release.sh" "$fixture/web" "http://127.0.0.1:$port" "$fixture/previous-assets" > "$fixture/unsafe.out" 2>&1; then
printf 'expected an unsafe manifest path to fail\n' >&2
exit 1
fi
grep -q 'unsafe asset path' "$fixture/unsafe.out"
printf 'verify-web-release tests passed\n'