Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
148 changes: 148 additions & 0 deletions .github/workflows/reusable-generate-sbom.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
name: Reusable - Generate SBOM

on:
workflow_call:
inputs:
lvproj_path:
description: 'Path to the .lvproj file, relative to the caller repository.'
required: true
type: string
labview_version:
description: 'Linux container release (YYYYq1 or YYYYq3; minimum 2025q3).'
required: false
type: string
default: '2026q1'
labview_bitness:
required: true
type: string
build_spec_name:
required: true
type: string
target_name:
required: false
type: string
default: 'My Computer'
product_name:
required: true
type: string
product_version:
required: true
type: string
output_path:
description: 'SBOM output path, relative to the caller repository.'
required: true
type: string
format:
required: false
type: string
default: 'cyclonedx'
schema_version:
required: false
type: string
default: '1.5'
artifact_name:
description: 'Name for the uploaded SBOM artifact.'
required: false
type: string
default: 'sbom'

permissions:
contents: read

jobs:
generate-sbom:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955
- name: Validate LabVIEW Version for Linux Docker
id: validate-version
shell: pwsh
env:
LABVIEW_VERSION_INPUT: ${{ inputs.labview_version }}
run: |
$lvVersion = $env:LABVIEW_VERSION_INPUT
if ($lvVersion -match '^(\d{4})q([13])$') {
$year = [int]$Matches[1]
$quarter = [int]$Matches[2]
} else {
throw "Invalid LabVIEW version format: $lvVersion. Expected YYYYq1 or YYYYq3 (for example, 2026q1)."
}

$isValid = ($year -gt 2025) -or (($year -eq 2025) -and ($quarter -ge 3))
if (-not $isValid) {
throw "LabVIEW version $lvVersion is not available in Linux Docker. Minimum supported version is 2025q3."
}

"year=$year" >> $env:GITHUB_OUTPUT
- name: Generate SBOM in LabVIEW Linux container
shell: pwsh
env:
LABVIEW_CONTAINER_VERSION: ${{ inputs.labview_version }}
LABVIEW_YEAR: ${{ steps.validate-version.outputs.year }}
LVPROJ_PATH: ${{ inputs.lvproj_path }}
LABVIEW_BITNESS: ${{ inputs.labview_bitness }}
BUILD_SPEC_NAME: ${{ inputs.build_spec_name }}
TARGET_NAME: ${{ inputs.target_name }}
PRODUCT_NAME: ${{ inputs.product_name }}
PRODUCT_VERSION: ${{ inputs.product_version }}
OUTPUT_PATH: ${{ inputs.output_path }}
SBOM_FORMAT: ${{ inputs.format }}
SBOM_SCHEMA_VERSION: ${{ inputs.schema_version }}
run: |
$containerScript = @'
set -e
apt-get update && apt-get install -y wget
wget --no-check-certificate -O /tmp/vipm.deb https://traffic.libsyn.com/secure/jkinc/vipm_26.3.1-4025_amd64.deb
dpkg -i /tmp/vipm.deb
vipm --version
output_file="/workspace/$OUTPUT_PATH"
mkdir -p "$(dirname "$output_file")"
timeout 300 xvfb-run -a vipm sbom "/workspace/$LVPROJ_PATH" \
--format "$SBOM_FORMAT" \
--schema-version "$SBOM_SCHEMA_VERSION" \
--labview-version "$LABVIEW_YEAR" \
--labview-bitness "$LABVIEW_BITNESS" \
--lvproj-build-spec "$BUILD_SPEC_NAME" \
--lvproj-target "$TARGET_NAME" \
--product-name "$PRODUCT_NAME" \
--product-version "$PRODUCT_VERSION" \
--output "$output_file"
'@

$workspacePath = $env:GITHUB_WORKSPACE
$labviewImage = "nationalinstruments/labview:$($env:LABVIEW_CONTAINER_VERSION)-linux"
docker run --rm `
--volume "${workspacePath}:/workspace" `
--env LVPROJ_PATH `
--env LABVIEW_YEAR `
--env LABVIEW_BITNESS `
--env BUILD_SPEC_NAME `
--env TARGET_NAME `
--env PRODUCT_NAME `
--env PRODUCT_VERSION `
--env OUTPUT_PATH `
--env SBOM_FORMAT `
--env SBOM_SCHEMA_VERSION `
$labviewImage `
bash -c $containerScript

if ($LASTEXITCODE -ne 0) {
throw "SBOM generation inside the LabVIEW Linux container failed with exit code $LASTEXITCODE"
}
- name: Verify SBOM exists
shell: pwsh
env:
OUTPUT_PATH: ${{ inputs.output_path }}
run: |
$sbomPath = Join-Path $env:GITHUB_WORKSPACE $env:OUTPUT_PATH
if (-not (Test-Path -Path $sbomPath -PathType Leaf)) {
throw "No SBOM file found at '$sbomPath'. Ensure vipm sbom completed successfully."
}
Write-Host "SBOM verified: $sbomPath"
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
if: success()
with:
name: ${{ inputs.artifact_name }}
path: ${{ inputs.output_path }}
retention-days: 90
if-no-files-found: error
4 changes: 3 additions & 1 deletion actions/OpenSourceActions.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
'Invoke-CloseLabVIEW'
'Invoke-ConfigureLabview'
'Invoke-GenerateReleaseNotes'
'Invoke-GenerateSbom'
'Invoke-MissingInProject'
'Invoke-ModifyVIPBDisplayInfo'
'Invoke-PrepareLabVIEWSource'
Expand All @@ -43,7 +44,8 @@
PSData = @{
ReleaseNotes = @(
'1.0.0 - Initial release: adapters for apply-vipc, build-lvlibp, missing-in-project, run-unit-tests; Format-UnitTestReport helper; discovery flags; DryRun; cross-platform g-cli support.'
'1.1.1 - Fix: update-vipm-dependencies now fails the job (instead of warning and continuing) when any package cannot be resolved/updated, so a partial dependency bump never reaches vipm.lock or a PR.'
'1.1.0 - Add generate-sbom adapter: minimal CycloneDX SBOM generation via vipm sbom, assuming VIPM is already installed/activated on the runner.'
'1.1.1 - Fix Invoke-GenerateSbom dispatcher DryRun compatibility with aliases and optional product metadata; make update-vipm-dependencies fail when a package cannot be resolved or updated.'
)
}
}
Expand Down
51 changes: 51 additions & 0 deletions actions/OpenSourceActions.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -930,4 +930,55 @@ function Invoke-ViaLvDocker {
DockerImage = $DockerImage
}
return Invoke-OpenSourceActionScript -ScriptSegments @('via-lv-docker','RunViaLvDocker.ps1') -Arguments $args -DryRun:$DryRun -gcliPath $gcliPath
}

# Generates a CycloneDX SBOM for a LabVIEW build spec using `vipm sbom`.
# Assumes VIPM CLI is already installed/activated and LabVIEW is available on
# the runner; the caller is responsible for that (same as ni/labview-icon-editor#540's
# own "Install VIPM CLI" step).
# LvprojPath: Path to the .lvproj file to scan.
# LabVIEWVersion: LabVIEW version (YYYY).
# LabVIEWBitness: "32" or "64".
# BuildSpecName: Name of the build specification to scope the SBOM to.
# TargetName: LabVIEW project target containing the build spec (default: "My Computer").
# ProductName: Name recorded in the SBOM's metadata.component.
# ProductVersion: Version recorded in the SBOM's metadata.component.
# OutputPath: Output file path for the generated SBOM.
# Format: SBOM output format (default: "cyclonedx").
# SchemaVersion: CycloneDX schema version (default: "1.5").
# DryRun: If set, prints the command instead of executing it.
function Invoke-GenerateSbom {
[CmdletBinding()]
param(
[Parameter(Mandatory)][Alias('ProjectPath', 'ProjectFile')] [string] $LvprojPath,
[Parameter(Mandatory)][Alias('LVVersion', 'MinimumSupportedLVVersion')] [string] $LabVIEWVersion,
[Parameter(Mandatory)][Alias('LVBitness', 'SupportedBitness')] [string] $LabVIEWBitness,
[Parameter(Mandatory)][Alias('Build_Spec')] [string] $BuildSpecName,
[Parameter()] [string] $TargetName = "My Computer",
[Parameter()] [string] $ProductName = "",
[Parameter()] [string] $ProductVersion = "",
[Parameter(Mandatory)] [string] $OutputPath,
[Parameter()] [string] $Format = "cyclonedx",
[Parameter()] [string] $SchemaVersion = "1.5",
[switch] $DryRun
)
Write-Information "Invoking GenerateSbom" -InformationAction Continue

$result = Invoke-OpenSourceActionScript `
-ScriptSegments @('generate-sbom', 'GenerateSbom.ps1') `
-Arguments @{
LvprojPath = $LvprojPath
LabVIEWVersion = $LabVIEWVersion
LabVIEWBitness = $LabVIEWBitness
BuildSpecName = $BuildSpecName
TargetName = $TargetName
ProductName = $ProductName
ProductVersion = $ProductVersion
OutputPath = $OutputPath
Format = $Format
SchemaVersion = $SchemaVersion
} `
-DryRun:$DryRun

return $result
}
158 changes: 158 additions & 0 deletions artifacts/linux/action-docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -585,6 +585,71 @@
}
}
},
"Invoke-GenerateSbom": {
"description": "Generates a CycloneDX SBOM for a LabVIEW build spec using `vipm sbom`. Assumes VIPM CLI is already installed/activated and LabVIEW is available on the runner; the caller is responsible for that (same as ni/labview-icon-editor#540's own \"Install VIPM CLI\" step). LvprojPath: Path to the .lvproj file to scan. LabVIEWVersion: LabVIEW version (YYYY). LabVIEWBitness: \"32\" or \"64\". BuildSpecName: Name of the build specification to scope the SBOM to. TargetName: LabVIEW project target containing the build spec (default: \"My Computer\"). ProductName: Name recorded in the SBOM's metadata.component. ProductVersion: Version recorded in the SBOM's metadata.component. OutputPath: Output file path for the generated SBOM. Format: SBOM output format (default: \"cyclonedx\"). SchemaVersion: CycloneDX schema version (default: \"1.5\"). DryRun: If set, prints the command instead of executing it.",
"parameters": {
"BuildSpecName": {
"type": "string",
"required": true,
"description": "Name of the build specification to scope the SBOM to"
},
"DryRun": {
"type": "boolean",
"required": false,
"description": "If set, prints the command instead of executing it"
},
"Format": {
"type": "string",
"required": false,
"default": "cyclonedx",
"description": "SBOM output format (default: \"cyclonedx\")"
},
"LabVIEWBitness": {
"type": "string",
"required": true,
"description": "\"32\" or \"64\""
},
"LabVIEWVersion": {
"type": "string",
"required": true,
"description": "LabVIEW version (YYYY)"
},
"LvprojPath": {
"type": "string",
"required": true,
"description": "Path to the"
},
"OutputPath": {
"type": "string",
"required": true,
"description": "Output file path for the generated SBOM"
},
"ProductName": {
"type": "string",
"required": false,
"default": "",
"description": "Name recorded in the SBOM's metadata"
},
"ProductVersion": {
"type": "string",
"required": false,
"default": "",
"description": "Version recorded in the SBOM's metadata"
},
"SchemaVersion": {
"type": "string",
"required": false,
"default": "1.5",
"description": "CycloneDX schema version (default: \"1"
},
"TargetName": {
"type": "string",
"required": false,
"default": "My Computer",
"description": "LabVIEW project target containing the build spec (default: \"My Computer\")"
}
}
},
"Invoke-MissingInProject": {
"description": "Lists files referenced in a LabVIEW project that are missing on disk. LVVersion: LabVIEW version of the project. SupportedBitness: Target LabVIEW bitness (32- or 64-bit). ProjectFile: Path to the .lvproj file to analyze. DryRun: If set, prints the command instead of executing it. gcliPath: Optional path prepended to PATH for locating the g CLI.",
"parameters": {
Expand Down Expand Up @@ -1892,6 +1957,99 @@
"type": "string"
}
],
"generate-sbom": [
{
"name": "lvproj_path",
"description": "Path to the .lvproj file to scan.",
"required": true,
"default": "",
"type": "string"
},
{
"name": "labview_version",
"description": "LabVIEW version (YYYY).",
"required": true,
"default": "",
"type": "string"
},
{
"name": "labview_bitness",
"description": "\"32\" or \"64\".",
"required": true,
"default": "",
"type": "string"
},
{
"name": "build_spec_name",
"description": "Name of the build specification to scope the SBOM to.",
"required": true,
"default": "",
"type": "string"
},
{
"name": "target_name",
"description": "LabVIEW project target containing the build spec.",
"required": false,
"default": "My Computer",
"type": "string"
},
{
"name": "product_name",
"description": "Name recorded in the SBOM metadata.component.",
"required": true,
"default": "",
"type": "string"
},
{
"name": "product_version",
"description": "Version recorded in the SBOM metadata.component.",
"required": true,
"default": "",
"type": "string"
},
{
"name": "output_path",
"description": "Output file path for the generated SBOM.",
"required": true,
"default": "",
"type": "string"
},
{
"name": "format",
"description": "SBOM output format.",
"required": false,
"default": "cyclonedx",
"type": "string"
},
{
"name": "schema_version",
"description": "CycloneDX schema version.",
"required": false,
"default": "1.5",
"type": "string"
},
{
"name": "working_directory",
"description": "Working directory where the action will run.",
"required": false,
"default": "",
"type": "string"
},
{
"name": "log_level",
"description": "Verbosity level (ERROR|WARN|INFO|DEBUG).",
"required": false,
"default": "INFO",
"type": "string"
},
{
"name": "dry_run",
"description": "If true, simulate the action without side effects.",
"required": false,
"default": false,
"type": "string"
}
],
"missing-in-project": [
{
"name": "lv_version",
Expand Down
Loading
Loading