Restore keg settings from pre-rename archives - #90
Merged
Conversation
Renaming the keg document from config to settings renamed the archive manifest key with it -- `with_config` became `with_settings` -- but left the format identifier at `keg-archive/v3` on both sides of the change. An archive is a stored artifact that outlives the code that wrote it, so v3 tarballs carrying the old spelling are already in users' hands. They still pass the version gate, then unmarshal with WithSettings false because `with_config` matches no field. Three decisions read that one value: validation skips the document, replaceSettings never runs, and the inverted branch that stamps settings-updated for a settings-free archive now fires. The restore reports success while silently dropping the keg's title, summary, instructions, and custom indexes, and refreshes the updated stamp so it looks handled. Nodes, schemas, attachments, and history all restore correctly, which is what makes it easy to miss. Accept the pre-rename spelling and fold it forward immediately after the manifest is parsed, before anything reads WithSettings. Export is unchanged and still writes only `with_settings`; the alias is read-only, and a test asserts a freshly written manifest never contains `with_config` so it cannot leak back into the writer. The entry itself was always keg-archive/keg.yaml, so the document was present in every affected archive and merely unread -- nothing is unrecoverable. No released version is affected: v0.38.0 both writes and reads `with_config`, self-consistently. The break existed only between that rename and this commit. Covered by an import test built from a real export whose manifest is rewritten to the old spelling. Confirmed it fails without the fix, with the destination keeping its own title instead of the archived one.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Renaming the keg document from config to settings renamed the archive manifest key with it —
with_configbecamewith_settings— but left the format identifier atkeg-archive/v3on both sides of the change.An archive is a stored artifact that outlives the code that wrote it, so v3 tarballs carrying the old spelling are already in users' hands. They still pass the version gate, then unmarshal with
WithSettingsfalse becausewith_configmatches no field.Why it is worse than a skipped field
Three decisions read that one value, and the third is inverted:
replaceSettingsnever runs, so the document is not restoredif !manifest.WithSettingsis now true, so import takes the "archive carried no settings" branch and callstouchSettingsUpdatedSo a settings-bearing archive is treated as settings-free and the settings-updated stamp is refreshed. The restore returns success and looks like it handled the document. What is lost is the keg's title, summary, instructions, and custom indexes; nodes, schemas, attachments, and history all restore correctly, which is what makes it easy to miss.
The fix
Accept the pre-rename spelling and fold it forward immediately after the manifest is parsed, before anything reads
WithSettings. There is exactly one unmarshal site and all three gates read that same local variable, so one normalization covers every path.Export is unchanged and still writes only
with_settings. The alias is read-only, and a test asserts a freshly written manifest never containswith_configso it cannot leak back into the writer.The entry itself was always
keg-archive/keg.yaml, so the document was present in every affected archive and merely unread — nothing was unrecoverable.Scope
Deliberately narrow, after checking what else the rename touched:
SettingsV2and the formerConfigV2carry the same 13 YAML keys. Storedkeg.yaml, the archive entry, and Hub-side storage were never at risk.KegInfo.Config→Settingsis the only other renamed key. That is a live wire format betweentapand the Hub, not a stored artifact, and is covered by the coordinated release.with_configwas the only persisted break.No released version is affected
v0.38.0 both writes and reads
with_config, self-consistently, so it restores its own archives correctly. The break exists only between the rename and this commit — which is why this needs to land before the next release tag, so no published version ever ships it.Verification
go build ./...,go vet ./...,go test ./..., andgo test -race ./pkg/keg ./pkg/tapperall pass.TestArchiveImportRestoresKegSettingsFromLegacyConfigManifestbuilds its fixture from a real export whose manifest is rewritten to the old spelling, then imports it into a keg seeded with different settings — so matching the source proves the archived document was applied rather than the target's values surviving.expected: "Legacy Title", actual: "Target Title", exactly the reported failure mode.updated; after it, the document applies.