|
1 | 1 | import { execSync } from "child_process"; |
2 | | -import { createHash } from "crypto"; |
3 | | -import { chmod, rename, rm, writeFile } from "fs/promises"; |
4 | | -import { basename } from "path"; |
| 2 | +import { rm, writeFile } from "fs/promises"; |
| 3 | +import { tmpdir } from "os"; |
| 4 | +import { join } from "path"; |
5 | 5 |
|
6 | 6 | import chalk from "chalk"; |
7 | 7 | import ky from "ky"; |
@@ -77,7 +77,7 @@ export class UpdateCommand { |
77 | 77 | stdio: "inherit", |
78 | 78 | }); |
79 | 79 | } else { |
80 | | - await this.updateBinary(latestVersion); |
| 80 | + await this.updateBinary(); |
81 | 81 | } |
82 | 82 |
|
83 | 83 | if (Output.isJson()) { |
@@ -150,65 +150,17 @@ export class UpdateCommand { |
150 | 150 | return `npm install -g @jup-ag/cli@${version}`; |
151 | 151 | } |
152 | 152 |
|
153 | | - private static async updateBinary(version: string): Promise<void> { |
154 | | - const binaryPath = process.execPath; |
155 | | - const execName = basename(binaryPath); |
156 | | - |
157 | | - if (execName === "bun" || execName === "node") { |
158 | | - throw new Error( |
159 | | - "Cannot self-update: process.execPath points to the runtime, not the jup binary" |
160 | | - ); |
161 | | - } |
162 | | - |
163 | | - const assetName = `jup-${process.platform}-${process.arch}`; |
164 | | - const baseUrl = `https://github.com/jup-ag/cli/releases/download/v${version}`; |
165 | | - |
166 | | - // Fetch checksums first to validate platform support before downloading |
167 | | - const checksums = await ky.get(`${baseUrl}/checksums.txt`).text(); |
168 | | - const checksumLine = checksums |
169 | | - .split("\n") |
170 | | - .map((line) => line.trim().split(/\s+/)) |
171 | | - .find((parts) => parts.length === 2 && parts[1] === assetName); |
172 | | - |
173 | | - if (!checksumLine) { |
174 | | - const supported = checksums |
175 | | - .split("\n") |
176 | | - .map((line) => line.trim().split(/\s+/)) |
177 | | - .filter((parts) => parts.length === 2) |
178 | | - .map((parts) => parts[1]!.replace("jup-", "")) |
179 | | - .join(", "); |
180 | | - throw new Error( |
181 | | - `Unsupported platform: ${process.platform}-${process.arch}. ` + |
182 | | - `Supported: ${supported}` |
183 | | - ); |
184 | | - } |
185 | | - |
186 | | - const binary = await ky.get(`${baseUrl}/${assetName}`).arrayBuffer(); |
187 | | - const buf = Buffer.from(binary); |
188 | | - |
189 | | - const expectedHash = checksumLine[0]; |
190 | | - const actualHash = createHash("sha256").update(buf).digest("hex"); |
191 | | - |
192 | | - if (actualHash !== expectedHash) { |
193 | | - throw new Error( |
194 | | - `Checksum mismatch for ${assetName}: expected ${expectedHash}, got ${actualHash}` |
195 | | - ); |
196 | | - } |
197 | | - |
198 | | - const tmpPath = `${binaryPath}.tmp`; |
| 153 | + private static async updateBinary(): Promise<void> { |
| 154 | + const scriptUrl = |
| 155 | + "https://github.com/jup-ag/cli/releases/latest/download/install.sh"; |
| 156 | + const scriptPath = join(tmpdir(), "jup-install.sh"); |
199 | 157 |
|
200 | 158 | try { |
201 | | - await writeFile(tmpPath, buf); |
202 | | - await chmod(tmpPath, 0o755); |
203 | | - await rename(tmpPath, binaryPath); |
204 | | - } catch (err: unknown) { |
205 | | - await rm(tmpPath, { force: true }).catch(() => {}); |
206 | | - if (err instanceof Error && "code" in err && err.code === "EACCES") { |
207 | | - throw new Error( |
208 | | - "Permission denied. Try running with sudo: sudo jup update" |
209 | | - ); |
210 | | - } |
211 | | - throw err; |
| 159 | + const script = await ky.get(scriptUrl).text(); |
| 160 | + await writeFile(scriptPath, script); |
| 161 | + execSync(`bash ${scriptPath}`, { stdio: "inherit" }); |
| 162 | + } finally { |
| 163 | + await rm(scriptPath, { force: true }).catch(() => {}); |
212 | 164 | } |
213 | 165 | } |
214 | 166 | } |
0 commit comments