Skip to content

Latest commit

 

History

History
67 lines (42 loc) · 7.39 KB

File metadata and controls

67 lines (42 loc) · 7.39 KB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

markfluence is a Go CLI (cobra + net/http + goldmark + lipgloss) for publishing markdown to Confluence. It was originally a Python tool; the Go rewrite is now the sole implementation.

Commands

Tasks run through a Makefile (run make with no target for the annotated rule list).

  • make build — build ./bin/markfluence (version stamped via ldflags)
  • make testgo test ./... (also runs the go vet subset)
  • make lint — golangci-lint (installs the pinned v2.6.0 into ./bin; enables the default set plus lll at 120)
  • make vet / make fmt
  • make regen-regressions — regenerate the converter's golden outputs (go test ./internal/convert -run TestRegression -update)
  • Run a single package/test: go test ./internal/frontmatter -run TestReadValues

Run make test && make lint && make vet before considering work done.

Configuration

The CLI needs a base URL, a username, and an API token. Each resolves with the precedence flag > environment variable > .env file:

Setting Flag Env / .env
base URL --url CONFLUENCE_URL
username --username CONFLUENCE_USERNAME
API token (none) CONFLUENCE_TOKEN

markfluence reads a .env from the working directory itself (a minimal built-in parser — no shell expansion), or an explicit path via the persistent --env-file flag (a missing explicit path is an error; a missing default ./.env is not); .env.example is the template. The API token is deliberately never a command-line flag. internal/client.Resolve is the single place this is read and validated.

Commit conventions

Commits must follow Conventional Commits 1.0.0: type(optional-scope): description, e.g. feat(convert): rewrite anchor links, fix(client): handle 404 on missing page. Common types: feat, fix, docs, refactor, test, chore, build.

Do not add AI attribution to commits or pull requests — no Co-Authored-By: Claude trailers, no "Generated with Claude Code" lines, no similar attribution.

Architecture

Module github.com/mozilla/markfluence (go 1.25). main.go is a shim to cmd.Execute().

Layout

  • cmd/root.go — the cobra root: --url/--username/--debug/--no-color persistent flags, version from internal/buildinfo, and registration of the four subcommands. Execute() prints cobra-generated errors (bad args/flags) but not ui.ErrSilent, which marks a failure a command already reported.
  • cmd/{update,create,fix,info}/ — one package per command (each exports Cmd), orchestrating the internal packages and internal/ui output. create is two-phase and transactional (validate all, then create parents-first in topological order); fix is read-only on the server.
  • internal/clientConfluenceClient over net/http with basic auth. Pages are Confluence v2; attachment writes and the user lookup are v1 (/wiki/rest/api/...). Typed HTTPError, per-attempt context timeouts, centralized retry/backoff in send (429 for any method honoring Retry-After, plus 502/503/504 and network errors for idempotent methods only; exponential backoff capped), SetContentProperty retry-once on top (recovers a lost create-POST response), SyncAttachments (SHA-256-in-comment skip/update), _links.next pagination. config.go holds Resolve and the .env reader.
  • internal/convert — the converter (the crux). MdToConfluence(md *frontmatter.MarkdownFile, baseURL, spaceKey, version string) (*ConfluencePage, error). It parses with goldmark (GFM) and renders through a custom storageRenderer registered at priority 100 (below the default HTML=1000 and table=500 renderers) that emits Confluence storage format. shield.go renames raw ac:/ri: tags to colon-free sentinels around the goldmark step so pasted storage passes through; callouts.go is an AST transformer + blockquote renderer for GitHub alerts; images.go, links.go (sibling-file scans, GitHub/Confluence slugs, doc-link + anchor rewriting), and renderer.go (code macros, text soft-break→space, images, links) do the rest. The <!-- confluence-toc --> and <!-- markfluence-version --> token substitutions happen inside MdToConfluence.
  • internal/frontmatter — flat YAML frontmatter parse/quote/UpdateField, and the MarkdownFile type (Parse/ParseFile, exported Filename/Content/Frontmatter/Body, and Title/PageID/Space/Parent accessors that normalize missing/blank/"null").
  • internal/pagewidth — the page_width Width enum (narrow/wide/max, default max), Declared, the vocab↔content-property maps, WidthFromProperties, and Apply/Read against the client.
  • internal/buildinfoVersion (set via ldflags), CommitDate (from the vcs.time build setting), and Stamp.
  • internal/ui — lipgloss output helpers (colored Header/Success/Warn/Error, errors to stderr, NO_COLOR/piped detection) and the ErrSilent sentinel.

The converter

The design target is semantic (not byte-for-byte) equivalence to valid Confluence storage. Order matters in the pipeline: shield before parse / unshield after render; anchor rewriting must precede doc-link rewriting (link/anchor rewriting reads sibling .md files for their page_id/title and heading anchors). Go's regexp is RE2, so slug/link code avoids lazy quantifiers and uses \p{L} etc. for Unicode.

Regression suite

internal/convert/testdata/regression/<case>/ is a golden-file suite over MdToConfluence, one directory per case: a primary main.md (frontmatter allowed), optional sibling *.md and assets/ image stubs, an optional test.input (JSON: filename/base_url/space_key defaulting to main.md/https://wiki.example.net/ENG, plus an enforced files manifest), and a generated test.output golden. regression_test.go runs each case, redacts attachment paths to <ROOT>, and exact-matches the golden; regenerate with make regen-regressions.

Build / distribution

Makefile (build/install/lint/vet/fmt/test/regen-regressions), .golangci.yml (default linters + lll at 120), and .goreleaser.yaml (darwin+linux × arm64+amd64, CGO_ENABLED=0, -s -w, version into internal/buildinfo.Version, tar.gz archives, a Homebrew cask). _plans/ holds the port's design/step history.

Frontmatter-driven publishing

Each markdown file is one page. Frontmatter carries title, page_id, space (a key), parent (null / a .md path / a page id), and page_width. update requires a page_id (from frontmatter or --page-id) and errors without one; --title/--page-id override the frontmatter (single FILE only), --page-width overrides too (batch allowed), and update never writes back to files. It asserts page_width only when set via flag or frontmatter (otherwise the live width is left alone), and skips a file whose mtime predates the page's last version unless --force. create takes --title/--page-width overrides (--title single-FILE only; --page-width batch-ok, default max) and, unless --no-persist is given, writes title/space/parent/page_id/page_width back after creating; fix reconciles all of these (plus page_width) from the live page. Commands process multiple files (except info, single-arg) and exit non-zero if any fail.