|
| 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