Documentation
¶
Overview ¶
Package preset loads and applies curated scaffolds ("presets") on top of an existing maestro project. A preset bundles a TOML manifest, a set of service declarations to scaffold, and a tree of source files that the loader substitutes and writes into those services.
The package is structured around three building blocks:
- Manifest types (this file): preset.toml shape — services, parameters.
- Substitution (subst.go): sentinel-replacement engine over file bytes.
- Loader (loader.go): walks an fs.FS, validates a manifest, produces a Loaded preset the orchestrator can iterate over.
Discovery is via //go:embed so presets ship inside the binary; the loader takes an fs.FS so tests can hand it synthetic trees.
Index ¶
- Constants
- func Apply(ctx *project.Context, fsys fs.FS, presetName string, mainService string, ...) error
- func FS() fs.FS
- func List(fsys fs.FS) ([]string, error)
- func RewriteCommongoPrefix(content, projectModule string) string
- func RewriteSourcePrefix(content, presetName, overlayDir, userModule string) string
- func Substitute(content string, vars Vars) string
- func SubstitutePath(p string, vars Vars) string
- type File
- type MainDB
- type Manifest
- type Parameter
- type ParameterType
- type Selections
- type ServiceSpec
- type Vars
Constants ¶
const ( // SentinelService stands in for the user-chosen service name. SentinelService = "PRESETSERVICE" // SentinelModule stands in for the full Go module path of the // generated service, e.g. github.com/owner/proj/auth. SentinelModule = "PRESETMODULE" // PresetSourceRoot is the maestro-module path prefix every preset // overlay file lives under. Overlay code can write its sibling- // package imports using real paths under this prefix — gopls and // `go build ./...` resolve them in author-mode because they're // honest paths inside the maestro module. Apply rewrites the // prefix at apply time to the user's actual service module path. // // If maestro itself ever gets renamed, this constant has to be // updated in lockstep — the prefix is the load-bearing string // that makes cross-package imports work in both worlds. PresetSourceRoot = "github.com/Zagforge-Org/maestro/internal/preset/presets" // CommongoSourceRoot is the maestro-module path prefix for the // shared common/go starter packages. Overlay code that needs to // reach a common/go package (ginx, logger, errs, …) imports it // from this path — gopls resolves it in author-mode because the // real Go files live there. Apply rewrites the prefix at apply // time to the user's common/go module path, which is the project // namespace root (github.com/<owner>/<project>). Aliased to the // commongo package's own constant so the prefix has one definition. CommongoSourceRoot = commongo.ImportRoot )
Sentinels are the named tokens preset authors embed in source files and manifest strings; the loader replaces each one with the corresponding runtime value before writing the file to disk.
Following the gonew convention, every sentinel is a single uppercase Go identifier so template source files compile as valid Go in the preset author's editor (gopls treats `package PRESETSERVICE` as a normal package named PRESETSERVICE).
Variables ¶
This section is empty.
Functions ¶
func Apply ¶
func Apply( ctx *project.Context, fsys fs.FS, presetName string, mainService string, mainDB MainDB, picks Selections, ) error
Apply scaffolds every [service] block in the named preset's manifest into ctx's project, then overlays the preset's template files onto each service the manifest's files/ tree targets.
mainService is the user-chosen name that PRESETSERVICE resolves to. mainDB is the runtime database choice the dbwizard captured for that service (engine + shape come from the manifest; mode + port from the wizard). picks is the parameter selection map the wizard collected.
The function does no rollback — partial failure leaves whatever was written on disk. Callers are expected to run inside a clean working tree so a `git checkout` reverts a botched apply.
func FS ¶
FS returns the embedded preset filesystem rooted at the presets/ directory — i.e. fs.Sub of presetsFS so callers see preset directories at the root, matching the shape Walk and LoadManifest already expect.
func List ¶
List returns the names of every preset directory in fsys that has a readable, validating manifest. Broken manifests are silently skipped — the picker only ever shows usable presets. Use LoadManifest if you need the error for a specific name.
func RewriteCommongoPrefix ¶
RewriteCommongoPrefix rewrites every occurrence of the maestro-internal common/go source prefix with the user's project module path (which is the common/go module's path — common/go owns the namespace root). An overlay file can write
import "github.com/Zagforge-Org/maestro/internal/scaffold/commongo/ginx"
and Apply turns it into
import "github.com/<owner>/<project>/ginx"
at apply time. The substitution is a literal ReplaceAll for the same reason the per-service one is: the prefix is unique enough that incidental matches outside import lines are vanishingly unlikely.
func RewriteSourcePrefix ¶
RewriteSourcePrefix rewrites every occurrence of the preset's maestro-internal source prefix with the user's service module path. This is what lets an overlay file write a real, gopls-resolvable import like
github.com/Zagforge-Org/maestro/internal/preset/presets/<preset>/<dir>/internal/db
and have Apply rewrite it to
github.com/<owner>/<project>/<service>/internal/db
at apply time. The substitution is intentionally a literal-string ReplaceAll: the maestro-internal prefix is unique enough that any match outside an import line (a comment, a doc string) is almost certainly intentional too.
func Substitute ¶
Substitute returns content with every sentinel mention replaced by the matching field in vars. Files without any sentinels pass through byte-for-byte. Empty vars fields don't replace — leaving a sentinel in the output is loud at the call site and easier to debug than a silent blank-out.
func SubstitutePath ¶
SubstitutePath is the same as Substitute but applied to a file path. Kept as a distinct helper so a future change to path-only escaping rules (e.g. disallowing path separators in a substituted name) has a single place to land.
Types ¶
type File ¶
File is one preset source file the walker has decided should be included for the current parameter selection.
Dir is the overlay directory under the preset's root that this file came from — matches a [service].dir value in the manifest. Apply uses Dir to look up which resolved on-disk service the file should be written into.
TargetPath is the path the file should land at relative to the target service's root. Sentinels in both TargetPath and Bytes are unresolved — the caller substitutes after walk completes.
func Walk ¶
Walk lists every file under presets/<name>/<dir>/ in fsys that should be applied given the parameter picks in picks and the parameter schema in m.Parameters.
Layout: the preset root holds preset.toml plus one directory per service that ships an overlay. Each directory's name must match the dir field of some [service] block. Stray top-level files (e.g. a preset-level README.md) are ignored; stray top-level directories are rejected as authoring typos.
Inclusion rule (filename-suffix convention):
- A file whose basename ends in `_<option>.<ext>` — where <option> matches one of the declared options for some parameter — is included only when that option is in picks[<that parameter's key>].
- Every other file is included unconditionally.
Keeping every preset file in the same physical directory it'll occupy in the generated service means cross-file symbol references inside a single Go package work both in the preset (gopls + `go build`) and after apply.
type MainDB ¶
type MainDB struct {
DBMode projectcfg.DBMode
Port int
// Redis is the docker-vs-live choice for the main service's Redis
// dependency, applied only when the manifest's main [[service]] sets
// redis = true. Empty leaves the service without Redis.
Redis projectcfg.RedisMode
}
MainDB carries the runtime answers the dbwizard captured for the preset's first (main) service. The manifest declares the engine and shape; the wizard supplies the mode, port, and (for live mode) the URL the user already pinged.
type Manifest ¶
type Manifest struct {
// Name is the preset's stable identifier — used by `maestro preset
// apply <name>`. Must be non-empty.
Name string `toml:"name"`
// Description is the one-liner shown in the picker. Optional.
Description string `toml:"description"`
// Services lists the services this preset scaffolds. At least one is
// required. Each entry hands off to the existing service.Generate()
// pipeline; the loader resolves sentinels in fields like Name and
// DBRef before generation runs.
Services []ServiceSpec `toml:"service"`
// Parameters lists the interactive prompts the wizard renders for
// this preset. Keys feed into the file-walker's conditional inclusion
// rules (files/<key>/<value>/ is included only when the user picks
// value for key). May be empty for presets that take no parameters.
Parameters []Parameter `toml:"parameter"`
// CommonGo lists the opt-in common/go capability libraries this preset
// depends on (e.g. "jwtx"). Apply delivers them into the project's
// common/go so the preset's overlay can import them — and so they're
// then reusable by hand in any other service. Each entry must be a
// known capability lib. May be empty.
CommonGo []string `toml:"commongo"`
}
Manifest is the on-disk shape of a preset.toml. The loader parses one of these per preset, validates it, and pairs it with the preset's files/ tree to produce a Loaded preset.
func LoadManifest ¶
LoadManifest reads and validates the manifest for the preset at name inside fsys (typically the embedded presets/ FS). The returned Manifest is safe to hand to the wizard and the file walker.
type Parameter ¶
type Parameter struct {
Key string `toml:"key"`
Prompt string `toml:"prompt"`
Type ParameterType `toml:"type"`
Options []string `toml:"options"`
Preselect []string `toml:"preselect,omitempty"`
MinPick int `toml:"min_pick,omitempty"`
}
Parameter is one [[parameter]] block. It declares an interactive prompt and the constraints the wizard enforces before the user can advance. For multiselect, Preselect is the set of options ticked when the wizard opens (pure UI state) and MinPick is the floor on how many must be ticked before "continue" advances.
type ParameterType ¶
type ParameterType string
ParameterType enumerates the prompt shapes the wizard supports. v1 ships multiselect only; the type is open for future single-select, text, and bool prompts.
const (
ParameterMultiselect ParameterType = "multiselect"
)
type Selections ¶
Selections maps parameter key → the option values the user picked. Used by Walk to decide which conditional files to include.
type ServiceSpec ¶
type ServiceSpec struct {
Name string `toml:"name"`
Dir string `toml:"dir,omitempty"`
Shape config.ServiceShape `toml:"shape"`
DB config.DBEngine `toml:"db,omitempty"`
DBRef string `toml:"db_ref,omitempty"`
// Redis declares that this service uses Redis; the apply flow then
// captures a docker-vs-live choice for it (the gateway uses this for
// rate-limit state). Independent of DB.
Redis bool `toml:"redis,omitempty"`
}
ServiceSpec is one [service] block. The loader substitutes sentinels in Name and DBRef before passing the resulting config.ServiceConfig to the service generator.
Dir is the top-level directory under the preset's root that holds this service's overlay tree. The convention is a Go-idiomatic role name ("main", "cleanup", "api"), decoupled from the sentinel-laden Name so the on-disk tree is pleasant to navigate. Optional — a service with no Dir scaffolds from the maestro template alone, no overlay.
Directories
¶
| Path | Synopsis |
|---|---|
|
presets
|
|
|
api-gateway/main/internal/auth
Package auth verifies the access tokens where the gateway upstream services are behind.
|
Package auth verifies the access tokens where the gateway upstream services are behind. |
|
api-gateway/main/internal/gateway
Package gateway is the reverse-proxy core of api gateway preset which routes inbound requests to upstream services by longest-matching path prefix.
|
Package gateway is the reverse-proxy core of api gateway preset which routes inbound requests to upstream services by longest-matching path prefix. |
|
api-gateway/main/internal/server
Package server wires the api-gateway: request logging, local health probes, and the reverse proxy as the catch-all for everything else.
|
Package server wires the api-gateway: request logging, local health probes, and the reverse proxy as the catch-all for everything else. |
|
auth-jwt/cleanup/internal/worker
Package worker is the cleanup target for the auth-jwt preset.
|
Package worker is the cleanup target for the auth-jwt preset. |
|
auth-jwt/main/internal/auth
Authenticated symmetric encryption for the auth-jwt preset.
|
Authenticated symmetric encryption for the auth-jwt preset. |
|
auth-jwt/main/internal/db
Package db owns the database connection for this service.
|
Package db owns the database connection for this service. |
|
auth-jwt/main/internal/email
Package email builds the typed mailer.Message values the auth service sends.
|
Package email builds the typed mailer.Message values the auth service sends. |
|
auth-jwt/main/internal/requests
Package requests holds the typed JSON bodies every auth route expects, with go-playground/validator constraints declared inline via Gin's `binding:` tags.
|
Package requests holds the typed JSON bodies every auth route expects, with go-playground/validator constraints declared inline via Gin's `binding:` tags. |
|
auth-jwt/main/internal/responses
Package responses holds the typed JSON bodies the auth routes return on success.
|
Package responses holds the typed JSON bodies the auth routes return on success. |
|
auth-jwt/main/internal/routes
Always-on base of the auth-jwt preset's API.
|
Always-on base of the auth-jwt preset's API. |
|
auth-jwt/main/internal/server
Package server wires the auth-jwt service: opens the database, builds the auth service container (JWT + OTP configs + sqlc queries), attaches middleware + base routes, and registers the feature routes collected in internal/routes.
|
Package server wires the auth-jwt service: opens the database, builds the auth service container (JWT + OTP configs + sqlc queries), attaches middleware + base routes, and registers the feature routes collected in internal/routes. |
|
auth-jwt/main/internal/services
Always-on auth services for the auth-jwt preset: refresh and logout, used by every sign-in method.
|
Always-on auth services for the auth-jwt preset: refresh and logout, used by every sign-in method. |