drukbox-python-sdk 0.4.0__tar.gz → 0.5.1__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.
- {drukbox_python_sdk-0.4.0 → drukbox_python_sdk-0.5.1}/AGENTS.md +4 -3
- {drukbox_python_sdk-0.4.0 → drukbox_python_sdk-0.5.1}/PKG-INFO +1 -1
- {drukbox_python_sdk-0.4.0 → drukbox_python_sdk-0.5.1}/pyproject.toml +1 -1
- {drukbox_python_sdk-0.4.0 → drukbox_python_sdk-0.5.1}/src/drukbox_sdk/api.py +9 -7
- {drukbox_python_sdk-0.4.0 → drukbox_python_sdk-0.5.1}/tests/test_api.py +27 -2
- {drukbox_python_sdk-0.4.0 → drukbox_python_sdk-0.5.1}/.github/workflows/on-main-merge.yml +0 -0
- {drukbox_python_sdk-0.4.0 → drukbox_python_sdk-0.5.1}/.github/workflows/on-pull-request.yml +0 -0
- {drukbox_python_sdk-0.4.0 → drukbox_python_sdk-0.5.1}/.github/workflows/release.yml +0 -0
- {drukbox_python_sdk-0.4.0 → drukbox_python_sdk-0.5.1}/.gitignore +0 -0
- {drukbox_python_sdk-0.4.0 → drukbox_python_sdk-0.5.1}/LICENSE +0 -0
- {drukbox_python_sdk-0.4.0 → drukbox_python_sdk-0.5.1}/README.md +0 -0
- {drukbox_python_sdk-0.4.0 → drukbox_python_sdk-0.5.1}/src/drukbox_sdk/__init__.py +0 -0
- {drukbox_python_sdk-0.4.0 → drukbox_python_sdk-0.5.1}/src/drukbox_sdk/exceptions.py +0 -0
- {drukbox_python_sdk-0.4.0 → drukbox_python_sdk-0.5.1}/tests/__init__.py +0 -0
|
@@ -57,6 +57,7 @@ run.
|
|
|
57
57
|
consumers unless the user asks for them.
|
|
58
58
|
- Preserve user changes in the worktree; do not clean or rewrite unrelated
|
|
59
59
|
files.
|
|
60
|
-
- A version bump rides alone:
|
|
61
|
-
only `pyproject.toml`, with no other change in it.
|
|
62
|
-
a feature or fix commit. The release tag is
|
|
60
|
+
- A version bump rides alone: its own commit (`chore: bump to X`) that
|
|
61
|
+
touches only `pyproject.toml`, in its own PR with no other change in it.
|
|
62
|
+
Never fold the bump into a feature or fix commit or PR. The release tag is
|
|
63
|
+
cut from the bump commit.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: drukbox-python-sdk
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.1
|
|
4
4
|
Summary: Async Python client for Drukbox.
|
|
5
5
|
Project-URL: Homepage, https://github.com/clawhaven/drukbox-python-sdk
|
|
6
6
|
Project-URL: Repository, https://github.com/clawhaven/drukbox-python-sdk
|
|
@@ -198,7 +198,8 @@ class DoctorReport:
|
|
|
198
198
|
|
|
199
199
|
ok: bool
|
|
200
200
|
active_provider: str
|
|
201
|
-
|
|
201
|
+
# Whether the active provider's hosts join the tailnet.
|
|
202
|
+
tailnet: bool
|
|
202
203
|
checks: list[DoctorCheck]
|
|
203
204
|
|
|
204
205
|
|
|
@@ -490,7 +491,8 @@ class SandboxAPI:
|
|
|
490
491
|
return DoctorReport(
|
|
491
492
|
ok=data["ok"],
|
|
492
493
|
active_provider=data["active_provider"],
|
|
493
|
-
|
|
494
|
+
# drukbox before "Join the tailnet per provider" sent tailscale_enabled.
|
|
495
|
+
tailnet=data["tailnet"] if "tailnet" in data else data["tailscale_enabled"],
|
|
494
496
|
checks=[DoctorCheck(**check) for check in data["checks"]],
|
|
495
497
|
)
|
|
496
498
|
|
|
@@ -624,11 +626,11 @@ class SandboxAPI:
|
|
|
624
626
|
raise SandboxUnavailableError(json_response.get("detail", "service unavailable"))
|
|
625
627
|
|
|
626
628
|
if response.status_code == 422:
|
|
627
|
-
#
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
raise SandboxValidationError(detail)
|
|
629
|
+
# The detail is FastAPI's list of error dicts or the service's own sentence.
|
|
630
|
+
detail = json_response.get("detail")
|
|
631
|
+
if isinstance(detail, list):
|
|
632
|
+
detail = "; ".join(error.get("msg", "") for error in detail)
|
|
633
|
+
raise SandboxValidationError(detail or "validation error")
|
|
632
634
|
|
|
633
635
|
if response.status_code >= 400:
|
|
634
636
|
raise SandboxResponseError(json_response.get("detail", "error"))
|
|
@@ -48,7 +48,7 @@ def _doctor_payload(**overrides: Any) -> dict[str, Any]:
|
|
|
48
48
|
payload: dict[str, Any] = {
|
|
49
49
|
"ok": True,
|
|
50
50
|
"active_provider": "aws",
|
|
51
|
-
"
|
|
51
|
+
"tailnet": True,
|
|
52
52
|
"checks": [
|
|
53
53
|
{
|
|
54
54
|
"name": "db",
|
|
@@ -744,6 +744,20 @@ async def test_422_raises_sandbox_validation_error(api: SandboxAPI):
|
|
|
744
744
|
await api.create_host(env={"FOO BAR": "x"})
|
|
745
745
|
|
|
746
746
|
|
|
747
|
+
@respx.mock
|
|
748
|
+
async def test_422_string_detail_raises_sandbox_validation_error(api: SandboxAPI):
|
|
749
|
+
# Drukbox answers a refused secret with the issuer's reason as a plain string.
|
|
750
|
+
respx.post(f"{BASE_URL}/hosts").mock(
|
|
751
|
+
return_value=httpx.Response(
|
|
752
|
+
422,
|
|
753
|
+
json={"detail": "GitHub App not installed for acme", "error_code": "SECRET_REFUSED"},
|
|
754
|
+
),
|
|
755
|
+
)
|
|
756
|
+
|
|
757
|
+
with pytest.raises(SandboxValidationError, match=r"^GitHub App not installed for acme$"):
|
|
758
|
+
await api.create_host()
|
|
759
|
+
|
|
760
|
+
|
|
747
761
|
@respx.mock
|
|
748
762
|
async def test_500_raises_sandbox_response_error(api: SandboxAPI):
|
|
749
763
|
respx.get(f"{BASE_URL}/hosts").mock(
|
|
@@ -925,6 +939,17 @@ def test_from_env_strips_trailing_slash_via_constructor(
|
|
|
925
939
|
assert sandbox.base_url == "https://trailing.test"
|
|
926
940
|
|
|
927
941
|
|
|
942
|
+
@respx.mock
|
|
943
|
+
async def test_doctor_reads_tailscale_enabled_from_an_older_drukbox(api: SandboxAPI):
|
|
944
|
+
payload = _doctor_payload()
|
|
945
|
+
payload["tailscale_enabled"] = payload.pop("tailnet")
|
|
946
|
+
respx.get(f"{BASE_URL}/doctor").mock(return_value=httpx.Response(200, json=payload))
|
|
947
|
+
|
|
948
|
+
report = await api.doctor()
|
|
949
|
+
|
|
950
|
+
assert report.tailnet is True
|
|
951
|
+
|
|
952
|
+
|
|
928
953
|
@respx.mock
|
|
929
954
|
async def test_doctor_parses_report_and_checks(api: SandboxAPI):
|
|
930
955
|
route = respx.get(f"{BASE_URL}/doctor").mock(
|
|
@@ -937,7 +962,7 @@ async def test_doctor_parses_report_and_checks(api: SandboxAPI):
|
|
|
937
962
|
assert route.calls.last.request.headers["Authorization"] == "Bearer t-test"
|
|
938
963
|
assert report.ok is True
|
|
939
964
|
assert report.active_provider == "aws"
|
|
940
|
-
assert report.
|
|
965
|
+
assert report.tailnet is True
|
|
941
966
|
assert [check.name for check in report.checks] == ["db", "provider", "tailscale"]
|
|
942
967
|
provider = next(check for check in report.checks if check.name == "provider")
|
|
943
968
|
assert provider.status == "ok"
|
|
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
|