Files
kandev/.github/workflows/preview-env.yml
T
2026-05-16 20:07:57 +01:00

174 lines
6.8 KiB
YAML

name: Preview Environment
# Dual triggers — mirrors the claude-code-review.yml pattern exactly:
# pull_request: same-repo PRs (secrets available, safe to run on PR head)
# pull_request_target: fork PRs (secrets available via base repo, gated by allowlist)
# Exactly one job runs per PR event based on event_name AND head repo identity.
on:
pull_request:
types: [opened, synchronize, reopened, closed]
pull_request_target:
types: [opened, synchronize, reopened, closed]
jobs:
# -----------------------------------------------------------------------
# Deploy / update preview (same-repo PRs)
# Always runs for same-repo pushes that are not a close event.
# -----------------------------------------------------------------------
deploy-same-repo:
if: >
github.event.action != 'closed' &&
github.event_name == 'pull_request' &&
github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v6
with:
ref: ${{ github.event.pull_request.head.sha }}
- uses: actions/setup-go@v6
with:
go-version-file: apps/backend/go.mod
cache-dependency-path: apps/backend/go.sum
- uses: pnpm/action-setup@v5
with:
version: "9.15.9"
- uses: actions/setup-node@v6
with:
node-version: "24"
- name: Install frontend dependencies
run: pnpm -C apps install --frozen-lockfile
- name: Deploy preview environment
working-directory: apps/backend
run: go run ./cmd/preview deploy --pr "$PR" --sha "$SHA" --repo "$REPO" --skip-web-install
env:
PR: ${{ github.event.pull_request.number }}
SHA: ${{ github.event.pull_request.head.sha }}
REPO: ${{ github.repository }}
SPRITES_API_TOKEN: ${{ secrets.SPRITES_API_TOKEN }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# -----------------------------------------------------------------------
# Deploy / update preview (fork PRs, gated by allowlist)
# Uses pull_request_target so secrets are available.
# Gated by: OWNER/MEMBER/COLLABORATOR author_association,
# `safe-to-test` label (maintainer opt-in), or
# PREVIEW_ENV_ALLOWLIST repo variable (JSON array of logins).
# -----------------------------------------------------------------------
deploy-fork:
if: >
github.event.action != 'closed' &&
github.event_name == 'pull_request_target' &&
github.event.pull_request.head.repo.full_name != github.repository &&
(contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.pull_request.author_association) ||
contains(github.event.pull_request.labels.*.name, 'safe-to-test') ||
(vars.PREVIEW_ENV_ALLOWLIST != '' && contains(fromJSON(vars.PREVIEW_ENV_ALLOWLIST), github.event.pull_request.user.login)))
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v6
with:
# pull_request_target checks out base by default; explicitly use PR head.
# Safe: the preview CLI only reads files to build artifacts and posts comments.
# NOTE: The allowlist gate (OWNER/MEMBER/COLLABORATOR, safe-to-test, PREVIEW_ENV_ALLOWLIST)
# authorizes running the fork's cmd/preview code with SPRITES_API_TOKEN and GH_TOKEN.
# The safe-to-test label must be re-applied after each new push (see strip-safe-to-test job).
repository: ${{ github.event.pull_request.head.repo.full_name }}
ref: ${{ github.event.pull_request.head.sha }}
- uses: actions/setup-go@v6
with:
go-version-file: apps/backend/go.mod
cache-dependency-path: apps/backend/go.sum
- uses: pnpm/action-setup@v5
with:
version: "9.15.9"
- uses: actions/setup-node@v6
with:
node-version: "24"
- name: Install frontend dependencies
run: pnpm -C apps install --frozen-lockfile
- name: Deploy preview environment
working-directory: apps/backend
run: go run ./cmd/preview deploy --pr "$PR" --sha "$SHA" --repo "$REPO" --skip-web-install
env:
PR: ${{ github.event.pull_request.number }}
SHA: ${{ github.event.pull_request.head.sha }}
REPO: ${{ github.repository }}
SPRITES_API_TOKEN: ${{ secrets.SPRITES_API_TOKEN }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# -----------------------------------------------------------------------
# Strip safe-to-test label on new pushes to fork PRs.
# Prevents a once-approved fork from running updated (potentially malicious)
# code on subsequent pushes without a fresh maintainer re-approval.
# -----------------------------------------------------------------------
strip-safe-to-test:
if: >
github.event_name == 'pull_request_target' &&
github.event.action == 'synchronize' &&
github.event.pull_request.head.repo.full_name != github.repository &&
contains(github.event.pull_request.labels.*.name, 'safe-to-test')
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- uses: actions/github-script@v7
with:
script: |
await github.rest.issues.removeLabel({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.pull_request.number,
name: 'safe-to-test',
});
# -----------------------------------------------------------------------
# Cleanup (all closed PRs — no allowlist check)
# The sprite must be destroyed regardless of how the PR was closed or
# whether a preview was ever successfully deployed. Handles gracefully
# if the sprite does not exist (was never deployed or already destroyed).
# Runs only on pull_request_target to avoid duplicate runs — both events
# fire `closed`, but only pull_request_target has secrets for all PRs.
# -----------------------------------------------------------------------
cleanup-preview:
if: >
github.event.action == 'closed' &&
github.event_name == 'pull_request_target'
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version-file: apps/backend/go.mod
cache-dependency-path: apps/backend/go.sum
- name: Destroy preview environment
working-directory: apps/backend
run: go run ./cmd/preview cleanup --pr "$PR" --repo "$REPO"
env:
PR: ${{ github.event.pull_request.number }}
REPO: ${{ github.repository }}
SPRITES_API_TOKEN: ${{ secrets.SPRITES_API_TOKEN }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}