mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-21 16:18:55 +00:00
git diff on a lockfile is unreadable: npm reorders entries, rewrites
integrity hashes, and moves packages between nesting levels, so a
one-line package.json bump produces a thousand-line textual diff.
scripts/ci/lockfile_diff.py instead parses the `packages` map out of
both versions of every tracked package-lock.json (via `git show`),
reduces each to {install path: version}, and set-diffs the maps —
reorder/hash churn vanishes, leaving only actual version movement
(added / removed / updated, with nested dedup copies tracked
separately).
The lockfile-diff workflow posts the result as a Markdown table in a
PR comment gated behind a hidden marker: subsequent pushes PATCH the
existing comment instead of stacking new ones, and a push that reverts
all lockfile changes updates the comment to say so. Advisory only —
never fails on findings; fork PRs (read-only token) degrade to a
warning.
Wired through the ci.yml orchestrator with a new npm_lock lane in
classify_changes.py (fails open on .github/ changes per the existing
contract).
98 lines
4.1 KiB
YAML
98 lines
4.1 KiB
YAML
name: Lockfile diff
|
|
|
|
# Advisory PR comment showing the *semantic* diff of package-lock.json
|
|
# changes — which packages were added/removed/updated and their versions.
|
|
# The raw textual diff of a lockfile is unreadable (npm reorders entries
|
|
# and rewrites integrity hashes), so scripts/ci/lockfile_diff.py parses
|
|
# the ``packages`` map at the merge base and at HEAD and set-diffs the
|
|
# {install path: version} maps instead.
|
|
#
|
|
# The comment is upserted: the script embeds a hidden HTML marker and the
|
|
# workflow PATCHes the existing comment when one is found, so a PR gets
|
|
# exactly one lockfile-diff comment that tracks the latest push instead
|
|
# of a stack of stale ones. When a later push reverts all lockfile
|
|
# changes, the comment is updated to say so (deleting it would be more
|
|
# surprising than telling the reviewer it's resolved).
|
|
#
|
|
# Never blocking — this is review signal, not enforcement. Exit is 0 even
|
|
# when commenting fails (fork PRs get a read-only GITHUB_TOKEN).
|
|
|
|
on:
|
|
workflow_call:
|
|
|
|
permissions:
|
|
contents: read
|
|
pull-requests: write # post/update the diff comment
|
|
|
|
concurrency:
|
|
group: lockfile-diff-${{ github.event.pull_request.number || github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
diff:
|
|
name: package-lock.json semantic diff
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 5
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
with:
|
|
fetch-depth: 0 # need history for the merge base
|
|
|
|
- name: Generate semantic lockfile diff
|
|
id: diff
|
|
run: |
|
|
set -euo pipefail
|
|
# Three-dot semantics by hand: diff from the merge base with the
|
|
# target branch to the PR head, so changes that landed on main
|
|
# after the branch point don't show up as this PR's doing.
|
|
BASE_SHA=$(git merge-base "origin/${{ github.base_ref }}" HEAD)
|
|
echo "Merge base: ${BASE_SHA}"
|
|
python3 scripts/ci/lockfile_diff.py \
|
|
--base "$BASE_SHA" \
|
|
--head HEAD \
|
|
--output /tmp/lockfile-diff.md
|
|
if [ -s /tmp/lockfile-diff.md ]; then
|
|
echo "changed=true" >> "$GITHUB_OUTPUT"
|
|
cat /tmp/lockfile-diff.md >> "$GITHUB_STEP_SUMMARY"
|
|
else
|
|
echo "changed=false" >> "$GITHUB_OUTPUT"
|
|
fi
|
|
|
|
- name: Post or update PR comment
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
REPO: ${{ github.repository }}
|
|
PR: ${{ github.event.pull_request.number }}
|
|
CHANGED: ${{ steps.diff.outputs.changed }}
|
|
run: |
|
|
set -euo pipefail
|
|
MARKER='<!-- hermes-lockfile-diff -->'
|
|
|
|
# Find our previous comment (paginated — busy PRs exceed one page).
|
|
EXISTING=$(gh api --paginate "repos/${REPO}/issues/${PR}/comments" \
|
|
--jq ".[] | select(.body | startswith(\"$MARKER\")) | .id" \
|
|
| head -1 || true)
|
|
|
|
if [ "$CHANGED" != "true" ]; then
|
|
if [ -n "$EXISTING" ]; then
|
|
# A previous push changed the lockfile but the latest one
|
|
# doesn't — update the comment rather than leave stale info.
|
|
printf '%s\n✅ package-lock.json changes from an earlier push have been reverted — locked versions now match the target branch.\n' "$MARKER" > /tmp/lockfile-diff.md
|
|
else
|
|
echo "No lockfile changes and no existing comment — nothing to do."
|
|
exit 0
|
|
fi
|
|
fi
|
|
|
|
if [ -n "$EXISTING" ]; then
|
|
echo "Updating existing comment ${EXISTING}"
|
|
gh api --method PATCH "repos/${REPO}/issues/comments/${EXISTING}" \
|
|
-F body=@/tmp/lockfile-diff.md > /dev/null \
|
|
|| echo "::warning::Could not update PR comment (expected for fork PRs — GITHUB_TOKEN is read-only)"
|
|
else
|
|
echo "Creating new comment"
|
|
gh api "repos/${REPO}/issues/${PR}/comments" \
|
|
-F body=@/tmp/lockfile-diff.md > /dev/null \
|
|
|| echo "::warning::Could not post PR comment (expected for fork PRs — GITHUB_TOKEN is read-only)"
|
|
fi
|