Wiki pipeline audit
Changes Made - page-index.json now includes generated meta/status pages, not just entity pages. - Wiki page count now comes from the final page index, so wiki pages: matches generated HTML files. - Page IDs now normalize /, \, and _ to : to prevent duplicate index entries for slash-bearing keys. - Added page-index validation for duplicate page_id and duplicate output_path.
This commit is contained in:
@@ -148,7 +148,6 @@ func buildWiki(p *project.Project, nativeResult BuildResult, force bool, progres
|
||||
pageTitles := map[string]string{}
|
||||
pageStatuses := map[string]string{}
|
||||
pageIndex := wikiPageIndex{Version: wikiGeneratorVersion}
|
||||
pageCount := 0
|
||||
|
||||
writePage := func(pageID, content, title, status string) error {
|
||||
path := filepath.Join(outputDir, wikiPageIDToRelPath(pageID))
|
||||
@@ -177,7 +176,6 @@ func buildWiki(p *project.Project, nativeResult BuildResult, force bool, progres
|
||||
EditPolicy: wikiEditPolicy(namespace),
|
||||
StalePolicy: p.EffectiveConfig().TopData.Wiki.StalePages.Default,
|
||||
})
|
||||
pageCount++
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -187,6 +185,9 @@ func buildWiki(p *project.Project, nativeResult BuildResult, force bool, progres
|
||||
if err := generateStatusPages(outputDir, pageStatuses, pageTitles, p.EffectiveConfig().TopData.Wiki.ManagedNamespaces, loadWikiManualSections(p)); err != nil {
|
||||
return wikiResult{}, err
|
||||
}
|
||||
if err := indexUnregisteredWikiPages(rootDir, outputDir, p.EffectiveConfig().TopData.Wiki.StalePages.Default, &pageIndex); err != nil {
|
||||
return wikiResult{}, err
|
||||
}
|
||||
if err := saveWikiPageIndex(filepath.Join(rootDir, "page-index.json"), pageIndex); err != nil {
|
||||
return wikiResult{}, err
|
||||
}
|
||||
@@ -194,6 +195,7 @@ func buildWiki(p *project.Project, nativeResult BuildResult, force bool, progres
|
||||
if err := os.MkdirAll(rootDir, 0o755); err != nil {
|
||||
return wikiResult{}, fmt.Errorf("create wiki root dir: %w", err)
|
||||
}
|
||||
pageCount := len(pageIndex.Pages)
|
||||
state = wikiStateDocument{Version: wikiGeneratorVersion, Digest: digest, Pages: pageCount}
|
||||
if err := saveWikiState(statePath, state); err != nil {
|
||||
return wikiResult{}, err
|
||||
@@ -243,6 +245,9 @@ func loadWikiManualSections(p *project.Project) []wikiManualSection {
|
||||
}
|
||||
|
||||
func saveWikiPageIndex(path string, index wikiPageIndex) error {
|
||||
if err := validateWikiPageIndex(index); err != nil {
|
||||
return err
|
||||
}
|
||||
slices.SortFunc(index.Pages, func(a, b wikiPageIndexEntry) int {
|
||||
return strings.Compare(a.PageID, b.PageID)
|
||||
})
|
||||
@@ -256,6 +261,68 @@ func saveWikiPageIndex(path string, index wikiPageIndex) error {
|
||||
return os.WriteFile(path, append(raw, '\n'), 0o644)
|
||||
}
|
||||
|
||||
func validateWikiPageIndex(index wikiPageIndex) error {
|
||||
pageIDs := map[string]string{}
|
||||
outputPaths := map[string]string{}
|
||||
for _, entry := range index.Pages {
|
||||
if previous, ok := pageIDs[entry.PageID]; ok {
|
||||
return fmt.Errorf("duplicate wiki page-index page_id %q for %s and %s", entry.PageID, previous, entry.OutputPath)
|
||||
}
|
||||
if previous, ok := outputPaths[entry.OutputPath]; ok {
|
||||
return fmt.Errorf("duplicate wiki page-index output_path %q for %s and %s", entry.OutputPath, previous, entry.PageID)
|
||||
}
|
||||
pageIDs[entry.PageID] = entry.OutputPath
|
||||
outputPaths[entry.OutputPath] = entry.PageID
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func indexUnregisteredWikiPages(rootDir, outputDir, stalePolicy string, index *wikiPageIndex) error {
|
||||
seen := map[string]struct{}{}
|
||||
for _, entry := range index.Pages {
|
||||
seen[entry.PageID] = struct{}{}
|
||||
}
|
||||
return filepath.WalkDir(outputDir, func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if d.IsDir() || !strings.EqualFold(filepath.Ext(path), ".html") {
|
||||
return nil
|
||||
}
|
||||
relToPages, err := filepath.Rel(outputDir, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pageID := strings.TrimSuffix(filepath.ToSlash(relToPages), filepath.Ext(relToPages))
|
||||
pageID = strings.ReplaceAll(pageID, "/", ":")
|
||||
if _, ok := seen[pageID]; ok {
|
||||
return nil
|
||||
}
|
||||
raw, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
namespace := strings.SplitN(pageID, ":", 2)[0]
|
||||
relToRoot, err := filepath.Rel(rootDir, path)
|
||||
if err != nil {
|
||||
relToRoot = filepath.Base(path)
|
||||
}
|
||||
index.Pages = append(index.Pages, wikiPageIndexEntry{
|
||||
PageID: pageID,
|
||||
Namespace: namespace,
|
||||
SourceDataset: categoryFromWikiNamespace(namespace),
|
||||
SourceKey: pageID,
|
||||
Title: extractHTMLTitle(string(raw), pageID),
|
||||
Hash: computeManagedHash(string(raw)),
|
||||
OutputPath: filepath.ToSlash(relToRoot),
|
||||
EditPolicy: wikiEditPolicy(namespace),
|
||||
StalePolicy: stalePolicy,
|
||||
})
|
||||
seen[pageID] = struct{}{}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func loadWikiState(path string) (wikiStateDocument, error) {
|
||||
raw, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
@@ -1482,7 +1549,8 @@ func wikiPageIDForKey(key string) string {
|
||||
if namespace == "" {
|
||||
return ""
|
||||
}
|
||||
return namespace + ":" + strings.ReplaceAll(value, "_", ":")
|
||||
value = strings.NewReplacer("/", ":", "\\", ":", "_", ":").Replace(value)
|
||||
return namespace + ":" + value
|
||||
}
|
||||
|
||||
func wikiPageIDToRelPath(pageID string) string {
|
||||
|
||||
Reference in New Issue
Block a user