@codepassion/skills 1.4.0 → 1.5.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.
package/package.json
CHANGED
package/skills/c9n/SKILL.md
CHANGED
|
@@ -271,12 +271,20 @@ already owns, not a file drop (see Phase 2).
|
|
|
271
271
|
|
|
272
272
|
Run in order; this is a table of contents — rules are below.
|
|
273
273
|
|
|
274
|
-
1. **Detect (Phase 1).**
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
274
|
+
1. **Detect (Phase 1).** First decide the repo shape:
|
|
275
|
+
- **Monorepo** — `apps/` and/or `turbo.json` exist. Scan `apps/*/package.json`, classify each:
|
|
276
|
+
`next`→web, `elysia`→api, else→service. `{{APP_DIR}}` = `apps/<name>`.
|
|
277
|
+
- **Single-app** — no `apps/`, no `turbo.json`; the deployable app is the repo root. Classify the
|
|
278
|
+
root `package.json` (`next`→web, etc.). `{{APP_DIR}}` = `.`, `{{PACKAGE_NAME}}` = the repo name
|
|
279
|
+
(no `-web` suffix needed), and the **single-app template variants** apply (see "Single-app repos"
|
|
280
|
+
below) — drop the workspace-manifest copies, `outputFileTracingRoot`, `transpilePackages`, and
|
|
281
|
+
the `apps/web/` path segments. The generated `ci.yml` also drops Turbo (see that template's note).
|
|
282
|
+
|
|
283
|
+
Then detect DB by **real artifacts** — a migration-generate script in some `package.json` AND a
|
|
284
|
+
migrations dir on disk — capture their actual names; never assume `drizzle:generate` /
|
|
285
|
+
`packages/supabase/migrations`. Read the app's `package.json` for the Bun version / lockfile name
|
|
286
|
+
(but **pin ≥ 1.3.10** — see `{{BUN_VERSION}}`). Ask which environments exist (Alpha / Beta /
|
|
287
|
+
Production). **Print the detected plan and confirm before writing anything.**
|
|
280
288
|
2. **Generate (Phase 2).** Copy templates with tokens substituted. **Diff/confirm per file** — if a
|
|
281
289
|
target exists, show the diff and ask overwrite/skip/merge; never blind-clobber; leave unrelated
|
|
282
290
|
files (a hand-kept `staging.yaml`) alone. Then do the **web edits** (below). Run the web build
|
|
@@ -291,11 +299,11 @@ Run in order; this is a table of contents — rules are below.
|
|
|
291
299
|
| Token | Source |
|
|
292
300
|
|-------|--------|
|
|
293
301
|
| `{{PACKAGE_NAME}}` | `<project>-<appkey>`, e.g. `smiley-web`. Lowercase. This is the ghcr image name and the `package_name` workflow input. |
|
|
294
|
-
| `{{APP_DIR}}` | The app's path, e.g. `apps/web`. |
|
|
302
|
+
| `{{APP_DIR}}` | The app's path, e.g. `apps/web`. **Single-app repo: `.`** (the app is the repo root — this leaves valid `./.next/...` paths in the template). |
|
|
295
303
|
| `{{APP_KEY}}` | Short lowercase id unique per app: `api`, `web`, `worker`. Used in job names. |
|
|
296
304
|
| `{{APP_PORT}}` | `3001` api · `3000` web · service's listen port (drop `EXPOSE` for a portless worker). |
|
|
297
|
-
| `{{BUN_VERSION}}` | Pin
|
|
298
|
-
| `{{WORKSPACE_MANIFEST_COPIES}}` | **web template only:** one `COPY <app>/package.json ./<app>/package.json` line **per app** — the `deps` stage runs `bun install` before `COPY . .`, so it needs every workspace manifest to resolve the graph. The api/service compile templates copy only the built app's manifest (matches Smiley's api Dockerfile; avoids pulling a sibling app's postinstall without its src). |
|
|
305
|
+
| `{{BUN_VERSION}}` | **Pin ≥ 1.3.10** (same across all Dockerfiles). Do **not** copy the host's `bun --version` — 1.2.x breaks `next build` on Linux with `TypeError: Expected CommonJS module to have a function wrapper` (edge/jest-worker; only shows on Linux, not macOS). |
|
|
306
|
+
| `{{WORKSPACE_MANIFEST_COPIES}}` | **web template only:** one `COPY <app>/package.json ./<app>/package.json` line **per app** — the `deps` stage runs `bun install` before `COPY . .`, so it needs every workspace manifest to resolve the graph. The api/service compile templates copy only the built app's manifest (matches Smiley's api Dockerfile; avoids pulling a sibling app's postinstall without its src). **Single-app repo: empty string** (root `package.json` + `bun.lock` are already copied; there is no `packages/` — also delete the `COPY packages/ ./packages/` line). |
|
|
299
307
|
| `{{MIGRATION_CHECK}}` | `_migration-check.partial.yml` with `{{MIGRATION_SCRIPT}}`/`{{MIGRATIONS_DIR}}` filled, **or empty string** if no DB. |
|
|
300
308
|
| `{{ORG_REF}}` | The org reusable-workflow ref — currently `turborepo`. One place; bump here if the org repo retags. |
|
|
301
309
|
| `{{BUILD_JOBS}}` / `{{DEPLOY_JOBS}}` | Stamp `_build-job.partial` once per app (Alpha only) and `_deploy-job.partial` once per app per environment, pruning unused envs. |
|
|
@@ -306,17 +314,46 @@ Run in order; this is a table of contents — rules are below.
|
|
|
306
314
|
For each **web** app, edit the files the dev owns; fail loudly if they can't be parsed rather than
|
|
307
315
|
overwrite:
|
|
308
316
|
|
|
309
|
-
- `next.config.js` — add `output: "standalone"
|
|
310
|
-
`transpilePackages` for the workspace packages the app imports.
|
|
317
|
+
- `next.config.js` — add `output: "standalone"`. **Monorepo only:** also add `outputFileTracingRoot`
|
|
318
|
+
(monorepo root) and `transpilePackages` for the workspace packages the app imports. **Single-app:
|
|
319
|
+
omit both** (root app traces natively; no workspace packages).
|
|
311
320
|
- `app/layout.tsx` — add `<head><PublicEnvScript /></head>` (`import { PublicEnvScript } from
|
|
312
|
-
"next-runtime-env"`).
|
|
313
|
-
|
|
321
|
+
"next-runtime-env"`).
|
|
322
|
+
- **Convert EVERY `process.env.NEXT_PUBLIC_*` read to `env("NEXT_PUBLIC_X")`** — not just client
|
|
323
|
+
components. Next **inlines every `process.env.NEXT_PUBLIC_*` literal at build time in SERVER code and
|
|
324
|
+
middleware too**, so any read left as `process.env` bakes the build value (or a build-time
|
|
325
|
+
placeholder) into the server/middleware bundle and runtime injection can't override it. This is the
|
|
326
|
+
#1 time-sink: a stray `process.env.NEXT_PUBLIC_SUPABASE_URL` in `middleware.ts` bakes the placeholder
|
|
327
|
+
→ `supabase.auth.getUser()` fails → **auth redirect loop** that only shows in the container, not
|
|
328
|
+
locally. Convert: client components, **server components, route handlers (`app/api/**`), middleware,
|
|
329
|
+
and any t3-env `runtimeEnv` mapping**. Non-public server vars (`DATABASE_URL`, `*_SERVICE_KEY`) stay
|
|
330
|
+
on `process.env` — they aren't inlined and are present at runtime.
|
|
331
|
+
- **Phase-2 gate — must pass before you call the web edits done:**
|
|
332
|
+
`grep -rn 'process.env.NEXT_PUBLIC' {{APP_DIR}}` (the app dir — `.` for single-app, `apps/web` for a
|
|
333
|
+
monorepo app; not every repo has a `src/`) returns **nothing**. Any hit is a latent runtime bug.
|
|
314
334
|
- Flag `next-runtime-env` as a dependency to add — **do not run `bun add` yourself**; tell the dev.
|
|
315
335
|
- **Caveat:** runtime values only reach the browser on non-statically-prerendered pages. A page that
|
|
316
336
|
reads runtime public env must opt out of static caching (`export const dynamic = "force-dynamic"`)
|
|
317
337
|
or rely on `<PublicEnvScript>` keeping the document dynamic. Note this so future feature work
|
|
318
338
|
doesn't silently bake a static page.
|
|
319
339
|
|
|
340
|
+
## Single-app repos (no `apps/`, no `turbo.json`)
|
|
341
|
+
|
|
342
|
+
The templates are monorepo-shaped; one set of token substitutions makes them serve a root-level single
|
|
343
|
+
app. Apply these deltas (verified on `ddg-jewelry-admin`):
|
|
344
|
+
|
|
345
|
+
- **Tokens:** `{{APP_DIR}}` = `.`, `{{PACKAGE_NAME}}` = repo name (no `-web` suffix),
|
|
346
|
+
`{{WORKSPACE_MANIFEST_COPIES}}` = empty.
|
|
347
|
+
- **`Dockerfile.web`:** delete the `COPY packages/ ./packages/` line (no `packages/`). The runner
|
|
348
|
+
`COPY` lines collapse to `.next/standalone ./`, `.next/static ./.next/static`, `public ./public`
|
|
349
|
+
(the `{{APP_DIR}}=.` substitution yields valid `./.next/...` — fine to simplify).
|
|
350
|
+
- **`next.config`:** `output: "standalone"` only — no `outputFileTracingRoot`, no `transpilePackages`.
|
|
351
|
+
- **`ci.yml`:** drop the Turbo `env:`, the `.turbo` cache step, and swap `bun run check-types` for
|
|
352
|
+
`bunx tsc --noEmit` if the repo has no `check-types` script (single apps usually don't).
|
|
353
|
+
- **Runner (optional but recommended):** a single Next app deploys cleanly on a Node runner
|
|
354
|
+
(`node:22-alpine` or `gcr.io/distroless/nodejs22-debian12`, `CMD ["server.js"]`). Distroless cuts the
|
|
355
|
+
image ~4× and drops the runner CVEs (seen on `ddg-jewelry-admin`).
|
|
356
|
+
|
|
320
357
|
## Phase 3 — interactive ops checklist (the deliverable)
|
|
321
358
|
|
|
322
359
|
Derived from the actual `codepassion-team/cicd` reusable workflows. Walk these one at a time:
|
|
@@ -342,8 +379,14 @@ visibility), or the image pull silently fails.
|
|
|
342
379
|
**Web runtime env in Coolify** (the scars):
|
|
343
380
|
- `PORT=3000` — Next standalone obeys `PORT`; Coolify defaults it to `80`, which 502s behind a
|
|
344
381
|
Caddy `:3000` proxy. This is the single most common web-deploy failure.
|
|
345
|
-
- `
|
|
382
|
+
- **Coolify `Ports Exposes` (General page) is a SEPARATE setting from the `PORT` env** — it defaults
|
|
383
|
+
to `80` and must be set to the app port (`3000`). With `PORT=3000` but Ports Exposes still `80`, the
|
|
384
|
+
deploy log warns `PORT (3000) does not match configured ports_exposes: 80` and the proxy 502s. Fix
|
|
385
|
+
both.
|
|
386
|
+
- `HOSTNAME=0.0.0.0` — the Dockerfile already bakes this; only set it if something overrides it.
|
|
346
387
|
- The app's `NEXT_PUBLIC_*` values — injected here at runtime, not baked into the image.
|
|
388
|
+
- **Set ALL env values UNQUOTED** (public and server alike) — Coolify passes values literally, so a
|
|
389
|
+
quoted `DATABASE_URL="…"` keeps the quotes and breaks `new URL()` (`ERR_INVALID_URL`).
|
|
347
390
|
|
|
348
391
|
## Phase 4 — verify
|
|
349
392
|
|
|
@@ -31,12 +31,20 @@ already owns, not a file drop (see Phase 2).
|
|
|
31
31
|
|
|
32
32
|
Run in order; this is a table of contents — rules are below.
|
|
33
33
|
|
|
34
|
-
1. **Detect (Phase 1).**
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
34
|
+
1. **Detect (Phase 1).** First decide the repo shape:
|
|
35
|
+
- **Monorepo** — `apps/` and/or `turbo.json` exist. Scan `apps/*/package.json`, classify each:
|
|
36
|
+
`next`→web, `elysia`→api, else→service. `{{APP_DIR}}` = `apps/<name>`.
|
|
37
|
+
- **Single-app** — no `apps/`, no `turbo.json`; the deployable app is the repo root. Classify the
|
|
38
|
+
root `package.json` (`next`→web, etc.). `{{APP_DIR}}` = `.`, `{{PACKAGE_NAME}}` = the repo name
|
|
39
|
+
(no `-web` suffix needed), and the **single-app template variants** apply (see "Single-app repos"
|
|
40
|
+
below) — drop the workspace-manifest copies, `outputFileTracingRoot`, `transpilePackages`, and
|
|
41
|
+
the `apps/web/` path segments. The generated `ci.yml` also drops Turbo (see that template's note).
|
|
42
|
+
|
|
43
|
+
Then detect DB by **real artifacts** — a migration-generate script in some `package.json` AND a
|
|
44
|
+
migrations dir on disk — capture their actual names; never assume `drizzle:generate` /
|
|
45
|
+
`packages/supabase/migrations`. Read the app's `package.json` for the Bun version / lockfile name
|
|
46
|
+
(but **pin ≥ 1.3.10** — see `{{BUN_VERSION}}`). Ask which environments exist (Alpha / Beta /
|
|
47
|
+
Production). **Print the detected plan and confirm before writing anything.**
|
|
40
48
|
2. **Generate (Phase 2).** Copy templates with tokens substituted. **Diff/confirm per file** — if a
|
|
41
49
|
target exists, show the diff and ask overwrite/skip/merge; never blind-clobber; leave unrelated
|
|
42
50
|
files (a hand-kept `staging.yaml`) alone. Then do the **web edits** (below). Run the web build
|
|
@@ -51,11 +59,11 @@ Run in order; this is a table of contents — rules are below.
|
|
|
51
59
|
| Token | Source |
|
|
52
60
|
|-------|--------|
|
|
53
61
|
| `{{PACKAGE_NAME}}` | `<project>-<appkey>`, e.g. `smiley-web`. Lowercase. This is the ghcr image name and the `package_name` workflow input. |
|
|
54
|
-
| `{{APP_DIR}}` | The app's path, e.g. `apps/web`. |
|
|
62
|
+
| `{{APP_DIR}}` | The app's path, e.g. `apps/web`. **Single-app repo: `.`** (the app is the repo root — this leaves valid `./.next/...` paths in the template). |
|
|
55
63
|
| `{{APP_KEY}}` | Short lowercase id unique per app: `api`, `web`, `worker`. Used in job names. |
|
|
56
64
|
| `{{APP_PORT}}` | `3001` api · `3000` web · service's listen port (drop `EXPOSE` for a portless worker). |
|
|
57
|
-
| `{{BUN_VERSION}}` | Pin
|
|
58
|
-
| `{{WORKSPACE_MANIFEST_COPIES}}` | **web template only:** one `COPY <app>/package.json ./<app>/package.json` line **per app** — the `deps` stage runs `bun install` before `COPY . .`, so it needs every workspace manifest to resolve the graph. The api/service compile templates copy only the built app's manifest (matches Smiley's api Dockerfile; avoids pulling a sibling app's postinstall without its src). |
|
|
65
|
+
| `{{BUN_VERSION}}` | **Pin ≥ 1.3.10** (same across all Dockerfiles). Do **not** copy the host's `bun --version` — 1.2.x breaks `next build` on Linux with `TypeError: Expected CommonJS module to have a function wrapper` (edge/jest-worker; only shows on Linux, not macOS). |
|
|
66
|
+
| `{{WORKSPACE_MANIFEST_COPIES}}` | **web template only:** one `COPY <app>/package.json ./<app>/package.json` line **per app** — the `deps` stage runs `bun install` before `COPY . .`, so it needs every workspace manifest to resolve the graph. The api/service compile templates copy only the built app's manifest (matches Smiley's api Dockerfile; avoids pulling a sibling app's postinstall without its src). **Single-app repo: empty string** (root `package.json` + `bun.lock` are already copied; there is no `packages/` — also delete the `COPY packages/ ./packages/` line). |
|
|
59
67
|
| `{{MIGRATION_CHECK}}` | `_migration-check.partial.yml` with `{{MIGRATION_SCRIPT}}`/`{{MIGRATIONS_DIR}}` filled, **or empty string** if no DB. |
|
|
60
68
|
| `{{ORG_REF}}` | The org reusable-workflow ref — currently `turborepo`. One place; bump here if the org repo retags. |
|
|
61
69
|
| `{{BUILD_JOBS}}` / `{{DEPLOY_JOBS}}` | Stamp `_build-job.partial` once per app (Alpha only) and `_deploy-job.partial` once per app per environment, pruning unused envs. |
|
|
@@ -66,17 +74,46 @@ Run in order; this is a table of contents — rules are below.
|
|
|
66
74
|
For each **web** app, edit the files the dev owns; fail loudly if they can't be parsed rather than
|
|
67
75
|
overwrite:
|
|
68
76
|
|
|
69
|
-
- `next.config.js` — add `output: "standalone"
|
|
70
|
-
`transpilePackages` for the workspace packages the app imports.
|
|
77
|
+
- `next.config.js` — add `output: "standalone"`. **Monorepo only:** also add `outputFileTracingRoot`
|
|
78
|
+
(monorepo root) and `transpilePackages` for the workspace packages the app imports. **Single-app:
|
|
79
|
+
omit both** (root app traces natively; no workspace packages).
|
|
71
80
|
- `app/layout.tsx` — add `<head><PublicEnvScript /></head>` (`import { PublicEnvScript } from
|
|
72
|
-
"next-runtime-env"`).
|
|
73
|
-
|
|
81
|
+
"next-runtime-env"`).
|
|
82
|
+
- **Convert EVERY `process.env.NEXT_PUBLIC_*` read to `env("NEXT_PUBLIC_X")`** — not just client
|
|
83
|
+
components. Next **inlines every `process.env.NEXT_PUBLIC_*` literal at build time in SERVER code and
|
|
84
|
+
middleware too**, so any read left as `process.env` bakes the build value (or a build-time
|
|
85
|
+
placeholder) into the server/middleware bundle and runtime injection can't override it. This is the
|
|
86
|
+
#1 time-sink: a stray `process.env.NEXT_PUBLIC_SUPABASE_URL` in `middleware.ts` bakes the placeholder
|
|
87
|
+
→ `supabase.auth.getUser()` fails → **auth redirect loop** that only shows in the container, not
|
|
88
|
+
locally. Convert: client components, **server components, route handlers (`app/api/**`), middleware,
|
|
89
|
+
and any t3-env `runtimeEnv` mapping**. Non-public server vars (`DATABASE_URL`, `*_SERVICE_KEY`) stay
|
|
90
|
+
on `process.env` — they aren't inlined and are present at runtime.
|
|
91
|
+
- **Phase-2 gate — must pass before you call the web edits done:**
|
|
92
|
+
`grep -rn 'process.env.NEXT_PUBLIC' {{APP_DIR}}` (the app dir — `.` for single-app, `apps/web` for a
|
|
93
|
+
monorepo app; not every repo has a `src/`) returns **nothing**. Any hit is a latent runtime bug.
|
|
74
94
|
- Flag `next-runtime-env` as a dependency to add — **do not run `bun add` yourself**; tell the dev.
|
|
75
95
|
- **Caveat:** runtime values only reach the browser on non-statically-prerendered pages. A page that
|
|
76
96
|
reads runtime public env must opt out of static caching (`export const dynamic = "force-dynamic"`)
|
|
77
97
|
or rely on `<PublicEnvScript>` keeping the document dynamic. Note this so future feature work
|
|
78
98
|
doesn't silently bake a static page.
|
|
79
99
|
|
|
100
|
+
## Single-app repos (no `apps/`, no `turbo.json`)
|
|
101
|
+
|
|
102
|
+
The templates are monorepo-shaped; one set of token substitutions makes them serve a root-level single
|
|
103
|
+
app. Apply these deltas (verified on `ddg-jewelry-admin`):
|
|
104
|
+
|
|
105
|
+
- **Tokens:** `{{APP_DIR}}` = `.`, `{{PACKAGE_NAME}}` = repo name (no `-web` suffix),
|
|
106
|
+
`{{WORKSPACE_MANIFEST_COPIES}}` = empty.
|
|
107
|
+
- **`Dockerfile.web`:** delete the `COPY packages/ ./packages/` line (no `packages/`). The runner
|
|
108
|
+
`COPY` lines collapse to `.next/standalone ./`, `.next/static ./.next/static`, `public ./public`
|
|
109
|
+
(the `{{APP_DIR}}=.` substitution yields valid `./.next/...` — fine to simplify).
|
|
110
|
+
- **`next.config`:** `output: "standalone"` only — no `outputFileTracingRoot`, no `transpilePackages`.
|
|
111
|
+
- **`ci.yml`:** drop the Turbo `env:`, the `.turbo` cache step, and swap `bun run check-types` for
|
|
112
|
+
`bunx tsc --noEmit` if the repo has no `check-types` script (single apps usually don't).
|
|
113
|
+
- **Runner (optional but recommended):** a single Next app deploys cleanly on a Node runner
|
|
114
|
+
(`node:22-alpine` or `gcr.io/distroless/nodejs22-debian12`, `CMD ["server.js"]`). Distroless cuts the
|
|
115
|
+
image ~4× and drops the runner CVEs (seen on `ddg-jewelry-admin`).
|
|
116
|
+
|
|
80
117
|
## Phase 3 — interactive ops checklist (the deliverable)
|
|
81
118
|
|
|
82
119
|
Derived from the actual `codepassion-team/cicd` reusable workflows. Walk these one at a time:
|
|
@@ -102,8 +139,14 @@ visibility), or the image pull silently fails.
|
|
|
102
139
|
**Web runtime env in Coolify** (the scars):
|
|
103
140
|
- `PORT=3000` — Next standalone obeys `PORT`; Coolify defaults it to `80`, which 502s behind a
|
|
104
141
|
Caddy `:3000` proxy. This is the single most common web-deploy failure.
|
|
105
|
-
- `
|
|
142
|
+
- **Coolify `Ports Exposes` (General page) is a SEPARATE setting from the `PORT` env** — it defaults
|
|
143
|
+
to `80` and must be set to the app port (`3000`). With `PORT=3000` but Ports Exposes still `80`, the
|
|
144
|
+
deploy log warns `PORT (3000) does not match configured ports_exposes: 80` and the proxy 502s. Fix
|
|
145
|
+
both.
|
|
146
|
+
- `HOSTNAME=0.0.0.0` — the Dockerfile already bakes this; only set it if something overrides it.
|
|
106
147
|
- The app's `NEXT_PUBLIC_*` values — injected here at runtime, not baked into the image.
|
|
148
|
+
- **Set ALL env values UNQUOTED** (public and server alike) — Coolify passes values literally, so a
|
|
149
|
+
quoted `DATABASE_URL="…"` keeps the quotes and breaks `new URL()` (`ERR_INVALID_URL`).
|
|
107
150
|
|
|
108
151
|
## Phase 4 — verify
|
|
109
152
|
|
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
# Public env is read at RUNTIME via next-runtime-env (<PublicEnvScript>), so this
|
|
4
4
|
# image bakes no NEXT_PUBLIC_* — Coolify injects them at run, like the API image.
|
|
5
5
|
# Generated by c9n-pipeline; see the ADR this skill points you to.
|
|
6
|
+
#
|
|
7
|
+
# {{BUN_VERSION}} must be >= 1.3.10 — 1.2.x breaks `next build` on Linux.
|
|
8
|
+
# Single-app repo: {{APP_DIR}}=. and {{WORKSPACE_MANIFEST_COPIES}}= empty; also
|
|
9
|
+
# delete the `COPY packages/ ./packages/` line below (no packages/ dir). See the
|
|
10
|
+
# "Single-app repos" section in SKILL.md.
|
|
6
11
|
# -----------------------------------------------------------------------------
|
|
7
12
|
|
|
8
13
|
FROM oven/bun:{{BUN_VERSION}} AS base
|
|
@@ -34,5 +34,17 @@ jobs:
|
|
|
34
34
|
- name: Check types
|
|
35
35
|
run: bun run check-types
|
|
36
36
|
- name: Build
|
|
37
|
-
|
|
37
|
+
# Placeholder NEXT_PUBLIC_* values satisfy module-init that reads them at import
|
|
38
|
+
# time (e.g. Supabase clients) — CI has no .env files, so without these
|
|
39
|
+
# `next build` throws "Error: supabaseUrl is required" collecting page
|
|
40
|
+
# data. Real values are injected at runtime via next-runtime-env, never
|
|
41
|
+
# baked. Mirrors the Dockerfile build stage. Add one line per public var
|
|
42
|
+
# the app constructs a client from at import.
|
|
43
|
+
run: SKIP_ENV_VALIDATION=1 bun run build
|
|
44
|
+
env:
|
|
45
|
+
NEXT_PUBLIC_SUPABASE_URL: http://placeholder.invalid
|
|
46
|
+
NEXT_PUBLIC_SUPABASE_ANON_KEY: placeholder
|
|
38
47
|
{{MIGRATION_CHECK}}
|
|
48
|
+
# Single-app repos (no turbo.json): remove the TURBO_TOKEN/TURBO_TEAM `env:` and
|
|
49
|
+
# the `.turbo` cache step above, and swap `bun run check-types` for
|
|
50
|
+
# `bunx tsc --noEmit` if the repo has no check-types script.
|