Files
safishamsiandClaude Opus 4.8 83b3c417be adoption: lead README with the diff + killer demo; release-diff GitHub Action
Repositions and packages the one validated, unique win so people can see it and
reuse it (per the positioning review).

- README: new lede leads with "tell what actually changed, not what just moved" +
  the live `reagraph demo` contrast (59 of 56 nodes "changed" vs 1 real change) +
  the zero-setup `uv tool install reagraph && reagraph demo` + a GIF placeholder
  (record with examples/demo.tape). The feature list stays, below the pitch.
- .github/actions/reagraph-diff/ (composite action): diffs two build graphs with
  the byte-agnostic diff, writes a Markdown summary, and posts/updates a PR comment
  (stable marker, no dupes); outputs the real changed-count. Inputs base/head/by/
  comment/reagraph-version.
- .github/workflows/release-diff.yml: example consumer (copy into your repo) that
  diffs two builds on release; the in-repo default runs green against the committed
  real-REA fixtures, with the live "pull from REA in CI" pattern shown commented.
- docs/GITHUB_ACTION.md: usage, both modes (pre-exported graphs vs live pull),
  inputs/outputs, PR-comment behavior.
- README "## Diff your releases in CI" section links it: "a bot comment on every
  release — the real change, not the thousands of phantom ones."

Validated: the exact `reagraph diff` the action runs exits 0 on the fixtures
(+4 nodes added / 2 rewired on the added-module build); all workflow YAML parses.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-10-19 21:01:10 +01:00

166 lines
6.3 KiB
YAML

name: "REAgraph byte-agnostic diff"
description: >-
Diff two build graphs with REAgraph's structural, byte-agnostic diff and
surface the real delta -- not the thousands of per-build-id phantom changes.
Writes a Markdown summary and (on pull requests) posts/updates a PR comment.
author: "Graphify Labs"
branding:
icon: git-commit
color: green
inputs:
base:
description: "Path to the base build graph (reagraph / REA node-link JSON)."
required: true
head:
description: "Path to the head build graph (reagraph / REA node-link JSON)."
required: true
by:
description: >-
Node-identity regime for matching: 'auto' (detect REA graphs and match by
build-stable signature, else by id), 'id', or 'signature'.
required: false
default: "auto"
comment:
description: >-
When 'true' and the event is a pull_request, post or update a PR comment
with the diff. A stable marker makes re-runs update rather than duplicate.
required: false
default: "true"
reagraph-version:
description: >-
How to install reagraph. Default installs from the public repo (reagraph
is not yet on PyPI). Once published, set this to a pinned spec such as
'reagraph==0.1.0'.
required: false
default: "reagraph @ git+https://github.com/Graphify-Labs/REAgraph@main"
outputs:
changed:
description: "Count of real changes (added + removed + rewired nodes)."
value: ${{ steps.diff.outputs.changed }}
added:
description: "Added node count."
value: ${{ steps.diff.outputs.added }}
removed:
description: "Removed node count."
value: ${{ steps.diff.outputs.removed }}
rewired:
description: "Rewired node count."
value: ${{ steps.diff.outputs.rewired }}
runs:
using: "composite"
steps:
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install reagraph
shell: bash
run: |
python -m pip install --upgrade pip
# reagraph is not yet on PyPI, so the default installs from the repo.
# Pin to a released version once it is published (see reagraph-version).
python -m pip install "${{ inputs.reagraph-version }}"
reagraph --help >/dev/null
- name: Run byte-agnostic diff
id: diff
shell: bash
env:
REAGRAPH_BASE: ${{ inputs.base }}
REAGRAPH_HEAD: ${{ inputs.head }}
REAGRAPH_BY: ${{ inputs.by }}
run: |
set -euo pipefail
# Fail early with a clear message if either graph is missing.
for var in REAGRAPH_BASE REAGRAPH_HEAD; do
path="${!var}"
if [ -z "$path" ]; then
echo "::error::$var is empty -- pass a path to a reagraph/REA graph JSON file."
exit 1
fi
if [ ! -f "$path" ]; then
echo "::error::graph file not found: $path (set the '$( [ "$var" = REAGRAPH_BASE ] && echo base || echo head )' input to a reagraph/REA node-link JSON file)."
exit 1
fi
done
# Run the exact diff command and capture its full output (banner + report).
set +e
out="$(reagraph diff "$REAGRAPH_BASE" "$REAGRAPH_HEAD" --by "$REAGRAPH_BY" 2>&1)"
rc=$?
set -e
echo "$out"
if [ $rc -ne 0 ]; then
echo "::error::reagraph diff exited with status $rc"
# Still surface what we captured, then fail.
printf '%s\n' "$out" >&2
exit $rc
fi
# Parse the counts out of the text report. Lines look like:
# added nodes : 4
# removed nodes : 0
# rewired nodes : 2
added="$(printf '%s\n' "$out" | sed -n 's/^[[:space:]]*added nodes[[:space:]]*:[[:space:]]*\([0-9][0-9]*\).*/\1/p' | head -n1)"
removed="$(printf '%s\n' "$out" | sed -n 's/^[[:space:]]*removed nodes[[:space:]]*:[[:space:]]*\([0-9][0-9]*\).*/\1/p' | head -n1)"
rewired="$(printf '%s\n' "$out" | sed -n 's/^[[:space:]]*rewired nodes[[:space:]]*:[[:space:]]*\([0-9][0-9]*\).*/\1/p' | head -n1)"
added="${added:-0}"; removed="${removed:-0}"; rewired="${rewired:-0}"
changed=$(( added + removed + rewired ))
echo "added=$added" >> "$GITHUB_OUTPUT"
echo "removed=$removed" >> "$GITHUB_OUTPUT"
echo "rewired=$rewired" >> "$GITHUB_OUTPUT"
echo "changed=$changed" >> "$GITHUB_OUTPUT"
# Build the shared Markdown block (summary + PR comment use the same text).
marker="<!-- reagraph-diff -->"
{
echo "$marker"
echo "## REAgraph build diff (byte-agnostic)"
echo ""
echo '```text'
printf '%s\n' "$out"
echo '```'
echo ""
echo "**${changed} real change(s)** (byte-agnostic): ${added} added, ${removed} removed, ${rewired} rewired. The real delta, not the thousands of phantom per-build-id ones."
} > "$RUNNER_TEMP/reagraph-diff.md"
# Step summary.
cat "$RUNNER_TEMP/reagraph-diff.md" >> "$GITHUB_STEP_SUMMARY"
echo "body_path=$RUNNER_TEMP/reagraph-diff.md" >> "$GITHUB_OUTPUT"
echo "marker=$marker" >> "$GITHUB_OUTPUT"
- name: Post or update PR comment
if: ${{ inputs.comment == 'true' && github.event_name == 'pull_request' }}
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const marker = '<!-- reagraph-diff -->';
const body = fs.readFileSync(process.env.REAGRAPH_BODY_PATH, 'utf8');
const { owner, repo } = context.repo;
const issue_number = context.issue.number;
if (!issue_number) {
core.info('No PR number in context; skipping comment.');
return;
}
const existing = await github.paginate(
github.rest.issues.listComments,
{ owner, repo, issue_number, per_page: 100 }
);
const mine = existing.find(c => c.body && c.body.includes(marker));
if (mine) {
await github.rest.issues.updateComment({ owner, repo, comment_id: mine.id, body });
core.info(`Updated existing comment ${mine.id}.`);
} else {
await github.rest.issues.createComment({ owner, repo, issue_number, body });
core.info('Created new comment.');
}
env:
REAGRAPH_BODY_PATH: ${{ steps.diff.outputs.body_path }}