Skip to content

feat(server): add rename command for dedicated servers#219

Merged
canyugs merged 3 commits intomainfrom
can/sei-406-cli-支援-server-rename
Apr 13, 2026

Hidden character warning

The head ref may contain hidden characters: "can/sei-406-cli-\u652f\u63f4-server-rename"
Merged

feat(server): add rename command for dedicated servers#219
canyugs merged 3 commits intomainfrom
can/sei-406-cli-支援-server-rename

Conversation

@canyugs
Copy link
Copy Markdown
Contributor

@canyugs canyugs commented Apr 9, 2026

Summary

  • The Zeabur dashboard supports renaming dedicated servers, but the CLI had no equivalent. This wires up the existing updateServerName GraphQL mutation through a new zeabur server rename subcommand.
  • Supports both interactive (server picker + name prompt) and non-interactive (--id / --name flags or positional arg) modes, matching the convention of server reboot.
  • Whitespace-only names are rejected client-side to avoid a confusing backend error.

Supersedes #218 (branch renamed for SEI-406 tracking).

Test plan

  • go build ./... passes
  • go vet ./... passes
  • go test ./... passes (no regressions)
  • zeabur server rename --help shows expected flags
  • Non-interactive error paths verified: missing id, missing name, whitespace-only name, conflicting positional/--id
  • Manual end-to-end against a real dedicated server

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added a server rename command with both interactive and non-interactive modes.
    • Users can select a server by ID or interactively from a list, then specify a new name.
    • Includes built-in validation for required parameters and clear success/error messaging.

canyugs and others added 3 commits April 9, 2026 17:00
The Zeabur dashboard supports renaming dedicated servers, but the CLI
had no equivalent. Wire up the existing `updateServerName` GraphQL
mutation through a new `zeabur server rename` subcommand with both
interactive and non-interactive modes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Client-side trim prevents passing " " as --name, which would otherwise
round-trip to the backend and surface a generic "rename server was not
accepted" error. Addresses review feedback from PR #218.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Mention both the positional server-id arg and the --id flag so users
know either form is accepted. Addresses CodeRabbit review on PR #218.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings April 9, 2026 17:05
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 9, 2026

Walkthrough

Introduces a new server rename subcommand that allows renaming servers via CLI. The implementation includes interactive mode for server selection via listing and user prompts, and non-interactive mode for direct API calls. The feature is integrated via a GraphQL mutation-backed RenameServer API method.

Changes

Cohort / File(s) Summary
CLI Rename Command
internal/cmd/server/rename/rename.go, internal/cmd/server/server.go
Adds rename subcommand with dual-mode execution: interactive mode lists available servers for selection and prompts for new name; non-interactive mode validates inputs and calls the API. Registers subcommand with server root command.
API Interface & Implementation
pkg/api/interface.go, pkg/api/server.go
Extends ServerAPI interface with RenameServer method and implements it via GraphQL updateServerName mutation, returning error if mutation acknowledgment flag is false.

Sequence Diagram

sequenceDiagram
    actor User
    participant CLI as CLI Rename Command
    participant APIClient as API Client
    participant APIServer as API Server

    User->>CLI: invoke rename (interactive mode, no --id)
    CLI->>APIClient: ListServers()
    APIClient->>APIServer: GraphQL query: listServers
    APIServer-->>APIClient: server list with names/locations
    APIClient-->>CLI: server list
    CLI->>User: display selectable server list
    User->>CLI: select server
    CLI->>User: prompt for new server name
    User->>CLI: provide new name
    CLI->>APIClient: RenameServer(id, name)
    APIClient->>APIServer: GraphQL mutation: updateServerName
    APIServer-->>APIClient: mutation result with acceptance flag
    APIClient-->>CLI: error or nil
    CLI->>User: log success/failure
Loading

Estimated Code Review Effort

🎯 2 (Simple) | ⏱️ ~12 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: adding a rename command for dedicated servers, which aligns with the primary purpose of the pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch can/sei-406-cli-支援-server-rename

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a zeabur server rename CLI subcommand to rename dedicated servers by wiring the existing updateServerName GraphQL mutation, supporting both interactive and non-interactive workflows.

Changes:

  • Add RenameServer(ctx, id, name) to the API client + interface, backed by the updateServerName GraphQL mutation.
  • Register a new server rename subcommand under zeabur server.
  • Implement interactive (server picker + name prompt) and non-interactive (--id/positional + --name) rename flows with whitespace trimming.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
pkg/api/server.go Adds RenameServer mutation call and handles false-return rejection.
pkg/api/interface.go Extends ServerAPI with RenameServer.
internal/cmd/server/server.go Registers the new rename subcommand under server.
internal/cmd/server/rename/rename.go Implements the zeabur server rename command (interactive + non-interactive).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


func runRenameNonInteractive(f *cmdutil.Factory, opts *Options) error {
if opts.id == "" {
return fmt.Errorf("server-id or --id is required")
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

In non-interactive mode, the missing-ID error message here differs from the established pattern used by other server subcommands that accept a positional server-id (e.g. server reboot/get/ssh all return "--id is required"). If the goal is to match those conventions (as noted in the PR description), consider aligning this message (or standardizing all of them) so UX and scripts are consistent.

Suggested change
return fmt.Errorf("server-id or --id is required")
return fmt.Errorf("--id is required")

Copilot uses AI. Check for mistakes.
Comment on lines +48 to +73
func runRenameInteractive(f *cmdutil.Factory, opts *Options) error {
if opts.id == "" {
servers, err := f.ApiClient.ListServers(context.Background())
if err != nil {
return fmt.Errorf("list servers failed: %w", err)
}
if len(servers) == 0 {
return fmt.Errorf("no servers found")
}

options := make([]string, len(servers))
for i, s := range servers {
location := s.IP
if s.City != nil && s.Country != nil {
location = fmt.Sprintf("%s, %s", *s.City, *s.Country)
} else if s.Country != nil {
location = *s.Country
}
options[i] = fmt.Sprintf("%s (%s)", s.Name, location)
}

idx, err := f.Prompter.Select("Select a server to rename", "", options)
if err != nil {
return err
}
opts.id = servers[idx].ID
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

This interactive server-picker logic (list servers, build location strings, prompt select) is now duplicated across multiple server subcommands (get/reboot/ssh/rename). Consider extracting a shared helper (e.g. in internal/cmd/server or cmdutil) to keep the formatting and selection behavior consistent and reduce future maintenance drift.

Suggested change
func runRenameInteractive(f *cmdutil.Factory, opts *Options) error {
if opts.id == "" {
servers, err := f.ApiClient.ListServers(context.Background())
if err != nil {
return fmt.Errorf("list servers failed: %w", err)
}
if len(servers) == 0 {
return fmt.Errorf("no servers found")
}
options := make([]string, len(servers))
for i, s := range servers {
location := s.IP
if s.City != nil && s.Country != nil {
location = fmt.Sprintf("%s, %s", *s.City, *s.Country)
} else if s.Country != nil {
location = *s.Country
}
options[i] = fmt.Sprintf("%s (%s)", s.Name, location)
}
idx, err := f.Prompter.Select("Select a server to rename", "", options)
if err != nil {
return err
}
opts.id = servers[idx].ID
func promptServerIDForRename(f *cmdutil.Factory) (string, error) {
servers, err := f.ApiClient.ListServers(context.Background())
if err != nil {
return "", fmt.Errorf("list servers failed: %w", err)
}
if len(servers) == 0 {
return "", fmt.Errorf("no servers found")
}
options := make([]string, len(servers))
for i, s := range servers {
location := s.IP
if s.City != nil && s.Country != nil {
location = fmt.Sprintf("%s, %s", *s.City, *s.Country)
} else if s.Country != nil {
location = *s.Country
}
options[i] = fmt.Sprintf("%s (%s)", s.Name, location)
}
idx, err := f.Prompter.Select("Select a server to rename", "", options)
if err != nil {
return "", err
}
return servers[idx].ID, nil
}
func runRenameInteractive(f *cmdutil.Factory, opts *Options) error {
if opts.id == "" {
id, err := promptServerIDForRename(f)
if err != nil {
return err
}
opts.id = id

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
internal/cmd/server/rename/rename.go (1)

60-65: Harden server location label formatting for empty-string fields.

This block only checks pointer nil-ness. Empty City/Country values can render awkward labels (for example ", "), and city-only values currently fall back to IP.

♻️ Suggested tweak
 		for i, s := range servers {
 			location := s.IP
-			if s.City != nil && s.Country != nil {
+			if s.City != nil && *s.City != "" && s.Country != nil && *s.Country != "" {
 				location = fmt.Sprintf("%s, %s", *s.City, *s.Country)
-			} else if s.Country != nil {
+			} else if s.Country != nil && *s.Country != "" {
 				location = *s.Country
+			} else if s.City != nil && *s.City != "" {
+				location = *s.City
 			}
 			options[i] = fmt.Sprintf("%s (%s)", s.Name, location)
 		}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/cmd/server/rename/rename.go` around lines 60 - 65, The location
formatting currently only checks nil pointers and can produce empty labels
(e.g., ", ") or fall back to IP when City is present but empty; update the logic
around variable location and struct fields s.City and s.Country to treat a field
as present only if the pointer is non-nil and the dereferenced string is not
empty (e.g., len(strings.TrimSpace(*s.City)) > 0). Build location as: both
non-empty => "City, Country"; only city non-empty => city; only country
non-empty => country; otherwise fallback to s.IP; update the block that sets
location to use these checks (referencing s.City, s.Country, and variable
location).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@internal/cmd/server/rename/rename.go`:
- Around line 60-65: The location formatting currently only checks nil pointers
and can produce empty labels (e.g., ", ") or fall back to IP when City is
present but empty; update the logic around variable location and struct fields
s.City and s.Country to treat a field as present only if the pointer is non-nil
and the dereferenced string is not empty (e.g., len(strings.TrimSpace(*s.City))
> 0). Build location as: both non-empty => "City, Country"; only city non-empty
=> city; only country non-empty => country; otherwise fallback to s.IP; update
the block that sets location to use these checks (referencing s.City, s.Country,
and variable location).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7a4f66f3-8e27-48fe-bba4-dbfbcba500fa

📥 Commits

Reviewing files that changed from the base of the PR and between 4750f37 and c985cd2.

📒 Files selected for processing (4)
  • internal/cmd/server/rename/rename.go
  • internal/cmd/server/server.go
  • pkg/api/interface.go
  • pkg/api/server.go

@canyugs canyugs assigned canyugs and unassigned canyugs Apr 10, 2026
@canyugs canyugs merged commit eb837cb into main Apr 13, 2026
12 checks passed
@canyugs canyugs deleted the can/sei-406-cli-支援-server-rename branch April 13, 2026 04:16
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