Backstage TechDocs FAQ 权威解读:MkDocs 生成机制、实体注解与编辑反馈配置实战
本篇技术指南基于 Backstage 仓库中 docs/features/techdocs/FAQ.md 展开,系统解答 TechDocs 使用中最常见的六类问题:静态站点生成器选型、mkdocs-techdocs-core 插件职责、Markdown 之外的格式支持、backstage.io/techdocs-ref 注解在外部构建模式下的行为、backstage.io/techdocs-entity 注解的重定向机制,以及如何在文档页面启用"编辑本页/反馈"按钮。读完本文,你将能理清 TechDocs 的生成技术栈与构建流程,并能在自己的 catalog-info.yaml、mkdocs.yml 与 app-config.yaml 中正确完成对应配置。
TechDocs 使用什么静态站点生成器?
TechDocs 底层使用 MkDocs 将项目文档从 Markdown 源码构建成站点。MkDocs 是一个面向项目文档的快速、简单的静态站点生成器,由 Python 生态驱动,其生态中丰富的插件和主题是 TechDocs 阅读体验的重要来源。
对于使用 techdocs-container(即默认的 spotify/techdocs Docker 镜像)构建的文档,站点采用 MkDocs 的 Material Theme(mkdocs-material)。这一主题提供了现代、响应式的阅读界面,也是 TechDocs 默认文档站点的外观基础。
在仓库的 app-config.yaml 中可以看到 TechDocs 生成器的默认运行方式:
techdocs:
builder: 'local' # Alternatives - 'external'
generator:
runIn: 'docker'
publisher:
type: 'local' # Alternatives - 'googleGcs' or 'awsS3' or 'azureBlobStorage' or 'openStackSwift'
这里的 techdocs.generator.runIn: 'docker' 表示生成文档时会拉起 techdocs-container Docker 镜像来执行 MkDocs,其完整配置项说明见 docs/features/techdocs/configuration.md。若你在自定义 Docker 环境中运行 Backstage、希望避免"Docker 中的 Docker"(Docker-in-Docker)问题,可将该值改为 'local',前提是本机已安装好 MkDocs 及其所需依赖。
mkdocs-techdocs-core 插件是什么?
mkdocs-techdocs-core 是一个 MkDocs 插件包,它扮演"包装器"(wrapper)的角色:内部聚合了多个 MkDocs 插件(例如 MkDocs Monorepo Plugin),并精选了一批 TechDocs 官方支持的 Python Markdown 扩展。其目的是让用户开箱即用地获得 TechDocs 官方推荐的 Markdown 渲染能力,而无需在每份 mkdocs.yml 里手工声明一长串插件和扩展。
在 mkdocsPatchers.ts 中可以看到 TechDocs 在生成前对 mkdocs.yml 的自动修补逻辑:
export const patchMkdocsYmlWithPlugins = async (
mkdocsYmlPath: string,
logger: LoggerService,
defaultPlugins: string[] = ['techdocs-core'],
) => {
默认情况下 techdocs-core 会被自动注入到每一份 mkdocs.yml;如果该文件未声明 plugins,则直接写入默认插件列表;如果已声明,则会逐项检查并补充缺失的默认插件。若想改变这一行为,可以通过 techdocs.generator.mkdocs.defaultPlugins 配置全局默认插件,或用 techdocs.generator.mkdocs.omitTechdocsCorePlugin: true 完全关闭自动注入(详见 configuration.md 中的 MkDocs Configuration 一节)。注意:手动指定的插件必须已经安装在你使用的 Docker 镜像或本地环境中。
TechDocs 是否支持 Markdown 之外的文件格式(如 RST、AsciiDoc)?
目前不支持。 由于 TechDocs 当前通过 MkDocs 从源码生成文档,因此源文件必须为 Markdown 格式。FAQ 明确说明,未来有计划支持其他静态站点生成器(SSG),届时才有望支持 RST、AsciiDoc 等其他格式。因此现阶段编写 TechDocs 文档时,应统一使用 Markdown,并配合 MkDocs 的插件生态(docs_dir、nav 等)组织站点结构。
使用外部构建与存储时,backstage.io/techdocs-ref 应该取什么值?
backstage.io/techdocs-ref 注解的值在 TechDocs 的构建过程中会被使用。但当你把 techdocs.builder 在 app-config.yaml 中设为 'external' 时,这个注解的值不会被使用——因为此时文档由仓库的 CI/CD 等外部流程构建并发布,techdocs-backend 只负责从存储读取,不再触发生成。
不过,该注解仍然必须存在于实体描述文件(如 catalog-info.yaml)中,其作用是让 Backstage 知道该实体启用了 TechDocs。
该注解的取值语义定义在 well-known-annotations.md:
metadata:
annotations:
backstage.io/techdocs-ref: dir:.
它告知 TechDocs 文档源内容存放的位置,最常见的写法是相对 catalog-info.yaml 所在位置、能找到 mkdocs.yml 的路径;在文档与实体源码不在一起的少数场景下,也可以写成绝对 URL 形式的 location reference,例如 url:https://github.com/backstage/backstage/tree/master。
从源码看,注解的解析逻辑在 helpers.ts 的 parseReferenceAnnotation 中:它读取实体注解并以 location ref 格式拆分为 type 与 target。而 dir 类型的相对引用会通过 transformDirLocation(helpers.ts)基于实体的来源位置解析为绝对地址:实体来自 url 位置时,用 SCM 集成的 resolveUrl 拼出目标子目录的完整 URL;实体来自 file 位置时,则解析为文件系统上的绝对目录。这正好解释了 FAQ 中"值在外部构建模式下不被使用"的细节:'external' 模式下没有本地生成环节,自然不需要去定位文档源;注解只作为 TechDocs 的启用标志存在。
'local' 与 'external' 两种构建策略的本质区别
FAQ 中"外部构建"的行为,可以结合 router.ts 中 /sync/:namespace/:kind/:name 路由的源码来理解:
techdocs.builder: 'local'(默认,即"Basic"架构):当用户打开 TechDocs 页面时,techdocs-backend 会尝试现场生成文档、发布到存储并展示,此时backstage.io/techdocs-ref注解的值参与定位文档源;techdocs.builder: 'external'(推荐,即"Recommended"架构):techdocs-backend 只负责从存储获取文档,不会尝试生成与发布,backstage.io/techdocs-ref的值不再被用于构建。
路由中通过注入的 docsBuildStrategy.shouldBuild({ entity }) 决定是否触发构建;默认策略 DefaultDocsBuildStrategy(DefaultDocsBuildStrategy.ts)读取 techdocs.builder 配置实现上述两种行为,同时允许自定义策略基于实体属性实现更复杂的逻辑。仓库根目录的 app-config.yaml 中默认使用 builder: 'local' 并注释了 'external' 作为备选。
访问带 backstage.io/techdocs-entity 注解的实体时会发生什么?
当你在浏览器中访问形如 docs/{namespace}/{kind}/{name} 的 TechDocs URL,且目标实体带有 backstage.io/techdocs-entity 注解(而非 backstage.io/techdocs-ref)时,Backstage 会重定向到该注解值所引用实体的 TechDocs 页面。
该注解的语义定义于 well-known-annotations.md:
metadata:
annotations:
backstage.io/techdocs-entity: component:default/example
其值指向"拥有这些 TechDocs"的外部实体。典型场景是:多个组件共享同一个仓库(通常是 monorepo)和同一份文档位置,通过该注解让多个组件引用同一份 TechDocs,既避免重复构建,也无需在多个页面重复维护文档副本。与之配套的 backstage.io/techdocs-entity-path 注解(well-known-annotations.md)则允许指定组件文档在外部实体 TechDocs 中的子路径,实现"深链接"(deep linking),而不仅仅指向外部实体文档的根目录。
前端重定向的具体实现位于 useExternalRedirect.ts:页面加载时通过 catalog API 读取实体,若发现存在 TECHDOCS_EXTERNAL_ANNOTATION(即 backstage.io/techdocs-entity)注解,就调用 buildTechDocsURL 构造目标实体的 TechDocs URL,并通过 navigate(..., { replace: true }) 完成跳转。钩子还利用 checkedEntityRef 记录已检查过的实体,避免在同一个实体文档内浏览子页面时反复触发重定向检查与整页加载动画。对应测试见 useExternalRedirect.test.tsx,其中覆盖了 techdocs-entity-path 深链接场景与注解为空的情况。
如何让用户对 TechDocs 页面提出修改建议或反馈?
对于源码托管在 GitHub 或 GitLab 的 TechDocs 站点,可以启用"编辑本页"(edit this page)和"反馈"(leave feedback)按钮。要做到这一点,需要在 mkdocs.yml 中按 MkDocs 官方配置规范提供 repo_url 与 edit_uri 两个配置项。
值得注意的一个自动化行为:即使你的 mkdocs.yml 里没有写 repo_url / edit_uri,TechDocs 生成器也可能会替你补上。在 mkdocsPatchers.ts 的 patchMkdocsYmlPreBuild 中,生成器会检查 mkdocs.yml 是否缺少这两个键,若缺失则根据实体的 backstage.io/techdocs-ref 位置注解推导仓库 URL 并写入:
if (!('repo_url' in mkdocsYml) || !('edit_uri' in mkdocsYml)) {
const result = getRepoUrlFromLocationAnnotation(
parsedLocationAnnotation,
scmIntegrations,
mkdocsYml.docs_dir,
);
if (result.repo_url || result.edit_uri) {
mkdocsYml.repo_url = mkdocsYml.repo_url || result.repo_url;
mkdocsYml.edit_uri = mkdocsYml.edit_uri || result.edit_uri;
...
}
}
不过 FAQ 强调:如果源码托管 URL 的主机名中不包含 github 或 gitlab(例如自托管的 GitHub Enterprise / GitLab 实例,或其他 SCM),还需要在 app-config.yaml 中为源码托管方添加一条 integrations 配置,其中只需提供 host 键即可。例如仓库根目录 app-config.yaml 中已有的 GitHub / GitLab 集成写法:
integrations:
github:
- host: github.com
token: ${GITHUB_TOKEN}
gitlab:
- host: gitlab.com
token: ${GITLAB_TOKEN}
这条配置让 TechDocs 生成器在推导 repo_url / edit_uri 时能够识别并正确处理你的源码托管主机,从而保证编辑/反馈按钮的链接可正常工作。若你手动在 mkdocs.yml 中显式设置过 repo_url / edit_uri,生成器会保留原值而不覆盖(见上方源码中 mkdocsYml.repo_url || result.repo_url 的保留逻辑)。
小结:FAQ 之外,值得留意的相关配置
FAQ 的每个回答都直接关联到 TechDocs 的配置与注解体系。为便于读者继续深入,这里汇总相关仓库资料:
- TechDocs 总览与特性:docs/features/techdocs/README.md(支持的源码托管商、文件存储商与完整技术栈列表)
- 完整配置参考:docs/features/techdocs/configuration.md(
generator/builder/publisher/cache全部配置项) - 注解语义:docs/features/software-catalog/well-known-annotations.md(
backstage.io/techdocs-ref、backstage.io/techdocs-entity、backstage.io/techdocs-entity-path等) - 生成器底层实现:plugins/techdocs-node/src/stages/generate/mkdocsPatchers.ts(
repo_url/edit_uri、默认插件、禁用外部字体的自动修补) - 后端路由与构建策略:plugins/techdocs-backend/src/service/router.ts、plugins/techdocs-backend/src/service/DefaultDocsBuildStrategy.ts
- 前端重定向实现:plugins/techdocs/src/reader/components/TechDocsReaderPage/useExternalRedirect.ts
只要理解了"MkDocs 生成、techdocs-core 聚合插件、Markdown 为唯一源码格式"这条技术主线,再配合 backstage.io/techdocs-ref 与 backstage.io/techdocs-entity 的注解语义,以及 mkdocs.yml + app-config.yaml 的反馈按钮配置,你就能准确预判 TechDocs 在各类部署形态下的实际行为,并在排查"文档不构建""编辑按钮不显示""多组件共享文档"等问题时快速定位根因。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0634
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
jforgamejforgame是一个一站式游戏服务器开发框架。包含游戏服务器开发所需要的各种组件,比如网关,socket服务端与客户端,自定义高效消息编解码,游戏热更新,游戏通用工具等等。包含游戏服,跨服,匹配服,后台管理系统等实现,同时提供大量业务案例以供学习。亦可用于其他socket应用,例如及时聊天等。Java01
fizz-gateway-nodeAn Aggregation API Gateway in Java . FizzGate 是一个基于 Java开发的微服务聚合网关,是拥有自主知识产权的应用网关国产化替代方案,能够实现热服务编排聚合、自动授权选择、线上服务脚本编码、在线测试、高性能路由、API审核管理、回调管理等目的,拥有强大的自定义插件系统可以自行扩展,并且提供友好的图形化配置界面,能够快速帮助企业进行API服务治理、减少中间层胶水代码以及降低编码投入、提高 API 服务的稳定性和安全性。Java00
certd开源SSL证书管理工具;全自动证书申请、更新、续期;通配符证书,泛域名证书申请;证书自动化部署到阿里云、腾讯云、主机、群晖、宝塔;https证书,pfx证书,der证书,TLS证书,nginx证书自动续签自动部署JavaScript00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00