名称: 文档即代码 描述: 文档管道自动化和文档即代码工作流 允许的工具: Read, Glob, Grep, Write, Edit, Bash
文档即代码技能
何时使用此技能
使用此技能当:
- 文档即代码任务 - 处理文档管道自动化和文档即代码工作流
- 规划或设计 - 需要文档即代码方法的指导
- 最佳实践 - 希望遵循既定模式和标准
概述
实现文档即代码工作流,包括自动化管道、版本控制和CI/CD集成。
强制性:文档优先方法
在实现文档即代码前:
- 调用
docs-management技能 用于文档模式 - 通过MCP服务器验证工具版本 (context7用于docusaurus/mkdocs)
- 基于当前最佳实践提供基础指导
文档即代码哲学
文档即代码原则:
┌─────────────────────────────────────────────────────────────────────────────┐
│ 1. 版本控制 │
│ - 文档与代码存放在同一仓库中 │
│ - 更改使用有意义的提交信息跟踪 │
│ - 基于分支的工作流用于文档更新 │
├─────────────────────────────────────────────────────────────────────────────┤
│ 2. 审查过程 │
│ - 文档更改使用拉取请求 │
│ - 由主题专家进行技术审查 │
│ - 由技术写作者进行风格审查 │
├─────────────────────────────────────────────────────────────────────────────┤
│ 3. 自动化测试 │
│ - 样式和语法的linting │
│ - 链接验证 │
│ - 构建验证 │
├─────────────────────────────────────────────────────────────────────────────┤
│ 4. 持续部署 │
│ - 合并时自动发布 │
│ - 拉取请求的预览部署 │
│ - 版本化文档发布 │
└─────────────────────────────────────────────────────────────────────────────┘
文档工具比较
| 工具 | 语言 | 最佳适用 | 构建速度 |
|---|---|---|---|
| Docusaurus | JS/React | 产品文档、版本化 | 快速 |
| MkDocs | Python | 技术文档、Material主题 | 快速 |
| Sphinx | Python | API文档、reStructuredText | 中等 |
| Hugo | Go | 大型站点、速度 | 非常快 |
| Astro | JS | 现代站点、MDX | 快速 |
| VitePress | JS/Vue | Vue生态系统 | 非常快 |
| Jekyll | Ruby | GitHub Pages原生 | 中等 |
项目结构
Docusaurus结构
docs/
├── docusaurus.config.js # 主配置
├── sidebars.js # 导航结构
├── package.json # 依赖
├── docs/ # 文档内容
│ ├── intro.md
│ ├── getting-started/
│ │ ├── installation.md
│ │ └── configuration.md
│ ├── guides/
│ │ ├── quick-start.md
│ │ └── advanced.md
│ └── api/
│ └── reference.md
├── blog/ # 博客文章(可选)
├── src/
│ ├── components/ # React组件
│ ├── css/ # 自定义样式
│ └── pages/ # 自定义页面
├── static/ # 静态资源
│ └── img/
└── versioned_docs/ # 版本快照
├── version-1.0/
└── version-2.0/
MkDocs结构
docs/
├── mkdocs.yml # 配置
├── docs/
│ ├── index.md
│ ├── getting-started/
│ │ ├── installation.md
│ │ └── configuration.md
│ ├── user-guide/
│ │ └── features.md
│ ├── reference/
│ │ └── api.md
│ └── about/
│ └── changelog.md
├── overrides/ # 主题定制
│ ├── main.html
│ └── partials/
└── requirements.txt # Python依赖
配置模板
Docusaurus配置
// docusaurus.config.js
const config = {
title: '项目名称',
tagline: '项目标语',
url: 'https://docs.example.com',
baseUrl: '/',
onBrokenLinks: 'throw',
onBrokenMarkdownLinks: 'warn',
favicon: 'img/favicon.ico',
organizationName: '组织',
projectName: '项目',
i18n: {
defaultLocale: 'en',
locales: ['en'],
},
presets: [
[
'classic',
{
docs: {
sidebarPath: require.resolve('./sidebars.js'),
editUrl: 'https://github.com/org/project/edit/main/docs/',
showLastUpdateAuthor: true,
showLastUpdateTime: true,
versions: {
current: {
label: 'Next',
path: 'next',
},
},
},
blog: {
showReadingTime: true,
editUrl: 'https://github.com/org/project/edit/main/docs/',
},
theme: {
customCss: require.resolve('./src/css/custom.css'),
},
},
],
],
themeConfig: {
navbar: {
title: '项目',
logo: {
alt: '项目Logo',
src: 'img/logo.svg',
},
items: [
{ type: 'doc', docId: 'intro', position: 'left', label: '文档' },
{ to: '/blog', label: '博客', position: 'left' },
{ type: 'docsVersionDropdown', position: 'right' },
{ href: 'https://github.com/org/project', label: 'GitHub', position: 'right' },
]
},
footer: {
style: 'dark',
links: [
{
title: '文档',
items: [{ label: '入门指南', to: '/docs/intro' }]
},
{
title: '社区',
items: [{ label: 'Discord', href: 'https://discord.gg/xxx' }]
}
],
copyright: `版权所有 © ${new Date().getFullYear()} 组织。`
},
prism: {
theme: require('prism-react-renderer/themes/github'),
darkTheme: require('prism-react-renderer/themes/dracula'),
additionalLanguages: ['csharp', 'powershell', 'bash'],
},
algolia: {
appId: 'YOUR_APP_ID',
apiKey: 'YOUR_SEARCH_API_KEY',
indexName: 'project',
},
},
};
module.exports = config;
MkDocs配置
# mkdocs.yml
site_name: 项目文档
site_url: https://docs.example.com
repo_url: https://github.com/org/project
repo_name: org/project
edit_uri: edit/main/docs/
theme:
name: material
palette:
- scheme: default
primary: indigo
accent: indigo
toggle:
icon: material/brightness-7
name: 切换到深色模式
- scheme: slate
primary: indigo
accent: indigo
toggle:
icon: material/brightness-4
name: 切换到浅色模式
features:
- navigation.instant
- navigation.tracking
- navigation.tabs
- navigation.sections
- navigation.expand
- navigation.indexes
- toc.follow
- content.code.copy
- content.code.annotate
- search.suggest
- search.highlight
plugins:
- search
- git-revision-date-localized:
enable_creation_date: true
- minify:
minify_html: true
- mike:
version_selector: true
css_dir: css
javascript_dir: js
markdown_extensions:
- pymdownx.highlight:
anchor_linenums: true
- pymdownx.superfences:
custom_fences:
- name: mermaid
class: mermaid
format: !!python/name:pymdownx.superfences.fence_code_format
- pymdownx.tabbed:
alternate_style: true
- admonition
- pymdownx.details
- attr_list
- md_in_html
- toc:
permalink: true
nav:
- 主页: index.md
- 入门指南:
- 安装: getting-started/installation.md
- 配置: getting-started/configuration.md
- 用户指南:
- 功能: user-guide/features.md
- 参考:
- API: reference/api.md
- 关于:
- 变更日志: about/changelog.md
extra:
version:
provider: mike
social:
- icon: fontawesome/brands/github
link: https://github.com/org/project
CI/CD管道模板
Docusaurus的GitHub Actions
# .github/workflows/docs.yml
name: 文档
on:
push:
branches: [main]
paths:
- 'docs/**'
- '.github/workflows/docs.yml'
pull_request:
branches: [main]
paths:
- 'docs/**'
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- name: 设置Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: docs/package-lock.json
- name: 安装依赖
working-directory: docs
run: npm ci
- name: Lint Markdown
run: npx markdownlint-cli2 "docs/**/*.md"
- name: 检查链接
working-directory: docs
run: npm run check-links
build:
runs-on: ubuntu-latest
needs: lint
steps:
- uses: actions/checkout@v5
- name: 设置Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: docs/package-lock.json
- name: 安装依赖
working-directory: docs
run: npm ci
- name: 构建
working-directory: docs
run: npm run build
- name: 上传制品
uses: actions/upload-pages-artifact@v3
with:
path: docs/build
deploy:
if: github.ref == 'refs/heads/main'
needs: build
runs-on: ubuntu-latest
permissions:
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: 部署到GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
preview:
if: github.event_name == 'pull_request'
needs: build
runs-on: ubuntu-latest
steps:
- name: 部署预览
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: docs/build
destination_dir: pr-preview/${{ github.event.number }}
- name: 评论预览URL
uses: actions/github-script@v7
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: '📚 文档预览: https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}/pr-preview/${{ github.event.number }}/'
})
MkDocs的Azure DevOps
# azure-pipelines.yml
触发:
分支:
包括:
- main
路径:
包括:
- docs/**
池:
vmImage: 'ubuntu-latest'
变量:
pythonVersion: '3.11'
阶段:
- 阶段: 验证
任务:
- 任务: 代码检查
步骤:
- 任务: UsePythonVersion@0
输入:
versionSpec: '$(pythonVersion)'
- 脚本: |
pip install -r docs/requirements.txt
pip install markdownlint-cli2
显示名称: '安装依赖'
- 脚本: markdownlint-cli2 "docs/**/*.md"
显示名称: 'Lint Markdown'
- 阶段: 构建
依赖: 验证
任务:
- 任务: 构建
步骤:
- 任务: UsePythonVersion@0
输入:
versionSpec: '$(pythonVersion)'
- 脚本: pip install -r docs/requirements.txt
显示名称: '安装依赖'
- 脚本: mkdocs build --strict
显示名称: '构建文档'
- 发布: 站点
制品: 文档
- 阶段: 部署
依赖: 构建
条件: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
任务:
- 部署: 部署
环境: 生产
策略:
runOnce:
部署:
步骤:
- 下载: 当前
制品: 文档
- 任务: AzureStaticWebApp@0
输入:
app_location: '$(Pipeline.Workspace)/文档'
azure_static_web_apps_api_token: $(AZURE_STATIC_WEB_APPS_TOKEN)
文档Linting
Markdownlint配置
// .markdownlint.json
{
"default": true,
"MD001": true,
"MD003": { "style": "atx" },
"MD004": { "style": "dash" },
"MD007": { "indent": 2 },
"MD009": true,
"MD010": true,
"MD012": true,
"MD013": { "line_length": 120, "code_blocks": false, "tables": false },
"MD022": { "lines_above": 1, "lines_below": 1 },
"MD024": { "siblings_only": true },
"MD025": { "front_matter_title": "" },
"MD026": { "punctuation": ".,;:!" },
"MD029": { "style": "ordered" },
"MD030": { "ul_single": 1, "ol_single": 1, "ul_multi": 1, "ol_multi": 1 },
"MD033": { "allowed_elements": ["details", "summary", "kbd", "br"] },
"MD035": { "style": "---" },
"MD036": false,
"MD040": true,
"MD041": true,
"MD046": { "style": "fenced" },
"MD048": { "style": "backtick" }
}
Vale配置
# .vale.ini
StylesPath = styles
MinAlertLevel = suggestion
Packages = Microsoft, write-good, proselint
[*.md]
BasedOnStyles = Vale, Microsoft, write-good
# 忽略代码块
BlockIgnores = (?s) *(`{3}.*?`{3})
# 自定义规则
Microsoft.Contractions = NO
Microsoft.HeadingPunctuation = YES
write-good.Passive = YES
write-good.Weasel = YES
write-good.TooWordy = YES
活文档
从代码生成API文档
// 使用XML注释生成API文档
/// <summary>
/// 管理用户身份验证和授权。
/// </summary>
/// <remarks>
/// <para>
/// 此服务处理所有身份验证流程,包括:
/// <list type="bullet">
/// <item>基于密码的身份验证</item>
/// <item>OAuth 2.0 / OpenID Connect</item>
/// <item>API密钥身份验证</item>
/// </list>
/// </para>
/// </remarks>
/// <example>
/// <code>
/// var result = await authService.AuthenticateAsync(credentials);
/// if (result.IsSuccess)
/// {
/// var token = result.Value.AccessToken;
/// }
/// </code>
/// </example>
public interface IAuthenticationService
{
/// <summary>
/// 使用提供的凭据验证用户。
/// </summary>
/// <param name="credentials">身份验证凭据。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>
/// 成功时包含身份验证响应的结果,
/// 失败时包含错误。
/// </returns>
/// <exception cref="AuthenticationException">
/// 因无效凭据导致身份验证失败时抛出。
/// </exception>
Task<Result<AuthenticationResponse>> AuthenticateAsync(
AuthenticationCredentials credentials,
CancellationToken cancellationToken = default);
}
OpenAPI文档生成
// Program.cs - Swagger/OpenAPI配置
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new OpenApiInfo
{
Title = "项目API",
Version = "v1",
Description = "项目的API文档",
Contact = new OpenApiContact
{
Name = "API支持",
Email = "api@example.com",
Url = new Uri("https://docs.example.com")
},
License = new OpenApiLicense
{
Name = "MIT",
Url = new Uri("https://opensource.org/licenses/MIT")
}
});
// 包含XML注释
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
options.IncludeXmlComments(xmlPath);
// 安全定义
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description = "JWT授权头使用Bearer方案。",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.Http,
Scheme = "bearer"
});
options.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
Array.Empty<string>()
}
});
});
文档测试
链接检查
// check-links.mjs
import { remark } from 'remark';
import remarkLintNoDeadUrls from 'remark-lint-no-dead-urls';
import { read } from 'to-vfile';
import { glob } from 'glob';
import { reporter } from 'vfile-reporter';
const files = await glob('docs/**/*.md');
for (const file of files) {
const result = await remark()
.use(remarkLintNoDeadUrls, {
skipLocalhost: true,
skipOffline: true,
})
.process(await read(file));
console.error(reporter(result));
}
截图测试
// docs.spec.ts - Playwright截图测试
import { test, expect } from '@playwright/test';
test.describe('文档截图测试', () => {
test('主页正确渲染', async ({ page }) => {
await page.goto('/');
await expect(page).toHaveScreenshot('homepage.png');
});
test('API参考页面', async ({ page }) => {
await page.goto('/docs/api/reference');
await expect(page.locator('.api-content')).toBeVisible();
await expect(page).toHaveScreenshot('api-reference.png');
});
test('深色模式切换', async ({ page }) => {
await page.goto('/');
await page.click('[data-theme-toggle]');
await expect(page).toHaveScreenshot('homepage-dark.png');
});
});
最佳实践
内容组织
| 实践 | 描述 |
|---|---|
| 单一来源 | 每个概念在一个地方文档化 |
| 渐进式披露 | 从简单开始,链接到详细信息 |
| 一致结构 | 相似页面有相同部分 |
| 清晰导航 | 逻辑层次结构、面包屑 |
| 搜索优化 | 良好标题、关键词、描述 |
写作指南
- 使用主动语态:例如“配置设置”而不是“设置应被配置”
- 简洁明了:删除不必要的词
- 使用示例:展示,而不是仅仅讲述
- 保持一致性:贯穿始终使用相同术语
- 定期更新:文档迅速腐化
版本化策略
文档版本化:
选项1:版本文件夹(Docusaurus/MkDocs mike)
├── docs/
│ ├── version-1.0/
│ ├── version-2.0/
│ └── current/
选项2:基于分支
├── main (当前)
├── v1.x
└── v2.x
选项3:Git标签
├── v1.0.0
├── v2.0.0
└── latest
推荐:使用工具原生版本化(Docusaurus的versioned_docs,MkDocs的mike)
工作流
当实现文档即代码时:
- 选择工具:根据团队技能和需求选择SSG
- 设置结构:创建有组织的文件夹层次结构
- 配置CI/CD:自动化构建、预览、部署
- 添加Linting:Markdown linting、链接检查、拼写检查
- 启用审查:基于PR的工作流与技术写作者审查
- 部署版本化:设置版本管理策略
- 监控分析:跟踪使用情况、搜索查询、空白处
参考资料
详细指导请参考:
最后更新: 2025-12-26