name: Close Solved on: push: branches: [main] paths: ["work/**"] workflow_dispatch: inputs: dry_run: description: Report matches and would-be releases without closing or releasing anything type: boolean default: false permissions: contents: read issues: write concurrency: group: close-solved cancel-in-progress: false jobs: close: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: oven-sh/setup-bun@v2 # No install step: the script only uses Bun builtins + fetch. # # SRS_ADMIN_KEY lets the script report the solved set to the Worker # (POST /admin/solved). Closing an issue is invisible to D1 — the # catalog reconcile never reads issue state, and only logAttempt() moves # the ladder — so without this a pushed solution would never reach the # charts, the digest's solved ticks, or the drill/gate pools. - run: bun apps/cli/close-solved.ts env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} DRY_RUN: ${{ inputs.dry_run }} SRS_ADMIN_KEY: ${{ secrets.SRS_ADMIN_KEY }} # Project fields are a projection of the issue labels, so the same push # that adds a solution (and any issue filed since the last run) can leave # the board's Set/Difficulty empty. Reconciling here keeps the one # "solution landed" entry point responsible for the whole fan-out; the # script is idempotent, so a run with nothing to do writes nothing. # # Needs the `project` scope, which GITHUB_TOKEN does not have — hence # PROJECT_PAT. Unset = skip, matching SRS_ADMIN_KEY above. Best-effort by # design: D1 and the issues are the truth, so a Project failure must not # fail a run that already closed issues and logged the solve. - name: Reconcile Project Set/Difficulty continue-on-error: true env: GH_TOKEN: ${{ secrets.PROJECT_PAT }} DRY_RUN: ${{ inputs.dry_run }} run: | if [ -z "$GH_TOKEN" ]; then echo "PROJECT_PAT unset — skipping the Project field reconcile" exit 0 fi bun apps/cli/sync-project-fields.ts # One timever release per finished topic (every core sub-issue closed) and # per finished `Review — ` issue (every sub-issue closed), tagged # at the instant the last required problem closed. It reads the same rules # close-topics uses (apps/cli/topics.ts) straight off the sub-issues, so the # push that finishes a topic or review releases it in this run — no waiting # for Close Topics, which chains after this workflow. A problem re-parented # into a Review issue still belongs to its topic (apps/api/src/reviews.ts), # so moving it never rewrites a topic's release. Reconciling: a run releases # everything finished and still missing a release, so a manual dispatch # backfills and a problem closed by hand in the UI is picked up by the next # run. The docs changelog (deploy.yml) tags each release Topic or Review. release: needs: close # Like close-topics: a partially failed close still closed some issues, # and the script only reads state, so run unless the run was cancelled. if: ${{ !cancelled() }} runs-on: ubuntu-latest permissions: contents: write # push tags, create releases issues: read steps: # Full history: each tag targets the last main commit before its topic # finished, and backfilled tags reach far behind HEAD. - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: oven-sh/setup-bun@v2 # The tagger of the annotated, backdated tags. - run: | git config user.name "github-actions[bot]" git config user.email "41898282+github-actions[bot]@users.noreply.github.com" # No install step: the script only uses Bun builtins + fetch. - run: bun apps/cli/release-topics.ts env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} DRY_RUN: ${{ inputs.dry_run }}