mirror of
https://github.com/kovetskiy/mark.git
synced 2025-04-24 05:42:40 +08:00
Simplify config handling
* Switch to urfave/cli/v2 * Add more environment variables
This commit is contained in:
parent
d1f69bc704
commit
262853f6c0
58
README.md
58
README.md
@ -597,35 +597,33 @@ $ cp mark /usr/local/bin
|
|||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
```
|
|
||||||
mark [options] [-u <username>] [-p <password>] [-k] [-l <url>] -f <file>
|
|
||||||
mark [options] [-u <username>] [-p <password>] [-k] [-b <url>] -f <file>
|
|
||||||
mark [options] [-u <username>] [-p <password>] [--drop-h1] -f <file>
|
|
||||||
mark -v | --version
|
|
||||||
mark -h | --help
|
|
||||||
```
|
```
|
||||||
|
|
||||||
- `-u <username>` — Use specified username for updating Confluence page.
|
USAGE:
|
||||||
- `-p <password>` — Use specified password for updating Confluence page.
|
mark [global options] [arguments...]
|
||||||
Specify `-` as password to read password from stdin.
|
|
||||||
- `-l <url>` — Edit specified Confluence page.
|
GLOBAL OPTIONS:
|
||||||
If -l is not specified, file should contain metadata (see above).
|
--files value, -f value use specified markdown file(s) for converting to html. Supports file globbing patterns (needs to be quoted). [$MARK_FILES]
|
||||||
- `-b <url>` or `--base-url <url>` – Base URL for Confluence.
|
--compile-only show resulting HTML and don't update Confluence page content. (default: false) [$MARK_COMPILE_ONLY]
|
||||||
Alternative option for `base_url` config field.
|
--dry-run resolve page and ancestry, show resulting HTML and exit. (default: false) [$MARK_DRY_RUN]
|
||||||
- `-f <file>` — Use specified markdown file(s) for converting to html. Supports file globbing patterns (needs to be quoted).
|
--edit-lock, -k lock page editing to current user only to prevent accidental manual edits over Confluence Web UI. (default: false) [$MARK_EDIT_LOCK]
|
||||||
- `-c <path>` or `--config <path>` — Specify a path to the configuration file.
|
--drop-h1 don't include H1 headings in Confluence output. (default: false) [$MARK_H1_DROP]
|
||||||
- `-k` — Lock page editing to current user only to prevent accidental
|
--title-from-h1 extract page title from a leading H1 heading. If no H1 heading on a page exists, then title must be set in the page metadata. (default: false) [$MARK_H1_TITLE]
|
||||||
manual edits over Confluence Web UI.
|
--minor-edit don't send notifications while updating Confluence page. (default: false) [$MARK_MINOR_EDIT]
|
||||||
- `--space <space>` - Use specified space key. If the space key is not specified, it must be set in the page metadata.
|
--color value display logs in color. Possible values: auto, never. (default: "auto") [$MARK_COLOR]
|
||||||
- `--drop-h1` – Don't include H1 headings in Confluence output.
|
--debug enable debug logs. (default: false) [$MARK_DEBUG]
|
||||||
This option corresponds to the `h1_drop` setting in the configuration file.
|
--trace enable trace logs. (default: false) [$MARK_TRACE]
|
||||||
- `--title-from-h1` - Extract page title from a leading H1 heading. If no H1 heading on a page exists, then title must be set in the page metadata.
|
--username value, -u value use specified username for updating Confluence page. [$MARK_USERNAME]
|
||||||
This option corresponds to the `h1_title` setting in the configuration file.
|
--password value, -p value use specified token for updating Confluence page. Specify - as password to read password from stdin, or your Personal access token. Username is not mandatory if personal access token is provided. For more info please see: https://developer.atlassian.com/server/confluence/confluence-server-rest-api/#authentication. [$MARK_PASSWORD]
|
||||||
- `--dry-run` — Show resulting HTML and don't update Confluence page content.
|
--target-url value, -l value edit specified Confluence page. If -l is not specified, file should contain metadata (see above). [$MARK_TARGET_URL]
|
||||||
- `--minor-edit` — Don't send notifications while updating Confluence page.
|
--base-url value, -b value base URL for Confluence. Alternative option for base_url config field. [$MARK_BASE_URL]
|
||||||
- `--trace` — Enable trace logs.
|
--config value, -c value use the specified configuration file. (default: "/home/mruger/.config/mark") [$MARK_CONFIG]
|
||||||
- `-v | --version` — Show version.
|
--ci run on CI mode. It won't fail if files are not found. (default: false) [$MARK_CI]
|
||||||
- `-h | --help` — Show help screen and call 911.
|
--space value use specified space key. If the space key is not specified, it must be set in the page metadata. [$MARK_SPACE]
|
||||||
|
--help, -h show help
|
||||||
|
--version, -v print the version
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
You can store user credentials in the configuration file, which should be
|
You can store user credentials in the configuration file, which should be
|
||||||
located in ~/.config/mark (or specified via `-c --config <path>`) with the following format (TOML):
|
located in ~/.config/mark (or specified via `-c --config <path>`) with the following format (TOML):
|
||||||
@ -634,9 +632,9 @@ located in ~/.config/mark (or specified via `-c --config <path>`) with the follo
|
|||||||
username = "your-email"
|
username = "your-email"
|
||||||
password = "password-or-api-key-for-confluence-cloud"
|
password = "password-or-api-key-for-confluence-cloud"
|
||||||
# If you are using Confluence Cloud add the /wiki suffix to base_url
|
# If you are using Confluence Cloud add the /wiki suffix to base_url
|
||||||
base_url = "http://confluence.local"
|
base-url = "http://confluence.local"
|
||||||
h1_title = true
|
h1-title = true
|
||||||
h1_drop = true
|
h1-drop = true
|
||||||
```
|
```
|
||||||
|
|
||||||
**NOTE**: Labels aren't supported when using `minor-edit`!
|
**NOTE**: Labels aren't supported when using `minor-edit`!
|
||||||
|
46
auth.go
46
auth.go
@ -18,32 +18,23 @@ type Credentials struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func GetCredentials(
|
func GetCredentials(
|
||||||
flags Flags,
|
username string,
|
||||||
config *Config,
|
password string,
|
||||||
|
targetURL string,
|
||||||
|
baseURL string,
|
||||||
|
compileOnly bool,
|
||||||
|
|
||||||
) (*Credentials, error) {
|
) (*Credentials, error) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
var (
|
|
||||||
username = flags.Username
|
|
||||||
password = flags.Password
|
|
||||||
targetURL = flags.TargetURL
|
|
||||||
)
|
|
||||||
|
|
||||||
if username == "" {
|
|
||||||
username = config.Username
|
|
||||||
}
|
|
||||||
|
|
||||||
if password == "" {
|
if password == "" {
|
||||||
password = config.Password
|
if !compileOnly {
|
||||||
if password == "" {
|
return nil, errors.New(
|
||||||
if !flags.CompileOnly {
|
"confluence password should be specified using -p " +
|
||||||
return nil, errors.New(
|
"flag or be stored in configuration file",
|
||||||
"Confluence password should be specified using -p " +
|
)
|
||||||
"flag or be stored in configuration file",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
password = "none"
|
|
||||||
}
|
}
|
||||||
|
password = "none"
|
||||||
}
|
}
|
||||||
|
|
||||||
if password == "-" {
|
if password == "-" {
|
||||||
@ -58,7 +49,7 @@ func GetCredentials(
|
|||||||
password = string(stdin)
|
password = string(stdin)
|
||||||
}
|
}
|
||||||
|
|
||||||
if flags.CompileOnly && targetURL == "" {
|
if compileOnly && targetURL == "" {
|
||||||
targetURL = "http://localhost"
|
targetURL = "http://localhost"
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,20 +61,15 @@ func GetCredentials(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
baseURL := url.Scheme + "://" + url.Host
|
|
||||||
|
|
||||||
if url.Host == "" {
|
if url.Host == "" {
|
||||||
baseURL = flags.BaseURL
|
|
||||||
if baseURL == "" {
|
|
||||||
baseURL = config.BaseURL
|
|
||||||
}
|
|
||||||
|
|
||||||
if baseURL == "" {
|
if baseURL == "" {
|
||||||
return nil, errors.New(
|
return nil, errors.New(
|
||||||
"Confluence base URL should be specified using -l " +
|
"confluence base URL should be specified using -l " +
|
||||||
"flag or be stored in configuration file",
|
"flag or be stored in configuration file",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
baseURL = url.Scheme + "://" + url.Host
|
||||||
}
|
}
|
||||||
|
|
||||||
baseURL = strings.TrimRight(baseURL, `/`)
|
baseURL = strings.TrimRight(baseURL, `/`)
|
||||||
|
29
config.go
29
config.go
@ -1,29 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/kovetskiy/ko"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Config struct {
|
|
||||||
Username string `env:"MARK_USERNAME" toml:"username"`
|
|
||||||
Password string `env:"MARK_PASSWORD" toml:"password"`
|
|
||||||
BaseURL string `env:"MARK_BASE_URL" toml:"base_url"`
|
|
||||||
H1Title bool `env:"MARK_H1_TITLE" toml:"h1_title"`
|
|
||||||
H1Drop bool `env:"MARK_H1_DROP" toml:"h1_drop"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func LoadConfig(path string) (*Config, error) {
|
|
||||||
config := &Config{}
|
|
||||||
err := ko.Load(path, config)
|
|
||||||
if err != nil {
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return config, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return config, nil
|
|
||||||
}
|
|
7
go.mod
7
go.mod
@ -3,14 +3,13 @@ module github.com/kovetskiy/mark
|
|||||||
go 1.19
|
go 1.19
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815
|
|
||||||
github.com/kovetskiy/gopencils v0.0.0-20230119081704-a73db75b2f69
|
github.com/kovetskiy/gopencils v0.0.0-20230119081704-a73db75b2f69
|
||||||
github.com/kovetskiy/ko v1.6.1
|
|
||||||
github.com/kovetskiy/lorg v1.2.0
|
github.com/kovetskiy/lorg v1.2.0
|
||||||
github.com/reconquest/karma-go v0.0.0-20220904173930-21741aa386a6
|
github.com/reconquest/karma-go v0.0.0-20220904173930-21741aa386a6
|
||||||
github.com/reconquest/pkg v1.3.0
|
github.com/reconquest/pkg v1.3.0
|
||||||
github.com/reconquest/regexputil-go v0.0.0-20160905154124-38573e70c1f4
|
github.com/reconquest/regexputil-go v0.0.0-20160905154124-38573e70c1f4
|
||||||
github.com/stretchr/testify v1.8.2
|
github.com/stretchr/testify v1.8.2
|
||||||
|
github.com/urfave/cli/v2 v2.25.1
|
||||||
github.com/yuin/goldmark v1.5.4
|
github.com/yuin/goldmark v1.5.4
|
||||||
golang.org/x/tools v0.7.0
|
golang.org/x/tools v0.7.0
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
@ -18,11 +17,13 @@ require (
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/BurntSushi/toml v1.2.1 // indirect
|
github.com/BurntSushi/toml v1.2.1 // indirect
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/iancoleman/strcase v0.2.0 // indirect
|
|
||||||
github.com/kr/pretty v0.1.0 // indirect
|
github.com/kr/pretty v0.1.0 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/reconquest/cog v0.0.0-20210820140837-c5c4e8f49c65 // indirect
|
github.com/reconquest/cog v0.0.0-20210820140837-c5c4e8f49c65 // indirect
|
||||||
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
|
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||||
github.com/zazab/zhash v0.0.0-20210630080733-6e809466f8d3 // indirect
|
github.com/zazab/zhash v0.0.0-20210630080733-6e809466f8d3 // indirect
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
|
||||||
)
|
)
|
||||||
|
14
go.sum
14
go.sum
@ -1,17 +1,13 @@
|
|||||||
github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||||
github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak=
|
github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak=
|
||||||
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 h1:bWDMxwH3px2JBh6AyO7hdCn/PkvCZXii8TGj7sbtEbQ=
|
|
||||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
|
||||||
github.com/iancoleman/strcase v0.2.0 h1:05I4QRnGpI0m37iZQRuskXh+w77mr6Z41lwQzuHLwW0=
|
|
||||||
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
|
|
||||||
github.com/kovetskiy/gopencils v0.0.0-20230119081704-a73db75b2f69 h1:vn82v0gKhTTm67znr7nxYBNW4mJ8zfY7dywZivUy3tY=
|
github.com/kovetskiy/gopencils v0.0.0-20230119081704-a73db75b2f69 h1:vn82v0gKhTTm67znr7nxYBNW4mJ8zfY7dywZivUy3tY=
|
||||||
github.com/kovetskiy/gopencils v0.0.0-20230119081704-a73db75b2f69/go.mod h1:t7LFI5v8Q5+nl9sqId9PS0C9H9F4c5d4XlhkLve1MCM=
|
github.com/kovetskiy/gopencils v0.0.0-20230119081704-a73db75b2f69/go.mod h1:t7LFI5v8Q5+nl9sqId9PS0C9H9F4c5d4XlhkLve1MCM=
|
||||||
github.com/kovetskiy/ko v1.6.1 h1:EO5v6CrW6x6vzxo7CKbN0r+foIRjz06U6wVSgxUVqMc=
|
|
||||||
github.com/kovetskiy/ko v1.6.1/go.mod h1:WH6doo9XYpbDWe9HsELro1vXAfXCM4ByG5arIp9JjDE=
|
|
||||||
github.com/kovetskiy/lorg v0.0.0-20200107130803-9a7136a95634/go.mod h1:B8HeKAukXULNzWWsW5k/SQyDkiQZPn7lTBJDB46MZ9I=
|
github.com/kovetskiy/lorg v0.0.0-20200107130803-9a7136a95634/go.mod h1:B8HeKAukXULNzWWsW5k/SQyDkiQZPn7lTBJDB46MZ9I=
|
||||||
github.com/kovetskiy/lorg v1.2.0 h1:wNIUT/VOhcjKOmizDClZLvchbKFGW+dzf9fQXbSVS5E=
|
github.com/kovetskiy/lorg v1.2.0 h1:wNIUT/VOhcjKOmizDClZLvchbKFGW+dzf9fQXbSVS5E=
|
||||||
github.com/kovetskiy/lorg v1.2.0/go.mod h1:rdiamaIRUCkX9HtFZd0D9dQqUbad21hipHk+sat7Z6s=
|
github.com/kovetskiy/lorg v1.2.0/go.mod h1:rdiamaIRUCkX9HtFZd0D9dQqUbad21hipHk+sat7Z6s=
|
||||||
@ -31,6 +27,8 @@ github.com/reconquest/pkg v1.3.0 h1:Yuoxiw92rP/srKXMo5qSML2InhJ+xAqHJIx3/y/2zh8=
|
|||||||
github.com/reconquest/pkg v1.3.0/go.mod h1:hUQ0SzzBlFRSbo6lFYG2tSpLMjqOuUqm2LtpjR/+1sg=
|
github.com/reconquest/pkg v1.3.0/go.mod h1:hUQ0SzzBlFRSbo6lFYG2tSpLMjqOuUqm2LtpjR/+1sg=
|
||||||
github.com/reconquest/regexputil-go v0.0.0-20160905154124-38573e70c1f4 h1:bcDXaTFC09IIg13Z8gfQHk4gSu001ET7ssW/wKRvPzg=
|
github.com/reconquest/regexputil-go v0.0.0-20160905154124-38573e70c1f4 h1:bcDXaTFC09IIg13Z8gfQHk4gSu001ET7ssW/wKRvPzg=
|
||||||
github.com/reconquest/regexputil-go v0.0.0-20160905154124-38573e70c1f4/go.mod h1:OI1di2iiFSwX3D70iZjzdmCPPfssjOl+HX40tI3VaXA=
|
github.com/reconquest/regexputil-go v0.0.0-20160905154124-38573e70c1f4/go.mod h1:OI1di2iiFSwX3D70iZjzdmCPPfssjOl+HX40tI3VaXA=
|
||||||
|
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||||
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
@ -39,6 +37,10 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
|||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
||||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
github.com/urfave/cli/v2 v2.25.1 h1:zw8dSP7ghX0Gmm8vugrs6q9Ku0wzweqPyshy+syu9Gw=
|
||||||
|
github.com/urfave/cli/v2 v2.25.1/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
|
||||||
|
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
|
||||||
|
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
|
||||||
github.com/yuin/goldmark v1.5.4 h1:2uY/xC0roWy8IBEGLgB1ywIoEJFGmRrX21YQcvGZzjU=
|
github.com/yuin/goldmark v1.5.4 h1:2uY/xC0roWy8IBEGLgB1ywIoEJFGmRrX21YQcvGZzjU=
|
||||||
github.com/yuin/goldmark v1.5.4/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
github.com/yuin/goldmark v1.5.4/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
github.com/zazab/zhash v0.0.0-20210630080733-6e809466f8d3 h1:BhVaeQJc3xalHGONn215FylzuxdQBIT3d/aRjDg4nXQ=
|
github.com/zazab/zhash v0.0.0-20210630080733-6e809466f8d3 h1:BhVaeQJc3xalHGONn215FylzuxdQBIT3d/aRjDg4nXQ=
|
||||||
|
269
main.go
269
main.go
@ -7,7 +7,6 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/docopt/docopt-go"
|
|
||||||
"github.com/kovetskiy/lorg"
|
"github.com/kovetskiy/lorg"
|
||||||
"github.com/kovetskiy/mark/pkg/confluence"
|
"github.com/kovetskiy/mark/pkg/confluence"
|
||||||
"github.com/kovetskiy/mark/pkg/mark"
|
"github.com/kovetskiy/mark/pkg/mark"
|
||||||
@ -16,96 +15,170 @@ import (
|
|||||||
"github.com/kovetskiy/mark/pkg/mark/stdlib"
|
"github.com/kovetskiy/mark/pkg/mark/stdlib"
|
||||||
"github.com/reconquest/karma-go"
|
"github.com/reconquest/karma-go"
|
||||||
"github.com/reconquest/pkg/log"
|
"github.com/reconquest/pkg/log"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
"github.com/urfave/cli/v2/altsrc"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Flags struct {
|
|
||||||
FileGlobPatten string `docopt:"-f"`
|
|
||||||
CompileOnly bool `docopt:"--compile-only"`
|
|
||||||
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"`
|
|
||||||
Trace bool `docopt:"--trace"`
|
|
||||||
Username string `docopt:"-u"`
|
|
||||||
Password string `docopt:"-p"`
|
|
||||||
TargetURL string `docopt:"-l"`
|
|
||||||
BaseURL string `docopt:"--base-url"`
|
|
||||||
Config string `docopt:"--config"`
|
|
||||||
Ci bool `docopt:"--ci"`
|
|
||||||
Space string `docopt:"--space"`
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
version = "9.1.4"
|
version = "9.1.4"
|
||||||
usage = `mark - a tool for updating Atlassian Confluence pages from markdown.
|
usage = "A tool for updating Atlassian Confluence pages from markdown."
|
||||||
|
description = `Mark is a tool to update Atlassian Confluence pages from markdown. Documentation is available here: https://github.com/kovetskiy/mark
|
||||||
Docs: https://github.com/kovetskiy/mark
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
mark [options] [-u <username>] [-p <token>] [-k] [-l <url>] -f <file>
|
|
||||||
mark [options] [-u <username>] [-p <password>] [-k] [-b <url>] -f <file>
|
|
||||||
mark -v | --version
|
|
||||||
mark -h | --help
|
|
||||||
|
|
||||||
Options:
|
|
||||||
-u <username> Use specified username for updating Confluence page.
|
|
||||||
-p <token> Use specified token for updating Confluence page.
|
|
||||||
Specify - as password to read password from stdin, or your Personal access token.
|
|
||||||
Username is not mandatory if personal access token is provided.
|
|
||||||
For more info please see: https://developer.atlassian.com/server/confluence/confluence-server-rest-api/#authentication.
|
|
||||||
-l <url> Edit specified Confluence page.
|
|
||||||
If -l is not specified, file should contain metadata (see
|
|
||||||
above).
|
|
||||||
-b --base-url <url> Base URL for Confluence.
|
|
||||||
Alternative option for base_url config field.
|
|
||||||
-f <file> Use specified markdown file(s) for converting to html.
|
|
||||||
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 the space key is not specified, it must
|
|
||||||
be set in the 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 exists, then title must be set in the 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.
|
|
||||||
--debug Enable debug logs.
|
|
||||||
--trace Enable trace logs.
|
|
||||||
--color <when> Display logs in color. Possible values: auto, never.
|
|
||||||
[default: auto]
|
|
||||||
-c --config <path> Use the specified configuration file.
|
|
||||||
[default: $HOME/.config/mark]
|
|
||||||
--ci Runs on CI mode. It won't fail if files are not found.
|
|
||||||
-h --help Show this message.
|
|
||||||
-v --version Show version.
|
|
||||||
`
|
`
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var flags = []cli.Flag{
|
||||||
|
altsrc.NewStringFlag(&cli.StringFlag{
|
||||||
|
Name: "files",
|
||||||
|
Aliases: []string{"f"},
|
||||||
|
Value: "",
|
||||||
|
Usage: "use specified markdown file(s) for converting to html. Supports file globbing patterns (needs to be quoted).",
|
||||||
|
EnvVars: []string{"MARK_FILES"},
|
||||||
|
}),
|
||||||
|
altsrc.NewBoolFlag(&cli.BoolFlag{
|
||||||
|
Name: "compile-only",
|
||||||
|
Value: false,
|
||||||
|
Usage: "show resulting HTML and don't update Confluence page content.",
|
||||||
|
EnvVars: []string{"MARK_COMPILE_ONLY"},
|
||||||
|
}),
|
||||||
|
altsrc.NewBoolFlag(&cli.BoolFlag{
|
||||||
|
Name: "dry-run",
|
||||||
|
Value: false,
|
||||||
|
Usage: "resolve page and ancestry, show resulting HTML and exit.",
|
||||||
|
EnvVars: []string{"MARK_DRY_RUN"},
|
||||||
|
}),
|
||||||
|
altsrc.NewBoolFlag(&cli.BoolFlag{
|
||||||
|
Name: "edit-lock",
|
||||||
|
Value: false,
|
||||||
|
Aliases: []string{"k"},
|
||||||
|
Usage: "lock page editing to current user only to prevent accidental manual edits over Confluence Web UI.",
|
||||||
|
EnvVars: []string{"MARK_EDIT_LOCK"},
|
||||||
|
}),
|
||||||
|
altsrc.NewBoolFlag(&cli.BoolFlag{
|
||||||
|
Name: "drop-h1",
|
||||||
|
Value: false,
|
||||||
|
Usage: "don't include H1 headings in Confluence output.",
|
||||||
|
EnvVars: []string{"MARK_H1_DROP"},
|
||||||
|
}),
|
||||||
|
altsrc.NewBoolFlag(&cli.BoolFlag{
|
||||||
|
Name: "title-from-h1",
|
||||||
|
Value: false,
|
||||||
|
Usage: "extract page title from a leading H1 heading. If no H1 heading on a page exists, then title must be set in the page metadata.",
|
||||||
|
EnvVars: []string{"MARK_H1_TITLE"},
|
||||||
|
}),
|
||||||
|
altsrc.NewBoolFlag(&cli.BoolFlag{
|
||||||
|
Name: "minor-edit",
|
||||||
|
Value: false,
|
||||||
|
Usage: "don't send notifications while updating Confluence page.",
|
||||||
|
EnvVars: []string{"MARK_MINOR_EDIT"},
|
||||||
|
}),
|
||||||
|
altsrc.NewStringFlag(&cli.StringFlag{
|
||||||
|
Name: "color",
|
||||||
|
Value: "auto",
|
||||||
|
Usage: "display logs in color. Possible values: auto, never.",
|
||||||
|
EnvVars: []string{"MARK_COLOR"},
|
||||||
|
}),
|
||||||
|
altsrc.NewBoolFlag(&cli.BoolFlag{
|
||||||
|
Name: "debug",
|
||||||
|
Value: false,
|
||||||
|
Usage: "enable debug logs.",
|
||||||
|
EnvVars: []string{"MARK_DEBUG"},
|
||||||
|
}),
|
||||||
|
altsrc.NewBoolFlag(&cli.BoolFlag{
|
||||||
|
Name: "trace",
|
||||||
|
Value: false,
|
||||||
|
Usage: "enable trace logs.",
|
||||||
|
EnvVars: []string{"MARK_TRACE"},
|
||||||
|
}),
|
||||||
|
altsrc.NewStringFlag(&cli.StringFlag{
|
||||||
|
Name: "username",
|
||||||
|
Aliases: []string{"u"},
|
||||||
|
Value: "",
|
||||||
|
Usage: "use specified username for updating Confluence page.",
|
||||||
|
EnvVars: []string{"MARK_USERNAME"},
|
||||||
|
}),
|
||||||
|
altsrc.NewStringFlag(&cli.StringFlag{
|
||||||
|
Name: "password",
|
||||||
|
Aliases: []string{"p"},
|
||||||
|
Value: "",
|
||||||
|
Usage: "use specified token for updating Confluence page. Specify - as password to read password from stdin, or your Personal access token. Username is not mandatory if personal access token is provided. For more info please see: https://developer.atlassian.com/server/confluence/confluence-server-rest-api/#authentication.",
|
||||||
|
EnvVars: []string{"MARK_PASSWORD"},
|
||||||
|
}),
|
||||||
|
altsrc.NewStringFlag(&cli.StringFlag{
|
||||||
|
Name: "target-url",
|
||||||
|
Aliases: []string{"l"},
|
||||||
|
Value: "",
|
||||||
|
Usage: "edit specified Confluence page. If -l is not specified, file should contain metadata (see above).",
|
||||||
|
EnvVars: []string{"MARK_TARGET_URL"},
|
||||||
|
}),
|
||||||
|
altsrc.NewStringFlag(&cli.StringFlag{
|
||||||
|
Name: "base-url",
|
||||||
|
Aliases: []string{"b"},
|
||||||
|
Value: "",
|
||||||
|
Usage: "base URL for Confluence. Alternative option for base_url config field.",
|
||||||
|
EnvVars: []string{"MARK_BASE_URL"},
|
||||||
|
}),
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "config",
|
||||||
|
Aliases: []string{"c"},
|
||||||
|
Value: filepath.Join(os.Getenv("HOME"), ".config/mark"),
|
||||||
|
Usage: "use the specified configuration file.",
|
||||||
|
TakesFile: true,
|
||||||
|
EnvVars: []string{"MARK_CONFIG"},
|
||||||
|
},
|
||||||
|
altsrc.NewBoolFlag(&cli.BoolFlag{
|
||||||
|
Name: "ci",
|
||||||
|
Value: false,
|
||||||
|
Usage: "run on CI mode. It won't fail if files are not found.",
|
||||||
|
EnvVars: []string{"MARK_CI"},
|
||||||
|
}),
|
||||||
|
altsrc.NewStringFlag(&cli.StringFlag{
|
||||||
|
Name: "space",
|
||||||
|
Value: "",
|
||||||
|
Usage: "use specified space key. If the space key is not specified, it must be set in the page metadata.",
|
||||||
|
EnvVars: []string{"MARK_SPACE"},
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
cmd, err := docopt.ParseArgs(os.ExpandEnv(usage), nil, version)
|
app := &cli.App{
|
||||||
if err != nil {
|
Name: "mark",
|
||||||
panic(err)
|
Usage: usage,
|
||||||
|
Description: description,
|
||||||
|
Version: version,
|
||||||
|
Flags: flags,
|
||||||
|
Before: altsrc.InitInputSourceWithContext(flags,
|
||||||
|
func(context *cli.Context) (altsrc.InputSourceContext, error) {
|
||||||
|
if context.IsSet("config") {
|
||||||
|
filePath := context.String("config")
|
||||||
|
return altsrc.NewTomlSourceFromFile(filePath)
|
||||||
|
} else {
|
||||||
|
// Fall back to default if config is unset
|
||||||
|
return altsrc.NewTomlSourceFromFile(filepath.Join(os.Getenv("HOME"), ".config/mark"))
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
EnableBashCompletion: true,
|
||||||
|
HideHelpCommand: true,
|
||||||
|
Action: func(cCtx *cli.Context) error {
|
||||||
|
return RunMark(cCtx)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var flags Flags
|
if err := app.Run(os.Args); err != nil {
|
||||||
err = cmd.Bind(&flags)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if flags.Debug {
|
func RunMark(cCtx *cli.Context) error {
|
||||||
|
|
||||||
|
if cCtx.Bool("debug") {
|
||||||
log.SetLevel(lorg.LevelDebug)
|
log.SetLevel(lorg.LevelDebug)
|
||||||
}
|
}
|
||||||
|
|
||||||
if flags.Trace {
|
if cCtx.Bool("trace") {
|
||||||
log.SetLevel(lorg.LevelTrace)
|
log.SetLevel(lorg.LevelTrace)
|
||||||
}
|
}
|
||||||
|
|
||||||
if flags.Color == "never" {
|
if cCtx.String("color") == "never" {
|
||||||
log.GetLogger().SetFormat(
|
log.GetLogger().SetFormat(
|
||||||
lorg.NewFormat(
|
lorg.NewFormat(
|
||||||
`${time:2006-01-02 15:04:05.000} ${level:%s:left:true} ${prefix}%s`,
|
`${time:2006-01-02 15:04:05.000} ${level:%s:left:true} ${prefix}%s`,
|
||||||
@ -114,33 +187,20 @@ func main() {
|
|||||||
log.GetLogger().SetOutput(os.Stderr)
|
log.GetLogger().SetOutput(os.Stderr)
|
||||||
}
|
}
|
||||||
|
|
||||||
config, err := LoadConfig(flags.Config)
|
creds, err := GetCredentials(cCtx.String("username"), cCtx.String("password"), cCtx.String("target-url"), cCtx.String("base-url"), cCtx.Bool("compile-only"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
return err
|
||||||
}
|
|
||||||
|
|
||||||
if !flags.TitleFromH1 && config.H1Title {
|
|
||||||
flags.TitleFromH1 = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if !flags.DropH1 && config.H1Drop {
|
|
||||||
flags.DropH1 = true
|
|
||||||
}
|
|
||||||
|
|
||||||
creds, err := GetCredentials(flags, config)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
api := confluence.NewAPI(creds.BaseURL, creds.Username, creds.Password)
|
api := confluence.NewAPI(creds.BaseURL, creds.Username, creds.Password)
|
||||||
|
|
||||||
files, err := filepath.Glob(flags.FileGlobPatten)
|
files, err := filepath.Glob(cCtx.String("files"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
return err
|
||||||
}
|
}
|
||||||
if len(files) == 0 {
|
if len(files) == 0 {
|
||||||
msg := "No files matched"
|
msg := "No files matched"
|
||||||
if flags.Ci {
|
if cCtx.Bool("ci") {
|
||||||
log.Warning(msg)
|
log.Warning(msg)
|
||||||
} else {
|
} else {
|
||||||
log.Fatal(msg)
|
log.Fatal(msg)
|
||||||
@ -155,7 +215,7 @@ func main() {
|
|||||||
file,
|
file,
|
||||||
)
|
)
|
||||||
|
|
||||||
target := processFile(file, api, flags, creds.PageID, creds.Username)
|
target := processFile(file, api, cCtx, creds.PageID, creds.Username)
|
||||||
|
|
||||||
log.Infof(
|
log.Infof(
|
||||||
nil,
|
nil,
|
||||||
@ -165,12 +225,13 @@ func main() {
|
|||||||
|
|
||||||
fmt.Println(creds.BaseURL + target.Links.Full)
|
fmt.Println(creds.BaseURL + target.Links.Full)
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func processFile(
|
func processFile(
|
||||||
file string,
|
file string,
|
||||||
api *confluence.API,
|
api *confluence.API,
|
||||||
flags Flags,
|
cCtx *cli.Context,
|
||||||
pageID string,
|
pageID string,
|
||||||
username string,
|
username string,
|
||||||
) *confluence.PageInfo {
|
) *confluence.PageInfo {
|
||||||
@ -181,7 +242,7 @@ func processFile(
|
|||||||
|
|
||||||
markdown = bytes.ReplaceAll(markdown, []byte("\r\n"), []byte("\n"))
|
markdown = bytes.ReplaceAll(markdown, []byte("\r\n"), []byte("\n"))
|
||||||
|
|
||||||
meta, markdown, err := mark.ExtractMeta(markdown, flags.Space, flags.TitleFromH1)
|
meta, markdown, err := mark.ExtractMeta(markdown, cCtx.String("space"), cCtx.Bool("title-from-h1"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -258,24 +319,22 @@ func processFile(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
links, err := mark.ResolveRelativeLinks(api, meta, markdown, filepath.Dir(file), flags.Space, flags.TitleFromH1)
|
links, err := mark.ResolveRelativeLinks(api, meta, markdown, filepath.Dir(file), cCtx.String("space"), cCtx.Bool("title-from-h1"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf(err, "unable to resolve relative links")
|
log.Fatalf(err, "unable to resolve relative links")
|
||||||
}
|
}
|
||||||
|
|
||||||
markdown = mark.SubstituteLinks(markdown, links)
|
markdown = mark.SubstituteLinks(markdown, links)
|
||||||
|
|
||||||
if flags.DryRun {
|
if cCtx.Bool("dry-run") {
|
||||||
flags.CompileOnly = true
|
_, _, err := mark.ResolvePage(cCtx.Bool("dry-run"), api, meta)
|
||||||
|
|
||||||
_, _, err := mark.ResolvePage(flags.DryRun, api, meta)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf(err, "unable to resolve page location")
|
log.Fatalf(err, "unable to resolve page location")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if flags.CompileOnly {
|
if cCtx.Bool("compile-only") || cCtx.Bool("dry-run") {
|
||||||
if flags.DropH1 {
|
if cCtx.Bool("drop-h1") {
|
||||||
log.Info(
|
log.Info(
|
||||||
"the leading H1 heading will be excluded from the Confluence output",
|
"the leading H1 heading will be excluded from the Confluence output",
|
||||||
)
|
)
|
||||||
@ -289,7 +348,7 @@ func processFile(
|
|||||||
var target *confluence.PageInfo
|
var target *confluence.PageInfo
|
||||||
|
|
||||||
if meta != nil {
|
if meta != nil {
|
||||||
parent, page, err := mark.ResolvePage(flags.DryRun, api, meta)
|
parent, page, err := mark.ResolvePage(cCtx.Bool("dry-run"), api, meta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf(
|
log.Fatalf(
|
||||||
karma.Describe("title", meta.Title).Reason(err),
|
karma.Describe("title", meta.Title).Reason(err),
|
||||||
@ -346,7 +405,7 @@ func processFile(
|
|||||||
|
|
||||||
markdown = mark.CompileAttachmentLinks(markdown, attaches)
|
markdown = mark.CompileAttachmentLinks(markdown, attaches)
|
||||||
|
|
||||||
if flags.DropH1 {
|
if cCtx.Bool("drop-h1") {
|
||||||
log.Info(
|
log.Info(
|
||||||
"the leading H1 heading will be excluded from the Confluence output",
|
"the leading H1 heading will be excluded from the Confluence output",
|
||||||
)
|
)
|
||||||
@ -378,12 +437,12 @@ func processFile(
|
|||||||
html = buffer.String()
|
html = buffer.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
err = api.UpdatePage(target, html, flags.MinorEdit, meta.Labels, meta.ContentAppearance)
|
err = api.UpdatePage(target, html, cCtx.Bool("minor-edit"), meta.Labels, meta.ContentAppearance)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if flags.EditLock {
|
if cCtx.Bool("edit-lock") {
|
||||||
log.Infof(
|
log.Infof(
|
||||||
nil,
|
nil,
|
||||||
`edit locked on page %q by user %q to prevent manual edits`,
|
`edit locked on page %q by user %q to prevent manual edits`,
|
||||||
|
@ -181,7 +181,7 @@ func parseLinks(markdown string) []markdownLink {
|
|||||||
return links
|
return links
|
||||||
}
|
}
|
||||||
|
|
||||||
// getConfluenceLink build (to be) link for Conflunce, and tries to verify from
|
// getConfluenceLink build (to be) link for Confluence, and tries to verify from
|
||||||
// API if there's real link available
|
// API if there's real link available
|
||||||
func getConfluenceLink(
|
func getConfluenceLink(
|
||||||
api *confluence.API,
|
api *confluence.API,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user