From 6c16794173806a7b28e987c7ccc2391110282e70 Mon Sep 17 00:00:00 2001 From: Garrett Delfosse Date: Tue, 24 Feb 2026 16:51:48 -0800 Subject: [PATCH] fix(cli): proactively use active template version when require_active_version is set (#22033) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #22030 ## Problem When a template has `require_active_version = true` and a workspace is outdated, the web UI always shows "Update and start" as the **only** button (for all users including admins), but `coder start` starts with the old version. For admins, this silently succeeds on the stale version. For non-admins, it goes through a clunky 403→retry path. This also affects the VS Code extension, which calls `coder start --yes` under the hood. ## Root Cause `buildWorkspaceStartRequest()` in `cli/start.go` checks `workspace.AutomaticUpdates == "always"` but ignores `workspace.TemplateRequireActiveVersion`. The server-side autostart already ORs both settings together: ```go // coderd/autobuild/lifecycle_executor.go func useActiveVersion(opts, ws) bool { return opts.RequireActiveVersion || ws.AutomaticUpdates == "always" } ``` The CLI was missing the `RequireActiveVersion` check. ## Fix Add `workspace.TemplateRequireActiveVersion` to the existing OR condition: ```go // Before: if workspace.AutomaticUpdates == codersdk.AutomaticUpdatesAlways || action == WorkspaceUpdate { // After: if workspace.AutomaticUpdates == codersdk.AutomaticUpdatesAlways || workspace.TemplateRequireActiveVersion || action == WorkspaceUpdate { ``` Now `coder start` and `coder restart` proactively use the active template version when `require_active_version` is set, matching the web UI and server autostart behavior. The 403→retry fallback remains as a safety net but is no longer the primary path for any user. ## Testing Updated `enterprise/cli/start_test.go` — all user types (owner, template admin, ACL admin, group ACL admin, member) now expect the active version when `require_active_version` is set, and verify the 403→retry message does NOT appear. --- cli/start.go | 2 +- enterprise/cli/start_test.go | 33 +++++++++++++++------------------ 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/cli/start.go b/cli/start.go index e4e2bd40f3..7949f30871 100644 --- a/cli/start.go +++ b/cli/start.go @@ -120,7 +120,7 @@ func (r *RootCmd) start() *serpent.Command { func buildWorkspaceStartRequest(inv *serpent.Invocation, client *codersdk.Client, workspace codersdk.Workspace, parameterFlags workspaceParameterFlags, buildFlags buildFlags, action WorkspaceCLIAction) (codersdk.CreateWorkspaceBuildRequest, error) { version := workspace.LatestBuild.TemplateVersionID - if workspace.AutomaticUpdates == codersdk.AutomaticUpdatesAlways || action == WorkspaceUpdate { + if workspace.AutomaticUpdates == codersdk.AutomaticUpdatesAlways || workspace.TemplateRequireActiveVersion || action == WorkspaceUpdate { version = workspace.TemplateActiveVersionID if version != workspace.LatestBuild.TemplateVersionID { action = WorkspaceUpdate diff --git a/enterprise/cli/start_test.go b/enterprise/cli/start_test.go index 2ef3b8cd80..3dfd277e3c 100644 --- a/enterprise/cli/start_test.go +++ b/enterprise/cli/start_test.go @@ -86,30 +86,32 @@ func TestStart(t *testing.T) { ExpectedVersion uuid.UUID } + // All users should be updated to the active version when + // require_active_version is set, matching web UI behavior. cases := []testcase{ { - Name: "OwnerUnchanged", + Name: "OwnerUpdates", Client: ownerClient, WorkspaceOwner: owner.UserID, - ExpectedVersion: oldVersion.ID, + ExpectedVersion: activeVersion.ID, }, { - Name: "TemplateAdminUnchanged", + Name: "TemplateAdminUpdates", Client: templateAdminClient, WorkspaceOwner: templateAdmin.ID, - ExpectedVersion: oldVersion.ID, + ExpectedVersion: activeVersion.ID, }, { - Name: "TemplateACLAdminUnchanged", + Name: "TemplateACLAdminUpdates", Client: templateACLAdminClient, WorkspaceOwner: templateACLAdmin.ID, - ExpectedVersion: oldVersion.ID, + ExpectedVersion: activeVersion.ID, }, { - Name: "TemplateGroupACLAdminUnchanged", + Name: "TemplateGroupACLAdminUpdates", Client: templateGroupACLAdminClient, WorkspaceOwner: templateGroupACLAdmin.ID, - ExpectedVersion: oldVersion.ID, + ExpectedVersion: activeVersion.ID, }, { Name: "MemberUpdates", @@ -156,16 +158,11 @@ func TestStart(t *testing.T) { ws = coderdtest.MustWorkspace(t, c.Client, ws.ID) require.Equal(t, c.ExpectedVersion, ws.LatestBuild.TemplateVersionID) - if initialTemplateVersion == ws.LatestBuild.TemplateVersionID { - return - } - - if cmd == "start" { - require.Contains(t, buf.String(), "Unable to start the workspace with the template version from the last build") - } - - if cmd == "restart" { - require.Contains(t, buf.String(), "Unable to restart the workspace with the template version from the last build") + // The CLI should proactively use the active version + // without hitting the 403→retry path. + if initialTemplateVersion != ws.LatestBuild.TemplateVersionID { + require.NotContains(t, buf.String(), "Unable to start the workspace with the template version from the last build") + require.NotContains(t, buf.String(), "Unable to restart the workspace with the template version from the last build") } }) }