|
1 | 1 | // `moshcode upgrade` — update everything that has a newer version: moshcode |
2 | 2 | // itself and every installed coding engine. Conductor pattern: we just re-run |
3 | 3 | // each tool's own updater/installer (they fetch latest), never vendor them. |
| 4 | +import { fileURLToPath } from "node:url"; |
| 5 | +import path from "node:path"; |
| 6 | +import fs from "node:fs"; |
4 | 7 | import { ENGINES, engineStatus, resolveEngine, upgradeSpec, runCmd } from "./engines.mjs"; |
5 | 8 |
|
6 | 9 | // Self-upgrade re-runs the moshcode installer's `update` path. Defaults to the |
7 | 10 | // GitHub-hosted install.sh (always live); override with MOSHCODE_INSTALL_URL. |
8 | 11 | const SELF_URL = process.env.MOSHCODE_INSTALL_URL |
9 | 12 | || "https://raw.githubusercontent.com/moshcoder/moshcode/main/install.sh"; |
10 | 13 |
|
| 14 | +// Where the *running* moshcode actually lives (…/<home>/src/upgrade.mjs → <home>). |
| 15 | +// We point the installer at this so it updates THIS copy in place, not a default |
| 16 | +// path that might not be the one on your PATH. |
| 17 | +export const MOSHCODE_HOME = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); |
| 18 | + |
| 19 | +// Running from a git checkout? Then a reinstall would blow away the working tree |
| 20 | +// — update it with `git pull`, not the installer. |
| 21 | +function isGitCheckout(dir) { |
| 22 | + return fs.existsSync(path.join(dir, ".git")); |
| 23 | +} |
| 24 | + |
| 25 | +function selfVersion() { |
| 26 | + try { return JSON.parse(fs.readFileSync(path.join(MOSHCODE_HOME, "package.json"), "utf8")).version || null; } |
| 27 | + catch { return null; } |
| 28 | +} |
| 29 | + |
11 | 30 | function selfSpec() { |
12 | | - return { cmd: "sh", args: ["-c", `curl -fsSL ${SELF_URL} | sh -s -- update`] }; |
| 31 | + // Export MOSHCODE_HOME so install.sh updates the exact dir we run from. |
| 32 | + return { cmd: "sh", args: ["-c", `export MOSHCODE_HOME='${MOSHCODE_HOME}'; curl -fsSL ${SELF_URL} | sh -s -- update`] }; |
13 | 33 | } |
14 | 34 |
|
15 | 35 | /** |
@@ -83,11 +103,22 @@ export async function runUpgrade(targets = [], io = {}) { |
83 | 103 | return ok; |
84 | 104 | }; |
85 | 105 |
|
86 | | - if (self) await run("moshcode", selfSpec(), "(self)"); |
| 106 | + if (self) { |
| 107 | + if (isGitCheckout(MOSHCODE_HOME)) { |
| 108 | + // Don't reinstall over a working tree — just tell the user how to update it. |
| 109 | + log(`\n· moshcode runs from a git checkout (${MOSHCODE_HOME}) — \`git pull\` there to update it (skipping self-reinstall).`); |
| 110 | + } else { |
| 111 | + const before = selfVersion(); |
| 112 | + const ok = await run("moshcode", selfSpec(), "(self)"); |
| 113 | + const after = selfVersion(); |
| 114 | + if (ok && before && after) { |
| 115 | + log(before === after ? `· moshcode already at ${after}` : `· moshcode ${before} → ${after} — restart moshcode to load it.`); |
| 116 | + } |
| 117 | + } |
| 118 | + } |
87 | 119 | for (const it of items) await run(it.label, it.spec, it.installed ? "" : "(installing — not present)"); |
88 | 120 |
|
89 | 121 | const failed = results.filter((r) => !r.ok); |
90 | 122 | log(`\n${failed.length ? "✗" : "✓"} upgraded ${results.length - failed.length}/${results.length}${failed.length ? ` — failed: ${failed.map((r) => r.name).join(", ")}` : "."} 🤘`); |
91 | | - if (self) log("· restart moshcode to pick up its own new version."); |
92 | 123 | return results; |
93 | 124 | } |
0 commit comments