@next-friday/create-app 2.0.0 → 2.1.0

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.
Files changed (85) hide show
  1. package/README.md +16 -15
  2. package/dist/main.mjs +18 -7
  3. package/package.json +1 -1
  4. package/templates/base/pnpm-workspace.yaml +24 -2
  5. package/templates/payload/README.md +1 -1
  6. package/templates/payload/package.json +0 -1
  7. package/templates/payload/src/payload.config.test.ts +31 -0
  8. package/templates/turborepo/.agents/rules/typescript-first.md +7 -0
  9. package/templates/turborepo/.agents/rules.json +41 -0
  10. package/templates/turborepo/.claude/rules/deterministic-testing.md +21 -0
  11. package/templates/turborepo/.claude/rules/typescript-first.md +15 -0
  12. package/templates/turborepo/.cursor/rules/deterministic-testing.mdc +21 -0
  13. package/templates/turborepo/.cursor/rules/typescript-first.mdc +13 -0
  14. package/templates/turborepo/.dockerignore +12 -0
  15. package/templates/turborepo/.editorconfig-checker.json +3 -0
  16. package/templates/turborepo/.env.example +4 -0
  17. package/templates/turborepo/.github/ISSUE_TEMPLATE/bug.yml +66 -0
  18. package/templates/turborepo/.github/ISSUE_TEMPLATE/feature.yml +51 -0
  19. package/templates/turborepo/.github/dependabot.yml +79 -0
  20. package/templates/turborepo/.github/labeler.yml +25 -0
  21. package/templates/turborepo/.github/pull_request_template.md +25 -0
  22. package/templates/turborepo/.github/workflows/ci.yml +212 -0
  23. package/templates/turborepo/.github/workflows/deploy.yml +135 -0
  24. package/templates/turborepo/.github/workflows/sast.yml +46 -0
  25. package/templates/turborepo/.lintstagedrc.json +10 -0
  26. package/templates/turborepo/.prettierignore +5 -0
  27. package/templates/turborepo/AGENTS.md +20 -0
  28. package/templates/turborepo/CONTRIBUTING.md +58 -0
  29. package/templates/turborepo/README.md +102 -0
  30. package/templates/turborepo/_gitignore +53 -0
  31. package/templates/turborepo/apps/app/.dependency-cruiser.json +34 -0
  32. package/templates/turborepo/apps/app/Dockerfile +25 -0
  33. package/templates/turborepo/apps/app/app/favicon.ico +0 -0
  34. package/templates/turborepo/apps/app/app/globals.css +26 -0
  35. package/templates/turborepo/apps/app/app/health/route.test.ts +20 -0
  36. package/templates/turborepo/apps/app/app/health/route.ts +23 -0
  37. package/templates/turborepo/apps/app/app/layout.tsx +29 -0
  38. package/templates/turborepo/apps/app/app/page.test.ts +11 -0
  39. package/templates/turborepo/apps/app/app/page.tsx +8 -0
  40. package/templates/turborepo/apps/app/next.config.ts +40 -0
  41. package/templates/turborepo/apps/app/package.json +41 -0
  42. package/templates/turborepo/apps/app/playwright.config.ts +27 -0
  43. package/templates/turborepo/apps/app/postcss.config.mjs +7 -0
  44. package/templates/turborepo/apps/app/test/home.e2e.test.ts +11 -0
  45. package/templates/turborepo/apps/app/tsconfig.json +33 -0
  46. package/templates/turborepo/apps/app/vitest.config.ts +12 -0
  47. package/templates/turborepo/apps/backoffice/.dependency-cruiser.json +34 -0
  48. package/templates/turborepo/apps/backoffice/Dockerfile +28 -0
  49. package/templates/turborepo/apps/backoffice/next.config.ts +48 -0
  50. package/templates/turborepo/apps/backoffice/package.json +49 -0
  51. package/templates/turborepo/apps/backoffice/playwright.config.ts +27 -0
  52. package/templates/turborepo/apps/backoffice/src/app/(frontend)/layout.tsx +18 -0
  53. package/templates/turborepo/apps/backoffice/src/app/(frontend)/page.test.ts +14 -0
  54. package/templates/turborepo/apps/backoffice/src/app/(frontend)/page.tsx +14 -0
  55. package/templates/turborepo/apps/backoffice/src/app/(frontend)/styles.css +15 -0
  56. package/templates/turborepo/apps/backoffice/src/app/(payload)/admin/[[...segments]]/not-found.tsx +24 -0
  57. package/templates/turborepo/apps/backoffice/src/app/(payload)/admin/[[...segments]]/page.tsx +24 -0
  58. package/templates/turborepo/apps/backoffice/src/app/(payload)/admin/importMap.ts +6 -0
  59. package/templates/turborepo/apps/backoffice/src/app/(payload)/api/[...slug]/route.ts +19 -0
  60. package/templates/turborepo/apps/backoffice/src/app/(payload)/custom.scss +0 -0
  61. package/templates/turborepo/apps/backoffice/src/app/(payload)/layout.tsx +31 -0
  62. package/templates/turborepo/apps/backoffice/src/app/health/route.test.ts +20 -0
  63. package/templates/turborepo/apps/backoffice/src/app/health/route.ts +23 -0
  64. package/templates/turborepo/apps/backoffice/src/collections/media.test.ts +27 -0
  65. package/templates/turborepo/apps/backoffice/src/collections/media.ts +18 -0
  66. package/templates/turborepo/apps/backoffice/src/collections/users.test.ts +10 -0
  67. package/templates/turborepo/apps/backoffice/src/collections/users.ts +10 -0
  68. package/templates/turborepo/apps/backoffice/src/environment.test.ts +32 -0
  69. package/templates/turborepo/apps/backoffice/src/environment.ts +27 -0
  70. package/templates/turborepo/apps/backoffice/src/migrations/20261030_005055_initial.json +969 -0
  71. package/templates/turborepo/apps/backoffice/src/migrations/20261030_005055_initial.ts +134 -0
  72. package/templates/turborepo/apps/backoffice/src/migrations/index.ts +9 -0
  73. package/templates/turborepo/apps/backoffice/src/payload-types.ts +339 -0
  74. package/templates/turborepo/apps/backoffice/src/payload.config.test.ts +31 -0
  75. package/templates/turborepo/apps/backoffice/src/payload.config.ts +50 -0
  76. package/templates/turborepo/apps/backoffice/test/home.e2e.test.ts +16 -0
  77. package/templates/turborepo/apps/backoffice/tsconfig.json +34 -0
  78. package/templates/turborepo/apps/backoffice/vitest.config.ts +19 -0
  79. package/templates/turborepo/eslint.config.ts +40 -0
  80. package/templates/turborepo/knip.json +9 -0
  81. package/templates/turborepo/package.json +70 -0
  82. package/templates/turborepo/pnpm-workspace.yaml +45 -0
  83. package/templates/turborepo/prettier.config.ts +11 -0
  84. package/templates/turborepo/tsconfig.json +16 -0
  85. package/templates/turborepo/turbo.json +50 -0
@@ -0,0 +1,212 @@
1
+ name: Verify
2
+
3
+ on:
4
+ pull_request:
5
+ types: [opened, synchronize, reopened, ready_for_review, converted_to_draft]
6
+ push:
7
+ branches:
8
+ - main
9
+ schedule:
10
+ - cron: "0 3 * * 1"
11
+
12
+ permissions:
13
+ contents: read
14
+
15
+ concurrency:
16
+ group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
17
+ cancel-in-progress: true
18
+
19
+ jobs:
20
+ changes:
21
+ name: Detect Changes
22
+ runs-on: ubuntu-latest
23
+ timeout-minutes: 5
24
+ permissions:
25
+ contents: read
26
+ pull-requests: read # Needed by paths-filter to inspect pull request changes.
27
+ outputs:
28
+ heavy: ${{ steps.filter.outputs.heavy }}
29
+ ci_policy: ${{ steps.filter.outputs.ci_policy }}
30
+ full_verification: ${{ steps.mode.outputs.full_verification }}
31
+ steps:
32
+ - name: Checkout repository
33
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
34
+ with:
35
+ persist-credentials: false
36
+ - name: Detect relevant changes
37
+ id: filter
38
+ if: github.event_name == 'pull_request'
39
+ uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3
40
+ with:
41
+ predicate-quantifier: some-with-excludes
42
+ filters: .github/file-filters.yml
43
+ - name: Select verification mode
44
+ id: mode
45
+ shell: bash
46
+ env:
47
+ EVENT_NAME: ${{ github.event_name }}
48
+ DRAFT: ${{ github.event.pull_request.draft }}
49
+ HEAVY: ${{ steps.filter.outputs.heavy }}
50
+ CI_POLICY: ${{ steps.filter.outputs.ci_policy }}
51
+ run: |
52
+ full_verification=false
53
+ if [[ "$EVENT_NAME" == "push" || ( "$EVENT_NAME" == "pull_request" && "$DRAFT" != "true" && ( "$HEAVY" == "true" || "$CI_POLICY" == "true" ) ) ]]; then
54
+ full_verification=true
55
+ fi
56
+
57
+ echo "full_verification=$full_verification" >> "$GITHUB_OUTPUT"
58
+
59
+ checks:
60
+ name: Static Checks, Tests & Build
61
+ needs: changes
62
+ if: needs.changes.outputs.full_verification == 'true'
63
+ runs-on: ubuntu-latest
64
+ timeout-minutes: 20
65
+ services:
66
+ postgres:
67
+ image: postgres:17.11-alpine@sha256:266d3c257594e195b309ae9d1805e54c788f1d7b375f56bf6c367aac6c917e7b
68
+ env:
69
+ POSTGRES_PASSWORD: postgres
70
+ POSTGRES_DB: payload
71
+ ports:
72
+ - 5432:5432
73
+ options: >-
74
+ --health-cmd "pg_isready --username postgres"
75
+ --health-interval 5s
76
+ --health-timeout 5s
77
+ --health-retries 10
78
+ env:
79
+ DATABASE_URL: postgres://postgres:postgres@localhost:5432/payload
80
+ PAYLOAD_SECRET: ci-only-secret
81
+ APP_URL: http://localhost:3001
82
+ steps:
83
+ - name: Checkout repository
84
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
85
+ with:
86
+ fetch-depth: 0
87
+ persist-credentials: false
88
+ - name: Set up Node.js and pnpm
89
+ uses: pnpm/setup@fbda4c85fc2e1e08721cd8763afea8f48d60f024 # v3.0.0
90
+ with:
91
+ cache: true
92
+ require-lockfile: true
93
+ - name: Reject suppression directives
94
+ run: pnpm suppressions:check
95
+ - name: Check Cursor rules match .agents/rules
96
+ run: pnpm rules:check
97
+ - name: Check agent gates
98
+ run: pnpm gates:check
99
+ - name: Check lint
100
+ run: pnpm lint:check
101
+ - name: Check formatting
102
+ run: pnpm format:check
103
+ env:
104
+ GITHUB_TOKEN: ${{ github.token }} # editorconfig-checker downloads its binary from GitHub releases; the token avoids the anonymous rate limit.
105
+ - name: Check package.json ordering
106
+ run: pnpm sort-package-json:check
107
+ - name: Validate peer dependency contracts
108
+ run: pnpm peers:check
109
+ - name: Check types
110
+ run: pnpm typecheck
111
+ - name: Run Knip
112
+ run: pnpm knip
113
+ - name: Check dependency graph
114
+ run: pnpm depcruise
115
+ - name: Run tests with coverage
116
+ run: pnpm test:coverage
117
+ - name: Build
118
+ run: pnpm build
119
+ env:
120
+ NEXT_TELEMETRY_DISABLED: "1"
121
+ - name: Install Playwright browser
122
+ run: pnpm --filter ./apps/app exec playwright install --with-deps chromium
123
+ - name: Run end-to-end tests
124
+ run: pnpm test:e2e
125
+ env:
126
+ NEXT_TELEMETRY_DISABLED: "1"
127
+ - name: Check patch coverage
128
+ if: github.event_name == 'pull_request'
129
+ env:
130
+ BASE_REF: ${{ github.base_ref }}
131
+ run: |
132
+ python3 -m venv "$RUNNER_TEMP/diff-cover"
133
+ "$RUNNER_TEMP/diff-cover/bin/pip" install --quiet --require-hashes --only-binary=:all: -r .github/requirements/diff-cover.txt
134
+ "$RUNNER_TEMP/diff-cover/bin/diff-cover" apps/app/coverage/cobertura-coverage.xml --compare-branch "origin/$BASE_REF" --fail-under 95 --branch-coverage
135
+ "$RUNNER_TEMP/diff-cover/bin/diff-cover" apps/backoffice/coverage/cobertura-coverage.xml --compare-branch "origin/$BASE_REF" --fail-under 95 --branch-coverage
136
+
137
+ image:
138
+ name: Container Image
139
+ needs: changes
140
+ if: needs.changes.outputs.full_verification == 'true' || github.event_name == 'schedule'
141
+ runs-on: ubuntu-latest
142
+ timeout-minutes: 15
143
+ env:
144
+ HADOLINT_VERSION: 2.15.1
145
+ HADOLINT_SHA256: c7187db94eeeeca956519a6af171adc31453941a1e777961f6e680f697c8c507
146
+ TRIVY_VERSION: 0.76.1
147
+ TRIVY_SHA256: c7cbe3137fb8a48a17574bede3dd6beb16742d1e9772929940fc90082c39d9c0
148
+ steps:
149
+ - name: Checkout repository
150
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
151
+ with:
152
+ persist-credentials: false
153
+ - name: Lint Dockerfile
154
+ run: |
155
+ curl --silent --show-error --fail --location --output hadolint \
156
+ "https://github.com/hadolint/hadolint/releases/download/v${HADOLINT_VERSION}/hadolint-linux-x86_64"
157
+ echo "${HADOLINT_SHA256} hadolint" | sha256sum --check --strict
158
+ chmod +x hadolint
159
+ ./hadolint apps/app/Dockerfile apps/backoffice/Dockerfile
160
+ - name: Build images
161
+ run: |
162
+ docker build --file apps/app/Dockerfile --tag app:ci .
163
+ docker build --file apps/backoffice/Dockerfile --tag backoffice:ci .
164
+ - name: Smoke test images
165
+ id: smoke
166
+ run: |
167
+ docker run --detach --name app --env PORT=3000 --publish 3000:3000 app:ci
168
+ 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
169
+ curl --silent --show-error --fail --retry 15 --retry-delay 1 --retry-all-errors --output /dev/null http://localhost:3000/health
170
+ curl --silent --show-error --fail --retry 15 --retry-delay 1 --retry-all-errors --output /dev/null http://localhost:3001/health
171
+ - name: Show container logs
172
+ if: failure() && steps.smoke.outcome == 'failure'
173
+ run: |
174
+ docker logs app
175
+ docker logs backoffice
176
+ - name: Scan images for vulnerabilities
177
+ run: |
178
+ archive="trivy_${TRIVY_VERSION}_Linux-64bit.tar.gz"
179
+ curl --silent --show-error --fail --location --output "$archive" \
180
+ "https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}/${archive}"
181
+ echo "${TRIVY_SHA256} ${archive}" | sha256sum --check --strict
182
+ tar --extract --gzip --file "$archive" trivy
183
+ ./trivy image --exit-code 1 --severity HIGH,CRITICAL --ignore-unfixed --no-progress app:ci
184
+ ./trivy image --exit-code 1 --severity HIGH,CRITICAL --ignore-unfixed --no-progress backoffice:ci
185
+
186
+ deploy-dev:
187
+ name: Deploy to dev
188
+ needs:
189
+ - checks
190
+ - image
191
+ if: github.event_name == 'push' && github.ref == 'refs/heads/main' && vars.DEPLOY_ENABLED == 'true'
192
+ permissions:
193
+ contents: read
194
+ actions: read # Needed by deploy.yml to find the CI run of the deployed commit.
195
+ uses: ./.github/workflows/deploy.yml
196
+ with:
197
+ environment: dev
198
+ commit_sha: ${{ github.sha }}
199
+
200
+ ci:
201
+ name: All Checks
202
+ if: always()
203
+ timeout-minutes: 5
204
+ needs:
205
+ - changes
206
+ - checks
207
+ - image
208
+ runs-on: ubuntu-latest
209
+ steps:
210
+ - name: Require all verification jobs
211
+ if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')
212
+ run: exit 1
@@ -0,0 +1,135 @@
1
+ name: Deploy
2
+ run-name: Deploy ${{ inputs.commit_sha }} to ${{ inputs.environment }}
3
+
4
+ on:
5
+ workflow_call:
6
+ inputs:
7
+ environment:
8
+ required: true
9
+ type: string
10
+ commit_sha:
11
+ required: true
12
+ type: string
13
+ workflow_dispatch:
14
+ inputs:
15
+ environment:
16
+ description: Railway environment to promote to
17
+ required: true
18
+ type: choice
19
+ options:
20
+ - uat
21
+ - prod
22
+ commit_sha:
23
+ description: Full commit SHA on main that passed CI
24
+ required: true
25
+ type: string
26
+
27
+ permissions: {}
28
+
29
+ concurrency:
30
+ group: deploy-${{ inputs.environment }}
31
+ cancel-in-progress: false
32
+
33
+ jobs:
34
+ verify:
35
+ name: Verify the requested commit
36
+ runs-on: ubuntu-latest
37
+ timeout-minutes: 5
38
+ permissions:
39
+ contents: read
40
+ actions: read # Needed to find the CI and deploy runs of the requested commit.
41
+ env:
42
+ COMMIT_SHA: ${{ inputs.commit_sha }}
43
+ TARGET: ${{ inputs.environment }}
44
+ steps:
45
+ - name: Checkout the requested revision
46
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
47
+ with:
48
+ ref: ${{ inputs.commit_sha }}
49
+ fetch-depth: 0
50
+ persist-credentials: false
51
+ - name: Require the commit on main with passing CI and a deployment to the previous environment
52
+ if: github.event_name == 'workflow_dispatch'
53
+ env:
54
+ GH_TOKEN: ${{ github.token }}
55
+ REPOSITORY: ${{ github.repository }}
56
+ run: |
57
+ [[ "$COMMIT_SHA" =~ ^[0-9a-f]{40}$ ]] || { echo "::error::Expected a full lowercase commit SHA"; exit 1; }
58
+ git merge-base --is-ancestor "$COMMIT_SHA" origin/main || { echo "::error::$COMMIT_SHA is not on main"; exit 1; }
59
+ ci_run="$(gh api "repos/$REPOSITORY/actions/workflows/ci.yml/runs?head_sha=$COMMIT_SHA&event=push&branch=main&status=success" --jq '.workflow_runs[0].id // empty')"
60
+ [[ -n "$ci_run" ]] || { echo "::error::No successful CI run on main for $COMMIT_SHA"; exit 1; }
61
+ case "$TARGET" in
62
+ uat)
63
+ previous=dev
64
+ deployed="$(gh api "repos/$REPOSITORY/actions/runs/$ci_run/jobs" --jq '[.jobs[] | select(.name == "Deploy to dev / Deploy to dev" and .conclusion == "success")] | length')"
65
+ ;;
66
+ prod)
67
+ previous=uat
68
+ export EXPECTED_TITLE="Deploy $COMMIT_SHA to uat"
69
+ deployed="$(gh api --paginate "repos/$REPOSITORY/actions/workflows/deploy.yml/runs?event=workflow_dispatch&status=success&per_page=100" --jq '.workflow_runs[] | select(.display_title == env.EXPECTED_TITLE) | .id' | wc -l)"
70
+ ;;
71
+ esac
72
+ [[ "$deployed" -gt 0 ]] || { echo "::error::$COMMIT_SHA has no successful $previous deployment; promote through $previous first"; exit 1; }
73
+
74
+ deploy:
75
+ name: Deploy to ${{ inputs.environment }}
76
+ needs: verify
77
+ runs-on: ubuntu-latest
78
+ timeout-minutes: 40
79
+ environment:
80
+ name: ${{ inputs.environment }}
81
+ url: ${{ vars.APP_URL }}
82
+ permissions:
83
+ contents: read
84
+ env:
85
+ APP_URL: ${{ vars.APP_URL }}
86
+ BACKOFFICE_URL: ${{ vars.BACKOFFICE_URL }}
87
+ RAILWAY_CLI_SHA256: 2e682fd2ddf8d6fdf9e46d9d99d99646ff0022a6abb0c2bca93011348a269fb9
88
+ RAILWAY_CLI_VERSION: 5.71.0
89
+ RAILWAY_ENVIRONMENT: ${{ inputs.environment }}
90
+ RAILWAY_SERVICE_APP: ${{ vars.RAILWAY_SERVICE_APP }}
91
+ RAILWAY_SERVICE_BACKOFFICE: ${{ vars.RAILWAY_SERVICE_BACKOFFICE }}
92
+ steps:
93
+ - name: Require environment settings
94
+ env:
95
+ RAILWAY_TOKEN: ${{ secrets.RAILWAY_TOKEN }}
96
+ run: |
97
+ [[ -n "$RAILWAY_TOKEN" ]] || { echo "::error::Set the RAILWAY_TOKEN secret in the $RAILWAY_ENVIRONMENT environment"; exit 1; }
98
+ [[ -n "$RAILWAY_SERVICE_APP" ]] || { echo "::error::Set the RAILWAY_SERVICE_APP variable in the $RAILWAY_ENVIRONMENT environment"; exit 1; }
99
+ [[ -n "$RAILWAY_SERVICE_BACKOFFICE" ]] || { echo "::error::Set the RAILWAY_SERVICE_BACKOFFICE variable in the $RAILWAY_ENVIRONMENT environment"; exit 1; }
100
+ [[ "$APP_URL" == https://* ]] || { echo "::error::Set the APP_URL variable to an https URL in the $RAILWAY_ENVIRONMENT environment"; exit 1; }
101
+ [[ "$BACKOFFICE_URL" == https://* ]] || { echo "::error::Set the BACKOFFICE_URL variable to an https URL in the $RAILWAY_ENVIRONMENT environment"; exit 1; }
102
+ - name: Checkout the requested revision
103
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
104
+ with:
105
+ ref: ${{ inputs.commit_sha }}
106
+ persist-credentials: false
107
+ - name: Install verified Railway CLI
108
+ run: |
109
+ archive="railway-v${RAILWAY_CLI_VERSION}-x86_64-unknown-linux-musl.tar.gz"
110
+ curl --silent --show-error --fail --location --retry 3 --output "$RUNNER_TEMP/$archive" \
111
+ "https://github.com/railwayapp/cli/releases/download/v${RAILWAY_CLI_VERSION}/${archive}"
112
+ echo "${RAILWAY_CLI_SHA256} $RUNNER_TEMP/$archive" | sha256sum --check --strict
113
+ tar --extract --gzip --file "$RUNNER_TEMP/$archive" --directory "$RUNNER_TEMP"
114
+ echo "$RUNNER_TEMP" >> "$GITHUB_PATH"
115
+ - name: Deploy
116
+ env:
117
+ RAILWAY_TOKEN: ${{ secrets.RAILWAY_TOKEN }}
118
+ run: |
119
+ railway up --ci --service "$RAILWAY_SERVICE_APP" --environment "$RAILWAY_ENVIRONMENT"
120
+ railway up --ci --service "$RAILWAY_SERVICE_BACKOFFICE" --environment "$RAILWAY_ENVIRONMENT"
121
+ - name: Require a healthy deployment
122
+ run: |
123
+ wait_health() {
124
+ url="$1"
125
+ for _ in $(seq 30); do
126
+ if curl --silent --fail --output /dev/null "$url/health"; then
127
+ return 0
128
+ fi
129
+ sleep 10
130
+ done
131
+ echo "::error::$url/health did not respond"
132
+ return 1
133
+ }
134
+ wait_health "$APP_URL"
135
+ wait_health "$BACKOFFICE_URL"
@@ -0,0 +1,46 @@
1
+ name: SAST
2
+
3
+ on:
4
+ pull_request: {}
5
+ push:
6
+ branches:
7
+ - main
8
+ paths-ignore:
9
+ - "**/*.md"
10
+ - ".agents/**"
11
+ - ".claude/**"
12
+ - ".cursor/**"
13
+ schedule:
14
+ - cron: "17 2 * * 1"
15
+
16
+ permissions: {}
17
+
18
+ concurrency:
19
+ group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
20
+ cancel-in-progress: true
21
+
22
+ jobs:
23
+ semgrep:
24
+ name: Semgrep (OWASP)
25
+ runs-on: ubuntu-latest
26
+ timeout-minutes: 15
27
+ permissions:
28
+ contents: read
29
+ container:
30
+ image: semgrep/semgrep:1.179.0@sha256:93963d9295a366f59e4850127b1550400ee7b388f04fe144e4a1f6325d96e01b
31
+ steps:
32
+ - name: Checkout repository
33
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
34
+ with:
35
+ persist-credentials: false
36
+ - name: Scan source
37
+ run: >-
38
+ semgrep scan
39
+ --config p/owasp-top-ten
40
+ --config p/typescript
41
+ --config p/nodejsscan
42
+ --config p/react
43
+ --severity ERROR --severity WARNING
44
+ --error --metrics=off
45
+ --timeout 60 --timeout-threshold 0
46
+ .
@@ -0,0 +1,10 @@
1
+ {
2
+ "*.{ts,tsx,js,jsx,mjs,cjs}": [
3
+ "pnpm exec eslint --fix --cache --max-warnings=0 --no-warn-ignored --no-inline-config",
4
+ "prettier --write --cache"
5
+ ],
6
+ "!(package).json": ["prettier --write --cache"],
7
+ "*.{css,scss,md,yml,yaml}": ["prettier --write --cache"],
8
+ "package.json": ["sort-package-json", "prettier --write --cache"],
9
+ ".husky/{commit-msg,pre-commit,pre-merge-commit,pre-push}": ["prettier --write --cache"]
10
+ }
@@ -0,0 +1,5 @@
1
+ .husky/_
2
+ pnpm-lock.yaml
3
+ apps/backoffice/src/app/(payload)/
4
+ apps/backoffice/src/migrations/
5
+ apps/backoffice/src/payload-types.ts
@@ -0,0 +1,20 @@
1
+ # Agent guide
2
+
3
+ Turborepo workspace on pnpm. `apps/app` is the Next.js site. `apps/backoffice` is the Payload CMS. Root scripts run the same checks as the other Next Friday templates and delegate framework commands with Turborepo.
4
+
5
+ - Follow the rules in `.agents/rules/`; `.agents/rules.json` gives each rule's scope. Claude Code and Cursor load them automatically.
6
+ - Rule files, agent hooks, and agent settings are company policy: propose a change to the user, who edits them.
7
+ - Put unit tests next to the code as `*.test.ts`. Put Playwright end-to-end tests in `apps/app/test/` and `apps/backoffice/test/` as `*.e2e.test.ts`. Component tests need a DOM test environment, which is not set up yet; ask the user before adding one.
8
+ - The Next.js site lives in `apps/app/app/`. The backoffice site lives in `apps/backoffice/src/app/(frontend)/`, collections in `apps/backoffice/src/collections/`, and the Payload config in `apps/backoffice/src/payload.config.ts`.
9
+ - Payload generates `apps/backoffice/src/app/(payload)/`, `apps/backoffice/src/payload-types.ts`, and `apps/backoffice/src/migrations/`. After changing a collection, regenerate them with `pnpm generate:types`, `pnpm generate:importmap`, and `pnpm migrate:create <name>`, and commit the results.
10
+ - Read backoffice configuration through `requireEnvironment` in `apps/backoffice/src/environment.ts`. Document each variable in `.env.example`.
11
+
12
+ <!-- BEGIN:nextjs-agent-rules -->
13
+
14
+ ## This is NOT the Next.js you know
15
+
16
+ This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` (resolved from this file's directory; in monorepos the `next` package may not be visible from the repo root) before writing any code. Heed deprecation notices.
17
+
18
+ This block is written and re-added by `next dev` — verify at `node_modules/next/dist/server/lib/generate-agent-files.js`. Removing it from a diff only re-creates the uncommitted change; committing it with your work keeps the tree clean.
19
+
20
+ <!-- END:nextjs-agent-rules -->
@@ -0,0 +1,58 @@
1
+ # Contributing
2
+
3
+ Use this guide to prepare changes to this repository.
4
+
5
+ ## Prerequisites
6
+
7
+ - Use the Node.js version declared by `.nvmrc` and `package.json#engines`.
8
+ - Change the Node.js version in `.nvmrc` and both Dockerfiles together. Dependabot updates only the Dockerfiles.
9
+ - Install pnpm once with `npm install --global pnpm`. It switches to the version declared by `package.json#packageManager` automatically.
10
+ - Use Claude Code v2.1.295 or later. It reads `AGENTS.md` directly and supports the `onFailure` field the agent hooks use.
11
+ - Install gitleaks 8.19 or later, which the `pre-commit` hook runs, and jq, which the agent hooks and the `pre-push` hook (`pnpm gates:check`) need: `brew install gitleaks jq` on macOS, or install them from each project's releases on Linux.
12
+
13
+ From the repository root:
14
+
15
+ ```sh
16
+ npm install --global pnpm
17
+ pnpm install
18
+ ```
19
+
20
+ ## Quality standards
21
+
22
+ - Fix lint, type, and test failures at their root cause.
23
+ - Put unit tests next to the code as `*.test.ts` and Playwright end-to-end tests in each app's `test/` as `*.e2e.test.ts`.
24
+ - Change the database schema only through collections, then run `pnpm generate:types`, `pnpm generate:importmap`, and `pnpm migrate:create <name>` and commit the generated files. Production applies pending migrations when the server starts.
25
+ - Treat `coverage/` as generated output.
26
+ - Do not commit `.env` files or credentials.
27
+
28
+ ## Verification
29
+
30
+ Run the narrowest check that covers the change while you work. Git hooks run these checks:
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 and the unit tests related to your changes |
38
+
39
+ - **Scans:** CI also runs coverage, the build, the container image, and the secret, SAST, and OSV scans. Dependabot and OSV-Scanner report vulnerable dependencies.
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
+ - **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
+
43
+ Run `pnpm verify` for repository changes.
44
+
45
+ Agent rules live in `.agents/rules/` with their scope in `.agents/rules.json`. After you change either, run `pnpm rules:sync` to regenerate `.claude/rules/` and `.cursor/rules/`.
46
+
47
+ ## Pull requests
48
+
49
+ We use trunk-based development. `main` is the only long-lived branch, and every deploy promotes a commit from `main` (see the Deploy section in the README).
50
+
51
+ - Branch from `main` and merge back within a day or two. Do not create `develop` or release branches.
52
+ - Merge unfinished work behind a feature flag or keep it unreachable instead of holding a long-lived branch.
53
+ - Pull requests are squash-merged, so the pull request title becomes the commit on `main`.
54
+ - Write commit messages in the format that `@next-friday/commitlint-config-friday` defines. Pull request titles use `<type>(<scope>): <subject>`, which `.github/workflows/validate-pr-title.yml` checks.
55
+ - Keep each pull request focused on one cohesive change.
56
+ - Describe observable page, collection, API, or configuration effects.
57
+ - Report only checks that actually ran and include their results.
58
+ - Add or update tests only when they protect a distinct observable contract.
@@ -0,0 +1,102 @@
1
+ # next-friday-template-turborepo
2
+
3
+ Next Friday starter for a Turborepo workspace. `apps/app` is a Next.js site on port 3000. `apps/backoffice` is a Payload CMS on port 3001. The backoffice API is REST at `/api`.
4
+
5
+ ## Getting started
6
+
7
+ ```sh
8
+ npm install --global pnpm
9
+ pnpm install
10
+ pnpm dev
11
+ ```
12
+
13
+ The CLI created `.env` from `.env.example` and set `PAYLOAD_SECRET` to a random secret. Before `pnpm dev`, set in `.env`:
14
+
15
+ - `DATABASE_URL`: a PostgreSQL database.
16
+ - `APP_URL`: the URL you open the backoffice at, such as `http://localhost:3001`.
17
+ - `PAYLOAD_SECRET`, only if you recreate `.env` from `.env.example`: generate one with `openssl rand -hex 32`.
18
+
19
+ Then open `http://localhost:3000` for the site and `http://localhost:3001/admin` to create the first user.
20
+
21
+ **Warning:** until the first user exists, anyone who can reach the backoffice `/admin` can create it. Create the first user in each deployed environment right after its first deploy, before you share the URL.
22
+
23
+ In development, Payload applies collection changes to the database directly. In production, the server applies the migrations in `apps/backoffice/src/migrations/` when it starts.
24
+
25
+ ## After creating a project from this template
26
+
27
+ 1. Install gitleaks and jq (see [CONTRIBUTING.md](CONTRIBUTING.md)). Make the first commit on `main` and push it. The Git hooks allow only this first commit and push on `main`; work on branches after that.
28
+
29
+ ```sh
30
+ git add --all
31
+ git commit -m "chore(repo): start from template"
32
+ git push --set-upstream origin main
33
+ ```
34
+
35
+ 2. Add your team as the owner of every file in `.github/CODEOWNERS`, and replace the owner placeholder in `.github/zizmor.yml`.
36
+ 3. Replace the template's description and owner: `package.json#description`, `repository.url`, and `author`, and the `Next Friday Next.js template` text in `apps/app/app/layout.tsx`, `apps/app/app/page.tsx`, and their tests, and the `Next Friday Payload template` text in `apps/backoffice/src/app/(frontend)/layout.tsx`, `apps/backoffice/src/app/(frontend)/page.tsx`, and their tests. Change `license` from `UNLICENSED` if the project is open source.
37
+ 4. In the repository settings:
38
+ - **Actions → General → Workflow permissions:** select **Read repository contents and packages permissions**. Every workflow requests the extra permissions it needs.
39
+ - **Advanced Security:** enable private vulnerability reporting, Dependabot alerts, Dependabot security updates, secret scanning, and push protection. `SECURITY.md` sends reporters to private vulnerability reporting.
40
+ - **Cursor Bugbot:** enable the repository at [Bugbot](https://cursor.com/automations/from-cursor/bugbot). Bugbot reviews pull requests against the rules linked from `.cursor/BUGBOT.md`.
41
+ - **Rules → Rulesets:** protect `main` with squash merges only, a linear history, no force pushes or deletion, and the required checks `All Checks` and `Validate pull request title`. Rulesets in a private repository need GitHub Pro, Team, or Enterprise; on GitHub Free, make the repository public or upgrade.
42
+ - Create the `needs triage` label, which the issue forms apply: `gh label create "needs triage"`.
43
+
44
+ ## Scripts
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 and the unit tests for changed code (the `pre-push` hook) |
58
+ | `pnpm verify` | Run the static checks and all unit tests with coverage |
59
+
60
+ CI also runs the build, the end-to-end tests, the container image scans, and the security scans.
61
+
62
+ ## Container
63
+
64
+ Each app has a Dockerfile. Build both from the workspace root. The images run as the `node` user. The app listens on `PORT`, or 3000 when it is unset. The backoffice listens on `PORT`, or 3000 when it is unset, and needs `DATABASE_URL`, `PAYLOAD_SECRET`, and `APP_URL`.
65
+
66
+ ```sh
67
+ docker build --file apps/app/Dockerfile --tag app .
68
+ docker build --file apps/backoffice/Dockerfile --tag backoffice .
69
+ docker run --publish 3000:3000 app
70
+ docker run --publish 3001:3001 --env PORT=3001 --env DATABASE_URL --env PAYLOAD_SECRET --env APP_URL backoffice
71
+ ```
72
+
73
+ Uploaded images, up to 10 MB each, are written to the backoffice container filesystem, which a redeploy discards. Add a Payload storage adapter, for example `@payloadcms/storage-s3`, before storing media in production.
74
+
75
+ On Vercel, deploy an app without its image. The Next.js preset takes precedence over the Dockerfile.
76
+
77
+ ## Deploy
78
+
79
+ `.github/workflows/deploy.yml` deploys both apps and promotes one commit through `dev`, `uat`, and `prod`. `railway.json` sets the Railway health check to `/health`. Create one Railway service per app, with that service's Dockerfile path set to `apps/app/Dockerfile` or `apps/backoffice/Dockerfile`. The workflow stays off until you configure it:
80
+
81
+ 1. In Railway, create a project with one service for each app, one PostgreSQL database for the backoffice, and the environments `dev`, `uat`, and `prod`. The workflow passes these names to `railway up --environment`. In each backoffice environment, set the service variables:
82
+ - `DATABASE_URL` to `${{Postgres.DATABASE_URL}}`;
83
+ - `PAYLOAD_SECRET` to a separate long random string;
84
+ - `APP_URL` to that environment's public backoffice `https://` URL.
85
+
86
+ Create one project token per environment under **Project Settings → Tokens**. CI tests against PostgreSQL 17; change the image in `ci.yml` if the database runs another major version.
87
+
88
+ 2. In GitHub, create the `dev`, `uat`, and `prod` environments under **Settings → Environments**, and add to each:
89
+ - the `RAILWAY_TOKEN` secret: that environment's project token;
90
+ - the `RAILWAY_SERVICE_APP` variable: the Next.js service name;
91
+ - the `RAILWAY_SERVICE_BACKOFFICE` variable: the Payload service name;
92
+ - the `APP_URL` variable: the public Next.js `https://` URL;
93
+ - the `BACKOFFICE_URL` variable: the public backoffice `https://` URL.
94
+
95
+ 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
+
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
+
100
+ Remove the workflow, `railway.json`, and the `deploy-dev` job in `ci.yml` if the project deploys elsewhere.
101
+
102
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for the contribution workflow.
@@ -0,0 +1,53 @@
1
+ node_modules
2
+ .pnpm-store
3
+
4
+ coverage
5
+ playwright-report
6
+ test-results
7
+ *.tsbuildinfo
8
+
9
+ .cache
10
+
11
+ *.pem
12
+
13
+ npm-debug.log*
14
+ pnpm-debug.log*
15
+ *.log
16
+
17
+ **/.env*
18
+ !.env.example
19
+
20
+ .eslintcache
21
+
22
+ .vscode/*
23
+ !.vscode/settings.json
24
+ !.vscode/extensions.json
25
+ /.idea
26
+
27
+ logs
28
+ .temp
29
+ .tmp
30
+
31
+ .DS_Store
32
+ .DS_Store?
33
+ ._*
34
+ .Spotlight-V100
35
+ .Trashes
36
+ ehthumbs.db
37
+ Thumbs.db
38
+ **/.DS_Store
39
+
40
+ *.key
41
+ *.p12
42
+ *.pfx
43
+ id_rsa*
44
+ id_ed25519*
45
+
46
+ /.claude/settings.local.json
47
+ /.claude/worktrees/
48
+
49
+ .next
50
+ .turbo
51
+ apps/backoffice/media
52
+ /out
53
+ next-env.d.ts