fix(cli/postinstall): rerun bootstrap when scripts skipped (#369)

This commit is contained in:
James Peter
2025-10-31 07:54:59 +10:00
parent 427c24ae24
commit 8d842b8638
3 changed files with 62 additions and 13 deletions
+21 -6
View File
@@ -6,6 +6,7 @@ import { fileURLToPath } from "url";
import { platform as nodePlatform, arch as nodeArch } from "os";
import { execSync } from "child_process";
import { get as httpsGet } from "https";
import { runPostinstall } from "../postinstall.js";
// __dirname equivalent in ESM
const __filename = fileURLToPath(import.meta.url);
@@ -270,12 +271,26 @@ const tryBootstrapBinary = async () => {
};
// If missing, attempt to bootstrap into place (helps when Bun blocks postinstall)
if (!existsSync(binaryPath) && !existsSync(legacyBinaryPath)) {
const ok = await tryBootstrapBinary();
if (!ok) {
// retry legacy name in case archive provided coder-*
if (existsSync(legacyBinaryPath) && !existsSync(binaryPath)) {
binaryPath = legacyBinaryPath;
let binaryReady = existsSync(binaryPath) || existsSync(legacyBinaryPath);
if (!binaryReady) {
let runtimePostinstallError = null;
try {
await runPostinstall({ invokedByRuntime: true, skipGlobalAlias: true });
} catch (err) {
runtimePostinstallError = err;
}
binaryReady = existsSync(binaryPath) || existsSync(legacyBinaryPath);
if (!binaryReady) {
const ok = await tryBootstrapBinary();
if (!ok) {
if (runtimePostinstallError && !lastBootstrapError) {
lastBootstrapError = runtimePostinstallError;
}
// retry legacy name in case archive provided coder-*
if (existsSync(legacyBinaryPath) && !existsSync(binaryPath)) {
binaryPath = legacyBinaryPath;
}
}
}
}
+27 -7
View File
@@ -2,7 +2,7 @@
// Non-functional change to trigger release workflow
import { existsSync, mkdirSync, createWriteStream, chmodSync, readFileSync, readSync, writeFileSync, unlinkSync, statSync, openSync, closeSync, copyFileSync, fsyncSync, renameSync, realpathSync } from 'fs';
import { join, dirname } from 'path';
import { join, dirname, resolve } from 'path';
import { fileURLToPath } from 'url';
import { get } from 'https';
import { platform, arch, tmpdir } from 'os';
@@ -288,13 +288,21 @@ function validateDownloadedBinary(p) {
}
}
async function main() {
export async function runPostinstall(options = {}) {
const { skipGlobalAlias = false, invokedByRuntime = false } = options;
if (process.env.CODE_POSTINSTALL_DRY_RUN === '1') {
return { skipped: true };
}
if (invokedByRuntime) {
process.env.CODE_RUNTIME_POSTINSTALL = process.env.CODE_RUNTIME_POSTINSTALL || '1';
}
// Detect potential PATH conflict with an existing `code` command (e.g., VS Code)
// Only relevant for global installs; skip for npx/local installs to keep postinstall fast.
const ua = process.env.npm_config_user_agent || '';
const isNpx = ua.includes('npx');
const isGlobal = process.env.npm_config_global === 'true';
if (isGlobal && !isNpx) {
if (!skipGlobalAlias && isGlobal && !isNpx) {
try {
const whichCmd = process.platform === 'win32' ? 'where code' : 'command -v code || which code || true';
const resolved = execSync(whichCmd, { stdio: ['ignore', 'pipe', 'ignore'], shell: process.platform !== 'win32' }).toString().split(/\r?\n/).filter(Boolean)[0];
@@ -760,7 +768,19 @@ async function main() {
}
}
main().catch(error => {
console.error('Installation failed:', error);
process.exit(1);
});
function isExecutedDirectly() {
const entry = process.argv[1];
if (!entry) return false;
try {
return resolve(entry) === fileURLToPath(import.meta.url);
} catch {
return false;
}
}
if (isExecutedDirectly()) {
runPostinstall().catch(error => {
console.error('Installation failed:', error);
process.exit(1);
});
}
+14
View File
@@ -0,0 +1,14 @@
import { test } from 'node:test';
import assert from 'node:assert/strict';
test('runPostinstall resolves in dry-run mode', async () => {
const { runPostinstall } = await import('../postinstall.js');
process.env.CODE_POSTINSTALL_DRY_RUN = '1';
try {
const result = await runPostinstall({ invokedByRuntime: true, skipGlobalAlias: true });
assert.ok(result && result.skipped === true);
} finally {
delete process.env.CODE_POSTINSTALL_DRY_RUN;
delete process.env.CODE_RUNTIME_POSTINSTALL;
}
});