migrations

package
v0.7.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 4, 2026 License: MIT Imports: 20 Imported by: 0

Documentation

Overview

Package migrations wraps Atlas versioned migrations for Gombit apps.

MakeMigrations generates SQL via Atlas Program Mode. Migrate, Status, and Rollback apply and report those migrations while recording D4 metadata in framework_migrations. Seed runs ordered SQL files from database/seeds, and Reset drops the schema then migrate + seed.

Index

Constants

This section is empty.

Variables

View Source
var BookkeepingTables = []string{revisionsTable, atlasRevisionsTable}

BookkeepingTables are the tables Gombit and Atlas keep in the application database to record applied migrations. They are not part of the schema the migrations build, so a schema comparison ignores them.

View Source
var ErrSchemaInspect = errors.New("migrations: inspect the database schema")

ErrSchemaInspect wraps an InspectDatabase failure that happened after the migration ledger was read: the database answered, `atlas schema inspect` did not. The returned state still carries the ledger.

Functions

func AllowDirectives added in v0.4.0

func AllowDirectives(sql string) []string

AllowDirectives returns the step IDs or codes a migration's `-- gombit:allow` lines acknowledge.

func AtlasURL

func AtlasURL(cfg config.DatabaseConfig) (string, error)

AtlasURL converts a Gombit database config into an Atlas --url value.

func DownFilename

func DownFilename(upFilename string) (string, error)

DownFilename returns the companion down filename (basename) for an up migration file.

func DownPath

func DownPath(migrationDir string, version string, name string) string

DownPath returns the path to the Gombit-owned down SQL for an up migration. Downs live under <migrationDir>/downs/ so Atlas never scans them (Atlas panics on golang-migrate-style .down.sql files in the versioned migration directory).

func DropSchema

func DropSchema(ctx context.Context, opts ApplyOptions) error

DropSchema removes all user tables and views from the configured database.

Driver wipe strategy:

  • SQLite: DROP every non-sqlite_* table/view from sqlite_master
  • Postgres: DROP SCHEMA atlas_schema_revisions CASCADE (Atlas default); DROP SCHEMA public CASCADE; CREATE SCHEMA public; restore grants
  • MySQL: disable FK checks, DROP every base table and view, re-enable checks

The SQLite database file is not deleted so in-memory and shared-cache DSNs work.

func Hash added in v0.1.14

func Hash(ctx context.Context, opts ApplyOptions) error

Hash recomputes the Atlas migration directory's checksum file (atlas.sum) by wrapping `atlas migrate hash`. After a migration is hand-edited — e.g. to backfill a renamed column so a NOT NULL rebuild can apply — Atlas refuses to run it with a checksum error. This lets a developer recover with `gombit db hash` instead of reaching for the raw Atlas CLI (#219). It does not touch the application database, and `atlas migrate hash` is part of Atlas Community Edition (ADR-012).

func InspectDirAt added in v0.4.0

func InspectDirAt(ctx context.Context, opts DirOptions, version string) ([]byte, error)

InspectDirAt returns the HCL of the schema the migration directory builds up to and including version, from `atlas schema inspect` on the dev database. An empty version is the state before the first migration.

func MakeMigrations

func MakeMigrations(ctx context.Context, opts Options) error

MakeMigrations generates an Atlas Program Mode loader and runs atlas migrate diff.

func Migrate

func Migrate(ctx context.Context, opts ApplyOptions) error

Migrate applies pending Atlas migrations and mirrors them into framework_migrations.

After atlas migrate apply succeeds, only pending versions that appear in atlas_schema_revisions are recorded. This keeps Gombit's ledger aligned with Atlas when the two previously diverged or when Atlas applies a subset.

func ParseDownFilename

func ParseDownFilename(filename string) (version string, name string, err error)

ParseDownFilename parses a Gombit companion down migration filename.

func ParseMigrationFilename

func ParseMigrationFilename(filename string) (version string, name string, err error)

ParseMigrationFilename parses an Atlas up migration filename.

func RegistryPath

func RegistryPath(migrationDir string) string

RegistryPath returns the path to the persisted model registry inside migrationDir.

func Reset

func Reset(ctx context.Context, opts ResetOptions) error

Reset drops the database schema, applies migrations, then runs seeders.

When Environment is production, Force must be true.

func Rollback

func Rollback(ctx context.Context, opts ApplyOptions) error

Rollback rolls back the latest framework_migrations batch using companion down SQL. Each down file is split into individual statements (like Seed) and executed one Exec per statement, so a down file is no longer required to be a single SQL statement on MySQL.

On SQLite and PostgreSQL, down SQL and revision deletes run in one transaction so a mid-batch failure leaves neither schema nor revision ledgers partially updated. MySQL DDL often auto-commits, so a mid-batch failure can leave the schema partially rolled back while revision rows remain — including inside a single down file, if an earlier statement in it already committed before a later one failed; the error message names the failing statement and file, and revision rows are not deleted until every down succeeds.

func SaveRegistry

func SaveRegistry(migrationDir string, models []Model) error

SaveRegistry persists models to migrationDir, sorted for stable diffs regardless of the order callers passed them in.

func Seed

func Seed(ctx context.Context, opts SeedOptions) error

Seed executes SQL seed files from the configured seed directory in lexical order.

Missing or empty seed directories succeed with "No seed files.". Non-.sql entries and nested subdirectories are skipped with a warning on stderr. Each file may contain multiple statements separated by `;`; statements are executed in order and execution stops on the first SQL error.

func Status

func Status(ctx context.Context, opts ApplyOptions) error

Status reports applied and pending migrations, then runs atlas migrate status.

func ValidateDir added in v0.4.0

func ValidateDir(ctx context.Context, opts DirOptions) error

ValidateDir checks the migration directory's integrity with `atlas migrate validate` (Atlas Community Edition): atlas.sum matches every file, and the migrations replay on an empty dev database. It returns a *ChecksumError or a *ReplayError.

Types

type ApplyOptions

type ApplyOptions struct {
	WorkDir      string
	MigrationDir string
	AtlasBinary  string
	Database     config.DatabaseConfig
	Stdout       io.Writer
	Stderr       io.Writer
	// contains filtered or unexported fields
}

ApplyOptions configures migrate, rollback, and status commands.

type ChecksumError added in v0.4.0

type ChecksumError struct {
	// Files are the migration files Atlas reports as edited, added, or
	// removed since atlas.sum was written.
	Files []string
}

ChecksumError reports migration files that no longer match atlas.sum.

func (*ChecksumError) Error added in v0.4.0

func (e *ChecksumError) Error() string

type DatabaseState added in v0.4.0

type DatabaseState struct {
	// Pending are the up migrations the database has not applied, in order.
	Pending []MigrationFile
	// LastApplied is the newest applied version with a file in the
	// directory, or "" when none is.
	LastApplied string
	// Unknown are versions the database records as applied that have no
	// file in the migration directory (a migration renamed or deleted after
	// it ran).
	Unknown []string
	// Schema is the HCL `atlas schema inspect` prints for the live database.
	Schema []byte
}

DatabaseState is what `gombit db check` reads from the application database: which migrations it has applied and the schema it actually has.

func InspectDatabase added in v0.4.0

func InspectDatabase(ctx context.Context, opts ApplyOptions) (DatabaseState, error)

InspectDatabase reads the application database without changing it: the applied versions from framework_migrations (a missing table means none), the migrations still pending, and the live schema. The ledger and the schema are separate reads: when only the schema read fails, the error wraps ErrSchemaInspect and the state still carries the ledger.

type DirOptions added in v0.4.0

type DirOptions struct {
	WorkDir      string
	Driver       config.DatabaseDriver
	MigrationDir string
	AtlasBinary  string
	Stdout       io.Writer
	Stderr       io.Writer
	// contains filtered or unexported fields
}

DirOptions configures the migration-directory checks `gombit db lint` and `gombit db repair` run on the dev database.

type Gate added in v0.4.0

type Gate func(ctx context.Context, name string, in Inspection, sql string) (acknowledged []string, err error)

Gate decides whether MakeMigrations keeps the migration named name. It runs once the directory already holds a migration, after `atlas migrate diff` wrote sql; in is the schema before it and the schema the models declare. A non-nil error refuses the migration: MakeMigrations removes the file and restores atlas.sum. The step IDs it returns are the destructive or unsafe changes the run acknowledged: MakeMigrations records each as a `-- gombit:allow` line in the migration, so `gombit db lint`, which applies the same classification, accepts it later. schemaplan.Gate is the implementation the gombit CLI installs.

type InspectOptions added in v0.4.0

type InspectOptions struct {
	WorkDir      string
	Driver       config.DatabaseDriver
	MigrationDir string
	AtlasBinary  string
	Models       []Model
	ForgetModels []Model
	Stderr       io.Writer
	// contains filtered or unexported fields
}

InspectOptions configures Inspect. The model fields mean what they mean for MakeMigrations: the desired schema is the persisted registry plus Models, minus ForgetModels.

type Inspection added in v0.4.0

type Inspection struct {
	Driver config.DatabaseDriver
	// Current is the schema the migration directory builds. It is empty
	// before the first migration.
	Current []byte
	// Desired is the schema the models declare.
	Desired []byte
	// NewModels are the models this run adds to the registry. A refused run
	// saves no registry, so reproducing it has to name them again.
	NewModels []Model
	// ForgetModels are the models this run stops tracking: their tables are
	// meant to be dropped.
	ForgetModels []Model
}

Inspection is the pair of schema states a migration plan compares, as the HCL `atlas schema inspect` prints on the dev database. Package schemaplan classifies it. It stays raw here so this package, which generated apps' loaders compile, does not depend on the Atlas SQL libraries.

func Inspect added in v0.4.0

func Inspect(ctx context.Context, opts InspectOptions) (Inspection, error)

Inspect loads the desired schema from the models and inspects it and the migration directory with `atlas schema inspect`, without writing a migration. It is what `gombit db plan` classifies.

type MigrationFile

type MigrationFile struct {
	Version  string
	Name     string
	UpPath   string
	DownPath string
}

MigrationFile is one Atlas versioned migration on disk.

func ListMigrationFiles

func ListMigrationFiles(dir string) ([]MigrationFile, error)

ListMigrationFiles lists Atlas up migrations in dir, with optional down paths. Non-matching *.sql filenames (other than atlas.sum) are skipped with a warning. Companion downs live under downs/ and are not listed as up files.

func ListMigrationFilesWithSkipped

func ListMigrationFilesWithSkipped(dir string) ([]MigrationFile, []string, error)

ListMigrationFilesWithSkipped lists migrations and returns skipped *.sql names.

type Model

type Model struct {
	ImportPath string `json:"import_path"`
	TypeName   string `json:"type_name"`
}

Model identifies one GORM model type imported by the generated Atlas loader.

func LoadRegistry

func LoadRegistry(migrationDir string) ([]Model, error)

LoadRegistry reads the model set previously persisted in migrationDir. A missing file is not an error — it means no migration has been generated there yet — and returns a nil slice.

func MergeModels

func MergeModels(existing, additional []Model) []Model

MergeModels unions existing (typically the persisted registry) with additional (typically the --model flags for this invocation), removing duplicates. additional's relative order is preserved first — it is what the caller explicitly asked for this run — followed by any existing-only models in sorted order.

func ParseModel

func ParseModel(spec string) (Model, error)

ParseModel parses a model spec in the form "import/path.TypeName".

func SubtractModels

func SubtractModels(models, remove []Model) []Model

SubtractModels returns models with every entry in remove removed.

type Options

type Options struct {
	WorkDir      string
	Name         string
	Driver       config.DatabaseDriver
	MigrationDir string
	AtlasBinary  string
	// Models are the GORM models this invocation is declaring. They are
	// merged with whatever is already in the persisted registry
	// (RegistryPath) — not the entire desired schema by themselves — so a
	// migration for one new model does not implicitly drop tables for
	// models used in earlier migrations.
	Models []Model
	// ForgetModels removes models from the persisted registry (and this
	// invocation's desired schema), the explicit way to let Atlas propose
	// dropping a table that's genuinely going away.
	ForgetModels []Model
	// Renames, when non-empty, makes this a rename migration: Gombit emits a
	// native, data-preserving RENAME COLUMN instead of the drop+add table rebuild
	// `atlas migrate diff` would generate for a field rename. It is not combined
	// with model/schema diffing in the same run.
	Renames []Rename
	// TableRenames, like Renames, makes this a rename migration: Gombit emits
	// a native ALTER TABLE ... RENAME TO, applied before the column renames.
	// It may name the models the rename swaps (Models / ForgetModels): they
	// update the registry, and no model diff runs.
	TableRenames []TableRename
	// Gate, when set, may refuse the migration after inspecting the change
	// (see Gate). Nil writes whatever `atlas migrate diff` produces.
	Gate   Gate
	Stdout io.Writer
	Stderr io.Writer
	// contains filtered or unexported fields
}

Options configures MakeMigrations.

type Rename added in v0.2.1

type Rename struct {
	Table     string
	OldColumn string
	NewColumn string
}

Rename is a single column rename: OldColumn on Table becomes NewColumn. Gombit emits it as a native, data-preserving `ALTER TABLE ... RENAME COLUMN`, which SQLite (>= 3.25), PostgreSQL, and MySQL 8 all support.

func ParseRename added in v0.2.1

func ParseRename(spec string) (Rename, error)

ParseRename parses a rename spec of the form "table.old_column:new_column", or "table.old_column=table.new_column".

type ReplayError added in v0.4.0

type ReplayError struct {
	// Detail is Atlas's explanation, with raw-Atlas recovery hints rewritten
	// to their gombit commands.
	Detail string
}

ReplayError reports a migration directory that does not apply cleanly on an empty dev database.

func (*ReplayError) Error added in v0.4.0

func (e *ReplayError) Error() string

type ResetOptions

type ResetOptions struct {
	ApplyOptions
	SeedDir     string
	Force       bool
	Environment config.Environment
}

ResetOptions configures gombit db reset (drop + migrate + seed).

type Revision

type Revision struct {
	Version   string    `gorm:"column:version;primaryKey;size:64"`
	Name      string    `gorm:"column:name;size:255;not null"`
	Batch     int       `gorm:"column:batch;not null"`
	AppliedAt time.Time `gorm:"column:applied_at;not null"`
}

Revision is one applied migration recorded in framework_migrations (D4).

func (Revision) TableName

func (Revision) TableName() string

TableName returns the D4 revision table name.

type SeedOptions

type SeedOptions struct {
	WorkDir  string
	SeedDir  string
	Database config.DatabaseConfig
	Stdout   io.Writer
	Stderr   io.Writer
	// contains filtered or unexported fields
}

SeedOptions configures gombit db seed.

type TableRename added in v0.4.0

type TableRename struct {
	Old string
	New string
}

TableRename renames table Old to New with a native, data-preserving `ALTER TABLE ... RENAME TO`, which SQLite, PostgreSQL, and MySQL all support. Foreign keys in other tables follow the table on all three.

func ParseTableRename added in v0.4.0

func ParseTableRename(spec string) (TableRename, error)

ParseTableRename parses a table rename spec of the form "old_table:new_table".

Directories

Path Synopsis
Package schemaplan classifies the schema change a migration would make before it is written (SCHEMA-2, #309), and lints the migrations already written (SCHEMA-4, #311).
Package schemaplan classifies the schema change a migration would make before it is written (SCHEMA-2, #309), and lints the migrations already written (SCHEMA-4, #311).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL