From 5c5d21bf4380bfc077ec65a5e1e405c297512f16 Mon Sep 17 00:00:00 2001 From: admin <572701190@qq.com> Date: Wed, 20 May 2026 01:27:12 +0800 Subject: [PATCH] add install package smoke test --- scripts/test-personal-install-packages.sh | 178 ++++++++++++++++++++++ 1 file changed, 178 insertions(+) create mode 100755 scripts/test-personal-install-packages.sh diff --git a/scripts/test-personal-install-packages.sh b/scripts/test-personal-install-packages.sh new file mode 100755 index 0000000..e755af7 --- /dev/null +++ b/scripts/test-personal-install-packages.sh @@ -0,0 +1,178 @@ +#!/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="amruthpillai/reactive-resume:latest" +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 + +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" \ + | 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@$ARM64_DIGEST" 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" + +[ -f "$TMP_DIR/arm64-root/app/apps/server/dist/index.mjs" ] || fail "arm64 镜像中未找到 /app/apps/server/dist/index.mjs" +[ -d "$TMP_DIR/arm64-root/app/apps/web/dist/assets" ] || fail "arm64 镜像中未找到 /app/apps/web/dist/assets" + +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' "$TMP_DIR/arm64-root/app/apps/web/dist/assets" >/dev/null \ + || fail "arm64 public PDF bundle 未注入 Buffer polyfill" +grep -R -F 'replace(/[\\/:*?"<>|]/g' "$TMP_DIR/arm64-root/app/apps/web/dist/assets" >/dev/null \ + || fail "arm64 文件名 bundle 未改为按标题下载" +grep -q 'function generateFilename(prefix, extension)' "$TMP_DIR/arm64-root/app/apps/server/dist/index.mjs" \ + || fail "arm64 server entry 未包含 generateFilename" +grep -F 'filename.replace(/[\\/:*?"<>|]/g' "$TMP_DIR/arm64-root/app/apps/server/dist/index.mjs" >/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=$TMP_DIR/arm64-root/app" "$TMP_DIR/entrypoint-result.txt" \ + || fail "arm64 entrypoint 未切换到 /app" +grep -q "ARGS=node apps/server/dist/index.mjs" "$TMP_DIR/entrypoint-result.txt" \ + || fail "arm64 entrypoint 未选择 apps/server/dist/index.mjs" + +log "清理 direct 测试容器" +cleanup_direct + +log "全部测试通过" +printf 'direct health: %s\n' "$(cat /tmp/reactive-resume-health.json)" +printf 'arm64 digest: %s\n' "$ARM64_DIGEST"