From 0416a02f1fcba7a1d44043ede78832712a990f68 Mon Sep 17 00:00:00 2001 From: Akira HIGUCHI Date: Thu, 2 Jul 2026 16:14:49 +0900 Subject: [PATCH 1/7] ci: add check-licenses workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a Check Licenses workflow using the shared check-licenses composite action (tailor-platform/actions), covering the whole pnpm workspace including examples/ so license issues in example apps are caught too, not just packages/core. license-groups / additional-licenses / denied-licenses are sourced from LICENSE_GROUPS / ALLOWED_LICENSES / DENIED_LICENSES GitHub Variables, centrally managed instead of hardcoded in the workflow. Verified locally against the actual dependency tree (pnpm licenses list --json): examples/nextjs-app pulls in @img/sharp-libvips-* (LGPL- 3.0-or-later) transitively via next's built-in image optimization, used unmodified as a prebuilt binary — the standard case LGPL's dynamic-linking allowance covers. This repo's ALLOWED_LICENSES variable overrides the org default to include LGPL-3.0-or-later as an app-shell-specific exception. All other licenses in use are covered by the reciprocal/notice/unencumbered baseline plus the shared BlueOak-1.0.0/WTFPL/Unknown extras. --- .github/workflows/check-licenses.yaml | 47 +++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 .github/workflows/check-licenses.yaml diff --git a/.github/workflows/check-licenses.yaml b/.github/workflows/check-licenses.yaml new file mode 100644 index 00000000..533bc95b --- /dev/null +++ b/.github/workflows/check-licenses.yaml @@ -0,0 +1,47 @@ +name: Check Licenses + +on: + push: + branches: [main] + paths: + - 'pnpm-lock.yaml' + pull_request: + paths: + - 'pnpm-lock.yaml' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: {} + +jobs: + check-licenses: + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + contents: read + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: false + + - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 + with: + run_install: false + + - uses: actions/setup-node@v4 + with: + cache: pnpm + cache-dependency-path: ./pnpm-lock.yaml + node-version-file: './package.json' + + - name: Install dependencies + run: pnpm install + + - name: Check licenses + uses: tailor-platform/actions/check-licenses@v1 + with: + license-groups: ${{ vars.LICENSE_GROUPS }} + additional-licenses: ${{ vars.ALLOWED_LICENSES }} + denied-licenses: ${{ vars.DENIED_LICENSES }} From 1883df5eac31e797c54c709ca6cdbfc6414eb333 Mon Sep 17 00:00:00 2001 From: Akira HIGUCHI Date: Thu, 2 Jul 2026 16:57:07 +0900 Subject: [PATCH 2/7] ci: declare LGPL package-exceptions directly in the workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Moves the sharp/libvips LGPL-3.0-or-later exception from a repo-level ALLOWED_LICENSES override to package-exceptions declared inline here, via the check-licenses action's new package-exceptions input. Approves only the exact @img/sharp-libvips-* package+license pairs pulled in by examples/nextjs-app's use of next's image optimization, rather than allowing LGPL-3.0-or-later in general (which would silently approve any future LGPL dependency, unrelated to this one). One entry per platform variant since sharp ships a separate prebuilt binary per OS/arch — package-exceptions matches on exact package name, no globs. Declared directly in this workflow rather than through a shared GitHub Variable, since the exception is tied to this repo's specific dependency tree, not organization policy. --- .github/workflows/check-licenses.yaml | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/.github/workflows/check-licenses.yaml b/.github/workflows/check-licenses.yaml index 533bc95b..93c090ab 100644 --- a/.github/workflows/check-licenses.yaml +++ b/.github/workflows/check-licenses.yaml @@ -4,10 +4,10 @@ on: push: branches: [main] paths: - - 'pnpm-lock.yaml' + - "pnpm-lock.yaml" pull_request: paths: - - 'pnpm-lock.yaml' + - "pnpm-lock.yaml" concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -34,7 +34,7 @@ jobs: with: cache: pnpm cache-dependency-path: ./pnpm-lock.yaml - node-version-file: './package.json' + node-version-file: "./package.json" - name: Install dependencies run: pnpm install @@ -45,3 +45,13 @@ jobs: license-groups: ${{ vars.LICENSE_GROUPS }} additional-licenses: ${{ vars.ALLOWED_LICENSES }} denied-licenses: ${{ vars.DENIED_LICENSES }} + # examples/nextjs-app pulls in @img/sharp-libvips-* (LGPL-3.0-or-later) + # transitively via next's built-in image optimization, used + # unmodified as a prebuilt binary — the standard case LGPL's + # dynamic-linking allowance covers. One entry per platform variant + # (exact match only, no globs); add more if a new one shows up. + package-exceptions: | + @img/sharp-libvips-linux-x64@LGPL-3.0-or-later + @img/sharp-libvips-linux-arm64@LGPL-3.0-or-later + @img/sharp-libvips-darwin-x64@LGPL-3.0-or-later + @img/sharp-libvips-darwin-arm64@LGPL-3.0-or-later From d632c7640ef1547b78f63f47979fae43360afb26 Mon Sep 17 00:00:00 2001 From: Akira HIGUCHI Date: Thu, 2 Jul 2026 17:09:06 +0900 Subject: [PATCH 3/7] ci: use dependency-chain package-exceptions for the LGPL sharp exception Switches from four flat @ entries (one per OS/arch sharp-libvips binary variant) to a single dependency-chain exception, matching check-licenses' redesigned package-exceptions input: {"LGPL-3.0-or-later": [["nextjs-app", "next"]]}. Approves the route (examples/nextjs-app's use of next, which pulls in sharp/libvips for image optimization) rather than the license or a specific package name, so it covers every platform variant without enumerating them, and won't silently approve an unrelated future LGPL dependency elsewhere in the workspace. --- .github/workflows/check-licenses.yaml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/workflows/check-licenses.yaml b/.github/workflows/check-licenses.yaml index 93c090ab..0d10cfa7 100644 --- a/.github/workflows/check-licenses.yaml +++ b/.github/workflows/check-licenses.yaml @@ -48,10 +48,9 @@ jobs: # examples/nextjs-app pulls in @img/sharp-libvips-* (LGPL-3.0-or-later) # transitively via next's built-in image optimization, used # unmodified as a prebuilt binary — the standard case LGPL's - # dynamic-linking allowance covers. One entry per platform variant - # (exact match only, no globs); add more if a new one shows up. + # dynamic-linking allowance covers. Approves the route (nextjs-app + # -> next -> ... -> @img/sharp-libvips-*), not the license or + # package name alone, so it doesn't cover an unrelated future LGPL + # dependency and doesn't need one entry per OS/arch binary variant. package-exceptions: | - @img/sharp-libvips-linux-x64@LGPL-3.0-or-later - @img/sharp-libvips-linux-arm64@LGPL-3.0-or-later - @img/sharp-libvips-darwin-x64@LGPL-3.0-or-later - @img/sharp-libvips-darwin-arm64@LGPL-3.0-or-later + {"LGPL-3.0-or-later": [["nextjs-app", "next"]]} From 841fe2917ff7f46205c9e29fa19b6fe687da6036 Mon Sep 17 00:00:00 2001 From: Akira HIGUCHI Date: Fri, 3 Jul 2026 14:53:52 +0900 Subject: [PATCH 4/7] ci: move package-exceptions to a JSON file, validate exception roots MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extracts the LGPL sharp/libvips exception from inline workflow YAML into .github/license-package-exceptions.json, easier to find and edit than digging into the workflow. A new "Load and validate package exceptions" step reads the file and rejects any chain that doesn't start with an example app (nextjs-app, vite-app): package-exceptions may excuse a license issue found in a non-published example, never in @tailor-platform/app-shell itself (the published package) — approving an exception rooted at the actual library would let an unreviewed license slip into what customers install. --- .github/license-package-exceptions.json | 3 ++ .github/workflows/check-licenses.yaml | 41 +++++++++++++++++++------ 2 files changed, 35 insertions(+), 9 deletions(-) create mode 100644 .github/license-package-exceptions.json diff --git a/.github/license-package-exceptions.json b/.github/license-package-exceptions.json new file mode 100644 index 00000000..ed4410a5 --- /dev/null +++ b/.github/license-package-exceptions.json @@ -0,0 +1,3 @@ +{ + "LGPL-3.0-or-later": [["nextjs-app", "next"]] +} diff --git a/.github/workflows/check-licenses.yaml b/.github/workflows/check-licenses.yaml index 0d10cfa7..807b0493 100644 --- a/.github/workflows/check-licenses.yaml +++ b/.github/workflows/check-licenses.yaml @@ -5,9 +5,11 @@ on: branches: [main] paths: - "pnpm-lock.yaml" + - ".github/license-package-exceptions.json" pull_request: paths: - "pnpm-lock.yaml" + - ".github/license-package-exceptions.json" concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -39,18 +41,39 @@ jobs: - name: Install dependencies run: pnpm install + # Chains must start with an example app (nextjs-app, vite-app), never + # @tailor-platform/app-shell itself — package-exceptions may excuse a + # license issue in a non-published example, never in the published + # package. + - name: Load and validate package exceptions + id: package-exceptions + run: | + node <<'EOF' + const fs = require("node:fs"); + const ALLOWED_ROOTS = ["nextjs-app", "vite-app"]; + const configPath = ".github/license-package-exceptions.json"; + const config = JSON.parse(fs.readFileSync(configPath, "utf-8")); + const violations = []; + for (const [license, chains] of Object.entries(config)) { + for (const chain of chains) { + if (!ALLOWED_ROOTS.includes(chain[0])) { + violations.push( + `"${license}": chain ${JSON.stringify(chain)} must start with one of ${ALLOWED_ROOTS.join(", ")}`, + ); + } + } + } + if (violations.length > 0) { + console.error(`::error::Invalid ${configPath}:\n${violations.join("\n")}`); + process.exit(1); + } + fs.appendFileSync(process.env.GITHUB_OUTPUT, `exceptions=${JSON.stringify(config)}\n`); + EOF + - name: Check licenses uses: tailor-platform/actions/check-licenses@v1 with: license-groups: ${{ vars.LICENSE_GROUPS }} additional-licenses: ${{ vars.ALLOWED_LICENSES }} denied-licenses: ${{ vars.DENIED_LICENSES }} - # examples/nextjs-app pulls in @img/sharp-libvips-* (LGPL-3.0-or-later) - # transitively via next's built-in image optimization, used - # unmodified as a prebuilt binary — the standard case LGPL's - # dynamic-linking allowance covers. Approves the route (nextjs-app - # -> next -> ... -> @img/sharp-libvips-*), not the license or - # package name alone, so it doesn't cover an unrelated future LGPL - # dependency and doesn't need one entry per OS/arch binary variant. - package-exceptions: | - {"LGPL-3.0-or-later": [["nextjs-app", "next"]]} + package-exceptions: ${{ steps.package-exceptions.outputs.exceptions }} From 1d3bc19c8b2c7f2342ff4ff5181890e3a59b1c1a Mon Sep 17 00:00:00 2001 From: Akira HIGUCHI Date: Fri, 3 Jul 2026 15:34:56 +0900 Subject: [PATCH 5/7] fix: pin all third-party actions in check-licenses workflow to SHAs actions/checkout@v4, actions/setup-node@v4, and tailor-platform/actions/check-licenses@v1 were still referenced by mutable tag, unlike every other action in this repo. Pinned all three to full commit SHAs with pinact (check-licenses to v1's current commit, v1.6.0), matching the org's SHA-pinning convention and now also passing tailor-platform/sdk's ghalint check (action_ref_should_be_full_length_commit_sha), which surfaced this. --- .github/workflows/check-licenses.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/check-licenses.yaml b/.github/workflows/check-licenses.yaml index 807b0493..4362ead9 100644 --- a/.github/workflows/check-licenses.yaml +++ b/.github/workflows/check-licenses.yaml @@ -24,15 +24,15 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 + - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 with: run_install: false - - uses: actions/setup-node@v4 + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: cache: pnpm cache-dependency-path: ./pnpm-lock.yaml @@ -71,7 +71,7 @@ jobs: EOF - name: Check licenses - uses: tailor-platform/actions/check-licenses@v1 + uses: tailor-platform/actions/check-licenses@d3040e3b5da92f72696d89db24bc9da62e0993d8 # v1.6.0 with: license-groups: ${{ vars.LICENSE_GROUPS }} additional-licenses: ${{ vars.ALLOWED_LICENSES }} From 5735239c1f0b062700daa2d4d9eed280728423e4 Mon Sep 17 00:00:00 2001 From: Akira HIGUCHI Date: Fri, 3 Jul 2026 17:18:53 +0900 Subject: [PATCH 6/7] ci: rename check-licenses org variable references Match the CHECK_LICENSES_* namespace now applied to the ALLOWED_LICENSES/ DENIED_LICENSES/LICENSE_GROUPS org variables upstream. --- .github/workflows/check-licenses.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/check-licenses.yaml b/.github/workflows/check-licenses.yaml index 4362ead9..2d9b6a61 100644 --- a/.github/workflows/check-licenses.yaml +++ b/.github/workflows/check-licenses.yaml @@ -73,7 +73,7 @@ jobs: - name: Check licenses uses: tailor-platform/actions/check-licenses@d3040e3b5da92f72696d89db24bc9da62e0993d8 # v1.6.0 with: - license-groups: ${{ vars.LICENSE_GROUPS }} - additional-licenses: ${{ vars.ALLOWED_LICENSES }} - denied-licenses: ${{ vars.DENIED_LICENSES }} + license-groups: ${{ vars.CHECK_LICENSES_ALLOWED_LICENSE_GROUPS }} + additional-licenses: ${{ vars.CHECK_LICENSES_ALLOWED_LICENSES }} + denied-licenses: ${{ vars.CHECK_LICENSES_DENIED_LICENSES }} package-exceptions: ${{ steps.package-exceptions.outputs.exceptions }} From cb36988a232a4f5a1aaf672e774265e77471b4a1 Mon Sep 17 00:00:00 2001 From: Akira HIGUCHI Date: Fri, 3 Jul 2026 18:09:26 +0900 Subject: [PATCH 7/7] ci: trigger check-licenses on its own workflow file changes Edits to check-licenses.yaml itself (e.g. its inputs, action version) should re-run the check, not just pnpm-lock.yaml/exceptions-file changes. --- .github/workflows/check-licenses.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/check-licenses.yaml b/.github/workflows/check-licenses.yaml index 2d9b6a61..bccf86c5 100644 --- a/.github/workflows/check-licenses.yaml +++ b/.github/workflows/check-licenses.yaml @@ -6,10 +6,12 @@ on: paths: - "pnpm-lock.yaml" - ".github/license-package-exceptions.json" + - ".github/workflows/check-licenses.yaml" pull_request: paths: - "pnpm-lock.yaml" - ".github/license-package-exceptions.json" + - ".github/workflows/check-licenses.yaml" concurrency: group: ${{ github.workflow }}-${{ github.ref }}