From a3b367f1d3c954ebcb0736d98eb7c0f6006cf4f3 Mon Sep 17 00:00:00 2001 From: Frank Liu Date: Tue, 9 Jun 2026 13:58:01 +0800 Subject: [PATCH 1/6] Add source/install telemetry and drop unreliable activation check Follow-up to v0.27.5 telemetry analysis (devdiv-azure-service-dmitryr/azure-java-migration-copilot-vscode-extension#5979): - Add a source field (SOURCE_CVE / SOURCE_JAVA_UPGRADE) to the upgradeNotification.show event so NOTIFY-level data can be split by CVE vs upgrade and aligned with downstream funnel stages. - Emit an installSucceeded event (with the initial extensionState, so fresh installs and updates can be told apart) right after installExtension resolves. - Drop the post-install getExtensionState re-check that produced false activation-timeout results; the not-installed path now reports proceeded and returns true after install completes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/upgrade/display/notificationManager.ts | 1 + src/upgrade/utility.ts | 15 ++++++++------- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/upgrade/display/notificationManager.ts b/src/upgrade/display/notificationManager.ts index d8e6a461..55c3422c 100644 --- a/src/upgrade/display/notificationManager.ts +++ b/src/upgrade/display/notificationManager.ts @@ -93,6 +93,7 @@ class NotificationManager implements IUpgradeIssuesRenderer { sendInfo(operationId, { operationName: "java.dependency.upgradeNotification.show", extensionState, + source: hasCVEIssue ? Upgrade.SOURCE_CVE : Upgrade.SOURCE_JAVA_UPGRADE, }); const buttons = hasCVEIssue diff --git a/src/upgrade/utility.ts b/src/upgrade/utility.ts index 97461b75..cd621c2d 100644 --- a/src/upgrade/utility.ts +++ b/src/upgrade/utility.ts @@ -197,6 +197,10 @@ export async function checkOrInstallAppModExtensionForUpgrade( } await commands.executeCommand("workbench.extensions.installExtension", ExtensionName.APP_MODERNIZATION_FOR_JAVA); + sendInfo(operationId, { + operationName: "java.dependency.upgradeFlow.installSucceeded", + extensionState: state, + }); if (state === "outdated") { // Extension was updated (not freshly installed) — reload required @@ -221,16 +225,13 @@ export async function checkOrInstallAppModExtensionForUpgrade( await checkOrPromptToEnableAppModExtension("upgrade"); - // Wait briefly for the newly installed extension to activate + // Give the newly installed extension a moment to activate before proceeding await new Promise(resolve => setTimeout(resolve, 2000)); - // Re-check if the newly installed extension is active and meets version requirement - const newState = getExtensionState(extensionIdToCheck); - const canProceed = newState === "up-to-date"; sendInfo(operationId, { operationName: "java.dependency.upgradeFlow.result", - upgradeFlowResult: canProceed ? "proceeded" : "activation-timeout", + upgradeFlowResult: "proceeded", }); - return canProceed; + return true; })(); -} \ No newline at end of file +} From c7b1fbae7edc036c76268dd0090c08016b8ddf68 Mon Sep 17 00:00:00 2001 From: Frank Liu Date: Tue, 9 Jun 2026 17:31:11 +0800 Subject: [PATCH 2/6] Drop dedicated installSucceeded telemetry event Address review: avoid adding a new business operationName for the install step. Install success is already implied by the subsequent upgradeFlow.result event under the same operation ID. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/upgrade/utility.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/upgrade/utility.ts b/src/upgrade/utility.ts index cd621c2d..fc2ca141 100644 --- a/src/upgrade/utility.ts +++ b/src/upgrade/utility.ts @@ -197,10 +197,6 @@ export async function checkOrInstallAppModExtensionForUpgrade( } await commands.executeCommand("workbench.extensions.installExtension", ExtensionName.APP_MODERNIZATION_FOR_JAVA); - sendInfo(operationId, { - operationName: "java.dependency.upgradeFlow.installSucceeded", - extensionState: state, - }); if (state === "outdated") { // Extension was updated (not freshly installed) — reload required From ec4fb1cf5cf57bc399ad5b6b221c558ce8af4260 Mon Sep 17 00:00:00 2001 From: Frank Liu Date: Tue, 9 Jun 2026 17:39:13 +0800 Subject: [PATCH 3/6] Track install-success as a dimension on the result event Address review: instead of a dedicated installSucceeded operationName, reuse the stable java.dependency.upgradeFlow.result event and mark the install step with an upgradeFlowStep dimension (plus extensionState so fresh installs and updates can be told apart). Easier to query and evolve. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/upgrade/utility.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/upgrade/utility.ts b/src/upgrade/utility.ts index fc2ca141..7c4516b9 100644 --- a/src/upgrade/utility.ts +++ b/src/upgrade/utility.ts @@ -197,6 +197,11 @@ export async function checkOrInstallAppModExtensionForUpgrade( } await commands.executeCommand("workbench.extensions.installExtension", ExtensionName.APP_MODERNIZATION_FOR_JAVA); + sendInfo(operationId, { + operationName: "java.dependency.upgradeFlow.result", + upgradeFlowStep: "installSucceeded", + extensionState: state, + }); if (state === "outdated") { // Extension was updated (not freshly installed) — reload required From 362d375da7588d9fa546e92b5f316abe5aaf0600 Mon Sep 17 00:00:00 2001 From: Frank Liu Date: Tue, 9 Jun 2026 17:48:53 +0800 Subject: [PATCH 4/6] Label install step by action instead of pre-install state Address review: extensionState was the pre-install value, so tagging a 'succeeded' event with 'not-installed'/'outdated' was contradictory. Replace it with installType ('installed' vs 'updated') derived from the pre-install state, which is unambiguous and avoids an unreliable post-install state recompute (the original activation-race bug). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/upgrade/utility.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/upgrade/utility.ts b/src/upgrade/utility.ts index 7c4516b9..d4f86ae6 100644 --- a/src/upgrade/utility.ts +++ b/src/upgrade/utility.ts @@ -200,7 +200,7 @@ export async function checkOrInstallAppModExtensionForUpgrade( sendInfo(operationId, { operationName: "java.dependency.upgradeFlow.result", upgradeFlowStep: "installSucceeded", - extensionState: state, + installType: state === "outdated" ? "updated" : "installed", }); if (state === "outdated") { From e270f83cc509702c6551f2adc0eab70923e973da Mon Sep 17 00:00:00 2001 From: Frank Liu Date: Tue, 9 Jun 2026 18:19:06 +0800 Subject: [PATCH 5/6] Don't prompt to enable extension in upgrade flow extensions.getExtension() returns undefined both when the extension is disabled and when it is freshly installed but not yet activated, so the 'seems disabled, enable it manually' prompt is misleading right after a successful install. Skip it on the upgrade path; the function and the modernization caller are left unchanged. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/upgrade/utility.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/upgrade/utility.ts b/src/upgrade/utility.ts index d4f86ae6..cd47626f 100644 --- a/src/upgrade/utility.ts +++ b/src/upgrade/utility.ts @@ -224,8 +224,6 @@ export async function checkOrInstallAppModExtensionForUpgrade( return false; } - await checkOrPromptToEnableAppModExtension("upgrade"); - // Give the newly installed extension a moment to activate before proceeding await new Promise(resolve => setTimeout(resolve, 2000)); From 4891595f1cc45d52e67a4bb52b3f638d53f6f86c Mon Sep 17 00:00:00 2001 From: Frank Liu Date: Tue, 9 Jun 2026 18:26:39 +0800 Subject: [PATCH 6/6] Wait for extension registration via onDidChange instead of fixed sleep Replace the fixed 2s wait after a fresh install with an event-driven wait: resolve immediately if the extension is already registered, otherwise listen on extensions.onDidChange and proceed as soon as it appears, with a 5s timeout fallback. Returns as early as possible while still bounding the wait. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/upgrade/utility.ts | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/upgrade/utility.ts b/src/upgrade/utility.ts index cd47626f..0f364a20 100644 --- a/src/upgrade/utility.ts +++ b/src/upgrade/utility.ts @@ -224,8 +224,9 @@ export async function checkOrInstallAppModExtensionForUpgrade( return false; } - // Give the newly installed extension a moment to activate before proceeding - await new Promise(resolve => setTimeout(resolve, 2000)); + // Wait until the freshly installed extension is registered, returning as + // soon as it is ready, or after a 5s timeout fallback at the latest. + await waitForExtensionReady(extensionIdToCheck, 5000); sendInfo(operationId, { operationName: "java.dependency.upgradeFlow.result", @@ -234,3 +235,24 @@ export async function checkOrInstallAppModExtensionForUpgrade( return true; })(); } + +function waitForExtensionReady(extensionId: string, timeoutMs: number): Promise { + return new Promise(resolve => { + if (extensions.getExtension(extensionId)) { + resolve(); + return; + } + let timer: NodeJS.Timeout; + const disposable = extensions.onDidChange(() => { + if (extensions.getExtension(extensionId)) { + clearTimeout(timer); + disposable.dispose(); + resolve(); + } + }); + timer = setTimeout(() => { + disposable.dispose(); + resolve(); + }, timeoutMs); + }); +}