Backstage v1.31.0 版本深度解析:Backend System 1.0 稳定发布与新前端系统演进
【免费下载链接】backstageBackstage is an open framework for building developer portals项目地址: https://gitcode.com/GitHub_Trending/ba/backstage
Backstage v1.31.0 是开发者门户框架演进历程中的一个标志性版本:全新的后端系统(Backend System)在历经多轮迭代后正式以 1.0 版本稳定发布,同时新前端系统(New Frontend System)完成多轮 API 收口,应用运行时模板化(App Runtime Templating)机制落地。本文以 官方 v1.31.0 发布说明 为骨架,结合仓库内 v1.31.0 完整变更日志 及对应包源码,逐项解读本版本的核心变更、破坏性改动与升级要点,帮助你评估现有部署的迁移成本并快速完成版本升级。
一、版本概览与升级路径
v1.31.0 的完整包级变更记录保存在 docs/releases/v1.31.0-changelog.md 中,按@backstage/*包逐个列出 Major / Minor / Patch 变更。官方强烈建议所有 Backstage 项目紧跟最新版本,具体升级操作步骤可参考 keeping-backstage-updated 指南。
本次发布的核心主题可以概括为三个层面:
- 后端体系收编:新后端系统稳定为 1.0,旧式
createRouter形态的后端插件开始进入淘汰通道; - 前端体系收编:新前端系统移除 V1 扩展支持,全面转向 Blueprint 与模块(Module)模型;
- 运行时能力增强:前端应用支持运行时配置模板化,多个组件获得新的配置项与测试工具。
需要特别提醒的是:本版本包含多处BREAKING(破坏性)变更,升级前建议对照本文第四、五节逐项检查自己的代码。
二、核心里程碑:Backend System 1.0 稳定发布
原发布说明将本版本称为 "Backend System 1.0 🎉",意味着新后端系统的 API 已经稳定,在 2.0 之前不应再出现破坏性变更,具体约束见 package versioning policy。
新后端系统相比旧式“基于约定(convention-based)”的插件结构,核心变化在于:后端及其功能(features)通过依赖注入像拼图一样组合在一起,插件可以动态扩展彼此的行为,并复用一系列强大的核心服务(core services)。官方文档对后端系统的架构说明见 docs/backend-system/index.md,迁移指引见 migrating backends 与 migrating plugins。主仓库与 community-plugins 仓库已经基本完成向新后端系统的迁移,社区也正推进对已迁移插件的旧后端能力进行弃用清理。
2.1 包版本状态与技术细节
从 v1.31.0-changelog 可以确认以下包级事实:
晋升为 major version 1(停止接收 0.x 的功能更新):
| 包 | v1.31.0 版本 | 说明 |
|---|---|---|
@backstage/backend-app-api | 1.0.0 | 后端应用运行时(backend instance)核心实现 |
@backstage/backend-plugin-api | 1.0.0 | 后端插件 / 模块 / 服务工厂的公开 API |
@backstage/backend-test-utils | 1.0.0 | 后端测试工具,含mockServices与createServiceMock等 |
弃用并冻结(停止更新):
@backstage/backend-common(0.25.0,最后一个版本)@backstage/backend-tasks
官方建议用上表中的三个 1.x 包加@backstage/backend-defaults进行替换;在渐进迁移期间仍可短期使用@backstage/backend-common中的兼容适配器(例如legacyPlugin/makeLegacyPlugin会自带 identity 与 token manager 的 shim 实现)。
被完全移除的核心服务:
coreServices.identitycoreServices.tokenManager
这两项需要迁移到新的认证系统(见 docs/auth/index.md 与仓库内相关教程)。此外,@backstage/backend-defaults中旧式 token manager 的向后兼容回退也被移除——这意味着:对新后端实例中不支持新认证系统的插件发起的请求将直接失败,而不再静默回退到旧 token manager。因此升级时必须确保部署中的所有插件都运行在同一个新后端系统实例内,新旧混合部署会引发认证问题。
2.2 创建 API 的返回值形态变化(重要破坏性变更)
changelog 中 ID 为d425fc4的变更贯穿了几乎所有后端包,是本次升级中最需要留意的行为变化:
createBackendPlugin、createBackendModule、createServiceFactory的返回值从“一个返回 feature 的函数”变为直接返回BackendFeature/ServiceFactory对象;createServiceFactory不再接受“以函数形式直接传入 options”的回调写法;- 受此影响,
coreServices.*的 service ref 形态也随之变化。
对测试代码的影响最直接:如果之前写的是createBackendModule({...})()(注意末尾多余的一对括号),现在可以直接去掉这对括号。同样的写法也可能出现在packages/backend/src/index.ts中——在该文件中以backend.add(...)方式注册插件、模块与服务时,请确认传入的是 feature 本身而不是调用后的结果。若之前依赖createServiceFactory传函数来注入选项,可以改用新的 multiton 模式,或将相关设置挪到 app-config 中。
2.3 特性发现机制的演进:discoveryFeatureLoader
@backstage/backend-defaults中弃用了featureDiscoveryServiceFactory/featureDiscoveryServiceRef,取而代之的是新的discoveryFeatureLoader。它作为一个后端系统 feature loader,会从当前package.json及其依赖中自动发现后端特性。changelog 给出的用法如下:
import { createBackend } from '@backstage/backend-defaults'; import { discoveryFeatureLoader } from '@backstage/backend-defaults'; const backend = createBackend(); // ... backend.add(discoveryFeatureLoader); // ... backend.start();同样地,@backstage/backend-dynamic-feature-service中弃用了dynamicPluginsServiceRef/dynamicPluginsServiceFactory/dynamicPluginsServiceFactoryWithOptions,改为使用dynamicPluginsFeatureDiscoveryLoader,且该 loader 支持传入选项(例如自定义moduleLoader):
import { createBackend } from '@backstage/backend-defaults'; import { dynamicPluginsFeatureDiscoveryLoader } from '@backstage/backend-dynamic-feature-service'; import { myCustomModuleLoader } from './myCustomModuleLoader'; const backend = createBackend(); backend.add( dynamicPluginsFeatureDiscoveryLoader({ moduleLoader: myCustomModuleLoader, }), ); backend.start();2.4 其余值得关注的后端行为变化
@backstage/backend-app-api:所有 backend 实例共享同一组process退出监听器,退出时会等待所有实例关闭后再结束进程,修复了测试中的EventEmitter泄漏告警;依赖缺失时的错误信息现在会附带插件与模块 ID,更易于定位问题。@backstage/backend-defaults:DatabaseManager.forPlugin现在要求传入deps(logger 与 lifecycle 服务),并直接返回DatabaseService;CacheManager.forPlugin直接返回CacheService,不再需要额外的.getClient()调用;新增skipMigrations: true配置项,可在全局或按插件 ID 跳过数据库迁移;cache 服务的 TTL 现在支持人类可读的时长格式(如'2h')。@backstage/backend-common:host discovery 实现不再接受basePath选项;dropDatabase函数被移除且无替代品。@backstage/backend-test-utils:新增mockErrorHandler工具用于在测试中 mock 错误中间件;mockServices.rootConfig.mock与mockServices.rootHttpRouter.factory的定义得到修正。
如果确实还有依赖 identity / token manager 的存量插件,changelog 提供了在自有后端中手动重建这两个服务的示例(identity 基于DefaultIdentityClient,token manager 基于ServerTokenManager并开启allowDisabledTokenManager),可作为短期过渡手段。
三、新前端系统持续演进:模块化收口
v1.31.0 在新前端系统上做了一轮密集的 API 收口,涉及多个破坏性移除与替代方案。
3.1 新增 @backstage/plugin-app 包
新引入的 @backstage/plugin-app 包(plugins/app/src/plugin.ts)负责承载内建扩展(built-in extensions),并为覆盖它们提供入口——通过appPlugin.override()可以对内建扩展进行定制。其配套的后端包@backstage/plugin-app-backend也同步修复了依赖元数据问题。从源码结构看,该插件下还细分了apis、extensions、components等目录,体现了“内建能力模块化、可替换”的设计意图。
3.2 namespace 不再必填,默认取 pluginId
createExtension/createExtensionBlueprint/createFrontendModule等 API 的namespace参数不再要求显式提供,未提供时默认使用其安装所在插件的 ID。这也意味着通过 ID 直接覆盖 API 时,ID 可能因包含 pluginId 而发生变化(见@backstage/core-compat-api的对应破坏性说明)。
3.3 createExtensionOverrides 弃用,改用 createFrontendModule
createExtensionOverrides被弃用,新方法 createFrontendModule 需要提供必填的pluginId,用于声明所提供扩展要关联/覆盖/补全的目标插件。changelog 给出了对照迁移示例:
// Before createExtensionOverrides({ extensions: [ createExtension({ name: 'my-extension', namespace: 'my-namespace', kind: 'test', // ... }), ], }); // After createFrontendModule({ pluginId: 'my-namespace', extensions: [ createExtension({ name: 'my-extension', kind: 'test', // ... }), ], });从 createFrontendModule.ts 的源码可以确认:模块会以pluginId作为解析扩展定义的默认 namespace,并生成$$type: '@backstage/FrontendModule'的模块对象;当模块提供的扩展与插件内建扩展 ID 相同时,模块中的扩展总是优先;若目标插件不在应用中,模块会被直接忽略。源码注释还推荐模块变量的命名规范为<pluginId>Module<ModuleName>。
此外,createExtensionInput新增replaces选项,允许将缺失的attachTo点重定向到新创建扩展的输入上(例如attachTo: { id: 'app', input: 'themes' }被重定向到api:app-theme的themes输入),这是内建 API 从 app 扩展迁移到独立 API 扩展的技术基础。
3.4 createApp 迁移到 frontend-defaults
@backstage/frontend-app-api中导出的createApp已被弃用,应从@backstage/frontend-defaults导入同名函数。这一设计与新后端系统“从backend-defaults获取默认实现”的模式对齐。新包 packages/frontend-defaults/src/createApp.tsx 提供默认的应用装配,并附带CreateAppOptions类型与createPublicSignInApp(用于创建公开入口的应用)。同时createSpecializedApp现在创建的是不含默认结构/API 的“裸应用”,如需原功能可安装app插件补齐。
3.5 V1 扩展支持移除与 Blueprint 化
- V1 扩展支持被移除:所有扩展必须使用数组形式的 outputs,此前已弃用的对象形式不再被支持。
- 旧式扩展创建器全部移除:所有存在 Blueprint 对等物的
create<Kind>Extension创建器均已删除,应迁移到<Kind>Blueprint.make(唯一的例外是createComponentExtension得以保留)。 createExtensionTester的.render()方法被移除:应直接使用renderInTestApp配合tester.reactElement()完成渲染。ExtensionDefinition/ExtensionBlueprint类型参数收口为单一对象参数:例如ExtensionDefinition<any, any>改为ExtensionDefinition,ExtensionDefinition<TConfig>改为ExtensionDefinition<{ config: TConfig }>;如需推断参数可借助ExtensionDefinitionParameters。这是一次即刻生效的破坏性变更,但仅影响类型层面的使用,不影响运行时行为。
这些移除项对应的迁移指引记录在 前端系统迁移文档 的 1.30 / 1.31 小节。本版本另一个运行时细节是:defaultConfigLoader现在会优先读取页面中script[type="backstage.io/config"]标签内的 JSON 序列化AppConfig数组(实现见 defaultConfigLoader.ts),若存在这些标签则不再使用静态资源中注入的配置——这一机制与下文的 App Runtime Templating 直接衔接。
四、App 运行时模板化(App Runtime Templating)
@backstage/plugin-app-backend在本版本获得重大能力升级:支持在运行时注入前端配置的所有部分,包括 public path 与模板化进index.html的配置值。
4.1 构建产物变化:index.html.tmpl
@backstage/cli的前端构建流程现在会额外输出一个index.html.tmpl文件(详见 packages/cli/CHANGELOG.md 中0e1a817相关条目)。该文件是未做模板化的index.html,并带有一个backstage-public-pathmeta 标签,由@backstage/cli/config/webpack-public-path.js入口脚本在运行时据此设置 Webpack bundle 的运行时 public path。
4.2 运行时模板化原理
如果构建产物中存在index.html.tmpl,app后端将基于自身持有的配置,用它模板化生成新的index.html。核心实现在 injectConfigIntoHtml.ts:
- 读取
index.html.tmpl内容(常量HTML_TEMPLATE_NAME,第 23 行); - 通过 lodash 的
compileTemplate以<%= ... %>插值语法编译模板(第 44-46 行); - 向模板上下文注入
config与publicPath(publicPath由app.baseUrl的路径部分解析而来,见第 73-78 行的resolvePublicPath); - 在
</head>前插入script[type="backstage.io/config"]标签,内含完整的AppConfig数组(第 54-68 行),并对</script、<!--等序列做转义处理以避免注入破坏。
从 router.ts 可以看到,readFrontendConfig会结合后端配置、process.env与 config schema 读取前端配置;若配置了app.disableConfigInjection: true,则跳过该注入流程。
4.3 关键行为影响(Breaking)
changelog(590fb2d条目)明确指出这是破坏性变更,对部署形态产生两点影响:
- 必须把构建期配置交给后端:模板化依赖 app 后端在运行时持有正确的前端配置;
- public path 无需再在构建期硬编码:只需在运行时为 app 后端插件提供正确的
app.baseUrl即可。
副作用是index.html会以易读的形式直接呈现前端配置(这些数据此前也存在于前端,但被注入并隐藏在静态 bundle 深处),这反而有利于调试。该行为默认开启,可通过配置关闭:
app: disableConfigInjection: true五、认证与权限相关变化
5.1 Guest 认证不再阻塞生产启动
@backstage/plugin-auth-backend-module-guest-provider的行为发生重要变化:不再因未设置dangerouslyAllowOutsideDevelopment而在生产环境启动时让后端直接启动失败,改为在认证尝试时拒绝请求。此前“启动即失败”的模式可能引发连锁问题,例如数据库迁移表被长期锁定。源码佐证位于 authenticator.ts:当非开发环境且未开启auth.providers.guest.dangerouslyAllowOutsideDevelopment时抛出拒绝错误;resolvers.ts 中的 sign-in resolver 也给出对应的提示信息,指引在生产环境显式开启该配置项(不建议)。
5.2 最后两个 auth provider 完成迁移
Auth0 与 Bitbucket Server 两个后端认证 provider 迁移到了新后端模块:
@backstage/plugin-auth-backend-module-auth0-provider@0.1.0(changelogd908d8c条目);@backstage/plugin-auth-backend-module-bitbucket-server-provider@0.1.0(changelog527d973条目)。
至此,社区推动的 auth providers 迁移工作(@backstage/plugin-auth-backend内部实现全面改为基于新模块)正式收官。相关模块与文档可在仓库的 plugins/auth-backend-module-* 系列 中找到。
5.3 Catalog 新增权限点
如果启用了权限系统(见 permissions 概览),需要关注两个新的权限保护点(定义于 catalog-common/src/permissions.ts):
| 端点 | 新增权限 | 影响 |
|---|---|---|
analyze-location | catalog.location.analyze | 分析/注册位置的接口受权限保护 |
validate-entity | catalog.entity.validate | 实体校验接口受权限保护 |
若未调整策略,启用了权限系统的部署中这些接口可能默认被拒绝访问,请务必按需更新 permission policy。
六、Catalog 生态改进
6.1 新的 catalogServiceMock 测试工具
@backstage/plugin-catalog-node的/testUtils子路径新增catalogServiceMock,用于在测试中注入 mock 或 fake 的 catalog 客户端。实现位于 catalog-node/src/testUtils/catalogServiceMock.ts:
catalogServiceMock(options?):返回一个基于内存存储的 fake catalog 客户端(InMemoryCatalogClient),可预置静态实体集合;catalogServiceMock.factory(...):以createServiceFactory形式返回可注册到测试后端的 catalog 服务工厂;catalogServiceMock.mock:基于createServiceMock生成各方法均为jest.fn()的 mock 客户端(getEntities、queryEntities、addLocation、validateEntity、analyzeLocation等约 20 个方法),可按需覆写以做断言。
用法示例(测试中注入带预置实体的 fake catalog):
import { catalogServiceMock } from '@backstage/plugin-catalog-node/testUtils'; import { catalogServiceRef } from '@backstage/plugin-catalog-node'; import { startTestBackend } from '@backstage/backend-test-utils'; const fakeCatalog = catalogServiceMock({ entities: [ { apiVersion: 'backstage.io/v1alpha1', kind: 'Component', metadata: { name: 'example' } }, ], }); await startTestBackend({ features: [ catalogServiceMock.factory({ entities: /* ... */ }), // 或在需要断言时使用 catalogServiceMock.mock ], });这与其姊妹能力@backstage/catalog-client/testUtils的InMemoryCatalogClient相互配合,显著降低了测试涉及 catalog 交互时的 mock 成本。
6.2 Catalog Client 性能优化与排序语义变化
changelog(1882cfe条目)说明:getEntities的排序逻辑从 catalog 客户端移入数据库查询完成。此前调用方在客户端侧执行排序,在拉取大量实体时会产生显著 CPU 热点;服务端化后,大规模拉取场景下的调用方 CPU 占用有望明显下降。
需要留意的是行为差异:升级后客户端返回的实体顺序可能与旧版本不同。如果业务代码依赖旧客户端排序结果,需要相应更新后端插件或调用代码;同时by-query调用还修复了“按并非所有实体都存在的字段排序导致结果缺失”的问题(53cce86条目)。另外,catalog-model为 Component kind 新增了dependencyOf属性,配合既有dependsOn可双向构建依赖关系图。
6.3 GitLab 组织 provider:relations 配置
@backstage/plugin-catalog-backend-module-gitlab的 GitLab org entity provider 新增relations配置项,用于控制 GitLab 成员关系在 Backstage 中的建模方式;原有allowInherited选项被弃用,因为其语义现在可用relations: [INHERITED]表达。
依据 config.d.ts,relations数组支持以下值(对应 GitLab GraphQL 的GroupMemberRelation枚举):
| 取值 | 含义 |
|---|---|
DIRECT | 组的直接成员。始终包含,即使未显式列出也无法排除 |
INHERITED | 从父(祖先)组继承的成员 |
DESCENDANTS | 来自子(后代)组的成员 |
SHARED_FROM_GROUPS | 从其他组共享而来的成员 |
配置示例(CHANGELOG 原文):
catalog: providers: gitlab: development: relations: - INHERITED从 lib/client.ts 的实现可以看到,relations会作为 GraphQL 分页查询的变量传入,测试代码(src/__testUtils__/handlers.ts)也验证了不同relations组合下的成员拉取行为。同模块还新增includeUsersWithoutSeat配置项,允许导入无付费席位的用户(如 GitLab Free/SaaS),默认false。
七、Scaffolder 表单与字段修复
7.1 liveOmit 与 omitExtraData
Scaffolder 表单(基于 rjsf)新增对liveOmit与omitExtraData两个选项的支持,用于裁剪用户提交数据中的多余字段,避免最终收集到的参数包含 schema 之外的冗余数据。该能力由@backstage/plugin-scaffolder与@backstage/plugin-scaffolder-react同步提供(changelog4baad34条目),官方计划在本版本先行测试,若表现稳定将在下一个 mainline 版本中提升为默认行为。
7.2 SecretField 增强与 ReviewState 修复
SecretField(密钥字段)现在支持从 schema 设置disabled、required,以及minLength/maxLength约束;并修复了嵌套对象中 required/disabled 不生效的问题(changelog3ebb64f条目)。ReviewState组件修复了嵌套字段ui:backstage选项的解析问题;带重复尾段 key 的字段现在能全部展示,key 的格式化改为以>分隔完整 schema 路径(8dd6ef6条目);多步模板中 schema 选项处理问题也得到修复(1f3c5aa条目);secret 字段在 review 页面以固定数量的星号展示(9a0672a条目)。- 新增
ui:backstage.review.name选项,可为 review 页的自定义条目命名,并支持渲染 schema 的title属性而非 key 名(4512f71条目)。
7.3 其他 Scaffolder 相关变化
- 新增
publish:bitbucketCloud:pull-requestscaffolder action(df9ae9e条目); debug:logaction 支持列出文件内容(f0c6b25条目);OwnedEntityPicker的ui:options现在透传给EntityPicker,因此可使用allowArbitraryValues、defaultNamespace等选项(b0a5c9f条目);TemplateListPage新增EntityOwnerPicker用于按 owner 过滤,并移除重复标题(5143616/0944334条目);MultiEntityPicker在达到 JSONSchemamaxItems上限后自动禁用(7976081条目);@backstage/plugin-scaffolder-backend的createRouter及关联类型被标记为 deprecated,明确指引改用新后端系统初始化。
八、Yarn v4 成为新项目默认
使用@backstage/create-app创建的新仓库现在默认采用 Yarn 4(含其后的多个版本改进),改善了开箱即用的体验。仍在 Yarn 1.x 的既有仓库,官方建议尽快迁移到新版本(相关迁移教程位于 docs/tutorials 目录)。Yarn 1.x 已进入官方建议退役的名单,长期维护角度应尽早脱离。
九、安全修复
本版本包含三处安全修复(详见发布说明 Security Fixes 一节):
- Catalog 后端:修复了一个可被利用来破坏后端实例可用性的漏洞;
- TechDocs 后端:修复了外部托管内容场景下可能被未授权访问 TechDocs 内容的漏洞;
- TechDocs 后端:修复了已认证用户可绕过脚本注入防护的漏洞。
建议所有使用 Catalog 与 TechDocs 的部署尽快升级。
十、升级建议与相关资源
综合以上变更,升级到 v1.31.0 时建议按以下清单逐项核对:
- 后端:确认
packages/backend/src/index.ts中backend.add(...)传入的是BackendFeature/ServiceFactory本身;移除对coreServices.identity/coreServices.tokenManager的依赖,必要时按 changelog 示例自行重建服务;将featureDiscoveryServiceFactory替换为discoveryFeatureLoader;确保所有插件运行在同一新后端实例中,避免新旧混跑导致认证失败。 - 前端:将扩展 outputs 改为数组形式;用对应
Blueprint.make替换create<Kind>Extension;createExtensionOverrides迁移到createFrontendModule({ pluginId, ... });createApp改从@backstage/frontend-defaults导入;测试中的tester.render()改用renderInTestApp+tester.reactElement()。 - 配置:为 app 后端提供正确的构建期配置与
app.baseUrl(如需可设app.disableConfigInjection);按需调整 permission policy 以放行新增的catalog.location.analyze与catalog.entity.validate;GitLab 组织 provider 如有allowInherited请替换为relations。 - 行为变化:关注 catalog
getEntities排序语义变化与 guest provider 在生产环境的启动行为变化。
关于本版本的完整包级变更,可继续查阅 docs/releases/v1.31.0-changelog.md;后端系统与新前端系统的深入文档分别位于 docs/backend-system 与 docs/frontend-system;相关源码入口包括 packages/backend-plugin-api、packages/frontend-plugin-api、plugins/app-backend 与 plugins/catalog-node。
【免费下载链接】backstageBackstage is an open framework for building developer portals项目地址: https://gitcode.com/GitHub_Trending/ba/backstage
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考