Open11

ReactのUIコンポーネントを自作してみる

cotaponcotapon

やりたいこと

  • npm install @xxx/yyy or yarn add @xxx/yyy みたいにインストールできる
  • ゼロから作るのは大変なので、 Material UI をカスタマイズする
  • Material UI にないコンポーネントなども自作コンポーネントに組み込む感じにする
  • 1つのリポジトリでコンポーネントの管理をする
cotaponcotapon

モノレポ

1つのリポジトリで複数のパッケージを管理することを モノレポ と呼ぶ。モノレポ(monorepo)とは「一枚岩のリポジトリ(monolithic repository)」のこと。

モノレポの例

  • サーバーとフロントを1つのリポジトリで管理する
  • WEBのReactとスマホアプリのReact Nativeとでコンポーネントを共有できる
  • React, Material UI, Chakra UIなど各コンポーネントごとに管理できる

モノレポのメリット

  • 1つのブランチで複数のpackageを管理できる
  • すべてにおいて現時点でコミットされたものが動きます
  • コードを構成可能なモジュールへ簡単に分割できます
  • 依存関係をより簡単に管理できます
  • 単一ツールチェーンでのセットアップできます
  • コードエディタやIDEは “ワークスペース単位” で動かします
  • 一貫した開発者エクスペリエンスがあります

モノレポのデメリット

  • トランクベース開発は、より一段と重要になります
  • すべてのサービスがモノレポで上手く動くわけではありません
  • より精巧なCIセットアップが必要です
  • 大規模な変更について考える必要があります
cotaponcotapon

参考サイト

eslint, prettier など。lernaのversionはfixed

https://medium.com/reactbrasil/reuse-your-eslint-prettier-config-in-a-monorepo-with-lerna-54c1800cacdc

monorepoのメリットデメリットとユースケース

https://speakerdeck.com/nitaking/monorepowozhi-ri-monoreponiku-sintahua

azuさんのリポジトリ。リリース手順など網羅できてる

https://github.com/azu/lerna-monorepo-github-actions-release

azuさんの記事。lerna version Fixed, Independent の違いやリリースフローなど詳しく書いてある。

https://efcl.info/2019/01/26/monorepo-release-flow/

https://efcl.info/2014/07/20/git-tag-to-release-github/

cotaponcotapon

Lerna package追加手順

作業ディレクトリを作って yarn v3 に上げる

Yarn Workspaces機能は v3 のほうが安定してるらしい

$ mkdir example && cd example
$ yarn set version berry
$ yarn -v
> 3.x.x

yarnをv1からv2(Berry)へ移行する
https://zenn.dev/kkoudev/articles/5b440e1e341458

.yarnrc.yml を編集する

yarn v2 以降は node_modules が生成されません。node_modules ディレクトリがあること前提で作られてるmodulesが多いので、node_modules ディレクトリが作れるように設定し直します。node-modulesハイフン区切りです

.yarnrc.yml
nodeLinker: node-modules

yarn init

private を ture にするのでオプションにprivateをつける

$ yarn init --private

.gitignore を追加

https://github.com/lerna/lerna/blob/main/.gitignore

Lerna init

yarn global に lerna を add しなくても良さそう

$ npx lerna init --independent
$ yarn install

lerna.json を変更します

lerna.json
{
  "packages": [
    "packages/*"
  ],
  "npmClient": "yarn",
  "useWorkspaces": true,
  "version": "independent"
}
  • npmClient: "yarn" 内部的に yarn を使います。
  • useWorkspaces: true Yarn Workspaces を設定をLernaでも使えます
  • "version": "independent" packages内の各packageのバージョンを独立させます

package.json を編集

workspaecsを追加

package.json
"workspaces": [
   "packages/*"
],

typescriptをインストール

typescriptをインストール。ルートに共通で使うパッケージとしてインストールする場合には、インストールのオプションに明示的に -W オプションを付ける必要があります

$ yarn add -D typescript -W

tsconfig.json 生成

$ npx tsc --init

tsconfig.json を編集

tsconfig.json
{
   "compilerOptions": {
     "module": "commonjs",
     "declaration": true,
     "noImplicitAny": false,
     "removeComments": true,
     "noLib": false,
     "emitDecoratorMetadata": true,
     "experimentalDecorators": true,
     "target": "es6",
     "sourceMap": true,
     "lib": [
       "es6"
     ]
   },
   "exclude": [
     "node_modules",
     "**/*.spec.ts"
   ]
  }

pakcageの追加

$ npx lerna create package_name
cotaponcotapon

Lerna Publish手順

lerna publish

セマンティックバージョニングのメジャー、マイナー、パッチをコマンドにつけることで自動的にpackage.json内のversionが書き換わり、github 上の release ページが自動で更新されます。バージョン X.Y.Z に対して、 majorXminorYpatchZ 部分がインクリメントされます。また、今回はnpmのレジストリには登録しないので --skip-npm を付けます。 gitを無視するオプションは --skip-git です。

$ npx lerna publish major
$ npx lerna publish minor
$ npx lerna publish patch

github repository から yarn install する横着はあかん

https://github.com/lerna/lerna/issues/1003#issuecomment-326379708

私の経験では、Gitの依存関係は、すべてのnpmで最も複雑でエラーが発生しやすい機能の1つです。 Lernaは、実際にはこのように消費されることを意図したものではなく、パッケージを公開することを目的としています(ただし、必ずしもパブリックパッケージであるとは限りません)。

lernaを使用してパッケージを開発している場合は、いつでも内部のプライベートレジストリに公開するか、パブリックレジストリのプライベート(スコープ)パッケージとして公開できます。 gitの依存関係を完全に回避することを強くお勧めします。

つまり、公開したくないpackagesをインストールするにしろ、レジストリからインストールしたほうがよいとのこと。

ようやく理解した

例えば、 @cotapon/react-ui というリポジトリの中にある packages には、 eslint-conf 、ボタンやインプットなどの要素のUIをまとめた elements とかがあるとして、それらを使いたい場合は、 yarn add @cotapon/eslint-conf yarn add @cotapon/elements でインストールしたい。そうするには、 npx lerna publishpackages 内のパッケージを個別でレジストリにアップロードする必要がある。

pckage.jsonにgitHeadがついてしまう

publish時にnpm registryの認証エラーで付いてしまう。--skip-npmをつける

npx lerna publish --conventional-commits --skip-npm

Verdaccio(GitHub Packages Registryを使うのでVardaccioは使わない)

プライベートなnpmレジストリが作れる決定版的なツール
https://github.com/verdaccio/verdaccio

docker pull できるのでやってみる

docker pull verdaccio/verdaccio:nightly-master

.npmrcを追加する

lerna publishコマンドを実行するとcommitされてるところまで自動でpushしてくれます。そのため、github からパーソナルアクセストークンを発行して設定する必要があります。
https://github.com/settings/tokens
repo, write:packages, read:packages, delete:packages
tokenが発行されたら、 .npmrc ファイルを新規作成し、下記の内容を追加します。

.npmrc
@GITHUB_USERNAME:registry=https://npm.pkg.github.com/
//npm.pkg.github.com/:_authToken=発行したtoken
.gitignore
.npmrc

package.jsonを編集

repositoryを追加

lerna publish した時にpushされる先のリポジトリを指定

package.json
"repository": {
    "type": "git",
    "url": "git+https://github.com/xxx/xxx.git"
  },

publishConfigを追加

private repository に push するので、npmjs には push しないけど、publishConfig を書いておかないと lerna publish 時にエラーがでます。 private: true でかつ、 publishConfig の access: restricted にしておくとパブリックに公開されることはない。不安ならnpmjsからログアウトしておく。

package.json
  "publishConfig": {
    "access": "restricted",
    "registry": "https://npm.pkg.github.com/"
  }
cotaponcotapon

Verdaccio の使い方

Verdaccioをインストール

$ npm i -g verdaccio

Verdaccio を起動

$ verdaccio

ユーザー作成

$ npm adduser --registry http://localhost:4873/

.yarnrc

registry "http://localhost:4873"
cotaponcotapon

GitHub Package を使ってみる

クイックスタートどおりにやってみる

https://docs.github.com/ja/packages/quickstart

git push で Error が出た

refusing to allow a Personal Access Token to create or update workflow .github/workflows/release-package.yml without workflow scope

Personal access tokenのSelect scropeで workflow にチェックを入れる
https://github.com/settings/tokens

Homeディレクトリにある .gitconfig に書いてもよし?

npx lerna publish 時に Error が出た

lerna ERR! E401 Unable to authenticate, need: Basic realm="GitHub Package Registry"

.npmrc に Personal access token を入れる

//npm.pkg.github.com/:_authToken=発行したtoken

いや、、これらのエラーは関係ないかも。手順としては、

  1. git push (いる?)
  2. npx lerna publish (git push される?)
  3. release が追加されたらgithub actionsが動く。なのでregistryを指定する必要はない

GitHub Actions でエラー

Run npm ci でエラーが出る

npm ERR! cipm can only install packages with an existing package-lock.json or npm-shrinkwrap.json with lockfileVersion >= 1. Run an install with npm@5 or later to generate it, then try again.

package-lock.json がどうしてみるみたい。lerna と yarn と npm 共存してもええんか???

publish-gpr の npm publish でコケる

npm ERR! Remove the 'private' field from the package.json to publish it.

package.json の private : true がいらない。と書いてあった

npx lerna publish で github packages registry にあげてみる

そのままpublishするとnpmのregistryを参照してしまうのでオプションをつける

$ npx lerna publish --registry=https://npm.pkg.github.com/

変更なしでも強制的にpublishする場合は --force-publish をつける

$ npx lerna publish --registry=https://npm.pkg.github.com/ --force-publish

lerna ERR! E404 The expected resource was not found.

このエラーにずっとはまっていた。

├── packages
│   ├── packae-1
│   └── packae-2

この場合、各packages内のpackage-1, package-2のpackage.jsonにrepositoryの設定が必要。

"repository": {
    "type": "git",
    "url": "git@github.com:org/repo.git"
  },```
cotaponcotapon

GitHub Pacakges Registry にあげたpackagesをnpm installする

privateなリポジトリではもちろんそのままインストールできないので、.npmrc に書き加える

registry=https://npm.pkg.github.com/
//npm.pkg.github.com/:_authToken=token

この場合の token はパーソナルトークンを発行しておく。

cotaponcotapon

conventional commitsとsemantic-release

Conventional Commits

git commit のメッセージをある一定のルールに沿って書くこと。fix, featがあることによって、lerna publish時にpatchなのかminorなのかを自動で判断してくれる
https://www.conventionalcommits.org/ja/v1.0.0/

semantic-release

github actionsでsemantic-releaseを実行すると、コミットログからConventional Commitsのルールにそって自動でrelease noteを作ってくれる
https://github.com/semantic-release/semantic-release

cotaponcotapon

release手順構想

packagesの中の各packageが独立しててlerna.jsonのversionがindependentの場合

使用例は、lintなどをまとめたpackages。lint-configというリポジトリに、eslint-configとprettier-configを作るとします。eslintとprettierは依存関係はないのでpackageは独立、independentで良いと思います。

cotaponcotapon

まとめ

色々ごちゃごちゃ書いたけど、最終的なまとめをここに書きます。

Lernaを使ったpackagesのバージョン管理とPrivateなResistryにリリースする手順

  1. やりたいことと運用指針
  2. Lernaのセットアップ
  3. Lerna Publish
  4. GitHub Actionsでリリースする

やりたいことと運用指針

  • 1つのリポジトリで複数のpackagを管理したい
  • packageのデプロイ先はPrivateなRegistryにしたい
  • プルリクからmasterにマージしたらRegistryにデプロイしてリリースしたい
  • リリースノートを自動化したい

1つのリポジトリで複数のpackagを管理したい

1つのリポジトリで複数のパッケージを管理することを「モノレポ」と呼びます。モノレポ(monorepo)とは「一枚岩のリポジトリ(monolithic repository)」のことを意味すそうです。monorepoの方法は幾つかあり、yarn workspacesLerna を使う2つの方法が有名です。1つのリポジトリのプロジェクト内のみ依存関係が成立し、monorepo化したpackageをRegistryで公開しないのであれば、 yarn workspaces で管理するほうがyarnだけで完結するので良いと思います。また、monorepoで管理しているpackagesをregistryにデプロイするのであれば、 Lerna を使うほうがvarsion管理をした運用がしやすいメリットがあります。詳しくは後述します。

packageのデプロイ先はPrivateなRegistryにしたい

npm installyarn add でインストールできるpackageはnpmjsのregistryに公開されています。しかし、世の中に公開したくないpackageをチーム内や社内だけで使いたいという時はどうすればよいでしょうか。パターンとしては3つ考えられます。

  1. npmに課金してprivateなregistryが使えるようにする
  2. Verdaccioを使って自前でregistryサーバーを建てる
  3. GitHub Packages Registry(GPR)を使う

GitHubでプライベートリポジトリからGPRにpublishすればプライベートなregistryで出来る要件は満たし、npmに課金せずにすみ、Verdaccioを運用する手間もなく、GitHub上で一元管理できるのであればGitHub Packages Registry(GPR)一択と思います。

プルリクからmasterにマージしたらRegistryにデプロイしてリリースしたい