@@ -3637,12 +3637,11 @@ async function run(config) {
36373637 }
36383638
36393639 // Parse and display SBOM summary regardless of stop exit code.
3640- const sbomEntries = ( 0 , _sbom_summary_js__WEBPACK_IMPORTED_MODULE_4__ /* .parseSBOMEntries */ . w ) ( stopOutput ) ;
3641- if ( sbomEntries . length > 0 ) {
3642- const reportJobSummary = _actions_core__WEBPACK_IMPORTED_MODULE_0__ . getBooleanInput ( 'report-job-summary' ) ;
3643- if ( reportJobSummary ) {
3644- await ( 0 , _sbom_summary_js__WEBPACK_IMPORTED_MODULE_4__ /* .writeSBOMSummary */ . k ) ( _actions_core__WEBPACK_IMPORTED_MODULE_0__ , sbomEntries ) ;
3645- }
3640+ const reportJobSummary = _actions_core__WEBPACK_IMPORTED_MODULE_0__ . getBooleanInput ( 'report-job-summary' ) ;
3641+ if ( reportJobSummary ) {
3642+ const sbomEntries = ( 0 , _sbom_summary_js__WEBPACK_IMPORTED_MODULE_4__ /* .parseSBOMEntries */ . ws ) ( stopOutput ) ;
3643+ const sbomEnabled = ( 0 , _sbom_summary_js__WEBPACK_IMPORTED_MODULE_4__ /* .isSBOMEnabled */ . iE ) ( stopOutput ) ;
3644+ await ( 0 , _sbom_summary_js__WEBPACK_IMPORTED_MODULE_4__ /* .writeSBOMSummary */ . kq ) ( _actions_core__WEBPACK_IMPORTED_MODULE_0__ , sbomEntries , { sbomEnabled } ) ;
36463645 }
36473646
36483647 if ( retval !== 0 ) {
@@ -3677,8 +3676,9 @@ __webpack_handle_async_dependencies__();
36773676/***/ ( ( __unused_webpack___webpack_module__ , __webpack_exports__ , __nccwpck_require__ ) => {
36783677
36793678/* harmony export */ __nccwpck_require__ . d ( __webpack_exports__ , {
3680- /* harmony export */ "w" : ( ) => ( /* binding */ parseSBOMEntries ) ,
3681- /* harmony export */ "k" : ( ) => ( /* binding */ writeSBOMSummary )
3679+ /* harmony export */ "ws" : ( ) => ( /* binding */ parseSBOMEntries ) ,
3680+ /* harmony export */ "iE" : ( ) => ( /* binding */ isSBOMEnabled ) ,
3681+ /* harmony export */ "kq" : ( ) => ( /* binding */ writeSBOMSummary )
36823682/* harmony export */ } ) ;
36833683/**
36843684 * Pure helpers for parsing and displaying SBOM results in the
@@ -3689,10 +3689,11 @@ __webpack_handle_async_dependencies__();
36893689/**
36903690 * Parses SBOM file entries from cimon agent stop output.
36913691 * Each JSON log line that contains `"SBOM files written"` is expected
3692- * to carry `cyclonedx` and/or `spdx` path fields.
3692+ * to carry `cyclonedx` and/or `spdx` path fields, and optionally
3693+ * `components`, `relationships`, and `artifacts` counts.
36933694 *
36943695 * @param {string } output - Combined stdout+stderr from `cimon agent stop`.
3695- * @returns {Array<{cyclonedx: string, spdx: string}> }
3696+ * @returns {Array<{cyclonedx: string, spdx: string, components: number, relationships: number, artifacts: number }> }
36963697 */
36973698function parseSBOMEntries ( output ) {
36983699 const entries = [ ] ;
@@ -3704,6 +3705,9 @@ function parseSBOMEntries(output) {
37043705 entries . push ( {
37053706 cyclonedx : parsed . cyclonedx || '' ,
37063707 spdx : parsed . spdx || '' ,
3708+ components : parsed . components || 0 ,
3709+ relationships : parsed . relationships || 0 ,
3710+ artifacts : parsed . artifacts || 0 ,
37073711 } ) ;
37083712 }
37093713 } catch {
@@ -3714,29 +3718,96 @@ function parseSBOMEntries(output) {
37143718}
37153719
37163720/**
3717- * Builds a GitHub Actions job summary table from SBOM entries.
3721+ * Checks whether the cimon stop output indicates that SBOM generation
3722+ * was enabled (i.e. the feature was active, regardless of whether any
3723+ * SBOMs were actually produced).
3724+ *
3725+ * @param {string } output - Combined stdout+stderr from `cimon agent stop`.
3726+ * @returns {boolean }
3727+ */
3728+ function isSBOMEnabled ( output ) {
3729+ // The SBOM feature logs at least one of these markers when active.
3730+ return (
3731+ output . includes ( '"SBOM files written"' ) ||
3732+ output . includes ( '"SBOM generation"' ) ||
3733+ output . includes ( 'CIMON_SBOM_ENABLED' )
3734+ ) ;
3735+ }
3736+
3737+ /**
3738+ * Builds a GitHub Actions job summary with SBOM results.
3739+ * Handles three scenarios:
3740+ * 1. SBOMs generated → table with details per SBOM
3741+ * 2. SBOM enabled but none generated → informational notice
3742+ * 3. SBOM not enabled → no summary (silent)
37183743 *
37193744 * @param {import('@actions/core') } core - The @actions/core module.
3720- * @param {Array<{cyclonedx: string, spdx: string}> } sbomEntries
3745+ * @param {Array<{cyclonedx: string, spdx: string, components: number, relationships: number, artifacts: number}> } sbomEntries
3746+ * @param {{sbomEnabled: boolean} } options
37213747 */
3722- async function writeSBOMSummary ( core , sbomEntries ) {
3723- if ( sbomEntries . length === 0 ) return ;
3748+ async function writeSBOMSummary ( core , sbomEntries , options = { } ) {
3749+ const { sbomEnabled = false } = options ;
37243750
3725- const rows = [ [ 'Format' , 'Path' ] ] ;
3726- for ( const entry of sbomEntries ) {
3727- if ( entry . cyclonedx ) {
3728- rows . push ( [ 'CycloneDX' , `\`${ entry . cyclonedx } \`` ] ) ;
3729- }
3730- if ( entry . spdx ) {
3731- rows . push ( [ 'SPDX' , `\`${ entry . spdx } \`` ] ) ;
3751+ // Case 3: SBOM not enabled — nothing to report.
3752+ if ( sbomEntries . length === 0 && ! sbomEnabled ) return ;
3753+
3754+ // Case 2: SBOM was enabled but produced no output.
3755+ if ( sbomEntries . length === 0 && sbomEnabled ) {
3756+ await core . summary
3757+ . addHeading ( 'Cimon SBOM Report' , 2 )
3758+ . addRaw (
3759+ 'SBOM generation was enabled but no SBOMs were produced. ' +
3760+ 'This can happen when no build artifacts (executables or libraries) were detected during the build.\n'
3761+ )
3762+ . write ( ) ;
3763+ return ;
3764+ }
3765+
3766+ // Case 1: SBOMs generated — build a rich summary.
3767+ const totalComponents = sbomEntries . reduce (
3768+ ( sum , e ) => sum + e . components ,
3769+ 0
3770+ ) ;
3771+ const totalRelationships = sbomEntries . reduce (
3772+ ( sum , e ) => sum + e . relationships ,
3773+ 0
3774+ ) ;
3775+
3776+ // Overview line.
3777+ const parts = [
3778+ `**${ sbomEntries . length } ** SBOM${ sbomEntries . length > 1 ? 's' : '' } generated during build` ,
3779+ ] ;
3780+ if ( totalComponents > 0 ) {
3781+ parts . push (
3782+ `covering **${ totalComponents } ** component${ totalComponents > 1 ? 's' : '' } ` +
3783+ ` and **${ totalRelationships } ** relationship${ totalRelationships !== 1 ? 's' : '' } `
3784+ ) ;
3785+ }
3786+
3787+ // Detail table: one row per SBOM entry.
3788+ const hasStats = totalComponents > 0 ;
3789+ const header = hasStats
3790+ ? [ '#' , 'CycloneDX' , 'SPDX' , 'Components' , 'Relationships' ]
3791+ : [ '#' , 'CycloneDX' , 'SPDX' ] ;
3792+
3793+ const rows = [ header ] ;
3794+ for ( let i = 0 ; i < sbomEntries . length ; i ++ ) {
3795+ const entry = sbomEntries [ i ] ;
3796+ const row = [
3797+ `${ i + 1 } ` ,
3798+ entry . cyclonedx ? `\`${ entry . cyclonedx } \`` : '-' ,
3799+ entry . spdx ? `\`${ entry . spdx } \`` : '-' ,
3800+ ] ;
3801+ if ( hasStats ) {
3802+ row . push ( `${ entry . components } ` ) ;
3803+ row . push ( `${ entry . relationships } ` ) ;
37323804 }
3805+ rows . push ( row ) ;
37333806 }
37343807
37353808 await core . summary
37363809 . addHeading ( 'Cimon SBOM Report' , 2 )
3737- . addRaw (
3738- `**${ sbomEntries . length } ** SBOM${ sbomEntries . length > 1 ? 's' : '' } generated during build.\n\n`
3739- )
3810+ . addRaw ( parts . join ( ', ' ) + '.\n\n' )
37403811 . addTable ( rows )
37413812 . write ( ) ;
37423813}
0 commit comments