OGKit

把 OG 装进 CI:防回归门禁

  • open-graph
  • CI
  • 门禁
  • lint

改版合入三周后,市场部在 LinkedIn 分享新品页:卡片没图,标题回退成布局里的 <title>。查 git blame,og:image 是在一次「清理重复 head 标签」的重构里被顺手删掉的——PR 过了 Lighthouse、过了视觉回归,就是没人查原始 HTML 里的 meta。

这不是「再截一张分享预览图」能防住的问题。OG 是典型的改了没人发现坏了字段:人眼看页面正常,爬虫看的才是卡片契约。

事故复盘模板(下次可直接贴 issue)

写进 postmortem 时,尽量把下面五格填满,而不是只写「OG 坏了」:

要写清的内容 示例
症状 哪个平台、什么卡片形态 LinkedIn 无图;Slack 标题来自错误页面
首次发现 谁、何时、什么渠道 运营在发帖日发现;用户反馈截图
引入 commit 删标签 / 改模板 / 换渲染路径 重构 head、CMS 默认字段、SPA 壳替换 SSR
为何门禁没拦 缺检查 / 只看浏览器 / 只看预览站 CI 无 OG 步骤;扩展只验了 localhost
修复与防再发 补标签 + 哪条自动化 恢复 meta;CI 对 out/og-kit check

把「为何门禁没拦」单独成格,是为了逼出流程债:多半不是规则不存在,而是没有稳定、可失败的自动化

为什么扩展替不掉 CI

浏览器扩展适合开发期与私有环境:localhost、登录态、staging、一键 rescrape。它跑在你的会话里,能看见真实 DOM。

GitHub Actions / 其他 CI 没有你的扩展。跑在无头 runner 上的检查必须是:

  1. 可安装的 CLI 或脚本
  2. 确定性退出码(绿 / 红 / 工具挂了)
  3. 机器可读输出(JSON / SARIF / JUnit),好让 PR 注释与 Code Scanning 接得上

把 OG 想成 ESLint:编辑器插件提升手感,仓库门禁才防止「别人合入时弄坏」。Lighthouse 同理——本地分数很爽,PR 不 fail 就会漂移。

门禁最小集(先过这三条,再谈完美)

不必第一天启用全部 32 条规则。对「防回归」最值钱的是:

1. 必填字段还在

规则 id 严重级别 含义
og-title-missing error 没有 og:title
og-type-missing error 没有 og:type
og-url-missing error 没有 og:url
og-image-missing error 没有 og:image

实务上还会把 og-description-missing(warn)和 twitter-card-missing(warn)纳入视线;默认 --fail-on error 时 warn 不拦 PR,但报告里能看见。

2. 图真的可被爬虫拿到

规则 id 严重级别 常见事故
og-image-relative-url error 相对路径,爬虫常直接丢掉
og-image-not-https error 非 HTTPS
og-image-forbidden error 鉴权图床 / 403
og-image-content-type-mismatch error Content-Type 与真实格式不一致

本地扫静态 HTML 时,部分网络图规则需要可达的公网资源;对纯构建产物目录,至少应先拦住「标签缺失 + 相对路径」这类确定 bug。

3. 体积与尺寸别把 IM 打爆(可先 warn)

规则 id 严重级别 说明
og-image-bytes-exceeds-platform-budget warn 超出平台字节预算
og-image-dimensions-too-small warn 小于推荐 1200×630

字节数字因平台而异,且不少来自社区实测而非单一官方承诺——规则文档会标来源档位。CI 里先 warn 可见、error 必填 通常更稳。

稳定规则 id 是公共 API:文档 URL、--disable / --only、baseline 条目都挂在 id 上。改名等于破坏门禁——这点和 ESLint rule id 一样。

接入示例:概念 GitHub Actions

已发布包入口是 npx --yes @og-kit/cli(可执行文件名 og-kit)。同一套规则与报告 schema 与网站扫描共用。

# .github/workflows/og-check.yml — 概念示例,按你的构建产物路径改
name: Open Graph gate

on:
  pull_request:
  push:
    branches: [main]

jobs:
  og:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: "20"

      # 你自己的站点构建:Next / Astro / 任意静态导出
      - name: Build site
        run: npm ci && npm run build

      - name: Check Open Graph on build output
        run: |
          npx --yes @og-kit/cli check ./out \
            --fail-on error \
            --base-url https://example.com \
            --json > og-report.json
        # 目录按框架改:out/ dist/ build/ .next/ 等
        # 退出码:0 通过 · 1 有达到 fail-on 的问题 · 2 工具/配置失败

      - name: Upload report (optional)
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: og-report
          path: og-report.json

要点:

  • 先构建再扫产物(或扫你关心的公网 URL)。只扫源码里的 TSX 模板,抓不到框架最终吐出的 HTML。
  • --base-url 让相对路径解析与「页面身份」类规则有意义;没有它时,依赖公网身份的规则会被跳过并列出,而不是假装通过。
  • 人类可读输出不是稳定接口;自动化请用 --json--sarif--junit
  • 退出码契约:0 通过,1 findings,2 工具失败——CI 才能把「OG 坏了」和「安装挂了」分开。

本地等价:

npx --yes @og-kit/cli check https://example.com
npx --yes @og-kit/cli check ./dist --base-url https://example.com --fail-on error
npx --yes @og-kit/cli --version
# 四行:og-kit / engine / ruleset / report-schema(版本契约见仓库 docs)

GitHub Action 封装现状(诚实边界)

仓库里有 @og-kit/action 目录,定位是 CLI 的薄封装。以当前仓库现状为准:它仍是占位 / 未上架的 marketplace Action,不要写成「点一下就能用的官方 Action」。生产可用路径就是上面的 npx @og-kit/cli check …。等 Action 真正发布、有 action.yml 与版本标签后,再把 YAML 换成 uses: 即可——门禁语义(规则 id、退出码、JSON)不变。

baseline 与噪音治理

第一天对存量站点全开 error,往往几十条历史债把 PR 刷红。可采纳路径:

  1. 收窄目标
    --only og-title-missing,og-type-missing,og-url-missing,og-image-missing,og-image-relative-url
    或只扫关键模板:check ./out/pricing.html ./out/index.html

  2. baseline 豁免已知债

    npx --yes @og-kit/cli check ./out \
      --base-url https://example.com \
      --baseline .ogkit-baseline.json \
      --update-baseline
    

    把当前 findings 写入 baseline 并提交。之后相同 target + ruleId(可选 pin message)不再导致 exit 1;回归仍会红。

  3. 按规则关掉,而不是关掉整条 job
    --disable title-truncation-risk,og-title-matches-title
    ogkit.config.* 里写 rules / failOn / baseline,与 CLI 标志合并。

  4. 勿用「永远 continue-on-error」当 baseline
    那会让回归静默。baseline 的价值是:旧债可见、新债失败。

治理节奏建议:每月从 baseline 抠掉几条(修标签),而不是无限追加豁免。规则 id 稳定,才方便在 issue 里写「关掉 og-image-bytes-exceeds-platform-budget 直到压图 pipeline 就绪」。

和视觉 QA、预览站怎么分工

手段 擅长 不擅长
设计稿 / 人眼 品牌、安全区、对比度 合入后标签被删
第三方 / 站内预览 布局与截断近似 权威缓存;CI 无头环境
浏览器扩展 localhost、登录态、rescrape 引导 Actions runner
CLI 门禁 稳定规则 id、exit code、PR 失败 替你做视觉审美

第三方预览是近似;权威仍是各平台官方 debugger(系列前文已写)。CI 解决的是「标签与可达性是否还在」,不是「LinkedIn 缓存里是不是上周的图」——后者要 rescrape,见系列缓存篇。

检查清单(可复制进团队 wiki)

  • 构建产物或关键公网 URL 在 PR 上跑 og-kit check
  • --fail-on error(或配置等价)使缺 og:image 等 error 失败
  • 使用 --json / SARIF / JUnit 之一做工件或 Code Scanning
  • 存量债走 baseline 或 --only,而不是关掉 job
  • 规则文档链到稳定 id(例如 og-image-missing
  • 未把未上架的 GitHub Action 写成依赖路径;以 npx @og-kit/cli 为准
  • 改版 / 换 head 组件的 PR 必须触达上述 job

用 CLI 自检一次

公网页可先贴首页扫描看报告形态;同一套规则进 CI 时用 CLI:

npx --yes @og-kit/cli check https://你的域名/关键页 --fail-on error

完整规则表:规则参考。平台差异与 rescrape 不在本文展开——门禁只保证「契约字段还在且图可达」,不保证「所有平台缓存已更新」。

下一篇读什么

  1. Open Graph 到底在解决什么 —— 全流程与工具地图(CI 只是第 5 步)
  2. 为什么你的 Open Graph 卡片一片空白 —— 五分钟分诊
  3. 必填 meta 清单、图片工程、缓存 / rescrape —— 系列相邻篇
  4. localhost / staging 死角 —— 扩展路径(CI 够不到时)

三条可带走的结论: 视觉 QA 代替不了 meta 回归门禁;门禁最小集是「必填 + 图可达 +(可选)体积」且 id 必须稳定;今天就用 npx @og-kit/cli check 进 Actions,Action 封装以上架为准、不夸大。