Repository: kovetskiy/mark
Pull request #153 (2022-01-17 12:52:18)

Space from config and title from h1
This PR resolves:
Support defining space from config #96
Support page title from first h1 #95

--space and --title-from-h1 options were added. If Space is defined both in metadata and in a command line then the value from file metadata overrides the value from CL. If Title is defined in metadata it's being used regardless of is H1 header was set in a file or not.

kovetskiy: Hey, this looks good to me and I'm ready to merge and release the new version. I'm just wondering, what is the usecase where you need such a behavior as specifying Space through flags?
klysunkin: From my perspective Space as well as a base URL does not relate to a page but rather set up a context. So, I believe it'll be useful for those who don't want to couple all pages with a space key. One use case from my mind is to have documentation for release and for WIP in different branches and publish to different spaces.
Also, with https://github.com/kovetskiy/mark/pull/151 merged and https://github.com/kovetskiy/mark/issues/105 fixed it will be possible to publish pages hierarchy with one command.

Patch:
diff --git a/README.md b/README.md
index 9848183..b45a3ed 100644
--- a/README.md
+++ b/README.md
@@ -438,7 +438,9 @@ mark -h | --help
 - `-c <path>` or `--config <path>` — Specify a path to the configuration file.
 - `-k` — Lock page editing to current user only to prevent accidental
     manual edits over Confluence Web UI.
+- `--space <space>` - Use specified space key. If not specified space ley must be set in a page metadata.
 - `--drop-h1` – Don't include H1 headings in Confluence output.
+- `--title-from-h1` - Extract page title from a leading H1 heading. If no H1 heading on a page then title must be set in a page metadata.
 - `--dry-run` — Show resulting HTML and don't update Confluence page content.
 - `--minor-edit` — Don't send notifications while updating Confluence page.
 - `--trace` — Enable trace logs.
diff --git a/main.go b/main.go
index fbd67b8..84a3ff9 100644
--- a/main.go
+++ b/main.go
@@ -24,6 +24,7 @@ type Flags struct {
 	DryRun         bool   `docopt:"--dry-run"`
 	EditLock       bool   `docopt:"-k"`
 	DropH1         bool   `docopt:"--drop-h1"`
+	TitleFromH1    bool   `docopt:"--title-from-h1"`
 	MinorEdit      bool   `docopt:"--minor-edit"`
 	Color          string `docopt:"--color"`
 	Debug          bool   `docopt:"--debug"`
@@ -34,6 +35,7 @@ type Flags struct {
 	BaseURL        string `docopt:"--base-url"`
 	Config         string `docopt:"--config"`
 	Ci             bool   `docopt:"--ci"`
+	Space          string `docopt:"--space"`
 }
 
 const (
@@ -61,7 +63,11 @@ Options:
                         Supports file globbing patterns (needs to be quoted).
   -k                   Lock page editing to current user only to prevent accidental
                         manual edits over Confluence Web UI.
+  --space <space>      Use specified space key. If not specified space ley must
+                        be set in a page metadata.
   --drop-h1            Don't include H1 headings in Confluence output.
+  --title-from-h1      Extract page title from a leading H1 heading. If no H1 heading
+                        on a page then title must be set in a page metadata.
   --dry-run            Resolve page and ancestry, show resulting HTML and exit.
   --compile-only       Show resulting HTML and don't update Confluence page content.
   --minor-edit         Don't send notifications while updating Confluence page.
@@ -170,6 +176,24 @@ func processFile(
 		log.Fatal(err)
 	}
 
+	switch {
+	case meta.Space == "" && flags.Space == "":
+		log.Fatal("space is not set ('Space' header is not set and '--space' option is not set)")
+	case meta.Space == "" && flags.Space != "":
+		meta.Space = flags.Space
+	}
+
+	if meta.Title == "" && flags.TitleFromH1 {
+		meta.Title = mark.ExtractDocumentLeadingH1(markdown)
+	}
+
+	if meta.Title == "" {
+		log.Fatal(
+			`page title is not set ('Title' header is not set ` +
+				`and '--title-from-h1' option is not set or there is no H1 in the file)`,
+		)
+	}
+
 	stdlib, err := stdlib.New(api)
 	if err != nil {
 		log.Fatal(err)
diff --git a/pkg/mark/markdown.go b/pkg/mark/markdown.go
index e1017f4..de418b8 100644
--- a/pkg/mark/markdown.go
+++ b/pkg/mark/markdown.go
@@ -147,3 +147,14 @@ func DropDocumentLeadingH1(
 	markdown = h1.ReplaceAll(markdown, []byte(""))
 	return markdown
 }
+
+// ExtractDocumentLeadingH1 will extract leading H1 heading
+func ExtractDocumentLeadingH1(markdown []byte) string {
+	h1 := regexp.MustCompile(`^#[^#]\s*(.*)\s*\n`)
+	groups := h1.FindSubmatch(markdown)
+	if groups == nil {
+		return ""
+	} else {
+		return string(groups[1])
+	}
+}
diff --git a/pkg/mark/meta.go b/pkg/mark/meta.go
index 3cc6d51..3e9e8e4 100644
--- a/pkg/mark/meta.go
+++ b/pkg/mark/meta.go
@@ -128,19 +128,5 @@ func ExtractMeta(data []byte) (*Meta, []byte, error) {
 		return nil, data, nil
 	}
 
-	if meta.Space == "" {
-		return nil, nil, fmt.Errorf(
-			"space key is not set (%s header is not set)",
-			HeaderSpace,
-		)
-	}
-
-	if meta.Title == "" {
-		return nil, nil, fmt.Errorf(
-			"page title is not set (%s header is not set)",
-			HeaderTitle,
-		)
-	}
-
 	return meta, data[offset:], nil
 }


Test patch:
diff --git a/pkg/mark/markdown_test.go b/pkg/mark/markdown_test.go
index d831b88..9e936a7 100644
--- a/pkg/mark/markdown_test.go
+++ b/pkg/mark/markdown_test.go
@@ -48,3 +48,16 @@ func TestCompileMarkdown(t *testing.T) {
 		test.EqualValues(string(html), actual, filename+" vs "+htmlname)
 	}
 }
+
+func TestExtractDocumentLeadingH1(t *testing.T) {
+	filename := "testdata/header.md"
+
+	markdown, err := ioutil.ReadFile(filename)
+	if err != nil {
+		panic(err)
+	}
+
+	actual := ExtractDocumentLeadingH1(markdown)
+
+	assert.Equal(t, "a", actual)
+}