Collect TODO Fragments #45
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
| # file: .github/workflows/todo-collect.yml | |
| # version: 1.0.0 | |
| # guid: 31b770c1-e21e-4f13-b78e-cd30348f2b8d | |
| # last-edited: 2026-07-19 | |
| # Fold todo.d/ fragments into TODO.md and commit the result to the default | |
| # branch. Opt-in by presence: every step below is a no-op in a repo without | |
| # todo.d/todo.ini, so this file is safe to ship anywhere. | |
| # | |
| # Unlike the changelog system there is deliberately NO pull-request check — | |
| # adding a task is optional, not something to enforce on every code PR. | |
| name: Collect TODO Fragments | |
| on: | |
| schedule: | |
| # Daily at 07:17 UTC. Off the hour so it does not pile into the top-of-hour | |
| # crush of scheduled jobs across the org. | |
| - cron: '17 7 * * *' | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: todo-collect-${{ github.repository }} | |
| cancel-in-progress: false | |
| jobs: | |
| collect: | |
| name: Assemble TODO.md | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| # write, not read: the collect step pushes the assembled TODO.md back to the | |
| # default branch. The push normally authenticates with JF_CI_GH_PAT, which is | |
| # independent of this token — but without contents: write the documented | |
| # GITHUB_TOKEN fallback would be rejected, making it dead code. Matches the | |
| # changelog collector in reusable-release.yml. | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| fetch-depth: 0 | |
| - name: Set up Python | |
| uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 | |
| with: | |
| python-version: '3.12' | |
| - name: Collect fragments into TODO.md | |
| env: | |
| DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} | |
| TODO_PAT: ${{ secrets.JF_CI_GH_PAT }} | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| set -euo pipefail | |
| # Opt-in by presence: repos without the fragment system stop here. | |
| if [ ! -f todo.d/todo.ini ]; then | |
| echo "::notice::todo.d/todo.ini not present — TODO fragments not enabled here." | |
| exit 0 | |
| fi | |
| if [ ! -f scripts/assemble_todo.py ]; then | |
| echo "::warning::todo.d/todo.ini present but scripts/assemble_todo.py is missing; skipping." | |
| exit 0 | |
| fi | |
| # Only real fragments count — README.md and templates/ never do. | |
| if ! find todo.d -maxdepth 1 -name '*.md' ! -name 'README.md' | grep -q .; then | |
| echo "::notice::No TODO fragments to collect." | |
| exit 0 | |
| fi | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| # The assembler git rm's the fragments it consumes, so the insertion | |
| # and the removals are staged together. | |
| python3 scripts/assemble_todo.py | |
| if git diff --quiet && git diff --cached --quiet; then | |
| echo "::notice::Assembler produced no changes." | |
| exit 0 | |
| fi | |
| git add -A TODO.md todo.d | |
| # [skip ci] stops this docs-only commit from retriggering the full CI. | |
| git commit -m "docs(todo): collect fragments into TODO.md [skip ci]" | |
| # Prefer the PAT (passes branch protection); fall back to the workflow | |
| # token. Push to an explicit authenticated URL so we do not depend on | |
| # the checkout's persisted credentials. | |
| TOKEN="${TODO_PAT:-$GITHUB_TOKEN}" | |
| REMOTE="https://x-access-token:${TOKEN}@github.com/${GITHUB_REPOSITORY}.git" | |
| n=0 | |
| until git push "${REMOTE}" "HEAD:${DEFAULT_BRANCH}" || [ "$n" -ge 4 ]; do | |
| n=$((n + 1)) | |
| echo "Retry $n/4 for TODO push..." | |
| sleep $((2 ** n)) | |
| git pull --rebase "${REMOTE}" "${DEFAULT_BRANCH}" | |
| done | |
| [ "$n" -lt 4 ] || { echo "::error::Failed to push TODO.md"; exit 1; } | |
| echo "TODO.md updated." |