kept data: the list names a leftover by the app that binds it through HDD_PATH, never the file browser (R-692, found live on 9202)
gates / gates (push) Successful in 26s

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
2026-09-25 13:51:26 +02:00
parent 43e99d160c
commit 064f23896a
2 changed files with 37 additions and 2 deletions
+10 -2
View File
@@ -244,11 +244,19 @@ func overlaps(a, b string) bool {
func (m *Manager) ownerOf(drive, abs string) (string, string) {
var cands []Stack
for _, st := range m.GetStacks() {
if st.ComposePath == "" {
if st.ComposePath == "" || st.Protected {
continue
}
// v0.274.0 (found live on 9202): only an app whose definition binds its drive folder THROUGH
// ${HDD_PATH} can own one. The file browser's own compose binds every kept folder by its absolute
// path (the read-only view), and matched first — the list named romm's and paperless's leftovers
// "Filebrowser". Pinned by TestKept_OwnerIsNeverTheFileBrowser.
if b, err := os.ReadFile(st.ComposePath); err != nil || !strings.Contains(string(b), "${HDD_PATH}") {
continue
}
for _, p := range ParseComposeHDDMounts(st.ComposePath, drive) {
if filepath.Clean(p) == abs {
// the folder itself, or one inside it (paperless binds appdata/paperless/media and …/export)
if c := filepath.Clean(p); c == abs || strings.HasPrefix(c, abs+string(filepath.Separator)) {
cands = append(cands, st)
break
}
+27
View File
@@ -268,3 +268,30 @@ func TestKept_AfterLoadRunsTheDeclaredCommand(t *testing.T) {
t.Fatalf("after_load ran %q, want %q", strings.Join(got, " "), want)
}
}
// TestKept_OwnerIsNeverTheFileBrowser — found live on 9202 (0.274.0-rc1): the file browser's compose binds
// every kept folder by its absolute path (the read-only view), and the list named two leftovers
// "Filebrowser". An owner is an app that binds the folder THROUGH ${HDD_PATH} — the folder or one inside it.
// COMPANION RED-PROOF (REPORT.md): drop the ${HDD_PATH} filter — this fails at "named Filebrowser".
func TestKept_OwnerIsNeverTheFileBrowser(t *testing.T) {
m, drive := keptManager(t)
fb := filepath.Join(m.cfg.Paths.StacksDir, "filebrowser")
must(t, os.MkdirAll(fb, 0o755))
must(t, os.WriteFile(filepath.Join(fb, "docker-compose.yml"), []byte("services:\n filebrowser:\n image: fb\n volumes:\n - "+
filepath.Join(drive, "appdata/cloudapp")+":/srv/kept:ro\n - "+filepath.Join(drive, "appdata/paperless")+":/srv/kept2:ro\n"), 0o644))
must(t, os.WriteFile(filepath.Join(fb, ".felhom.yml"), []byte("display_name: Filebrowser\n"), 0o644))
pl := filepath.Join(m.cfg.Paths.StacksDir, "paperless-ngx")
must(t, os.MkdirAll(pl, 0o755))
must(t, os.WriteFile(filepath.Join(pl, "docker-compose.yml"), []byte("services:\n web:\n image: p\n volumes:\n - ${HDD_PATH}/appdata/paperless/media:/m\n"), 0o644))
must(t, os.WriteFile(filepath.Join(pl, ".felhom.yml"), []byte("display_name: Paperless-ngx\n"), 0o644))
must(t, m.ScanStacks())
must(t, os.MkdirAll(filepath.Join(drive, "appdata/paperless/media"), 0o755))
must(t, os.WriteFile(filepath.Join(drive, "appdata/paperless/media/doc.pdf"), []byte("x"), 0o644))
got := map[string]string{}
for _, it := range m.ListKept([]string{drive}) {
got[filepath.Base(it.Path)] = it.DisplayName
}
if got["cloudapp"] != "Cloud App" || got["paperless"] != "Paperless-ngx" {
t.Fatalf("the leftovers must be named by the app that binds them through HDD_PATH, never the file browser: %v", got)
}
}