2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

この記事はドワンゴ Advent Calendar 2023の21日目の記事です。

フロントエンド開発をしている方は、何かしらnpm registryを利用していると思います。
特に業務などで内製のパッケージを作った場合はprivate registryに置いて利用することが多いはずです。

本記事は、private npm registryを別のところに移行する際の方法についてお話します。

背景

private npm registryとしてはnpmjs、Github Packages Registry、AWS Codeartifactといったクラウドのものから、Sinopia, Verdaccioなど自分でサーバを立てて運用するものなど様々なものがあります。

一つのレジストリをずっと使い続けられれば良いですが、何らかの事情により別のレジストリへ移行しなければならない状況が発生することもあります。
扱うリポジトリやパッケージが少なく、1, 2日で移行が完了する場合はnpmrcやregistry周りの設定をササっと書き換えれば済む可能性も高いですが、移行対象のパッケージ数が多く、かつパッケージ間の依存関係がある場合、日々の開発作業を止めずに移行するのは難しくなってきます。

そのような状況で単にnpmrcや周辺の設定を切り替えるだけでは済まない場合はどのように移行を進めれば良いのでしょうか。

単純な移行で問題となる点

単に1つずつレジストリ設定を書き換えていくとどのような問題が起こるか。

基本的に、1つのスコープで複数のレジストリを運用できない

例として以下のような依存構造を考えてみます。
@example/lib-a, @example/lib-b, @example/core というパッケージがあり、lib-a, lib-bcoreに依存していたとします。

screenshot.1702465958.png

この時、coreのレジストリが registry-2 に切り替わると、それ以降のバージョンはregistry-2にのみpublishされるため、registry-1 を使っているlib-a, lib-bからは core の最新版が取れず更新が滞ってしまいます。

パッケージ間の依存がなかったり、lib-a, lib-bのレジストリをすぐにregistry-2に切り替えられる場合はあまり大きな問題になりにくいですが、長期にわたると必要な変更がすぐに反映できなかったり、マイグレーションコストも高くなってきます。

この問題は、スコープ毎にレジストリの設定を分けられる(≒同じスコープであれば同じレジストリを使うという前提の)仕様によって起こるものですが、スコープはパッケージ名で決まるのでパッケージ名の変更容易性が移行難易度に影響します。

上記例でいえば、coreスコープを別のものにして、レジストリ設定も分けることができれば移行作業がシンプルになります。

screenshot.1702465991.png

スコープを変えずに移行する

スコープを変えることができる場合は、順次新しいスコープ、レジストリへ移行していき、参照する側も新しいものへ切り替えていけば時間はかかってもシンプルな手順で移行を進めることができます。
ではスコープを変更することができない場合を考えてみます。

前述の通り、単純に切り替えていってしまうと最新版の取得ができなくなるため、移行に時間がかかる見込みがある場合は移行前と移行後両方のレジストリにパッケージがある必要があります

1つのパッケージを複数のレジストリへ配置

移行期間中は両方のレジストリへパッケージを配置するとした場合、それを実現する方法はいくつかあります。
ここでは3つ挙げますが、結論から言えば3つ目がオススメです。

  • 移行後のレジストリのupstreamとして移行前のレジストリを指定する
  • リリース時に両方のレジストリへpublish1する
  • 片方のレジストリへpublishした後、そのパッケージのアーカイブをもう片方のレジストリへアップロードする

移行後のレジストリのupstreamとして移行前のレジストリを指定する

これはできるレジストリや条件が限られると思いますが、移行後のレジストリが存在しないパッケージはupstreamから取得するようにできれば、シンプルに移行が可能です。

20723896-0814-45df-9968-3c91193981fa.png

リリース時に両方のレジストリへpublishする

1つ目のレジストリへpublishした後に、もう1つのレジストリへ再publishする方法です(便宜上、以後multi-publishと呼称します)。
両方のレジストリへ配置するとなった場合にすぐ思いつきそうな方法ですが、リポジトリの設定やリリースの仕方によって対応が異なる場合があります。

詳細は後述しますが、あまりオススメしません。

51ff8bb3-ed2e-a396-0a6e-3d02b01d0865.png

片方のレジストリへpublishした後、そのパッケージのアーカイブをもう片方のレジストリへアップロードする

3つのうちでは一番確実で難易度も低い方法に感じます。

具体的には、移行前のレジストリへpublishした後、そのレジストリからアーカイブをダウンロードし、移行後のレジストリへアップロードします(逆方向の同期でもOK)。

直接アーカイブをダウンロード、アップロードできないレジストリだと使えない方法ですが、大抵のレジストリは対応しているのではないかと思います。

この方法の場合、単一の仕組みで両方のレジストリへ配置できるので、publishやリリースの方法に左右されず作業コストが下がります。
なので個人的には1番オススメな方法です。

d828218c-b20a-215b-434b-7f23f95a6157.png

両方のレジストリへpublishする際の問題

先ほどオススメしない方法として、リリース時に両方のレジストリへpublishする方法を挙げましたが、これがなぜ避けた方がいいのかを具体的に書きます。

パッケージマネージャによって対応が異なる

npm, yarn, pnpmなどパッケージマネージャの種類によって使い方が異なるので、リポジトリによって使っているパッケージマネージャが異なると各々の対応が必要になります。

例えば、npmの場合は npm publish した後に、別のレジストリへpublishするため npm publish --@example:registry=https://~ というコマンドを実行しますが、
yarnの場合はyarn publishした後に環境変数 YARN_REGISTRYで別のレジストリへ変えたり、configを変更してからyarn publishし直す、というようにツールによって対応方法が変わってきます。

特にスコープ付きの場合、CLIでレジストリの向き先を簡単に変更できないことが多いので、.npmrc 等を書き換えてpublishするという複雑な対応が必要になります。

ちなみに、nrmyrm というレジストリ切り替えツールもあるので使えるかもしれないです(私は未検証です)。

publish以外の操作も一緒にやるツールを使っていると対応が複雑になる

semantic-releaseやlernaなど、バージョニングの処理も含めてpublishするツールを使っている場合、2重にバージョンアップをしないようにする必要があり、lerna publishした後にnpm publishをするといった対応が必要になります2

特に、yarn v3 + lernaの構成の場合、lernaはpublish時に("npmClient": "yarn"の設定を入れていても.yarnrc.ymlではなく).npmrcを参照する3ので罠っぽい挙動にも繋がります。

リポジトリによって作法が異なると各対応が必要になる

上記のように、ツールの仕様に合わせた対応が必要になりますが、移行対象のすべてのリポジトリが同じツール、仕組みであれば対応方法も少なくて済みます。

しかし、リポジトリによってlernaを使っていたり、semantic-releaseを使っていたり、シンプルにnpmだけ使っていたり、というようにバラバラな仕組みだと各種対応が必要で非常に大変です。

こういった理由から複数のレジストリへpublishする仕組みを使うのは避けた方が良いでしょう。

どうしてもスコープを変えずにmulti-publishしないといけない!というあなたへ

ここまで読めばnpmレジストリ移行をしようとしている方はmulti-publishを選択するとは思えないですが、それでもこの方法をとらなければならない方もいるかもしれません。

そんな方のために、役に立つかもしれないTipsを載せておきます。

lernaの --registry オプションは、スコープ付きのパッケージのレジストリ切り替えに使えない

--registryはスコープ指定なしのレジストリを変更するオプションです。
したがって、.npmrc に以下のような記述があったとすると、--registry オプションで変更されるのは registryのconfigであり、@example:registryではないため、適切にレジストリの向き先が変えられません。

registry=https://registry.npmjs.org/
@example:registry=https://example.com/npm/
//example.com/npm/:_authToken=~(以下略)

スコープ付きのレジストリ設定をCLIオプションで指定できないようなので、この場合は npm config setなどを使って.npmrcを書き換えて対応します。

lerna使用時、プロジェクトディレクトリの.npmrcを書き換えると uncommitted changes のエラーでpublishできなくなってしまう

lernaはgitの差分があるとpublishに失敗するため、プロジェクトディレクトリの.npmrcを書き換える方法でレジストリ変更をする場合、ダミーのコミットをしてからpublishするという対応が必要になります。

ignoreできるようにして欲しいという提案 も出ているようですが、対応される見込みはなさそうです。

yarn v3以降を使っている場合、.npmrc をバージョン管理の対象にせず、プロジェクト内では.yarnrc.yml のみを管理し、.npmrcを変更してもlernaに怒られないようにするという回避策は可能です。

semantic-release使用時は、npm publishでmulti-publishする

semantic-releaseはlernaほど複雑ではないので、1つ目のレジストリにsemantic-releaseでpublishした後、 npm publish --@scope:registry=https://~ などでmulti-publishが可能です。

さいごに

文字ばかりになってしまいましたが、private npm registryの移行を考えている方の一助になれば幸いです。

今回紹介した方法以外でも良い移行方法はあるかと思いますし、良いアイディアがある方はぜひコメント等残していっていただきたいです。
(意見や間違いの指摘も歓迎です)

それではまた。

  1. 本稿でいうpublishとは、npm publishを始めとする、パッケージをpackし、レジストリへメタ情報も含めてアップロードする各種ツールの操作を指します

  2. publishの互換性を保つためにlerna publishを2回やらないといけない場合はさらに複雑になります

  3. https://lerna.js.org/docs/features/version-and-publish

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?