|
|
@@ -1,113 +1,8 @@
|
|
|
import type { Argv } from "yargs"
|
|
|
import { UI } from "../ui"
|
|
|
import { VERSION } from "../version"
|
|
|
-import path from "path"
|
|
|
-import fs from "fs/promises"
|
|
|
-import os from "os"
|
|
|
import * as prompts from "@clack/prompts"
|
|
|
-import { Global } from "../../global"
|
|
|
-
|
|
|
-const API = "https://api.github.com/repos/sst/opencode"
|
|
|
-
|
|
|
-interface Release {
|
|
|
- tag_name: string
|
|
|
- name: string
|
|
|
- assets: Array<{
|
|
|
- name: string
|
|
|
- browser_download_url: string
|
|
|
- }>
|
|
|
-}
|
|
|
-
|
|
|
-function asset(): string {
|
|
|
- const platform = os.platform()
|
|
|
- const arch = os.arch()
|
|
|
-
|
|
|
- if (platform === "darwin") {
|
|
|
- return arch === "arm64"
|
|
|
- ? "opencode-darwin-arm64.zip"
|
|
|
- : "opencode-darwin-x64.zip"
|
|
|
- }
|
|
|
- if (platform === "linux") {
|
|
|
- return arch === "arm64"
|
|
|
- ? "opencode-linux-arm64.zip"
|
|
|
- : "opencode-linux-x64.zip"
|
|
|
- }
|
|
|
- if (platform === "win32") {
|
|
|
- return "opencode-windows-x64.zip"
|
|
|
- }
|
|
|
-
|
|
|
- throw new Error(`Unsupported platform: ${platform}-${arch}`)
|
|
|
-}
|
|
|
-
|
|
|
-function compare(current: string, latest: string): number {
|
|
|
- const a = current.replace(/^v/, "")
|
|
|
- const b = latest.replace(/^v/, "")
|
|
|
-
|
|
|
- const aParts = a.split(".").map(Number)
|
|
|
- const bParts = b.split(".").map(Number)
|
|
|
-
|
|
|
- for (let i = 0; i < Math.max(aParts.length, bParts.length); i++) {
|
|
|
- const aPart = aParts[i] || 0
|
|
|
- const bPart = bParts[i] || 0
|
|
|
-
|
|
|
- if (aPart < bPart) return -1
|
|
|
- if (aPart > bPart) return 1
|
|
|
- }
|
|
|
-
|
|
|
- return 0
|
|
|
-}
|
|
|
-
|
|
|
-async function latest(): Promise<Release> {
|
|
|
- const response = await fetch(`${API}/releases/latest`)
|
|
|
- if (!response.ok) {
|
|
|
- throw new Error(`Failed to fetch latest release: ${response.statusText}`)
|
|
|
- }
|
|
|
- return response.json()
|
|
|
-}
|
|
|
-
|
|
|
-async function specific(version: string): Promise<Release> {
|
|
|
- const tag = version.startsWith("v") ? version : `v${version}`
|
|
|
- const response = await fetch(`${API}/releases/tags/${tag}`)
|
|
|
- if (!response.ok) {
|
|
|
- throw new Error(`Failed to fetch release ${tag}: ${response.statusText}`)
|
|
|
- }
|
|
|
- return response.json()
|
|
|
-}
|
|
|
-
|
|
|
-async function download(url: string): Promise<string> {
|
|
|
- const response = await fetch(url)
|
|
|
- if (!response.ok) {
|
|
|
- throw new Error(`Failed to download: ${response.statusText}`)
|
|
|
- }
|
|
|
-
|
|
|
- const buffer = await response.arrayBuffer()
|
|
|
- const temp = path.join(Global.Path.cache, `opencode-update-${Date.now()}.zip`)
|
|
|
-
|
|
|
- await Bun.write(temp, buffer)
|
|
|
-
|
|
|
- const extractDir = path.join(
|
|
|
- Global.Path.cache,
|
|
|
- `opencode-extract-${Date.now()}`,
|
|
|
- )
|
|
|
- await fs.mkdir(extractDir, { recursive: true })
|
|
|
-
|
|
|
- const proc = Bun.spawn(["unzip", "-o", temp, "-d", extractDir], {
|
|
|
- stdout: "pipe",
|
|
|
- stderr: "pipe",
|
|
|
- })
|
|
|
-
|
|
|
- const result = await proc.exited
|
|
|
- if (result !== 0) {
|
|
|
- throw new Error("Failed to extract update")
|
|
|
- }
|
|
|
-
|
|
|
- await fs.unlink(temp)
|
|
|
-
|
|
|
- const binary = path.join(extractDir, "opencode")
|
|
|
- await fs.chmod(binary, 0o755)
|
|
|
-
|
|
|
- return binary
|
|
|
-}
|
|
|
+import { Installation } from "../../installation"
|
|
|
|
|
|
export const UpgradeCommand = {
|
|
|
command: "upgrade [target]",
|
|
|
@@ -123,14 +18,35 @@ export const UpgradeCommand = {
|
|
|
UI.println(UI.logo(" "))
|
|
|
UI.empty()
|
|
|
prompts.intro("Upgrade")
|
|
|
-
|
|
|
- if (!process.execPath.includes(path.join(".opencode", "bin")) && false) {
|
|
|
+ const method = await Installation.method()
|
|
|
+ if (method === "unknown") {
|
|
|
prompts.log.error(
|
|
|
`opencode is installed to ${process.execPath} and seems to be managed by a package manager`,
|
|
|
)
|
|
|
prompts.outro("Done")
|
|
|
return
|
|
|
}
|
|
|
+ const target = args.target ?? (await Installation.latest())
|
|
|
+ prompts.log.info(`From ${VERSION} → ${target}`)
|
|
|
+ const spinner = prompts.spinner()
|
|
|
+ spinner.start("Upgrading...")
|
|
|
+ const err = await Installation.upgrade(method, target).catch((err) => err)
|
|
|
+ if (err) {
|
|
|
+ spinner.stop("Upgrade failed")
|
|
|
+ if (err instanceof Installation.UpgradeFailedError)
|
|
|
+ prompts.log.error(err.data.stderr)
|
|
|
+ else if (err instanceof Error) prompts.log.error(err.message)
|
|
|
+ prompts.outro("Done")
|
|
|
+ return
|
|
|
+ }
|
|
|
+ spinner.stop("Upgrade complete")
|
|
|
+ prompts.outro("Done")
|
|
|
+ return
|
|
|
+
|
|
|
+ /*
|
|
|
+ if (!process.execPath.includes(path.join(".opencode", "bin")) && false) {
|
|
|
+ return
|
|
|
+ }
|
|
|
|
|
|
const release = args.target
|
|
|
? await specific(args.target).catch(() => {})
|
|
|
@@ -188,5 +104,6 @@ export const UpgradeCommand = {
|
|
|
|
|
|
prompts.log.success(`Successfully upgraded to ${target}`)
|
|
|
prompts.outro("Done")
|
|
|
+ */
|
|
|
},
|
|
|
}
|