Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .changeset/deploy-health-check.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
"@bunny.net/cli": patch
---

`bunny sites deploy` asks the site for a page before it calls the deploy a
success.

A published Edge Script that will not start makes the edge answer 400 with an
empty body, and the deploy said nothing: a green line, a URL, and a site that
served nothing. `withastro/astro.build` deployed exactly like that.

The check probes the production URL up to three times, each with its own query so
the CDN cache cannot hold the answer. A redirect or a 404 counts as a working
script; only 400 and 5xx are faults, and a site that cannot be reached at all is
not called one.

A site that answers is then asked for a path it cannot hold. The answer has to be
the deploy's own `404.html`, because a pull zone with no error page of its own
answers a miss with bunny.net's. That shipped once, on a documentation site.

`--output json` carries `serving`, `status` when it is not, and `notFoundStatus`
when the 404 page did not answer.
16 changes: 16 additions & 0 deletions .changeset/deploy-polish.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
"@bunny.net/cli": patch
---

Four smaller things around `bunny sites deploy`.

- `--name <name>` is honoured when the deploy creates the site. Without it an
unattended run stopped with "No site specified and no linked site found."
- `--region <code>` chooses the storage region for a site the deploy creates.
Only `sites create` could name one before.
- The domain prompt after a first deploy refuses a value that is not a hostname,
and says so. It used to send it, and the API's answer is `An error has
occurred.`
- The upload counts bytes as well as files. `withastro/astro.build` sends 1.4 GB
in 8828 files, and ten minutes of `4210/8828 files` says nothing about how much
is left.
76 changes: 76 additions & 0 deletions .changeset/lab-astro-deploy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
---
"@bunny.net/cli": minor
"@bunny.net/config": minor
---

`bunny lab deploy astro` deploys an Astro project that renders pages per request.

Two commands, and no more:

```bash
bunny lab deploy astro
bunny lab undeploy astro
```

Astro's server becomes a standalone Edge Script. The client build goes into a
storage zone the script reads. The pull zone's origin is the script, so nothing
sits between a request and the code. The command provisions the three resources
on its first run, uploads the build, sets every variable from what it already
knows, applies the pull zone settings the adapter asks for, and publishes. No
password passes through the terminal.

`bunny sites deploy` goes back to deploying a directory of files. It used to do
both jobs, and the share was the problem: a project that renders per request
cannot use `CURRENT_DEPLOY`, because one script serves one release, and a
directory of files cannot use a build manifest. Each flow carried checks for the
shape it was not, and a reader could not tell which command applied to which
project. Nothing under `lab/` imports from `sites/` now.

`lab` says the interface is still being shaped. The namespace is hidden from help
and from the landing page, and a workflow built on either command should expect
to be updated.

Server-side rendering only. A static Astro build is a directory of files, and
this command refuses one and names the command that deploys it.

Measured against two real templates, deployed to a real account:
`withastro/astro/examples/ssr` and `render-examples/astro-ssr`. What that
changed:

- **Astro 7 is checked before the install.** The adapter's peer range is
`^7.0.0`, and `render-examples/astro-ssr` ships Astro 5. npm answers that with
an ERESOLVE about peer ranges, which tells a developer nothing to act on. The
command stops first, and names `npx @astrojs/upgrade`. Upgrading a framework
major stays the developer's decision.
- **The adapter it replaces is uninstalled, not only unimported.**
`@astrojs/node@9` peers on `astro@^5`, so after an upgrade to Astro 7 it makes
every later install in that project fail. Replacing the adapter in the config
and leaving the package in `package.json` left the project broken in a way
nothing explained.
- **The pull zone's cache override goes off.** With the zone default in place the
edge rewrites every `Cache-Control` the adapter sets, so an HTML page would sit
a month stale in a browser that a purge cannot reach.
- **The state file belongs to the project, not to the working directory.**
`bunny lab deploy astro ./project` run from anywhere else found no state,
decided the app was new, and created a second set of resources beside the first.
- **The prefix is not added twice.** An app called `astro-ssr-demo` became
`astro-astro-ssr-demo-a1b2c3`, which reads like a mistake and spends six
characters of a 63-character DNS label on nothing.

The deploy asks the site for its home page, and for a path it does not hold,
before it calls itself a success. Above 7.5 MB the warning says why a script
answers 400: measured in August 2026, the same code served every request at
7.44 MB and none at 7.83 MB, well under the documented 10 MB.

Each deploy's files live at `deploys/{id}/`, and the folder's name is written into
the top of the bundle at publish time, so a release can only read the files it was
built against. There is no rollback, so every folder but the current one and the
one before it is deleted after a publish.

`bunny lab undeploy astro` deletes the pull zone, the script, and the storage
zone. `--keep-storage` keeps the files. It lists what will go before it asks, and
`--name` finds the same resources with no state file, which is the fresh-clone and
CI case.

`BuildManifestSchema` in `@bunny.net/config` is the contract with the adapter. The
CLI knows no framework: it reads the manifest.
34 changes: 34 additions & 0 deletions .changeset/router-static-layer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
"@bunny.net/cli": minor
---

Serve a static site's 404 page, redirects, and headers from the router.

The router reads three file names out of the deploy it is serving: `404.html`,
`_redirects`, and `_headers`. Cloudflare Pages and Netlify read the same three,
so nothing in the router knows about a framework and every preset gets it.

- **`404.html`** answers a path the deploy does not hold, at status 404. Without
it the pull zone answers with bunny.net's error page, whatever the site built.
That shipped: a documentation site went up and every wrong URL showed
bunny.net's page.
- **`_redirects`** sends a real redirect. One rule per line, `/from /to [status]`,
`#` comments, a trailing `*` captured as `:splat`, and `!` to beat a file at the
same path. 301 is the default status; 302, 303, 307 and 308 are read too. A
rewrite (`200`) is not: it would have the router fetch another path of its own
site, which can be made to loop.
- **`_headers`** carries the headers Bunny Storage cannot hold. A `/path` line
opens a block, `Name: value` lines under it belong to it, and a later block
wins the same name.

A rule and a header match on a trailing-slash-normalised path, so `/about` and
`/about/` are one rule. The rules are read once per deploy and held in memory,
never written into the script, so a publish stays an environment variable change.

The router now sets `Cache-Control` on every response, and a site's pull zone
stops overriding it (`CacheControlMaxAgeOverride: -1`). The zone default of 30
days replaced every answer the script gave, so an HTML page could be a month
stale in a browser that no purge reaches. A page now gets 60 seconds, anything
else 30 days as before, and `_headers` wins where it says anything.
`bunny sites upgrade-router` applies the router and the setting together, and
`bunny sites deploy` does it for a site whose router lags.
Loading
Loading