Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
d536636
ci(release): add SBOM evidence for published packages, disable npm pu…
turbobobbytraykov Oct 26, 2026
ebd5c3f
chore(mcp): declare the MIT license in @igniteui/mcp-server's package…
turbobobbytraykov Oct 26, 2026
c4faf5b
ci(release): drop registry-url from the build job
turbobobbytraykov Oct 26, 2026
48562a5
chore(release): set versions to 0.0.0 for the dummy release
turbobobbytraykov Oct 26, 2026
ecf2d4a
Revert "chore(release): set versions to 0.0.0 for the dummy release"
turbobobbytraykov Oct 27, 2026
560edd4
ci(release): re-enable npm and MCP Registry publishing
turbobobbytraykov Oct 27, 2026
62e51a9
Merge branch 'master' into btraykov/sbom-generation
kdinev Oct 28, 2026
69a01d6
Remove SPDX SBOM-related logic; was not required in the first place
turbobobbytraykov Oct 28, 2026
27dbfd9
chore(release): disable publishing and set versions to 0.0.1 for a du…
turbobobbytraykov Oct 28, 2026
1012fc4
Merge branch 'master' into btraykov/sbom-generation
kdinev Nov 2, 2026
23ce5e0
Merge branch 'master' into btraykov/sbom-generation
kdinev Nov 4, 2026
8fe3306
Merge branch 'master' into btraykov/sbom-generation
kdinev Nov 4, 2026
fb44ef4
Account for Typescript strict
turbobobbytraykov Nov 4, 2026
6b46eee
chore(release): restore versions and re-enable npm and MCP Registry p…
turbobobbytraykov Nov 4, 2026
7510480
chore(release): set versions to 15.8.3-alpha.0
turbobobbytraykov Nov 4, 2026
9860328
Add conditional execution for downloading artifacts
turbobobbytraykov Nov 4, 2026
b547593
Merge branch 'master' into btraykov/sbom-generation
turbobobbytraykov Nov 4, 2026
d57350f
Refactor direct dependency check in SBOM build
turbobobbytraykov Nov 4, 2026
87fb93e
Merge branch 'master' into btraykov/sbom-generation
turbobobbytraykov Nov 4, 2026
4505433
Merge branch 'master' into btraykov/sbom-generation
turbobobbytraykov Nov 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
322 changes: 308 additions & 14 deletions .github/workflows/npm-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,36 @@ on:
description: "Release tag to register with the MCP Registry (e.g. 15.8.0)"
required: true

permissions:
id-token: write
contents: read
permissions: {}

jobs:
# Builds, packs and inventories the release. Holds no publishing rights and no OIDC token.
build:
name: Build and inventory
if: ${{ github.event_name == 'release' }}
runs-on: ubuntu-latest
timeout-minutes: 60
permissions:
contents: read
env:
VERSION: ${{ github.event.release.tag_name }}
outputs:
npm-tag: ${{ steps.npm-tag.outputs.npm-tag }}
packages: ${{ steps.digests.outputs.packages }}
checksums: ${{ steps.digests.outputs.checksums }}

steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
node-version: 24
registry-url: "https://registry.npmjs.org"
persist-credentials: false

- run: echo "VERSION=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_ENV
- run: echo ${VERSION}
# No registry-url: this job never publishes, and the .npmrc it writes references
# ${NODE_AUTH_TOKEN}, which setup-node v7 no longer defaults and yarn 1 refuses to leave unset.
- name: Setup Node.js
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: 24

- name: Install packages
run: yarn install --frozen-lockfile
Expand All @@ -41,27 +53,309 @@ jobs:
run: npm run validate:package --workspace=@igniteui/mcp-server -- --expected-version "$VERSION"

- name: Define npm tag
id: npm-tag
run: |
if [[ ${VERSION} == *"alpha"* || ${VERSION} == *"beta"* || ${VERSION} == *"rc"* ]]; then
NPM_TAG=next
else
NPM_TAG=latest
fi
echo "NPM_TAG=$NPM_TAG" >> $GITHUB_ENV
echo "npm-tag=$NPM_TAG" >> $GITHUB_OUTPUT
echo "$NPM_TAG"

# Packs every publishable package, then describes the exact tarballs it produced. Everything
# downstream uses those files, so their digests, SBOMs, attestations and publication agree.
- name: Generate delivered SBOMs (CycloneDX 1.6)
run: node scripts/build-sbom.js --release="$VERSION" --out=artifacts

- name: Generate build environment SBOM (CycloneDX 1.6)
# Supplementary best-effort inventory: npm tree errors are captured for the release notes.
run: node scripts/build-sbom.js --release="$VERSION" --out=artifacts --build-env

- name: Record package digests
id: digests
run: |
set -euo pipefail
cd artifacts

{
echo "checksums<<CHECKSUMS_EOF"
sha256sum *.tgz
echo "CHECKSUMS_EOF"
echo "packages=$(jq -c . packages.json)"
} >> $GITHUB_OUTPUT

{ echo "### Packed artifacts"; echo '```'; sha256sum *.tgz; echo '```'; } >> $GITHUB_STEP_SUMMARY

# Rendered here because this job has the compiled tooling; the write-scoped job only applies it.
- name: Prepare release notes
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
mkdir -p release-notes
gh release view "$VERSION" -R "${{ github.repository }}" --json body --jq .body > release-notes/original.md
node scripts/update-release-notes.js \
release-notes/original.md \
"artifacts/igniteui-cli-release-${VERSION}.supply-chain.md" \
release-notes/updated.md

- name: Upload release artifacts
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: release-artifacts
path: artifacts/*
retention-days: 30
if-no-files-found: error

- name: Upload release notes
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: release-notes
path: release-notes/*
retention-days: 30
if-no-files-found: error

attest:
name: Attest build provenance
needs: build
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: read
id-token: write
attestations: write
env:
# Artifact attestations are unavailable in private repositories outside GitHub Enterprise Cloud.
ATTESTATIONS_ENABLED: ${{ !github.event.repository.private }}

steps:
- name: Download release artifacts
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: release-artifacts
path: artifacts
digest-mismatch: error

- name: Verify the packages are the ones that were built
env:
EXPECTED: ${{ needs.build.outputs.checksums }}
run: |
set -euo pipefail
cd artifacts
printf '%s\n' "$EXPECTED" | sha256sum --check --strict

- name: Attest build provenance
id: attest-provenance
if: env.ATTESTATIONS_ENABLED == 'true'
uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4.2.2
with:
subject-path: artifacts/*.tgz

- name: Collect attestation bundle
if: env.ATTESTATIONS_ENABLED == 'true'
run: |
set -euo pipefail
mkdir -p attestations
cp "${{ steps.attest-provenance.outputs.bundle-path }}" attestations/provenance.sigstore.json
echo "- Provenance: ${{ steps.attest-provenance.outputs.attestation-url }}" >> $GITHUB_STEP_SUMMARY

- name: Upload attestation bundle
if: env.ATTESTATIONS_ENABLED == 'true'
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: attestations-provenance
path: attestations/*
retention-days: 30
if-no-files-found: error

# One SBOM attestation per package: each binds that package's delivered SBOM to its tarball digest.
attest-sbom:
name: Attest SBOM (${{ matrix.package.name }})
needs: build
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: read
id-token: write
attestations: write
strategy:
matrix:
package: ${{ fromJSON(needs.build.outputs.packages) }}
env:
ATTESTATIONS_ENABLED: ${{ !github.event.repository.private }}

steps:
- name: Download release artifacts
if: env.ATTESTATIONS_ENABLED == 'true'
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: release-artifacts
path: artifacts
digest-mismatch: error

- name: Verify the packages are the ones that were built
if: env.ATTESTATIONS_ENABLED == 'true'
env:
EXPECTED: ${{ needs.build.outputs.checksums }}
run: |
set -euo pipefail
cd artifacts
printf '%s\n' "$EXPECTED" | sha256sum --check --strict

- name: Attest SBOM (CycloneDX)
id: attest-sbom
if: env.ATTESTATIONS_ENABLED == 'true'
uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4.2.2
with:
subject-path: artifacts/${{ matrix.package.tarball }}
sbom-path: artifacts/${{ matrix.package.sbom }}

- name: Collect attestation bundle
if: env.ATTESTATIONS_ENABLED == 'true'
env:
PACKAGE_ID: ${{ matrix.package.id }}
PACKAGE_NAME: ${{ matrix.package.name }}
run: |
set -euo pipefail
mkdir -p attestations
cp "${{ steps.attest-sbom.outputs.bundle-path }}" "attestations/${PACKAGE_ID}.sbom-cyclonedx.sigstore.json"
echo "- SBOM (CycloneDX) for ${PACKAGE_NAME}: ${{ steps.attest-sbom.outputs.attestation-url }}" >> $GITHUB_STEP_SUMMARY

- name: Upload attestation bundle
if: env.ATTESTATIONS_ENABLED == 'true'
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: attestations-sbom-${{ matrix.package.id }}
path: attestations/*
retention-days: 30
if-no-files-found: error

# The only job that can publish to npm. It compiles nothing and never checks out the repository.
publish:
name: Publish to npmjs.org
needs: [build, attest, attest-sbom]
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
id-token: write

steps:
- name: Setup Node.js
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: 24
registry-url: "https://registry.npmjs.org"

- name: Download release artifacts
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: release-artifacts
path: artifacts
digest-mismatch: error

- name: Verify the packages that were built and attested
env:
EXPECTED: ${{ needs.build.outputs.checksums }}
run: |
set -euo pipefail
cd artifacts
printf '%s\n' "$EXPECTED" | sha256sum --check --strict

# Publishing the packed tarballs guarantees the published bytes are the attested bytes.
# Authentication and npm provenance both come from the OIDC token, so no npm token is needed.
- name: Publish packages
# use npm run as yarn run changes the registry and publishes to https://registry.yarnpkg.com
run: npm run deploy ${NPM_TAG}
env:
NPM_TAG: ${{ needs.build.outputs.npm-tag }}
PACKAGES: ${{ needs.build.outputs.packages }}
run: |
set -euo pipefail
cd artifacts
# In dependency order, so no package is published before the workspace packages it depends on.
jq -r '.[].tarball' <<< "$PACKAGES" | while read -r tarball; do
npm publish "./${tarball}" --tag "$NPM_TAG"
done

attach-to-release:
name: Attach release evidence
needs: [build, attest, attest-sbom]
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: write
env:
VERSION: ${{ github.event.release.tag_name }}

steps:
- name: Download release artifacts
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: release-artifacts
path: artifacts

- name: Download attestation bundles
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
if: ${{ !github.event.repository.private }}
with:
pattern: attestations-*
merge-multiple: true
path: attestations

- name: Download release notes
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: release-notes
path: release-notes

- name: Publish supply-chain notes to the release
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail

# The notes were merged into the body the build job saw; never overwrite a later edit.
gh release view "$VERSION" -R "${{ github.repository }}" --json body --jq .body > current.md
if cmp -s current.md release-notes/original.md; then
gh release edit "$VERSION" -R "${{ github.repository }}" --notes-file release-notes/updated.md
else
echo "::warning title=Release notes::The release notes changed during the build; the supply-chain section was not added. It is in the SBOM archive."
fi

- name: Package the SBOM archive
run: |
set -euo pipefail

zip -j "igniteui-cli-release-${VERSION}.sbom.zip" \
artifacts/*.cdx.json \
"artifacts/igniteui-cli-release-${VERSION}.supply-chain.md" \
"artifacts/igniteui-cli-release-${VERSION}.sbom.README.md"

- name: Attach evidence to the release
env:
GH_TOKEN: ${{ github.token }}
PACKAGES: ${{ needs.build.outputs.packages }}
run: |
set -euo pipefail

gh release upload "$VERSION" --clobber -R "${{ github.repository }}" \
artifacts/*.tgz \
artifacts/*.tgz.sha256 \
artifacts/*.tgz.sha512 \
$(jq -r '.[].sbom | "artifacts/" + .' <<< "$PACKAGES") \
"igniteui-cli-release-${VERSION}.sbom.zip" \
$(ls attestations/*.json 2>/dev/null || true)

# Separate job so a failed registry publish can be retried with "Re-run failed jobs"
# without repeating the npm publish.
# Skipped for prereleases — the MCP registry has no dist-tag equivalent,
# so publishing an alpha/beta/rc would make it the current version.
mcp-registry:
needs: build
if: ${{ !cancelled() && (github.event_name == 'workflow_dispatch' || (needs.build.result == 'success' && !github.event.release.prerelease)) }}
needs: publish
if: ${{ !cancelled() && (github.event_name == 'workflow_dispatch' || (needs.publish.result == 'success' && !github.event.release.prerelease)) }}
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
defaults:
run:
working-directory: packages/igniteui-mcp/igniteui-doc-mcp
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
},
"scripts": {
"release": "lerna version --conventional-commits --force-publish=* --yes --no-git-tag-version --no-push",
"deploy": "lerna exec 'npm publish --tag'",
"version": "node ./scripts/versionScript.js",
"build": "node ./node_modules/typescript/lib/tsc.js && npm run config-schema && npm run build:mcp",
"build:mcp": "cd packages/igniteui-mcp/igniteui-doc-mcp && npx tsc && npx tsx scripts/build.ts",
Expand Down Expand Up @@ -65,6 +64,7 @@
"handlebars": "4.7.10"
},
"devDependencies": {
"@cyclonedx/cyclonedx-npm": "^6.0.1",
"@inquirer/prompts": "^7.9.0",
"@types/jasmine": "^5.1.13",
"@types/minimatch": "^5.1.2",
Expand Down
8 changes: 4 additions & 4 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "igniteui-cli",
"version": "15.8.2",
"version": "15.8.3-alpha.0",
"description": "CLI tool for creating Ignite UI projects",
"keywords": [
"CLI",
Expand Down Expand Up @@ -66,9 +66,9 @@
"all": true
},
"dependencies": {
"@igniteui/angular-templates": "~22.2.1582",
"@igniteui/cli-core": "~15.8.2",
"@igniteui/mcp-server": "~15.8.2",
"@igniteui/angular-templates": "~22.2.1583-alpha.0",
"@igniteui/cli-core": "~15.8.3-alpha.0",
"@igniteui/mcp-server": "~15.8.3-alpha.0",
"@inquirer/prompts": "^7.9.0",
"chalk": "^5.3.0",
"glob": "^11.0.0",
Expand Down
Loading
Loading