Skip to content

fix(operations): the inventory read a location Hetzner no longer sends - #193

Merged
glennmichael123 merged 1 commit into
mainfrom
fix/inventory-provider-payload-type
Aug 31, 2026
Merged

fix(operations): the inventory read a location Hetzner no longer sends#193
glennmichael123 merged 1 commit into
mainfrom
fix/inventory-provider-payload-type

Conversation

@glennmichael123

Copy link
Copy Markdown
Member

The bug

toInventoryServer resolved a box's place name as:

text(raw?.datacenter?.location?.name) ?? text(raw?.datacenter?.name) ?? text(raw?.location)

Hetzner has retired datacenter (it sends an explicit null) and nests the place under location as an object. So on a live listing:

  • datacenter.location.name misses, datacenter is null
  • datacenter.name misses, same reason
  • location misses, because it is { name: 'fsn1' } and text() only accepts a string

All three miss and every box reports no location at all. buddy cloud:sites prints the column empty for the whole fleet.

ts-cloud already knew

This is not new information to this package, which is what makes it worth fixing rather than shrugging at:

  • drivers/hetzner/resize.ts:116 reads server.location?.name ?? server.datacenter?.location.name - current shape first.
  • drivers/hetzner/role-swap.ts carries a comment about the retirement and a regression suite for it, including falls back to the location now that datacenter is gone with the note "The current API shape: datacenter null, location present."

operations/inventory.ts was written against the recorded fixtures, which still carry datacenter, so it picked up the legacy order and the string assumption. This is the third reader to need the same fallback.

The fix

A placeName() helper that accepts either { name } or a bare string, read in the current-shape-first order the rest of the package uses. Older fixtures with datacenter still resolve, and a non-Hetzner driver answering a flat string still resolves.

Also here

raw: any becomes ProviderServerPayload, matching the any sweep Chris ran over the copy that used to live in stacks (c379cee). Two notes on the shape:

  • The flat ipv4 / ipv6 / type / location fields are declared alongside the nested ones, because this function's contract is that a simpler driver can satisfy it. The stacks copy did not read those, so its interface did not need them.
  • datacenter is typed | null because null is what the API actually sends. Stating that is what let the regression test drop the cast it needed at first - a cast there would have been the type disagreeing with reality.

Verification

  • New test goes red on the old resolution (24 pass / 1 fail) and green on the new.
  • operations: 126 pass / 0 fail.
  • Full suite: 4331 pass / 0 fail (4382 across 327 files).
  • typecheck clean, lint clean (865 files), build clean.

Follow-up to #192.

`toInventoryServer` resolved the place name as
`datacenter.location.name ?? datacenter.name ?? location`, treating the last
one as a bare string. Hetzner has retired `datacenter` - it sends an explicit
`null` - and nests the place under `location` as an object, so on a live
listing all three reads miss and every box reports no location at all.
`buddy cloud:sites` printed the column empty for the whole fleet.

ts-cloud already knew this. `resize.ts` reads `location?.name` first and
`role-swap.ts` carries a comment and a regression suite about the same
retirement; this reader was written against the recorded fixtures and picked
up the legacy order. It now reads the current shape first and accepts either
`{ name }` or a bare string, so the older fixtures and a non-Hetzner driver
with a flatter listing both still resolve.

While here, `raw: any` becomes `ProviderServerPayload`, matching the sweep in
stacks c379cee. The null on `datacenter` is in the type because null is what
the API actually sends - stating it is what let the regression test drop its
cast.
@glennmichael123
glennmichael123 merged commit ba6c2f2 into main Aug 31, 2026
4 checks passed
@glennmichael123
glennmichael123 deleted the fix/inventory-provider-payload-type branch August 31, 2026 12:54
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.

1 participant