From feece9bb745b3ed1b8d7c4d1181a3c5f200f3562 Mon Sep 17 00:00:00 2001 From: key Date: Wed, 29 Jul 2026 03:14:26 +0800 Subject: [PATCH] fix(release): verify frozen backend artifacts --- docs/DEV_SETUP.md | 4 +++- scripts/release-preflight.sh | 10 ++++++++-- scripts/tests/release-preflight-scope.test.sh | 3 +++ 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/docs/DEV_SETUP.md b/docs/DEV_SETUP.md index e2d8b90f..34912982 100644 --- a/docs/DEV_SETUP.md +++ b/docs/DEV_SETUP.md @@ -310,11 +310,13 @@ RELEASE_REMOTE_URL=https://peilian.njzhmj.top ./scripts/release-preflight.sh RELEASE_REMOTE_URL=https://peilian.njzhmj.top RELEASE_VERIFY_REMOTE_MATCH=true ./scripts/release-preflight.sh # 定向后端发布后只核对后端与 schema,不要求本轮未发布的管理端/H5 与本地一致 RELEASE_REMOTE_URL=https://peilian.njzhmj.top RELEASE_VERIFY_REMOTE_BACKEND=true RELEASE_VERIFY_REMOTE_SCHEMA=true ./scripts/release-preflight.sh +# 验证冻结提交生成的指定 JAR;提交必须是当前 HEAD 的祖先,且产物时间不得早于该提交 +RELEASE_REMOTE_URL=https://peilian.njzhmj.top RELEASE_VERIFY_REMOTE_BACKEND=true RELEASE_VERIFY_REMOTE_SCHEMA=true RELEASE_ARTIFACT_COMMIT= RELEASE_LOCAL_BACKEND_PATH= ./scripts/release-preflight.sh # 发布后同时核对线上静态资源、后端包和必需表(schema 检查为只读) RELEASE_REMOTE_URL=https://peilian.njzhmj.top RELEASE_VERIFY_REMOTE_MATCH=true RELEASE_VERIFY_REMOTE_BACKEND=true RELEASE_VERIFY_REMOTE_SCHEMA=true ./scripts/release-preflight.sh ``` -三个远端核验开关可以按实际发布单元独立组合,但只要启用任一开关就必须提供 `RELEASE_REMOTE_URL`。定向模式只要求对应的本地产物保持新鲜:静态核验要求管理端/H5,后端核验要求后端 JAR,单独 schema 核验不要求无关构建产物。`RELEASE_VERIFY_REMOTE_BACKEND=true` 会打印整包 jar SHA-256 和 `ruoyi-aihr` 模块内容 SHA-256,实际匹配以模块内容 hash 为准;它不再隐式要求管理端/H5 静态资源匹配。只有同时启用 `RELEASE_VERIFY_REMOTE_MATCH=true`、`RELEASE_VERIFY_REMOTE_BACKEND=true` 和 `RELEASE_VERIFY_REMOTE_SCHEMA=true`,才能称为完整包匹配。远端后端默认核对 `/opt/wygj/app/ruoyi-admin.jar`,如发布路径不同可通过 `RELEASE_REMOTE_BACKEND_PATH` 覆盖;路径必须是安全的绝对路径。开启远端后端或 schema 核验时,预检还会只读检查 `wygj-aihr.service` 及其 `EnvironmentFile` 的有效配置,`AIHR_PRACTICE_RUNTIME_SCHEMA_BOOTSTRAP` 必须为 `false` 或未设置;无法读取引用的环境文件会失败关闭,而不会把本机环境变量当作线上证据。 +三个远端核验开关可以按实际发布单元独立组合,但只要启用任一开关就必须提供 `RELEASE_REMOTE_URL`。定向模式只要求对应的本地产物保持新鲜:静态核验要求管理端/H5,后端核验要求后端 JAR,单独 schema 核验不要求无关构建产物。默认以当前 `HEAD` 判断产物时间;冻结 RC 应同时设置 `RELEASE_ARTIFACT_COMMIT` 和 `RELEASE_LOCAL_BACKEND_PATH`,前者必须是当前 `HEAD` 的祖先,后者必须指向实际准备发布的 JAR,避免后续文档提交误伤冻结物或误用 `target` 下的其他构建。`RELEASE_VERIFY_REMOTE_BACKEND=true` 会打印整包 jar SHA-256 和 `ruoyi-aihr` 模块内容 SHA-256,实际匹配以模块内容 hash 为准;它不再隐式要求管理端/H5 静态资源匹配。只有同时启用 `RELEASE_VERIFY_REMOTE_MATCH=true`、`RELEASE_VERIFY_REMOTE_BACKEND=true` 和 `RELEASE_VERIFY_REMOTE_SCHEMA=true`,才能称为完整包匹配。远端后端默认核对 `/opt/wygj/app/ruoyi-admin.jar`,如发布路径不同可通过 `RELEASE_REMOTE_BACKEND_PATH` 覆盖;路径必须是安全的绝对路径。开启远端后端或 schema 核验时,预检还会只读检查 `wygj-aihr.service` 及其 `EnvironmentFile` 的有效配置,`AIHR_PRACTICE_RUNTIME_SCHEMA_BOOTSTRAP` 必须为 `false` 或未设置;无法读取引用的环境文件会失败关闭,而不会把本机环境变量当作线上证据。 带 `RELEASE_REMOTE_URL` 时,预检同时校验租户接口 JSON 的业务 `code=200`;HTTP 200 但业务返回 401/405 会判定失败。 diff --git a/scripts/release-preflight.sh b/scripts/release-preflight.sh index 22aa4497..638585e2 100755 --- a/scripts/release-preflight.sh +++ b/scripts/release-preflight.sh @@ -56,7 +56,12 @@ fi frontend_index="frontend/dist/index.html" mobile_index="mobile-uni/dist/build/h5/index.html" -backend_jar="backend/ruoyi-admin/target/ruoyi-admin.jar" +backend_jar="${RELEASE_LOCAL_BACKEND_PATH:-backend/ruoyi-admin/target/ruoyi-admin.jar}" +artifact_commit="${RELEASE_ARTIFACT_COMMIT:-HEAD}" +artifact_commit_sha="$(git rev-parse --verify "${artifact_commit}^{commit}" 2>/dev/null)" \ + || fail "RELEASE_ARTIFACT_COMMIT is not a valid commit: $artifact_commit" +git merge-base --is-ancestor "$artifact_commit_sha" HEAD \ + || fail "RELEASE_ARTIFACT_COMMIT must be an ancestor of HEAD: $artifact_commit_sha" require_static_artifacts="true" require_backend_artifact="true" if [[ "$remote_verification_requested" == "true" ]]; then @@ -91,7 +96,7 @@ if [[ "${AIHR_PRACTICE_RUNTIME_SCHEMA_BOOTSTRAP:-false}" == "true" ]]; then fail "AIHR_PRACTICE_RUNTIME_SCHEMA_BOOTSTRAP must stay false for a release; apply the formal SQL migration instead" fi -head_epoch="$(git show -s --format=%ct HEAD)" +head_epoch="$(git show -s --format=%ct "$artifact_commit_sha")" file_epoch() { if stat -f %m "$1" >/dev/null 2>&1; then @@ -839,6 +844,7 @@ changed_files="$(git status --porcelain)" [[ -z "$changed_files" ]] || fail "worktree has uncommitted changes; commit the release batch before publishing" echo "commit=$(git rev-parse HEAD)" +echo "artifact_commit=$artifact_commit_sha" echo "head_epoch=$head_epoch" echo "worktree=clean" if [[ "$require_static_artifacts" == "true" ]]; then diff --git a/scripts/tests/release-preflight-scope.test.sh b/scripts/tests/release-preflight-scope.test.sh index 48d46b1f..830a0b1a 100755 --- a/scripts/tests/release-preflight-scope.test.sh +++ b/scripts/tests/release-preflight-scope.test.sh @@ -32,6 +32,9 @@ static_scope_line="$(grep -n '^[[:space:]]*if \[\[ "\$verify_remote_match" == "t grep -Fq 'require_static_artifacts="$verify_remote_match"' "$SCRIPT" grep -Fq 'require_backend_artifact="$verify_remote_backend"' "$SCRIPT" +grep -Fq 'backend_jar="${RELEASE_LOCAL_BACKEND_PATH:-backend/ruoyi-admin/target/ruoyi-admin.jar}"' "$SCRIPT" +grep -Fq 'artifact_commit="${RELEASE_ARTIFACT_COMMIT:-HEAD}"' "$SCRIPT" +grep -Fq 'git merge-base --is-ancestor "$artifact_commit_sha" HEAD' "$SCRIPT" grep -Fq 'RELEASE_VERIFY_REMOTE_BACKEND=true RELEASE_VERIFY_REMOTE_SCHEMA=true ./scripts/release-preflight.sh' "$DEV_SETUP" grep -Fq 'RELEASE_VERIFY_REMOTE_MATCH=true RELEASE_VERIFY_REMOTE_BACKEND=true RELEASE_VERIFY_REMOTE_SCHEMA=true ./scripts/release-preflight.sh' "$DEV_SETUP"