いろいろ作りたいけど、汎用的な処理とかUIをライブラリ化したいなと思って、
いろいろ調べたときの備忘録(*´ω`*)
構成
- pnpm ... パッケージマネージャー。workspace機能を活用
- Turborepo ... モノレポ向けのビルドシステム。依存関係を考慮してビルドしてくれる
- lerna-lite ... バージョン更新とパッケージの公開
- GitHub Packages ... パッケージのレジストリ
pnpm workspaces x Turborepoのmonorepo構成
ベースの部分はpnpm workspacesとTurborepoで構成
pnpm workspaces
まずは、package.jsonやworkspacesの設定
# rootのpackage.json作成 $ pnpm init # workspace用の設定ファイルの作成 $ echo -e "packages:\n - "packages/*"" > pnpm-workspace.yaml # 各パッケージの作成と初期化 $ mkdir -p packages/lib-a packages/lib-b # 各パッケージの初期化 $ cd packages/lib-a $ pnpm init $ cd ../lib-a $ pnpm init
ここまでだとこんな感じ。
- packages - lib-a - package.json - lib-b - package.json - package.json - pnpm-workspace.yaml
Turborepo
$ pnpm -w add -D turbo # turborepoの設定ファイルを作成 $ touch turbo.json
turbo.json
turbo.json
の中身はこんな感じ。
使いたいscriptsコマンドを追加しておく
{ "$schema": "https://turbo.build/schema.json", "pipeline": { "dev": {}, "build": {} } }
lib-*/package.json
各パッケージのpackage.json
にもコマンドを追加
{ // ... "scripts": { + "dev": "echo \"DEV!! lib-a\"", + "build": "echo \"BUILD!! lib-a\"", "test": "echo \"Error: no test specified\" && exit 1" }, // ... }
./package.json
rootのpackage.json
にturbo
コマンドを追加
{ // ... "scripts": { + "dev": "turbo dev --parallel", + "build": "turbo build", "test": "echo \"Error: no test specified\" && exit 1" }, // ... }
コマンドの実行(依存関係なし)
依存関係無しで実行してみるとこんな感じ。
それぞれbuild
コマンドを実行してくれる。
$ pn -w build > example_monorepo@1.0.0 build ./example_monorepo > turbo build • Packages in scope: lib-a, lib-b • Running build in 2 packages • Remote caching disabled lib-a:build: cache miss, executing 5c143b0237edae01 lib-b:build: cache miss, executing ac02900df05fa28a lib-a:build: lib-a:build: > lib-a@1.0.0 build ./example_monorepo/packages/lib-a lib-a:build: > echo "BUILD!! lib-a" lib-a:build: lib-b:build: lib-b:build: > lib-b@1.0.0 build ./example_monorepo/packages/lib-b lib-b:build: > echo "BUILD!! lib-b" lib-b:build: lib-b:build: BUILD!! lib-b lib-a:build: BUILD!! lib-a Tasks: 2 successful, 2 total Cached: 0 cached, 2 total Time: 287ms
2回目以降はキャッシュを使ってくれるのでさらに早い。
コマンドの実行(依存関係あり)
次にlib-a
がlib-b
を使う感じの依存関係を含めてみる。
$ cd packages/lib-a/ $ pn add -D lib-b devDependencies: + lib-b 1.0.0 <- ../lib-b
これで実行してみると、lib-bから先に実行してくれる。
$ pn -w build > example_monorepo@1.0.0 build ./example_monorepo > turbo build • Packages in scope: lib-a, lib-b • Running build in 2 packages • Remote caching disabled lib-a:build: cache miss, executing 0a1e263ac434cb60 lib-b:build: cache hit, replaying logs ac02900df05fa28a lib-b:build: lib-b:build: > lib-b@1.0.0 build ./example_monorepo/packages/lib-b lib-b:build: > echo "BUILD!! lib-b" lib-b:build: lib-b:build: BUILD!! lib-b lib-a:build: lib-a:build: > lib-a@1.0.0 build ./example_monorepo/packages/lib-a lib-a:build: > echo "BUILD!! lib-a" lib-a:build: lib-a:build: BUILD!! lib-a Tasks: 2 successful, 2 total Cached: 1 cached, 2 total Time: 325ms
lerna-lite
各パッケージのバージョン変更/更新やパッケージの公開のために利用
# rootにインストール $ pn -w add -D @lerna-lite/cli @lerna-lite/publish # lerna-liteの初期設定 $ pn lerna init $ cat lerna.json { "$schema": "node_modules/@lerna-lite/cli/schemas/lerna-schema.json", "version": "0.0.0", "packages": ["packages/*"] }
pnpmを使うので、lerna.json
に設定を追加。
{
//...
"version": "0.0.0",
+ "npmClient": "pnpm",
"packages": ["packages/*"]
}
各パッケージのバージョンと統一したいバージョンがあうように、
各パッケージのバージョンを一括で変更。
$ pnpm lerna version --no-git-tag-version --no-push 0.0.0 --yes lerna-lite notice cli v2.4.2 lerna-lite info current project version 0.0.0 lerna-lite notice FYI git repository validation has been skipped, please ensure your version bumps are correct lerna-lite info Assuming all packages changed lerna-lite WARN version Skipping working tree validation, proceed at your own risk Changes (2 packages): - lib-a: 0.0.0 => 0.0.0 - lib-b: 0.0.0 => 0.0.0
lerna-liteは、gitのタグ付やpushもしてくれるけど、
いまはしたくないので、--no-git-tag-version --no-push
とつけておく
scriptにコマンドを追加
コマンドが長くなりがちなので、いくつかscripts
に追加。
version:set
はさっき使ったのもの。
version:set
: バージョンの設定publish:latest
: 最新版のリリースpublish:canary
: カナリアリリース
{ // ... "scripts": { // ... + "version:set": "lerna version --no-git-tag-version --no-push", + "publish:latest": "lerna publish --yes --conventional-commits", + "publish:canary": "lerna publish --yes --canary --dist-tag beta --preid beta --conventional-commits", // ... }, // ... }
最初のリリース(dry-run)
とりあえず、dry-runでお試し
# dry-run $ pnpm publish:latest --no-git-tag-version --no-push --dry-run Changes (2 packages): - lib-a: 0.0.0 => 0.0.1 - lib-b: 0.0.0 => 0.0.1 $ git st -s M lerna.json M packages/lib-a/package.json M packages/lib-b/package.json ?? CHANGELOG.md ?? packages/lib-a/CHANGELOG.md ?? packages/lib-b/CHANGELOG.md
pnpm larna publish
を実行すると、
lerna.json
と各パッケージのバージョンの更新- CHANGELOG.mdの作成/更新
- version/CHANGELOG.mdの変更
git commit
- リリースバージョンのタグ付け
git tag
- コミットとタグのpush
git push
- npmレジストリへのpublish
実際の変更内容はこんな感じ。
$ git diff lerna.json diff --git a/lerna.json b/lerna.json index 8c45f97..ef95be7 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "$schema": "node_modules/@lerna-lite/cli/schemas/lerna-schema.json", - "version": "0.0.0", + "version": "0.0.1", "packages": [ "packages/* $ git diff packages/lib-a/package.json diff --git a/packages/lib-a/package.json b/packages/lib-a/package.json index a817e2f..f1e1f28 100644 --- a/packages/lib-a/package.json +++ b/packages/lib-a/package.json @@ -1,6 +1,6 @@ { "name": "lib-a", - "version": "0.0.0", + "version": "0.0.1", "description": "", "main": "index.js", "scripts": { @@ -13,6 +13,7 @@ "license": "ISC", "devDependencies": { "@types/node": "^20.3.3", - "lib-b": "workspace:^" - } + "lib-b": "^0.0.1" + }, + "gitHead": "ee3ab490a4e3bb027ac13aa5d7f518ec3756cac3" }
$ cat CHANGELOG.md # Change Log All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. ## 0.0.1 (2023-07-05) **Note:** Version bump only for package example_monorepo $ cat packages/lib-a/CHANGELOG.md # Change Log All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. ## 0.0.1 (2023-07-05) **Note:** Version bump only for package lib-a
dry-runの結果を確認したら、元に戻しておく
$ git checkout -- . $ git clean -f Removing CHANGELOG.md Removing packages/lib-a/CHANGELOG.md Removing packages/lib-b/CHANGELOG.md
最初のリリース
git push
だけしないように設定。
# dry-run $ pnpm publish:latest --no-push Changes (2 packages): - lib-a: 0.0.0 => 0.0.1 - lib-b: 0.0.0 => 0.0.1
パッケージをリリースするNPM関連の設定とかをしていないので、
パッケージの公開に関しては以下のようにエラーがでる
lerna-lite WARN notice Package failed to publish: lib-b lerna-lite ERR! E429 429 Too Many Requests - PUT https://registry.npmjs.org/lib-b lerna-lite ERR! E429 429 Too Many Requests - PUT https://registry.npmjs.org/lib-b
コミットされた内容とかを確認してみると、
ちゃんとコミットとタグ付けがされている。
$ git log -1 commit 9c5d7018958ebe4248fed4e21cc8368193ea37bd (HEAD -> main, tag: v0.0.1) Author: memory-lovers <8280057+memory-lovers@users.noreply.github.com> Date: Wed Jul 5 10:00:05 2023 +0900 v0.0.1 $ git tag v0.0.1
Conventional Commitsを使ったのbump
lerna publish --yes --conventional-commits
のように、
--conventional-commits
をつけていると、
コミットログからどのバージョンを上げるかを自動で決めてくれる。
インクリメントされる箇所は、以下のコミットのtypeに従う。
- PATCH ...
fix:
- MAINOR ...
feat:
- MAJOR ...
fix!:
/feat!
- no bump ...
build:
/chore:
/ci:
/docs:
/style:
/refactor:
/perf:
/test:
カナリアリリース
何も変更がないとこんな感じで、何もおきない。
$ pnpm publish:canary --no-push lerna-lite notice cli v2.4.2 lerna-lite info canary enabled lerna-lite notice Current HEAD is already released, skipping change detection. lerna-lite success No changed packages to publish
なので、改行を追加するだけのコミットを追加。
$ echo "" >> packages/lib-a/package.json $ git commit -am "update"
もう一度。
$ pnpm publish:canary --no-push $ git st -s M packages/lib-a/package.json $ git diff diff --git a/packages/lib-a/package.json b/packages/lib-a/package.json index 9b59a83..f118109 100644 --- a/packages/lib-a/package.json +++ b/packages/lib-a/package.json @@ -1,6 +1,6 @@ { "name": "lib-a", - "version": "0.0.1", + "version": "0.0.2-beta.0+d445d82", "description": "", "main": "index.js", "scripts": { @@ -13,7 +13,7 @@ "license": "ISC", "devDependencies": { "@types/node": "^20.3.3", - "lib-b": "workspace:^" - } + "lib-b": "^0.0.1" } } -
こんな感じで、変更のあるパッケージだけ、
バージョンを更新してくれる。
pnpm larna publish --carnary
を実行すると、
- 各パッケージのバージョンの更新
- npmレジストリへのpublish
をしてくれて、CHANGELOG.mdなどは更新されない。
バージョンは統一かパッケージ個別か
上記の例では全パッケージ共通のバージョンにしているけど、
個別にバージョンをつけることもできる。
- 統一モード ... fixed mode
- 個別モード ... independent mode
please make sure that you have a lerna.json config file and a version property defined with either a fixed or independent mode (for example:
"version": "independent"
). An error will be thrown if you're missing any of them.
これに従い、lerna.json
のversion
を変更すればOK
{ //... - "version": "0.0.0", + "version": "independent", "npmClient": "pnpm", "packages": ["packages/*"] }
ハマったポイント
--carnary
オプションは、割といろいろハマった。。
lerna publish --carnary
はtagがないと全部publish。。--carnary
オプションはタグが1つ以上あるときにフラグが立つよう
--dry-run
をつけるとエラーになる。。lerna publish --canary --dry-run
の問題はv3.2.1で対応されたっぽい!
GitHub Packagesで非公開パッケージを配布
GitHub Packagesを使うと、
非公開のパッケージを配布することができる。
GitHub Packagesを使うためにいくつかルールがあるので、
それに従い、設定していく
- スコープ付きのnpmパッケージのみ
- 同じリポジトリから複数パッケージを公開する場合は、
repository
を設定する
リポジトリのURLが
https://github.com/memorylovers/example_monorepo
だった場合は、こんな感じ。
package.jsonの設定
$ git diff diff --git a/package.json b/package.json { - "name": "example_monorepo", + "name": "@memorylovers/example_monorepo", "version": "0.0.1", diff --git a/packages/lib-a/package.json b/packages/lib-a/package.json { - "name": "lib-a", + "name": "@memorylovers/lib-a", + "repository": { + "type": "git", + "url": "https://github.com/memorylovers/example_monorepo", + "directory": "packages/lib-a" + }, "version": "0.0.1", } - diff --git a/packages/lib-b/package.json b/packages/lib-b/package.json { - "name": "lib-b", + "name": "@memorylovers/lib-b", + "repository": { + "type": "git", + "url": "https://github.com/memorylovers/example_monorepo", + "directory": "packages/lib-b" + }, "version": "0.0.1",
.npmrcの設定
@memorylovers/*
のパッケージは、GitHub Packagesを見てほしいので、
.npmrc
か~/.npmrc
を作成 or 変更
# ~/.npmrc ; registory @memorylovers:registry=https://npm.pkg.github.com ; GitHub PAT(classic): Write GitHub Packages //npm.pkg.github.com/:_authToken=ghp_<YOUR_PERSONAL_ACCESS_TOKEN>
また、パッケージの公開やインストールには、
個人用アクセストークン(PAT)が必要なので、それも合わせて設定。
Personal access tokens (classic)じゃないといけないので注意。
ダウンロードだけなら、read:packages
権限でOK。
アップロードもするので、write:packages
権限が必要。
これでローカルからpublishできるように。
$ pnpm publish:canary // ... Found 2 packages to publish: - @memorylovers/lib-a => 0.0.2-beta.1+500dec2 - @memorylovers/lib-b => 0.0.2-beta.1+500dec2 // ... Successfully published: - @memorylovers/lib-a@0.0.2-beta.1+500dec2 - @memorylovers/lib-b@0.0.2-beta.1+500dec2
GitHub Actionsで自動デプロイ
ローカルからパッケージを公開できるようになったので、
GitHub Actionsを使って、自動でデプロイできるようにしてみる。
workflowファイルはこんな感じ。
# .github/workflows/release-latest.yml name: Publish Latest "on": push: branches: - main paths: - "**/package.json" - "**/pnpm-lock.yaml" - "workspaces/**" workflow_dispatch: jobs: publish: runs-on: ubuntu-latest steps: # ***************************************************** # * SETUP # ***************************************************** - uses: actions/checkout@v3 with: fetch-depth: 0 - uses: pnpm/action-setup@v2 with: version: 8 - uses: actions/setup-node@v3 with: cache: "pnpm" node-version: "18" registry-url: https://npm.pkg.github.com scope: "@memorylovers" - run: pnpm i # ***************************************************** # * Setup Git Config # ***************************************************** - run: | git config --global user.name 'github-actions[bot]' git config --global user.email 'github-actions[bot]@users.noreply.github.com' git remote set-url origin https://x-access-token:${GITHUB_TOKEN}@github.com/$GITHUB_REPOSITORY env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # ***************************************************** # * Build # ***************************************************** - run: pnpm build # ***************************************************** # * Publish # ***************************************************** - run: pnpm publish:latest env: NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ポイントとしては以下の3点
actions/setup-node
でregistryなどの設定- commit/pushをするので
git config --global
を追加 lerna publish
のために、NODE_AUTH_TOKEN
などが必要
あとは、git push
して少し待つと、リリースコミットがされてる感じ。
$ git push -u origin main $ git pull Updating f12647d..276ecf8 Fast-forward CHANGELOG.md | 4 ++++ lerna.json | 2 +- packages/lib-a/CHANGELOG.md | 4 ++++ packages/lib-a/package.json | 2 +- packages/lib-b/CHANGELOG.md | 4 ++++ packages/lib-b/package.json | 2 +- 6 files changed, 15 insertions(+), 3 deletions(-) $ git tag v0.0.2 v0.0.1
GitHub Packageにあるパッケージを利用する
公開したパッケージを利用したい場合は、PATが必要なので注意。
# ~/.npmrc ; registory @memorylovers:registry=https://npm.pkg.github.com ; GitHub PAT(classic): Read GitHub Packages //npm.pkg.github.com/:_authToken=ghp_<YOUR_PERSONAL_ACCESS_TOKEN>
GitHub Actions内でパッケージを利用する
GitHub Actions内でも、PATが必要なので注意。
pnpm install
する前に、.npmrc
に追記をしておく。
name: Build And Deploy "on": push: branches: - main workflow_dispatch: jobs: build_and_deploy: runs-on: ubuntu-latest steps: # ***************************************************** # * SETUP # ***************************************************** - uses: actions/checkout@v3 - uses: pnpm/action-setup@v2 with: version: 8 - uses: actions/setup-node@v3 with: cache: "pnpm" node-version: "18" - name: setup .npmrc run: | echo "@memorylovers:registry=https://npm.pkg.github.com" >> .npmrc echo "//npm.pkg.github.com/:_authToken=${{ secrets.GITHUB_PAT }}" >> .npmrc # install and build - run: pnpm install - run: pnpm build
以上!! これでだいぶ共通化が捗りそう。。(´ω`)
参考にしたサイト様
- Turborepo でモノレポ構成のプロジェクトを爆速でビルドする | DevelopersIO
- もはや pnpm と Turborepo で Monorepo 環境作れるから
- lernaからlerna-lite + turborepoに移行する | Web Scratch
- lernaでのmonorepoにおけるリリースフロー(Fixed/Independent) | Web Scratch
- zenn-editor の Monorepo 環境を pnpm + Turborepo + lerna-lite で構築した話
- GitHub Packagesでプライベートnpmパッケージを公開する - くらげになりたい。
公式サイト
- turborepo
- lerna
- Conventional Commits
- GitHub Packages
- others