vitrine

make setExhibits leave no trace when rejecting a config

commit 0cee357d4aaa3cf030e90e019be62fc09d419df7 · parent 6c942a8c45 · browse this tree
Ximianth, 2026-07-28 15:43:05 UTC

Two refinements for the reload path:
- roots opened before the failing one get closed (they were never published, so nothing can be holding them);
- git update-server-info moves after full validation of the set,
  so a rejected config doesn't even freshen git metadata as a side effect.
Re-running update-server-info on every accepted reload is deliberate;
watching the repos themselves for new commits stays future work.
diff --git a/serve.go b/serve.go
index 72d12b4..cdb03d6 100644
--- a/serve.go
+++ b/serve.go
@@ -60,9 +60,20 @@ func (s *server) setExhibits(exhibits map[string]exhibit) error {
 	for name, ex := range exhibits {
 		root, err := os.OpenRoot(ex.Path)
 		if err != nil {
+			// Nothing from this attempt was published, so closing is safe here.
+			for _, tr := range mounts {
+				tr.root.Close()
+			}
 			return fmt.Errorf("opening exhibit %q: %w", name, err)
 		}
 		mounts[name] = tree{root: root, exposeGit: ex.ExposeGit}
+	}
+	// Run update-server-info only once the whole set has validated:
+	// a rejected config should leave no trace, not even freshened git metadata.
+	// Re-running on every successful reload is deliberate and idempotent.
+	// (Watching the git repos themselves for new commits is possible future work;
+	// today a commit becomes clonable at the next config reload or restart.)
+	for name, ex := range exhibits {
 		if ex.ExposeGit {
 			updateGitServerInfo(name, ex.Path)
 		}