release

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Releasing nurb

nurb 发布流程

One release ships everything under one version: the PyPI package, the signed DMG, the in-app update feed, and the site changelog. This skill runs the whole ceremony. The one human act left in it is merging the bump PR, and that merge is the user approving the release.
一次发布将所有内容统一为同一版本:PyPI包、签名DMG文件、应用内更新源以及网站变更日志。此流程负责整个发布环节。唯一需要人工操作的是合并版本更新PR,该合并操作即代表用户批准发布。

Step 0: Preflight

步骤0:预检查

All of these must pass before touching anything. If one fails, say which and stop.
bash
gh auth status
test -f desktop/.env
test -f ~/.tauri/nurb-desktop.key
security find-identity -v -p codesigning | grep -q "Developer ID Application"
git fetch origin main
The updater key check matters most: without
~/.tauri/nurb-desktop.key
shipped apps cannot update, and generating a fresh key would strand every existing install. If it is missing, stop and tell the user; never regenerate it.
Start from up-to-date origin/main. One PR is the release: if the current branch has work in flight, the bump and changelog ride that branch and its PR becomes the release PR. Only cut a fresh branch when the workspace is clean and the release is just collecting already-merged work.
在进行任何操作前,必须确保以下所有检查通过。若有一项失败,告知用户具体项并停止操作。
bash
gh auth status
test -f desktop/.env
test -f ~/.tauri/nurb-desktop.key
security find-identity -v -p codesigning | grep -q "Developer ID Application"
git fetch origin main
更新器密钥检查最为关键:若缺少
~/.tauri/nurb-desktop.key
,已发布的应用将无法更新,且生成新密钥会导致所有现有安装版本无法继续更新。如果密钥缺失,请停止操作并告知用户;切勿重新生成。
从最新的origin/main分支开始操作。发布仅对应一个PR:若当前分支有正在进行的工作,则版本更新和变更日志将基于该分支,且该分支的PR将成为发布PR。仅当工作区干净且发布仅汇总已合并的工作内容时,才新建分支。

Step 1: Pick the version

步骤1:选择版本号

If the user named a version, use it. Otherwise decide and propose one: read what's merged since the last release, recommend a minor bump for new capabilities and a patch for fixes-and-polish-only, and say why in one line. The PR is the proposal; merging it is the user's yes.
bash
gh release list --limit 5
gh pr list --state merged --base main --limit 50 --json title,mergedAt
若用户指定了版本号,则使用该版本号。否则自行决定并提议一个版本号:查看自上一版本以来合并的内容,若有新功能则建议小版本更新,若仅包含修复和优化则建议补丁版本更新,并以一句话说明原因。PR即为提议,合并PR即表示用户同意该版本。
bash
gh release list --limit 5
gh pr list --state merged --base main --limit 50 --json title,mergedAt

Step 2: The release PR

步骤2:创建发布PR

Four version strings move together, and tests enforce every pairing:
bash
uv version X.Y.Z
then the
version:
frontmatter line in
src/nurb/skill.md
and
skills/nurb/SKILL.md
, then
version
in
desktop/src-tauri/tauri.conf.json
.
The bump also stales
evals/uv.lock
, because evals is its own uv project with nurb as an editable path dependency and CI syncs it with
--locked
. Relock it and commit the one-line change with the bump:
bash
cd evals && uv lock && cd ..
Then write the changelog into the same PR: run /changelog for the pending version. Pre-merge there is no tag or GitHub release yet, so it draws from the PRs merged since the last release plus this branch's own changes, dated today.
Prove the agreement before pushing:
uv run pytest tests/test_cli.py -q
. Commit the bump and the changelog together with a plain-sentence message, push, and open the PR against main with the release summary as the body (what shipped, in user-visible terms). Then stop and hand the merge to the user.
四处版本字符串需同步更新,且测试会验证所有配对是否一致:
bash
uv version X.Y.Z
随后更新
src/nurb/skill.md
skills/nurb/SKILL.md
中的
version:
前置行,以及
desktop/src-tauri/tauri.conf.json
中的
version
字段。
版本更新还会使
evals/uv.lock
失效,因为evals是独立的uv项目,且将nurb作为可编辑路径依赖,CI会通过
--locked
参数同步依赖。重新生成锁文件,并将该单行变更与版本更新一同提交:
bash
cd evals && uv lock && cd ..
然后将变更日志写入同一PR:针对待发布版本运行/changelog命令。合并前尚未创建标签或GitHub发布,因此变更日志将基于自上一版本以来合并的PR以及当前分支的自有更改,日期标注为今日。
推送前需验证一致性:
uv run pytest tests/test_cli.py -q
。将版本更新和变更日志一同提交,使用简洁的句子作为提交信息,推送分支后向main分支创建PR,以发布摘要(用户可见的发布内容)作为PR正文。随后停止操作,等待用户合并PR。

Step 3: After the merge

步骤3:合并后的操作

publish.yml reacts to the merge on its own: PyPI upload, tag
vX.Y.Z
, GitHub release with generated notes. Do not wait for it; start the desktop half immediately, because it builds while publish.yml runs and then waits for the release before uploading:
bash
cd desktop && scripts/release.sh
A fresh worktree has no
desktop/node_modules
, and the script dies immediately with
tauri: command not found
. Run
npm ci
in
desktop/
first if it is missing.
About ten minutes: signed build, notarization, stapling, chain verification, upload of
nurb.dmg
plus the updater archive into the
vX.Y.Z
release, and the
desktop-latest
feed refresh. It refuses to double-upload, so re-running after a failure is safe. It needs this Mac; the signing cert and updater key live here by design.
publish.yml会自动响应合并操作:上传至PyPI、创建
vX.Y.Z
标签、生成发布说明并创建GitHub发布。无需等待该流程完成,立即启动桌面应用部分的构建,因为该部分会在publish.yml运行时进行构建,之后等待发布完成再上传:
bash
cd desktop && scripts/release.sh
新的工作树中没有
desktop/node_modules
,脚本会立即报错
tauri: command not found
。若缺少该目录,请先在
desktop/
目录下运行
npm ci
整个过程约需十分钟:签名构建、公证、装订、链验证、将
nurb.dmg
以及更新器归档文件上传至
vX.Y.Z
发布版本,并刷新
desktop-latest
更新源。该脚本不会重复上传,因此失败后重新运行是安全的。此操作需在该Mac设备上进行,因为签名证书和更新器密钥默认存储在此设备。

Step 4: Verify, then report

步骤4:验证并汇报

Three probes, all of which must say X.Y.Z (the DMG check must return a redirect or 200):
bash
curl -sfI https://github.com/Shpigford/nurb/releases/latest/download/nurb.dmg | head -1
curl -sfL https://github.com/Shpigford/nurb/releases/download/desktop-latest/latest.json | python3 -c "import json,sys; print(json.load(sys.stdin)['version'])"
curl -sf https://pypi.org/pypi/nurb/json | python3 -c "import json,sys; print(json.load(sys.stdin)['info']['version'])"
Report with the release URL and what is now true: package users get the new version from
nurb update
and the viewer's nudge, desktop users see the one-click update at next launch, and the site's changelog names what changed. If any probe disagrees, say which channel is not live yet instead of calling the release done.
进行三项验证,结果均需显示为X.Y.Z(DMG检查需返回重定向或200状态码):
bash
curl -sfI https://github.com/Shpigford/nurb/releases/latest/download/nurb.dmg | head -1
curl -sfL https://github.com/Shpigford/nurb/releases/download/desktop-latest/latest.json | python3 -c "import json,sys; print(json.load(sys.stdin)['version'])"
curl -sf https://pypi.org/pypi/nurb/json | python3 -c "import json,sys; print(json.load(sys.stdin)['info']['version'])"
汇报时需包含发布URL以及当前状态:包用户可通过
nurb update
和应用内提示获取新版本,桌面应用用户下次启动时将看到一键更新选项,网站变更日志已更新发布内容。若任何一项验证不一致,需告知用户哪个渠道尚未上线,而非宣布发布完成。