Skip to content

Commit 19777d8

Browse files
replace auto-update with fork-based git pull + rebuild (#43)
Replace the upstream npm/brew/choco/scoop update mechanism with a git-based flow that checks jairad26/opencode GitHub releases and upgrades via git pull + bun install + bun run build.
1 parent 7e65272 commit 19777d8

5 files changed

Lines changed: 69 additions & 359 deletions

File tree

packages/opencode/src/cli/cmd/tui/app.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -896,7 +896,7 @@ function App(props: { onSnapshot?: () => Promise<string[]> }) {
896896
const choice = await DialogConfirm.show(
897897
dialog,
898898
`Update Available`,
899-
`A new release v${version} is available. Would you like to update now?`,
899+
`A new fork release v${version} is available. Would you like to pull and rebuild now?`,
900900
"skip",
901901
)
902902

@@ -928,7 +928,7 @@ function App(props: { onSnapshot?: () => Promise<string[]> }) {
928928
await DialogAlert.show(
929929
dialog,
930930
"Update Complete",
931-
`Successfully updated to OpenCode v${result.data.version}. Please restart the application.`,
931+
`Successfully pulled and rebuilt v${result.data.version}. Please restart the application.`,
932932
)
933933

934934
exit()

packages/opencode/src/cli/cmd/uninstall.ts

Lines changed: 4 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -87,21 +87,18 @@ export const UninstallCommand = {
8787
},
8888
}
8989

90-
async function collectRemovalTargets(args: UninstallArgs, method: Installation.Method): Promise<RemovalTargets> {
90+
async function collectRemovalTargets(args: UninstallArgs, _method: Installation.Method): Promise<RemovalTargets> {
9191
const directories: RemovalTargets["directories"] = [
9292
{ path: Global.Path.data, label: "Data", keep: args.keepData },
9393
{ path: Global.Path.cache, label: "Cache", keep: false },
9494
{ path: Global.Path.config, label: "Config", keep: args.keepConfig },
9595
{ path: Global.Path.state, label: "State", keep: false },
9696
]
9797

98-
const shellConfig = method === "curl" ? await getShellConfigFile() : null
99-
const binary = method === "curl" ? process.execPath : null
100-
101-
return { directories, shellConfig, binary }
98+
return { directories, shellConfig: null, binary: null }
10299
}
103100

104-
async function showRemovalSummary(targets: RemovalTargets, method: Installation.Method) {
101+
async function showRemovalSummary(targets: RemovalTargets, _method: Installation.Method) {
105102
prompts.log.message("The following will be removed:")
106103

107104
for (const dir of targets.directories) {
@@ -126,22 +123,9 @@ async function showRemovalSummary(targets: RemovalTargets, method: Installation.
126123
if (targets.shellConfig) {
127124
prompts.log.info(` ✓ Shell PATH in ${shortenPath(targets.shellConfig)}`)
128125
}
129-
130-
if (method !== "curl" && method !== "unknown") {
131-
const cmds: Record<string, string> = {
132-
npm: "npm uninstall -g opencode-ai",
133-
pnpm: "pnpm uninstall -g opencode-ai",
134-
bun: "bun remove -g opencode-ai",
135-
yarn: "yarn global remove opencode-ai",
136-
brew: "brew uninstall opencode",
137-
choco: "choco uninstall opencode",
138-
scoop: "scoop uninstall opencode",
139-
}
140-
prompts.log.info(` ✓ Package: ${cmds[method] || method}`)
141-
}
142126
}
143127

144-
async function executeUninstall(method: Installation.Method, targets: RemovalTargets) {
128+
async function executeUninstall(_method: Installation.Method, targets: RemovalTargets) {
145129
const spinner = prompts.spinner()
146130
const errors: string[] = []
147131

@@ -178,48 +162,6 @@ async function executeUninstall(method: Installation.Method, targets: RemovalTar
178162
}
179163
}
180164

181-
if (method !== "curl" && method !== "unknown") {
182-
const cmds: Record<string, string[]> = {
183-
npm: ["npm", "uninstall", "-g", "opencode-ai"],
184-
pnpm: ["pnpm", "uninstall", "-g", "opencode-ai"],
185-
bun: ["bun", "remove", "-g", "opencode-ai"],
186-
yarn: ["yarn", "global", "remove", "opencode-ai"],
187-
brew: ["brew", "uninstall", "opencode"],
188-
choco: ["choco", "uninstall", "opencode"],
189-
scoop: ["scoop", "uninstall", "opencode"],
190-
}
191-
192-
const cmd = cmds[method]
193-
if (cmd) {
194-
spinner.start(`Running ${cmd.join(" ")}...`)
195-
const result = await Process.run(method === "choco" ? ["choco", "uninstall", "opencode", "-y", "-r"] : cmd, {
196-
nothrow: true,
197-
})
198-
if (result.code !== 0) {
199-
spinner.stop(`Package manager uninstall failed: exit code ${result.code}`, 1)
200-
const text = `${result.stdout.toString("utf8")}\n${result.stderr.toString("utf8")}`
201-
if (method === "choco" && text.includes("not running from an elevated command shell")) {
202-
prompts.log.warn(`You may need to run '${cmd.join(" ")}' from an elevated command shell`)
203-
} else {
204-
prompts.log.warn(`You may need to run manually: ${cmd.join(" ")}`)
205-
}
206-
} else {
207-
spinner.stop("Package removed")
208-
}
209-
}
210-
}
211-
212-
if (method === "curl" && targets.binary) {
213-
UI.empty()
214-
prompts.log.message("To finish removing the binary, run:")
215-
prompts.log.info(` rm "${targets.binary}"`)
216-
217-
const binDir = path.dirname(targets.binary)
218-
if (binDir.includes(".opencode")) {
219-
prompts.log.info(` rmdir "${binDir}" 2>/dev/null`)
220-
}
221-
}
222-
223165
if (errors.length > 0) {
224166
UI.empty()
225167
prompts.log.warn("Some operations failed:")

packages/opencode/src/cli/cmd/upgrade.ts

Lines changed: 14 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -5,43 +5,25 @@ import { Installation } from "../../installation"
55

66
export const UpgradeCommand = {
77
command: "upgrade [target]",
8-
describe: "upgrade opencode to the latest or a specific version",
8+
describe: "pull latest from fork and rebuild",
99
builder: (yargs: Argv) => {
10-
return yargs
11-
.positional("target", {
12-
describe: "version to upgrade to, for ex '0.1.48' or 'v0.1.48'",
13-
type: "string",
14-
})
15-
.option("method", {
16-
alias: "m",
17-
describe: "installation method to use",
18-
type: "string",
19-
choices: ["curl", "npm", "pnpm", "bun", "brew", "choco", "scoop"],
20-
})
10+
return yargs.positional("target", {
11+
describe: "version to upgrade to, for ex '0.1.48' or 'v0.1.48'",
12+
type: "string",
13+
})
2114
},
22-
handler: async (args: { target?: string; method?: string }) => {
15+
handler: async (args: { target?: string }) => {
2316
UI.empty()
2417
UI.println(UI.logo(" "))
2518
UI.empty()
2619
prompts.intro("Upgrade")
27-
const detectedMethod = await Installation.method()
28-
const method = (args.method as Installation.Method) ?? detectedMethod
20+
const method = await Installation.method()
2921
if (method === "unknown") {
30-
prompts.log.error(`opencode is installed to ${process.execPath} and may be managed by a package manager`)
31-
const install = await prompts.select({
32-
message: "Install anyways?",
33-
options: [
34-
{ label: "Yes", value: true },
35-
{ label: "No", value: false },
36-
],
37-
initialValue: false,
38-
})
39-
if (!install) {
40-
prompts.outro("Done")
41-
return
42-
}
22+
prompts.log.error("Could not detect git repo — upgrade requires a cloned fork")
23+
prompts.outro("Done")
24+
return
4325
}
44-
prompts.log.info("Using method: " + method)
26+
prompts.log.info("Using method: git (pull + rebuild)")
4527
const target = args.target ? args.target.replace(/^v/, "") : await Installation.latest()
4628

4729
if (Installation.VERSION === target) {
@@ -52,18 +34,12 @@ export const UpgradeCommand = {
5234

5335
prompts.log.info(`From ${Installation.VERSION}${target}`)
5436
const spinner = prompts.spinner()
55-
spinner.start("Upgrading...")
37+
spinner.start("Pulling and rebuilding...")
5638
const err = await Installation.upgrade(method, target).catch((err) => err)
5739
if (err) {
5840
spinner.stop("Upgrade failed", 1)
59-
if (err instanceof Installation.UpgradeFailedError) {
60-
// necessary because choco only allows install/upgrade in elevated terminals
61-
if (method === "choco" && err.stderr.includes("not running from an elevated command shell")) {
62-
prompts.log.error("Please run the terminal as Administrator and try again")
63-
} else {
64-
prompts.log.error(err.stderr)
65-
}
66-
} else if (err instanceof Error) prompts.log.error(err.message)
41+
if (err instanceof Installation.UpgradeFailedError) prompts.log.error(err.stderr)
42+
else if (err instanceof Error) prompts.log.error(err.message)
6743
prompts.outro("Done")
6844
return
6945
}

0 commit comments

Comments
 (0)