@lunora/cli
Version:
The Lunora CLI: init, dev, deploy, codegen, migrate, seed, doctor, insights, logs, registry, and the rest of the project commands
151 lines (118 loc) • 6.28 kB
Markdown
---
name: lunora-deploy
description: Deploys a Lunora app to Cloudflare. Use for `lunora deploy`, wrangler.jsonc
bindings (SHARD/SESSION DOs, D1, R2), provisioning databases/buckets, secrets
(`wrangler secret` vs `.dev.vars`), the `lunora doctor` preflight, the
schema-drift gate, and dev-vs-prod separation.
---
# Lunora Deploy
Ship a Lunora app to Cloudflare Workers + Durable Objects. Unlike a managed
backend, deployment owns real Cloudflare resources — Durable Object bindings, a
D1 database, R2 buckets, and secrets — so the work is mostly making
`wrangler.jsonc` and the remote resources line up.
## When to Use
- Deploying to production (or a Cloudflare environment) for the first time.
- A deploy fails on a binding, a placeholder id, or the schema-drift gate.
- Provisioning D1 / R2 / secrets for a Lunora app.
## When Not to Use
- Local development — that is `lunora dev` (see `lunora-quickstart`).
- A schema/data change that needs migrating — do that first with
`lunora-migration-helper`, then deploy.
## What `lunora deploy` Does
`lunora deploy` runs a fixed pipeline:
1. **Codegen** — regenerates `lunora/_generated/` and typechecks.
2. **Validate `wrangler.jsonc`** — required `compatibility_date`, the
`nodejs_compat` flag, and the `SHARD` Durable Object binding.
3. **Schema-drift gate** — blocks if the committed baseline
(`lunora/.lunora-schema.json`) drifted with a breaking change and no
accompanying migration. The baseline is re-blessed only after the deploy
succeeds.
4. **`wrangler deploy`** — builds and pushes the worker (and any container
images).
Useful flags: `--env <name>` (Cloudflare environment), `--migrate` (run pending
data migrations against the live worker after deploy, with `--migrate-token` /
`--migrate-url`), `--allow-schema-drift` (override the gate — use sparingly), and
`--update-schema-baseline` (re-bless the baseline with the current shape).
## Preflight: `lunora doctor`
Run the read-only preflight before deploying. It checks:
- `wrangler.jsonc` present with the `SHARD` durable-object binding.
- D1 `database_id`s are real, not placeholders (`<replace>` / empty).
- `send_email` destination addresses aren't placeholders.
- `.dev.vars` secret-looking keys are filled.
- Declared containers are exported by the worker entry.
```bash
lunora doctor # FAIL → exit 1; WARN/INFO don't block
```
`lunora verify` and `lunora prepare` run related checks (drift gate, wrangler
validation) — wire `lunora verify` into CI to catch drift before a deploy.
## `wrangler.jsonc` — the binding contract
A Lunora worker needs the ShardDO (and SessionDO when auth is wired), the
SQLite-DO migration tag, and whatever D1/R2 the app uses:
```jsonc
{
"name": "my-app",
"main": "src/index.ts",
"compatibility_date": "2026-04-07",
"compatibility_flags": ["nodejs_compat"],
"durable_objects": {
"bindings": [
{ "name": "SHARD", "class_name": "ShardDO" },
{ "name": "SESSION", "class_name": "SessionDO" }, // only with @lunora/auth
],
},
"migrations": [{ "tag": "v1", "new_sqlite_classes": ["ShardDO", "SessionDO"] }],
"d1_databases": [{ "binding": "DB", "database_name": "my-app-global", "database_id": "<replace>" }],
"r2_buckets": [{ "binding": "FILES", "bucket_name": "my-app-files" }],
}
```
`lunora dev` auto-reconciles most of this (and `lunora registry add` adds the
bindings an item needs), but the **resources themselves** must exist and their
ids must be filled in:
```bash
wrangler d1 create my-app-global # paste the returned database_id into wrangler.jsonc
wrangler r2 bucket create my-app-files
```
The DO `class_name`s must be exported by your worker entry (the
`createShardDO()` / generated container exports) — wrangler rejects a binding
whose class the worker doesn't export. `lunora doctor` surfaces a missing
container export proactively.
## Secrets: `wrangler secret`, not `.dev.vars`
`.dev.vars` is **dev only** — it is git-ignored and never deployed. Production
secrets (`BETTER_AUTH_SECRET`, `RESEND_API_KEY`, provider client secrets, …) go
into Cloudflare:
```bash
wrangler secret put BETTER_AUTH_SECRET # prompts for the value, stored encrypted
wrangler secret list
```
Set every secret your app reads (mirror the secret-looking keys in `.dev.vars`)
before the first request hits production.
## Dev vs Production
- **Development:** `lunora dev` (Vite + workerd + Studio + codegen-on-save). The
schema baseline and `.dev.vars` belong to dev.
- **Production:** `lunora deploy`. Separate D1 database / R2 buckets / secrets
from dev. Never point a dev worker at prod resources.
For `.global()` table DDL, generate and commit SQL migrations with `lunora
migrate generate` before deploying; `@lunora/d1`'s runner applies them. For data
backfills, deploy first, then `lunora deploy --migrate` (or `lunora migrate up
--prod`). See `lunora-migration-helper`.
## Common Pitfalls
1. **Placeholder `database_id`.** The D1 binding ships with `<replace>`; run
`wrangler d1 create` and paste the id. `lunora doctor` catches this.
2. **DO class not exported.** `wrangler deploy` fails if a `class_name` isn't
exported by the worker entry — export `ShardDO`/`SessionDO`/generated
containers.
3. **Secrets only in `.dev.vars`.** They never reach production; use `wrangler
secret put` for every prod secret.
4. **Bypassing the drift gate.** `--allow-schema-drift` ships a breaking schema
with no migration — stage the change (`lunora-migration-helper`) instead.
5. **Deploying with uncommitted codegen.** Commit `lunora/_generated/` and
`lunora/.lunora-schema.json` so CI and the gate see the same baseline.
## Checklist
- [ ] `lunora doctor` passes (no FAIL).
- [ ] `wrangler.jsonc` has `compatibility_date`, `nodejs_compat`, the `SHARD` DO
binding, and the SQLite migration tag.
- [ ] D1 / R2 resources created; real ids pasted into `wrangler.jsonc`.
- [ ] DO + container `class_name`s exported by the worker entry.
- [ ] Production secrets set via `wrangler secret put`.
- [ ] Schema changes migrated; the drift gate is green (no `--allow-schema-drift`).
- [ ] `lunora deploy` succeeded; `--migrate` run if data backfills were pending.