Documentation
¶
Overview ¶
Package statefile is the one place forge's JSON-record-under-.forge/state helpers live. Three callers — internal/cli (build_state.go), internal/buildtarget (per-service build state), and internal/deploytarget (per-provider deploy state) — each used to hand-roll the same MkdirAll + MarshalIndent + WriteFile dance and the same missing-file-is-nil read, against the same hard-coded `.forge/state` directory. They drifted: only deploytarget sanitized the env/service path segments before joining them into a filename, so buildtarget's statePath was a latent path-traversal smell. Hoisting the IO here makes the on-disk dir, the file mode, and the one sanitizer a single source of truth.
Scope is deliberately small: this package owns *how* a record is read and written (directory, modes, JSON encoding, missing-file semantics), NOT *what* the record is or how its filename is composed. Each caller keeps its own struct and its own filename builder — the filename shapes (build-<env>.json, build-<env>-<service>.json, <provider>-<env>-<service>.json) are part of each caller's on-disk contract and must stay stable so existing state files keep loading.
Index ¶
Constants ¶
const DirRel = ".forge/state"
DirRel is the per-project on-disk location, relative to the project root (the directory holding forge.yaml), for forge runtime state that must survive across `forge build` / `forge deploy` invocations. Sits under .forge/ so the single existing `.forge/` .gitignore rule covers it alongside checksums.json and the ownership state.
Variables ¶
This section is empty.
Functions ¶
func Path ¶
Path joins the project root, .forge/state, and a caller-built filename into the absolute path of a state file. The filename is the caller's responsibility (each caller owns its own on-disk filename shape); Path just centralizes the directory so the layout lives in one place.
func Read ¶
Read loads and JSON-decodes the state file at path into a freshly allocated *T. A missing file returns (nil, nil) — every caller treats "no state file" as a non-error "no previous record" signal distinct from "file exists but is malformed", which returns (nil, err). label names the record in error messages.
func SafeSegment ¶
SafeSegment sanitizes one path segment (an env or service name) down to a flat filename-safe token: [A-Za-z0-9_-] pass through, everything else (path separators included) becomes '_'. The inputs are KCL-validated identifiers in practice, but a state filename is composed from them, so we strip separators defensively to keep the write inside .forge/state. An empty input yields "_" so the segment is never empty.
func Write ¶
Write marshals v as indented JSON and persists it to the state file at path, creating .forge/state lazily so projects that never touch a given state path never grow the tree. The 2-space MarshalIndent and 0o644 mode match what all three legacy impls produced, so on-disk files are byte-for-byte unchanged. label names the record in error messages (e.g. "build state", "deploy state").
Types ¶
This section is empty.