Skip to content

Commit aaaf91d

Browse files
authored
refactor(build): track third-party notices deterministically + add CI SBOM (#978)
The bundled dist/ is committed and redistributes ~214 production dependencies, so their license notices must ship alongside it. The generated notice file sat in limbo — produced on every build, committed by the release pipeline, deleted by Renovate commits, excluded from the CI dist-diff check for renovate branches, and never gitignored — so it perpetually showed as untracked. - Rename dist/licenses.txt to dist/THIRD_PARTY_NOTICES.txt and commit it as the tracked attribution for the bundled dependencies. Generation is already deterministic (stable sort, normalized line endings); a total license-collection failure now fails the build instead of silently writing nothing, while benign per-dependency gaps still produce the file. - Remove the renovate-branch carve-out from the dist/ comparison so every branch verifies the notice file like the rest of dist/. - Generate a CycloneDX dependency SBOM in CI via pnpm sbom and upload it as a build artifact. Extract formatThirdPartyNotices() as a pure, tested helper.
1 parent 571b2cd commit aaaf91d

6 files changed

Lines changed: 15077 additions & 19 deletions

File tree

.github/workflows/ci.yaml

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -95,13 +95,9 @@ jobs:
9595
run: pnpm build
9696
- name: Compare the expected and actual dist/ directories
9797
run: |
98-
DIFF_TARGETS="dist/"
99-
if [[ "${{ github.head_ref || github.ref_name }}" == renovate/* ]]; then
100-
DIFF_TARGETS="dist/ ':!dist/licenses.txt'"
101-
fi
102-
if [ "$(eval git diff --ignore-space-at-eol $DIFF_TARGETS | wc -l)" -gt "0" ]; then
98+
if [ "$(git diff --ignore-space-at-eol dist/ | wc -l)" -gt "0" ]; then
10399
echo "Detected uncommitted changes after build. See status below:"
104-
eval git diff --text $DIFF_TARGETS
100+
git diff --text dist/
105101
exit 1
106102
fi
107103
id: diff
@@ -111,6 +107,17 @@ jobs:
111107
with:
112108
name: dist
113109
path: dist/
110+
# The SBOM is an informational artifact, not a build gate — a generation
111+
# failure must not fail the build.
112+
- name: Generate the dependency SBOM
113+
id: sbom
114+
continue-on-error: true
115+
run: pnpm sbom --sbom-format cyclonedx --prod > sbom.cdx.json
116+
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
117+
if: ${{ steps.sbom.outcome == 'success' }}
118+
with:
119+
name: sbom-cyclonedx
120+
path: sbom.cdx.json
114121

115122
test:
116123
name: Test

RULES.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -730,7 +730,8 @@ lint-staged # Runs on staged files
730730
- **Bundle (setup)**: `dist/setup.js` (ESM, minified)
731731
- **Bundle (post)**: `dist/post.js` (ESM, minified) - post-action cache hook
732732
- **Bundle (plugin)**: `dist/plugin/fro-bot-agent.js` (ESM, self-contained)
733-
- **Licenses**: `dist/licenses.txt` (auto-extracted)
733+
- **Third-party notices**: `dist/THIRD_PARTY_NOTICES.txt` (auto-generated, committed attribution for the bundled dependencies)
734+
- **SBOM**: a CycloneDX dependency SBOM is generated in CI (`pnpm sbom`) and uploaded as a build artifact (not committed)
734735
- **Source maps**: Not included in production
735736

736737
### dist/ Directory Rules

0 commit comments

Comments
 (0)