Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 32 additions & 19 deletions .agents/skills/mcpp-contributing/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ mcpp 项目的贡献流程:先创建 Issue → 切分支 → 实现改动 →

- 仓库:https://github.com/mcpp-community/mcpp
- 构建:`mcpp build`(C++23 模块自举)
- 测试:`tests/e2e/` 下的 bash 脚本
- CI:GitHub Actions,base 为 `main` 的 PR 自动触发
- 测试:`mcpp test` 覆盖 `tests/**/*.cpp`,`tests/e2e/` 提供真实二进制的端到端脚本
- CI:GitHub Actions,base 为 `main` 的 PR 触发分平台构建、测试与 E2E 检查

## 核心原则

Expand Down Expand Up @@ -126,20 +126,28 @@ git checkout -b <type>/<short-description>
**构建验证**

```bash
# 找到 mcpp 二进制
ls target/x86_64-linux-gnu/*/bin/mcpp
# 构建
<mcpp-binary> build
# 用现有 bootstrap mcpp 自举构建
mcpp build
# 选择刚生成的 target/**/bin/mcpp(Windows 为 mcpp.exe),不要硬编码宿主 triple
<fresh-mcpp-binary> --version
```

**测试**

```bash
bash tests/e2e/01_help_and_version.sh # 基础测试
bash tests/e2e/<relevant-test>.sh # 相关测试
# 新功能应创建对应 E2E 测试
# C++ 单元/集成测试:由刚构建的二进制发现 tests/**/*.cpp
<fresh-mcpp-binary> test
# 端到端测试:显式把刚构建的二进制交给脚本
# 路径必须是刚构建产物的绝对路径;Windows 使用 mcpp.exe。
MCPP=<absolute-path-to-fresh-mcpp-or-mcpp.exe> bash tests/e2e/01_help_and_version.sh
MCPP=<absolute-path-to-fresh-mcpp-or-mcpp.exe> bash tests/e2e/<relevant-test>.sh
# 新功能按变更契约补充 focused unit/integration 和/或 E2E 覆盖
```

E2E 并不保证完全离线:部分脚本需要工具链、索引或 capability provider。
按 CI 等价方式设置 `MCPP_HOME`、镜像和其他 capability 后再运行;不要让缓存命中
或空 workspace 选择冒充行为覆盖。

### 4. 提交 PR

**提交信息前缀**:`feat:` / `fix:` / `refactor:` / `test:` / `docs:` / `chore:`
Expand All @@ -154,8 +162,9 @@ gh pr create \
Closes #<issue>

## Test plan
- [ ] mcpp build 通过
- [ ] E2E 测试通过"
- [ ] 文档-only:示例与链接已按当前实现复核,无运行时行为变更
- [ ] 涉及行为或测试文档时:`mcpp test`(unit/integration)通过
- [ ] 涉及行为或测试文档时:相关 E2E 脚本使用 fresh `MCPP` 通过"
```

**PR 要求**:
Expand All @@ -173,14 +182,16 @@ gh pr checks <pr-number> # 查看状态
gh run view <run-id> --log-failed # 查看失败日志
```

CI 包含三个平台
CI 由分平台的基础构建/单元集成检查与独立 E2E 检查组成
| Workflow | 平台 | 内容 |
|----------|------|------|
| `ci` | Linux x86_64 | 自举构建 + E2E 测试 |
| `ci-macos` | macOS ARM64 | 自举构建 + E2E 测试 |
| `ci-windows` | Windows x86_64 | 自举构建 + E2E 测试 |
| `ci-linux` / `ci-linux-e2e` | Linux x86_64 | 自举构建、unit/integration / 分片 E2E |
| `ci-macos` / `ci-macos-e2e` | macOS ARM64 | 自举构建、unit/integration / E2E |
| `ci-windows` / `ci-windows-e2e` | Windows x86_64 | 自举构建、toolchain 回归 / E2E |
| `cross-build-test` | Linux/Windows cross targets | 交叉构建、产物运行与 MinGW/Wine 检查 |
| `ci-aarch64-fresh-install` | Linux ARM64 native | path-filtered fresh install、原生自举与 musl `build.mcpp` host-helper 回归 |

**三个平台全部通过才能合入。** 如果某个平台失败:
**以 PR 实际 required checks 为准,所有未跳过的 required checks 必须通过。** 如果某个平台失败:
1. 下载日志分析原因
2. 修复后 push 到同一分支,CI 自动重跑
3. 如果是 flaky test,在 PR 中说明
Expand Down Expand Up @@ -242,7 +253,7 @@ gh pr merge <pr-number> --merge
src/
├── cli.cppm ← 命令行入口
├── config.cppm ← 全局配置
├── manifest.cppm ← mcpp.toml 解析
├── manifest/ ← manifest 模型、TOML/xpkg 解析
├── platform/ ← 平台抽象层(所有平台相关代码)
│ ├── platform.cppm ← 统一外观模块
│ ├── common.cppm ← 平台常量与检测
Expand All @@ -260,7 +271,8 @@ src/
├── modgraph/ ← 模块图扫描验证
├── pack/ ← 打包发布
└── xlings.cppm ← xlings 抽象层
tests/e2e/ ← E2E 测试脚本
tests/unit/ ← C++ unit/integration tests (`mcpp test`)
tests/e2e/ ← E2E 测试脚本 (`MCPP=...` + `run_all.sh`)
docs/ ← 用户文档
.agents/docs/ ← 设计文档
.agents/skills/ ← Agent 技能文档
Expand All @@ -270,6 +282,7 @@ docs/ ← 用户文档

- C++23 模块项目,修改模块时注意 import 依赖顺序
- 平台相关代码统一放 `src/platform/`,不在其他模块中直接使用 `#if defined`
- E2E 测试应独立运行,不依赖网络
- E2E 测试应声明所需 capability,并使用隔离的 `MCPP_HOME`;需要网络/索引的脚本
不得被描述为完全离线
- 不确定方向时先在 Issue 讨论再动手
- **永远走 PR 流程,不直接 push main**
89 changes: 45 additions & 44 deletions .agents/skills/mcpp-release/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@ description: Use when releasing a new version of mcpp — bumps version, creates

## Overview

mcpp 的版本号存在于 **四个位置**,但它们分属**两组,在发布流程的两个不同时刻更新**。
把四处一起 bump 是一个会让全部 CI 变红的经典错误 —— 见下面第二组的解释。
mcpp 有 **三个持久化版本位置**,以及 `ci-fresh-install` 的一个运行时推导值。它们分属
两组,在不同时间更新;把"正在构建的版本"与 bootstrap pin 一起前移会让 CI 尝试安装
尚未发布的 mcpp。

**第一组:正在构建的版本**(发布时改,走 bump PR)

Expand All @@ -35,31 +36,31 @@ mcpp 的版本号存在于 **四个位置**,但它们分属**两组,在发
这两处必须**在同一个 commit 里**一起改:`tests/e2e/01_help_and_version.sh` 交叉比对
`mcpp.toml` 与 `mcpp --version`,只改一处 CI 立刻红。

**第二组:bootstrap pin —— CI 用哪个 mcpp 来自举**(发布并进索引之后才改
**第二组:bootstrap pin —— CI 用哪个 mcpp 来自举**(发布并进索引之后才可改

3. `.xlings.json` → `workspace.mcpp` — CI bootstrap 装哪个 mcpp
4. `.github/workflows/ci-fresh-install.yml` → `MCPP_PIN` — 全新安装验证的目标版本

这两处指向的是一个**已经发布、且已经进了索引**的版本。在 bump PR 里把它们一起挪到新版,
等于让每一个 CI job 去 `xlings install` 一个还不存在的 mcpp —— 全线红。
所以它们在 bump PR 里保持**上一个已发布版本**不动,直到发布收尾那一步才前移
(见「发布后的收尾」第 3 步)。`check_version_pins.sh` 正是按这个语义校验的:它只要求
两处 pin **彼此相等**、且**不得新于**正在构建的版本,并不要求等于它。
`.xlings.json` 必须指向一个**已经发布、镜像并进入索引**的版本。因此它在 bump PR 中
保持已有的可安装版本,直到发布收尾时才可前移。

对照最近一次发布:`fd27314`(bump 到 2026.7.29.1)只动了第一组两个文件,第二组仍停在
2026.7.28.2;`fde3b70` 才在发布、镜像、进索引之后把 pin 推到 2026.7.29.1。
`ci-fresh-install.yml` 的 `MCPP_PIN` 不是持久化 pin:`wait-index` 从最新 GitHub
Release 推导一次,所有安装 job 消费同一个输出。绝不能手工编辑或恢复字面量
`MCPP_PIN`,否则 index guard 和实际安装版本会再次漂移。

**版本不一致会导致 release smoke test 失败**(CI 检查 `mcpp --version` 是否匹配 tag)。
第二组历史上多次漂移(`MCPP_PIN` 曾落后五个版本),所以现在有机器校验:
`.github/tools/check_version_pins.sh` 校验版本关系和 xlings pin:

```bash
bash .github/tools/check_version_pins.sh
```

它同时校验第二组不变量:**`.github/` 下所有 xlings pin 必须等于 `src/xlings.cppm` 的
`pinned::kXlingsVersion`**(当前 16 个 pin 点、7 个文件,含 release.yml 里三处硬编码的
aarch64 tarball 字面量)。`kXlingsVersion` 是唯一真源,也是 release 打进
`<install>/registry/bin/xlings` 的那一份。改 xlings 版本只改常量,然后跑这个脚本找出其余落点。
**必须用 `bash` 跑,不能用 `sh`。** 脚本用了进程替换(`done < <(...)`),POSIX
`sh`/dash 解析不了,用 `sh` 调用会在第 95 行附近报 `Syntax error: redirection
unexpected`。那是调用它的 shell 的问题,不是脚本的缺陷 —— 它的 shebang 是
`#!/usr/bin/env bash`,CI 也是用 `bash` 调的。别据此把这条 guard 当成坏的而跳过:
它是唯一能机器化捕捉 pin 漂移的东西。

也不要通过修改文档或 workflow 绕开动态 `MCPP_PIN` 设计。`src/xlings.cppm` 的
`pinned::kXlingsVersion` 仍是 xlings 版本的唯一真源。

## 发布步骤

Expand All @@ -71,12 +72,15 @@ git checkout main && git pull origin main
gh run list --branch main --limit 3
```

所有 CI(ci / ci-macos / ci-windows)必须为 `success`。不要在 CI 红的时候发版。
以分支保护和 `gh pr checks <pr-number>` 显示的 actual required checks 为准。
在 main 上监控当前运行时,检查 `ci-linux`、`ci-linux-e2e`、`ci-macos`、
`ci-macos-e2e`、`ci-windows`、`ci-windows-e2e` 与 `cross-build-test` 的结果;
跳过或非 required 的 workflow 不是合入 gate。不要在 required CI 红的时候发版。

### 2. bump 版本号(第一组两处,单个 commit,走 PR)

**只改第一组的两个文件**,并且在同一个 commit 里。bootstrap pin(`.xlings.json`
`MCPP_PIN`)**不要动** —— 它们指向上一个已发布版本,见 Overview。
**只改第一组的两个文件**,并且在同一个 commit 里。`.xlings.json` bootstrap pin
**不要动**;`MCPP_PIN` 是 workflow 运行时推导值,绝不能手工编辑,见 Overview。

```bash
# 日期版本:当天序号从 .1 起;.0 仅用于正式/稳定版
Expand All @@ -87,7 +91,7 @@ git checkout -b "chore/bump-$NEW_VERSION"
sed -i "s/^version.*=.*/version = \"$NEW_VERSION\"/" mcpp.toml
sed -i "s/MCPP_VERSION = \".*\"/MCPP_VERSION = \"$NEW_VERSION\"/" src/toolchain/fingerprint.cppm

# 机器校验(building 是新版、bootstrap pin 仍是旧版,是预期状态)
# 校验:mcpp.toml 与 MCPP_VERSION 相等,.xlings.json 不领先于正在构建的版本。
bash .github/tools/check_version_pins.sh

# 自查:构建产物真的报新版本。注意 target/ 目录名带指纹哈希,
Expand Down Expand Up @@ -154,8 +158,8 @@ gh release view "v$NEW_VERSION"
- `install.sh`
- `SHA256SUMS`

**顺带核对体积**(2026.7.29.1 起,见下方"载荷瘦身"):linux 两个 tarball 应在
**5MB 上下**。如果又回到 30MB 量级,说明 strip 断言被绕过了,先查再发
同时比较 Linux 资产与最近一次成功 release 的体积。若出现明显回升,先确认
strip 和打包步骤的断言仍然执行,再继续发布

## Release CI 详解

Expand All @@ -170,20 +174,18 @@ gh release view "v$NEW_VERSION"
5. Linux: `mcpp self env` 中 MCPP_HOME 正确解析
6. xlings 二进制已捆绑

### 载荷瘦身(2026.7.29.1 起)
### 载荷瘦身

每个 linux 平台在**打包后、打 tar 前**调用 `.github/tools/slim_linux_payload.sh`,
strip `bin/mcpp` 与 `registry/bin/xlings` 并**断言结果**(`file` 不得再含
`not stripped`)。

为什么必须断言:在此之前,vendored 的 xlings 从来没被 strip 过(97.3MB,带
`debug_info`),而 x86_64 那句 `strip` 跑在 `mcpp pack` **之前** —— pack 会重建
二进制把它覆盖掉,于是直到 2026.7.28.2 发布的 `bin/mcpp` 一直是未 strip 的。
一个不校验效果的 `strip` 等于注释。修完 linux-x86_64 tarball 从 **34.81MB 降到
4.62MB(7.5×)**。
为什么必须断言:单独执行一次 `strip` 不足以证明最终 tarball 已变小,后续的
`mcpp pack` 可能重建并覆盖二进制。检查最终 payload 的 `file` 输出和资产体积,而非
依赖固定的 MB 数或历史发布大小。

macOS / Windows **故意不做**:载荷本来就 6.1MB / 4.2MB,且 strip Mach-O 会让
ad-hoc 签名失效
macOS / Windows **故意不做**:strip Mach-O 会让 ad-hoc 签名失效;按各平台的
release 规则验证最终资产,不要套用 Linux 的 strip 判断

### publish-ecosystem:镜像 + 索引(发布的后半程)

Expand Down Expand Up @@ -216,11 +218,10 @@ gh pr merge <n> --repo openxlings/xim-pkgindex --squash --admin
# 2) 真实验证(注意:不带 @版本 不会升级已装的旧版)
xlings update && xlings install mcpp@$NEW_VERSION -y

# 3) bootstrap pin 收尾 —— 第二组两处,到这一步才前移
# 新版此时已发布、已镜像、已进索引,CI 装得到,pin 才可以指向它
# 3) bootstrap pin 收尾 —— 仅 .xlings.json;新版此时已发布、已镜像、已进索引
sed -i "s/\"mcpp\": \"[^\"]*\"/\"mcpp\": \"$NEW_VERSION\"/" .xlings.json
sed -i "s/MCPP_PIN: '[^']*'/MCPP_PIN: '$NEW_VERSION'/" .github/workflows/ci-fresh-install.yml
bash .github/tools/check_version_pins.sh
# 不编辑 ci-fresh-install.yml 的 MCPP_PIN:它由 wait-index 运行时推导。
bash .github/tools/check_version_pins.sh # 复核 pin 关系
git commit -am "ci: workspace mcpp bootstrap pin -> $NEW_VERSION (released, mirrored, indexed)"
```

Expand Down Expand Up @@ -254,11 +255,11 @@ gh workflow run bump-formula.yml -R mcpp-community/homebrew-mcpp
| 症状 | 原因 | 修复 |
|------|------|------|
| `mcpp X.Y.Z-1` 但 tag 是 `vX.Y.Z` | `fingerprint.cppm` 版本未更新 | 更新 `MCPP_VERSION`,重新打 tag |
| bump PR 里**所有** CI job 都红在 bootstrap,报 `package 'mcpp@X.Y.Z' not found` | 把第二组的 bootstrap pin 也一起 bump 了,CI 去装一个还没发布的版本 | 把 `.xlings.json` / `MCPP_PIN` 回退到上一个已发布版本,发布收尾时再前移 |
| bump PR 里**所有** CI job 都红在 bootstrap,报 `package 'mcpp@X.Y.Z' not found` | 把 `.xlings.json` bootstrap pin 一起 bump 了,CI 去装一个还没发布的版本 | 把 `.xlings.json` 回退到上一个已发布版本;不要修改运行时推导的 `MCPP_PIN` |
| 自查 `--version` 显示旧版本,但源码已改 | `target/<triple>/<指纹>/` 的指纹随版本变,`ls \| head -1` 取到了上一次构建的目录 | 用 `ls -dt … \| head -1` 取最新构建 |
| Smoke test 输出旧版本 | CI 缓存了旧的 sandbox/target | 删除 GitHub Actions cache 后重跑 |
| e2e `01_help_and_version.sh` 挂 | 只改了 `mcpp.toml` 没改 `fingerprint.cppm`(它把两者交叉比对) | 同步四处版本;注意这个 e2e 只在部分分片里跑,可能表现为"只有某个平台红" |
| xlings bootstrap 失败 | xlings 版本不兼容 | 改 `src/xlings.cppm::kXlingsVersion`(**唯一真源**)后跑 `check_version_pins.sh` 找出其余 15 个 pin |
| e2e `01_help_and_version.sh` 挂 | 只改了 `mcpp.toml` 没改 `fingerprint.cppm`(它把两者交叉比对) | 同步两处正在构建的版本;注意这个 e2e 只在部分分片里跑,可能表现为"只有某个平台红" |
| xlings bootstrap 失败 | xlings 版本不兼容 | 改 `src/xlings.cppm::kXlingsVersion`(唯一真源),再核对引用它的 workflow 与脚本;当前 pin-check 脚本修复前不能依赖它完成扫描 |
| macOS/Windows 构建失败 | 需要等 Linux job 先完成 | 检查 Linux job 是否成功 |
| `slim: FAIL: ... still not stripped` | strip 工具没生效/被 pack 覆盖 | 别绕过断言——它就是为了拦住 34.8MB 的 tarball 再次发出去 |
| mirror leg 报 `missing/unverified` | 资产没传上去或还没传播 | 先 GET 核验(**必须 GET,`curl -I` 会骗你**),gitcode 用 `gitcode.com` 直链而非 `api.` 主机;确认缺件后本地补传再 `gh run rerun --failed`(脚本幂等,已验证的资产会跳过) |
Expand Down Expand Up @@ -310,9 +311,9 @@ gh workflow run release.yml --ref "v$NEW_VERSION"
| `mcpp.toml` | `version = "X.Y.Z"` — 项目版本,release.yml 由它推导 tag |
| `src/toolchain/fingerprint.cppm` | `MCPP_VERSION = "X.Y.Z"` — 编译期版本常量 |
| `.xlings.json` | `workspace.mcpp` — CI bootstrap 装哪个 mcpp(发布**后**才 bump) |
| `.github/workflows/ci-fresh-install.yml` | `MCPP_PIN` — 全新安装验证目标(发布**后**才 bump |
| `src/xlings.cppm` | `kXlingsVersion` — xlings pin 的**唯一真源**(其余 15 处由脚本校验) |
| `.github/tools/check_version_pins.sh` | 机器校验上述两组不变量,别靠肉眼 |
| `.github/workflows/ci-fresh-install.yml` | `MCPP_PIN` — 由 `wait-index` 从最新 release 推导,**从不手工 bump** |
| `src/xlings.cppm` | `kXlingsVersion` — xlings pin 的**唯一真源** |
| `.github/tools/check_version_pins.sh` | 版本/pin 校验 guard(**用 `bash` 跑,不能用 `sh`**) |
| `.github/tools/slim_linux_payload.sh` | linux 载荷 strip + 断言 |
| `.github/tools/mirror_res.sh` | 双端镜像(并发上传 + leg deadline + 完整性 gate) |
| `.github/tools/gtc` | GitCode CLI(release create/upload、PR) |
Expand All @@ -321,6 +322,6 @@ gh workflow run release.yml --ref "v$NEW_VERSION"
| `CHANGELOG.md` | Release notes 来源(按 `## [X.Y.Z]` 提取) |

> **注意版本 bump 的两个阶段**:`mcpp.toml` + `fingerprint.cppm` 在发版**前**改
> (它们定义要发什么);`.xlings.json` + `MCPP_PIN` 在发版**成功后**改(它们指向
> bootstrap 用哪个已发布版本)。`check_version_pins.sh` 认得这个差异,不会因为
> bootstrap pin 落后一版就报错
> (它们定义要发什么);`.xlings.json` 只在发版成功、镜像并进索引后才可更新
> (它指定 bootstrap 使用的已发布版本)。`MCPP_PIN` 是被测版本的运行时推导值,
> 不属于任何手工 bump 阶段。这些关系由 `bash .github/tools/check_version_pins.sh` 校验
Loading
Loading