@@ -439,13 +439,19 @@ jobs:
439439 path : pywry/dist/*.whl
440440 retention-days : 30
441441
442+ generate-sbom :
443+ name : Generate release SBOM
444+ needs : [test]
445+ if : ${{ !cancelled() && (needs.test.result == 'success' || needs.test.result == 'skipped') }}
446+ uses : ./.github/workflows/sbom.yml
447+
442448 # =============================================================================
443449 # Step 3: Merge all artifacts into single dist
444450 # =============================================================================
445451 merge-artifacts :
446452 name : Merge artifacts
447- needs : [build-linux-x86_64, build-linux-aarch64, build-macos-x86_64, build-macos-arm64, build-windows-amd64, build-windows-arm64]
448- if : ${{ !cancelled() && needs.build-linux-x86_64.result == 'success' }}
453+ needs : [build-linux-x86_64, build-linux-aarch64, build-macos-x86_64, build-macos-arm64, build-windows-amd64, build-windows-arm64, generate-sbom ]
454+ if : ${{ !cancelled() && needs.build-linux-x86_64.result == 'success' && needs.generate-sbom.result == 'success' }}
449455 runs-on : ubuntu-24.04
450456 defaults :
451457 run :
@@ -469,6 +475,15 @@ jobs:
469475 name : sdist
470476 path : artifacts/sdist/
471477
478+ - name : Download SBOM artifacts
479+ uses : actions/download-artifact@v8.0.1
480+ with :
481+ name : sbom
482+ path : artifacts/sbom/
483+
484+ - name : Ensure output directories exist
485+ run : mkdir -p dist
486+
472487 - name : Consolidate artifacts and fail on duplicate wheel names
473488 shell : bash
474489 run : |
@@ -522,6 +537,34 @@ jobs:
522537 print(f"Skipped {skipped_identical} identical duplicate wheel artifact(s)")
523538 PY
524539
540+ - name : Add distributed artifacts to merged SBOM
541+ shell : bash
542+ run : |
543+ test -f artifacts/sbom/sbom.cdx.json
544+ docker run --rm \
545+ -v "$PWD:/work" \
546+ -w /work \
547+ cyclonedx/cyclonedx-cli:latest \
548+ add files \
549+ --input-file artifacts/sbom/sbom.cdx.json \
550+ --input-format json \
551+ --output-format json \
552+ --output-file dist/sbom.cdx.json \
553+ --base-path dist \
554+ --include "**/*.whl" "**/*.tar.gz"
555+
556+ - name : Validate release SBOM
557+ shell : bash
558+ run : |
559+ docker run --rm \
560+ -v "$PWD:/work" \
561+ -w /work \
562+ cyclonedx/cyclonedx-cli:latest \
563+ validate \
564+ --input-file dist/sbom.cdx.json \
565+ --input-format json \
566+ --fail-on-errors
567+
525568 - name : Validate wheel archives for duplicate ZIP members
526569 shell : bash
527570 run : |
@@ -834,6 +877,12 @@ jobs:
834877 contents : write
835878
836879 steps :
880+ - name : Download dist artifacts
881+ uses : actions/download-artifact@v8.0.1
882+ with :
883+ name : dist
884+ path : dist/
885+
837886 - name : Create draft release
838887 uses : softprops/action-gh-release@v2
839888 with :
@@ -866,8 +915,14 @@ jobs:
866915
867916 - Python 3.10, 3.11, 3.12, 3.13, 3.14
868917
918+ ### SBOM
919+
920+ - CycloneDX SBOM is included in this release as `sbom.cdx.json`.
921+
869922 ---
870923 *This is a draft release. Wheels are published to PyPI separately.*
924+ files : |
925+ dist/*
871926
872927 # =============================================================================
873928 # Step 6: Publish to PyPI - Trusted Publisher (workflow_dispatch only)
0 commit comments