@@ -1361,14 +1361,37 @@ func SlimUnsupported(w io.Writer, cmd string) {
13611361os .Exit (1 )
13621362}
13631363
1364- func defaultUpgradeMessage (version string )string {
1365- // Our installation script doesn't work on Windows, so instead we direct the user
1366- // to the GitHub release page to download the latest installer.
1367- version = strings .TrimPrefix (version ,"v" )
1364+ // defaultUpgradeMessage builds an appropriate upgrade message for the platform.
1365+ // Precedence:
1366+ // 1. If a custom upgrade message is provided by the server, use it.
1367+ // 2. If the server provides a dashboard URL (v2.19+) and the platform is not Windows,
1368+ // recommend the site-local install.sh.
1369+ // 3. On Windows, point to the tagged GitHub release page where binaries are published.
1370+ // 4. Otherwise, recommend the global install.sh with explicit version.
1371+ func defaultUpgradeMessage (version ,dashboardURL ,customUpgradeMessage string )string {
1372+ if customUpgradeMessage != "" {
1373+ return customUpgradeMessage
1374+ }
1375+
1376+ // Ensure canonical semver for comparisons and display
1377+ canonical := semver .Canonical (version )
1378+ trimmed := strings .TrimPrefix (canonical ,"v" )
1379+
1380+ // The site-local `install.sh` was introduced in v2.19.0.
1381+ if dashboardURL != "" && semver .Compare (semver .MajorMinor (canonical ),"v2.19" )>= 0 {
1382+ // The site-local install.sh is only valid for macOS and Linux.
1383+ if runtime .GOOS != "windows" {
1384+ return fmt .Sprintf ("download %s with: 'curl -fsSL %s/install.sh | sh'" ,canonical ,dashboardURL )
1385+ }
1386+ // Fall through to Windows-specific suggestion below.
1387+ }
1388+
13681389if runtime .GOOS == "windows" {
1369- return fmt .Sprintf ("download the server version from: https://github.com/coder/coder/releases/v%s" ,version )
1390+ // Link directly to the release page; Windows binaries are published there.
1391+ return fmt .Sprintf ("download the server version from: https://github.com/coder/coder/releases/v%s" ,trimmed )
13701392}
1371- return fmt .Sprintf ("download the server version with: 'curl -L https://coder.com/install.sh | sh -s -- --version %s'" ,version )
1393+
1394+ return fmt .Sprintf ("download the server version with: 'curl -L https://coder.com/install.sh | sh -s -- --version %s'" ,trimmed )
13721395}
13731396
13741397// wrapTransportWithEntitlementsCheck adds a middleware to the HTTP transport
@@ -1407,19 +1430,19 @@ func wrapTransportWithVersionMismatchCheck(rt http.RoundTripper, inv *serpent.In
14071430if buildinfo .VersionsMatch (clientVersion ,serverVersion ) {
14081431return
14091432}
1410- upgradeMessage := defaultUpgradeMessage ( semver . Canonical ( serverVersion ))
1433+ var dashboardURL , customUpgradeMessage string
14111434if serverInfo ,err := getBuildInfo (inv .Context ());err == nil {
1412- switch {
1413- case serverInfo .UpgradeMessage != "" :
1414- upgradeMessage = serverInfo .UpgradeMessage
1415- // The site-local `install.sh` was introduced in v2.19.0
1416- case serverInfo .DashboardURL != "" && semver .Compare (semver .MajorMinor (serverVersion ),"v2.19" )>= 0 :
1417- upgradeMessage = fmt .Sprintf ("download %s with: 'curl -fsSL %s/install.sh | sh'" ,serverVersion ,serverInfo .DashboardURL )
1418- }
1435+ dashboardURL = serverInfo .DashboardURL
1436+ customUpgradeMessage = serverInfo .UpgradeMessage
14191437}
1420- fmtWarningText := "version mismatch: client %s, server %s\n %s"
1421- fmtWarn := pretty .Sprint (cliui .DefaultStyles .Warn ,fmtWarningText )
1422- warning := fmt .Sprintf (fmtWarn ,clientVersion ,serverVersion ,upgradeMessage )
1438+ upgradeMessage := defaultUpgradeMessage (serverVersion ,dashboardURL ,customUpgradeMessage )
1439+ warning := pretty .Sprintf (
1440+ cliui .DefaultStyles .Warn ,
1441+ "version mismatch: client %s, server %s\n %s" ,
1442+ clientVersion ,
1443+ serverVersion ,
1444+ upgradeMessage ,
1445+ )
14231446
14241447_ ,_ = fmt .Fprintln (inv .Stderr ,warning )
14251448})