@decocms/start 2.28.2 → 2.30.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 (49) hide show
  1. package/.agents/skills/deco-to-tanstack-migration/SKILL.md +1 -1
  2. package/.agents/skills/deco-to-tanstack-migration/references/vtex-commerce.md +5 -1
  3. package/.agents/skills/deco-to-tanstack-migration/references/worker-cloudflare.md +107 -10
  4. package/.agents/skills/deco-to-tanstack-migration/templates/package-json.md +5 -1
  5. package/.cursor/rules/migration-tooling-policy.mdc +22 -2
  6. package/.github/workflows/deploy.yml +115 -0
  7. package/.github/workflows/preview.yml +143 -0
  8. package/.github/workflows/regen-blocks.yml +56 -0
  9. package/.github/workflows/release.yml +26 -0
  10. package/.github/workflows/sync-secrets.yml +173 -0
  11. package/CODEOWNERS +16 -0
  12. package/MIGRATION_TOOLING_PLAN.md +16 -4
  13. package/README.md +178 -79
  14. package/deploy/README.md +85 -0
  15. package/deploy/sites/als-tanstack.jsonc +7 -0
  16. package/deploy/sites/americanas-tanstack.jsonc +4 -0
  17. package/deploy/sites/baggagio-tanstack.jsonc +4 -0
  18. package/deploy/sites/casaevideo-storefront.jsonc +11 -0
  19. package/deploy/sites/lebiscuit-tanstack.jsonc +19 -0
  20. package/deploy/sites/miess-01-tanstack.jsonc +8 -0
  21. package/deploy/wrangler-template.jsonc +28 -0
  22. package/package.json +18 -15
  23. package/scripts/deploy/build-wrangler-config.mjs +49 -0
  24. package/scripts/deploy/jsonc.mjs +76 -0
  25. package/scripts/deploy/resolve-site.mjs +58 -0
  26. package/scripts/deploy/site-registry.mjs +142 -0
  27. package/scripts/deploy/wrangler-wrapper.mjs +126 -0
  28. package/scripts/migrate/phase-scaffold.ts +13 -3
  29. package/scripts/migrate/phase-verify.ts +6 -1
  30. package/scripts/migrate/templates/github-workflows.ts +98 -0
  31. package/scripts/migrate/templates/package-json.ts +9 -2
  32. package/src/cms/resolve.ts +81 -63
  33. package/src/cms/sectionLoaders.ts +11 -0
  34. package/src/index.ts +3 -0
  35. package/src/sdk/cachedLoader.ts +36 -13
  36. package/src/sdk/composite.test.ts +121 -0
  37. package/src/sdk/composite.ts +114 -0
  38. package/src/sdk/instrumentedFetch.ts +56 -0
  39. package/src/sdk/logger.test.ts +135 -0
  40. package/src/sdk/logger.ts +166 -0
  41. package/src/sdk/observability.ts +75 -0
  42. package/src/sdk/otel.test.ts +59 -0
  43. package/src/sdk/otel.ts +270 -29
  44. package/src/sdk/otelAdapters.test.ts +135 -0
  45. package/src/sdk/otelAdapters.ts +401 -0
  46. package/src/sdk/sampler.test.ts +127 -0
  47. package/src/sdk/sampler.ts +183 -0
  48. package/src/sdk/workerEntry.ts +541 -476
  49. package/scripts/migrate/templates/wrangler.ts +0 -30
@@ -315,7 +315,7 @@ See: `references/search.md`
315
315
  5. **`tsconfig.json` mirrors `vite.config.ts`** -- only `"~/*": ["./src/*"]` in paths
316
316
  6. **Signals don't auto-subscribe in React** -- reading `signal.value` in render creates NO subscription; use `useStore(signal.store)` from `@tanstack/react-store`
317
317
  7. **Commerce loaders need request context** -- `resolve.ts` must pass URL/path to PLP/PDP loaders
318
- 8. **`wrangler.jsonc` main must be a custom worker-entry** -- TanStack Start ignores `export default` in `server.ts`
318
+ 8. **`wrangler.jsonc` main must be a custom worker-entry** -- TanStack Start ignores `export default` in `server.ts`. The `main` field lives in the **central** `decocms/deco-start/deploy/wrangler-template.jsonc` (D6); site repos do not commit `wrangler.jsonc`.
319
319
  9. **Copy components faithfully, never rewrite** -- `cp` the original, then only change mechanical things (class→className, imports). NEVER regenerate or "improve" — AI-rewritten components are the #1 source of visual regressions
320
320
  10. **Tailwind v4 logical property hazard** -- mixed `px-*` + `pl-*/pr-*` on the same element breaks the cascade
321
321
  11. **oklch CSS variables need triplets, not hex** -- `oklch(var(--x))` must store variables as oklch triplets
@@ -36,7 +36,11 @@ This lets the core component render while gracefully degrading features that dep
36
36
 
37
37
  ## 7. VTEX API Auth on Cloudflare Workers
38
38
 
39
- Env vars must be set via `wrangler secret put` or `.dev.vars`, not `.env`.
39
+ Env vars are stored as `SECRET_*` GitHub repo secrets (e.g. `SECRET_VTEX_APP_KEY`)
40
+ and pushed to the worker via the centralized `Sync worker secrets` workflow
41
+ (D6). Locally, use `.dev.vars` (gitignored) for development. Do not run
42
+ `npx wrangler secret put` per-site — the central workflow keeps GitHub and
43
+ Cloudflare in sync.
40
44
 
41
45
 
42
46
  ## 8. Cookie Handling
@@ -37,7 +37,7 @@ TanStack Start's Cloudflare adapter **completely ignores** the `export default`
37
37
 
38
38
  **Diagnosis**: Search the built `dist/server/worker-entry-*.js` bundle for your custom code (e.g., `X-Cache`, `caches.open`, `_cache/purge`). If absent, TanStack stripped it.
39
39
 
40
- **Fix**: Create a **separate** `src/worker-entry.ts` file that wraps TanStack Start's built handler. Point `wrangler.jsonc` to this file instead of `@tanstack/react-start/server-entry`.
40
+ **Fix**: Create a **separate** `src/worker-entry.ts` file that wraps TanStack Start's built handler. Wrangler is told to use this file via `main: "./src/worker-entry.ts"` in the **canonical wrangler template** at `decocms/deco-start/deploy/wrangler-template.jsonc` (D6) — sites do not configure this themselves.
41
41
 
42
42
  ```typescript
43
43
  // src/worker-entry.ts
@@ -57,13 +57,10 @@ export default createDecoWorkerEntry(serverEntry, {
57
57
  });
58
58
  ```
59
59
 
60
- ```jsonc
61
- // wrangler.jsonc -- MUST point to custom entry, NOT the default
62
- {
63
- "main": "./src/worker-entry.ts",
64
- // NOT: "main": "@tanstack/react-start/server-entry"
65
- }
66
- ```
60
+ The `main` field is set centrally so a future migration of the entry path
61
+ applies to every site at once (single PR to the template). If you ever need to
62
+ override `main` for a single site, add it under `deploy/sites/<repo>.jsonc` —
63
+ never to a per-site `wrangler.jsonc` (sites don't commit one; see D6).
67
64
 
68
65
  This ensures admin route interception AND edge caching survive the build because they're in the Worker's own fetch handler, outside of TanStack's build pipeline.
69
66
 
@@ -136,9 +133,14 @@ Imports like `import Color from "npm:colorjs.io"` use the Deno-specific `npm:` p
136
133
  After deploying a new build to Cloudflare Workers, the edge cache may still serve old HTML that references previous JS bundle hashes. This causes module import failures.
137
134
 
138
135
  **Fix**: After every deploy, purge the cache:
139
- 1. Set a `PURGE_TOKEN` secret: `npx wrangler secret put PURGE_TOKEN`
136
+ 1. Set a `PURGE_TOKEN` secret. Add `SECRET_PURGE_TOKEN` to the site repo's
137
+ GitHub Secrets, then trigger the centralized `Sync worker secrets`
138
+ workflow (`workflow_dispatch` → `apply`). This pushes it to the Cloudflare
139
+ worker via `wrangler secret put PURGE_TOKEN`. **Do not** run
140
+ `npx wrangler secret put` manually per-site — the central workflow keeps
141
+ GitHub and Cloudflare in sync.
140
142
  2. Call the purge endpoint: `POST /_cache/purge` with `Authorization: Bearer <token>` and body `{"paths":["/"]}`
141
- 3. Automate this in CI/CD (see the deploy.yml workflow)
143
+ 3. The right place to automate this is the **central** `deco-start/.github/workflows/deploy.yml` (D6) so every site picks it up at once. Do not add site-local deploy.yml steps; site repos hold only ~5-line caller workflows.
142
144
 
143
145
 
144
146
  ## 44. Runtime Module Import Kills Lazy-Loaded Sections
@@ -207,3 +209,98 @@ Or in project `.npmrc` with an env var (for CI):
207
209
  ```
208
210
 
209
211
  **Tradeoff with `github:` syntax**: No semver resolution — `npm update` is meaningless. Pin to a tag for stability: `github:decocms/deco-start#v0.14.2`. Without a tag, you get HEAD of the default branch.
212
+
213
+
214
+ ## 46. Central Deploy / Wrangler Config (D6)
215
+
216
+ **Severity**: HIGH — site repos must NOT commit `wrangler.jsonc` or per-site deploy logic. Doing so reintroduces drift.
217
+
218
+ Per [D6](../../../../.cursor/rules/migration-tooling-policy.mdc), all
219
+ storefronts deploy via reusable workflows shipped from
220
+ `decocms/deco-start/.github/workflows/{deploy,preview,sync-secrets,regen-blocks}.yml@v2`.
221
+ The canonical `wrangler.jsonc` lives at
222
+ `decocms/deco-start/deploy/wrangler-template.jsonc`; per-site overrides live at
223
+ `decocms/deco-start/deploy/sites/<repo-name>.jsonc`. The two are deep-merged at
224
+ deploy time and written to a generated `wrangler.jsonc` in the runner; site
225
+ repos gitignore the file.
226
+
227
+ ### What goes in the site repo
228
+
229
+ Four ~5-line caller workflow stubs and nothing else:
230
+
231
+ ```yaml
232
+ # .github/workflows/deploy.yml
233
+ name: Deploy
234
+ on:
235
+ push:
236
+ branches: [main]
237
+ permissions:
238
+ contents: write
239
+ jobs:
240
+ deploy:
241
+ uses: decocms/deco-start/.github/workflows/deploy.yml@v2
242
+ secrets: inherit
243
+ ```
244
+
245
+ (Plus equivalent `preview.yml`, `regen-blocks.yml`, `sync-secrets.yml` —
246
+ see `scripts/migrate/templates/github-workflows.ts` for the canonical text.)
247
+ The migration script generates these for new sites; the same stubs are
248
+ hand-applied to existing sites.
249
+
250
+ ### What goes in `decocms/deco-start/deploy/sites/<repo>.jsonc`
251
+
252
+ The minimum:
253
+
254
+ ```jsonc
255
+ {
256
+ "worker_name": "<repo-name>"
257
+ }
258
+ ```
259
+
260
+ Plus optional `routes`, `kv_namespaces`, `analytics_engine_datasets`,
261
+ `version_metadata` for the few sites that need them. Adding a new site is a
262
+ PR to deco-start, not a change to the site repo.
263
+
264
+ ### Local dev
265
+
266
+ Site repos add three package.json hooks so vite picks up the generated
267
+ `wrangler.jsonc`:
268
+
269
+ ```jsonc
270
+ "scripts": {
271
+ "gen:wrangler": "deco-wrangler gen",
272
+ "predev": "deco-wrangler gen",
273
+ "prebuild": "deco-wrangler gen",
274
+ "types": "deco-wrangler types",
275
+ "deploy": "echo 'Production deploys are managed by .github/workflows/deploy.yml on push to main. For an emergency manual deploy run: npx deco-wrangler deploy'; exit 1"
276
+ }
277
+ ```
278
+
279
+ `deco-wrangler` is a `bin` shipped from `@decocms/start` that materializes the
280
+ canonical config from the central registry, then either exits (`gen` mode) or
281
+ execs the real `wrangler` with that config in cwd.
282
+
283
+ ### Trust model
284
+
285
+ - The central workflow ignores all caller `inputs:` for site identity.
286
+ - Site name is derived from `${{ github.repository }}` (set by GitHub,
287
+ untamperable by user code) and looked up in `deploy/sites/<repo>.jsonc`.
288
+ - A customer cannot misroute their deploy onto another site's worker
289
+ because they can't write to `decocms/deco-start` (CODEOWNERS-protected).
290
+
291
+ ### Common mistakes (do not do these)
292
+
293
+ - **Committing `wrangler.jsonc` to a site repo.** Generated only;
294
+ always gitignored. If you see it tracked, the site missed migration.
295
+ - **Adding a site-local `deploy.yml` step** (e.g. cache purge after deploy).
296
+ Add it to `deco-start/.github/workflows/deploy.yml` instead so every site
297
+ picks it up at once.
298
+ - **Hard-coding `account_id` in a site's wrangler config.** It comes from
299
+ `CLOUDFLARE_ACCOUNT_ID` (org-level GitHub secret in CI; `wrangler login`
300
+ locally). Removing it from JSON is the one-way protection against
301
+ accidentally deploying to the wrong account.
302
+ - **Setting `worker_name` to anything other than the repo name** without
303
+ a strong reason. The 1:1 binding makes audit (and incident response)
304
+ trivial. Exceptions today: `casaevideo-storefront` -> `casaevideo-tanstack`
305
+ and `miess-01-tanstack` -> `miess-tanstack` (both for historical
306
+ Cloudflare worker names that predate the repo).
@@ -12,7 +12,11 @@
12
12
  "generate:blocks": "tsx node_modules/@decocms/start/scripts/generate-blocks.ts",
13
13
  "generate:invoke": "tsx node_modules/@decocms/start/scripts/generate-invoke.ts",
14
14
  "generate:schema": "tsx node_modules/@decocms/start/scripts/generate-schema.ts --site storefront",
15
- "deploy": "wrangler deploy"
15
+ "gen:wrangler": "deco-wrangler gen",
16
+ "predev": "deco-wrangler gen",
17
+ "prebuild": "deco-wrangler gen",
18
+ "types": "deco-wrangler types",
19
+ "deploy": "echo 'Production deploys are managed by .github/workflows/deploy.yml on push to main. See D6.'; exit 1"
16
20
  },
17
21
  "dependencies": {
18
22
  "@decocms/apps": "^0.20.1",
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: Constitutional decisions for the deco-start migration-tooling effort (D1–D5, priorities, process). Always loaded.
2
+ description: Constitutional decisions for the deco-start migration-tooling effort (D1–D6, priorities, process). Always loaded.
3
3
  alwaysApply: true
4
4
  ---
5
5
 
@@ -14,7 +14,7 @@ summary; always defer to the plan when they conflict.
14
14
  1. **Design for 100 sites, not 3.** When the surface of an abstraction is
15
15
  well-understood, ship it. Don't defer waiting for "more signal" — design
16
16
  for the projection. When the surface is *not* understood, *decide*
17
- explicitly (write a D-record like D1–D5), don't ship fast.
17
+ explicitly (write a D-record like D1–D6), don't ship fast.
18
18
  2. **Strict over flexible.** No support layers, no soft drift, no
19
19
  fork-runtime adapters. Every fork divergence becomes either a PR back to
20
20
  canonical or visible local debt.
@@ -76,6 +76,25 @@ Failure modes get documented in skills, not encoded as escape hatches.
76
76
  **Agent behavior**: when a migration goes sideways, propose deletion +
77
77
  re-run, not in-place repair. Add the failure mode to the skill.
78
78
 
79
+ ### D6 — Deploy / preview / secrets pipelines: centralize in `deco-start` (signed off 2026-05-07)
80
+ All storefronts deploy via reusable workflows under
81
+ `decocms/deco-start/.github/workflows/{deploy,preview,sync-secrets,regen-blocks}.yml@v2`.
82
+ Per-site overrides live in [`deploy/sites/<repo>.jsonc`](../../deploy/) and
83
+ are deep-merged on top of [`deploy/wrangler-template.jsonc`](../../deploy/wrangler-template.jsonc)
84
+ at deploy time. **Customer repos do not commit `wrangler.jsonc`** — it is
85
+ generated locally by `deco-wrangler gen` (a `bin` shipped from
86
+ `@decocms/start`) and gitignored. The repo→worker binding is the trust
87
+ boundary: the central workflow ignores caller `inputs:` for identity and
88
+ derives the site name from `${{ github.repository }}`. `deploy/**` is
89
+ CODEOWNERS-protected.
90
+
91
+ **Agent behavior**: when adding or migrating a site, do **not** generate a
92
+ per-site `wrangler.jsonc` or per-site `deploy.yml` / `preview.yml` /
93
+ `sync-secrets.yml` / `regen-blocks.yml`. The site repo gets the four
94
+ ~5-line caller stubs only. Per-site config goes in a PR to
95
+ `decocms/deco-start` adding `deploy/sites/<repo>.jsonc`. Do not commit
96
+ `wrangler.jsonc` or any wrangler config to a site repo.
97
+
79
98
  ## Priority order
80
99
 
81
100
  Work proceeds in this order. Higher priorities don't block on lower ones,
@@ -109,3 +128,4 @@ order — but only if explicitly identified as urgent.
109
128
  - When reading a section of the plan or a skill, prefer the plan or skill
110
129
  over your memory of past conversations.
111
130
  - Do not run `deco-deploy` or any deploy command from agent flows.
131
+ - Do not commit `wrangler.jsonc` to a site repo. See **D6** above.
@@ -0,0 +1,115 @@
1
+ name: deploy (central)
2
+
3
+ # Reusable workflow that drives `wrangler deploy` for any storefront repo
4
+ # registered under `deploy/sites/<repo-name>.jsonc`.
5
+ #
6
+ # Site identity (worker name, KV bindings, routes, ...) is derived ONLY from
7
+ # `${{ github.repository }}` and the registry checked in to deco-start. The
8
+ # caller cannot pass an `inputs:` block to override the worker name -- this is
9
+ # the trust boundary that prevents one site's commits from deploying on top of
10
+ # another site's worker.
11
+ #
12
+ # Caller usage (in customer repo, `.github/workflows/deploy.yml`):
13
+ #
14
+ # on:
15
+ # push:
16
+ # branches: [main]
17
+ # permissions:
18
+ # contents: write
19
+ # jobs:
20
+ # deploy:
21
+ # uses: decocms/deco-start/.github/workflows/deploy.yml@v2
22
+ # secrets: inherit
23
+
24
+ on:
25
+ workflow_call:
26
+ secrets:
27
+ CLOUDFLARE_API_TOKEN:
28
+ required: true
29
+ CLOUDFLARE_ACCOUNT_ID:
30
+ required: true
31
+
32
+ permissions:
33
+ contents: write
34
+
35
+ concurrency:
36
+ group: deploy-${{ github.repository }}
37
+ cancel-in-progress: false
38
+
39
+ jobs:
40
+ deploy:
41
+ runs-on: ubuntu-latest
42
+ steps:
43
+ - name: Checkout caller repo
44
+ uses: actions/checkout@v4
45
+
46
+ - name: Resolve deco-start ref + site identity
47
+ id: meta
48
+ run: |
49
+ # github.workflow_ref looks like
50
+ # decocms/deco-start/.github/workflows/deploy.yml@refs/tags/v1
51
+ # or
52
+ # decocms/deco-start/.github/workflows/deploy.yml@refs/heads/main
53
+ WF_REF="${{ github.workflow_ref }}"
54
+ REF="${WF_REF##*@}"
55
+ REF="${REF#refs/tags/}"
56
+ REF="${REF#refs/heads/}"
57
+ echo "deco_start_ref=$REF" >> "$GITHUB_OUTPUT"
58
+ echo "site_name=${GITHUB_REPOSITORY#*/}" >> "$GITHUB_OUTPUT"
59
+
60
+ - name: Checkout deco-start registry at the same ref
61
+ uses: actions/checkout@v4
62
+ with:
63
+ repository: decocms/deco-start
64
+ ref: ${{ steps.meta.outputs.deco_start_ref }}
65
+ path: .deco-start
66
+
67
+ - uses: actions/setup-node@v4
68
+ with:
69
+ node-version: 22
70
+
71
+ - name: Generate lockfile if missing
72
+ if: hashFiles('package-lock.json') == ''
73
+ run: npm install --package-lock-only
74
+
75
+ - name: Restore npm cache
76
+ uses: actions/cache@v4
77
+ with:
78
+ path: ~/.npm
79
+ key: npm-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
80
+ restore-keys: npm-${{ runner.os }}-
81
+
82
+ - name: Sync lockfile and install
83
+ run: |
84
+ npm install --package-lock-only
85
+ if ! git diff --quiet package-lock.json 2>/dev/null; then
86
+ git config user.name "github-actions[bot]"
87
+ git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
88
+ git add package-lock.json
89
+ git commit -m "chore: sync package-lock.json"
90
+ git push || echo "Lockfile push failed (remote ahead); proceeding with deploy"
91
+ fi
92
+ npm ci
93
+
94
+ - name: Build
95
+ run: npm run build
96
+
97
+ - name: Resolve site manifest
98
+ id: site
99
+ run: node .deco-start/scripts/deploy/resolve-site.mjs
100
+ env:
101
+ DECO_START_PATH: .deco-start
102
+ SITE_NAME: ${{ steps.meta.outputs.site_name }}
103
+
104
+ - name: Generate wrangler.jsonc
105
+ run: node .deco-start/scripts/deploy/build-wrangler-config.mjs
106
+ env:
107
+ DECO_START_PATH: .deco-start
108
+ SITE_NAME: ${{ steps.meta.outputs.site_name }}
109
+ OUTPUT_PATH: ./wrangler.jsonc
110
+
111
+ - name: Deploy to Cloudflare Workers
112
+ run: npx wrangler deploy --var BUILD_HASH:$(git rev-parse --short HEAD)
113
+ env:
114
+ CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
115
+ CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
@@ -0,0 +1,143 @@
1
+ name: preview (central)
2
+
3
+ # Reusable workflow that uploads a preview version (alias) for any storefront
4
+ # repo registered under `deploy/sites/<repo-name>.jsonc`.
5
+ #
6
+ # Caller usage (in customer repo, `.github/workflows/preview.yml`):
7
+ #
8
+ # on:
9
+ # repository_dispatch:
10
+ # types: [preview-deploy]
11
+ # pull_request:
12
+ # types: [opened, synchronize, reopened]
13
+ # push:
14
+ # branches: ['env/**']
15
+ # permissions:
16
+ # contents: read
17
+ # pull-requests: write
18
+ # statuses: write
19
+ # jobs:
20
+ # preview:
21
+ # uses: decocms/deco-start/.github/workflows/preview.yml@v2
22
+ # secrets: inherit
23
+
24
+ on:
25
+ workflow_call:
26
+ secrets:
27
+ CLOUDFLARE_API_TOKEN:
28
+ required: true
29
+ CLOUDFLARE_ACCOUNT_ID:
30
+ required: true
31
+
32
+ permissions:
33
+ contents: read
34
+ pull-requests: write
35
+ statuses: write
36
+
37
+ concurrency:
38
+ group: preview-${{ github.repository }}-${{ github.event.client_payload.ref || github.head_ref || github.ref_name }}
39
+ cancel-in-progress: true
40
+
41
+ jobs:
42
+ preview:
43
+ runs-on: ubuntu-latest
44
+ steps:
45
+ - name: Resolve ref
46
+ id: resolve
47
+ run: |
48
+ if [ "${{ github.event_name }}" = "repository_dispatch" ]; then
49
+ echo "ref=${{ github.event.client_payload.ref }}" >> "$GITHUB_OUTPUT"
50
+ else
51
+ echo "ref=${{ github.head_ref || github.ref_name }}" >> "$GITHUB_OUTPUT"
52
+ fi
53
+
54
+ - uses: actions/checkout@v4
55
+ with:
56
+ ref: ${{ steps.resolve.outputs.ref }}
57
+
58
+ - name: Resolve deco-start ref + site identity
59
+ id: meta
60
+ run: |
61
+ WF_REF="${{ github.workflow_ref }}"
62
+ REF="${WF_REF##*@}"
63
+ REF="${REF#refs/tags/}"
64
+ REF="${REF#refs/heads/}"
65
+ echo "deco_start_ref=$REF" >> "$GITHUB_OUTPUT"
66
+ echo "site_name=${GITHUB_REPOSITORY#*/}" >> "$GITHUB_OUTPUT"
67
+
68
+ - name: Checkout deco-start registry at the same ref
69
+ uses: actions/checkout@v4
70
+ with:
71
+ repository: decocms/deco-start
72
+ ref: ${{ steps.meta.outputs.deco_start_ref }}
73
+ path: .deco-start
74
+
75
+ - uses: actions/setup-node@v4
76
+ with:
77
+ node-version: 22
78
+
79
+ - name: Compute preview alias
80
+ id: alias
81
+ run: |
82
+ REF="${{ steps.resolve.outputs.ref }}"
83
+ if echo "$REF" | grep -q '^env/'; then
84
+ ALIAS=$(echo "$REF" | sed 's|^env/||')
85
+ elif [ "${{ github.event_name }}" = "pull_request" ]; then
86
+ ALIAS="pr-${{ github.event.pull_request.number }}"
87
+ else
88
+ ALIAS=$(echo "$REF" | sed 's|[^a-z0-9-]|-|g')
89
+ fi
90
+ echo "alias=$ALIAS" >> "$GITHUB_OUTPUT"
91
+
92
+ - name: Install dependencies
93
+ run: npm install
94
+
95
+ - name: Build
96
+ run: npm run build
97
+
98
+ - name: Resolve site manifest
99
+ id: site
100
+ run: node .deco-start/scripts/deploy/resolve-site.mjs
101
+ env:
102
+ DECO_START_PATH: .deco-start
103
+ SITE_NAME: ${{ steps.meta.outputs.site_name }}
104
+
105
+ - name: Generate wrangler.jsonc
106
+ run: node .deco-start/scripts/deploy/build-wrangler-config.mjs
107
+ env:
108
+ DECO_START_PATH: .deco-start
109
+ SITE_NAME: ${{ steps.meta.outputs.site_name }}
110
+ OUTPUT_PATH: ./wrangler.jsonc
111
+
112
+ - name: Upload preview version
113
+ id: deploy
114
+ run: |
115
+ set +e
116
+ OUTPUT=$(npx wrangler versions upload --preview-alias ${{ steps.alias.outputs.alias }} 2>&1)
117
+ EXIT_CODE=$?
118
+ set -e
119
+ echo "$OUTPUT"
120
+ if [ $EXIT_CODE -ne 0 ]; then
121
+ echo "::error::wrangler versions upload failed with exit code $EXIT_CODE"
122
+ exit $EXIT_CODE
123
+ fi
124
+ PREVIEW_URL=$(echo "$OUTPUT" | grep 'Version Preview URL:' | sed 's/.*Version Preview URL: //')
125
+ ALIAS_URL=$(echo "$OUTPUT" | grep 'Version Preview Alias URL:' | sed 's/.*Version Preview Alias URL: //')
126
+ echo "preview_url=${PREVIEW_URL}" >> "$GITHUB_OUTPUT"
127
+ echo "alias_url=${ALIAS_URL}" >> "$GITHUB_OUTPUT"
128
+ env:
129
+ CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
130
+ CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
131
+
132
+ - name: Comment preview URL on PR
133
+ if: github.event_name == 'pull_request'
134
+ uses: marocchino/sticky-pull-request-comment@v2
135
+ with:
136
+ header: preview-url
137
+ message: |
138
+ ### Preview deployed
139
+
140
+ | | URL |
141
+ |---|---|
142
+ | **Version** | ${{ steps.deploy.outputs.preview_url }} |
143
+ | **Alias** | ${{ steps.deploy.outputs.alias_url }} |
@@ -0,0 +1,56 @@
1
+ name: regen-blocks (central)
2
+
3
+ # Reusable workflow. Regenerates `src/server/cms/blocks.gen.json` and commits
4
+ # the result back to the caller branch. Runs in the caller's runner context;
5
+ # uses bun because that's what the existing `generate:blocks` script expects.
6
+ #
7
+ # Caller usage (in customer repo, `.github/workflows/regen-blocks.yml`):
8
+ #
9
+ # on:
10
+ # push:
11
+ # branches: [main]
12
+ # paths: [".deco/blocks/**"]
13
+ # permissions:
14
+ # contents: write
15
+ # jobs:
16
+ # regen:
17
+ # uses: decocms/deco-start/.github/workflows/regen-blocks.yml@v2
18
+ # secrets: inherit
19
+
20
+ on:
21
+ workflow_call: {}
22
+
23
+ permissions:
24
+ contents: write
25
+
26
+ concurrency:
27
+ group: regen-blocks-${{ github.repository }}
28
+ cancel-in-progress: false
29
+
30
+ jobs:
31
+ regen:
32
+ runs-on: ubuntu-latest
33
+ steps:
34
+ - uses: actions/checkout@v4
35
+
36
+ - uses: oven-sh/setup-bun@v2
37
+ with:
38
+ bun-version: latest
39
+
40
+ - name: Install deps
41
+ run: bun install --frozen-lockfile
42
+
43
+ - name: Regenerate blocks.gen.json
44
+ run: bun run generate:blocks
45
+
46
+ - name: Commit and push if changed
47
+ run: |
48
+ if git diff --quiet src/server/cms/blocks.gen.json; then
49
+ echo "blocks.gen.json already up to date — nothing to commit."
50
+ exit 0
51
+ fi
52
+ git config user.name "github-actions[bot]"
53
+ git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
54
+ git add src/server/cms/blocks.gen.json
55
+ git commit -m "chore: regenerate blocks.gen.json after .deco sync"
56
+ git push
@@ -29,3 +29,29 @@ jobs:
29
29
  run: npx semantic-release
30
30
  env:
31
31
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
32
+
33
+ # Caller workflows in storefront repos pin to a moveable major tag (`@v2`),
34
+ # GitHub Actions convention. After every release we fast-forward that major
35
+ # tag to the latest exact tag in the same series. Idempotent: if no new
36
+ # release happened, this just re-points to the existing latest, which is a
37
+ # no-op force-push.
38
+ #
39
+ # Why not a separate workflow on `push: tags`? GitHub suppresses workflow
40
+ # triggers caused by the auto-issued `GITHUB_TOKEN` (anti-recursion guard),
41
+ # and `@semantic-release/git` pushes tags using exactly that token, so a
42
+ # listener would never fire. Inlining keeps the lifecycle visible in one
43
+ # job log.
44
+ - name: Advance moveable major tag
45
+ run: |
46
+ git fetch --tags --force
47
+ LATEST=$(git tag -l 'v*.*.*' --sort=-v:refname | head -n 1)
48
+ if [ -z "$LATEST" ]; then
49
+ echo "::notice::no v*.*.* tags yet; nothing to advance"
50
+ exit 0
51
+ fi
52
+ MAJOR=$(echo "$LATEST" | sed -E 's/^(v[0-9]+).*/\1/')
53
+ git config user.name "github-actions[bot]"
54
+ git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
55
+ git tag -f "$MAJOR" "$LATEST"
56
+ git push origin "$MAJOR" --force
57
+ echo "::notice::Moved $MAJOR -> $LATEST"