Rename project from TopDeck to Canon

- Module github.com/cdnutter/canon, binary/command cmd/canon, Docker
  image and compose project "canon"
- Environment variables now use the CANON_ prefix
- Default site title is "<Your name>'s Canon" (or "Canon" with no name);
  a Site title set in Settings still overrides it
- Existing topdeck.db (and WAL files) is renamed to canon.db on start
- README: upgrade notes, including moving the old compose volume

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
cdnutterandClaude Opus 5.5 committed 2026-09-24 17:22:17 -07:00
1 parent 58a29b7339
commit 3923444b3b
26 files changed
+215 -120

No files matched your search

+24 -1
View File
@@ -6,6 +6,7 @@ import (
"database/sql"
"errors"
"fmt"
"os"
"path/filepath"
_ "modernc.org/sqlite"
@@ -18,7 +19,10 @@ type Store struct {
}
func Open(dataDir string) (*Store, error) {
path := filepath.Join(dataDir, "topdeck.db")
path := filepath.Join(dataDir, "canon.db")
if err := adoptLegacyDB(dataDir, path); err != nil {
return nil, err
}
dsn := fmt.Sprintf("file:%s?_pragma=journal_mode(WAL)&_pragma=foreign_keys(1)&_pragma=busy_timeout(5000)&_pragma=synchronous(NORMAL)&_txlock=immediate", path)
db, err := sql.Open("sqlite", dsn)
if err != nil {
@@ -35,6 +39,25 @@ func Open(dataDir string) (*Store, error) {
func (s *Store) Close() error { return s.db.Close() }
// adoptLegacyDB renames a database from before the project was called
// Canon (topdeck.db, plus its WAL files) so upgrades keep their data.
func adoptLegacyDB(dataDir, path string) error {
legacy := filepath.Join(dataDir, "topdeck.db")
if _, err := os.Stat(path); !errors.Is(err, os.ErrNotExist) {
return nil
}
if _, err := os.Stat(legacy); err != nil {
return nil
}
for _, suffix := range []string{"-wal", "-shm", ""} {
err := os.Rename(legacy+suffix, path+suffix)
if err != nil && !errors.Is(err, os.ErrNotExist) {
return fmt.Errorf("renaming %s to %s: %w", legacy+suffix, path+suffix, err)
}
}
return nil
}
// migrations are applied in order; the index into this slice (plus one) is
// the schema version stored in PRAGMA user_version.
var migrations = []string{
+30
View File
@@ -4,6 +4,8 @@ import (
"context"
"errors"
"fmt"
"os"
"path/filepath"
"strings"
"testing"
)
@@ -177,3 +179,31 @@ func TestCustomList(t *testing.T) {
t.Errorf("movie aspect = %q", m.Aspect())
}
}
func TestAdoptLegacyDB(t *testing.T) {
dir := t.TempDir()
s, err := Open(dir)
if err != nil {
t.Fatal(err)
}
if _, err := s.CreateList(context.Background(), List{Title: "Keep me", Category: "movie", MaxItems: 5}); err != nil {
t.Fatal(err)
}
s.Close()
// Pretend this data dir predates the rename.
for _, suffix := range []string{"", "-wal", "-shm"} {
os.Rename(filepath.Join(dir, "canon.db"+suffix), filepath.Join(dir, "topdeck.db"+suffix))
}
s, err = Open(dir)
if err != nil {
t.Fatal(err)
}
defer s.Close()
lists, err := s.Lists(context.Background(), true)
if err != nil || len(lists) != 1 || lists[0].Title != "Keep me" {
t.Fatalf("legacy data not adopted: %v %v", lists, err)
}
if _, err := os.Stat(filepath.Join(dir, "topdeck.db")); !os.IsNotExist(err) {
t.Errorf("topdeck.db should have been renamed")
}
}