aa17c4d706
## Problem Every merge to main touching the agent queued a gated `staging`+`prod` deploy that sat `pending` on a reviewer approval nobody grants routinely. Because the gated runs never completed, they never drained the concurrency queue and cancelled each other, so the Actions tab filled with never-completing runs and the agent only ever actually deployed via a manual dispatch + approval. The reviewer gate bought nothing here: the agent deploys with `--skip-promotion`, so a deploy lands **dormant** and nothing goes live until the consuming webapp flips `DASHBOARD_AGENT_VERSION`. Promotion is already a deliberate act (the env-var flip); gating the dormant deploy on top of that just created the pile-up. ## Change - **Remove the reviewer gate** by dropping the required-reviewers rule on the `dashboard-agent-*` environments (repo-settings change, done). The `environment:` key **stays** so the per-environment scoped deploy token still resolves — no secret migration. - **`workflow_dispatch` `ref` input** — deploy a specific commit SHA, branch, or tag; defaults to the ref the run launches from. Checkout uses `github.event.inputs.ref || github.sha`. - **Require the ref to be an ancestor of `main`.** Constrains which commit gets deployed to merged code only. A push is always main's tip (passes trivially); a dispatched unmerged ref is rejected before the deploy step. Because an explicit `ref:` checkout doesn't create remote-tracking branches, `origin/main` is fetched explicitly before `git merge-base --is-ancestor`. - **`cancel-in-progress: false`** (kept). Cancelling the runner wouldn't stop the remote build (it finishes server-side), and a superseding concurrent deploy would race the same project's indexer. With the gate gone, deploys are short, so a brief queue can't pile up. - `max-parallel: 1` stays (parallel deploys of the same project race at the indexer). ## Owner actions (repo settings — not in the diff) 1. **Remove required-reviewers** on `dashboard-agent-staging` and `dashboard-agent-prod` — done. 2. **Add a deployment branch policy** on both environments restricting deployments to `main`. This is the authoritative token guard: `workflow_dispatch` runs the workflow file from the selected ref, so the in-file ancestor check alone can't protect `TRIGGER_ACCESS_TOKEN` (a branch could edit the check out). GitHub enforces the branch policy server-side against `GITHUB_REF` regardless of file contents. With it in place, the workflow only runs (and the token is only exposed) when dispatched from `main`, and the in-file check then constrains the independent `ref` input to merged commits. ## Pile-up root cause The stacking was caused by the **reviewer gate** (runs waited forever, so the queue never drained), not by `cancel-in-progress`. Removing the gate is what fixes it; `cancel-in-progress` stays `false`.
120 lines
5.5 KiB
YAML
120 lines
5.5 KiB
YAML
name: "🤖 Deploy dashboard agent"
|
|
|
|
# Deploys the @internal/dashboard-agent chat.agent to its Trigger.dev project
|
|
# with --skip-promotion, so a deploy never becomes "current" on its own. The
|
|
# consuming app cuts over by pinning DASHBOARD_AGENT_VERSION to the new version.
|
|
# Runs a leg per environment (staging + prod); a push to main that touches the
|
|
# agent or its store deploys both. Version numbers are per-environment, so pin
|
|
# each environment to its own leg's version.
|
|
#
|
|
# The deploy lands dormant, so it doesn't need a reviewer gate: nothing goes live
|
|
# until DASHBOARD_AGENT_VERSION is flipped. The `environment:` below is kept only
|
|
# to scope the deploy token per environment; its required-reviewers rule is
|
|
# removed in repo settings so pushes deploy unattended. workflow_dispatch takes an
|
|
# optional ref (SHA, branch, or tag) to deploy a specific commit instead of head.
|
|
#
|
|
# The deployed ref must be an ancestor of main, so only reviewed, merged code ever
|
|
# runs with the deploy token (the checked-out build + trigger.config.ts execute
|
|
# with it). A push is always on main; a dispatched ref is checked before deploy.
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
paths:
|
|
- "internal-packages/dashboard-agent/**"
|
|
- "internal-packages/dashboard-agent-db/**"
|
|
workflow_dispatch:
|
|
inputs:
|
|
ref:
|
|
description: "Commit SHA, branch, or tag to deploy. Defaults to the ref the workflow runs from."
|
|
required: false
|
|
type: string
|
|
|
|
permissions: {}
|
|
|
|
jobs:
|
|
deploy:
|
|
name: Deploy dashboard agent (${{ matrix.environment }})
|
|
runs-on: ubuntu-latest
|
|
strategy:
|
|
fail-fast: false
|
|
# One environment at a time: parallel deploys of the same project race.
|
|
max-parallel: 1
|
|
matrix:
|
|
environment: [staging, prod]
|
|
# Kept to scope the deploy token per environment. The required-reviewers rule
|
|
# on these environments is removed in repo settings, so this no longer gates.
|
|
environment: dashboard-agent-${{ matrix.environment }}
|
|
concurrency:
|
|
# Queue a superseding deploy behind an in-flight one; do NOT cancel it.
|
|
# Cancelling the runner wouldn't stop the remote build (it finishes
|
|
# server-side), and a second concurrent deploy of the same project would
|
|
# race the indexer. Deploys are short now the gate is gone, so a brief queue
|
|
# is fine and can't pile up.
|
|
group: dashboard-agent-deploy-${{ matrix.environment }}
|
|
cancel-in-progress: false
|
|
permissions:
|
|
contents: read
|
|
env:
|
|
TRIGGER_API_URL: https://api.trigger.dev
|
|
TRIGGER_DASHBOARD_AGENT_PROJECT_REF: ${{ vars.TRIGGER_DASHBOARD_AGENT_PROJECT_REF }}
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
with:
|
|
# push: the pushed commit. workflow_dispatch: the input ref if given,
|
|
# otherwise the head of the ref the run was launched from.
|
|
ref: ${{ github.event.inputs.ref || github.sha }}
|
|
# Full history so the ancestor-of-main check below can find a merge base.
|
|
fetch-depth: 0
|
|
persist-credentials: false
|
|
|
|
- name: Require the ref to be an ancestor of main
|
|
# The deploy token runs the checked-out code, so refuse anything that
|
|
# hasn't landed on main. A push is main's tip (ancestor of itself); this
|
|
# only ever rejects a dispatched, unmerged ref.
|
|
#
|
|
# NOTE: this in-file check only constrains WHICH commit is deployed. It
|
|
# can't protect the token on its own, because workflow_dispatch runs the
|
|
# workflow file from the selected ref. The real guard is the deployment
|
|
# branch policy on the dashboard-agent-* environments (main only), set in
|
|
# repo settings, which GitHub enforces server-side against GITHUB_REF.
|
|
run: |
|
|
set -euo pipefail
|
|
# An explicit `ref:` checkout doesn't create remote-tracking branches,
|
|
# so fetch main before comparing against it.
|
|
git fetch --no-tags --quiet origin +refs/heads/main:refs/remotes/origin/main
|
|
if ! git merge-base --is-ancestor HEAD origin/main; then
|
|
echo "::error::Refusing to deploy $(git rev-parse HEAD): not an ancestor of origin/main. Only merged code can be deployed."
|
|
exit 1
|
|
fi
|
|
echo "$(git rev-parse --short HEAD) is an ancestor of origin/main"
|
|
|
|
- name: Setup pnpm
|
|
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
|
|
with:
|
|
version: 10.33.2
|
|
|
|
- name: Setup node
|
|
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
|
with:
|
|
node-version: 24.18.0
|
|
cache: "pnpm"
|
|
|
|
- name: Install + build the CLI and the agent's deps
|
|
run: |
|
|
set -euo pipefail
|
|
pnpm install --frozen-lockfile
|
|
# Prisma client is needed because the build closure pulls in @trigger.dev/database.
|
|
pnpm run generate
|
|
# Config-time imports the agent's trigger.config.ts needs: defineConfig (sdk), aptGet (build).
|
|
pnpm run build --filter trigger.dev --filter @trigger.dev/build --filter @trigger.dev/sdk
|
|
|
|
- name: Deploy (--skip-promotion)
|
|
working-directory: internal-packages/dashboard-agent
|
|
env:
|
|
TRIGGER_ACCESS_TOKEN: ${{ secrets.TRIGGER_DASHBOARD_AGENT_DEPLOY_TOKEN }}
|
|
# Invoke the built CLI directly (what the workspace .bin/trigger wrapper does),
|
|
# so a not-yet-linked bin after a fresh install can't break the deploy.
|
|
run: node ../../packages/cli-v3/dist/esm/index.js deploy --skip-promotion --env ${{ matrix.environment }}
|