mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-09-01 13:18:08 +00:00
feat(stacks): per-stack environment inventory and secret-safe guardrails (#1397)
* feat(stacks): per-stack environment inventory and secret-safe guardrails
Add an Environment tab to Stack Anatomy that derives a per-stack inventory
of environment variables from the compose files and env files. Each variable
shows its source, whether Compose interpolates it or injects it into a
container, and a status (present, missing, unused, duplicate, or shell-only),
plus likely-secret classification. The inventory works from variable names
only: a value is never read, returned, or logged, and a likely secret shows
presence only. A copy env checklist action exports names and status without
values.
Surface a missing required env_file as a Compose Doctor preflight finding,
and add an opt-in node setting that refuses a deploy or update when a
required ${VAR:?...} variable is unset or empty, before any backup, pull, or
up runs. Default off.
The Environment tab is capability-gated so it hides on older remote nodes.
* fix(stacks): harden env-file reader against a stat-then-open race
Open the env-file handle first and fstat the open handle instead of
stat-ing the path before opening, removing the check-then-use window in
readEnvFileKeys. Use a secure mkdtemp directory for the out-of-base test
path instead of a predictable name in the temp root.
* fix(stacks): resolve nested env_file paths per compose file, reconcile inline keys per service
Resolve each env_file relative to the directory of the compose file that
declared it, so a nested multi-file Git override (infra/prod.yml referencing
./prod.env) lands next to that file instead of the stack root. The root
compose file is unaffected, since its directory is the stack directory.
Reconcile inline environment provenance per service, so a key an override
removed from one service's effective env is not labeled compose-inline just
because another service injects the same name from a different source.
This commit is contained in:
@@ -87,6 +87,21 @@ const envUnset: PreflightRule = {
|
||||
},
|
||||
};
|
||||
|
||||
const envFileMissing: PreflightRule = {
|
||||
id: 'env-file-missing',
|
||||
run(ctx) {
|
||||
return ctx.missingEnvFiles.map(f => ({
|
||||
ruleId: 'env-file-missing',
|
||||
severity: 'high' as const,
|
||||
title: `Missing env file ${f.rawPath}`,
|
||||
message: `The Compose file declares env_file "${f.rawPath}"${f.services.length ? ` for service ${f.services.join(', ')}` : ''}, but no such file exists in the stack directory. Compose fails to start the stack when a required env_file is absent.`,
|
||||
sourcePath: f.rawPath,
|
||||
remediation: `Create ${f.rawPath} in the stack directory, fix the path, or mark the entry optional with "required: false".`,
|
||||
service: f.services[0],
|
||||
}));
|
||||
},
|
||||
};
|
||||
|
||||
const portConflictNode: PreflightRule = {
|
||||
id: 'port-conflict-node',
|
||||
run(ctx) {
|
||||
@@ -671,6 +686,7 @@ const sensitiveServiceBroadExposure: PreflightRule = {
|
||||
export const PREFLIGHT_RULES: PreflightRule[] = [
|
||||
renderFailed,
|
||||
envUnset,
|
||||
envFileMissing,
|
||||
portConflictNode,
|
||||
portConflictInternal,
|
||||
portExposedAllInterfaces,
|
||||
|
||||
Reference in New Issue
Block a user