Files
Reactive_Resume/scripts/test-personal-install-packages.sh

202 lines
8.9 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/sh
set -eu
ROOT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)"
DIRECT_COMPOSE="$ROOT_DIR/packages/reactive-resume-personal-direct/compose.yml"
DIRECT_ENV="$ROOT_DIR/packages/reactive-resume-personal-direct/.env"
QNAP_COMPOSE="$ROOT_DIR/packages/reactive-resume-personal-qnap-nas/compose-Nas.yml"
QNAP_PATCH_DIR="$ROOT_DIR/packages/reactive-resume-personal-qnap-nas/patches"
DIRECT_PATCH_DIR="$ROOT_DIR/packages/reactive-resume-personal-direct/patches"
QNAP_ZIP="$ROOT_DIR/dist/reactive-resume-personal-qnap-nas-20260520.zip"
DIRECT_ZIP="$ROOT_DIR/dist/reactive-resume-personal-direct-20260520.zip"
IMAGE_REPO="amruthpillai/reactive-resume"
IMAGE_INDEX="$IMAGE_REPO@sha256:b760446c4301af067e7d595537a877e378363aa6ce921b7349e62983621826aa"
PROJECT="reactive-resume-personal"
log() {
printf '\n[TEST] %s\n' "$*"
}
fail() {
printf '\n[FAIL] %s\n' "$*" >&2
exit 1
}
cleanup_direct() {
docker compose -f "$DIRECT_COMPOSE" --env-file "$DIRECT_ENV" down -v >/dev/null 2>&1 || true
}
cleanup_tmp() {
if [ -n "${TMP_DIR:-}" ] && [ -d "$TMP_DIR" ]; then
rm -rf "$TMP_DIR"
fi
}
cleanup_all() {
cleanup_direct
cleanup_tmp
}
trap cleanup_all HUP INT TERM EXIT
cd "$ROOT_DIR"
log "检查补丁脚本语法"
sh -n "$QNAP_PATCH_DIR/reactive-resume-runtime-patch.sh"
sh -n "$QNAP_PATCH_DIR/reactive-resume-entrypoint.sh"
sh -n "$DIRECT_PATCH_DIR/reactive-resume-runtime-patch.sh"
sh -n "$DIRECT_PATCH_DIR/reactive-resume-entrypoint.sh"
log "检查 Compose 配置可解析"
docker compose -f "$QNAP_COMPOSE" config >/tmp/reactive-resume-qnap-compose-test.yml
docker compose -f "$DIRECT_COMPOSE" --env-file "$DIRECT_ENV" config >/tmp/reactive-resume-direct-compose-test.yml
grep -q 'reactive-resume-entrypoint.sh' /tmp/reactive-resume-qnap-compose-test.yml
grep -q 'reactive-resume-entrypoint.sh' /tmp/reactive-resume-direct-compose-test.yml
log "检查 zip 安装包内容"
unzip -t "$QNAP_ZIP" >/dev/null
unzip -t "$DIRECT_ZIP" >/dev/null
unzip -l "$QNAP_ZIP" | grep -q 'reactive_resume/compose-Nas.yml'
unzip -l "$QNAP_ZIP" | grep -q 'reactive_resume/patches/reactive-resume-entrypoint.sh'
unzip -l "$QNAP_ZIP" | grep -q 'reactive_resume/patches/reactive-resume-runtime-patch.sh'
unzip -l "$DIRECT_ZIP" | grep -q 'reactive-resume-personal-direct/compose.yml'
unzip -l "$DIRECT_ZIP" | grep -q 'reactive-resume-personal-direct/patches/reactive-resume-entrypoint.sh'
if unzip -p "$QNAP_ZIP" 'reactive_resume/*' 2>/dev/null | grep -E 'isiseg|10004|Reactive_Resume_Personal|/share/Container/Reactive_Resume_Personal' >/dev/null; then
fail "QNAP zip 中仍有旧域名、旧端口或旧路径"
fi
if unzip -p "$QNAP_ZIP" 'reactive_resume/*' 2>/dev/null | grep -E 'amruthpillai/reactive-resume:latest|snowdreamtech/frpc:latest' >/dev/null; then
fail "QNAP zip 中仍有 latest 镜像"
fi
if unzip -p "$DIRECT_ZIP" 'reactive-resume-personal-direct/*' 2>/dev/null | grep -E 'amruthpillai/reactive-resume:latest|snowdreamtech/frpc:latest' >/dev/null; then
fail "direct zip 中仍有 latest 镜像"
fi
log "真实启动 direct 包并检查健康状态"
cleanup_direct
docker compose -f "$DIRECT_COMPOSE" --env-file "$DIRECT_ENV" up -d postgres reactive-resume seed
attempt=0
until curl -fsS "http://127.0.0.1:3004/api/health" >/tmp/reactive-resume-health.json 2>/dev/null; do
attempt=$((attempt + 1))
if [ "$attempt" -ge 60 ]; then
docker logs "$PROJECT-reactive-resume-1" --tail 200 >&2 || true
fail "direct 包启动后 /api/health 未在 60 秒内就绪"
fi
sleep 1
done
docker wait "$PROJECT-seed-1" >/tmp/reactive-resume-seed-exit
seed_status="$(docker inspect "$PROJECT-seed-1" --format '{{.State.ExitCode}}' 2>/dev/null || printf 'missing')"
[ "$seed_status" = "0" ] || fail "seed 容器退出码不是 0$seed_status"
attempt=0
until curl -fsS -I "http://127.0.0.1:3004/audience/resume" >/tmp/reactive-resume-audience.headers 2>/dev/null \
&& grep -q '200 OK' /tmp/reactive-resume-audience.headers; do
attempt=$((attempt + 1))
if [ "$attempt" -ge 30 ]; then
docker logs "$PROJECT-reactive-resume-1" --tail 200 >&2 || true
fail "seed 完成后 /audience/resume 未在 30 秒内返回 200"
fi
sleep 1
done
docker logs "$PROJECT-reactive-resume-1" --tail 200 >/tmp/reactive-resume-direct.log 2>&1 || true
if grep -E 'Cannot find module|Buffer is not defined|Unexpected end of input' /tmp/reactive-resume-direct.log >/dev/null; then
cat /tmp/reactive-resume-direct.log >&2
fail "direct 包日志仍包含已知启动或前端错误"
fi
log "离线检查 arm64/QNAP 镜像布局"
ARM64_DIGEST="$(
docker manifest inspect "$IMAGE_INDEX" \
| node -e '
let source = "";
process.stdin.on("data", (chunk) => source += chunk);
process.stdin.on("end", () => {
const manifest = JSON.parse(source);
const arm = manifest.manifests.find((item) => item.platform?.os === "linux" && item.platform?.architecture === "arm64");
if (!arm) process.exit(2);
process.stdout.write(arm.digest);
});
'
)"
TMP_DIR="$(mktemp -d)"
CID="$(docker create --platform linux/arm64 "$IMAGE_INDEX" 2>/dev/null || true)"
[ -n "$CID" ] || fail "无法创建 arm64 镜像容器用于离线检查"
docker export "$CID" -o "$TMP_DIR/arm64-root.tar"
docker rm "$CID" >/dev/null
mkdir -p "$TMP_DIR/arm64-root"
tar -xf "$TMP_DIR/arm64-root.tar" -C "$TMP_DIR/arm64-root"
if [ -f "$TMP_DIR/arm64-root/app/apps/server/dist/index.mjs" ]; then
ARM64_SERVER_ENTRY="$TMP_DIR/arm64-root/app/apps/server/dist/index.mjs"
ARM64_FILENAME_ENTRY="$ARM64_SERVER_ENTRY"
ARM64_ASSETS_DIR="$TMP_DIR/arm64-root/app/apps/web/dist/assets"
EXPECTED_ENTRYPOINT_PWD="$TMP_DIR/arm64-root/app"
EXPECTED_ENTRYPOINT_ARGS="node apps/server/dist/index.mjs"
elif [ -f "$TMP_DIR/arm64-root/app/apps/web/.output/server/index.mjs" ]; then
ARM64_SERVER_ENTRY="$TMP_DIR/arm64-root/app/apps/web/.output/server/index.mjs"
ARM64_FILENAME_ENTRY="$(grep -Rsl 'function generateFilename' "$TMP_DIR/arm64-root/app/apps/web/.output/server/_ssr" 2>/dev/null | head -n 1 || true)"
[ -n "$ARM64_FILENAME_ENTRY" ] || fail "arm64 .output 布局中未找到 generateFilename SSR bundle"
ARM64_ASSETS_DIR="$TMP_DIR/arm64-root/app/apps/web/.output/public/assets"
EXPECTED_ENTRYPOINT_PWD="$TMP_DIR/arm64-root/app/apps/web"
EXPECTED_ENTRYPOINT_ARGS="node .output/server/index.mjs"
else
find "$TMP_DIR/arm64-root/app" -maxdepth 6 \( -name index.mjs -o -name server.js -o -name main.js \) 2>/dev/null >&2 || true
fail "arm64 镜像中未找到支持的服务入口"
fi
[ -d "$ARM64_ASSETS_DIR" ] || fail "arm64 镜像中未找到 assets 目录:$ARM64_ASSETS_DIR"
perl -0pe "
s#/app/apps#$TMP_DIR/arm64-root/app/apps#g;
s#for candidate in $TMP_DIR/arm64-root/app/apps/web /app#for candidate in $TMP_DIR/arm64-root/app/apps/web $TMP_DIR/arm64-root/app#g;
s#find /app#find $TMP_DIR/arm64-root/app#g;
s#APP_DIR=\"/app\"#APP_DIR=\"$TMP_DIR/arm64-root/app\"#g;
s#under /app#under $TMP_DIR/arm64-root/app#g;
" "$QNAP_PATCH_DIR/reactive-resume-runtime-patch.sh" > "$TMP_DIR/runtime-patch-arm64-test.sh"
sh "$TMP_DIR/runtime-patch-arm64-test.sh" >/tmp/reactive-resume-arm64-runtime.log 2>&1 || {
cat /tmp/reactive-resume-arm64-runtime.log >&2
fail "arm64 离线运行 runtime patch 失败"
}
grep -R 'rr-browser-buffer-polyfill' "$ARM64_ASSETS_DIR" >/dev/null \
|| fail "arm64 public PDF bundle 未注入 Buffer polyfill"
grep -R -F 'replace(/[\\/:*?"<>|]/g' "$ARM64_ASSETS_DIR" >/dev/null \
|| fail "arm64 文件名 bundle 未改为按标题下载"
grep -q 'function generateFilename(prefix, extension)' "$ARM64_FILENAME_ENTRY" \
|| fail "arm64 server entry 未包含 generateFilename"
grep -F 'filename.replace(/[\\/:*?"<>|]/g' "$ARM64_FILENAME_ENTRY" >/dev/null \
|| fail "arm64 server entry 未改为按标题生成下载文件名"
perl -0pe "
s#/app/apps#$TMP_DIR/arm64-root/app/apps#g;
s#cd /app#cd $TMP_DIR/arm64-root/app#g;
s#find /app#find $TMP_DIR/arm64-root/app#g;
s#under /app#under $TMP_DIR/arm64-root/app#g;
" "$QNAP_PATCH_DIR/reactive-resume-entrypoint.sh" > "$TMP_DIR/entrypoint-arm64-test.sh"
mkdir -p "$TMP_DIR/fakebin"
{
printf '#!/bin/sh\n'
printf 'printf "PWD=%%s\\n" "$PWD" > "%s/entrypoint-result.txt"\n' "$TMP_DIR"
printf 'printf "ARGS=%%s\\n" "$*" >> "%s/entrypoint-result.txt"\n' "$TMP_DIR"
} > "$TMP_DIR/fakebin/docker-entrypoint.sh"
chmod +x "$TMP_DIR/fakebin/docker-entrypoint.sh"
PATH="$TMP_DIR/fakebin:$PATH" sh "$TMP_DIR/entrypoint-arm64-test.sh" >/tmp/reactive-resume-arm64-entrypoint.log 2>&1 || {
cat /tmp/reactive-resume-arm64-entrypoint.log >&2
fail "arm64 entrypoint 选择测试失败"
}
grep -q "PWD=$EXPECTED_ENTRYPOINT_PWD" "$TMP_DIR/entrypoint-result.txt" \
|| fail "arm64 entrypoint 未切换到预期目录:$EXPECTED_ENTRYPOINT_PWD"
grep -q "ARGS=$EXPECTED_ENTRYPOINT_ARGS" "$TMP_DIR/entrypoint-result.txt" \
|| fail "arm64 entrypoint 未选择预期入口:$EXPECTED_ENTRYPOINT_ARGS"
log "清理 direct 测试容器"
cleanup_direct
log "全部测试通过"
printf 'direct health: %s\n' "$(cat /tmp/reactive-resume-health.json)"
printf 'arm64 digest: %s\n' "$ARM64_DIGEST"