fix(frontseat-plugin-gib): GH-199 image plugins claim the default export - #240
fix(frontseat-plugin-gib): GH-199 image plugins claim the default export#240jbadeau wants to merge 1 commit into
Conversation
Both image plugins registered only a named "image" export, so an entity combining an image build with any other plugin's named export (e.g. a uv "source" export) hit "ambiguous primary export" — the #131 reproducer. apko now always claims the default export alongside "image"; gib claims it only when it is the artifact producer, because with useArtifact set its $(location :) must keep resolving to the producing plugin's output. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| // named exports from other plugins stack instead of tripping the | ||
| // ambiguous-primary check (#131); "image" stays addressable by name. | ||
| packageExports := []*plugin.EntityExport{ | ||
| {Patterns: []string{imageTarPath}}, |
There was a problem hiding this comment.
Claiming the default unconditionally hard-fails any entity whose language plugin already owns it. export_registry.go:62 is a plain duplicate check:
if _, exists := r.exports[entityRef][name]; exists {
return fmt.Errorf("duplicate export %q on entity %q", name, entityRef)
}Three plugins already register Name: "":
maven/tasks.go:345— unconditional for any non-pompackaginggo/task_build.go:106— first main,name := ""cargo/task_package.go:26— library-only crate
The registry comment at line 33 states the contract: named exports stack, one plugin declares the default. No fixture pairs apko.yaml with a language manifest today, so this is latent — but apko/tasks.go declines to gate on an apko-typed facet specifically to support that layout, and it planned cleanly before.
A config-level signal is available if you want gib-style conditioning: apko reads local artifacts through contents.repositories file-path repos (@local /path, package referenced as name@local) and through contents.baseimage.image for a local OCI layout — both visible in apko.yaml.
Options: claim the default only when no peer has one, or keep image exports named-only and fix the plugins that emit only named secondaries — which is what #131 is about.
On gib: the gate narrows the window rather than closing it. A jib.yaml with no ${ARTIFACT} still gets an unnamed default and collides identically. It is also convention-only — ARTIFACT is a frontseat name, not a gib one; gib substitutes any ${name} and hard-fails with missing required template parameter when a used name is not supplied, so a jib.yaml that calls its artifact parameter something else both defeats the gate and breaks the build.
Minor: apko puts the default first in the slice, gib appends it last. Worth matching, since registration order decides which plugin trips first.
There was a problem hiding this comment.
You're right that the mechanism needs rethinking, not patching — claiming the default just relocates the collision (apko unconditionally; gib's !useArtifact only narrows the window, and ARTIFACT is a frontseat-side convention a jib.yaml need not honor).
Rather than iterate on the default-export claim, we're going to remove the export registry and move to explicit, facet-qualified outputs (<entity>:<facet>:<export>, Bazel-label style), dropping the unnamed default entirely — which resolves this collision class structurally instead of by picking a winner. Wrote that up as #271 and linked the cluster (#131, #199, this PR, plus the output-modelling side #119/#145/#218).
So I'm going to park the mechanism rework on this PR rather than push a fix that #271 will delete. Options: hold #240 as superseded by #271, or land only the test that encodes the collision contract (currently it asserts slice length/names only) as a regression guard for the redesign. What's your preference?
Generated by Claude Code
There was a problem hiding this comment.
Requesting changes — the apko task claims the default with no gate of any kind.
maven/tasks.go:345, go/task_build.go:106 and cargo/task_package.go:26 already register the unnamed default, and export_registry.go:62 treats a second one as duplicate export "". So an entity whose root holds both a language manifest and an apko.yaml fails at plan time. No fixture uses that layout today, so this is latent rather than currently breaking — but apko/tasks.go deliberately declines to gate on an apko-typed facet precisely so that layout is supported, and it planned cleanly before this change.
gib's !useArtifact gate narrows the window rather than closing it: a jib.yaml with no ${ARTIFACT} still gets an unnamed default and collides the same way.
Tests assert only slice length and names, so neither the collision nor the apko half is covered.
Fixes #131 is right in intent; the mechanism needs rethinking.
This review was published with assistance from Claude.
|
Superseded is the right call — #271 subsumes this structurally rather than picking a winner, which is what the collision needed. On the test: I'd skip it here. It would assert
It's green on Two things worth knowing before you close this, both raised on #271: the migration inventory undercounts, and dropping the default is a breaking change for One piece of bookkeeping: No interim mitigation needed as far as I can tell: no directory in the tree pairs an This comment was published with assistance from Claude. |
Fixes #199. Fixes #131.
Both image plugins registered only a named
"image"export, so combining an image build with any other plugin's named export on one entity trippedexport_registry.go's "ambiguous primary export" error — the #131 reproducer."image"— it has no artifact-consuming mode.useArtifactis false: when consuming$(location :)the default export must keep resolving to the producing plugin's artifact, not gib's own image.The gib test now encodes the contract both ways.
🤖 Generated with Claude Code