LoginSignup
4
1

More than 3 years have passed since last update.

lerna + yarn workspaces で GitHub Packages の private repository を利用してみた(殴り書きメモ)

Last updated at Posted at 2021-01-22

「サーバーとクライアントの共有部分をライブラリ化したい → でも非公開にしたい」という要望への解決方法の一つ。コードが JS/TS 一本で、リポジトリと所有権が一カ所で良ければ使いやすそう。

そもそも、公開パッケージとして分離できるならそれでいい話だし。

今回は Windows, nodist, node v14, yarn install v1.22.10 で試した。

以下では、github のユーザ名を GITHUB_USERNAMEgithub のリポジトリ名を GITHUB_REPONAME と記すことにする(コマンドをコピペして確認していないので細部に誤りがあると思います)。PACKAGE_NAMElib, client, server のいずれかとする。

Github Actionspublish する方法は試してないです。

シンプルな競合対象としては、npm linkgit submodule などでの結合だと思う。

とりあえず、手元で試しに導入してみたのでメモを書き殴ります。

lerna

複数パッケージを一つのリポジトリで管理運用するためのツール。

コミットを元に、サブディレクトリ毎に package として publish するなど。

パッケージの依存関係を一括で解決する lerna bootstrap の機能については、Yarn Workspaces を利用することにした。

Yarn Workspaces

複数のパッケージで依存関係を一括管理する機能。

依存関係の共有による最適化と、競合問題の回避を謳っている。

yarn は使っていて、覚えることが少なそうなので採用。

最終的なフォルダ構成

lib, client, server という三つの構成を一つのリポジトリに入れて、clientserver から lib を参照する構成を例として想定した。

- GITHUB_REPONAME/
    - packages/
        - lib/
            - package.json
        - client/
            - package.json
        - server/
            - package.json
    - .npmrc
    - lerna.json
    - package.json

https://github.com/GITHUB_USERNAME/GITHUB_REPONAME にリポジトリが保存されて、

@GITHUB_USERNAME/lib, @GITHUB_USERNAME/client, @GITHUB_USERNAME/server という三つのパッケージが作成され、npm パッケージと同様に参照可能。

リポジトリの用意

github に新しいリポジトリを用意して、手元に git clone して、そこのフォルダで作業する。いつも通り

git clone https://github.com/GITHUB_USERNAME/GITHUB_REPONAME
cd GITHUB_REPONAME

Yarn Workspaces のために packages.json の設定

"private": true の設定と、"workspaces" 以下の設定を追加。

packages.json
{
  // 
  "private": true,
  "workspaces": {
    "packages": [
      "packages/*"
    ]
  },

lerna のインストール

インストール

lerna 公式のドキュメントには、いきなり npx lerna init せよと書かれているが、手元の環境では graceful-fs 関係のエラー(cb.apply is not a function)が出て動作しなかったので、yarn global add lerna してから使い始めた。

yarn global add lerna
yarn lerna init

(追記)npx で 「cb.apply is not a function」 エラーが出る を参考に、npx 内での graceful-fs の polyfill を無効化したら通るかも知れない

lerna.json の設定

設定内容の概略は以下の通りと認識している

  • 習わしに倣って packages/ 以下にソースコードを置くことにしたので、"packages": ["packages/*"]
  • yarn を使うので "npmClient": "yarn"
  • yarn workspaces を使うので "useWorkspaces": true
  • パッケージ毎にバージョンを設定したいので "version": "independent"
  • LernaとYarn WorkspacesでMonorepo管理 に倣って command を追加したが、まだ十分に理解していない。lerna publishmain ブランチを GitHub Packagespublish する。その際には conventionalCommits でコミットログが書かれているものとして扱うというそのままの認識で良いと思う。
lerna.json
{
  "packages": ["packages/*"],
  "npmClient": "yarn",
  "useWorkspaces": true,
  "version": "independent",
  "command": {
    "publish": {
      "conventionalCommits": true,
      "message": "chore(release): publish",
      "registry": "https://npm.pkg.github.com",
      "allowBranch": "main"
    }
  }
}

全体で共通して利用する npm パッケージのインストール

Yarn Workspaces を利用した。yarn workspacesyarn -W で省略可能。-W or workspaces) が付く以外はいつも通りに add / remove すればいい。

yarn -W -D add typescript @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint eslint-plugin-import lerna

(良く分かってないメモ)この後、パッケージ毎にも npm パッケージをインストールするが、yarn workspaces の場合、それらもhoisting(巻き上げ) という機能によって、GITHUB_REPONAME/node_modules 以下に集約される模様。lerna 単体だと、パッケージ毎のものは個別に入るのかも。便利だけど、いかにもトラブりそうな機能で怖い。

パッケージ毎の設定

パッケージ毎の packages.json を作成する。

ディレクトリは lerna のコマンドで作成しても良いが、ここでは yarn workspaces を利用するので、普通にパッケージのディレクトリを作って、packages.json を置けば良い。create-react-app などでも良いはず。

packages.json の 設定

namescope を付ける(GitHub Packages 用)

name@GITHUB_USERNAME/PACKAGE_NAME の用に、github のユーザ名を scope として付与する。これは、GitHub Packages の為に必要。組織で利用する場合は org名 を(https://github.com/ の後ろと一致させる必要があるという認識)。

nameGITHUB_REPONAME も入れたかったのだけど、scope についてはユーザ名と揃える必要があった。埋め込むなら PACKAGE_NAME に埋め込むことになりそう。

repositorypublishConfig

パッケージのリポジトリと publish 先を GitHub Packages に設定する。デフォルトは npmjs なので不安なら、npmjs からログアウトするなどしておくこと。

リポジトリ全体を private にした環境でしか試行していないが、 "access": "restricted にしておくと private になるのかも。

"private": true を設定していると、packages として publish されないので、設定に不安がある間は "private": true にしておくと良いかも。

packages/PACKAGE_NAME/packages.json
{
  // 前略
  "repository": {
    "type": "git",
    "url": "ssh://git@github.com/GITHUB_USERNAME/GITHUB_REPONAME.git",
    "directory": "packages/PACKAGE_NAME"
  },
  "publishConfig": {
    "access": "restricted",
    "registry": "https://npm.pkg.github.com/"
  }
  // 後略
}

依存関係

dependencies, devDependencies もいつも通り。編集して yarn なり、必要に応じて yarn add / yarn add -D すればいい。パッケージ直下の node_modules ではなく、ルートの node_modules にインストールされる辺りの挙動が異なる。

lerna だけを利用する場合は、この辺りが異なるはず。Yarn Workspaces はお手軽そうだった。

個別に入れたい場合は、nohoist オプション を利用する。

GitHub の認証情報を .npmrc に埋め込む

GitHub の Settings Developer settings / Personal access tokensPersonal access token を発効する。

名前は分かりやすい名前を付ければOK。

権限は、パッケージの作成・編集が必要なので Select scopesrepo, write:packages, read:packages, delete:packages を付与した。

パッケージの読み出しだけなど、利用者に応じて権限は調整すること。

作成するとトークンに対応したパスワードが表示される。以下ではそのパスワードを TOKEN_SECRET とする。

ルートに、以下の内容の .npmrc を作成する。ユーザ名とシークレットは置き換えること。

@GITHUB_USERNAME:registry=https://npm.pkg.github.com/
//npm.pkg.github.com/:_authToken=TOKEN_SECRET
  • 一行目が、 @GITHUB_USERNAME スコープのパッケージが GitHub Packages にあることの設定
  • 二行目が、GitHub Packages の認証情報
  • 三行目は、動いた際のおまじないをそのまま残している

npm login コマンドを利用する方法だと、C:\Users\username\.npmrc (~/.npmrc) に保存される。それでも大丈夫なはずなのだけど、試行錯誤の最中に消したので分からない。

TOKEN_SECRET が流出するとリポジトリに無制限にアクセスされてしまうので、作成後は、.gitignore に追加するのをお忘れ無く。

.npmrc

libs を作成する

npm パッケージを作る要領で、libs 以下にコードを書いていく。

共通設定はルートの tsconfig.common.json に記述して、lib 用の設定から読み込んだ。tsconfig.json をまるっと貼っておく。

packages/lib/tsconfog.json
{
  "extends": "../../tsconfig.common.json",
  "compilerOptions": {
    "outDir": "lib",
    "rootDir": "src",
    "baseUrl": "src",
    "module": "commonjs",
    "moduleResolution": "node",
    "declaration": true
  },
  "include": ["src"]
}

publish する

git コミットしておく。push は自動でしてくれる。

yarn lerna publish minor を実行すると、更新されているパッケージの X.Y.ZY の部分がインクリメントされてパッケージ化される。バージョンは minor のオプションを変えればコントロール出来る。major なら X、patch なら Z。

当然だが、.npcrc が参照できるフォルダ(つまりルートなど)で実行しないと認証がこける。~/.npcrc に書く場合は気にしなくて良いが。

上手く packages が publish された場合、https://github.com/GITHUB_USERNAME?tab=packages&repo_name=GITHUB_REPONAME にパッケージができている。もしくは、https:://github.com/GITHUB_USERNAME/GITHUB_REPONAME の右列に Packages が増えている。

エラーが出た場合、随時、エラーメッセージに対応する。大体、C:\Users\username\AppData\Roaming\npm-cache\_logs のログに書かれている内容に対応していけば解決出来た。

記憶にあるトラブルシューティング

各パッケージの下に LICENCE.md なども置いておくと publish 時に lerna に怒られない。

org.couchdb.user' is not in the npm registry.

.npmrc の記述間違いだったような… token を作り直したような、試行錯誤をした記憶があるが、npm login コマンドを使わずに .npmrc を手動で作って解決したと思う。

This command requires you to be logged in.

  • .npmrc の記述が間違っており、npmjs を見に行こうとしていた
  • C:\users\username.npmrc を消して、パッケージ直下に .npmrc を作り直したなど

エラーが無かったのにパッケージができなかった。

"private": true を指定していたのでパッケージされなかった。ログには verbose stack Remove the 'private' field from the package.json to publish it. などと出力されている。

npm package "PACKAGE_NAME" does not exist under owner "OTHER_SCOPE"

scope の部分が GITHUB_USERNAME と一致していなかった。

packages.jsongitHead の項目が残ってしまう

認証エラーなどで publish に失敗すると、中間で書き換えられた内容が書き戻されずに、以降で gitHead が残ってしまうことがある。

全部の packages.json から gitHead を削除して publish し直すことで以降消えるようになった。

lib を利用する

server / clientpackages.json に依存関係を指定する。その際にパッケージ化されているバージョン名を指定しないと駄目みたい。

packages/server/packages.json
{
  // 
  "dependencies": {
    "@GITHUB_USERNAME/lib": "^0.6.0",
  },

後は、npm パッケージと同様に import なり require なりで利用できるはず。

packages/server/lib/hoge.ts
import { FooBar } from "@GITHUB_USERNAME/lib";

No matching version found for @GITHUB_USERNAME/lib@^0.8.0.

バージョン番号の不一致をチェックする。

Couldn't find package "@GITHUB_USERNAME/lib" on the "npm" registry.

.npmrc の内容を見直すと良さそうだった。GitHub Packages を見に行っていない可能性が大。

Cloud Functions からも利用する

サーバ側で Cloud Functions を利用したかったので、.npmrcservers/.npmrc にコピーした。

他には特別な設定は必要なく、firebase deploy --only functions の実行時間が延びてしまったぐらいで、上手く参照してくれているとおもう。

ライブラリの作り方が不味いなど lernayarn workspaces には無関係のエラーは起こしていたが、それは別の問題。

新規に git clone した後

yarn を実行すると、全パッケージの依存関係を全て解決してくれる。node_modules 以下を消した場合なども同様。

npm を使う場合は、yarn lerna bootstrap を実行した後、パッケージ毎に npm install になるっぽい。

参照したページ

感謝。

4
1
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
4
1