Documentation
¶
Index ¶
- Constants
- Variables
- func CheckChain(names []string) error
- func CheckChainFS(fsys fs.FS, dir string, requireLinks bool) error
- func CheckRepairTotality(fsys fs.FS, dir string) error
- func ContentDigest(content string) string
- func DetectCI() (string, bool)
- func Prefix(name string) string
- func RepairTotalityFindings(filePath, body string) []string
- func ValidatePostgresMigrations(ctx context.Context, db *sql.DB, sources ...MigrationSource) error
- func VerifyChain(migrations []Migration, opts ...LoadOption) error
- type AppliedRecord
- type Constraint
- type ConstraintKind
- type Discrepancy
- type DiscrepancyKind
- type LoadOption
- type Migration
- type MigrationSource
- type ParentLink
- type Postgres
- func (p *Postgres) Applied(ctx context.Context) ([]string, error)
- func (p *Postgres) AppliedRecords(ctx context.Context) (map[string]AppliedRecord, error)
- func (p *Postgres) ApplyMigrations(ctx context.Context, migrations []Migration) error
- func (p *Postgres) ApplyWithOrderingException(ctx context.Context, migrations []Migration, allowBelow []string, ...) error
- func (p *Postgres) RepairAcceptContent(ctx context.Context, m Migration, req RepairRequest) (RepairResult, error)
- func (p *Postgres) RepairAdopt(ctx context.Context, m Migration, req RepairRequest) (RepairResult, error)
- func (p *Postgres) RepairAdoptAllUnmatched(ctx context.Context, migrations []Migration, req RepairRequest) ([]RepairResult, error)
- func (p *Postgres) RepairHistory(ctx context.Context) ([]RepairRecord, error)
- func (p *Postgres) RepairResolve(ctx context.Context, m Migration, mode ResolveMode, req RepairRequest) (RepairResult, error)
- func (p *Postgres) Setup(ctx context.Context) error
- func (p *Postgres) Status(ctx context.Context, migrations []Migration) (Status, error)
- func (p *Postgres) ValidateAllApplied(ctx context.Context, migrations []Migration) error
- func (p *Postgres) WithSchema(schema string, rewriteFrom ...string) *Postgres
- func (p *Postgres) WithStrictContent() *Postgres
- func (p *Postgres) WithStrictOrdering() *Postgres
- func (p *Postgres) WithWarnFunc(fn func(Discrepancy)) *Postgres
- type RelinkChange
- type RelinkOptions
- type RepairRecord
- type RepairRequest
- type RepairResult
- type ResolveMode
- type Severity
- type Status
Constants ¶
const ( StatusApplied = "applied" StatusRunning = "running" StatusFailed = "failed" )
Ledger statuses. An EMPTY status is 'applied': every row written before this existed, and every transactional insert since, is complete by construction.
Variables ¶
var ErrNothingToRepair = errors.New("migratekit: nothing to repair")
ErrNothingToRepair is returned when the ledger already matches the tree.
var ErrRepairInCI = errors.New("migratekit: repairs are operator-runtime verbs and refuse to run in CI")
ErrRepairInCI is returned when a repair verb is invoked in CI.
Functions ¶
func CheckChain ¶ added in v1.0.1
CheckChain validates a migration chain as a FILE LISTING — no database. It reports duplicate numbers, gaps and non-monotonic numbering, which is what a CI gate on the merge boundary needs. names may be in any order.
func CheckChainFS ¶ added in v1.0.1
CheckChainFS is the CI gate on a migration directory: the numbering rules of CheckChain plus parent-link validation, which needs the bytes and not just the names. requireLinks refuses a headerless migration.
func CheckRepairTotality ¶ added in v1.0.1
CheckRepairTotality lints every *.up.sql under dir. Pure file analysis — no database, no configuration, no deployment names. It belongs on the merge boundary beside CheckChain: a database that has already applied a migration cannot be helped by discovering it lacked a repair. If dir is empty it defaults to "." (root of the filesystem).
func ContentDigest ¶ added in v1.0.1
ContentDigest is the ledger's digest of a migration's CANONICAL BODY — the file with its own `-- parent:` header removed (see chain.go). A headerless file hashes to exactly what v1.5.0 recorded, so adding a parent line to an already-applied migration changes no ledger digest.
func DetectCI ¶ added in v1.0.1
DetectCI reports the name of the CI environment variable that is set, if any. An empty-string value does not count — some shells export CI="".
func Prefix ¶
Prefix extracts numeric prefix from migration filenames and normalizes it. Supports both underscore and hyphen separators. Examples:
"001_create_users.up.sql" -> "1" "1-create-users.up.sql" -> "1" "0042_add_field.up.sql" -> "42"
func RepairTotalityFindings ¶ added in v1.0.1
RepairTotalityFindings returns the lint's complaints about one migration, empty when it is clean. Every message names the statement AND both ways to satisfy it, because a gate whose output does not say what to write is a gate people route around.
func ValidatePostgresMigrations ¶
ValidatePostgresMigrations validates multiple Postgres migration sources at once. Returns an error if any migrations are pending.
func VerifyChain ¶ added in v1.0.1
func VerifyChain(migrations []Migration, opts ...LoadOption) error
VerifyChain validates the parent links of migrations, which must be in apply order (as returned by loadFiles). It reports the FIRST violation, naming both files involved, because a chain error is always about a relationship.
Types ¶
type AppliedRecord ¶ added in v1.0.1
type AppliedRecord struct {
// Key is the ledger key — Prefix(filename).
Key string
// Filename is the full migration filename that claimed Key. Empty for
// rows written before v1.5.0.
Filename string
// Digest is the sha256 of the applied migration's content. Empty for
// rows written before v1.5.0.
Digest string
// Status is the apply state: applied, running or failed. Empty for rows
// written before v1.7.0, which are applied by construction — only a
// no-transaction migration can be anything else.
Status string
// Error is the recorded cause of a failed no-transaction apply.
Error string
}
AppliedRecord is one row of the applied-migrations ledger.
type Constraint ¶ added in v1.0.1
type Constraint struct {
Line int
Kind ConstraintKind
Table string
Name string
}
Constraint is one detected statement that must hold over rows the migration did not write. Table is empty when the lexical scan could not resolve it — that is the UNKNOWN case, and it is reported rather than dropped.
type ConstraintKind ¶ added in v1.0.1
type ConstraintKind string
ConstraintKind classifies WHY a statement can refuse rows the migration did not write.
const ( ConstraintCheck ConstraintKind = "add-check" // ADD CONSTRAINT ... CHECK, validating ConstraintValidate ConstraintKind = "validate-constraint" // VALIDATE CONSTRAINT <name> ConstraintForeignKey ConstraintKind = "add-foreign-key" // ADD CONSTRAINT ... FOREIGN KEY, validating ConstraintUnique ConstraintKind = "add-unique" // ADD CONSTRAINT ... UNIQUE / PRIMARY KEY ConstraintUniqueIndex ConstraintKind = "create-unique-index" // CREATE UNIQUE INDEX on a pre-existing table ConstraintNotNull ConstraintKind = "set-not-null" // ALTER COLUMN ... SET NOT NULL ConstraintColumnType ConstraintKind = "alter-column-type" // ALTER COLUMN ... TYPE — the cast can fail ConstraintNotNullCol ConstraintKind = "add-not-null-column" // ADD COLUMN ... NOT NULL with no DEFAULT )
type Discrepancy ¶ added in v1.0.1
type Discrepancy struct {
Kind DiscrepancyKind
Severity Severity
// Key is the ledger key (the bare migration number).
Key string
// File is the migration file in the tree. Empty for KindLedgerOnly.
File string
// LedgerFilename is the filename the ledger recorded, if any.
LedgerFilename string
// LedgerDigest is the content digest the ledger recorded, if any.
LedgerDigest string
// FileDigest is the digest of the file in the tree, if there is one.
FileDigest string
// Blocker is the already-applied migration a KindOrderViolation sorts
// below.
Blocker string
// LedgerStatus is the recorded apply state for a KindDirtyMigration.
LedgerStatus string
// LedgerError is the recorded failure cause for a KindDirtyMigration.
LedgerError string
}
Discrepancy is one disagreement between the migration files and the ledger, with everything an operator needs to resolve it.
func (Discrepancy) Headline ¶ added in v1.0.1
func (d Discrepancy) Headline() string
Headline is the one-sentence WHAT.
func (Discrepancy) OneLine ¶ added in v1.0.1
func (d Discrepancy) OneLine() string
OneLine is the whole warning in one line: what, the risk, and the verb that resolves it. This is what a log line has room for.
func (Discrepancy) String ¶ added in v1.0.1
func (d Discrepancy) String() string
String renders the full operator-facing explanation: what, why, and what to do about it, ending with the pointer at `migratekit status`.
type DiscrepancyKind ¶ added in v1.0.1
type DiscrepancyKind string
DiscrepancyKind names a class of ledger/tree disagreement.
const ( // KindNumberMismatch: the ledger says this number was applied by a // different file. Hard error — the file in the tree would never run. KindNumberMismatch DiscrepancyKind = "number_applied_by_different_file" // KindContentDrift: an applied migration's file has been edited since it // ran. Warning by default; error under WithStrictContent. KindContentDrift DiscrepancyKind = "applied_migration_edited" // KindOrderViolation: a pending migration sorts below one already // applied. Error under WithStrictOrdering. KindOrderViolation DiscrepancyKind = "pending_sorts_below_applied" // KindLedgerOnly: a ledger row with no migration file behind it. KindLedgerOnly DiscrepancyKind = "ledger_row_without_file" // KindDirtyMigration: a no-transaction migration that failed or was // interrupted, so it may be partially applied. Always a hard error. KindDirtyMigration DiscrepancyKind = "no_transaction_migration_unfinished" )
type LoadOption ¶ added in v1.0.1
type LoadOption func(*loadConfig)
LoadOption configures Load.
func RequireParentLinks ¶ added in v1.0.1
func RequireParentLinks() LoadOption
RequireParentLinks makes a headerless migration an error instead of a warning. Off by default for one minor version so existing consumers keep booting; adopt it once your chain carries headers.
func WithChainWarnFunc ¶ added in v1.0.1
func WithChainWarnFunc(fn func(string)) LoadOption
WithChainWarnFunc redirects legacy-tolerance warnings. Defaults to the standard logger.
type Migration ¶
Migration is a single SQL migration
func Load ¶ added in v1.0.1
Load reads migrations and verifies the parent-link chain. LoadFromFS is this with default options.
func LoadFromFS ¶
LoadFromFS loads migrations from an embedded filesystem and verifies the parent-link chain with default options (headerless migrations tolerated with a warning). It is Load(fsys, dir) — use Load directly for RequireParentLinks or a custom warning sink. If dir is empty, defaults to "." (root of the filesystem).
type MigrationSource ¶
type MigrationSource struct {
App string
FS fs.FS
// Schema optionally mirrors Postgres.WithSchema for validation helpers.
// RewriteFrom carries canonical schema names to rewrite when applying via
// Postgres.WithSchema(schema, rewriteFrom...).
Schema string
RewriteFrom []string
}
MigrationSource represents a migration source with an app name and a migration filesystem (any fs.FS; embed.FS satisfies it).
type ParentLink ¶ added in v1.0.1
type ParentLink struct {
// Present is false for a headerless (legacy) migration.
Present bool
// IsRoot is true for `-- parent: root`.
IsRoot bool
// Parent is the normalized ledger key of the parent migration.
Parent string
// Digest is the claimed sha256 of the parent's canonical body.
Digest string
}
ParentLink is the parsed `-- parent:` header of a migration.
type Postgres ¶
type Postgres struct {
// contains filtered or unexported fields
}
Postgres handles PostgreSQL migrations
func NewPostgres ¶
NewPostgres creates a Postgres migrator
func (*Postgres) AppliedRecords ¶ added in v1.0.1
AppliedRecords returns the applied-migrations ledger keyed by ledger key.
func (*Postgres) ApplyMigrations ¶
ApplyMigrations applies all unapplied migrations (only locks if needed). Setup() always runs first, so there is no missing-table special case.
func (*Postgres) ApplyWithOrderingException ¶ added in v1.0.1
func (p *Postgres) ApplyWithOrderingException(ctx context.Context, migrations []Migration, allowBelow []string, req RepairRequest) error
ApplyWithOrderingException applies the chain while exempting the named migrations from the strict-ordering rule — the one-shot escape for a genuine backport that must land below the high-water mark. allowBelow entries may be bare numbers or filenames. Every exemption that actually applies a migration records an audit row.
The identity checks are NOT relaxed: this exempts ordering only.
func (*Postgres) RepairAcceptContent ¶ added in v1.0.1
func (p *Postgres) RepairAcceptContent(ctx context.Context, m Migration, req RepairRequest) (RepairResult, error)
RepairAcceptContent re-stamps the recorded digest of an applied migration after a verified edit. It is the "I looked at the diff and it is cosmetic" verb: it acknowledges the content-drift warning and silences it, on the record. It refuses when the ledger names a DIFFERENT file, because that is an identity problem and `repair adopt` is the verb that says so.
func (*Postgres) RepairAdopt ¶ added in v1.0.1
func (p *Postgres) RepairAdopt(ctx context.Context, m Migration, req RepairRequest) (RepairResult, error)
RepairAdopt binds the file in the tree as the applied identity for its ledger key: filename and content digest both become this file's.
This is the verb for a ledger that is the WRONG SIDE — a restored backup, a database adopted from somewhere else, a row somebody fixed by hand. The files are right and the ledger remembers a tree that no longer exists. It is NOT the verb for a lane collision: if two files genuinely claim one number, adopting one of them silently drops the other's DDL, which is exactly the failure this package exists to prevent. Read the diff first.
func (*Postgres) RepairAdoptAllUnmatched ¶ added in v1.0.1
func (p *Postgres) RepairAdoptAllUnmatched(ctx context.Context, migrations []Migration, req RepairRequest) ([]RepairResult, error)
RepairAdoptAllUnmatched adopts EVERY ledger row whose identity disagrees with the file in the tree that carries its number. This is the restored- backup shape: one dump, one restore, and every row mismatches at once. Rows that already match, and rows with no file behind them, are left alone.
func (*Postgres) RepairHistory ¶ added in v1.0.1
func (p *Postgres) RepairHistory(ctx context.Context) ([]RepairRecord, error)
RepairHistory returns this app's repair audit trail, newest first.
func (*Postgres) RepairResolve ¶ added in v1.0.1
func (p *Postgres) RepairResolve(ctx context.Context, m Migration, mode ResolveMode, req RepairRequest) (RepairResult, error)
RepairResolve clears a dirty ledger row. Like every repair verb it requires a reason, refuses to run in CI, writes an audit row in the same transaction as the change, and never touches a schema — resolving is the operator's statement about work they did by hand, not a re-run of the DDL.
func (*Postgres) Status ¶ added in v1.0.1
Status reports the ledger, the pending set, every discrepancy and the repair history for this app. It is read-only apart from ensuring the tracking tables exist, and it never fails on a discrepancy — reporting one is the whole point.
func (*Postgres) ValidateAllApplied ¶
ValidateAllApplied checks if all provided migrations have been applied. Returns an error listing any pending migrations if validation fails. This is intended for use during application startup to ensure the database schema is up-to-date before the app starts serving requests.
func (*Postgres) WithSchema ¶
WithSchema configures the schema to target for migrations.
Unqualified DDL/DML runs under:
SET LOCAL search_path = "<schema>", public
Passing one or more canonical schema names also rewrites those schema references in migration SQL before execution. This lets apps author portable hard-qualified DDL against a default schema and relocate it at runtime:
migratekit.NewPostgres(db, "app").WithSchema(cfg.Schema, "openrails")
Tracking always stays in public.migrations.
func (*Postgres) WithStrictContent ¶ added in v1.6.0
WithStrictContent turns content drift — an applied migration whose file has been edited since it ran — back into a hard error. The default is a warning (see the integrity note above); this is for consumers whose chain must be byte-reproducible and who would rather not boot than diverge.
func (*Postgres) WithStrictOrdering ¶ added in v1.0.1
WithStrictOrdering refuses to apply a pending migration that sorts below a migration already applied. Off by default; see the ordering note above.
func (*Postgres) WithWarnFunc ¶ added in v1.0.1
func (p *Postgres) WithWarnFunc(fn func(Discrepancy)) *Postgres
WithWarnFunc replaces the sink for warning-severity discrepancies. The default logs them through slog.Default() at warn level. A warning nobody sees is the failure mode this whole package exists to prevent, so the sink is never silent by default — pass func(Discrepancy){} to silence it deliberately.
type RelinkChange ¶ added in v1.0.1
type RelinkChange struct {
File string
From string // the previous header line, empty when there was none
To string // the header line it now carries
}
RelinkChange is one parent line Relink rewrote (or would rewrite).
func Relink ¶ added in v1.0.1
func Relink(dir string, opts RelinkOptions) ([]RelinkChange, error)
Relink rewrites each migration's parent line to the digest of the file that actually precedes it, and stamps root on the lowest-numbered one. It reads and writes files and nothing else — no database, no context, no DSN.
type RelinkOptions ¶ added in v1.0.1
type RelinkOptions struct {
// DryRun computes the changes without writing them.
DryRun bool
// From limits rewriting to migrations numbered at or above this prefix,
// so a lane can rebase only its own new files.
From string
}
RelinkOptions configures Relink.
type RepairRecord ¶ added in v1.0.1
type RepairRecord struct {
ID int64
App string
Schema string
Key string
Verb string
Reason string
Operator string
OSUser string
Host string
OldFilename string
OldDigest string
NewFilename string
NewDigest string
At time.Time
}
RepairRecord is one row of the repair audit trail.
func (RepairRecord) Line ¶ added in v1.0.1
func (r RepairRecord) Line() string
Line renders one audit row for a terminal.
type RepairRequest ¶ added in v1.0.1
type RepairRequest struct {
// Reason is required and recorded verbatim. "Why" is the only part of a
// repair a future reader cannot reconstruct.
Reason string
// Operator optionally names the human, alongside the OS user the audit
// row records anyway.
Operator string
// DryRun computes and returns the repair without writing anything.
DryRun bool
}
RepairRequest is the operator's authorization for a repair.
type RepairResult ¶ added in v1.0.1
type RepairResult struct {
Verb string
Key string
OldFilename string
OldDigest string
NewFilename string
NewDigest string
DryRun bool
}
RepairResult describes one ledger identity row before and after a repair.
func (RepairResult) String ¶ added in v1.0.1
func (r RepairResult) String() string
type ResolveMode ¶ added in v1.0.1
type ResolveMode string
ResolveMode is how an operator disposes of a migration left dirty by a failed or interrupted no-transaction apply.
const ( // ResolveApplied marks it done: the operator finished or verified the work. ResolveApplied ResolveMode = "applied" // ResolveRerun deletes the row so the next boot runs the migration again. // Only safe when the migration is idempotent. ResolveRerun ResolveMode = "rerun" )
type Status ¶ added in v1.0.1
type Status struct {
App string
Schema string
// Applied is the ledger, in apply order.
Applied []AppliedRecord
// Pending names the migrations that have not run.
Pending []string
// Discrepancies is every disagreement, most severe first.
Discrepancies []Discrepancy
// Repairs is the audit trail, newest first.
Repairs []RepairRecord
}
Status is the whole picture for one app: what is applied, what is pending, what disagrees, and every repair anybody has ever run here.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package chmigrate is migratekit's ClickHouse migration driver.
|
Package chmigrate is migratekit's ClickHouse migration driver. |
|
cmd
|
|
|
migratekit
command
Command migratekit is the operator's side of migration identity: it explains a boot refusal and resolves it.
|
Command migratekit is the operator's side of migration identity: it explains a boot refusal and resolves it. |
|
internal
|
|
|
coremigrate
Package coremigrate holds implementation primitives shared by migratekit's Postgres migrator (the root package) and its ClickHouse migrator (migratekit/chmigrate): the migrations-tracking table DDL and environment template substitution.
|
Package coremigrate holds implementation primitives shared by migratekit's Postgres migrator (the root package) and its ClickHouse migrator (migratekit/chmigrate): the migrations-tracking table DDL and environment template substitution. |