Files
newgrok/CONTRIBUTING.md
iceBear67 62bd461356
CI / test (push) Canceled after 0s
CI / grok-linux-amd64 (push) Canceled after 0s
CI / grok-windows-amd64 (push) Canceled after 0s
CI / grok-macos-arm64 (push) Canceled after 0s
CI / release (push) Canceled after 0s
update readme
2026-08-16 03:26:51 +00:00

9.3 KiB
Raw Permalink Blame History

协作与开发工作流

本仓库是 xai-org/grok-build 的 patch 化 fork。fork 多了哪些能力见 README.md;这里只讲怎么改、怎么同步、怎么不把自己的活弄丢。

以 CraftBukkit / Spigot 的模型,在不接受外部 PR 的上游之上维护本地改动。

为什么是 patch,而不是 fork 后直接改

上游有两个决定性特征:

  1. 它是 xAI 内部 monorepo 的周期性单向导出。历史是一串线性的 Synced from monorepo 压扁提交,根目录 SOURCE_REV 记录对应的内部 commit。上游随时会把整棵树重新投放一次。
  2. 上游 CONTRIBUTING.md 明确写着不接受任何外部 PR 或补丁

也就是说:改动推不回去,而上游会不断整体覆盖。直接在 fork 上改,每次同步都要打一场 merge 混战,而且没人说得清「我们到底改了什么」。

这正是 CraftBukkit 面对 Mojang 的处境。解法也一样——把改动本身作为版本库里的唯一真相

patches/ 是源码,work/ 是编译产物。

目录结构

newgrok/                 ← 你提交的仓库
├── patches/             ★ 唯一真相:0001-*.patch,一个补丁一件事
├── scripts/             工作流脚本
├── upstream.rev         钉住的上游 commit
├── Makefile
├── upstream/            派生:上游的纯净 clone     .gitignore
└── work/                派生:upstream@rev + patches.gitignore

对照 Spigot

这里 Spigot / BuildTools
upstream/ CraftBukkit 的 clone(纯净上游)
work/ 打完补丁的工作目录
patches/ CraftBukkit-Patches/
scripts/apply-patches.sh applyPatches.sh
scripts/rebuild-patches.sh rebuildPatches.sh
upstream.rev versions/*.json 里的版本钉

upstream/work/不进 git——它们任何时候都能从 upstream.rev + patches/ 一字不差地重建。

两条规则,违反任意一条都会默默丢掉工作成果:

  1. Rust 源码改动写在 work/,不要手改 patches/ 补丁文件是生成物,手改之后下次就合不上。
  2. make rebuild 是离开 work/ 的唯一出口。 make applygit reset --hard + git clean -fdx。没 commit 并且没 export 的改动,下次 apply 就没了。

日常循环

改代码永远在 work/ 里,一个提交 = 一个补丁

make apply                                   # 拿到干净的、打好补丁的树
$EDITOR work/crates/codegen/.../foo.rs       # 改
cd work && git add -A && git commit -m '...' # 提交(提交信息就是补丁标题)
cd .. && make rebuild                        # 写回 patches/
git add patches/ && git commit -m '...'      # 提交到 fork 仓库

make rebuild 是唯一的出口。 没跑过它的改动,下次 make apply 就没了。

修改已有的补丁,用普通的 git 手段就行——work/ 是个正常的 git 仓库,base 标签是上游和我们的分界:

cd work
git rebase -i base        # 改写、合并、重排、删除任意补丁
cd .. && make rebuild     # patches/ 会整体重新生成

随时看当前状态:

make status

跟进上游同步

make update-dry   # 只看会拉进来什么,不动任何东西
make update       # 重新钉 upstream.rev,并把补丁前移到新基线

make update 会:

  1. git fetch 上游,列出 upstream.rev..origin/main 之间的新提交和 SOURCE_REV 变化
  2. upstream.rev 更新为最新
  3. 在新基线上重放 patches/
  4. 干净通过则自动 make rebuild,让补丁上下文对齐新代码

冲突时脚本会停下并给出解决步骤——和处理 rebase 冲突完全一样:

cd work
git status                 # 看冲突文件
$EDITOR <冲突文件>          # 消掉冲突标记
git add -A
git am --continue          # 或 git am --skip / git am --abort
cd .. && make rebuild      # 把前移后的结果写回去

补丁数量多的时候,冲突是这个模型的固有成本,也是它的价值所在:冲突精确地指出上游改动 撞上了你的哪一处改动,而不是让它悄悄消失。

全部命令

从仓库根目录跑 makemake help 列出全部目标)。脚本自己把 ~/.cargo/bin 加进 PATH,交互 shell 里不必先有 cargo

命令 作用
make setup 安装工具链(rustup / dotslash / protoc
make apply upstream.rev + patches/ → work/;加 FORCE=1 丢弃 work/ 里的未提交改动
make rebuild work/base 之上的提交 → patches/
make update 跟进最新上游同步并前移补丁
make update-dry 预览上游差异,不做修改
make build / make release 编译 debug / release 二进制
make check / make clippy / make fmt 快速类型检查 / lint / 格式化
make test 跑测试(PKG=<crate> 指定 crate
make run 编译并启动 TUI
make status 显示当前钉住的 rev、补丁数、work/ 状态
make clean work/target
make distclean work/upstream/(不动 patches/

make build 之后的额外参数会透传给 cargo,例如 ./scripts/build.sh --release --locked

跑单测。 make test 是薄封装;要带 filter,直接走 build.sh,多出来的参数会原样转给 cargo

CARGO_CMD=test PKG=xai-grok-version ./scripts/build.sh test_fork_tag
CARGO_CMD=test PKG=xai-grok-pager-bin ./scripts/build.sh version_output_writer -- --nocapture

安全网

脚本刻意不会静默吞掉工作成果:

  • work/未提交改动时,make apply 拒绝执行(提示先 commit + rebuild,或 FORCE=1
  • work/ 的提交数和 patches/ 的文件数对不上时同样拒绝——这基本意味着你忘了 make rebuild。故意增删补丁文件时也会触发,检查分不清这两种情况
  • git am 进行到一半时,make rebuild / make build 会拒绝执行,而不是产出半成品
  • FORCE=1(即 make apply FORCE=1)是两条 apply 护栏的逃生口,意思是「丢掉 work/,按现在的 patches/ 重放」
  • make applygit clean 显式保留 work/target,否则每次打补丁都要付一次冷编译的代价

补丁格式

rebuild 用的是:

git format-patch --no-stat -N --zero-commit --full-index --no-signature
  • --zero-commit —— 否则每次 rebase 后每个补丁的 From <sha> 行都会变,git diff 里全是噪音
  • --full-index —— 给 git am --3way 留下按 blob 哈希回退的余地,上游漂移时更容易自动合上

构建依赖

make setup 会处理,这里说明它在做什么:

  • Rust 1.94.0 —— 由上游 rust-toolchain.toml 钉住,rustup 自动装
  • protoc —— 只有 xai-grok-tools-api 需要(编译 proto/grok-tools.proto)。上游的解析顺序是 $PROTOC → 向上查找 bin/protocPATHbin/protoc 是个 DotSlash wrapper,会按需下载 protoc 29.3;所以 setup.sh 装 dotslash,失败则回退到系统 protobuf-compiler

两个 git 依赖(our-forks/async-openaihelix-editor/nucleo)都是公开可访问的。

在这台机器上(4 核 / 7 GB),xai-grok-pager-bin 冷编译 debug 大约 11 分钟,work/target 会长到约 24 GB,二进制大约 612 MB。cargo checkcargo build 各有一份缓存,所以 make build 之后再 make check 并不快(冷跑大约 6 分钟)。不到真的缺盘,别跑 make clean / make distclean。迭代时尽量用单 cratebuild.sh 总会带 -p)。

上游树里该改哪

work/ 里大约 91 个 workspace member。常动的几个:

Crate 角色
xai-grok-pager-bin 组合根,产出 xai-grok-pager 二进制(发行名 grok
xai-grok-pager TUIscrollback、prompt、modal、渲染;用户手册也在这里
xai-grok-shell Agent runtime + leader / stdio / headless 入口
xai-grok-tools 工具实现(terminal、文件编辑、搜索……)
xai-grok-workspace 宿主文件系统、VCS、执行、checkpoint

从上游继承的约束:

  • 根目录 Cargo.toml 是生成的,当只读。 改各 crate 自己的 Cargo.toml。它还带着 [patch.crates-io] 里对 our-forks/async-openai 的 git pin。
  • 永远指定 crate-p <crate>)。 全 workspace 编译慢到不现实;build.sh 会强制带 -p
  • 动上游代码时,先看它已有的测试还成不成立。

示例补丁

patches/00010002 的提交信息标了 [EXAMPLE PATCH],演示「改上游 Rust」和「新增文件」两种补丁,不需要就可以删。0001 同时也是 fork 品牌标记(--version 打出 [newgrok]),删之前想清楚。

删补丁文件要带 FORCE=1——work/ 里还留着对应提交,删完之后 patches/work/ 少,安全检查会拦下来(它无法区分「你故意删的」和「你忘了 rebuild」):

rm patches/0001-*.patch patches/0002-*.patch
make apply FORCE=1

许可

上游代码为 Apache-2.0(见 work/LICENSE)。patches/ 里的改动同样按 Apache-2.0 分发。 本仓库是非官方 fork,与 xAI 无关。