From 968231d0c8aaeecefc71766a9203b982929b2b74 Mon Sep 17 00:00:00 2001 From: vickydotbat Date: Fri, 10 Jul 2026 10:04:40 +0200 Subject: [PATCH] fix fragile hak packing --- internal/topdata/top_package.go | 28 ++++++++++++++--- internal/topdata/topdata.go | 16 ++-------- internal/topdata/topdata_test.go | 54 ++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 18 deletions(-) diff --git a/internal/topdata/top_package.go b/internal/topdata/top_package.go index 32abcf1..a860988 100644 --- a/internal/topdata/top_package.go +++ b/internal/topdata/top_package.go @@ -211,11 +211,12 @@ func collectTopPackageResources(p *project.Project, compiled2DADir string) ([]er if d.IsDir() { return nil } - if strings.HasPrefix(filepath.Base(path), ".") { - return nil + rel, err := filepath.Rel(assetsDir, path) + if err != nil { + return err } - if strings.EqualFold(filepath.Ext(path), ".md") { - return nil // docs (AGENTS.md, README.md, ...) are never HAK resources + if skipTopPackageAsset(rel) { + return nil } resource, err := topPackageResourceFromPath(path) if err != nil { @@ -388,6 +389,25 @@ func newestMatchingAutogenOverrideInput(scanRoot string, include []string) (time return newest, newestPath, nil } +// skipTopPackageAsset reports whether a file under assets/ is not a HAK +// resource and must be ignored by both validation and packing: anything in a +// hidden or underscore-prefixed directory (working dirs like _candidates), +// hidden files, docs, and any extension that is not a known NWN ResType +// (erf.extensionTypes is the whitelist). rel is the path relative to assets/. +func skipTopPackageAsset(rel string) bool { + for _, part := range strings.Split(filepath.ToSlash(rel), "/") { + if strings.HasPrefix(part, ".") || strings.HasPrefix(part, "_") { + return true + } + } + if strings.EqualFold(filepath.Ext(rel), ".md") { + return true + } + ext := strings.TrimPrefix(strings.ToLower(filepath.Ext(rel)), ".") + _, ok := erf.HAKResourceTypeForExtension(ext) + return !ok +} + func topPackageResourceFromPath(path string) (erf.Resource, error) { extension := strings.TrimPrefix(strings.ToLower(filepath.Ext(path)), ".") resourceType, ok := erf.HAKResourceTypeForExtension(extension) diff --git a/internal/topdata/topdata.go b/internal/topdata/topdata.go index 7268600..8291c1e 100644 --- a/internal/topdata/topdata.go +++ b/internal/topdata/topdata.go @@ -12,7 +12,6 @@ import ( "strconv" "strings" - "git.westgate.pw/ShadowsOverWestgate/sow-tools/internal/erf" "git.westgate.pw/ShadowsOverWestgate/sow-tools/internal/project" ) @@ -2230,22 +2229,11 @@ func validateTopPackageAssets(sourceDir, dataDir string, report *ValidationRepor if err != nil { return err } - if strings.HasPrefix(filepath.Base(path), ".") { - return nil - } - if strings.EqualFold(filepath.Ext(path), ".md") { - return nil // docs (AGENTS.md, README.md, ...) are never HAK resources + if skipTopPackageAsset(rel) { + return nil // not a NWN ResType (script, doc, _work dir, ...): never packed, never checked } base := strings.ToLower(strings.TrimSuffix(filepath.Base(path), filepath.Ext(path))) ext := strings.TrimPrefix(strings.ToLower(filepath.Ext(path)), ".") - if _, ok := erf.HAKResourceTypeForExtension(ext); !ok { - report.Diagnostics = append(report.Diagnostics, Diagnostic{ - Severity: SeverityError, - Path: path, - Message: fmt.Sprintf("unsupported topdata asset HAK resource extension %q", filepath.Ext(path)), - }) - return nil - } key := base + "." + ext if previous, ok := seen[key]; ok { report.Diagnostics = append(report.Diagnostics, Diagnostic{ diff --git a/internal/topdata/topdata_test.go b/internal/topdata/topdata_test.go index 4684941..bc6a9a0 100644 --- a/internal/topdata/topdata_test.go +++ b/internal/topdata/topdata_test.go @@ -15116,6 +15116,60 @@ func writeBytes(t *testing.T, path string, content []byte) { } } +func TestSkipTopPackageAsset(t *testing.T) { + skipped := []string{ + "gui/regions/comfyui-generate.sh", // non-NWN extension + "gui/regions/_candidates/abyss.opt4.png", + "gui/.hidden/banner.png", + "gui/.DS_Store", + "gui/AGENTS.md", + "gui/noextension", + } + kept := []string{ + "gui/regions/abyss.png", + "2da-src/placeables.2da", + "tex/floor01.dds", + } + for _, rel := range skipped { + if !skipTopPackageAsset(rel) { + t.Errorf("expected %s to be skipped", rel) + } + } + for _, rel := range kept { + if skipTopPackageAsset(rel) { + t.Errorf("expected %s to be kept", rel) + } + } +} + +func TestValidateTopPackageAssetsIgnoresNonNWNFiles(t *testing.T) { + dir := t.TempDir() + assets := filepath.Join(dir, "assets", "gui", "regions") + candidates := filepath.Join(assets, "_candidates") + if err := os.MkdirAll(candidates, 0o755); err != nil { + t.Fatal(err) + } + for path, content := range map[string]string{ + filepath.Join(assets, "comfyui-generate.sh"): "#!/bin/sh", + filepath.Join(assets, "abyss.png"): "png", + filepath.Join(candidates, "abyss.opt4.png"): "png", + filepath.Join(candidates, "notes.txt.backup"): "junk", + } { + if err := os.WriteFile(path, []byte(content), 0o644); err != nil { + t.Fatal(err) + } + } + dataDir := filepath.Join(dir, "data") + if err := os.MkdirAll(dataDir, 0o755); err != nil { + t.Fatal(err) + } + var report ValidationReport + validateTopPackageAssets(dir, dataDir, &report) + for _, d := range report.Diagnostics { + t.Errorf("unexpected diagnostic: %s: %s", d.Path, d.Message) + } +} + func TestValidateTopPackageAssetsSkipsMarkdown(t *testing.T) { dir := t.TempDir() assets := filepath.Join(dir, "assets", "gui")