Wiki pipeline (#3)
Implemented the wiki pipeline to a shippable local state across `toolkit/`, `module/`, and the NodeBB wiki plugin.
**Scope Audited**
- Toolkit config/project loading, wiki renderer, deployer, app command wiring.
- Module `nwn-tool.yaml`, wiki source/docs, release script, topdata docs.
- Plugin sanitizer/API storage contract docs and sanitizer tests.
**Changes Made**
- Added topdata-owned wiki declarations under `module/topdata/wiki/`.
- Added `topdata.wiki.*` config fields, validation, effective config output.
- Switched generated wiki pages to `.html` with HTML comment managed/manual markers.
- Added `page-index.json` generation and deterministic metadata.
- Added manual-section merge preservation and stale page `report`/`archive` handling.
- Added namespace `category_env` loading from `topdata/wiki/namespaces.yaml`, while keeping `--category`/`NODEBB_WIKI_CATEGORIES` overrides.
- Updated release deployment to keep dry-run first and make stale cleanup opt-in via `SOW_MODULE_WIKI_DEPLOY_STALE_POLICY`.
- Updated plugin sanitizer to preserve only `sow-topdata-wiki` comments.
**Configuration/Schema Impact**
- `module/nwn-tool.yaml` now declares wiki source, renderer, link strategy, templates, manual sections, managed marker policy, and stale policy.
- Deploy manifest entries now support stale/archive metadata: `stale`, `last_seen_hash`, `archived_hash`, `title`, `namespace`, `tid`, `pid`, `cid`.
**Compatibility Impact**
- Legacy Markdown managed markers are still readable for migration.
- Generated output is now HTML, not Markdown.
- Existing mapped pages update by `pid`; creates still require explicit `--create`.
**Tests Added/Updated**
- Go tests for wiki config validation, HTML rendering, page index, manual merge, stale report/archive, app summary output.
- Plugin sanitizer test for generated topdata bot HTML markers/subset.
**Validation Performed**
- `go test ./internal/project ./internal/topdata ./internal/app` passed.
- `./build-tool.sh` passed.
- `SOW_TOOLS_DEV_BINARY=../toolkit/tools/sow-toolkit ./validate-topdata.sh` passed.
- `SOW_TOOLS_DEV_BINARY=../toolkit/tools/sow-toolkit ./build-wiki.sh --force` passed, generating 1345 entity pages.
- `deploy-wiki --dry-run --create` with dummy endpoint/token and namespace CID env vars passed locally: 1377 planned creates, 0 stale/drift.
- `node tests/wiki-html-sanitizer.test.js` passed.
**Remaining Risks**
- Full plugin `npm test` is blocked by an existing unrelated drawer CSS contract failure in `tests/wiki-article-drawers.test.js`.
- Controlled live migration was not performed because it requires a non-production NodeBB namespace and credentials.
- Direct `PUT /api/v3/posts/{pid}` save-filter behavior is documented as needing live confirmation against the deployed NodeBB/plugin stack.
Reviewed-on: https://gitea.westgate.pw/ShadowsOverWestgate/sow-tools/pulls/3
Co-authored-by: vickydotbat <vickydotbat@tutamail.com>
Co-committed-by: vickydotbat <vickydotbat@tutamail.com>
This commit is contained in:
+115
-81
@@ -13,41 +13,51 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultBuildPath = "build"
|
||||
DefaultCachePath = ".cache"
|
||||
DefaultToolsPath = "tools"
|
||||
DefaultModuleArchiveTemplate = "{module.resref}.mod"
|
||||
DefaultHAKManifest = "haks.json"
|
||||
DefaultHAKArchiveTemplate = "{hak.name}.hak"
|
||||
DefaultSourceJSONPattern = "{resref}.{extension}.json"
|
||||
DefaultValidationProfile = "nwn_module"
|
||||
DefaultScriptsSourceDir = "scripts"
|
||||
DefaultScriptsCache = "{paths.cache}/ncs"
|
||||
DefaultScriptCompilerEnvPath = "SOW_NWN_SCRIPT_COMPILER"
|
||||
DefaultMusicStageRoot = "{paths.cache}/music"
|
||||
DefaultCreditsRoot = "{paths.cache}/credits"
|
||||
DefaultCreditsOverlayFile = "CREDITS.md"
|
||||
DefaultMusicNamingScheme = "nwn_bmu"
|
||||
DefaultMusicOutputExtension = ".bmu"
|
||||
DefaultTopDataBuild = "{paths.build}/topdata"
|
||||
DefaultTopDataCompiled2DADir = "2da"
|
||||
DefaultTopDataCompiledTLK = "sow_tlk.tlk"
|
||||
DefaultTopDataWikiOutputRoot = "wiki"
|
||||
DefaultTopDataWikiPagesDir = "pages"
|
||||
DefaultTopDataWikiStateFile = "state.json"
|
||||
DefaultWikiDeployManifest = ".wiki_deploy_manifest.json"
|
||||
DefaultWikiDeployEditSummary = "Auto-generated from native builder"
|
||||
DefaultTopDataPackageHAK = "sow_top.hak"
|
||||
DefaultTopDataPackageTLK = "sow_tlk.tlk"
|
||||
DefaultAutogenCacheRoot = "{paths.cache}"
|
||||
DefaultAutogenCacheMaxAge = time.Hour
|
||||
DefaultAutogenRefreshEnv = "SOW_AUTOGEN_MANIFEST_REFRESH"
|
||||
DefaultPartsManifestRefreshEnv = "SOW_PARTS_MANIFEST_REFRESH"
|
||||
DefaultAutogenReleaseProvider = "gitea"
|
||||
DefaultAutogenServerURLEnv = "SOW_ASSETS_SERVER_URL"
|
||||
DefaultAutogenRepoEnv = "SOW_ASSETS_REPO"
|
||||
DefaultExtractLayout = "nwn_canonical_json"
|
||||
DefaultExtractHAKDiscovery = "build_glob"
|
||||
DefaultBuildPath = "build"
|
||||
DefaultCachePath = ".cache"
|
||||
DefaultToolsPath = "tools"
|
||||
DefaultModuleArchiveTemplate = "{module.resref}.mod"
|
||||
DefaultHAKManifest = "haks.json"
|
||||
DefaultHAKArchiveTemplate = "{hak.name}.hak"
|
||||
DefaultSourceJSONPattern = "{resref}.{extension}.json"
|
||||
DefaultValidationProfile = "nwn_module"
|
||||
DefaultScriptsSourceDir = "scripts"
|
||||
DefaultScriptsCache = "{paths.cache}/ncs"
|
||||
DefaultScriptCompilerEnvPath = "SOW_NWN_SCRIPT_COMPILER"
|
||||
DefaultMusicStageRoot = "{paths.cache}/music"
|
||||
DefaultCreditsRoot = "{paths.cache}/credits"
|
||||
DefaultCreditsOverlayFile = "CREDITS.md"
|
||||
DefaultMusicNamingScheme = "nwn_bmu"
|
||||
DefaultMusicOutputExtension = ".bmu"
|
||||
DefaultTopDataBuild = "{paths.build}/topdata"
|
||||
DefaultTopDataCompiled2DADir = "2da"
|
||||
DefaultTopDataCompiledTLK = "sow_tlk.tlk"
|
||||
DefaultTopDataWikiOutputRoot = "wiki"
|
||||
DefaultTopDataWikiPagesDir = "pages"
|
||||
DefaultTopDataWikiStateFile = "state.json"
|
||||
DefaultTopDataWikiSource = "topdata/wiki"
|
||||
DefaultTopDataWikiRenderer = "nodebb_tiptap_html"
|
||||
DefaultTopDataWikiLinkStrategy = "preserve_westgate_wiki_links"
|
||||
DefaultTopDataWikiNamespacesFile = "namespaces.yaml"
|
||||
DefaultTopDataWikiTemplatesDir = "templates"
|
||||
DefaultTopDataWikiManualSectionsDir = "manual-sections"
|
||||
DefaultTopDataWikiStaleDefault = "report"
|
||||
DefaultTopDataWikiStaleLiveCleanup = "archive"
|
||||
DefaultTopDataWikiMarkerFormat = "html_comments"
|
||||
DefaultTopDataWikiPageMarkerPrefix = "sow-topdata-wiki"
|
||||
DefaultWikiDeployManifest = ".wiki_deploy_manifest.json"
|
||||
DefaultWikiDeployEditSummary = "Auto-generated from native builder"
|
||||
DefaultTopDataPackageHAK = "sow_top.hak"
|
||||
DefaultTopDataPackageTLK = "sow_tlk.tlk"
|
||||
DefaultAutogenCacheRoot = "{paths.cache}"
|
||||
DefaultAutogenCacheMaxAge = time.Hour
|
||||
DefaultAutogenRefreshEnv = "SOW_AUTOGEN_MANIFEST_REFRESH"
|
||||
DefaultPartsManifestRefreshEnv = "SOW_PARTS_MANIFEST_REFRESH"
|
||||
DefaultAutogenReleaseProvider = "gitea"
|
||||
DefaultAutogenServerURLEnv = "SOW_ASSETS_SERVER_URL"
|
||||
DefaultAutogenRepoEnv = "SOW_ASSETS_REPO"
|
||||
DefaultExtractLayout = "nwn_canonical_json"
|
||||
DefaultExtractHAKDiscovery = "build_glob"
|
||||
)
|
||||
|
||||
type ConfigProvenance map[string]ConfigValueProvenance
|
||||
@@ -228,6 +238,20 @@ func (p *Project) EffectiveConfig() EffectiveConfig {
|
||||
ManagedNamespaces: defaultStringSlice(p.Config.TopData.Wiki.ManagedNamespaces, []string{"classes", "feat", "itemtypes", "races", "skills", "spells", "meta"}),
|
||||
DeployManifest: defaultString(p.Config.TopData.Wiki.DeployManifest, DefaultWikiDeployManifest),
|
||||
DeployEditSummary: defaultString(p.Config.TopData.Wiki.DeployEditSummary, DefaultWikiDeployEditSummary),
|
||||
Source: defaultString(p.Config.TopData.Wiki.Source, DefaultTopDataWikiSource),
|
||||
Renderer: defaultString(p.Config.TopData.Wiki.Renderer, DefaultTopDataWikiRenderer),
|
||||
LinkStrategy: defaultString(p.Config.TopData.Wiki.LinkStrategy, DefaultTopDataWikiLinkStrategy),
|
||||
NamespacesFile: defaultString(p.Config.TopData.Wiki.NamespacesFile, DefaultTopDataWikiNamespacesFile),
|
||||
TemplatesDir: defaultString(p.Config.TopData.Wiki.TemplatesDir, DefaultTopDataWikiTemplatesDir),
|
||||
ManualSectionsDir: defaultString(p.Config.TopData.Wiki.ManualSectionsDir, DefaultTopDataWikiManualSectionsDir),
|
||||
StalePages: TopDataWikiStalePagesConfig{
|
||||
Default: defaultString(p.Config.TopData.Wiki.StalePages.Default, DefaultTopDataWikiStaleDefault),
|
||||
LiveCleanup: defaultString(p.Config.TopData.Wiki.StalePages.LiveCleanup, DefaultTopDataWikiStaleLiveCleanup),
|
||||
},
|
||||
ManagedRegion: TopDataWikiManagedRegionConfig{
|
||||
MarkerFormat: defaultString(p.Config.TopData.Wiki.ManagedRegion.MarkerFormat, DefaultTopDataWikiMarkerFormat),
|
||||
PageMarkerPrefix: defaultString(p.Config.TopData.Wiki.ManagedRegion.PageMarkerPrefix, DefaultTopDataWikiPageMarkerPrefix),
|
||||
},
|
||||
},
|
||||
}
|
||||
extract := p.Config.Extract
|
||||
@@ -407,52 +431,62 @@ func lookupEffectiveValue(effective EffectiveConfig, key string) (any, bool) {
|
||||
|
||||
func markMissingDefaults(provenance ConfigProvenance) {
|
||||
defaults := map[string]string{
|
||||
"paths.build": "build",
|
||||
"paths.cache": ".cache",
|
||||
"paths.tools": "tools",
|
||||
"outputs.module_archive": DefaultModuleArchiveTemplate,
|
||||
"outputs.hak_manifest": DefaultHAKManifest,
|
||||
"outputs.hak_archive": DefaultHAKArchiveTemplate,
|
||||
"inventory.source_extensions": "NWN source resource extensions",
|
||||
"inventory.asset_extensions": "NWN asset resource extensions",
|
||||
"inventory.source_json_pattern": DefaultSourceJSONPattern,
|
||||
"validation.profile": DefaultValidationProfile,
|
||||
"validation.builtin_script_prefixes": "NWN built-in script prefixes",
|
||||
"validation.required_fields": "NWN required GFF fields",
|
||||
"scripts.source_dir": DefaultScriptsSourceDir,
|
||||
"scripts.cache": DefaultScriptsCache,
|
||||
"scripts.compiler.env.path": DefaultScriptCompilerEnvPath,
|
||||
"scripts.compiler.search": "repo tools and PATH:nwn_script_comp",
|
||||
"music.stage_root": DefaultMusicStageRoot,
|
||||
"music.credits_root": DefaultCreditsRoot,
|
||||
"music.credits_overlay": DefaultCreditsOverlayFile,
|
||||
"music.max_stem_length": "16",
|
||||
"music.naming_scheme": DefaultMusicNamingScheme,
|
||||
"music.output_extension": DefaultMusicOutputExtension,
|
||||
"music.convert_extensions": strings.Join(music.DefaultConvertExtensions(), ", "),
|
||||
"topdata.build": DefaultTopDataBuild,
|
||||
"topdata.compiled_2da_dir": DefaultTopDataCompiled2DADir,
|
||||
"topdata.compiled_tlk": DefaultTopDataCompiledTLK,
|
||||
"topdata.package_hak": DefaultTopDataPackageHAK,
|
||||
"topdata.package_tlk": DefaultTopDataPackageTLK,
|
||||
"topdata.wiki.output_root": DefaultTopDataWikiOutputRoot,
|
||||
"topdata.wiki.pages_dir": DefaultTopDataWikiPagesDir,
|
||||
"topdata.wiki.state_file": DefaultTopDataWikiStateFile,
|
||||
"topdata.wiki.managed_namespaces": "NWN topdata wiki namespaces",
|
||||
"topdata.wiki.deploy_manifest": DefaultWikiDeployManifest,
|
||||
"topdata.wiki.deploy_edit_summary": DefaultWikiDeployEditSummary,
|
||||
"extract.layout": DefaultExtractLayout,
|
||||
"extract.hak_discovery": DefaultExtractHAKDiscovery,
|
||||
"extract.archives": DefaultModuleArchiveTemplate,
|
||||
"extract.cleanup_stale": "true",
|
||||
"extract.consume_archives": "false",
|
||||
"autogen.cache.root": DefaultAutogenCacheRoot,
|
||||
"autogen.cache.max_age": DefaultAutogenCacheMaxAge.String(),
|
||||
"autogen.cache.refresh_env": DefaultAutogenRefreshEnv,
|
||||
"autogen.cache.parts_manifest_refresh_env": DefaultPartsManifestRefreshEnv,
|
||||
"autogen.release_source.provider": DefaultAutogenReleaseProvider,
|
||||
"autogen.release_source.server_url_env": DefaultAutogenServerURLEnv,
|
||||
"autogen.release_source.repo_env": DefaultAutogenRepoEnv,
|
||||
"paths.build": "build",
|
||||
"paths.cache": ".cache",
|
||||
"paths.tools": "tools",
|
||||
"outputs.module_archive": DefaultModuleArchiveTemplate,
|
||||
"outputs.hak_manifest": DefaultHAKManifest,
|
||||
"outputs.hak_archive": DefaultHAKArchiveTemplate,
|
||||
"inventory.source_extensions": "NWN source resource extensions",
|
||||
"inventory.asset_extensions": "NWN asset resource extensions",
|
||||
"inventory.source_json_pattern": DefaultSourceJSONPattern,
|
||||
"validation.profile": DefaultValidationProfile,
|
||||
"validation.builtin_script_prefixes": "NWN built-in script prefixes",
|
||||
"validation.required_fields": "NWN required GFF fields",
|
||||
"scripts.source_dir": DefaultScriptsSourceDir,
|
||||
"scripts.cache": DefaultScriptsCache,
|
||||
"scripts.compiler.env.path": DefaultScriptCompilerEnvPath,
|
||||
"scripts.compiler.search": "repo tools and PATH:nwn_script_comp",
|
||||
"music.stage_root": DefaultMusicStageRoot,
|
||||
"music.credits_root": DefaultCreditsRoot,
|
||||
"music.credits_overlay": DefaultCreditsOverlayFile,
|
||||
"music.max_stem_length": "16",
|
||||
"music.naming_scheme": DefaultMusicNamingScheme,
|
||||
"music.output_extension": DefaultMusicOutputExtension,
|
||||
"music.convert_extensions": strings.Join(music.DefaultConvertExtensions(), ", "),
|
||||
"topdata.build": DefaultTopDataBuild,
|
||||
"topdata.compiled_2da_dir": DefaultTopDataCompiled2DADir,
|
||||
"topdata.compiled_tlk": DefaultTopDataCompiledTLK,
|
||||
"topdata.package_hak": DefaultTopDataPackageHAK,
|
||||
"topdata.package_tlk": DefaultTopDataPackageTLK,
|
||||
"topdata.wiki.output_root": DefaultTopDataWikiOutputRoot,
|
||||
"topdata.wiki.pages_dir": DefaultTopDataWikiPagesDir,
|
||||
"topdata.wiki.state_file": DefaultTopDataWikiStateFile,
|
||||
"topdata.wiki.managed_namespaces": "NWN topdata wiki namespaces",
|
||||
"topdata.wiki.deploy_manifest": DefaultWikiDeployManifest,
|
||||
"topdata.wiki.deploy_edit_summary": DefaultWikiDeployEditSummary,
|
||||
"topdata.wiki.source": DefaultTopDataWikiSource,
|
||||
"topdata.wiki.renderer": DefaultTopDataWikiRenderer,
|
||||
"topdata.wiki.link_strategy": DefaultTopDataWikiLinkStrategy,
|
||||
"topdata.wiki.namespaces_file": DefaultTopDataWikiNamespacesFile,
|
||||
"topdata.wiki.templates_dir": DefaultTopDataWikiTemplatesDir,
|
||||
"topdata.wiki.manual_sections_dir": DefaultTopDataWikiManualSectionsDir,
|
||||
"topdata.wiki.stale_pages.default": DefaultTopDataWikiStaleDefault,
|
||||
"topdata.wiki.stale_pages.live_cleanup": DefaultTopDataWikiStaleLiveCleanup,
|
||||
"topdata.wiki.managed_region.marker_format": DefaultTopDataWikiMarkerFormat,
|
||||
"topdata.wiki.managed_region.page_marker_prefix": DefaultTopDataWikiPageMarkerPrefix,
|
||||
"extract.layout": DefaultExtractLayout,
|
||||
"extract.hak_discovery": DefaultExtractHAKDiscovery,
|
||||
"extract.archives": DefaultModuleArchiveTemplate,
|
||||
"extract.cleanup_stale": "true",
|
||||
"extract.consume_archives": "false",
|
||||
"autogen.cache.root": DefaultAutogenCacheRoot,
|
||||
"autogen.cache.max_age": DefaultAutogenCacheMaxAge.String(),
|
||||
"autogen.cache.refresh_env": DefaultAutogenRefreshEnv,
|
||||
"autogen.cache.parts_manifest_refresh_env": DefaultPartsManifestRefreshEnv,
|
||||
"autogen.release_source.provider": DefaultAutogenReleaseProvider,
|
||||
"autogen.release_source.server_url_env": DefaultAutogenServerURLEnv,
|
||||
"autogen.release_source.repo_env": DefaultAutogenRepoEnv,
|
||||
}
|
||||
for key, detail := range defaults {
|
||||
if _, ok := provenance[key]; !ok {
|
||||
|
||||
@@ -204,12 +204,30 @@ type TopDataConfig struct {
|
||||
}
|
||||
|
||||
type TopDataWikiConfig struct {
|
||||
OutputRoot string `json:"output_root" yaml:"output_root"`
|
||||
PagesDir string `json:"pages_dir" yaml:"pages_dir"`
|
||||
StateFile string `json:"state_file" yaml:"state_file"`
|
||||
ManagedNamespaces []string `json:"managed_namespaces" yaml:"managed_namespaces"`
|
||||
DeployManifest string `json:"deploy_manifest" yaml:"deploy_manifest"`
|
||||
DeployEditSummary string `json:"deploy_edit_summary" yaml:"deploy_edit_summary"`
|
||||
OutputRoot string `json:"output_root" yaml:"output_root"`
|
||||
PagesDir string `json:"pages_dir" yaml:"pages_dir"`
|
||||
StateFile string `json:"state_file" yaml:"state_file"`
|
||||
ManagedNamespaces []string `json:"managed_namespaces" yaml:"managed_namespaces"`
|
||||
DeployManifest string `json:"deploy_manifest" yaml:"deploy_manifest"`
|
||||
DeployEditSummary string `json:"deploy_edit_summary" yaml:"deploy_edit_summary"`
|
||||
Source string `json:"source" yaml:"source"`
|
||||
Renderer string `json:"renderer" yaml:"renderer"`
|
||||
LinkStrategy string `json:"link_strategy" yaml:"link_strategy"`
|
||||
NamespacesFile string `json:"namespaces_file" yaml:"namespaces_file"`
|
||||
TemplatesDir string `json:"templates_dir" yaml:"templates_dir"`
|
||||
ManualSectionsDir string `json:"manual_sections_dir" yaml:"manual_sections_dir"`
|
||||
StalePages TopDataWikiStalePagesConfig `json:"stale_pages" yaml:"stale_pages"`
|
||||
ManagedRegion TopDataWikiManagedRegionConfig `json:"managed_region" yaml:"managed_region"`
|
||||
}
|
||||
|
||||
type TopDataWikiStalePagesConfig struct {
|
||||
Default string `json:"default" yaml:"default"`
|
||||
LiveCleanup string `json:"live_cleanup" yaml:"live_cleanup"`
|
||||
}
|
||||
|
||||
type TopDataWikiManagedRegionConfig struct {
|
||||
MarkerFormat string `json:"marker_format" yaml:"marker_format"`
|
||||
PageMarkerPrefix string `json:"page_marker_prefix" yaml:"page_marker_prefix"`
|
||||
}
|
||||
|
||||
type ExtractConfig struct {
|
||||
@@ -485,6 +503,10 @@ func (p *Project) ValidateLayout() error {
|
||||
failures = append(failures, validateRelativePath("topdata.wiki.pages_dir", effective.TopData.Wiki.PagesDir)...)
|
||||
failures = append(failures, validateRelativePath("topdata.wiki.state_file", effective.TopData.Wiki.StateFile)...)
|
||||
failures = append(failures, validateRelativePath("topdata.wiki.deploy_manifest", effective.TopData.Wiki.DeployManifest)...)
|
||||
failures = append(failures, validateTreeRootPath("topdata.wiki.source", effective.TopData.Wiki.Source)...)
|
||||
failures = append(failures, validateRelativePath("topdata.wiki.namespaces_file", effective.TopData.Wiki.NamespacesFile)...)
|
||||
failures = append(failures, validateTreeRootPath("topdata.wiki.templates_dir", effective.TopData.Wiki.TemplatesDir)...)
|
||||
failures = append(failures, validateTreeRootPath("topdata.wiki.manual_sections_dir", effective.TopData.Wiki.ManualSectionsDir)...)
|
||||
failures = append(failures, validateRelativePath("autogen.cache.root", effective.Autogen.Cache.Root)...)
|
||||
if err := validateOutputFileName("outputs.module_archive", filepath.Base(effective.Outputs.ModuleArchive), ".mod"); err != nil {
|
||||
failures = append(failures, err)
|
||||
@@ -498,6 +520,26 @@ func (p *Project) ValidateLayout() error {
|
||||
if err := validateOutputFileName("topdata.wiki.deploy_manifest", filepath.Base(effective.TopData.Wiki.DeployManifest), ".json"); err != nil {
|
||||
failures = append(failures, err)
|
||||
}
|
||||
switch effective.TopData.Wiki.Renderer {
|
||||
case "nodebb_tiptap_html":
|
||||
default:
|
||||
failures = append(failures, fmt.Errorf("topdata.wiki.renderer %q is not supported", effective.TopData.Wiki.Renderer))
|
||||
}
|
||||
switch effective.TopData.Wiki.LinkStrategy {
|
||||
case "preserve_westgate_wiki_links":
|
||||
default:
|
||||
failures = append(failures, fmt.Errorf("topdata.wiki.link_strategy %q is not supported", effective.TopData.Wiki.LinkStrategy))
|
||||
}
|
||||
for key, policy := range map[string]string{
|
||||
"topdata.wiki.stale_pages.default": effective.TopData.Wiki.StalePages.Default,
|
||||
"topdata.wiki.stale_pages.live_cleanup": effective.TopData.Wiki.StalePages.LiveCleanup,
|
||||
} {
|
||||
switch policy {
|
||||
case "report", "archive", "unpublish":
|
||||
default:
|
||||
failures = append(failures, fmt.Errorf("%s %q is not supported", key, policy))
|
||||
}
|
||||
}
|
||||
switch effective.Extract.Layout {
|
||||
case "nwn_canonical_json":
|
||||
default:
|
||||
@@ -834,6 +876,14 @@ func (p *Project) TopDataWikiRootDir() string {
|
||||
return filepath.Join(p.TopDataBuildDir(), wikiRoot)
|
||||
}
|
||||
|
||||
func (p *Project) TopDataWikiSourceDir() string {
|
||||
source := filepath.FromSlash(p.EffectiveConfig().TopData.Wiki.Source)
|
||||
if filepath.IsAbs(source) {
|
||||
return source
|
||||
}
|
||||
return filepath.Join(p.Root, source)
|
||||
}
|
||||
|
||||
func (p *Project) TopDataWikiPagesDir() string {
|
||||
return filepath.Join(p.TopDataWikiRootDir(), filepath.FromSlash(p.EffectiveConfig().TopData.Wiki.PagesDir))
|
||||
}
|
||||
|
||||
@@ -256,10 +256,19 @@ extract:
|
||||
cleanup_stale: false
|
||||
topdata:
|
||||
wiki:
|
||||
source: topdata/wiki
|
||||
managed_namespaces:
|
||||
- skills
|
||||
renderer: nodebb_tiptap_html
|
||||
link_strategy: preserve_westgate_wiki_links
|
||||
namespaces_file: namespaces.yaml
|
||||
templates_dir: templates
|
||||
manual_sections_dir: manual-sections
|
||||
deploy_manifest: wiki-manifest.json
|
||||
deploy_edit_summary: Custom summary
|
||||
stale_pages:
|
||||
default: report
|
||||
live_cleanup: archive
|
||||
autogen:
|
||||
cache:
|
||||
root: "{paths.cache}/released"
|
||||
@@ -297,6 +306,30 @@ autogen:
|
||||
if got, want := effective.TopData.Wiki.DeployManifest, "wiki-manifest.json"; got != want {
|
||||
t.Fatalf("expected wiki deploy manifest %q, got %q", want, got)
|
||||
}
|
||||
if got, want := effective.TopData.Wiki.Source, "topdata/wiki"; got != want {
|
||||
t.Fatalf("expected wiki source %q, got %q", want, got)
|
||||
}
|
||||
if got, want := effective.TopData.Wiki.Renderer, "nodebb_tiptap_html"; got != want {
|
||||
t.Fatalf("expected wiki renderer %q, got %q", want, got)
|
||||
}
|
||||
if got, want := effective.TopData.Wiki.LinkStrategy, "preserve_westgate_wiki_links"; got != want {
|
||||
t.Fatalf("expected wiki link strategy %q, got %q", want, got)
|
||||
}
|
||||
if got, want := effective.TopData.Wiki.NamespacesFile, "namespaces.yaml"; got != want {
|
||||
t.Fatalf("expected wiki namespaces file %q, got %q", want, got)
|
||||
}
|
||||
if got, want := effective.TopData.Wiki.TemplatesDir, "templates"; got != want {
|
||||
t.Fatalf("expected wiki templates dir %q, got %q", want, got)
|
||||
}
|
||||
if got, want := effective.TopData.Wiki.ManualSectionsDir, "manual-sections"; got != want {
|
||||
t.Fatalf("expected wiki manual sections dir %q, got %q", want, got)
|
||||
}
|
||||
if got, want := effective.TopData.Wiki.StalePages.Default, "report"; got != want {
|
||||
t.Fatalf("expected wiki stale default %q, got %q", want, got)
|
||||
}
|
||||
if got, want := effective.TopData.Wiki.StalePages.LiveCleanup, "archive"; got != want {
|
||||
t.Fatalf("expected wiki stale live cleanup %q, got %q", want, got)
|
||||
}
|
||||
if got, want := effective.Autogen.Cache.MaxAge, "30m"; got != want {
|
||||
t.Fatalf("expected autogen cache max age %q, got %q", want, got)
|
||||
}
|
||||
@@ -704,6 +737,54 @@ func TestValidateLayoutRejectsUnsafeGeneratedOutputPaths(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateLayoutRejectsInvalidTopDataWikiConfig(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
mkdirAll(t, filepath.Join(root, "src"))
|
||||
mkdirAll(t, filepath.Join(root, "build"))
|
||||
|
||||
proj := &Project{
|
||||
Root: root,
|
||||
Config: Config{
|
||||
Module: ModuleConfig{Name: "Test", ResRef: "test"},
|
||||
Paths: PathConfig{Source: "src", Build: "build"},
|
||||
TopData: TopDataConfig{
|
||||
Source: "topdata",
|
||||
Wiki: TopDataWikiConfig{
|
||||
Source: "../wiki",
|
||||
Renderer: "dokuwiki",
|
||||
LinkStrategy: "rewrite_links",
|
||||
NamespacesFile: "../namespaces.yaml",
|
||||
TemplatesDir: ".",
|
||||
ManualSectionsDir: "../manual",
|
||||
StalePages: TopDataWikiStalePagesConfig{
|
||||
Default: "delete",
|
||||
LiveCleanup: "delete",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := proj.ValidateLayout()
|
||||
if err == nil {
|
||||
t.Fatal("expected invalid wiki config validation error")
|
||||
}
|
||||
for _, needle := range []string{
|
||||
"topdata.wiki.source",
|
||||
"topdata.wiki.renderer",
|
||||
"topdata.wiki.link_strategy",
|
||||
"topdata.wiki.namespaces_file",
|
||||
"topdata.wiki.templates_dir",
|
||||
"topdata.wiki.manual_sections_dir",
|
||||
"topdata.wiki.stale_pages.default",
|
||||
"topdata.wiki.stale_pages.live_cleanup",
|
||||
} {
|
||||
if !strings.Contains(err.Error(), needle) {
|
||||
t.Fatalf("expected validation error to mention %s, got %v", needle, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateLayoutRejectsRootSourceAndAssetPaths(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
mkdirAll(t, filepath.Join(root, "build"))
|
||||
|
||||
Reference in New Issue
Block a user