Skip to content

ci: stricter biome (lint+format+imports) and AGENTS commands #40

ci: stricter biome (lint+format+imports) and AGENTS commands

ci: stricter biome (lint+format+imports) and AGENTS commands #40

Workflow file for this run

name: CI / Deploy
# Pipeline:
# PR (any branch) -> test
# push dev -> test -> deploy(dev) -> e2e against https://dev.nn.nshen.net
# push main -> test -> deploy(prod) -> e2e against https://nn.nshen.net (smoke)
#
# A failed test job blocks deploy. A failed e2e job runs after deploy is
# already live (dev/prod env contract: dev tolerates broken code, prod
# alarms on regression).
#
# Required secrets (see README "Required Secrets"):
# CLOUDFLARE_API_TOKEN, ALCHEMY_STATE_TOKEN, CLOUDFLARE_EMAIL,
# ENV_SERVER_DEV / ENV_SERVER_PROD, ENV_WEB_DEV / ENV_WEB_PROD
on:
push:
branches: [main, dev]
pull_request:
branches: [main, dev]
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
jobs:
test:
name: Lint + Unit + Build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
run_install: false
- uses: actions/setup-node@v4
with:
node-version: 24
cache: pnpm
- name: Install dependencies
run: pnpm install
- name: Lint + format + import-sort (biome ci)
run: pnpm exec biome ci
- name: Type-check
run: pnpm typecheck
- name: Unit / integration tests
run: pnpm test
# Note: tanstack build is exercised by `alchemy deploy` in the deploy
# job. Running `vite build` here would fail because alchemy's vite
# plugin requires .alchemy/local/wrangler.jsonc, generated only by
# alchemy dev/deploy.
deploy:
name: Deploy (${{ github.ref == 'refs/heads/main' && 'prod' || 'dev' }})
needs: test
if: github.event_name == 'push'
runs-on: ubuntu-latest
permissions:
contents: read
outputs:
base_url: ${{ steps.url.outputs.base_url }}
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
run_install: false
- uses: actions/setup-node@v4
with:
node-version: 24
cache: pnpm
- name: Install dependencies
run: pnpm install
- name: Inject env files (dev)
if: github.ref == 'refs/heads/dev'
run: |
echo "${{ secrets.ENV_SERVER_DEV }}" > apps/server/.dev.env
echo "${{ secrets.ENV_WEB_DEV }}" > apps/tanstack/.dev.env
- name: Inject env files (prod)
if: github.ref == 'refs/heads/main'
run: |
echo "${{ secrets.ENV_SERVER_PROD }}" > apps/server/.prod.env
echo "${{ secrets.ENV_WEB_PROD }}" > apps/tanstack/.prod.env
- name: Deploy dev
if: github.ref == 'refs/heads/dev'
run: |
pnpm --filter server deploy:dev
pnpm --filter tanstack deploy:dev
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_EMAIL: ${{ secrets.CLOUDFLARE_EMAIL }}
ALCHEMY_STATE_TOKEN: ${{ secrets.ALCHEMY_STATE_TOKEN }}
- name: Deploy prod
if: github.ref == 'refs/heads/main'
run: |
pnpm --filter server deploy:prod
pnpm --filter tanstack deploy:prod
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_EMAIL: ${{ secrets.CLOUDFLARE_EMAIL }}
ALCHEMY_STATE_TOKEN: ${{ secrets.ALCHEMY_STATE_TOKEN }}
- name: Resolve frontend URL
id: url
run: |
if [ "${{ github.ref }}" = "refs/heads/main" ]; then
echo "base_url=https://nn.nshen.net" >> $GITHUB_OUTPUT
else
echo "base_url=https://dev.nn.nshen.net" >> $GITHUB_OUTPUT
fi
e2e:
name: E2E (deployed)
needs: deploy
if: github.event_name == 'push'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
run_install: false
- uses: actions/setup-node@v4
with:
node-version: 24
cache: pnpm
- name: Install dependencies
run: pnpm install
- name: Install Playwright browsers
run: pnpm --filter tanstack exec playwright install --with-deps chromium
- name: Run E2E against deployed URL
run: pnpm --filter tanstack test:e2e
env:
PLAYWRIGHT_BASE_URL: ${{ needs.deploy.outputs.base_url }}
- name: Upload Playwright report on failure
if: failure()
uses: actions/upload-artifact@v4
with:
name: playwright-report-${{ github.run_id }}
path: apps/tanstack/playwright-report/
retention-days: 7