confpub-cli 1.7.3__tar.gz → 1.7.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/PKG-INFO +1 -1
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/confpub/__init__.py +1 -1
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/confpub/front_matter.py +18 -0
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/confpub/manifest.py +3 -0
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/confpub/puller.py +35 -51
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/tests/test_puller.py +14 -10
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/.github/copilot-instructions.md +0 -0
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/.github/workflows/publish.yml +0 -0
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/.gitignore +0 -0
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/CLAUDE.md +0 -0
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/LICENSE +0 -0
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/PRD.md +0 -0
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/README.md +0 -0
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/confpub/applier.py +0 -0
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/confpub/assets.py +0 -0
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/confpub/cli.py +0 -0
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/confpub/config.py +0 -0
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/confpub/confluence.py +0 -0
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/confpub/converter.py +0 -0
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/confpub/envelope.py +0 -0
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/confpub/errors.py +0 -0
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/confpub/guide.py +0 -0
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/confpub/lockfile.py +0 -0
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/confpub/macro_plugin.py +0 -0
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/confpub/output.py +0 -0
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/confpub/planner.py +0 -0
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/confpub/publish.py +0 -0
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/confpub/py.typed +0 -0
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/confpub/reverse_converter.py +0 -0
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/confpub/validator.py +0 -0
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/confpub/verifier.py +0 -0
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/confpub.lock +0 -0
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/pyproject.toml +0 -0
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/tests/__init__.py +0 -0
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/tests/conftest.py +0 -0
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/tests/test_applier.py +0 -0
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/tests/test_assets.py +0 -0
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/tests/test_config.py +0 -0
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/tests/test_confluence.py +0 -0
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/tests/test_converter.py +0 -0
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/tests/test_envelope.py +0 -0
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/tests/test_errors.py +0 -0
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/tests/test_front_matter.py +0 -0
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/tests/test_guide.py +0 -0
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/tests/test_integration.py +0 -0
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/tests/test_lockfile.py +0 -0
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/tests/test_macro_plugin.py +0 -0
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/tests/test_manifest.py +0 -0
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/tests/test_output.py +0 -0
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/tests/test_planner.py +0 -0
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/tests/test_publish.py +0 -0
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/tests/test_reverse_converter.py +0 -0
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/tests/test_validator.py +0 -0
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/tests/test_verifier.py +0 -0
- {confpub_cli-1.7.3 → confpub_cli-1.7.5}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: confpub-cli
|
|
3
|
-
Version: 1.7.
|
|
3
|
+
Version: 1.7.5
|
|
4
4
|
Summary: Agent-first CLI to publish Markdown to Confluence
|
|
5
5
|
Project-URL: Homepage, https://github.com/ThomasRohde/confpub-cli
|
|
6
6
|
Project-URL: Repository, https://github.com/ThomasRohde/confpub-cli.git
|
|
@@ -10,6 +10,8 @@ import dataclasses
|
|
|
10
10
|
from dataclasses import dataclass, field
|
|
11
11
|
from typing import Any
|
|
12
12
|
|
|
13
|
+
import yaml
|
|
14
|
+
|
|
13
15
|
from confpub.converter import extract_front_matter
|
|
14
16
|
from confpub.errors import ERR_VALIDATION_MARKDOWN, ConfpubError
|
|
15
17
|
|
|
@@ -24,6 +26,22 @@ class FrontMatterData:
|
|
|
24
26
|
labels: list[str] = field(default_factory=list)
|
|
25
27
|
page_id: str | None = None
|
|
26
28
|
|
|
29
|
+
def to_yaml_block(self) -> str:
|
|
30
|
+
"""Serialize to a YAML front matter block (``--- ... ---``)."""
|
|
31
|
+
data: dict[str, Any] = {}
|
|
32
|
+
if self.title is not None:
|
|
33
|
+
data["title"] = self.title
|
|
34
|
+
if self.page_id is not None:
|
|
35
|
+
data["page_id"] = self.page_id
|
|
36
|
+
if self.space is not None:
|
|
37
|
+
data["space"] = self.space
|
|
38
|
+
if self.parent is not None:
|
|
39
|
+
data["parent"] = self.parent
|
|
40
|
+
if self.labels:
|
|
41
|
+
data["labels"] = self.labels
|
|
42
|
+
yaml_str = yaml.dump(data, default_flow_style=False, allow_unicode=True, sort_keys=False)
|
|
43
|
+
return f"---\n{yaml_str}---\n\n"
|
|
44
|
+
|
|
27
45
|
|
|
28
46
|
def _validate_string(raw: dict[str, Any], key: str) -> str | None:
|
|
29
47
|
"""Extract and validate a string field, or None if absent."""
|
|
@@ -191,6 +191,9 @@ def generate_manifest_yaml(
|
|
|
191
191
|
result = []
|
|
192
192
|
for p in pages:
|
|
193
193
|
entry: dict[str, Any] = {"title": p["title"], "file": p["file"]}
|
|
194
|
+
assets = p.get("assets", [])
|
|
195
|
+
if assets:
|
|
196
|
+
entry["assets"] = assets
|
|
194
197
|
labels = p.get("labels", [])
|
|
195
198
|
if labels:
|
|
196
199
|
entry["labels"] = labels
|
|
@@ -9,9 +9,7 @@ from __future__ import annotations
|
|
|
9
9
|
import os
|
|
10
10
|
import re
|
|
11
11
|
from pathlib import Path
|
|
12
|
-
from typing import Any
|
|
13
|
-
|
|
14
|
-
import yaml
|
|
12
|
+
from typing import Any, Literal
|
|
15
13
|
|
|
16
14
|
from confpub.confluence import ConfluenceClient, build_client
|
|
17
15
|
from confpub.output import emit_progress
|
|
@@ -21,10 +19,13 @@ from confpub.errors import (
|
|
|
21
19
|
ERR_VALIDATION_REQUIRED,
|
|
22
20
|
ConfpubError,
|
|
23
21
|
)
|
|
22
|
+
from confpub.front_matter import FrontMatterData
|
|
24
23
|
from confpub.lockfile import Lockfile, load_lockfile, save_lockfile, update_lockfile
|
|
25
24
|
from confpub.manifest import generate_manifest_yaml
|
|
26
25
|
from confpub.reverse_converter import convert_storage_to_markdown
|
|
27
26
|
|
|
27
|
+
Layout = Literal["flat", "nested"]
|
|
28
|
+
|
|
28
29
|
|
|
29
30
|
def _slugify(title: str) -> str:
|
|
30
31
|
"""Convert a page title to a filename-safe slug.
|
|
@@ -77,15 +78,22 @@ def _collect_tree(
|
|
|
77
78
|
def _compute_file_paths(
|
|
78
79
|
pages: list[dict[str, Any]],
|
|
79
80
|
output_dir: str,
|
|
80
|
-
layout:
|
|
81
|
+
layout: Layout,
|
|
81
82
|
root_page_id: str,
|
|
82
|
-
) -> dict[str, str]:
|
|
83
|
-
"""Compute output file paths for each page.
|
|
83
|
+
) -> tuple[dict[str, str], dict[str, str], dict[str, str | None]]:
|
|
84
|
+
"""Compute output file paths and lookup maps for each page.
|
|
85
|
+
|
|
86
|
+
Returns (file_paths, id_to_title, id_to_parent) where:
|
|
87
|
+
- file_paths: {page_id: file_path}
|
|
88
|
+
- id_to_title: {page_id: title}
|
|
89
|
+
- id_to_parent: {page_id: parent_page_id or None}
|
|
90
|
+
"""
|
|
84
91
|
paths: dict[str, str] = {}
|
|
85
92
|
root_slug: str | None = None
|
|
86
93
|
|
|
87
|
-
# Build
|
|
94
|
+
# Build lookup maps
|
|
88
95
|
id_to_slug: dict[str, str] = {}
|
|
96
|
+
id_to_title: dict[str, str] = {}
|
|
89
97
|
id_to_parent: dict[str, str | None] = {}
|
|
90
98
|
|
|
91
99
|
for entry in pages:
|
|
@@ -93,6 +101,7 @@ def _compute_file_paths(
|
|
|
93
101
|
pid = str(page["id"])
|
|
94
102
|
slug = _slugify(page.get("title", pid))
|
|
95
103
|
id_to_slug[pid] = slug
|
|
104
|
+
id_to_title[pid] = page.get("title", "")
|
|
96
105
|
id_to_parent[pid] = str(entry["parent_id"]) if entry["parent_id"] else None
|
|
97
106
|
if pid == root_page_id:
|
|
98
107
|
root_slug = slug
|
|
@@ -119,7 +128,7 @@ def _compute_file_paths(
|
|
|
119
128
|
# Flat layout
|
|
120
129
|
paths[pid] = os.path.join(output_dir, f"{slug}.md")
|
|
121
130
|
|
|
122
|
-
return paths
|
|
131
|
+
return paths, id_to_title, id_to_parent
|
|
123
132
|
|
|
124
133
|
|
|
125
134
|
def _check_conflicts(file_paths: dict[str, str], force: bool) -> None:
|
|
@@ -147,7 +156,7 @@ def _download_page_attachments(
|
|
|
147
156
|
page_id: str,
|
|
148
157
|
slug: str,
|
|
149
158
|
output_dir: str,
|
|
150
|
-
layout:
|
|
159
|
+
layout: Layout,
|
|
151
160
|
warnings: list[str],
|
|
152
161
|
file_path: str | None = None,
|
|
153
162
|
) -> dict[str, str]:
|
|
@@ -164,8 +173,6 @@ def _download_page_attachments(
|
|
|
164
173
|
if layout == "nested" and file_path:
|
|
165
174
|
# Place assets next to the markdown file (e.g. .../page-slug/assets/)
|
|
166
175
|
assets_dir = os.path.join(os.path.dirname(file_path), "assets")
|
|
167
|
-
elif layout == "nested":
|
|
168
|
-
assets_dir = os.path.join(output_dir, slug, "assets")
|
|
169
176
|
else:
|
|
170
177
|
assets_dir = os.path.join(output_dir, "assets", slug)
|
|
171
178
|
|
|
@@ -194,11 +201,13 @@ def _build_page_tree(
|
|
|
194
201
|
root_page_id: str,
|
|
195
202
|
output_dir: str = ".",
|
|
196
203
|
page_labels: dict[str, list[str]] | None = None,
|
|
204
|
+
page_assets: dict[str, list[str]] | None = None,
|
|
197
205
|
) -> list[dict[str, Any]]:
|
|
198
206
|
"""Build a hierarchical page tree for manifest generation."""
|
|
199
207
|
id_to_entry: dict[str, dict[str, Any]] = {}
|
|
200
208
|
children_map: dict[str | None, list[str]] = {}
|
|
201
209
|
labels_map = page_labels or {}
|
|
210
|
+
assets_map = page_assets or {}
|
|
202
211
|
|
|
203
212
|
for entry in pages:
|
|
204
213
|
page = entry["page"]
|
|
@@ -214,6 +223,8 @@ def _build_page_tree(
|
|
|
214
223
|
}
|
|
215
224
|
if labels_map.get(pid):
|
|
216
225
|
node["labels"] = labels_map[pid]
|
|
226
|
+
if assets_map.get(pid):
|
|
227
|
+
node["assets"] = assets_map[pid]
|
|
217
228
|
id_to_entry[pid] = node
|
|
218
229
|
children_map.setdefault(parent_id, []).append(pid)
|
|
219
230
|
|
|
@@ -230,27 +241,6 @@ def _build_page_tree(
|
|
|
230
241
|
return [root_entry]
|
|
231
242
|
|
|
232
243
|
|
|
233
|
-
def _build_front_matter(
|
|
234
|
-
title: str,
|
|
235
|
-
page_id: str,
|
|
236
|
-
space: str,
|
|
237
|
-
parent: str | None = None,
|
|
238
|
-
labels: list[str] | None = None,
|
|
239
|
-
) -> str:
|
|
240
|
-
"""Build a YAML front matter block for a pulled markdown file."""
|
|
241
|
-
data: dict[str, Any] = {
|
|
242
|
-
"title": title,
|
|
243
|
-
"page_id": page_id,
|
|
244
|
-
"space": space,
|
|
245
|
-
}
|
|
246
|
-
if parent:
|
|
247
|
-
data["parent"] = parent
|
|
248
|
-
if labels:
|
|
249
|
-
data["labels"] = labels
|
|
250
|
-
yaml_str = yaml.dump(data, default_flow_style=False, allow_unicode=True, sort_keys=False)
|
|
251
|
-
return f"---\n{yaml_str}---\n\n"
|
|
252
|
-
|
|
253
|
-
|
|
254
244
|
def pull_pages(
|
|
255
245
|
*,
|
|
256
246
|
space: str | None = None,
|
|
@@ -259,7 +249,7 @@ def pull_pages(
|
|
|
259
249
|
output_dir: str = ".",
|
|
260
250
|
recursive: bool = False,
|
|
261
251
|
force: bool = False,
|
|
262
|
-
layout:
|
|
252
|
+
layout: Layout = "flat",
|
|
263
253
|
include_attachments: bool = True,
|
|
264
254
|
) -> dict[str, Any]:
|
|
265
255
|
"""Pull pages from Confluence to local Markdown files.
|
|
@@ -292,29 +282,21 @@ def pull_pages(
|
|
|
292
282
|
# Collect all pages to pull
|
|
293
283
|
all_pages = _collect_tree(client, root_id, recursive)
|
|
294
284
|
|
|
295
|
-
# Compute output file paths
|
|
296
|
-
file_paths = _compute_file_paths(all_pages, output_dir, layout, root_id)
|
|
285
|
+
# Compute output file paths and lookup maps
|
|
286
|
+
file_paths, id_to_title, id_to_parent = _compute_file_paths(all_pages, output_dir, layout, root_id)
|
|
297
287
|
|
|
298
288
|
# Check for conflicts
|
|
299
289
|
_check_conflicts(file_paths, force)
|
|
300
290
|
|
|
301
|
-
#
|
|
302
|
-
|
|
303
|
-
id_to_parent: dict[str, str | None] = {}
|
|
304
|
-
for entry in all_pages:
|
|
305
|
-
page = entry["page"]
|
|
306
|
-
pid = str(page["id"])
|
|
307
|
-
id_to_title[pid] = page.get("title", "")
|
|
308
|
-
id_to_parent[pid] = str(entry["parent_id"]) if entry["parent_id"] else None
|
|
309
|
-
|
|
310
|
-
# Get the root page's parent in Confluence (for front matter + manifest)
|
|
311
|
-
ancestors = client.get_page_ancestors(root_id)
|
|
291
|
+
# Get the root page's parent from already-fetched ancestors (no extra API call)
|
|
292
|
+
ancestors = root_page.get("ancestors", [])
|
|
312
293
|
root_parent_title = ancestors[-1].get("title", "") if ancestors else None
|
|
313
294
|
|
|
314
295
|
# Process each page
|
|
315
296
|
files_result: list[dict[str, Any]] = []
|
|
316
297
|
total_attachments = 0
|
|
317
298
|
pull_warnings: list[str] = []
|
|
299
|
+
pulled_assets: dict[str, list[str]] = {} # page_id -> list of relative asset paths
|
|
318
300
|
|
|
319
301
|
for entry in all_pages:
|
|
320
302
|
page = entry["page"]
|
|
@@ -335,6 +317,8 @@ def pull_pages(
|
|
|
335
317
|
)
|
|
336
318
|
attachments_downloaded = len(attachment_map)
|
|
337
319
|
total_attachments += attachments_downloaded
|
|
320
|
+
if attachment_map:
|
|
321
|
+
pulled_assets[pid] = list(attachment_map.values())
|
|
338
322
|
|
|
339
323
|
# Convert storage format to markdown
|
|
340
324
|
body_storage = page.get("body", {}).get("storage", {}).get("value", "")
|
|
@@ -353,14 +337,14 @@ def pull_pages(
|
|
|
353
337
|
parent_title = id_to_title.get(par_id) if par_id else None
|
|
354
338
|
|
|
355
339
|
# Build and prepend front matter
|
|
356
|
-
|
|
340
|
+
fm = FrontMatterData(
|
|
357
341
|
title=page_title,
|
|
358
342
|
page_id=pid,
|
|
359
343
|
space=root_space,
|
|
360
344
|
parent=parent_title,
|
|
361
|
-
labels=page_labels,
|
|
345
|
+
labels=page_labels or [],
|
|
362
346
|
)
|
|
363
|
-
markdown_content =
|
|
347
|
+
markdown_content = fm.to_yaml_block() + conv_result.markdown
|
|
364
348
|
|
|
365
349
|
# Write markdown file
|
|
366
350
|
os.makedirs(os.path.dirname(out_path) or ".", exist_ok=True)
|
|
@@ -377,11 +361,11 @@ def pull_pages(
|
|
|
377
361
|
|
|
378
362
|
# Always generate manifest
|
|
379
363
|
root_title = root_page.get("title", "")
|
|
380
|
-
manifest_parent =
|
|
364
|
+
manifest_parent = root_parent_title or root_title
|
|
381
365
|
pulled_labels: dict[str, list[str]] = {
|
|
382
366
|
f["page_id"]: f.get("labels", []) for f in files_result
|
|
383
367
|
}
|
|
384
|
-
page_tree = _build_page_tree(all_pages, file_paths, root_id, output_dir, page_labels=pulled_labels)
|
|
368
|
+
page_tree = _build_page_tree(all_pages, file_paths, root_id, output_dir, page_labels=pulled_labels, page_assets=pulled_assets)
|
|
385
369
|
manifest_yaml = generate_manifest_yaml(root_space, manifest_parent, page_tree)
|
|
386
370
|
manifest_path = os.path.join(output_dir, "confpub.yaml")
|
|
387
371
|
Path(manifest_path).write_text(manifest_yaml, encoding="utf-8")
|
|
@@ -10,7 +10,8 @@ import pytest
|
|
|
10
10
|
import yaml
|
|
11
11
|
|
|
12
12
|
from confpub.errors import ERR_CONFLICT_FILE_EXISTS, ERR_VALIDATION_REQUIRED, ConfpubError
|
|
13
|
-
from confpub.
|
|
13
|
+
from confpub.front_matter import FrontMatterData
|
|
14
|
+
from confpub.puller import _slugify, pull_pages
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
# ---------------------------------------------------------------------------
|
|
@@ -594,10 +595,10 @@ class TestManifestFlag:
|
|
|
594
595
|
|
|
595
596
|
class TestBuildFrontMatter:
|
|
596
597
|
def test_basic_fields(self):
|
|
597
|
-
|
|
598
|
-
assert
|
|
599
|
-
assert
|
|
600
|
-
parsed = yaml.safe_load(
|
|
598
|
+
block = FrontMatterData(title="My Page", page_id="123", space="SD").to_yaml_block()
|
|
599
|
+
assert block.startswith("---\n")
|
|
600
|
+
assert block.endswith("---\n\n")
|
|
601
|
+
parsed = yaml.safe_load(block.strip("- \n"))
|
|
601
602
|
assert parsed["title"] == "My Page"
|
|
602
603
|
assert parsed["page_id"] == "123"
|
|
603
604
|
assert parsed["space"] == "SD"
|
|
@@ -605,14 +606,17 @@ class TestBuildFrontMatter:
|
|
|
605
606
|
assert "labels" not in parsed
|
|
606
607
|
|
|
607
608
|
def test_with_parent_and_labels(self):
|
|
608
|
-
|
|
609
|
-
|
|
609
|
+
block = FrontMatterData(
|
|
610
|
+
title="Child", page_id="456", space="SD",
|
|
611
|
+
parent="Parent Page", labels=["a", "b"],
|
|
612
|
+
).to_yaml_block()
|
|
613
|
+
parsed = yaml.safe_load(block.strip("- \n"))
|
|
610
614
|
assert parsed["parent"] == "Parent Page"
|
|
611
615
|
assert parsed["labels"] == ["a", "b"]
|
|
612
616
|
|
|
613
617
|
def test_empty_labels_omitted(self):
|
|
614
|
-
|
|
615
|
-
parsed = yaml.safe_load(
|
|
618
|
+
block = FrontMatterData(title="Page", page_id="1", space="SD").to_yaml_block()
|
|
619
|
+
parsed = yaml.safe_load(block.strip("- \n"))
|
|
616
620
|
assert "labels" not in parsed
|
|
617
621
|
|
|
618
622
|
|
|
@@ -661,8 +665,8 @@ class TestFrontMatterInPulledFiles:
|
|
|
661
665
|
def test_root_page_has_parent_from_ancestors(self, tmp_path):
|
|
662
666
|
"""Root page gets parent from Confluence ancestors."""
|
|
663
667
|
page = _make_page("1", "Root")
|
|
668
|
+
page["ancestors"] = [{"title": "Space Home"}]
|
|
664
669
|
client = _mock_client({"1": page})
|
|
665
|
-
client.get_page_ancestors = lambda pid: [{"title": "Space Home"}]
|
|
666
670
|
|
|
667
671
|
with patch("confpub.puller.build_client", return_value=client):
|
|
668
672
|
pull_pages(page_id="1", output_dir=str(tmp_path))
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|