src/env/ owns environment file lifecycle helpers for stack projects. It discovers .env.example
and .env.example.<profile> files, scaffolds .env files, materializes profile presets into root
.env targets, compares env keys, lists env status, and audits plaintext .env files that have or
lack encrypted .env.enc counterparts.
Public entry points in mod.ts are discoverEnvExamples, createEnvFromExample, diffEnvFiles,
batchCreateEnvs, getEnvStatusList, materializeEnvFromProfile, and envDoctor. Public data
contracts live in types.ts.
- File discovery uses
@std/fswalkand narrows results with deterministic filters:DEFAULT_SKIP_DIRS,hasSkipAncestor,isInHiddenDir, andmatchesPaths. - Env parsing is key focused.
parseEnvKeysreads.envstyle files, ignores blank lines and comments, strips an optionalexportprefix, and returns keys before=without exposing values. - Operations are option driven.
DiscoverOptions,CreateOptions,MaterializeOptions, andDoctorOptionsconfigure profile selection, scoped paths, overwrite behavior, dry runs, and remediation suggestions. - Write operations are conservative.
createEnvFromExampleandmaterializeEnvFromProfileskip existing targets unlessforceis set, andbackupEnvBeforeOverwritecreates timestamped.bak.<timestamp>files before overwriting. - Batch functions accumulate structured results instead of failing the whole operation on item
errors.
BatchCreateResult,MaterializeResult, andDoctorResultseparate successes, skips, errors, findings, and warning state.
- Discovery starts with
discoverEnvExamples(projectDir, options). It selects the expected example suffix fromoptions.profile, walks the project tree, excludes skipped and hidden directories, appliespathsfilters, derivesserviceNamewithderiveServiceName, and computesstatusby comparing keys from the example file and corresponding env file. - Scaffolding uses
batchCreateEnvs(projectDir, options)for project wide creation. It callsdiscoverEnvExamples, optionally filters byserviceName, then delegates each item tocreateEnvFromExample. The single file helper validates the example, refuses existing env files unless forced, supportsdryRun, backs up overwritten env files, and copies text content. - Profile materialization uses
materializeEnvFromProfile(projectDir, options). It requiresoptions.profile, finds.env.example.<profile>files, applies the same directory and path filters, and writes each matching file to.envin the same directory. Existing targets are skipped unlessforceis set. - Diffing uses
diffEnvFiles(examplePath, envPath, serviceName). It parses keys from files that exist, wraps parse failures with file specific errors, and returnsEnvDiffarrays foronlyInExample,onlyInEnv, andcommon. - Status listing uses
getEnvStatusList(projectDir, options). It includes base examples fromdiscoverEnvExamples, adds profile variants when no profile filter is set, checks for.envand.env.enc, and returns sortedEnvStatusEntryrecords. - Audit uses
envDoctor(projectDir, options). It walks plaintext.envfiles, applies scope filters, checks for adjacent.env.enc, and emitsDoctorFindingentries. A plaintext file with encrypted counterpart is awarning; a plaintext file without encrypted counterpart isinfo.hasWarningsis derived from warning findings.
- CLI commands import these helpers through
src/env/mod.tsto implement env list, create, materialize, diff, and doctor workflows. - The secrets subsystem is coupled by convention through adjacent
.env.encdetection ingetEnvStatusListandenvDoctor, and by remediation messages that referencestackctl secrets encryptandstackctl secrets clean. - The compose and configuration workflows consume the resulting
.envfiles indirectly as project artifacts. This module does not parse values or render compose content. - All filesystem side effects use Deno APIs, specifically
Deno.readTextFileandDeno.writeTextFile, with standard path helpers from@std/path.