
开发工具代码编辑器数据科学【免费下载链接】positronPositron, a next-generation data science IDE项目地址https://gitcode.com/gh_mirrors/po/positron点击查看免费下载Positron 的 CI 测试环境依赖一组预构建的 Docker 镜像ubuntu24_04、rocky_8、rocky_9、debian、openSUSE15_6、SLES15_6 与 postgres当 Node.js 需要升级或镜像需要随依赖打上统一的新 tag 时逐个手工触发构建、盯日志、修错误代价高昂。update-ci-images是一个面向长期编排的 Agent Skill它把新建分支 → 改 Node 版本 → 以受限并发 dispatch 构建工作流 → 监控 run → 自动修复失败并重试 → 全部变绿后留下 PR 供人工审查这条完整链路固化成了可重复执行的流程。读完本文你将掌握这套 7 镜像批量重建的输入参数、阶段化执行模型、失败分类与重试预算、以及仓库中每个脚本的真实行为。背景7 个 CI 镜像与两条构建工作流镜像构建的实际执行者是两个workflow_dispatch工作流Skill 只是它们的指挥者。构建矩阵共 7 个构建任务工作流变体数量ci-images-build-os.ymlos∈ {ubuntu24_04, rocky_8, rocky_9, debian, openSUSE15_6, SLES15_6}6ci-images-build-postgres.ymlpostgres1每个 run 同时产出 amd64 与 arm64 的 manifest list每次 dispatch 只触发一个 run但该 run 内部是两个架构的矩阵构建加一个合并任务build矩阵在原生 runner上分别构建linux/amd64与linux/arm64ci-images-build-os.yml 中fail-fast: false一个架构失败不会取消另一个各自推送单架构 tag如positron-ubuntu24-amd64:tag、positron-ubuntu24-arm64:tag随后的merge任务用docker buildx imagetools create将两个单架构 tag 合并为多架构 manifest list即ghcr.io/owner/positron-ubuntu24:tag单架构 tag 会保留在 GHCR 上作为过渡期存量消费方的兼容输出。postgres 镜像在 Dockerfile.postgres 中只是postgres:latest加初始化脚本COPY init-scripts/与COPY sql/没有任何 Node因此它不受NODE_VERSION变更影响。镜像里到底装了什么从 Dockerfile.ubuntu24_04 可以看到这类 OS 测试镜像的内容系统依赖与 VNC/窗口管理器Fluxbox x11vnc 用于本地观察测试、AWS CLI v2、Node.js 与全局 npm 依赖、Quarto、Miniforge Conda、rig 管理的多个 R 版本、TinyTeXLaTeX、uv 管理的 Python 3.10.12 venv 与 e2e 测试内容依赖、pyenv、隐藏 Python/R、以及 Positron license server。镜像还暴露8080、5900、9323端口与 docker/images/README.md 中本地开发复刻 CI 环境的用途一致。输入参数一次升级只需要三个信息Skill 从用户的调用中解析输入缺失时才询问tag必填——所有镜像统一使用的 Docker tag例如2025.06.0node version必填——完整 Node 版本号例如22.22.1且必须同时存在于https://nodejs.org/dist/vversion/的 x64 与 arm64 路径下预检会用 HEAD 请求验证concurrency N可选默认 4——同时最多有多少个构建任务在飞行中。五个执行阶段Phase 0 — Preflight动手前先确认基线在改动任何东西之前依次确认以下前置条件全部通过gh auth status # 已认证且有 workflow repo scope git -C $REPO status --porcelain # 必须为空工作树干净 git -C $REPO rev-parse --abbrev-ref HEAD # 汇报当前基础分支任意分支均可 gh repo view --json nameWithOwner -q .nameWithOwner # 期望 posit-dev/positron用 HEAD 请求验证 Node 版本在双架构都存在curl -sfI https://nodejs.org/dist/vnode/node-vnode-linux-x64.tar.xz与...-linux-arm64.tar.xz任一返回 404 就停止并告知用户版本无效工作树必须干净否则停止并询问如何处理基础分支不必是mainupdate-images/tag分支从当前所在位置 fork 即可这对从功能分支上测试很有用。只需在 PR body 中注明 fork 自哪个基础分支。Phase 1 — 分支、改版本、开 PRgit -C $REPO switch -c update-images/tag # 非字母数字字符会被清洗为 - bash dir/scripts/bump-node-version.sh node # 编辑全部 12 个 compose 文件并打印证明 git -C $REPO add -A git -C $REPO commit -m Bump Node to node and rebuild all images at tag git -C $REPO push -u origin HEAD gh pr create --base main --head update-images/tag \ --title Rebuild all CI images at tag (Node node) \ --body see template belowPR body 模板构建逐个变绿时勾选Rebuilds every image at tag tag with Node node. Driven by the update-ci-images skill. Builds are dispatched on this branch via workflow_dispatch with max N jobs in flight. ### Builds (each is a multi-arch amd64arm64 manifest) - [ ] ubuntu24_04 - [ ] rocky_8 - [ ] rocky_9 - [ ] debian - [ ] openSUSE15_6 - [ ] SLES15_6 - [ ] postgres更新 PR body/checklist 时务必用 REST API 而不是gh pr editgh pr edit --body可能触发 Projects-classic GraphQL 弃用错误并静默地不更新 body因此统一用不受影响的 REST PATCHgh api -X PATCH repos/owner/repo/pulls/n -f body$NEW_BODY每次勾选时都基于最新内容重建完整 body 再 PATCH。bump-node-version.sh 到底改了哪些文件scripts/bump-node-version.sh 维护一个 12 个文件的显式列表6 个 OS × amd64/arm64 各一用sed -i.bak只替换NODE_VERSION:的值并保留缩进。它的防御性设计值得一提任一预期文件缺失、或文件中没有NODE_VERSION:行脚本都会报错退出从而杜绝静默部分更新。postgres 没有 Node刻意排除在列表之外。执行后打印Updated NODE_VERSION to node in 12 files:当前仓库中这些文件的实际值例如 ubuntu24_04 的 amd64 compose 里NODE_VERSION: 24.18.0对应的 Dockerfile 在 Dockerfile.ubuntu24_04 中用curl -fsSL https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-${NODE_ARCH}.tar.xz安装 Node 并用node -v npm -v验证。PPM_SNAPSHOT 是独立的旋钮普通 Node 重建不动它R 的 dev-deps 安装还有一个独立的PPM_SNAPSHOTbuild arg由 scripts/bump-ppm-snapshot.sh 负责修改它在正常的 Node 重建中不会被触碰——除非遇到 Phase 4 描述的 PPM 发布窗口竞争。bump-ppm-snapshot.sh 同样以显式文件列表 缺失即报错的方式工作并且把日期用引号包裹sed写成${NEW}避免 YAML 把日期解析成时间戳buildx bake 会拒绝time.Timedebian 被有意排除冻结在2026-03-01见 debian compose原因是 terra/GDAL 问题。Phase 2 — 建队列与状态在 scratchpad 里创建一个状态文件以支撑长时间运行。每个任务的状态沿queued→running记录 run id→done/failed迁移。每个任务维护两个相互独立的计数器原因见 Phase 4fix_attempts——做了真正的代码修复并重新 dispatch 的次数transient_retries——因基础设施抖动无代码改动重新 dispatch 的次数。7 个任务按 checklist 行命名。每个任务调用dispatch-job.sh branch tag [os]6 个 OS 任务传ospostgres 任务省略每次 dispatch 只产生一个 run双架构构建 manifest 合并。Phase 3 — Dispatch 监控循环长时间运行部分维护in_flightrun id → 任务与queued循环直到queued与in_flight都为空补位当len(in_flight) N且queued非空时弹出一个任务并执行id$(bash dir/scripts/dispatch-job.sh branch tag [os])记录id、标记任务running、递增其attempts、更新状态文件。等待把 waiter 放到后台启动不阻塞会话传入所有在飞 run id每 60s 轮询一次。macOS 上包一层caffeinate -i防止主机空闲休眠导致轮询停滞WAITbash dir/scripts/wait-for-runs.sh 60 id1 id2 ... if [ $(uname) Darwin ]; then caffeinate -i $WAIT; else $WAIT; fi注意caffeinate -i只防空闲休眠不防电池状态下的合盖休眠Linux 上可换systemd-inhibit --whatidle。只要有任一在飞 run 完成脚本就以id conclusion格式打印并退出harness 会携带该输出重新调用 Agent。处理完成事件对每行输出——success→ 标记done、移出in_flight、用gh api -X PATCH勾选 PR checklist 与 TodoWrite 项回到步骤 1 补位其他结论failure/cancelled/timed_out/startup_failure→ 进入 Phase 4。当queued与in_flight都为空 → 进入 Phase 5。不要用前台的sleep轮询用步骤 2 的后台 waiterharness 会在其退出时重新调用你。等待期间也可以使用 Monitor 工具。脚本实现细节scripts/dispatch-job.sh 用run-namedisplayTitle来匹配刚触发的 run有os参数时工作流是ci-images-build-os.yml、标题tag-os无os时是ci-images-build-postgres.yml、标题postgres-tag。它先查同分支同标题已有 run 的最大databaseId再 dispatch然后最多 3 次尝试、每次 20 次 × 3s 轮询去定位databaseId更大的新 run——这样即使 dispatch 命令报错但实际已落地也不会重复触发。run id 输出到 stdout其余日志走 stderr便于调用方干净地捕获 id。scripts/wait-for-runs.sh 阻塞直到至少一个给定 run 达到statuscompleted然后逐行打印已完成 run 的id conclusion进行中的不打印success | failure | cancelled | timed_out | ...一旦有完成即退出 0。后续用仍在飞的 id 重新调用即可继续等待。Phase 4 — 诊断、修复、重试失败的构建对失败的 run idgh run view id --log-failed # 失败步骤 错误每个 run 有两个build矩阵腿amd64、arm64加一个merge任务。--log-failed会指出哪条腿哪个架构失败——据此选择要编辑的文件按架构区分的docker/images/os/docker-compose.arch.yml或共享的docker/images/os/Dockerfile.*。merge任务失败说明某条腿没推上它的架构 tag真正的失败在腿修腿或者是一次瞬时的 GHCR/imagetools抖动重新 dispatch。重新 dispatch 会重建两个架构并重新合并这是预期行为。按失败特征失败步骤 错误分类而不是只看任务名瞬时失败runner 丢失、网络抖动、GHCR 5xx、no space left、setup-qemu抖动、CTAN/mirror 拉取失败、甚至一次脆弱的gh调用无代码改动重新 dispatch 同一任务无需 push放回in_flight递增transient_retries上限5——抖动不是任务的错真实构建错误包/仓库找不到、某架构 Node 404、Dockerfile/compose 问题、基础镜像或上游包变更编辑对应文件docker/images/os/Dockerfile.*、docker/images/os/docker-compose.arch.yml或docker/images/postgres/Dockerfile.postgres保持改动最小且匹配周围风格然后git -C $REPO add -A git -C $REPO commit -m Fix os/arch build: what git -C $REPO push在已更新的分支上重新 dispatch放回in_flight递增fix_attempts某架构 Node 版本 404 通常意味着输入错误——停止并上报不要浪费重试。计数按根因、不按任务一个任务可能连续命中多个不同的失败先真实错误、再独立的基础设施抖动。要把相应计数器重置/限定到当前失败特征避免一个问题的预算被另一个问题消耗。停止条件当某个任务满足以下任一条件时暂停循环并升级——同一失败特征下fix_attempts达到3真实修复不生效、或transient_retries达到5持续抖动、或失败不明确/修复会改变镜像内容而应由所有者决策例如固定包版本。暂停时报告任务、失败步骤、已尝试内容、gh run view id --log-failed摘录。其他在飞/排队任务继续只有卡住的任务阻塞最终成功。不要无限循环。推送到分支的修复只影响推送之后dispatch 的构建已在飞的 run 沿用旧代码让它们自然结束并独立评判即可。仓库内已证实的三类失败模式1. R 源码包需要比发行版更新的系统库例如terra1.9 需要 GDAL ≥ 3.7而 Debian bookworm 只有 3.6.2报gdal_multidimensional.cpp ... no matching function for ... AsClassicDataset。修复把 dev-deps 安装固定到破坏性包版本之前的某个 PPM 快照日期作用于该 OS 的R_REPO/RSPMhttps://packagemanager.posit.co/cran/DATE源码与https://packagemanager.posit.co/cran/__linux__/distro/DATE二进制。破坏日期可从https://cran.r-project.org/src/contrib/Archive/pkg/查得。rocky_8走不同路线从源码构建更新的 GEOS/GDAL/libgit2所以这只影响 Debian 系rocky_9两者都不需要EL9 EPEL9 已自带 GEOS 3.13 / GDAL 3.10 / PROJ 9.6 / libgit2 1.7 和支持 C20 的 GCC那里应优先用固定日期的 PPM 快照而不是重新引入源码构建。2. PPMlatest发布窗口竞争报! Failed to download pkg from https://packagemanager.posit.co/cran/.../latest/.../pkg_ver.tar.gz发生在pak::local_install_dev_deps期间前面很多包已正常下载。根因滚动latest通道上pak 从PACKAGES元数据解析出版本但对应的二进制/源码文件可能已在发布中途轮出latest→ 间歇性 404。它看起来像瞬时问题但会反复出现。修复把构建期dev-deps 安装固定到有日期快照源码https://packagemanager.posit.co/cran/DATE、二进制.../cran/__linux__/distro/DATE保证内部一致各镜像运行期的.Rprofile/ENV RSPM保持latest。日期就是PPM_SNAPSHOTbuild arg按 OS 设置在docker/images/os/docker-compose.*.yml中由各Dockerfile.*消费参见 Dockerfile.ubuntu24_04 中options(reposc(RSPM.../${PPM_SNAPSHOT}), ...)的用法。四个滚动 OS 用scripts/bump-ppm-snapshot.sh DATE提升debian 有意排除因上述 terra/GDAL 原因冻结在2026-03-01不要随其他 OS 一起动。改变镜像内容包版本属于所有者决策——重新固定前要与用户确认日期。3. TinyTeX /tlmgr间歇性失败could not get texlive.tlpdb或从随机镜像拉取后安装失败。根因tlmgr option repository https://mirror.ctan.org/...是轮询式分配每次调用可能落在过期/不可达的镜像上。修复固定到单个完整镜像https://ctan.math.illinois.edu/systems/texlive/tlnet并把网络依赖的tlmgr update/install包进一个小重试循环。6 个 OS 的 Dockerfile 中都存在该模式Dockerfile.ubuntu24_04 即是固定 CTAN 镜像 retry函数包裹tlmgr步骤的实例。Phase 5 — 收尾全部 7 个任务donesuccess后用gh api -X PATCH见 Phase 1 备注勾选全部 PR checklist 框用gh pr comment n添加总结评论列出每个推送的 manifest:tag例如ghcr.io/posit-dev/positron-ubuntu24:tag…positron-postgres:tag单架构-amd64/-arm64tag 也作为构建产物推上去了PR 保持打开供人工审查——不要合并向用户汇报PR URL、tag、Node 版本以及 7 个多架构镜像全部构建并推送成功的确认注明任何需要修复的内容附上对应 commit 链接。如果因卡住的任务提前停止则精确报告哪些已绿、哪些被阻塞、诊断结果并把分支/PR 留在原地供用户接管。一次完整重建的端到端视图把整个流程串起来看预检确认认证与干净工作树 → 在update-images/tag分支上由 bump-node-version.sh 批量更新 12 个 compose 文件的NODE_VERSION并开 PR → 用 dispatch-job.sh 以默认并发 4 逐批 dispatch 7 个构建 → 由 wait-for-runs.sh 后台轮询、按成功/失败分流 → 失败按特征归类瞬时抖动最多重试 5 次真实错误修复后最多 3 次 → 全绿后勾选 checklist、留总结评论PR 交给人工审查合并。每个 OS 的构建最终产出ghcr.io/owner/positron-os-short:tag多架构 manifest同时保留-amd64/-arm64单架构 tag 供存量流程过渡。这套编排的关键设计都沉淀在 SKILL.md 与其 scripts 目录中双计数器分离代码修复与基础设施抖动的重试预算、run-name databaseId 双保险防重复 dispatch、后台 waiter 避免长轮询阻塞会话以及三类已证实的失败模式及其修复方案——它们共同保证了在数小时的批量重建中Agent 能以受控并发稳定地把全部 7 个镜像推向同一个新 tag。赞分享开发工具代码编辑器数据科学【免费下载链接】positronPositron, a next-generation data science IDE项目地址https://gitcode.com/gh_mirrors/po/positron点击查看免费下载相关推荐osquery CI 多架构 Docker 构建镜像实战基于 buildx 的 x86/arm64 Linux 工具链镜像生成指南osquery CI 多架构 Docker 构建镜像实战基于 buildx 的 x86/arm64 Linux 工具链镜像生成指南 导读 osquery 作为观测代理网络安全5分钟上手多架构Docker镜像GitLab CIbuildx实战指南5分钟上手多架构Docker镜像GitLab CIbuildx实战指南 在容器化技术快速发展的今天构建支持多架构的Docker镜像已成为开发者的必备技能。云原生DevOps构建 Earthly CI 镜像为容器化 CI 定制内含 Earthly 的 Docker 镜像完整指南构建 Earthly CI 镜像为容器化 CI 定制内含 Earthly 的 Docker 镜像完整指南 导读 本指南面向需要在容器化 CI如 JenkinCI/CDDevOps构建工具创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考