# Consumer-side TVU **mockup conformance** audit (GitHub Actions template) # # ⚠️ THIS IS A TEMPLATE — copy to your consumer product repo's # `.github/workflows/tvu-mockup-audit.yml` and fill in the two marked places. # # ─── 它与另一份模板的分工(⛔ 别只装一份就以为齐了)──────────────────────────── # # `templates/audit-workflow.yml` → **代码面**(`audit:code` = R1/R2/R16 静态闸) # `templates/audit-mockup-workflow.yml` → **设计面**(本文件;mockup conformance 引擎) # # 两份都装才覆盖「代码网 + 设计网」两半。前一份的头注释逐字写着 # 「`audit:consumer-mockup` is NOT run in CI」——**本文件就是补上那一半的**。 # # ─── 为什么这份模板 2026-08-25 才出现 ──────────────────────────────────────── # # mockup conformance 此前**在任何地方都没有自动执行点**:它要 Figma REST token # **加**一个具体 fileKey,而 DS 仓的 pre-commit / prepublishOnly / CI 三处都给不出 # 「当前该审哪个产品文件」这个输入。**consumer 仓能给** —— 一个 consumer 产品对应 # 一个(或几个)确定的 Figma 文件,写死在它自己的 `package.json` 里正好。 # 这也正是 `V1_RELEASE_CHECKLIST.md` 那条 Recommendation 的可执行版本 # (「L5 触发条件重定义为:tooling ships **且至少 1 个 consumer 真的消费了这条闸**」)。 # # ─── ⚠️ 本模板刻意只跑 8/10 条规则,另 2 条**结构上**在 consumer 仓跑不了 ────── # # ⛔ 这不是「先跑一部分」的偷懒,是如实登记两个**缺的输入**。写死跑全量只会得到一条 # 永远红的 CI step —— 那比没有更糟(没人会再看它)。 # # · `library-origin` 需要 `docs/site-review-manifest.json`(指定设计库的 fileKey)。 # 那份文件**不在** npm 包的 `files[]` 里 ⇒ 从 npm 装的包里没有它。 # **要启用**:给下面那条命令加 `--lib <设计库 fileKey>`,key 从 Figma 库文件 URL 里取 # (`/design/` 与下一个 `/` 之间那段)。加了它这条规则就能跑。 # · `library-binding` 只读离线缓存 `figma-data/mockup/.json`(某天的整文件快照), # **无 live fallback**,而生成它的 `figma-sync/sync-mockup-data.mjs` 也不在包里。 # ⇒ 在 consumer 仓**目前无法启用**。它在 DS 仓里由 owner 侧的 `pnpm sync:mockup` 喂。 # # ⚠️ 另有一条**降级但会跑**的:`binding-fidelity` 的 B-SEM 需要 # `figma-data/normalized/binding-source-map.json`(也不在包里)⇒ 它会走内置 fallback # 刻度集,**可能 under-report**。引擎每次运行都会自印这一点,别把它的绿读成「全验过」。 # # ─── 前置条件(consumer 仓里要先有的东西)──────────────────────────────────── # # 1. `@ux-team/tvu-design-system` 是依赖(`pnpm add @ux-team/tvu-design-system`)。 # 2. `package.json` 里有 `audit:mockup`,**fileKey 写在那里**(唯一一处,⛔ 别在本 # workflow 里再写一份 —— 两处会漂): # "audit:mockup": "node node_modules/@ux-team/tvu-design-system/scripts/audit-mockup-conformance.mjs --file YOUR_FIGMA_FILE_KEY" # 这与 `docs/CONSUMER_AUDIT_SETUP.md` §3 给的写法逐字一致。 # 3. 两个 repo secret: # · `FIGMA_TOKEN` —— Figma personal access token(read-only 够用) # · `TVU_GITEA_PACKAGES_TOKEN` —— Gitea token(`read:package`),装包用 # ⚠️ 自动注入的 `secrets.GITHUB_TOKEN` 对 Gitea registry **无用**(Gitea 直接拒 GitHub 凭据)。 # # ⚠️ 耗时:整文件跑一次在大产品文件上 **实测 27s**(DS 仓 2026-08-24,合并前是 140s)。 # 真正大的文件会更久,`timeout-minutes` 给足。想更快就在 `audit:mockup` 里加 # `--node `,但**注意 `--node` 只约束本模板这 8 条里的 7 条** —— `connector` 是 # 全文件口径(另一条全文件口径的 `library-binding` 本模板本来就没跑)。引擎逐规则自印 # scope,⛔ 别把它读成「这个节点验过了」。⚠️ 这个「7」是从引擎的 `--list-rules` 现算的, # ⛔ 别在这里手抄一个会漂的数 —— 上一步那个 `--list-rules` step 就是为此存在。 name: TVU mockup conformance on: pull_request: branches: [main, master] # 手动触发:mockup 改动常常发生在 Figma 侧、仓库里没有对应 commit。 workflow_dispatch: # ⬇️ 想要 gate 平权(push 到主干也过同一关)就取消注释。默认不开:它每次都打 Figma REST, # 而 push 事件在多数 consumer 仓比 PR 频繁得多。开不开由 consumer 自己权衡。 # push: # branches: [main, master] jobs: mockup-conformance: runs-on: ubuntu-latest timeout-minutes: 15 steps: - name: Checkout uses: actions/checkout@v4 - name: Install pnpm uses: pnpm/action-setup@v3 with: version: 10 - name: Setup Node uses: actions/setup-node@v4 with: node-version: '20' cache: 'pnpm' # ⛔ 刻意不设 registry-url:Gitea registry 要一行手写 .npmrc(见下一步)。 - name: Point npm at the Gitea registry run: | echo "@ux-team:registry=https://product-demo.tvustream.com/gitea/api/packages/ux-team/npm/" >> .npmrc echo "//product-demo.tvustream.com/gitea/api/packages/ux-team/npm/:_authToken=${{ secrets.TVU_GITEA_PACKAGES_TOKEN }}" >> .npmrc - name: Install dependencies run: pnpm install --frozen-lockfile # fail closed:缺 `audit:mockup` 就当场说清楚,⛔ 不让 CI 抛一句 ERR_PNPM_NO_SCRIPT # 了事 —— 那条报错读不出「该去 package.json 加什么」。 # (同类教训:`templates/audit-workflow.yml` 曾要求一个 consumer 侧根本不存在的 # `audit:consumer-code` key,照抄即 CI 红,而报错完全指不出病因。) - name: Preflight — consumer 必须自带 audit:mockup(fileKey 写在那里) run: | if ! node -e "process.exit(JSON.parse(require('fs').readFileSync('package.json','utf8')).scripts?.['audit:mockup']?0:1)"; then echo "::error::package.json 缺 \"audit:mockup\" —— 本 workflow 靠它拿 fileKey。" echo "加这一行(把 YOUR_FIGMA_FILE_KEY 换成本产品的 Figma 文件 key):" echo ' "audit:mockup": "node node_modules/@ux-team/tvu-design-system/scripts/audit-mockup-conformance.mjs --file YOUR_FIGMA_FILE_KEY"' exit 1 fi echo "audit:mockup = $(node -e "console.log(JSON.parse(require('fs').readFileSync('package.json','utf8')).scripts['audit:mockup'])")" # 规则清单真源 = 引擎自己(⛔ 别在本文件维护第二份条数)。 - name: 列出引擎当前的规则清单(真源自印,便于对照下一步的 --rule) run: node node_modules/@ux-team/tvu-design-system/scripts/audit-mockup-conformance.mjs --list-rules # ⚠️ `--rule` 显式排除 library-origin / library-binding —— 理由与启用方式见文件头。 # ⛔ 别把它们加回来「图个全」:缺输入时它们会记 exitCode=2(could not run)并把 # 整条 step 判红,而那红是缺输入、不是设计缺陷 —— 分不清的红等于噪声。 # ⚠️ 排除即**缺席**,不是通过:引擎的 summary 里不会出现它们,report 里也没有。 # ⚠️ ⛔ **这里没有 `--`**(不是漏了):`pnpm run X -- ` 会把**字面 `--`** 当成 # 第一个参数传给脚本(2026-08-25 本机实测:`pnpm run p -- --rule a` → argv # `["--","--rule","a"]`;不加 `--` → `["--rule","a"]`)。本引擎能容忍那个字面 `--`, # 但没有理由喂它一个。 - name: Mockup conformance(8/10 条规则;另 2 条缺输入,见文件头) run: | pnpm audit:mockup \ --rule integrity,colors,typography-icon,binding-fidelity,bilingual-spacing,overlap,connector,geometry-consistency \ --report tvu-mockup-report.json env: FIGMA_PERSONAL_ACCESS_TOKEN: ${{ secrets.FIGMA_TOKEN }} # report 是防伪产物(脚本 writeFileSync 出来的,含 fileKey / nodeIds / timestamp / # figmaLastModified),交付卡引用它的路径即可,⛔ 不要手打「Integrity audit: pass」文本。 - name: Upload conformance report if: always() uses: actions/upload-artifact@v4 with: name: tvu-mockup-report path: | tvu-mockup-report.json tvu-mockup-report.lines.json if-no-files-found: warn