🌟はじめに
本記事はプロもくチャット Adevent Calendar2023の5日目です
🤔 なんで必要なの?
🔥 Firestoreを使っている
僕が所属しているプロダクトでは、下記のような構成を取っています
- フロントエンド: Vue3/TypeScript
- バックエンド: Node.js/TypeScript
- インフラ: Firebase/GCP
📚 Firestoreはカスタムオブジェクト経由でRead/Writeできる
DBは基本的にはFirestore、一部CloudSQLを使っています。
フロントエンドから直接Firestoreの読み書きする部分が多く、随所でバックエンド経由でFirestoreへアクセスするため、フロントエンド・バックエンドともに同じドキュメントやコレクションにアクセスするのが多いです。
FirestoreはNoSQLのため基本的にはスキーマがありませんが、下記のようにモデルクラスと変換メソッド(toFirestore
, fromFirestore
)を定義することでカスタムオブジェクトを使って保存・読み取りすることができます。
https://firebase.google.com/docs/firestore/query-data/get-data?hl=ja#custom_objects
※このあたりの解説は今回は割愛します
🌐 共有したい、でも普通にnpmパッケージ化すると全世界に公開されてしまう…
上記のような状態なので、モデルクラスや変換メソッドをフロントエンド・バックエンドで共有する必要が出てきました。
別リポジトリをシンボリックリンク化してデプロイしてゴニョゴニョ…してましたが、デプロイが遅くCIのコードもかなり複雑化するため、npmパッケージ化したいと考えました。
が、npmパッケージは全世界に公開されてしまうため、ちょっと微妙…
そこでGithubPackagesを使ってプライベートなnpmパッケージを作成しそれらをVue, Node.jsそれぞれでnpm install
して使うようにします。
🎯 ゴール
- モデルクラス・変換メソッドをGithubのプライベートリポジトリに作成する
- プライベートリポジトリのコードをGithubPackagesにパッケージを公開する
- 公開されたパッケージをVue3/Node.jsそれぞれで
npm install XXX@YY.YY.YY
のような形で利用することができる
📝 本題
🛠️ モデルクラスをGithubのプライベートリポジトリに作成する
- さくっと共有したいリポジトリを作成します。
npm init -y npm install typescript -D npx tsc --init
-
.gitignore
を作成(最低限しか入れてません)node_modules .DS_Store Thumbs.db dist/**/*
- 共有したいコードを作る
src/index.ts
export class City { constructor( readonly name: string, readonly state: string, readonly country: string ) {} static toFirestore(city: City) { return { name: city.name, state: city.state, country: city.country, }; } }
-
tsconfig.json
を設定
僕の担当するプロダクトではNode.jsはCommonJS、VueはESModuleで動いているため、それぞれの形式でビルドする必要があります。このあたりの詳しい話は割愛します。
下記のような形でtsconfigをCommonJS用、ESModule用に作成します。(ベースとなるtsconfigはデフォルトのままです)tsconfig.cjs.json{ "extends": "./tsconfig.json", "compilerOptions": { "module": "CommonJS", "outDir": "./dist/cjs" }, "include": ["src/**/*"], "exclude": ["node_modules"] }
tsconfig.esm.json{ "extends": "./tsconfig.json", "compilerOptions": { "module": "esnext", "outDir": "./dist/esm" }, "include": ["src/**/*"], "exclude": ["node_modules"] }
-
npm script
でビルドできるようにするpackage.json{ ... "scripts": { "build:esm": "tsc -d -p tsconfig.esm.json", "build:cjs": "tsc -d -p tsconfig.cjs.json" }, ... }
-
npm run build:esm
,npm run build:cjs
で、distディレクトリに出力されることを確認 - Common.js, ESModule用にそれぞれエンドポイントを定義
package.json
{ ... "main": "dist/cjs/index.js", "module": "dist/esm/index.js", "types": "dist/esm/index.d.ts", ... }
ここまでで、ファイル構成は下記のようになっていると思います
private-common % tree
.
├── dist
│ ├── cjs
│ │ ├── index.d.ts
│ │ └── index.js
│ └── esm
│ ├── index.d.ts
│ └── index.js
├── node_modules
├── package-lock.json
├── package.json
├── src
│ └── index.ts
├── tsconfig.cjs.json
├── tsconfig.esm.json
└── tsconfig.json
📦 プライベートリポジトリのコードをGithubPackagesにパッケージを公開する
-
PATを作成
- プライベートなパッケージの作成・インストールをするには、GithubのPersonal Access Token(PAT)を作成し、ローカルに配置する必要があります。
作り方はこのあたりを参照してください
https://docs.github.com/ja/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-fine-grained-personal-access-token -
write:packages
権限をつけてあげてください
- プライベートなパッケージの作成・インストールをするには、GithubのPersonal Access Token(PAT)を作成し、ローカルに配置する必要があります。
-
パッケージ名を定義
プライベートとして発行するにはGithub名 or Oraganization名/リポジトリ名
とする必要があります。これを下記のようにpackage.jsonに定義します。package.json{ "name": "@IkumaHayashi/private-common", ... }
-
.npmrc
ファイルを作成
Github Packagesは、通常のnpmレジストリとは別の場所(https://npm.pkg.github.com
)に公開されます。
このレジストリにプライベートなパッケージを公開するため、下記2点の情報をnpm
コマンドに伝えてあげる必要があります- レジストリの場所(
https://npm.pkg.github.com
のこと) - プライベートなパッケージをインストールするための秘密鍵(PATのこと)
この2点が載っているファイルを
.npmrc
として作成することで、npm
が自動的にこのファイルの情報を読み取り、パッケージの公開やインストール時に使用します。
ただし、PATはリポジトリ管理するわけにはいきません。そこで、プロジェクト内ではレジストリの場所を、各開発者のホームディレクトリにはPATを配置します。
下記のようにファイルを作成します。プロジェクトルート/.npmrc@あなたのGithub名もしくはOraganization名:registry=https://npm.pkg.github.com
~/.npmrc//npm.pkg.github.com/:_authToken=PAT
- レジストリの場所(
-
.npmignore
を空ファイルで作成-
.npmignore
はパッケージを発行する際にどのファイルを無視するかを指定するファイルです。 - npm v9から、
.npmignore
がない場合暗黙的に.gitignore
が使用されます。今回の構成ではdist
ディレクトリをパッケージとして発行したいため、このままではsrcディレクトリなどしか発行されず、使うことができません。 - そのため空の
.npmignore
を定義して除外するファイルをなくすことで、dist
ディレクトリを含めてあげます。
-
-
発行
パッケージの発行はnpm publish
で行えます。--dry-run
をつけると、発行前にどういう内容で発行されるか確認することができます。npm notice npm notice 📦 @IkumaHayashi/private-common@0.0.1 npm notice === Tarball Contents === npm notice 293B dist/cjs/index.d.ts npm notice 428B dist/cjs/index.js npm notice 293B dist/esm/index.d.ts npm notice 314B dist/esm/index.js npm notice 599B package.json npm notice 264B src/index.ts npm notice 174B tsconfig.cjs.json npm notice 172B tsconfig.esm.json npm notice 12.3kB tsconfig.json npm notice === Tarball Details === npm notice name: @IkumaHayashi/private-common npm notice version: 0.0.1 npm notice filename: @IkumaHayashi/private-common-0.0.1.tgz npm notice package size: 4.5 kB npm notice unpacked size: 14.8 kB npm notice shasum: 87328bb7206b4661afce03868d8e888ded15194a npm notice integrity: sha512-B3FeOLAppTO3w[...]cb/qtu6kFfrUg== npm notice total files: 9 npm notice npm notice Publishing to https://npm.pkg.github.com (dry-run) + @IkumaHayashi/private-common@0.0.1
チェックすべきは下記の2点です
- パブリッシュ先が
https://npm.pkg.github.com
になっている - 発行されるファイル(
Tarball Contents
)にdist
ディレクトリが含まれている
上記が問題なければ
npm publish
で実際に発行します。private-common % npm publish npm notice npm notice 📦 @IkumaHayashi/private-common@0.0.1 npm notice === Tarball Contents === ... npm notice Publishing to https://npm.pkg.github.com + @IkumaHayashi/private-common@0.0.1
問題なく発行されました
- パブリッシュ先が
発行されるとこんな感じでGithubのリポジトリに表示されます
🚀 インストールしてみる
- 使用するリポジトリに
.npmrc
ファイルを作成プロジェクトルート/.npmrc@あなたのGithub名もしくはOraganization名:registry=https://npm.pkg.github.com
npm install @IkumaHayashi/private-common@0.0.1
💡 補足
🤖 Github Actionsでnpm install
するとコケる😭
GithubのPATがないことが原因だと思います。ただ、GithubActionsではルートディレクトリにファイルを作成することができません。
そんなときは、npm installするステップで環境変数にNODE_AUTH_TOKENにPATを定義してあげるとインストールできます
- name: 依存関係をインストール
run: |-
npm install
env:
NODE_AUTH_TOKEN: ${{ secrets.PAT }}
🙏 最後に
ここまで読んでくださりありがとうござました!
ちょっと時間がなくてインストール部分は確認できておりません…
コメントいただければ確認・修正行いますのでお気軽にコメントしてください🙆♂️