diff --git a/crates/tracedecay-sdk/tests/sdk_suite/production_daemon.rs b/crates/tracedecay-sdk/tests/sdk_suite/production_daemon.rs index ec803de922..6e2ead1c76 100644 --- a/crates/tracedecay-sdk/tests/sdk_suite/production_daemon.rs +++ b/crates/tracedecay-sdk/tests/sdk_suite/production_daemon.rs @@ -303,7 +303,13 @@ fn enrolled_remote_client_rejects_an_untrusted_private_authority_and_isolates_en .json(&json!({"grant": grant, "admission": admission})) .send() .unwrap(); - assert_eq!(provisioned.status(), reqwest::StatusCode::NO_CONTENT); + let provision_status = provisioned.status(); + let provision_body = provisioned.text().unwrap_or_default(); + assert_eq!( + provision_status, + reqwest::StatusCode::NO_CONTENT, + "{provision_body}" + ); let request = enrollment_request(&grant); let untrusted_authority = EnrolledRemoteClient::new_with_root_certificate( diff --git a/crates/tracedecay/src/daemon/http_application.rs b/crates/tracedecay/src/daemon/http_application.rs index 73f602856a..543477933f 100644 --- a/crates/tracedecay/src/daemon/http_application.rs +++ b/crates/tracedecay/src/daemon/http_application.rs @@ -400,7 +400,10 @@ async fn provision_remote_node( .await { Ok(()) => StatusCode::NO_CONTENT.into_response(), - Err(_) => StatusCode::CONFLICT.into_response(), + Err(error) => { + tracing::error!(%error, "remote node provision failed"); + (StatusCode::CONFLICT, error.to_string()).into_response() + } } } diff --git a/scripts/check-sdk-publish-workflow.py b/scripts/check-sdk-publish-workflow.py index 1cfb252ced..bce28e140a 100644 --- a/scripts/check-sdk-publish-workflow.py +++ b/scripts/check-sdk-publish-workflow.py @@ -62,13 +62,16 @@ def assert_actions_pinned(name: str, job: dict[str, Any]) -> None: fail(f"'{name}' uses unpinned action {uses!r}") -def assert_release_trigger(workflow: dict[str, Any]) -> None: +def assert_master_dispatch_trigger(workflow: dict[str, Any]) -> None: triggers = workflow.get("on", workflow.get(True, {})) - if not isinstance(triggers, dict) or set(triggers) != {"release", "workflow_dispatch"}: - fail("npm publication must ride the GitHub Release trigger plus tag recovery only") - release = triggers.get("release") - if not isinstance(release, dict) or release.get("types") != ["published"]: - fail("the release trigger must fire on published releases only") + # A run whose ref is the release tag cannot restore the previous release's + # Actions cache. release-please dispatches this workflow on master after + # the immutable tag exists, so a `release` trigger is not an allowed path. + if not isinstance(triggers, dict) or set(triggers) != {"workflow_dispatch"}: + fail( + "npm publication must be dispatched on master after the immutable " + "tag exists; a tag-ref run cannot restore the release cache" + ) dispatch = triggers.get("workflow_dispatch") inputs = dispatch.get("inputs") if isinstance(dispatch, dict) else None if not isinstance(inputs, dict) or set(inputs) != {"release_tag"}: @@ -204,7 +207,7 @@ def main() -> None: ) workflow = yaml.safe_load(text) - assert_release_trigger(workflow) + assert_master_dispatch_trigger(workflow) if workflow.get("permissions") != {"contents": "read"}: fail("top-level permissions must grant contents: read only") diff --git a/scripts/test-check-sdk-publish-workflow.py b/scripts/test-check-sdk-publish-workflow.py index 1b1a8e79a8..74b129cab8 100644 --- a/scripts/test-check-sdk-publish-workflow.py +++ b/scripts/test-check-sdk-publish-workflow.py @@ -36,10 +36,26 @@ def assert_rejected(self, workflow: str) -> None: with self.assertRaises(SystemExit): self.checker.main() - def test_rejects_dropping_the_release_trigger(self) -> None: + def test_rejects_dropping_the_release_dispatch(self) -> None: mutated = self.workflow.replace( - "on:\n release:\n types: [published]\n workflow_dispatch:", - "on:\n workflow_dispatch:", + "on:\n" + " workflow_dispatch:\n" + " inputs:\n" + " release_tag:\n" + ' description: "Stable release tag to build or recover"\n' + " required: true\n" + " type: string\n", + "on:\n" + " push:\n" + " branches: [master]\n", + 1, + ) + self.assert_rejected(mutated) + + def test_rejects_restoring_the_release_trigger(self) -> None: + mutated = self.workflow.replace( + "on:\n workflow_dispatch:\n", + "on:\n release:\n types: [published]\n workflow_dispatch:\n", 1, ) self.assert_rejected(mutated)