logo hsb.horse
← ブログ一覧に戻る

ブログ

@hsblabs/web-stream-extras を npm publish するまでに詰まったところ全部

手動 publish から GitHub Actions + npm Trusted Publisher への移行で実際に踏んだ失敗を時系列でまとめた。provenance の 422、tag と version の順序、publish 前検証の整備まで。

公開日:

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: read

NODE_AUTH_TOKEN を残したままにすると、Trusted Publisher の OIDC フローと競合して publish が失敗することがある。token ベースの案内が README や TODO に残っていると混乱の元なので、移行時にまとめて消す。

実際に踏んだ失敗

422 Unprocessable Entity で publish が止まる

Actions の publish step だけが失敗した。エラーは 422 Unprocessable Entity。他のステップは通っているので、最初は原因の見当がつかなかった。

原因は package.jsonrepository.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 を上げようとすると、検証で落ちる。正しい順序はこうだ。

  1. package.json.version を上げて commit する
  2. その commit に tag を打つ
  3. push する

順序が逆だと、tag が指す commit の version と tag 名がずれる。失敗したら tag を削除して付け直す。

Terminal window
git tag -d v0.1.0
git push origin :refs/tags/v0.1.0
# version を上げた commit の後で
git tag v0.1.0
git push origin v0.1.0

最終的に安定した release フロー

今の構成はこうなっている。

  1. 実装・テストを進める
  2. package.json.version を上げて commit する
  3. v{version} の tag を打って push する
  4. 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 側の設定が一対一で対応していることを確認してから動かすのが一番早い。