Skip to content

fix(sites): recoverable delete and create, duplicate name checks, and list, open and link fixes - #248

Open
jamie-at-bunny wants to merge 2 commits into
mainfrom
sites-lifecycle
Open

jamie-at-bunny wants to merge 2 commits into
mainfrom
sites-lifecycle

Conversation

@jamie-at-bunny

Copy link
Copy Markdown
Member

Makes bunny sites create and delete recoverable and tidies list, open and link.

@bunnynet-devops

Copy link
Copy Markdown

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-07T16:28:43.564098Z ea062fe Manual request
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@greptile-apps

greptile-apps Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 3/5

[Medium risk] Improves site lifecycle commands with better error handling and recovery.

Fix the destructive retry instructions, the resumed-create name check, and the required command-reference update before merging.

Fix All in Claude CodeFindings

  1. P1 Retry deletes files meant to stay ▶
  2. P1 Resuming creates a duplicate name ▶
  3. P2 Working sites get false warnings ▶
  4. P2 Command reference gives old rules ▶
  5. P2 Recovery branches lack tests ▶

Summary

This PR improves recovery after partial site creation or deletion, checks imported site names, warns about incomplete listings, and lets interactive linking switch sites.

  • The deletion retry command can discard --keep-storage and delete files.
  • Resuming creation skips the new imported-name check.
  • The first-deploy warning is wrong for imported sites.
  • The command reference and deletion recovery tests need updates.
Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Delete site] --> B[Delete pull zone]
  B -->|Failed| C[Keep storage and local link]
  C --> D[Print retry command]
  D --> E[Retry loses keep-storage choice]
  B -->|Deleted or already missing| F{Keep storage requested?}
  F -->|Yes| G[Remove site marker]
  F -->|No| H[Delete storage and files]
  E --> H
Loading

Reviews (1) · Last reviewed commit: "fix(sites): recoverable delete and creat..." · Reviewed by Greptile

if (failures.length > 0) {
logger.dim(" Re-run the command to retry the failed deletions.");
logger.dim(
` Re-run \`bunny sites delete ${state.storageZoneId}\` to retry the failed deletions.`,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Retry deletes files meant to stay

If the pull-zone deletion fails during bunny sites delete my-site --keep-storage, the printed retry command drops --keep-storage. Following it later deletes the storage zone and all its files, despite the user's explicit choice to keep them. Preserve --keep-storage in the retry command when it was requested.

Fix in Claude Code

Comment thread packages/cli/src/commands/sites/api.ts Outdated
Comment on lines +533 to +536
} else {
// An imported site keeps its original zone names, so the name-pattern scan above can't see it.
const sites = await fetchSites(coreClient, { strict: true });
if (sites.some((s) => s.state.name === name)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Resuming creates a duplicate name

The new name check runs only when no half-finished storage zone was found. A failed create can leave a zone without site state; importing another zone under that name is then allowed. Retrying the original create skips this check and finishes a second site with the same name, so commands using the name cannot identify one site. Run the account-wide name check before both fresh creation and resumption.

Fix in Claude Code

Comment thread packages/cli/src/commands/sites/open.ts Outdated
Comment on lines +86 to +88
if (!state.current) {
logger.dim(
" Nothing is published yet, so this URL serves a 404: run `bunny sites deploy`.",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Working sites get false warnings

Missing state.current does not always mean the URL serves a 404. Imported sites have no current until their first CLI deploy, but import deliberately leaves their existing content live. Users opening those sites are incorrectly told they are unavailable. Say that no CLI deploy has been published, or distinguish imported sites before claiming the URL returns a 404.

Knowledge Base Used: CLI automation workflows

Fix in Claude Code

Comment thread packages/cli/src/commands/sites/link.ts Outdated
site: ref,
link: false,
output,
pick: isInteractive(output),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Command reference gives old rules

Interactive sites link now skips the linked site and bunny.jsonc to show the picker. packages/cli/README.md still says those values win before the picker. The repository guide requires this command reference to be updated when commands change. Add the sites link exception before merging to satisfy that requirement.

Context Used: CLAUDE.md (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Fix in Claude Code

Comment on lines +1053 to +1064
await attempt("pull zone", state.pullZoneId, async () => {
try {
await coreClient.DELETE("/pullzone/{id}", {
params: { path: { id: state.pullZoneId } },
});
} catch (err) {
// Already gone (a re-run after a partial delete) is the goal.
if (!(err instanceof ApiError && err.status === 404)) throw err;
}
});
// The storage zone's site marker is the only way back to a pull zone that failed to delete, so keep both for the re-run.
if (!results.every((r) => r.deleted)) return results;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Recovery branches lack tests

The existing deletion tests cover only success, and their fake DELETE always succeeds. The new recovery behavior can regress without a test catching it. Add cases for a failed pull-zone deletion leaving storage intact, a missing pull zone allowing storage cleanup, and a failed storage deletion followed by a successful retry.

Fix in Claude Code

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ea062fedb7

ℹ️ 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".

Comment thread packages/cli/src/commands/sites/api.ts Outdated
if (unreadable > 0) {
const message = `Couldn't read ${unreadable} storage zone${unreadable === 1 ? "" : "s"}, so some sites may be missing`;
// A uniqueness check can't trust a partial list.
if (opts.strict) throw new UserError(`${message}.`, "Re-run to retry.");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve the underlying error during strict discovery

When strict discovery is used by fresh sites create and create --from-zone, any per-zone failure—such as a 401/403 from fetchStorageZone or a stale storage password from siteContextFromZone—is discarded here and replaced with “Re-run to retry.” Consequently, one unrelated inaccessible zone blocks every create/import while losing the status and actionable authentication hint; retain the first underlying error or its context in strict mode.

AGENTS.md reference: AGENTS.md:L269-L272

Useful? React with 👍 / 👎.

Comment on lines +94 to +95
if (err instanceof ApiError && !err.hint) {
err.hint = `Re-run \`bunny sites create ${opts.name}\` to resume where it stopped.`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Add the resume hint to non-Core provisioning failures

If provisioning fails after creating the zones while connecting to storage or writing _bunny/site.json, the error comes from connectStorageZone or @bunny.net/storage-sdk, so it is a UserError or ordinary error rather than an ApiError. This condition therefore omits the newly promised resume guidance precisely for state-write failures that leave resumable resources behind; preserve any existing hint and add the resume instruction for these failures too.

AGENTS.md reference: AGENTS.md:L83-L87

Useful? React with 👍 / 👎.

Comment thread packages/cli/src/commands/sites/api.ts Outdated
Comment on lines +533 to +535
} else {
// An imported site keeps its original zone names, so the name-pattern scan above can't see it.
const sites = await fetchSites(coreClient, { strict: true });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Check imported names before resuming a stateless zone

The imported-site uniqueness scan only runs when no matching stateless sites-<name>-* zone exists. If an account already has an imported site named foo plus a leftover half-created sites-foo-* zone, this branch resumes the leftover zone and writes a second site named foo, making name-based resolution ambiguous instead of enforcing the new uniqueness guarantee. Run the imported-name check even when a resumable zone was found.

Useful? React with 👍 / 👎.

Comment thread packages/cli/src/commands/sites/open.ts Outdated
Comment on lines +86 to +89
if (!state.current) {
logger.dim(
" Nothing is published yet, so this URL serves a 404: run `bunny sites deploy`.",
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid claiming imported sites serve a 404

An imported site intentionally has no state.current until its first CLI deploy, but importSite leaves the existing pull-zone routing and root content live during that period. Running sites open for such a site now incorrectly says the URL serves a 404 even though it may be serving the pre-import website; distinguish newly provisioned sites from imported sites before printing this warning.

Useful? React with 👍 / 👎.

@changeset-bot

changeset-bot Bot commented Oct 7, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: e1b9105

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 6 packages
Name Type
@bunny.net/cli Patch
@bunny.net/cli-darwin-arm64 Patch
@bunny.net/cli-darwin-x64 Patch
@bunny.net/cli-linux-arm64 Patch
@bunny.net/cli-linux-x64 Patch
@bunny.net/cli-windows-x64 Patch

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

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This review was skipped because it would exceed your organization's monthly flex usage limit. Raise the limit in billing settings or wait until the next billing period resets limits.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants