Skip to content

Commit b482df9

Browse files
anandgupta42claude
andauthored
fix: upgrade notification silently skipped in multiple scenarios (#389)
* fix: upgrade notification silently skipped in multiple scenarios The upgrade indicator was never shown when: - `autoupdate` was `false` or `OPENCODE_DISABLE_AUTOUPDATE` was set - Install method was `"unknown"` or `"yarn"` - Auto-upgrade failed (error swallowed by `.catch(() => {})`) Additionally, there was no guard against downgrading canary/preview users to an older stable release. Changes: - Always publish `UpdateAvailable` event so the indicator and toast appear - Add `semver.gte()` guard to prevent downgrades - Add `log.warn()` for `latest()` fetch failures and auto-upgrade errors - Handle `yarn` install method explicitly (detected but not auto-upgradeable) - Add 34 tests covering every decision path in `upgrade()` - Update docs: clarify `autoupdate` config values, add upgrade indicator note Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: pre-existing typecheck errors in `training-import.test.ts` - `readFile` mock return type mismatch (string vs Buffer) - `count` mock missing `context` and `rule` keys Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: wire up `ALTIMATE_CLI_DISABLE_AUTOUPDATE` env var using `altTruthy` The documented env var `ALTIMATE_CLI_DISABLE_AUTOUPDATE` was not functional — `flag.ts` only read the legacy `OPENCODE_DISABLE_AUTOUPDATE`. Updated to use `altTruthy()` pattern matching other flags in the codebase. Found during multi-model code review (GPT 5.2 Codex, Gemini 3.1 Pro). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent e71d1fa commit b482df9

File tree

9 files changed

+395
-8
lines changed

9 files changed

+395
-8
lines changed
67.9 KB
Loading
49.1 KB
Loading

docs/docs/configure/config.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ Configuration is loaded from multiple sources, with later sources overriding ear
4444
| `default_agent` | `string` | Default agent to use on startup |
4545
| `logLevel` | `string` | Log level: `DEBUG`, `INFO`, `WARN`, `ERROR` |
4646
| `share` | `string` | Session sharing: `"manual"`, `"auto"`, `"disabled"` |
47-
| `autoupdate` | `boolean \| "notify"` | Auto-update behavior |
47+
| `autoupdate` | `boolean \| "notify"` | Auto-update behavior: `true` (default) auto-upgrades, `"notify"` shows an indicator without upgrading, `false` disables auto-upgrade but still shows the update indicator |
4848
| `provider` | `object` | Provider configurations (see [Providers](providers.md)) |
4949
| `mcp` | `object` | MCP server configurations (see [MCP Servers](mcp-servers.md)) |
5050
| `formatter` | `object \| false` | Formatter settings (see [Formatters](formatters.md)) |

docs/docs/reference/troubleshooting.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,14 +104,23 @@ Disable auto-update if it causes problems:
104104
export ALTIMATE_CLI_DISABLE_AUTOUPDATE=true
105105
```
106106

107-
Or set to notification only:
107+
Or set to notification only in your config:
108108

109109
```json
110110
{
111111
"autoupdate": "notify"
112112
}
113113
```
114114

115+
Both options still show an upgrade indicator in the footer when a new version is available. To upgrade manually, run:
116+
117+
```bash
118+
altimate upgrade
119+
```
120+
121+
!!! note
122+
When an update is available, you'll see `↑ <version> update available · altimate upgrade` in the bottom-right corner of the TUI.
123+
115124
### Context Too Large
116125

117126
If conversations hit context limits:

docs/docs/usage/cli.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ Configuration can be controlled via environment variables:
6868

6969
| Variable | Description |
7070
| -------------------------------------- | ------------------------------------ |
71-
| `ALTIMATE_CLI_DISABLE_AUTOUPDATE` | Disable automatic updates |
71+
| `ALTIMATE_CLI_DISABLE_AUTOUPDATE` | Disable automatic updates (still shows upgrade indicator) |
7272
| `ALTIMATE_CLI_DISABLE_LSP_DOWNLOAD` | Don't auto-download LSP servers |
7373
| `ALTIMATE_CLI_DISABLE_AUTOCOMPACT` | Disable automatic context compaction |
7474
| `ALTIMATE_CLI_DISABLE_DEFAULT_PLUGINS` | Skip loading default plugins |

packages/opencode/src/cli/upgrade.ts

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,55 @@ import { Bus } from "@/bus"
22
import { Config } from "@/config/config"
33
import { Flag } from "@/flag/flag"
44
import { Installation } from "@/installation"
5+
// altimate_change start — robust upgrade notification
6+
import semver from "semver"
7+
import { Log } from "@/util/log"
8+
9+
const log = Log.create({ service: "upgrade" })
510

611
export async function upgrade() {
712
const config = await Config.global()
813
const method = await Installation.method()
9-
const latest = await Installation.latest(method).catch(() => {})
14+
const latest = await Installation.latest(method).catch((err) => {
15+
log.warn("failed to fetch latest version", { error: String(err), method })
16+
return undefined
17+
})
1018
if (!latest) return
1119
if (Installation.VERSION === latest) return
1220

21+
// Prevent downgrade: if current version is already >= latest, skip
22+
if (
23+
Installation.VERSION !== "local" &&
24+
semver.valid(Installation.VERSION) &&
25+
semver.valid(latest) &&
26+
semver.gte(Installation.VERSION, latest)
27+
) {
28+
return
29+
}
30+
31+
const notify = () => Bus.publish(Installation.Event.UpdateAvailable, { version: latest })
32+
33+
// Always notify when update is available, regardless of autoupdate setting
1334
if (config.autoupdate === false || Flag.OPENCODE_DISABLE_AUTOUPDATE) {
35+
await notify()
1436
return
1537
}
1638
if (config.autoupdate === "notify") {
17-
await Bus.publish(Installation.Event.UpdateAvailable, { version: latest })
39+
await notify()
40+
return
41+
}
42+
43+
// Can't auto-upgrade for unknown or unsupported methods — notify instead
44+
if (method === "unknown" || method === "yarn") {
45+
await notify()
1846
return
1947
}
2048

21-
if (method === "unknown") return
2249
await Installation.upgrade(method, latest)
2350
.then(() => Bus.publish(Installation.Event.Updated, { version: latest }))
24-
.catch(() => {})
51+
.catch(async (err) => {
52+
log.warn("auto-upgrade failed, notifying instead", { error: String(err), method, target: latest })
53+
await notify()
54+
})
2555
}
56+
// altimate_change end

packages/opencode/src/flag/flag.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ export namespace Flag {
2828
export declare const OPENCODE_TUI_CONFIG: string | undefined
2929
export declare const OPENCODE_CONFIG_DIR: string | undefined
3030
export const OPENCODE_CONFIG_CONTENT = process.env["OPENCODE_CONFIG_CONTENT"]
31-
export const OPENCODE_DISABLE_AUTOUPDATE = truthy("OPENCODE_DISABLE_AUTOUPDATE")
31+
// altimate_change start — support ALTIMATE_CLI_DISABLE_AUTOUPDATE env var (documented name)
32+
export const OPENCODE_DISABLE_AUTOUPDATE = altTruthy("ALTIMATE_CLI_DISABLE_AUTOUPDATE", "OPENCODE_DISABLE_AUTOUPDATE")
33+
// altimate_change end
3234
export const OPENCODE_DISABLE_PRUNE = truthy("OPENCODE_DISABLE_PRUNE")
3335
// altimate_change start - global opt-out for Altimate Memory
3436
export const ALTIMATE_DISABLE_MEMORY = altTruthy("ALTIMATE_DISABLE_MEMORY", "OPENCODE_DISABLE_MEMORY")

packages/opencode/test/altimate/training-import.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ function setupMocks(opts: {
5656
context: opts.currentCount ?? 0,
5757
rule: opts.currentCount ?? 0,
5858
pattern: opts.currentCount ?? 0,
59+
context: opts.currentCount ?? 0,
60+
rule: opts.currentCount ?? 0,
5961
}))
6062
saveSpy = spyOn(TrainingStore, "save").mockImplementation(async () => {
6163
if (opts.saveShouldFail) throw new Error("store write failed")

0 commit comments

Comments
 (0)