Skip to content

Commit 2fb27d8

Browse files
committed
Enforce the licence position in CI, document it
Every check here exists because something was found missing; the point is that it cannot regress quietly. Two jobs. The first mirrors `pnpm compliance` — LICENSE complete with a filled-in copyright, required files present, package.json declares Apache-2.0, no assets loaded from third-party origins, the footer attribution link intact, inventory fresh against the installed tree. The second is the one that matters. It builds the app and asserts the licence text is in dist/, carries 40+ copyright notices, includes the Apache ECharts NOTICE attribution and the OFL-1.1 text, and that the bundle references it at the production base path. Everything in the first job verifies files exist in a repository; only this one verifies the notices reach the person receiving the software, which is what the obligation actually is. Both jobs pass `package_json_file: Benchmarking/package.json` to pnpm/action-setup. `defaults.run.working-directory` only affects `run:` steps — `uses:` steps run from the workspace root, and there is no root package.json, so without this the action fails before setup-node runs. Three checks were written weakly on the first pass and are now tested against the cases they missed: Footer link `.includes('LICENCES_URL')` passed when the <Anchor> was deleted and only the import remained. Now requires `href={LICENCES_URL}`, and fails if a `visibleFrom` appears on it — a mobile visitor gets the same bundle as a desktop one, so a breakpoint-hidden link leaves them no route to the notices while every other check stays green. CDN origins A blocklist of known CDN hostnames let esm.sh, ajax.googleapis.com, skypack.dev and use.typekit.net through. Replaced with an allowlist over every absolute URL surviving comment-stripping. Verified against all four plus jsdelivr and fonts.googleapis.com; prose URLs in comments still pass. Build output `pnpm compliance` could report green while the bundle shipped no attribution at all. Added as `pnpm compliance:full`, opt-in locally because it costs ~20s, always-on in CI. README gains a Licensing section — for a change whose whole purpose is making the licence position legible, the most-read file in the repo was silent on it. docs/LICENCE-REVIEW-RESPONSE.md is the finding-by-finding write-up, including which of the server's ten findings do not apply here and why. Two were already satisfied before this branch: dependency pinning, since pnpm-lock.yaml pins the tree, and self-hosted fonts, since Inter has always come through @fontsource rather than a CDN.
1 parent 5e96bd6 commit 2fb27d8

4 files changed

Lines changed: 819 additions & 0 deletions

File tree

‎.github/workflows/compliance.yml‎

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
name: Licence compliance
2+
3+
# Makes the licence position self-enforcing. Every check here exists because
4+
# something was found missing in the review; the point is that it cannot
5+
# regress quietly.
6+
#
7+
# The central concern for this repo is different from aistudio-server. This is
8+
# a publicly deployed browser app, so serving the bundle IS distribution and
9+
# the attribution conditions of ~54 permissive licences apply on every page
10+
# load. esbuild strips comments during minification, so attribution has to be
11+
# emitted deliberately — and CI has to confirm it still is.
12+
13+
on:
14+
push:
15+
branches: [main, master]
16+
pull_request:
17+
schedule:
18+
# Weekly. Upstream packages change licences occasionally and silently.
19+
- cron: "0 6 * * 1"
20+
workflow_dispatch:
21+
22+
permissions:
23+
contents: read
24+
25+
defaults:
26+
run:
27+
working-directory: Benchmarking
28+
29+
jobs:
30+
compliance:
31+
name: Licence files, attribution, inventory
32+
runs-on: ubuntu-latest
33+
steps:
34+
- uses: actions/checkout@v4
35+
36+
- uses: pnpm/action-setup@v4
37+
with:
38+
# The app lives in Benchmarking/, and there is no root package.json.
39+
# `defaults.run.working-directory` only affects `run:` steps — `uses:`
40+
# steps run from the workspace root, so without this the action looks
41+
# for <workspace>/package.json, finds nothing, and fails before
42+
# setup-node runs.
43+
#
44+
# The version itself comes from the `packageManager` field in that
45+
# file. Pinning it matters: this workflow's inventory check compares
46+
# committed artifacts against `pnpm licenses list --prod --json`
47+
# output, whose shape has changed across pnpm majors. An unpinned
48+
# bump would turn the artifacts "stale" and report it as a missing
49+
# `pnpm licences` run.
50+
package_json_file: Benchmarking/package.json
51+
52+
- uses: actions/setup-node@v4
53+
with:
54+
node-version: 22
55+
cache: pnpm
56+
cache-dependency-path: Benchmarking/pnpm-lock.yaml
57+
58+
- name: Install (frozen lockfile)
59+
# --frozen-lockfile is what makes the ^ ranges in package.json safe.
60+
# An unfrozen install would silently resolve a different tree, and the
61+
# committed inventory would stop describing what actually ships.
62+
run: pnpm install --frozen-lockfile
63+
64+
- name: Run compliance checks
65+
# Same script as `pnpm compliance` locally, so the feedback loop does
66+
# not require a push. Covers: LICENSE completeness and filled-in
67+
# copyright, required files present, package.json license field,
68+
# no CDN origins, the footer attribution link, and inventory freshness.
69+
run: pnpm compliance
70+
71+
bundle-attribution:
72+
name: Built bundle carries attribution
73+
runs-on: ubuntu-latest
74+
steps:
75+
- uses: actions/checkout@v4
76+
- uses: pnpm/action-setup@v4
77+
with:
78+
# The app lives in Benchmarking/, and there is no root package.json.
79+
# `defaults.run.working-directory` only affects `run:` steps — `uses:`
80+
# steps run from the workspace root, so without this the action looks
81+
# for <workspace>/package.json, finds nothing, and fails before
82+
# setup-node runs.
83+
#
84+
# The version itself comes from the `packageManager` field in that
85+
# file. Pinning it matters: this workflow's inventory check compares
86+
# committed artifacts against `pnpm licenses list --prod --json`
87+
# output, whose shape has changed across pnpm majors. An unpinned
88+
# bump would turn the artifacts "stale" and report it as a missing
89+
# `pnpm licences` run.
90+
package_json_file: Benchmarking/package.json
91+
- uses: actions/setup-node@v4
92+
with:
93+
node-version: 22
94+
cache: pnpm
95+
cache-dependency-path: Benchmarking/pnpm-lock.yaml
96+
97+
- run: pnpm install --frozen-lockfile
98+
99+
- name: Build
100+
env:
101+
# vite.config.ts throws without this. Value is irrelevant to the
102+
# licence check — nothing is called at build time.
103+
VITE_API_URL: https://example.invalid
104+
run: pnpm build
105+
106+
- name: Licence text is present in the deployed output
107+
# This is the check that actually matters. Everything else verifies
108+
# files exist in the repo; this verifies the notices reach the person
109+
# receiving the bundle. Before this was fixed, `grep -c Copyright`
110+
# across dist/assets/*.js returned 0.
111+
run: |
112+
file=dist/third-party-licences.txt
113+
if [ ! -f "$file" ]; then
114+
echo "::error::$file was not emitted into the build output."
115+
echo "Vite copies public/ into dist/. Run 'pnpm licences' and commit."
116+
exit 1
117+
fi
118+
119+
copyrights=$(grep -c "Copyright" "$file" || true)
120+
if [ "$copyrights" -lt 40 ]; then
121+
echo "::error::only $copyrights copyright notices in $file — expected 40+."
122+
exit 1
123+
fi
124+
echo "$copyrights copyright notices present."
125+
126+
# Apache-2.0 section 4(d): echarts ships a NOTICE whose attributions
127+
# must be reproduced in any distribution of a derivative work.
128+
grep -q "The Apache Software Foundation" "$file" || {
129+
echo "::error::the Apache ECharts NOTICE attribution is missing (Apache-2.0 s4(d))."
130+
exit 1
131+
}
132+
echo "Apache NOTICE attribution present."
133+
134+
# OFL-1.1 section 2: the licence must accompany the bundled fonts.
135+
grep -qi "SIL OPEN FONT LICENSE" "$file" || {
136+
echo "::error::the OFL-1.1 text is missing, but Inter is bundled (OFL s2)."
137+
exit 1
138+
}
139+
echo "OFL-1.1 text present."
140+
141+
- name: The app links to it, with the production base path
142+
run: |
143+
# A licence file nobody can reach is not attribution. The footer link
144+
# is the delivery mechanism, and the base path has to survive the
145+
# build — the app is served under /aistudio/, not /.
146+
if ! grep -rq "/aistudio/third-party-licences.txt" dist/assets/*.js; then
147+
echo "::error::the bundle does not reference /aistudio/third-party-licences.txt."
148+
echo "Check LICENCES_URL in src/app/constants.ts and the footer link."
149+
exit 1
150+
fi
151+
echo "Bundle links the licence file at the correct base path."
152+
153+
- uses: actions/upload-artifact@v4
154+
if: always()
155+
with:
156+
name: third-party-licences
157+
path: Benchmarking/dist/third-party-licences.txt
158+
if-no-files-found: warn

0 commit comments

Comments
 (0)