Dry runs? Pls...

This commit is contained in:
2026-05-23 22:11:36 +02:00
parent e275422b91
commit 025995815d
4 changed files with 115 additions and 3 deletions
+3 -2
View File
@@ -428,8 +428,9 @@ is only used where it cannot cross a canonical parent path. `--create` is still
required for genuinely new pages that have no existing remote topic. If NodeBB
rejects a create because the clean wiki URL already exists, `deploy-wiki`
re-queries the namespace by page leaf, adopts the matching topic, and updates it
instead. Updates and stale-page archives acquire a Westgate Wiki edit lock
before saving, then send the returned
instead. Pass `--debug` when deploying large wiki changes to emit planning and
live mutation progress. Updates and stale-page archives acquire a Westgate Wiki
edit lock before saving, then send the returned
`wikiEditLockToken` with the NodeBB post edit request. Stale generated pages are
reported by default.
`--stale-policy archive` rewrites stale generated pages in place as archived
+4
View File
@@ -1030,6 +1030,10 @@ func (c *topdataConsole) phaseLabel(message string) string {
return "planning wiki deploy"
case strings.HasPrefix(message, "Planning NodeBB wiki page "):
return "planning wiki pages"
case strings.HasPrefix(message, "Executing NodeBB wiki actions"):
return "executing wiki deploy"
case strings.HasPrefix(message, "Executing NodeBB wiki action "):
return "executing wiki actions"
case strings.HasPrefix(message, "Listing NodeBB wiki namespace category "):
return "listing remote wiki pages"
case strings.HasPrefix(message, "Collecting "):
+14 -1
View File
@@ -200,7 +200,12 @@ func DeployWikiWithOptions(p *project.Project, opts DeployWikiOptions, progress
if opts.Version != "" {
summary = fmt.Sprintf("%s (%s)", summary, opts.Version)
}
for _, plan := range orderNodeBBDeployPlans(plans) {
orderedPlans := orderNodeBBDeployPlans(plans)
progress(fmt.Sprintf("Executing NodeBB wiki actions: total %d, create %d, update %d, rename %d, archive %d, purge %d", len(orderedPlans), result.Created, result.Updated, result.Renamed, result.Archived, result.Purged))
for i, plan := range orderedPlans {
if shouldReportNodeBBDeployActionProgress(i, len(orderedPlans)) {
progress(fmt.Sprintf("Executing NodeBB wiki action %d/%d: %s %s", i+1, len(orderedPlans), plan.Action, plan.Page.PageID))
}
switch plan.Action {
case "create":
created, err := client.createTopic(plan.Entry.CID, plan.Page.Title, plan.Content, summary)
@@ -246,6 +251,14 @@ func DeployWikiWithOptions(p *project.Project, opts DeployWikiOptions, progress
return result, nil
}
func shouldReportNodeBBDeployActionProgress(index, total int) bool {
if total <= 0 {
return false
}
position := index + 1
return position == 1 || position == total || position%100 == 0
}
func orderNodeBBDeployPlans(plans []wikiDeployPlan) []wikiDeployPlan {
if len(plans) <= 1 {
return plans
+94
View File
@@ -132,6 +132,100 @@ func TestDeployWikiReportsPlanningProgressBeforeRemoteWork(t *testing.T) {
}
}
func TestDeployWikiReportsLiveExecutionProgress(t *testing.T) {
root := t.TempDir()
sourceDir := filepath.Join(root, "pages")
if err := os.MkdirAll(filepath.Join(sourceDir, "feat"), 0755); err != nil {
t.Fatalf("create source dir: %v", err)
}
updatePage := `<!-- sow-topdata-wiki:page=feat:updated -->
<!-- sow-topdata-wiki:managed:start hash="sha256:local-update" -->
<h1>Updated</h1>
<p>New generated content</p>
<!-- sow-topdata-wiki:managed:end -->
`
renamePage := `<!-- sow-topdata-wiki:page=feat:renamed -->
<!-- sow-topdata-wiki:managed:start hash="sha256:local-rename" -->
<h1>Renamed</h1>
<p>Unchanged generated content</p>
<!-- sow-topdata-wiki:managed:end -->
`
if err := os.WriteFile(filepath.Join(sourceDir, "feat", "updated.html"), []byte(updatePage), 0644); err != nil {
t.Fatalf("write update page: %v", err)
}
if err := os.WriteFile(filepath.Join(sourceDir, "feat", "renamed.html"), []byte(renamePage), 0644); err != nil {
t.Fatalf("write rename page: %v", err)
}
oldUpdate := `<!-- sow-topdata-wiki:page=feat:updated -->
<!-- sow-topdata-wiki:managed:start hash="sha256:old-update" -->
<h1>Updated</h1>
<p>Old generated content</p>
<!-- sow-topdata-wiki:managed:end -->
`
manifestPath := filepath.Join(root, "deploy-manifest.json")
if err := saveDeployManifest(manifestPath, wikiDeployManifest{
Version: "nodebb-v1",
Pages: map[string]wikiDeployManifestPage{
"feat:renamed": {Hash: computeManagedHash(renamePage), Title: "Old Renamed", TopicTitle: "Old Renamed", Namespace: "feat", TID: 7, PID: 70, CID: 5},
"feat:updated": {Hash: computeManagedHash(oldUpdate), Title: "Updated", TopicTitle: "Updated", Namespace: "feat", TID: 8, PID: 80, CID: 5},
},
}); err != nil {
t.Fatalf("write deploy manifest: %v", err)
}
progress := []string{}
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch {
case r.Method == http.MethodGet && r.URL.Path == "/api/v3/topics/7":
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(map[string]any{"response": map[string]any{"tid": 7, "title": "Old Renamed", "mainPid": 70}})
case r.Method == http.MethodGet && r.URL.Path == "/api/v3/posts/80":
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(map[string]any{"response": map[string]any{"pid": 80, "tid": 8, "content": oldUpdate}})
case r.Method == http.MethodPut && r.URL.Path == "/api/v3/plugins/westgate-wiki/edit-lock":
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(map[string]any{"response": map[string]any{"token": "lock-token"}})
case r.Method == http.MethodPut && r.URL.Path == "/api/v3/posts/80":
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(map[string]any{"response": map[string]any{"pid": 80, "tid": 8}})
case r.Method == http.MethodPut && r.URL.Path == "/api/v3/plugins/westgate-wiki/page/move":
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(map[string]any{"response": map[string]any{"tid": 7}})
default:
t.Fatalf("unexpected request %s %s", r.Method, r.URL.String())
}
}))
defer server.Close()
result, err := DeployWikiWithOptions(&project.Project{}, DeployWikiOptions{
SourceDir: sourceDir,
Endpoint: server.URL,
Token: "nodebb-token",
ManifestPath: manifestPath,
}, func(message string) {
progress = append(progress, message)
})
if err != nil {
t.Fatalf("DeployWikiWithOptions live deploy failed: %v", err)
}
if result.Updated != 1 || result.Renamed != 1 {
t.Fatalf("expected one update and one rename, got %#v", result)
}
joined := strings.Join(progress, "\n")
for _, expected := range []string{
"Executing NodeBB wiki actions: total 2, create 0, update 1, rename 1, archive 0, purge 0",
"Executing NodeBB wiki action 1/2:",
"Executing NodeBB wiki action 2/2:",
"rename feat:renamed",
"update feat:updated",
} {
if !strings.Contains(joined, expected) {
t.Fatalf("expected execution progress %q, got:\n%s", expected, joined)
}
}
}
func TestNodeBBNamespacePaginationRejectsRepeatedCursor(t *testing.T) {
requests := 0
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {