Add `relspec job list` and `relspec job run <name>` driven by YAML job manifests (relspec.yml / relspec.<name>.yml), so multi-file merge and conversion workflows can be expressed declaratively instead of as long shell command lines. v1 contract (see docs/JOB_FILES.md): - `command` is a closed allow-list (convert, merge, scripts-list); no field accepts a shell string or executable path. - Deterministic discovery: default file first, then named files sorted lexically; all files merged into one namespace; duplicate job names across files are a hard error. - Every path resolves relative to the job file's directory; absolute, home-relative and directory-escaping paths are rejected at validation. - Database credentials referenced by env-var name via `conn_env:`; connection strings are never stored and are redacted from logs/plan. - Full validation (version, unknown fields, command/format, per-command input/output shape, path traversal, depends_on targets, dependency cycles) runs before anything is read, written or executed; per-job pre-flight then checks input existence, script dirs, env vars and the output overwrite policy for the whole plan. - `depends_on` closure runs in deterministic topological order; `--no-deps` runs only the named job. - `--dry-run` (alias `--plan`) prints the resolved plan and exits 0 without touching inputs, outputs or databases. - A failing job propagates the underlying non-zero exit status, logs FAILED (never OK), and writes no success marker. pkg/jobs is side-effect free (discovery/parse/validate/plan only); execution adapters live in cmd/relspec/job.go. Includes unit tests for discovery, validation, planning and path safety, plus CLI tests for end-to-end convert/merge, scripts-list across multiple directories, dry-run, dependency chains, exit-code propagation and log redaction. Deferred: live `scripts execute` from jobs, split/inspect/diff/templ commands, job-to-job output wiring, log rotation/retention. Refs #20 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
RelSpec
Bidirectional database schema conversion, validation, and templating tool.
Install
go install -v git.warky.dev/wdevs/relspecgo/cmd/relspec@latest
Supported Formats
| Direction | Formats |
|---|---|
| Readers | bun dbml dctx drawdb drizzle gorm graphql json mssql pgsql prisma sqldir sqlite typeorm yaml |
| Writers | bun dbml dctx drawdb drizzle gorm graphql json mssql pgsql prisma sqlexec sqlite template typeorm yaml |
Commands
convert — Schema conversion
# PostgreSQL → GORM models
relspec convert --from pgsql --from-conn "postgres://user:pass@localhost/mydb" \
--to gorm --to-path models/ --package models
# DBML → PostgreSQL DDL
relspec convert --from dbml --from-path schema.dbml --to pgsql --to-path schema.sql
# PostgreSQL → SQLite (auto flattens schemas)
relspec convert --from pgsql --from-conn "postgres://..." --to sqlite --to-path schema.sql
# Multiple input files merged
relspec convert --from json --from-list "a.json,b.json" --to yaml --to-path merged.yaml
PostgreSQL connections opened by relspec set application_name by default to
relspecgo/<version> (with component suffixes internally, e.g. readers/writers).
If you need a custom value, provide application_name explicitly in the connection
string query parameters.
merge — Additive schema merge (never modifies existing items)
# Merge two JSON schemas
relspec merge --target json --target-path base.json \
--source json --source-path additions.json \
--output json --output-path merged.json
# Merge PostgreSQL into JSON, skipping tables
relspec merge --target json --target-path current.json \
--source pgsql --source-conn "postgres://user:pass@localhost/db" \
--output json --output-path updated.json \
--skip-tables "audit_log,temp_tables"
Skip flags: --skip-relations --skip-views --skip-domains --skip-enums --skip-sequences
inspect — Schema validation / linting
# Validate PostgreSQL database
relspec inspect --from pgsql --from-conn "postgres://user:pass@localhost/mydb"
# Validate DBML with custom rules
relspec inspect --from dbml --from-path schema.dbml --rules .relspec-rules.yaml
# JSON report output
relspec inspect --from json --from-path db.json --output-format json --output report.json
# Filter to specific schema
relspec inspect --from pgsql --from-conn "..." --schema public
Rules: naming conventions, PK/FK standards, missing indexes, reserved keywords, circular dependencies.
diff — Schema comparison
relspec diff --from pgsql --from-conn "postgres://localhost/db1" \
--to pgsql --to-conn "postgres://localhost/db2"
templ — Custom template rendering
# Render database schema to Markdown docs
relspec templ --from pgsql --from-conn "postgres://user:pass@localhost/db" \
--template docs.tmpl --output schema-docs.md
# One TypeScript file per table
relspec templ --from dbml --from-path schema.dbml \
--template ts-model.tmpl --mode table \
--output ./models/ --filename-pattern "{{.Name | toCamelCase}}.ts"
Modes: database (default) · schema · table · script
Template functions: string utils (toCamelCase, toSnakeCase, pluralize, …), type converters (sqlToGo, sqlToTypeScript, …), filters, loop helpers, safe access.
job — Declarative job files
Run named jobs from a relspec.yml manifest instead of repeating long command lines.
# List jobs discovered in ./relspec.yml and ./relspec.<name>.yml (deterministic)
relspec job list
# Validate and print the plan without running anything
relspec job run build-schema --plan
# Run a job (and its declared dependencies)
relspec job run build-schema
# relspec.yml
version: 1
jobs:
build-schema:
command: convert # closed allow-list: convert | merge | scripts-list
description: Merge the DBML sources and emit PostgreSQL DDL
inputs:
- path: schema/core.dbml
format: dbml
- path: schema/tenant.dbml
format: dbml
output:
format: pgsql
path: build/schema.sql
overwrite: true
options:
flatten_schema: false
logfile: .relspec/log/build-schema.log
The job system is not a shell: command is a fixed enum, every path is
resolved relative to the job file and may not escape it, and remote database
credentials are referenced by environment-variable name (conn_env:) and
redacted from logs. The whole plan — unknown commands/formats, duplicate job
names, missing inputs, path traversal, dependency cycles — is validated before
any job runs. See docs/JOB_FILES.md.
edit — Interactive TUI editor
# Edit DBML schema interactively
relspec edit --from dbml --from-path schema.dbml --to dbml --to-path schema.dbml
# Edit live PostgreSQL database
relspec edit --from pgsql --from-conn "postgres://user:pass@localhost/mydb" \
--to pgsql --to-conn "postgres://user:pass@localhost/mydb"
Development
Prerequisites: Go 1.24.0+
make build # → build/relspec
make test # race detection + coverage
make lint # requires golangci-lint
make coverage # → coverage.html
make install # → $GOPATH/bin
Project Structure
cmd/relspec/ CLI commands
pkg/readers/ Input format readers
pkg/writers/ Output format writers
pkg/inspector/ Schema validation
pkg/diff/ Schema comparison
pkg/merge/ Schema merging
pkg/models/ Internal data models
pkg/transform/ Transformation logic
pkg/pgsql/ PostgreSQL utilities
pkg/sqltypes/ Nullable SQL types for generated/hand-written models (see below)
Nullable Types (pkg/sqltypes)
The bun and gorm writers can generate model structs using
pkg/sqltypes — nullable types (SqlString,
SqlInt32, SqlTimeStamp, SqlStringArray, …) that implement
database/sql.Scanner, driver.Valuer, and JSON/YAML/XML marshalling in one
type, selected via --types sqltypes. See the
pkg/sqltypes README for the full type
reference, or the bun /
gorm writer docs for the --types flag
(sqltypes, stdlib, or baselib). PostgreSQL array columns are the one
exception: the bun writer always generates native Go slices ([]string,
[]int32, …) with an explicit array bun tag, regardless of --types —
see bun's --array-nullable
flag for nullable-array handling. The SqlXxxArray wrapper types remain
available in pkg/sqltypes and are still used by the gorm writer.
Contributing
- Register or sign in with GitHub at git.warky.dev
- Clone the repository:
git clone https://git.warky.dev/wdevs/relspecgo.git - Create a feature branch:
git checkout -b feature/your-feature-name - Commit your changes and push the branch
- Open a pull request with a description of the new feature or fix
For questions or discussion, join the Discord: discord.gg/74rcTujp25 — warkyhein
Links
- Todo
- AI Use Policy
- License — Apache 2.0 · Copyright 2025 Warky Devs



