diff --git a/README.md b/README.md index 0b6261a..8d3536e 100644 --- a/README.md +++ b/README.md @@ -316,7 +316,10 @@ resources, so the default module-only extraction does not prune assets. `topdata.wiki.status_listing_scope` controls the generated wiki-status detail pages. The default `all` writes both per-namespace listings and aggregate `meta:wikistatus:` listings. Use `namespace` when aggregate status -lists would be too large for the target NodeBB post limit. +lists would be too large for the target NodeBB post limit. Set +`topdata.wiki.status_pages: false` to omit status pages entirely; existing +tracked status pages then become stale deploy-manifest entries and can be +removed with `deploy-wiki --stale-policy purge`. GFF JSON extraction can merge selected fields and lists from the existing source file instead of replacing the whole extracted document. Rules are matched by diff --git a/internal/project/effective.go b/internal/project/effective.go index 66f7c22..d66e0ac 100644 --- a/internal/project/effective.go +++ b/internal/project/effective.go @@ -48,6 +48,7 @@ const ( DefaultTopDataWikiMarkerFormat = "html_comments" DefaultTopDataWikiPageMarkerPrefix = "sow-topdata-wiki" DefaultTopDataWikiTitlePrefixMinLength = 3 + DefaultTopDataWikiStatusPages = true DefaultTopDataWikiStatusListingScope = "all" DefaultWikiDeployManifest = ".wiki_deploy_manifest.json" DefaultWikiDeployEditSummary = "Auto-generated from native builder" @@ -252,6 +253,7 @@ func (p *Project) EffectiveConfig() EffectiveConfig { TemplatesDir: defaultString(p.Config.TopData.Wiki.TemplatesDir, DefaultTopDataWikiTemplatesDir), ManualSectionsDir: defaultString(p.Config.TopData.Wiki.ManualSectionsDir, DefaultTopDataWikiManualSectionsDir), TitlePrefixMinLength: defaultInt(p.Config.TopData.Wiki.TitlePrefixMinLength, DefaultTopDataWikiTitlePrefixMinLength), + StatusPages: defaultBoolPointer(p.Config.TopData.Wiki.StatusPages, DefaultTopDataWikiStatusPages), StatusListingScope: defaultString(p.Config.TopData.Wiki.StatusListingScope, DefaultTopDataWikiStatusListingScope), StalePages: TopDataWikiStalePagesConfig{ Default: defaultString(p.Config.TopData.Wiki.StalePages.Default, DefaultTopDataWikiStaleDefault), @@ -490,6 +492,7 @@ func markMissingDefaults(provenance ConfigProvenance) { "topdata.wiki.templates_dir": DefaultTopDataWikiTemplatesDir, "topdata.wiki.manual_sections_dir": DefaultTopDataWikiManualSectionsDir, "topdata.wiki.title_prefix_min_length": "3", + "topdata.wiki.status_pages": "true", "topdata.wiki.status_listing_scope": DefaultTopDataWikiStatusListingScope, "topdata.wiki.stale_pages.default": DefaultTopDataWikiStaleDefault, "topdata.wiki.stale_pages.live_cleanup": DefaultTopDataWikiStaleLiveCleanup, @@ -538,6 +541,14 @@ func defaultInt(value, fallback int) int { return value } +func defaultBoolPointer(value *bool, fallback bool) *bool { + out := fallback + if value != nil { + out = *value + } + return &out +} + func defaultStringSlice(value, fallback []string) []string { if len(value) == 0 { return slices.Clone(fallback) diff --git a/internal/project/project.go b/internal/project/project.go index d73d2a1..d68543e 100644 --- a/internal/project/project.go +++ b/internal/project/project.go @@ -233,6 +233,7 @@ type TopDataWikiConfig struct { TemplatesDir string `json:"templates_dir" yaml:"templates_dir"` ManualSectionsDir string `json:"manual_sections_dir" yaml:"manual_sections_dir"` TitlePrefixMinLength int `json:"title_prefix_min_length" yaml:"title_prefix_min_length"` + StatusPages *bool `json:"status_pages,omitempty" yaml:"status_pages,omitempty"` StatusListingScope string `json:"status_listing_scope" yaml:"status_listing_scope"` StalePages TopDataWikiStalePagesConfig `json:"stale_pages" yaml:"stale_pages"` ManagedRegion TopDataWikiManagedRegionConfig `json:"managed_region" yaml:"managed_region"` diff --git a/internal/project/project_test.go b/internal/project/project_test.go index 217f446..45db165 100644 --- a/internal/project/project_test.go +++ b/internal/project/project_test.go @@ -280,6 +280,7 @@ topdata: deploy_manifest: wiki-manifest.json deploy_edit_summary: Custom summary title_prefix_min_length: 4 + status_pages: false status_listing_scope: namespace stale_pages: default: report @@ -367,6 +368,9 @@ autogen: if got, want := effective.TopData.Wiki.TitlePrefixMinLength, 4; got != want { t.Fatalf("expected wiki title prefix minimum length %d, got %d", want, got) } + if effective.TopData.Wiki.StatusPages == nil || *effective.TopData.Wiki.StatusPages { + t.Fatalf("expected wiki status pages to be disabled, got %#v", effective.TopData.Wiki.StatusPages) + } if got, want := effective.TopData.Wiki.StatusListingScope, "namespace"; got != want { t.Fatalf("expected wiki status listing scope %q, got %q", want, got) } diff --git a/internal/topdata/wiki_native.go b/internal/topdata/wiki_native.go index 398f7de..8a1e932 100644 --- a/internal/topdata/wiki_native.go +++ b/internal/topdata/wiki_native.go @@ -233,8 +233,10 @@ func buildWiki(p *project.Project, nativeResult BuildResult, force bool, progres if err := generateEntityPages(outputDir, ctx, writePage); err != nil { return wikiResult{}, err } - if err := generateStatusPages(outputDir, pageStatuses, pageTitles, p.EffectiveConfig().TopData.Wiki.ManagedNamespaces, p.EffectiveConfig().TopData.Wiki.StatusListingScope, manualSections, ctx); err != nil { - return wikiResult{}, err + if wikiStatusPagesEnabled(p.EffectiveConfig().TopData.Wiki) { + if err := generateStatusPages(outputDir, pageStatuses, pageTitles, p.EffectiveConfig().TopData.Wiki.ManagedNamespaces, p.EffectiveConfig().TopData.Wiki.StatusListingScope, manualSections, ctx); err != nil { + return wikiResult{}, err + } } if err := indexUnregisteredWikiPages(rootDir, outputDir, p.EffectiveConfig().TopData.Wiki.StalePages.Default, ctx, &pageIndex); err != nil { return wikiResult{}, err @@ -259,6 +261,10 @@ func buildWiki(p *project.Project, nativeResult BuildResult, force bool, progres }, nil } +func wikiStatusPagesEnabled(cfg project.TopDataWikiConfig) bool { + return cfg.StatusPages == nil || *cfg.StatusPages +} + func wikiCachedPagesAreCurrent(outputDir string, state wikiStateDocument) bool { if state.Pages <= 0 { return false diff --git a/internal/topdata/wiki_native_test.go b/internal/topdata/wiki_native_test.go index ad1c8ee..5da8f9d 100644 --- a/internal/topdata/wiki_native_test.go +++ b/internal/topdata/wiki_native_test.go @@ -316,6 +316,54 @@ func TestBuildNativeCanOmitAggregateWikiStatusListings(t *testing.T) { } } +func TestBuildNativeCanDisableWikiStatusPages(t *testing.T) { + root := testProjectRoot(t) + mkdirAll(t, filepath.Join(root, "topdata", "data", "skills")) + writeFile(t, filepath.Join(root, "topdata", "base_dialog.json"), "{}\n") + writeFile(t, filepath.Join(root, "topdata", "data", "skills", "base.json"), `{ + "output": "skills.2da", + "columns": ["Label", "Name", "Description", "HideFromLevelUp", "Untrained", "KeyAbility", "ArmorCheckPenalty", "Constant"], + "rows": [ + { + "id": 0, + "key": "skills:athletics", + "Label": "Athletics", + "Name": {"tlk": {"key": "skills:athletics.name", "text": "Athletics"}}, + "Description": {"tlk": {"key": "skills:athletics.description", "text": "Ability: Strength."}}, + "HideFromLevelUp": "0", + "Untrained": "1", + "KeyAbility": "STR", + "ArmorCheckPenalty": "1", + "Constant": "SKILL_ATHLETICS" + } + ] +}`+"\n") + writeFile(t, filepath.Join(root, "topdata", "data", "skills", "lock.json"), `{"skills:athletics":0}`+"\n") + + disabled := false + proj := testProject(root) + proj.Config.TopData.ReferenceBuilder = "" + proj.Config.TopData.Wiki.StatusPages = &disabled + + result, err := BuildNative(proj, nil) + if err != nil { + t.Fatalf("BuildNative failed: %v", err) + } + if _, err := os.Stat(filepath.Join(root, ".cache", "wiki", "pages", "meta")); !os.IsNotExist(err) { + t.Fatalf("expected wiki status pages to be disabled, got err=%v", err) + } + indexRaw, err := os.ReadFile(filepath.Join(root, ".cache", "wiki", "page-index.json")) + if err != nil { + t.Fatalf("read page index: %v", err) + } + if strings.Contains(string(indexRaw), `"namespace": "meta"`) || strings.Contains(string(indexRaw), `"meta:wikistatus`) { + t.Fatalf("expected disabled status pages to be omitted from page-index:\n%s", indexRaw) + } + if result.WikiPages != 1 { + t.Fatalf("expected only entity page to be generated, got %d pages", result.WikiPages) + } +} + func countGeneratedWikiHTMLFiles(t *testing.T, root string) int { t.Helper() count := 0