pgdesign v0.26.0 /internal/imports
On this page

Package imports vendors another pgdesign project's table surface and type closure as pinned content-addressed forms, with an offline drift check.

#internal/imports

#internal/imports

Package imports implements cross-repository schema imports (roadmap 7.2): the surface snapshot, pinning, and offline drift check for another pgdesign project's schema referenced via an [imports.] declaration.

The import surface is a sub-model under the kernel's encoder (internal/enc, law L1) and its content-addressed store (internal/objstore, law L2): the referenced tables plus the transitive composition-closure of their type definitions are vendored, each as its canonical per-object form, into a store rooted at imports//. A lockfile (imports//lock.json) pins the git URL, ref, resolved commit, per-object keys+ids, and two hashes:

- SurfaceHash — hash of the sorted per-object content ids. Integrity: it changes iff the vendored bytes change. Stable across re-lock of the same commit (identical bytes -> identical ids). - SemanticHash — hash of the sorted N-normalized per-object forms. Drift: it is INVARIANT under equivalently-spelled defaults (N folds them), so it does not false-drift where SurfaceHash (raw ids) would. Roadmap 1.2's N dependency made explicit.

This package is pure aside from git plumbing (git.go) and the filesystem: it depends on config, parse, model, enc, objstore, sqlparse, typeinfo. It never touches a database — offline builds and the drift check never need the remote.

#LockfileName

Go go
const LockfileName = "lock.json"

LockfileName is the per-alias lockfile, co-located with the vendored objects under the alias's store root (imports//lock.json). Per-alias placement (rather than one aggregate pgdesign.lock) keeps each import self-contained: the store root and its pin travel together, and import update <alias> writes exactly one directory. Visible (non-dot) name per the roadmap's [%%] committed-load-bearing-data convention.

#ObjectEntry

Go go
type ObjectEntry struct

ObjectEntry pins one vendored surface object: its kind-qualified manifest key (enc.Key.String()) and its content id in the alias store.

#Lockfile

Go go
type Lockfile struct

Lockfile is the pinned, committed record of one import alias's vendored surface. It is written by import lock/import update and read by the offline check --tag imports.

#Check

Go go
func Check(projectDir, alias string, consumer *model.Schema) diagnostic.Diagnostics

Check runs the OFFLINE import drift check for one alias (roadmap 7.2). It never touches the remote — it reads only the vendored surface and the lockfile. It reports, at Error severity:

- Integrity (E233/E234): every locked object id resolves in the alias store (present, correct epoch, hashes to its id), and the surface hash of the sorted resolved ids matches the lockfile. - Semantic consistency (E235): the N-normalized re-encoding of the vendored surface hashes to the lockfile's semantic hash. Because the semantic hash folds equivalently-spelled defaults, a re-spelled default does NOT trip this; a real column-type change DOES. - Reference drift (E236/E237): every FK in the consumer model that resolves through this alias names a table AND columns the vendored surface actually provides, and the FK's local column type matches the referenced surface column type (compared via typeinfo normalization). A drifted column type yields an exact column+FK error. Columns the consumer does not reference are never examined, so unreferenced framework changes are silent.

#ImportAliases

Go go
func ImportAliases(projectDir string, declared []string) []string

ImportAliases returns the sorted list of alias names that have a committed lockfile under projectDir. It is the offline check's discovery source — the remote is never consulted.

#CheckGitAvailable

Go go
func CheckGitAvailable() error

CheckGitAvailable returns a hard error if the git binary is not on PATH. Import lock/update require git; the absence of git is a loud failure, not a fallback.

#ResolveRemoteRef

Go go
func ResolveRemoteRef(url, ref string) (string, error)

ResolveRemoteRef probes the remote with git ls-remote to confirm reachability and that ref exists, returning the resolved commit sha when ls-remote reports one. A ref that ls-remote does not list (e.g. a bare commit sha, which ls-remote never returns) yields ("", nil) — reachability succeeded but the ref must be resolved by CloneAt. A non-zero git exit (unreachable/auth) is a hard error naming the remote.

#CloneAt

Go go
func CloneAt(url, ref, dest string) (string, error)

CloneAt clones url into dest and checks out ref, returning the resolved commit sha (rev-parse HEAD). It handles tags, branches, and bare commit shas uniformly via a full clone + checkout. A clone failure (unreachable remote) or a checkout failure (bad ref) is a hard error with git's stderr attached.

#AliasDir

Go go
func AliasDir(projectDir, alias string) string

AliasDir returns the store root directory for an alias under projectDir.

#LockfilePath

Go go
func LockfilePath(projectDir, alias string) string

LockfilePath returns the lockfile path for an alias under projectDir.

#WriteLockfile

Go go
func WriteLockfile(projectDir string, lf *Lockfile) error

WriteLockfile writes lf to imports//lock.json under projectDir, creating directories as needed. Objects are sorted by key so the on-disk form is deterministic (a committed, diff-stable artifact).

#ReadLockfile

Go go
func ReadLockfile(projectDir, alias string) (*Lockfile, error)

ReadLockfile reads and parses imports//lock.json under projectDir.

#LockfileExists

Go go
func LockfileExists(projectDir, alias string) bool

LockfileExists reports whether an alias has a committed lockfile.

#LoadSurface

Go go
func LoadSurface(projectDir, alias string) (*model.Schema, error)

LoadSurface decodes the vendored surface for one alias (imports//) into a sub-model containing the imported REFERENCE objects — tables plus their type closure (enums, domains, composites, state machines) — already stamped into the target schema. It reads only the lockfile and the content-addressed store; it never touches the remote (offline builds and the drift check need no network).

The returned schema is a bare sub-model: its Tables/Enums/... are populated and each object carries the target Schema it was vendored into, but no canonicalization or FK-graph derivation is performed (the consumer's Build folds these into its own model via WithImportedTables and the registry). A referenced object that fails to resolve or decode is a hard error — the vendored surface is part of the committed project state, so corruption must fail loudly rather than silently drop a reference (which would then trip a spurious E204).

#LoadAllSurfaces

Go go
func LoadAllSurfaces(projectDir string, aliases []string) (*model.Schema, error)

LoadAllSurfaces decodes the vendored surfaces for every alias that has a committed lockfile under projectDir, aggregating their reference objects into one sub-model. aliases is the set of declared import aliases (typically the keys of the project's [imports] config); aliases without a lockfile are skipped (the project has not run import lock yet — the build proceeds without the union, and the unresolved FK surfaces as a normal E204/E236). The aggregated tables are suitable for model.WithImportedTables; enums/domains/composites/state machines are returned for registry loading so imported types are usable in local columns.

#CheckRequirements

Go go
func CheckRequirements(projectDir string, aliases []string, consumer *model.Schema) diagnostic.Diagnostics

CheckRequirements enforces the lockfile's carried REQUIREMENTS against the consumer's own declarations (roadmap 7.3): the consumer must re-declare every extension the imported surface requires, and its pg_version must be >= the imported floor. The lockfile carries these (7.2 InferRequirements); 7.3 turns them into hard errors surfaced by the check --tag imports framework (cmd checkImports calls this) — NOT by the build/parseAndBuild path. So a consumer that would fail at apply time (a missing extension type, or a version-gated feature the consumer's target cannot run) fails loudly at CHECK time, in CI, before ever reaching apply.

- Requirement violations use E241 (missing extension) and E242 (pg_version below floor), naming the requiring alias. (These are distinct from the live import-verification codes E238/E239/E240 raised by revise's DB tier and the drift codes E230-E237 from 7.1/7.2.)

consumer supplies the project's own declared Extensions and PGVersion. aliases is the set of declared import aliases; only those with a committed lockfile are consulted (the remote is never touched).

#ExtractSurface

Go go
func ExtractSurface(framework *model.Schema, refTables []string, targetSchema string) (*model.Schema, error)

ExtractSurface computes the import surface a consumer needs from a framework model: the referenced tables plus the transitive composition-closure of their type definitions (enums, domains, composites, state machines). Every surface object is re-stamped into targetSchema so it matches how the consumer references it (fk.RefSchema == targetSchema). refTables is the set of table names the consumer references through this alias.

A referenced table absent from the framework model is a hard error (the alias promises a table the framework does not provide). The returned surface is a sub-model containing ONLY the surface objects; it is not canonicalized against a full schema (it has no meta), so callers encode it per-object.

#Vendor

Go go
func Vendor(surface *model.Schema, root string) (entries []ObjectEntry, surfaceHash, semanticHash string, err error)

Vendor encodes the surface objects, stores them in the alias objstore rooted at root, and returns the per-object entries plus the surface and semantic hashes. Puts are idempotent (content-addressed), so re-vendoring the same surface is a no-op on disk.

#InferRequirements

Go go
func InferRequirements(framework *model.Schema) (extensions []string, pgVersion int)

InferRequirements returns the extension requirements and pg_version floor to carry in the lockfile. Extensions are the framework's declared set (a safe superset; per-object refinement is deferred — 7.3 wires the re-declaration error). pg_version is the framework model's PGVersion.

Search