A CLI-first git forge.

cli forge git self-hosted

https://gitbay.org

Commit 30ef5fd837

30ef5fd837383670c932e5e4520565b2b93e4987

parent: c8bf623f41

Verified · cmc ci/build: success

cmc <hello@cleberg.net> · 2026-08-30T02:24:25Z

gitbayd: log when a restart moves the schema

openStore migrates on every start and said nothing about it, so an
unexpected user_version had nothing in the journal tying it to the deploy
that applied it. Tracing 0027 to an unrelated deploy took a binary
strings dump for want of one line.

Reads the version either side of MigrateUp and logs from/to when they
differ. An up-to-date database stays silent; the migrate subcommand
already prints its result.

Ref #28.
cmd/gitbayd/main.go +16
@@ -38,10 +38,26 @@ func openStore(cfg config.Config) (*store.Store, error) {
3838 if err != nil {
3939 return nil, err
4040 }
41 // Say so when the schema moves. A restart migrates in silence otherwise,
42 // which makes an unexpected schema version hard to attribute to the deploy
43 // that caused it.
44 before, err := s.Version()
45 if err != nil {
46 s.Close()
47 return nil, err
48 }
4149 if err := s.MigrateUp(); err != nil {
4250 s.Close()
4351 return nil, err
4452 }
53 after, err := s.Version()
54 if err != nil {
55 s.Close()
56 return nil, err
57 }
58 if after != before {
59 slog.Info("schema migrated", "from", before, "to", after)
60 }
4561 return s, nil
4662 }
4763
cmd/gitbayd/main_test.go added +56
@@ -0,0 +1,56 @@
1package main
2
3import (
4 "bytes"
5 "log/slog"
6 "strconv"
7 "strings"
8 "testing"
9
10 "gitbay.org/gitbay/internal/config"
11)
12
13// A restart that moves the schema says so. Migrations used to run in silence,
14// which left an unexpected user_version with nothing in the journal tying it to
15// the deploy that applied it.
16func TestOpenStoreLogsSchemaMigration(t *testing.T) {
17 cfg := config.Config{Server: config.Server{Root: t.TempDir()}}
18
19 var buf bytes.Buffer
20 prev := slog.Default()
21 slog.SetDefault(slog.New(slog.NewTextHandler(&buf, nil)))
22 defer slog.SetDefault(prev)
23
24 // First open creates the database and migrates it from nothing.
25 s, err := openStore(cfg)
26 if err != nil {
27 t.Fatalf("openStore: %v", err)
28 }
29 version, err := s.Version()
30 if err != nil {
31 t.Fatalf("version: %v", err)
32 }
33 s.Close()
34
35 logged := buf.String()
36 if !strings.Contains(logged, "schema migrated") {
37 t.Fatalf("no migration logged:\n%s", logged)
38 }
39 for _, want := range []string{"from=0", "to=" + strconv.Itoa(version)} {
40 if !strings.Contains(logged, want) {
41 t.Errorf("expected %q in the log line:\n%s", want, logged)
42 }
43 }
44
45 // Reopening an already-current database is silent: every restart would
46 // otherwise claim a migration that did not happen.
47 buf.Reset()
48 s, err = openStore(cfg)
49 if err != nil {
50 t.Fatalf("reopen: %v", err)
51 }
52 s.Close()
53 if strings.Contains(buf.String(), "schema migrated") {
54 t.Errorf("logged a migration on an up-to-date database:\n%s", buf.String())
55 }
56}