APC 技術ブログ

株式会社エーピーコミュニケーションズの技術ブログです。

株式会社 エーピーコミュニケーションズの技術ブログです。

Backstageのアップデートを楽にするyarn pluginのご紹介

はじめに

新年あけましておめでとうございます。ACS事業部 亀崎です。

2025年最初の投稿も「Backstage」関連情報をお届けします。

Backstageとは

まず最初に「Backstage」とはから振り返りましょう。実は本ブログで最初に取り上げたのが2023年の3月でした。

techblog.ap-com.co.jp

Backstageは、Spotify社が開発し、現在はCNCFに寄贈された Internal Developer Portalを実現するツールです。

backstage.io

Backstageは完成されたツールではありません。皆さん自身で実行する Typesccriptで実装するソースコードを生成し、ビルドして利用するものです。 また、Backstageの大きな特徴の1つが、Pluginによる機能の拡張です。P,luginは独自の機能を追加できるものです。そしてそのPluginもOSSとして世界中の開発者によって開発されており、必要な機能を自在に追加することができます。

実際どのような機能があり、どのようにコードを生成するのか、については以下の記事で紹介させていただいておりますのでぜひご覧ください。

techblog.ap-com.co.jp

Backstageのメンテナンスの課題

さて、そんなBackstageですが2025年1月の現在でも活発に開発が進んでいるプロジェクトであり、毎月新しいバージョンが公開されています。 また、さきほどご紹介した通りさまざまな機能がPluginとして公開されていますが、実はメインとなる機能そのものもPluginとして公開され、毎月のように更新されています。

このため、基本機能だけでも数多くの依存モジュールを指定しています。

frontend

  "dependencies": {
    "@backstage-community/plugin-github-actions": "^0.6.26",
    "@backstage-community/plugin-tech-radar": "^0.7.11",
    "@backstage/app-defaults": "^1.5.15",
    "@backstage/catalog-model": "^1.7.2",
    "@backstage/cli": "^0.29.4",
    "@backstage/core-app-api": "^1.15.3",
    "@backstage/core-components": "^0.16.2",
    "@backstage/core-plugin-api": "^1.10.2",
    "@backstage/integration-react": "^1.2.2",
    "@backstage/plugin-api-docs": "^0.12.2",
    "@backstage/plugin-catalog": "^1.26.0",
    "@backstage/plugin-catalog-common": "^1.1.2",
    "@backstage/plugin-catalog-graph": "^0.4.14",
    "@backstage/plugin-catalog-import": "^0.12.8",
    "@backstage/plugin-catalog-react": "^1.15.0",
    "@backstage/plugin-org": "^0.6.34",
    "@backstage/plugin-permission-react": "^0.4.29",
    "@backstage/plugin-scaffolder": "^1.27.3",
    "@backstage/plugin-search": "^1.4.21",
    "@backstage/plugin-search-react": "^1.8.4",
    "@backstage/plugin-techdocs": "^1.12.0",
    "@backstage/plugin-techdocs-module-addons-contrib": "^1.1.19",
    "@backstage/plugin-techdocs-react": "^1.2.12",
    "@backstage/plugin-user-settings": "^0.8.17",
    "@backstage/theme": "^0.6.3",
    "@material-ui/core": "^4.12.2",
    "@material-ui/icons": "^4.9.1",
    "@roadiehq/backstage-plugin-github-pull-requests": "^2.6.0",
    "history": "^5.0.0",
    "react": "^18.0.2",
    "react-dom": "^18.0.2",
    "react-router": "^6.3.0",
    "react-router-dom": "^6.3.0"
  },
  "devDependencies": {
    "@backstage/test-utils": "^1.7.3",
    "@playwright/test": "^1.32.3",
    "@testing-library/dom": "^9.0.0",
    "@testing-library/jest-dom": "^6.0.0",
    "@testing-library/react": "^14.0.0",
    "@testing-library/user-event": "^14.0.0",
    "@types/node": "^20.0.0",
    "@types/react-dom": "*",
    "cross-env": "^7.0.0"
  },

backend

  "dependencies": {
    "@backstage/backend-defaults": "^0.6.1",
    "@backstage/backend-plugin-api": "^1.1.0",
    "@backstage/catalog-client": "^1.9.0",
    "@backstage/catalog-model": "^1.7.2",
    "@backstage/config": "^1.3.1",
    "@backstage/plugin-app-backend": "^0.4.3",
    "@backstage/plugin-auth-backend": "^0.24.1",
    "@backstage/plugin-auth-backend-module-github-provider": "^0.2.3",
    "@backstage/plugin-auth-backend-module-guest-provider": "^0.2.3",
    "@backstage/plugin-auth-node": "^0.5.5",
    "@backstage/plugin-catalog-backend": "^1.29.0",
    "@backstage/plugin-catalog-backend-module-github": "^0.7.8",
    "@backstage/plugin-catalog-backend-module-github-org": "^0.3.5",
    "@backstage/plugin-catalog-backend-module-logs": "^0.1.5",
    "@backstage/plugin-catalog-backend-module-scaffolder-entity-model": "^0.2.3",
    "@backstage/plugin-permission-backend": "^0.5.52",
    "@backstage/plugin-permission-backend-module-allow-all-policy": "^0.2.3",
    "@backstage/plugin-permission-common": "^0.8.3",
    "@backstage/plugin-permission-node": "^0.8.6",
    "@backstage/plugin-proxy-backend": "^0.5.9",
    "@backstage/plugin-scaffolder-backend": "^1.28.0",
    "@backstage/plugin-scaffolder-backend-module-github": "^0.5.4",
    "@backstage/plugin-search-backend": "^1.8.0",
    "@backstage/plugin-search-backend-module-catalog": "^0.2.6",
    "@backstage/plugin-search-backend-module-pg": "^0.5.39",
    "@backstage/plugin-search-backend-module-techdocs": "^0.3.4",
    "@backstage/plugin-search-backend-node": "^1.3.6",
    "@backstage/plugin-techdocs-backend": "^1.11.4",
    "@internal/backstage-plugin-auth-backend-module-github-as-guest-provider": "^0.1.0",
    "app": "link:../app",
    "better-sqlite3": "^8.0.0",
    "node-gyp": "^10.0.0",
    "pg": "^8.12.0"
  },
  "devDependencies": {
    "@backstage/cli": "^0.29.4"
  },

さらにご自身で独自のpluginを開発する場合、同様の依存関係(package.json)をpluginごとに作成・管理しなければなりません。多くの機能を拡張している場合はその数が2桁になることもあります。

毎月新しいバージョンが公開されていますから、これらの依存関係の指定も毎月更新していく必要があります。さすがにこれだけあると面倒なのはイメージできると思います。そこで、これを少しでも簡単にするためにバージョンアップツールが用意されていました。それが

yarn backstage-cli versions:bump

というコマンドで、package.jsonの中の "@backstage/...." の行を特定し、自動的に最新版に更新してくれました。

yarn backstage-cli versions:bump コマンドの課題

yarn backstage-cli versions:bump があれば面倒さは解消されるように思えますが、実は1つ課題があります。それはBackstageの開発スピードの速さです。

さきほど紹介した依存関係のファイルをご覧いただくとわかると思いますが、 "@backstage/... "のところのバージョン番号がバラバラです。それぞれのpluginごとにバージョンを管理しているためこのようになります。pluginごとの管理としては問題がないのですが、集合体としてのBackstageのバージョンとの関係がばらばらになることです。Backstageはその集合体としてもバージョンが管理されており、例えば2024年12月公開のバージョンは v1.34.x です。

たとえば、ご自身でPluginを開発している場合少し前のBackstageバージョンを前提に開発していることもあると思います。このタイミングで新しいPluginを追加する場合、何も指定しなければ最新バージョンのモジュールを新しいPlugin用で参照することになります。

Backstageの開発はとても活発で、これまでなかった機能が増えたり、これまで使っていた(古い)機能がなくなったりします。このため、できれば自分で開発しているPluginのBackstageバージョンは同一のものにしたいものです。 (とくに2024年後半には新Backend Systemというものが公開され、古いBackend Systemのコードは削除が進んでいるため、Backstage本体のバージョンを意識することはとても重要になっています) ところが、Backstageバージョン名と各Pluginのバージョン名には見た目上の関係性はないため、それぞれの依存モジュールのバージョンが自分の欲しいBackstageバージョンに合致しているものかは1つ1つ調べなければならない状況でした。

yarn pluginの登場

こうした問題を解決するのがBackstage用のyarn pluginです。 pluginという言葉が同じなのでややこしいのですが、ここでいうpluginは yarn というツールで利用する Backstage向けplugin という意味です。

backstage.io

このpluginを使うことで yarn で依存関係モジュールのバージョン番号の解決を Backstageのバージョンごとに実施してくれます。

それでは簡単にインストールと設定方法をご紹介します。

yarn pluginのインストールと設定

ご利用になっているBackstageのソースコードのトップディレクトリで以下のコマンドを実行します。

yarn plugin import https://versions.backstage.io/v1/tags/main/yarn-plugin

これで yarn plugiinがインストールされます。

続いて、依存関係(package.json)の内容の更新です。依存先の "@backstage/... " のバージョン指定をすべて backstage:^ に置き換えます。さきほどご紹介したファイルを例にとると以下のようになります。

frontend

  "dependencies": {
    "@backstage-community/plugin-github-actions": "^0.6.26",
    "@backstage-community/plugin-tech-radar": "^0.7.11",
    "@backstage/app-defaults": "backstage:^",
    "@backstage/catalog-model": "backstage:^",
    "@backstage/cli": "backstage:^",
    "@backstage/core-app-api": "backstage:^",
    "@backstage/core-components": "backstage:^",
    "@backstage/core-plugin-api": "backstage:^",
    "@backstage/integration-react": "backstage:^",
    "@backstage/plugin-api-docs": "backstage:^",
    "@backstage/plugin-catalog": "backstage:^",
    "@backstage/plugin-catalog-common": "backstage:^",
    "@backstage/plugin-catalog-graph": "backstage:^",
    "@backstage/plugin-catalog-import": "backstage:^",
    "@backstage/plugin-catalog-react": "backstage:^",
    "@backstage/plugin-org": "backstage:^",
    "@backstage/plugin-permission-react": "backstage:^",
    "@backstage/plugin-scaffolder": "backstage:^",
    "@backstage/plugin-search": "backstage:^",
    "@backstage/plugin-search-react": "backstage:^",
    "@backstage/plugin-techdocs": "backstage:^",
    "@backstage/plugin-techdocs-module-addons-contrib": "backstage:^",
    "@backstage/plugin-techdocs-react": "backstage:^",
    "@backstage/plugin-user-settings": "^0.8.17",
    "@backstage/theme": "backstage:^",
    "@material-ui/core": "^4.12.2",
    "@material-ui/icons": "^4.9.1",
    "@roadiehq/backstage-plugin-github-pull-requests": "^2.6.0",
    "history": "^5.0.0",
    "react": "^18.0.2",
    "react-dom": "^18.0.2",
    "react-router": "^6.3.0",
    "react-router-dom": "^6.3.0"
  },
  "devDependencies": {
    "@backstage/test-utils": "backstage:^",
    "@playwright/test": "^1.32.3",
    "@testing-library/dom": "^9.0.0",
    "@testing-library/jest-dom": "^6.0.0",
    "@testing-library/react": "^14.0.0",
    "@testing-library/user-event": "^14.0.0",
    "@types/node": "^20.0.0",
    "@types/react-dom": "*",
    "cross-env": "^7.0.0"
  },

backend

  "dependencies": {
    "@backstage/backend-defaults": "backstage:^",
    "@backstage/backend-plugin-api": "backstage:^",
    "@backstage/catalog-client": "backstage:^",
    "@backstage/catalog-model": "backstage:^",
    "@backstage/config": "backstage:^",
    "@backstage/plugin-app-backend": "backstage:^",
    "@backstage/plugin-auth-backend": "backstage:^",
    "@backstage/plugin-auth-backend-module-github-provider": "backstage:^",
    "@backstage/plugin-auth-backend-module-guest-provider": "backstage:^",
    "@backstage/plugin-auth-node": "backstage:^",
    "@backstage/plugin-catalog-backend": "backstage:^",
    "@backstage/plugin-catalog-backend-module-github": "backstage:^",
    "@backstage/plugin-catalog-backend-module-github-org": "backstage:^",
    "@backstage/plugin-catalog-backend-module-logs": "backstage:^",
    "@backstage/plugin-catalog-backend-module-scaffolder-entity-model": "backstage:^",
    "@backstage/plugin-permission-backend": "backstage:^",
    "@backstage/plugin-permission-backend-module-allow-all-policy": "backstage:^",
    "@backstage/plugin-permission-common": "backstage:^",
    "@backstage/plugin-permission-node": "backstage:^",
    "@backstage/plugin-proxy-backend": "backstage:^",
    "@backstage/plugin-scaffolder-backend": "backstage:^",
    "@backstage/plugin-scaffolder-backend-module-github": "backstage:^",
    "@backstage/plugin-search-backend": "backstage:^",
    "@backstage/plugin-search-backend-module-catalog": "backstage:^",
    "@backstage/plugin-search-backend-module-pg": "backstage:^",
    "@backstage/plugin-search-backend-module-techdocs": "backstage:^",
    "@backstage/plugin-search-backend-node": "backstage:^",
    "@backstage/plugin-techdocs-backend": "backstage:^",
    "@internal/backstage-plugin-auth-backend-module-github-as-guest-provider": "^0.1.0",
    "app": "link:../app",
    "better-sqlite3": "^8.0.0",
    "node-gyp": "^10.0.0",
    "pg": "^8.12.0"
  },
  "devDependencies": {
    "@backstage/cli": "backstage:^"
  },

あとは yarn install を実行すると、毎回トップディレクトリにある backstage.json で記載されているBackstageのバージョンにあわせたモジュールをダウンロードしてくれます。

しくみ

yarn pluginのソースコードはこちらにあるのでご興味のある方はご覧ください。

github.com

Backstageを公開する際、それぞれpluginのバージョンを記録しています。そのツールがこちらになります。

github.com

たとえば v1.34.0 の情報は以下のようなものです。

https://versions.backstage.io/v1/releases/1.34.0/manifest.json

{
  "releaseVersion": "1.34.0",
  "packages": [
    {
      "name": "@backstage/app-defaults",
      "version": "1.5.15"
    },
    {
      "name": "@backstage/backend-app-api",
      "version": "1.1.0"
    },
    {
      "name": "@backstage/backend-defaults",
      "version": "0.6.0"
    },
    {
      "name": "@backstage/backend-dev-utils",
      "version": "0.1.5"
    },
  ....  以下略。

yarn install を実行する際、この情報を参照している、というわけです。

このツールを使うことで、 "@backstage/... " モジュールのバージョンを意識する必要がなくなり、とても楽な気分でいられます。

そしてもう1つ楽なことがあります。それは毎月のBackstage本体のアップデートです。これまで 全package.jsonファイルが更新されていましたが、今後はそれも不要になり、 backstage.json のバージョンだけ更新すればよいということになります。ツールにより自動的に行われていた部分ですが、それでも差分確認する範囲が減るということで、間違いがすくなくなります。

ということで、2025年の最初はBackstageのアップデートやPlugin追加の作業を楽にする yarn pluginをご紹介させていただきました。

この記事が皆様のBackstageライフを少しでもよいものにできれば幸いです。

さいごに

弊社ではPlatform Engineering、Backstageに関するご支援をさせていただいております。ご興味のある方ぜひご連絡ください。

www.ap-com.co.jp

www.ap-com.co.jp

www.ap-com.co.jp