feat(chv-guest): M4 Chromium 单独钉死 + /app 收紧为最小集

- Chromium 钉死:CHROMIUM_HEADLESS_SHELL_REV(纯数字修订号)
  设即钉死,buildinfo.py 校验读回的 ms-playwright 缓存目录名
  须恰好是该修订号,否则构建失败;generate/check 的 --verify
  也复核;未设时只记录、构建输出明说未钉死,BUILDINFO 记 null
- /app 最小集:build.sh 不再把整个 $REPO_DIR/cloud/ 拷进客机,
  只拷 host.py(只用标准库,不依赖 cloud/);REPO_DIR 预检
  不再要求 cloud/ 存在
- generate_lockfile.py 只读 BUILDINFO.versions 一事已在 to10 轨
  改写,本棒以合成 BUILDINFO 干跑复核(假版本 9.9.9 进 lockfile,
  宿主 git 1:2.43.0 未混入)并补钉死复核;真实 lockfile 产出
  仍待 V-9 真机构建,不编造版本号
This commit is contained in:
host committed 2026-11-06 18:41:56 +00:00
1 parent c39715659e
commit 2ad29eeb1f
5 files changed
+99 -21

No files matched your search

+10 -3
View File
@@ -30,10 +30,10 @@
| 语言与工具链 | Python 3.12、git、curl、ca-certificates、xz-utils、fontconfig、openssh-client(Node.js 不装:Playwright 驱动是 Python playwright,见 PLAYWRIGHT=1 分支;任务镜像 Dockerfile 里也没有 Node.js,本清单不虚构它) | 构建时钉小版本并记入构建日志;apt 用 --no-install-recommends(B-21:默认装 Recommends 会连带拉入 gcc 全套工具链顶满根分区) | | 语言与工具链 | Python 3.12、git、curl、ca-certificates、xz-utils、fontconfig、openssh-client(Node.js 不装:Playwright 驱动是 Python playwright,见 PLAYWRIGHT=1 分支;任务镜像 Dockerfile 里也没有 Node.js,本清单不虚构它) | 构建时钉小版本并记入构建日志;apt 用 --no-install-recommends(B-21:默认装 Recommends 会连带拉入 gcc 全套工具链顶满根分区) |
| 文档/图片工具 | 已裁剪:pandoc、poppler-utils、latexmk、texlive-latex-base、python-docx、openpyxl、python-pptx、PyMuPDF、Pillow 不在轻量镜像内 | 全量版曾照抄 `cloud/docker/Dockerfile` 的 apt/pip 清单,轻量版按用户决定砍掉 | | 文档/图片工具 | 已裁剪:pandoc、poppler-utils、latexmk、texlive-latex-base、python-docx、openpyxl、python-pptx、PyMuPDF、Pillow 不在轻量镜像内 | 全量版曾照抄 `cloud/docker/Dockerfile` 的 apt/pip 清单,轻量版按用户决定砍掉 |
| agent 引擎 | codex、codex-code-mode-host、codex-resources/bwrap(成套,约 367M,缺 sibling 直接构建失败) | codex 0.164.0-alpha.9(与生产任务镜像一致);`CODEX_BIN_DIR` 由构建方提供,bin/ 不进仓库 | | agent 引擎 | codex、codex-code-mode-host、codex-resources/bwrap(成套,约 367M,缺 sibling 直接构建失败) | codex 0.164.0-alpha.9(与生产任务镜像一致);`CODEX_BIN_DIR` 由构建方提供,bin/ 不进仓库 |
| 浏览器 | Playwright + chromium-headless-shell(默认装,不要完整 Chromium) | Python playwright 装当时的 headless-shell,版本以 ms-playwright 缓存目录名记入 BUILDINFO.versions.chromium;浏览器在系统路径 /usr/local/share/ms-playwright(B-22:virt-customize 里 HOME=/,默认缓存会落到根目录 /.cache),agent 运行时须设 PLAYWRIGHT_BROWSERS_PATH 指向它 | | 浏览器 | Playwright + chromium-headless-shell(默认装,不要完整 Chromium) | Python playwright 装当时的 headless-shell,版本以 ms-playwright 缓存目录名记入 BUILDINFO.versions.chromium;浏览器在系统路径 /usr/local/share/ms-playwright(B-22:virt-customize 里 HOME=/,默认缓存会落到根目录 /.cache),agent 运行时须设 PLAYWRIGHT_BROWSERS_PATH 指向它;单独钉死见 §3:CHROMIUM_HEADLESS_SHELL_REV 设为纯数字修订号即钉死,未设时只记录、不强制 |
| 客机基础 | openssh-server(SSH 通道用)、cloud-init(qemu-guest-agent 已裁剪,CHV 不用它) | sshd 只允许 key 登录,禁止密码登录——build.sh 写入 /etc/ssh/sshd_config.d/99-chv.conf 并经 sshd -t + grep 断言(feat-chv-secfix,M8,此前只是文档声称) | | 客机基础 | openssh-server(SSH 通道用)、cloud-init(qemu-guest-agent 已裁剪,CHV 不用它) | sshd 只允许 key 登录,禁止密码登录——build.sh 写入 /etc/ssh/sshd_config.d/99-chv.conf 并经 sshd -t + grep 断言(feat-chv-secfix,M8,此前只是文档声称) |
| Gitea 同步 | gt 1.0.0(官方 install.sh + SHA256 校验)、chv-sync(启动时 clone、显式 pull/push 的同步脚本) | 凭证走 seed 盘 /etc/chv/guest-env(0600)投递,不进镜像;见 §7 | | Gitea 同步 | gt 1.0.0(官方 install.sh + SHA256 校验)、chv-sync(启动时 clone、显式 pull/push 的同步脚本) | 凭证走 seed 盘 /etc/chv/guest-env(0600)投递,不进镜像;见 §7 |
| 任务代码 | codex-app-server 仓库代码置于 `/app`(host.py 入口) | 版本见 §3,不挂载宿主代码目录(VM 与宿主无共享路径) | | 任务代码 | 仅 `/app/host.py` 一个文件(host.py 只用标准库,不依赖 cloud/;旧版把整个 cloud/ 拷进 /app/,客内可读全套 server/worker 代码,M4 收尾已收紧为最小集) | 版本见 §3(repo_commit),不挂载宿主代码目录(VM 与宿主无共享路径) |
中文 LaTeX 说明:与 Docker 镜像同款口径——xelatex + CJK 中文 LaTeX 说明:与 Docker 镜像同款口径——xelatex + CJK
字体会再增数百 MB,本清单不默认包含;需要时单独一版镜像, 字体会再增数百 MB,本清单不默认包含;需要时单独一版镜像,
@@ -60,13 +60,20 @@ chv-guest-<ubuntu 版本>-<codex 版本>-<仓库 commit 短 hash>
在 `versions` 里(B-02:dpkg + pip freeze 从客机读回, 在 `versions` 里(B-02:dpkg + pip freeze 从客机读回,
chromium 以 ms-playwright 缓存目录名记,gt 以 gt_version 记), chromium 以 ms-playwright 缓存目录名记,gt 以 gt_version 记),
`generate_lockfile.py` / `check_lockfile.py` 以它为唯一来源。 `generate_lockfile.py` / `check_lockfile.py` 以它为唯一来源。
Chromium 单独钉死:构建时设 CHROMIUM_HEADLESS_SHELL_REV
为纯数字修订号(如 1248),buildinfo.py 校验读回的缓存
目录名须恰好是 `chromium_headless_shell-<rev>`,否则构建
失败,BUILDINFO.chromium_pinned_rev 记该值;未设时记 null、
只记录不强制,构建输出明说“未钉死”。真实 lockfile 产出
须等 V-9 真机构建(本机无 /dev/kvm,版本号绝不编造)。
## 4. 构建脚本(补缺口轨:已从骨架改成真实步骤) ## 4. 构建脚本(补缺口轨:已从骨架改成真实步骤)
见 `build.sh`:参数校验 → 下载 Ubuntu Minimal 云镜像(SHA256 见 `build.sh`:参数校验 → 下载 Ubuntu Minimal 云镜像(SHA256
必填)→ qemu-img 转 raw(轻量版不扩盘,底座根分区 2.4G 够用; 必填)→ qemu-img 转 raw(轻量版不扩盘,底座根分区 2.4G 够用;
全量版 B-16 的 +12G 扩盘已删除)→ virt-customize 装 §2 清单、 全量版 B-16 的 +12G 扩盘已删除)→ virt-customize 装 §2 清单、
拷入 codex 成套二进制与仓库代码到 /app、装 gt 1.0.0、拷入 拷入 codex 成套二进制与 host.py(仅此一个文件,不拷 cloud/)
到 /app、装 gt 1.0.0、拷入
chv-sync 并写 cloud-init 启动钩子、写 host key 不重生成配置 → chv-sync 并写 cloud-init 启动钩子、写 host key 不重生成配置 →
virt-get-kernel 取内核 → 生成并校验 BUILDINFO.json。 virt-get-kernel 取内核 → 生成并校验 BUILDINFO.json。
`./build.sh --dry-run out/` 只打印命令、不执行、不要求 `./build.sh --dry-run out/` 只打印命令、不执行、不要求
+23 -6
View File
@@ -7,7 +7,10 @@
# ubuntu-24.04-minimal-cloudimg-amd64.img——minimal 发布在 noble # ubuntu-24.04-minimal-cloudimg-amd64.img——minimal 发布在 noble
# 代号目录下,不是 releases/24.04)。只装 codex 0.164.0-alpha.9 # 代号目录下,不是 releases/24.04)。只装 codex 0.164.0-alpha.9
# linux-x64 + Python + SSH + cloud-init + 一个薄客机 agent # linux-x64 + Python + SSH + cloud-init + 一个薄客机 agent
# (/app/host.py + /app/cloud)。浏览器:Playwright + # (/app/host.py,仅此一个文件——host.py 只用标准库,不依赖
# cloud/;旧版把整个 $REPO_DIR/cloud/ 拷进 /app/,客内 agent
# 可读全套 server/worker 代码,M4 收尾已收紧为最小集)。
# 浏览器:Playwright +
# chromium-headless-shell(不要完整 Chromium);字体只要最小的 # chromium-headless-shell(不要完整 Chromium);字体只要最小的
# fonts-wqy-microhei(中文能看),不要全套 fonts-noto-cjk。 # fonts-wqy-microhei(中文能看),不要全套 fonts-noto-cjk。
# 目标:qcow2 实际占用 ≤1GB,raw ≤2GB(见下注)。 # 目标:qcow2 实际占用 ≤1GB,raw ≤2GB(见下注)。
@@ -84,6 +87,18 @@ REPO_DIR="${REPO_DIR:-$(cd "$SCRIPT_DIR/.." && pwd)}"
# 验 SHA256;此处再验脚本自身的 SHA256(2026-11-06 实测值, # 验 SHA256;此处再验脚本自身的 SHA256(2026-11-06 实测值,
# 上游轮换脚本后构建会明确失败,不静默跑新脚本)。 # 上游轮换脚本后构建会明确失败,不静默跑新脚本)。
GT_VERSION="${GT_VERSION:-1.0.0}" GT_VERSION="${GT_VERSION:-1.0.0}"
# feat-chv-closeup(M4):Chromium 单独钉死。PLAYWRIGHT=1 时
# ms-playwright 缓存目录名即 Chromium 版本标识(如
# chromium_headless_shell-1248),此前只记录不钉死——playwright
# 一升级,目录名跟着变,构建静默换浏览器版本。
# CHROMIUM_HEADLESS_SHELL_REV 设为纯数字修订号(如 1248)即钉死:
# buildinfo.py 校验读回的目录名须恰好是该修订号,否则构建失败。
# 未设时只记录、不强制,并在构建输出里明说“未钉死”,绝不假装
# 已钉死;真机 V-9 验收须用已设该变量的构建。
CHROMIUM_HEADLESS_SHELL_REV="${CHROMIUM_HEADLESS_SHELL_REV:-}"
if [ -n "$CHROMIUM_HEADLESS_SHELL_REV" ] && ! printf '%s' "$CHROMIUM_HEADLESS_SHELL_REV" | grep -Eq '^[0-9]+$'; then
echo "CHROMIUM_HEADLESS_SHELL_REV 须为纯数字修订号(如 1248):$CHROMIUM_HEADLESS_SHELL_REV" >&2; exit 1
fi
GT_INSTALL_SH_SHA256="${GT_INSTALL_SH_SHA256:-cf82acecc0e3ffa6f690d33902eefcf1cc21a5b11004103bf23c4436664288d5}" GT_INSTALL_SH_SHA256="${GT_INSTALL_SH_SHA256:-cf82acecc0e3ffa6f690d33902eefcf1cc21a5b11004103bf23c4436664288d5}"
# B-03/B-04:输入格式校验(干跑与全量都做,报错点提前, # B-03/B-04:输入格式校验(干跑与全量都做,报错点提前,
# 不等到 curl 404 或 sha256sum -c 才失败)。 # 不等到 curl 404 或 sha256sum -c 才失败)。
@@ -143,8 +158,8 @@ if [ ! -d "$CODEX_BIN_DIR/codex-resources" ]; then
echo "缺少 codex-resources(bwrap 载体),构建失败" >&2 echo "缺少 codex-resources(bwrap 载体),构建失败" >&2
exit 1 exit 1
fi fi
if [ ! -f "$REPO_DIR/host.py" ] || [ ! -d "$REPO_DIR/cloud" ]; then if [ ! -f "$REPO_DIR/host.py" ]; then
echo "REPO_DIR 不是 codex-app-server 仓库根(缺 host.py/cloud/):$REPO_DIR" >&2 echo "REPO_DIR 不是 codex-app-server 仓库根(缺 host.py):$REPO_DIR" >&2
exit 1 exit 1
fi fi
if [ ! -f "$SCRIPT_DIR/chv-sync.sh" ]; then if [ ! -f "$SCRIPT_DIR/chv-sync.sh" ]; then
@@ -241,7 +256,6 @@ VC_ARGS=(-a "$OUT/disk.raw"
--run-command 'chmod +x /usr/local/bin/codex /usr/local/bin/codex-code-mode-host; ln -sf /usr/local/bin/codex-resources/bwrap /usr/local/bin/bwrap' --run-command 'chmod +x /usr/local/bin/codex /usr/local/bin/codex-code-mode-host; ln -sf /usr/local/bin/codex-resources/bwrap /usr/local/bin/bwrap'
--mkdir /app --mkdir /app
--copy-in "$REPO_DIR/host.py:/app/" --copy-in "$REPO_DIR/host.py:/app/"
--copy-in "$REPO_DIR/cloud:/app/"
--copy-in "$SCRIPT_DIR/chv-sync.sh:/usr/local/bin/" --copy-in "$SCRIPT_DIR/chv-sync.sh:/usr/local/bin/"
--run-command 'mv /usr/local/bin/chv-sync.sh /usr/local/bin/chv-sync && chmod +x /usr/local/bin/chv-sync' --run-command 'mv /usr/local/bin/chv-sync.sh /usr/local/bin/chv-sync && chmod +x /usr/local/bin/chv-sync'
--run-command 'printf "runcmd:\n - systemctl restart ssh || service ssh restart || true\n - /usr/local/bin/chv-sync boot >>/var/log/chv-sync.log 2>&1 || true\n" > /etc/cloud/cloud.cfg.d/99-chv-sync.cfg' --run-command 'printf "runcmd:\n - systemctl restart ssh || service ssh restart || true\n - /usr/local/bin/chv-sync boot >>/var/log/chv-sync.log 2>&1 || true\n" > /etc/cloud/cloud.cfg.d/99-chv-sync.cfg'
@@ -302,11 +316,14 @@ fi
# 产物校验(干跑只打印,不执行校验命令的副作用部分)。 # 产物校验(干跑只打印,不执行校验命令的副作用部分)。
if [ "$DRY_RUN" = "1" ]; then if [ "$DRY_RUN" = "1" ]; then
echo "DRY-RUN: test -s $OUT/vmlinux && test -s $OUT/disk.raw" echo "DRY-RUN: test -s $OUT/vmlinux && test -s $OUT/disk.raw"
echo "DRY-RUN: python3 $SCRIPT_DIR/buildinfo.py $OUT $TAG $UBUNTU_VERSION $CODEX_VERSION $REPO_COMMIT <sha256> ${PLAYWRIGHT:-1} # 生成 $OUT/BUILDINFO.json 并校验 tag=$TAG" echo "DRY-RUN: python3 $SCRIPT_DIR/buildinfo.py $OUT $TAG $UBUNTU_VERSION $CODEX_VERSION $REPO_COMMIT <sha256> ${PLAYWRIGHT:-1} ${CHROMIUM_HEADLESS_SHELL_REV:-} # 生成 $OUT/BUILDINFO.json 并校验 tag=$TAG"
else else
test -s "$OUT/vmlinux" || { echo "内核产物为空:$OUT/vmlinux" >&2; exit 1; } test -s "$OUT/vmlinux" || { echo "内核产物为空:$OUT/vmlinux" >&2; exit 1; }
test -s "$OUT/disk.raw" || { echo "磁盘产物为空:$OUT/disk.raw" >&2; exit 1; } test -s "$OUT/disk.raw" || { echo "磁盘产物为空:$OUT/disk.raw" >&2; exit 1; }
python3 "$SCRIPT_DIR/buildinfo.py" "$OUT" "$TAG" "$UBUNTU_VERSION" "$CODEX_VERSION" "$REPO_COMMIT" "$UBUNTU_IMAGE_SHA256" "${PLAYWRIGHT:-1}" if [ "${PLAYWRIGHT:-1}" = "1" ] && [ -z "$CHROMIUM_HEADLESS_SHELL_REV" ]; then
echo "警告:CHROMIUM_HEADLESS_SHELL_REV 未设,Chromium 版本只记录、不钉死(V-9 验收须设该变量重建)" >&2
fi
python3 "$SCRIPT_DIR/buildinfo.py" "$OUT" "$TAG" "$UBUNTU_VERSION" "$CODEX_VERSION" "$REPO_COMMIT" "$UBUNTU_IMAGE_SHA256" "${PLAYWRIGHT:-1}" "${CHROMIUM_HEADLESS_SHELL_REV:-}"
fi fi
echo "tag=$TAG" echo "tag=$TAG"
echo "out=$OUT" echo "out=$OUT"
+34 -6
View File
@@ -11,6 +11,14 @@ sha256/size 是对产物文件实测的;image_sha256 即 disk.raw
用法: 用法:
python3 buildinfo.py OUT_DIR TAG UBUNTU CODEX_VERSION \\ python3 buildinfo.py OUT_DIR TAG UBUNTU CODEX_VERSION \\
REPO_COMMIT UBUNTU_IMAGE_SHA256 PLAYWRIGHT(0|1) REPO_COMMIT UBUNTU_IMAGE_SHA256 PLAYWRIGHT(0|1)
[CHROMIUM_HEADLESS_SHELL_REV]
CHROMIUM_HEADLESS_SHELL_REV(可选,纯数字):钉死的
ms-playwright 缓存修订号(如 1248)。PLAYWRIGHT=1 且
给了该值时,versions.chromium 里须恰好出现
chromium_headless_shell-<rev>,否则退出 1、构建失败;
未给时只记录、不强制,BUILDINFO.chromium_pinned_rev
记 null,构建输出由 build.sh 明说“未钉死”。
""" """
import hashlib import hashlib
import json import json
@@ -59,7 +67,7 @@ def _sha256_size(path):
def build(outdir, tag, ubuntu, codex_version, repo_commit, def build(outdir, tag, ubuntu, codex_version, repo_commit,
ubuntu_image_sha256, playwright): ubuntu_image_sha256, playwright, chromium_pinned_rev=None):
disk_path = os.path.join(outdir, "disk.raw") disk_path = os.path.join(outdir, "disk.raw")
vmlinux_path = os.path.join(outdir, "vmlinux") vmlinux_path = os.path.join(outdir, "vmlinux")
disk_sha, disk_size = _sha256_size(disk_path) disk_sha, disk_size = _sha256_size(disk_path)
@@ -98,6 +106,9 @@ def build(outdir, tag, ubuntu, codex_version, repo_commit,
"playwright": bool(playwright), "playwright": bool(playwright),
"playwright_browsers_path": PLAYWRIGHT_BROWSERS_PATH "playwright_browsers_path": PLAYWRIGHT_BROWSERS_PATH
if playwright else "", if playwright else "",
# feat-chv-closeup(M4):钉死的 Chromium 修订号(未设
# 记 null,绝不编一个默认值出来假装已钉死)。
"chromium_pinned_rev": chromium_pinned_rev or None,
"image_sha256": disk_sha, "image_sha256": disk_sha,
"artifacts": { "artifacts": {
"disk.raw": {"sha256": disk_sha, "size": disk_size}, "disk.raw": {"sha256": disk_sha, "size": disk_size},
@@ -112,15 +123,22 @@ def build(outdir, tag, ubuntu, codex_version, repo_commit,
def main(argv): def main(argv):
if len(argv) != 8: if len(argv) not in (8, 9):
print("用法:buildinfo.py OUT TAG UBUNTU CODEX_VERSION " print("用法:buildinfo.py OUT TAG UBUNTU CODEX_VERSION "
"REPO_COMMIT UBUNTU_IMAGE_SHA256 PLAYWRIGHT", "REPO_COMMIT UBUNTU_IMAGE_SHA256 PLAYWRIGHT "
file=sys.stderr) "[CHROMIUM_HEADLESS_SHELL_REV]", file=sys.stderr)
return 2 return 2
_, outdir, tag, ubuntu, codex_version, repo_commit, \ _, outdir, tag, ubuntu, codex_version, repo_commit, \
ubuntu_image_sha256, playwright = argv ubuntu_image_sha256, playwright = argv[:8]
chromium_pinned_rev = argv[8] if len(argv) == 9 else None
if chromium_pinned_rev and not re.fullmatch(r"[0-9]+",
chromium_pinned_rev):
print("CHROMIUM_HEADLESS_SHELL_REV 须为纯数字:%s"
% chromium_pinned_rev, file=sys.stderr)
return 2
info = build(outdir, tag, ubuntu, codex_version, repo_commit, info = build(outdir, tag, ubuntu, codex_version, repo_commit,
ubuntu_image_sha256, playwright == "1") ubuntu_image_sha256, playwright == "1",
chromium_pinned_rev)
if not info["versions"]["dpkg"]: if not info["versions"]["dpkg"]:
print("BUILDINFO versions.dpkg 为空", file=sys.stderr) print("BUILDINFO versions.dpkg 为空", file=sys.stderr)
return 1 return 1
@@ -134,6 +152,16 @@ def main(argv):
print("BUILDINFO versions.chromium 为空(浏览器未装好或" print("BUILDINFO versions.chromium 为空(浏览器未装好或"
"读回路径不对)", file=sys.stderr) "读回路径不对)", file=sys.stderr)
return 1 return 1
if playwright == "1" and chromium_pinned_rev:
# feat-chv-closeup(M4):钉死校验——读回的缓存目录名
# 须恰好是该修订号,playwright 升级导致目录名变了就
# 在这里失败,不静默换浏览器版本。
expected = "chromium_headless_shell-%s" % chromium_pinned_rev
if expected not in (info["versions"].get("chromium") or []):
print("Chromium 修订号与钉死值不符:读回 %s,钉死 %s"
% (info["versions"].get("chromium"), expected),
file=sys.stderr)
return 1
if info["tag"] != tag: if info["tag"] != tag:
print("BUILDINFO tag 不一致", file=sys.stderr) print("BUILDINFO tag 不一致", file=sys.stderr)
return 1 return 1
+17 -6
View File
@@ -1,12 +1,13 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""校验已构建镜像的 BUILDINFO 供应链清单(事后 lockfile 检查)。 """校验已构建镜像的 BUILDINFO 供应链清单(事后 lockfile 检查)。
事前 apt/pip 精确 lockfile 无法在本轨生成:apt 版本在构建 生成端见 generate_lockfile.py(读 BUILDINFO.versions 生成
宿主上解析、pip 版本在客机里解析,没有构建宿主就编不出 lockfile.json,版本唯一来源是 build.sh 从客机读回的
版本号——本脚本绝不编造。构建时 build.sh 已从客机读回 dpkg/pip freeze + chromium 目录名 + codex/repo_commit/gt
dpkg + pip freeze 写进 BUILDINFO.versions(事后 lockfile), 声明字段,绝不查构建宿主现场、绝不编造版本号)。
本脚本校验它:versions 非空、每条有版本、contents 全覆盖、 本脚本校验 BUILDINFO:versions 非空、每条有版本、contents
tag 与预期一致、可选的冻结清单(--frozen JSON,形如 全覆盖、tag 与预期一致、钉死的 Chromium 修订号一致、
可选的冻结清单(--frozen JSON,形如
{"dpkg": ["pkg version", ...]})双向比对,漂移即退出非 0。 {"dpkg": ["pkg version", ...]})双向比对,漂移即退出非 0。
用法: 用法:
@@ -111,6 +112,16 @@ def main(argv):
if missing: if missing:
print("contents 未覆盖:%s" % sorted(missing), file=sys.stderr) print("contents 未覆盖:%s" % sorted(missing), file=sys.stderr)
return 1 return 1
# feat-chv-closeup(M4):钉死的 Chromium 修订号须与读回
# 的缓存目录名一致;未钉(pinned_rev 为空)时不校验。
pinned = data.get("chromium_pinned_rev")
if pinned:
expected = "chromium_headless_shell-%s" % pinned
if expected not in (versions.get("chromium") or []):
print("Chromium 修订号与钉死值不符:读回 %s,钉死 %s"
% (versions.get("chromium"), expected),
file=sys.stderr)
return 1
if frozen_path: if frozen_path:
with open(frozen_path) as fh: with open(frozen_path) as fh:
frozen = json.load(fh) frozen = json.load(fh)
+15
View File
@@ -145,6 +145,21 @@ def verify(lockfile_path, buildinfo_path):
% (lockfile.get("tag"), buildinfo.get("tag")), % (lockfile.get("tag"), buildinfo.get("tag")),
file=sys.stderr) file=sys.stderr)
return 1 return 1
# feat-chv-closeup(M4):钉死的 Chromium 修订号须与
# lockfile 里记的 chromium-headless-shell 条目一致;
# BUILDINFO 没钉(pinned_rev 为空)时不校验。
pinned = buildinfo.get("chromium_pinned_rev")
if pinned:
expected = "chromium_headless_shell-%s" % pinned
declared = (lockfile.get("versions") or {}).get(
"declared") or []
hit = [e for e in declared
if str(e).startswith("chromium-headless-shell ")]
if not hit or expected not in str(hit[0]):
print("Chromium 修订号与钉死值不符:lockfile %s,"
"钉死 chromium_headless_shell-%s"
% (hit, pinned), file=sys.stderr)
return 1
versions = lockfile.get("versions") or {} versions = lockfile.get("versions") or {}
for kind in ("dpkg", "pip"): for kind in ("dpkg", "pip"):
entries = versions.get(kind) entries = versions.get(kind)