Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .env.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Dedicated test environment. Loaded by the Vitest and Playwright harnesses
# INSTEAD of .env / .env.local so tests never inherit developer or
# production-like configuration. Contains NO secrets.
#
# - Integration tests (Vitest) derive a UNIQUE, test-named database from
# DATABASE_URI's host (vitest.globalSetup.ts) and drop it around the suite.
# - E2E tests (Playwright) run the app against DATABASE_URI directly.
# Both require a LOCAL MongoDB (CI provides a service container). The isolation
# guard rejects any shared/hosted host (Atlas, DocumentDB, etc.).

NODE_ENV=test
PAYLOAD_SECRET=test-payload-secret-not-for-production

# Local, test-named database host. Overridden by CI to point at the MongoDB
# service container. Never a shared/hosted cluster.
DATABASE_URI=mongodb://127.0.0.1:27017/promisetracker_e2e_test

# Enforce the isolated-database guard at app startup during E2E runs.
PT_ASSERT_TEST_DB=true
22 changes: 22 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
version: 2
updates:
# Continuous software-composition security updates for app dependencies.
- package-ecosystem: npm
directory: "/"
schedule:
interval: weekly
open-pull-requests-limit: 10
groups:
payload:
patterns:
- "@payloadcms/*"
- "payload"
ai-sdk:
patterns:
- "@ai-sdk/*"
- "ai"

- package-ecosystem: github-actions
directory: "/"
schedule:
interval: weekly
14 changes: 14 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
name: CI

on:
pull_request:
push:
branches-ignore: [main]

concurrency:
group: "${{ github.workflow }} @ ${{ github.ref }}"
cancel-in-progress: true

jobs:
verify:
uses: ./.github/workflows/verify.yml

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {}
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
5 changes: 5 additions & 0 deletions .github/workflows/deploy-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,54 +18,59 @@
SENTRY_ENVIRONMENT: "development"

jobs:
# Mandatory verification gate. Deployment cannot run when this fails.
verify:
uses: ./.github/workflows/verify.yml

deploy:

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {}
needs: verify
runs-on: ${{ matrix.os }}
strategy:
matrix:
node-version: [20.16]
os: [ubuntu-latest]
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 1

# Add support for more platforms with QEMU (optional)
- name: Set up QEMU
uses: docker/setup-qemu-action@v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Login to DockerHub
uses: docker/login-action@v3
with:
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
username: ${{ secrets.DOCKER_HUB_USERNAME }}

- name: Build Docker image
uses: docker/build-push-action@v6
with:
build-args: |
NEXT_PUBLIC_APP_URL=${{ env.NEXT_PUBLIC_APP_URL }}
NEXT_PUBLIC_SENTRY_DSN=${{ secrets.SENTRY_DSN }}
SENTRY_ENVIRONMENT=${{ env.SENTRY_ENVIRONMENT }}
cache-from: type=gha
cache-to: type=gha,mode=max
context: .
platforms: linux/arm64
push: true
secrets: |
"database_uri=${{ secrets.DATABASE_URI }}"
"payload_secret=${{ secrets.PAYLOAD_SECRET }}"
"sentry_auth_token=${{ secrets.SENTRY_AUTH_TOKEN }}"
"sentry_org=${{ secrets.SENTRY_ORG }}"
"sentry_project=${{ secrets.SENTRY_PROJECT }}"
tags: "${{ env.IMAGE_NAME }}:${{ github.sha }}"

- name: Push to Dokku
uses: dokku/github-action@v1.7.0
with:
git_remote_url: ${{ env.DOKKU_REMOTE_URL }}/${{ env.APP_NAME }}
ssh_private_key: ${{ secrets.SSH_PRIVATE_KEY }}
deploy_docker_image: ${{ env.IMAGE_NAME }}:${{ github.sha }}

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}
102 changes: 102 additions & 0 deletions .github/workflows/verify.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
name: Verify

# Reusable verification pipeline. Called by CI (on pull requests) and by the
# deploy workflow (which must not run when this fails).
on:
workflow_call:

env:
NODE_VERSION: "22.12.0"
# Isolated, local, test-named database — never a shared/hosted cluster.
DATABASE_URI: mongodb://127.0.0.1:27017/promisetracker_ci_test
PAYLOAD_SECRET: test-payload-secret-not-for-production
PT_ASSERT_TEST_DB: "true"
NODE_OPTIONS: "--no-deprecation"

jobs:
verify:
name: Lint, types, tests
runs-on: ubuntu-latest
services:
mongodb:
image: mongo:7
ports:
- 27017:27017
options: >-
--health-cmd "mongosh --quiet --eval 'db.runCommand({ ping: 1 })'"
--health-interval 10s
--health-timeout 5s
--health-retries 10
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up pnpm
uses: pnpm/action-setup@v4

- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: pnpm

# Frozen installation: fail if the lockfile is out of date.
- name: Install dependencies
run: pnpm install --frozen-lockfile

# Generated-artifact drift: regenerate committed types/import map and
# fail if anything changed.
- name: Check generated artifacts are up to date
run: |
pnpm run generate:all
if ! git diff --exit-code; then
echo "::error::Generated artifacts are out of date. Run 'pnpm run generate:all' and commit the result."
exit 1
fi

- name: Lint
run: pnpm run lint

- name: Type-check
run: pnpm run typecheck

- name: Integration tests
run: pnpm run test:int

- name: Install Playwright browsers
run: pnpm exec playwright install --with-deps chromium

- name: Critical E2E smoke tests
run: pnpm run test:e2e

- name: Upload Playwright report
if: ${{ !cancelled() }}
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: playwright-report/
retention-days: 7
if-no-files-found: ignore

security:

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
name: Dependency security scan
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up pnpm
uses: pnpm/action-setup@v4

- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: pnpm

- name: Install dependencies
run: pnpm install --frozen-lockfile

# Software-composition analysis: fail on high/critical advisories.
- name: Audit dependencies
run: pnpm audit --audit-level high --prod

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"generate:ai-model-ids": "node scripts/generate-ai-provider-model-ids.mjs",
"generate:all": "pnpm run generate:importmap && pnpm run generate:types",
"lint": "cross-env NODE_OPTIONS=--no-deprecation next lint",
"typecheck": "tsc --noEmit",
"payload": "cross-env NODE_OPTIONS=--no-deprecation payload",
"migrate": "NODE_OPTIONS=--no-deprecation payload migrate",
"start": "cross-env NODE_OPTIONS=--no-deprecation next start",
Expand Down Expand Up @@ -97,6 +98,7 @@
"eslint": "^9.34.0",
"eslint-config-next": "15.4.6",
"jsdom": "26.1.0",
"mongodb": "^7.5.0",
"playwright": "1.55.0",
"playwright-core": "1.55.0",
"prettier": "^3.6.2",
Expand Down
27 changes: 17 additions & 10 deletions playwright.config.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,29 @@
import { defineConfig, devices } from '@playwright/test'
import { config as loadEnv } from 'dotenv'

/**
* Read environment variables from file.
* https://github.com/motdotla/dotenv
*/
import 'dotenv/config'
// Load the DEDICATED test environment (never .env / .env.local) so E2E runs
// use isolated configuration. CI overrides DATABASE_URI to point at its
// MongoDB service container.
loadEnv({ path: ".env.test" })

/**
* See https://playwright.dev/docs/test-configuration.
*/
export default defineConfig({
testDir: './tests/e2e',
// Enforce the isolated-DB guard and reset the database before the suite.
globalSetup: './tests/e2e/globalSetup.ts',
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: !!process.env.CI,
/* Retry on CI only */
retries: process.env.CI ? 2 : 0,
/* Opt out of parallel tests on CI. */
workers: process.env.CI ? 1 : undefined,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: 'html',
reporter: process.env.CI ? [['github'], ['html', { open: 'never' }]] : 'html',
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Base URL to use in actions like `await page.goto('/')`. */
// baseURL: 'http://localhost:3000',

baseURL: 'http://localhost:3000',
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'on-first-retry',
},
Expand All @@ -35,7 +35,14 @@ export default defineConfig({
],
webServer: {
command: 'pnpm dev',
reuseExistingServer: true,
reuseExistingServer: !process.env.CI,
url: 'http://localhost:3000',
// Pass the isolated test database and secret through to the app process.
env: {
DATABASE_URI: process.env.DATABASE_URI ?? '',
PAYLOAD_SECRET: process.env.PAYLOAD_SECRET ?? '',
PT_ASSERT_TEST_DB: 'true',
},
timeout: 120 * 1000,
},
})
Loading
Loading