api: add /builders CRUD and prune endpoints - #341
Conversation
✱ Stainless preview builds for hypemanThis PR will update the Edit this comment to update it. It will appear in the SDK's changelogs. ✅ hypeman-openapi studio · code · diff
✅ hypeman-typescript studio · code · diff
✅ hypeman-go studio · code · diff
This comment is auto-generated by GitHub Actions and is automatically kept up to date as you push. |
8820a02 to
ac772e4
Compare
Expose the Builder resource over the API. POST /builders creates a
builder with an optional caller-supplied ID (validated, conflicts on
replay for control-plane idempotency), optional non-unique name, tags,
and disk size; the cache disk is provisioned eagerly and 201 returns the
builder. GET /builders lists with tag filtering and always returns a
non-nil array; GET /builders/{id} returns one builder; DELETE
/builders/{id} removes builder and disk (409 while in use); POST
/builders/{id}/prune resets the cache disk asynchronously and returns
202 with status pruning. Responses carry id, name, disk_size_gb, status,
tags, created_at, and last_used_at only. Every builder route documents
the 401 bearer-auth failure response.
Builders resolve by opaque ID through the resource resolver middleware;
builder:read/write/delete scopes gate the routes. The builders manager
is wired through config (builders.max_count, default_disk_size_gb,
max_disk_size_gb, idle_ttl), providers, and the generated injector, and
started with the server so startup reconciliation runs. Regenerating
oapi.go renamed the ImageStatus enum constants (oapi-codegen conflict
resolution with the new BuilderStatus enum); the one referencing test is
updated.
Handler tests cover create (defaults, custom ID, invalid ID, idempotent
replay), get, list with tag filtering, delete, prune, and 409s while a
build holds the builder. Config validation tests cover the new
builders.* settings.
ac772e4 to
a6f1763
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Prune misses not-found race
- PruneBuilder now maps ResetDisk returning ErrNotFound to a 404 not_found response, matching the documented race handling used by DeleteBuilder.
Or push these changes by commenting:
@cursor push d305f20bd3
Preview (d305f20bd3)
diff --git a/cmd/api/api/builders.go b/cmd/api/api/builders.go
--- a/cmd/api/api/builders.go
+++ b/cmd/api/api/builders.go
@@ -149,6 +149,13 @@
log := logger.FromContext(ctx)
if err := s.BuilderManager.ResetDisk(ctx, b.ID); err != nil {
+ if errors.Is(err, builders.ErrNotFound) {
+ // Deleted between resolution and this call (e.g. idle reaper)
+ return oapi.PruneBuilder404JSONResponse{
+ Code: "not_found",
+ Message: "builder not found",
+ }, nil
+ }
if errors.Is(err, builders.ErrInUse) {
return oapi.PruneBuilder409JSONResponse{
Code: "conflict",You can send follow-ups to the cloud agent here.
Reviewed by Cursor Bugbot for commit a6f1763. Configure here.
| Code: "internal_error", | ||
| Message: "failed to prune builder", | ||
| }, nil | ||
| } |
There was a problem hiding this comment.
Prune misses not-found race
Medium Severity
PruneBuilder maps ErrInUse to 409 but not ErrNotFound. If the idle reaper removes the builder between middleware resolution and ResetDisk, the API returns 500 instead of the documented 404. DeleteBuilder already handles this race.
Reviewed by Cursor Bugbot for commit a6f1763. Configure here.



Expose the Builder resource over the API. POST /builders creates a
builder with an optional caller-supplied ID (validated, conflicts on
replay for control-plane idempotency), optional non-unique name, tags,
and disk size; the cache disk is provisioned eagerly and 201 returns the
builder. GET /builders lists with tag filtering and always returns a
non-nil array; GET /builders/{id} returns one builder; DELETE
/builders/{id} removes builder and disk (409 while in use); POST
/builders/{id}/prune resets the cache disk asynchronously and returns
202 with status pruning. Responses carry id, name, disk_size_gb, status,
tags, created_at, and last_used_at only. Every builder route documents
the 401 bearer-auth failure response.
Builders resolve by opaque ID through the resource resolver middleware;
builder:read/write/delete scopes gate the routes. The builders manager
is wired through config (builders.max_count, default_disk_size_gb,
max_disk_size_gb, idle_ttl), providers, and the generated injector, and
started with the server so startup reconciliation runs. Regenerating
oapi.go renamed the ImageStatus enum constants (oapi-codegen conflict
resolution with the new BuilderStatus enum); the one referencing test is
updated.
Handler tests cover create (defaults, custom ID, invalid ID, idempotent
replay), get, list with tag filtering, delete, prune, and 409s while a
build holds the builder. Config validation tests cover the new
builders.* settings.
Stack created with GitHub Stacks CLI • Give Feedback 💬
Note
Medium Risk
New API surface and lifecycle operations (delete/prune) touch volumes and builder state; misconfiguration of idle_ttl or quotas could delete or block builders unexpectedly, but changes are scoped with tests and validation.
Overview
Adds a Builder HTTP API for persistent BuildKit cache disks: list (with tag filter), create with eager cache volume provisioning, get/delete by opaque ID, and prune to reset the cache asynchronously (
202, statuspruning).BuilderManageris injected intoApiService, wired through config (builders.*), providers, and startup (Startfor reconciliation/idle reaper)./builders/{id}routes use ResolveResource middleware with a newBuilderResolverandbuilder:read/builder:write/builder:deletescopes.Handlers map domain errors to 400/409/404 (duplicate ID, quota, in-use, invalid ID/tags). OpenAPI and generated
oapiclient/server code include the new routes andBuildertypes. Regeneratingoapi.gorenamed ImageStatus enum constants (oapi.Readyetc.); one registry test was updated accordingly.Reviewed by Cursor Bugbot for commit a6f1763. Bugbot is set up for automated code reviews on this repo. Configure here.