@next-friday/create-app 9.7.0 → 9.7.1
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/dist/main.mjs +3 -2
- package/package.json +1 -1
- package/templates/base/.agents/hooks/agent-production-gate.sh +8 -3
- package/templates/base/scripts/check-agent-gates.ts +40 -35
- package/templates/nestjs/.github/workflows/ci.yml +3 -1
- package/templates/nestjs/README.md +12 -0
- package/templates/nextjs/.github/workflows/ci.yml +3 -1
- package/templates/nextjs/README.md +12 -0
- package/templates/nodejs/.github/workflows/ci.yml +3 -1
- package/templates/nodejs/README.md +12 -0
- package/templates/payload/.github/workflows/ci.yml +3 -1
- package/templates/payload/README.md +12 -0
- package/templates/turborepo/.github/workflows/ci.yml +3 -1
- package/templates/turborepo/README.md +12 -0
- /package/templates/{base → deploy-railway/base}/.github/workflows/deploy.yml +0 -0
- /package/templates/{turborepo → deploy-railway/turborepo}/.github/workflows/deploy.yml +0 -0
package/dist/main.mjs
CHANGED
|
@@ -285,10 +285,11 @@ function resolveDeploySections(content, target) {
|
|
|
285
285
|
const kept = [];
|
|
286
286
|
let section;
|
|
287
287
|
let isAfterMarker = false;
|
|
288
|
-
|
|
288
|
+
const lines = content.split("\n");
|
|
289
|
+
for (const [index, line] of lines.entries()) {
|
|
289
290
|
const name = marker.exec(line)?.groups?.name;
|
|
290
291
|
if (name === void 0) {
|
|
291
|
-
if (!(isAfterMarker && line.trim() === "") && (section ?? target) === target) kept.push(line);
|
|
292
|
+
if (!(isAfterMarker && line.trim() === "" && index < lines.length - 1) && (section ?? target) === target) kept.push(line);
|
|
292
293
|
} else section = sectionAfterMarker(name, section);
|
|
293
294
|
isAfterMarker = name !== void 0;
|
|
294
295
|
}
|
package/package.json
CHANGED
|
@@ -45,9 +45,9 @@ release_gate="A release manager runs prod deploys, commands against prod, and re
|
|
|
45
45
|
|
|
46
46
|
# The gate reads the command line as text, joining continued lines and dropping quotes,
|
|
47
47
|
# backslashes, and fd redirections, so wrappers such as bash -c, eval, or npx and split words such
|
|
48
|
-
# as pr''od do not hide the common forms. A dispatch input
|
|
49
|
-
#
|
|
50
|
-
#
|
|
48
|
+
# as pr''od do not hide the common forms. A dispatch input the gate cannot read (a variable, a
|
|
49
|
+
# command substitution, a file, or stdin) counts as prod. It is a convenience, not a security
|
|
50
|
+
# boundary: the prod environment's required reviewers and the deploy credentials are.
|
|
51
51
|
text=${command//'\\'/}
|
|
52
52
|
text=${text//$'\\\n'/}
|
|
53
53
|
text=$(sed -E 's/([0-9]*[<>]&[0-9-]*|&>>?)//g' <<< "${text//[\"\'\\]/}")
|
|
@@ -56,16 +56,21 @@ prod="(prod([^[:alnum:]_-]|$)|[\$\`@])"
|
|
|
56
56
|
dispatch="(workflow[[:space:]]+run|/dispatches)"
|
|
57
57
|
environment_field="environment[]]*[=:][[:space:]]*$prod"
|
|
58
58
|
unreadable_inputs="--(json|input)([[:space:]=]|$)"
|
|
59
|
+
# deploy:railway
|
|
60
|
+
# A Railway environment the gate cannot read counts as prod too.
|
|
59
61
|
railway="(^|[^[:alnum:]_.-])(railway|@railway/cli(@[^[:space:]]*)?)([[:space:]]$simple)?"
|
|
62
|
+
# deploy:end
|
|
60
63
|
git_push="(^|[^[:alnum:]_.-])git([[:space:]]+(-[Cc][[:space:]]+[^[:space:]]+|--(git-dir|work-tree|namespace|config-env)[[:space:]]+[^[:space:]]+|--?[^[:space:]]+))*[[:space:]]+push([[:space:]]$simple)?"
|
|
61
64
|
tag_ref="([[:space:]:+]((refs/)?tags/|v[0-9])|[[:space:]]--((follow-)?tags|mirror)([[:space:]=]|$))"
|
|
62
65
|
|
|
63
66
|
if [[ $text =~ $dispatch$simple($environment_field|$unreadable_inputs) || $text =~ $environment_field$simple$dispatch ]]; then
|
|
64
67
|
block "$release_gate"
|
|
65
68
|
fi
|
|
69
|
+
# deploy:railway
|
|
66
70
|
if [[ $text =~ $railway[[:space:]](-[[:alpha:]]*e([[:space:]]+|=)?|--environment([[:space:]]+|=))$prod || $text =~ $railway[[:space:]](env|environment)([[:space:]]+[[:alpha:]]+)?[[:space:]]+$prod ]]; then
|
|
67
71
|
block "$release_gate"
|
|
68
72
|
fi
|
|
73
|
+
# deploy:end
|
|
69
74
|
if [[ $text =~ $git_push$tag_ref || $(tr '[:upper:]' '[:lower:]' <<< "$text") == *push.followtags* ]]; then
|
|
70
75
|
block "$release_gate"
|
|
71
76
|
fi
|
|
@@ -137,7 +137,7 @@ const allowedCommands = [
|
|
|
137
137
|
'gh api repos/next-friday/example/issues/1/comments -f body="APPROVED by QA"',
|
|
138
138
|
];
|
|
139
139
|
|
|
140
|
-
const
|
|
140
|
+
const sampleProductionCommand = "gh workflow run deploy.yml -f environment=prod";
|
|
141
141
|
|
|
142
142
|
const productionCommands = [
|
|
143
143
|
"gh workflow run deploy.yml -f environment=prod -f commit_sha=abc",
|
|
@@ -158,8 +158,30 @@ const productionCommands = [
|
|
|
158
158
|
"gh api repos/o/r/actions/workflows/1/dispatches --input body.json",
|
|
159
159
|
"gh api repos/o/r/actions/workflows/1/dispatches -f inputs[environment]=$E",
|
|
160
160
|
"bash -c 'gh workflow run deploy.yml -f environment=prod'",
|
|
161
|
+
sampleProductionCommand,
|
|
162
|
+
"git push origin v1.0.0",
|
|
163
|
+
"git push origin HEAD:refs/tags/v1.0.0",
|
|
164
|
+
"git push --tags",
|
|
165
|
+
"git push --follow-tags origin main",
|
|
166
|
+
"git -C . push origin v2.3.4",
|
|
167
|
+
"gh workflow run deploy.yml -f environment=pr''od",
|
|
168
|
+
"gh workflow run deploy.yml -f environment=p\\rod",
|
|
169
|
+
'gh workflow run deploy.yml -f "envi"ronment=prod',
|
|
170
|
+
"git push origin 'refs/tags/*'",
|
|
171
|
+
'git push origin "refs/tags/v*"',
|
|
172
|
+
"git push --mirror",
|
|
173
|
+
"git -c push.followTags=true push origin main",
|
|
174
|
+
"git config push.followTags true && git push",
|
|
175
|
+
"gh workflow run deploy.yml 2>&1 -f environment=prod",
|
|
176
|
+
"git push origin tags/v1.0.0",
|
|
177
|
+
"git --git-dir .git push origin v1.0.0",
|
|
178
|
+
"gh workflow run deploy.yml \\\n -f environment=prod \\\n -f commit_sha=abc",
|
|
179
|
+
"git push \\\n origin v1.0.0",
|
|
180
|
+
"git push &>/dev/null origin v1.0.0",
|
|
181
|
+
"cd a\\\\\ngit push origin v1.0.0",
|
|
182
|
+
// deploy:railway
|
|
161
183
|
"railway up --ci --environment prod",
|
|
162
|
-
|
|
184
|
+
"railway up -e prod",
|
|
163
185
|
"railway up -e=prod",
|
|
164
186
|
"railway up -eprod",
|
|
165
187
|
"railway up -ce prod",
|
|
@@ -181,32 +203,13 @@ const productionCommands = [
|
|
|
181
203
|
"/usr/local/bin/railway up -e prod",
|
|
182
204
|
"timeout 60 railway up -e prod",
|
|
183
205
|
"eval railway up -e prod",
|
|
184
|
-
"git push origin v1.0.0",
|
|
185
|
-
"git push origin HEAD:refs/tags/v1.0.0",
|
|
186
|
-
"git push --tags",
|
|
187
|
-
"git push --follow-tags origin main",
|
|
188
|
-
"git -C . push origin v2.3.4",
|
|
189
206
|
"npx @railway/cli@5.71.0 up -e prod",
|
|
190
207
|
"npx @railway/cli@latest up --environment prod",
|
|
191
208
|
"railway up -e pr''od",
|
|
192
|
-
"gh workflow run deploy.yml -f environment=pr''od",
|
|
193
|
-
"gh workflow run deploy.yml -f environment=p\\rod",
|
|
194
|
-
'gh workflow run deploy.yml -f "envi"ronment=prod',
|
|
195
|
-
"git push origin 'refs/tags/*'",
|
|
196
|
-
'git push origin "refs/tags/v*"',
|
|
197
|
-
"git push --mirror",
|
|
198
|
-
"git -c push.followTags=true push origin main",
|
|
199
|
-
"git config push.followTags true && git push",
|
|
200
|
-
"gh workflow run deploy.yml 2>&1 -f environment=prod",
|
|
201
209
|
"railway up 2>&1 -e prod",
|
|
202
|
-
"git push origin tags/v1.0.0",
|
|
203
|
-
"git --git-dir .git push origin v1.0.0",
|
|
204
|
-
"gh workflow run deploy.yml \\\n -f environment=prod \\\n -f commit_sha=abc",
|
|
205
210
|
"railway up \\\n --environment prod",
|
|
206
|
-
"git push \\\n origin v1.0.0",
|
|
207
211
|
"railway up &>/dev/null -e prod",
|
|
208
|
-
|
|
209
|
-
"cd a\\\\\ngit push origin v1.0.0",
|
|
212
|
+
// deploy:end
|
|
210
213
|
];
|
|
211
214
|
|
|
212
215
|
const approvalLiftsGate = ["CHG-123", "CHG-1"];
|
|
@@ -229,10 +232,6 @@ const approvalKeepsGate = [
|
|
|
229
232
|
const nonProductionCommands = [
|
|
230
233
|
"gh workflow run deploy.yml -f environment=uat -f commit_sha=abc",
|
|
231
234
|
"gh api repos/o/r/actions/workflows/deploy.yml/dispatches -f 'inputs[environment]=uat'",
|
|
232
|
-
"railway up -e uat",
|
|
233
|
-
"railway up -e prod-eu",
|
|
234
|
-
"railway environment uat",
|
|
235
|
-
"railway status",
|
|
236
235
|
"gh workflow list",
|
|
237
236
|
"gh run list --workflow deploy.yml",
|
|
238
237
|
"gh api repos/o/r/environments/prod",
|
|
@@ -241,14 +240,20 @@ const nonProductionCommands = [
|
|
|
241
240
|
'git commit -m "release v1.0" && git push',
|
|
242
241
|
'git commit -m "docs(x): deploy to production"',
|
|
243
242
|
'gh pr create --title "ci(x): promote to prod" --body "run gh workflow run later"',
|
|
244
|
-
"echo railway",
|
|
245
243
|
"git tag v1.0.0",
|
|
246
244
|
'git stash push -m "wip v2"',
|
|
247
245
|
'git commit -m "fix(ci): push v2 images"',
|
|
248
|
-
'git commit -m "fix: railway env uat and prod split"',
|
|
249
246
|
"git push -u origin HEAD",
|
|
250
247
|
"git push origin feature/push-v2",
|
|
251
248
|
"pnpm verify 2>&1 | tail && git push",
|
|
249
|
+
// deploy:railway
|
|
250
|
+
"railway up -e uat",
|
|
251
|
+
"railway up -e prod-eu",
|
|
252
|
+
"railway environment uat",
|
|
253
|
+
"railway status",
|
|
254
|
+
"echo railway",
|
|
255
|
+
'git commit -m "fix: railway env uat and prod split"',
|
|
256
|
+
// deploy:end
|
|
252
257
|
];
|
|
253
258
|
|
|
254
259
|
const mcpTools = {
|
|
@@ -461,11 +466,11 @@ const cases: GateCase[] = [
|
|
|
461
466
|
expectedApproval: releaseApproval,
|
|
462
467
|
input: {
|
|
463
468
|
tool_input: {
|
|
464
|
-
command:
|
|
469
|
+
command: sampleProductionCommand,
|
|
465
470
|
},
|
|
466
471
|
},
|
|
467
472
|
expectedPermission: "allow" as const,
|
|
468
|
-
name: `production with RELEASE_APPROVAL="${releaseApproval}": ${
|
|
473
|
+
name: `production with RELEASE_APPROVAL="${releaseApproval}": ${sampleProductionCommand}`,
|
|
469
474
|
expectedExit: 0 as const,
|
|
470
475
|
})),
|
|
471
476
|
...approvalKeepsGate.map(releaseApproval => ({
|
|
@@ -473,10 +478,10 @@ const cases: GateCase[] = [
|
|
|
473
478
|
releaseApproval,
|
|
474
479
|
input: {
|
|
475
480
|
tool_input: {
|
|
476
|
-
command:
|
|
481
|
+
command: sampleProductionCommand,
|
|
477
482
|
},
|
|
478
483
|
},
|
|
479
|
-
name: `production with RELEASE_APPROVAL="${releaseApproval}": ${
|
|
484
|
+
name: `production with RELEASE_APPROVAL="${releaseApproval}": ${sampleProductionCommand}`,
|
|
480
485
|
expectedExit: 2 as const,
|
|
481
486
|
})),
|
|
482
487
|
...nonProductionCommands.map(command => ({
|
|
@@ -590,7 +595,7 @@ const cases: GateCase[] = [
|
|
|
590
595
|
name: "record: the conversation of a Cursor hook input",
|
|
591
596
|
expectedExit: 2,
|
|
592
597
|
input: {
|
|
593
|
-
command:
|
|
598
|
+
command: sampleProductionCommand,
|
|
594
599
|
conversation_id: "cursor-conversation",
|
|
595
600
|
},
|
|
596
601
|
},
|
|
@@ -617,7 +622,7 @@ const cases: GateCase[] = [
|
|
|
617
622
|
releaseApproval: "CHG-123",
|
|
618
623
|
expectedExit: 2,
|
|
619
624
|
input: {
|
|
620
|
-
command:
|
|
625
|
+
command: sampleProductionCommand,
|
|
621
626
|
},
|
|
622
627
|
},
|
|
623
628
|
{
|
|
@@ -636,7 +641,7 @@ const cases: GateCase[] = [
|
|
|
636
641
|
expectedExit: 2,
|
|
637
642
|
isRecordUnwritable: true,
|
|
638
643
|
input: {
|
|
639
|
-
command:
|
|
644
|
+
command: sampleProductionCommand,
|
|
640
645
|
},
|
|
641
646
|
},
|
|
642
647
|
];
|
|
@@ -118,7 +118,7 @@ jobs:
|
|
|
118
118
|
image:
|
|
119
119
|
name: Container Image
|
|
120
120
|
needs: changes
|
|
121
|
-
if: needs.changes.outputs.full_verification == 'true' || github.event_name == 'schedule'
|
|
121
|
+
if: (needs.changes.outputs.full_verification == 'true' || github.event_name == 'schedule') && vars.CONTAINER_IMAGE != 'false'
|
|
122
122
|
runs-on: ubuntu-latest
|
|
123
123
|
timeout-minutes: 15
|
|
124
124
|
env:
|
|
@@ -157,6 +157,7 @@ jobs:
|
|
|
157
157
|
tar --extract --gzip --file "$archive" trivy
|
|
158
158
|
./trivy image --exit-code 1 --severity HIGH,CRITICAL --ignore-unfixed --no-progress app:ci
|
|
159
159
|
|
|
160
|
+
# deploy:railway
|
|
160
161
|
deploy-dev:
|
|
161
162
|
name: Deploy to dev
|
|
162
163
|
needs:
|
|
@@ -171,6 +172,7 @@ jobs:
|
|
|
171
172
|
environment: dev
|
|
172
173
|
commit_sha: ${{ github.sha }}
|
|
173
174
|
|
|
175
|
+
# deploy:end
|
|
174
176
|
triage:
|
|
175
177
|
name: Triage failed build
|
|
176
178
|
needs:
|
|
@@ -34,8 +34,18 @@ CI also runs the build, the end-to-end tests, the container image scan, and the
|
|
|
34
34
|
|
|
35
35
|
## Container
|
|
36
36
|
|
|
37
|
+
<!-- deploy:railway -->
|
|
38
|
+
|
|
39
|
+
The `Dockerfile` builds a production image that runs as a non-root user (UID 1000). The platform must set `PORT`; Railway sets it automatically. The build needs `pnpm-lock.yaml`, so run `pnpm install` once before the first `docker build`.
|
|
40
|
+
|
|
41
|
+
<!-- deploy:end -->
|
|
42
|
+
|
|
43
|
+
<!-- deploy:none -->
|
|
44
|
+
|
|
37
45
|
The `Dockerfile` builds a production image that runs as a non-root user (UID 1000). The platform must set `PORT`. The build needs `pnpm-lock.yaml`, so run `pnpm install` once before the first `docker build`.
|
|
38
46
|
|
|
47
|
+
<!-- deploy:end -->
|
|
48
|
+
|
|
39
49
|
```sh
|
|
40
50
|
docker build -t app .
|
|
41
51
|
docker run --env PORT=3000 --publish 3000:3000 app
|
|
@@ -58,6 +68,8 @@ docker run --env PORT=3000 --publish 3000:3000 app
|
|
|
58
68
|
3. Set the repository variable `DEPLOY_ENABLED` to `true`. Each push to `main` that passes CI then deploys to `dev`.
|
|
59
69
|
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`.
|
|
60
70
|
|
|
71
|
+
Remove the workflow, `railway.json`, and the `deploy-dev` job in `ci.yml` if the project deploys elsewhere.
|
|
72
|
+
|
|
61
73
|
<!-- deploy:end -->
|
|
62
74
|
|
|
63
75
|
<!-- deploy:none -->
|
|
@@ -124,7 +124,7 @@ jobs:
|
|
|
124
124
|
image:
|
|
125
125
|
name: Container Image
|
|
126
126
|
needs: changes
|
|
127
|
-
if: needs.changes.outputs.full_verification == 'true' || github.event_name == 'schedule'
|
|
127
|
+
if: (needs.changes.outputs.full_verification == 'true' || github.event_name == 'schedule') && vars.CONTAINER_IMAGE != 'false'
|
|
128
128
|
runs-on: ubuntu-latest
|
|
129
129
|
timeout-minutes: 15
|
|
130
130
|
env:
|
|
@@ -163,6 +163,7 @@ jobs:
|
|
|
163
163
|
tar --extract --gzip --file "$archive" trivy
|
|
164
164
|
./trivy image --exit-code 1 --severity HIGH,CRITICAL --ignore-unfixed --no-progress app:ci
|
|
165
165
|
|
|
166
|
+
# deploy:railway
|
|
166
167
|
deploy-dev:
|
|
167
168
|
name: Deploy to dev
|
|
168
169
|
needs:
|
|
@@ -177,6 +178,7 @@ jobs:
|
|
|
177
178
|
environment: dev
|
|
178
179
|
commit_sha: ${{ github.sha }}
|
|
179
180
|
|
|
181
|
+
# deploy:end
|
|
180
182
|
triage:
|
|
181
183
|
name: Triage failed build
|
|
182
184
|
needs:
|
|
@@ -32,8 +32,18 @@ CI also runs the build, the end-to-end tests, the container image scan, and the
|
|
|
32
32
|
|
|
33
33
|
## Container
|
|
34
34
|
|
|
35
|
+
<!-- deploy:railway -->
|
|
36
|
+
|
|
37
|
+
The `Dockerfile` builds a standalone production image that runs as the `node` user, for platforms such as Railway. The server listens on `PORT`, or 3000 when it is unset.
|
|
38
|
+
|
|
39
|
+
<!-- deploy:end -->
|
|
40
|
+
|
|
41
|
+
<!-- deploy:none -->
|
|
42
|
+
|
|
35
43
|
The `Dockerfile` builds a standalone production image that runs as the `node` user for a container platform. The server listens on `PORT`, or 3000 when it is unset.
|
|
36
44
|
|
|
45
|
+
<!-- deploy:end -->
|
|
46
|
+
|
|
37
47
|
```sh
|
|
38
48
|
docker build -t app .
|
|
39
49
|
docker run --publish 3000:3000 app
|
|
@@ -58,6 +68,8 @@ On Vercel, deploy without the image; the Next.js preset takes precedence over th
|
|
|
58
68
|
3. Set the repository variable `DEPLOY_ENABLED` to `true`. Each push to `main` that passes CI then deploys to `dev`.
|
|
59
69
|
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`.
|
|
60
70
|
|
|
71
|
+
Remove the workflow, `railway.json`, and the `deploy-dev` job in `ci.yml` if the project deploys elsewhere.
|
|
72
|
+
|
|
61
73
|
<!-- deploy:end -->
|
|
62
74
|
|
|
63
75
|
<!-- deploy:none -->
|
|
@@ -116,7 +116,7 @@ jobs:
|
|
|
116
116
|
image:
|
|
117
117
|
name: Container Image
|
|
118
118
|
needs: changes
|
|
119
|
-
if: needs.changes.outputs.full_verification == 'true' || github.event_name == 'schedule'
|
|
119
|
+
if: (needs.changes.outputs.full_verification == 'true' || github.event_name == 'schedule') && vars.CONTAINER_IMAGE != 'false'
|
|
120
120
|
runs-on: ubuntu-latest
|
|
121
121
|
timeout-minutes: 15
|
|
122
122
|
env:
|
|
@@ -155,6 +155,7 @@ jobs:
|
|
|
155
155
|
tar --extract --gzip --file "$archive" trivy
|
|
156
156
|
./trivy image --exit-code 1 --severity HIGH,CRITICAL --ignore-unfixed --no-progress app:ci
|
|
157
157
|
|
|
158
|
+
# deploy:railway
|
|
158
159
|
deploy-dev:
|
|
159
160
|
name: Deploy to dev
|
|
160
161
|
needs:
|
|
@@ -169,6 +170,7 @@ jobs:
|
|
|
169
170
|
environment: dev
|
|
170
171
|
commit_sha: ${{ github.sha }}
|
|
171
172
|
|
|
173
|
+
# deploy:end
|
|
172
174
|
triage:
|
|
173
175
|
name: Triage failed build
|
|
174
176
|
needs:
|
|
@@ -32,8 +32,18 @@ CI also runs the build, the container image scan, and the security scans, includ
|
|
|
32
32
|
|
|
33
33
|
## Container
|
|
34
34
|
|
|
35
|
+
<!-- deploy:railway -->
|
|
36
|
+
|
|
37
|
+
The `Dockerfile` builds a production image that runs as a non-root user (UID 1000). The platform must set `PORT`; Railway sets it automatically. The build needs `pnpm-lock.yaml`, so run `pnpm install` once before the first `docker build`.
|
|
38
|
+
|
|
39
|
+
<!-- deploy:end -->
|
|
40
|
+
|
|
41
|
+
<!-- deploy:none -->
|
|
42
|
+
|
|
35
43
|
The `Dockerfile` builds a production image that runs as a non-root user (UID 1000). The platform must set `PORT`. The build needs `pnpm-lock.yaml`, so run `pnpm install` once before the first `docker build`.
|
|
36
44
|
|
|
45
|
+
<!-- deploy:end -->
|
|
46
|
+
|
|
37
47
|
```sh
|
|
38
48
|
docker build -t app .
|
|
39
49
|
docker run --env PORT=3000 --publish 3000:3000 app
|
|
@@ -56,6 +66,8 @@ docker run --env PORT=3000 --publish 3000:3000 app
|
|
|
56
66
|
3. Set the repository variable `DEPLOY_ENABLED` to `true`. Each push to `main` that passes CI then deploys to `dev`.
|
|
57
67
|
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`.
|
|
58
68
|
|
|
69
|
+
Remove the workflow, `railway.json`, and the `deploy-dev` job in `ci.yml` if the project deploys elsewhere.
|
|
70
|
+
|
|
59
71
|
<!-- deploy:end -->
|
|
60
72
|
|
|
61
73
|
<!-- deploy:none -->
|
|
@@ -141,7 +141,7 @@ jobs:
|
|
|
141
141
|
image:
|
|
142
142
|
name: Container Image
|
|
143
143
|
needs: changes
|
|
144
|
-
if: needs.changes.outputs.full_verification == 'true' || github.event_name == 'schedule'
|
|
144
|
+
if: (needs.changes.outputs.full_verification == 'true' || github.event_name == 'schedule') && vars.CONTAINER_IMAGE != 'false'
|
|
145
145
|
runs-on: ubuntu-latest
|
|
146
146
|
timeout-minutes: 15
|
|
147
147
|
services:
|
|
@@ -196,6 +196,7 @@ jobs:
|
|
|
196
196
|
tar --extract --gzip --file "$archive" trivy
|
|
197
197
|
./trivy image --exit-code 1 --severity HIGH,CRITICAL --ignore-unfixed --no-progress app:ci
|
|
198
198
|
|
|
199
|
+
# deploy:railway
|
|
199
200
|
deploy-dev:
|
|
200
201
|
name: Deploy to dev
|
|
201
202
|
needs:
|
|
@@ -210,6 +211,7 @@ jobs:
|
|
|
210
211
|
environment: dev
|
|
211
212
|
commit_sha: ${{ github.sha }}
|
|
212
213
|
|
|
214
|
+
# deploy:end
|
|
213
215
|
triage:
|
|
214
216
|
name: Triage failed build
|
|
215
217
|
needs:
|
|
@@ -48,8 +48,18 @@ CI also runs the build, the end-to-end tests, the container image scan, and the
|
|
|
48
48
|
|
|
49
49
|
## Container
|
|
50
50
|
|
|
51
|
+
<!-- deploy:railway -->
|
|
52
|
+
|
|
53
|
+
The `Dockerfile` builds a standalone production image that runs as the `node` user, for platforms such as Railway. The server listens on `PORT`, or 3000 when it is unset, and needs `DATABASE_URL`, `PAYLOAD_SECRET`, and `APP_URL`, the public URL that Payload accepts cookie-authenticated and cross-origin requests from.
|
|
54
|
+
|
|
55
|
+
<!-- deploy:end -->
|
|
56
|
+
|
|
57
|
+
<!-- deploy:none -->
|
|
58
|
+
|
|
51
59
|
The `Dockerfile` builds a standalone production image that runs as the `node` user for a container platform. The server listens on `PORT`, or 3000 when it is unset, and needs `DATABASE_URL`, `PAYLOAD_SECRET`, and `APP_URL`, the public URL that Payload accepts cookie-authenticated and cross-origin requests from.
|
|
52
60
|
|
|
61
|
+
<!-- deploy:end -->
|
|
62
|
+
|
|
53
63
|
```sh
|
|
54
64
|
docker build -t app .
|
|
55
65
|
docker run --publish 3000:3000 --env DATABASE_URL --env PAYLOAD_SECRET --env APP_URL app
|
|
@@ -84,6 +94,8 @@ On Vercel, deploy without the image; the Next.js preset takes precedence over th
|
|
|
84
94
|
3. Set the repository variable `DEPLOY_ENABLED` to `true`. Each push to `main` that passes CI then deploys to `dev`.
|
|
85
95
|
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`.
|
|
86
96
|
|
|
97
|
+
Remove the workflow, `railway.json`, and the `deploy-dev` job in `ci.yml` if the project deploys elsewhere.
|
|
98
|
+
|
|
87
99
|
<!-- deploy:end -->
|
|
88
100
|
|
|
89
101
|
<!-- deploy:none -->
|
|
@@ -212,7 +212,7 @@ jobs:
|
|
|
212
212
|
needs:
|
|
213
213
|
- changes
|
|
214
214
|
- affected-apps
|
|
215
|
-
if: (needs.changes.outputs.full_verification == 'true' || github.event_name == 'schedule') && needs.affected-apps.outputs.apps != '[]'
|
|
215
|
+
if: (needs.changes.outputs.full_verification == 'true' || github.event_name == 'schedule') && needs.affected-apps.outputs.apps != '[]' && vars.CONTAINER_IMAGE != 'false'
|
|
216
216
|
runs-on: ubuntu-latest
|
|
217
217
|
timeout-minutes: 20
|
|
218
218
|
strategy:
|
|
@@ -272,6 +272,7 @@ jobs:
|
|
|
272
272
|
tar --extract --gzip --file "$archive" trivy
|
|
273
273
|
./trivy image --exit-code 1 --severity HIGH,CRITICAL --ignore-unfixed --no-progress "$APP:ci"
|
|
274
274
|
|
|
275
|
+
# deploy:railway
|
|
275
276
|
deploy-dev:
|
|
276
277
|
name: Deploy to dev
|
|
277
278
|
needs:
|
|
@@ -286,6 +287,7 @@ jobs:
|
|
|
286
287
|
environment: dev
|
|
287
288
|
commit_sha: ${{ github.sha }}
|
|
288
289
|
|
|
290
|
+
# deploy:end
|
|
289
291
|
triage:
|
|
290
292
|
name: Triage failed build
|
|
291
293
|
needs:
|
|
@@ -86,6 +86,8 @@ On Vercel, deploy an app without its image. The Next.js preset takes precedence
|
|
|
86
86
|
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 commit of the last successful deploy to that environment, found from the successful `deploy.yml` and `ci.yml` runs on `main`, 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.
|
|
87
87
|
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.
|
|
88
88
|
|
|
89
|
+
Remove the workflow, `railway.json`, and the `deploy-dev` job in `ci.yml` if the project deploys elsewhere.
|
|
90
|
+
|
|
89
91
|
<!-- deploy:end -->
|
|
90
92
|
|
|
91
93
|
<!-- deploy:none -->
|
|
@@ -108,6 +110,16 @@ To add an Expo app, create it under `apps/mobile`:
|
|
|
108
110
|
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/)).
|
|
109
111
|
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/)).
|
|
110
112
|
|
|
113
|
+
<!-- deploy:railway -->
|
|
114
|
+
|
|
115
|
+
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.
|
|
116
|
+
|
|
117
|
+
<!-- deploy:end -->
|
|
118
|
+
|
|
119
|
+
<!-- deploy:none -->
|
|
120
|
+
|
|
111
121
|
EAS Build and Submit deploy that app, in place of the project's deploy step. CI `--affected` already includes `apps/*` and `packages/*`, so an Expo app is skipped when it did not change.
|
|
112
122
|
|
|
123
|
+
<!-- deploy:end -->
|
|
124
|
+
|
|
113
125
|
See [CONTRIBUTING.md](CONTRIBUTING.md) for the contribution workflow.
|
|
File without changes
|
|
File without changes
|