Summary
catalog.Validate() (in catalog/validate.go) applies the #392 "shipped catalog must be public" gate to List(), which is the overlay-merged app list — not the embedded-only catalog. So a developer whose local ~/.spawn/catalog.yaml overlay legitimately binds an app to a private image gets a spurious validation error, even though the shipped/embedded catalog is clean.
This directly contradicts the intent documented in the same function:
// ... validateApps stays overlay-safe — it does
// NOT enforce this, since overlays legitimately carry private images.
for _, app := range apps { // apps := List() ← overlay-merged
if app.Containerized() && app.ImageVisibility() != VisibilityPublic {
errs = append(errs, fmt.Errorf("%s: image %q is %s — the shipped catalog must be public...", ...))
}
}
The public-only loop is supposed to guard only the shipped artifact, but it reads the overlay-merged list, so it enforces "public" on overlay entries too.
Repro
With a local overlay binding paraview/chimerax to private ECR (a supported BYO setup):
# ~/.spawn/catalog.yaml
apps:
- name: paraview
image: 942542972736.dkr.ecr.us-east-1.amazonaws.com/paraview
- name: chimerax
image: 942542972736.dkr.ecr.us-east-1.amazonaws.com/chimerax
catalog.Validate() returns:
paraview: image "942542972736.dkr.ecr.us-east-1.amazonaws.com/paraview" is private — the shipped catalog must be public; put private images in a local overlay (#392)
chimerax: image "...chimerax" is private — the shipped catalog must be public...
This surfaces in consumers too: spawn's cmd/app_catalog_test.go (TestCatalogValid) fails locally for any dev with such an overlay. (Spawn's CI is unaffected — clean $HOME, no overlay — so this is a local-dev false negative, not a CI break.)
Fix
The public-only gate should iterate the embedded/global catalog only, not the overlay-merged List(). Something like validating over the parsed-embedded entries (the pre-merge set) while validateApps continues to run over the full merged list for structural checks. The overlay is the sanctioned home for private images (per #392), so it must be exempt from the public-only rule — as the comment already says it should be.
Suggested: split so Validate() runs the public-only loop over the embedded catalog and the structural validateApps over List(); or add an internal validateShipped(embeddedApps) used by the gate.
Context
Found while working spawn#435/#434 — TestCatalogValid failed on my machine purely because of my local BYO overlay. Related to the #392 catalog-visibility work (libs#16–#19); libs#19 makes paraview/chimerax definition-only, but that won't fix this class of false failure for arbitrary private overlays.
Summary
catalog.Validate()(incatalog/validate.go) applies the #392 "shipped catalog must be public" gate toList(), which is the overlay-merged app list — not the embedded-only catalog. So a developer whose local~/.spawn/catalog.yamloverlay legitimately binds an app to a private image gets a spurious validation error, even though the shipped/embedded catalog is clean.This directly contradicts the intent documented in the same function:
The public-only loop is supposed to guard only the shipped artifact, but it reads the overlay-merged list, so it enforces "public" on overlay entries too.
Repro
With a local overlay binding paraview/chimerax to private ECR (a supported BYO setup):
catalog.Validate()returns:This surfaces in consumers too: spawn's
cmd/app_catalog_test.go(TestCatalogValid) fails locally for any dev with such an overlay. (Spawn's CI is unaffected — clean$HOME, no overlay — so this is a local-dev false negative, not a CI break.)Fix
The public-only gate should iterate the embedded/global catalog only, not the overlay-merged
List(). Something like validating over the parsed-embedded entries (the pre-merge set) whilevalidateAppscontinues to run over the full merged list for structural checks. The overlay is the sanctioned home for private images (per #392), so it must be exempt from the public-only rule — as the comment already says it should be.Suggested: split so
Validate()runs the public-only loop over the embedded catalog and the structuralvalidateAppsoverList(); or add an internalvalidateShipped(embeddedApps)used by the gate.Context
Found while working spawn#435/#434 —
TestCatalogValidfailed on my machine purely because of my local BYO overlay. Related to the #392 catalog-visibility work (libs#16–#19); libs#19 makes paraview/chimerax definition-only, but that won't fix this class of false failure for arbitrary private overlays.