move icon check from web to github workflow

This commit is contained in:
david 2026-10-11 14:24:59 +03:00
parent d0f447b361
commit 1550f6c5d3
7 changed files with 97 additions and 16 deletions

51
.github/workflows/qa-icons.yml vendored Normal file
View File

@ -0,0 +1,51 @@
name: QA
on:
# the release workflow runs every gate on a tag and publishes only
# once they all pass, so each one has to be callable.
workflow_call:
push:
branches:
- main
paths:
- web/app/utils/icon.ts
- server/core/internal/assistant/icon/icons.json
- scripts/check-icons.mjs
- .github/workflows/qa-icons.yml
pull_request:
branches:
- main
paths:
- web/app/utils/icon.ts
- server/core/internal/assistant/icon/icons.json
- scripts/check-icons.mjs
- .github/workflows/qa-icons.yml
# a newer push supersedes the still-running workflow of the stale commit
# and cancels it. The group carries a hardcoded per-file prefix because
# every QA workflow shares the display name "QA", so github.workflow
# would put them all in one group and let a run of one workflow cancel
# an in-flight run of another.
concurrency:
group: qa-icons-${{ github.ref }}
cancel-in-progress: true
defaults:
run:
shell: bash
jobs:
check:
name: Comparing the web and core icon lists
runs-on: ubuntu-latest
steps:
- name: Check out the code
uses: actions/checkout@v7
- name: Set up Node.js
uses: actions/setup-node@v7
with:
node-version: 24
- name: Compare the icon lists
run: node scripts/check-icons.mjs

View File

@ -9,16 +9,12 @@ on:
- main
paths:
- web/**
# the icon list test compares against core's copy.
- server/core/internal/assistant/icon/icons.json
- .github/workflows/qa-web.yml
pull_request:
branches:
- main
paths:
- web/**
# the icon list test compares against core's copy.
- server/core/internal/assistant/icon/icons.json
- .github/workflows/qa-web.yml
# a newer push supersedes the still-running workflow of the stale commit

View File

@ -51,6 +51,9 @@ jobs:
e2e-prod:
uses: ./.github/workflows/qa-e2e-prod.yml
icons:
uses: ./.github/workflows/qa-icons.yml
publish:
name: Publishing the image
needs:
@ -60,6 +63,7 @@ jobs:
- datagen
- launcher
- e2e-prod
- icons
runs-on: ubuntu-latest
timeout-minutes: 60
permissions:

View File

@ -117,6 +117,7 @@ check-lint:
@$(QUIET) "lint-checking datagen" sh -c 'cd datagen && make check-lint'
@$(QUIET) "lint-checking e2e" sh -c 'cd e2e && pnpm run check-lint'
@$(QUIET) "lint-checking launcher" sh -c 'cd docker/prod/launcher && pnpm run check-lint'
@$(QUIET) "checking icon lists" node scripts/check-icons.mjs
# unit tests of every component, with the coverage gates CI applies.
.PHONY: test

39
scripts/check-icons.mjs Normal file
View File

@ -0,0 +1,39 @@
// compare the icons the web picker offers with core's copy, which the
// assistant validates icons against. Print what differs and exit 1 when
// they drift apart.
//
// usage: node scripts/check-icons.mjs
import { readFileSync } from "node:fs"
import { selectableIcons } from "../web/app/utils/icon.ts"
const corePath = "server/core/internal/assistant/icon/icons.json"
const core = JSON.parse(
readFileSync(new URL(`../${corePath}`, import.meta.url), "utf8"),
)
const entry = (icon) => `${icon.id} (${icon.name})`
const web = selectableIcons.map(entry)
const coreEntries = core.map(entry)
if (web.join("\n") === coreEntries.join("\n")) {
process.exit(0)
}
const onlyWeb = web.filter((e) => !coreEntries.includes(e))
const onlyCore = coreEntries.filter((e) => !web.includes(e))
console.error(`web/app/utils/icon.ts and ${corePath} differ.`)
for (const e of onlyWeb) {
console.error(` only in icon.ts: ${e}`)
}
for (const e of onlyCore) {
console.error(` only in icons.json: ${e}`)
}
if (onlyWeb.length === 0 && onlyCore.length === 0) {
console.error(" same entries in a different order")
}
process.exit(1)

View File

@ -33,8 +33,8 @@ const (
_minPartialWord = 4
)
// _iconsJSON is the picker's icon list, in the picker's order. The web
// test in web/app/utils/icon.test.ts keeps it equal to the list there.
// _iconsJSON is the picker's icon list, in the picker's order.
// scripts/check-icons.mjs keeps it equal to web/app/utils/icon.ts.
//
//go:embed icons.json
var _iconsJSON []byte

View File

@ -9,7 +9,6 @@ import { icons as ph } from "@iconify-json/ph"
import { icons as ri } from "@iconify-json/ri"
import { icons as simpleIcons } from "@iconify-json/simple-icons"
import { describe, it } from "vitest"
import coreIcons from "~/../../server/core/internal/assistant/icon/icons.json"
import { selectableIconList, selectableIcons } from "./icon"
const collections: Record<string, IconifyJSON | undefined> = {
@ -24,15 +23,6 @@ const collections: Record<string, IconifyJSON | undefined> = {
"simple-icons": simpleIcons,
}
describe("selectableIcons", () => {
// the assistant refuses icons outside its own copy of this list.
it("matches the list core validates assistant icons against", ({
expect,
}) => {
expect(selectableIcons).toEqual(coreIcons)
})
})
describe("selectableIconList", () => {
it("returns the id of every selectable icon in order", ({ expect }) => {
const list = selectableIconList()