package main
import (
"fmt"
"io"
"os"
"github.com/spf13/cobra"
"go.bigb.es/confluence-md-utilities/api"
"go.bigb.es/confluence-md-utilities/converter"
"go.bigb.es/confluence-md-utilities/template"
)
var (
pushMessage string
pushRaw bool
pushTemplate bool
pushMarkerStart string
pushMarkerEnd string
)
var pushCmd = &cobra.Command{
Use: "push <confluence-url> [input.md]",
Short: "Push local Markdown to a Confluence page",
Long: `Convert a local Markdown file to Confluence storage format and update
the page at the given URL.
By default, the entire page body is replaced with the converted content.
With --template, the current page body is preserved as a template:
the content between marker comments is replaced with the new content,
keeping everything else (metadata table, changelog, etc.) intact.
With --raw, the input is treated as Confluence storage XML (no conversion).
Reads from stdin if no input file is specified.
Authentication via --token flag or CONFLUENCE_TOKEN environment variable.`,
Args: cobra.RangeArgs(1, 2),
RunE: func(cmd *cobra.Command, args []string) error {
token := resolveToken()
if token == "" {
return fmt.Errorf("Confluence token required: use --token flag or set CONFLUENCE_TOKEN env var")
}
ref, err := api.ParsePageURL(args[0])
if err != nil {
return err
}
// Read input
var input []byte
if len(args) > 1 {
input, err = os.ReadFile(args[1])
} else {
input, err = io.ReadAll(os.Stdin)
}
if err != nil {
return fmt.Errorf("reading input: %w", err)
}
// Convert to Confluence XML if not raw
var newXML string
if pushRaw {
newXML = string(input)
} else {
newXML, err = converter.MarkdownToConfluence(input)
if err != nil {
return fmt.Errorf("converting markdown: %w", err)
}
}
client := api.NewClient(ref.BaseURL, token)
// Fetch current page for version info (and template if needed)
page, err := client.GetPage(ref)
if err != nil {
return err
}
fmt.Fprintf(os.Stderr, "Updating page: %s (id=%s, version=%d -> %d)\n",
page.Title, page.ID, page.Version.Number, page.Version.Number+1)
// If template mode, embed into existing page body
body := newXML
if pushTemplate {
body, err = template.Embed(page.Body.Storage.Value, newXML, pushMarkerStart, pushMarkerEnd)
if err != nil {
return fmt.Errorf("embedding into template: %w", err)
}
}
if err := client.UpdateContent(page.ID, page, body, pushMessage); err != nil {
return err
}
fmt.Fprintf(os.Stderr, "Page updated successfully\n")
return nil
},
}
func init() {
pushCmd.Flags().StringVarP(&pushMessage, "message", "m", "", "Version message for the update")
pushCmd.Flags().BoolVar(&pushRaw, "raw", false, "Input is raw Confluence storage XML (skip conversion)")
pushCmd.Flags().BoolVar(&pushTemplate, "template", false, "Embed content into existing page body between markers")
pushCmd.Flags().StringVar(&pushMarkerStart, "marker-start", template.DefaultMarkerStart, "Start marker comment (with --template)")
pushCmd.Flags().StringVar(&pushMarkerEnd, "marker-end", template.DefaultMarkerEnd, "End marker comment (with --template)")
rootCmd.AddCommand(pushCmd)
}