TT Lab
はじめる
学ぶ 学習パス コース

CBA — Backstage認定アソシエイト

Backstageアプリリポジトリとプラグインの配線

TT Labで続きを見る

目標

create-appが作り出すBackstageアプリのリポジトリの構造を自分で組み立て、社内プラグインを1つ作って、フロントエンドとバックエンドの両方に配線します。ラボのPodはインターネットが遮断されていて、Nodeの依存関係をダウンロードできないので、ビルドは行わず、構造と配線だけを扱います。

なぜ重要なのか

Backstageを使うということは、自分たちが所有するモノレポを1つ持つという意味です。そのため、「どのファイルを直す必要があるか」に答えられなければ、何もカスタマイズできず、画面から見つけられる答えもありません。特に、3つのことが、実務で繰り返し人を止めます。1つ目は、package.jsonの役割の表示1つが、そのパッケージのビルド方式を決めること。2つ目は、新しいバックエンドシステムでは、プラグインとモジュールが別のもので、名前にmoduleが入ったものは、対になるプラグインがなければ何もしないこと。3つ目は、エンティティページにタブだけを付けて、アプリのルートを登録しないと、タブは見えるのに、クリックすると空の画面が表示され、エラーも出ないことです。このラボは、その3つの箇所を、手で作ってみます。

ステップ

  1. /root/cba-app/package.jsonを書いてください。private: true、workspaces.packagesにpackages/*とplugins/*の2つ、scriptsにdev・build:all・tsc・test:allの4つです。build:allはbackstage-cli repo buildで始まるコマンド、test:allはbackstage-cli repo testで始まるコマンドです。
  2. /root/cba-app/packages/app/package.jsonを書いてください。name: app、backstage.role: frontend、scripts.startはbackstage-cli package startです。/root/cba-app/packages/backend/package.jsonも書いてください。name: backend、backstage.role: backend、mainはdist/で始まるパス、scripts.startは同じコマンドです。
  3. /root/cba-app/packages/backend/src/index.tsを書いてください。@backstage/backend-defaultsからcreateBackendを取り込んで呼び出し、backend.add(import('...'))をちょうど7回書きます。登録するパッケージは、@backstage/plugin-app-backend、@backstage/plugin-catalog-backend、@backstage/plugin-catalog-backend-module-github、@backstage/plugin-scaffolder-backend、@backstage/plugin-techdocs-backend、@backstage/plugin-auth-backend、@backstage/plugin-auth-backend-module-github-providerです。最後にbackend.start()を呼び出し、createRouter・PluginEnvironment・apiRouterのような旧バックエンド方式の痕跡は残さないでください。
  4. /root/cba-app/plugins/oncall/package.jsonを書いてください。name: @internal/plugin-oncall、backstage.role: frontend-plugin、sideEffects: false、dependenciesに@backstage/core-plugin-apiです。/root/cba-app/plugins/oncall-backend/package.jsonも書いてください。name: @internal/plugin-oncall-backend、backstage.role: backend-plugin、mainはdist/で始まるパス、dependenciesに@backstage/backend-plugin-apiです。
  5. /root/cba-app/plugins/oncall/src/の下に3つのファイルを書いてください。routes.tsは、createRouteRefでid: 'oncall'のrootRouteRefを作ってエクスポートします。plugin.tsは、./routesからその参照を取り込み、createPluginでoncallPluginを作り、createRoutableExtensionでOncallPageを作って、mountPoint: rootRouteRefをかけます。plugin.tsの中でcreateRouteRefをもう一度呼び出さないでください。index.tsは、oncallPluginとOncallPageの2つを再エクスポートします。
  6. /root/cba-app/packages/app/src/components/catalog/EntityPage.tsxを書いてください。@internal/plugin-oncallからOncallPageを取り込み、isKind('component')の条件の中で、EntityLayout.Routeでpath="/oncall"、title="On-call"のタブを付けます。/root/cba-app/packages/app/src/App.tsxも書いてください。同じプラグインを取り込んで、path="/oncall"のルートに<OncallPage />をelementとしてかけます。
  7. /root/cba-app/packages.txtに、packages/とplugins/の下のすべてのパッケージの名前と役割を、이름=역할(プレースホルダーは名前と役割です)の形式で、1行に1つずつ、重複なしで並べ替えて書いてください。
  8. /root/cba-app/.github/workflows/ci.yamlを書いてください。pull_requestトリガー、jobs.build.runs-on: ubuntu-latest、stepsはちょうど6つです。1つ目はactions/checkout、2つ目はactions/setup-node、そのあとの4つは、順番にyarn install --immutable、yarn tsc、yarn build:all、yarn test:allをrunで実行します。

参考

ワークスペースのルート

/root/cba-app/package.jsonを書いてください。private: true、workspaces.packagesにpackages/*とplugins/*の2つ、scriptsにdev・build:all・tsc・test:allの4つです。build:allはbackstage-cli repo buildで始まるコマンド、test:allはbackstage-cli repo testで始まるコマンドです。

ルートのパッケージはデプロイしません。そして、社内プラグインをレジストリにアップロードせずに使うには、ワークスペースのパスにプラグインのディレクトリが入っている必要があります。

アプリ2つと役割

/root/cba-app/packages/app/package.jsonを書いてください。name: app、backstage.role: frontend、scripts.startはbackstage-cli package startです。/root/cba-app/packages/backend/package.jsonも書いてください。name: backend、backstage.role: backend、mainはdist/で始まるパス、scripts.startは同じコマンドです。

backstage-cliは、package.jsonの役割の表示1つを見て、ビルド方式を選びます。バックエンドパッケージの入口は、ソースではなく、ビルドのアーティファクトを指す必要があります。

新しいバックエンドシステムの配線

/root/cba-app/packages/backend/src/index.tsを書いてください。@backstage/backend-defaultsからcreateBackendを取り込んで呼び出し、backend.add(import('...'))をちょうど7回書きます。登録するパッケージは、@backstage/plugin-app-backend、@backstage/plugin-catalog-backend、@backstage/plugin-catalog-backend-module-github、@backstage/plugin-scaffolder-backend、@backstage/plugin-techdocs-backend、@backstage/plugin-auth-backend、@backstage/plugin-auth-backend-module-github-providerです。最後にbackend.start()を呼び出し、createRouter・PluginEnvironment・apiRouterのような旧バックエンド方式の痕跡は残さないでください。

新しいバックエンドシステムは、ルーターを手で配線しません。そして、名前にmoduleが入ったものは、単独では動作しないので、対になるプラグインが一緒に登録されている必要があります。

社内プラグインのパッケージ

/root/cba-app/plugins/oncall/package.jsonを書いてください。name: @internal/plugin-oncall、backstage.role: frontend-plugin、sideEffects: false、dependenciesに@backstage/core-plugin-apiです。/root/cba-app/plugins/oncall-backend/package.jsonも書いてください。name: @internal/plugin-oncall-backend、backstage.role: backend-plugin、mainはdist/で始まるパス、dependenciesに@backstage/backend-plugin-apiです。

社内プラグインはレジストリにアップロードしないので、社内スコープの名前を使います。フロントエンドプラグインは、バンドラーが使われないコードを削れるように、副作用がないことを宣言します。

プラグインのソース3ファイル

/root/cba-app/plugins/oncall/src/の下に3つのファイルを書いてください。routes.tsは、createRouteRefでid: 'oncall'のrootRouteRefを作ってエクスポートします。plugin.tsは、./routesからその参照を取り込み、createPluginでoncallPluginを作り、createRoutableExtensionでOncallPageを作って、mountPoint: rootRouteRefをかけます。plugin.tsの中でcreateRouteRefをもう一度呼び出さないでください。index.tsは、oncallPluginとOncallPageの2つを再エクスポートします。

ルート参照を作る場所は、1か所だけでなければなりません。ページの拡張には、その参照をマウントポイントとしてかけてください。

エンティティページのタブとアプリのルート

/root/cba-app/packages/app/src/components/catalog/EntityPage.tsxを書いてください。@internal/plugin-oncallからOncallPageを取り込み、isKind('component')の条件の中で、EntityLayout.Routeでpath="/oncall"、title="On-call"のタブを付けます。/root/cba-app/packages/app/src/App.tsxも書いてください。同じプラグインを取り込んで、path="/oncall"のルートに<OncallPage />をelementとしてかけます。

タブを付ける場所と、ルートを登録する場所は、別のファイルです。片方だけでは、画面にはエラーがないのに、何も出ません。

パッケージのインベントリ

/root/cba-app/packages.txtに、packages/とplugins/の下のすべてのパッケージの名前と役割を、이름=역할(プレースホルダーは名前と役割です)の形式で、1行に1つずつ、重複なしで並べ替えて書いてください。

役割は、各パッケージが自分で宣言した値です。手で書かずに、ファイルから読み取って集めてください。

CIゲート

/root/cba-app/.github/workflows/ci.yamlを書いてください。pull_requestトリガー、jobs.build.runs-on: ubuntu-latest、stepsはちょうど6つです。1つ目はactions/checkout、2つ目はactions/setup-node、そのあとの4つは、順番にyarn install --immutable、yarn tsc、yarn build:all、yarn test:allをrunで実行します。

マージの前に動くゲートでなければならず、順序は、インストールのあとに型検査です。インストールの段階でロックファイルが変わりうるなら、ゲートが守るものがありません。