Skip to content

Commit d602750

Browse files
fix(environment): re-import withdraws registry.artifacts and registry.auth
Like registry.mirrors (cli#1140), both are contract-owned: a contract that declares registry without them withdraws the value an earlier import wrote. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 36e584b commit d602750

3 files changed

Lines changed: 29 additions & 8 deletions

File tree

‎cmd/environment/import.go‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -465,10 +465,12 @@ func applyContractFields(target, declaration *yaml.Node) {
465465

466466
// contractOwnedOptional names the keys a coordinate contract owns whose ABSENCE is
467467
// itself a statement (docs/coordinate-contract-conventions.md in infra-base): an absent
468-
// registry.mirrors means "pull the upstream as the Dockerfile names it". Merging alone
469-
// would keep a mirror the contract withdrew, and the builder would keep mapping to a
470-
// repository that no longer exists.
471-
var contractOwnedOptional = map[string][]string{"registry": {"mirrors"}}
468+
// registry.mirrors means "pull the upstream as the Dockerfile names it", an absent
469+
// registry.artifacts "publish bundles to registry.url", an absent registry.auth "log
470+
// in to nothing". Merging alone would keep a value the contract withdrew, and the
471+
// render would keep pulling from, publishing to or logging in to a registry the
472+
// environment no longer holds.
473+
var contractOwnedOptional = map[string][]string{"registry": {"mirrors", "artifacts", "auth"}}
472474

473475
// withdrawContractOwnedAbsences removes each contract-owned optional key the contract
474476
// no longer carries, under a parent the contract does declare. A parent the contract

‎cmd/environment/import_test.go‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -838,6 +838,24 @@ func TestImportRefusesANullRunnableScopeSelectionWithoutWriting(t *testing.T) {
838838
}
839839
}
840840

841+
func TestReimportWithdrawsArtifactsAndAuthTheContractNoLongerCarries(t *testing.T) {
842+
dir := writeWorkspace(t, existingAzureWorkspace)
843+
declared := strings.Replace(fixtureContract, `"registry": {"url": "x.azurecr.io"}`,
844+
`"registry": {"url": "x.azurecr.io", "auth": "acr", "artifacts": "x.azurecr.io/bundles"}`, 1)
845+
doImport(t, dir, importOptions{contractData: []byte(declared)})
846+
if got := loadWorkspace(t, dir).FindEnvironment("azure").Registry; got.Auth != "acr" || got.Artifacts != "x.azurecr.io/bundles" {
847+
t.Fatalf("auth and artifacts not imported: %+v", got)
848+
}
849+
doImport(t, dir, importOptions{contractData: []byte(fixtureContract)})
850+
env := loadWorkspace(t, dir).FindEnvironment("azure")
851+
if env.Registry.Auth != "" || env.Registry.Artifacts != "" || env.Registry.URL != "x.azurecr.io" {
852+
t.Fatalf("a value the contract withdrew survived re-import: %+v", env.Registry)
853+
}
854+
if strings.Contains(readFile(t, filepath.Join(dir, resources.WorkspaceConfigurationName)), "bundles") {
855+
t.Fatal("the withdrawn artifacts registry is still written in the workspace")
856+
}
857+
}
858+
841859
func TestReimportWithdrawsAMirrorTheContractNoLongerCarries(t *testing.T) {
842860
dir := writeWorkspace(t, existingAzureWorkspace)
843861
mirrored := strings.Replace(fixtureContract, `"registry": {"url": "x.azurecr.io"}`,

‎docs/commands.md‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2318,10 +2318,11 @@ former spelling of `--coordinate-contract` for one release.
23182318
Declared fields replace their exact named values. Maps merge by explicit key;
23192319
omitted fields and unrelated entries remain intact. Explicit empty maps, empty
23202320
sequences and nulls clear the corresponding field where its schema permits.
2321-
One key is the contract's alone and its absence is a statement: `registry.mirrors`.
2322-
A contract that declares `registry` without `mirrors` withdraws any mirror an earlier
2323-
import wrote, so the builder stops mapping to a repository the environment no longer
2324-
holds.
2321+
Three keys are the contract's alone and their absence is a statement:
2322+
`registry.mirrors`, `registry.artifacts` and `registry.auth`. A contract that declares
2323+
`registry` without one of them withdraws the value an earlier import wrote, so the
2324+
render stops pulling from, publishing bundles to or logging in to a registry the
2325+
environment no longer holds.
23252326
There is no special `store` alias, single-database limit, default secret path or
23262327
ignored producer extension. Unknown fields and capabilities fail validation.
23272328

0 commit comments

Comments
 (0)