Documentation
¶
Overview ¶
Package builder is Mooring's build subsystem: it turns a declarative build spec (from mooring.yaml's compose.services[].build) into a hardened, multi-stage Dockerfile. The operator never writes a Dockerfile — Mooring owns it, the same way it owns the compose. A registry of language builders covers the popular stacks; an `auto` language detects the stack from the repo; a `generic` builder wraps the operator's own base + commands as a best-effort fallback.
Security: the operator's install/build commands are run as Dockerfile RUN steps (it is their build), but they are SANITIZED — no newline/CR/NUL — so a value can never break out of its RUN line to inject extra Dockerfile directives (e.g. USER root, FROM, another COPY). Every generated image runs as a non-root user by default.
Index ¶
Constants ¶
const ( NonrootUID = 10001 NonrootUser = "app" )
NonrootUID / NonrootUser are the FIXED identity every non-root build runs as. The UID is pinned to an explicit high value rather than left to `useradd -r` / `adduser -S`, which allocate a SYSTEM UID counting DOWN from 999 — so the app's UID was an accident of how many accounts the base image and the `packages:` list created first, and adding one OS package could silently shift it. Docker only copies ownership onto an EMPTY named volume, so a shifted UID left an app unable to write its own data (silent, permanent). 10001 sits above the system range and above the conventional first human UID (1000) on both Debian and Alpine, so no package or host account can collide with it.
Variables ¶
This section is empty.
Functions ¶
func DockerfilePath ¶
DockerfilePath is the run_dir-relative path where Mooring writes the generated Dockerfile for a service (and the compose `build.dockerfile` value). Kept here so the compose generator and the deploy-time writer always agree. The service name is schema-validated ([a-z0-9][a-z0-9_-]*), so the path is traversal-free.
func RunsAsNonroot ¶ added in v0.16.0
RunsAsNonroot reports whether a GENERATED Dockerfile ends up running as the pinned non-root user — i.e. it emitted the `USER app` directive. It reads the actual generated output, so a build with no non-root user (php/apache drops to www-data itself and never gets `USER app`, even when detected from `language: auto`) is correctly reported as false. Used to decide which volumes may be ownership-reconciled to NonrootUID without breaking a container that runs as something else.
func SupportedLanguages ¶
func SupportedLanguages() []string
SupportedLanguages lists the first-class builders (for errors/docs).
Types ¶
type Builder ¶
type Builder interface {
Name() string
Detect(files map[string]bool) bool // does this stack match the repo's top-level files?
Dockerfile(s Spec, files map[string]bool) (string, error)
}
Builder generates a Dockerfile for one language/stack. files is the build root's file set (same as Detect sees) so a builder can pick the dependency manager from the committed lockfile (e.g. pnpm-lock.yaml → pnpm); builders that don't care ignore it.
type Spec ¶
type Spec struct {
Service string // service name (labels / Dockerfile naming)
Language string // auto | node | python | go | ruby | php | static | generic
Dir string // repo-relative subdir to build from ("" = repo root)
Version string // runtime version (e.g. "20"); builder picks a sane default
Base string // generic only: the base image
Install string // dependency install command (shell)
Build string // build/compile command (shell)
Start []string // container start (exec form) → Dockerfile CMD
Env map[string]string // build-time env
Packages []string // extra OS packages
Output string // build output dir to ship (static: served dir, e.g. "dist")
Nonroot bool // run the image as a non-root user (default true at the caller)
}
Spec is the declarative build input (projected from the definition Build).