Backstage 应用仓库与插件接线
目标
亲手搭建 create-app 生成的 Backstage 应用仓库结构,并创建一个内部插件,将其接入前端和后端。实验 Pod 无法访问互联网,不能下载 Node 依赖,因此不进行构建,只处理目录结构和接线。
为什么重要
使用 Backstage 意味着你会拥有一个自己的 monorepo。如果无法回答“该修改哪个文件”,就无法进行任何定制,而且这个答案也无法从界面上找到。实践中尤其有三个问题反复阻碍开发者。第一,package.json 中的一个角色标记就决定该包的构建方式。第二,在新后端系统中,插件和模块不是同一种东西;名称含 module 的包若没有与之配套的插件,就不会做任何事。第三,如果只在实体页面添加标签页而不注册应用路由,标签页虽然可见,点击后却是空白页,也不会显示错误。本实验将亲手搭建这三个关键位置。
步骤
- 编写
/root/cba-app/package.json:设置private: true;在workspaces.packages中加入packages/*和plugins/*;在scripts中加入dev、build:all、tsc、test:all。build:all必须是以backstage-cli repo build开头的命令,test:all必须是以backstage-cli repo test开头的命令。 - 编写
/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使用相同命令。 - 编写
/root/cba-app/packages/backend/src/index.ts:从@backstage/backend-defaults导入并调用createBackend,准确写七次backend.add(import('...'))。注册以下包:@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等旧后端方式的痕迹。 - 编写
/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。 - 在
/root/cba-app/plugins/oncall/src/下编写三个文件。routes.ts使用createRouteRef创建并导出id: 'oncall'的rootRouteRef。plugin.ts从./routes导入该引用,用createPlugin创建oncallPlugin,再用createRoutableExtension创建OncallPage,并设置mountPoint: rootRouteRef。不要在plugin.ts中再次调用createRouteRef。index.ts重新导出oncallPlugin和OncallPage。 - 编写
/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/下所有包的名称与角色,格式为이름=역할,去重并排序。 - 编写
/root/cba-app/.github/workflows/ci.yaml:使用pull_request触发器和jobs.build.runs-on: ubuntu-latest,并准确设置六个steps。第一步使用actions/checkout,第二步使用actions/setup-node,随后四步依次通过 run 执行yarn install --immutable、yarn tsc、yarn build:all、yarn test:all。
提示
- 角色值使用
frontend、backend、frontend-plugin、backend-plugin四种。 - 名称含
-module-的后端包,是接入配套插件扩展点的组件。 - 常见错误 1:只添加标签页,却漏掉应用路由。界面不会报错,因此很难定位原因。
- 常见错误 2:在
plugin.ts中创建路由引用。文件拆分本来是为了防止循环导入,这样做会让拆分失去意义。 - 常见错误 3:在 CI 中省略
--immutable。如果锁文件能被悄悄改动,昨天通过的提交今天可能会以不同方式构建。
工作区根目录
编写 /root/cba-app/package.json:设置 private: true;在 workspaces.packages 中加入 packages/* 和 plugins/*;在 scripts 中加入 dev、build:all、tsc、test:all。build:all 必须是以 backstage-cli repo build 开头的命令,test:all 必须是以 backstage-cli repo test 开头的命令。
根包不会发布。如果不把内部插件上传到注册表,要直接使用它们,就必须在工作区路径中包含插件目录。
两个应用及其角色
编写 /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 中的一个角色标记选择构建方式。后端包的入口必须指向构建产物,而不是源代码。
新后端系统接线
编写 /root/cba-app/packages/backend/src/index.ts:从 @backstage/backend-defaults 导入并调用 createBackend,准确写七次 backend.add(import('...'))。注册以下包:@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。
内部插件不发布到注册表,因此使用内部作用域名称。前端插件声明无副作用,让打包器可以移除未使用的代码。
插件的三个源文件
在 /root/cba-app/plugins/oncall/src/ 下编写三个文件。routes.ts 使用 createRouteRef 创建并导出 id: 'oncall' 的 rootRouteRef。plugin.ts 从 ./routes 导入该引用,用 createPlugin 创建 oncallPlugin,再用 createRoutableExtension 创建 OncallPage,并设置 mountPoint: rootRouteRef。不要在 plugin.ts 中再次调用 createRouteRef。index.ts 重新导出 oncallPlugin 和 OncallPage。
创建路由引用的位置必须只有一个。把该引用设为页面扩展的挂载点。
实体页面标签与应用路由
编写 /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/ 下所有包的名称与角色,格式为 이름=역할,去重并排序。
角色值由每个包自行声明。不要手工填写,应从文件中读取并汇总。
CI 门禁
编写 /root/cba-app/.github/workflows/ci.yaml:使用 pull_request 触发器和 jobs.build.runs-on: ubuntu-latest,并准确设置六个 steps。第一步使用 actions/checkout,第二步使用 actions/setup-node,随后四步依次通过 run 执行 yarn install --immutable、yarn tsc、yarn build:all、yarn test:all。
门禁必须在合并前运行,且安装后应先进行类型检查。如果安装步骤允许修改锁文件,门禁就失去了保障作用。