Update Star History Chart #48
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Update Star History Chart | |
| on: | |
| schedule: | |
| - cron: '15 2 * * *' | |
| workflow_dispatch: | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| statuses: write | |
| jobs: | |
| update: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Generate static star-history SVGs | |
| id: generate | |
| env: | |
| GH_TOKEN: ${{ secrets.STAR_HISTORY_TOKEN }} | |
| # Chart the repo this workflow runs on (upstream or fork). | |
| STAR_HISTORY_REPO: ${{ github.repository }} | |
| run: | | |
| if [ -z "$GH_TOKEN" ]; then | |
| # GITHUB_TOKEN cannot list stargazers (GitHub Jul 2026 restriction). | |
| # Forks and clones without STAR_HISTORY_TOKEN should not fail daily CI. | |
| echo "::notice::STAR_HISTORY_TOKEN not set — skipping star-history update. To enable: add a fine-grained PAT with read access to this repository as the STAR_HISTORY_TOKEN secret (see scripts/generate-star-history.py)." | |
| echo "skipped=true" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| echo "skipped=false" >> "$GITHUB_OUTPUT" | |
| python3 scripts/generate-star-history.py | |
| - name: Run validate gates (for PR status) | |
| id: validate_gates | |
| if: steps.generate.outputs.skipped != 'true' | |
| continue-on-error: true | |
| run: bash scripts/ci-validate-gates.sh | |
| - name: Run audit gates (for PR status) | |
| id: audit_gates | |
| if: steps.generate.outputs.skipped != 'true' | |
| continue-on-error: true | |
| run: bash scripts/ci-audit-gates.sh | |
| - name: Fail the run if either gate failed | |
| if: steps.generate.outputs.skipped != 'true' && (steps.validate_gates.outcome == 'failure' || steps.audit_gates.outcome == 'failure') | |
| run: | | |
| echo "validate gates: ${{ steps.validate_gates.outcome }}" | |
| echo "audit gates: ${{ steps.audit_gates.outcome }}" | |
| echo "One or more dogfood gates failed — not opening an automated PR." | |
| exit 1 | |
| - name: Open PR for chart SVGs if changed | |
| id: pr | |
| if: steps.generate.outputs.skipped != 'true' | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| if git diff --quiet assets/visuals/star-history.svg assets/visuals/star-history-dark.svg; then | |
| echo "No chart changes — skip PR" | |
| echo "opened=false" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| BRANCH="automated/star-history-$(date -u +%Y-%m-%d)" | |
| git fetch origin main "$BRANCH" 2>/dev/null || git fetch origin main | |
| git checkout -B "$BRANCH" | |
| git add assets/visuals/star-history.svg assets/visuals/star-history-dark.svg | |
| git commit -m "chore: update star-history chart SVGs" | |
| if git rev-parse --verify "refs/remotes/origin/${BRANCH}" >/dev/null 2>&1; then | |
| git rebase "origin/${BRANCH}" | |
| fi | |
| git push -u origin "$BRANCH" --force-with-lease | |
| echo "head_sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" | |
| echo "branch=${BRANCH}" >> "$GITHUB_OUTPUT" | |
| echo "opened=true" >> "$GITHUB_OUTPUT" | |
| EXISTING=$(gh pr list --head "${{ github.repository_owner }}:${BRANCH}" --base main --state open --json number -q '.[0].number' 2>/dev/null || true) | |
| if [ -n "$EXISTING" ] && [ "$EXISTING" != "null" ]; then | |
| echo "PR #$EXISTING already open" | |
| PR_NUMBER="$EXISTING" | |
| else | |
| gh pr create \ | |
| --base main \ | |
| --head "$BRANCH" \ | |
| --title "chore: update star-history chart $(date -u +%Y-%m-%d)" \ | |
| --body "Automated star-history SVG refresh from \`update-star-history.yml\`. | |
| Branch protection requires PR merge — direct pushes to \`main\` are blocked." | |
| PR_NUMBER=$(gh pr view "$BRANCH" --json number -q '.number') | |
| fi | |
| echo "number=${PR_NUMBER}" >> "$GITHUB_OUTPUT" | |
| gh pr merge "$PR_NUMBER" --auto --squash --delete-branch | |
| # README/showcase serve charts via jsDelivr (@main). Purge so the | |
| # next visit is not stuck on a stale cached SVG after merge lands. | |
| curl -fsS "https://purge.jsdelivr.net/gh/${{ github.repository }}@main/assets/visuals/star-history.svg" || true | |
| curl -fsS "https://purge.jsdelivr.net/gh/${{ github.repository }}@main/assets/visuals/star-history-dark.svg" || true | |
| - name: Post required commit statuses for branch protection | |
| if: steps.generate.outputs.skipped != 'true' && steps.pr.outputs.opened == 'true' | |
| uses: actions/github-script@v9 | |
| with: | |
| script: | | |
| const sha = '${{ steps.pr.outputs.head_sha }}'; | |
| if (!sha) { | |
| core.setFailed('Missing head_sha for commit statuses'); | |
| return; | |
| } | |
| const toState = (outcome) => (outcome === 'success' ? 'success' : 'failure'); | |
| const checks = [ | |
| { | |
| context: 'validate', | |
| description: 'Pattern/registry gates (star-history inline)', | |
| state: toState('${{ steps.validate_gates.outcome }}'), | |
| }, | |
| { | |
| context: 'audit', | |
| description: 'Loop readiness gates (star-history inline)', | |
| state: toState('${{ steps.audit_gates.outcome }}'), | |
| }, | |
| ]; | |
| for (const check of checks) { | |
| await github.rest.repos.createCommitStatus({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| sha, | |
| ...check, | |
| }); | |
| } |