@next-friday/create-app 2.1.0 → 2.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/package.json +3 -3
- package/templates/base/.github/labeler.yml +3 -3
- package/templates/base/.github/workflows/codeql.yml +47 -0
- package/templates/base/.github/workflows/dependency-review.yml +25 -0
- package/templates/base/.github/workflows/scorecard.yml +42 -0
- package/templates/base/.github/workflows/secret-scan.yml +1 -1
- package/templates/base/.github/workflows/zizmor.yml +4 -5
- package/templates/nestjs/CONTRIBUTING.md +8 -8
- package/templates/nestjs/README.md +13 -13
- package/templates/nestjs/package.json +2 -2
- package/templates/nextjs/CONTRIBUTING.md +8 -8
- package/templates/nextjs/README.md +3 -3
- package/templates/nextjs/package.json +2 -2
- package/templates/nodejs/CONTRIBUTING.md +8 -8
- package/templates/nodejs/README.md +11 -11
- package/templates/nodejs/package.json +2 -2
- package/templates/payload/CONTRIBUTING.md +8 -8
- package/templates/payload/README.md +3 -3
- package/templates/payload/package.json +2 -2
- package/templates/turborepo/.github/scripts/affected-apps.sh +31 -0
- package/templates/turborepo/.github/workflows/ci.yml +104 -16
- package/templates/turborepo/.github/workflows/deploy.yml +69 -4
- package/templates/turborepo/.lintstagedrc.json +2 -1
- package/templates/turborepo/CONTRIBUTING.md +8 -8
- package/templates/turborepo/README.md +29 -17
- package/templates/turborepo/package.json +9 -9
- package/templates/turborepo/pnpm-workspace.yaml +1 -0
- package/templates/turborepo/turbo.json +2 -7
- package/templates/turborepo/.github/labeler.yml +0 -25
package/README.md
CHANGED
|
@@ -30,7 +30,7 @@ Every stack is built from [`templates/base`](templates/base), which holds the sh
|
|
|
30
30
|
|
|
31
31
|
- **Code quality:** TypeScript, ESLint and Prettier with the Next Friday configs, Vitest with coverage, fast-check, Knip, dependency-cruiser, editorconfig-checker, and sort-package-json.
|
|
32
32
|
- **Git hooks:** Husky runs gitleaks and lint-staged before each commit, commitlint on each message, and `pnpm check` before each push. Commits and pushes to `main` are blocked after the first one.
|
|
33
|
-
- **CI:** GitHub Actions run the static checks, tests with patch coverage, the build, end-to-end tests for Next.js and Payload, a container build and vulnerability scan, Semgrep, OSV-Scanner, gitleaks, TruffleHog, zizmor, and pull request title validation. Dependabot keeps dependencies current.
|
|
33
|
+
- **CI:** GitHub Actions run the static checks, tests with patch coverage, the build, end-to-end tests for Next.js and Payload, a container build and vulnerability scan, Semgrep, OSV-Scanner, CodeQL, dependency review, OpenSSF Scorecard, gitleaks, TruffleHog, zizmor, and pull request title validation. Dependabot keeps dependencies current.
|
|
34
34
|
- **Deployment:** a `Dockerfile` that runs as the `node` user, and a Railway workflow that promotes one commit through `dev`, `uat`, and `prod`.
|
|
35
35
|
- **Agent rules:** shared rules in `.agents/rules`, with hooks that enforce them in Claude Code and Cursor, and a Cursor Bugbot configuration.
|
|
36
36
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@next-friday/create-app",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.2",
|
|
4
4
|
"description": "Create a Next Friday project from a template.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"create",
|
|
@@ -48,11 +48,11 @@
|
|
|
48
48
|
"jiti": "2.7.0",
|
|
49
49
|
"knip": "6.43.0",
|
|
50
50
|
"lint-staged": "17.6.0",
|
|
51
|
-
"pkg-pr-new": "0.0.
|
|
51
|
+
"pkg-pr-new": "0.0.92",
|
|
52
52
|
"prettier": "3.9.10",
|
|
53
53
|
"publint": "0.3.25",
|
|
54
54
|
"sort-package-json": "4.1.0",
|
|
55
|
-
"tsdown": "0.23.
|
|
55
|
+
"tsdown": "0.23.2",
|
|
56
56
|
"typescript": "6.0.3",
|
|
57
57
|
"vitest": "5.0.5"
|
|
58
58
|
},
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
name: CodeQL
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request: {}
|
|
5
|
+
push:
|
|
6
|
+
branches:
|
|
7
|
+
- main
|
|
8
|
+
schedule:
|
|
9
|
+
- cron: "18 4 * * 1"
|
|
10
|
+
|
|
11
|
+
permissions: {} # No token beyond the job grants below.
|
|
12
|
+
|
|
13
|
+
concurrency:
|
|
14
|
+
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
|
|
15
|
+
cancel-in-progress: true
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
analyze:
|
|
19
|
+
name: Analyze (${{ matrix.language }})
|
|
20
|
+
if: github.event.repository.visibility == 'public' || vars.CODE_SCANNING == 'true'
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
timeout-minutes: 30
|
|
23
|
+
permissions:
|
|
24
|
+
actions: read # Needed to fetch CodeQL packs used by this workflow.
|
|
25
|
+
contents: read # Needed to check out the repository.
|
|
26
|
+
packages: read # Needed to fetch internal or private CodeQL packs.
|
|
27
|
+
security-events: write # Needed to upload SARIF results to GitHub code scanning.
|
|
28
|
+
strategy:
|
|
29
|
+
fail-fast: false
|
|
30
|
+
matrix:
|
|
31
|
+
language:
|
|
32
|
+
- actions
|
|
33
|
+
- javascript-typescript
|
|
34
|
+
steps:
|
|
35
|
+
- name: Checkout repository
|
|
36
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
|
37
|
+
with:
|
|
38
|
+
persist-credentials: false
|
|
39
|
+
- name: Initialize CodeQL
|
|
40
|
+
uses: github/codeql-action/init@1fc58d04a4d4374f921bdac573c3811c240f9c26 # v4.39.2
|
|
41
|
+
with:
|
|
42
|
+
languages: ${{ matrix.language }}
|
|
43
|
+
build-mode: none
|
|
44
|
+
- name: Perform CodeQL analysis
|
|
45
|
+
uses: github/codeql-action/analyze@1fc58d04a4d4374f921bdac573c3811c240f9c26 # v4.39.2
|
|
46
|
+
with:
|
|
47
|
+
category: "/language:${{ matrix.language }}"
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
name: Dependency Review
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request: {}
|
|
5
|
+
|
|
6
|
+
permissions: {} # No token beyond the job grants below.
|
|
7
|
+
|
|
8
|
+
concurrency:
|
|
9
|
+
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
|
|
10
|
+
cancel-in-progress: true
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
dependency-review:
|
|
14
|
+
name: Dependency Review
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
timeout-minutes: 10
|
|
17
|
+
permissions:
|
|
18
|
+
contents: read # Needed to check out the repository.
|
|
19
|
+
steps:
|
|
20
|
+
- name: Checkout repository
|
|
21
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
|
22
|
+
with:
|
|
23
|
+
persist-credentials: false
|
|
24
|
+
- name: Review dependency changes
|
|
25
|
+
uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
name: Scorecard
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
branch_protection_rule: {}
|
|
5
|
+
push:
|
|
6
|
+
branches:
|
|
7
|
+
- main
|
|
8
|
+
schedule:
|
|
9
|
+
- cron: "27 4 * * 1"
|
|
10
|
+
|
|
11
|
+
permissions: {} # No token beyond the job grants below.
|
|
12
|
+
|
|
13
|
+
concurrency:
|
|
14
|
+
group: ${{ github.workflow }}-${{ github.run_id }}
|
|
15
|
+
cancel-in-progress: true
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
analysis:
|
|
19
|
+
name: Scorecard analysis
|
|
20
|
+
if: (github.event.repository.visibility == 'public' || vars.CODE_SCANNING == 'true') && (github.event.repository.default_branch == github.ref_name || github.event_name == 'branch_protection_rule')
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
timeout-minutes: 15
|
|
23
|
+
permissions:
|
|
24
|
+
actions: read # Needed to inspect workflow history for Scorecard checks.
|
|
25
|
+
contents: read # Needed to check out the repository.
|
|
26
|
+
id-token: write # Needed to publish Scorecard results with OIDC.
|
|
27
|
+
security-events: write # Needed to upload SARIF results to GitHub code scanning.
|
|
28
|
+
steps:
|
|
29
|
+
- name: Checkout repository
|
|
30
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
|
31
|
+
with:
|
|
32
|
+
persist-credentials: false
|
|
33
|
+
- name: Run analysis
|
|
34
|
+
uses: ossf/scorecard-action@fa7117db83c52d787ae5a89f350999701505d78e # v2.5.0
|
|
35
|
+
with:
|
|
36
|
+
results_file: results.sarif
|
|
37
|
+
results_format: sarif
|
|
38
|
+
publish_results: true
|
|
39
|
+
- name: Upload to code scanning
|
|
40
|
+
uses: github/codeql-action/upload-sarif@1fc58d04a4d4374f921bdac573c3811c240f9c26 # v4.39.2
|
|
41
|
+
with:
|
|
42
|
+
sarif_file: results.sarif
|
|
@@ -31,7 +31,7 @@ jobs:
|
|
|
31
31
|
fetch-depth: 0
|
|
32
32
|
persist-credentials: false
|
|
33
33
|
- name: Scan commits for secrets
|
|
34
|
-
uses: trufflesecurity/trufflehog@
|
|
34
|
+
uses: trufflesecurity/trufflehog@31ce31ee1abbcc7dc8eee5764c17121c3044c8b2 # v3.99.7
|
|
35
35
|
with:
|
|
36
36
|
extra_args: --results=verified,unknown
|
|
37
37
|
- name: Install verified gitleaks
|
|
@@ -11,7 +11,7 @@ on:
|
|
|
11
11
|
paths:
|
|
12
12
|
- ".github/**"
|
|
13
13
|
|
|
14
|
-
permissions: {}
|
|
14
|
+
permissions: {} # No token beyond the job grants below.
|
|
15
15
|
|
|
16
16
|
concurrency:
|
|
17
17
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
|
|
@@ -23,17 +23,16 @@ jobs:
|
|
|
23
23
|
runs-on: ubuntu-latest
|
|
24
24
|
timeout-minutes: 10
|
|
25
25
|
permissions:
|
|
26
|
-
contents: read
|
|
26
|
+
contents: read # Needed to check out the repository.
|
|
27
27
|
steps:
|
|
28
28
|
- name: Checkout repository
|
|
29
29
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
|
30
30
|
with:
|
|
31
31
|
persist-credentials: false
|
|
32
32
|
- name: Analyze GitHub Actions security
|
|
33
|
-
uses: zizmorcore/zizmor-action@
|
|
33
|
+
uses: zizmorcore/zizmor-action@45d7276c9ca790aabd705e31ca99c6185c3e76dd # v0.8.1
|
|
34
34
|
with:
|
|
35
35
|
inputs: .github
|
|
36
|
-
version: 1.30.1
|
|
37
36
|
persona: pedantic
|
|
38
37
|
advanced-security: false
|
|
39
38
|
|
|
@@ -42,7 +41,7 @@ jobs:
|
|
|
42
41
|
runs-on: ubuntu-latest
|
|
43
42
|
timeout-minutes: 10
|
|
44
43
|
permissions:
|
|
45
|
-
contents: read
|
|
44
|
+
contents: read # Needed to check out the repository.
|
|
46
45
|
env:
|
|
47
46
|
ACTIONLINT_VERSION: 1.7.12
|
|
48
47
|
ACTIONLINT_SHA256: 8aca8db96f1b94770f1b0d72b6dddcb1ebb8123cb3712530b08cc387b349a3d8
|
|
@@ -28,14 +28,14 @@ pnpm install
|
|
|
28
28
|
|
|
29
29
|
Run the narrowest check that covers the change while you work. Git hooks run these checks:
|
|
30
30
|
|
|
31
|
-
| Hook | Checks
|
|
32
|
-
| :----------------- |
|
|
33
|
-
| `pre-commit` | blocks commits on `main` except the first commit of a new repository, gitleaks on staged changes, `pnpm rules:check`, and lint-staged on staged files
|
|
34
|
-
| `commit-msg` | commitlint
|
|
35
|
-
| `pre-merge-commit` | blocks merge commits on `main`
|
|
36
|
-
| `pre-push` | blocks pushes to `main` except the push that creates it, then `pnpm check`: the static checks
|
|
37
|
-
|
|
38
|
-
- **Scans:** CI also runs coverage, the build, the container image, and the secret, SAST, and
|
|
31
|
+
| Hook | Checks |
|
|
32
|
+
| :----------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
33
|
+
| `pre-commit` | blocks commits on `main` except the first commit of a new repository, gitleaks on staged changes, `pnpm rules:check`, and lint-staged on staged files |
|
|
34
|
+
| `commit-msg` | commitlint |
|
|
35
|
+
| `pre-merge-commit` | blocks merge commits on `main` |
|
|
36
|
+
| `pre-push` | blocks pushes to `main` except the push that creates it, then `pnpm check`: the static checks, the unit tests related to your changes, and the production build |
|
|
37
|
+
|
|
38
|
+
- **Scans:** CI also runs coverage, the build, the container image, and the secret, SAST, OSV, CodeQL, dependency-review, and OpenSSF Scorecard scans. Dependabot and OSV-Scanner report vulnerable dependencies.
|
|
39
39
|
- **End-to-end tests:** run `pnpm test:e2e` when you change endpoints. CI runs it on every pull request that is ready for review and changes code.
|
|
40
40
|
- **Coverage:** CI requires 95% line coverage on changed lines in `src/`, except `main.ts` and `*.module.ts`, which hold only wiring. Keep logic out of them, as `src/port.ts` does for `main.ts`. CI does not gate branch coverage, because NestJS decorators emit metadata branches that never run.
|
|
41
41
|
|
|
@@ -33,19 +33,19 @@ The server listens on the port set by `PORT` in `.env`, which the CLI created fr
|
|
|
33
33
|
|
|
34
34
|
## Scripts
|
|
35
35
|
|
|
36
|
-
| Script | Purpose
|
|
37
|
-
| :----------------- |
|
|
38
|
-
| `pnpm start` | Start once, without watch mode; loads `.env` when present
|
|
39
|
-
| `pnpm start:dev` | Start in watch mode; loads `.env` when present
|
|
40
|
-
| `pnpm start:debug` | Start in watch mode with the Node.js inspector; loads `.env` when present
|
|
41
|
-
| `pnpm build` | Compile to `dist/`
|
|
42
|
-
| `pnpm start:prod` | Run `dist/main.js`; the environment sets `PORT`
|
|
43
|
-
| `pnpm test` | Run unit tests (`src/**/*.test.ts`)
|
|
44
|
-
| `pnpm test:e2e` | Run end-to-end tests (`test/**/*.e2e.test.ts`)
|
|
45
|
-
| `pnpm check` | Run the static checks
|
|
46
|
-
| `pnpm verify` | Run the static checks
|
|
47
|
-
|
|
48
|
-
CI also runs the build, the end-to-end tests, the container image scan, and the security scans.
|
|
36
|
+
| Script | Purpose |
|
|
37
|
+
| :----------------- | :----------------------------------------------------------------------------------------------------- |
|
|
38
|
+
| `pnpm start` | Start once, without watch mode; loads `.env` when present |
|
|
39
|
+
| `pnpm start:dev` | Start in watch mode; loads `.env` when present |
|
|
40
|
+
| `pnpm start:debug` | Start in watch mode with the Node.js inspector; loads `.env` when present |
|
|
41
|
+
| `pnpm build` | Compile to `dist/` |
|
|
42
|
+
| `pnpm start:prod` | Run `dist/main.js`; the environment sets `PORT` |
|
|
43
|
+
| `pnpm test` | Run unit tests (`src/**/*.test.ts`) |
|
|
44
|
+
| `pnpm test:e2e` | Run end-to-end tests (`test/**/*.e2e.test.ts`) |
|
|
45
|
+
| `pnpm check` | Run the static checks, the unit tests for changed code, and the production build (the `pre-push` hook) |
|
|
46
|
+
| `pnpm verify` | Run the static checks, all unit tests with coverage, and the production build |
|
|
47
|
+
|
|
48
|
+
CI also runs the build, the end-to-end tests, the container image scan, and the security scans, including CodeQL, dependency review, and OpenSSF Scorecard. CodeQL and Scorecard run on public repositories, and on private ones when the `CODE_SCANNING` repository variable is `true`.
|
|
49
49
|
|
|
50
50
|
## Container
|
|
51
51
|
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"type": "module",
|
|
13
13
|
"scripts": {
|
|
14
14
|
"build": "tsc -p tsconfig.build.json",
|
|
15
|
-
"check": "pnpm verify:static && pnpm test:changed",
|
|
15
|
+
"check": "pnpm verify:static && pnpm test:changed && pnpm build",
|
|
16
16
|
"clean": "rm -rf dist coverage *.tsbuildinfo .eslintcache node_modules/.cache",
|
|
17
17
|
"debug:build": "tsc -p tsconfig.build.json --watch --preserveWatchOutput",
|
|
18
18
|
"debug:serve": "node --env-file-if-exists=.env --inspect --watch dist/main",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"test:e2e": "vitest run --config vitest.e2e.config.ts",
|
|
43
43
|
"test:watch": "vitest",
|
|
44
44
|
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
45
|
-
"verify": "pnpm verify:static && pnpm test:coverage",
|
|
45
|
+
"verify": "pnpm verify:static && pnpm test:coverage && pnpm build",
|
|
46
46
|
"verify:static": "pnpm suppressions:check && pnpm rules:check && pnpm gates:check && pnpm lint:check && pnpm format:check && pnpm sort-package-json:check && pnpm peers:check && pnpm typecheck && pnpm knip && pnpm depcruise"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
@@ -28,14 +28,14 @@ pnpm install
|
|
|
28
28
|
|
|
29
29
|
Run the narrowest check that covers the change while you work. Git hooks run these checks:
|
|
30
30
|
|
|
31
|
-
| Hook | Checks
|
|
32
|
-
| :----------------- |
|
|
33
|
-
| `pre-commit` | blocks commits on `main` except the first commit of a new repository, gitleaks on staged changes, `pnpm rules:check`, and lint-staged on staged files
|
|
34
|
-
| `commit-msg` | commitlint
|
|
35
|
-
| `pre-merge-commit` | blocks merge commits on `main`
|
|
36
|
-
| `pre-push` | blocks pushes to `main` except the push that creates it, then `pnpm check`: the static checks
|
|
37
|
-
|
|
38
|
-
- **Scans:** CI also runs coverage, the build, the container image, and the secret, SAST, and
|
|
31
|
+
| Hook | Checks |
|
|
32
|
+
| :----------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
33
|
+
| `pre-commit` | blocks commits on `main` except the first commit of a new repository, gitleaks on staged changes, `pnpm rules:check`, and lint-staged on staged files |
|
|
34
|
+
| `commit-msg` | commitlint |
|
|
35
|
+
| `pre-merge-commit` | blocks merge commits on `main` |
|
|
36
|
+
| `pre-push` | blocks pushes to `main` except the push that creates it, then `pnpm check`: the static checks, the unit tests related to your changes, and the production build |
|
|
37
|
+
|
|
38
|
+
- **Scans:** CI also runs coverage, the build, the container image, and the secret, SAST, OSV, CodeQL, dependency-review, and OpenSSF Scorecard scans. Dependabot and OSV-Scanner report vulnerable dependencies.
|
|
39
39
|
- **End-to-end tests:** run `pnpm test:e2e` when you change pages. It starts the development server unless one is already running. Run `pnpm exec playwright install chromium` once before the first run. CI runs it against the production build on every pull request that is ready for review and changes code.
|
|
40
40
|
- **Coverage:** CI requires 95% branch coverage on changed lines in `app/`, except `layout.tsx`.
|
|
41
41
|
|
|
@@ -38,10 +38,10 @@ pnpm dev
|
|
|
38
38
|
| `pnpm start` | Serve the standalone production build after `pnpm build`. The build copies `.env`, so rebuild after you change it |
|
|
39
39
|
| `pnpm test` | Run unit tests |
|
|
40
40
|
| `pnpm test:e2e` | Run Playwright end-to-end tests (`test/**/*.e2e.test.ts`) |
|
|
41
|
-
| `pnpm check` | Run the static checks
|
|
42
|
-
| `pnpm verify` | Run the static checks
|
|
41
|
+
| `pnpm check` | Run the static checks, the unit tests for changed code, and the production build (the `pre-push` hook) |
|
|
42
|
+
| `pnpm verify` | Run the static checks, all unit tests with coverage, and the production build |
|
|
43
43
|
|
|
44
|
-
CI also runs the build, the end-to-end tests, the container image scan, and the security scans.
|
|
44
|
+
CI also runs the build, the end-to-end tests, the container image scan, and the security scans, including CodeQL, dependency review, and OpenSSF Scorecard. CodeQL and Scorecard run on public repositories, and on private ones when the `CODE_SCANNING` repository variable is `true`.
|
|
45
45
|
|
|
46
46
|
## Container
|
|
47
47
|
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"type": "module",
|
|
13
13
|
"scripts": {
|
|
14
14
|
"build": "next build",
|
|
15
|
-
"check": "pnpm verify:static && pnpm test:changed",
|
|
15
|
+
"check": "pnpm verify:static && pnpm test:changed && pnpm build",
|
|
16
16
|
"clean": "rm -rf .next coverage *.tsbuildinfo .eslintcache node_modules/.cache",
|
|
17
17
|
"depcruise": "depcruise --config .dependency-cruiser.json app test eslint.config.ts next.config.ts playwright.config.ts postcss.config.mjs vitest.config.ts",
|
|
18
18
|
"dev": "next dev",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"test:e2e": "playwright test",
|
|
37
37
|
"test:watch": "vitest",
|
|
38
38
|
"typecheck": "next typegen && tsc --noEmit -p tsconfig.json",
|
|
39
|
-
"verify": "pnpm verify:static && pnpm test:coverage",
|
|
39
|
+
"verify": "pnpm verify:static && pnpm test:coverage && pnpm build",
|
|
40
40
|
"verify:static": "pnpm suppressions:check && pnpm rules:check && pnpm gates:check && pnpm lint:check && pnpm format:check && pnpm sort-package-json:check && pnpm peers:check && pnpm typecheck && pnpm knip && pnpm depcruise"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
@@ -28,14 +28,14 @@ pnpm install
|
|
|
28
28
|
|
|
29
29
|
Run the narrowest check that covers the change while you work. Git hooks run these checks:
|
|
30
30
|
|
|
31
|
-
| Hook | Checks
|
|
32
|
-
| :----------------- |
|
|
33
|
-
| `pre-commit` | blocks commits on `main` except the first commit of a new repository, gitleaks on staged changes, `pnpm rules:check`, and lint-staged on staged files
|
|
34
|
-
| `commit-msg` | commitlint
|
|
35
|
-
| `pre-merge-commit` | blocks merge commits on `main`
|
|
36
|
-
| `pre-push` | blocks pushes to `main` except the push that creates it, then `pnpm check`: the static checks
|
|
37
|
-
|
|
38
|
-
- **Scans:** CI also runs coverage, the build, the container image, and the secret, SAST, and
|
|
31
|
+
| Hook | Checks |
|
|
32
|
+
| :----------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
33
|
+
| `pre-commit` | blocks commits on `main` except the first commit of a new repository, gitleaks on staged changes, `pnpm rules:check`, and lint-staged on staged files |
|
|
34
|
+
| `commit-msg` | commitlint |
|
|
35
|
+
| `pre-merge-commit` | blocks merge commits on `main` |
|
|
36
|
+
| `pre-push` | blocks pushes to `main` except the push that creates it, then `pnpm check`: the static checks, the unit tests related to your changes, and the production build |
|
|
37
|
+
|
|
38
|
+
- **Scans:** CI also runs coverage, the build, the container image, and the secret, SAST, OSV, CodeQL, dependency-review, and OpenSSF Scorecard scans. Dependabot and OSV-Scanner report vulnerable dependencies.
|
|
39
39
|
- **Coverage:** CI requires 95% line coverage on changed lines in `src/`, except `main.ts`, which holds only wiring. Keep logic out of it, as `src/port.ts` and `src/server.ts` do.
|
|
40
40
|
|
|
41
41
|
Run `pnpm verify` for repository changes.
|
|
@@ -33,17 +33,17 @@ The server listens on the port set by `PORT` in `.env`, which the CLI created fr
|
|
|
33
33
|
|
|
34
34
|
## Scripts
|
|
35
35
|
|
|
36
|
-
| Script | Purpose
|
|
37
|
-
| :---------------- |
|
|
38
|
-
| `pnpm start:dev` | Start in watch mode from `src/main.ts`; loads `.env` when present
|
|
39
|
-
| `pnpm build` | Compile to `dist/`
|
|
40
|
-
| `pnpm start` | Run `dist/main.js`; loads `.env` when present
|
|
41
|
-
| `pnpm start:prod` | Run `dist/main.js`; the environment sets `PORT`
|
|
42
|
-
| `pnpm test` | Run tests (`src/**/*.test.ts`)
|
|
43
|
-
| `pnpm check` | Run the static checks
|
|
44
|
-
| `pnpm verify` | Run the static checks
|
|
45
|
-
|
|
46
|
-
CI also runs the build, the container image scan, and the security scans.
|
|
36
|
+
| Script | Purpose |
|
|
37
|
+
| :---------------- | :------------------------------------------------------------------------------------------------ |
|
|
38
|
+
| `pnpm start:dev` | Start in watch mode from `src/main.ts`; loads `.env` when present |
|
|
39
|
+
| `pnpm build` | Compile to `dist/` |
|
|
40
|
+
| `pnpm start` | Run `dist/main.js`; loads `.env` when present |
|
|
41
|
+
| `pnpm start:prod` | Run `dist/main.js`; the environment sets `PORT` |
|
|
42
|
+
| `pnpm test` | Run tests (`src/**/*.test.ts`) |
|
|
43
|
+
| `pnpm check` | Run the static checks, the tests for changed code, and the production build (the `pre-push` hook) |
|
|
44
|
+
| `pnpm verify` | Run the static checks, all tests with coverage, and the production build |
|
|
45
|
+
|
|
46
|
+
CI also runs the build, the container image scan, and the security scans, including CodeQL, dependency review, and OpenSSF Scorecard. CodeQL and Scorecard run on public repositories, and on private ones when the `CODE_SCANNING` repository variable is `true`.
|
|
47
47
|
|
|
48
48
|
## Container
|
|
49
49
|
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"type": "module",
|
|
13
13
|
"scripts": {
|
|
14
14
|
"build": "tsc -p tsconfig.build.json",
|
|
15
|
-
"check": "pnpm verify:static && pnpm test:changed",
|
|
15
|
+
"check": "pnpm verify:static && pnpm test:changed && pnpm build",
|
|
16
16
|
"clean": "rm -rf dist coverage *.tsbuildinfo .eslintcache node_modules/.cache",
|
|
17
17
|
"depcruise": "depcruise --config .dependency-cruiser.json src eslint.config.ts vitest.config.ts",
|
|
18
18
|
"format": "prettier --write --cache .",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"test:coverage": "vitest run --coverage --passWithNoTests",
|
|
37
37
|
"test:watch": "vitest",
|
|
38
38
|
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
39
|
-
"verify": "pnpm verify:static && pnpm test:coverage",
|
|
39
|
+
"verify": "pnpm verify:static && pnpm test:coverage && pnpm build",
|
|
40
40
|
"verify:static": "pnpm suppressions:check && pnpm rules:check && pnpm gates:check && pnpm lint:check && pnpm format:check && pnpm sort-package-json:check && pnpm peers:check && pnpm typecheck && pnpm knip && pnpm depcruise"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
@@ -29,14 +29,14 @@ pnpm install
|
|
|
29
29
|
|
|
30
30
|
Run the narrowest check that covers the change while you work. Git hooks run these checks:
|
|
31
31
|
|
|
32
|
-
| Hook | Checks
|
|
33
|
-
| :----------------- |
|
|
34
|
-
| `pre-commit` | blocks commits on `main` except the first commit of a new repository, gitleaks on staged changes, `pnpm rules:check`, and lint-staged on staged files
|
|
35
|
-
| `commit-msg` | commitlint
|
|
36
|
-
| `pre-merge-commit` | blocks merge commits on `main`
|
|
37
|
-
| `pre-push` | blocks pushes to `main` except the push that creates it, then `pnpm check`: the static checks
|
|
38
|
-
|
|
39
|
-
- **Scans:** CI also runs coverage, the build, the container image, and the secret, SAST, and
|
|
32
|
+
| Hook | Checks |
|
|
33
|
+
| :----------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
34
|
+
| `pre-commit` | blocks commits on `main` except the first commit of a new repository, gitleaks on staged changes, `pnpm rules:check`, and lint-staged on staged files |
|
|
35
|
+
| `commit-msg` | commitlint |
|
|
36
|
+
| `pre-merge-commit` | blocks merge commits on `main` |
|
|
37
|
+
| `pre-push` | blocks pushes to `main` except the push that creates it, then `pnpm check`: the static checks, the unit tests related to your changes, and the production build |
|
|
38
|
+
|
|
39
|
+
- **Scans:** CI also runs coverage, the build, the container image, and the secret, SAST, OSV, CodeQL, dependency-review, and OpenSSF Scorecard scans. Dependabot and OSV-Scanner report vulnerable dependencies.
|
|
40
40
|
- **End-to-end tests:** run `pnpm test:e2e` when you change pages or the admin panel. It starts the development server unless one is already running, and needs `DATABASE_URL` and `PAYLOAD_SECRET` from `.env`. Run `pnpm exec playwright install chromium` once before the first run. CI runs it against the production build and a PostgreSQL service on every pull request that is ready for review and changes code.
|
|
41
41
|
- **Coverage:** CI requires 95% branch coverage on changed lines in `src/`, except layouts, the Payload config, and generated files.
|
|
42
42
|
|
|
@@ -54,10 +54,10 @@ In development, Payload applies collection changes to the database directly. In
|
|
|
54
54
|
| `pnpm generate:importmap` | Regenerate the admin import map after adding a custom component |
|
|
55
55
|
| `pnpm migrate:create` | Create a migration from collection changes, for example `pnpm migrate:create add-posts` |
|
|
56
56
|
| `pnpm migrate` | Apply pending migrations to the database in `DATABASE_URL` |
|
|
57
|
-
| `pnpm check` | Run the static checks
|
|
58
|
-
| `pnpm verify` | Run the static checks
|
|
57
|
+
| `pnpm check` | Run the static checks, the unit tests for changed code, and the production build (the `pre-push` hook) |
|
|
58
|
+
| `pnpm verify` | Run the static checks, all unit tests with coverage, and the production build |
|
|
59
59
|
|
|
60
|
-
CI also runs the build, the end-to-end tests, the container image scan, and the security scans.
|
|
60
|
+
CI also runs the build, the end-to-end tests, the container image scan, and the security scans, including CodeQL, dependency review, and OpenSSF Scorecard. CodeQL and Scorecard run on public repositories, and on private ones when the `CODE_SCANNING` repository variable is `true`.
|
|
61
61
|
|
|
62
62
|
## Container
|
|
63
63
|
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"type": "module",
|
|
13
13
|
"scripts": {
|
|
14
14
|
"build": "next build",
|
|
15
|
-
"check": "pnpm verify:static && pnpm test:changed",
|
|
15
|
+
"check": "pnpm verify:static && pnpm test:changed && pnpm build",
|
|
16
16
|
"clean": "rm -rf .next coverage *.tsbuildinfo .eslintcache node_modules/.cache",
|
|
17
17
|
"depcruise": "depcruise --config .dependency-cruiser.json src test eslint.config.ts next.config.ts playwright.config.ts vitest.config.ts",
|
|
18
18
|
"dev": "next dev",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"test:e2e": "playwright test",
|
|
42
42
|
"test:watch": "vitest",
|
|
43
43
|
"typecheck": "next typegen && tsc --noEmit -p tsconfig.json",
|
|
44
|
-
"verify": "pnpm verify:static && pnpm test:coverage",
|
|
44
|
+
"verify": "pnpm verify:static && pnpm test:coverage && pnpm build",
|
|
45
45
|
"verify:static": "pnpm suppressions:check && pnpm rules:check && pnpm gates:check && pnpm lint:check && pnpm format:check && pnpm sort-package-json:check && pnpm peers:check && pnpm typecheck && pnpm knip && pnpm depcruise"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
base="${1-}"
|
|
5
|
+
head="${2:-HEAD}"
|
|
6
|
+
|
|
7
|
+
if [[ -z "$base" ]]; then
|
|
8
|
+
printf '%s\n' app backoffice
|
|
9
|
+
exit 0
|
|
10
|
+
fi
|
|
11
|
+
|
|
12
|
+
query="$(pnpm exec turbo query affected --packages --base "$base" --head "$head")"
|
|
13
|
+
paths="$(printf '%s' "$query" | jq -r '.data.affectedPackages.items[].path // empty')"
|
|
14
|
+
|
|
15
|
+
app=false
|
|
16
|
+
backoffice=false
|
|
17
|
+
|
|
18
|
+
while IFS= read -r path; do
|
|
19
|
+
case "$path" in
|
|
20
|
+
apps/app) app=true ;;
|
|
21
|
+
apps/backoffice) backoffice=true ;;
|
|
22
|
+
esac
|
|
23
|
+
done <<< "$paths"
|
|
24
|
+
|
|
25
|
+
if [[ "$app" == true ]]; then
|
|
26
|
+
printf '%s\n' app
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
if [[ "$backoffice" == true ]]; then
|
|
30
|
+
printf '%s\n' backoffice
|
|
31
|
+
fi
|
|
@@ -62,6 +62,9 @@ jobs:
|
|
|
62
62
|
if: needs.changes.outputs.full_verification == 'true'
|
|
63
63
|
runs-on: ubuntu-latest
|
|
64
64
|
timeout-minutes: 20
|
|
65
|
+
permissions:
|
|
66
|
+
contents: read
|
|
67
|
+
id-token: write # Needed to exchange GitHub OIDC for a Turborepo Remote Cache token.
|
|
65
68
|
services:
|
|
66
69
|
postgres:
|
|
67
70
|
image: postgres:17.11-alpine@sha256:266d3c257594e195b309ae9d1805e54c788f1d7b375f56bf6c367aac6c917e7b
|
|
@@ -79,17 +82,36 @@ jobs:
|
|
|
79
82
|
DATABASE_URL: postgres://postgres:postgres@localhost:5432/payload
|
|
80
83
|
PAYLOAD_SECRET: ci-only-secret
|
|
81
84
|
APP_URL: http://localhost:3001
|
|
85
|
+
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
|
86
|
+
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
|
|
82
87
|
steps:
|
|
83
88
|
- name: Checkout repository
|
|
84
89
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
|
85
90
|
with:
|
|
86
91
|
fetch-depth: 0
|
|
87
92
|
persist-credentials: false
|
|
93
|
+
- name: Fetch the pull request base
|
|
94
|
+
if: github.event_name == 'pull_request'
|
|
95
|
+
env:
|
|
96
|
+
BASE_REF: ${{ github.base_ref }}
|
|
97
|
+
run: git fetch --no-tags origin "$BASE_REF"
|
|
88
98
|
- name: Set up Node.js and pnpm
|
|
89
99
|
uses: pnpm/setup@fbda4c85fc2e1e08721cd8763afea8f48d60f024 # v3.0.0
|
|
90
100
|
with:
|
|
91
101
|
cache: true
|
|
92
102
|
require-lockfile: true
|
|
103
|
+
- name: Set up Turborepo Remote Cache
|
|
104
|
+
if: vars.TURBO_TEAM != ''
|
|
105
|
+
uses: vercel/setup-turborepo-remote-cache-action@49d7b1b46ba4c9251e1977986bfe18336feabc8f # v1.1.0
|
|
106
|
+
with:
|
|
107
|
+
team: ${{ vars.TURBO_TEAM }}
|
|
108
|
+
- name: Scope Turborepo to affected packages
|
|
109
|
+
if: github.event_name == 'pull_request'
|
|
110
|
+
env:
|
|
111
|
+
BASE_REF: ${{ github.base_ref }}
|
|
112
|
+
run: |
|
|
113
|
+
echo "TURBO_FILTER=--affected" >> "$GITHUB_ENV"
|
|
114
|
+
echo "TURBO_SCM_BASE=origin/$BASE_REF" >> "$GITHUB_ENV"
|
|
93
115
|
- name: Reject suppression directives
|
|
94
116
|
run: pnpm suppressions:check
|
|
95
117
|
- name: Check Cursor rules match .agents/rules
|
|
@@ -118,8 +140,16 @@ jobs:
|
|
|
118
140
|
run: pnpm build
|
|
119
141
|
env:
|
|
120
142
|
NEXT_TELEMETRY_DISABLED: "1"
|
|
121
|
-
- name: Install Playwright
|
|
122
|
-
run:
|
|
143
|
+
- name: Install Playwright browsers
|
|
144
|
+
run: |
|
|
145
|
+
if [[ -n "${TURBO_FILTER-}" ]]; then
|
|
146
|
+
mapfile -t apps < <(bash .github/scripts/affected-apps.sh "$TURBO_SCM_BASE" HEAD)
|
|
147
|
+
else
|
|
148
|
+
mapfile -t apps < <(bash .github/scripts/affected-apps.sh)
|
|
149
|
+
fi
|
|
150
|
+
for app in "${apps[@]}"; do
|
|
151
|
+
pnpm --filter "./apps/$app" exec playwright install --with-deps chromium
|
|
152
|
+
done
|
|
123
153
|
- name: Run end-to-end tests
|
|
124
154
|
run: pnpm test:e2e
|
|
125
155
|
env:
|
|
@@ -131,15 +161,19 @@ jobs:
|
|
|
131
161
|
run: |
|
|
132
162
|
python3 -m venv "$RUNNER_TEMP/diff-cover"
|
|
133
163
|
"$RUNNER_TEMP/diff-cover/bin/pip" install --quiet --require-hashes --only-binary=:all: -r .github/requirements/diff-cover.txt
|
|
134
|
-
|
|
135
|
-
|
|
164
|
+
if [[ -f apps/app/coverage/cobertura-coverage.xml ]]; then
|
|
165
|
+
"$RUNNER_TEMP/diff-cover/bin/diff-cover" apps/app/coverage/cobertura-coverage.xml --compare-branch "origin/$BASE_REF" --fail-under 95 --branch-coverage
|
|
166
|
+
fi
|
|
167
|
+
if [[ -f apps/backoffice/coverage/cobertura-coverage.xml ]]; then
|
|
168
|
+
"$RUNNER_TEMP/diff-cover/bin/diff-cover" apps/backoffice/coverage/cobertura-coverage.xml --compare-branch "origin/$BASE_REF" --fail-under 95 --branch-coverage
|
|
169
|
+
fi
|
|
136
170
|
|
|
137
171
|
image:
|
|
138
172
|
name: Container Image
|
|
139
173
|
needs: changes
|
|
140
174
|
if: needs.changes.outputs.full_verification == 'true' || github.event_name == 'schedule'
|
|
141
175
|
runs-on: ubuntu-latest
|
|
142
|
-
timeout-minutes:
|
|
176
|
+
timeout-minutes: 20
|
|
143
177
|
env:
|
|
144
178
|
HADOLINT_VERSION: 2.15.1
|
|
145
179
|
HADOLINT_SHA256: c7187db94eeeeca956519a6af171adc31453941a1e777961f6e680f697c8c507
|
|
@@ -149,30 +183,80 @@ jobs:
|
|
|
149
183
|
- name: Checkout repository
|
|
150
184
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
|
151
185
|
with:
|
|
186
|
+
fetch-depth: 0
|
|
152
187
|
persist-credentials: false
|
|
188
|
+
- name: Fetch the pull request base
|
|
189
|
+
if: github.event_name == 'pull_request'
|
|
190
|
+
env:
|
|
191
|
+
BASE_REF: ${{ github.base_ref }}
|
|
192
|
+
run: git fetch --no-tags origin "$BASE_REF"
|
|
193
|
+
- name: Set up Node.js and pnpm
|
|
194
|
+
uses: pnpm/setup@fbda4c85fc2e1e08721cd8763afea8f48d60f024 # v3.0.0
|
|
195
|
+
with:
|
|
196
|
+
cache: true
|
|
197
|
+
require-lockfile: true
|
|
198
|
+
- name: List affected apps
|
|
199
|
+
id: affected
|
|
200
|
+
env:
|
|
201
|
+
EVENT_NAME: ${{ github.event_name }}
|
|
202
|
+
BASE_REF: ${{ github.base_ref }}
|
|
203
|
+
run: |
|
|
204
|
+
if [[ "$EVENT_NAME" == "pull_request" ]]; then
|
|
205
|
+
mapfile -t apps < <(bash .github/scripts/affected-apps.sh "origin/$BASE_REF" HEAD)
|
|
206
|
+
else
|
|
207
|
+
mapfile -t apps < <(bash .github/scripts/affected-apps.sh)
|
|
208
|
+
fi
|
|
209
|
+
{
|
|
210
|
+
echo "app=false"
|
|
211
|
+
echo "backoffice=false"
|
|
212
|
+
for app in "${apps[@]}"; do
|
|
213
|
+
echo "$app=true"
|
|
214
|
+
done
|
|
215
|
+
} >> "$GITHUB_OUTPUT"
|
|
153
216
|
- name: Lint Dockerfile
|
|
154
217
|
run: |
|
|
155
218
|
curl --silent --show-error --fail --location --output hadolint \
|
|
156
219
|
"https://github.com/hadolint/hadolint/releases/download/v${HADOLINT_VERSION}/hadolint-linux-x86_64"
|
|
157
220
|
echo "${HADOLINT_SHA256} hadolint" | sha256sum --check --strict
|
|
158
221
|
chmod +x hadolint
|
|
159
|
-
|
|
222
|
+
files=()
|
|
223
|
+
if [[ "${{ steps.affected.outputs.app }}" == "true" ]]; then
|
|
224
|
+
files+=(apps/app/Dockerfile)
|
|
225
|
+
fi
|
|
226
|
+
if [[ "${{ steps.affected.outputs.backoffice }}" == "true" ]]; then
|
|
227
|
+
files+=(apps/backoffice/Dockerfile)
|
|
228
|
+
fi
|
|
229
|
+
if [[ "${#files[@]}" -gt 0 ]]; then
|
|
230
|
+
./hadolint "${files[@]}"
|
|
231
|
+
fi
|
|
160
232
|
- name: Build images
|
|
161
233
|
run: |
|
|
162
|
-
|
|
163
|
-
|
|
234
|
+
if [[ "${{ steps.affected.outputs.app }}" == "true" ]]; then
|
|
235
|
+
docker build --file apps/app/Dockerfile --tag app:ci .
|
|
236
|
+
fi
|
|
237
|
+
if [[ "${{ steps.affected.outputs.backoffice }}" == "true" ]]; then
|
|
238
|
+
docker build --file apps/backoffice/Dockerfile --tag backoffice:ci .
|
|
239
|
+
fi
|
|
164
240
|
- name: Smoke test images
|
|
165
241
|
id: smoke
|
|
166
242
|
run: |
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
243
|
+
if [[ "${{ steps.affected.outputs.app }}" == "true" ]]; then
|
|
244
|
+
docker run --detach --name app --env PORT=3000 --publish 3000:3000 app:ci
|
|
245
|
+
curl --silent --show-error --fail --retry 15 --retry-delay 1 --retry-all-errors --output /dev/null http://localhost:3000/health
|
|
246
|
+
fi
|
|
247
|
+
if [[ "${{ steps.affected.outputs.backoffice }}" == "true" ]]; then
|
|
248
|
+
docker run --detach --name backoffice --env PORT=3001 --env DATABASE_URL=postgres://build-only@localhost/build-only --env PAYLOAD_SECRET=ci-only-secret --env APP_URL=http://localhost:3001 --publish 3001:3001 backoffice:ci
|
|
249
|
+
curl --silent --show-error --fail --retry 15 --retry-delay 1 --retry-all-errors --output /dev/null http://localhost:3001/health
|
|
250
|
+
fi
|
|
171
251
|
- name: Show container logs
|
|
172
252
|
if: failure() && steps.smoke.outcome == 'failure'
|
|
173
253
|
run: |
|
|
174
|
-
docker
|
|
175
|
-
|
|
254
|
+
if docker container inspect app >/dev/null 2>&1; then
|
|
255
|
+
docker logs app
|
|
256
|
+
fi
|
|
257
|
+
if docker container inspect backoffice >/dev/null 2>&1; then
|
|
258
|
+
docker logs backoffice
|
|
259
|
+
fi
|
|
176
260
|
- name: Scan images for vulnerabilities
|
|
177
261
|
run: |
|
|
178
262
|
archive="trivy_${TRIVY_VERSION}_Linux-64bit.tar.gz"
|
|
@@ -180,8 +264,12 @@ jobs:
|
|
|
180
264
|
"https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}/${archive}"
|
|
181
265
|
echo "${TRIVY_SHA256} ${archive}" | sha256sum --check --strict
|
|
182
266
|
tar --extract --gzip --file "$archive" trivy
|
|
183
|
-
|
|
184
|
-
|
|
267
|
+
if [[ "${{ steps.affected.outputs.app }}" == "true" ]]; then
|
|
268
|
+
./trivy image --exit-code 1 --severity HIGH,CRITICAL --ignore-unfixed --no-progress app:ci
|
|
269
|
+
fi
|
|
270
|
+
if [[ "${{ steps.affected.outputs.backoffice }}" == "true" ]]; then
|
|
271
|
+
./trivy image --exit-code 1 --severity HIGH,CRITICAL --ignore-unfixed --no-progress backoffice:ci
|
|
272
|
+
fi
|
|
185
273
|
|
|
186
274
|
deploy-dev:
|
|
187
275
|
name: Deploy to dev
|
|
@@ -81,6 +81,7 @@ jobs:
|
|
|
81
81
|
url: ${{ vars.APP_URL }}
|
|
82
82
|
permissions:
|
|
83
83
|
contents: read
|
|
84
|
+
actions: read # Needed to find the last successful deploy of this environment.
|
|
84
85
|
env:
|
|
85
86
|
APP_URL: ${{ vars.APP_URL }}
|
|
86
87
|
BACKOFFICE_URL: ${{ vars.BACKOFFICE_URL }}
|
|
@@ -103,7 +104,58 @@ jobs:
|
|
|
103
104
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
|
104
105
|
with:
|
|
105
106
|
ref: ${{ inputs.commit_sha }}
|
|
107
|
+
fetch-depth: 0
|
|
106
108
|
persist-credentials: false
|
|
109
|
+
- name: Set up Node.js and pnpm
|
|
110
|
+
uses: pnpm/setup@fbda4c85fc2e1e08721cd8763afea8f48d60f024 # v3.0.0
|
|
111
|
+
with:
|
|
112
|
+
cache: true
|
|
113
|
+
require-lockfile: true
|
|
114
|
+
- name: List affected apps
|
|
115
|
+
id: affected
|
|
116
|
+
env:
|
|
117
|
+
COMMIT_SHA: ${{ inputs.commit_sha }}
|
|
118
|
+
GH_TOKEN: ${{ github.token }}
|
|
119
|
+
RAILWAY_ENVIRONMENT: ${{ inputs.environment }}
|
|
120
|
+
REPOSITORY: ${{ github.repository }}
|
|
121
|
+
run: |
|
|
122
|
+
previous=""
|
|
123
|
+
latest=""
|
|
124
|
+
while IFS=$'\t' read -r run_id title; do
|
|
125
|
+
[[ -z "$run_id" ]] && continue
|
|
126
|
+
if [[ "$title" =~ ^Deploy\ ([0-9a-f]{40})\ to\ ${RAILWAY_ENVIRONMENT}$ ]]; then
|
|
127
|
+
candidate="${BASH_REMATCH[1]}"
|
|
128
|
+
else
|
|
129
|
+
continue
|
|
130
|
+
fi
|
|
131
|
+
[[ "$candidate" == "$COMMIT_SHA" ]] && continue
|
|
132
|
+
completed="$(gh api "repos/$REPOSITORY/actions/runs/$run_id/jobs" | jq -r --arg env "$RAILWAY_ENVIRONMENT" '[.jobs[] | select(.conclusion == "success" and (.name == "Deploy to \($env)" or .name == "Deploy to \($env) / Deploy to \($env)")) | .completed_at] | max // empty')"
|
|
133
|
+
if [[ -n "$completed" && ( -z "$latest" || "$completed" > "$latest" ) ]]; then
|
|
134
|
+
latest="$completed"
|
|
135
|
+
previous="$candidate"
|
|
136
|
+
fi
|
|
137
|
+
done < <(gh api --paginate "repos/$REPOSITORY/actions/workflows/deploy.yml/runs?status=success&per_page=50" --jq '.workflow_runs[] | [.id, .display_title] | @tsv')
|
|
138
|
+
while IFS=$'\t' read -r run_id head_sha; do
|
|
139
|
+
[[ -z "$run_id" || "$head_sha" == "$COMMIT_SHA" ]] && continue
|
|
140
|
+
completed="$(gh api "repos/$REPOSITORY/actions/runs/$run_id/jobs" | jq -r --arg env "$RAILWAY_ENVIRONMENT" '[.jobs[] | select(.conclusion == "success" and .name == "Deploy to \($env) / Deploy to \($env)") | .completed_at] | max // empty')"
|
|
141
|
+
if [[ -n "$completed" && ( -z "$latest" || "$completed" > "$latest" ) ]]; then
|
|
142
|
+
latest="$completed"
|
|
143
|
+
previous="$head_sha"
|
|
144
|
+
fi
|
|
145
|
+
done < <(gh api "repos/$REPOSITORY/actions/workflows/ci.yml/runs?event=push&branch=main&status=success&per_page=50" --jq '.workflow_runs[] | [.id, .head_sha] | @tsv')
|
|
146
|
+
if [[ -n "$previous" ]]; then
|
|
147
|
+
git cat-file -e "$previous^{commit}" 2>/dev/null || git fetch --no-tags origin "$previous"
|
|
148
|
+
mapfile -t apps < <(bash .github/scripts/affected-apps.sh "$previous" "$COMMIT_SHA")
|
|
149
|
+
else
|
|
150
|
+
mapfile -t apps < <(bash .github/scripts/affected-apps.sh)
|
|
151
|
+
fi
|
|
152
|
+
{
|
|
153
|
+
echo "app=false"
|
|
154
|
+
echo "backoffice=false"
|
|
155
|
+
for app in "${apps[@]}"; do
|
|
156
|
+
echo "$app=true"
|
|
157
|
+
done
|
|
158
|
+
} >> "$GITHUB_OUTPUT"
|
|
107
159
|
- name: Install verified Railway CLI
|
|
108
160
|
run: |
|
|
109
161
|
archive="railway-v${RAILWAY_CLI_VERSION}-x86_64-unknown-linux-musl.tar.gz"
|
|
@@ -115,10 +167,19 @@ jobs:
|
|
|
115
167
|
- name: Deploy
|
|
116
168
|
env:
|
|
117
169
|
RAILWAY_TOKEN: ${{ secrets.RAILWAY_TOKEN }}
|
|
170
|
+
DEPLOY_APP: ${{ steps.affected.outputs.app }}
|
|
171
|
+
DEPLOY_BACKOFFICE: ${{ steps.affected.outputs.backoffice }}
|
|
118
172
|
run: |
|
|
119
|
-
|
|
120
|
-
|
|
173
|
+
if [[ "$DEPLOY_APP" == "true" ]]; then
|
|
174
|
+
railway up --ci --service "$RAILWAY_SERVICE_APP" --environment "$RAILWAY_ENVIRONMENT"
|
|
175
|
+
fi
|
|
176
|
+
if [[ "$DEPLOY_BACKOFFICE" == "true" ]]; then
|
|
177
|
+
railway up --ci --service "$RAILWAY_SERVICE_BACKOFFICE" --environment "$RAILWAY_ENVIRONMENT"
|
|
178
|
+
fi
|
|
121
179
|
- name: Require a healthy deployment
|
|
180
|
+
env:
|
|
181
|
+
DEPLOY_APP: ${{ steps.affected.outputs.app }}
|
|
182
|
+
DEPLOY_BACKOFFICE: ${{ steps.affected.outputs.backoffice }}
|
|
122
183
|
run: |
|
|
123
184
|
wait_health() {
|
|
124
185
|
url="$1"
|
|
@@ -131,5 +192,9 @@ jobs:
|
|
|
131
192
|
echo "::error::$url/health did not respond"
|
|
132
193
|
return 1
|
|
133
194
|
}
|
|
134
|
-
|
|
135
|
-
|
|
195
|
+
if [[ "$DEPLOY_APP" == "true" ]]; then
|
|
196
|
+
wait_health "$APP_URL"
|
|
197
|
+
fi
|
|
198
|
+
if [[ "$DEPLOY_BACKOFFICE" == "true" ]]; then
|
|
199
|
+
wait_health "$BACKOFFICE_URL"
|
|
200
|
+
fi
|
|
@@ -6,5 +6,6 @@
|
|
|
6
6
|
"!(package).json": ["prettier --write --cache"],
|
|
7
7
|
"*.{css,scss,md,yml,yaml}": ["prettier --write --cache"],
|
|
8
8
|
"package.json": ["sort-package-json", "prettier --write --cache"],
|
|
9
|
-
".husky/{commit-msg,pre-commit,pre-merge-commit,pre-push}": ["prettier --write --cache"]
|
|
9
|
+
".husky/{commit-msg,pre-commit,pre-merge-commit,pre-push}": ["prettier --write --cache"],
|
|
10
|
+
"*.sh": ["prettier --write --cache"]
|
|
10
11
|
}
|
|
@@ -29,14 +29,14 @@ pnpm install
|
|
|
29
29
|
|
|
30
30
|
Run the narrowest check that covers the change while you work. Git hooks run these checks:
|
|
31
31
|
|
|
32
|
-
| Hook | Checks
|
|
33
|
-
| :----------------- |
|
|
34
|
-
| `pre-commit` | blocks commits on `main` except the first commit of a new repository, gitleaks on staged changes, `pnpm rules:check`, and lint-staged on staged files
|
|
35
|
-
| `commit-msg` | commitlint
|
|
36
|
-
| `pre-merge-commit` | blocks merge commits on `main`
|
|
37
|
-
| `pre-push` | blocks pushes to `main` except the push that creates it, then `pnpm check`: the static checks
|
|
38
|
-
|
|
39
|
-
- **Scans:** CI also runs coverage, the build, the container image, and the secret, SAST, and
|
|
32
|
+
| Hook | Checks |
|
|
33
|
+
| :----------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
34
|
+
| `pre-commit` | blocks commits on `main` except the first commit of a new repository, gitleaks on staged changes, `pnpm rules:check`, and lint-staged on staged files |
|
|
35
|
+
| `commit-msg` | commitlint |
|
|
36
|
+
| `pre-merge-commit` | blocks merge commits on `main` |
|
|
37
|
+
| `pre-push` | blocks pushes to `main` except the push that creates it, then `pnpm check`: the static checks, the unit tests related to your changes, and the production build |
|
|
38
|
+
|
|
39
|
+
- **Scans:** CI also runs coverage, the build, the container image, and the secret, SAST, OSV, CodeQL, dependency-review, and OpenSSF Scorecard scans. Dependabot and OSV-Scanner report vulnerable dependencies.
|
|
40
40
|
- **End-to-end tests:** run `pnpm test:e2e` when you change pages or the admin panel. It starts both development servers unless they are already running, and the backoffice needs `DATABASE_URL` and `PAYLOAD_SECRET` from `.env`. Run `pnpm --filter ./apps/app exec playwright install chromium` once before the first run. CI runs it against the production build and a PostgreSQL service on every pull request that is ready for review and changes code.
|
|
41
41
|
- **Coverage:** CI requires 95% branch coverage on changed lines in `apps/app/app/` and `apps/backoffice/src/`, except layouts, the Payload config, and generated files.
|
|
42
42
|
|
|
@@ -43,21 +43,23 @@ In development, Payload applies collection changes to the database directly. In
|
|
|
43
43
|
|
|
44
44
|
## Scripts
|
|
45
45
|
|
|
46
|
-
| Script | Purpose
|
|
47
|
-
| :------------------------ |
|
|
48
|
-
| `pnpm dev` | Start the Next.js app on port 3000 and the Payload backoffice on port 3001
|
|
49
|
-
| `pnpm build` | Build both apps for production
|
|
50
|
-
| `pnpm start` | Serve both standalone production builds after `pnpm build`
|
|
51
|
-
| `pnpm test` | Run unit tests in both apps
|
|
52
|
-
| `pnpm test:e2e` | Run Playwright end-to-end tests in both apps. The backoffice uses the database in `DATABASE_URL`
|
|
53
|
-
| `pnpm generate:types` | Regenerate `apps/backoffice/src/payload-types.ts` after a collection change
|
|
54
|
-
| `pnpm generate:importmap` | Regenerate the admin import map after adding a custom component
|
|
55
|
-
| `pnpm migrate:create` | Create a migration from collection changes, for example `pnpm migrate:create add-posts`
|
|
56
|
-
| `pnpm migrate` | Apply pending migrations to the database in `DATABASE_URL`
|
|
57
|
-
| `pnpm check` | Run the static checks
|
|
58
|
-
| `pnpm verify` | Run the static checks
|
|
59
|
-
|
|
60
|
-
CI also runs the build, the end-to-end tests, the container image scans, and the security scans.
|
|
46
|
+
| Script | Purpose |
|
|
47
|
+
| :------------------------ | :----------------------------------------------------------------------------------------------------- |
|
|
48
|
+
| `pnpm dev` | Start the Next.js app on port 3000 and the Payload backoffice on port 3001 |
|
|
49
|
+
| `pnpm build` | Build both apps for production |
|
|
50
|
+
| `pnpm start` | Serve both standalone production builds after `pnpm build` |
|
|
51
|
+
| `pnpm test` | Run unit tests in both apps |
|
|
52
|
+
| `pnpm test:e2e` | Run Playwright end-to-end tests in both apps. The backoffice uses the database in `DATABASE_URL` |
|
|
53
|
+
| `pnpm generate:types` | Regenerate `apps/backoffice/src/payload-types.ts` after a collection change |
|
|
54
|
+
| `pnpm generate:importmap` | Regenerate the admin import map after adding a custom component |
|
|
55
|
+
| `pnpm migrate:create` | Create a migration from collection changes, for example `pnpm migrate:create add-posts` |
|
|
56
|
+
| `pnpm migrate` | Apply pending migrations to the database in `DATABASE_URL` |
|
|
57
|
+
| `pnpm check` | Run the static checks, the unit tests for changed code, and the production build (the `pre-push` hook) |
|
|
58
|
+
| `pnpm verify` | Run the static checks, all unit tests with coverage, and the production build |
|
|
59
|
+
|
|
60
|
+
CI also runs the build, the end-to-end tests, the container image scans, and the security scans, including CodeQL, dependency review, and OpenSSF Scorecard. CodeQL and Scorecard run on public repositories, and on private ones when the `CODE_SCANNING` repository variable is `true`. Pull requests run Turborepo with `--affected` against the base branch, so only packages that changed are typed, tested, built, and scanned. Pushes to `main` and the weekly schedule still run every app. Patch coverage runs only for apps that produced a coverage file.
|
|
61
|
+
|
|
62
|
+
To reuse task output across CI runs, create a Vercel team, add the repository variable `TURBO_TEAM`, and follow [Turborepo on GitHub Actions](https://turborepo.dev/docs/guides/ci-vendors/github-actions) to allow OIDC. The workflow exchanges GitHub OIDC for a short-lived Remote Cache token. If OIDC is unavailable, set the `TURBO_TOKEN` secret instead. Leave both unset to run without a Remote Cache.
|
|
61
63
|
|
|
62
64
|
## Container
|
|
63
65
|
|
|
@@ -94,9 +96,19 @@ On Vercel, deploy an app without its image. The Next.js preset takes precedence
|
|
|
94
96
|
|
|
95
97
|
Limit `prod` to the `main` branch and add required reviewers. Environment secrets and variables in a private repository need GitHub Pro, Team, or Enterprise; required reviewers in a private repository need GitHub Enterprise.
|
|
96
98
|
|
|
97
|
-
3. Set the repository variable `DEPLOY_ENABLED` to `true`. Each push to `main` that passes CI then deploys to `dev`.
|
|
98
|
-
4. Promote with **Actions → Deploy → Run workflow**, giving the environment and the full commit SHA. The workflow requires a successful CI run on `main` for that commit and a successful deployment to the previous environment, then waits for `/health
|
|
99
|
+
3. Set the repository variable `DEPLOY_ENABLED` to `true`. Each push to `main` that passes CI then deploys to `dev`. The workflow compares the commit with the last GitHub deployment to that environment and runs `railway up` only for services whose packages changed. A promotion through `uat` or `prod` uses the same comparison against the commit last deployed there, so several commits still ship every changed service. The first deploy to an environment ships both services.
|
|
100
|
+
4. Promote with **Actions → Deploy → Run workflow**, giving the environment and the full commit SHA. The workflow requires a successful CI run on `main` for that commit and a successful deployment to the previous environment, then waits for `/health` on each service it uploaded.
|
|
99
101
|
|
|
100
102
|
Remove the workflow, `railway.json`, and the `deploy-dev` job in `ci.yml` if the project deploys elsewhere.
|
|
101
103
|
|
|
104
|
+
## Mobile app
|
|
105
|
+
|
|
106
|
+
The template does not ship an Expo app. Add one under `apps/mobile` when you need it:
|
|
107
|
+
|
|
108
|
+
1. Create the app from the workspace root with `pnpm create expo-app@latest apps/mobile`, then add `packages/*` workspace packages as you share code. Keep `eas.json` and `credentials.json` inside `apps/mobile` and run the EAS CLI from that directory ([Expo: Build with monorepos](https://docs.expo.dev/build-reference/build-with-monorepos/)).
|
|
109
|
+
2. Use `@expo/fingerprint` with `expo-github-action/fingerprint` so a pull request requests a native build only when the native layer changed ([Expo fingerprint](https://docs.expo.dev/versions/latest/sdk/fingerprint/)).
|
|
110
|
+
3. Keep a single `react-native` version in the workspace. Duplicate React Native versions in one monorepo are not supported ([Expo: Monorepos](https://docs.expo.dev/guides/monorepos/)).
|
|
111
|
+
|
|
112
|
+
EAS Build and Submit replace Railway for that app. CI `--affected` already includes `apps/*` and `packages/*`, so an Expo app is skipped when it did not change.
|
|
113
|
+
|
|
102
114
|
See [CONTRIBUTING.md](CONTRIBUTING.md) for the contribution workflow.
|
|
@@ -11,10 +11,10 @@
|
|
|
11
11
|
"author": "Next Friday",
|
|
12
12
|
"type": "module",
|
|
13
13
|
"scripts": {
|
|
14
|
-
"build": "turbo run build --filter=./apps
|
|
15
|
-
"check": "pnpm verify:static && pnpm test:changed",
|
|
14
|
+
"build": "turbo run build ${TURBO_FILTER:-\"--filter=./apps/*\"}",
|
|
15
|
+
"check": "pnpm verify:static && pnpm test:changed && pnpm build",
|
|
16
16
|
"clean": "rm -rf apps/app/.next apps/app/coverage apps/backoffice/.next apps/backoffice/coverage .turbo coverage *.tsbuildinfo .eslintcache node_modules/.cache",
|
|
17
|
-
"depcruise": "turbo run depcruise --filter=./apps
|
|
17
|
+
"depcruise": "turbo run depcruise ${TURBO_FILTER:-\"--filter=./apps/*\"}",
|
|
18
18
|
"dev": "turbo run dev --filter=./apps/*",
|
|
19
19
|
"format": "prettier --write --cache .",
|
|
20
20
|
"format:check": "prettier --check --cache . && editorconfig-checker",
|
|
@@ -34,13 +34,13 @@
|
|
|
34
34
|
"sort-package-json:check": "sort-package-json --check package.json apps/app/package.json apps/backoffice/package.json",
|
|
35
35
|
"start": "turbo run start --filter=./apps/*",
|
|
36
36
|
"suppressions:check": "bash .agents/hooks/agent-suppression-gate.sh --repo",
|
|
37
|
-
"test": "turbo run test --filter=./apps
|
|
38
|
-
"test:changed": "turbo run test:changed --filter=./apps
|
|
39
|
-
"test:coverage": "turbo run test:coverage --filter=./apps
|
|
40
|
-
"test:e2e": "turbo run test:e2e --filter=./apps
|
|
37
|
+
"test": "turbo run test ${TURBO_FILTER:-\"--filter=./apps/*\"}",
|
|
38
|
+
"test:changed": "turbo run test:changed ${TURBO_FILTER:-\"--filter=./apps/*\"}",
|
|
39
|
+
"test:coverage": "turbo run test:coverage ${TURBO_FILTER:-\"--filter=./apps/*\"}",
|
|
40
|
+
"test:e2e": "turbo run test:e2e ${TURBO_FILTER:-\"--filter=./apps/*\"}",
|
|
41
41
|
"test:watch": "turbo run test:watch --filter=./apps/*",
|
|
42
|
-
"typecheck": "tsc --noEmit -p tsconfig.json && turbo run typecheck --filter=./apps
|
|
43
|
-
"verify": "pnpm verify:static && pnpm test:coverage",
|
|
42
|
+
"typecheck": "tsc --noEmit -p tsconfig.json && turbo run typecheck ${TURBO_FILTER:-\"--filter=./apps/*\"}",
|
|
43
|
+
"verify": "pnpm verify:static && pnpm test:coverage && pnpm build",
|
|
44
44
|
"verify:static": "pnpm suppressions:check && pnpm rules:check && pnpm gates:check && pnpm lint:check && pnpm format:check && pnpm sort-package-json:check && pnpm peers:check && pnpm typecheck && pnpm knip && pnpm depcruise"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
@@ -13,9 +13,7 @@
|
|
|
13
13
|
"dependsOn": ["^build"],
|
|
14
14
|
"outputs": [".next/**", "!.next/cache/**"]
|
|
15
15
|
},
|
|
16
|
-
"depcruise": {
|
|
17
|
-
"cache": false
|
|
18
|
-
},
|
|
16
|
+
"depcruise": {},
|
|
19
17
|
"dev": {
|
|
20
18
|
"cache": false,
|
|
21
19
|
"persistent": true
|
|
@@ -24,14 +22,11 @@
|
|
|
24
22
|
"cache": false,
|
|
25
23
|
"persistent": true
|
|
26
24
|
},
|
|
27
|
-
"test": {
|
|
28
|
-
"cache": false
|
|
29
|
-
},
|
|
25
|
+
"test": {},
|
|
30
26
|
"test:changed": {
|
|
31
27
|
"cache": false
|
|
32
28
|
},
|
|
33
29
|
"test:coverage": {
|
|
34
|
-
"cache": false,
|
|
35
30
|
"outputs": ["coverage/**"]
|
|
36
31
|
},
|
|
37
32
|
"test:e2e": {
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
documentation:
|
|
2
|
-
- changed-files:
|
|
3
|
-
- any-glob-to-any-file:
|
|
4
|
-
- "**/*.md"
|
|
5
|
-
|
|
6
|
-
ci:
|
|
7
|
-
- changed-files:
|
|
8
|
-
- any-glob-to-any-file:
|
|
9
|
-
- ".github/workflows/**"
|
|
10
|
-
- ".github/file-filters.yml"
|
|
11
|
-
- ".github/dependabot.yml"
|
|
12
|
-
- ".github/labeler.yml"
|
|
13
|
-
- ".github/zizmor.yml"
|
|
14
|
-
- ".github/requirements/**"
|
|
15
|
-
- ".github/CODEOWNERS"
|
|
16
|
-
- ".husky/**"
|
|
17
|
-
|
|
18
|
-
dependencies:
|
|
19
|
-
- changed-files:
|
|
20
|
-
- any-glob-to-any-file:
|
|
21
|
-
- "**/package.json"
|
|
22
|
-
- "**/pnpm-lock.yaml"
|
|
23
|
-
- "pnpm-workspace.yaml"
|
|
24
|
-
- "**/Dockerfile"
|
|
25
|
-
- ".github/requirements/**"
|