PlantUML + CI:让架构图不会过期的 4 个办法
「我们画的架构图过期了」是每个团队的痛。这篇讲怎么用 CI 让图永远跟得上代码 —— 自动检查、自动重渲染、过期提醒、PR 自动出图。
反模式:写一张图就忘 1 2 3 docs/architecture/ flow.png # 2023 年画的,没人维护 README.md
半年后服务下线了、新服务上线了,这张 flow.png 还是 2023 年的样子。架构图活在真空里。
正确做法是 CI 把图「绑死」在仓库里。
技巧 1:CI 自动渲染 .github/workflows/uml.yml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 name: Render PlantUML on: push: paths: - 'docs/**/*.puml' - 'docs/**/*.uml' pull_request: paths: - 'docs/**/*.puml' - 'docs/**/*.uml' jobs: render: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-java@v4 with: java-version: '21' distribution: temurin - name: Download PlantUML run: curl -L -o plantuml.jar https://github.com/plantuml/plantuml/releases/download/v1.2024.7/plantuml-1.2024.7.jar - name: Render .puml to .svg run: | find docs -name '*.puml' -o -name '*.uml' | while read f; do java -jar plantuml.jar -tsvg -failfast2 -nometadata "$f" done - name: Check for changes run: | git diff --quiet docs/ || ( echo "::warning::SVG files differ. Re-render locally and commit." git diff --stat docs/ exit 1 )
每次有人提交 .puml 改动,CI 自动重新渲染 SVG。如果 SVG 有变化(说明本地忘跑了),CI 报警。
更友好的版本:自动 commit 回来。
1 2 3 4 5 6 7 8 9 - name: Auto-commit regenerated SVG run: | if [[ -n "$(git status --porcelain docs/)" ]]; then git config user.name github-actions git config user.email github-actions@github.com git add docs/**/*.svg git commit -m "render: regenerate SVG [skip ci]" git push fi
技巧 2:在 PR 中出图 .github/workflows/uml-pr-comment.yml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 name: PR PlantUML comment on: pull_request: paths: - 'docs/**/*.puml' jobs: comment: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-java@v4 with: java-version: '21' distribution: temurin - run: | curl -L -o plantuml.jar https://github.com/plantuml/plantuml/releases/latest/download/plantuml.jar mkdir -p rendered find docs -name '*.puml' | while read f; do base=$(basename "$f" .puml) java -jar plantuml.jar -tsvg -pipe < "$f" > "rendered/${base}.svg" done - name: Comment on PR uses: marocchino/sticky-pull-request-comment@v2 with: header: plantuml-preview message: | 📐 PlantUML diagrams in this PR: ${{ join(steps.render.outputs.diagram_links, ', ') }}
这样 PR 里直接看到所有 .puml 渲染出的 SVG 缩略图。Reviewer 改图、改文档时能立刻比对。
技巧 3:过期检查(防止图腐化) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 name: Check diagram staleness on: schedule: - cron: '0 9 * * 1' jobs: check: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Find diagrams older than 6 months run: | stale=$(find docs -name '*.puml' -mtime +180) if [[ -n "$stale" ]]; then echo "::warning::Stale diagrams (>180 days):" echo "$stale" exit 1 fi - name: Open issue if stale if: failure() uses: actions/github-script@v7 with: script: | const files = `$(find docs -name '*.puml' -mtime +180)`.trim(); await github.rest.issues.create({ owner: context.repo.owner, repo: context.repo.repo, title: '[stale] architecture diagrams older than 180 days', body: `需要回看和更新的图:\n\n\`\`\`\n${files}\n\`\`\`` });
每周一早晨跑一次脚本,任何 .puml 超过 180 天没动就自动开一个 issue。
技巧 4:在 README 中强制引用图 不需要的技术债:
1 2 3 # 项目 我们的架构图请见 Confluence。
CI 加 lint:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 name: Lint README on: pull_request: paths: - 'README.md' jobs: lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Check README references repo diagrams run: | if ! grep -q 'docs/architecture' README.md; then echo "::error::README.md must link to docs/architecture/" exit 1 fi
强制 README 里贴出仓库内的图,禁止「图在某个外部 wiki」式漂移。
完整工作流 把这 4 个技巧合起来:
PR 提交时 :CI 自动重新渲染所有 SVG(图过期 = 失败 PR)
PR 评论里 :自动贴所有 .puml 的 SVG 缩略图(评审者看得见)
每周一早晨 :扫描 180 天没动的图,自动开 issue 提醒维护
任何 README.md 改动 :强制引用仓库内的图
触发
CI 任务
作用
push to main
render + auto-commit
自动更新 SVG
pull_request
render + PR comment
评审可见图
每周一 9 点 cron
staleness check
提醒维护
修改 README.md
lint
防外部漂移
实战踩坑
CI 卡在 plantuml.jar 下载 :仓库内提前 curl 一份 jar,作为 Git LFS 资产或 base64 嵌入 Vendor 目录。
Java 启动慢 :用 temurin 而非 default-jdk,install 阶段只用 30 秒。
-failfast2 比 -failfast 严格,遇到第一个错误就停止。一个文件出错不阻塞整个 build,加 || true 容错,但要 warn。
CJK 字体加载 :CI 跑在 Ubuntu 上默认字体只有 OpenJDK 自带的,CJK 字符都成方块。装 fonts-noto-cjk:1 2 - name: Install CJK fonts run: sudo apt-get update && sudo apt-get install -y fonts-noto-cjk
PR 自动 commit SVG 触发下一轮 CI :加 [skip ci] 到 commit message,或者设 path 过滤让 SVG 改动不触发 build:1 2 3 4 on: push: paths: - 'docs/**/*.puml'
一键启用的模板 直接复制到你的仓库:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 name: PlantUML CI on: push: paths: ['docs/**/*.puml' , 'docs/**/*.uml' ] branches: [main ] pull_request: paths: ['docs/**/*.puml' , 'docs/**/*.uml' ] jobs: render: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-java@v4 with: { java-version: '21' , distribution: temurin } - run: sudo apt-get install -y fonts-noto-cjk - run: curl -L -o plantuml.jar https://github.com/plantuml/plantuml/releases/latest/download/plantuml.jar - run: | find docs -name '*.puml' -o -name '*.uml' | while read f; do java -jar plantuml.jar -tsvg -failfast2 -nometadata "$f" done - name: Check or auto-commit run: | if [[ -n "$(git status --porcelain docs/)" ]]; then git config user.name github-actions[bot] git config user.email 41898282+github-actions[bot]@users.noreply.github.com git add docs/ git commit -m "render: regenerate SVG [skip ci]" || echo "no changes" git push fi
粘贴 5 秒开张。