From dcdfd15a9b192f64cc313c65b74fc2d9ce30df9f Mon Sep 17 00:00:00 2001 From: Matthew Phillips Date: Tue, 18 Aug 2026 16:33:47 -0400 Subject: [PATCH] Switch issue triage from triagebot-action to factory (#17733) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follows the code review switch in #17725. Triage now runs in the factory Worker instead of `withastro/triagebot-action`, so `.github/workflows/issue-triage.yml` goes away — it was the only place the action was used. The triage config keeps the behaviour the action was configured with: the repository's own `.agents/skills/triage`, Opus for the pipeline and Sonnet for the fix-verification and re-triage classifiers, and no auto-PR, so a fix still waits for the reporter to confirm a preview release before a pull request is opened. The triage state labels are unchanged, so `issue-needs-repro.yml` and `.github/scripts/stale-issues.ts` keep working. Two things the action did in its own CI steps now have to be configured, because the factory hands the agent a bare checkout: - `installCommand` / `buildCommand` install and build the workspace. Without them a reproduction project resolves `astro` to `packages/astro/dist` and nothing runs, so the agent would report "could not reproduce" about its own unbuilt checkout. The install also clones the compiler for the diagnose and verify steps to read, non-fatally since it is reference-only. - `factory-preview.yml` publishes preview releases. The factory can't publish them itself — pkg.pr.new authenticates with this repository's Actions OIDC identity, which a Worker can't present — so it dispatches this workflow on the default branch and reads the published package list back from a check run. Publishing and reporting are separate jobs so the one that runs fix-branch code holds neither repository secrets nor `checks: write`. The Anthropic API key now lives on the Worker, so the `CI_ANTHROPIC_API_KEY` repository secret is no longer used by anything. --- .github/factory.yml | 14 ++- .github/workflows/factory-preview.yml | 166 ++++++++++++++++++++++++++ .github/workflows/issue-triage.yml | 75 ------------ 3 files changed, 177 insertions(+), 78 deletions(-) create mode 100644 .github/workflows/factory-preview.yml delete mode 100644 .github/workflows/issue-triage.yml diff --git a/.github/factory.yml b/.github/factory.yml index e9292e5efe35..8a258440fe4b 100644 --- a/.github/factory.yml +++ b/.github/factory.yml @@ -17,7 +17,15 @@ review: - documentation - changeset -# Review only for now. Triage is enabled by default, so it has to be -# switched off explicitly. triage: - enabled: false + enabled: true + skill: .agents/skills/triage + model: anthropic/claude-opus-4-6 + verificationModel: anthropic/claude-sonnet-4-6 + autoPrOnFix: false + installCommand: + - pnpm install --no-frozen-lockfile + - git clone --depth 1 https://github.com/withastro/compiler.git .compiler || true + buildCommand: pnpm build + previewRelease: + workflow: factory-preview.yml diff --git a/.github/workflows/factory-preview.yml b/.github/workflows/factory-preview.yml new file mode 100644 index 000000000000..fd7bef8cad59 --- /dev/null +++ b/.github/workflows/factory-preview.yml @@ -0,0 +1,166 @@ +# Lets the factory bot publish a preview release for a fix branch it has pushed. + +name: Factory preview release + +on: + workflow_dispatch: + inputs: + branch: + description: Fix branch to publish a preview release for + required: true + issue: + description: Issue number the fix belongs to + required: false + +permissions: {} + +concurrency: + group: factory-preview-${{ inputs.branch }} + cancel-in-progress: true + +env: + FORCE_COLOR: true + ASTRO_TELEMETRY_DISABLED: true + # 7 GiB by default on GitHub, setting to 6 GiB + NODE_OPTIONS: --max-old-space-size=6144 + +jobs: + publish: + name: Build & publish preview + if: github.repository_owner == 'withastro' + runs-on: ubuntu-latest + timeout-minutes: 20 + permissions: + contents: read + id-token: write # pkg.pr.new authenticates with Actions OIDC + outputs: + sha: ${{ steps.resolve.outputs.sha }} + packages: ${{ steps.collect.outputs.packages }} + conclusion: ${{ steps.collect.outputs.conclusion }} + steps: + - name: Disable git crlf + run: git config --global core.autocrlf false + + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + ref: ${{ inputs.branch }} + # Diffing against the default branch needs its history. + fetch-depth: 0 + persist-credentials: false + + # Resolved before anything can fail, so the report job always has a commit + # to attach its check run to. + - name: Resolve head commit + id: resolve + run: echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" + + # Publishing every package would be far too slow, so only publish the ones + # the fix touched. + - name: Find changed packages + id: changed + env: + # The ref this workflow was dispatched on, i.e. the default branch. + BASE_REF: ${{ github.ref_name }} + run: | + git diff --name-only "origin/${BASE_REF}...HEAD" > /tmp/changed-files.txt + node --input-type=module -e ' + import { appendFileSync, readFileSync } from "node:fs"; + const files = readFileSync("/tmp/changed-files.txt", "utf8").split("\n"); + const dirs = new Set(); + for (const file of files) { + // Charset restricted because the publish step word-splits this unquoted. + const match = /^(packages\/(?:integrations\/)?[a-zA-Z0-9._-]+)\//.exec(file); + if (match) dirs.add(match[1]); + } + appendFileSync(process.env.GITHUB_OUTPUT, `dirs=${[...dirs].join(" ")}\n`); + ' + + - name: Setup PNPM + if: steps.changed.outputs.dirs != '' + uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8 + + - name: Setup Node + if: steps.changed.outputs.dirs != '' + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + with: + node-version: 24.15.0 + # Explicitly disable cache — a fresh install on every run avoids + # the risk of a poisoned cache being used during the publish step + # which has access to the OIDC id-token. + package-manager-cache: false + + # These two steps run code from the fix branch, which is why this job holds + # no repository secrets — not even the Turbo cache credentials. + - name: Install dependencies + if: steps.changed.outputs.dirs != '' + run: pnpm install + + - name: Build packages + if: steps.changed.outputs.dirs != '' + run: pnpm run build + + - name: Publish preview packages + id: publish + if: steps.changed.outputs.dirs != '' + env: + DIRS: ${{ steps.changed.outputs.dirs }} + run: | + # Unquoted on purpose: pkg-pr-new takes one argument per package. + pnpm exec pkg-pr-new publish --pnpm --compact --no-template \ + --comment=off --json /tmp/pkg-pr-new.json $DIRS + + - name: Collect published packages + id: collect + if: always() + env: + OUTCOME: ${{ steps.publish.outcome }} + run: | + node --input-type=module -e ' + import { appendFileSync, readFileSync } from "node:fs"; + let packages = []; + try { + const report = JSON.parse(readFileSync("/tmp/pkg-pr-new.json", "utf8")); + packages = (report.packages ?? []).map((entry) => ({ + name: entry.name, + url: entry.url, + })); + } catch { + // Nothing to publish, or publishing failed; report an empty list. + } + const conclusion = + process.env.OUTCOME === "success" && packages.length > 0 ? "success" : "failure"; + appendFileSync(process.env.GITHUB_OUTPUT, `packages=${JSON.stringify({ packages })}\n`); + appendFileSync(process.env.GITHUB_OUTPUT, `conclusion=${conclusion}\n`); + ' + + # A dispatched workflow can't return a value, so the result is posted as a check + # run for the factory to read. It's a separate job because it needs + # `checks: write`, which the job running fix-branch code must not have. + report: + name: Report preview release + runs-on: ubuntu-latest + needs: publish + # Report a failure too, rather than leaving the factory waiting. An empty sha + # means the run never got far enough to have anything to report. + if: always() && needs.publish.outputs.sha != '' + timeout-minutes: 5 + permissions: + checks: write + steps: + - name: Create check run + env: + GH_TOKEN: ${{ github.token }} + HEAD_SHA: ${{ needs.publish.outputs.sha }} + PACKAGES: ${{ needs.publish.outputs.packages }} + CONCLUSION: ${{ needs.publish.outputs.conclusion || 'failure' }} + run: | + PAYLOAD="${PACKAGES}" + if [ -z "$PAYLOAD" ]; then PAYLOAD='{"packages":[]}'; fi + SUMMARY=$(printf '```json\n%s\n```' "$PAYLOAD") + gh api "repos/${GITHUB_REPOSITORY}/check-runs" \ + -f name='factory/preview-release' \ + -f head_sha="$HEAD_SHA" \ + -f status=completed \ + -f conclusion="$CONCLUSION" \ + -f 'output[title]=Preview release' \ + -f "output[summary]=$SUMMARY" diff --git a/.github/workflows/issue-triage.yml b/.github/workflows/issue-triage.yml deleted file mode 100644 index 6807de92dddb..000000000000 --- a/.github/workflows/issue-triage.yml +++ /dev/null @@ -1,75 +0,0 @@ -name: Issue Triage - -on: - issues: - types: [opened, reopened, closed] - issue_comment: - types: [created] - -permissions: {} - -concurrency: - # Only one triage run per issue at a time. New runs queue (not cancel) - # to avoid killing in-flight runs when the bot posts its own comment. - group: issue-triage-${{ github.event.issue.number }} - cancel-in-progress: false - -jobs: - triage: - # Skip pull requests and bot comments - if: >- - !github.event.issue.pull_request && - (github.event.action != 'created' || - (github.event.comment.user.login != 'astrobot-houston' && - github.event.comment.user.login != 'github-actions[bot]')) - runs-on: ubuntu-latest - timeout-minutes: 60 - permissions: - contents: read # Read repo (push uses FREDKBOT_GITHUB_TOKEN) - issues: read # Read issue details for triage - id-token: write # OIDC auth for pkg.pr.new preview releases - steps: - - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - fetch-depth: 0 - persist-credentials: false - - - name: Configure Git identity - run: | - git config user.name "astrobot-houston" - git config user.email "fred+astrobot@astro.build" - - - name: Setup PNPM - uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8 - - - name: Setup Node - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 - with: - node-version: 24.15.0 - cache: pnpm - - - name: Install agent CLIs (bgproc, agent-browser) - run: | - npm install -g bgproc agent-browser - agent-browser install - - - name: Clone Astro Compiler (for debugging) - run: git clone --depth 1 https://github.com/withastro/compiler.git .compiler - - - name: Install deps - run: pnpm install --frozen-lockfile - - - name: Build - run: pnpm build - - - name: Run triagebot - uses: withastro/triagebot-action@b16c8f2209da49daed89811cf6f9a50b0270168e # v0.3.9 - with: - read-token: ${{ secrets.GITHUB_TOKEN }} - write-token: ${{ secrets.FREDKBOT_GITHUB_TOKEN }} - anthropic-api-key: ${{ secrets.CI_ANTHROPIC_API_KEY }} - triage-skill: .agents/skills/triage - pr-skill: .agents/skills/astro-pr-writer - pr-skill-name: astro-pr-writer - bot-logins: astrobot-houston