diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml index 49e3a4c..4682181 100644 --- a/.github/workflows/code-quality.yml +++ b/.github/workflows/code-quality.yml @@ -1,8 +1,7 @@ name: Code Quality Checks -# pull_request also covers fork PRs, so lint + type-check gate them too. +# pull_request covers fork PRs too; merges to main are checked by deploy.yml. on: - push: pull_request: permissions: @@ -15,6 +14,8 @@ concurrency: env: CI: true +# Actions pinned to full commit SHAs: tags are mutable and can be moved +# after a compromise. Bump SHA + version comment together. jobs: run-linter: name: Run linter @@ -22,16 +23,16 @@ jobs: timeout-minutes: 15 steps: - name: Check out repository - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 - name: Set up pnpm - uses: pnpm/action-setup@v4 + uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4.4.0 with: version: 10.15 run_install: false - name: Set up Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: '22' cache: pnpm @@ -51,16 +52,16 @@ jobs: timeout-minutes: 5 steps: - name: Check out repository - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 - name: Set up pnpm - uses: pnpm/action-setup@v4 + uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4.4.0 with: version: 10.15 run_install: false - name: Set up Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: '22' cache: pnpm @@ -72,7 +73,7 @@ jobs: run: pnpm install --frozen-lockfile - name: Cache TypeScript build info - uses: actions/cache@v4 + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: path: | .tsbuildinfo diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..45a2ebd --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,190 @@ +name: Deploy + +on: + push: + branches: [main] + +permissions: + contents: read + +env: + CI: true + +# Actions are pinned to full commit SHAs, not @v4 tags: tags are mutable, +# and the deploy job runs these before handing production secrets to the +# deploy step. A moved tag must not run here. Bump SHA + version comment together. +jobs: + lint: + name: Run linter + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - name: Check out repository + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + + - name: Set up pnpm + uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4.4.0 + with: + version: 10.15 + run_install: false + + - name: Set up Node.js + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + with: + node-version: '22' + cache: pnpm + cache-dependency-path: | + pnpm-lock.yaml + **/pnpm-lock.yaml + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Run linter + run: pnpm lint + + typecheck: + name: TypeScript type-check + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: Check out repository + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + + - name: Set up pnpm + uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4.4.0 + with: + version: 10.15 + run_install: false + + - name: Set up Node.js + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + with: + node-version: '22' + cache: pnpm + cache-dependency-path: | + pnpm-lock.yaml + **/pnpm-lock.yaml + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Type check + run: pnpm typecheck + + unit-tests: + name: Unit tests + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Check out repository + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + + - name: Set up pnpm + uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4.4.0 + with: + version: 10.15 + run_install: false + + - name: Set up Node.js + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + with: + node-version: '22' + cache: pnpm + cache-dependency-path: | + pnpm-lock.yaml + **/pnpm-lock.yaml + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Run unit tests + run: pnpm test + + integration-tests: + name: Integration tests + runs-on: ubuntu-latest + # Testcontainers spins up a real Postgres via the Docker daemon that + # ubuntu-latest runners provide out of the box — no `services:` needed. + timeout-minutes: 20 + steps: + - name: Check out repository + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + + - name: Set up pnpm + uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4.4.0 + with: + version: 10.15 + run_install: false + + - name: Set up Node.js + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + with: + node-version: '22' + cache: pnpm + cache-dependency-path: | + pnpm-lock.yaml + **/pnpm-lock.yaml + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Run integration tests + run: pnpm test:integration + + deploy: + name: Deploy to Trigger.dev + needs: [lint, typecheck, unit-tests, integration-tests] + runs-on: ubuntu-latest + environment: Production + timeout-minutes: 20 + # Serialize deploys and never cancel one mid-publish: a newer push waits + # rather than interrupting an in-flight production deploy. + concurrency: + group: deploy-production + cancel-in-progress: false + steps: + - name: Check out repository + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + + - name: Set up pnpm + uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4.4.0 + with: + version: 10.15 + run_install: false + + - name: Set up Node.js + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + with: + node-version: '22' + cache: pnpm + cache-dependency-path: | + pnpm-lock.yaml + **/pnpm-lock.yaml + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + # Skip if a newer commit already landed on the branch, so a slower older + # run can't publish stale code over a newer deploy. + - name: Skip if a newer commit is on the branch + id: freshness + run: | + git fetch --depth=1 origin "$GITHUB_REF_NAME" + latest="$(git rev-parse FETCH_HEAD)" + if [ "$GITHUB_SHA" != "$latest" ]; then + echo "Newer commit $latest on $GITHUB_REF_NAME; skipping stale deploy of $GITHUB_SHA." + echo "skip=true" >> "$GITHUB_OUTPUT" + fi + + - name: Deploy to Trigger.dev production + if: steps.freshness.outputs.skip != 'true' + run: pnpm trigger:deploy + env: + TRIGGER_ACCESS_TOKEN: ${{ secrets.TRIGGER_ACCESS_TOKEN }} + TRIGGER_PROJECT_ID: ${{ secrets.TRIGGER_PROJECT_ID }} + VERCEL_ENV: production + SENTRY_ORG: ${{ secrets.SENTRY_ORG }} + SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }} + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} + NEXT_PUBLIC_SENTRY_DSN: ${{ secrets.NEXT_PUBLIC_SENTRY_DSN }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 51e5949..3109a5f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,8 +1,7 @@ name: Tests -# pull_request also covers fork PRs, which never trigger push upstream. +# pull_request covers fork PRs too; merges to main are checked by deploy.yml. on: - push: pull_request: permissions: @@ -15,6 +14,8 @@ concurrency: env: CI: true +# Actions pinned to full commit SHAs: tags are mutable and can be moved +# after a compromise. Bump SHA + version comment together. jobs: unit-tests: name: Unit tests @@ -22,16 +23,16 @@ jobs: timeout-minutes: 10 steps: - name: Check out repository - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 - name: Set up pnpm - uses: pnpm/action-setup@v4 + uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4.4.0 with: version: 10.15 run_install: false - name: Set up Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: '22' cache: pnpm @@ -53,16 +54,16 @@ jobs: timeout-minutes: 20 steps: - name: Check out repository - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 - name: Set up pnpm - uses: pnpm/action-setup@v4 + uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4.4.0 with: version: 10.15 run_install: false - name: Set up Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: '22' cache: pnpm diff --git a/package.json b/package.json index 2383118..0657df1 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "version": "0.1.0", "scripts": { "trigger:dev": "npx trigger.dev@4.0.6 dev", + "trigger:deploy": "npx trigger.dev@4.0.6 deploy", "dev": "next dev --turbopack", "concurrent:dev": "npx concurrently --kill-others --names \"next,trigger\" --prefix-colors \"yellow,blue\" \"next dev --turbopack\" \"npm run trigger:dev\"", "build": "next build --turbopack",