From 9280e2eb2c9551ca3adb300f7e4e7e90b371456a Mon Sep 17 00:00:00 2001 From: clawbot <35+clawbot@noreply.example.org> Date: Tue, 20 Oct 2026 03:58:56 +0200 Subject: [PATCH] Keep the database files, logs and binaries out of the build context (closes #120) The daemon's database is db.sqlite with its -wal and -shm files, which the .db patterns in .dockerignore and .gitignore did not match, so a database left in the clone shipped in the image's source archive. .dockerignore now also excludes the logs and the .exe, .dll, .so and .dylib files .gitignore already ignores. A test opens a database and checks that .dockerignore has an any-depth pattern for each of its files. Model: opus-5-5 --- .dockerignore | 14 ++++- .gitignore | 6 +- .../database/dockerignore_internal_test.go | 61 +++++++++++++++++++ 3 files changed, 78 insertions(+), 3 deletions(-) create mode 100644 internal/database/dockerignore_internal_test.go diff --git a/.dockerignore b/.dockerignore index fdde48f..875a9f5 100644 --- a/.dockerignore +++ b/.dockerignore @@ -73,7 +73,8 @@ # This repository's host-built artifacts: `make build` writes bin/, `go # build` in the root the binaries, `make run` log.txt, `make asupdate` its -# .tmp file; test binaries, coverage profiles and source archives. +# .tmp file; logs, test binaries, coverage profiles, source archives, and +# the binaries and libraries a build for another platform writes. /bin /routewatch /asinfo-gen @@ -82,15 +83,24 @@ /out /pkg/asinfo/asdata.json **/*.tar.zst +**/*.log **/*.test **/*.out **/*.tmp +**/*.exe +**/*.dll +**/*.so +**/*.dylib # Local databases. The image carries a source archive of the whole build -# context, so these would otherwise ship inside it. +# context, so these would otherwise ship inside it. The daemon's database +# is db.sqlite, with its -wal and -shm files while it runs. **/*.db **/*.db-journal **/*.db-wal +**/*.sqlite +**/*.sqlite-wal +**/*.sqlite-shm # A local Go workspace points at directories outside the build context. /go.work diff --git a/.gitignore b/.gitignore index 6407e7a..b42a480 100644 --- a/.gitignore +++ b/.gitignore @@ -67,10 +67,14 @@ node_modules/ go.work go.work.sum -# Local databases, temporary files and debug output. +# Local databases, temporary files and debug output. The daemon's +# database is db.sqlite, with its -wal and -shm files while it runs. *.db *.db-journal *.db-wal +*.sqlite +*.sqlite-wal +*.sqlite-shm *.tmp log.txt out diff --git a/internal/database/dockerignore_internal_test.go b/internal/database/dockerignore_internal_test.go new file mode 100644 index 0000000..151967d --- /dev/null +++ b/internal/database/dockerignore_internal_test.go @@ -0,0 +1,61 @@ +package database + +import ( + "os" + "path/filepath" + "strings" + "testing" + + "git.eeqj.de/sneak/routewatch/internal/config" + "git.eeqj.de/sneak/routewatch/internal/logger" +) + +// TestDockerignoreExcludesDatabaseFiles checks that .dockerignore keeps the +// database file, and the WAL and shared-memory files SQLite writes next to it, +// out of the build context wherever they sit in the clone. The image ships the +// whole build context as a source archive. +func TestDockerignoreExcludesDatabaseFiles(t *testing.T) { + t.Parallel() + + db, err := New(&config.Config{StateDir: t.TempDir()}, logger.New()) + if err != nil { + t.Fatalf("failed to create database: %v", err) + } + defer func() { _ = db.Close() }() + + data, err := os.ReadFile("../../.dockerignore") + if err != nil { + t.Fatalf("failed to read .dockerignore: %v", err) + } + + // Docker reads `**/` as any number of directories and the rest of the + // pattern as filepath.Match does, so `**/` followed by a pattern that + // matches a file's name excludes that file at any depth. + var anyDepth []string + + for line := range strings.Lines(string(data)) { + pattern, ok := strings.CutPrefix(strings.TrimSpace(line), "**/") + if ok { + anyDepth = append(anyDepth, pattern) + } + } + + name := filepath.Base(db.path) + + for _, file := range []string{name, name + "-wal", name + "-shm"} { + excluded := false + + for _, pattern := range anyDepth { + matched, err := filepath.Match(pattern, file) + if err != nil { + t.Fatalf("bad .dockerignore pattern **/%s: %v", pattern, err) + } + + excluded = excluded || matched + } + + if !excluded { + t.Errorf(".dockerignore has no **/ pattern that excludes %s", file) + } + } +}