Skip to content

feat(server): show whether a server runs ZeaburOS in get and list#267

Merged
leechenghsiu merged 1 commit into
mainfrom
matthewlee/zea-10206-cli-server-os
Jul 22, 2026
Merged

feat(server): show whether a server runs ZeaburOS in get and list#267
leechenghsiu merged 1 commit into
mainfrom
matthewlee/zea-10206-cli-server-os

Conversation

@leechenghsiu

@leechenghsiu leechenghsiu commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Background

Renting a server provisions the base OS only — installing Zeabur services is a separate, optional step. Whether a machine runs ZeaburOS is therefore a real branch point: it determines whether the machine can host Zeabur projects at all.

Neither server get nor server list exposed this. Given a server ID, there was no way to tell the two apart short of running a kubectl command over SSH and checking whether it fails with command not found — a wasted round trip that also cannot be done before deciding whether to create a project on that machine.

hasK3s has been on the Server type in the GraphQL schema all along, so this is a CLI-side change with no backend work. Queries are generated by reflecting over struct tags, so adding the field to the model puts it in the query — pkg/api/server.go is untouched.

Changes

  • server get and server list gain an OS column (ZeaburOS / Ubuntu).
  • JSON output carries a derived os string alongside the raw HasK3s. This is the main point of the change — callers should not have to rediscover the field's three-state semantics. The table column is a convenience on top.
$ zeabur server list -i=false
     ID              NAME              IP           PROVIDER   LOCATION    STATUS   VM STATUS   OS
 ...............   my-server     ..............   Akamai     Tokyo, JP   Online   RUNNING     ZeaburOS
$ zeabur server list -i=false --json | jq '.[] | {Name, HasK3s, os}'
{ "Name": "my-server", "HasK3s": true, "os": "ZeaburOS" }

hasK3s is three-state, and the third state is a trap

Value Meaning
true Zeabur services are installed
false Explicitly none
null A legacy server — which does have them

The backend infers the legacy case from the server's certificate data, but the GraphQL field is bound to the raw column, so that inference never reaches the wire. The test is therefore == false, never !hasK3s: the latter would sweep every legacy server in and quietly mislabel it. This mirrors the equivalent contract in the dashboard, and is pinned by unit tests.

Two fixes to the resource columns, while in the area

  • A zero total now renders as instead of 0/0 MB. Zero total is the contract for "not measured" — a real machine cannot have zero cores. Printing 0/0 reads as a machine in trouble rather than one whose metrics have not been collected yet. Servers without Zeabur services only recently started reporting CPU/RAM/disk, so this path is newly reachable.
  • CPU now carries its unit (m). The backend reports millicores, so a 4-core machine reads as 4000; without a unit that looks like 4000 cores. Matches the k8s-style units the dashboard already uses.

Verification

  • go build ./... and go test ./... pass. New pkg/model/server_test.go covers all three states (including null), the rendering, and that JSON carries os while retaining the raw HasK3s.
  • Table and JSON output verified against real servers. All machines available for testing reported hasK3s: true; the false and null states are covered by unit tests.

Out of scope

  • Memory and disk keep the backend's MB rather than switching to Gi.
  • Choosing an OS at rent time is handled outside the CLI and is not added here.

🤖 Generated with Claude Code

agent 拿到一個 server ID 之後無從判斷它是不是 ZeaburOS——`server get` /
`server list` 都不露出這個資訊。結果 skills 只能靠反推:打一個 kubectl
指令看它會不會 `command not found`。這浪費一次 SSH round trip,而且在「要不要
在這台建專案」之前根本判斷不了。

`hasK3s` 早就在 GraphQL schema 上,純 CLI 端補齊即可,後端零改動。query 由
struct tag 反射生成,所以 model 加欄位就自動帶進 query。

- `server get` / `server list` 加 OS 欄(ZeaburOS / Ubuntu)。
- JSON 輸出除了原始 `HasK3s`,另外導出 `os` 字串——這是本次的主要目的,
  讓呼叫端不必自己記三態語意,表格顯示只是附帶。

`hasK3s` 是三態,第三態是陷阱:`null` 代表 legacy server,而它**是**
ZeaburOS(後端從 certificate data 推導,但 GraphQL 綁的是原始欄位,推導不會
上 wire)。所以判斷式是 `== false`,不是 `!hasK3s`——後者會把所有舊機器掃進
來、悄悄從部署目標裡消失。與 dashboard 的 `isCleanMachine()` 契約一致。

順帶修資源欄位的兩個顯示問題:
- total 為 0 時印 `—` 而非 `0/0 MB`。零總量是「未測得」的契約(真實機器不可能
  0 核),印 `0/0` 會讀成機器出事,而不是指標還沒收集到。乾淨 VPS 現在也會回
  傳 CPU/RAM/Disk 了,這條路徑才開始有機會踩到。
- CPU 標上單位 `m`。後端回的是 millicore,4 核機器讀出來是 4000,沒有單位會
  看成 4000 核。與 dashboard 的 k8s 風格顯示一致。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@linear-code

linear-code Bot commented Jul 22, 2026

Copy link
Copy Markdown

ZEA-10206

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Server models now derive ZeaburOS or Ubuntu labels from the tri-state HasK3s field, preserving ZeaburOS for legacy nil values. JSON serialization and tabular output include the derived OS. CPU, memory, and disk usage now include units and display an em dash when totals are zero. Tests cover OS rendering, resource formatting, and JSON fields.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: exposing server OS information in get and list outputs.

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

@opencodezebra

opencodezebra Bot commented Jul 22, 2026

Copy link
Copy Markdown

LGTM ✅ — CLI-only change correctly exposes server OS via three-state hasK3s derivation with solid test coverage.
Reviewed at 2d86bbc (round 1)

What This PR Does

Adds an OS column to server get and server list CLI output (table and JSON), derived from the three-state hasK3s GraphQL field. Also fixes resource display: em-dash for unmeasured metrics and explicit millicore unit for CPU.

How It Works

  • serverOS(hasK3s *bool) maps the three-state field: nil and true → ZeaburOS, false → Ubuntu. Mirrors dashboard isCleanMachine().
  • Custom MarshalJSON on both ServerDetail and ServerListItem injects a derived os string into JSON output using the alias pattern to avoid infinite recursion.
  • formatUsage(used, total, unit) returns em-dash when total is zero (unmeasured), otherwise used/total unit.
  • Header/Rows alignment verified: OS column inserted at same index in both methods for both types.

Findings

ID Severity Finding Location
F1 🟢 MarshalJSON value-receiver correctly promoted to pointer; test confirms it works via json.Marshal(&ServerDetail{}) pkg/model/server.go:138
F2 🟢 Exported field names used as JSON keys (no json: tags) — consistent with existing struct convention, not a regression pkg/model/server.go:123
F3 🟢 Alias pattern correctly breaks MarshalJSON recursion cycle pkg/model/server.go:140
F4 🟢 Header and Rows column alignment verified consistent for both ServerDetail and ServerListItems pkg/model/server.go:159,279
F5 🟢 Three-state guard hasK3s != nil && !*hasK3s is correct; nil (legacy) correctly maps to ZeaburOS pkg/model/server.go:40
What's Good (🟢)
  • Three-state semantics are well-documented in code comments and match the dashboard contract.
  • Comprehensive test coverage: all three hasK3s states, resource dash/unit formatting, and JSON serialization including raw field preservation.
  • Clean separation — pure CLI-side, no backend changes needed thanks to struct-tag reflection.
  • formatUsage helper eliminates the misleading "0/0" display for unmeasured metrics.
  • Consistent implementation across both ServerDetail and ServerListItem.
Baseline Check
  • Main already has: server get/server list commands with table and JSON output, hasK3s exists in GraphQL schema but was not queried by CLI.
  • Net-new value: OS visibility in CLI output, enabling agents and scripts to determine deployment path without SSH probing.
  • No prior review rounds.
Review Metadata
  • Reviewers: rev-claude-g (approve)
  • Consensus: approve
  • Absent reviewers: none

🔴×0 🟡×0 🟢×5 · 💬 Comment @opencodezebra <question> for a follow-up · 🔁 Push new commits or comment @opencodezebra review <fix notes> to re-run the council

@opencodezebra opencodezebra 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.

Council verdict: LGTM — see the council comment above.

@leechenghsiu leechenghsiu self-assigned this Jul 22, 2026
@leechenghsiu
leechenghsiu requested a review from canyugs July 22, 2026 08:11
@leechenghsiu leechenghsiu changed the title feat(server): 在 server get / list 露出機器 OS(ZEA-10206) feat(server): show whether a server runs ZeaburOS in get and list Jul 22, 2026

@canyugs canyugs left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

@leechenghsiu
leechenghsiu merged commit dc6168a into main Jul 22, 2026
6 checks passed
@leechenghsiu
leechenghsiu deleted the matthewlee/zea-10206-cli-server-os branch July 22, 2026 08:26
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