Skip to content
82 changes: 73 additions & 9 deletions .github/workflows/weekly-api-diff.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
- name: Build current API snapshot
run: yarn build:api-branch

# Build release baseline using the last Publish commit (~2 min, always fresh)
# Build release baseline using the last minor/major release commit (skips patch-only releases)
- name: Build release baseline
run: yarn build:api-published

Expand All @@ -50,38 +50,90 @@ jobs:
CURRENT_PUBLISH=$(git log --grep='^Publish$' --oneline -1 | awk '{print $1}')
PREV_PUBLISH=$(cat snapshots/last-publish-hash.txt 2>/dev/null || echo "")

echo "=== Release detection ==="
echo "CURRENT_PUBLISH=$CURRENT_PUBLISH"
echo "PREV_PUBLISH=$PREV_PUBLISH"

if [ -n "$PREV_PUBLISH" ] && [ "$CURRENT_PUBLISH" != "$PREV_PUBLISH" ]; then
# New release landed so skip comparing to last week, commit fresh as new baseline
echo "NEW_RELEASE=true" >> $GITHUB_ENV
# Detect minor/major vs patch: check if s2 or react-aria-components got a x.y.0 tag on this Publish commit
IS_MINOR=$(git tag --points-at "$CURRENT_PUBLISH" | grep -E '^(@react-spectrum/s2|react-aria-components)@[0-9]+\.[0-9]+\.0$' | head -1)
echo "IS_MINOR_OR_MAJOR=${IS_MINOR:-(none)}"
if [ -n "$IS_MINOR" ]; then
echo "Minor/major release detected, resetting baseline"
echo "NEW_RELEASE=true" >> $GITHUB_ENV
else
echo "Patch release detected, updating hash, continuing with delta"
echo "$CURRENT_PUBLISH" > snapshots/last-publish-hash.txt
fi
else
echo "No new release"
fi

NEW_RELEASE="${NEW_RELEASE:-false}"

if [ "$NEW_RELEASE" != "true" ]; then
# Compare against the last diff in the snapshots repo
PREV=$(ls snapshots/diffs/*.txt 2>/dev/null | sort -r | head -1)
echo ""
echo "=== Delta computation ==="
echo "Comparing against: ${PREV:-(none, first run)}"
if [ -n "$PREV" ]; then
diff "$PREV" /tmp/diff-current.txt > /tmp/weekly-delta.txt || true
else
echo "(first run β€” no previous diff to compare against)" > /tmp/weekly-delta.txt
fi
fi

# Commit a diff if there is a new release (fresh baseline), or diff changed from last week
echo ""
echo "=== File sizes ==="
echo "diff-current.txt: $(wc -c < /tmp/diff-current.txt) bytes"
ls /tmp/weekly-delta.txt 2>/dev/null && echo "weekly-delta.txt: $(wc -c < /tmp/weekly-delta.txt) bytes" || echo "weekly-delta.txt: not created"
echo ""
echo "=== Delta content (first 20 lines) ==="
head -20 /tmp/weekly-delta.txt 2>/dev/null || echo "(none)"

# Commit a diff if there is a new minor/major release (fresh baseline), or diff changed from last week
# Skip if no difference from last diff (aka no change from last week/last run), or if the diff against the release code is empty
NEW_RELEASE="${NEW_RELEASE:-false}"
echo ""
echo "=== Commit decision ==="
if [ -s /tmp/diff-current.txt ] && ([ "$NEW_RELEASE" = "true" ] || [ -s /tmp/weekly-delta.txt ]); then
echo "Committing diff to snapshots repo"
cp /tmp/diff-current.txt snapshots/diffs/$TODAY.txt
mkdir -p snapshots/deltas
if [ -s /tmp/weekly-delta.txt ]; then
# Save processed delta: strip > / < markers and line-number markers,
# split into two labeled sections so it can be passed directly to the model
{
echo "=== NEW API CHANGES THIS WEEK (not in last week's diff) ==="
grep '^> ' /tmp/weekly-delta.txt | sed 's/^> //'
RELEASED=$(grep '^< ' /tmp/weekly-delta.txt | sed 's/^< //')
if [ -n "$RELEASED" ]; then
echo ""
echo "=== API CHANGES RELEASED SINCE LAST WEEK (were in last week's diff, now gone) ==="
echo "$RELEASED"
fi
} > snapshots/deltas/$TODAY.txt
fi
cd snapshots
git config user.email "github-actions@github.com"
git config user.name "GitHub Actions"
git add diffs/$TODAY.txt
[ -f deltas/$TODAY.txt ] && git add deltas/$TODAY.txt
echo "$CURRENT_PUBLISH" > last-publish-hash.txt
git add last-publish-hash.txt
echo "=== Staged changes ==="
git diff --cached --stat
git diff --cached --quiet || (git commit -m "weekly api diff $TODAY" && git push)
else
echo "Skipping commit β€” diff-current empty=$([ ! -s /tmp/diff-current.txt ] && echo yes || echo no), new_release=$NEW_RELEASE, delta_empty=$([ ! -s /tmp/weekly-delta.txt ] && echo yes || echo no)"
fi

# Summarize with GitHub Models (free via GITHUB_TOKEN) and post to Slack
- name: Summarize and post to Slack
env:
SLACK_TSDIFF_CHROMATIC_BOT_TOKEN: ${{ secrets.SLACK_TSDIFF_CHROMATIC_BOT_TOKEN }}
SLACK_CHANNEL_ID: ${{ secrets.SLACK_CHANNEL_ID }}
TEST_SLACK_ID: ${{ secrets.TEST_SLACK_ID }}
GITHUB_TOKEN: ${{ github.token }}
run: |
python3 << 'PYEOF'
Expand All @@ -99,6 +151,7 @@ jobs:
github_token = os.environ['GITHUB_TOKEN']
workspace = os.environ['GITHUB_WORKSPACE']
diff_url = f"https://github.com/{snapshots_repo}/blob/main/diffs/{today}.txt"
delta_url = f"https://github.com/{snapshots_repo}/blob/main/deltas/{today}.txt"

vs_release_size = os.path.getsize('/tmp/diff-current.txt')
vs_last_week_size = os.path.getsize('/tmp/weekly-delta.txt') if os.path.exists('/tmp/weekly-delta.txt') else 0
Expand All @@ -117,7 +170,9 @@ jobs:
elif new_release:
message = f"πŸ“Š Weekly API Diff β€” {today}\n\nNew release since last diff β€” resetting baseline. Full diff vs release: {diff_url}\n\nReact βœ… if changes look expected, or 🚨 if something looks wrong."
else:
delta = open('/tmp/weekly-delta.txt').read()[:4000]
# Read the already-processed delta saved by the shell step
delta_path = f"{workspace}/snapshots/deltas/{today}.txt"
model_input = open(delta_path).read() if os.path.exists(delta_path) else ""

# Extract classification rules from prompt.md (single source of truth)
prompt_md = open(f"{workspace}/scripts/weekly-api-diff/prompt.md").read()
Expand All @@ -127,10 +182,19 @@ jobs:

payload = {
"model": "gpt-4o-mini",
"max_tokens": 600,
"max_tokens": 800,
"messages": [{
"role": "user",
"content": f"Summarize this week-to-week react-spectrum API diff in under 200 words using bullet points.\n\n{rules}\n\nDelta (changes from last week):\n{delta}"
"content": (
f"Summarize this week's react-spectrum API changes using grouped bullet points. Group related changes together (e.g. multiple layout classes losing the same method = one bullet). For each group or item:\n"
f"- Name the component(s) or interface(s)\n"
f"- Say what changed (added, removed, signature changed)\n"
f"- Flag net-new components \n"
f"Don't spell out full type signatures. Aim for a knowledgeable teammate skimming Slack.\n\n"
f"IMPORTANT: Only report what is explicitly listed below. Do not infer or add anything from your training knowledge.\n\n"
f"{rules}\n\n"
f"{model_input}"
)
}]
}

Expand All @@ -143,7 +207,7 @@ jobs:
}
)
summary = json.loads(urllib.request.urlopen(req).read())['choices'][0]['message']['content']
message = f"πŸ“Š Weekly API Diff β€” {today}\n\n{summary}\n\nFull diff vs release: {diff_url}\n\nReact βœ… if changes look expected, or 🚨 if something looks wrong."
message = f"πŸ“Š Weekly API Diff β€” {today}\n\n{summary}\n\nWhat's new this week: {delta_url}\nFull diff vs release: {diff_url}\n\nReact βœ… if changes look expected, or 🚨 if something looks wrong."

req = urllib.request.Request(
'https://slack.com/api/chat.postMessage',
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
"release": "lerna publish from-package --yes",
"version:nightly": "yarn workspaces foreach --all --no-private -t version -d 3.0.0-nightly-$(git rev-parse --short HEAD)-$(date +'%y%m%d') && yarn apply-nightly --all",
"publish:nightly": "yarn workspaces foreach --all --no-private -t npm publish --tag nightly --access public",
"build:api-published": "node scripts/buildBranchAPI.js --githash=$(git log --grep='^Publish$' --oneline -1 | awk '{print $1}') --output=base-api",
"build:api-published": "node scripts/buildBranchAPI.js --githash=$(git rev-list -n 1 $(git tag -l 'react-aria-components@*' | grep -E '@[0-9]+\\.[0-9]+\\.0$' | sort -V | tail -1)) --output=base-api",
"build:api-branch": "node scripts/buildBranchAPI.js",
"compare:apis": "node scripts/compareAPIs.js",
"check-apis": "yarn build:api-branch --githash=\"origin/main\" --output=\"base-api\" && yarn build:api-branch && yarn compare:apis",
Expand Down
Loading