Skip to content

ci(release): add SBOM evidence and attestations for published packages - #1878

Open
turbobobbytraykov wants to merge 19 commits into
masterfrom
btraykov/sbom-generation
Open

turbobobbytraykov wants to merge 19 commits into
masterfrom
btraykov/sbom-generation

Conversation

@turbobobbytraykov

Copy link
Copy Markdown
Contributor

Description

Adds release supply-chain evidence for every package npm-publish.yml publishes, using the same approach as IgniteUI/igniteui-angular, IgniteUI/igniteui-webcomponents and IgniteUI/igniteui-react: one delivered CycloneDX SBOM per package, plus an overall SBOM README for the release. npm publishing now uses the exact tarballs that were inventoried and attested.

What the release workflow now does

  • build (read-only, no OIDC): builds as before, then scripts/build-sbom.js packs every publishable lerna package once. The packages are @igniteui/cli-core, @igniteui/angular-templates, @igniteui/angular-schematics, @igniteui/mcp-server and igniteui-cli, read from lerna.json and sorted in dependency order. For each tarball it:

    • installs it in isolation (--omit=dev --ignore-scripts), with workspace siblings resolved from the tarballs of the same build through npm overrides, because those versions aren't on npm yet;
    • generates a CycloneDX 1.6 SBOM with @cyclonedx/cyclonedx-npm, re-roots it on the published package and checks the graph for duplicate, unknown or unreachable refs;
    • validates the SBOM against the official schema, writes .sha256/.sha512 digests and runs npm audit on the delivered closure.

    It also writes a best-effort build-environment SBOM of the repository toolchain and one README covering every package in the release.

  • attest: build provenance for all tarballs. attest-sbom: a matrix job that creates one CycloneDX SBOM attestation per package. Both are skipped in private repos.

  • publish: now publishes the verified tarballs in dependency order instead of lerna exec npm publish, after verifying their checksums. It runs only after both attestation jobs succeed.

  • attach-to-release: uploads the tarballs, digests, delivered SBOMs, an SBOM archive (with README and supply-chain notes) and the attestation bundles to the release. It also adds a supply-chain section to the release notes, unless the notes were edited during the build.

  • mcp-registry: now needs: publish and runs after a successful npm publish (still skipped for prereleases). The workflow_dispatch path is unchanged.

Licenses in each SBOM are the ones the installed packages declare, as in the Angular, Web Components and React releases. No external license lookup is performed and no SPDX documents are produced.

Dependency manifest change: adds @cyclonedx/cyclonedx-npm (^6.0.1) as a root devDependency (package.json / yarn.lock). The generator runs from node_modules, so release-time generation does not fetch it from the network. Also removes the root deploy script, which the workflow no longer uses.

Package manifest change: adds "license": "MIT" to @igniteui/mcp-server's package.json, matching the License section of its README. Without it, that package's SBOM component had no license.

Versions: this branch sets igniteui-cli, @igniteui/cli-core and @igniteui/mcp-server to 15.8.3-alpha.0, and @igniteui/angular-templates and @igniteui/angular-schematics to 22.2.1583-alpha.0, for a full prerelease of this flow.

Related Issue

Duplicate of #1865 (opened from the fork), pushed to this repository.

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update
  • Refactoring / code cleanup
  • Build / CI configuration change

Affected Packages

  • @igniteui/mcp-server (packages/igniteui-mcp): license field only

No other package contents change; the release workflow now inventories all of them.

Checklist

  • I have tested my changes locally (npm run jasmine: 714 passed, 2 pre-existing pending)
  • I have built the project successfully (tsc, tsconfig-pack, build:mcp)
  • I have run the linter (npm run lint: 0 errors; 1 pre-existing warning in packages/core/util/Schematics.ts)
  • I have added/updated tests as needed: not applicable. SBOM generation is a release supplement, not package functionality, so it has no Jasmine specs; it is validated by the release run below.
  • My changes do not introduce new warnings or errors

Additional Context

Ran scripts/build-sbom.js end to end locally against the current packages (15.8.2 / 22.2.1582). All five SBOMs and the build-environment SBOM validated against CycloneDX 1.6, and the overall README rendered with every package. Findings:

  • @igniteui/mcp-server declared no license in its package.json, so its SBOM component had none. Fixed in this PR by declaring MIT, as its README states.
  • Before the merge from master bumped zod to ^4.6.6, npm audit reported a high-severity advisory in zod, reached through @igniteui/mcp-server (and so also through igniteui-cli). The next release run's supply-chain notes will show whether the bump clears it.

Validated with a dummy release (previous revision; a re-run against this revision is pending). A 0.0.0 GitHub release in this fork ran the workflow with publish disabled and the versions temporarily set to 0.0.0 (both since reverted). All jobs succeeded. Checked:

  • All 27 expected release assets: per package, the tarball, .sha256, .sha512, SBOM and SBOM attestation bundle, plus the SBOM archive and the provenance bundle. Every digest matches its tarball.
  • Each SBOM is rooted on the published package (purl, MIT license, tarball hashes), has no unreachable components, and resolves workspace siblings to the tarballs of the same build.
  • gh attestation verify passes for every tarball with both the provenance and CycloneDX predicate types, bound to the release tag, commit and run. The attested SBOMs match the released ones.
  • The supply-chain section was added to the release notes without changing the authored text.

🤖 Generated with Claude Code

turbobobbytraykov and others added 15 commits October 26, 2026 17:09
…blish

Pack every publishable package once and describe the exact tarballs with
validated CycloneDX 1.6 SBOMs, build provenance and per-package SBOM
attestations, then attach the evidence to the GitHub release. Licenses a
package does not declare are looked up on ClearlyDefined; gaps produce a
warning instead of failing the release.

npm publish (and the MCP Registry job that depends on it) is disabled
until the flow is validated with a dummy GitHub release.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
….json

The README already states MIT; without the field the package's SBOM
component carried no license and every release raised a license-gap warning.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
setup-node v7 no longer exports a dummy NODE_AUTH_TOKEN, and yarn 1 fails on
the ${NODE_AUTH_TOKEN} reference in the .npmrc that registry-url writes. The
build job never publishes, so it does not need the registry configuration.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
TEST ONLY - revert before merging. The release workflow validates that the
MCP server's package.json and server.json match the release tag, so the
dummy 0.0.0 release needs igniteui-cli, @igniteui/cli-core and
@igniteui/mcp-server at 0.0.0 and the internal ~ranges pointing at it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This reverts commit 48562a5.
The dummy 0.0.0 release validated the workflow; the packages return to 15.8.2.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The release flow was validated end to end with a dummy 0.0.0 release:
the publish job runs again after the attestations, and the MCP Registry
job follows a successful npm publish.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…mmy release

TEST ONLY - revert before merging. Validates the per-package CycloneDX
SBOM flow with a dummy 0.0.1 GitHub release in this fork:

- the npm publish job is disabled, and the MCP Registry job is skipped
  with it, since it runs only after a successful publish;
- igniteui-cli, @igniteui/cli-core and @igniteui/mcp-server (package.json
  and server.json) are set to 0.0.1, with the internal ~ranges pointing at
  it, because the workflow checks the MCP server version against the
  release tag.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ublishing

Reverts 27dbfd9 now that the dummy 0.0.1 release validated the SBOM flow
end to end:

- igniteui-cli, @igniteui/cli-core and @igniteui/mcp-server (package.json
  and server.json) are back at 15.8.2, matching IgniteUI/igniteui-cli master,
  with the internal ~ranges pointing at it again;
- the npm publish job runs again, and the MCP Registry job with it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Prepares a full prerelease of the SBOM release flow:

- igniteui-cli, @igniteui/cli-core and @igniteui/mcp-server (package.json
  and server.json) are set to 15.8.3-alpha.0;
- @igniteui/angular-templates and @igniteui/angular-schematics are set to
  22.2.1583-alpha.0, following the scheme in scripts/versionScript.ts;
- the internal ~ranges point at the new versions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings November 4, 2026 14:57
@coveralls

coveralls commented Nov 4, 2026 •

Copy link
Copy Markdown

Coverage Status

coverage: 95.372%. remained the same — btraykov/sbom-generation into master

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Public-release evidence uploads can succeed without the promised attestation bundles because download failures and missing bundle files are ignored.

1 open issue
What changed in this PR

Adds supply-chain evidence to the CLI release process, tying package inventories and attestations to the exact tarballs published to npm.

Changes:

  • Generates per-package CycloneDX SBOMs, checksums, audit summaries, and release documentation.
  • Separates build, attestation, publication, and evidence-upload jobs with scoped permissions.
  • Updates package versions for the prerelease and declares the MCP server’s MIT license.
File Description
yarn.lock Locks the CycloneDX generator and its dependencies.
scripts/​update-release-notes.ts Adds a command to merge supply-chain release notes.
scripts/​templates/​sbom-readme.md Documents inventory scope and artifact verification.
scripts/​sbom-notes.ts Formats audit results, diagnostics, and release-note sections.
scripts/​sbom-document.ts Re-roots SBOMs and validates dependency graph references.
scripts/​build-sbom.ts Packs packages and generates validated inventories and digests.
packages/​ng-schematics/​package.json Updates prerelease version and workspace dependency ranges.
packages/​igx-templates/​package.json Updates prerelease version and core dependency range.
packages/​igniteui-mcp/​igniteui-doc-mcp/​server.json Synchronizes MCP registry prerelease versions.
packages/​igniteui-mcp/​igniteui-doc-mcp/​package.json Updates prerelease version and declares MIT licensing.
packages/​core/​package.json Updates the core prerelease version.
packages/​cli/​package.json Updates prerelease version and workspace dependency ranges.
package.json Adds the CycloneDX generator and removes the old deploy script.
.github/​workflows/​npm-publish.yml Adds inventory, attestation, verified-tarball publishing, and release evidence jobs.

Review effort: Balanced

Comment thread .github/workflows/npm-publish.yml Resolved
turbobobbytraykov and others added 2 commits November 4, 2026 17:44
Copilot code review comment

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Release notes mislabel the subject package’s direct dependencies as transitive because the “Direct” flag reflects the synthetic audit workspace rather than the subject package’s runtime dependency declarations.

1 open issue
1 resolved since last review

Review effort: Balanced

Comment thread scripts/build-sbom.ts Resolved
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Nested dependencies under the promoted main component are neither normalized nor included in known refs, causing valid dependency trees to fail validation and block releases.

No open issues 🎉

1 previously missed issue

In code that hasn't changed since last review

High severity Normalize descendants when promoting the main component

scripts/​build-sbom.ts:365

CycloneDX preserves physical component nesting by default, but promoteMainComponent() does not normalize descendants inside main.components, and assertDeliveredShape() also omits those descendants from its known refs. If the subject contains a nested dependency, its graph ref is normalized while its component ref is not, so validation rejects a valid dependency tree and stops the release. Pass --flatten-components here, or update both helpers to traverse the promoted component's descendants.

1 resolved since last review

Review effort: Balanced

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants