From 7145cb49b3937ba425e40789f96b8b28b2d65836 Mon Sep 17 00:00:00 2001 From: Charles Southerland Date: Thu, 25 Jun 2020 00:36:08 -0500 Subject: [PATCH 1/3] Add ExactAttachment header --- README.md | 29 +++++++++++++++++++++++++++++ pkg/mark/attachment.go | 24 +++++++++++++----------- pkg/mark/meta.go | 19 ++++++++++++------- 3 files changed, 54 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index b8ca79a..04823f0 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,8 @@ File in extended format should follow specification + + ``` @@ -61,6 +63,33 @@ follows include tag: --> ``` +Mark also supports attachments. The standard way involves declaring an +`Attachment` along with the other items in the header, then have any links +start with an `attachment://` pseudo-URL: + +```markdown + + + + +An attached link is [here](attachment://) +``` + +The standard attachment mechanism may not work in some circumstances (e.g. +keeping content as close to identical as possible between GitHub and +Confluence), so `ExactAttachment` has been introduced: + +```markdown + + + + +An attached link is [here]() +``` + +*NOTE* Be careful with `ExactAttachment`! If your path string is a subset of +another longer string or referenced in text, you may get undesired behavior. + Mark also supports macro definitions, which are defined as regexps which will be replaced with specified template: diff --git a/pkg/mark/attachment.go b/pkg/mark/attachment.go index 6ae83d3..abd2ff3 100644 --- a/pkg/mark/attachment.go +++ b/pkg/mark/attachment.go @@ -28,20 +28,22 @@ type Attachment struct { Path string Checksum string Link string + Replace string } func ResolveAttachments( api *confluence.API, page *confluence.PageInfo, base string, - names []string, + replacements map[string]string, ) ([]Attachment, error) { attaches := []Attachment{} - for _, name := range names { + for replace, name := range replacements { attach := Attachment{ Name: name, Filename: strings.ReplaceAll(name, "/", "_"), Path: filepath.Join(base, name), + Replace: replace, } checksum, err := getChecksum(attach.Path) @@ -160,18 +162,18 @@ func ResolveAttachments( func CompileAttachmentLinks(markdown []byte, attaches []Attachment) []byte { links := map[string]string{} - names := []string{} + replaces := []string{} for _, attach := range attaches { uri, err := url.ParseRequestURI(attach.Link) if err != nil { - links[attach.Name] = strings.ReplaceAll("&", "&", attach.Link) + links[attach.Replace] = strings.ReplaceAll("&", "&", attach.Link) } else { - links[attach.Name] = uri.Path + + links[attach.Replace] = uri.Path + "?" + url.QueryEscape(uri.Query().Encode()) } - names = append(names, attach.Name) + replaces = append(replaces, attach.Replace) } // sort by length so first items will have bigger length @@ -179,13 +181,13 @@ func CompileAttachmentLinks(markdown []byte, attaches []Attachment) []byte { // attachments/a.jpg // attachments/a.jpg.jpg // so we replace longer and then shorter - sort.SliceStable(names, func(i, j int) bool { - return len(names[i]) > len(names[j]) + sort.SliceStable(replaces, func(i, j int) bool { + return len(replaces[i]) > len(replaces[j]) }) - for _, name := range names { - from := `attachment://` + name - to := links[name] + for _, replace := range replaces { + from := replace + to := links[replace] log.Debugf(nil, "replacing: %q -> %q", from, to) diff --git a/pkg/mark/meta.go b/pkg/mark/meta.go index f70300f..252007d 100644 --- a/pkg/mark/meta.go +++ b/pkg/mark/meta.go @@ -11,11 +11,12 @@ import ( ) const ( - HeaderParent = `Parent` - HeaderSpace = `Space` - HeaderTitle = `Title` - HeaderLayout = `Layout` - HeaderAttachment = `Attachment` + HeaderParent = `Parent` + HeaderSpace = `Space` + HeaderTitle = `Title` + HeaderLayout = `Layout` + HeaderAttachment = `Attachment` + HeaderExactAttachment = `ExactAttachment` ) type Meta struct { @@ -23,7 +24,7 @@ type Meta struct { Space string Title string Layout string - Attachments []string + Attachments map[string]string } var ( @@ -64,6 +65,7 @@ func ExtractMeta(data []byte) (*Meta, []byte, error) { if meta == nil { meta = &Meta{} + meta.Attachments = make(map[string]string) } header := strings.Title(matches[1]) @@ -87,7 +89,10 @@ func ExtractMeta(data []byte) (*Meta, []byte, error) { meta.Layout = strings.TrimSpace(value) case HeaderAttachment: - meta.Attachments = append(meta.Attachments, value) + meta.Attachments["attachment://"+value] = value + + case HeaderExactAttachment: + meta.Attachments[value] = value default: log.Errorf( From ccbd9d9307e5d79fe311cf227b5175b17711980f Mon Sep 17 00:00:00 2001 From: Charles Southerland Date: Thu, 25 Jun 2020 00:41:42 -0500 Subject: [PATCH 2/3] Bold, not italic --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 04823f0..5c8dc7f 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ Confluence), so `ExactAttachment` has been introduced: An attached link is [here]() ``` -*NOTE* Be careful with `ExactAttachment`! If your path string is a subset of +**NOTE**: Be careful with `ExactAttachment`! If your path string is a subset of another longer string or referenced in text, you may get undesired behavior. Mark also supports macro definitions, which are defined as regexps which will From da99cbb9d72c932574c34fd98b943028291ca780 Mon Sep 17 00:00:00 2001 From: Egor Kovetskiy Date: Sat, 25 Jul 2020 10:58:32 +0300 Subject: [PATCH 3/3] drop attachment:// proto, improve docs Removed attachment:// proto from docs, back compatibility is saved. Now using the implementatin of `Attachment:` provided by @csoutherland. Removed a bunch of swear stuff from docs, oh boy I was so frustrated at that time. --- README.md | 68 ++++++++++++++++++------------------------ main.go | 9 ++++-- pkg/mark/attachment.go | 38 ++++++++++++++++++----- pkg/mark/meta.go | 14 ++++----- 4 files changed, 72 insertions(+), 57 deletions(-) diff --git a/README.md b/README.md index 5c8dc7f..458bdf6 100644 --- a/README.md +++ b/README.md @@ -3,40 +3,32 @@ Mark — tool for syncing your markdown documentation with Atlassian Confluence pages. -This is very usable if you store documentation to your orthodox software in git -repository and don't want to do a handjob with updating Confluence page using -fucking tinymce wysiwyg enterprise core editor. +This is very useful if you store documentation to your software in a Git +repository and don't want to do an extra job of updating Confluence page using +a tinymce wysiwyg enterprise core editor which always breaks everything. -You can store a user credentials in the configuration file, which should be -located in ~/.config/mark with following format: +Mark does the same but in a different way. Mark reads your markdown file, creates a Confluence page +if it's not found by its name, uploads attachments, translates Markdown into HTML and updates the +contents of the page via REST API. It's like you don't even need to create sections/pages in your +Confluence anymore, just use them in your Markdown documentation. -```toml -username = "smith" -password = "matrixishere" -base_url = "http://confluence.local" -``` - -*NOTE: Cloud Confluence also need to specify /wiki path to the base_url -parameter.* - -Mark understands extended file format, which, still being valid markdown, -contains several metadata headers, which can be used to locate page inside +Mark uses an extended file format, which, still being valid markdown, +contains several HTML-ish metadata headers, which can be used to locate page inside Confluence instance and update it accordingly. -File in extended format should follow specification +File in the extended format should follow the specification: ```markdown - ``` -There can be any number of 'Parent' headers, if mark can't find specified -parent by title, it will be created. +There can be any number of `Parent` headers, if Mark can't find specified +parent by title, Mark creates it. Also, optional following headers are supported: @@ -55,8 +47,8 @@ to the template relative to current working dir, e.g.: ``` -Templates may accept configuration data in YAML format which immediately -follows include tag: +Templates can accept configuration data in YAML format which immediately +follows the `Include` tag: ```markdown + -An attached link is [here](attachment://) +An attached link is [here]() ``` -The standard attachment mechanism may not work in some circumstances (e.g. -keeping content as close to identical as possible between GitHub and -Confluence), so `ExactAttachment` has been introduced: - -```markdown - - - - -An attached link is [here]() -``` - -**NOTE**: Be careful with `ExactAttachment`! If your path string is a subset of +**NOTE**: Be careful with `Attachment`! If your path string is a subset of another longer string or referenced in text, you may get undesired behavior. Mark also supports macro definitions, which are defined as regexps which will @@ -212,6 +192,16 @@ mark -h | --help - `-v | --version` — Show version. - `-h | --help` — Show help screen and call 911. +You can store user credentials in the configuration file, which should be +located in ~/.config/mark with the following format (TOML): + +```toml +username = "smith" +password = "matrixishere" +# If you are using Confluence Cloud add the /wiki suffix to base_url +base_url = "http://confluence.local" +``` + # Tricks ## Confluence & Gitlab integration @@ -250,5 +240,5 @@ Apply: and declare the following environment variables (secrets) * `DOCS_WIKI_USERNAME` — Username for access to Confluence -* `DOCS_WIKI_PASSWORD` — Password for access to Confluence +* `DOCS_WIKI_PASSWORD` — Password for access to Confluence * `DOCS_WIKI_BASE_URL` — Base URL of Confluence (cloud users should add /wiki at the end) diff --git a/main.go b/main.go index 21bc91c..6c7ff11 100644 --- a/main.go +++ b/main.go @@ -324,8 +324,13 @@ func main() { } } - fmt.Printf( - "page successfully updated: %s\n", + log.Infof( + nil, + "page successfully updated: %s", creds.BaseURL+target.Links.Full, ) + + fmt.Println( + creds.BaseURL + target.Links.Full, + ) } diff --git a/pkg/mark/attachment.go b/pkg/mark/attachment.go index abd2ff3..5e9fcf7 100644 --- a/pkg/mark/attachment.go +++ b/pkg/mark/attachment.go @@ -186,16 +186,40 @@ func CompileAttachmentLinks(markdown []byte, attaches []Attachment) []byte { }) for _, replace := range replaces { - from := replace to := links[replace] - log.Debugf(nil, "replacing: %q -> %q", from, to) + found := false + if bytes.Contains(markdown, []byte("attachment://"+replace)) { + from := "attachment://" + replace - markdown = bytes.ReplaceAll( - markdown, - []byte(from), - []byte(to), - ) + log.Debugf(nil, "replacing legacy link: %q -> %q", from, to) + + markdown = bytes.ReplaceAll( + markdown, + []byte(from), + []byte(to), + ) + + found = true + } + + if bytes.Contains(markdown, []byte(replace)) { + from := replace + + log.Debugf(nil, "replacing link: %q -> %q", from, to) + + markdown = bytes.ReplaceAll( + markdown, + []byte(from), + []byte(to), + ) + + found = true + } + + if !found { + log.Warningf(nil, "unused attachment: %s", replace) + } } return markdown diff --git a/pkg/mark/meta.go b/pkg/mark/meta.go index 252007d..d1737b5 100644 --- a/pkg/mark/meta.go +++ b/pkg/mark/meta.go @@ -11,12 +11,11 @@ import ( ) const ( - HeaderParent = `Parent` - HeaderSpace = `Space` - HeaderTitle = `Title` - HeaderLayout = `Layout` - HeaderAttachment = `Attachment` - HeaderExactAttachment = `ExactAttachment` + HeaderParent = `Parent` + HeaderSpace = `Space` + HeaderTitle = `Title` + HeaderLayout = `Layout` + HeaderAttachment = `Attachment` ) type Meta struct { @@ -89,9 +88,6 @@ func ExtractMeta(data []byte) (*Meta, []byte, error) { meta.Layout = strings.TrimSpace(value) case HeaderAttachment: - meta.Attachments["attachment://"+value] = value - - case HeaderExactAttachment: meta.Attachments[value] = value default: