@@ -5,8 +5,8 @@ This file provides context for Claude Code when working in this project.
55
66## What this project does
77
8- ` py_conf_sync.py ` is a single-file CLI tool that syncs Confluence Data Center pages
9- with Markdown files in a git repository. It pulls pages down from Confluence (converting
8+ ` py_conf_sync.py ` is a single-file CLI tool that syncs Confluence pages ( Data Center
9+ or Cloud) with Markdown files in a git repository. It pulls pages down from Confluence (converting
1010storage format to Markdown), allows local editing, then pushes changes back (converting
1111Markdown back to Confluence storage format).
1212
@@ -48,10 +48,20 @@ Dependabot regenerates them automatically on its weekly dep-bump PRs.
4848
4949Everything lives in ` py_conf_sync.py ` . The main sections are:
5050
51- - ** ` ConfluenceClient ` ** — thin ` requests ` wrapper around the Confluence REST API.
52- Three methods: ` get_page() ` , ` update_page() ` , and ` upload_attachment() ` . Auth is
51+ - ** ` ConfluenceClient ` ** — thin ` requests ` wrapper around the Confluence DC REST API
52+ (v1). Three methods: ` get_page() ` , ` update_page() ` , and ` upload_attachment() ` . Auth is
5353 either a PAT Bearer token or basic auth, loaded from ` .env ` via ` python-dotenv ` .
5454
55+ - ** ` CloudConfluenceClient ` ** — subclass for Atlassian Cloud. Overrides ` get_page() `
56+ and ` update_page() ` to use the v2 API (` /api/v2/pages/{id} ` ); inherits
57+ ` upload_attachment() ` because v2 has no attachment upload endpoint (Cloud still uses
58+ the v1 path). Auth is basic auth with ` CONFLUENCE_EMAIL ` + ` CONFLUENCE_TOKEN `
59+ (Atlassian API token) — no ` --unsafe-auth ` needed. Selected by ` _is_cloud() ` :
60+ explicit ` instance_type ` config field wins, else hostname ends with ` .atlassian.net ` .
61+ ` _get_client() ` appends ` /wiki ` to the base URL if missing and writes it back to
62+ ` config["confluence_url"] ` (conversion code builds/matches attachment download URLs
63+ from that value).
64+
5565- ** ` storage_to_markdown() ` ** — converts Confluence storage format (XHTML + ` ac:* ` /` ri:* `
5666 macros) to Markdown. Named macro patterns are applied in order before markdownify runs.
5767 Many macros have full round-trip support; unknown macros are stripped by ` _MACRO_RE ` .
@@ -113,6 +123,12 @@ pages:
113123` page_id` is always treated as a string. `file_path` is relative to wherever the script
114124is run from (intended to be the repo root).
115125
126+ For Atlassian Cloud, set `confluence_url : https://yoursite.atlassian.net/wiki` (the
127+ ` /wiki` suffix is appended automatically if omitted) and `jira_url` to the bare site URL.
128+ Cloud is auto-detected from `*.atlassian.net` hostnames; the optional
129+ `instance_type : cloud | datacenter` field overrides detection (needed for Cloud behind
130+ a custom domain).
131+
116132**`img_dir`**: On pull, if an attachment image's filename exists as `{img_dir}/{filename}`
117133locally, the Markdown image reference uses the local relative path instead of the Confluence
118134download URL. On push, local image references are uploaded as Confluence attachments before
@@ -121,19 +137,27 @@ converting to storage format.
121137# # Credentials (`.csync.env`)
122138
123139```
140+ # Data Center
124141CONFLUENCE_TOKEN=<personal access token >
125142# or (requires --unsafe-auth flag at runtime)
126143CONFLUENCE_USERNAME=user@example.com
127144CONFLUENCE_PASSWORD=<password >
145+
146+ # Cloud — API token from id.atlassian.com + account email; no --unsafe-auth needed
147+ CONFLUENCE_TOKEN=<api token >
148+ CONFLUENCE_EMAIL=user@example.com
128149```
129150
130151Credentials are **never created by `init`** — users set them up manually by copying
131- `.csync.env.example` to `~/.csync.env` and filling in their PAT .
152+ `.csync.env.example` to `~/.csync.env` and filling in their token .
132153
133154The credential file is located by searching in order: `~/.csync.env` (preferred), then
134155the current working directory, then the script's own directory.
135156
136- Basic auth is disabled by default and requires `--unsafe-auth` to activate.
157+ Password basic auth (DC only) is disabled by default and requires `--unsafe-auth` to
158+ activate. Cloud's email + API token basic auth is not gated — it is Atlassian's
159+ recommended method for scripts. Cloud with a missing `CONFLUENCE_EMAIL` or
160+ `CONFLUENCE_TOKEN` is a hard error.
137161
138162## Known limitations / gotchas
139163
@@ -180,10 +204,25 @@ Basic auth is disabled by default and requires `--unsafe-auth` to activate.
180204
181205## Confluence API reference
182206
207+ Data Center (v1, base `{confluence_url}/rest/api`):
208+
183209- Get page: `GET /rest/api/content/{id}?expand=body.storage,version,title`
184210- Update page: `PUT /rest/api/content/{id}` with JSON body (see `update_page()`)
185211- Upload attachment: `POST /rest/api/content/{id}/child/attachment`
186212- Space content: `GET /rest/api/space/{KEY}/content`
187213
188- All endpoints are Confluence DC REST API v1 (`/rest/api/`). This tool does not use the
189- v2 API (`/api/v2/`) introduced in newer DC versions.
214+ Cloud (base `{confluence_url}` = `https://site.atlassian.net/wiki`):
215+
216+ - Get page: `GET /api/v2/pages/{id}?body-format=storage` — response carries `title`,
217+ `version.number`, and `body.storage.value` in the same shapes the v1 consumer code
218+ reads, so `cmd_pull`/`cmd_push` are client-agnostic.
219+ - Update page: `PUT /api/v2/pages/{id}` with `{id, status: "current", title, body, version}`
220+ - Upload attachment: `POST /rest/api/content/{id}/child/attachment` — still v1; the v2
221+ API has no attachment upload endpoint.
222+ - Download attachment content: `GET /rest/api/content/{id}/child/attachment/{attId}/download`
223+ (redirects to a signed media URL). The legacy `/download/attachments/{id}/{file}` path
224+ returns 401 for API-token auth on Cloud — it only accepts browser-session cookies —
225+ which is why `download_attachment_text()` is overridden in the Cloud client.
226+
227+ Atlassian is deprecating Cloud v1 content endpoints on a rolling timeline, which is why
228+ Cloud page reads/writes use v2 while DC stays on v1.
0 commit comments