Skip to content

feat: implement changesets in repo #6

feat: implement changesets in repo

feat: implement changesets in repo #6

name: 🧪 PR Snapshot Diff (PNG/YML)
on:
pull_request:
branches: ["main"]
types: [opened, synchronize, reopened, ready_for_review]
permissions:
contents: read
pull-requests: write
concurrency:
group: pr-snapshot-diff-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
snapshot-diff:
name: 🔎 Snapshot changes (png/yml) & bump guard
runs-on: ubuntu-latest
steps:
- name: ⏬ Checkout PR head (not merge ref)
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ github.event.pull_request.head.sha }}
- name: 🔁 Ensure base (origin/main) is present
shell: bash
run: |
set -euo pipefail
if [ -f .git/shallow ]; then
git fetch --unshallow --no-tags --prune
fi
git fetch --no-tags --prune origin +refs/heads/main:refs/remotes/origin/main
git rev-parse remotes/origin/main >/dev/null
- name: 🧾 Collect changed PNG/YML snapshots
id: diff
shell: bash
run: |
set -euo pipefail
git diff --unified=3 --no-color origin/main...HEAD -- __snapshots__/**/*.{png,yml} > snapshots.diff || true
git diff --name-status origin/main...HEAD -- __snapshots__/**/*.{png,yml} > snapshots.list || true
TOTAL=$(wc -l < snapshots.list 2>/dev/null | tr -d '[:space:]' || echo 0)
ADDED=$(grep -E '^[A]\s' snapshots.list 2>/dev/null | wc -l | tr -d '[:space:]' || echo 0)
MOD=$(grep -E '^[M]\s' snapshots.list 2>/dev/null | wc -l | tr -d '[:space:]' || echo 0)
DEL=$(grep -E '^[D]\s' snapshots.list 2>/dev/null | wc -l | tr -d '[:space:]' || echo 0)
REN=$(grep -E '^[R]\d*\s' snapshots.list 2>/dev/null | wc -l | tr -d '[:space:]' || echo 0)
head -n 200 snapshots.list > snapshots.short || true
{
echo "### Snapshot Changes (PNG/YML)"
echo
if [ "${TOTAL}" = "0" ]; then
echo "_No \`__snapshots__/**/*.{png,yml}\` changes detected._"
else
echo "- Total: **${TOTAL}** (added: ${ADDED}, modified: ${MOD}, deleted: ${DEL}, renamed: ${REN})"
echo
echo "| Status | File |"
echo "|-------:|:-----|"
while read -r LINE; do
[ -z "${LINE:-}" ] && continue
STATUS=$(echo "$LINE" | awk '{print $1}')
FILE=$(echo "$LINE" | awk '{print $2}')
if echo "$STATUS" | grep -qE '^R'; then
FILE=$(echo "$LINE" | awk '{print $3}')
fi
echo "| \`$STATUS\` | \`$FILE\` |"
done < snapshots.short
if [ "$TOTAL" -gt 200 ]; then
echo ""
echo "_…and $(($TOTAL - 200)) more_"
fi
fi
echo ""
echo "<!-- snapshot-summary-marker -->"
} > snapshot-summary.md
{
echo "SNAP_TOTAL=${TOTAL}"
echo "SNAP_ADDED=${ADDED}"
echo "SNAP_MODIFIED=${MOD}"
echo "SNAP_DELETED=${DEL}"
echo "SNAP_RENAMED=${REN}"
echo "SNAP_SUMMARY_MD_PATH=$(pwd)/snapshot-summary.md"
} >> "$GITHUB_ENV"
- name: 📎 Upload unified diff (artifact)
if: env.SNAP_TOTAL != '0'
uses: actions/upload-artifact@v4
with:
name: snapshots-diff
path: snapshots.diff
if-no-files-found: ignore
retention-days: 7
- name: 💬 Upsert PR comment
uses: actions/github-script@v7
env:
SNAP_SUMMARY_MD_PATH: ${{ env.SNAP_SUMMARY_MD_PATH }}
with:
script: |
const fs = require('fs');
const { owner, repo, number } = context.issue;
const marker = '<!-- snapshot-summary-marker -->';
let body = '_No snapshot info._';
try {
const p = process.env.SNAP_SUMMARY_MD_PATH;
if (p && fs.existsSync(p)) body = fs.readFileSync(p, 'utf8');
} catch {}
const { data: comments } = await github.rest.issues.listComments({ owner, repo, issue_number: number, per_page: 100 });
const existing = comments.find(c => c.user?.type === 'Bot' && c.body?.includes(marker));
if (existing) {
await github.rest.issues.updateComment({ owner, repo, comment_id: existing.id, body });
} else {
await github.rest.issues.createComment({ owner, repo, issue_number: number, body });
}
# If snapshots (png/yml) were changed, require at least a minor bump
- name: 🟦 Setup Node (for changeset status)
uses: actions/setup-node@v4
with:
node-version-file: ".nvmrc"
cache: "npm"
- name: 📦 Install deps (root)
run: npm ci
- name: 🔎 Determine bump plan (JSON + Fallback)
id: plan
shell: bash
run: |
set -euo pipefail
npx -y @changesets/cli@^2 status --since=origin/main --output=json > .changeset-status.json || true
node > .changeset-env <<'NODE'
const fs = require('fs');
let hasMinor = 0, hasMajor = 0;
try {
const txt = fs.readFileSync('.changeset-status.json','utf8').trim();
if (txt) {
const j = JSON.parse(txt);
for (const r of (j.releases || [])) {
if (r.type === 'minor') hasMinor = 1;
if (r.type === 'major') hasMajor = 1;
}
}
} catch (_) {}
process.stdout.write(`HAS_MINOR=${hasMinor}\nHAS_MAJOR=${hasMajor}\n`);
NODE
source .changeset-env || true
if [ "${HAS_MINOR}" = "0" ] && [ "${HAS_MAJOR}" = "0" ]; then
git diff --name-only origin/main...HEAD -- ".changeset/*.md" > .cs-files || true
if [ -s .cs-files ]; then
while read -r f; do
[ -z "${f:-}" ] && continue
if grep -E ':\s*minor\b' "$f" >/dev/null 2>&1; then HAS_MINOR=1; fi
if grep -E ':\s*major\b' "$f" >/dev/null 2>&1; then HAS_MAJOR=1; fi
done < .cs-files
fi
{
echo "HAS_MINOR=${HAS_MINOR}"
echo "HAS_MAJOR=${HAS_MAJOR}"
} > .changeset-env
fi
cat .changeset-env >> "$GITHUB_ENV"
{
echo "### Bump Plan Detection"
echo
echo "- HAS_MINOR: ${HAS_MINOR}"
echo "- HAS_MAJOR: ${HAS_MAJOR}"
} >> "$GITHUB_STEP_SUMMARY"
- name: 🚦 Enforce Snapshot changes require >= minor
if: env.SNAP_TOTAL != '0'
shell: bash
run: |
set -euo pipefail
if [ "${HAS_MINOR}" = "0" ] && [ "${HAS_MAJOR}" = "0" ]; then
echo "::error title=Snapshots changed but only patch detected::PNG/YML snapshots changed. Please bump at least MINOR in your changeset."
exit 1
else
echo "Snapshot changes and non-patch bump present → OK."
fi