mirror of
https://github.com/kovetskiy/mark.git
synced 2025-04-23 21:32:41 +08:00
rework attachments, improve docs
This commit is contained in:
commit
951b276a9b
63
README.md
63
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
|
||||
<!-- Space: <space key> -->
|
||||
<!-- Parent: <parent 1> -->
|
||||
<!-- Parent: <parent 2> -->
|
||||
<!-- Title: <title> -->
|
||||
<!-- Attachment: <local path> -->
|
||||
|
||||
<page contents>
|
||||
```
|
||||
|
||||
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.:
|
||||
<!-- Include: <path> -->
|
||||
```
|
||||
|
||||
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
|
||||
<!-- Include: <path>
|
||||
<yaml-data> -->
|
||||
```
|
||||
|
||||
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
|
||||
<!-- Attachment: <path-to-image> -->
|
||||
|
||||
<beginning of page content>
|
||||
|
||||
An attached link is [here](<path-to-image>)
|
||||
```
|
||||
|
||||
**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)
|
||||
|
9
main.go
9
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,
|
||||
)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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(
|
||||
|
Loading…
x
Reference in New Issue
Block a user