peng

package module
v0.2.1 Latest Latest
Warning

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

Go to latest
Published: Jun 17, 2026 License: Apache-2.0 Imports: 10 Imported by: 0

README

peng

A Postgres migration + seed tool, with sqlc generating the type-safe glue and an MCP server so LLMs can drive the whole thing.

Status: v1.1.0, Postgres only.

Why

Migration tools usually treat seeds as an afterthought — a bash script, an ad-hoc INSERT, a hand-written Go func. peng makes them first-class:

  • SQL seeds are top-level .sql files under .peng/seeds/ — the peng CLI runs them inline.
  • Go seeds are full Go packages with query.sql (sqlc input), seed.go (your logic, free to call bcrypt, faker, any third-party Go), and query.sql.go (sqlc-generated, committed). peng build compiles them all into a single seed-runner binary.
  • Both kinds run once by default — the bookkeeping row records each successful application; a re-run is a no-op unless you pass --force.
  • The same operations are exposed over MCP with destructive ops gated by confirm: true.

Install

The GitHub release archives ship peng, peng-tui, and bundled Graphviz dot binaries together. Keep them in the same directory so peng tui can find its companion tools.

# Option 1 — install script (prebuilt release, checksum verified)
curl -fsSL https://raw.githubusercontent.com/2kims/peng/main/install.sh | sh

# Optional overrides:
VERSION=v0.1.0 BINDIR=$HOME/.local/bin sh install.sh

# Option 2 — Homebrew
brew install --cask 2kims/tap/peng

# Option 3 — prebuilt archive from GitHub Releases
#   https://github.com/2kims/peng/releases
#   Extract the archive for your platform; place peng, peng-tui, and dot on PATH.

# Option 4 — install from source into a user-writable bin directory
git clone https://github.com/2kims/peng.git
cd peng
make install BINDIR=$HOME/.local/bin

# Option 5 — Go CLI only; does not install peng-tui or bundled dot
go install github.com/2kims/peng/cmd/peng@latest

You also need sqlc on your PATH if you use Go seeds (peng shells out to it for sqlc code generation). For Go seeds you also need the Go toolchain on the host that runs peng build (apply hosts don't need it once the seed-runner is compiled).

Library use (migrations only)

go get github.com/2kims/peng
import (
    "github.com/jackc/pgx/v5/pgxpool"
    "github.com/2kims/peng"
    "github.com/2kims/peng/pg"
)

pool, _ := pgxpool.New(ctx, dsn)
p, _ := pg.NewProvider(peng.Config{Dialect: peng.DialectPostgres}, pool)
p.Up(ctx)

peng's root package holds dialect-agnostic types (Config, Migration, MigrationStatus, SeedStatus, …); per-dialect packages (peng/pg, future peng/sqlite) hold the concrete Provider and store implementations.

Seeds are intentionally a CLI-only concept. If you need data fixtures from inside your own Go program, write SQL directly against the pool — there's no library API for seeds.

See examples/01-library-usage/ and examples/02-embed-server/.

Environment files

Before any command, peng loads .env files in priority order. Each tier is checked in two locations: the project root and .peng/. Project root wins over .peng/ for the same key — .peng/.env* is the "peng-only" tier for keys you don't want leaking to the rest of your stack.

Tier File at project root File inside .peng/
1 (highest) .env.local .peng/.env.local
2 .env.<env> (when --env=<env> or PENG_ENV=<env>) .peng/.env.<env>
3 (lowest) .env .peng/.env

Existing process environment variables are never overwritten by any of these — that's the CI override path. Inside a single tier, the cwd file beats the .peng/ file. Gitignore the .local variants in both locations.

peng --env=staging up    # loads .env.staging + .env.local + .env
PENG_ENV=prod peng mcp

Quick start

# In your project directory
peng init

# Edit .peng/peng.yaml to point at your Postgres
# (init scaffolds it with database_url: ${PENG_DATABASE_URL})
export PENG_DATABASE_URL=postgres://localhost:5432/myapp_dev?sslmode=disable

# Create a migration
peng create init_schema
$EDITOR .peng/migrations/*_init_schema.up.sql
$EDITOR .peng/migrations/*_init_schema.down.sql

# Apply it
peng up

# A SQL seed — single file, no build step
peng seed create cleanup_test_users
$EDITOR .peng/seeds/*_cleanup_test_users.sql
peng seed apply cleanup_test_users

# A Go seed — package with sqlc-generated queries + custom Go logic
peng seed create demo_users --go
$EDITOR .peng/seeds/*_demo_users/query.sql
$EDITOR .peng/seeds/*_demo_users/seed.go
peng generate           # regenerate query.sql.go via sqlc
peng build              # compile the seed-runner
peng seed apply demo_users
peng status
peng seed status

See examples/03-saas/ for a fully working project with a bcrypt-seeded admin user.

Directory layout in your project

After peng init:

your-project/
└─ .peng/
   ├─ peng.yaml                    # config
   ├─ go.mod                         # module peng-cli — pins pgx for the seed-runner
   ├─ .gitignore                     # hides bin/ and the generated dispatcher
   ├─ sqlc.yaml                      # generated; only present once a Go seed exists
   ├─ migrations/
   │   ├─ <ver>_<name>.up.sql
   │   └─ <ver>_<name>.down.sql
   └─ seeds/
       ├─ <ver>_<name>.sql           # SQL seed: a single top-level file
       └─ <ver>_<name>/              # Go seed: a Go package
           ├─ query.sql              # you edit (sqlc input)
           ├─ seed.go                # you edit; Run(ctx, *pgx.Conn)
           ├─ query.sql.go           # sqlc-generated, committed
           ├─ db.go                  # sqlc-generated, committed
           └─ models.go              # sqlc-generated, committed

.peng/ is its own self-contained Go module (module peng-cli); it exists so peng build has somewhere to compile the seed-runner against pinned dependencies. Your application's go.mod doesn't depend on peng or on .peng/.

CLI

Command Purpose
peng init Scaffold .peng/
peng create <name> New .up.sql/.down.sql migration pair
peng up Apply all pending migrations
peng down Revert the most recent migration
peng redo Down then up
peng reset Revert every applied migration
peng status Show migration state (text or --json)
peng drift Detect checksum / orphan / out-of-order drift
peng schema dump Snapshot the current schema to peng/schema.sql
peng schema apply Apply a snapshot to a fresh database, then up
peng schema diff Diff the live database against cumulative migrations
peng schema preview Show what pending migrations would change (live before/after)
peng squash plan Preview a schema-dump migration squash
peng squash create Create one squashed baseline and archive old migrations
peng squash mark-applied Replace DB bookkeeping with the squashed baseline checksum
peng query list List available named queries in .peng/queries/
peng query <name> Run a named query (--param=value flags for @params)
peng seed create <name> Scaffold a SQL seed (--go for a Go seed package)
peng seed apply <name> Run a seed (--force to re-run, --all for everything)
peng seed status Show seed state
peng build Compile the seed-runner binary (--target os/arch for cross-compile)
peng generate Regenerate sqlc.yaml and query.sql.go
peng watch Regenerate on file change
peng validate Run sqlc compile against .peng/sqlc.yaml
peng mcp Serve the MCP stdio protocol
peng tui Visualize the schema (TUI if peng-tui installed)
peng version Print version

MCP

peng mcp exposes 12 tools over stdio. Wire it into your MCP client:

{
  "mcpServers": {
    "myapp-db": {
      "command": "peng",
      "args": ["mcp"]
    }
  }
}

The tools:

Tool Notes
migrate_status, migrate_version, seed_status Read-only
migrate_up, migrate_create, seed_apply, seed_create, generate, migrate_validate Mutating but not destructive
migrate_down, migrate_redo, migrate_reset Destructive — require confirm: true

All results are JSON-encoded; the destructive gate is enforced server-side.

Architecture (one paragraph)

pg.Provider is the instance-scoped entry point with functional options. A Store interface (Postgres impl backed by sqlc-generated CRUD) handles the peng_migrations and peng_seeds tables. A SessionLocker (Postgres pg_advisory_lock) serializes concurrent invocations against the same database. Each migration runs in its own transaction wrapping the user SQL + the bookkeeping insert — partial application is impossible. SQL seeds use the same pattern. Go seeds run in a subprocess (the seed-runner compiled by peng build) that opens its own pgx connection from PENG_DSN; peng records the bookkeeping row only after the subprocess exits cleanly. The CLI and the MCP server are thin wrappers on top of the Provider.

Testing

make test               # unit tests
make test-integration   # spins up Postgres in Docker via testcontainers

Integration tests require Docker. On Colima, the Makefile sets TESTCONTAINERS_RYUK_DISABLED=true so testcontainers doesn't try to mount the Docker socket.

Goose

peng's Provider/Store/Dialect seams come from pressly/goose — that's a great architecture for a migration tool. peng diverges by dropping the legacy global API, generating per-seed sqlc code, requiring separate up/down files instead of in-file directives, treating seeds as a first-class concept with both SQL and Go shapes, and shipping the MCP server.

License

Apache 2.0 — see LICENSE and NOTICE.

Documentation

Overview

Package peng is a sqlc-powered database migration and seeding tool.

Peng provides SQL migrations with separate up/down files plus two shapes of seeds: top-level SQL files that the CLI runs inline, and Go packages compiled into a seed-runner binary by `peng build`. It also exposes its operations over MCP so language-model agents can drive migrations and seeds.

All peng-managed files live under a self-contained .peng/ directory in a user project:

.peng/peng.yaml                                  config (dialect, DSN)
.peng/go.mod                                       module peng-cli
.peng/migrations/<version>_<name>.up.sql
.peng/migrations/<version>_<name>.down.sql
.peng/seeds/<version>_<name>.sql                   SQL seed
.peng/seeds/<version>_<name>/seed.go               Go seed entry point
.peng/seeds/<version>_<name>/query.sql             sqlc input
.peng/seeds/<version>_<name>/query.sql.go          sqlc-generated
.peng/bin/seed-runner                              built by `peng build`

.peng/ is a separate Go module from the parent project — the parent never imports from it, so nothing breaks if you rename the parent's module path.

Index

Constants

View Source
const (
	DefaultMigrationsDir = ".peng/migrations"
	DefaultSeedsDir      = ".peng/seeds"
	DefaultQueriesDir    = ".peng/queries"
	// DefaultBinDir is where `peng build` writes the compiled
	// seed-runner binary that `peng seed apply` exec()s.
	DefaultBinDir = ".peng/bin"
)

Default paths used when Config fields are zero. The bookkeeping table names (peng_migrations, peng_seeds) are fixed in v1.

The .peng/ directory is dot-prefixed because it is a self-contained Go module (own go.mod) consumed by the peng CLI, not by the parent project. Nothing in the parent module needs to import packages from inside .peng/, so Go's "skip dot-prefixed dirs" rule does not bite.

View Source
const (
	// CreateMigrationUpTemplate is the default body written to
	// <version>_<name>.up.sql when no UpSQL override is provided.
	CreateMigrationUpTemplate = "-- Migration: %s (up)\n\n\n"
	// CreateMigrationDownTemplate is the default body written to
	// <version>_<name>.down.sql when no DownSQL override is provided.
	CreateMigrationDownTemplate = "-- Migration: %s (down)\n\n\n"
)
View Source
const (
	DialectPostgres = "postgres"
)

Supported dialects.

View Source
const SchemaWireVersion = 1

SchemaWireVersion is the current Schema.Version emitted by peng. peng-tui checks this and errors if it's higher than what it knows.

View Source
const SeedRunnerBinName = "seed-runner"

SeedRunnerBinName is the unsuffixed seed-runner binary name written by `peng build`. Cross-compile targets append `-<os>-<arch>` so multiple arches can coexist under .peng/bin/.

View Source
const SeedRunnerModule = "peng-cli"

SeedRunnerModule is the `module` line written to .peng/go.mod by `peng init`. The name is intentionally generic — .peng/ is its own world, decoupled from the parent project's module path.

Variables

View Source
var ErrInvalidMigrationName = errors.New("invalid migration name")

ErrInvalidMigrationName is returned when CreateMigration is called with a name that doesn't match MigrationNamePattern.

View Source
var MigrationNamePattern = regexp.MustCompile(`^[a-z][a-z0-9_]*$`)

MigrationNamePattern is the allowed shape of migration names. Names must start with a lowercase letter and contain only lowercase letters, digits, and underscores. Mirrors goose / dbmate conventions and lets the generated filename be safe across filesystems.

Functions

This section is empty.

Types

type AdoptScan

type AdoptScan struct {
	Format     SourceFormat
	Migrations []Migration
	Skipped    []SkippedFile
}

AdoptScan is the result of scanning another tool's migration folder. Migrations is sorted by version ascending and ready to hand to WriteMigrationPair; Skipped explains anything that didn't import.

func ScanSource

func ScanSource(dir string) (*AdoptScan, error)

ScanSource reads a source migration directory, detects which tool produced it, and parses the migrations into peng's Migration shape. It never writes anything. A non-existent dir is an error (unlike CollectMigrations) because adopting a missing folder is always a user mistake worth surfacing.

func (*AdoptScan) Importable

func (s *AdoptScan) Importable() bool

Importable reports whether the scan can be imported wholesale: the format was recognized, at least one migration parsed, and no fatal incompatibility was found. When false, callers fall back to a baseline snapshot.

type ApplySeedOption

type ApplySeedOption func(*ApplySeedOpts)

ApplySeedOption configures one or more seed applications.

func WithForce

func WithForce() ApplySeedOption

WithForce makes ApplySeed / ApplyAllSeeds run a seed even when it has already been recorded as applied.

type ApplySeedOpts

type ApplySeedOpts struct {
	Force bool
}

ApplySeedOpts is the resolved state of zero or more ApplySeedOption values. Dialect packages call ResolveApplySeedOpts to read the resolved options rather than re-implementing the option pattern for each dialect.

func ResolveApplySeedOpts

func ResolveApplySeedOpts(opts []ApplySeedOption) ApplySeedOpts

ResolveApplySeedOpts collapses option functions into a struct. Dialect packages call this on their public ApplySeed methods.

type Column

type Column struct {
	Name     string `json:"name"`
	Type     string `json:"type"`
	Nullable bool   `json:"nullable"`
	Default  string `json:"default,omitempty"`
	IsPK     bool   `json:"is_pk,omitempty"`
	Comment  string `json:"comment,omitempty"`
}

Column is one column of a table or view.

type Config

type Config struct {
	Dialect       string
	MigrationsDir string
	SeedsDir      string
	QueriesDir    string
}

Config holds the static settings for a Provider. It excludes runtime concerns like the *sql.DB connection, which is passed separately to NewProvider.

func (*Config) ApplyDefaults

func (c *Config) ApplyDefaults()

ApplyDefaults fills in default values for unset Config fields. Exported so dialect packages can call it from their constructors.

func (Config) Validate

func (c Config) Validate() error

Validate checks that Dialect is set and supported.

type CreateMigrationOptions

type CreateMigrationOptions struct {
	// UpSQL overrides the empty up.sql template body. Used by the REST
	// API surface to create+populate a migration in one call.
	UpSQL string
	// DownSQL overrides the empty down.sql template body.
	DownSQL string
	// Now lets tests inject a deterministic clock. When nil,
	// time.Now().UTC() is used.
	Now func() time.Time
}

CreateMigrationOptions tunes CreateMigration's behavior.

type DriftIssue

type DriftIssue struct {
	Kind    DriftKind
	Version string
	Name    string
	Detail  string
}

DriftIssue is one detected drift condition.

type DriftKind

type DriftKind int

DriftKind discriminates the three drift conditions peng detects.

const (
	// DriftChecksumChanged: an applied migration's file has been
	// modified since it was applied.
	DriftChecksumChanged DriftKind = iota
	// DriftOrphaned: an applied row in peng_migrations has no
	// matching .up.sql on disk.
	DriftOrphaned
	// DriftOutOfOrder: a pending migration's version is older than
	// the current head.
	DriftOutOfOrder
)

func (DriftKind) String

func (k DriftKind) String() string

String returns the snake_case name of the drift kind, suitable for JSON output and CLI status flags.

type DriftReport

type DriftReport struct {
	Issues []DriftIssue
}

DriftReport collects every drift issue found in a single check. Issues is empty when no drift is detected.

type Enum

type Enum struct {
	Schema string   `json:"schema"`
	Name   string   `json:"name"`
	Values []string `json:"values"`
}

Enum is an enum type.

type FnArg

type FnArg struct {
	Name string `json:"name,omitempty"`
	Type string `json:"type"`
}

FnArg is one parameter of a function.

type ForeignKey

type ForeignKey struct {
	Name        string   `json:"name"`
	FromTable   string   `json:"from_table"` // qualified: "schema.name"
	FromColumns []string `json:"from_columns"`
	ToTable     string   `json:"to_table"` // qualified
	ToColumns   []string `json:"to_columns"`
	OnDelete    string   `json:"on_delete,omitempty"` // CASCADE / SET NULL / NO ACTION / RESTRICT
	OnUpdate    string   `json:"on_update,omitempty"`
}

ForeignKey is one referential constraint.

type Function

type Function struct {
	Schema string  `json:"schema"`
	Name   string  `json:"name"`
	Args   []FnArg `json:"args,omitempty"`
	// Signature is the raw argument list as the engine displays it
	// (e.g. "order_id bigint, currency text DEFAULT 'USD'") — used
	// when Args isn't populated structurally. Display-only.
	Signature string `json:"signature,omitempty"`
	Returns   string `json:"returns,omitempty"` // empty for procedures
	Body      string `json:"body,omitempty"`
}

Function is a stored function or procedure.

type Index

type Index struct {
	Name    string   `json:"name"`
	Columns []string `json:"columns"`
	Unique  bool     `json:"unique,omitempty"`
	Method  string   `json:"method,omitempty"` // e.g. "btree", "gin"
}

Index is a non-PK index. PRIMARY KEY constraints are reported via Table.PrimaryKey, not duplicated here.

type Migration

type Migration struct {
	Version  string
	Name     string
	UpSQL    string
	DownSQL  string
	UpPath   string
	DownPath string
}

Migration is a single pairing of an up.sql and down.sql file under the migrations directory. SQL bodies are loaded lazily by the collector; the paths are retained for diagnostic messages.

func CollectMigrations

func CollectMigrations(dir string) ([]Migration, error)

CollectMigrations walks dir for files matching <version>_<name>.(up|down).sql, pairs them by stem, errors if either half of a pair is missing, and returns the loaded migrations sorted by version ascending. A non-existent dir returns an empty slice with no error so callers can treat it as "no migrations yet" without a filesystem precheck.

func CreateMigration

func CreateMigration(dir, name string, opts *CreateMigrationOptions) (Migration, error)

CreateMigration writes a timestamped migration pair under dir and returns the resulting Migration (with paths + bodies populated).

The version is the current UTC time formatted as "20060102150405". If the down.sql write fails after up.sql succeeded, up.sql is removed best-effort so the caller doesn't see a partial pair on disk.

Hoisted from cli/create.go so the REST/IPC server can expose `POST /migrations` without shelling out to the CLI binary.

func WriteMigrationPair

func WriteMigrationPair(dir string, m Migration) (Migration, error)

WriteMigrationPair writes m as <version>_<name>.up.sql and .down.sql under dir, preserving m.Version (unlike CreateMigration, which stamps a fresh timestamp). It returns m with UpPath/DownPath populated. If the down write fails after the up succeeded, the up file is removed best-effort so no half-pair is left on disk.

type MigrationRecord

type MigrationRecord struct {
	Version    string
	Name       string
	AppliedAt  time.Time
	DurationMs int64
	// Checksum is the SHA-256 (hex) of the up.sql content at the
	// time the migration was applied. Empty for migrations applied
	// by versions of peng before checksums were tracked.
	Checksum string
}

MigrationRecord is one row in a dialect's peng_migrations table. Dialect Store implementations construct and return these.

type MigrationStatus

type MigrationStatus struct {
	Migration  Migration
	Applied    bool
	AppliedAt  time.Time
	DurationMs int64

	// AppliedChecksum is the SHA-256 (hex) recorded at apply time.
	// Empty when Applied is false or when the migration predates
	// checksum tracking.
	AppliedChecksum string
	// CurrentChecksum is the SHA-256 (hex) of the current up.sql
	// content on disk.
	CurrentChecksum string
	// ChecksumChanged is true when the migration is applied and the
	// recorded checksum differs from the current file's checksum.
	// Indicates someone edited the migration after it was applied.
	ChecksumChanged bool
	// OutOfOrder is true when the migration is pending and its
	// version is older than the current head (the largest applied
	// version). Indicates a rebase or branch-merge-out-of-order
	// situation.
	OutOfOrder bool
}

MigrationStatus combines a Migration with its applied state. The fields are dialect-agnostic; each dialect's Provider returns the same struct shape from its Status method.

type OutOfOrderPolicy

type OutOfOrderPolicy int

OutOfOrderPolicy controls what Provider.Up does when it discovers a pending migration whose version is older than the current head.

const (
	// OutOfOrderError (default): Up errors before applying any
	// out-of-order pendings.
	OutOfOrderError OutOfOrderPolicy = iota
	// OutOfOrderWarn: Up logs a warning and applies the migration
	// anyway. The peng_migrations row records applied_at = now()
	// so the audit trail captures the apply.
	OutOfOrderWarn
)

type QueryDef

type QueryDef struct {
	Name        string
	Description string
	Params      []QueryParam
	SQL         string // raw SQL with @param references
}

QueryDef is a parsed .peng/queries/<name>.sql file.

func DiscoverQueries

func DiscoverQueries(dir string) ([]*QueryDef, error)

DiscoverQueries walks dir for *.sql files and parses each one. Returns them sorted alphabetically by name. A non-existent dir returns an empty slice with no error.

func ParseQueryFile

func ParseQueryFile(path string) (*QueryDef, error)

ParseQueryFile reads a .peng/queries/<name>.sql file and returns the parsed QueryDef.

File format:

-- description: one-line summary
-- @param_name  type  Human-readable description
-- @other_param text  Another param

SELECT ... WHERE col = @param_name AND other = @other_param;

The header block is any leading run of comment lines. The first blank line ends the header; everything after it is the SQL body.

func (*QueryDef) BuildSQL

func (qd *QueryDef) BuildSQL(values map[string]string) (sql string, args []any, err error)

BuildSQL substitutes @param_name references in the SQL with $1, $2, ... positional parameters in the order they appear in Params, and returns the substituted SQL and the corresponding argument slice. Returns an error if a required param is missing from values.

type QueryParam

type QueryParam struct {
	Name        string
	Type        string // optional type hint (e.g. "uuid", "text")
	Description string
}

QueryParam is one @name entry documented in a query file's header.

type QueryResult

type QueryResult struct {
	Columns []string
	Rows    [][]string
}

QueryResult holds the output rows from RunQuery.

type Schema

type Schema struct {
	// Version of the wire format. Bumped on breaking shape changes;
	// additive fields keep the same version.
	Version int `json:"version"`
	// Dialect identifies the engine (postgres, sqlite, ...).
	Dialect string `json:"dialect"`

	Tables      []Table      `json:"tables"`
	Views       []View       `json:"views,omitempty"`
	Functions   []Function   `json:"functions,omitempty"`
	Enums       []Enum       `json:"enums,omitempty"`
	ForeignKeys []ForeignKey `json:"foreign_keys"`
}

Schema is the dialect-agnostic structural model of a database used by `peng tui` and by peng-tui over the JSON wire protocol. Each dialect implements its own SchemaModel(ctx) method returning this shape.

Phase 0 populates Tables, ForeignKeys. Views, Functions, Enums, and per-Index detail can be filled in later — peng-tui tolerates empty slices.

type SeedRecord

type SeedRecord struct {
	Version    string
	Name       string
	AppliedAt  time.Time
	DurationMs int64
}

SeedRecord is one row in a dialect's peng_seeds table.

type SeedRunResult

type SeedRunResult struct {
	Version    string
	Name       string
	Applied    bool
	DurationMs int64
}

SeedRunResult describes the outcome of an ApplySeed call. Applied is false when a previously-applied seed was skipped (no WithForce).

type SeedStatus

type SeedStatus struct {
	Version    string
	Name       string
	Applied    bool
	AppliedAt  time.Time
	DurationMs int64
}

SeedStatus combines a registered seed's identity (version + name) with its applied state. The seed's Run function is dialect-specific and lives in the dialect package, so it's not exposed here.

type SkippedFile

type SkippedFile struct {
	Path   string
	Reason string
	Fatal  bool
}

SkippedFile records a source file ScanSource could not import cleanly. Fatal marks an incompatibility that makes the whole folder un-importable (the caller should fall back to a schema-dump baseline); non-fatal entries are advisory — e.g. a synthesized empty down for a golang-migrate migration that shipped without one.

type Snapshot

type Snapshot struct {
	// Dialect identifies which database engine produced this snapshot.
	// SchemaApply refuses cross-dialect application.
	Dialect string
	// HeadVersion is the largest version in peng_migrations at
	// dump time. SchemaApply backfills bookkeeping rows for every
	// migration with version <= HeadVersion.
	HeadVersion string
	// DumpedAt is the wall-clock time the snapshot was produced.
	DumpedAt time.Time
	// DBVersion is the source server version string, captured for
	// diagnostic value.
	DBVersion string
	// SQL is the schema-only DDL body. peng bookkeeping tables
	// (peng_migrations, peng_seeds) are NOT included — the
	// snapshot is the user schema only.
	SQL string
}

Snapshot is a point-in-time schema dump produced by `peng schema dump`. Applying a snapshot fast-forwards a fresh database to the state it had when the snapshot was taken, then backfills peng_migrations rows for every migration up through HeadVersion so subsequent `peng up` runs only apply post-snapshot migrations.

Snapshots are NOT migrations. They are a separate concept that preserves the immutability invariant migrations rely on (see #5 drift detection — migrations are write-once because their checksums are tracked). Snapshots may be regenerated at any time.

func ReadSnapshot

func ReadSnapshot(path string) (*Snapshot, error)

ReadSnapshot loads a snapshot from path, parsing the peng: header. Returns an error if the required headers (dialect, head_version) are missing.

func (*Snapshot) WriteTo

func (s *Snapshot) WriteTo(path string) error

WriteTo writes the snapshot to path: header + SQL body.

type SourceFormat

type SourceFormat int

SourceFormat identifies the migration tool that produced a folder scanned by ScanSource.

const (
	// FormatUnknown means ScanSource could not recognize the folder as
	// any supported tool's migration layout.
	FormatUnknown SourceFormat = iota
	// FormatGolangMigrate is golang-migrate's layout:
	// <version>_<name>.up.sql + <version>_<name>.down.sql. Identical to
	// peng's own format, so import is a near-verbatim copy.
	FormatGolangMigrate
	// FormatGoose is goose's layout: a single <version>_<name>.sql file
	// per migration with `-- +goose Up` / `-- +goose Down` sections (or
	// a Go-based <version>_<name>.go file, which can't be translated).
	FormatGoose
)

func (SourceFormat) String

func (f SourceFormat) String() string

String returns the tool name, suitable for CLI output.

type Table

type Table struct {
	Schema     string   `json:"schema"`
	Name       string   `json:"name"`
	Columns    []Column `json:"columns"`
	PrimaryKey []string `json:"primary_key,omitempty"`
	Indexes    []Index  `json:"indexes,omitempty"`
	Comment    string   `json:"comment,omitempty"`
}

Table is one base table.

func (Table) Qualified

func (t Table) Qualified() string

Qualified returns "schema.name" — handy for diagram labels and foreign-key references.

type View

type View struct {
	Schema     string `json:"schema"`
	Name       string `json:"name"`
	Definition string `json:"definition"`
}

View is a stored view definition.

Directories

Path Synopsis
Package cli implements the peng command-line interface.
Package cli implements the peng command-line interface.
cmd
peng command
Command peng is the peng CLI entry point.
Command peng is the peng CLI entry point.
Package eventbus is a minimal in-process publish/subscribe channel for server-side change events (migration_added, schema_changed, etc.).
Package eventbus is a minimal in-process publish/subscribe channel for server-side change events (migration_added, schema_changed, etc.).
Package generator handles code generation for peng-managed artifacts: the per-seed sqlc.yaml that drives query.sql.go generation, the sqlc subprocess invocation, and (in a later milestone) the auto-generated seed registry.go.
Package generator handles code generation for peng-managed artifacts: the per-seed sqlc.yaml that drives query.sql.go generation, the sqlc subprocess invocation, and (in a later milestone) the auto-generated seed registry.go.
internal
modpath
Package modpath locates the nearest go.mod above a directory and extracts its module declaration.
Package modpath locates the nearest go.mod above a directory and extracts its module declaration.
queries/postgres
Package postgres implements peng's store.Store interface for PostgreSQL, backed by sqlc-generated query code.
Package postgres implements peng's store.Store interface for PostgreSQL, backed by sqlc-generated query code.
Package pg provides peng's PostgreSQL dialect.
Package pg provides peng's PostgreSQL dialect.
Package server is peng's transport-agnostic request mux.
Package server is peng's transport-agnostic request mux.
handlers
Package handlers wires up route registrations for the peng server.
Package handlers wires up route registrations for the peng server.
transport
httpx
Package httpx wraps a server.Mux as an http.Handler.
Package httpx wraps a server.Mux as an http.Handler.
ipc
Package ipc implements peng's framed JSON-RPC transport for embedding hosts like peng-tui.
Package ipc implements peng's framed JSON-RPC transport for embedding hosts like peng-tui.

Jump to

Keyboard shortcuts

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