ReviewOS

also looking at this

stacks/ts-cloud

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

#193
Merged glennmichael123 wants to merge fix/inventory-provider-payload-type into main
2 files +69 -2

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.

2 changed files on the files tab, with 0 review threads.