apply a template edit without a restart
The watcher fingerprinted templates/ but a rebuild only re-scanned content, so editing a template fired a rebuild that changed nothing. ADR-0022 already promised the opposite — "a template edit in the site root invalidates through the same path as content" — which makes this a defect against a recorded decision rather than a missing feature. ADR-0055 records the fix and supersedes ADR-0048's narrower clause. The parsed sets and the stylesheet become one parsedTheme behind an atomic.Pointer, swapped by Refresh once per rebuild instead of per request. A parse failure keeps the theme that was working, so a typo cannot take the site down. The swap also retires the in-place field mutation -dev was doing, which was a data race with every in-flight render. site.yaml goes the other way and leaves the fingerprint: the settings are copied by value into the renderer, the handler, the feeds and the sitemap, so applying a change to some of them is worse than applying it to none. It is restart-only. Corrects the Effects counter row while proving it did not move: it still said startup was the only change signal "until queue 21", but queue 21 shipped as ADR-0048 and put the derivative pass inside rebuilder, so that has been wrong since. The row now also answers the question ADR-0055 invites — an in-memory swap is not an Effect, because it writes no artifact and calls nothing outbound. Measured on the real binary: a template edit went live in ~2s; a typo logged "keeping the previous theme" and kept answering 200 with the last good markup; a site.yaml edit now fires no rebuild at all. core 2766/2800, ext 1030/2000, 34 gates green, 0 warnings.
This commit is contained in:
@@ -72,6 +72,10 @@ func Watch(fsys fs.FS, stop <-chan struct{}, onChange Changed) {
|
||||
// Names, sizes and modification times — not contents: reading every file to detect a change would cost more
|
||||
// than the rebuild it triggers. Editor droppings are excluded, or saving a file in vim would look like three
|
||||
// changes and a deletion.
|
||||
//
|
||||
// `content/` and `templates/` only. `site.yaml` is deliberately absent: a rebuild cannot apply it, since the
|
||||
// settings are copied by value into the renderer, the handler, the feeds and the sitemap, and a fingerprint
|
||||
// that fires a rebuild changing nothing is a lie told every two seconds (ADR-0055).
|
||||
func Fingerprint(fsys fs.FS) string {
|
||||
sum := sha256.New()
|
||||
for _, root := range []string{"content", "templates"} {
|
||||
@@ -79,11 +83,6 @@ func Fingerprint(fsys fs.FS) string {
|
||||
return record(sum, p, d, err)
|
||||
})
|
||||
}
|
||||
// site.yaml is part of the site's state, and editing it should not need a restart.
|
||||
if info, err := fs.Stat(fsys, "site.yaml"); err == nil {
|
||||
sum.Write([]byte("site.yaml"))
|
||||
_ = binary.Write(sum, binary.LittleEndian, info.ModTime().UnixNano())
|
||||
}
|
||||
return hex.EncodeToString(sum.Sum(nil))
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@ func TestFingerprintChangesOnlyForContent(t *testing.T) {
|
||||
"edited a bundle": {"content/posts/one.md": {Data: []byte("xx"), ModTime: time.Unix(200, 0)}},
|
||||
"added a bundle": {"content/posts/two.md": {Data: []byte("z"), ModTime: time.Unix(100, 0)}},
|
||||
"edited a template": {"templates/page.html": {Data: []byte("yy"), ModTime: time.Unix(200, 0)}},
|
||||
"edited site.yaml": {"site.yaml": {Data: []byte("base: y"), ModTime: time.Unix(200, 0)}},
|
||||
} {
|
||||
next := fstest.MapFS{}
|
||||
for k, v := range base {
|
||||
@@ -39,6 +38,18 @@ func TestFingerprintChangesOnlyForContent(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// site.yaml is deliberately unwatched: a rebuild cannot apply it, because the settings are copied by value
|
||||
// into the renderer, the handler, the feeds and the sitemap. Noticing it would fire a rebuild that changes
|
||||
// nothing (ADR-0055), so editing it needs a restart.
|
||||
settings := fstest.MapFS{}
|
||||
for k, v := range base {
|
||||
settings[k] = v
|
||||
}
|
||||
settings["site.yaml"] = &fstest.MapFile{Data: []byte("base: y"), ModTime: time.Unix(200, 0)}
|
||||
if Fingerprint(settings) != before {
|
||||
t.Error("site.yaml changed the fingerprint, so a rebuild will fire and apply nothing")
|
||||
}
|
||||
|
||||
// Editor droppings are not content: saving in vim writes several of these, and each would look like a change.
|
||||
noise := fstest.MapFS{}
|
||||
for k, v := range base {
|
||||
|
||||
Reference in New Issue
Block a user