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
- Variables
- func DefaultLayers() map[string][]string
- func Find(dir string) (string, bool)
- func SupportedDialect(name string) bool
- type Config
- func (c *Config) AppFileName(app string) string
- func (c *Config) AppsPath() string
- func (c *Config) DatabaseNames() []string
- func (c *Config) DeclaresDatabases() bool
- func (c *Config) DialectOf(db Database) string
- func (c *Config) HadComments() bool
- func (c *Config) IsLayerDir(dirName string) bool
- func (c *Config) LayerOf(dirName string) string
- func (c *Config) MirrorTests() bool
- func (c *Config) ModuleFileName(pkg string) string
- func (c *Config) Save(path string) error
- func (c *Config) SelectOne(name string) (Target, error)
- func (c *Config) SelectTargets(name string) ([]Target, error)
- func (c *Config) Targets() []Target
- func (c *Config) Validate() error
- type Database
- type Generate
- type Layout
- type Target
Constants ¶
const ( LayerController = "controller" LayerService = "service" LayerEntity = "entity" LayerDTO = "dto" LayerRepository = "repository" LayerConstants = "constants" )
Layer names used by the tooling.
const DefaultTargetName = "default"
DefaultTargetName is what the single-database form is called, matching inceptools-cli.
const FileName = "icpt.json"
FileName is the configuration file this package reads.
Variables ¶
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.
var ErrNoSuchDatabase = errors.New("no such database in " + FileName)
ErrNoSuchDatabase is returned when -db names something not configured.
Functions ¶
func DefaultLayers ¶
DefaultLayers is the recommended shape, plus the aliases seen most often.
func Find ¶
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 ¶
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 Load ¶
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 ¶
AppFileName renders the domain aggregator file name.
func (*Config) DatabaseNames ¶
DatabaseNames lists the configured migration targets, sorted.
func (*Config) DeclaresDatabases ¶
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) HadComments ¶
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 ¶
IsLayerDir reports whether a directory is a layer rather than a module.
func (*Config) MirrorTests ¶
MirrorTests reports whether tests live under TestsDir rather than beside the code.
func (*Config) ModuleFileName ¶
ModuleFileName renders the module declaration file name.
func (*Config) Save ¶
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 ¶
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 ¶
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 ¶
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.
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.