a4e2a112ac
Rebased onto main. The original commit (e480c96) was written againstd645210and main has moved 30 commits since, including b72aedb/77b8c00 which ported the static redesign and replaced the whole design system. Every file that version touched is gone: src/styles/tokens.css, src/lib/fonts.ts and src/components/Logo.tsx were deleted, main's token vocabulary shares exactly two names with the old one (--font-display, --font-mono), and fonts moved to public/assets/fonts/<family>/<family>-<weight>.woff2. Replaying it verbatim would have re-added ~200KB of unreferenced duplicate fonts, a tokens.css nothing imports, and a resurrected Logo.tsx. The intent survives the rebase, and is now worth more than it was: main hand-vendored its brand assets and src/app/fonts.css claims they are "per @molecule/brand (the visual SSOT)" — a package name that does not exist, on a claim that is only two-thirds true. scripts/sync-brand.mjs targets what the app actually serves: npm run brand:sync copy the SSOT's assets to their app paths npm run brand:check fail if any committed asset differs from the SSOT and CI gains a `brand-drift` context that runs both, so a re-vendored or hand-edited brand asset is a red build rather than something noticed a year later. molecule-ai/molecule-ai-sdk is public, so the job needs no token. The manifest is derived, not typed out. The script parses the @font-face rules from the SSOT's css/fonts.css and from src/app/fonts.css and reconciles them by (family, weight); logo/icon filenames come from brand.json. Add a weight to the SSOT and the manifest follows. It fails loudly and specifically rather than producing a partial brand: no package found (lists every path tried), a half-present package directory, an unparseable or empty @font-face set, a missing source file, an app-declared weight the SSOT does not ship, or an empty manifest — each is exit 1 with the reason. Six assets are byte-identical to @enteros/brand@0.1.0 and now hard-gated: enteros-logo.svg, enteros-icon.png, inter-tight-{400,500}.woff2, jetbrains-mono-{400,500}.woff2. Chillax is a declared divergence (#22): this repo ships the free-font-pack 400/500/600/700 cut, the SSOT ships 300/400/500/600, and they are differently subset so no weight matches byte-for-byte. Reconciling changes rendered type — five `font-weight: 620` rules would drop from Bold to Semibold — so it is a design decision, not a sync, and it is recorded in DECLARED_DIVERGENCE with a reason and a tracking issue rather than quietly excluded. That list is the only escape hatch in the gate. No rendered output changes: `next build` emits the same CSS (2b768740753229e4.css / bb4644fbaa9e8a3b.css, both byte-identical to main), `npm run brand:sync` rewrites nothing, tsc is clean and 7/7 tests pass.
132 lines
4.5 KiB
YAML
132 lines
4.5 KiB
YAML
# Enter OS landing site — CI gate.
|
|
#
|
|
# Everything a Next.js static-ish marketing site can be wrong about, checked
|
|
# before merge. Two independent contexts so a red one names its own failure:
|
|
#
|
|
# build npm ci -> next build -> tsc --noEmit -> node:test
|
|
# `next build` runs FIRST because the content-collections Next plugin
|
|
# is what emits .content-collections/generated (the `content-collections`
|
|
# module alias) and .next/types — without it `tsc --noEmit` reports
|
|
# ~20 phantom TS2307/TS7006 errors. Build, then typecheck.
|
|
# lint npm ci -> eslint
|
|
# `next lint` is deprecated in Next 15.5 and removed in 16, and was
|
|
# never configured in this repo (no eslint config, no eslint dep), so
|
|
# it silently did nothing. ESLint now runs directly off
|
|
# eslint.config.mjs (next/core-web-vitals + next/typescript).
|
|
# Errors fail the job; the repo's pre-existing `no-img-element`
|
|
# findings stay warnings (this is a verbatim DOM port of a static
|
|
# design that intentionally uses plain <img>).
|
|
# brand-drift
|
|
# re-run the @enteros/brand sync and fail if any committed brand asset
|
|
# differs from the SSOT. Mirrors molecule-ai-sdk's contracts-codegen-
|
|
# drift pattern: generated/copied artifacts are committed, so the only
|
|
# thing keeping them honest is a job that regenerates them and diffs.
|
|
# Node-only, no npm install, ~2s.
|
|
#
|
|
# Branch protection on main requires every context here to be green.
|
|
|
|
name: ci
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
pull_request:
|
|
branches: [main]
|
|
|
|
concurrency:
|
|
group: ci-${{ github.event.pull_request.number || github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
env:
|
|
NEXT_TELEMETRY_DISABLED: "1"
|
|
|
|
jobs:
|
|
build:
|
|
name: build
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 20
|
|
steps:
|
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
with:
|
|
persist-credentials: false
|
|
|
|
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
|
with:
|
|
# Matches the Dockerfile's node:22-bookworm-slim build stage.
|
|
node-version: "22"
|
|
cache: npm
|
|
|
|
- name: Install from lockfile
|
|
run: npm ci
|
|
|
|
- name: Build (emits content-collections bindings + .next/types)
|
|
run: npm run build
|
|
|
|
- name: Typecheck
|
|
run: npx tsc --noEmit
|
|
|
|
- name: Tests
|
|
run: npm test
|
|
|
|
lint:
|
|
name: lint
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 10
|
|
steps:
|
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
with:
|
|
persist-credentials: false
|
|
|
|
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
|
with:
|
|
node-version: "22"
|
|
cache: npm
|
|
|
|
- name: Install from lockfile
|
|
run: npm ci
|
|
|
|
- name: ESLint
|
|
run: npm run lint
|
|
|
|
brand-drift:
|
|
name: brand-drift
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 10
|
|
steps:
|
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
with:
|
|
persist-credentials: false
|
|
|
|
# The brand SSOT. molecule-ai/molecule-ai-sdk is a public repo, so this
|
|
# needs no token — nothing to leak, nothing to rotate.
|
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
with:
|
|
repository: molecule-ai/molecule-ai-sdk
|
|
path: .brand-ssot
|
|
persist-credentials: false
|
|
|
|
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
|
with:
|
|
node-version: "22"
|
|
|
|
# No `npm ci`: sync-brand.mjs is deliberately node-stdlib only, so this
|
|
# context stays fast and cannot be broken by a dependency.
|
|
- name: Brand assets match @enteros/brand byte-for-byte
|
|
env:
|
|
ENTEROS_BRAND_DIR: ${{ github.workspace }}/.brand-ssot/brand
|
|
run: npm run brand:check
|
|
|
|
# `brand:check` compares; `brand:sync` writes. Running the writer too
|
|
# proves the two agree — a check that passed while the sync would have
|
|
# rewritten files would be a check that means nothing.
|
|
- name: Re-running the sync produces no diff
|
|
env:
|
|
ENTEROS_BRAND_DIR: ${{ github.workspace }}/.brand-ssot/brand
|
|
run: |
|
|
set -euo pipefail
|
|
npm run brand:sync
|
|
if ! git diff --exit-code --stat; then
|
|
echo "::error::npm run brand:sync changed committed files. Commit the result."
|
|
exit 1
|
|
fi
|