diff --git a/deploy/container/README.md b/deploy/container/README.md index 6ecf28b5a..0f992bafa 100644 --- a/deploy/container/README.md +++ b/deploy/container/README.md @@ -89,6 +89,7 @@ Cargo registry 的压缩包与索引、npm `_cacache` 使用稳定命名、`shar ```bash sudo apt-get install docker-buildx +# Buildx 0.30.1 的 inspect 不支持 --format;脚本读取普通输出的 Driver 字段。 # 替换为运维已验证的完整 Image ID;只提取 registry/cache、registry/index 和 npm/_cacache。 sudo bash scripts/gitea-ci-job-image.sh seed-downloads 'sha256:<可信镜像的64位摘要>' ``` diff --git a/docs/project-memory/shared-memory/development-workflow.md b/docs/project-memory/shared-memory/development-workflow.md index 000ce042f..bb3a362a7 100644 --- a/docs/project-memory/shared-memory/development-workflow.md +++ b/docs/project-memory/shared-memory/development-workflow.md @@ -96,6 +96,8 @@ SpacetimeDB 任务统一先读取 `.codex/skills/genarrative-spacetimedb/SKILL.m ## Gitea CI 依赖闭合 +Buildx 0.30.1 的 `inspect` 不支持 `--format`,builder 驱动校验读取普通输出的 `Driver:` 字段。相关命令须在宿主真实插件上验证;测试替身应拒绝不支持的参数,避免把模拟命令成功误当兼容性证据。 + Gitea 基础镜像通过专用 `genarrative-ci-images` Buildx builder 持久复用 Cargo/npm 下载缓存;稳定 cache mount 与 commit、lock 哈希无关,以 `sharing=locked` 隔离并发写入,仅供可信宿主构建、不开放给 PR。最终镜像显式物化当前依赖下载快照,仍不包含 node_modules/target 或上一版 sccache 层。首次可用 `seed-downloads` 从可信完整 Image ID 提取包缓存,操作账号须与维护服务一致;部署要求及 builder GC 空间目标见 `deploy/container/README.md`。构建上下文必须覆盖 AGC vendor 与编辑器 bridge 的全部本地 path manifest,普通源码变化不应使依赖层失效。维护 journal 提供阶段耗时和失败 build.log 定位。 Gitea Rust 缓存自动维护由宿主 `genarrative-ci-cache.timer` 收集同一 master push run 六个 Rust job 的原生 V4 缓存产物,不重复执行 Cargo 预热。只传本轮新 key,命中对象只传使用时间;宿主与真实来源镜像对象合并、去重、按新近使用时间裁剪到 4 GiB,从无对象缓存基础镜像重新组装。源 run 不要求全绿,但取消、缺组、旧 attempt、未完成上传或混用来源镜像不得采用。网关暂停新 FetchTask、在途领取结束、持久化任务账本清空且内层活动容器为空才切换,不打断运行中的 CI。首次接入/升级网关需空闲窗口;Token 只需普通仓库 `write:repository`,不查管理员 API。候选装载后清理已收集 artifact,遗留项保留 7 天;真实 master CI 验证后才清理旧镜像,保留当前、一个回滚版、基础镜像及容器引用。部署入口见 `deploy/container/README.md`,合并代码不等于服务启用。 diff --git a/scripts/gitea-ci-job-image.sh b/scripts/gitea-ci-job-image.sh index c0df68721..526c0ad7b 100644 --- a/scripts/gitea-ci-job-image.sh +++ b/scripts/gitea-ci-job-image.sh @@ -18,7 +18,7 @@ prepare_builder() { --driver-opt image=moby/buildkit:v0.23.2@sha256:ddd1ca44b21eda906e81ab14a3d467fa6c39cd73b9a39df1196210edcb8db59e \ --buildkitd-config "${repo_root}/deploy/container/gitea-ci-buildkitd.toml" fi - if [[ "$(docker buildx inspect "${builder_name}" --format '{{.Driver}}')" != docker-container ]]; then + if [[ "$(docker buildx inspect "${builder_name}" | awk '$1 == "Driver:" { print $2 }')" != docker-container ]]; then echo "${builder_name} must use the isolated docker-container driver" >&2 return 1 fi diff --git a/scripts/test_gitea_cache_image_context.py b/scripts/test_gitea_cache_image_context.py index 82e6ec75f..eea8286b8 100644 --- a/scripts/test_gitea_cache_image_context.py +++ b/scripts/test_gitea_cache_image_context.py @@ -90,7 +90,8 @@ class GiteaCiImageContextTest(unittest.TestCase): set -eu if [[ "$1" == buildx && "$2" == version ]]; then exit 0; fi if [[ "$1" == buildx && "$2" == inspect ]]; then - if [[ " $* " == *" --format "* ]]; then printf 'docker-container\\n'; fi + if [[ "$#" != 3 ]]; then echo 'unsupported buildx inspect arguments' >&2; exit 2; fi + printf 'Name: genarrative-ci-images\\nDriver: %s\\n' "${FAKE_BUILDX_DRIVER:-docker-container}" exit 0 fi if [[ "$1" == buildx && "$2" == build ]]; then @@ -108,8 +109,10 @@ class GiteaCiImageContextTest(unittest.TestCase): ).encode("utf-8")) docker.chmod(docker.stat().st_mode | stat.S_IXUSR) - def run_script(self, script: Path, *arguments: str, capture_context: bool = False) -> subprocess.CompletedProcess[str]: + def run_script(self, script: Path, *arguments: str, capture_context: bool = False, + builder_driver: str = "docker-container") -> subprocess.CompletedProcess[str]: exports = [f"export PATH={shlex.quote(self.execution_bin)}:\"$PATH\""] + exports.append(f"export FAKE_BUILDX_DRIVER={shlex.quote(builder_driver)}") if capture_context: exports.append(f"export TAR_CAPTURE={shlex.quote(self.wsl_path(self.context_archive))}") command = "; ".join(exports) + "; cd /; exec bash " + shlex.quote(self.wsl_path(script)) @@ -129,6 +132,12 @@ class GiteaCiImageContextTest(unittest.TestCase): yield dependency["path"] yield from GiteaCiImageContextTest.dependency_paths(child) + def test_build_rejects_a_builder_with_the_wrong_driver(self) -> None: + result = self.run_script(IMAGE_SCRIPT, "build", capture_context=True, builder_driver="docker") + self.assertNotEqual(result.returncode, 0) + self.assertIn("must use the isolated docker-container driver", result.stderr) + self.assertFalse(self.context_archive.exists()) + def test_build_context_contains_all_local_dependency_manifests_and_no_source(self) -> None: result = self.run_script(IMAGE_SCRIPT, "build", capture_context=True) self.assertEqual(result.returncode, 0, result.stderr)