mirror of
https://github.com/kovetskiy/mark.git
synced 2025-04-23 13:22:40 +08:00
add code & testing files for continue-on-error flag
- add continue-on-error flag as a command line option - if set, doesnt exit on error and continues processing other files that were passed in - add fatalErrorHandler to handle fatal errors - if continue-on-error flag is set, does not exit - add temporary tests for continue-on-error flag - add tests in batch-tests subdirectory
This commit is contained in:
parent
ff015e2c24
commit
024259e480
35
error_handler.go
Normal file
35
error_handler.go
Normal file
@ -0,0 +1,35 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/reconquest/pkg/log"
|
||||
)
|
||||
|
||||
type FatalErrorHandler struct {
|
||||
ContinueOnError bool
|
||||
}
|
||||
|
||||
func NewErrorHandler(continueOnError bool) *FatalErrorHandler {
|
||||
return &FatalErrorHandler{
|
||||
ContinueOnError: continueOnError,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *FatalErrorHandler) Handle(err error, format string, args ...interface{}) {
|
||||
errorMesage := fmt.Sprintf(format, args...)
|
||||
|
||||
if err == nil {
|
||||
if h.ContinueOnError {
|
||||
log.Error(errorMesage)
|
||||
return
|
||||
}
|
||||
log.Fatal(errorMesage)
|
||||
}
|
||||
|
||||
if h.ContinueOnError {
|
||||
log.Errorf(err, errorMesage)
|
||||
return
|
||||
}
|
||||
log.Fatalf(err, errorMesage)
|
||||
}
|
152
main.go
152
main.go
@ -44,6 +44,12 @@ var flags = []cli.Flag{
|
||||
TakesFile: true,
|
||||
EnvVars: []string{"MARK_FILES"},
|
||||
}),
|
||||
altsrc.NewBoolFlag(&cli.BoolFlag{
|
||||
Name: "continue-on-error",
|
||||
Value: false,
|
||||
Usage: "dont exit if an error occurs while processing a file, continue processing remaining files.",
|
||||
EnvVars: []string{"MARK_CONTINUE_ON_ERROR"},
|
||||
}),
|
||||
altsrc.NewBoolFlag(&cli.BoolFlag{
|
||||
Name: "compile-only",
|
||||
Value: false,
|
||||
@ -276,6 +282,11 @@ func RunMark(cCtx *cli.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
fatalErrorHandler := NewErrorHandler(cCtx.Bool("continue-on-error"))
|
||||
|
||||
fmt.Printf("Processing %d files\n", len(files))
|
||||
fmt.Printf("continue-on-error: %t\n", cCtx.Bool("continue-on-error"))
|
||||
|
||||
// Loop through files matched by glob pattern
|
||||
for _, file := range files {
|
||||
log.Infof(
|
||||
@ -284,9 +295,9 @@ func RunMark(cCtx *cli.Context) error {
|
||||
file,
|
||||
)
|
||||
|
||||
target := processFile(file, api, cCtx, creds.PageID, creds.Username)
|
||||
target := processFile(file, api, cCtx, creds.PageID, creds.Username, fatalErrorHandler)
|
||||
|
||||
if target != nil && !(cCtx.Bool("dry-run") || cCtx.Bool("compile-only")) { // on dry-run or compile-only, the target is nil
|
||||
if target != nil { // on dry-run or compile-only, the target is nil
|
||||
log.Infof(
|
||||
nil,
|
||||
"page successfully updated: %s",
|
||||
@ -304,10 +315,13 @@ func processFile(
|
||||
cCtx *cli.Context,
|
||||
pageID string,
|
||||
username string,
|
||||
fatalErrorHandler *FatalErrorHandler,
|
||||
) *confluence.PageInfo {
|
||||
markdown, err := os.ReadFile(file)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
fatalErrorHandler.Handle(err, "unable to read file %q", file)
|
||||
return nil
|
||||
// log.Fatal(err)
|
||||
}
|
||||
|
||||
markdown = bytes.ReplaceAll(markdown, []byte("\r\n"), []byte("\n"))
|
||||
@ -316,7 +330,9 @@ func processFile(
|
||||
|
||||
meta, markdown, err := metadata.ExtractMeta(markdown, cCtx.String("space"), cCtx.Bool("title-from-h1"), parents, cCtx.Bool("title-append-generated-hash"))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
fatalErrorHandler.Handle(err, "unable to extract metadata from file %q", file)
|
||||
return nil
|
||||
// log.Fatal(err)
|
||||
}
|
||||
|
||||
if pageID != "" && meta != nil {
|
||||
@ -329,29 +345,37 @@ func processFile(
|
||||
}
|
||||
|
||||
if pageID == "" && meta == nil {
|
||||
log.Fatal(
|
||||
`specified file doesn't contain metadata ` +
|
||||
`and URL is not specified via command line ` +
|
||||
`or doesn't contain pageId GET-parameter`,
|
||||
)
|
||||
fatalErrorHandler.Handle(nil, "specified file doesn't contain metadata and URL is not specified via command line or doesn't contain pageId GET-parameter")
|
||||
return nil
|
||||
// log.Fatal(
|
||||
// `specified file doesn't contain metadata ` +
|
||||
// `and URL is not specified via command line ` +
|
||||
// `or doesn't contain pageId GET-parameter`,
|
||||
// )
|
||||
}
|
||||
|
||||
if meta.Space == "" {
|
||||
log.Fatal(
|
||||
"space is not set ('Space' header is not set and '--space' option is not set)",
|
||||
)
|
||||
fatalErrorHandler.Handle(nil, "space is not set ('Space' header is not set and '--space' option is not set)")
|
||||
return nil
|
||||
// log.Fatal(
|
||||
// "space is not set ('Space' header is not set and '--space' option is not set)",
|
||||
// )
|
||||
}
|
||||
|
||||
if meta.Title == "" {
|
||||
log.Fatal(
|
||||
`page title is not set ('Title' header is not set ` +
|
||||
`and '--title-from-h1' option and 'h1_title' config is not set or there is no H1 in the file)`,
|
||||
)
|
||||
fatalErrorHandler.Handle(nil, "page title is not set ('Title' header is not set and '--title-from-h1' option and 'h1_title' config is not set or there is no H1 in the file)")
|
||||
return nil
|
||||
// log.Fatal(
|
||||
// `page title is not set ('Title' header is not set ` +
|
||||
// `and '--title-from-h1' option and 'h1_title' config is not set or there is no H1 in the file)`,
|
||||
// )
|
||||
}
|
||||
|
||||
stdlib, err := stdlib.New(api)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
fatalErrorHandler.Handle(err, "unable to retrieve standard library")
|
||||
return nil
|
||||
// log.Fatal(err)
|
||||
}
|
||||
|
||||
templates := stdlib.Templates
|
||||
@ -366,7 +390,9 @@ func processFile(
|
||||
templates,
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
fatalErrorHandler.Handle(err, "unable to process includes")
|
||||
return nil
|
||||
// log.Fatal(err)
|
||||
}
|
||||
|
||||
if !recurse {
|
||||
@ -381,7 +407,9 @@ func processFile(
|
||||
templates,
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
fatalErrorHandler.Handle(err, "unable to extract macros")
|
||||
return nil
|
||||
// log.Fatal(err)
|
||||
}
|
||||
|
||||
macros = append(macros, stdlib.Macros...)
|
||||
@ -389,13 +417,17 @@ func processFile(
|
||||
for _, macro := range macros {
|
||||
markdown, err = macro.Apply(markdown)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
fatalErrorHandler.Handle(err, "unable to apply macro")
|
||||
return nil
|
||||
// log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
links, err := page.ResolveRelativeLinks(api, meta, markdown, filepath.Dir(file), cCtx.String("space"), cCtx.Bool("title-from-h1"), parents, cCtx.Bool("title-append-generated-hash"))
|
||||
if err != nil {
|
||||
log.Fatalf(err, "unable to resolve relative links")
|
||||
fatalErrorHandler.Handle(err, "unable to resolve relative links")
|
||||
return nil
|
||||
// log.Fatalf(err, "unable to resolve relative links")
|
||||
}
|
||||
|
||||
markdown = page.SubstituteLinks(markdown, links)
|
||||
@ -403,7 +435,9 @@ func processFile(
|
||||
if cCtx.Bool("dry-run") {
|
||||
_, _, err := page.ResolvePage(cCtx.Bool("dry-run"), api, meta)
|
||||
if err != nil {
|
||||
log.Fatalf(err, "unable to resolve page location")
|
||||
fatalErrorHandler.Handle(err, "unable to resolve page location")
|
||||
return nil
|
||||
// log.Fatalf(err, "unable to resolve page location")
|
||||
}
|
||||
}
|
||||
|
||||
@ -413,7 +447,6 @@ func processFile(
|
||||
"the leading H1 heading will be excluded from the Confluence output",
|
||||
)
|
||||
}
|
||||
|
||||
html, _ := mark.CompileMarkdown(markdown, stdlib, file, cCtx.String("mermaid-provider"), cCtx.Float64("mermaid-scale"), cCtx.Bool("drop-h1"), cCtx.Bool("strip-linebreaks"))
|
||||
fmt.Println(html)
|
||||
return nil
|
||||
@ -424,11 +457,13 @@ func processFile(
|
||||
if meta != nil {
|
||||
parent, page, err := page.ResolvePage(cCtx.Bool("dry-run"), api, meta)
|
||||
if err != nil {
|
||||
log.Fatalf(
|
||||
karma.Describe("title", meta.Title).Reason(err),
|
||||
"unable to resolve %s",
|
||||
meta.Type,
|
||||
)
|
||||
// log.Fatalf(
|
||||
// karma.Describe("title", meta.Title).Reason(err),
|
||||
// "unable to resolve %s",
|
||||
// meta.Type,
|
||||
// )
|
||||
fatalErrorHandler.Handle(karma.Describe("title", meta.Title).Reason(err), "unable to resolve %s", meta.Type)
|
||||
return nil
|
||||
}
|
||||
|
||||
if page == nil {
|
||||
@ -440,12 +475,14 @@ func processFile(
|
||||
``,
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatalf(
|
||||
err,
|
||||
"can't create %s %q",
|
||||
meta.Type,
|
||||
meta.Title,
|
||||
)
|
||||
fatalErrorHandler.Handle(err, "can't create %s %q", meta.Type, meta.Title)
|
||||
return nil
|
||||
// log.Fatalf(
|
||||
// err,
|
||||
// "can't create %s %q",
|
||||
// meta.Type,
|
||||
// meta.Title,
|
||||
// )
|
||||
}
|
||||
// (issues/139): A delay between the create and update call
|
||||
// helps mitigate a 409 conflict that can occur when attempting
|
||||
@ -456,12 +493,16 @@ func processFile(
|
||||
target = page
|
||||
} else {
|
||||
if pageID == "" {
|
||||
log.Fatalf(nil, "URL should provide 'pageId' GET-parameter")
|
||||
fatalErrorHandler.Handle(nil, "URL should provide 'pageId' GET-parameter")
|
||||
return nil
|
||||
// log.Fatalf(nil, "URL should provide 'pageId' GET-parameter")
|
||||
}
|
||||
|
||||
page, err := api.GetPageByID(pageID)
|
||||
if err != nil {
|
||||
log.Fatalf(err, "unable to retrieve page by id")
|
||||
fatalErrorHandler.Handle(err, "unable to retrieve page by id")
|
||||
return nil
|
||||
// log.Fatalf(err, "unable to retrieve page by id")
|
||||
}
|
||||
|
||||
target = page
|
||||
@ -470,7 +511,9 @@ func processFile(
|
||||
// Resolve attachments created from <!-- Attachment: --> directive
|
||||
localAttachments, err := attachment.ResolveLocalAttachments(vfs.LocalOS, filepath.Dir(file), meta.Attachments)
|
||||
if err != nil {
|
||||
log.Fatalf(err, "unable to locate attachments")
|
||||
fatalErrorHandler.Handle(err, "unable to locate attachments")
|
||||
return nil
|
||||
// log.Fatalf(err, "unable to locate attachments")
|
||||
}
|
||||
|
||||
attaches, err := attachment.ResolveAttachments(
|
||||
@ -479,7 +522,9 @@ func processFile(
|
||||
localAttachments,
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatalf(err, "unable to create/update attachments")
|
||||
fatalErrorHandler.Handle(err, "unable to create/update attachments")
|
||||
return nil
|
||||
// log.Fatalf(err, "unable to create/update attachments")
|
||||
}
|
||||
|
||||
markdown = attachment.CompileAttachmentLinks(markdown, attaches)
|
||||
@ -499,7 +544,9 @@ func processFile(
|
||||
inlineAttachments,
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatalf(err, "unable to create/update attachments")
|
||||
fatalErrorHandler.Handle(err, "unable to create/update attachments")
|
||||
return nil
|
||||
// log.Fatalf(err, "unable to create/update attachments")
|
||||
}
|
||||
|
||||
{
|
||||
@ -519,7 +566,9 @@ func processFile(
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
fatalErrorHandler.Handle(err, "unable to execute layout template")
|
||||
return nil
|
||||
// log.Fatal(err)
|
||||
}
|
||||
|
||||
html = buffer.String()
|
||||
@ -567,11 +616,15 @@ func processFile(
|
||||
if shouldUpdatePage {
|
||||
err = api.UpdatePage(target, html, cCtx.Bool("minor-edit"), finalVersionMessage, meta.Labels, meta.ContentAppearance, meta.Emoji)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
fatalErrorHandler.Handle(err, "unable to update page")
|
||||
return nil
|
||||
// log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
updateLabels(api, target, meta)
|
||||
if !updateLabels(api, target, meta, fatalErrorHandler) { // on error updating labels, return nil
|
||||
return nil
|
||||
}
|
||||
|
||||
if cCtx.Bool("edit-lock") {
|
||||
log.Infof(
|
||||
@ -583,14 +636,16 @@ func processFile(
|
||||
|
||||
err := api.RestrictPageUpdates(target, username)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
fatalErrorHandler.Handle(err, "unable to restrict page updates")
|
||||
return nil
|
||||
// log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
return target
|
||||
}
|
||||
|
||||
func updateLabels(api *confluence.API, target *confluence.PageInfo, meta *metadata.Meta) {
|
||||
func updateLabels(api *confluence.API, target *confluence.PageInfo, meta *metadata.Meta, fatalErrorHandler *FatalErrorHandler) bool {
|
||||
labelInfo, err := api.GetPageLabels(target, "global")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
@ -613,16 +668,21 @@ func updateLabels(api *confluence.API, target *confluence.PageInfo, meta *metada
|
||||
if len(addLabels) > 0 {
|
||||
_, err = api.AddPageLabels(target, addLabels)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
fatalErrorHandler.Handle(err, "error adding labels")
|
||||
return false
|
||||
// log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
for _, label := range delLabels {
|
||||
_, err = api.DeletePageLabel(target, label)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
fatalErrorHandler.Handle(err, "error deleting labels")
|
||||
return false
|
||||
// log.Fatal(err)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Page has label but label not in Metadata
|
||||
|
15
testdata/batch-tests/broken-test.md
vendored
Normal file
15
testdata/batch-tests/broken-test.md
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
# a
|
||||
|
||||
## b
|
||||
|
||||
### c
|
||||
|
||||
#### d
|
||||
|
||||
##### e
|
||||
|
||||
# f
|
||||
|
||||
## g
|
||||
|
||||
# This/is some_Heading.yml
|
10
testdata/batch-tests/irfan-test.md
vendored
Normal file
10
testdata/batch-tests/irfan-test.md
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
<!-- Space: MySpace -->
|
||||
<!-- Parent: Parent -->
|
||||
<!-- Title: whatnot -->
|
||||
|
||||
## Foo
|
||||
|
||||
> **TL;DR:** Thingy!
|
||||
> More stuff
|
||||
|
||||
Foo
|
15
testdata/batch-tests/mark-test.md
vendored
Normal file
15
testdata/batch-tests/mark-test.md
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
# a
|
||||
|
||||
## b
|
||||
|
||||
### c
|
||||
|
||||
#### d
|
||||
|
||||
##### e
|
||||
|
||||
# f
|
||||
|
||||
## g
|
||||
|
||||
# This/is some_Heading.yml
|
19
testdata/batch-tests/rich-test.md
vendored
Normal file
19
testdata/batch-tests/rich-test.md
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
<!-- Space: TEST2 -->
|
||||
<!-- Title: H2 -->
|
||||
<!-- Title: whatnot -->
|
||||
|
||||
# a
|
||||
|
||||
## b
|
||||
|
||||
### c
|
||||
|
||||
#### d
|
||||
|
||||
##### e
|
||||
|
||||
# f
|
||||
|
||||
## g
|
||||
|
||||
# This/is some_Heading.yml
|
Loading…
x
Reference in New Issue
Block a user