상황
작은 서비스의 새 버전을 내려고 메인 브랜치를 올리고 릴리스 태그를 푸시했다. 보고는 "태그 푸시, 워크플로 검증 중"이었다. 네 시간쯤 뒤 "지금 최신 릴리스가 돌고 있느냐"는 질문을 받고서야 확인했다. 레지스트리에는 새 버전이 없었고, 릴리스 페이지도 없었다. 저장소 안의 package.json은 여전히 이전 버전 번호를 들고 있었고, 레지스트리는 이미 존재하는 버전이라며 발행을 거부했다.
왜 놓쳤나
태그를 푸시하는 순간 일을 넘긴 것처럼 느꼈다. 릴리스 워크플로가 나머지를 처리할 테니, 다음 확인은 워크플로가 해 줄 거라고 생각했다. 그런데 워크플로가 실패하면 알려 줄 사람이 없었다. "워크플로가 돌고 있다"는 문장은 결과가 아니라, 다음 차례의 나에게 넘기는 숙제였다. 그 숙제를 아무도 받지 않았다.
태그 전: 버전 번호를 맞춘다
태그를 만들기 전에 발행할 모든 패키지의 version 필드를 읽는다. 모노레포라면 패키지가 여럿이고, 하나만 올리고 나머지를 잊기 쉽다. 태그 이름에서 앞의 v를 뗀 값과 각 version이 정확히 같아야 한다. 하나라도 다르면 태그를 만들지 않는다. 이 비교는 사람이 눈으로 하는 것보다 릴리스 스크립트나 CI 첫 단계에 넣어 두는 편이 낫다. 맞지 않으면 빌드도 하기 전에 멈추게 한다.
태그 후: 결과를 직접 읽는다
릴리스는 태그를 푸시했을 때가 아니라, 레지스트리가 새 버전을 보여 주고 릴리스 페이지가 존재할 때 끝난다. 레지스트리에 해당 패키지의 최신 버전을 묻는 명령 하나, 릴리스 페이지를 여는 요청 하나면 된다. 둘 다 새 버전을 가리키면 그때 "릴리스됨"이라고 보고한다.
백그라운드 작업에도 같은 규칙
"CI가 끝나면 백그라운드 작업이 병합하고 다시 태그한다" 같은 약속도 똑같다. 그 작업은 조용히 죽을 수 있다. 결과가 늦어질 때까지 기다리지 말고, 다음 차례에 작업이 아직 살아 있는지 먼저 확인한다. 죽어 있으면 손으로 이어서 하고, 그 사실을 보고한다.
확인 방법
릴리스 보고를 쓰기 전에 세 가지를 같은 자리에서 확인한다. 발행한 모든 패키지의 version이 태그와 같은가? 레지스트리가 그 버전을 보여 주는가? 릴리스 페이지가 존재하는가? 셋 중 하나라도 확인하지 못했다면, 그 보고는 "진행 중"이지 "완료"가 아니다.
The situation
To ship a new version of a small service, I moved the main branch forward and pushed a release tag. My report was "tag pushed, workflow verifying". About four hours later someone asked whether the latest release was actually running, and only then did I look. The registry had no new version and there was no release page. The package.json files in the repository still carried the previous version number, so the registry rejected the publish as a version that already existed.
Why I missed it
Pushing the tag felt like handing the work off. The release workflow would do the rest, so I assumed the next check belonged to the workflow. But when the workflow failed, nobody was there to say so. "The workflow is running" was not a result. It was homework for my next turn, and nobody picked it up.
Before the tag: line up the version numbers
Before creating the tag, read the version field of every package you are about to publish. In a monorepo there are several, and it is easy to bump one and forget the rest. Each version must equal the tag name with the leading v removed. If any of them differs, do not create the tag. This comparison is better done in the release script or the first CI step than by eye, so a mismatch stops the run before anything is built.
After the tag: read the result yourself
A release is finished when the registry shows the new version and the release page exists, not when the tag is pushed. That takes one command asking the registry for the package's latest version and one request for the release page. When both point at the new version, report it as released.
The same rule for background jobs
A promise like "a background job will merge after CI and re-tag" works the same way. That job can die without a sound. Do not wait until its result is overdue; on your next turn, check first that the job is still alive. If it is dead, finish the steps by hand and say that you did.
How to check
Before writing a release report, confirm three things in one place. Does every published package's version equal the tag? Does the registry show that version? Does the release page exist? If you could not confirm any one of them, the report says "in progress", not "done".
情况
为了发布一个小服务的新版本,我把主分支往前推了一步,并推送了发布标签。我的报告是“标签已推送,工作流正在验证”。大约四个小时后,有人问最新的版本是否真的在运行,我这才去看。注册表里没有新版本,也没有发布页面。仓库里的 package.json 仍然写着上一个版本号,所以注册表以“该版本已存在”为由拒绝了发布。
为什么漏掉了
推送标签的那一刻,我感觉工作已经交出去了。发布工作流会处理剩下的事,所以我以为下一步检查是工作流的事。可是工作流失败时,没有人来告诉我。“工作流正在运行”不是结果,而是留给下一轮自己的作业,而这份作业没有人接手。
打标签之前:对齐版本号
创建标签之前,读一遍每个要发布的包的 version 字段。在 monorepo 里包有好几个,很容易只改了一个而忘了其他的。每个 version 都必须与去掉开头 v 的标签名完全一致。只要有一个不同,就不要创建标签。这个比较最好放进发布脚本或 CI 的第一步,而不是靠肉眼,这样不一致时在构建之前就会停下。
打标签之后:亲自读结果
发布完成的标志是注册表显示了新版本、发布页面已经存在,而不是标签已经推送。这只需要一条向注册表查询该包最新版本的命令,加上一次打开发布页面的请求。两者都指向新版本时,再报告“已发布”。
后台任务也适用同一条规则
“CI 结束后后台任务会合并并重新打标签”这样的承诺也是一样。那个任务可能悄无声息地死掉。不要等到结果迟迟不来才去看;下一轮先确认任务是否还活着。如果已经死了,就手动把步骤做完,并说明是你手动完成的。
如何确认
写发布报告之前,在同一处确认三件事。每个已发布包的 version 是否等于标签?注册表是否显示了这个版本?发布页面是否存在?只要有一项没能确认,报告就应写“进行中”,而不是“已完成”。
状況
小さなサービスの新バージョンを出すために、メインブランチを進めてリリースタグをプッシュした。報告は「タグをプッシュ、ワークフローが検証中」だった。四時間ほど後に「最新リリースは本当に動いているのか」と聞かれて、ようやく確認した。レジストリに新バージョンはなく、リリースページもなかった。リポジトリの package.json は前のバージョン番号のままで、レジストリは既に存在するバージョンだとして公開を拒否していた。
なぜ見落としたか
タグをプッシュした瞬間に、仕事を手渡したような気になっていた。残りはリリースワークフローがやるのだから、次の確認もワークフローの役目だと思った。けれどワークフローが失敗しても、それを知らせてくれる人はいなかった。「ワークフローが走っている」は結果ではなく、次の自分への宿題だった。その宿題を受け取る人はいなかった。
タグの前に:バージョン番号をそろえる
タグを作る前に、公開するすべてのパッケージの version フィールドを読む。モノレポならパッケージは複数あり、一つだけ上げて残りを忘れやすい。各 version は、タグ名から先頭の v を取った値と完全に一致していなければならない。一つでも違えばタグは作らない。この比較は目で見るより、リリーススクリプトや CI の最初のステップに入れておくほうがいい。不一致ならビルドの前に止まるようにする。
タグの後に:結果を自分で読む
リリースが終わるのは、タグをプッシュしたときではなく、レジストリが新バージョンを示し、リリースページが存在するときだ。レジストリにそのパッケージの最新バージョンを問い合わせるコマンドが一つ、リリースページを開くリクエストが一つあれば足りる。両方が新バージョンを指したら「リリース済み」と報告する。
バックグラウンドジョブにも同じルール
「CI が終わったらバックグラウンドジョブがマージして再タグする」という約束も同じだ。そのジョブは音もなく死ぬことがある。結果が遅れるまで待たず、次の番でまずジョブがまだ生きているかを確かめる。死んでいたら手で続きをやり、そうしたことを報告する。
確認のしかた
リリース報告を書く前に、同じ場所で三つを確かめる。公開したすべてのパッケージの version はタグと一致しているか。レジストリはそのバージョンを示しているか。リリースページは存在するか。一つでも確かめられなかったなら、その報告は「完了」ではなく「進行中」だ。