project

package
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Nov 7, 2026 License: Apache-2.0 Imports: 8 Imported by: 0

Documentation

Overview

Package project loads icpt.json, the per-repository configuration for the icpt CLI.

It exists because there is no one right layout. Some repositories keep tests beside the code, some mirror them under tests/; some call the data layer repository/, some repo/, some store/; some have one database, some have one per region. A tool that hard-codes one answer is a tool that has to be fought.

So the conventions are configuration, and the defaults are the shape this framework recommends. A repository with no icpt.json gets those defaults and works; a repository with one gets its own.

Unknown keys are PRESERVED on write. inceptools-cli's `dialect` and `databases` live in this same file, and a tool that rewrote the file without them would break migrations the first time somebody ran `icpt init`.

Index

Constants

View Source
const (
	LayerController = "controller"
	LayerService    = "service"
	LayerEntity     = "entity"
	LayerDTO        = "dto"
	LayerRepository = "repository"
	LayerConstants  = "constants"
)

Layer names used by the tooling.

View Source
const DefaultTargetName = "default"

DefaultTargetName is what the single-database form is called, matching inceptools-cli.

View Source
const FileName = "icpt.json"

FileName is the configuration file this package reads.

Variables

View Source
var Dialects = []string{
	"postgres", "pg",
	"mysql",
	"sqlite",
	"sqlserver", "mssql",
	"clickhouse",
}

Dialects are the SQL engines the migrator supports, with the aliases inceptools-cli accepts. It is declared here, rather than imported from the migrator, so that validating a config file does not pull five database drivers into every build that reads one.

View Source
var ErrNoSuchDatabase = errors.New("no such database in " + FileName)

ErrNoSuchDatabase is returned when -db names something not configured.

Functions

func DefaultLayers

func DefaultLayers() map[string][]string

DefaultLayers is the recommended shape, plus the aliases seen most often.

func Find

func Find(dir string) (string, bool)

Find walks up from dir looking for icpt.json, stopping at the filesystem root or at a directory holding go.mod.

Walking up means a command run from apps/catalog/products finds the repository's configuration, which is what anybody would expect and what every other tool does.

func SupportedDialect

func SupportedDialect(name string) bool

SupportedDialect reports whether name is one of Dialects, ignoring case.

Types

type Config

type Config struct {
	// Module is the Go module path. Read from go.mod when absent.
	Module string `json:"module,omitempty"`
	// Dialect is the default SQL dialect for migrations.
	Dialect string `json:"dialect,omitempty"`

	// EnvVariable and MigrationPath are the single-database form.
	EnvVariable   string `json:"env_variable,omitempty"`
	MigrationPath string `json:"migration_path,omitempty"`

	// Databases are the migration targets, keyed by the name passed to -db.
	Databases map[string]Database `json:"databases,omitempty"`

	Layout   Layout   `json:"layout,omitempty"`
	Generate Generate `json:"generate,omitempty"`
	// Rules overrides a lint rule's severity: "error", "warn" or "off".
	Rules map[string]string `json:"rules,omitempty"`
	// Skip are path fragments the linter ignores.
	Skip []string `json:"skip,omitempty"`

	// Root is the directory the file was found in. Not serialised.
	Root string `json:"-"`
	// Found reports whether a file was actually read, as opposed to
	// defaults being returned.
	Found bool `json:"-"`
	// contains filtered or unexported fields
}

Config is the whole file.

Two shapes are supported, because inceptools-cli supports both and a repository using it must keep working untouched:

{ "env_variable": "DATABASE_URL", "migration_path": "./migrations" }

is the single-database form, and yields one target named "default". The other is the "databases" map below. Setting both is an error rather than a merge — it reads as though it configures two targets and does not.

func Default

func Default() *Config

Default returns the configuration used when there is no file.

func Load

func Load(dir string) (*Config, error)

Load reads the configuration for dir, falling back to the defaults.

A missing file is NOT an error: the defaults are a working configuration, and requiring a config file to run a linter is friction with no benefit. A malformed file IS an error — silently ignoring it would apply defaults the author did not ask for and did not notice.

func (*Config) AppFileName

func (c *Config) AppFileName(app string) string

AppFileName renders the domain aggregator file name.

func (*Config) AppsPath

func (c *Config) AppsPath() string

AppsPath is the absolute path of the domains directory.

func (*Config) DatabaseNames

func (c *Config) DatabaseNames() []string

DatabaseNames lists the configured migration targets, sorted.

func (*Config) DeclaresDatabases

func (c *Config) DeclaresDatabases() bool

DeclaresDatabases reports whether the file actually configures a migration target, in either shape.

Targets always returns at least one, because `icpt migrate` in a project with no configuration should still do the obvious thing. That synthetic default is useful for the migrator and misleading for anything that reports on what the project HAS — `icpt doctor` should not complain about a database nobody asked for.

func (*Config) DialectOf

func (c *Config) DialectOf(db Database) string

DialectOf returns this target's dialect, falling back to the file's.

func (*Config) HadComments

func (c *Config) HadComments() bool

HadComments reports whether the file on disk carried comments or trailing commas. Save cannot preserve them — encoding/json has nowhere to put them — so a caller about to rewrite the file should say so first.

func (*Config) IsLayerDir

func (c *Config) IsLayerDir(dirName string) bool

IsLayerDir reports whether a directory is a layer rather than a module.

func (*Config) LayerOf

func (c *Config) LayerOf(dirName string) string

LayerOf returns the logical layer a directory name belongs to, or "".

func (*Config) MirrorTests

func (c *Config) MirrorTests() bool

MirrorTests reports whether tests live under TestsDir rather than beside the code.

func (*Config) ModuleFileName

func (c *Config) ModuleFileName(pkg string) string

ModuleFileName renders the module declaration file name.

func (*Config) Save

func (c *Config) Save(path string) error

Save writes the configuration, preserving any keys this version does not know about.

It does NOT preserve comments: they are stripped before parsing and there is nowhere in the decoded value to keep them. Check HadComments and warn before overwriting somebody's annotated file.

func (*Config) SelectOne

func (c *Config) SelectOne(name string) (Target, error)

SelectOne resolves exactly one target, and refuses to guess between several.

A rollback that fanned out across every database because -db was omitted is unrecoverable, and "it picked the first one" is the same mistake with better odds.

func (*Config) SelectTargets

func (c *Config) SelectTargets(name string) ([]Target, error)

SelectTargets filters Targets by name.

An empty name returns EVERY target. That is what `inceptools migrate` with no -db does, and changing it would silently stop migrating a database in a repository that relied on it. Callers for which fanning out is wrong — a rollback, creating a file — use SelectOne instead.

func (*Config) Targets

func (c *Config) Targets() []Target

Targets resolves every configured database into a deterministic, name-sorted list.

The single-database form yields one target called "default", and a database entry with no migration_path defaults to ./migrations/<name> — both to match inceptools-cli, so a repository already using it resolves to the same directories here.

func (*Config) Validate

func (c *Config) Validate() error

Validate reports configuration that cannot work.

type Database

type Database struct {
	// EnvVariable names the environment variable holding the DSN. The DSN
	// itself is never stored in the file: a connection string holds a
	// password, and the repository is the wrong place for one.
	EnvVariable string `json:"env_variable"`
	// MigrationPath is the directory of .up.sql / .down.sql files, relative
	// to the repository root.
	MigrationPath string `json:"migration_path"`
	// Dialect overrides the top-level one for this target. A repository with
	// a Postgres primary and a SQLite cache, or two engines mid-migration,
	// sets it here.
	Dialect string `json:"dialect,omitempty"`
	// LedgerTable overrides the name of the applied-migrations table, for a
	// database that already has a schema_migrations belonging to something
	// else.
	LedgerTable string `json:"ledger_table,omitempty"`
	// NoLock skips the advisory lock. Setting it means two concurrent
	// deploys can both apply the same migration; only do it where the lock
	// genuinely cannot work.
	NoLock bool `json:"no_lock,omitempty"`
}

Database is one migration target.

These two fields are inceptools-cli's existing schema and are read, not invented: a repository already using that CLI needs no change to its file.

type Generate

type Generate struct {
	// Repository emits a repository layer. Turn it off for a service whose
	// modules talk to something other than a database.
	Repository *bool `json:"repository,omitempty"`
	// Tests emits a test file per module.
	Tests *bool `json:"tests,omitempty"`
	// Constants emits a constants package.
	Constants *bool `json:"constants,omitempty"`
}

Generate configures the scaffolder.

type Layout

type Layout struct {
	// Apps is where the domains live. Default "apps".
	Apps string `json:"apps,omitempty"`
	// Tests selects where generated tests go and where the linter expects
	// them: "colocated" (beside the code, the Go default) or "mirror" (under
	// TestsDir, mirroring the package path).
	Tests string `json:"tests,omitempty"`
	// TestsDir is the root for mirrored tests. Default "tests".
	TestsDir string `json:"tests_dir,omitempty"`
	// ModuleFile is the name of a module's declaration file. {module} is
	// replaced with the module's package name. Default "{module}_module.go".
	ModuleFile string `json:"module_file,omitempty"`
	// AppFile is the name of a domain's aggregator. {app} is replaced.
	// Default "{app}_app.go".
	AppFile string `json:"app_file,omitempty"`
	// Layers maps each logical layer to the directory names that count as
	// it. A repository that calls its data layer "store" adds that here and
	// the layering rules start applying to it.
	Layers map[string][]string `json:"layers,omitempty"`
}

Layout names the directories and files the tooling looks for.

type Target

type Target struct {
	Name string
	// EnvVariable holds the DSN. Never the DSN itself.
	EnvVariable string
	// MigrationPath is absolute, resolved against the repository root.
	MigrationPath string
	Dialect       string
	LedgerTable   string
	NoLock        bool
}

Target is a fully resolved migration target.

Jump to

Keyboard shortcuts

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