Migrate parts data to assets repo

This commit is contained in:
2026-05-17 13:46:09 +02:00
parent faf1459dba
commit d7704c5f68
9 changed files with 652 additions and 48 deletions
+8
View File
@@ -75,6 +75,7 @@ type EffectiveConfig struct {
Paths EffectivePathConfig `json:"paths" yaml:"paths"`
Outputs EffectiveOutputConfig `json:"outputs" yaml:"outputs"`
Build BuildConfig `json:"build" yaml:"build"`
Generated GeneratedConfig `json:"generated_assets" yaml:"generated_assets"`
Inventory InventoryConfig `json:"inventory" yaml:"inventory"`
Validation ValidationConfig `json:"validation" yaml:"validation"`
Scripts EffectiveScriptsConfig `json:"scripts" yaml:"scripts"`
@@ -256,6 +257,11 @@ func (p *Project) EffectiveConfig() EffectiveConfig {
},
},
}
generated := p.Config.Generated
for index := range generated.TopData2DA {
generated.TopData2DA[index].Output = expandPathTemplate(defaultString(generated.TopData2DA[index].Output, "{paths.cache}/generated-assets/"+generated.TopData2DA[index].ID), paths)
}
extract := p.Config.Extract
extract.Layout = defaultString(extract.Layout, DefaultExtractLayout)
extract.HAKDiscovery = defaultString(extract.HAKDiscovery, DefaultExtractHAKDiscovery)
@@ -352,6 +358,7 @@ func (p *Project) EffectiveConfig() EffectiveConfig {
Paths: paths,
Outputs: outputs,
Build: p.Config.Build,
Generated: generated,
Inventory: inventory,
Validation: validation,
Scripts: scripts,
@@ -439,6 +446,7 @@ func markMissingDefaults(provenance ConfigProvenance) {
"outputs.module_archive": DefaultModuleArchiveTemplate,
"outputs.hak_manifest": DefaultHAKManifest,
"outputs.hak_archive": DefaultHAKArchiveTemplate,
"generated_assets.topdata_2da": "no generated topdata 2DA assets",
"inventory.source_extensions": "NWN source resource extensions",
"inventory.asset_extensions": "NWN asset resource extensions",
"inventory.source_json_pattern": DefaultSourceJSONPattern,
+66
View File
@@ -83,6 +83,7 @@ type Config struct {
Paths PathConfig `json:"paths" yaml:"paths"`
Outputs OutputConfig `json:"outputs" yaml:"outputs"`
Build BuildConfig `json:"build" yaml:"build"`
Generated GeneratedConfig `json:"generated_assets" yaml:"generated_assets"`
HAKs []HAKConfig `json:"haks" yaml:"haks"`
Inventory InventoryConfig `json:"inventory" yaml:"inventory"`
Validation ValidationConfig `json:"validation" yaml:"validation"`
@@ -118,6 +119,19 @@ type BuildConfig struct {
KeepExistingHAKs bool `json:"keep_existing_haks,omitempty" yaml:"keep_existing_haks,omitempty"`
}
type GeneratedConfig struct {
TopData2DA []GeneratedTopData2DAConfig `json:"topdata_2da" yaml:"topdata_2da"`
}
type GeneratedTopData2DAConfig struct {
ID string `json:"id" yaml:"id"`
Source string `json:"source" yaml:"source"`
Output string `json:"output" yaml:"output"`
IncludeDatasets []string `json:"include_datasets" yaml:"include_datasets"`
PackageRoot string `json:"package_root" yaml:"package_root"`
Autogen AutogenConsumerConfig `json:"autogen" yaml:"autogen"`
}
type InventoryConfig struct {
SourceExtensions []string `json:"source_extensions" yaml:"source_extensions"`
AssetExtensions []string `json:"asset_extensions" yaml:"asset_extensions"`
@@ -459,6 +473,7 @@ func (p *Project) ValidateLayout() error {
"outputs.module_archive": p.Config.Outputs.ModuleArchive,
"outputs.hak_manifest": p.Config.Outputs.HAKManifest,
"outputs.hak_archive": p.Config.Outputs.HAKArchive,
"generated_assets.topdata_2da": "",
"validation.profile": p.Config.Validation.Profile,
"scripts.source_dir": p.Config.Scripts.SourceDir,
"scripts.cache": p.Config.Scripts.Cache,
@@ -496,6 +511,7 @@ func (p *Project) ValidateLayout() error {
failures = append(failures, validateRelativePath("outputs.module_archive", effective.Outputs.ModuleArchive)...)
failures = append(failures, validateRelativePath("outputs.hak_manifest", effective.Outputs.HAKManifest)...)
failures = append(failures, validateRelativePath("outputs.hak_archive", effective.Outputs.HAKArchive)...)
failures = append(failures, validateGeneratedConfig(effective.Generated)...)
failures = append(failures, validateRelativePath("scripts.cache", effective.Scripts.Cache)...)
failures = append(failures, validateRelativePath("scripts.source_dir", effective.Scripts.SourceDir)...)
failures = append(failures, validateRelativePath("topdata.compiled_2da_dir", effective.TopData.Compiled2DADir)...)
@@ -1301,6 +1317,56 @@ func validateAutogenConfig(cfg AutogenConfig) []error {
return failures
}
func validateGeneratedConfig(cfg GeneratedConfig) []error {
var failures []error
seen := map[string]struct{}{}
for index, top2da := range cfg.TopData2DA {
fieldPrefix := fmt.Sprintf("generated_assets.topdata_2da[%d]", index)
id := strings.TrimSpace(top2da.ID)
if id == "" {
failures = append(failures, fmt.Errorf("%s.id is required", fieldPrefix))
} else {
if _, exists := seen[id]; exists {
failures = append(failures, fmt.Errorf("%s.id %q is duplicated", fieldPrefix, id))
}
seen[id] = struct{}{}
}
if strings.TrimSpace(top2da.Source) == "" {
failures = append(failures, fmt.Errorf("%s.source is required", fieldPrefix))
}
failures = append(failures, validateTreeRootPath(fieldPrefix+".source", top2da.Source)...)
if strings.TrimSpace(top2da.Output) == "" {
failures = append(failures, fmt.Errorf("%s.output is required", fieldPrefix))
}
failures = append(failures, validateRelativePath(fieldPrefix+".output", top2da.Output)...)
if strings.TrimSpace(top2da.PackageRoot) == "" {
failures = append(failures, fmt.Errorf("%s.package_root is required", fieldPrefix))
}
failures = append(failures, validateTreeRootPath(fieldPrefix+".package_root", top2da.PackageRoot)...)
if len(top2da.IncludeDatasets) == 0 {
failures = append(failures, fmt.Errorf("%s.include_datasets must contain at least one pattern", fieldPrefix))
}
failures = append(failures, validateGlobList(fieldPrefix+".include_datasets", top2da.IncludeDatasets)...)
switch strings.TrimSpace(top2da.Autogen.Mode) {
case "parts_rows":
case "":
failures = append(failures, fmt.Errorf("%s.autogen.mode is required", fieldPrefix))
default:
failures = append(failures, fmt.Errorf("%s.autogen.mode %q is not supported", fieldPrefix, top2da.Autogen.Mode))
}
if strings.TrimSpace(top2da.Autogen.Root) == "" {
failures = append(failures, fmt.Errorf("%s.autogen.root is required", fieldPrefix))
}
failures = append(failures, validateTreeRootPath(fieldPrefix+".autogen.root", top2da.Autogen.Root)...)
if len(top2da.Autogen.Include) == 0 {
failures = append(failures, fmt.Errorf("%s.autogen.include must contain at least one glob", fieldPrefix))
}
failures = append(failures, validateGlobList(fieldPrefix+".autogen.include", top2da.Autogen.Include)...)
failures = append(failures, validateAutogenDeriveConfig(fieldPrefix+".autogen.derive", top2da.Autogen.Derive)...)
}
return failures
}
func validateMusicConfig(cfg MusicConfig) []error {
var failures []error
if cfg.Defaults != nil {
+75
View File
@@ -859,6 +859,81 @@ func TestValidateLayoutRejectsInvalidAutogenConsumerConfig(t *testing.T) {
}
}
func TestValidateLayoutAcceptsGeneratedTopData2DAConfig(t *testing.T) {
root := t.TempDir()
mkdirAll(t, filepath.Join(root, "assets"))
mkdirAll(t, filepath.Join(root, "build"))
proj := Project{
Root: root,
Config: Config{
Module: ModuleConfig{Name: "Test", ResRef: "testmod"},
Paths: PathConfig{Assets: "assets"},
Generated: GeneratedConfig{
TopData2DA: []GeneratedTopData2DAConfig{
{
ID: "parts",
Source: "topdata",
Output: "{paths.cache}/generated-assets/parts-2da",
IncludeDatasets: []string{"parts/**"},
PackageRoot: "part",
Autogen: AutogenConsumerConfig{
ID: "parts",
Mode: "parts_rows",
Root: "part",
Include: []string{"**/*.mdl"},
Derive: AutogenDeriveConfig{Kind: "trailing_numeric_suffix", GroupFrom: "first_path_segment"},
},
},
},
},
},
}
if err := proj.ValidateLayout(); err != nil {
t.Fatalf("ValidateLayout returned error: %v", err)
}
effective := proj.EffectiveConfig()
if got, want := effective.Generated.TopData2DA[0].Output, ".cache/generated-assets/parts-2da"; got != want {
t.Fatalf("expected expanded output %q, got %q", want, got)
}
}
func TestValidateLayoutRejectsEscapingGeneratedTopData2DAConfig(t *testing.T) {
root := t.TempDir()
mkdirAll(t, filepath.Join(root, "assets"))
mkdirAll(t, filepath.Join(root, "build"))
proj := Project{
Root: root,
Config: Config{
Module: ModuleConfig{Name: "Test", ResRef: "testmod"},
Paths: PathConfig{Assets: "assets"},
Generated: GeneratedConfig{
TopData2DA: []GeneratedTopData2DAConfig{
{
ID: "parts",
Source: "../topdata",
Output: "{paths.cache}/generated-assets/parts-2da",
IncludeDatasets: []string{"parts/**"},
PackageRoot: "part",
Autogen: AutogenConsumerConfig{
ID: "parts",
Mode: "parts_rows",
Root: "part",
Include: []string{"**/*.mdl"},
Derive: AutogenDeriveConfig{Kind: "trailing_numeric_suffix", GroupFrom: "first_path_segment"},
},
},
},
},
},
}
err := proj.ValidateLayout()
if err == nil || !strings.Contains(err.Error(), "generated_assets.topdata_2da[0].source") {
t.Fatalf("expected generated topdata source validation error, got %v", err)
}
}
func TestValidateLayoutRejectsDuplicateHAKNames(t *testing.T) {
root := t.TempDir()
mkdirAll(t, filepath.Join(root, "src"))