Wiki deploy manifest fix
This commit is contained in:
@@ -322,6 +322,11 @@ use `namespace=cid`, for example:
|
|||||||
sow-toolkit deploy-wiki --dry-run --namespace spells --category spells=42
|
sow-toolkit deploy-wiki --dry-run --namespace spells --category spells=42
|
||||||
```
|
```
|
||||||
|
|
||||||
|
When a deploy manifest is missing but matching wiki pages already exist in
|
||||||
|
NodeBB, `deploy-wiki` checks the Westgate Wiki namespace directory API and
|
||||||
|
adopts those topic/post mappings before planning updates. `--create` is still
|
||||||
|
required for genuinely new pages that have no existing remote topic.
|
||||||
|
|
||||||
`build-changelog` renders a release changelog from tagged pushes. Pull-request
|
`build-changelog` renders a release changelog from tagged pushes. Pull-request
|
||||||
style commits keep their PR links and authors from the Gitea API; direct pushes
|
style commits keep their PR links and authors from the Gitea API; direct pushes
|
||||||
are rendered as commit links using the commit author. It defaults to
|
are rendered as commit links using the commit author. It defaults to
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"html"
|
"html"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"slices"
|
"slices"
|
||||||
@@ -324,6 +325,7 @@ func isDir(path string) bool {
|
|||||||
func planNodeBBDeploy(pages map[string]wikiDeployPage, manifest wikiDeployManifest, opts DeployWikiOptions, client *nodeBBClient) ([]wikiDeployPlan, deployResult, wikiDeployManifest, error) {
|
func planNodeBBDeploy(pages map[string]wikiDeployPage, manifest wikiDeployManifest, opts DeployWikiOptions, client *nodeBBClient) ([]wikiDeployPlan, deployResult, wikiDeployManifest, error) {
|
||||||
pageIDs := sortedKeysFromMap(pages)
|
pageIDs := sortedKeysFromMap(pages)
|
||||||
next := wikiDeployManifest{Version: "nodebb-v1", Pages: map[string]wikiDeployManifestPage{}}
|
next := wikiDeployManifest{Version: "nodebb-v1", Pages: map[string]wikiDeployManifestPage{}}
|
||||||
|
remotePagesByCID := map[int][]nodeBBWikiPage{}
|
||||||
var plans []wikiDeployPlan
|
var plans []wikiDeployPlan
|
||||||
var result deployResult
|
var result deployResult
|
||||||
|
|
||||||
@@ -340,6 +342,21 @@ func planNodeBBDeploy(pages map[string]wikiDeployPage, manifest wikiDeployManife
|
|||||||
}
|
}
|
||||||
next.Pages[pageID] = entry
|
next.Pages[pageID] = entry
|
||||||
|
|
||||||
|
if entry.PID == 0 {
|
||||||
|
if entry.CID != 0 {
|
||||||
|
adopted, ok, err := adoptExistingNodeBBPage(page, entry.CID, remotePagesByCID, client)
|
||||||
|
if err != nil {
|
||||||
|
return nil, result, next, err
|
||||||
|
}
|
||||||
|
if ok {
|
||||||
|
entry.TID = adopted.TID
|
||||||
|
entry.PID = adopted.PID
|
||||||
|
entry.CID = adopted.CID
|
||||||
|
next.Pages[pageID] = entry
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if entry.PID == 0 {
|
if entry.PID == 0 {
|
||||||
if !opts.AllowCreates {
|
if !opts.AllowCreates {
|
||||||
return nil, result, next, fmt.Errorf("wiki page %q has no manifest post mapping; pass --create and --category %s=<cid> to create it", pageID, page.Namespace)
|
return nil, result, next, fmt.Errorf("wiki page %q has no manifest post mapping; pass --create and --category %s=<cid> to create it", pageID, page.Namespace)
|
||||||
@@ -410,6 +427,120 @@ func planNodeBBDeploy(pages map[string]wikiDeployPage, manifest wikiDeployManife
|
|||||||
return plans, result, next, nil
|
return plans, result, next, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func adoptExistingNodeBBPage(page wikiDeployPage, cid int, remotePagesByCID map[int][]nodeBBWikiPage, client *nodeBBClient) (wikiDeployManifestPage, bool, error) {
|
||||||
|
remotePages, ok := remotePagesByCID[cid]
|
||||||
|
if !ok {
|
||||||
|
var err error
|
||||||
|
remotePages, err = client.listNamespacePages(cid)
|
||||||
|
if err != nil {
|
||||||
|
return wikiDeployManifestPage{}, false, err
|
||||||
|
}
|
||||||
|
remotePagesByCID[cid] = remotePages
|
||||||
|
}
|
||||||
|
remotePage, ok := matchExistingNodeBBPage(page, remotePages)
|
||||||
|
if !ok || remotePage.TID == 0 {
|
||||||
|
return wikiDeployManifestPage{}, false, nil
|
||||||
|
}
|
||||||
|
topic, err := client.getTopic(remotePage.TID)
|
||||||
|
if err != nil {
|
||||||
|
return wikiDeployManifestPage{}, false, err
|
||||||
|
}
|
||||||
|
if topic.PID == 0 {
|
||||||
|
return wikiDeployManifestPage{}, false, fmt.Errorf("NodeBB topic %d did not include main post id for wiki page %q", remotePage.TID, page.PageID)
|
||||||
|
}
|
||||||
|
if topic.TID == 0 {
|
||||||
|
topic.TID = remotePage.TID
|
||||||
|
}
|
||||||
|
return wikiDeployManifestPage{TID: topic.TID, PID: topic.PID, CID: cid}, true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func matchExistingNodeBBPage(page wikiDeployPage, remotePages []nodeBBWikiPage) (nodeBBWikiPage, bool) {
|
||||||
|
desiredTitles := map[string]struct{}{}
|
||||||
|
for _, title := range []string{page.Title, pageIDLeaf(page.PageID)} {
|
||||||
|
if normalized := normalizeWikiTitle(title); normalized != "" {
|
||||||
|
desiredTitles[normalized] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
desiredSlugs := map[string]struct{}{}
|
||||||
|
for title := range desiredTitles {
|
||||||
|
if slug := slugifyWikiTitle(title); slug != "" {
|
||||||
|
desiredSlugs[slug] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, slug := range []string{pageIDLeaf(page.PageID), strings.ReplaceAll(pageIDRemainder(page.PageID), ":", "-")} {
|
||||||
|
if normalized := slugifyWikiTitle(slug); normalized != "" {
|
||||||
|
desiredSlugs[normalized] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, candidate := range remotePages {
|
||||||
|
for _, title := range []string{candidate.TitleLeaf, candidate.Title} {
|
||||||
|
if _, ok := desiredTitles[normalizeWikiTitle(title)]; ok {
|
||||||
|
return candidate, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, slug := range []string{nodeBBPathLeaf(candidate.Slug), nodeBBPathLeaf(candidate.WikiPath)} {
|
||||||
|
if _, ok := desiredSlugs[slugifyWikiTitle(slug)]; ok {
|
||||||
|
return candidate, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nodeBBWikiPage{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func pageIDRemainder(pageID string) string {
|
||||||
|
_, rest, ok := strings.Cut(pageID, ":")
|
||||||
|
if !ok {
|
||||||
|
return pageID
|
||||||
|
}
|
||||||
|
return rest
|
||||||
|
}
|
||||||
|
|
||||||
|
func pageIDLeaf(pageID string) string {
|
||||||
|
pageID = strings.TrimSpace(pageID)
|
||||||
|
if pageID == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
parts := strings.FieldsFunc(pageID, func(r rune) bool {
|
||||||
|
return r == ':' || r == '/'
|
||||||
|
})
|
||||||
|
if len(parts) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return parts[len(parts)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func nodeBBPathLeaf(value string) string {
|
||||||
|
parts := strings.FieldsFunc(strings.TrimSpace(value), func(r rune) bool {
|
||||||
|
return r == '/'
|
||||||
|
})
|
||||||
|
if len(parts) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return parts[len(parts)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalizeWikiTitle(value string) string {
|
||||||
|
return strings.Join(strings.Fields(strings.ToLower(strings.ReplaceAll(strings.TrimSpace(value), "_", " "))), " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func slugifyWikiTitle(value string) string {
|
||||||
|
var b strings.Builder
|
||||||
|
lastDash := false
|
||||||
|
for _, r := range strings.ToLower(strings.TrimSpace(value)) {
|
||||||
|
if (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') {
|
||||||
|
b.WriteRune(r)
|
||||||
|
lastDash = false
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !lastDash && b.Len() > 0 {
|
||||||
|
b.WriteByte('-')
|
||||||
|
lastDash = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return strings.Trim(b.String(), "-")
|
||||||
|
}
|
||||||
|
|
||||||
func loadDeployManifest(path string) wikiDeployManifest {
|
func loadDeployManifest(path string) wikiDeployManifest {
|
||||||
doc := wikiDeployManifest{Version: "nodebb-v1", Pages: map[string]wikiDeployManifestPage{}}
|
doc := wikiDeployManifest{Version: "nodebb-v1", Pages: map[string]wikiDeployManifestPage{}}
|
||||||
raw, err := os.ReadFile(path)
|
raw, err := os.ReadFile(path)
|
||||||
@@ -692,6 +823,14 @@ type nodeBBPost struct {
|
|||||||
Content string
|
Content string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type nodeBBWikiPage struct {
|
||||||
|
TID int
|
||||||
|
Title string
|
||||||
|
TitleLeaf string
|
||||||
|
Slug string
|
||||||
|
WikiPath string
|
||||||
|
}
|
||||||
|
|
||||||
func newNodeBBClient(endpoint, token string) *nodeBBClient {
|
func newNodeBBClient(endpoint, token string) *nodeBBClient {
|
||||||
return &nodeBBClient{
|
return &nodeBBClient{
|
||||||
endpoint: strings.TrimRight(endpoint, "/"),
|
endpoint: strings.TrimRight(endpoint, "/"),
|
||||||
@@ -708,6 +847,39 @@ func (c *nodeBBClient) getPost(pid int) (nodeBBPost, error) {
|
|||||||
return parseNodeBBPost(doc), nil
|
return parseNodeBBPost(doc), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *nodeBBClient) getTopic(tid int) (nodeBBPost, error) {
|
||||||
|
var doc any
|
||||||
|
if err := c.request("GET", fmt.Sprintf("/api/v3/topics/%d", tid), nil, &doc); err != nil {
|
||||||
|
return nodeBBPost{}, err
|
||||||
|
}
|
||||||
|
post := parseNodeBBPost(doc)
|
||||||
|
if post.TID == 0 {
|
||||||
|
post.TID = tid
|
||||||
|
}
|
||||||
|
return post, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *nodeBBClient) listNamespacePages(cid int) ([]nodeBBWikiPage, error) {
|
||||||
|
var out []nodeBBWikiPage
|
||||||
|
after := ""
|
||||||
|
for {
|
||||||
|
path := fmt.Sprintf("/api/v3/plugins/westgate-wiki/namespace/%d/pages?limit=80", cid)
|
||||||
|
if after != "" {
|
||||||
|
path += "&after=" + url.QueryEscape(after)
|
||||||
|
}
|
||||||
|
var doc any
|
||||||
|
if err := c.request("GET", path, nil, &doc); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pages, next, hasMore := parseNodeBBWikiPageList(doc)
|
||||||
|
out = append(out, pages...)
|
||||||
|
if !hasMore || next == "" {
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
after = next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (c *nodeBBClient) createTopic(cid int, title, content, summary string) (nodeBBPost, error) {
|
func (c *nodeBBClient) createTopic(cid int, title, content, summary string) (nodeBBPost, error) {
|
||||||
body := map[string]any{"cid": cid, "title": title, "content": content}
|
body := map[string]any{"cid": cid, "title": title, "content": content}
|
||||||
if summary != "" {
|
if summary != "" {
|
||||||
@@ -801,6 +973,46 @@ func parseNodeBBPost(doc any) nodeBBPost {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseNodeBBWikiPageList(doc any) ([]nodeBBWikiPage, string, bool) {
|
||||||
|
for {
|
||||||
|
m, ok := doc.(map[string]any)
|
||||||
|
if !ok {
|
||||||
|
return nil, "", false
|
||||||
|
}
|
||||||
|
if response, ok := m["response"]; ok {
|
||||||
|
doc = response
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
pagesRaw, _ := m["pages"].([]any)
|
||||||
|
pages := make([]nodeBBWikiPage, 0, len(pagesRaw))
|
||||||
|
for _, raw := range pagesRaw {
|
||||||
|
row, ok := raw.(map[string]any)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
pages = append(pages, nodeBBWikiPage{
|
||||||
|
TID: firstInt(row, "tid"),
|
||||||
|
Title: firstString(row, "title"),
|
||||||
|
TitleLeaf: firstString(row, "titleLeaf", "title_leaf"),
|
||||||
|
Slug: firstString(row, "slug"),
|
||||||
|
WikiPath: firstString(row, "wikiPath", "wiki_path"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return pages, firstString(m, "nextCursor", "next_cursor"), boolFromAny(m["hasMore"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func boolFromAny(value any) bool {
|
||||||
|
switch typed := value.(type) {
|
||||||
|
case bool:
|
||||||
|
return typed
|
||||||
|
case string:
|
||||||
|
return typed == "true" || typed == "1"
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func firstInt(m map[string]any, keys ...string) int {
|
func firstInt(m map[string]any, keys ...string) int {
|
||||||
for _, key := range keys {
|
for _, key := range keys {
|
||||||
if value := intFromAny(m[key]); value != 0 {
|
if value := intFromAny(m[key]); value != 0 {
|
||||||
|
|||||||
@@ -92,6 +92,11 @@ func TestDeployWikiCreatesNodeBBTopicAndWritesManifest(t *testing.T) {
|
|||||||
if got := r.Header.Get("Authorization"); got != "Bearer nodebb-token" {
|
if got := r.Header.Get("Authorization"); got != "Bearer nodebb-token" {
|
||||||
t.Fatalf("unexpected authorization header %q", got)
|
t.Fatalf("unexpected authorization header %q", got)
|
||||||
}
|
}
|
||||||
|
if r.Method == http.MethodGet && r.URL.Path == "/api/v3/plugins/westgate-wiki/namespace/5/pages" {
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
_ = json.NewEncoder(w).Encode(map[string]any{"response": map[string]any{"pages": []any{}, "hasMore": false}})
|
||||||
|
return
|
||||||
|
}
|
||||||
if r.Method != http.MethodPost || r.URL.Path != "/api/v3/topics" {
|
if r.Method != http.MethodPost || r.URL.Path != "/api/v3/topics" {
|
||||||
t.Fatalf("unexpected request %s %s", r.Method, r.URL.Path)
|
t.Fatalf("unexpected request %s %s", r.Method, r.URL.Path)
|
||||||
}
|
}
|
||||||
@@ -154,6 +159,11 @@ func TestDeployWikiCreatesNodeBBTopicWithFallbackForShortTitle(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.Method == http.MethodGet && r.URL.Path == "/api/v3/plugins/westgate-wiki/namespace/5/pages" {
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
_ = json.NewEncoder(w).Encode(map[string]any{"response": map[string]any{"pages": []any{}, "hasMore": false}})
|
||||||
|
return
|
||||||
|
}
|
||||||
if r.Method != http.MethodPost || r.URL.Path != "/api/v3/topics" {
|
if r.Method != http.MethodPost || r.URL.Path != "/api/v3/topics" {
|
||||||
t.Fatalf("unexpected request %s %s", r.Method, r.URL.Path)
|
t.Fatalf("unexpected request %s %s", r.Method, r.URL.Path)
|
||||||
}
|
}
|
||||||
@@ -196,6 +206,107 @@ func TestDeployWikiCreatesNodeBBTopicWithFallbackForShortTitle(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDeployWikiAdoptsExistingNodeBBPageWhenManifestIsMissingWithoutCreate(t *testing.T) {
|
||||||
|
root := t.TempDir()
|
||||||
|
sourceDir := filepath.Join(root, "pages")
|
||||||
|
if err := os.MkdirAll(filepath.Join(sourceDir, "classes"), 0755); err != nil {
|
||||||
|
t.Fatalf("create source dir: %v", err)
|
||||||
|
}
|
||||||
|
generated := `<!-- sow-topdata-wiki:page=classes:acolyte -->
|
||||||
|
<!-- sow-topdata-wiki:managed:start hash="sha256:local" -->
|
||||||
|
<h1>Acolyte</h1>
|
||||||
|
<p>Generated acolyte page</p>
|
||||||
|
<!-- sow-topdata-wiki:managed:end -->
|
||||||
|
<!-- sow-topdata-wiki:manual:start id="notes" -->
|
||||||
|
<h2>Notes</h2>
|
||||||
|
<p></p>
|
||||||
|
<!-- sow-topdata-wiki:manual:end id="notes" -->
|
||||||
|
`
|
||||||
|
if err := os.WriteFile(filepath.Join(sourceDir, "classes", "acolyte.html"), []byte(generated), 0644); err != nil {
|
||||||
|
t.Fatalf("write source page: %v", err)
|
||||||
|
}
|
||||||
|
old := `<!-- sow-topdata-wiki:page=classes:acolyte -->
|
||||||
|
<!-- sow-topdata-wiki:managed:start hash="sha256:old" -->
|
||||||
|
<h1>Acolyte</h1>
|
||||||
|
<p>Old acolyte page</p>
|
||||||
|
<!-- sow-topdata-wiki:managed:end -->
|
||||||
|
<!-- sow-topdata-wiki:manual:start id="notes" -->
|
||||||
|
<h2>Notes</h2>
|
||||||
|
<p>Keep this remote note.</p>
|
||||||
|
<!-- sow-topdata-wiki:manual:end id="notes" -->
|
||||||
|
`
|
||||||
|
|
||||||
|
createCalls := 0
|
||||||
|
var updated string
|
||||||
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
switch {
|
||||||
|
case r.Method == http.MethodGet && r.URL.Path == "/api/v3/plugins/westgate-wiki/namespace/9/pages":
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
_ = json.NewEncoder(w).Encode(map[string]any{
|
||||||
|
"response": map[string]any{
|
||||||
|
"pages": []map[string]any{
|
||||||
|
{
|
||||||
|
"tid": 77,
|
||||||
|
"title": "Acolyte",
|
||||||
|
"titleLeaf": "Acolyte",
|
||||||
|
"slug": "77/acolyte",
|
||||||
|
"wikiPath": "/wiki/classes/acolyte",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"hasMore": false,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
case r.Method == http.MethodGet && r.URL.Path == "/api/v3/topics/77":
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
_ = json.NewEncoder(w).Encode(map[string]any{"response": map[string]any{"tid": 77, "mainPid": 88}})
|
||||||
|
case r.Method == http.MethodGet && r.URL.Path == "/api/v3/posts/88":
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
_ = json.NewEncoder(w).Encode(map[string]any{"response": map[string]any{"pid": 88, "tid": 77, "content": old}})
|
||||||
|
case r.Method == http.MethodPut && r.URL.Path == "/api/v3/posts/88":
|
||||||
|
var req struct {
|
||||||
|
Content string `json:"content"`
|
||||||
|
}
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||||
|
t.Fatalf("decode update request: %v", err)
|
||||||
|
}
|
||||||
|
updated = req.Content
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
_ = json.NewEncoder(w).Encode(map[string]any{"response": map[string]any{"pid": 88, "tid": 77}})
|
||||||
|
case r.Method == http.MethodPost && r.URL.Path == "/api/v3/topics":
|
||||||
|
createCalls++
|
||||||
|
t.Fatalf("existing wiki page should be adopted and updated, not recreated")
|
||||||
|
default:
|
||||||
|
t.Fatalf("unexpected request %s %s", r.Method, r.URL.String())
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
manifestPath := filepath.Join(root, "deploy-manifest.json")
|
||||||
|
result, err := DeployWikiWithOptions(&project.Project{}, DeployWikiOptions{
|
||||||
|
SourceDir: sourceDir,
|
||||||
|
Endpoint: server.URL,
|
||||||
|
Token: "nodebb-token",
|
||||||
|
ManifestPath: manifestPath,
|
||||||
|
CategoryIDs: map[string]int{"classes": 9},
|
||||||
|
StalePolicy: "report",
|
||||||
|
NotesDelimiter: "",
|
||||||
|
}, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("DeployWikiWithOptions adopt failed: %v", err)
|
||||||
|
}
|
||||||
|
if result.Created != 0 || result.Updated != 1 || createCalls != 0 {
|
||||||
|
t.Fatalf("expected one adopted update and no creates, result=%#v createCalls=%d", result, createCalls)
|
||||||
|
}
|
||||||
|
if !containsAll(updated, "<p>Generated acolyte page</p>", "<p>Keep this remote note.</p>") {
|
||||||
|
t.Fatalf("expected adopted update to merge generated and remote manual content:\n%s", updated)
|
||||||
|
}
|
||||||
|
manifest := loadDeployManifest(manifestPath)
|
||||||
|
entry := manifest.Pages["classes:acolyte"]
|
||||||
|
if entry.TID != 77 || entry.PID != 88 || entry.CID != 9 || entry.Hash != computeManagedHash(generated) {
|
||||||
|
t.Fatalf("unexpected adopted manifest entry: %#v", entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestDeployWikiMergesHTMLManagedAndManualRegions(t *testing.T) {
|
func TestDeployWikiMergesHTMLManagedAndManualRegions(t *testing.T) {
|
||||||
root := t.TempDir()
|
root := t.TempDir()
|
||||||
sourceDir := filepath.Join(root, "pages")
|
sourceDir := filepath.Join(root, "pages")
|
||||||
|
|||||||
Reference in New Issue
Block a user