From 58e9f719576a28dfd63d1d6b07f83e172b82cc6a Mon Sep 17 00:00:00 2001 From: Eduard Kovalets Date: Wed, 17 Dec 2025 08:10:22 +0000 Subject: [PATCH 1/4] PHPLIB-1688: Updating Release pipeline with SBOM generation steps --- .github/actions/update-sbom/action.yml | 31 +++++++++ .github/workflows/release.yml | 87 ++++++++++++++++++++++++-- 2 files changed, 112 insertions(+), 6 deletions(-) create mode 100644 .github/actions/update-sbom/action.yml diff --git a/.github/actions/update-sbom/action.yml b/.github/actions/update-sbom/action.yml new file mode 100644 index 000000000..7f439e438 --- /dev/null +++ b/.github/actions/update-sbom/action.yml @@ -0,0 +1,31 @@ +name: Generate SBOM +description: Generates CycloneDX SBOM using CycloneDX PHP Composer plugin +inputs: + output-file: + description: "Output filename for the SBOM" + required: false + default: "sbom.json" +runs: + using: composite + steps: + - name: Allow CycloneDX plugin + shell: bash + run: composer config allow-plugins.cyclonedx/cyclonedx-php-composer true + - name: Install CycloneDX plugin + shell: bash + run: composer require --dev cyclonedx/cyclonedx-php-composer --ignore-platform-reqs + - name: Generate SBOM + shell: bash + working-directory: ${{ inputs.working-directory }} + run: | + echo "Generating SBOM for 'php' project..." + composer CycloneDX:make-sbom --output-file=${{ inputs.output-file }} --output-format=json --spec-version=1.5 + - name: Validate SBOM presence + shell: bash + run: | + if [ ! -f "${{ inputs.output-file }}" ]; then + echo "Error: SBOM file not found" + exit 1 + fi + + echo "SBOM file validated: ${{ inputs.output-file }}" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 37bb2ed3f..a8ebde73c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,6 +14,9 @@ on: type: "string" env: + PHP_VERSION: "8.2" + DRIVER_VERSION: "mongodb/mongo-php-driver@${{ inputs.version }}" + SBOM_FILE: "sbom.json" default-release-message: | The PHP team is happy to announce that version {0} of the MongoDB PHP library is now available. @@ -48,12 +51,6 @@ jobs: - name: "Create release output" run: echo '🎬 Release process for version ${{ inputs.version }} started by @${{ github.triggering_actor }}' >> $GITHUB_STEP_SUMMARY - - name: "Generate token and checkout repository" - uses: mongodb-labs/drivers-github-tools/secure-checkout@v3 - with: - app_id: ${{ vars.APP_ID }} - private_key: ${{ secrets.APP_PRIVATE_KEY }} - - name: "Store version numbers in env variables" run: | echo RELEASE_VERSION=${{ inputs.version }} >> $GITHUB_ENV @@ -93,6 +90,84 @@ jobs: git checkout -b ${RELEASE_BRANCH} git push origin ${RELEASE_BRANCH} + # + # Preliminary checks done - generate SBOM before tagging + # + - name: Checkout repository (Base Branch) + uses: actions/checkout@v5 + with: + ref: ${{ github.event.pull_request.base.ref || github.ref }} + token: ${{ secrets.GITHUB_TOKEN }} + - name: "Setup PHP environment" + id: setup-php + uses: ./.github/actions/setup + with: + php-version: ${{ env.PHP_VERSION }} + driver-version: ${{ env.DRIVER_VERSION }} + working-directory: '.' + continue-on-error: true + + - name: "Generate/Update composer.lock" + id: composer-lock + run: | + echo "Resolving dependencies and generating composer.lock..." + composer update --no-install --ignore-platform-reqs + echo "composer.lock generated with resolved versions" + continue-on-error: true + + - name: "Generate SBOM" + id: generate-sbom + if: steps.composer-lock.outcome == 'success' + uses: ./.github/actions/update-sbom + with: + php-version: ${{ env.PHP_VERSION }} + working-directory: '.' + output-file: ${{ env.SBOM_FILE }} + output-format: 'json' + continue-on-error: true + + - name: "Check for SBOM changes" + id: sbom_status + if: steps.generate-sbom.outcome == 'success' + run: | + JQ_NORMALIZER='del(.serialNumber) | del(.metadata.timestamp) | walk(if type == "object" and .timestamp then .timestamp = "TIMESTAMP_NORMALIZED" else . end)' + + if ! git show HEAD:${{ env.SBOM_FILE }} > /dev/null 2>&1; then + echo "HAS_CHANGES=true" >> $GITHUB_OUTPUT + echo "SBOM file is new" + exit 0 + fi + + if diff -q \ + <(git show HEAD:${{ env.SBOM_FILE }} | jq -r "$JQ_NORMALIZER") \ + <(cat ${{ env.SBOM_FILE }} | jq -r "$JQ_NORMALIZER"); then + echo "HAS_CHANGES=false" >> $GITHUB_OUTPUT + echo "No changes detected in SBOM" + else + echo "HAS_CHANGES=true" >> $GITHUB_OUTPUT + echo "Changes detected in SBOM" + fi + continue-on-error: true + + - name: "Commit SBOM changes" + if: steps.sbom_status.outputs.HAS_CHANGES == 'true' + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add ${{ env.SBOM_FILE }} + git commit -m "chore: Update SBOM for release ${{ inputs.version }}" + git push + echo "📦 SBOM updated and committed" >> $GITHUB_STEP_SUMMARY + continue-on-error: true + + - name: "Report SBOM status" + run: | + if [[ "${{ steps.generate-sbom.outcome }}" == "success" ]]; then + echo "✅ SBOM generation completed successfully" >> $GITHUB_STEP_SUMMARY + else + echo "⚠️ SBOM generation skipped or failed - continuing with release" >> $GITHUB_STEP_SUMMARY + fi + # # Preliminary checks done - commence the release process # From 878cdf14b8061e632f54fb9990b6b41ff5991fc4 Mon Sep 17 00:00:00 2001 From: Eduard Kovalets Date: Fri, 19 Dec 2025 00:13:38 +0000 Subject: [PATCH 2/4] PHPLIB-1688: Addressing PR comments --- .github/actions/update-sbom/action.yml | 2 +- .github/workflows/release.yml | 46 ++++++++++++++------------ 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/.github/actions/update-sbom/action.yml b/.github/actions/update-sbom/action.yml index 7f439e438..59326922b 100644 --- a/.github/actions/update-sbom/action.yml +++ b/.github/actions/update-sbom/action.yml @@ -13,7 +13,7 @@ runs: run: composer config allow-plugins.cyclonedx/cyclonedx-php-composer true - name: Install CycloneDX plugin shell: bash - run: composer require --dev cyclonedx/cyclonedx-php-composer --ignore-platform-reqs + run: composer require --dev cyclonedx/cyclonedx-php-composer - name: Generate SBOM shell: bash working-directory: ${{ inputs.working-directory }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a8ebde73c..809d26bbe 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -91,13 +91,24 @@ jobs: git push origin ${RELEASE_BRANCH} # - # Preliminary checks done - generate SBOM before tagging + # Preliminary checks done - setting up the environment # - - name: Checkout repository (Base Branch) - uses: actions/checkout@v5 + + - name: "Generate token and checkout repository" + uses: mongodb-labs/drivers-github-tools/secure-checkout@v3 with: - ref: ${{ github.event.pull_request.base.ref || github.ref }} - token: ${{ secrets.GITHUB_TOKEN }} + app_id: ${{ vars.APP_ID }} + private_key: ${{ secrets.APP_PRIVATE_KEY }} + submodules: true + fetch-depth: 0 + + - name: "Set up drivers-github-tools" + uses: mongodb-labs/drivers-github-tools/setup@v3 + with: + aws_role_arn: ${{ secrets.AWS_ROLE_ARN }} + aws_region_name: ${{ vars.AWS_REGION_NAME }} + aws_secret_id: ${{ secrets.AWS_SECRET_ID }} + - name: "Setup PHP environment" id: setup-php uses: ./.github/actions/setup @@ -107,11 +118,15 @@ jobs: working-directory: '.' continue-on-error: true + # + # Preliminary checks and setup done - generate SBOM before tagging + # + - name: "Generate/Update composer.lock" id: composer-lock run: | echo "Resolving dependencies and generating composer.lock..." - composer update --no-install --ignore-platform-reqs + composer update --no-install echo "composer.lock generated with resolved versions" continue-on-error: true @@ -120,10 +135,7 @@ jobs: if: steps.composer-lock.outcome == 'success' uses: ./.github/actions/update-sbom with: - php-version: ${{ env.PHP_VERSION }} - working-directory: '.' output-file: ${{ env.SBOM_FILE }} - output-format: 'json' continue-on-error: true - name: "Check for SBOM changes" @@ -152,33 +164,23 @@ jobs: - name: "Commit SBOM changes" if: steps.sbom_status.outputs.HAS_CHANGES == 'true' run: | - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" git add ${{ env.SBOM_FILE }} git commit -m "chore: Update SBOM for release ${{ inputs.version }}" git push - echo "📦 SBOM updated and committed" >> $GITHUB_STEP_SUMMARY + echo "SBOM updated and committed" >> $GITHUB_STEP_SUMMARY continue-on-error: true - name: "Report SBOM status" run: | if [[ "${{ steps.generate-sbom.outcome }}" == "success" ]]; then - echo "✅ SBOM generation completed successfully" >> $GITHUB_STEP_SUMMARY + echo "SBOM generation completed successfully" >> $GITHUB_STEP_SUMMARY else - echo "⚠️ SBOM generation skipped or failed - continuing with release" >> $GITHUB_STEP_SUMMARY + echo "SBOM generation skipped or failed - continuing with release" >> $GITHUB_STEP_SUMMARY fi # # Preliminary checks done - commence the release process # - - - name: "Set up drivers-github-tools" - uses: mongodb-labs/drivers-github-tools/setup@v3 - with: - aws_role_arn: ${{ secrets.AWS_ROLE_ARN }} - aws_region_name: ${{ vars.AWS_REGION_NAME }} - aws_secret_id: ${{ secrets.AWS_SECRET_ID }} - - name: "Prepare release message" run: | cat > release-message <<'EOL' From 335cba64b8f576c650922db87d3c632b63076f46 Mon Sep 17 00:00:00 2001 From: Eduard Kovalets Date: Fri, 19 Dec 2025 00:40:29 +0000 Subject: [PATCH 3/4] PHPLIB-1688: Surrounding the auto commit step with a next branch merge strategy suppression --- .github/workflows/release.yml | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 809d26bbe..9d2310c10 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -93,7 +93,7 @@ jobs: # # Preliminary checks done - setting up the environment # - + - name: "Generate token and checkout repository" uses: mongodb-labs/drivers-github-tools/secure-checkout@v3 with: @@ -161,6 +161,15 @@ jobs: fi continue-on-error: true + - name: "Determine branch to merge up to" + id: get-next-branch + uses: alcaeus/automatic-merge-up-action/get-next-branch@1.0.1 + with: + ref: ${{ github.ref_name }} + branchNamePattern: 'v.' + devBranchNamePattern: 'v.x' + ignoredBranches: ${{ vars.IGNORED_MERGE_UP_BRANCHES }} + - name: "Commit SBOM changes" if: steps.sbom_status.outputs.HAS_CHANGES == 'true' run: | @@ -170,6 +179,16 @@ jobs: echo "SBOM updated and committed" >> $GITHUB_STEP_SUMMARY continue-on-error: true + - name: "Manually merge up changes" + if: steps.sbom_status.outputs.HAS_CHANGES == 'true' && steps.get-next-branch.outputs.hasNextBranch == 'true' + run: | + git checkout ${NEXT_BRANCH} + git merge --strategy=ours ${RELEASE_BRANCH} + git push origin ${NEXT_BRANCH} + git checkout ${RELEASE_BRANCH} + env: + NEXT_BRANCH: ${{ steps.get-next-branch.outputs.branchName }} + - name: "Report SBOM status" run: | if [[ "${{ steps.generate-sbom.outcome }}" == "success" ]]; then From 59a89b02f384f8376cbb329f7b36d9e63b5cc0d9 Mon Sep 17 00:00:00 2001 From: Eduard Kovalets Date: Fri, 19 Dec 2025 02:33:25 +0000 Subject: [PATCH 4/4] PHPLIB-1688: Adding extra SBOM validation step --- .github/actions/update-sbom/action.yml | 76 ++++++++++++++++++++++++-- .github/workflows/release.yml | 44 ++++----------- 2 files changed, 83 insertions(+), 37 deletions(-) diff --git a/.github/actions/update-sbom/action.yml b/.github/actions/update-sbom/action.yml index 59326922b..9821701f4 100644 --- a/.github/actions/update-sbom/action.yml +++ b/.github/actions/update-sbom/action.yml @@ -5,27 +5,95 @@ inputs: description: "Output filename for the SBOM" required: false default: "sbom.json" +outputs: + HAS_CHANGES: + description: "Whether the SBOM has meaningful changes compared to the existing version" + value: ${{ steps.check_changes.outputs.HAS_CHANGES }} runs: using: composite steps: - name: Allow CycloneDX plugin shell: bash run: composer config allow-plugins.cyclonedx/cyclonedx-php-composer true + - name: Install CycloneDX plugin shell: bash run: composer require --dev cyclonedx/cyclonedx-php-composer + - name: Generate SBOM + id: generate-sbom shell: bash - working-directory: ${{ inputs.working-directory }} run: | echo "Generating SBOM for 'php' project..." - composer CycloneDX:make-sbom --output-file=${{ inputs.output-file }} --output-format=json --spec-version=1.5 + composer CycloneDX:make-sbom --output-file=sbom-new.json --output-format=json --spec-version=1.5 + - name: Validate SBOM presence shell: bash run: | - if [ ! -f "${{ inputs.output-file }}" ]; then + if [ ! -f "sbom-new.json" ]; then echo "Error: SBOM file not found" exit 1 fi - + echo "SBOM file generated: sbom-new.json" + + - name: Download CycloneDX CLI + shell: bash + run: | + curl -L -s -o /tmp/cyclonedx "https://github.com/CycloneDX/cyclonedx-cli/releases/download/v0.29.1/cyclonedx-linux-x64" + chmod +x /tmp/cyclonedx + + - name: Validate SBOM + shell: bash + run: /tmp/cyclonedx validate --input-file sbom-new.json --fail-on-errors + + - name: Check for SBOM changes + id: check_changes + if: steps.generate-sbom.outcome == 'success' + shell: bash + env: + SBOM_FILE: ${{ inputs.output-file }} + run: | + JQ_NORMALIZER='del(.serialNumber) | del(.metadata.timestamp) | walk(if type == "object" and .timestamp then .timestamp = "TIMESTAMP_NORMALIZED" else . end)' + + if [ -f "$SBOM_FILE" ]; then + echo "Comparing new SBOM with existing $SBOM_FILE..." + + # First try cyclonedx diff for component-level comparison + DIFF_OUTPUT=$(/tmp/cyclonedx diff "$SBOM_FILE" sbom-new.json --component-versions 2>/dev/null || true) + + if echo "$DIFF_OUTPUT" | grep -q "^None$"; then + echo "No component changes detected via cyclonedx diff" + + # Double-check with jq normalization (excludes metadata like timestamps) + if diff -q \ + <(cat "$SBOM_FILE" | jq -r "$JQ_NORMALIZER") \ + <(cat sbom-new.json | jq -r "$JQ_NORMALIZER") > /dev/null 2>&1; then + echo "HAS_CHANGES=false" >> $GITHUB_OUTPUT + echo "No meaningful changes detected in SBOM" + rm sbom-new.json + else + echo "HAS_CHANGES=true" >> $GITHUB_OUTPUT + echo "Changes detected in SBOM (non-component changes)" + mv sbom-new.json "$SBOM_FILE" + fi + else + echo "Component changes detected:" + echo "$DIFF_OUTPUT" + echo "HAS_CHANGES=true" >> $GITHUB_OUTPUT + mv sbom-new.json "$SBOM_FILE" + fi + else + echo "No existing $SBOM_FILE found, creating initial version" + echo "HAS_CHANGES=true" >> $GITHUB_OUTPUT + mv sbom-new.json "$SBOM_FILE" + fi + continue-on-error: true + + - name: Final SBOM validation + shell: bash + run: | + if [ ! -f "${{ inputs.output-file }}" ]; then + echo "Error: Final SBOM file not found at ${{ inputs.output-file }}" + exit 1 + fi echo "SBOM file validated: ${{ inputs.output-file }}" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9d2310c10..244756616 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -138,30 +138,8 @@ jobs: output-file: ${{ env.SBOM_FILE }} continue-on-error: true - - name: "Check for SBOM changes" - id: sbom_status - if: steps.generate-sbom.outcome == 'success' - run: | - JQ_NORMALIZER='del(.serialNumber) | del(.metadata.timestamp) | walk(if type == "object" and .timestamp then .timestamp = "TIMESTAMP_NORMALIZED" else . end)' - - if ! git show HEAD:${{ env.SBOM_FILE }} > /dev/null 2>&1; then - echo "HAS_CHANGES=true" >> $GITHUB_OUTPUT - echo "SBOM file is new" - exit 0 - fi - - if diff -q \ - <(git show HEAD:${{ env.SBOM_FILE }} | jq -r "$JQ_NORMALIZER") \ - <(cat ${{ env.SBOM_FILE }} | jq -r "$JQ_NORMALIZER"); then - echo "HAS_CHANGES=false" >> $GITHUB_OUTPUT - echo "No changes detected in SBOM" - else - echo "HAS_CHANGES=true" >> $GITHUB_OUTPUT - echo "Changes detected in SBOM" - fi - continue-on-error: true - - name: "Determine branch to merge up to" + if: steps.generate-sbom.outputs.HAS_CHANGES == 'true' id: get-next-branch uses: alcaeus/automatic-merge-up-action/get-next-branch@1.0.1 with: @@ -170,17 +148,8 @@ jobs: devBranchNamePattern: 'v.x' ignoredBranches: ${{ vars.IGNORED_MERGE_UP_BRANCHES }} - - name: "Commit SBOM changes" - if: steps.sbom_status.outputs.HAS_CHANGES == 'true' - run: | - git add ${{ env.SBOM_FILE }} - git commit -m "chore: Update SBOM for release ${{ inputs.version }}" - git push - echo "SBOM updated and committed" >> $GITHUB_STEP_SUMMARY - continue-on-error: true - - name: "Manually merge up changes" - if: steps.sbom_status.outputs.HAS_CHANGES == 'true' && steps.get-next-branch.outputs.hasNextBranch == 'true' + if: steps.generate-sbom.outputs.HAS_CHANGES == 'true' && steps.get-next-branch.outputs.hasNextBranch == 'true' run: | git checkout ${NEXT_BRANCH} git merge --strategy=ours ${RELEASE_BRANCH} @@ -189,6 +158,15 @@ jobs: env: NEXT_BRANCH: ${{ steps.get-next-branch.outputs.branchName }} + - name: "Commit SBOM changes" + if: steps.generate-sbom.outputs.HAS_CHANGES == 'true' + run: | + git add ${{ env.SBOM_FILE }} + git commit -m "chore: Update SBOM for release ${{ inputs.version }}" + git push + echo "SBOM updated and committed" >> $GITHUB_STEP_SUMMARY + continue-on-error: true + - name: "Report SBOM status" run: | if [[ "${{ steps.generate-sbom.outcome }}" == "success" ]]; then