From 76876c17249eda149bfb80265288ac1b30fd3579 Mon Sep 17 00:00:00 2001 From: "Sanket M." Date: Tue, 14 Jul 2026 17:45:55 +0530 Subject: [PATCH] fix(deploy): activate the release and application after deploy (DEVX-539) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `application deploy` uploaded extension artifacts but never activated anything, so every deployed app stayed INACTIVE and could not be enabled — the core deploy workflow was effectively a no-op. Ports the TS deploy behavior (PR #77). - ApplicationClient: add activate_release(applicationId, releaseId) mirroring the ActivateRelease GraphQL mutation - deploy_command: after the extension loop (regardless of extension count) call activate_release then update_application(status: ACTIVE), streaming release.activate / application.activate step events - test: httpmock coverage that activate_release sends both IDs and returns data Note: the full deploy-flow integration test (T1) is deferred to the test-layer ticket; this covers the new client method directly. Co-Authored-By: Claude Opus 4.8 (1M context) --- rust/src/application/client.rs | 41 ++++++++++++++++++++++++++++ rust/src/application/commands/mod.rs | 28 ++++++++++++++++++- 2 files changed, 68 insertions(+), 1 deletion(-) diff --git a/rust/src/application/client.rs b/rust/src/application/client.rs index 433d957..7179cad 100644 --- a/rust/src/application/client.rs +++ b/rust/src/application/client.rs @@ -128,6 +128,18 @@ impl ApplicationClient { .await } + pub async fn activate_release( + &self, + application_id: &str, + release_id: &str, + ) -> Result { + self.query(json!({ + "query": "mutation ActivateRelease($applicationId: ID!, $releaseId: ID!) { activateRelease(applicationId: $applicationId, releaseId: $releaseId) { id version description status activatedAt createdAt updatedAt } }", + "variables": { "applicationId": application_id, "releaseId": release_id } + })) + .await + } + pub async fn enable_application(&self, input: Value) -> Result { self.query(json!({ "query": "mutation EnableApplication($input: MutationEnableStoreApplicationInput!) { enableStoreApplication(input: $input) { id } }", @@ -243,4 +255,33 @@ mod tests { // may point the default at an http:// local proxy). assert!(url.contains("://"), "{url:?}"); } + + #[tokio::test] + async fn activate_release_sends_ids_and_returns_data() { + use httpmock::prelude::*; + use serde_json::json; + + let server = MockServer::start(); + let mock = server.mock(|when, then| { + when.method(POST) + .path("/v1/apps/app-registry-subgraph") + .body_contains("activateRelease") + .body_contains("app-1") + .body_contains("rel-9"); + then.status(200).header("content-type", "application/json").json_body(json!({ + "data": { "activateRelease": { "id": "rel-9", "version": "1.0.0", "status": "ACTIVE" } } + })); + }); + + let client = super::ApplicationClient::new(server.base_url(), "test-token"); + let data = client + .activate_release("app-1", "rel-9") + .await + .expect("activate_release should succeed"); + + // The mutation was sent with both IDs, and the response data is returned. + mock.assert(); + assert_eq!(data["activateRelease"]["status"], "ACTIVE"); + assert_eq!(data["activateRelease"]["id"], "rel-9"); + } } diff --git a/rust/src/application/commands/mod.rs b/rust/src/application/commands/mod.rs index d558f0e..6c9a3f9 100644 --- a/rust/src/application/commands/mod.rs +++ b/rust/src/application/commands/mod.rs @@ -817,8 +817,34 @@ fn deploy_command() -> RuntimeCommandSpec { .await?; } + // Activate the release, then set the application ACTIVE. This runs + // regardless of extension count — without it the app stays INACTIVE + // and can't be enabled, so a deploy that only uploads artifacts is + // effectively a no-op. (Parity with the TS deploy flow / PR #77.) sender - .send(json!({ "type": "step", "name": "deploy", "status": "completed", "name": name, "extensions": total })) + .send(json!({ "type": "step", "name": "release.activate", "status": "started", "releaseId": release_id })) + .await; + client + .activate_release(&application_id, &release_id) + .await + .map_err(client_err)?; + sender + .send(json!({ "type": "step", "name": "release.activate", "status": "completed", "releaseId": release_id })) + .await; + + sender + .send(json!({ "type": "step", "name": "application.activate", "status": "started", "id": application_id })) + .await; + client + .update_application(&application_id, json!({ "status": "ACTIVE" })) + .await + .map_err(client_err)?; + sender + .send(json!({ "type": "step", "name": "application.activate", "status": "completed", "id": application_id })) + .await; + + sender + .send(json!({ "type": "step", "name": "deploy", "status": "completed", "name": name, "extensions": total, "status_after": "ACTIVE" })) .await; Ok(()) },