td-ai-tools 1.4.1 → 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 +1 -1
- package/skills/README.md +1 -0
- package/skills/cloudflare/SKILL.md +78 -0
- package/skills/cloudflare/agents/openai.yaml +4 -0
- package/skills/cloudflare/assets/deploy.yml +71 -0
- package/skills/cloudflare/assets/wrangler.jsonc +57 -0
- package/skills/cloudflare/references/auth-and-accounts.md +62 -0
- package/skills/cloudflare/references/ci-cd.md +48 -0
- package/skills/cloudflare/references/config-secrets-envs.md +55 -0
- package/skills/cloudflare/references/data-d1-kv-r2.md +96 -0
- package/skills/cloudflare/references/deploy-and-release.md +107 -0
- package/skills/cloudflare/references/domains-and-dns.md +99 -0
- package/skills/cloudflare/references/forms-email-turnstile.md +66 -0
- package/skills/cloudflare/references/launch-new-site.md +193 -0
- package/skills/cloudflare/references/observability-and-incidents.md +78 -0
- package/skills/cloudflare/references/site-lifecycle.md +68 -0
- package/skills/cloudflare/references/troubleshooting.md +71 -0
- package/skills/cloudflare/references/zone-settings.md +123 -0
- package/skills/cloudflare/scripts/cf-api.sh +58 -0
- package/skills/cloudflare/scripts/preflight.sh +83 -0
- package/skills/cloudflare/scripts/verify-site.sh +61 -0
- package/skills/a11y-audit/SKILL.md +0 -60
- package/skills/a11y-audit/agents/openai.yaml +0 -4
- package/skills/a11y-audit/package.json +0 -17
- package/skills/a11y-audit/scripts/audit.js +0 -624
- package/skills/a11y-audit/scripts/setup.sh +0 -6
- package/skills/barrage/SKILL.md +0 -238
- package/skills/barrage/agents/openai.yaml +0 -4
- package/skills/barrage/scripts/__pycache__/build_queue.cpython-312.pyc +0 -0
- package/skills/barrage/scripts/__pycache__/manage_queue.cpython-312.pyc +0 -0
- package/skills/barrage/scripts/__pycache__/queue_paths.cpython-312.pyc +0 -0
- package/skills/barrage/scripts/build_queue.py +0 -288
- package/skills/barrage/scripts/manage_queue.py +0 -230
- package/skills/barrage/scripts/queue_paths.py +0 -50
- package/skills/barrage/tests/test_build_queue.py +0 -148
- package/skills/barrage/tests/test_manage_queue.py +0 -176
- package/skills/barrage/tests/test_queue_paths.py +0 -97
- package/skills/cache-reset/SKILL.md +0 -22
- package/skills/cache-reset/agents/openai.yaml +0 -4
- package/skills/client-overview/SKILL.md +0 -93
- package/skills/client-overview/agents/openai.yaml +0 -4
- package/skills/client-overview/scripts/branch_client_overview_context.py +0 -317
- package/skills/stylesheet-migration/SKILL.md +0 -37
- package/skills/stylesheet-migration/agents/openai.yaml +0 -6
- package/skills/stylesheet-migration/scripts/liquid_stylesheet_migrator.py +0 -204
- package/skills/stylesheet-migration/scripts/migrate_stylesheet_tags.py +0 -172
- package/skills/stylesheet-migration/scripts/test_liquid_stylesheet_migrator.py +0 -254
- package/skills/td-js-vanilla-rules/SKILL.md +0 -71
- package/skills/td-js-vanilla-rules/agents/openai.yaml +0 -3
- package/skills/td-review/SKILL.md +0 -123
- package/skills/td-review/agents/openai.yaml +0 -4
- package/skills/td-review/agents/td-theme-reviewer.md +0 -221
package/package.json
CHANGED
package/skills/README.md
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
- `browser-validation`: Before completing a task validate frontend or template changes in a real browser with the Playwright-CLI…
|
|
8
8
|
- `cache-reset`: Clear and warm Laravel and Statamic caches (including Statamic Glide image caches) after content or template…
|
|
9
9
|
- `client-overview`: Generate a client-facing markdown report that summarizes all changes on the current branch against the…
|
|
10
|
+
- `cloudflare`: Operate Theory Digital's Cloudflare-hosted sites (Leyline / Astro on Workers) with wrangler and the…
|
|
10
11
|
- `debugging-ios-webkit`: Debugs iOS Safari/Chrome-iOS rendering bugs — stale paints, viewport/browser-chrome clipping, mobile-only CSS…
|
|
11
12
|
- `forge-cli`: Manage Laravel Forge organizations, servers, sites, and provisioned resources with Forge CLI 2.x, falling…
|
|
12
13
|
- `horizon-component-migration`: Bundle Shopify Horizon components into a migration package for a different theme, including recursive…
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cloudflare
|
|
3
|
+
version: 1.0.0
|
|
4
|
+
description: Operate Theory Digital's Cloudflare-hosted sites (Leyline / Astro on Workers) with wrangler and the Cloudflare API — launch a new site end to end, deploy and roll back, preview branches, manage secrets, D1/KV/R2, custom domains and DNS, zone settings (AI crawlers, redirects, caching, SSL), Turnstile, email routing, logs, CI/CD, account/profile switching, client handoff and decommissioning. Use this whenever the user mentions Cloudflare, wrangler, Workers, deploying/shipping/publishing a site, going live, a domain or DNS change, a rollback, a secret or env var, D1 migrations, "the site is down/500ing", tailing logs, or moving a site to a client's account — even if they don't say "Cloudflare" explicitly.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Cloudflare sites
|
|
8
|
+
|
|
9
|
+
Theory Digital sites are Astro apps using the Leyline integration, built with `@astrojs/cloudflare` and deployed as **Workers with static assets** (not Pages). Some sites live in the Theory Digital account, some in the client's own account. This skill is the operating manual: SKILL.md gives the ground rules and routes you to one focused reference file per task.
|
|
10
|
+
|
|
11
|
+
## 1. Orient before acting (always)
|
|
12
|
+
|
|
13
|
+
Run the read-only preflight from the site's root. It reports wrangler version, who is logged in, the active auth profile, the Worker name, `account_id`, routes/domains, bindings, and git state:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
bash <skill-dir>/scripts/preflight.sh [site-dir]
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
From that output, answer three questions before any remote write:
|
|
20
|
+
|
|
21
|
+
1. **Which account?** The config's `account_id` (or the profile bound to this directory) must match the client this site belongs to. A mismatch is the most damaging mistake available here: it can create a duplicate Worker, or attach a domain, in the wrong client's account. If `account_id` is missing and the login can see more than one account, stop and set it. See `references/auth-and-accounts.md`.
|
|
22
|
+
2. **Which Worker / environment?** Read `name` and any `env.*` blocks. `--env staging` deploys a *different* Worker (`<name>-staging`).
|
|
23
|
+
3. **Is the tree clean and built from the right commit?** Deploying uncommitted work makes the deploy hard to reproduce or roll back to.
|
|
24
|
+
|
|
25
|
+
## 2. Guardrails: confirm production writes
|
|
26
|
+
|
|
27
|
+
Do the following freely. They only read, or only touch local or preview resources:
|
|
28
|
+
`whoami`, `deployments list/status`, `versions list/view`, `tail`, `d1 ... --local`, `d1 execute --remote` with a SELECT, `secret list`, `deploy --dry-run`, `wrangler preview`, `astro build`, `wrangler dev`, any `GET` through `cf-api.sh`, and `verify-site.sh`.
|
|
29
|
+
|
|
30
|
+
**Before** the following, state what will happen (account, Worker or zone, and the exact command) and get a yes from the user:
|
|
31
|
+
- `wrangler deploy` or `versions deploy` to production, and `rollback`
|
|
32
|
+
- `d1 migrations apply --remote`, `d1 execute --remote` that writes, `d1 time-travel restore`
|
|
33
|
+
- `secret put/delete/bulk` against a deployed Worker
|
|
34
|
+
- Creating, changing or deleting DNS records, zone settings, rules, custom domains or routes (any non-GET `cf-api.sh` call)
|
|
35
|
+
- Any `delete` (Worker, D1, KV, R2, Turnstile widget, zone)
|
|
36
|
+
|
|
37
|
+
Approval covers the action that was described, not similar actions later in the session. If the user has said up front "deploy it, don't ask", that counts as approval for that deploy.
|
|
38
|
+
|
|
39
|
+
Why the line falls here: everything in the second list is visible to the client's visitors or hard to undo. D1 writes and DNS deletions in particular have no undo button: D1 Time Travel exists but restores the *whole* database.
|
|
40
|
+
|
|
41
|
+
## 3. Route to the right reference
|
|
42
|
+
|
|
43
|
+
Read only the file for the task at hand. Each one is self-contained.
|
|
44
|
+
|
|
45
|
+
| Task | Read |
|
|
46
|
+
|---|---|
|
|
47
|
+
| Launch a brand-new site (repo → live on its domain) | `references/launch-new-site.md` |
|
|
48
|
+
| Deploy, gradual rollout, preview a branch, roll back, check what's live | `references/deploy-and-release.md` |
|
|
49
|
+
| Login, auth profiles, multiple accounts, API tokens, token permissions | `references/auth-and-accounts.md` |
|
|
50
|
+
| Custom domains, routes, adding a zone, nameservers, DNS records, www ↔ apex | `references/domains-and-dns.md` |
|
|
51
|
+
| Zone settings: AI crawlers / bot blocking, redirects, cache rules, SSL/TLS, HSTS, security | `references/zone-settings.md` |
|
|
52
|
+
| Secrets, vars, `.dev.vars`, environments (staging/prod), `wrangler.jsonc` structure | `references/config-secrets-envs.md` |
|
|
53
|
+
| D1 (create, migrate, query, back up, restore), KV, R2 media buckets | `references/data-d1-kv-r2.md` |
|
|
54
|
+
| Forms stack: Postmark, Turnstile widgets, Email Routing, SPF/DKIM/DMARC | `references/forms-email-turnstile.md` |
|
|
55
|
+
| Logs, tailing, observability, analytics, "the site is down" triage | `references/observability-and-incidents.md` |
|
|
56
|
+
| GitHub Actions / Workers Builds, per-PR previews, CI tokens | `references/ci-cd.md` |
|
|
57
|
+
| Inventory/audit across accounts, hand a site to a client's account, decommission, wrangler/compat-date upgrades | `references/site-lifecycle.md` |
|
|
58
|
+
| A specific error message or odd behaviour | `references/troubleshooting.md` |
|
|
59
|
+
|
|
60
|
+
When a task spans several areas (a launch touches nearly all of them), follow the primary file. It points to the others at the step where they become relevant.
|
|
61
|
+
|
|
62
|
+
## 4. Bundled tools
|
|
63
|
+
|
|
64
|
+
- `scripts/preflight.sh [dir]`: read-only orientation report (above).
|
|
65
|
+
- `scripts/cf-api.sh METHOD PATH [JSON]`: thin `curl` wrapper for the Cloudflare REST API. It is used for anything wrangler can't do: zones, DNS, zone settings, rulesets, bot management. It reads `CLOUDFLARE_API_TOKEN` (and `CLOUDFLARE_ACCOUNT_ID` for the `{account}` placeholder), and prints the JSON body or a readable error. Run it with no arguments for usage. Reference examples pipe its output through `jq`. If `jq` isn't installed, suggest `sudo apt install jq` (or `brew install jq`), or filter with `node -e` in the meantime.
|
|
66
|
+
- `scripts/verify-site.sh https://domain [--md /about.md]`: read-only post-deploy smoke test covering status codes, HTTPS/www redirects, robots.txt AI-crawler group, llms.txt, sitemap, the Markdown twin, the form token endpoint, and security headers.
|
|
67
|
+
- `assets/wrangler.jsonc`: annotated starting config for a new Leyline site.
|
|
68
|
+
- `assets/deploy.yml`: GitHub Actions workflow (preview on PR, deploy on main).
|
|
69
|
+
|
|
70
|
+
## 5. Working conventions
|
|
71
|
+
|
|
72
|
+
- **Build, then deploy.** `npx astro build` writes `dist/server/wrangler.json` and a redirect at `.wrangler/deploy/config.json`. A plain `npx wrangler deploy` from the site root then deploys the *built* config. Running `wrangler deploy` without a fresh build ships stale output. Don't pass `-c wrangler.jsonc` to deploy, because that bypasses the built config.
|
|
73
|
+
- **Use the project's wrangler** (`npx wrangler`, pinned in `package.json`), not a global one. Flags differ between versions. If a flag in these references is rejected, run `npx wrangler <cmd> --help` and trust that over this skill.
|
|
74
|
+
- **Tag deploys** with `--tag "$(git rev-parse --short HEAD)" --message "<what changed>"`. The tag and message show up in `deployments list` and make rollbacks legible.
|
|
75
|
+
- **Never print secret values** into the transcript. Pipe them in (`printf %s "$VAL" | npx wrangler secret put NAME`) and generate random ones with `openssl rand -base64 48`. Don't echo `CLOUDFLARE_API_TOKEN`.
|
|
76
|
+
- **Prefer config over clicks.** Whatever can live in `wrangler.jsonc` (routes, custom domains, bindings, observability, vars) should, so the repo describes the site. Use the API or dashboard only for zone-level things.
|
|
77
|
+
- **Report what happened**: the deployed version ID, the URL, and what `verify-site.sh` showed. If a step was skipped or failed, say so plainly.
|
|
78
|
+
- Cloudflare's docs are large and change often. When you need something not covered here, fetch the product's `llms.txt` (e.g. `https://developers.cloudflare.com/workers/llms.txt`) rather than guessing.
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Deploy a Leyline site to Cloudflare Workers.
|
|
2
|
+
# PR → upload a version with a preview alias (production untouched) and comment the URL
|
|
3
|
+
# main → apply D1 migrations (if any) and deploy to production
|
|
4
|
+
# Repo secrets: CLOUDFLARE_API_TOKEN, CLOUDFLARE_ACCOUNT_ID, NPM_TOKEN (read access to @theory-digital)
|
|
5
|
+
name: Deploy
|
|
6
|
+
|
|
7
|
+
on:
|
|
8
|
+
push:
|
|
9
|
+
branches: [main]
|
|
10
|
+
pull_request:
|
|
11
|
+
|
|
12
|
+
concurrency:
|
|
13
|
+
group: deploy-${{ github.ref }}
|
|
14
|
+
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
|
15
|
+
|
|
16
|
+
permissions:
|
|
17
|
+
contents: read
|
|
18
|
+
pull-requests: write
|
|
19
|
+
|
|
20
|
+
env:
|
|
21
|
+
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
|
22
|
+
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
|
23
|
+
|
|
24
|
+
jobs:
|
|
25
|
+
deploy:
|
|
26
|
+
runs-on: ubuntu-latest
|
|
27
|
+
steps:
|
|
28
|
+
- uses: actions/checkout@v4
|
|
29
|
+
|
|
30
|
+
- uses: actions/setup-node@v4
|
|
31
|
+
with:
|
|
32
|
+
node-version: 22
|
|
33
|
+
cache: npm
|
|
34
|
+
registry-url: https://registry.npmjs.org
|
|
35
|
+
|
|
36
|
+
- run: npm ci
|
|
37
|
+
env:
|
|
38
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
39
|
+
|
|
40
|
+
- name: Check
|
|
41
|
+
run: npx astro check
|
|
42
|
+
|
|
43
|
+
- name: Build
|
|
44
|
+
run: npx astro build
|
|
45
|
+
|
|
46
|
+
- name: Preview (pull request)
|
|
47
|
+
if: github.event_name == 'pull_request'
|
|
48
|
+
id: preview
|
|
49
|
+
run: |
|
|
50
|
+
npx wrangler versions upload \
|
|
51
|
+
--preview-alias "pr-${{ github.event.number }}" \
|
|
52
|
+
--tag "${GITHUB_SHA::7}" \
|
|
53
|
+
--message "PR #${{ github.event.number }}" | tee upload.log
|
|
54
|
+
url=$(grep -oE 'https://pr-${{ github.event.number }}-[^ ]+\.workers\.dev' upload.log | head -1)
|
|
55
|
+
echo "url=$url" >> "$GITHUB_OUTPUT"
|
|
56
|
+
|
|
57
|
+
- name: Comment preview URL
|
|
58
|
+
if: github.event_name == 'pull_request' && steps.preview.outputs.url != ''
|
|
59
|
+
env:
|
|
60
|
+
GH_TOKEN: ${{ github.token }}
|
|
61
|
+
run: |
|
|
62
|
+
gh pr comment ${{ github.event.number }} --edit-last --create-if-none \
|
|
63
|
+
--body "Preview: ${{ steps.preview.outputs.url }} (shares production bindings, so form submissions are real)"
|
|
64
|
+
|
|
65
|
+
- name: Migrate D1
|
|
66
|
+
if: github.event_name == 'push' && hashFiles('migrations/*.sql') != ''
|
|
67
|
+
run: npx wrangler d1 migrations apply DB --remote
|
|
68
|
+
|
|
69
|
+
- name: Deploy
|
|
70
|
+
if: github.event_name == 'push'
|
|
71
|
+
run: npx wrangler deploy --tag "${GITHUB_SHA::7}" --message "$(git log -1 --pretty=%s | cut -c1-100)"
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// Starting config for a Leyline site on Cloudflare Workers.
|
|
2
|
+
// Replace every <PLACEHOLDER>. Don't set "main" or "assets": @astrojs/cloudflare generates them
|
|
3
|
+
// into dist/server/wrangler.json at build time, and `wrangler deploy` reads that file.
|
|
4
|
+
{
|
|
5
|
+
"$schema": "node_modules/wrangler/config-schema.json",
|
|
6
|
+
"name": "<client>-site",
|
|
7
|
+
// Pins every wrangler command in this repo to the right account (wrangler whoami lists IDs).
|
|
8
|
+
"account_id": "<ACCOUNT_ID>",
|
|
9
|
+
// Pin it and bump it deliberately (see site-lifecycle.md).
|
|
10
|
+
"compatibility_date": "<YYYY-MM-DD>",
|
|
11
|
+
|
|
12
|
+
// Phase 9 of the launch: uncomment once the zone is active on Cloudflare.
|
|
13
|
+
// "routes": [
|
|
14
|
+
// { "pattern": "<domain>", "custom_domain": true },
|
|
15
|
+
// { "pattern": "www.<domain>", "custom_domain": true }
|
|
16
|
+
// ],
|
|
17
|
+
// Set to false after launch if a public *.workers.dev copy isn't wanted.
|
|
18
|
+
"workers_dev": true,
|
|
19
|
+
// Per-version / per-branch preview URLs (deploy-and-release.md → Preview a branch).
|
|
20
|
+
"preview_urls": true,
|
|
21
|
+
|
|
22
|
+
// Workers Logs: persisted, searchable logs in the dashboard.
|
|
23
|
+
"observability": { "enabled": true, "head_sampling_rate": 1 },
|
|
24
|
+
|
|
25
|
+
"vars": {
|
|
26
|
+
// Turnstile sitekey (public). Remove if the site has no Turnstile.
|
|
27
|
+
"PUBLIC_TURNSTILE_SITE_KEY": "<SITEKEY>"
|
|
28
|
+
},
|
|
29
|
+
// Secrets (never in this file): FORM_SIGNING_SECRET, POSTMARK_SERVER_TOKEN, TURNSTILE_SECRET_KEY
|
|
30
|
+
// local: .dev.vars production: npx wrangler secret put NAME
|
|
31
|
+
|
|
32
|
+
// Forms with `persist`: store submissions. `npx wrangler d1 create <client>-site --location enam`
|
|
33
|
+
"d1_databases": [
|
|
34
|
+
{
|
|
35
|
+
"binding": "DB",
|
|
36
|
+
"database_name": "<client>-site",
|
|
37
|
+
"database_id": "<DATABASE_ID>",
|
|
38
|
+
"migrations_dir": "migrations"
|
|
39
|
+
}
|
|
40
|
+
],
|
|
41
|
+
|
|
42
|
+
// Forms with `rateLimit`. namespace_id: any integer unique among this account's sites.
|
|
43
|
+
"ratelimits": [
|
|
44
|
+
{ "name": "FORM_RATE_LIMITER", "namespace_id": "<1001>", "simple": { "limit": 5, "period": 60 } }
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
// Optional staging Worker. Bindings and vars do NOT inherit, so repeat them here.
|
|
48
|
+
// "env": {
|
|
49
|
+
// "staging": {
|
|
50
|
+
// "name": "<client>-site-staging",
|
|
51
|
+
// "routes": [{ "pattern": "staging.<domain>", "custom_domain": true }],
|
|
52
|
+
// "vars": { "PUBLIC_TURNSTILE_SITE_KEY": "<SITEKEY>" },
|
|
53
|
+
// "d1_databases": [{ "binding": "DB", "database_name": "<client>-site-staging", "database_id": "<ID>", "migrations_dir": "migrations" }],
|
|
54
|
+
// "ratelimits": [{ "name": "FORM_RATE_LIMITER", "namespace_id": "<1002>", "simple": { "limit": 5, "period": 60 } }]
|
|
55
|
+
// }
|
|
56
|
+
// }
|
|
57
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Auth and accounts
|
|
2
|
+
|
|
3
|
+
Theory Digital has a mixed setup: most sites run in the Theory Digital account, and some in accounts the client owns, with TD as a member. Two separate credentials are involved:
|
|
4
|
+
|
|
5
|
+
| Credential | Used by | Covers |
|
|
6
|
+
|---|---|---|
|
|
7
|
+
| **Wrangler OAuth login** (per profile) | `wrangler …` | Workers, D1, KV, R2, secrets, Turnstile, routes/custom domains |
|
|
8
|
+
| **API token** (`CLOUDFLARE_API_TOKEN`) | `scripts/cf-api.sh`, CI, and wrangler too when set | Anything with matching permissions, including zone settings and DNS, which wrangler can't touch |
|
|
9
|
+
|
|
10
|
+
When `CLOUDFLARE_API_TOKEN` is set in the environment, **wrangler uses it instead of the OAuth login**. Most "wrong account", "Authentication error [code: 10000]" and "permission denied" confusion comes from a stale token in the shell. Check `env | grep CLOUDFLARE_` first.
|
|
11
|
+
|
|
12
|
+
## Wrangler login and profiles
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npx wrangler whoami # who, which accounts, token scopes
|
|
16
|
+
npx wrangler login # default profile, browser OAuth
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Named profiles (experimental in wrangler 4.14x, but they work well for mixed accounts):
|
|
20
|
+
```bash
|
|
21
|
+
npx wrangler auth create td # TD staff login
|
|
22
|
+
npx wrangler auth create client-acme # a login that can see Acme's account
|
|
23
|
+
npx wrangler auth activate client-acme ~/sites/acme # bind a directory → profile
|
|
24
|
+
npx wrangler auth list
|
|
25
|
+
npx wrangler deploy --profile td # one-off override
|
|
26
|
+
```
|
|
27
|
+
Even with a profile bound, still set `account_id` in `wrangler.jsonc`. A login that can see several accounts would otherwise prompt interactively, and in CI it would fail.
|
|
28
|
+
|
|
29
|
+
**WSL / headless login**: the OAuth callback goes to `localhost:8976`. In WSL2 the browser usually opens on Windows and the callback works. If it doesn't, copy the printed URL into a browser manually. If that callback fails too, use an API token instead.
|
|
30
|
+
|
|
31
|
+
**Account selection**: `npx wrangler whoami` lists account names and IDs. When the user names a client, match the name in that list and confirm the ID with them before writing it to the config. Similar names (e.g. "Acme" vs "Acme Holdings") are common.
|
|
32
|
+
|
|
33
|
+
## API tokens for `cf-api.sh` and CI
|
|
34
|
+
|
|
35
|
+
Create tokens at dash.cloudflare.com → My Profile → API Tokens (user-owned), or in the account under Manage Account → Account API Tokens (account-owned; better for CI because it survives staff leaving). Scope each one to a single account and only the zones it needs.
|
|
36
|
+
|
|
37
|
+
Recommended permission sets:
|
|
38
|
+
|
|
39
|
+
**Site operator token** (used interactively with this skill):
|
|
40
|
+
- Account: Workers Scripts Edit · D1 Edit · Workers KV Storage Edit · Workers R2 Storage Edit · Turnstile Sites Edit · Account Settings Read
|
|
41
|
+
- Zone (specific zones, or all zones in the account): Zone Read · DNS Edit · Zone Settings Edit · Workers Routes Edit · Bot Management Edit · Dynamic URL Redirects Edit (Single Redirects) · Cache Rules Edit · Email Routing Rules Edit · SSL and Certificates Edit
|
|
42
|
+
- Adding new zones needs **Zone: Edit** at the account level. Grant it only to tokens that launch sites.
|
|
43
|
+
|
|
44
|
+
**CI deploy token** (deploys only): Account: Workers Scripts Edit, D1 Edit (if CI applies migrations), Account Settings Read. Zone: Workers Routes Edit (for custom domains/routes in the config). Nothing else.
|
|
45
|
+
|
|
46
|
+
Permission names in the dashboard shift over time. If a call returns `10000 Authentication error` or `9109 Unauthorized`, compare the endpoint with the token's permission list rather than retrying.
|
|
47
|
+
|
|
48
|
+
Verify a token without printing it:
|
|
49
|
+
```bash
|
|
50
|
+
bash scripts/cf-api.sh GET /user/tokens/verify # user-owned token
|
|
51
|
+
bash scripts/cf-api.sh GET /accounts/{account}/tokens/verify # account-owned token
|
|
52
|
+
bash scripts/cf-api.sh GET '/zones?per_page=50' # which zones it sees
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Keeping the token out of the transcript: ask the user to `export CLOUDFLARE_API_TOKEN=…` in their own shell (`! export …` in Claude Code), or to store it in a gitignored file you `source`. Never put it in `wrangler.jsonc`, the repo, or a command line you echo.
|
|
56
|
+
|
|
57
|
+
## Clients' own accounts
|
|
58
|
+
|
|
59
|
+
- Ask the client to invite a Theory Digital address as a **member** with a role that covers Workers and zones ("Administrator", or narrower roles such as "Workers Admin" plus "DNS"). Avoid having them share their own login.
|
|
60
|
+
- For CI on the client's account, create an **account-owned token** in *their* account, so it doesn't depend on any one TD staff member.
|
|
61
|
+
- Billing: Workers Paid, Turnstile and so on are billed to whichever account owns the resource. Confirm the plan before enabling paid features in a client's account.
|
|
62
|
+
- Moving a site between accounts: see `site-lifecycle.md`.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# CI/CD
|
|
2
|
+
|
|
3
|
+
There are two ways to deploy automatically. Pick one per site; using both causes double deploys.
|
|
4
|
+
|
|
5
|
+
| | GitHub Actions (`assets/deploy.yml`) | Workers Builds (Cloudflare's Git integration) |
|
|
6
|
+
|---|---|---|
|
|
7
|
+
| Setup | Workflow file plus repo secrets | Connect the repo in the dashboard (Worker → Settings → Builds) |
|
|
8
|
+
| Runs | In GitHub, with your own test steps | In Cloudflare's build environment |
|
|
9
|
+
| PR previews | Via `versions upload --preview-alias`, commented on the PR | Built in (non-production branch builds get preview URLs) |
|
|
10
|
+
| Migrations / secrets | Scriptable in the workflow | Build command only. Migrations remain a manual step |
|
|
11
|
+
| Good for | Sites with lint/tests, D1 migrations, or anything custom | Simple sites where the client account owner wants it all in Cloudflare |
|
|
12
|
+
|
|
13
|
+
The Theory Digital default is **GitHub Actions**: it runs the same `astro check`/build as local, and the account and token are explicit.
|
|
14
|
+
|
|
15
|
+
## GitHub Actions setup
|
|
16
|
+
|
|
17
|
+
1. Copy `assets/deploy.yml` from this skill to `.github/workflows/deploy.yml` in the site repo. Adjust the Node version and the site's check script.
|
|
18
|
+
2. Create a **CI deploy token** in the account that owns the Worker. Permissions are in `auth-and-accounts.md`; an account-owned token is best.
|
|
19
|
+
3. Add repo secrets (the user does this in GitHub, or with `gh secret set` after confirming with them):
|
|
20
|
+
```bash
|
|
21
|
+
gh secret set CLOUDFLARE_API_TOKEN # prompts for the value, keeping it out of shell history
|
|
22
|
+
gh secret set CLOUDFLARE_ACCOUNT_ID --body "<account id>"
|
|
23
|
+
```
|
|
24
|
+
4. Protect `main`, so that what deploys has passed review.
|
|
25
|
+
5. On the first CI deploy, watch it with `gh run watch` and then run `verify-site.sh`.
|
|
26
|
+
|
|
27
|
+
What the workflow does:
|
|
28
|
+
- **Pull request** → build → `wrangler versions upload --preview-alias pr-<n>` → comment with the preview URL. Production is untouched. This needs `"preview_urls": true` in `wrangler.jsonc`.
|
|
29
|
+
- **Push to main** → build → `d1 migrations apply --remote` (only if a `migrations/` dir exists) → `wrangler deploy --tag <sha>`.
|
|
30
|
+
|
|
31
|
+
Adding CI to a repo is a code change to the site. The deploys it then performs were authorized by merging. You don't need to confirm each CI run.
|
|
32
|
+
|
|
33
|
+
## Secrets in CI
|
|
34
|
+
|
|
35
|
+
Worker runtime secrets (`FORM_SIGNING_SECRET` etc.) stay on the Worker. Set them once with `wrangler secret put`, and CI never sees them. Only `CLOUDFLARE_API_TOKEN` and `CLOUDFLARE_ACCOUNT_ID` go into GitHub.
|
|
36
|
+
|
|
37
|
+
## Workers Builds notes
|
|
38
|
+
|
|
39
|
+
- Build command: `npx astro build`. Deploy command: `npx wrangler deploy`. Non-production branch deploy command: `npx wrangler versions upload`.
|
|
40
|
+
- Builds run in the account that owns the Worker, so the client's account needs the Git connection authorized there. For client-owned accounts, the client may need to approve the GitHub app.
|
|
41
|
+
- Inspect builds in the dashboard. There's no first-class wrangler command for build logs.
|
|
42
|
+
|
|
43
|
+
## Troubleshooting CI
|
|
44
|
+
|
|
45
|
+
- `Authentication error [code: 10000]` → wrong token, token scoped to another account, or a missing permission.
|
|
46
|
+
- `More than one account available` → set `CLOUDFLARE_ACCOUNT_ID` (or `account_id` in the config).
|
|
47
|
+
- Deploy succeeds but the site is unchanged → CI deployed a stale build, or deployed to a different Worker name/env. Compare `deployments list` author and tag.
|
|
48
|
+
- `npm ci` fails on a private package → `@theory-digital/leyline` is restricted on npm. CI needs `NODE_AUTH_TOKEN` with read access and an `.npmrc` (`//registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN}`). The workflow template includes this.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Config, secrets and environments
|
|
2
|
+
|
|
3
|
+
## Where each kind of value lives
|
|
4
|
+
|
|
5
|
+
| Kind | Example | Where | In git? |
|
|
6
|
+
|---|---|---|---|
|
|
7
|
+
| Bindings | D1, KV, R2, ratelimits | `wrangler.jsonc` | yes |
|
|
8
|
+
| Public/plain vars | `PUBLIC_TURNSTILE_SITE_KEY` | `wrangler.jsonc` → `vars` | yes |
|
|
9
|
+
| Secrets | `FORM_SIGNING_SECRET`, `POSTMARK_SERVER_TOKEN`, `TURNSTILE_SECRET_KEY` | `wrangler secret put` (prod), `.dev.vars` (local) | **no** |
|
|
10
|
+
| Build-time site config | `site.url`, locales, forms | `astro.config.ts` (`leyline({...})`) | yes |
|
|
11
|
+
|
|
12
|
+
Leyline reads these through `astro:env`. A var or secret missing at runtime shows up as a startup or first-request error naming the key (see `troubleshooting.md`).
|
|
13
|
+
|
|
14
|
+
## `wrangler.jsonc` essentials
|
|
15
|
+
|
|
16
|
+
The adapter merges the site's `wrangler.jsonc` into `dist/server/wrangler.json` at build time, adding `main`, `assets`, a `SESSION` KV binding and an `IMAGES` binding. Edit the source file only. The `dist/` copy is regenerated.
|
|
17
|
+
|
|
18
|
+
Keys worth knowing (the template in `assets/wrangler.jsonc` is annotated):
|
|
19
|
+
- `name`: the Worker. Renaming it creates a **new** Worker; the old one keeps running on its domains until deleted.
|
|
20
|
+
- `account_id`: pin it. See `auth-and-accounts.md`.
|
|
21
|
+
- `compatibility_date`: when it's omitted, the adapter sets one at build. Pin it explicitly for reproducible builds, and bump it deliberately (`site-lifecycle.md`).
|
|
22
|
+
- `routes`, `workers_dev`, `preview_urls`: see `domains-and-dns.md` and `deploy-and-release.md`.
|
|
23
|
+
- `observability`: `{ "enabled": true }` turns on Workers Logs (`observability-and-incidents.md`).
|
|
24
|
+
- `vars`: wiped and replaced on every deploy unless `--keep-vars` is passed.
|
|
25
|
+
|
|
26
|
+
## Secrets
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npx wrangler secret list # names only, values are never readable
|
|
30
|
+
printf %s "$VALUE" | npx wrangler secret put NAME # (confirm), creates a new deployment immediately
|
|
31
|
+
npx wrangler secret delete NAME # (confirm)
|
|
32
|
+
npx wrangler secret bulk secrets.json # (confirm) {"A":"…","B":"…"}; "B": null deletes in JSON form
|
|
33
|
+
```
|
|
34
|
+
- `secret put` **deploys immediately**, so it counts as a production change. Under gradual deployments, use `npx wrangler versions secret put` instead, which creates a version without deploying it.
|
|
35
|
+
- Values never appear in the transcript. Generate with `openssl rand -base64 48`, read from `.dev.vars`, or ask the user to run `! printf %s 'value' | npx wrangler secret put NAME` themselves. For bulk files, write to the scratchpad, upload, then delete the file.
|
|
36
|
+
- `wrangler deploy --secrets-file <file>` uploads secrets together with the code in one version. It's additive: omitted secrets are kept.
|
|
37
|
+
- **Rotation** of `FORM_SIGNING_SECRET`: tokens already issued to open tabs stop validating, and those visitors see a retry. Do it at a quiet time. To rotate `POSTMARK_SERVER_TOKEN`, create the new token in Postmark → `secret put` → test a submission → revoke the old one.
|
|
38
|
+
- "Is secret X set?": `secret list`. To check a value, compare behaviour, e.g. a form submission works. You can't read the value back.
|
|
39
|
+
|
|
40
|
+
## `.dev.vars` (local)
|
|
41
|
+
|
|
42
|
+
Gitignored, `KEY=value` per line, read by `wrangler dev` and `astro dev`. For environments, `.dev.vars.staging` is used with `--env staging`. Keep a committed `.dev.vars.example` that lists the keys without values, so a new developer knows what's needed.
|
|
43
|
+
|
|
44
|
+
Leyline test helper: `POSTMARK_API_URL` points form delivery at a mock server. It only belongs in local or test configs, never in production.
|
|
45
|
+
|
|
46
|
+
## Environments
|
|
47
|
+
|
|
48
|
+
See `deploy-and-release.md` → Staging for the full pattern. Key rules:
|
|
49
|
+
- `--env X` targets Worker `<name>-X` (or `env.X.name` if set).
|
|
50
|
+
- **Non-inheritable keys** have to be repeated inside each env: `vars`, `d1_databases`, `kv_namespaces`, `r2_buckets`, `ratelimits`, `routes`, and the other bindings. Top-level `name`, `account_id`, `compatibility_date` and `observability` are inherited.
|
|
51
|
+
- Secrets are per-Worker, so `secret put NAME --env staging` is separate from production.
|
|
52
|
+
|
|
53
|
+
## Types
|
|
54
|
+
|
|
55
|
+
`npx wrangler types` generates `worker-configuration.d.ts` from the config, which gives typed `env` bindings. Run it after adding bindings. Leyline sites usually rely on `astro:env`, so it's optional.
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Data: D1, KV, R2
|
|
2
|
+
|
|
3
|
+
## Contents
|
|
4
|
+
- D1: create, migrate, query, export/backup, Time Travel restore, delete
|
|
5
|
+
- Form submissions: common queries
|
|
6
|
+
- EmDash on D1
|
|
7
|
+
- KV
|
|
8
|
+
- R2 (media, uploads, backups)
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## D1
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npx wrangler d1 list
|
|
16
|
+
npx wrangler d1 info acme-site # size, region, tables
|
|
17
|
+
npx wrangler d1 create acme-site --location enam # (confirm) also --jurisdiction eu|us|fedramp for data residency (permanent)
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### Migrations
|
|
21
|
+
Files go in `migrations/NNNN_name.sql` (`migrations_dir` in the config). Applied state is tracked in the `d1_migrations` table.
|
|
22
|
+
```bash
|
|
23
|
+
npx wrangler d1 migrations list DB --remote # what's pending
|
|
24
|
+
npx wrangler d1 migrations create DB add_x # new empty file with the next number
|
|
25
|
+
npx wrangler d1 migrations apply DB --local # dev
|
|
26
|
+
npx wrangler d1 migrations apply DB --remote # (confirm). Takes a backup bookmark first, rolls back a failing file
|
|
27
|
+
```
|
|
28
|
+
- Write migrations **additively** (add columns or tables; don't drop or rename in the same release as the code change). Rollbacks then stay safe, and a deploy window where old code runs against the new schema is harmless.
|
|
29
|
+
- Run remote migrations **before** deploying code that needs them.
|
|
30
|
+
- When Leyline adds a migration in a new version, copy the new file(s) from `node_modules/@theory-digital/leyline/src/forms/migrations/` into the site's `migrations/` as part of the Leyline upgrade.
|
|
31
|
+
|
|
32
|
+
### Querying
|
|
33
|
+
```bash
|
|
34
|
+
npx wrangler d1 execute DB --remote --command "SELECT count(*) FROM leyline_submissions"
|
|
35
|
+
npx wrangler d1 execute DB --remote --file ./query.sql
|
|
36
|
+
npx wrangler d1 execute DB --remote --json --command "…" # machine-readable output
|
|
37
|
+
```
|
|
38
|
+
Read-only queries are fine without asking. `INSERT/UPDATE/DELETE/DROP/ALTER` against `--remote` need confirmation. Show the exact SQL, and show a `SELECT count(*)` with the same `WHERE` clause first, so the user sees how many rows will change.
|
|
39
|
+
|
|
40
|
+
### Backup / export
|
|
41
|
+
```bash
|
|
42
|
+
npx wrangler d1 export DB --remote --output backups/acme-$(date +%F).sql # full
|
|
43
|
+
npx wrangler d1 export DB --remote --no-data --output schema.sql # schema only
|
|
44
|
+
```
|
|
45
|
+
Exports can contain personal data (form submissions). Keep them out of git, and delete local copies when done.
|
|
46
|
+
|
|
47
|
+
### Time Travel (point-in-time restore, last 30 days)
|
|
48
|
+
```bash
|
|
49
|
+
npx wrangler d1 time-travel info DB --timestamp 2026-10-01T12:00:00Z # get the bookmark for that moment
|
|
50
|
+
npx wrangler d1 time-travel restore DB --timestamp 2026-10-01T12:00:00Z # (confirm), overwrites the WHOLE database
|
|
51
|
+
```
|
|
52
|
+
A restore rewinds every table, including legitimate submissions received since then. Before a restore, export the current state, so lost rows can be re-inserted selectively. The restore prints a bookmark for the pre-restore state; record it, because it makes the restore itself undoable.
|
|
53
|
+
|
|
54
|
+
### Delete
|
|
55
|
+
`npx wrangler d1 delete acme-site` (confirm, and export first). Remove the binding from the config, or the next deploy fails.
|
|
56
|
+
|
|
57
|
+
## Form submissions: common queries
|
|
58
|
+
|
|
59
|
+
```sql
|
|
60
|
+
-- recent
|
|
61
|
+
SELECT form, locale, status, created_at FROM leyline_submissions ORDER BY created_at DESC LIMIT 20;
|
|
62
|
+
-- delivery failures (Postmark errors)
|
|
63
|
+
SELECT id, form, error, created_at FROM leyline_submissions WHERE status = 'failed' ORDER BY created_at DESC;
|
|
64
|
+
-- volume per day
|
|
65
|
+
SELECT substr(created_at,1,10) AS day, count(*) FROM leyline_submissions GROUP BY day ORDER BY day DESC LIMIT 30;
|
|
66
|
+
-- spam-flagged
|
|
67
|
+
SELECT created_at, flags FROM leyline_submissions WHERE flags <> '[]' ORDER BY created_at DESC LIMIT 20;
|
|
68
|
+
```
|
|
69
|
+
`data` holds the submitted fields as JSON (`json_extract(data, '$.email')`). It's personal data, so don't dump whole rows into the chat unless the user asks for specific records. Privacy or erasure requests: `DELETE FROM leyline_submissions WHERE json_extract(data,'$.email') = ?` (confirm, after a count).
|
|
70
|
+
|
|
71
|
+
## EmDash on D1
|
|
72
|
+
|
|
73
|
+
Production EmDash sites use `@emdash-cms/cloudflare`'s `d1()` driver (see Leyline's `docs/CONTENT-MODEL.md`). They usually have a separate D1 database for CMS content, apart from form submissions; that keeps exports and restores scoped. Content seeding: `leyline-seed` generates seed JSON from a static site, and EmDash's CLI applies it. Treat a content-DB restore as an editor-visible event and tell the client.
|
|
74
|
+
|
|
75
|
+
## KV
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
npx wrangler kv namespace list
|
|
79
|
+
npx wrangler kv namespace create acme-cache # (confirm) → paste id into kv_namespaces
|
|
80
|
+
npx wrangler kv key list --binding SESSION --remote | head
|
|
81
|
+
npx wrangler kv key get --binding X "key" --remote
|
|
82
|
+
npx wrangler kv key put --binding X "key" "value" --remote # (confirm)
|
|
83
|
+
```
|
|
84
|
+
The Astro adapter adds a `SESSION` KV binding, used by Astro sessions. Wrangler auto-provisions it on first deploy when it has no ID. If a site doesn't use sessions, it's harmless.
|
|
85
|
+
|
|
86
|
+
## R2
|
|
87
|
+
|
|
88
|
+
Use it for media too big or too dynamic for the repo (EmDash uploads, video, downloadable PDFs), or as a backup target.
|
|
89
|
+
```bash
|
|
90
|
+
npx wrangler r2 bucket list
|
|
91
|
+
npx wrangler r2 bucket create acme-media # (confirm), add --jurisdiction eu if needed
|
|
92
|
+
npx wrangler r2 bucket domain add acme-media --domain media.acme.com --zone-id <zone_id> # (confirm) public custom domain
|
|
93
|
+
npx wrangler r2 object put acme-media/path/file.pdf --file ./file.pdf --remote
|
|
94
|
+
npx wrangler r2 bucket cors set acme-media --file cors.json # if the browser fetches directly
|
|
95
|
+
```
|
|
96
|
+
Serve public media through a custom domain, not `r2.dev`, which is rate-limited and meant for development. Bind the bucket in `wrangler.jsonc` (`r2_buckets: [{ "binding": "MEDIA", "bucket_name": "acme-media" }]`) if the Worker reads or writes it.
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# Deploy and release
|
|
2
|
+
|
|
3
|
+
## Contents
|
|
4
|
+
- Standard production deploy
|
|
5
|
+
- What's live right now?
|
|
6
|
+
- Gradual rollout (versions)
|
|
7
|
+
- Preview a branch without touching production
|
|
8
|
+
- Roll back
|
|
9
|
+
- Staging environment
|
|
10
|
+
- Content-only changes (EmDash)
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Standard production deploy
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
bash <skill>/scripts/preflight.sh # account, Worker name, clean tree
|
|
18
|
+
npm run check 2>/dev/null || npx astro check # if the site has a check script; don't deploy type errors
|
|
19
|
+
npx astro build
|
|
20
|
+
npx wrangler deploy --dry-run # catches config/binding errors, uploads nothing
|
|
21
|
+
npx wrangler deploy --tag "$(git rev-parse --short HEAD)" --message "<one-line summary>" # (confirm)
|
|
22
|
+
bash <skill>/scripts/verify-site.sh https://<domain>
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Notes:
|
|
26
|
+
- `wrangler deploy` from the site root picks up the built config through `.wrangler/deploy/config.json`. If you see "Missing entry-point" or a deploy of the wrong thing, the build didn't run or ran in a different directory.
|
|
27
|
+
- **Vars set in the dashboard are wiped** on deploy unless you pass `--keep-vars`. Secrets are never wiped. If someone added a var by hand, move it into `wrangler.jsonc` rather than relying on `--keep-vars` forever.
|
|
28
|
+
- New D1 migrations? Apply them **before** deploying code that depends on them (`npx wrangler d1 migrations apply DB --remote`, confirm). Migrations should be additive, so the old code keeps working against the new schema during the window between the two steps.
|
|
29
|
+
- The deploy output prints the **Version ID** and URLs. Capture the Version ID for the report and for any rollback.
|
|
30
|
+
|
|
31
|
+
## What's live right now?
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npx wrangler deployments status # the active deployment: version(s) and traffic split
|
|
35
|
+
npx wrangler deployments list # last 10 deployments with tag, message, author, time
|
|
36
|
+
npx wrangler versions list # uploaded versions (including undeployed ones)
|
|
37
|
+
npx wrangler versions view <id> # one version's bindings, compat date, etc.
|
|
38
|
+
```
|
|
39
|
+
Compare the deployed tag with `git log --oneline` to see what's unreleased.
|
|
40
|
+
|
|
41
|
+
## Gradual rollout (versions)
|
|
42
|
+
|
|
43
|
+
Use this for risky changes on busy sites: a new form flow, a dependency upgrade, a compat-date bump.
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
npx astro build
|
|
47
|
+
npx wrangler versions upload --tag "$(git rev-parse --short HEAD)" --message "..." # uploads, no traffic. Prints a version ID and a preview URL
|
|
48
|
+
# test the version preview URL
|
|
49
|
+
npx wrangler versions deploy <new-id>@10% <current-id>@90% --message "canary" # (confirm)
|
|
50
|
+
npx wrangler tail --version-id <new-id> --status error # watch for errors
|
|
51
|
+
npx wrangler versions deploy <new-id>@100% --message "full rollout" # (confirm)
|
|
52
|
+
```
|
|
53
|
+
Things to know:
|
|
54
|
+
- A visitor can land on different versions on successive requests unless session affinity is configured. For a marketing site that's fine. Avoid splits on changes where the HTML and its form endpoint must match exactly. Leyline form tokens are validated by the shared secret, not by the version, so forms survive splits.
|
|
55
|
+
- Static assets belong to the version that serves them.
|
|
56
|
+
- Changes to routes, custom domains or cron triggers are **not** carried by `versions upload`. Apply them with `npx wrangler triggers deploy`, or do a normal `wrangler deploy`.
|
|
57
|
+
|
|
58
|
+
## Preview a branch without touching production
|
|
59
|
+
|
|
60
|
+
Two mechanisms; choose based on what's needed.
|
|
61
|
+
|
|
62
|
+
1. **Version preview URLs**, the stable option. Set `"preview_urls": true` in `wrangler.jsonc` (the template does this). Then:
|
|
63
|
+
```bash
|
|
64
|
+
npx wrangler versions upload --preview-alias "$(git branch --show-current | tr '/_' '--' | cut -c1-40)"
|
|
65
|
+
```
|
|
66
|
+
This gives `https://<alias>-<worker>.<subdomain>.workers.dev`. The preview shares production bindings, so **form submissions hit the real D1 and send real email**. Warn the user, or point Postmark at a test server for previews. The alias URL stays stable across uploads on the same branch.
|
|
67
|
+
|
|
68
|
+
2. **`wrangler preview`** (open beta). It creates a named Preview (defaulting to the git branch) with its own deployments and its own secrets (`wrangler preview secret put`). It uses the Preview base config from the dashboard unless you pass `--ignore-base-config`. It isolates secrets better, but it's beta: check `npx wrangler preview --help`, and expect behaviour to change.
|
|
69
|
+
|
|
70
|
+
Previews aren't "production writes" in the guardrail sense, so you don't need to ask. Do mention the shared-bindings caveat.
|
|
71
|
+
|
|
72
|
+
Search engines: workers.dev hosts serve the same robots.txt as production. Canonicals point at the production URL, so duplicate content isn't a real risk.
|
|
73
|
+
|
|
74
|
+
## Roll back
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
npx wrangler deployments list # find the last good version (by tag/time)
|
|
78
|
+
npx wrangler rollback <version-id> -m "reason" # (confirm). Without an ID it goes to the previous version
|
|
79
|
+
bash <skill>/scripts/verify-site.sh https://<domain>
|
|
80
|
+
```
|
|
81
|
+
What a rollback **does not** undo:
|
|
82
|
+
- **D1 schema or data changes.** If the bad deploy ran a migration, the old code now runs against the new schema. That's fine if the migration was additive; otherwise see `data-d1-kv-r2.md` → Time Travel.
|
|
83
|
+
- **Secrets changed since then.** Rollback restores the code and its config bindings, but secrets are whatever is current.
|
|
84
|
+
- Zone settings, DNS, and redirect rules. Those aren't part of the Worker.
|
|
85
|
+
|
|
86
|
+
After a rollback, the next normal `wrangler deploy` replaces it. Make sure the fix is in before you deploy again, or the bad code ships again.
|
|
87
|
+
|
|
88
|
+
## Staging environment
|
|
89
|
+
|
|
90
|
+
Optional. Use it for clients who want sign-off before production. In `wrangler.jsonc`:
|
|
91
|
+
```jsonc
|
|
92
|
+
"env": {
|
|
93
|
+
"staging": {
|
|
94
|
+
"name": "acme-site-staging", // explicit is clearer than the implicit suffix
|
|
95
|
+
"routes": [{ "pattern": "staging.acme.com", "custom_domain": true }],
|
|
96
|
+
"vars": { "PUBLIC_TURNSTILE_SITE_KEY": "1x00000000000000000000AA" },
|
|
97
|
+
"d1_databases": [{ "binding": "DB", "database_name": "acme-site-staging", "database_id": "…", "migrations_dir": "migrations" }]
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
Bindings and vars **don't inherit** into an env; repeat them there. Deploy with `npx wrangler deploy --env staging`, which counts as a production-style write to the staging Worker (confirm, but it's low stakes). Secrets are per-Worker: `npx wrangler secret put NAME --env staging`.
|
|
102
|
+
|
|
103
|
+
Protect staging from the public and from search engines with Cloudflare Access (Zero Trust → Access → application on `staging.acme.com`). That's a dashboard step.
|
|
104
|
+
|
|
105
|
+
## Content-only changes (EmDash)
|
|
106
|
+
|
|
107
|
+
EmDash sites serve content from D1 at request time. Editors publishing in the EmDash admin don't need a deploy. A deploy is only needed for code, template or config changes. If an editor says "my change isn't showing", look at caching (`zone-settings.md` → Cache rules) and the EmDash publish state before redeploying.
|