npm publish が通った瞬間は気持ちよかった。
問題はその後で、GitHub Actions に移したとたんに3か所で詰まった。Trusted Publisher の設定、provenance まわりの metadata、tag と version の順序——それぞれ別の原因で、別の場所で落ちた。
同じところで詰まる人の時間を少しでも省けるよう、踏んだ失敗を時系列で残しておく。
なぜ公開したかったか
@hsblabs/web-stream-extras は Web Streams API まわりの補助ユーティリティをまとめた小さなパッケージだ。プロジェクト内部で使い回す想定で書いていたが、汎用性があると判断して OSS にした。
0.0.1 をローカルから手動 publish した理由
初回は Actions を組む前に「まずパッケージとして動くことを確認したい」という理由でローカルから npm publish した。
npm に上がった状態で install・import が通るかを確かめるのが目的で、release フローの整備は後回しにした。release が安定しない段階で自動化を先に組んでも、デバッグの層が増えるだけだと思っていて。
もう一つ理由がある。npmjs 側で Trusted Publisher を設定するには、対象パッケージがすでに npm に存在している必要がある。つまり初回だけは手動でも何らかの方法で publish しないと、そもそも設定画面に進めない。
0.1.0 で GitHub Actions + Trusted Publisher に寄せた構成
手動 publish は一度きりにして、以降は GitHub Actions から publish する構成に移した。
npm の Trusted Publisher は、token を発行・管理しなくても OIDC で publish できる仕組みだ。workflow が発行した short-lived なトークンを使うので、NPM_TOKEN をシークレットに持つ必要がない。
設定の核心は2点だ。npm 側では Trusted Publisher を追加するときに workflow のファイル名を指定する。GitHub Actions 側では id-token: write パーミッションを付け、NODE_AUTH_TOKEN は置かない。
permissions: id-token: write contents: readNODE_AUTH_TOKEN を残したままにすると、Trusted Publisher の OIDC フローと競合して publish が失敗することがある。token ベースの案内が README や TODO に残っていると混乱の元なので、移行時にまとめて消す。
実際に踏んだ失敗
422 Unprocessable Entity で publish が止まる
Actions の publish step だけが失敗した。エラーは 422 Unprocessable Entity。他のステップは通っているので、最初は原因の見当がつかなかった。
原因は package.json の repository.url が空だったことだ。provenance を有効にすると、npm 側でパッケージと repo を結びつけるために repository.url を参照する。ここが抜けていると 422 で弾かれる。
// 直す前"repository": {}
// 直した後"repository": { "type": "git", "url": "https://github.com/hsblabs/web-stream-extras"}他の metadata を先に確認していても、ここは見落としやすい。
tag と package.json.version の順序
workflow は tag 名と package.json.version が一致しているかを検証している。
最初に tag を打ってから package.json.version を上げようとすると、検証で落ちる。正しい順序はこうだ。
package.json.versionを上げて commit する- その commit に tag を打つ
- push する
順序が逆だと、tag が指す commit の version と tag 名がずれる。失敗したら tag を削除して付け直す。
git tag -d v0.1.0git push origin :refs/tags/v0.1.0# version を上げた commit の後でgit tag v0.1.0git push origin v0.1.0最終的に安定した release フロー
今の構成はこうなっている。
- 実装・テストを進める
package.json.versionを上げて commit するv{version}の tag を打って push する- Actions が tag push を検知して publish を実行する
publish 前の検証は publish:check コマンドにまとめた。lint や型チェックを --write なしで走らせるもので、これを publish step の前に置いておくと、未コミット変更が混入する事故を防げる。自動修正系のコマンドを直接つなぐと、publish 直前に未コミット変更を作ってしまうことがあるので注意が必要だ。
pnpm pack --dry-run で tarball の中身も確認した。dist/ だけを files に指定していても、sourcemap がそのまま乗ることがある。公開したくないなら build 側で切る方が確実だ。
次に改善したいこと
changelog の自動生成をフローに組み込みたいと思っている。今は手書きで、tag を打つたびに漏れが出やすい。
手動 publish から Actions に移す手順自体は単純で、詰まるのはほぼ設定の取り違えだ。Trusted Publisher は token の管理が不要になる分、OIDC まわりの設定を正確に揃える必要がある。npm 側の設定と workflow 側の設定が一対一で対応していることを確認してから動かすのが一番早い。
hsb.horse