v0.235.0: freeze the version, keep the fixes flowing (operator ruling 2026-09-06)
gates / gates (push) Successful in 12s
gates / gates (push) Successful in 12s
Slice 3. R-447 was BLOCKED because R-438 established that RestartStack's use of up -d to pick up template changes was CHOSEN and written down in its own comment. The operator ruled Option 1, and this implements it. The rule: while the catalog offers the same version you run, its fixes flow to you; the moment it moves to a newer version you are frozen until you update. NOTHING was added to any of the thirteen compose up -d call sites. Most of them are repairs - the boot reconciler, the drive-return gate, the app-stop guard - and a repair path that refuses to repair leaves a customer's app down, which is worse than the problem. They are made safe by removing the reason. app.yaml gains pinned_images: what the app is SUPPOSED to run. It is NOT installed_images, which is an observation; letting a reading become a deployment is the R-166 category error one field over. Four writers, each also storing the exact definition as applied-compose.yml. UpdateStack advances the pin and re-renders BEFORE the pull, because pull and up -d act on the file on disk, and a pin set afterwards would pull the frozen version and report success. The syncer renders instead of copying, through one nil-safe seam. Catalog images equal the pin -> verbatim, so fixes and self-healing both survive; they differ -> the WHOLE stored definition, never a substitution of refs into a newer template (wger 2.6 needs a DB config the older template cannot supply). This is deliberately not 'skip deployed apps', which was option B and was rejected. AdoptPins runs once at boot after the backfill, files only, and skips loudly rather than inventing a pin. syncer.Start() moved to after it: the initial sync would otherwise run while every app was unpinned and overwrite a deployed app's version once per boot. THE BADGE HAD TO CHANGE OR SLICE 2 WOULD HAVE INVERTED SILENTLY. TemplateImages reads the LIVE compose file, which is now the frozen one, so the comparison would have answered Naprakesz on exactly the apps that are behind - with every test green, because the new field has the same type. It now reads CatalogImages. +16 tests (1729 -> 1745), 28 packages green. Three red-proofs run and reverted. A test also caught the syncer writing an empty compose file over a live app.
This commit is contained in:
@@ -29,7 +29,14 @@ const (
|
||||
//
|
||||
// NO REGISTRY QUERY, deliberately: a customer's box must not depend on reaching eight upstream
|
||||
// registries to render a page. The comparison is therefore reference-to-reference — what the
|
||||
// container was created from, against what the compose file now pins.
|
||||
// container was created from, against what the CATALOG currently offers.
|
||||
//
|
||||
// ⚠ IT COMPARES AGAINST Stack.CatalogImages, NEVER Stack.TemplateImages, AND v0.235.0 IS WHY.
|
||||
// Since the freeze, a pinned app's LIVE docker-compose.yml is rendered from its own stored
|
||||
// definition once the catalog moves past it — so the live file names the OLD version, installed
|
||||
// would equal template, and this function would answer „Naprakész" on precisely the apps that are
|
||||
// behind. It would invert the feature silently, with every test still green, because the two fields
|
||||
// have the same type and shape. CatalogImages is read from the syncer's git clone instead.
|
||||
//
|
||||
// KNOWN LIMITATION, stated rather than hidden (see 09-update-architecture.md and the register row):
|
||||
// 23 of the catalog's 66 distinct pins FLOAT (postgres:16-alpine, mariadb:11.6, …). For those the
|
||||
@@ -46,15 +53,15 @@ func compareInstalledToTemplate(s stacks.Stack) updateState {
|
||||
if s.AppConfig == nil || len(s.AppConfig.InstalledImages) == 0 {
|
||||
return updateUnknown // legacy app.yaml — no record was ever written
|
||||
}
|
||||
if len(s.TemplateImages) == 0 {
|
||||
return updateUnknown // the compose file could not be read or pins nothing
|
||||
if len(s.CatalogImages) == 0 {
|
||||
return updateUnknown // no readable catalog template — cannot tell, so say nothing
|
||||
}
|
||||
if len(s.AppConfig.InstalledImages) != len(s.TemplateImages) {
|
||||
if len(s.AppConfig.InstalledImages) != len(s.CatalogImages) {
|
||||
// A service was added or removed by the template. That IS a change the customer's running
|
||||
// stack has not taken up.
|
||||
return updateBehind
|
||||
}
|
||||
for svc, want := range s.TemplateImages {
|
||||
for svc, want := range s.CatalogImages {
|
||||
got, ok := s.AppConfig.InstalledImages[svc]
|
||||
if !ok || got.Ref != want {
|
||||
return updateBehind
|
||||
|
||||
Reference in New Issue
Block a user