Skip to content

feat(dns): scan and import existing records when adding a zone#107

Merged
jamie-at-bunny merged 3 commits into
mainfrom
feat/dns-record-scan-import
Jun 30, 2026
Merged

feat(dns): scan and import existing records when adding a zone#107
jamie-at-bunny merged 3 commits into
mainfrom
feat/dns-record-scan-import

Conversation

@jamie-at-bunny

Copy link
Copy Markdown
Member

No description provided.

@bogdan-at-bunny

Copy link
Copy Markdown

@codex review

@changeset-bot

changeset-bot Bot commented Jun 30, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 6173aa3

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

This PR includes changesets to release 9 packages
Name Type
@bunny.net/openapi-client Patch
@bunny.net/cli Patch
@bunny.net/app-config Patch
@bunny.net/sandbox 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 Jun 30, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds dns records scan [domain] and dns zones add --import to let users discover and migrate existing DNS records when onboarding to bunny. It also extracts shared write/review logic into write.ts, refactors preset.ts and import.ts to use it, and corrects generated DnsDiscoveredRecord types to include Flags/Tag needed for CAA records.

  • New scan command triggers a server-side record scan, deduplicates against existing zone records (with trailing-dot and case normalisation), reconstructs CAA Flags/Tag from rdata when the API omits them, and presents a multiselect for the user to confirm imports.
  • dns zones add now auto-scans on TTY, presents a next-steps menu (zone-file upload, manual add, or continue), and surfaces failures as structured JSON fields with a nonzero exit under --import.
  • Shared write.ts makes every bulk-write path resilient: one bad record no longer strands the rest; a total failure always exits nonzero in both text and JSON modes.

Confidence Score: 5/5

Safe to merge. The issues flagged in earlier review rounds (silent JSON failure, trailing-dot dedup, JobId fallback, all-writes-fail exit code) are all addressed in this revision.

All previously identified logic concerns have been resolved: the JSON import path now captures errors and exits nonzero on total failure; recordKey normalises trailing dots on values; scanZoneRecords tracks the prior JobId so a stale completed job is not accidentally accepted. New code is covered by unit tests for the dedup, CAA reconstruction, and resilient-write paths. No new defects found during this review.

No files require special attention.

Important Files Changed

Filename Overview
packages/cli/src/commands/dns/zone/add.ts Adds auto-scan, reviewAndApply, and offerNextSteps to the zone-creation flow; JSON path captures importedRecords/failedRecords/importError and exits nonzero on total failure; TTY-gated to keep non-interactive use scriptable.
packages/cli/src/commands/dns/record/write.ts New shared write utilities: writeRecords collects per-record successes and failures; reviewAndApply wraps multiselect + spinner + reporting and throws UserError on total failure in both text and JSON modes.
packages/cli/src/commands/dns/record/scan-records.ts discoverImportableRecords filters SOA/apex-NS, normalises name and value (trailing dot, case), deduplicates against zone.Records, and reconstructs CAA Flags/Tag from rdata when absent.
packages/cli/src/commands/dns/api.ts Adds scanZoneRecords: triggers a scan via POST, fetches the prior job state first so a fresh completion can be identified even when the trigger omits a JobId; polls with a 45 s timeout.
packages/cli/src/commands/dns/record/scan.ts New dns records scan command: resolves zone interactively, scans, and calls reviewAndApply; JSON mode returns {applied, failures} even when no records are found.
packages/openapi-client/src/dns.ts New hand-authored type corrections for generated DNS scan types; adds DnsDiscoveredRecord, DnsRecordScanJob, and DnsRecordScanStatus enum to recover Flags/Tag dropped by the generator.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant User
    participant CLI as zone/add handler
    participant API as Bunny API
    participant Scan as discoverImportableRecords
    participant Write as reviewAndApply / writeRecords

    User->>CLI: bunny dns zones add example.com [--import]
    CLI->>API: "POST /dnszone {Domain}"
    API-->>CLI: created zone (Id, Domain)

    alt "output === json && --import"
        CLI->>Scan: discoverImportableRecords(client, created)
        Scan->>API: "GET /dnszone/{id}/records/scan (prior job)"
        Scan->>API: "POST /dnszone/records/scan {ZoneId}"
        loop poll until Completed / Failed / timeout
            Scan->>API: "GET /dnszone/{id}/records/scan"
            API-->>Scan: "{Status, JobId, Records}"
        end
        Scan-->>CLI: AddDnsRecordModel[]
        CLI->>Write: writeRecords(client, zone, records)
        Write-->>CLI: "{applied, failures}"
        CLI->>User: "JSON {zone, ImportedRecords, FailedRecords?}"
        alt all failed
            CLI->>User: exit nonzero + UserError
        end
    else TTY text mode
        CLI->>Scan: discoverImportableRecords(client, created)
        Scan-->>CLI: AddDnsRecordModel[]
        alt records found
            CLI->>Write: "reviewAndApply(records, assumeYes=--import)"
            Note over Write: multiselect unless assumeYes, writeRecords per-record, warn on partial, throw on total failure
            Write-->>CLI: done
        end
        alt "doImport===undefined && TTY"
            loop until continue
                CLI->>User: offerNextSteps menu
                User-->>CLI: import zone file / add record / continue
            end
        end
        CLI->>API: check NS delegation
        CLI->>User: nameserver setup instructions
    end
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"}}}%%
sequenceDiagram
    participant User
    participant CLI as zone/add handler
    participant API as Bunny API
    participant Scan as discoverImportableRecords
    participant Write as reviewAndApply / writeRecords

    User->>CLI: bunny dns zones add example.com [--import]
    CLI->>API: "POST /dnszone {Domain}"
    API-->>CLI: created zone (Id, Domain)

    alt "output === json && --import"
        CLI->>Scan: discoverImportableRecords(client, created)
        Scan->>API: "GET /dnszone/{id}/records/scan (prior job)"
        Scan->>API: "POST /dnszone/records/scan {ZoneId}"
        loop poll until Completed / Failed / timeout
            Scan->>API: "GET /dnszone/{id}/records/scan"
            API-->>Scan: "{Status, JobId, Records}"
        end
        Scan-->>CLI: AddDnsRecordModel[]
        CLI->>Write: writeRecords(client, zone, records)
        Write-->>CLI: "{applied, failures}"
        CLI->>User: "JSON {zone, ImportedRecords, FailedRecords?}"
        alt all failed
            CLI->>User: exit nonzero + UserError
        end
    else TTY text mode
        CLI->>Scan: discoverImportableRecords(client, created)
        Scan-->>CLI: AddDnsRecordModel[]
        alt records found
            CLI->>Write: "reviewAndApply(records, assumeYes=--import)"
            Note over Write: multiselect unless assumeYes, writeRecords per-record, warn on partial, throw on total failure
            Write-->>CLI: done
        end
        alt "doImport===undefined && TTY"
            loop until continue
                CLI->>User: offerNextSteps menu
                User-->>CLI: import zone file / add record / continue
            end
        end
        CLI->>API: check NS delegation
        CLI->>User: nameserver setup instructions
    end
Loading

Reviews (3): Last reviewed commit: "updates" | Re-trigger Greptile

Comment thread packages/cli/src/commands/dns/zone/add.ts Outdated
Comment thread packages/cli/src/commands/dns/record/scan-records.ts Outdated
Comment thread packages/cli/src/commands/dns/api.ts

@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: 9ae65982cb

ℹ️ 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/dns/record/scan-records.ts Outdated
}
const { applied, failures } = result;

if (output === "json") {

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 Fail JSON runs when no records were written

For --output json, this branch returns before the all-failed check below runs. If every PUT fails in dns records scan --output json or dns records preset ... --output json, the command exits successfully with applied: [], which breaks automation that relies on the exit code even though nothing was imported.

Useful? React with 👍 / 👎.

Comment thread packages/cli/src/commands/dns/zone/add.ts Outdated
Comment thread packages/cli/src/commands/dns/record/scan-records.ts Outdated
Comment thread packages/cli/src/commands/dns/record/scan-records.ts Outdated
Comment thread packages/cli/src/commands/dns/zone/add.ts Outdated
Comment thread packages/cli/src/commands/dns/record/scan.ts Outdated
Comment thread packages/cli/src/commands/dns/zone/add.ts
@jamie-at-bunny jamie-at-bunny merged commit 18645ed into main Jun 30, 2026
1 check passed
@jamie-at-bunny jamie-at-bunny deleted the feat/dns-record-scan-import branch June 30, 2026 17:04
@github-actions github-actions Bot mentioned this pull request Jun 30, 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