Skip to content

Latest commit

 

History

History
116 lines (85 loc) · 2.95 KB

File metadata and controls

116 lines (85 loc) · 2.95 KB

migrations

Tier: Platform · Status: Full · Java original: Flyway · .NET project: EF Core migrations / DbUp

Overview

migrations is the framework's versioned-SQL migration runner. Migration files are named V{version}__{description}.sql (e.g. V001__init.sql); each file runs once, in version order, inside a transaction. The applied versions are recorded in a firefly_migrations table for idempotency.

The runner works against any database/sql.DB driver — the SQL is parameter-free and ANSI-compatible. Tested against PostgreSQL and SQLite.

Why a separate module?

Flyway and DbUp are mature in the JVM and .NET worlds. Go has many migration libraries, each with a slightly different file-name convention, locking strategy, and recovery story. migrations provides one convention so every Firefly Go service handles schema evolution the same way.

File layout

db/
├── V001__init.sql
├── V002__add_orders_index.sql
└── V003__seed_reference_data.sql

The runner only matches files with the V{version}__{description}.sql pattern; everything else (READMEs, .gitkeep) is ignored.

Public surface

type Migration struct {
    Version     int
    Description string
    Filename    string
    SQL         string
    Checksum    string  // SHA-256 of the SQL bytes
}

type Source interface { List() ([]Migration, error) }

func NewFSSource(fsys fs.FS, dir string) *FSSource  // perfect for embed.FS
type SliceSource struct { Items []Migration }       // hand-built (tests)

func Run(ctx, *sql.DB, Source) error
func Inspect(ctx, *sql.DB, Source) (Status, error)

type Status struct{ Applied, Pending []Migration }

var ErrChecksumMismatch = errors.New("…checksum mismatch")

Schema

CREATE TABLE IF NOT EXISTS firefly_migrations (
    version     INTEGER     PRIMARY KEY,
    description TEXT        NOT NULL,
    filename    TEXT        NOT NULL,
    checksum    TEXT        NOT NULL,
    applied_at  TIMESTAMP   NOT NULL
);

Checksum guard

When a migration is applied, its SHA-256 checksum is stored. If the file is later edited (something you should never do — migrations are append-only history), a subsequent Run returns ErrChecksumMismatch rather than silently skipping.

Quick start

Embed your migrations:

//go:embed db/*.sql
var migrationFS embed.FS

src := migrations.NewFSSource(migrationFS, "db")
if err := migrations.Run(ctx, db, src); err != nil { log.Fatal(err) }

Inspect status:

st, _ := migrations.Inspect(ctx, db, src)
fmt.Printf("applied=%d pending=%d\n", len(st.Applied), len(st.Pending))

Hand-built (tests):

src := &migrations.SliceSource{Items: []migrations.Migration{
    {Version: 1, Filename: "V001__init.sql", SQL: `CREATE TABLE t (id INTEGER)`},
}}
_ = migrations.Run(ctx, db, src)

Testing

cd migrations
go test ./...

Covers fresh apply, idempotent re-run, checksum-mismatch rejection, and the embed.FS variant.