Daily Activity Report #204
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: Daily Activity Report | |
| permissions: | |
| contents: write | |
| on: | |
| schedule: | |
| - cron: "0 9 * * *" # Daily at 9 AM UTC | |
| workflow_dispatch: # Manual trigger | |
| # Configure these in your repo's Settings > Variables and Secrets: | |
| # Variables (vars): | |
| # SCAN_PATHS - Comma-separated paths to scan (e.g. "~/Projects,~/code") | |
| # EXCLUDE_REPOS - Comma-separated repo name patterns to exclude (e.g. "archive,understanding-dana") | |
| # GH_AUTHOR - GitHub username for PR fetching (default: @me) | |
| # GIT_EMAIL - Email for git commits (default: activity-bot@users.noreply.github.com) | |
| # GIT_NAME - Name for git commits (default: Activity Bot) | |
| # SITE_CONFIG - JSON for dashboard branding (optional, see site-config.json.example) | |
| # CATEGORIES_CONFIG - JSON for repo grouping (optional, see categories.json.example) | |
| # Secrets: | |
| # CLOUDFLARE_API_TOKEN - For Cloudflare Pages deploy (optional) | |
| # CLOUDFLARE_ACCOUNT_ID - For Cloudflare Pages deploy (optional) | |
| jobs: | |
| generate: | |
| runs-on: self-hosted # Uses your machine with access to local repos | |
| env: | |
| REPORT_SCAN_PATHS: ${{ vars.SCAN_PATHS || '~/Projects,~/Local Sites' }} | |
| REPORT_EXCLUDE_REPOS: ${{ vars.EXCLUDE_REPOS || '' }} | |
| REPORT_GH_AUTHOR: ${{ vars.GH_AUTHOR || '@me' }} | |
| steps: | |
| - name: Clean runner state | |
| run: | | |
| cd "$GITHUB_WORKSPACE" 2>/dev/null || true | |
| # Abort any stuck rebase/merge from a previous failed run | |
| git rebase --abort 2>/dev/null || true | |
| git merge --abort 2>/dev/null || true | |
| # Discard any uncommitted changes to generated files | |
| git checkout -- . 2>/dev/null || true | |
| git clean -fd 2>/dev/null || true | |
| - uses: actions/checkout@v6 | |
| with: | |
| clean: true | |
| - name: Setup Node | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: 24 | |
| - name: Write categories config | |
| if: vars.CATEGORIES_CONFIG != '' | |
| run: echo '${{ vars.CATEGORIES_CONFIG }}' > categories.json | |
| - name: Generate report | |
| shell: bash | |
| run: | | |
| EXTRA_ARGS=() | |
| [ -n "${REPORT_EXCLUDE_REPOS}" ] && EXTRA_ARGS+=("--exclude=${REPORT_EXCLUDE_REPOS}") | |
| node generate-rich-report.mjs "--paths=${REPORT_SCAN_PATHS}" --gh-author="${REPORT_GH_AUTHOR}" "${EXTRA_ARGS[@]}" | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| CATEGORIES_CONFIG: ${{ vars.CATEGORIES_CONFIG }} | |
| - name: Commit and push | |
| id: commit | |
| shell: bash | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| git config user.email "${{ vars.GIT_EMAIL || 'activity-bot@users.noreply.github.com' }}" | |
| git config user.name "${{ vars.GIT_NAME || 'Activity Bot' }}" | |
| git add -f activity-data.json | |
| if git diff --cached --quiet; then | |
| echo "No changes to commit" | |
| echo "committed=false" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| git commit -m "chore: daily activity update" | |
| echo "committed=true" >> "$GITHUB_OUTPUT" | |
| # Push with retry + auto-resolve conflicts on generated files | |
| for attempt in 1 2 3; do | |
| if git push; then | |
| echo "Push succeeded on attempt $attempt" | |
| exit 0 | |
| fi | |
| echo "Push failed (attempt $attempt), rebasing on latest..." | |
| git fetch origin main | |
| # Use -X theirs to prefer our freshly generated files on conflict | |
| if git rebase -X theirs origin/main; then | |
| echo "Rebase succeeded, retrying push..." | |
| continue | |
| fi | |
| # If rebase still fails, abort and force-regenerate | |
| echo "Rebase failed, regenerating from latest..." | |
| git rebase --abort | |
| git reset --soft HEAD~1 | |
| git checkout -- . | |
| git pull origin main | |
| EXTRA_ARGS=() | |
| [ -n "${REPORT_EXCLUDE_REPOS}" ] && EXTRA_ARGS+=("--exclude=${REPORT_EXCLUDE_REPOS}") | |
| node generate-rich-report.mjs "--paths=${REPORT_SCAN_PATHS}" --gh-author="${REPORT_GH_AUTHOR}" "${EXTRA_ARGS[@]}" | |
| git add -f activity-data.json | |
| git diff --cached --quiet || git commit -m "chore: daily activity update" | |
| done | |
| echo "::warning::All push attempts failed" | |
| exit 1 | |
| - name: Prepare deploy directory | |
| if: always() && vars.CLOUDFLARE_DEPLOY == 'true' | |
| run: | | |
| mkdir -p _deploy | |
| cp index.html activity-data.json _deploy/ | |
| [ -n "$SITE_CONFIG" ] && echo "$SITE_CONFIG" > _deploy/site-config.json || true | |
| [ -n "$CATEGORIES" ] && echo "$CATEGORIES" > _deploy/categories.json || true | |
| env: | |
| SITE_CONFIG: ${{ vars.SITE_CONFIG }} | |
| CATEGORIES: ${{ vars.CATEGORIES_CONFIG }} | |
| - name: Deploy to Cloudflare | |
| if: always() && steps.commit.outcome != 'skipped' && vars.CLOUDFLARE_DEPLOY == 'true' | |
| run: npx wrangler@4 pages deploy _deploy --project-name=${{ vars.CLOUDFLARE_PROJECT || 'activity-report' }} | |
| env: | |
| CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} | |
| CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} |