migrations

package module
v0.0.0-...-9e30f77 Latest Latest
Warning

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

Go to latest
Published: Aug 24, 2026 License: MIT Imports: 16 Imported by: 0

README

migrations

migrations is an engine-neutral database migration runtime with a PostgreSQL backend. It owns migration identity, planning, status, locking, checksums, baselines, recovery, and the public.go_schema_migrations ledger. Goose is an internal, pinned SQL execution detail and never appears in the public API.

Install

go get github.com/faustbrian/golib/pkg/migrations

The supported Go and PostgreSQL versions are documented in compatibility.

Embedded migrations

//go:embed migrations/*.sql
var files embed.FS

source, err := migrations.NewFSSource(files, "migrations")
if err != nil {
	return err
}
backend, err := postgres.New(
	database,
	postgres.WithLockTimeout(30*time.Second),
	postgres.WithStatementTimeout(5*time.Minute),
)
if err != nil {
	return err
}
runner, err := migrations.NewRunner(source, backend)
if err != nil {
	return err
}
plan, err := runner.Plan(ctx)
if err != nil {
	return err
}
result, err := runner.Up(ctx)

Run this code in a dedicated deployment job. Do not run it implicitly in every service process.

Service migrate command

migrationsservice.New adapts a caller-constructed Runner to the standard one-shot service migrate role. The caller loads typed configuration, prepares only migration dependencies, and explicitly selects the runner operation. The adapter adds no migration policy, HTTP listener, management server, readiness check, retries, or long-lived resource ownership.

Migration-only components start before the task and stop in reverse order after it finishes or fails. A missing runner fails during plan construction. Use a dedicated deployment job and select Runner.Up, Plan, Status, Down, or recovery behavior explicitly according to the reviewed operation.

Safety properties

  • The complete source and ledger history is validated while an advisory lock is held.
  • Applied files cannot be changed, renamed, removed, or reordered.
  • Transactional migrations update schema and ledger atomically.
  • Explicit no-transaction migrations persist dirty state before executing SQL.
  • Dirty outcomes require a checksum-bound operator recovery decision.
  • Existing Laravel databases are adopted through an exact reviewed schema fingerprint without reading or modifying Laravel's migrations table.
  • Status, plans, records, events, and migration values are immutable snapshots.

Read the migration format, operations guide, and Laravel baseline runbook before production use.

Documentation

License

migrations is open-source software licensed under the MIT License.

Ecosystem

Use the Golib documentation portal to choose companion packages, supported stacks, recipes, and operations guidance.

Documentation

Overview

Package migrations provides engine-neutral database migration contracts.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrInvalidBaseline indicates malformed reviewed baseline metadata.
	ErrInvalidBaseline = errors.New("invalid schema baseline")
	// ErrBaselineMismatch indicates live schema drift from the reviewed contract.
	ErrBaselineMismatch = errors.New("schema does not match reviewed baseline")
	// ErrBaselineExists indicates that owned migration history is not empty.
	ErrBaselineExists = errors.New("migration baseline already exists")
	// ErrBaselineVersionConflict indicates Go migrations at or below the
	// baseline cutoff.
	ErrBaselineVersionConflict = errors.New("go migration conflicts with baseline version")
	// ErrBaselineUnsupported indicates a backend without baseline conformance.
	ErrBaselineUnsupported = errors.New("migration backend does not support baselines")
)
View Source
var (
	// ErrInvalidVersion indicates that a migration version is zero or malformed.
	ErrInvalidVersion = errors.New("invalid migration version")
	// ErrInvalidName indicates that a migration name is not canonical snake case.
	ErrInvalidName = errors.New("invalid migration name")
	// ErrInvalidTransactionMode indicates an unknown transaction mode.
	ErrInvalidTransactionMode = errors.New("invalid transaction mode")
	// ErrEmptyUpSQL indicates that a migration has no forward operation.
	ErrEmptyUpSQL = errors.New("migration up SQL is empty")
	// ErrInvalidChecksum indicates malformed or unsupported checksum text.
	ErrInvalidChecksum = errors.New("invalid migration checksum")
)
View Source
var (
	// ErrInvalidRecord indicates malformed owned-ledger data.
	ErrInvalidRecord = errors.New("invalid migration record")
	// ErrDirty indicates an interrupted or partially applied migration.
	ErrDirty = errors.New("dirty migration history")
	// ErrChecksumMismatch indicates an applied migration was modified.
	ErrChecksumMismatch = errors.New("migration checksum mismatch")
	// ErrRenamedMigration indicates an applied version now has another name.
	ErrRenamedMigration = errors.New("applied migration was renamed")
	// ErrDeletedMigration indicates an applied migration is absent from source.
	ErrDeletedMigration = errors.New("applied migration was deleted")
	// ErrReorderedHistory indicates non-monotonic or non-prefix history.
	ErrReorderedHistory = errors.New("migration history was reordered")
	// ErrInvalidTarget indicates an impossible or ambiguous plan target.
	ErrInvalidTarget = errors.New("invalid migration target")
	// ErrIrreversible indicates that rollback SQL was not provided.
	ErrIrreversible = errors.New("migration is irreversible")
)
View Source
var (
	// ErrInvalidRecovery indicates malformed recovery input.
	ErrInvalidRecovery = errors.New("invalid dirty migration recovery")
	// ErrRecoveryMismatch indicates that source identity differs from the
	// operator-reviewed recovery request.
	ErrRecoveryMismatch = errors.New("dirty migration recovery identity mismatch")
	// ErrNoDirtyMigration indicates there is no matching unresolved outcome.
	ErrNoDirtyMigration = errors.New("dirty migration not found")
	// ErrRecoveryUnsupported indicates a backend without recovery conformance.
	ErrRecoveryUnsupported = errors.New("migration backend does not support recovery")
	// ErrRecoveryConflict indicates more than one dirty migration in the ledger.
	ErrRecoveryConflict = errors.New("multiple dirty migrations require recovery")
)
View Source
var (
	// ErrInvalidRunner indicates missing or inconsistent runner dependencies.
	ErrInvalidRunner = errors.New("invalid migration runner")
	// ErrBackendResult indicates an engine violated the public backend contract.
	ErrBackendResult = errors.New("invalid migration backend result")
)
View Source
var (
	// ErrInvalidSource indicates an unusable filesystem source configuration.
	ErrInvalidSource = errors.New("invalid migration source")
	// ErrInvalidFilename indicates a non-canonical migration filename.
	ErrInvalidFilename = errors.New("invalid migration filename")
	// ErrInvalidFormat indicates malformed or ambiguous migration directives.
	ErrInvalidFormat = errors.New("invalid migration format")
	// ErrInvalidEncoding indicates non-UTF-8, NUL-containing, or oversized input.
	ErrInvalidEncoding = errors.New("invalid migration encoding")
	// ErrUnexpectedSourceEntry indicates a non-migration entry in the source.
	ErrUnexpectedSourceEntry = errors.New("unexpected migration source entry")
	// ErrDuplicateVersion indicates that two source files claim one identity.
	ErrDuplicateVersion = errors.New("duplicate migration version")
)

Functions

This section is empty.

Types

type Action

type Action uint8

Action identifies the operation represented by a plan step.

const (
	// ActionApply executes a migration's up SQL.
	ActionApply Action = iota + 1
	// ActionRollback executes a migration's down SQL.
	ActionRollback
)

type Backend

type Backend interface {
	Acquire(context.Context) (Session, error)
}

Backend is the replaceable migration-engine conformance boundary.

Session.Apply must return only after both execution and owned-ledger persistence have reached an explicit recoverable outcome. Implementations must leave a dirty record when non-transactional execution has an uncertain or partial result.

type Baseline

type Baseline struct {
	// contains filtered or unexported fields
}

Baseline is an immutable reviewed schema assertion for adopting an existing database without replaying historical migrations.

func NewBaseline

func NewBaseline(version Version, name string, fingerprint Checksum) (Baseline, error)

NewBaseline validates a reviewed baseline contract.

func (Baseline) Fingerprint

func (baseline Baseline) Fingerprint() Checksum

Fingerprint returns the expected canonical schema digest.

func (Baseline) Name

func (baseline Baseline) Name() string

Name returns the reviewed baseline contract name.

func (Baseline) Version

func (baseline Baseline) Version() Version

Version returns the immutable cutoff before all Go-owned migrations.

type Checksum

type Checksum struct {
	// contains filtered or unexported fields
}

Checksum is the SHA-256 digest of the canonical migration representation.

func ChecksumData

func ChecksumData(canonical []byte) Checksum

ChecksumData computes the stable SHA-256 representation of canonical bytes. It is intended for package-defined contracts such as schema fingerprints; migration identity uses its stricter built-in canonical representation.

func ParseChecksum

func ParseChecksum(value string) (Checksum, error)

ParseChecksum decodes the stable ledger representation of a checksum.

func (Checksum) GoString

func (checksum Checksum) GoString() string

GoString prevents checksum internals from becoming an accidental public serialization contract while keeping diagnostics useful.

func (Checksum) String

func (checksum Checksum) String() string

String returns the algorithm-qualified ledger representation.

type Event

type Event struct {
	// contains filtered or unexported fields
}

Event is an immutable structured diagnostic suitable for logging and tracing adapters. SQL text is deliberately excluded.

func (Event) Duration

func (event Event) Duration() time.Duration

Duration returns elapsed time for completed or failed operations.

func (Event) Err

func (event Event) Err() error

Err returns the operation failure without exposing SQL contents.

func (Event) Operation

func (event Event) Operation() Operation

Operation returns the lifecycle operation.

func (Event) Phase

func (event Event) Phase() Phase

Phase returns the lifecycle phase.

func (Event) Version

func (event Event) Version() Version

Version returns the affected migration version, or zero for job operations.

type FSSource

type FSSource struct {
	// contains filtered or unexported fields
}

FSSource loads canonical SQL migrations from one fs.FS directory. It works with embed.FS and rejects unrelated entries so packaging mistakes fail closed.

func NewFSSource

func NewFSSource(sourceFS fs.FS, root string) (*FSSource, error)

NewFSSource constructs a source rooted at a valid fs.FS path.

func (*FSSource) Load

func (source *FSSource) Load(ctx context.Context) ([]Migration, error)

Load reads, validates, and sorts the complete migration history.

type Migration

type Migration struct {
	// contains filtered or unexported fields
}

Migration is an immutable canonical SQL migration.

Fields are intentionally private so callers and execution engines cannot mutate identity or persisted checksum state after validation.

func NewMigration

func NewMigration(
	version Version,
	name string,
	transactionMode TransactionMode,
	upSQL string,
	downSQL string,
) (Migration, error)

NewMigration validates and constructs a canonical migration.

func (Migration) Checksum

func (migration Migration) Checksum() Checksum

Checksum returns the immutable content digest.

func (Migration) DownSQL

func (migration Migration) DownSQL() string

DownSQL returns the canonical rollback SQL, or an empty string when the migration is irreversible.

func (Migration) Name

func (migration Migration) Name() string

Name returns the canonical migration name without version or extension.

func (Migration) TransactionMode

func (migration Migration) TransactionMode() TransactionMode

TransactionMode returns the execution transaction policy.

func (Migration) UpSQL

func (migration Migration) UpSQL() string

UpSQL returns the canonical forward SQL.

func (Migration) Version

func (migration Migration) Version() Version

Version returns the immutable migration version.

type Observer

type Observer interface {
	Observe(context.Context, Event)
}

Observer consumes structured migration events.

type Operation

type Operation uint8

Operation identifies an observable migration lifecycle operation.

const (
	// OperationLock acquires exclusive migration-job ownership.
	OperationLock Operation = iota + 1
	// OperationApply applies one migration and persists its outcome.
	OperationApply
	// OperationRollback rolls back one migration and removes its ledger record.
	OperationRollback
	// OperationBaseline validates and records one reviewed existing schema.
	OperationBaseline
	// OperationRecover resolves one explicitly reviewed dirty outcome.
	OperationRecover
	// OperationUnlock releases exclusive migration-job ownership.
	OperationUnlock
)

type Option

type Option func(*Runner) error

Option configures a Runner.

func WithObserver

func WithObserver(observer Observer) Option

WithObserver installs a structured event observer. Observer panics are contained so diagnostics cannot change migration outcomes.

func WithUnlockTimeout

func WithUnlockTimeout(timeout time.Duration) Option

WithUnlockTimeout bounds best-effort lock release after the job context is canceled. Release always uses a detached context so cancellation cannot skip cleanup.

type Phase

type Phase uint8

Phase identifies the lifecycle phase of an emitted operation.

const (
	// PhaseStarted is emitted immediately before an operation.
	PhaseStarted Phase = iota + 1
	// PhaseCompleted is emitted after a successful operation.
	PhaseCompleted
	// PhaseFailed is emitted after a failed operation.
	PhaseFailed
)

type Plan

type Plan struct {
	// contains filtered or unexported fields
}

Plan is a deterministic execution plan derived from source and ledger state.

func PlanDown

func PlanDown(available []Migration, records []Record, count uint64) (Plan, error)

PlanDown validates the complete persisted history and returns exactly count rollback steps in descending version order. It never crosses a baseline.

func PlanUp

func PlanUp(available []Migration, records []Record) (Plan, error)

PlanUp validates the complete persisted history and returns pending migrations in ascending version order. Any ambiguity fails closed.

func (Plan) Steps

func (plan Plan) Steps() []Step

Steps returns a copy so callers cannot mutate a validated plan.

type Record

type Record struct {
	// contains filtered or unexported fields
}

Record is an immutable entry read from public.go_schema_migrations.

func NewRecord

func NewRecord(
	kind RecordKind,
	version Version,
	name string,
	checksum Checksum,
	appliedAt time.Time,
	duration time.Duration,
	dirty bool,
) (Record, error)

NewRecord validates data crossing the owned-ledger boundary.

func (Record) AppliedAt

func (record Record) AppliedAt() time.Time

AppliedAt returns when execution or baseline recording completed.

func (Record) Checksum

func (record Record) Checksum() Checksum

Checksum returns the persisted migration or schema fingerprint.

func (Record) Dirty

func (record Record) Dirty() bool

Dirty reports whether the operation has an unresolved partial outcome.

func (Record) Duration

func (record Record) Duration() time.Duration

Duration returns the measured operation duration.

func (Record) Kind

func (record Record) Kind() RecordKind

Kind returns whether this record is a migration or baseline.

func (Record) Name

func (record Record) Name() string

Name returns the persisted canonical name.

func (Record) Version

func (record Record) Version() Version

Version returns the record's immutable version.

type RecordKind

type RecordKind uint8

RecordKind distinguishes an executed migration from an explicit schema baseline in the owned ledger.

const (
	// RecordKindMigration is a migration executed by this package.
	RecordKindMigration RecordKind = iota + 1
	// RecordKindBaseline is a reviewed pre-existing schema assertion.
	RecordKindBaseline
)

type Recovery

type Recovery struct {
	// contains filtered or unexported fields
}

Recovery is an immutable, checksum-bound operator decision.

func NewRecovery

func NewRecovery(version Version, checksum Checksum, action RecoveryAction) (Recovery, error)

NewRecovery validates an explicit dirty-state resolution request.

func (Recovery) Action

func (recovery Recovery) Action() RecoveryAction

Action returns the operator's verified outcome.

func (Recovery) Checksum

func (recovery Recovery) Checksum() Checksum

Checksum returns the reviewed source checksum.

func (Recovery) Version

func (recovery Recovery) Version() Version

Version returns the reviewed migration version.

type RecoveryAction

type RecoveryAction uint8

RecoveryAction is an explicit operator assertion about a dirty migration.

const (
	// RecoveryMarkApplied certifies that the migration's full up SQL is present.
	RecoveryMarkApplied RecoveryAction = iota + 1
	// RecoveryMarkRolledBack certifies that all partial effects were removed.
	RecoveryMarkRolledBack
)

type RecoveryResult

type RecoveryResult struct {
	// contains filtered or unexported fields
}

RecoveryResult is the persisted outcome of resolving one dirty migration.

func (RecoveryResult) Action

func (result RecoveryResult) Action() RecoveryAction

Action returns the applied recovery decision.

func (RecoveryResult) Record

func (result RecoveryResult) Record() Record

Record returns the resolved ledger record. MarkRolledBack returns the dirty record that was removed; MarkApplied returns its new clean representation.

type Result

type Result struct {
	// contains filtered or unexported fields
}

Result describes the migrations completed by one runner invocation.

func (Result) Records

func (result Result) Records() []Record

Records returns a copy of completed ledger records.

type Runner

type Runner struct {
	// contains filtered or unexported fields
}

Runner coordinates source validation, exclusive locking, history revalidation, planning, and backend execution.

func NewRunner

func NewRunner(source Source, backend Backend, options ...Option) (*Runner, error)

NewRunner constructs an engine-neutral migration runner.

func (*Runner) Baseline

func (runner *Runner) Baseline(ctx context.Context, baseline Baseline) (record Record, err error)

Baseline validates and records an existing schema without replaying any historical framework migrations. The owned ledger must be empty, and every Go-owned source migration must have a version strictly above the baseline.

func (*Runner) Down

func (runner *Runner) Down(ctx context.Context, count uint64) (result Result, err error)

Down rolls back exactly count applied migrations, newest first. The source and complete ledger history are revalidated while holding the lock.

func (*Runner) Plan

func (runner *Runner) Plan(ctx context.Context) (plan Plan, err error)

Plan returns a locked dry-run plan without executing migration SQL.

func (*Runner) Recover

func (runner *Runner) Recover(
	ctx context.Context,
	recovery Recovery,
) (result RecoveryResult, err error)

Recover resolves one checksum-matched dirty migration after the operator has verified that its effects are either fully applied or fully removed.

func (*Runner) Status

func (runner *Runner) Status(ctx context.Context) (status Status, err error)

Status returns a locked, internally consistent source and ledger snapshot.

func (*Runner) Up

func (runner *Runner) Up(ctx context.Context) (result Result, err error)

Up applies all pending migrations. Source and ledger history are revalidated while holding the lock, eliminating plan-to-execution races.

type Session

type Session interface {
	Prepare(context.Context) error
	Records(context.Context) ([]Record, error)
	Apply(context.Context, Migration) (Record, error)
	Rollback(context.Context, Migration) (Record, error)
	Release(context.Context) error
}

Session represents exclusive migration-job ownership bound to one physical database connection. Ledger preparation, reads, and execution all happen through this value so connection loss and lock loss cannot be separated from execution and a one-connection pool cannot deadlock during preparation.

type Source

type Source interface {
	Load(context.Context) ([]Migration, error)
}

Source loads a complete immutable migration history.

type State

type State uint8

State is the deterministic relationship between source and owned ledger.

const (
	// StateBaseline is the reviewed adoption boundary for an existing schema.
	StateBaseline State = iota + 1
	// StateApplied is a clean, checksum-matched applied migration.
	StateApplied
	// StateDirty is an explicitly unresolved partial or uncertain outcome.
	StateDirty
	// StatePending is a source migration not yet represented in the ledger.
	StatePending
)

type Status

type Status struct {
	// contains filtered or unexported fields
}

Status is an immutable, version-ordered snapshot.

func BuildStatus

func BuildStatus(available []Migration, records []Record) (Status, error)

BuildStatus validates source and persisted identity while preserving dirty entries as explicit status instead of hiding them behind a generic error.

func (Status) Entries

func (status Status) Entries() []StatusEntry

Entries returns a copy so callers cannot mutate the validated snapshot.

type StatusEntry

type StatusEntry struct {
	// contains filtered or unexported fields
}

StatusEntry is one immutable source or ledger status item.

func (StatusEntry) AppliedAt

func (entry StatusEntry) AppliedAt() time.Time

AppliedAt returns the persisted completion time, or zero for pending work.

func (StatusEntry) Checksum

func (entry StatusEntry) Checksum() Checksum

Checksum returns the source checksum or persisted baseline fingerprint.

func (StatusEntry) Duration

func (entry StatusEntry) Duration() time.Duration

Duration returns the persisted execution duration, or zero for pending work.

func (StatusEntry) Name

func (entry StatusEntry) Name() string

Name returns the canonical migration or baseline name.

func (StatusEntry) State

func (entry StatusEntry) State() State

State returns the item's deterministic state.

func (StatusEntry) Version

func (entry StatusEntry) Version() Version

Version returns the migration or baseline version.

type Step

type Step struct {
	// contains filtered or unexported fields
}

Step is one immutable action in a deterministic plan.

func (Step) Action

func (step Step) Action() Action

Action returns the step operation.

func (Step) Migration

func (step Step) Migration() Migration

Migration returns the immutable migration for this step.

type TransactionMode

type TransactionMode uint8

TransactionMode controls whether an engine wraps a migration in a database transaction.

const (
	// TransactionModeDefault executes the migration atomically.
	TransactionModeDefault TransactionMode = iota
	// TransactionModeNone permits operations PostgreSQL cannot run in a
	// transaction. A failed migration in this mode requires explicit recovery.
	TransactionModeNone
)

type Version

type Version uint64

Version is the immutable, monotonically increasing migration identifier.

func (Version) String

func (version Version) String() string

String returns the canonical unpadded decimal representation.

Directories

Path Synopsis
Package conformance provides the reusable behavioral test suite every migration backend must pass without depending on its execution engine.
Package conformance provides the reusable behavioral test suite every migration backend must pass without depending on its execution engine.
examples
job command
Command job runs embedded migrations as a dedicated deployment task.
Command job runs embedded migrations as a dedicated deployment task.
internal
goose
Package goose contains the replaceable, hidden Goose execution adapter.
Package goose contains the replaceable, hidden Goose execution adapter.
Package migrationsservice adapts an explicit migrations.Runner to the standard service migrate command.
Package migrationsservice adapts an explicit migrations.Runner to the standard service migrate command.
Package postgres implements the owned PostgreSQL ledger and lock backend.
Package postgres implements the owned PostgreSQL ledger and lock backend.

Jump to

Keyboard shortcuts

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