Commit 947b9a3abac for nodejs

commit 947b9a3abacd931ec6a4bfed566508e4f14d16cb
Author: Marco Ippolito <marcoippolito54@gmail.com>
Date:   Sun Oct 18 14:30:44 2026 +0200

    tools: skip dependency update if PR has non-bot commits

    When the tools-deps-update workflow finds a new version of a
    dependency, peter-evans/create-pull-request force-pushes the
    Action's branch, discarding any commits a collaborator pushed to
    the existing PR to fix the update.

    On scheduled runs and on manual runs for all dependencies, check
    whether the open PR for the Action's branch contains commits not
    authored by the bot before running the updater, and if so skip the
    update for that dependency instead of overwriting them. A manual run
    for a single dependency still updates the PR, as it is assumed to be
    an intentional override.

    Signed-off-by: Marco Ippolito <marcoippolito54@gmail.com>
    Assisted-by: claude:opus-5.5
    PR-URL: https://github.com/nodejs/node/pull/66503
    Reviewed-By: James M Snell <jasnell@gmail.com>
    Reviewed-By: Filip Skokan <panva.ip@gmail.com>
    Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>

diff --git a/.github/workflows/tools.yml b/.github/workflows/tools.yml
index 013b445e2f3..ec996d6f5db 100644
--- a/.github/workflows/tools.yml
+++ b/.github/workflows/tools.yml
@@ -54,6 +54,9 @@ permissions:

 jobs:
   tools-deps-update:
+    permissions:
+      contents: read
+      pull-requests: read
     if: github.repository == 'nodejs/node' || github.event_name == 'workflow_dispatch'
     # cannot use ubuntu-slim here because some update scripts require Docker
     runs-on: ubuntu-latest
@@ -318,35 +321,53 @@ jobs:
               cat temp-output
               tail -n1 temp-output | grep "NEW_VERSION=" >> "$GITHUB_ENV" || true
               rm temp-output
+    env:
+      BRANCH_NAME: actions/${{ github.ref_name != 'main' && format('{0}/', github.ref_name) || '' }}tools-update-${{ matrix.id }}
     steps:
+      - name: Check for non-bot commits on the existing pull request
+        id: check-pr
+        # Updating the Action's PR force-pushes its branch, so skip the update
+        # if someone pushed their own commits to it to avoid discarding them.
+        # When 'inputs.id == matrix.id', non-bot commits are being overwritten
+        if: github.event_name == 'schedule' || inputs.id == 'all'
+        env:
+          GH_TOKEN: ${{ github.token }}
+        run: |
+          PR_URL=$(gh pr list --repo "$GITHUB_REPOSITORY" --head "$BRANCH_NAME" --state open \
+            --json url,commits,isCrossRepository \
+            --jq '[.[] | select(.isCrossRepository | not) | select(any(.commits[].authors[]; .email != "github-bot@iojs.org")) | .url] | join(" ")')
+          if [ -n "$PR_URL" ]; then
+            echo "::notice::Skipping update, $PR_URL has commits not authored by the bot"
+            echo "hasNonBotCommits=true" >> "$GITHUB_OUTPUT"
+          fi
       - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1  # v7.0.1
-        if: github.event_name == 'schedule' || inputs.id == 'all' || inputs.id == matrix.id
+        if: (github.event_name == 'schedule' || inputs.id == 'all' || inputs.id == matrix.id) && steps.check-pr.outputs.hasNonBotCommits != 'true'
         with:
           persist-credentials: false
       - name: Set up Python ${{ env.PYTHON_VERSION }}
-        if: matrix.requires-python && (github.event_name == 'schedule' || inputs.id == 'all' || inputs.id == matrix.id)
+        if: matrix.requires-python && (github.event_name == 'schedule' || inputs.id == 'all' || inputs.id == matrix.id) && steps.check-pr.outputs.hasNonBotCommits != 'true'
         uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97  # v7.0.0
         with:
           python-version: ${{ env.PYTHON_VERSION }}
           allow-prereleases: true
       - name: Set up Nix
-        if: matrix.id == 'nixpkgs-unstable' && (github.event_name == 'schedule' || inputs.id == 'all' || inputs.id == matrix.id)
+        if: matrix.id == 'nixpkgs-unstable' && (github.event_name == 'schedule' || inputs.id == 'all' || inputs.id == matrix.id) && steps.check-pr.outputs.hasNonBotCommits != 'true'
         uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24  # v31.11.1
       - run: ${{ matrix.run }}
-        if: github.event_name == 'schedule' || inputs.id == 'all' || inputs.id == matrix.id
+        if: (github.event_name == 'schedule' || inputs.id == 'all' || inputs.id == matrix.id) && steps.check-pr.outputs.hasNonBotCommits != 'true'
         env:
           GITHUB_TOKEN: ${{ secrets.GH_USER_TOKEN }}
       - name: Generate commit message if not set
-        if: env.COMMIT_MSG == '' && (github.event_name == 'schedule' || inputs.id == 'all' || inputs.id == matrix.id)
+        if: env.COMMIT_MSG == '' && (github.event_name == 'schedule' || inputs.id == 'all' || inputs.id == matrix.id) && steps.check-pr.outputs.hasNonBotCommits != 'true'
         run: |
           echo "COMMIT_MSG=${{ matrix.subsystem }}: update ${{ matrix.id }} to $NEW_VERSION" >> "$GITHUB_ENV"
       - uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1  # v8.1.1
-        if: github.event_name == 'schedule' || inputs.id == 'all' || inputs.id == matrix.id
+        if: (github.event_name == 'schedule' || inputs.id == 'all' || inputs.id == matrix.id) && steps.check-pr.outputs.hasNonBotCommits != 'true'
         # Creates a PR or update the Action's existing PR, or
         # no-op if the base branch is already up-to-date.
         with:
           token: ${{ secrets.GH_USER_TOKEN }}
-          branch: actions/${{ github.ref_name != 'main' && format('{0}/', github.ref_name) || '' }}tools-update-${{ matrix.id }}  # Custom branch *just* for this Action.
+          branch: ${{ env.BRANCH_NAME }}  # Custom branch *just* for this Action.
           delete-branch: true
           commit-message: ${{ env.COMMIT_MSG }}
           labels: ${{ matrix.label }}