From 62579ec831bf946b9b81acbfe212979a1a6d7442 Mon Sep 17 00:00:00 2001 From: geopanther Date: Wed, 13 May 2026 12:38:36 +0200 Subject: [PATCH] fix(upsert): handle missing version.message in only-changed mode Older self-hosted Confluence servers don't populate version.message on page create. Accessing it directly causes an AttributeError when using --only-changed. Guard with hasattr() and treat missing message as 'needs update'. Also guards the same pattern for attachments. Adapted from iamjackg/md2cf#135 by @briandcho. --- mdfluence/upsert.py | 12 ++++++++---- test_package/unit/test_upsert.py | 20 ++++++++++++++++++++ 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/mdfluence/upsert.py b/mdfluence/upsert.py index 2bb2e88..afde1ea 100644 --- a/mdfluence/upsert.py +++ b/mdfluence/upsert.py @@ -147,8 +147,10 @@ def page_needs_updating(page, existing_page, replace_all_labels): # print(f"Page labels have changed: {page.title} {page.labels}") return True else: - existing_page_hash_match = CONTENT_HASH_REGEX.search( - existing_page.version.message + existing_page_hash_match = ( + CONTENT_HASH_REGEX.search(existing_page.version.message) + if hasattr(existing_page.version, "message") + else None ) if existing_page_hash_match is not None: original_page_hash = existing_page_hash_match.group(1) @@ -188,8 +190,10 @@ def upsert_attachment( else: should_update = True if only_changed: - existing_attachment_hash_match = CONTENT_HASH_REGEX.search( - existing_attachment.version.message + existing_attachment_hash_match = ( + CONTENT_HASH_REGEX.search(existing_attachment.version.message) + if hasattr(existing_attachment.version, "message") + else None ) if existing_attachment_hash_match is not None: original_attachment_hash = existing_attachment_hash_match.group(1) diff --git a/test_package/unit/test_upsert.py b/test_package/unit/test_upsert.py index 104cf87..2af427a 100644 --- a/test_package/unit/test_upsert.py +++ b/test_package/unit/test_upsert.py @@ -1,6 +1,7 @@ import pytest import mdfluence.upsert +from mdfluence.api import Bunch from mdfluence.api import MinimalConfluence as Confluence from mdfluence.document import Page @@ -224,6 +225,25 @@ def test_upsert_page_only_changed_no_changes(mocker): assert upsert_result.action == upsert_result.action.SKIPPED +def test_page_needs_updating_when_no_message(mocker): + existing_page_mock = mocker.Mock() + ancestor_mock = mocker.Mock() + ancestor_mock.id = mocker.sentinel.parent_id + existing_page_mock.ancestors = [ancestor_mock] + existing_page_mock.version = Bunch() + + page = Page( + space=mocker.sentinel.space, + title=mocker.sentinel.title, + body="hello there", + parent_id=mocker.sentinel.parent_id, + ) + + assert mdfluence.upsert.page_needs_updating( + page, existing_page_mock, replace_all_labels=False + ) + + def test_page_needs_updating_page_not_changed(mocker): message_hash = "[v6e71b3cac15d32fe2d36c270887df9479c25c640]" existing_page_mock = mocker.Mock()