bunny sites#125
Conversation
Grounds the bunny sites plan in verified repo machinery: defineCommand/ defineNamespace registration in cli.ts, core/manifest.ts local-link pattern, storage files-api uploader (caller-side SHA-256 checksums), scripts env-var promote lever, createHostnamesCommands factory for the domains phase, and the changeset/testing/docs conventions each PR must follow. Adds a Phase 0 spike question for how a middleware script attaches to a storage-backed pull zone. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J6CzqnCzQrV6exqx4PrF13
|
@codex review |
🦋 Changeset detectedLatest commit: a067ab7 The changes in this PR will be included in the next version bump. This PR includes changesets to release 7 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
…c deploys
Implements the docs/plans/sites.md plan (phases 1-5):
- sites create provisions storage zone + pull zone + middleware router
(attached via the pull zone's MiddlewareScriptId); idempotent re-runs
converge on half-finished creates
- sites deploy uploads immutable deploys/{id}/ directories (git short-sha
when clean, content hash otherwise; unchanged deploys are no-ops) and
promotes by flipping the router's CURRENT_DEPLOY env var + purging the
cache; --build runs the configured build with remote env merged
- sites deployments list/publish/prune: instant rollback via --previous,
prune never drops current/previous
- sites domains mounts core/hostnames createHostnamesCommands (new
onAdded/onRemoved hooks) and pairs every apex with a *.preview.<domain>
wildcard for per-deploy preview URLs
- sites env manages build-time vars at _bunny/env.json (403-blocked by
the router, masked list, dotenv pull)
- site state lives at _bunny/site.json in the storage zone with a
sha256-etag optimistic lock; .bunny/site.json is the local pointer
- @bunny.net/app-config gains an optional sites { name, dir, build }
block consumed for deploy defaults
- co-located bun tests (36) via a swappable siteFiles IO seam and
path-branching fake clients; README/AGENTS/skills docs + changesets
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J6CzqnCzQrV6exqx4PrF13
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 215874a663
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
The live API returns GET /pullzone as { Items, CurrentPage, HasMoreItems }
for some queries (e.g. ?search=) even though the OpenAPI spec types the
response as a plain array, so createSite crashed on .find during pull
zone lookup. Route all pull zone listing through fetchPullZones, which
accepts both shapes and follows HasMoreItems across pages (with an
empty-page guard). /storagezone?search= genuinely returns a plain array,
so the storage lookup is unchanged.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J6CzqnCzQrV6exqx4PrF13
Greptile SummaryThis PR delivers the full
Confidence Score: 5/5The change is safe to merge. All core deploy paths are implemented with proper error handling and the optimistic-lock merge logic handles concurrent CI deploys correctly. The PR is a large, well-structured addition with no identified defects in the changed code paths. All issues from the previous review cycle are addressed. The one remaining note on deploy record ID validation in parseRemoteState is a defense-in-depth suggestion that does not affect any reachable production path today. No files require special attention. The api.ts merge logic and router/source.ts routing rules are the most complex paths and both read cleanly. Important Files Changed
Sequence Diagram%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant CLI as bunny CLI
participant StorageAPI as Storage API
participant ComputeAPI as Compute API
participant PullZoneAPI as Pull Zone API
participant CDNEdge as CDN Edge
note over CLI: bunny sites deploy ./dist --production
CLI->>StorageAPI: GET _bunny/site.json (read state + ETag)
StorageAPI-->>CLI: RemoteSiteState + ETag
CLI->>CLI: hashFiles + resolveDeployIdentity
alt files unchanged
CLI-->>CLI: skip upload, reuse existing deployId
else new content
CLI->>StorageAPI: "PUT deploys/id/** (concurrent, SHA-256)"
StorageAPI-->>CLI: ok
CLI->>StorageAPI: PUT _bunny/site.json (ETag + merge)
StorageAPI-->>CLI: new ETag
end
CLI->>ComputeAPI: "PUT script variables CURRENT_DEPLOY=id"
ComputeAPI-->>CLI: ok
CLI->>PullZoneAPI: POST purgeCache
PullZoneAPI-->>CLI: ok
CLI->>CDNEdge: probe GET (await non-404)
CDNEdge-->>CLI: 200
CLI->>PullZoneAPI: POST purgeCache (second)
PullZoneAPI-->>CLI: ok
CLI->>StorageAPI: PUT _bunny/site.json (mark current)
StorageAPI-->>CLI: ok
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant CLI as bunny CLI
participant StorageAPI as Storage API
participant ComputeAPI as Compute API
participant PullZoneAPI as Pull Zone API
participant CDNEdge as CDN Edge
note over CLI: bunny sites deploy ./dist --production
CLI->>StorageAPI: GET _bunny/site.json (read state + ETag)
StorageAPI-->>CLI: RemoteSiteState + ETag
CLI->>CLI: hashFiles + resolveDeployIdentity
alt files unchanged
CLI-->>CLI: skip upload, reuse existing deployId
else new content
CLI->>StorageAPI: "PUT deploys/id/** (concurrent, SHA-256)"
StorageAPI-->>CLI: ok
CLI->>StorageAPI: PUT _bunny/site.json (ETag + merge)
StorageAPI-->>CLI: new ETag
end
CLI->>ComputeAPI: "PUT script variables CURRENT_DEPLOY=id"
ComputeAPI-->>CLI: ok
CLI->>PullZoneAPI: POST purgeCache
PullZoneAPI-->>CLI: ok
CLI->>CDNEdge: probe GET (await non-404)
CDNEdge-->>CLI: 200
CLI->>PullZoneAPI: POST purgeCache (second)
PullZoneAPI-->>CLI: ok
CLI->>StorageAPI: PUT _bunny/site.json (mark current)
StorageAPI-->>CLI: ok
Reviews (15): Last reviewed commit: "fix(sites): infer the framework output d..." | Re-trigger Greptile |
bunny sites feature…an-15thnb # Conflicts: # AGENTS.md
…ader; sites changesets to patch
Summary
Add a comprehensive implementation plan for the
bunny sitesfeature — static-site hosting with atomic deploys, instant rollback, and per-deploy preview URLs. This document outlines the phased approach, platform assumptions to validate, command surface, and cross-cutting conventions.Changes
docs/plans/sites.md— a detailed 5-phase implementation plan covering:create,list,show,link,unlink,delete) with local manifests and remote statecreateHostnamesCommands()factory with wildcard preview subdomainsbunny.jsoncsupportNotable details
dpl-{id}.preview.{domain}; deploy IDs via git short-sha or content hash; site identity keyed by storage zone ID/deploys/{CURRENT_DEPLOY}{path}on apex, per-deploy prefixes on preview hosts, and a friendly "no deploys yet" pagedefineCommand()/defineNamespace(), flag equivalents for prompts,--output jsonhandling,UserErrorusage, client wiring, testing patterns, and documentation updatesThis plan is the foundation for all subsequent
sitesfeature PRs and ensures alignment on architecture, API usage, and CLI conventions before implementation begins.https://claude.ai/code/session_01J6CzqnCzQrV6exqx4PrF13