Files
newgrok/README.md
T
iceBear67andClaude Opus 5 618c8e31ee Set up CraftBukkit-style patch workflow for xai-org/grok-build
Upstream is a periodic one-way export of xAI's monorepo (linear
"Synced from monorepo" commits, SOURCE_REV pinning the internal SHA)
and explicitly refuses external contributions. So local changes can
never be upstreamed, and upstream re-drops the whole tree on every
sync -- structurally the same problem CraftBukkit has with Mojang.

Adopt the Spigot/BuildTools model: patches/ is the source of truth,
work/ is a disposable build artifact regenerated from upstream.rev
plus patches/.

  scripts/apply-patches.sh    ~ applyPatches.sh
  scripts/rebuild-patches.sh  ~ rebuildPatches.sh
  scripts/update-upstream.sh  forward-ports patches onto a new sync
  scripts/setup.sh            toolchain: rust 1.94.0, dotslash/protoc
  upstream.rev                ~ BuildTools versions/*.json pin

format-patch uses --zero-commit so rebases do not rewrite the From
line of every patch, and apply's git clean preserves work/target so
replaying patches does not cost a cold Rust rebuild.

Ships two [EXAMPLE PATCH] commits demonstrating a source edit and a
new-file addition; both are safe to delete.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 04:50:07 +00:00

189 lines
7.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# newgrok — `xai-org/grok-build` 的 patch 化开发工作流
以 CraftBukkit / Spigot 的模型,在**不接受外部 PR 的上游**之上维护本地改动。
## 为什么是 patch,而不是 fork 后直接改
上游 [`xai-org/grok-build`](https://github.com/xai-org/grok-build) 有两个决定性特征:
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/`
一字不差地重建。
## 快速开始
```sh
make setup # 装 Rust 工具链(1.94.0,由 rust-toolchain.toml 钉住)+ dotslash/protoc
make apply # 用 upstream.rev + patches/ 生成 work/
make build # 编译 work/target/debug/xai-grok-pager
make run # 直接启动 TUI
```
`make setup` 是幂等的,可以随时重跑。
> Rust 装在 `~/.cargo`,脚本会自己把它加进 `PATH`。若要在交互 shell 里直接用 `cargo`
> fish 执行 `source ~/.cargo/env.fish`bash 执行 `source ~/.cargo/env`。
## 日常循环
改代码永远在 `work/` 里,**一个提交 = 一个补丁**:
```sh
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 仓库:
```sh
cd work
git rebase -i base # 改写、合并、重排、删除任意补丁
cd .. && make rebuild # patches/ 会整体重新生成
```
随时看当前状态:
```sh
make status
```
## 跟进上游同步
```sh
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 冲突完全一样:
```sh
cd work
git status # 看冲突文件
$EDITOR <冲突文件> # 消掉冲突标记
git add -A
git am --continue # 或 git am --skip / git am --abort
cd .. && make rebuild # 把前移后的结果写回去
```
补丁数量多的时候,冲突是这个模型的固有成本,也是它的价值所在:冲突精确地指出上游改动
撞上了你的哪一处改动,而不是让它悄悄消失。
## 全部命令
| 命令 | 作用 |
|------|------|
| `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`
## 安全网
脚本刻意不会静默吞掉工作成果:
- `work/` 有**未提交改动**时,`make apply` 拒绝执行(提示先 commit + rebuild,或 `FORCE=1`
- `work/` 的提交数和 `patches/` 的文件数**对不上**时同样拒绝——这基本意味着你忘了 `make rebuild`
- `git am` 进行到一半时,`make rebuild` / `make build` 会拒绝执行,而不是产出半成品
- `make apply``git 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/protoc``PATH``bin/protoc` 是个
[DotSlash](https://dotslash-cli.com) wrapper,会按需下载 protoc 29.3;所以 `setup.sh`
装 dotslash,失败则回退到系统 `protobuf-compiler`
两个 git 依赖(`our-forks/async-openai``helix-editor/nucleo`)都是公开可访问的。
## 示例补丁
`patches/` 里预置了两个补丁,提交信息都标了 `[EXAMPLE PATCH]`
1. **`0001`** 改上游 Rust 源码——给 `xai-grok-version``FORK_NAME` / `fork_tag()`
并接进 pager 的版本输出,于是 `--version` 显示 `grok [newgrok] 1.0.3 (…)`
标记插在 `grok ` 之后而不是追加到末尾,因为 `main.rs`
`version_output_writer_preserves_channel_aware_contract` 断言该行仍以 channel label
(或 `)`)结尾——**改上游代码时顺手确认它的测试仍然成立**,这个补丁本身就是个例子。
2. **`0002`** 新增文件——根目录 `FORK.md`
不需要就直接删:
```sh
rm patches/0001-*.patch patches/0002-*.patch
make apply
```
## 许可
上游代码为 Apache-2.0(见 `work/LICENSE`)。`patches/` 里的改动同样按 Apache-2.0 分发。
本仓库是非官方 fork,与 xAI 无关。