Skip to content

fix(storage): align prompt fallbacks and TTY handling with CLI#119

Merged
jamie-at-bunny merged 2 commits into
mainfrom
fix/storage-behavioral-consistency
Jul 10, 2026
Merged

fix(storage): align prompt fallbacks and TTY handling with CLI#119
jamie-at-bunny merged 2 commits into
mainfrom
fix/storage-behavioral-consistency

Conversation

@jamie-at-bunny

Copy link
Copy Markdown
Member
  • Use the shared isInteractive() gate (stdin + stdout TTY, json-aware) instead of stdout-only checks in the zone resolver, link, and zone add
  • zones update: require field flags whenever non-interactive (json, non-TTY, or --force) instead of only under --output json, and abort the whole interactive edit on a mid-flow cancel rather than applying partial answers
  • zones update --force now matches zones add: skip prompts and confirmations, use flag values only
  • storage zone domains commands now resolve the zone like every other storage command: explicit ref, then linked zone, then picker

…tions

- Use the shared isInteractive() gate (stdin + stdout TTY, json-aware)
  instead of stdout-only checks in the zone resolver, link, and zone add
- zones update: require field flags whenever non-interactive (json,
  non-TTY, or --force) instead of only under --output json, and abort
  the whole interactive edit on a mid-flow cancel rather than applying
  partial answers
- zones update --force now matches zones add: skip prompts and
  confirmations, use flag values only
- storage zone domains commands now resolve the zone like every other
  storage command: explicit ref, then linked zone, then picker
@bogdan-at-bunny

Copy link
Copy Markdown

@codex review

@changeset-bot

changeset-bot Bot commented Jul 10, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: bba2541

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-linux-x64 Patch
@bunny.net/cli-linux-arm64 Patch
@bunny.net/cli-darwin-x64 Patch
@bunny.net/cli-darwin-arm64 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 commented Jul 10, 2026

Copy link
Copy Markdown

Greptile Summary

This PR standardises TTY detection and non-interactive gate across storage commands by replacing ad-hoc output === "json" || !process.stdout.isTTY checks with the shared isInteractive(output) helper (which also checks stdin.isTTY). The zones update command gains proper cancellation handling — mid-flow Ctrl+C now aborts the whole edit instead of applying partial answers — and avoids sending a no-op ReplicationZones PATCH body when no new replicas are picked. The storage zone domains subcommand now falls through to the linked-zone manifest and the interactive picker, matching every other storage command.

  • interactive.ts, link.ts, zone/add.ts, zone/update.ts: isInteractive(output) replaces output === "json" || !process.stdout.isTTY, which also closes the gap where a piped stdin (non-TTY) was incorrectly treated as interactive.
  • zone/update.ts: --force, non-TTY, and --output json all require flags and error cleanly; onCancel properly sets a flag so any mid-flow Ctrl+C throws before partial answers are used; ReplicationZones is now undefined in the PATCH body when the user picks no new regions.
  • zone/hostnames/index.ts: delegates zone resolution to resolveStorageZoneInteractive, adding the linked-zone and picker fallbacks that were missing.

Confidence Score: 5/5

Safe to merge. Changes are scoped to CLI prompt gating and PATCH body construction; no API calls, auth logic, or data-plane code is altered.

All changed code paths are straightforward control-flow fixes. The cancellation refactor correctly uses the onCancel flag pattern, the conditional ReplicationZones omission is logically sound, and the isInteractive() consolidation tightens (rather than loosens) the interactive gate. The domains fallback change is additive and mirrors the existing pattern used by every other storage command.

No files require special attention.

Important Files Changed

Filename Overview
packages/cli/src/commands/storage/zone/update.ts Cancellation handling corrected (onCancel flag pattern), ReplicationZones omitted when no new picks, --force/non-TTY/json guard unified; logic is correct.
packages/cli/src/commands/storage/interactive.ts Replaces stdout-only TTY check with isInteractive(); adds opts.force to prevent picker in non-interactive contexts; linked-zone manifest fallback unaffected.
packages/cli/src/commands/storage/zone/hostnames/index.ts Zone resolution upgraded from a hard "zone required" guard to resolveStorageZoneInteractive, adding manifest and picker fallbacks; output forwarded correctly.
packages/cli/src/commands/storage/link.ts Straightforward swap from manual TTY check to isInteractive(); no behavioural change beyond closing the stdin-not-TTY gap.
packages/cli/src/commands/storage/zone/add.ts Single-line interactive flag now uses isInteractive(); semantics unchanged otherwise.
.changeset/storage-behavioral-consistency.md Changeset entry correctly tagged as a patch fix for @bunny.net/cli.
AGENTS.md Documentation updated to reflect new --force semantics, mid-flow cancel behaviour, and domains picker fallback.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Command invoked] --> B{ref provided?}
    B -- yes --> C[resolveStorageZone by name/ID]
    B -- no --> D{manifest linked zone?}
    D -- yes --> E[fetchStorageZone from manifest]
    D -- no --> F{opts.force OR !isInteractive}
    F -- yes --> G[throw: A storage zone is required.]
    F -- no --> H[Interactive picker]
    H --> I{User selects zone?}
    I -- cancel --> J[throw: A storage zone is required.]
    I -- ok --> K[fetchStorageZone by ID]
    C --> L[Zone resolved]
    E --> L
    K --> L
    L --> M{hasFlags?}
    M -- yes --> N[settingsFromFlags]
    M -- no --> O{interactive?}
    O -- no --> P[throw: No changes requested.]
    O -- yes --> Q[promptSettings]
    Q --> R{User cancels mid-flow?}
    R -- yes --> S[throw: Update cancelled.]
    R -- no --> T[Return settings with ReplicationZones=undefined if no new picks]
    N --> U{settings.ReplicationZones set?}
    T --> U
    U -- yes --> V[confirmAddedReplicationRegions]
    V --> W{Confirmed or --force?}
    W -- no --> X[log Cancelled. return]
    W -- yes --> Y[PATCH /storagezone/id]
    U -- no --> Y
    Y --> Z[Output result]
Loading
%%{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"}}}%%
flowchart TD
    A[Command invoked] --> B{ref provided?}
    B -- yes --> C[resolveStorageZone by name/ID]
    B -- no --> D{manifest linked zone?}
    D -- yes --> E[fetchStorageZone from manifest]
    D -- no --> F{opts.force OR !isInteractive}
    F -- yes --> G[throw: A storage zone is required.]
    F -- no --> H[Interactive picker]
    H --> I{User selects zone?}
    I -- cancel --> J[throw: A storage zone is required.]
    I -- ok --> K[fetchStorageZone by ID]
    C --> L[Zone resolved]
    E --> L
    K --> L
    L --> M{hasFlags?}
    M -- yes --> N[settingsFromFlags]
    M -- no --> O{interactive?}
    O -- no --> P[throw: No changes requested.]
    O -- yes --> Q[promptSettings]
    Q --> R{User cancels mid-flow?}
    R -- yes --> S[throw: Update cancelled.]
    R -- no --> T[Return settings with ReplicationZones=undefined if no new picks]
    N --> U{settings.ReplicationZones set?}
    T --> U
    U -- yes --> V[confirmAddedReplicationRegions]
    V --> W{Confirmed or --force?}
    W -- no --> X[log Cancelled. return]
    W -- yes --> Y[PATCH /storagezone/id]
    U -- no --> Y
    Y --> Z[Output result]
Loading

Reviews (2): Last reviewed commit: "fix(storage): address review feedback on..." | Re-trigger Greptile

@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: 4b5363b18b

ℹ️ 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/storage/zone/update.ts
- Honor --force when resolving the update target: pass it through to
  resolveStorageZoneInteractive so a missing zone errors instead of
  opening the picker
- Omit ReplicationZones from the interactive editor's settings when no
  new regions were picked, keeping replication out of the update body
- Clarify that flags take full precedence over the interactive editor
@jamie-at-bunny jamie-at-bunny merged commit dfbe849 into main Jul 10, 2026
2 checks passed
@jamie-at-bunny jamie-at-bunny deleted the fix/storage-behavioral-consistency branch July 10, 2026 14:58
@github-actions github-actions Bot mentioned this pull request Jul 10, 2026
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.

3 participants