Configuration Hardening 2

What changed:

  - Added visible runtime override reporting with masking for sensitive values.
  - Expanded effective config for build cleanup, script compiler discovery/env, extract layout/cleanup/
    HAK discovery, wiki deploy defaults, autogen cache policy, and release-source env names.
  - Wired configured values into build, script compiler resolution, extract, autogen cache refresh/max
    age, parts manifest release source, wiki generation/deploy, and TLK output naming.
  - Added config sources override output and validation-rule hints in config explain.
  - Added CONFIGURATION_HARDENING_AUDIT.md.
  - Updated README.md with config commands, schema defaults, and JSON artifact boundaries.
  - Added regression tests for override visibility/masking, compiler/extract/wiki/autogen config,
    configured HAK discovery, and config sources output.
This commit is contained in:
2026-05-07 14:22:47 +02:00
parent 60d2de9f2d
commit b39bdf13e8
17 changed files with 877 additions and 171 deletions
+25 -25
View File
@@ -215,7 +215,7 @@ func planHAKs(p *project.Project, progress ProgressFunc) (BuildResult, error) {
}
func planOrBuildHAKs(p *project.Project, progress ProgressFunc, writeArchives bool, archiveNames []string, sourceManifestPath string) (result BuildResult, err error) {
preserveExistingHAKs := envBool("SOW_BUILD_HAKS_KEEP_EXISTING")
preserveExistingHAKs := p.EffectiveConfig().Build.KeepExistingHAKs || envBool("SOW_BUILD_HAKS_KEEP_EXISTING")
progressf(progress, "Validating project...")
if err := validateForBuild(p); err != nil {
@@ -556,7 +556,7 @@ func compileReferencedScripts(p *project.Project) ([]erf.Resource, error) {
if err != nil {
return nil, err
}
compilerEnv, err := resolveScriptCompilerEnvironment()
compilerEnv, err := resolveScriptCompilerEnvironment(p)
if err != nil {
return nil, err
}
@@ -630,40 +630,40 @@ func referencedScriptResrefs(p *project.Project) ([]string, error) {
}
func resolveScriptCompiler(p *project.Project) (string, error) {
if configured := strings.TrimSpace(os.Getenv("SOW_NWN_SCRIPT_COMPILER")); configured != "" {
if configured := p.ScriptCompilerPath(); configured != "" {
return configured, nil
}
if configured := strings.TrimSpace(os.Getenv(p.ScriptCompilerPathEnv())); configured != "" {
return configured, nil
}
candidates := []string{
filepath.Join(p.Root, "tools", "script-compiler", "nwn_script_comp"),
filepath.Join(p.Root, "tools", "script-compiler", "nwn_script_comp.exe"),
filepath.Join(p.Root, "tools", "nwn_script_comp"),
filepath.Join(p.Root, "tools", "nwn_script_comp.exe"),
}
candidates := p.ScriptCompilerSearchPaths()
if runtime.GOOS == "windows" {
candidates = []string{
filepath.Join(p.Root, "tools", "script-compiler", "nwn_script_comp.exe"),
filepath.Join(p.Root, "tools", "script-compiler", "nwn_script_comp"),
filepath.Join(p.Root, "tools", "nwn_script_comp.exe"),
filepath.Join(p.Root, "tools", "nwn_script_comp"),
}
slices.SortStableFunc(candidates, func(a, b string) int {
aExe := strings.HasSuffix(strings.ToLower(a), ".exe")
bExe := strings.HasSuffix(strings.ToLower(b), ".exe")
if aExe == bExe {
return strings.Compare(a, b)
}
if aExe {
return -1
}
return 1
})
}
for _, candidate := range candidates {
if binary, ok := strings.CutPrefix(candidate, "PATH:"); ok {
if compiler, err := exec.LookPath(binary); err == nil {
return compiler, nil
}
continue
}
if info, err := os.Stat(candidate); err == nil && !info.IsDir() {
return candidate, nil
}
}
if compiler, err := exec.LookPath("nwn_script_comp"); err == nil {
return compiler, nil
}
if runtime.GOOS == "windows" {
if compiler, err := exec.LookPath("nwn_script_comp.exe"); err == nil {
return compiler, nil
}
}
return "", errors.New("script compiler not found; install nwn_script_comp into ./tools or set SOW_NWN_SCRIPT_COMPILER")
return "", fmt.Errorf("script compiler not found; configure scripts.compiler.path or set %s", p.ScriptCompilerPathEnv())
}
func validatorLoadDocument(path string) (gff.Document, string, string, error) {