Files
opendance-windows/build/package-release.sh
T
zijunchiang e4e534f2a2 test(accept): 发布包级端到端验收(读包内 pck 而非源码)+ T-WIN-018
`build/verify.sh` 跑的是仓库源码(`godot --path .`),交付给用户的是 zip 里的
OpenDance.pck + OpenDance.exe,差别全在「导出」这一步:导出预设漏资源、pck 没
重新生成、版本号没同步、契约快照与 pck 不一致 —— 都只在包里才暴露。
本仓 v0.2.1 的 PE 版本元数据就是这么翻的车。

新增:
  * build/probe-release.gd —— 包内运行时探针。用
    `godot --headless --main-pack <pck> --script <探针>` 只读包内资源,断言并
    quit(0/1):
      1) application/config/version == 期望版本(界面版本唯一来源,§11.1);
      2) AboutScreen.app_version() == 期望版本(「关于」清单真正显示的值);
      3) 主场景可加载、四个 autoload 都在场景树里;
      4) 15 个关键资源(UI/模型/场景/网络/评分)在包内都可加载;
      5) 包内 Credits.CONTRACT_VERSIONS 与包内 contracts/VERSION 快照逐项一致
         (抓「pck 用旧契约构建、快照却拷了新的」)。
  * build/accept-release.sh —— 一个 zip(或已解压目录)跑完:sha256 → 解压
    (deflate 用 unzip / LZMA 用 7z,并检查没有 .partN 残留)→ 文件清单 →
    check-pe-version.sh → 包内 pck 启动 180 帧无 SCRIPT ERROR → 探针。
  * package-release.sh 结尾打印下一步验收命令;README 补用法。

实测(对**已发布**的 v0.2.2 资产重新下载后跑):
  sha256 OK;PE 版本资源 0.2.2;包内 pck 启动 180 帧无错误;
  包内 version=0.2.2;AboutScreen.app_version()=0.2.2;主场景
  res://scenes/main.tscn;autoload 齐备;15 项关键资源可加载;
  pck 内契约表与包内 contracts/VERSION 一致 → 发布包级验收全部通过。

反向验证:
  * 篡改包内 contracts/VERSION 的 rest-api=0.4.0 →
    ✗ ["rest-api: pck=0.5.0 快照=0.4.0"],退出码 1;
  * 期望版本写 0.2.1 对 v0.2.2 的包 → 三处版本断言全失败,退出码 1。

顺带修掉脚本自身一个缺陷:SHA256SUMS 用相对路径 + cd 到包目录后被二次拼接成
dist-release/dist-release/SHA256SUMS,导致 sha256 校验假失败(其余检查仍全绿,
很容易被忽略)。首跑时正是它把「全部通过」变成了失败。

verify.sh 仍全绿(754 通过 / 0 失败 / 5 跳过)。
2026-09-17 04:50:40 +08:00

184 lines
8.5 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/bash
# ============================================================
# OpenDance Studio —— Windows 客户端发布打包(T-WIN-014)
#
# 产物(文档 §5.3 / §5.7):
# opendance-windows-vX.Y.Z.zip ← **单个完整包,禁止 .partN 分卷**
# SHA256SUMS ← 裸文件名,下载目录内 sha256sum -c 直接可用
#
# 包内结构:
# opendance-windows-vX.Y.Z/
# OpenDance.exe
# OpenDance.pck
# THIRD_PARTY.md
# README.txt README.md
# contracts/
#
# 为什么强调"单个完整包":本项目的 Gitea 前置 IIS 默认
# maxAllowedContentLength = 30,000,000 字节(≈28.6 MiB),
# 30.5 MB / 40 MB 的上传实测直接 HTTP 413。当时的应对是把整包拆成
# zip.part0/1/2(每卷 13 MiB)——这正是"分卷有问题"的根因:
# 用户拿到的是无法直接解压的碎片,还要自己拼接。
# 现在改为:整包上传;若超过上限则**直接报错退出**,并提示运维放宽
# IIS/ARR 上限(做法见 opendance-web/deploy/proxy/README.md),
# 绝不静默退回分卷。
#
# 用法:bash build/package-release.sh [版本号]
# ============================================================
set -euo pipefail
PROJECT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
cd "$PROJECT_DIR"
VERSION="${1:-$(sed -n 's/^version:[[:space:]]*//p' project.yaml | head -1)}"
VERSION="${VERSION:-0.2.0}"
NAME="opendance-windows-v${VERSION}"
OUT_DIR="$PROJECT_DIR/dist-release"
STAGE="$OUT_DIR/$NAME"
# Gitea 前置 IIS 的实测上传上限(文档 §5.7 实测结论)
UPLOAD_LIMIT_BYTES=30000000
log() { printf '\033[1;34m[package]\033[0m %s\n' "$*"; }
warn() { printf '\033[1;33m[warn]\033[0m %s\n' "$*" >&2; }
die() { printf '\033[1;31m[error]\033[0m %s\n' "$*" >&2; exit 1; }
# ---- 0. 版本号一致性(文档 §11.2:产物名 / tag / 界面展示必须一致)----
bash "$PROJECT_DIR/build/check-version.sh" "$VERSION" || die "版本一致性校验失败,先统一 project.godot / project.yaml / 发布版本号"
EXE="$PROJECT_DIR/build/windows/OpenDance.exe"
PCK="$PROJECT_DIR/build/windows/OpenDance.pck"
[ -f "$EXE" ] || die "未找到 $EXE,请先执行 bash build/export-windows.sh release"
[ -f "$PCK" ] || die "未找到 $PCK,请先执行 bash build/export-windows.sh release"
# ---- 1. 源产物自检:PE 头 + pck 魔数 ----
MAGIC=$(head -c 2 "$EXE" | od -An -tx1 | tr -d ' \n')
[ "$MAGIC" = "4d5a" ] || die "$EXE 不是合法的 Windows PE 文件(头 $MAGIC)"
PCK_MAGIC=$(head -c 4 "$PCK" | tr -d '\0')
[ "$PCK_MAGIC" = "GDPC" ] || die "$PCK 不是合法的 Godot 资源包(头 '$PCK_MAGIC')"
log "源产物校验通过:exe PE 头 MZ | pck 头 GDPC"
# 产物 exe 的 PE 版本资源必须是本次发布版本 —— Windows「属性 → 详细信息」显示的就是它。
# v0.2.1 曾带着 0.1.0 发出去(导出预设里是硬编码的旧值,且没有任何环节复核产物)。
bash "$PROJECT_DIR/build/check-pe-version.sh" "$EXE" "$VERSION" \
|| die "$EXE 的 PE 版本资源不是 v$VERSION;先跑 bash build/sync-export-version.sh $VERSION 再重新导出"
log "产物 PE 版本资源校验通过(=$VERSION)"
# ---- 2. 整理产物 ----
log "整理产物到 $STAGE …"
rm -rf "$STAGE"
mkdir -p "$STAGE"
cp "$EXE" "$STAGE/OpenDance.exe"
cp "$PCK" "$STAGE/OpenDance.pck"
cp THIRD_PARTY.md README.txt "$STAGE/"
if [ -f README.md ]; then cp README.md "$STAGE/"; fi
cp -r contracts "$STAGE/contracts"
# ---- 3. 打包(单个完整包) ----
ZIP="$OUT_DIR/$NAME.zip"
# 清理历史分卷,避免 old .partN 被误当成本次产物上传
rm -f "$OUT_DIR/$NAME.zip".part* "$OUT_DIR/$NAME.zip" "$OUT_DIR/SHA256SUMS"*
# 压缩方式:默认 deflate(Windows 资源管理器可直接解压)。
# ZIP_METHOD=lzma 时改用 LZMA —— 包体从约 38 MB 降到约 26 MB,可绕过
# 「Gitea 站点 IIS 的 maxAllowedContentLength = 30,000,000 字节」这道闸
# (见 opendance-web/deploy/proxy/README.md 第九节)。
# **代价**:Windows 10 的资源管理器不支持 LZMA(method 14) 的 zip,用户必须用
# 7-Zip / WinRAR / PeaZip 解压(Win11 24H2+ 起资源管理器原生支持 7z/zip 变体,
# 但本项目未在 Windows 上实测)。因此只有在 IIS 上限确实无法放开时才用 LZMA。
ZIP_METHOD="${ZIP_METHOD:-deflate}"
log "生成单个完整包 $NAME.zip(压缩方式:$ZIP_METHOD)…"
case "$ZIP_METHOD" in
deflate)
( cd "$OUT_DIR" && zip -qr "$NAME.zip" "$NAME" )
;;
lzma)
command -v 7z >/dev/null 2>&1 || die "ZIP_METHOD=lzma 需要 7z(Debian/Ubuntu: apt install p7zip-full)"
( cd "$OUT_DIR" && rm -f "$NAME.zip" && 7z a -tzip -mm=LZMA -mx=9 "$NAME.zip" "$NAME" >/dev/null )
;;
*)
die "未知 ZIP_METHOD=$ZIP_METHOD(可选:deflate / lzma)"
;;
esac
# LZMA 包额外做一次完整性测试与解压自检(zip/unzip 对 method 14 支持不一致)
if [ "$ZIP_METHOD" = "lzma" ]; then
7z t "$ZIP" >/dev/null || die "LZMA 包完整性测试失败(7z t)"
log "LZMA 包完整性测试通过(7z t)"
fi
# ---- 4. 完整性自检:解压后必须含 exe + pck(同目录)且 exe 仍是合法 PE ----
CHECK_DIR=$(mktemp -d)
trap 'rm -rf "$CHECK_DIR"' EXIT
if [ "$ZIP_METHOD" = "lzma" ]; then
7z x -y -o"$CHECK_DIR" "$ZIP" >/dev/null
else
unzip -q "$ZIP" -d "$CHECK_DIR"
fi
[ -f "$CHECK_DIR/$NAME/OpenDance.exe" ] || die "包内缺少 OpenDance.exe"
[ -f "$CHECK_DIR/$NAME/OpenDance.pck" ] || die "包内缺少 OpenDance.pck(必须与 exe 同目录)"
[ -f "$CHECK_DIR/$NAME/README.txt" ] || die "包内缺少 README.txt"
[ -f "$CHECK_DIR/$NAME/THIRD_PARTY.md" ] || die "包内缺少 THIRD_PARTY.md"
[ -d "$CHECK_DIR/$NAME/contracts" ] || die "包内缺少 contracts/ 契约快照"
CHECK_MAGIC=$(head -c 2 "$CHECK_DIR/$NAME/OpenDance.exe" | od -An -tx1 | tr -d ' \n')
[ "$CHECK_MAGIC" = "4d5a" ] || die "解压后的 OpenDance.exe 不是合法 PE 文件(头 $CHECK_MAGIC)"
CHECK_PCK=$(head -c 4 "$CHECK_DIR/$NAME/OpenDance.pck" | tr -d '\0')
[ "$CHECK_PCK" = "GDPC" ] || die "解压后的 OpenDance.pck 不是合法 Godot 资源包"
log "整包完整性自检通过(解压后 exe + pck 同目录,PE/GDPC 头均合法)"
# 再核一次**解压后**的 exe 版本资源:确认打包/解包环节没有换错文件
bash "$PROJECT_DIR/build/check-pe-version.sh" "$CHECK_DIR/$NAME/OpenDance.exe" "$VERSION" \
|| die "包内 exe 的 PE 版本资源不是 v$VERSION"
log "包内 exe 的 PE 版本资源 = v$VERSION"
# pck 与 exe 必须同名同目录,否则 Godot 找不到资源
[ "$(basename "$CHECK_DIR/$NAME/OpenDance.exe" .exe)" = "$(basename "$CHECK_DIR/$NAME/OpenDance.pck" .pck)" ] \
|| die "exe 与 pck 主文件名不一致,导出产物不可运行"
# ---- 5. SHA256SUMS(裸文件名,下载目录内可直接校验) ----
( cd "$OUT_DIR" && sha256sum "$NAME.zip" > SHA256SUMS )
log "已生成 $OUT_DIR/SHA256SUMS"
# ---- 6. 体积与上传上限对比 ----
SIZE_BYTES=$(stat -c %s "$ZIP")
SIZE_HUMAN=$(du -h "$ZIP" | cut -f1)
log "包体 $SIZE_HUMAN($SIZE_BYTES 字节)| Gitea/IIS 实测上限 $UPLOAD_LIMIT_BYTES 字节"
if [ "$SIZE_BYTES" -gt "$UPLOAD_LIMIT_BYTES" ]; then
warn "整包超过当前 Gitea 前置 IIS 的上传上限,上传会返回 HTTP 413。"
warn "请让运维放宽上限后重传(做法见 opendance-web/deploy/proxy/README.md):"
warn " IIS requestFiltering/requestLimits@maxAllowedContentLength = 268435456"
warn " ARR maxRequestBytes >= 268435456"
warn " Gitea app.ini [attachment] MAX_SIZE >= 256MB"
warn " nginx(若前置)client_max_body_size 256m;"
warn "本脚本**不会**回退为 .partN 分卷——分卷包无法直接解压,是历史缺陷。"
warn "若暂时无法放开 IIS 上限,可用 ZIP_METHOD=lzma 重跑本脚本:包体约 26 MB 可绕过 30 MB 闸,"
warn "但 Windows 10 资源管理器不能直接解压 LZMA zip,必须用 7-Zip / WinRAR(README 中已注明)。"
else
log "低于上限,可直接作为 Release 资产上传。"
fi
echo ""
echo "产物:"
echo " $ZIP"
echo " $OUT_DIR/SHA256SUMS"
if ( cd "$OUT_DIR" && sha256sum -c SHA256SUMS --status ); then
echo " sha256 校验:OK"
else
die "sha256 自校验失败"
fi
echo ""
if [ "$ZIP_METHOD" = "lzma" ]; then
7z l "$ZIP" | tail -14
else
unzip -l "$ZIP" | sed -n '3,14p'
fi
echo ""
echo "分发:上传 $NAME.zip 与 SHA256SUMS 两个资产;用户下载后"
echo " sha256sum -c SHA256SUMS && unzip $NAME.zip 即可直接运行 OpenDance.exe。"
echo ""
echo "下一步(文档 §7.5:每次发布都要有一条读产物本身的验收):"
echo " GODOT_BIN=/path/to/godot bash build/accept-release.sh $ZIP"