Skip to content

Commit ea31dcd

Browse files
ekovaletsgithub-actions[bot]tadjik1
authored
ci(NODE-7025): New SBOM generation workflow on dependencies change (#4807)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Sergey Zelenov <mail@zelenov.su>
1 parent 8b900ee commit ea31dcd

File tree

4 files changed

+740
-48
lines changed

4 files changed

+740
-48
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
name: Generate SBOM
2+
description: Generates CycloneDX SBOM using cdxgen
3+
4+
inputs:
5+
output-file:
6+
description: "Output filename for the SBOM"
7+
required: false
8+
default: "sbom.json"
9+
10+
outputs:
11+
HAS_CHANGES:
12+
description: "Whether the SBOM has meaningful changes compared to the existing version"
13+
value: ${{ steps.generate.outputs.HAS_CHANGES }}
14+
15+
runs:
16+
using: composite
17+
steps:
18+
- name: Generate SBOM
19+
id: generate
20+
shell: bash
21+
env:
22+
SBOM_OUTPUT_FILE: ${{ inputs.output-file }}
23+
run: |
24+
SCRIPT_DIR="${{ github.action_path }}"
25+
chmod +x "${SCRIPT_DIR}/generate-sbom.sh"
26+
"${SCRIPT_DIR}/generate-sbom.sh" "$SBOM_OUTPUT_FILE"
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
#!/usr/bin/env bash
2+
#
3+
# generate-sbom.sh - Generate and validate CycloneDX SBOM
4+
#
5+
# Usage: ./generate-sbom.sh [output-file]
6+
#
7+
# Environment variables:
8+
# GITHUB_OUTPUT - Path to GitHub Actions output file (optional)
9+
#
10+
11+
set -euo pipefail
12+
13+
SBOM_FILE="${1:-sbom.json}"
14+
TEMP_SBOM="sbom-new.json"
15+
CYCLONEDX_CLI="/tmp/cyclonedx"
16+
CYCLONEDX_CLI_URL="https://github.com/CycloneDX/cyclonedx-cli/releases/download/v0.29.1/cyclonedx-linux-x64"
17+
JQ_NORMALIZER='del(.serialNumber) | del(.metadata.timestamp) | walk(if type == "object" and .timestamp then .timestamp = "TIMESTAMP_NORMALIZED" else . end)'
18+
19+
echo "Starting SBOM generation (output: $SBOM_FILE)"
20+
21+
echo "Generating SBOM for 'node' project..."
22+
23+
if ! npx @cyclonedx/cyclonedx-npm \
24+
--omit dev \
25+
--package-lock-only \
26+
--output-file "$TEMP_SBOM" \
27+
--output-format json \
28+
--spec-version 1.5; then
29+
echo "ERROR: Failed to generate SBOM" >&2
30+
exit 1
31+
fi
32+
33+
if [[ ! -f "$TEMP_SBOM" ]]; then
34+
echo "ERROR: SBOM file not found after generation" >&2
35+
exit 1
36+
fi
37+
38+
echo "SBOM file generated: $TEMP_SBOM"
39+
40+
echo "Downloading CycloneDX CLI..."
41+
42+
if ! curl -L -s -o "$CYCLONEDX_CLI" "$CYCLONEDX_CLI_URL"; then
43+
echo "ERROR: Failed to download CycloneDX CLI" >&2
44+
exit 1
45+
fi
46+
47+
chmod +x "$CYCLONEDX_CLI"
48+
49+
if [[ ! -x "$CYCLONEDX_CLI" ]]; then
50+
echo "ERROR: CycloneDX CLI is not executable" >&2
51+
exit 1
52+
fi
53+
54+
echo "CycloneDX CLI ready at $CYCLONEDX_CLI"
55+
56+
echo "Validating SBOM: $TEMP_SBOM"
57+
58+
if ! "$CYCLONEDX_CLI" validate --input-file "$TEMP_SBOM" --fail-on-errors; then
59+
echo "ERROR: SBOM validation failed for $TEMP_SBOM" >&2
60+
exit 1
61+
fi
62+
63+
echo "SBOM validation passed: $TEMP_SBOM"
64+
65+
echo "Checking for SBOM changes..."
66+
67+
HAS_CHANGES="false"
68+
69+
if [[ ! -f "$SBOM_FILE" ]]; then
70+
echo "No existing $SBOM_FILE found, creating initial version"
71+
mv "$TEMP_SBOM" "$SBOM_FILE"
72+
HAS_CHANGES="true"
73+
else
74+
echo "Comparing new SBOM with existing $SBOM_FILE..."
75+
76+
# Try cyclonedx diff for component-level comparison
77+
DIFF_OUTPUT=$("$CYCLONEDX_CLI" diff "$SBOM_FILE" "$TEMP_SBOM" --component-versions 2>/dev/null || true)
78+
79+
if echo "$DIFF_OUTPUT" | grep -q "^None$"; then
80+
echo "No component changes detected via cyclonedx diff"
81+
82+
# Double-check with jq normalization (excludes metadata like timestamps)
83+
if diff -q \
84+
<(jq -r "$JQ_NORMALIZER" < "$SBOM_FILE") \
85+
<(jq -r "$JQ_NORMALIZER" < "$TEMP_SBOM") > /dev/null 2>&1; then
86+
echo "No meaningful changes detected in SBOM"
87+
rm -f "$TEMP_SBOM"
88+
HAS_CHANGES="false"
89+
else
90+
echo "Changes detected in SBOM (non-component changes)"
91+
mv "$TEMP_SBOM" "$SBOM_FILE"
92+
HAS_CHANGES="true"
93+
fi
94+
else
95+
echo "Component changes detected:"
96+
echo "$DIFF_OUTPUT"
97+
mv "$TEMP_SBOM" "$SBOM_FILE"
98+
HAS_CHANGES="true"
99+
fi
100+
fi
101+
102+
if [[ -n "${GITHUB_OUTPUT:-}" ]]; then
103+
echo "HAS_CHANGES=${HAS_CHANGES}" >> "$GITHUB_OUTPUT"
104+
fi
105+
echo "Output: HAS_CHANGES=${HAS_CHANGES}"
106+
107+
if [[ ! -f "$SBOM_FILE" ]]; then
108+
echo "ERROR: Final SBOM file not found at $SBOM_FILE" >&2
109+
exit 1
110+
fi
111+
112+
echo "SBOM file validated: $SBOM_FILE"
113+
echo "SBOM generation completed successfully"

.github/workflows/sbom.yml

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
name: Post-Merge SBOM Update
2+
on:
3+
push:
4+
branches:
5+
- main
6+
paths:
7+
- 'package.json'
8+
- 'package-lock.json'
9+
10+
workflow_dispatch:
11+
12+
permissions:
13+
contents: write
14+
15+
jobs:
16+
sbom:
17+
name: Generate SBOM and Create PR
18+
runs-on: ubuntu-latest
19+
20+
concurrency:
21+
group: sbom-update
22+
cancel-in-progress: false
23+
24+
steps:
25+
- name: Checkout repository
26+
uses: actions/checkout@v5
27+
with:
28+
ref: ${{ github.ref }}
29+
token: ${{ secrets.GITHUB_TOKEN }}
30+
31+
- name: Install Node and dependencies
32+
uses: mongodb-labs/drivers-github-tools/node/setup@v3
33+
with:
34+
ignore_install_scripts: false
35+
36+
- name: Generate SBOM
37+
id: generate_sbom
38+
uses: ./.github/actions/sbom-update
39+
with:
40+
output-file: sbom.json
41+
42+
- name: Commit SBOM changes
43+
if: steps.generate_sbom.outputs.HAS_CHANGES == 'true'
44+
run: |
45+
git config user.name "github-actions[bot]"
46+
git config user.email "github-actions[bot]@users.noreply.github.com"
47+
git add sbom.json
48+
git commit -m "chore(deps): Update SBOM after dependency changes"
49+
git push
50+
echo "SBOM updated and committed" >> $GITHUB_STEP_SUMMARY
51+
continue-on-error: true

0 commit comments

Comments
 (0)