React Spectrum 周度 API Diff 自动化基线快照构建、发布检测与差异追踪全解【免费下载链接】react-spectrumA collection of libraries and tools that help you build adaptive, accessible, and robust user experiences.项目地址: https://gitcode.com/GitHub_Trending/re/react-spectrum本文基于 react-spectrum 仓库中的 scripts/weekly-api-diff/README.md 展开完整讲解这套“周度 API Diff 自动化”的工作原理如何用 GitHub Actions 与 macOS launchd 两条路径每周自动对比main分支的 API 面与最近一次发布的基线计算周环比增量把快照提交到独立的 snapshots 仓库并通过 LLM 摘要推送到 Slack。读完后你能掌握 monorepo 中“发布基线 vs 开发分支”类型级差异对比的完整工具链以及一套可复刻的定时 API 变更播报方案。一、整体定位与文件清单这套自动化解决的问题是react-spectrum 是一个包含数百个包的 monorepo每周都有大量组件 props 与 hook 签名在变化团队需要一份“相对最近一次发布版本main 分支上还挂着哪些未发布的 API 变更”的清单并在每周固定时间把变化推送给 Slack。仓库中该功能的文件布局如下完整清单来自 README文件位置作用GitHub Actions workflow.github/workflows/weekly-api-diff.yml主自动化在 GitHub 基础设施上每周一 9am PT 运行Prompt唯一事实来源scripts/weekly-api-diff/prompt.md本地 launchd 回退方案中 Claude 的指令修改后需同步到~/weekly-tsdiffer.mdPrompt实际生效副本~/weekly-tsdiffer.mdlaunchd 每次运行实际读取的文件launchd plist参考副本scripts/weekly-api-diff/launchd.plist本地回退方案的参考配置注释内含安装说明launchd plist实际生效副本~/Library/LaunchAgents/com.username.weekly-tsdiffer.plistmacOS 调度器实际读取的 plistSecrets本地~/.secrets含SLACK_TSDIFF_CHROMATIC_BOT_TOKENchmod 600严禁提交Snapshots 仓库本地~/dev/react-spectrum-api-snapshots存储每周 diff 文本仅本地回退方案使用Snapshots 仓库GitHubLFDanLu/react-spectrum-api-snapshots每周 diff 的公开存档运行日志/tmp/weekly-tsdiffer.log每次本地运行的 stdout/stderr错误日志/tmp/weekly-tsdiffer-error.logClaude prompt 中的步骤级错误记录两条执行路径共用同一套底层脚本区别只在于触发方式和汇总方式GitHub Actions主路径cron 定时触发用 GitHub Models 做摘要推 SlackmacOS launchd回退路径周一 9 点由本机调度触发直接调用claude -p执行一份自然语言 promptprompt.md逻辑与 Actions 完全对齐。二、GitHub Actions 主流程逐步骤解析Workflow 定义见 .github/workflows/weekly-api-diff.yml。README 中的 8 步流程与 workflow 中实际 steps 一一对应2.1 触发与检出on: schedule: - cron: 0 17 * * 1 # Monday 9am PST / 10am PDT (GH Actions cron is UTC) workflow_dispatch: # manual trigger for testing注意 GH Actions 的 cron 表达式按 UTC 计算0 17 * * 1即太平洋时间周一 9 点另外支持workflow_dispatch手动触发便于测试。checkout 步骤显式指定fetch-depth: 0注释说明原因build:api-published需要完整 git 历史来定位最后一次 Publish 提交。运行环境固定为ubuntu-latest Node 24 yarn cache。2.2 构建两侧 API 快照- run: yarn build:api-branch # 构建当前 main 的 API 快照注释称 CI 上约 2 分钟 - run: yarn build:api-published # 构建发布基线使用最后一次 minor/major 发布提交这两条脚本对应 package.json 中的定义build:api-published: node scripts/buildBranchAPI.js --githash$(git rev-list -n 1 $(git tag -l react-aria-components* | grep -E [0-9]\\.[0-9]\\.0$ | sort -V | tail -1)) --outputbase-api, build:api-branch: node scripts/buildBranchAPI.js, compare:apis: node scripts/compareAPIs.js从命令可以读出“发布基线”的自动检测逻辑列出所有react-aria-componentsx.y.0形式的 tag只保留 minor/major用grep -E [0-9]\\.[0-9]\\.0$过滤掉 patch 版本按版本排序取最新一个再通过git rev-list -n 1找到该 tag 指向的提交把这个提交作为--githash传给快照构建脚本。这就是 README 中“auto-detects last Publish commit”的具体实现。2.3 生成 diff 并计算周环比- name: Generate diff run: yarn compare:apis --isCI /tmp/diff-current.md || true--isCI控制输出为纯 Markdown不加终端彩色|| true表示即使比较脚本认为“无变更”也允许流程继续。随后 workflow 用actions/checkoutv4把 snapshots 仓库检出到snapshots/目录使用SNAPSHOTS_REPO_TOKEN并在 “Save diff and compute delta” 这一步完成发布检测CURRENT_PUBLISH取git log --grep^Publish$ --oneline -1的第一个字段即最近一个消息恰为Publish的提交短哈希与 snapshots 仓库里的last-publish-hash.txtPREV_PUBLISH比较minor/major 与 patch 区分若两者不同再用git tag --points-at $CURRENT_PUBLISH检查该 Publish 提交上是否有react-spectrum/s2x.y.0或react-aria-componentsx.y.0形式的 tag——有则判定为 minor/major 发布NEW_RELEASEtrue重置基线无则判定为 patch 发布只更新哈希、继续走周环比逻辑周环比增量对 snapshots 仓库diffs/下按字典序最大的历史 diffls ... | sort -r | head -1按文件名日期排序而非 mtime执行diff $PREV /tmp/diff-current.md /tmp/weekly-delta.txt得到“本周相对上周”的变化提交决策仅当/tmp/diff-current.md非空且NEW_RELEASEtrue或 delta 非空时才提交diffs/$TODAY.md同时会把 delta 加工成 snapshots/deltas/$TODAY.md——用grep ^ /grep ^ 把 diff-of-diffs 拆成两段带标题的纯文本“本周新增的 API 变更”与“本周已随发布消失的 API 变更”workflow 注释说明这是为了“可以直接喂给模型”。最后执行git diff --cached --quiet || (git commit -m weekly api diff $TODAY git push)保证无变化时不产生空提交。2.4 汇总与四种 Slack 消息README 第 8 步说明汇总阶段会基于以下状态发四种消息之一场景消息内容diff 为空相对发布版本无待发布变更release 已消费全部改动“No API changes detected vs last release...”NEW_RELEASEtrue上次 diff 之后出现了新发布提示新发布落地链接指向新基线的完整 diff周环比 delta 为空与上周 diff 相同“No new API changes since last diff (PREV_DATE): PREV_URL”正常本周有增量变化由 LLMGitHub Models对周环比 delta 生成的摘要三、快照构建与 API 对比的源码实现上面三条 yarn 脚本是整个自动化中最有技术含量的部分值得结合源码看细节。3.1 buildBranchAPI.js在隔离的临时 workspace 中生成 api.jsonscripts/buildBranchAPI.js 的职责其文件头注释原文通过.parcelrc中的apiCheckpipeline 运行文档构建器为每个包生成“可见对外暴露类型定义”的 JSON。核心流程可选的历史提交检出若传入--githashbuild:api-published场景执行git archive hash | tar -x -C tempdir把该提交的源码树解包到临时目录作为快照源不传则直接以当前工作树为源合成一个最小 workspace在tempy临时目录中生成新的package.jsonworkspaces覆盖packages/*/*等devDependencies 只保留根 package.json 中parcel前缀、parcel、patch-package、postcss、react等少量依赖buildBranchAPI.js使安装尽量快且只从 npm 拉外部依赖重写每个子包 manifest复制packages/下的包排除spectrum-css、example-theme、dev/等后删除main/module/devDependencies等字段删除指向 workspace 内部包的dependencies避免兄弟包因版本 pin 不匹配而去 npm 安装并统一注入两个关键字段buildBranchAPI.jsjson.apiCheck dist/api.json; json.targets { apiCheck: {} };这两个字段就是 Parcel 构建入口每个包都会以apiChecktarget 构建产物落到dist/api.json。执行构建yarn parcel build packages/react-aria-components packages/react-{spectrum,aria,stately}/* packages/internationalized/{message,string,date,number} --target apiCheck最后把packages/拷回dist/branch-api/--output参数可改为base-api删除临时目录。其中真正生成类型 JSON 的是 parcel-transformer-docs 转换管线——仓库根 .parcelrc 中有一条规则apiCheck:*.{js,ts,tsx,json}: [parcel-transformer-docs]即凡是被apiChecktarget 处理到的 TS/JS/JSON 文件都走 docs transformer其核心逻辑与文档站类型渲染器dev/docs中的types.js一致把 TypeScript 类型系统还原为带exports/links结构的 JSON。3.2 buildPublishedAPI.js从 npm 拉取“已发布世界”scripts/buildPublishedAPI.js 与 branch 版本同构但数据源完全不同——它构建的是“用户当前从 npm 上装到的 API 面”遍历本仓库所有包对非 private 且存在于 npm且至少有一个非nightly版本见 buildPublishedAPI.js 的npm view name versions检查的包注入pkg.dependencies[name] latest在临时目录yarn install后把node_modules中这些已发布包的实际源码移到packages/下参与构建buildPublishedAPI.js同样注入apiCheck字段并删除dist/再yarn constraints --fix建立内部链接最终用同一套 parcel 命令构建产物落到dist/base-api/。也就是说branch 快照 本仓库当前代码的类型面base 快照 npm 最新发布版本的类型面两者用同一管线生成保证结构可比。3.3 compareAPIs.js接口重建、依赖图与可读 diffscripts/compareAPIs.js 的头部注释概括得很清楚读取两侧构建出的api.json重建接口、建立接口间依赖图、对重建结果做 diff并利用依赖图说明“某个接口是因为它的依赖变了而连带变化”。关键机制配对策略先以 published 侧为基准按包名在 branch 侧找同名dist/api.jsonbranch 侧存在但 published 侧没有、且package.json非private的视为“即将发布的新包”同样纳入对比compareAPIs.js接口重建rebuildInterfaces把 JSON 中每个 export 还原为按字母序排列的伪源码文本props 逐条排序、含 optional/默认值同时用processType把类型树union、intersection、application、function、object 等十余种节点递归渲染为可读字符串遇到link节点时记录dependantOnLinks依赖边compareAPIs.jsdiff 与传播分析对每对接口用Diff.structuredPatch计算 hunkfollowDependencies找出“该接口因哪些已变更的依赖而变化”输出changed by:段落invertDependenciesfollowInvertedDependencies反向找出“该接口变化会影响哪些下游接口”输出it changed:段落--isCI模式diff 包裹进diff代码块受影响接口列表改用detailssummaryit changed/summary.../details折叠块compareAPIs.js避免长清单刷屏——周度自动化正是用这一模式把输出直接重定向为/tmp/diff-current.md降噪normalizeDefault会把默认值的引号风格、逗号/冒号空格统一注释说明是为了抹平“oxlint 格式化”引入的伪差异避免格式差异污染周度 diff。输出按包分组每个有变化的包输出### 包路径其下每个变化的接口输出#### 包:导出名加 diff 文本这正是 snapshots 仓库diffs/里存档的文档格式。另外package.json 还定义了两个便捷脚本方便本地手动复核同一套管线check-apis: yarn build:api-branch --githash\origin/main\ --output\base-api\ yarn build:api-branch yarn compare:apis, check-published-apis: yarn build:api-published yarn build:api-branch yarn compare:apis四、本地 launchd 回退路径当不想依赖 GH Actions例如想在本机跑、或 Actions 资源不足时README 提供了 macOS launchd 回退方案三步流程launchd 每周一 9 点触发笔记本睡眠后会补跑执行claude -p $(cat ~/weekly-tsdiffer.md)带 bash/read 权限Claude 按照 prompt.md 中“与 GH Actions workflow 相同”的步骤顺序执行。4.1 prompt.md 的九步工作流prompt.md 是一份完整的可执行剧本要求“按顺序执行所有步骤、不要提前停止、某步失败则记录到/tmp/weekly-tsdiffer-error.log并尽量继续”。配置段声明了仓库路径、snapshots 仓库路径、Slack 通道与环境变量名。九步要点date %Y-%m-%d取TODAYgit checkout main git pull origin mainyarn build:api-branchprompt 注明耗时 10–30 分钟产物在dist/branch-api/若dist/base-api/已有内容则跳过否则yarn build:api-published同样 10–30 分钟yarn compare:apis --isCI | tee /tmp/diff-current.txt——特别强调只捕获 stdout不加21避免 yarn 的 stderr 混入 diff 文件发布检测与周环比git log --grep^Publish$ --oneline -1 | awk {print $1}得CURRENT_PUBLISH与 snapshots 仓库last-publish-hash.txt的PREV_PUBLISH比较若不同则NEW_RELEASEtrue直接进下一步否则取diffs/中字典序最大的历史文件diff出WEEKLY_DELTA首次运行则记为 “(first run, no previous diff to compare against)”提交决策与 workflow 的[[ -s file ]]逻辑一致diff 为空则跳过提交NEW_RELEASEtrue且 diff 非空、或WEEKLY_DELTA非空且 diff 非空则提交diffs/$TODAY.txt与更新后的last-publish-hash.txt其余情况跳过摘要规则按四种 case 选择消息与第二节 2.4 的四类一致且对正常 case 定义了明确的分组与分类规则——delta 是“diff 的 diff”整个组件区块带前缀只表示“该组件这周开始相对基线有变化”不代表组件本身是新增只有 diff 中出现 ComponentName这种新导出行才能称为新组件同一族组件如 Checkbox、Radio、Switch新增同一 prop如description时应合并为一个 feature 描述新包装组件如CheckboxField、RadioField与内部组件的新 prop 应合并描述其共同启用的能力如 “help text support”现有组件新增 prop 必须显式点出不能被“新增导出数量”淹没prop 签名变化如回调多一个参数要标记为潜在 breaking changeCalendar 家族Calendar、RangeCalendar、CalendarState、DateRangePicker习惯一起变应合并描述用curl调 Slackchat.postMessageBearer token 来自SLACK_TSDIFF_CHROMATIC_BOT_TOKEN并校验响应包含ok: true。4.2 launchd plist 参考配置scripts/weekly-api-diff/launchd.plist 中的关键配置keyProgramArguments/key array string/bin/zsh/string string-c/string stringsource $HOME/.nvm/nvm.sh amp;amp; source $HOME/.secrets amp;amp; claude -p $(cat $HOME/weekly-tsdiffer.md) --allowedTools Bash,Read --dangerously-skip-permissions/string /array keyStartCalendarInterval/key dict keyWeekday/keyinteger1/integer keyHour/keyinteger9/integer keyMinute/keyinteger0/integer /dict即每周一 9:00Weekday 1以 zsh 执行先 source nvm 与~/.secrets注入 Slack token再调用claude -p并把--allowedTools限定为Bash,Read。plist 注释里还给出 load/unload/kickstart手动试跑与看日志的命令。4.3 Prompt 更新流程与全新机器安装更新 prompt 需要三步README “Updating the Prompt” 一节编辑 scripts/weekly-api-diff/prompt.md → 提交到仓库 → 同步到生效位置cp scripts/weekly-api-diff/prompt.md ~/weekly-tsdiffer.md。全新 macOS 机器的完整安装步骤README “Local Fallback Setup” 一节原样保留以便直接执行# 1. Copy prompt to home dir cp scripts/weekly-api-diff/prompt.md ~/weekly-tsdiffer.md # 2. Install launchd plist (substitutes your macOS username into the Label) sed s/username/$USER/g scripts/weekly-api-diff/launchd.plist ~/Library/LaunchAgents/com.$USER.weekly-tsdiffer.plist launchctl bootout gui/$(id -u)/com.$USER.weekly-tsdiffer 2/dev/null || true launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.$USER.weekly-tsdiffer.plist # 3. Add Slack bot token to ~/.secrets (chmod 600) echo export SLACK_TSDIFF_CHROMATIC_BOT_TOKENxoxb-... ~/.secrets chmod 600 ~/.secrets # 4. Clone snapshots repo git clone https://github.com/LFDanLu/react-spectrum-api-snapshots ~/dev/react-spectrum-api-snapshots # 5. Build the release baseline (one-time, ~20 min) cd ~/dev/react-spectrum yarn build:api-published注意两点适用前提该回退方案依赖 macOSlaunchd 为 macOS 专属调度器与本机已安装claudeCLI发布基线是一次性构建约 20 分钟之后每周只需构建 branch 侧快照。五、GitHub Actions 所需 SecretsREADME 列出的三个 Actions secretsSecret说明SLACK_TSDIFF_CHROMATIC_BOT_TOKENSlack bot tokenSLACK_CHANNEL_ID目标 Slack 频道SNAPSHOTS_REPO_TOKEN对react-spectrum-api-snapshots仓库具有 Contents: readwrite 权限的 GitHub PAT其中前两者与本地回退方案的~/.secretschmod 600、绝不入库形成对照同一份敏感信息在两条路径上分别以“Actions Secrets”和“本地 shell profile”的方式注入。六、小结这套设计做对了什么从源码结构看这套周度 API Diff 自动化的工程价值集中在四个设计决策上两侧快照用同一管线生成branch 侧与 published 侧都走apiChecktarget parcel-transformer-docsapi.json结构完全一致diff 才具备类型级语义基线自动跟随 minor/major 发布build:api-published通过react-aria-componentsx.y.0tag 自动定位基线提交patch 发布则不重置基线保证“周度 diff”始终相对最近的正式版本diff-of-diffs 的增量语义对历史 diff 再做一次diff并把/两段加工成带标题的纯文本喂给模型使 LLM 摘要只针对“本周新增/本周消失”的少量行而不是每周重新总结全量双路径冗余 prompt 即文档prompt.md 同时是本地 Claude 的执行剧本和人类可读的操作手册九步流程与 Actions workflow 严格对齐任何一侧的逻辑变更都可以直接对照源码脚本buildBranchAPI.js、buildPublishedAPI.js、compareAPIs.js验证。如果要复用这套方案到其他 monorepo最小移植集为两个快照构建脚本含apiChecktarget 约定、compareAPIs.js的依赖图 diff、一个带日期文件名的 snapshots 仓库diffs/last-publish-hash.txtdeltas/以及 cron/launchd 二选一的触发器——其余细节tag 命名规则、发布检测方式按各项目的发布流水线调整即可。【免费下载链接】react-spectrumA collection of libraries and tools that help you build adaptive, accessible, and robust user experiences.项目地址: https://gitcode.com/GitHub_Trending/re/react-spectrum创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
