anolilab/lunora/lunora-deploy

Claude Code and agent skill by anolilab/lunora

Trust tier 3
  • community
Source check pending

Checking that the SKILL.md file is still in the repository.

Deploys a Lunora app to Cloudflare Workers + Durable Objects and gets wrangler.jsonc, remote resources and secrets to line up. Covers `lunora deploy` (and `--env`, `--dry-run`, `--migrate`), isolated per-PR previews (`lunora preview deploy | list | delete`), the `lunora doctor` / `lunora verify` / `lunora prepare` preflights, Durable Object bindings (SHARD, SHARD_REGISTRY, SCHEDULER), the `DB` D1 binding and its placeholder id, R2 buckets, production secrets (`lunora env push`, `wrangler secret put`) versus `.dev.vars`, the schema-drift gate, the architecture diff, and rollback with `lunora cloudflare deployments`. Use when the user asks to "deploy", "ship to production", "set up a staging environment", wire CI deploys, or when a deploy fails on a binding, a placeholder `database_id`, an unexported Durable Object class, a missing secret, or "schema drift gate blocked deploy".

Install

npx vskill@latest install anolilab/lunora/lunora-deploy

SKILL.md

Lunora Deploy

Deploying a Lunora app means making wrangler.jsonc, the remote Cloudflare resources (D1, R2, secrets) and the worker's exports agree. lunora deploy automates most of that and refuses to ship when they don't.

Local development is lunora dev (see lunora-quickstart). If the deploy is blocked by a breaking schema change, stage it with lunora-migration-helper first.

What lunora deploy does

In order, stopping at the first failure:

  1. Codegen — regenerates lunora/_generated/. ERROR-level codegen

advisories fail the deploy in CI (--strict-advisories forces this locally, --no-strict-advisories turns it off). Nothing here runs tsc and wrangler strips types, so a project with a type error deploys — gate CI on lunora verify, which does type-check.

  1. Schema-drift gate — compares the schema with the committed baseline

lunora/.lunora-schema.json (details below).

  1. Architecture diff — prints modules and edges (calls, table reads/writes,

enqueues) added or removed since the last successful deploy. Informational only; the manifest is recorded in .lunora/architecture[.<env>].json after wrangler succeeds.

  1. Binding reconcile — writes the bindings the code implies into

wrangler.jsonc: Durable Object bindings plus their new_sqlite_classes migration, the DB D1 binding for .global() tables, crons, and the compatibility date. R2 buckets are not auto-written (the bucket name is yours to choose); you get a warning instead.

  1. Read-only checks — blocks on a D1 database_id still set to

<replace-with-d1-create-id>, on a loopback URL (localhost, 127.x, ::1) in vars, on a missing container Dockerfile, or on no Docker engine when a container builds locally.

  1. Validate `wrangler.jsonc` — the SHARD binding to ShardDO (or

LunoraDO for apps that merge their DOs), a minimum compatibility_date, DO classes registered in migrations, and the shape of every binding. compatibility_flags are not checked; templates ship nodejs_compat for dependencies that import node: builtins.

  1. Secrets gate — lists the secrets the app's packages need and checks

which are set on the worker. Interactive runs offer to mint and push the generatable ones (BETTER_AUTH_SECRET, LUNORA_ADMIN_TOKEN, …). In CI a missing required secret aborts the deploy. Skipped for --dry-run, for lunora preview (it uploads its own secrets), and when the worker doesn't exist yet.

  1. Services, then `wrangler deploy` — services declared in

lunora.config deploy first (--skip-services to skip), then the app.

  1. After a successful deploy — records the URL as the project link

(lunora link), optionally runs --migrate and --health-check, and only then re-blesses the schema baseline.

lunora prepare runs steps 1–6 without deploying (useful in CI), but it takes no --env, so it validates the top-level config only. lunora verify runs validation, a codegen dry run and tsc --noEmit without writing files, and does take --env.

Flags worth knowing:

FlagEffect
--env <name>Deploy a wrangler environment; checks read that env's (non-inherited) bindings.
--dry-runRun every gate and bundle without publishing; wrangler.jsonc edits roll back.
--health-checkProbe /_lunora/health/ready on the new version; fail if it never answers.
--prebuiltSkip codegen and the drift gate when lunora prepare already ran in this job.
--migrateRun every declared data migration (up is idempotent) against the live worker.
--allow-schema-driftDeploy despite blocked drift; checks nothing, the baseline is not advanced.

--migrate is checked before wrangler runs, so a missing requirement aborts the whole deploy:

  • --migrate-yes — confirms running production data migrations.
  • --migrate-url <https://…> — required unless this checkout is linked for the

same --env (a previous successful deploy links it automatically).

  • An admin token — --migrate-token or LUNORA_ADMIN_TOKEN.
bash
lunora deploy --migrate --migrate-yes --migrate-url https://app.example.com

Per-PR previews

lunora deploy --preview is refused: a Worker version shares production's Durable Objects, D1 and R2, and gets no preview URL. Use lunora preview:

bash
lunora preview deploy 123 --seed # own Worker + DO namespace + D1/KV/R2, migrated, seeded
lunora preview deploy 123 --dry-run # the plan and env block, no API call
lunora preview list
lunora preview delete 123 # Worker + every resource on its record
  • Records are keyed by lunora.appId in package.json (the first local

lunora preview deploy writes it; commit it, CI cannot mint one), so a Worker rename does not lose a preview. Old Worker-name keys are re-keyed on the next deploy/delete.

  • --env <name> plans from that Wrangler environment (top level plus env.<name>)

for an app whose bindings live only there; recorded, so delete needs no flag for the resources. If that environment names a different account_id, pass the same --env to list/delete (or set CLOUDFLARE_ACCOUNT_ID); they refuse rather than guess. Without it, env-only bindings are refused. CI leaves it unset.

  • Resources are named <worker>-preview-<name>-<binding> and recorded in the

account's lunora-preview-registry KV namespace; delete only removes what a record lists, and refuses anything production's config names.

  • Mintable secrets are minted per deploy, or derived from

LUNORA_PREVIEW_SECRET_SEED when set (stable across deploys); a provider key reaches the preview only via --secret KEY (value from the environment).

  • Production vars are copied; override one with --var KEY=VALUE (e.g. an

auth base URL pointing at the preview's workers.dev URL).

  • Refused (with the list) when the app binds Hyperdrive, services, queues,

workflows, Vectorize or another binding a preview cannot isolate yet.

  • Token needs Workers Scripts, D1, Workers KV Storage and Workers R2 Storage:

Edit. Cloudflare only; celld and Node refuse.

  • lunora init --ci github|gitlab scaffolds the deploy-on-PR and

delete-on-close jobs.

Preflight

bash
lunora doctor # read-only; exits 1 on any FAIL, WARN/INFO don't block
lunora doctor --format json # each finding carries a stable `code` for CI/agents
lunora verify --env production

lunora doctor fails on a missing or invalid wrangler.jsonc, a missing SHARD binding, a placeholder D1 id (d1-placeholder-id), and a binding whose class the worker doesn't export (wrangler-class-unexported), and an Artifacts namespace in the wrong jurisdiction. It warns on unfilled .dev.vars secrets, placeholder send_email destinations, an account near its Durable Object class cap, and a cloudflare.config.ts (cf-config-present).

wrangler.jsonc

Templates ship this shape. The d1_databases and r2_buckets entries are only needed when the app uses .global() tables or auth (D1) and storage (R2):

jsonc
{
"name": "my-app",
"main": "src/server.ts",
"compatibility_date": "2026-10-01",
"compatibility_flags": ["nodejs_compat"],
"durable_objects": {
"bindings": [
{ "name": "SHARD", "class_name": "ShardDO" },
{ "name": "SHARD_REGISTRY", "class_name": "ShardRegistryDO" },
],
},
"migrations": [{ "tag": "v1", "new_sqlite_classes": ["ShardDO", "ShardRegistryDO"] }],
"limits": { "cpu_ms": 30000 },
"d1_databases": [{ "binding": "DB", "database_name": "my-app", "database_id": "<replace-with-d1-create-id>" }],
"r2_buckets": [{ "binding": "UPLOADS", "bucket_name": "my-app-uploads" }],
}

Reconcile adds SCHEDULER → SchedulerDO when the app schedules work, and bindings for generated containers, workflows and agents. Every class_name must be exported by the worker entry or wrangler rejects the deploy.

Lunora writes the bindings but cannot create the remote resources:

bash
wrangler d1 create my-app # paste the returned database_id into wrangler.jsonc
wrangler r2 bucket create my-app-uploads

When using --env, remember that vars, d1_databases, durable_objects and containers do not inherit in wrangler. Each environment needs its own entries, including its own D1 database.

Secrets

.dev.vars is for development only: it is git-ignored and wrangler deploy never uploads it. Production secrets live in Cloudflare:

bash
lunora env diff --env production # keys local-only vs set remotely
lunora env push --env production --yes # uploads .dev.vars values; refuses placeholders
wrangler secret put RESEND_API_KEY --env production # set a prod-only value by hand

lunora env push uploads the values from .dev.vars. If production needs different values than dev (it usually does for provider keys), set those with wrangler secret put instead.

Schema-drift gate

The gate blocks when the schema changed in a breaking way since lunora/.lunora-schema.json and no new defineMigration on the affected table covers it. The block message lists each change and a paste-ready lunora migrate create … --table … command. Commit the baseline file so CI and every machine gate against the same shape. It is advanced only after a successful deploy, so a failed deploy never moves it past a change that didn't ship.

When the stored rows already fit a tightening, lunora deploy notices on its own: it reads the affected tables from the deployed worker (--migrate-url or the project link, plus LUNORA_ADMIN_TOKEN) and lets the change through, naming any row that does not fit otherwise. A new unique index is checked the same way, for repeated values. --allow-schema-drift skips the gate for one run without checking. Otherwise stage the change with lunora-migration-helper.

.global() table DDL

The runtime provisions .global() tables itself on first use (CREATE TABLE IF NOT EXISTS plus additive ADD COLUMN / indexes), so additive changes need no step. lunora migrate generate writes a reviewable SQL file for D1-backed global tables, but no command applies it, lunora deploy included. Destructive or hand-written statements in it (drops, a backfilling UPDATE, a rename) must be applied by hand:

bash
wrangler d1 execute DB --remote --file=lunora/migrations/<file>.sql

See lunora-migration-helper for the full flow.

Rollback

bash
lunora cloudflare deployments list
lunora cloudflare deployments status # which versions carry traffic, and how much
lunora cloudflare deployments rollback --yes # previous version
lunora cloudflare deployments promote <version-id> --yes # send 100% of traffic to a version

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 95 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

7-day trend score
5

Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/anolilab/lunora/lunora-deploy/badge)](https://verified-skill.com/skills/anolilab/lunora/lunora-deploy)

Something wrong with this skill or its page? Tell us.