Skip to content

Latest commit

 

History

History
183 lines (137 loc) · 8.16 KB

File metadata and controls

183 lines (137 loc) · 8.16 KB

11 —— 发布一个库到 mcpp-index

English | 简体中文

读者: 希望自己的包能被别人写进 [dependencies] 的库作者。

本章回答的那一个问题: 从打好一个 tag 到成为一个可解析的包,要走过哪些步骤, 以及它们必须按什么顺序发生。

不在这里: 什么使两个包成为同一个包 —— SPEC-001; 以及交付编译好的产物,那是 12。在此之前: 10 —— 打包一个应用。

一个库如何变成 [dependencies] 能够写出来的东西。这是库作者的链路; 92 —— 发布 mcpp 讲的是发布 mcpp 自身, 10 —— 打包与发布 讲的是 mcpp pack 打包一个应用。

发布顺序

the library repo          merge → git tag → GitHub auto-generates the tag tarball
      ↓
gitcode mirror     a byte-identical copy, for the CN region
      ↓
mcpplibs/mcpp-index   pkgs/<x>/<name>.lua — GLOBAL + CN URLs + sha256
      ↓            publish-artifact.yml pushes a content-hash artifact
      ↓
consumers          bump the version in their mcpp.toml

每一支箭头都是一道关。漏掉任何一道都不会当场报错,而是在几小时后,以别人构建里 一句 dependency not found 或一个 404 的形式出现。

1. 打 tag

mcpp.toml 里的版本必须与 tag 一致。GitHub 自动生成 archive/refs/tags/<tag>.tar.gz,那个 tarball 就是产物,不需要另行上传。

git tag 0.0.48 && git push origin 0.0.48
curl -fsSL -o pkg-0.0.48.tar.gz \
  https://github.com/<owner>/<repo>/archive/refs/tags/0.0.48.tar.gz
sha256sum pkg-0.0.48.tar.gz          # ← the digest the index will carry

tarball 解开后是 <repo>-<tag>/,mcpp 在这层包装目录里查找 mcpp.toml。仓库自带 mcpp.toml 时,索引条目不需要 mcpp 字段。

2. 镜像到 gitcode

CN 条目必须是 GitHub tarball 的逐字节拷贝,只改文件名。否则两个区域对同一个 sha256 的理解就不一致。

gtc release publish mcpp-res/<name> --tag 0.0.48 --asset pkg-0.0.48.tar.gz

随后验证它,因为上传报告成功,与资源真的能取到,不是同一回事:

# GET, never HEAD — gitcode answers HEAD with 401 and GET with 302 → CDN 200
curl -fsSL -o cn.tar.gz \
  https://gitcode.com/mcpp-res/<name>/releases/download/0.0.48/pkg-0.0.48.tar.gz
cmp cn.tar.gz pkg-0.0.48.tar.gz      # must be identical, not merely present

3. 添加索引条目

在 mcpplibs/mcpp-index 的 pkgs/<首字母>/<name>.lua 中:

["0.0.48"] = {
    url = {
        GLOBAL = "https://github.com/<owner>/<repo>/archive/refs/tags/0.0.48.tar.gz",
        CN     = "https://gitcode.com/mcpp-res/<name>/releases/download/0.0.48/pkg-0.0.48.tar.gz",
    },
    sha256 = "<the digest from step 1>",
},

三个平台块都要写 —— linux、macosx、windows。源码 tarball 在每个平台上是 同样的字节;只写在其中一个平台块里,会在另外两个平台上以 no such version 失败, 读起来像是消费方 manifest 里打错了字。

4. 等待 artifact

索引是一个 artifact,不是一次 git clone。 合进 main 还不够:必须等 publish-artifact.yml 跑完并推出一个内容哈希 artifact,客户端之上还叠着一层刷新 TTL。手改缓存里的 pkgs/** 不起任何作用。

gh run list --repo mcpplibs/mcpp-index --workflow publish-artifact.yml --limit 1
rm -rf ~/.mcpp/registry/data/<namespace>    # force a client refresh

5. 从一次冷解析验证,再升消费方

这一步的意义在于:本地那份库的 checkout 会掩盖上面每一步的错误。要像一个陌生人 那样解析它:

rm -rf ~/.mcpp/registry/data/xpkgs/<ns>-x-<name>/0.0.48
mcpp build                            # must download and compile 0.0.48

只有到这时,才去升消费方的 [dependencies]。

不要只用 find ~/.mcpp/registry -mindepth 1 -maxdepth 1 ! -name data -exec rm -rf {} + 来强制刷新。data/xpkgs 位于第 2 层且名字不是 data,再来一次粗心的清理就会把 整个 payload 仓(约 800 MB 的工具链)一并删掉。恢复办法是先用 mcpp self doctor 重新 provision,再 mcpp update。

针对尚未发布版本的测试

在上面这条链还没走完时,可以手工向 registry 播种,让消费方在库发布之前就能编译:

REG=~/.mcpp/registry/data/xpkgs/<ns>-x-<name>/0.0.48
mkdir -p "$REG"
git -C /path/to/library archive --format=tar --prefix=<repo>-0.0.48/ HEAD \
  | tar -x -C "$REG"
touch "$REG/.mcpp_ok"                 # the marker that says "resolved"
cp ../0.0.47/.xpkg.lua "$REG/.xpkg.lua"   # add a 0.0.48 entry to it

mcpp 的构建沙箱与网络隔离,file:// 和 http://127.0.0.1 形式的索引 URL 都取不到, 因此播种缓存是唯一可行的办法。

在相信「真的能用」之前,先删掉播种的那一份。 播种出来的 0.0.48 与已发布的 0.0.48 对构建而言毫无区别,而当发布实际上已悄悄失败时,留在那里的正是播种的那一份。

发布工作空间成员 (mcpp 2026.9.25.1+)

成员的清单可以把 version、[build] 标志与 x.workspace = true 条目交给工作空间 提供(07 §4.1)。两种发布方式都保留这一点。

  • 描述符指向仓库 tag tarball 中的成员(mcpp = "*/libs/http/mcpp.toml")。使用方 从 tarball 中读取成员的清单,并应用 tarball 所含的工作空间根,与通过 git 引用时 相同。清单按原样发布。
  • 在成员目录中执行 mcpp publish 或 mcpp emit xpkg。 归档只包含成员自己的目录, 因此其中的 mcpp.toml 被归一化:成员继承的值被写出,x.workspace = true 条目取得 解析后的来源,对兄弟成员的依赖以版本依赖发布。原样的清单以 mcpp.toml.orig 保留在 旁边,归一化后的文件同时写到 target/dist/<name>-<version>.mcpp.toml 供审阅。归档由 git 对象按提交的日期构建,两次运行产生相同的字节。不是工作空间成员的包与此前一样 原样归档。

兄弟依赖写出它发布时的版本:

[dependencies]
"acme.util" = { path = "../util", version = "0.3.0" }   # path 用于开发,version 用于使用方

mcpp publish 拒绝不带 version 的兄弟依赖,并给出该成员的版本和应写的一行。它同样 拒绝包外、且不是工作空间成员的 path 依赖,以及位于成员目录之外的继承头文件目录,因为 归档中二者都不存在。

需要版本下限的 manifest 键

多数 [build] 键在较旧的 mcpp 上会干净地降级:警告该键不受支持、忽略它,构建要么 照常成功,要么以一条清楚的信息失败。build_program_timeout 就属于这一类 —— 较旧的 mcpp 会回落到 600 秒的默认值,如果这个值太短,也会明确报出。

module_extensions 不属于这一类。 较旧的 mcpp 会警告并忽略它,随后把那些文件 当作普通翻译单元编译 —— 得到的是一个错误的构建,而不是一次干净的失败:模块 接口不产生 BMI,故障在更晚的地方浮现,报错既不点名这条键,也不点名那个文件。

使用 module_extensions 的已发布包,必须在其索引描述符中声明一个 mcpp 版本下限。 下限机制必须能够降级:因客户端过旧而不可用的包,必须被报告为不可用,绝不能 报告为不存在 —— 被告知「无此包」的客户端会不断刷新索引去找它。

检查清单

  • mcpp.toml 版本 == git tag
  • tag 已推送;tarball 可下载,sha256 已记录
  • gitcode 资源已用 GET 验证,且与 GitHub 那份逐字节一致
  • 索引条目写进了三个平台块
  • publish-artifact.yml 已成功
  • 冷解析(已删掉播种拷贝)能下载并编译
  • 消费方已升版本
  • 工作空间成员:已审阅归一化后的 target/dist/<name>-<version>.mcpp.toml