免费获取学习方案
ARTICLE DETAIL

资讯详情

深耕编程基础知识与建站技术分享的一线实战洞察。

pnpm/pnpr 跨生态发布事务:一次事务同时发布 npm 包、Cargo crate 与 Python 发行版

pnpm/pnpr 跨生态发布事务:一次事务同时发布 npm 包、Cargo crate 与 Python 发行版 pnpm/pnpr 跨生态发布事务一次事务同时发布 npm 包、Cargo crate 与 Python 发行版【免费下载链接】pnpmFast, disk space efficient package manager项目地址: https://gitcode.com/gh_mirrors/pn/pnpm导读pnpm 仓库中的 pnpr多生态私有仓库与发布服务新增了一个跨生态发布能力通过PUT /-/pnpr/v0/publish端点一个请求即可在一个事务中同时发布 npm 包、Cargo crate、Python 发行版甚至 OCI 镜像并保证「要么全部发布成功要么一个都不发布」。本文以 .changeset/cross-ecosystem-publish-transaction.md 为核心结合 pnpr/crates/pnpr/src/server/batch.rs、pnpr/crates/pnpr/src/server/publishing.rs 等源码与端到端测试完整讲解该端点的事件类型、请求体格式、原子性保证、崩溃恢复机制与一致性问题帮助你理解并正确使用这一跨生态发布协议。一、背景pnpr 的多生态发布模型pnpr 是一个可以同时充当 npm、Cargocrate、PyPIPython与 OCI容器镜像注册表的服务。每个生态有各自的原生发布端点例如 npm 的PUT /:pkg、Cargo 的 crate 上传、PyPI 的 legacy 上传 API。在跨生态发布事务出现之前一个同时产出 npm 包、crate 和 wheel 的 monorepo 需要分多次、向不同端点发起发布请求任何一步失败都可能造成「npm 已发布、crate 未发布」的中间状态。changeset 描述的核心变更对应pnpm/pnpr: minor版本正是为解决这个问题pnpr 现在可以在单个事务中发布多个生态的包。新端点PUT /-/pnpr/v0/publish接收一个批处理batch其中每个条目entry都声明自己的ecosystem从而让一个同时产出 npm 包、crate 和 Python 发行版的 workspace 能够一次性把它们一起发布。端点被放置在 pnpr 自己的命名空间下而非任何生态前缀下。正如 batch.rs 模块注释所述npm 表面已有自己的批处理端点/-/pnpm/v1/publish但它的地址属于 npm 表面而一个同时携带 crate 和 npm 包的批处理需要一个「不属于任何单一生态」的地址因此这个端点位于 pnpr 自身命名空间中与resolve等端点并列。对应路由注册见 routing.rs// 一个事务发布任意生态的包。它在这里应答而不是在 npm 表面内部。 let mut router router .route(/-/pnpr/v0/publish, put(batch::serve_ecosystem_publish)) .route(/-/pnpr/v0/registries, get(super::registry_directory::serve));二、请求体格式packages数组与ecosystem字段端点接收一个 JSON 对象包含一个名为packages的数组。官方 READMEpnpr/crates/pnpr/README.md给出的完整示例{ packages: [ { name: acme/ui, versions: {}, _attachments: {} }, { ecosystem: cargo, metadata: { name: acme, vers: 0.1.0 }, archive: base64 .crate }, { ecosystem: pypi, name: acme, version: 0.1.0, filetype: bdist_wheel, filename: acme-0.1.0-py3-none-any.whl, content: base64 wheel } ] }要点每个条目通过ecosystem字段声明自己所属生态。源码 entry_ecosystem() 定义了判定规则ecosystem缺失或为null时条目被当作 npm 发布文档——这正是 changeset 所述「An entry without anecosystemis an npm publish document, the same onePUT /-/pnpm/v1/publishtakes」其余值必须能解析为合法的生态名否则返回400 Bad Requestreason 为unknown ecosystem ... in packages。二进制部分crate 归档、wheel 文件、OCI manifest统一base64 编码后放入 JSON 字段这与 npm 发布文档中_attachments[].data的既有约定一致。packages数组不能为空body 必须是带packages数组的对象否则返回 400见 publish_batch()。各生态条目的字段从 batch.rs 的Deserialize结构与 validate_entry() 可以看出各生态条目的具体形态ecosystem关键字段二进制载体说明缺失 /nullnpm与PUT /:pkg相同的完整 packumentname、versions、_attachments等_attachments中的 base64 数据等价于 npm 批处理端点接受的文档cargometadataname、vers、deps、features、license等PublishMetadata字段archivearchive字段base64 编码的.crate归档与原生端点从二进制框架读取的元数据文档相同pypiname、version、filetype、filename可选requires_python、sha256_digestcontent字段base64 编码的文件字节与 legacy 上传表单的拼写一致ociname、reference、manifest可选content_typemanifest字段base64 编码content_type可选manifest 大小受http.oci.max_manifest_bytes配置约束三、执行流程先验证后暂存最后单事务提交跨生态发布的原子性不是靠「同时写所有包」实现的而是靠「先全部验证与暂存再一次性提交」实现的。源码 publish_batch() 展示了完整调用链解析与剥壳解析 JSON body 后立即drop(body)避免一份可能接近请求体上限的内容在内存中保留多份拷贝逐条验证validate_batch_entries→validate_entry在持有任何包锁之前尽可能完成所有可做的检查——每个条目的负载可解析、调用者有权发布该包、字节内容与条目声明一致crate 归档经verify_crate_archive校验、PyPI 上传经verify_upload校验、npm 文档经validate_publish_doc校验。同一批次内同名同生态的包出现两次会被拒绝返回 400reason 为duplicate ecosystem package ...因为每个包会经历一次「读取-合并-写入」同一包出现两次会让第二个条目的合并依赖第一个未提交的结果而不同生态的同名包互不冲突见 validate_batch_entries() 的注释说明加锁对批次涉及的所有包名调用state.inner.locks.packages.lock_many(names)持有全部锁贯穿整个暂存与提交过程从而与单包发布一样与并发的同包写入者串行化逐条暂存stage每个生态的条目调用各自的stagenpm 走stage_publishCargo/PyPI/OCI 走各自 publication 的stage把 tarball/crate/wheel 等写入临时槽位tmp slot此时对读者不可见。若某一条暂存失败则对已暂存的条目逐一执行cleanup_tmp_slots清理临时文件然后返回错误——任何失败都不会留下新的版本单事务提交commit_publishes把所有暂存条目组装为JournaledPublish条目列表交给state.inner.storage.publish_journal().commit(...)一次性提交到发布日志。commit_publishes在 publishing.rs 中实现其注释明确说明了事务语义「一次崩溃或 I/O 失败发生在提交中途绝不可能让批处理处于部分发布状态因为启动恢复会应用 seal 已提交的任何内容」并且「批处理可能混合多个生态每个包的文档按它自己表面所属的规则合并」。四、原子性保证与崩溃恢复changeset 的核心承诺可以归纳为三点均有源码与文档依据失败的批处理不发布任何东西所有条目在写入前完成授权与校验tarball 全部写入临时槽位后才对读者可见。READMEpnpr/crates/pnpr/README.md与 batch.rs 的模块注释都明确一个跨生态的发布要么整体落地要么什么都不留下。中途崩溃的服务器在下次启动时完成发布提交是单一的日志事务single journal transaction。启动恢复机制会应用日志中已提交的内容因此被中断的发布不会停留在半发布状态详见 publishing.rs 的注释与commit_publishes对publish_journal().commit的调用。结果保证而非实时一致性这是最容易误解的一点。事务的提交过程是逐包推进的——事务会一个包一个包地 promote 并记录因此在提交过程中恰好发起的读取可能看到该发布的一部分而看不到其余部分batch.rs 的模块注释对此有专门说明「That is a guarantee about outcomes, not about what a reader sees while it happens」。也就是说原子性承诺的是「最终状态要么全有要么全无」而不是「提交期间任何时刻的读视图都完整」。五、一个不可撤销的边界blob 槽位冲突源码明确承认存在一种无法整体回滚的情况某个 blob 的不可变槽位已被其他写入者占有。由于该槽位中的字节是别人已经发布的成果事务不会去撤销别人的发布而是记录其余所有条目并报告那个「失败」的包——以409响应呈现其余发布保留。README 的表述是「If another writer has already published one of the files, that package is left out and reported with409, and the rest of the release stays: the bytes that won the slot are someone elses published release.」见 pnpr/crates/pnpr/README.md对应的错误报告逻辑在 report_unrecorded()提交结果中unrecorded与lost_blobs两类未记录包会被合并、按「生态 包名」去重后拼接为PublishNotRecorded错误返回。注意这里每个包都带生态名因为两个生态中的同名包是两个不同的包。另外如果某版本无法声明 digest 引用槽位也会被同样地丢弃并报告。六、与 npm 批处理端点/-/pnpm/v1/publish的关系pnpr 中有两个批处理端点需要区分清楚PUT /-/pnpm/v1/publishnpm 专属批处理端点。body 为{packages: [发布文档, ...]}每个条目正是PUT /:pkg接受的 JSON body含_attachments。pnpm publish --batch发送此请求它不是标准 npm registry API 的一部分见 publishing.rs 的文档注释与 routing.rs 的路由注册。它挂在 npm 表面下因此在多生态配置下也响应于/npm/前缀之下。PUT /-/pnpr/v0/publish本文主角跨生态批处理端点。由于批处理「不属于任何单一生态」它位于 pnpr 自身命名空间。一个没有ecosystem字段的 npm 发布文档就是/-/pnpm/v1/publish接受的文档——这意味着 npm-only 的批处理 body 天然就是跨生态端点的合法 body向后兼容。此外端点能力会通过GET /-/pnpr广告为publish: [0]见 pnpr/crates/pnpr/README.md。七、测试验证端到端集成测试仓库提供了专门的端到端集成测试 pnpr/crates/pnpr/tests/cross_ecosystem_publish.rs静态模式、无上游保持测试封闭其中publishes_a_package_a_crate_and_a_wheel_in_one_transaction测试完整演示了三种生态在一个事务中发布的真实行为构造一个同时服务 npm、Cargo认领demo与 PyPI认领demo-pkg三个生态的静态配置tri_ecosystem_config见 cross_ecosystem_publish.rs组装npm_entry完整 packument _attachments、cargo_entrymetadata base64 的.crate归档归档内含Cargo.toml与src/lib.rs、pypi_entrywheel 字节 sha256_digest放入packages数组以 Bearer token 向/-/pnpr/v0/publish发送PUT断言返回201 Created随后分别通过GET /npm/mixed-pkg读取 npm packumentdist-tags.latest 1.0.0与 tarball、通过GET /cargo/index/de/mo/demo读取 Cargo 稀疏索引校验vers与cksum、通过GET /cargo/api/v1/crates/demo/0.1.0/download下载归档逐一验证三个生态的产物都已真实可读。同一测试文件还覆盖了事务失败不回滚、重复发布、未授权访问、批处理中重复包名等边界场景可作为实现契约的行为级文档来阅读。八、相关文件索引.changeset/cross-ecosystem-publish-transaction.md — 本次 minor 变更的 changeset 声明pnpr/crates/pnpr/src/server/batch.rs — 跨生态发布端点的核心实现解析、验证、暂存、提交编排pnpr/crates/pnpr/src/server/publishing.rs — 单包与批量发布的暂存/提交基础设施、journal 事务与错误报告pnpr/crates/pnpr/src/server/routing.rs — 端点与各生态表面的路由注册pnpr/crates/pnpr/README.md — 跨生态发布的使用文档与 JSON 示例pnpr/crates/pnpr/tests/cross_ecosystem_publish.rs — 三生态单事务发布的端到端集成测试pnpr/crates/pnpr/tests/batch_publish.rs — npm 专属批处理端点/-/pnpm/v1/publish的集成测试小结PUT /-/pnpr/v0/publish把「一次发布跨越多个生态」变成了一个具有明确事务语义的协议条目级ecosystem声明、无声明即 npm 的兼容设计、先全量验证暂存再单日志事务提交的执行模型以及「崩溃后下次启动完成发布」「blob 槽位冲突时报告而非撤销」两条边界规则共同构成了一个对发布者友好、对存储一致性严格的跨生态发布事务。结合 batch.rs 的编排与 cross_ecosystem_publish.rs 的端到端测试你可以把这个端点作为多生态 monorepo 发布流水线的核心一次请求完成 npm 包、crate 与 wheel 的整体发布。【免费下载链接】pnpmFast, disk space efficient package manager项目地址: https://gitcode.com/gh_mirrors/pn/pnpm创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表