Skip to content

catalog.Validate() public-only gate runs over the overlay-merged list → false failures for devs with private overlays #28

Description

@scttfrdmn

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions