diff --git a/README.md b/README.md index b8ca79a..458bdf6 100644 --- a/README.md +++ b/README.md @@ -3,38 +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: @@ -53,14 +47,29 @@ 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 ``` +Mark also supports attachments. The standard way involves declaring an +`Attachment` along with the other items in the header, then have any links +with the same path: + +```markdown + + + + +An attached link is [here]() +``` + +**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 be replaced with specified template: @@ -183,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 @@ -221,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 6ae83d3..5e9fcf7 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,21 +181,45 @@ 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 { + 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 f70300f..d1737b5 100644 --- a/pkg/mark/meta.go +++ b/pkg/mark/meta.go @@ -23,7 +23,7 @@ type Meta struct { Space string Title string Layout string - Attachments []string + Attachments map[string]string } var ( @@ -64,6 +64,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 +88,7 @@ func ExtractMeta(data []byte) (*Meta, []byte, error) { meta.Layout = strings.TrimSpace(value) case HeaderAttachment: - meta.Attachments = append(meta.Attachments, value) + meta.Attachments[value] = value default: log.Errorf(