首页
/ Ghost 开源仓库贡献工作流全指南:从本地开发环境到可合并的 Pull Request

Ghost 开源仓库贡献工作流全指南:从本地开发环境到可合并的 Pull Request

2026-09-07 14:07:08作者:邓越浪Henry

本文是 Ghost monorepo(根目录 package.json)贡献者路径的实战手册,系统讲解从"选一个 Issue"到"PR 通过 CI 合并进 main"的完整闭环,覆盖分支策略、pnpm check 一站式校验、Oxfmt 格式化、Changeset 发布意图记录、提交信息规范,以及维护者/外部贡献者两种分支发布方式。读完本文,你将掌握 Ghost 社区接受变更的全部质量门槛与操作命令,能够独立走完一次标准贡献流程。

本文的位置:开发文档的第二站

Ghost 的 docs 贡献者文档索引把贡献路径拆成了两段:先在 开发环境搭建指南 中把 Ghost Core 与前后端 watcher 跑起来,再按照本指南(docs/contributing/workflow.md)完成从写代码到提 PR 的收尾动作。与此同时,.github/CONTRIBUTING.md 提供精简版的规则摘要,而本文对应的完整工作流则面向仓库中 ghost/core(核心服务端)、apps/(Admin、Portal、Comments UI 等前端应用)、koenig/(Lexical 编辑器内核)与 packages/@tryghost/* 可发布包)这几大类工作区。

挑选合适的任务:从 Issue 标签到社区讨论

在动手前,仓库用两类 Issue 标签引导贡献者:

  • good first issue:面向首次贡献者的"上手友好"任务,改动范围可控、上下文清晰;
  • help wanted:项目欢迎的更大范围贡献清单。

两条规则决定了贡献的"自由度":

  1. 新功能与较大的产品/架构变更,应当先在 Ghost Forum 上与维护者充分讨论方案后再实现——这与 .github/CONTRIBUTING.md 中"generally don't merge new features and larger changes without prior discussion with the core product team"的原则互相印证;
  2. 聚焦的 bug 修复或已被认可的小改进,可以直接上手实现,无需前置讨论。

从最新的 main 起步:同步 + 建分支

任何贡献都要建立在干净的、与上游一致的 main 之上。在已克隆的 canonical 仓库中依次执行:

git fetch origin
git switch main
git pull --ff-only origin main
pnpm setup

git switch -c concise-change-name
  • git pull --ff-only 保证本地 main 只做快进式更新,避免产生意外的合并提交;
  • pnpm setup 不是普通的包安装:查根 package.json 可以看到它实际执行 pnpm install && git submodule update --init --recursive && git config --local blame.ignoreRevsFile .git-blame-ignore-revs,即一次性完成依赖安装、子模块初始化(Ghost 的默认主题等以 submodule 形式存在)与 git blame 忽略文件配置;
  • 分支名应当简短且描述变更意图(示例中的 concise-change-name),既便于维护者快速理解,也符合 Git 分支命名的可读性要求。

分支卫生的两条铁律:互不相关的改动必须放在不同分支、不同 PR 中;若较大的工作确需共享分支或发布分支,须先与维护者达成一致。普通 PR 一律以 main 为目标分支。

修改并验证:pnpm check 一站式质量门

行为发生变化时,必须同步新增或更新自动化测试。Ghost 是一个 monorepo,每个工作区(如 ghost/coreapps/admin)都有自己的 README.md,其中列有该区域专属的聚焦命令,改动时优先运行最贴合的那组命令,能显著缩短反馈回路。

改动完成、提交之前,在仓库根目录运行全仓的一站式检查:

pnpm check

从根 package.json 可以看到它的真实构成是三个连续步骤:

"check": "pnpm format:check && pnpm lint && pnpm test"

展开来看:

步骤 对应脚本 实际执行内容
格式检查 format:check oxfmt --check(只报告不写入)
静态检查 lint Nx 批量 lint + lint:boundaries(依赖边界)、lint:packageslint:docs(含 Markdown 链接校验)
单元测试 test Nx 批量 test排除 @tryghost/e2eghost-admin 两个项目

其中 test 的排除项非常关键——这正是文档强调"pnpm check 不包含浏览器 E2E 与 Ember Admin 测试"的源码出处:这两类套件代价高昂,需要在你改动的区域涉及它们时单独运行(如 pnpm test:e2e、对 ghost-admin 单独跑 nx run ghost-admin:test)。而 CI 侧(见 .github/workflows/ci.yml)通过 Nx affected 图 + 路径过滤器自动挑选 PR 相关的 lint、unit、integration、acceptance、build 与 browser-test 任务,实现"只测改动影响面"的高效流水线。

格式化:全仓统一的 Oxfmt

Ghost 的源码格式由 Oxfmtoxc 工具链 的格式化器)统一接管,唯一配置在根目录 .oxfmtrc.json

{
  "$schema": "./node_modules/oxfmt/configuration_schema.json",
  "singleQuote": true,
  "embeddedLanguageFormatting": "off",
  "ignorePatterns": ["**/build/**", "**/coverage/**", "**/dist/**", "..."]
}

两个要点解释了大多数格式化差异的来源:singleQuote: true 表示在 Oxfmt 默认基础上改用单引号embeddedLanguageFormatting: "off"禁止改写字符串内嵌语言的内容,保证注释、模板字符串等内部文本永不被意外重排。ignorePatterns 覆盖了构建产物、fixtures、快照以及 Ember Admin、Koenig 编辑器等历史包袱较重的目录。

实际使用遵循"通常无事可做"的原则,因为质量门已前置到提交之前:

  • CI 拒绝任何未格式化代码(见 ci.yml 中 pnpm format:check 步骤);
  • pre-commit 钩子自动格式化暂存文件。查看 .lintstagedrc.cjs 可以发现它不只跑格式化——对 *.{js,ts,tsx,jsx,cjs} 会执行 scripts/format.js(Oxfmt)+ 按工作区归组的 ESLint;对 ghost/coreapps/* 的源码还会额外跑 dependency-cruiser 依赖边界检查;对 **/*.md 则叠加 markdownlint 与 remark 死链校验。

需要手动格式化时:

pnpm format path/to/file.ts   # 只格式化指定文件
pnpm format                    # 无参数时格式化整个仓库
pnpm format:check              # 只报告差异,不写入

另外两条纪律值得记住:不要为单个包添加格式化配置(根配置是唯一权威);为统一换用 Oxfmt 而做的一次性全仓重排提交被记录在 .git-blame-ignore-revs 中,GitHub 的 blame 视图会自动跳过它。若你的本地仓库还没配好,可以手动执行一次:

git config --local blame.ignoreRevsFile .git-blame-ignore-revs

(对全新 checkout,pnpm setup 已自动完成该配置。)

记录发布意图:pnpm change 与 Changeset

Ghost 会把 koenig/packages/ 下的 @tryghost/* 编辑器、适配器包发布到 npm。因此,任何影响可发布包的改动——包括改动该包所消费的 catalog 条目——都需要一个 changeset,以便包在发布时获得正确的版本号与 changelog 条目:

pnpm change

运行后选择 patch / minor / major 三档之一,判断依据是该包对外的兼容性影响:

版本档位 适用场景(按语义化版本约定)
patch 向后兼容的 bug 修复
minor 向后兼容的新功能/行为增强
major 破坏性变更、公共 API 移除

你在交互式提示中填写的 summary 会成为该包的 changelog 条目,所以要站在包使用者的视角描述变更结果,而不是罗列内部改动细节。生成的 changeset 文件落在仓库 .changeset/ 目录下(如 warm-hotels-make.md 这类随机命名文件,配合 ledger.yaml 记账)。

如果确实不需要发布——例如纯测试改动或内部工具链改动——就显式记录这一意图,而不是什么都不做:

pnpm change --bump none

pnpm change status 随时查看当前待处理的发布意图。这套规则由 CI 强制执行:ci.yml 中名为 Check app version bump 的 job 会运行 scripts/check-change.js,凡改动可发布包却缺少覆盖性 changeset 的 PR 都会被直接打回;而仓库内 Markdown(AGENTS.mdCLAUDE.md、changelog、包内 docs/)不影响发布行为,则无需 changeset。注意一个边界:包的 README.md 会随包一起发布,改它同样需要一次 release。

提交信息:让 main 可读、让发布说明可用

GitHub 上所有提交信息遵循 .github/CONTRIBUTING.md 中定义的规范格式:

<optional release-note emoji> <past-tense summary, at most 80 characters>

<optional issue relationship, or "no ref">

<why this change was made>

要点包括:

  • 摘要以 Fixed / Changed / Updated / Improved / Added / Removed / Reverted / Moved / Released / Bumped / Cleaned过去式动词开头,且不超过 80 字符;
  • 第二行保持空白;
  • 有关联 Issue 时,用 ref <issue URL>fixes <issue URL>closes <issue URL> 等受支持的关系表述并附 URL;显式写 no ref 表示"确无关联 Issue",也可以留空;
  • 正文解释 why——为什么做这个改动、为什么是现在、为什么采用这种方案,因为"改了什么"已经由 diff 体现了。

规范还定义了发布说明 emoji,仅当改动是对用户有意义的显著变更时才在摘要最前方添加,并以用户视角书写:

  • ✨ 新功能
  • 🎨 改进或变更
  • 🐛 Bug 修复
  • 💡 其他值得用户注意的变更
  • 🌐 翻译提交(仅凭该 emoji 不会被选入生成的发布说明)

仓库的 commit-msg 本地钩子(.github/hooks/commit-msg.bash)会对大部分违规形式给出警告而不阻断提交,但 refs ...ref: ... 这类非法形式会被强制修正为受支持的 ref ... 写法。

发布分支:维护者与外部贡献者两条路径

Ghost 仓库的一个特点是 任何人都可以直接 clone、运行和修改 canonical 仓库,无需先 fork(fork 只在准备提交 PR 时才需要)。发布方式取决于你是否拥有 TryGhost/Ghost 的推送权限。

维护者:直接推送

git push -u origin HEAD
gh pr create --base main

外部贡献者:fork + 独立 remote

在改动就绪后创建 fork,并利用 GitHub CLI 在现有 canonical checkout 上直接把 fork 挂成独立 remote:

gh repo fork --remote --remote-name fork
git push -u fork HEAD
gh pr create --repo TryGhost/Ghost --base main
  • --remote-name fork 将 fork 以名为 fork 的 remote 添加到本地,与 origin(canonical 仓库)区分开;
  • --repo TryGhost/Ghost 告诉 gh pr create PR 的目标仓库是 canonical 仓库而非本地 remote 推断出的地址。

使用 GitHub Web 或桌面客户端的等价流程同样可行。文档特别提醒一个现代协作场景下的安全细节:如果由工具或编码 Agent 代为执行上述步骤,推送前务必核对目标 remote、分支与 PR 对象,确认无误后再授权。

打开 Pull Request:内容与协作规范

除维护者指定其他基准分支外,PR 一律以 main 为基准。参考 .github/PULL_REQUEST_TEMPLATE.md 的检查项,PR 描述应说清四件事:

  • 为什么需要这个变更
  • 行为或契约发生了什么变化(包括破坏性影响);
  • 它是如何被测试的(PR 模板要求"编写自动化测试证明改动有效");
  • 任何兼容性、发布、数据库迁移或灰度上线方面的考量

配套的协作要求:有关联 Issue 时在 PR 中链接它;可见的 UI 改动应附截图;如被要求则保持分支与 main 同步;用追加的新提交而非强推来响应评审意见(在 .github/CONTRIBUTING.md 中被明确为"we may ask you to rebase"的补充);CI 全绿是合并的前置条件,而与改动路径无关的任务被跳过属于预期现象,不必担心。

一次标准贡献的完整时间线

把以上各节串起来,一次符合 Ghost 社区预期的贡献大致是:

  1. 开发环境搭建指南 中完成本地环境(pnpm setup + pnpm dev)并确认站点与 Admin 可访问;
  2. good first issue / help wanted 选题,较大改动先在论坛对齐方案;
  3. git fetch origin && git switch main && git pull --ff-only origin main && pnpm setup,然后 git switch -c concise-change-name
  4. 实现改动并补充/更新测试,运行工作区 README 建议的聚焦命令;
  5. 提交前运行 pnpm check(format:check + lint + test)确保本地全绿,必要时单独跑 E2E 或 Ember Admin 测试;
  6. 若改动涉及 koenig/packages/ 下的可发布包,运行 pnpm change(或显式 pnpm change --bump none)并提交 changeset;
  7. 遵循 commit message 规范 提交(pre-commit 钩子会代劳格式化与部分 lint);
  8. 维护者 git push -u origin HEAD、外部贡献者 gh repo fork --remote --remote-name fork && git push -u fork HEAD,随后 gh pr create --base main 并填写完整描述;
  9. 响应评审、保持分支同步,等待 CI 全绿后由维护者合并。

至此,一条从本地开发环境通向 main 的完整贡献链路便打通了——这既是文档定义的标准流程,也是每次 CI 与钩子都在强制执行的仓库纪律。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
898
5.82 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
921
1.84 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.8 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
531
596
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.02 K
519
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
391