Skip to content

sync

sync #61

Workflow file for this run

name: sync
# Daily registry sync. Re-runs every producer entry, regenerates the
# README, stats, badges, and .well-known files, then opens a PR with the
# diff for auto-merge once required checks pass. The PR path lets us
# enforce branch protection (required status checks + codeowner review)
# while still automating ingestion.
#
# Escape hatch: `workflow_dispatch` with `direct_push=true` skips the PR
# and pushes straight to main. Use only when branch protection is
# bypassed (admin) — auto-merge handles normal operation.
on:
schedule:
- cron: '0 3 * * *'
repository_dispatch:
types: [sync-entry]
workflow_dispatch:
inputs:
id:
description: 'Single entry id (owner/repo) to sync'
required: false
type: string
revalidate:
description: 'Resync all entries even if unchanged'
required: false
type: boolean
default: false
direct_push:
description: 'Bypass PR; push directly to main (admin only)'
required: false
type: boolean
default: false
permissions:
contents: read
concurrency:
group: sync-${{ github.event.client_payload.id || github.event.inputs.id || 'all' }}
cancel-in-progress: false
jobs:
sync:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
actions: write
steps:
# pin: v6.0.0 -- actions/checkout
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3
# pin: v6.0.0 -- actions/setup-node
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903
with:
node-version-file: '.nvmrc'
cache: 'npm'
- run: npm ci
- name: Resolve target id
id: target
env:
DISPATCH_ID: ${{ github.event.client_payload.id }}
INPUT_ID: ${{ github.event.inputs.id }}
run: |
ID="${DISPATCH_ID:-$INPUT_ID}"
if [[ -n "$ID" && ! "$ID" =~ ^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$ ]]; then
echo "rejecting malformed id: $ID" >&2
exit 1
fi
echo "id=$ID" >> "$GITHUB_OUTPUT"
- name: Smoke (dry-run; cron only)
if: github.event_name == 'schedule'
run: npm run smoke
- name: Sync
env:
TARGET_ID: ${{ steps.target.outputs.id }}
run: |
if [ -n "$TARGET_ID" ]; then
node scripts/sync.mjs --only "$TARGET_ID"
else
node scripts/sync.mjs
fi
- name: Render README inline
run: npm run render
- name: Aggregate cross-graph stats
run: node scripts/aggregate.mjs --registry registry.json --out site/stats.json
- name: Render badges
run: node scripts/render-badges.mjs --registry registry.json --out site/badges
- name: Render well-known
run: node scripts/well-known.mjs --registry registry.json --out site/.well-known
- name: Check for diff
id: diff
run: |
if git diff --quiet registry.json README.md site/stats.json site/badges site/.well-known; then
echo "changed=false" >> "$GITHUB_OUTPUT"
echo "no changes to commit"
else
echo "changed=true" >> "$GITHUB_OUTPUT"
fi
# Direct push path (admin escape hatch). Used when
# workflow_dispatch.inputs.direct_push=true; bypasses the PR.
- name: Configure git (direct push only)
if: steps.diff.outputs.changed == 'true' && github.event.inputs.direct_push == 'true'
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
- name: Commit and push (direct push only)
if: steps.diff.outputs.changed == 'true' && github.event.inputs.direct_push == 'true'
run: |
git add registry.json README.md site/stats.json site/badges site/.well-known
git commit -m "chore(sync): update registry and README"
git push
# PR path (default). create-pull-request stages the diff, opens
# or updates the bot/sync branch, and the next step enables
# auto-merge so it lands once branch protection's required checks
# go green.
# pin: v8.1.1 -- peter-evans/create-pull-request
- name: Open or update sync PR
if: steps.diff.outputs.changed == 'true' && github.event.inputs.direct_push != 'true'
id: cpr
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1
with:
token: ${{ secrets.GITHUB_TOKEN }}
branch: bot/sync
delete-branch: true
commit-message: 'chore(sync): update registry and README'
title: 'chore(sync): update registry and README'
body: |
Automated registry resync from `scripts/sync.mjs`.
Touches:
- `registry.json` — entry-level `head_sha`, `last_validated_at`,
and any newly drifted entries.
- `README.md` — rendered entry table.
- `site/stats.json`, `site/badges/`, `site/.well-known/` —
regenerated derivatives.
Auto-merges once required status checks pass.
labels: |
bot-sync
automerge
chore
add-paths: |
registry.json
README.md
site/stats.json
site/badges
site/.well-known
# Auto-merge intentionally NOT enabled here. GitHub's
# branch-protection bypass allowlist does not accept the built-in
# `github-actions` app slug, so the bot's PR cannot satisfy
# `require_code_owner_reviews`. The PR sits open until a
# codeowner (amacsmith) reviews and merges manually, OR until we
# wire a custom GitHub App or fine-grained PAT through
# `secrets.BOT_PAT` and re-enable the enable-pull-request-automerge
# step using that token.
- name: Trigger pages redeploy (best-effort)
if: steps.diff.outputs.changed == 'true'
env:
GH_TOKEN: ${{ github.token }}
run: gh workflow run pages.yml --repo "$GITHUB_REPOSITORY" --ref main