fix fragile hak packing
build-binaries / build-binaries (pull_request) Successful in 2m5s
test-image / build-image (pull_request) Successful in 35s
test / test (pull_request) Successful in 1m20s

This commit is contained in:
2026-07-10 10:04:40 +02:00
parent 754375fb08
commit 968231d0c8
3 changed files with 80 additions and 18 deletions
+24 -4
View File
@@ -211,11 +211,12 @@ func collectTopPackageResources(p *project.Project, compiled2DADir string) ([]er
if d.IsDir() { if d.IsDir() {
return nil return nil
} }
if strings.HasPrefix(filepath.Base(path), ".") { rel, err := filepath.Rel(assetsDir, path)
return nil if err != nil {
return err
} }
if strings.EqualFold(filepath.Ext(path), ".md") { if skipTopPackageAsset(rel) {
return nil // docs (AGENTS.md, README.md, ...) are never HAK resources return nil
} }
resource, err := topPackageResourceFromPath(path) resource, err := topPackageResourceFromPath(path)
if err != nil { if err != nil {
@@ -388,6 +389,25 @@ func newestMatchingAutogenOverrideInput(scanRoot string, include []string) (time
return newest, newestPath, nil 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) { func topPackageResourceFromPath(path string) (erf.Resource, error) {
extension := strings.TrimPrefix(strings.ToLower(filepath.Ext(path)), ".") extension := strings.TrimPrefix(strings.ToLower(filepath.Ext(path)), ".")
resourceType, ok := erf.HAKResourceTypeForExtension(extension) resourceType, ok := erf.HAKResourceTypeForExtension(extension)
+2 -14
View File
@@ -12,7 +12,6 @@ import (
"strconv" "strconv"
"strings" "strings"
"git.westgate.pw/ShadowsOverWestgate/sow-tools/internal/erf"
"git.westgate.pw/ShadowsOverWestgate/sow-tools/internal/project" "git.westgate.pw/ShadowsOverWestgate/sow-tools/internal/project"
) )
@@ -2230,22 +2229,11 @@ func validateTopPackageAssets(sourceDir, dataDir string, report *ValidationRepor
if err != nil { if err != nil {
return err return err
} }
if strings.HasPrefix(filepath.Base(path), ".") { if skipTopPackageAsset(rel) {
return nil return nil // not a NWN ResType (script, doc, _work dir, ...): never packed, never checked
}
if strings.EqualFold(filepath.Ext(path), ".md") {
return nil // docs (AGENTS.md, README.md, ...) are never HAK resources
} }
base := strings.ToLower(strings.TrimSuffix(filepath.Base(path), filepath.Ext(path))) base := strings.ToLower(strings.TrimSuffix(filepath.Base(path), filepath.Ext(path)))
ext := strings.TrimPrefix(strings.ToLower(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 key := base + "." + ext
if previous, ok := seen[key]; ok { if previous, ok := seen[key]; ok {
report.Diagnostics = append(report.Diagnostics, Diagnostic{ report.Diagnostics = append(report.Diagnostics, Diagnostic{
+54
View File
@@ -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) { func TestValidateTopPackageAssetsSkipsMarkdown(t *testing.T) {
dir := t.TempDir() dir := t.TempDir()
assets := filepath.Join(dir, "assets", "gui") assets := filepath.Join(dir, "assets", "gui")