Articles

複数のリポジトリからlerna-js mono-repoへの移動

At mitter.io、私たちは公開する必要があるいくつかの公開向けのnpmパッケージを持っており、最近、Lernaが管理するmono-repo構造に移動し、それぞれのリポジトリ 今日は、この移行の経験と新しいmonorepo構造とのセットアップを共有したいと思います。 私たちのパッケージはすべてSDKターゲットであるか、SDKターゲットの依存関係です。

  • @mitter-io/core-mitterのコア機能。io Sdk
  • @mitter-io/models-typescriptモデル(クラス、型エイリアス、インターフェイスなど)/li>
  • @mitter-io/web-web SDK
  • @mitter-io/react-native-React Native SDK
  • @mitter-io/node-ノード。js SDK(ノードに使用されます。jsバックエンド)
  • @mitter-io/react-scl-ReactJSアプリケーションのための標準コンポーネントライブラリ

私たちのパッケージはすべてTypeScriptで書かれており、タイピングはパッケージ自体にバンドルされており、別々のタイピングパッケージ(通常は@types/で始まるもの)は配布されていません。 これらのパッケージをUMDとES5の両方のモジュール形式でバンドルするには、rollupを使用します。 これに加えて、TypeDocを使用してドキュメントを生成し、AWS S3のパブリックバケットに公開します。

Lernaを使用する前に、パッケージごとに別々のリポジトリがあり、web SDKしか持っていない間は正常に動作しました。 Sdkのロジックのほとんどが@mitter-io/corecoreパッケージと他のすべてのパッケージで発生したほぼすべての変更は、新バージョンを指すように更新する必要がありました。 そのため、React Nativeのように修正されるバグがあったとしても、変更はcorewebnodereact-nativeに反映される必要がありました。 開発者がターゲットを逃すことは非常に一般的でした。

  • SDKのほとんどすべての変更は、5つのパッケージのうち少なくとも3つの変更になります。
  • パッケージ間で同じバージョンを維持することに大きな利点がありました(開発者がtargetの最新バージョンを推測しやすくなります)が、これを手動で追
  • npm linkyarn linknpmから正しいものを使用し、開発のためにローカルリンクに戻っていることを確認するという独自の問題を抱えていました。
  • パッケージ間でスクリプトを実行するのは非常に一般的でした(例: そして、私たちは同じことを管理するためにシンボリックリンクとbashスクリプトの脆弱な足場を使用していました。
  • その頃、私たちはLernaに出会い、私たちの要件に完璧に合っているように見えました。

    私たちは、可能な限りデフォルトを使用しようと、そこにある最も単純なパスに従うことにしました。 私たちが経験したことから、Lernaへの移行は簡単でした。 新しいLernaレポを作成することから始めます:

    mkdir my-new-monorepo && cd my-new-monorepo
    git init .
    lerna init

    いくつかの簡単な質問に答えてください(常にデフォルトに頼っていました)。 古いパッケージをreposから新しいパッケージに移動することは、予想よりも簡単でした。

    lerna import ~/projects/my-single-repo-package-1 --flatten

    --flatten必要な場合とそうでない場合がありますが、それなしで問題に直面しました。

    Lernaの驚くべきことは、それと一緒にすべてのgitコミットをもたらすことです(--flatten)、新しいレポでは、このmonorepoで開発が常 これは、monorepoに移動した後に発見したバグのためにgit blame誰かが必要になるため、絶対に不可欠です。

    Current setup

    Lernaでは、次のようなディレクトリ構造を持つすべてのパッケージの単一のリポジトリを管理するようになりました:

    packages/
    core/
    models/
    node/
    react-native/
    web/
    lerna.json
    package.json

    変更されたパッケージを公開するには、次のようにするだけです。

    lerna boostrap
    lerna publish

    毎回lerna bootstrapnpmライフサイクル自体内のすべてのパッケージングタスクを追加することも決定しました。 これはLernaとは何の関係もないことに注意してください; これは、リポジトリ構造に関係なく、理想的にはnpmパッケージに存在する必要があるものです。 各パッケージについて、次のスクリプトが個々に存在しますpacakge.json:

    "scripts": {
    ...
    "prepare": "yarn run build",
    "prepublishOnly": "./../../ci-scripts/publish-tsdocs.sh"
    ...
    }

    これは、typescriptコンパイラでパッケージを構築し、ロールアップでバンドルし、typedocでドキュメントを生成します:

    "scripts": {
    ...
    "build": "tsc --module commonjs && rollup -c rollup.config.ts && typedoc --out docs --target es6 --theme minimal --mode file src"
    ...
    }

    単一のレポ構造を持つことで、すべてのパッケージに変更が適用されるように、共通のスクリプトを単一の場所に保持することもで

    開発者フロー

    リリースから離れた開発者フローは変更されません。 開発者はGitLabに課題を作成し(または割り当てられている)、その課題の新しいブランチを作成し、コードレビューの後に変更をmasterにマージします。 リリースのライフサイクルは、非常に構造化されたプロセスに従います:

    1. マイルストーンが完了し、新しいリリースを計画しているとき、(その特定のリリースを担当する)開発者の一人がlerna versionを実行して新しいバージ
    2. Lernaは、次のバージョンを把握するための非常に便利で使いやすいプロンプトを提供します
    (master) mitter-js-sdk ツ lerna version --force-publish
    lerna notice cli v3.8.1
    lerna info current version 0.6.2
    lerna info Looking for changed packages since v0.6.2
    ? Select a new version (currently 0.6.2) (Use arrow keys)
    ❯ Patch (0.6.3)
    Minor (0.7.0)
    Major (1.0.0)
    Prepatch (0.6.3-alpha.0)
    Preminor (0.7.0-alpha.0)
    Premajor (1.0.0-alpha.0)
    Custom Prerelease
    Custom Version

    新しいバージョンが選択されると、Lernaはパッケー これ以外にも、開発者は他に何もする必要はありません。 私たちのCIは、セマンティックバージョン番号に似た名前を持つすべてのタグを構築するように設定されています。

    注意してくださいlerna version--force-publishすべてのパッケージにバージョンの正確な系列を持たせたいためです。 だから、時には異なるバージョン間で異ならないパッケージがあるでしょう。 あなたの好みに応じて、あなたはそれをしないことを選択するかもしれません。

    CIの設定

    GitLabの統合CIを使用して、すべてのプロジェクト(JSおよびJava)のビルド、テスト、および公開を行います。 新しいJS monorepoには、次の二つのステージがあります。

    1. Build
    2. Publish

    ビルドフェーズは非常にシンプルで、次の二つのスクリプトを実行します。

    lerna bootstrap
    lerna run build

    このフェーズは、パッケージの健全性を本質的に検証するために、すべての単一のコミットで実行されます。 一方、パブリッシュフェーズでは、次のように実行されます:P>

    git checkout master
    lerna bootstrap
    git reset --hard
    lerna publish from-package --yes

    GitLabがレポをクローン(または設定に応じてフェッチ)し、ビルドされるコミットをチェックアウトするため、git checkout mastergit reset --hard….. つまり、refHEADはどこを指していません。 LernaはHEADを使用して、パッケージの現在のバージョンとdetached head状態のエラーを把握します。

    lerna publish from-packagelerna publishlerna publishlerna publishlerna publishを実行すると、現在のバージョンが既に公開されていると不平を言うLernaがを実行する必要があります。lerna versionローカル。 引数は、指定されたパッケージのnpmに現在存在しないすべてのバージョンを公開するようにLernaに指示します。 これは、何らかの理由で発行が失敗し、パイプラインを再試行している場合にも役立ちます。publishフェーズは、次の正規表現クレジットに一致するタグでのみ実行するように構成されています。

    ^v(0|\d*)\.(0|\d*)\.(0|\d*)(-(0|\d*|\d**)(\.(0|\d*|\d**))*)?(\++(\.+)*)?$

    これは少し派手であり、ほとんどのチーム 注まだ行っていませんが、私たちは小さなチームなので、上記の正規表現に続くタグをGitLabで保護されているようにマークして、パッケージをnpmに公開できるあなたは私たちのmonorepoをチェックアウトすることができますhttps://github.com/mitterio/js-sdk(これは私たちの内部GitLabレポからミラーリングされています)。

    クイックノート

    一般的なスクリプトを実行するとき(typescriptドキュメントを公開するときのように)、スクリプトを実行するパッケージの詳細を知 これは、npmライフサイクルのスクリプトと、lerna runlerna execを使用して実行されるスクリプトに適用されます。 Npmの特定のパッケージに対して、npmは環境変数を使用してスクリプトでpackage.jsonpackage.jsonを持つ特定のパッケージの場合:

    {
    "name": "@mitter-io/core",
    "version": "0.6.28",
    "repository": {
    "type": "git"
    }
    }

    ライフサイクルスクリプトの実行中に次の変数を使用できます。

    npm_package_name=@mitter-io/core
    npm_package_version=0.6.28
    npm_package_repository_type=git

    Quirks/Issues

    新しいセットアップでまだ取り組んでいるいくつかのこと(そのうちのいくつか)可能かどうかはわかりませんが、すべてのパッケージに対して共通のライフサイクルスクリプトを持つことができるようにしたいと考えています。 ルートでこれらを宣言するpackage.jsonは機能しません。実際にnpmに何かを公開せずにLerna設定を完全にテストすることは非常に困難です。 どこかに--dry-runがあるかどうかはわかりません。

  • Lernaには、devDependenciesdevDependenciesdependenciesconfigブロックは必要ありませんが、プロジェクト全体で使 たとえば、Java/Kotlin monorepoでは、gradle.propertiesspringBootVersionspringCoreVersionなどの変数を格納します。 これは、個々のgradleスクリプトによって使用されます。
  • monoreposに関する私たちの考え
    最近、monoreposとの激しい議論が行われており、マイクロサービスが大流行した時代を彷彿とさせる膨大な数が再び時流に飛び乗っているのかどうかが議論されています。

    ここで従う構造は複数のmonoreposを持っており、これはmonoreposを管理するのは初めてではありません。 プラットフォームとバックエンド全体がmonorepoで、プライベートでデプロイ可能なコードと、bintrayに公開された複数のパブリック向けパッケージが含まれています。 また、メインのwebサイトはSpringバックエンドで実行されており、フロントエンドはhot reloading(webpack watch)などをサポートするwebpackにバンドル 私たちは、ツールが単にそこになかったので、組織全体で単一のモノレポを使用することを決めたことはありません。gradleは、Java monorepoとlernaに必要なすべてのツールを提供し、js SDKのmonorepo用のツールを提供するnpmライフサイクルを提供するため、単一のレポ だから、簡単に言えば、あなたのレポに入る変更の範囲を特定すると、monoreposは素晴らしいです。 私たちのJavaバックエンドのために、私たちは単一の機能のためにプロジェクト間で複数のMRsを見ました、それは私たちがこの特定のプロジェクトのた そして、JS Sdkにも同様のパターンが現れるのを見たら、Lernaに移動しました。私たちのために働くものは、異なるサイズのチームでは機能しないかもしれません。

    私たちは約9人のエンジニアの小さなチームであることに注 私たちが主に指摘したいのは、解決策の採用はバイナリである必要はなく、規定どおりに行うか、まったく行わないということです。

    私たちがmonorepoのために見た動機のいくつかは間違いなく私たちに適用され、それらの多くはそうではありませんでした。 たとえば、私たちのコードベース全体が単一のレポに移動された場合、私たちが経験するかもしれないし、経験しないかもしれない利益に関係なく、ツールを構築するための時間を惜しむことはできません。 だから、議論は本当に”単一のレポ”を持つことについてではありません—それだけでは、新しいディレクトリ構造に過ぎません。 それらの処方は、特定の問題を軽減することであり、すべての”銀の弾丸”と同様に、注意点があります。

    議論は、ソフトウェア業界で直面している一般的な問題と、どのような解決策が一般的に取られているかについてです。”common”がキーワードです。 あなたが”共通の”アプリケーションから逸脱する領域は、革新し、変更を加え、少し構築する場所です。