Commit bf4534b4441 for nodejs

commit bf4534b44411b1f3210ddf8a8d27e244dcdfe78c
Author: Antoine du Hamel <duhamelantoine1995@gmail.com>
Date:   Tue Nov 10 22:45:07 2026 +0100

    tools: aggregate Nix changes in a table

    On large updates, the raw diff is not really human parsable. It can
    sometimes exceed the maximum length for comments, resulting in no diff
    being shown.
    To make reviews easier / more likely to spot something wrong, it now
    outputs a table, and report failures in PR checks.

    Signed-off-by: Antoine du Hamel <duhamelantoine1995@gmail.com>
    PR-URL: https://github.com/nodejs/node/pull/67348
    Reviewed-By: Filip Skokan <panva.ip@gmail.com>
    Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day>

diff --git a/.github/workflows/nix-changes-comment.yml b/.github/workflows/nix-changes-comment.yml
index 371ed669d1f..fc642dee29b 100644
--- a/.github/workflows/nix-changes-comment.yml
+++ b/.github/workflows/nix-changes-comment.yml
@@ -16,6 +16,7 @@ jobs:
     runs-on: ubuntu-slim
     permissions:
       pull-requests: write
+      statuses: write
     steps:
       - name: Download artefacts
         uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333  # v8.0.2
@@ -41,7 +42,7 @@ jobs:
           set -x
           > requisites-before.list
           > requisites-after.list
-          TABLE=$(
+          {
             echo '| Platform | Number of requisites | Proportion of new derivations |'
             echo '| :-: | -: | -: |'
             TOTAL_BEFORE=0
@@ -49,13 +50,17 @@ jobs:
             TOTAL_CHANGED=0
             QUOTE='`'
             for PLATFORM in x86_64-linux aarch64-linux x86_64-darwin aarch64-darwin; do
+              if [ ! -f "requisites-${PLATFORM}-before.list" ] || [ ! -f "requisites-${PLATFORM}-after.list" ]; then
+                echo "| ${QUOTE}${PLATFORM}${QUOTE} | _missing data_ | _missing data_ |"
+                continue
+              fi
               BEFORE=$(wc -l < "requisites-${PLATFORM}-before.list")
               TOTAL_BEFORE=$(($TOTAL_BEFORE + $BEFORE))
               AFTER=$(wc -l < "requisites-${PLATFORM}-after.list")
               TOTAL_AFTER=$(($TOTAL_AFTER + $AFTER))
               DIFF="_no changes_"
               [ "$BEFORE" = "$AFTER" ] || DIFF="**$([ "$AFTER" -lt "$BEFORE" ] || echo '+')$(($AFTER - $BEFORE))**"
-              CHANGED=$(git diff --no-index "requisites-${PLATFORM}-before.list" "requisites-${PLATFORM}-after.list" | grep -c '^+[^+]')
+              CHANGED=$(git diff --no-index "requisites-${PLATFORM}-before.list" "requisites-${PLATFORM}-after.list" | grep -c '^+[^+]' || true)
               TOTAL_CHANGED=$(($TOTAL_CHANGED + $CHANGED))
               PERCENT=100
               [ "$CHANGED" = "$AFTER" ] || {
@@ -63,8 +68,24 @@ jobs:
                 PERCENT="$((PERMILLE / 10)).$((PERMILLE % 10))"
               }
               echo "| ${QUOTE}${PLATFORM}${QUOTE} | $BEFORE -> $AFTER => $DIFF | $CHANGED / $AFTER = $PERCENT% |"
-              awk '{ print $0 " ('"$PLATFORM"')" }' "requisites-${PLATFORM}-before.list" >> requisites-before.list
-              awk '{ print $0 " ('"$PLATFORM"')" }' "requisites-${PLATFORM}-after.list" >> requisites-after.list
+              for STATE in before after; do
+                # Split store paths into package name, output (or source archive extension), and version.
+                awk -v OFS='\t' -v PLATFORM="$PLATFORM" '{
+                  name = substr($0, 45)
+                  pname = name
+                  output = "out"
+                  version = "_unversioned_"
+                  if (match(name, /-[^A-Za-z]/)) {
+                    pname = substr(name, 1, RSTART - 1)
+                    version = substr(name, RSTART + 1)
+                    if (match(version, /-[a-z]+$/) || match(version, /\.(tar(\.[a-z0-9]+)?|tgz|tbz2?|txz|zip|patch|diff)$/)) {
+                      output = substr(version, RSTART + 1)
+                      version = substr(version, 1, RSTART - 1)
+                    }
+                  }
+                  print pname, output, version, PLATFORM, $0
+                }' "requisites-${PLATFORM}-${STATE}.list" >> "requisites-${STATE}.list"
+              done
             done

             DIFF="_no changes_"
@@ -75,24 +96,87 @@ jobs:
               PERCENT="$((PERMILLE / 10)).$((PERMILLE % 10))"
             }
             echo "| **Total** | $TOTAL_BEFORE -> $TOTAL_AFTER => $DIFF | $TOTAL_CHANGED / $TOTAL_AFTER = $PERCENT% |"
-          )
-          sort -k1.45 requisites-before.list > requisites-before.sort
-          sort -k1.45 requisites-after.list > requisites-after.sort
+          } > comment.md
+          sort requisites-before.list > requisites-before.sort
+          sort requisites-after.list > requisites-after.sort
           if git diff -U0 --no-color --no-index requisites-before.sort requisites-after.sort > requisites.diff; then
             echo 'No changes detected.'
           else
-          {
-            echo "$TABLE"
-            echo
-            echo '<details><summary>Changelog</summary>'
-            echo
-            echo '```diff'
-            tail -n +5 requisites.diff
-            echo '```'
-            echo
-            echo '</details>'
-          } | gh pr comment -R "$GITHUB_REPOSITORY" "$PR_NUMBER" -F -
+            awk -F '\t' '
+              function insert(list, v,    n, a, i, out, done) {
+                n = split(list, a, " ")
+                for (i = 1; i <= n; i++) if (a[i] == v) return list
+                out = ""; done = 0
+                for (i = 1; i <= n; i++) {
+                  if (!done && v < a[i]) { out = out " " v; done = 1 }
+                  out = out " " a[i]
+                }
+                if (!done) out = out " " v
+                return substr(out, 2)
+              }
+              function join(list, pre, post,    s) {
+                s = pre list post
+                gsub(/ /, post ", " pre, s)
+                return s
+              }
+              /^[-+]/ && NF == 5 {
+                key = substr($1, 2) SUBSEP $2 SUBSEP $4
+                if (substr($1, 1, 1) == "-") removed[key] = insert(removed[key], $3)
+                else added[key] = insert(added[key], $3)
+              }
+              END {
+                for (key in removed) if (!(key in added)) added[key] = ""
+                for (key in added) {
+                  split(key, k, SUBSEP)
+                  r = (key in removed) ? removed[key] : ""
+                  a = added[key]
+                  if (r == a) change = "_no version change_: " join(a)
+                  else if (r == "") change = "_added_: " join(a, "**", "**")
+                  else if (a == "") change = "_removed_: " join(r)
+                  else change = join(r) " -> " join(a, "**", "**")
+                  platforms[k[1] SUBSEP k[2] SUBSEP change] = insert(platforms[k[1] SUBSEP k[2] SUBSEP change], k[3])
+                }
+                for (key in platforms) {
+                  split(key, k, SUBSEP)
+                  row = k[1] SUBSEP k[3] SUBSEP join(platforms[key])
+                  outputs[row] = insert(outputs[row], k[2])
+                }
+                for (row in outputs) {
+                  split(row, k, SUBSEP)
+                  n = split(outputs[row], o, " ")
+                  name = ""
+                  for (i = 1; i <= n; i++) name = name ", `" k[1] (o[i] == "out" ? "" : "." o[i]) "`"
+                  rank = k[2] ~ /^_added_/ ? 0 : k[2] ~ /^_removed_/ ? 2 : k[2] ~ /^_no version change_/ ? 3 : 1
+                  print rank "\t| " substr(name, 3) " | " k[2] " | " k[3] " |"
+                }
+              }
+            ' requisites.diff > versions.tsv
+            {
+              echo
+              echo '<details><summary>Version changes</summary>'
+              echo
+              echo '| Package | Version | Platforms |'
+              echo '| :- | :- | :- |'
+              LC_ALL=C sort -t '`' -k1,1 -k2,2 -k3 versions.tsv | cut -f2-
+              echo
+              echo '</details>'
+            } >> comment.md
+            gh pr comment -R "$GITHUB_REPOSITORY" "$PR_NUMBER" -F comment.md
           fi
         env:
           GH_TOKEN: ${{ github.token }}
           PR_NUMBER: ${{ steps.pr-data.outputs.NUMBER }}
+
+      - name: Report status on the PR
+        if: ${{ always() }}
+        run: |
+          gh api --method POST "repos/${GITHUB_REPOSITORY}/statuses/${HEAD_SHA}" \
+            -f state="$STATE" \
+            -f context="$GITHUB_WORKFLOW" \
+            -f description="$DESCRIPTION" \
+            -f target_url="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}"
+        env:
+          GH_TOKEN: ${{ github.token }}
+          HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
+          STATE: ${{ job.status == 'success' && 'success' || 'failure' }}
+          DESCRIPTION: ${{ job.status == 'success' && 'Nix requisites changes aggregated' || 'Failed to aggregate Nix requisites changes' }}