Monorepo for Tangled tangled.org
2

Configure Feed

Select the types of activity you want to include in your feed.

1package db 2 3import ( 4 "context" 5 "database/sql" 6 "fmt" 7 "log/slog" 8) 9 10type MigrationFn = func(context.Context, *sql.Tx) error 11 12type Migration struct { 13 Name string 14 Fn MigrationFn 15} 16 17func ensureMigrationsTable(ctx context.Context, conn *sql.Conn) error { 18 _, err := conn.ExecContext(ctx, ` 19 create table if not exists migrations ( 20 name text primary key, 21 applied_at timestamptz not null default now() 22 ); 23 `) 24 return err 25} 26 27func RunMigration(ctx context.Context, conn *sql.Conn, logger *slog.Logger, m Migration) error { 28 logger = logger.With("migration", m.Name) 29 30 tx, err := conn.BeginTx(ctx, nil) 31 if err != nil { 32 return fmt.Errorf("begin migration tx: %w", err) 33 } 34 defer tx.Rollback() 35 36 var exists bool 37 if err := tx.QueryRowContext(ctx, `select exists (select 1 from migrations where name = $1)`, m.Name).Scan(&exists); err != nil { 38 return fmt.Errorf("checking migration state: %w", err) 39 } 40 if exists { 41 logger.Debug("migration already applied") 42 return nil 43 } 44 45 if err := m.Fn(ctx, tx); err != nil { 46 logger.Error("migration failed", "err", err) 47 return fmt.Errorf("running migration %s: %w", m.Name, err) 48 } 49 50 if _, err := tx.ExecContext(ctx, `insert into migrations (name) values ($1)`, m.Name); err != nil { 51 return fmt.Errorf("recording migration: %w", err) 52 } 53 54 if err := tx.Commit(); err != nil { 55 return fmt.Errorf("commit migration: %w", err) 56 } 57 58 logger.Info("migration applied") 59 return nil 60} 61 62func RunMigrations(ctx context.Context, conn *sql.Conn, logger *slog.Logger, ms []Migration) error { 63 if err := ensureMigrationsTable(ctx, conn); err != nil { 64 return fmt.Errorf("ensuring migrations table: %w", err) 65 } 66 for _, m := range ms { 67 if err := RunMigration(ctx, conn, logger, m); err != nil { 68 return err 69 } 70 } 71 return nil 72}