mirror of
https://github.com/alibaba/open-code-review.git
synced 2026-07-09 17:28:58 +00:00
feat: switch auto-update to npm i -g and show update hints on failure
Some checks are pending
CI / test (push) Waiting to run
Some checks are pending
CI / test (push) Waiting to run
Replace GitHub releases download with `npm i -g` so platform package installations also get auto-updates. On permission failure, write a hint file that bin/ocr.js reads to prompt the user to update manually.
This commit is contained in:
parent
ca3b03dfc9
commit
c69108656b
2 changed files with 49 additions and 100 deletions
11
bin/ocr.js
11
bin/ocr.js
|
|
@ -17,6 +17,17 @@ if (!resolved) {
|
||||||
}
|
}
|
||||||
const binaryPath = resolved.path;
|
const binaryPath = resolved.path;
|
||||||
|
|
||||||
|
const hintFile = path.join(os.homedir(), ".opencodereview", "update-available");
|
||||||
|
try {
|
||||||
|
const hint = JSON.parse(fs.readFileSync(hintFile, "utf8"));
|
||||||
|
if (hint.version && hint.pkg) {
|
||||||
|
console.error(
|
||||||
|
`\x1b[33m[ocr] A new version (v${hint.version}) is available. Run to update:\x1b[0m\n` +
|
||||||
|
`\x1b[33m npm i -g ${hint.pkg}@${hint.version}\x1b[0m\n`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (_) {}
|
||||||
|
|
||||||
if (!process.env.OCR_NO_UPDATE) {
|
if (!process.env.OCR_NO_UPDATE) {
|
||||||
const stateDir = path.join(os.homedir(), ".opencodereview");
|
const stateDir = path.join(os.homedir(), ".opencodereview");
|
||||||
const tsFile = path.join(stateDir, "last-update-check");
|
const tsFile = path.join(stateDir, "last-update-check");
|
||||||
|
|
|
||||||
|
|
@ -7,22 +7,13 @@ const os = require("os");
|
||||||
const https = require("https");
|
const https = require("https");
|
||||||
const { spawnSync } = require("child_process");
|
const { spawnSync } = require("child_process");
|
||||||
|
|
||||||
const {
|
const { resolveNativeBinary } = require("./platform");
|
||||||
IS_WINDOWS,
|
const { loadPackageJson } = require("./install.js");
|
||||||
BINARY_NAME,
|
|
||||||
detectPlatform,
|
|
||||||
loadPackageJson,
|
|
||||||
buildUrl,
|
|
||||||
downloadText,
|
|
||||||
downloadBinary,
|
|
||||||
computeChecksum,
|
|
||||||
} = require("./install.js");
|
|
||||||
const packageRoot = path.join(__dirname, "..");
|
|
||||||
const binDir = path.join(packageRoot, "bin");
|
|
||||||
const binaryPath = path.join(binDir, BINARY_NAME);
|
|
||||||
const stateDir = path.join(os.homedir(), ".opencodereview");
|
const stateDir = path.join(os.homedir(), ".opencodereview");
|
||||||
const tsFile = path.join(stateDir, "last-update-check");
|
const tsFile = path.join(stateDir, "last-update-check");
|
||||||
const lockFile = path.join(stateDir, "update.lock");
|
const lockFile = path.join(stateDir, "update.lock");
|
||||||
|
const hintFile = path.join(stateDir, "update-available");
|
||||||
|
|
||||||
const DEFAULT_REGISTRY = "https://registry.npmjs.org";
|
const DEFAULT_REGISTRY = "https://registry.npmjs.org";
|
||||||
|
|
||||||
|
|
@ -65,9 +56,9 @@ function releaseLock() {
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getInstalledVersion() {
|
function getInstalledVersion(binPath) {
|
||||||
try {
|
try {
|
||||||
const result = spawnSync(binaryPath, ["version"], {
|
const result = spawnSync(binPath, ["version"], {
|
||||||
encoding: "utf8",
|
encoding: "utf8",
|
||||||
timeout: 3000,
|
timeout: 3000,
|
||||||
});
|
});
|
||||||
|
|
@ -118,24 +109,30 @@ function fetchLatestVersion(pkg) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const SEMVER_RE = /^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/;
|
||||||
|
|
||||||
function semverGt(a, b) {
|
function semverGt(a, b) {
|
||||||
const pa = a.split(".").map(Number);
|
const pa = a.replace(/-.*$/, "").split(".").map(Number);
|
||||||
const pb = b.split(".").map(Number);
|
const pb = b.replace(/-.*$/, "").split(".").map(Number);
|
||||||
for (let i = 0; i < 3; i++) {
|
for (let i = 0; i < 3; i++) {
|
||||||
if ((pa[i] || 0) > (pb[i] || 0)) return true;
|
if ((pa[i] || 0) > (pb[i] || 0)) return true;
|
||||||
if ((pa[i] || 0) < (pb[i] || 0)) return false;
|
if ((pa[i] || 0) < (pb[i] || 0)) return false;
|
||||||
}
|
}
|
||||||
|
const aPre = a.includes("-");
|
||||||
|
const bPre = b.includes("-");
|
||||||
|
if (bPre && !aPre) return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function cleanupTemp() {
|
function writeHint(latestVersion, pkgName) {
|
||||||
try {
|
try {
|
||||||
const files = fs.readdirSync(binDir);
|
fs.writeFileSync(hintFile, JSON.stringify({ version: latestVersion, pkg: pkgName }));
|
||||||
for (const f of files) {
|
} catch (_) {}
|
||||||
if (f.startsWith(".opencodereview.tmp.")) {
|
}
|
||||||
fs.unlinkSync(path.join(binDir, f));
|
|
||||||
}
|
function removeHint() {
|
||||||
}
|
try {
|
||||||
|
fs.unlinkSync(hintFile);
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -144,97 +141,38 @@ async function main() {
|
||||||
|
|
||||||
if (!acquireLock()) return;
|
if (!acquireLock()) return;
|
||||||
|
|
||||||
cleanupTemp();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { resolveNativeBinary } = require("./platform");
|
|
||||||
const resolved = resolveNativeBinary();
|
const resolved = resolveNativeBinary();
|
||||||
if (resolved && resolved.fromPlatformPkg) {
|
if (!resolved) return;
|
||||||
info("Binary managed by platform package, skipping auto-update.");
|
|
||||||
return;
|
const installedVersion = getInstalledVersion(resolved.path);
|
||||||
}
|
|
||||||
const installedVersion = getInstalledVersion();
|
|
||||||
if (!installedVersion) return;
|
if (!installedVersion) return;
|
||||||
|
|
||||||
const pkg = loadPackageJson();
|
const pkg = loadPackageJson();
|
||||||
const latestVersion = await fetchLatestVersion(pkg);
|
const latestVersion = await fetchLatestVersion(pkg);
|
||||||
if (!latestVersion) return;
|
if (!latestVersion) return;
|
||||||
|
|
||||||
if (!semverGt(latestVersion, installedVersion)) return;
|
if (!SEMVER_RE.test(latestVersion)) return;
|
||||||
|
|
||||||
const { os: platform, arch } = detectPlatform();
|
if (!semverGt(latestVersion, installedVersion)) {
|
||||||
const config = pkg.ocrConfig;
|
removeHint();
|
||||||
|
return;
|
||||||
const vars = { version: latestVersion, os: platform, arch };
|
|
||||||
let downloadUrl = buildUrl(config.urlPattern, vars);
|
|
||||||
if (IS_WINDOWS) {
|
|
||||||
downloadUrl += ".exe";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const tempPath = path.join(binDir, `.opencodereview.tmp.${process.pid}`);
|
const pkgName = pkg.name;
|
||||||
await downloadBinary(downloadUrl, tempPath);
|
const IS_WINDOWS = process.platform === "win32";
|
||||||
if (!IS_WINDOWS) {
|
const result = spawnSync("npm", ["i", "-g", `${pkgName}@${latestVersion}`], {
|
||||||
fs.chmodSync(tempPath, 0o755);
|
encoding: "utf8",
|
||||||
}
|
timeout: 120000,
|
||||||
|
shell: IS_WINDOWS,
|
||||||
|
});
|
||||||
|
|
||||||
if (config.checksumPattern) {
|
if (result.status === 0) {
|
||||||
const checksumUrl = buildUrl(config.checksumPattern, vars);
|
removeHint();
|
||||||
let shaContent;
|
|
||||||
try {
|
|
||||||
shaContent = await downloadText(checksumUrl);
|
|
||||||
} catch (_) {
|
|
||||||
fs.unlinkSync(tempPath);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let actualSha;
|
|
||||||
try {
|
|
||||||
actualSha = await computeChecksum(tempPath);
|
|
||||||
} catch (_) {
|
|
||||||
fs.unlinkSync(tempPath);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let verified = false;
|
|
||||||
for (const line of shaContent.split("\n")) {
|
|
||||||
const trimmed = line.trim();
|
|
||||||
if (trimmed.includes(`-${platform}-${arch}`)) {
|
|
||||||
const expectedSha = trimmed.split(/\s+/)[0].toLowerCase();
|
|
||||||
if (expectedSha && actualSha !== expectedSha) {
|
|
||||||
fs.unlinkSync(tempPath);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
verified = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!verified) {
|
|
||||||
fs.unlinkSync(tempPath);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IS_WINDOWS) {
|
|
||||||
const oldPath = binaryPath + ".old";
|
|
||||||
try { fs.unlinkSync(oldPath); } catch (_) {}
|
|
||||||
try {
|
|
||||||
fs.renameSync(binaryPath, oldPath);
|
|
||||||
} catch (e) {
|
|
||||||
if (fs.existsSync(binaryPath)) {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
fs.renameSync(tempPath, binaryPath);
|
|
||||||
} catch (e) {
|
|
||||||
try { fs.renameSync(oldPath, binaryPath); } catch (_) {}
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
try { fs.unlinkSync(oldPath); } catch (_) {}
|
|
||||||
} else {
|
} else {
|
||||||
fs.renameSync(tempPath, binaryPath);
|
writeHint(latestVersion, pkgName);
|
||||||
}
|
}
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
cleanupTemp();
|
|
||||||
} finally {
|
} finally {
|
||||||
releaseLock();
|
releaseLock();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue