diff --git a/package.json b/package.json index e534270e7..0c3e97201 100644 --- a/package.json +++ b/package.json @@ -313,7 +313,7 @@ }, { "id": "coderTasks", - "title": "Coder Tasks", + "title": "Coder Tasks (Deprecated)", "icon": "media/tasks-logo.svg" }, { @@ -354,14 +354,14 @@ "id": "coder.tasksLogin", "name": "Coder Tasks", "icon": "media/tasks-logo.svg", - "when": "!coder.authenticated" + "when": "!coder.authenticated && coder.tasksSupported" }, { "type": "webview", "id": "coder.tasksPanel", "name": "Coder Tasks", "icon": "media/tasks-logo.svg", - "when": "coder.authenticated" + "when": "coder.authenticated && coder.tasksSupported" } ], "coderExperimentalWorkspaces": [ diff --git a/src/core/contextManager.ts b/src/core/contextManager.ts index 7e57abd6f..39217a6f8 100644 --- a/src/core/contextManager.ts +++ b/src/core/contextManager.ts @@ -5,6 +5,7 @@ const CONTEXT_DEFAULTS = { "coder.isOwner": false, "coder.loaded": false, "coder.sharedWorkspacesSupported": true, + "coder.tasksSupported": false, "coder.workspace.connected": false, "coder.workspace.updatable": false, "coder.workspacesPanelEnabled": false, diff --git a/src/extension.ts b/src/extension.ts index 6dc82209e..55f978604 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -4,6 +4,7 @@ import axios, { isAxiosError } from "axios"; import { getErrorMessage } from "coder/site/src/api/errors"; import { createRequire } from "node:module"; import * as path from "node:path"; +import * as semver from "semver"; import * as vscode from "vscode"; import { AnnouncementManager } from "./announcements/manager"; @@ -15,6 +16,7 @@ import { ServiceContainer } from "./core/container"; import { DeploymentManager } from "./deployment/deploymentManager"; import { CertificateError } from "./error/certificateError"; import { getErrorDetail, toError } from "./error/errorUtils"; +import { tasksSupported } from "./featureSet"; import { ActivationTelemetry, type ActivationTracer, @@ -202,12 +204,32 @@ async function doActivate( ); ctx.subscriptions.push(sharedWorkspacesProvider); + // The deprecated Tasks panel only shows on deployments old enough to + // still support it, which requires fetching the deployment version. + const updateTasksSupported = async () => { + let supported = false; + if (deploymentManager.session.current.kind === "signedIn") { + try { + const buildInfo = await client.getBuildInfo(); + supported = tasksSupported(semver.parse(buildInfo.version)); + } catch (error) { + output.warn( + "Unable to fetch deployment version for Tasks panel", + error, + ); + } + } + contextManager.set("coder.tasksSupported", supported); + }; + void updateTasksSupported(); + // Re-probe support on session change (login, logout, deployment switch); // the next fetch clears the message again if still unsupported. ctx.subscriptions.push( - deploymentManager.session.onDidChange(() => - contextManager.set("coder.sharedWorkspacesSupported", true), - ), + deploymentManager.session.onDidChange(() => { + contextManager.set("coder.sharedWorkspacesSupported", true); + void updateTasksSupported(); + }), ); // createTreeView, unlike registerTreeDataProvider, gives us the tree view API diff --git a/src/featureSet.ts b/src/featureSet.ts index 2a209076e..1f8d53e52 100644 --- a/src/featureSet.ts +++ b/src/featureSet.ts @@ -26,6 +26,14 @@ function versionAtLeast( return version.compare(minVersion) >= 0 || version.prerelease[0] === "devel"; } +/** + * True when the deployment predates the June 2026 Tasks deprecation + * (2.34 and below). The deprecated Tasks panel is hidden everywhere else. + */ +export function tasksSupported(version: semver.SemVer | null): boolean { + return version !== null && !versionAtLeast(version, "2.35.0"); +} + /** * Builds and returns a FeatureSet object for a given coder version. */ diff --git a/test/unit/featureSet.test.ts b/test/unit/featureSet.test.ts index d8d91637f..ccfd50806 100644 --- a/test/unit/featureSet.test.ts +++ b/test/unit/featureSet.test.ts @@ -1,7 +1,11 @@ import * as semver from "semver"; import { describe, expect, it } from "vitest"; -import { type FeatureSet, featureSetForVersion } from "@/featureSet"; +import { + type FeatureSet, + featureSetForVersion, + tasksSupported, +} from "@/featureSet"; function expectFlag( flag: keyof FeatureSet, @@ -66,6 +70,16 @@ describe("check version support", () => { ["v2.36.0", "v2.36.1", "v2.37.0", "v3.0.0"], ); }); + it("tasks panel only on deployments before deprecation", () => { + for (const v of ["v2.34.0", "v2.34.7+e491217", "v2.0.0"]) { + expect(tasksSupported(semver.parse(v)), v).toBe(true); + } + for (const v of ["v2.35.0", "v2.36.1", "v3.0.0", "v2.36.0-devel+abc123"]) { + expect(tasksSupported(semver.parse(v)), v).toBe(false); + } + expect(tasksSupported(null)).toBe(false); + }); + it("enables all features for development builds", () => { const featureSet = featureSetForVersion( semver.parse("v0.0.0-devel+abc123"),