From 771be6a4716aa45c1184c080ef2cd56443432c64 Mon Sep 17 00:00:00 2001 From: Phillip Simonds Date: Tue, 19 May 2026 07:58:03 -0600 Subject: [PATCH 1/4] fix(branch): add MERGING to BranchStatus enum to match server (#9293) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The server-side BranchStatus enum gained MERGING in Infrahub 1.9.3 (PR opsmill/infrahub#9116) but the SDK enum was not updated. Any branch returned with status="MERGING" — including a branch stranded in that state by a task worker dying mid-merge — caused client.branch.all() to raise ValidationError, which crashloops the task worker on every sync_remote_repositories run. Co-Authored-By: Claude Opus 4.7 (1M context) --- changelog/+branchstatus-merging.fixed.md | 1 + infrahub_sdk/branch.py | 1 + tests/unit/sdk/test_branch.py | 18 ++++++++++++++++++ 3 files changed, 20 insertions(+) create mode 100644 changelog/+branchstatus-merging.fixed.md diff --git a/changelog/+branchstatus-merging.fixed.md b/changelog/+branchstatus-merging.fixed.md new file mode 100644 index 000000000..27a3bfd18 --- /dev/null +++ b/changelog/+branchstatus-merging.fixed.md @@ -0,0 +1 @@ +Add `MERGING` to `infrahub_sdk.branch.BranchStatus` to match the server-side enum (added in Infrahub 1.9.3). Without this, `client.branch.all()` raised `ValidationError` when any branch was returned with `status="MERGING"`, which could strand the task worker in a crashloop if a merge task died mid-flight and left a branch in that state. diff --git a/infrahub_sdk/branch.py b/infrahub_sdk/branch.py index 4c89bd410..5794f45df 100644 --- a/infrahub_sdk/branch.py +++ b/infrahub_sdk/branch.py @@ -19,6 +19,7 @@ class BranchStatus(str, Enum): NEED_REBASE = "NEED_REBASE" NEED_UPGRADE_REBASE = "NEED_UPGRADE_REBASE" DELETING = "DELETING" + MERGING = "MERGING" MERGED = "MERGED" diff --git a/tests/unit/sdk/test_branch.py b/tests/unit/sdk/test_branch.py index 88f4a5305..9ee6e4dcb 100644 --- a/tests/unit/sdk/test_branch.py +++ b/tests/unit/sdk/test_branch.py @@ -7,6 +7,7 @@ from infrahub_sdk.branch import ( BranchData, + BranchStatus, InfrahubBranchManager, InfrahubBranchManagerSync, ) @@ -38,6 +39,23 @@ def test_validate_method_signature(method: str) -> None: assert async_sig.return_annotation == sync_sig.return_annotation +@pytest.mark.parametrize( + "status_value", + ["OPEN", "NEED_REBASE", "NEED_UPGRADE_REBASE", "DELETING", "MERGING", "MERGED"], +) +def test_branch_data_accepts_all_server_statuses(status_value: str) -> None: + branch = BranchData( + id="01J0", + name="test", + sync_with_git=False, + is_default=False, + has_schema_changes=False, + status=status_value, + branched_from="2026-01-01T00:00:00Z", + ) + assert branch.status is BranchStatus(status_value) + + @pytest.mark.parametrize("client_type", client_types) async def test_get_branches(clients: BothClients, mock_branches_list_query: HTTPXMock, client_type: str) -> None: if client_type == "standard": From 585d6ec2e4d289f75e5704fffcd79709fb16276d Mon Sep 17 00:00:00 2001 From: Phillip Simonds Date: Tue, 19 May 2026 08:26:48 -0600 Subject: [PATCH 2/4] fix(tests): use model_validate so ty accepts the parametrized status string ty rejects passing str where BranchStatus is annotated; switch to BranchData.model_validate({...}) which mirrors how the SDK actually parses server JSON in branch.py. Co-Authored-By: Claude Opus 4.7 (1M context) --- tests/unit/sdk/test_branch.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/tests/unit/sdk/test_branch.py b/tests/unit/sdk/test_branch.py index 9ee6e4dcb..1a13e143f 100644 --- a/tests/unit/sdk/test_branch.py +++ b/tests/unit/sdk/test_branch.py @@ -44,14 +44,16 @@ def test_validate_method_signature(method: str) -> None: ["OPEN", "NEED_REBASE", "NEED_UPGRADE_REBASE", "DELETING", "MERGING", "MERGED"], ) def test_branch_data_accepts_all_server_statuses(status_value: str) -> None: - branch = BranchData( - id="01J0", - name="test", - sync_with_git=False, - is_default=False, - has_schema_changes=False, - status=status_value, - branched_from="2026-01-01T00:00:00Z", + branch = BranchData.model_validate( + { + "id": "01J0", + "name": "test", + "sync_with_git": False, + "is_default": False, + "has_schema_changes": False, + "status": status_value, + "branched_from": "2026-01-01T00:00:00Z", + } ) assert branch.status is BranchStatus(status_value) From 3cda70f521a8f748856eddb8a627293e32631b1c Mon Sep 17 00:00:00 2001 From: Phillip Simonds Date: Tue, 19 May 2026 08:48:56 -0600 Subject: [PATCH 3/4] =?UTF-8?q?review(branch):=20address=20PR=20feedback?= =?UTF-8?q?=20=E2=80=94=20drop=20test,=20rename=20changelog,=20shorten=20n?= =?UTF-8?q?ote?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Drop test_branch_data_accepts_all_server_statuses: per review, the enum-coverage test belongs in the infrahub community repo where it can iterate over the actual server-side BranchStatus enum instead of a hardcoded list of strings. It will be added there as part of the PR that bumps the SDK commit pin. - Rename changelog +branchstatus-merging.fixed.md → 1037.fixed.md to link it to the newly-filed SDK-side issue #1037 (migrated from opsmill/infrahub#9293). - Shorten changelog note per review. Co-Authored-By: Claude Opus 4.7 (1M context) --- ...hstatus-merging.fixed.md => 1037.fixed.md} | 0 tests/unit/sdk/test_branch.py | 20 ------------------- 2 files changed, 20 deletions(-) rename changelog/{+branchstatus-merging.fixed.md => 1037.fixed.md} (100%) diff --git a/changelog/+branchstatus-merging.fixed.md b/changelog/1037.fixed.md similarity index 100% rename from changelog/+branchstatus-merging.fixed.md rename to changelog/1037.fixed.md diff --git a/tests/unit/sdk/test_branch.py b/tests/unit/sdk/test_branch.py index 1a13e143f..88f4a5305 100644 --- a/tests/unit/sdk/test_branch.py +++ b/tests/unit/sdk/test_branch.py @@ -7,7 +7,6 @@ from infrahub_sdk.branch import ( BranchData, - BranchStatus, InfrahubBranchManager, InfrahubBranchManagerSync, ) @@ -39,25 +38,6 @@ def test_validate_method_signature(method: str) -> None: assert async_sig.return_annotation == sync_sig.return_annotation -@pytest.mark.parametrize( - "status_value", - ["OPEN", "NEED_REBASE", "NEED_UPGRADE_REBASE", "DELETING", "MERGING", "MERGED"], -) -def test_branch_data_accepts_all_server_statuses(status_value: str) -> None: - branch = BranchData.model_validate( - { - "id": "01J0", - "name": "test", - "sync_with_git": False, - "is_default": False, - "has_schema_changes": False, - "status": status_value, - "branched_from": "2026-01-01T00:00:00Z", - } - ) - assert branch.status is BranchStatus(status_value) - - @pytest.mark.parametrize("client_type", client_types) async def test_get_branches(clients: BothClients, mock_branches_list_query: HTTPXMock, client_type: str) -> None: if client_type == "standard": From a7a0a94d92ce9e3f00a3a9d9b659268539c1012e Mon Sep 17 00:00:00 2001 From: Phillip Simonds Date: Tue, 19 May 2026 10:08:07 -0600 Subject: [PATCH 4/4] chore(changelog): shorten 1037.fixed.md per review Co-Authored-By: Claude Opus 4.7 (1M context) --- changelog/1037.fixed.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog/1037.fixed.md b/changelog/1037.fixed.md index 27a3bfd18..c22cf9b0e 100644 --- a/changelog/1037.fixed.md +++ b/changelog/1037.fixed.md @@ -1 +1 @@ -Add `MERGING` to `infrahub_sdk.branch.BranchStatus` to match the server-side enum (added in Infrahub 1.9.3). Without this, `client.branch.all()` raised `ValidationError` when any branch was returned with `status="MERGING"`, which could strand the task worker in a crashloop if a merge task died mid-flight and left a branch in that state. +Add `MERGING` branch status so that a merging branch can still be correctly retrieved.