From 9bb15ff795b7fb4a247dc13c9d4bae5aa0b894e8 Mon Sep 17 00:00:00 2001 From: Jinwoo-H Date: Thu, 27 Aug 2026 19:13:18 -0400 Subject: [PATCH] fix(release): source-build patched native runtime --- .../install-node-dependencies/action.yml | 4 +++- config/scripts/ensure-native-runtime.mjs | 17 +++++++++++------ config/scripts/ensure-native-runtime.test.mjs | 16 ++++++++++++++++ .../install-node-dependencies-action.test.mjs | 15 +++++++++++++++ 4 files changed, 45 insertions(+), 7 deletions(-) diff --git a/.github/actions/install-node-dependencies/action.yml b/.github/actions/install-node-dependencies/action.yml index 39cb1465852..39edcc6af82 100644 --- a/.github/actions/install-node-dependencies/action.yml +++ b/.github/actions/install-node-dependencies/action.yml @@ -74,7 +74,9 @@ runs: run: | pnpm install --frozen-lockfile --ignore-scripts # Job containers can run composite steps from a source mirror without .git. - git -C "$GITHUB_WORKSPACE" diff --exit-code -- package.json pnpm-lock.yaml + if git -C "$GITHUB_WORKSPACE" rev-parse --is-inside-work-tree >/dev/null 2>&1; then + git -C "$GITHUB_WORKSPACE" diff --exit-code -- package.json pnpm-lock.yaml + fi # Why cached: `--ignore-scripts` leaves node-pty without build/Release, so # ensure-native-runtime node-gyp-compiles it in every job that asks for a runtime. diff --git a/config/scripts/ensure-native-runtime.mjs b/config/scripts/ensure-native-runtime.mjs index b570ac0e7ae..cd81417e029 100644 --- a/config/scripts/ensure-native-runtime.mjs +++ b/config/scripts/ensure-native-runtime.mjs @@ -19,6 +19,7 @@ const NATIVE_MODULES = [ : []) ] const NODE_PTY_CONPTY_RUNTIME_FILES = ['conpty.dll', 'OpenConsole.exe'] +const NODE_PTY_PATCH_PATH = resolve(projectDir, 'config', 'patches', 'node-pty@1.1.0.patch') const CHILD_CHECK_FLAG = '--check-only' if (process.argv.includes(CHILD_CHECK_FLAG)) { @@ -330,18 +331,17 @@ function getPatchedNodePtyRebuildReason() { } function requiresPatchedNodePtySourceBuild() { - if (process.platform === 'win32') { - return false - } - - const nodePtyPatchPath = resolve(projectDir, 'config', 'patches', 'node-pty@1.1.0.patch') - if (!existsSync(nodePtyPatchPath)) { + if (process.platform === 'win32' || !hasNodePtyPatch()) { return false } return existsSync(resolve(projectDir, 'node_modules', 'node-pty')) } +function hasNodePtyPatch() { + return existsSync(NODE_PTY_PATCH_PATH) +} + function isNodePtyReleaseBuildDir(nativeDir) { return typeof nativeDir === 'string' && nativeDir.replace(/\\/g, '/').includes('build/Release/') } @@ -353,8 +353,13 @@ function getWindowsBuildNumber() { function runPnpm(args) { const command = process.platform === 'win32' ? 'pnpm.cmd' : 'pnpm' + const env = + args.includes('node-pty') && hasNodePtyPatch() + ? { ...process.env, npm_config_build_from_source: 'true' } + : process.env const result = spawnSync(command, args, { cwd: projectDir, + env, stdio: 'inherit', shell: process.platform === 'win32' }) diff --git a/config/scripts/ensure-native-runtime.test.mjs b/config/scripts/ensure-native-runtime.test.mjs index d1ea104b863..993e941ee72 100644 --- a/config/scripts/ensure-native-runtime.test.mjs +++ b/config/scripts/ensure-native-runtime.test.mjs @@ -29,6 +29,7 @@ describe('ensure-native-runtime', () => { const binDir = join(projectDir, 'bin') copyFileSync(sourceScriptPath, scriptPath) writeFakeNativeModules(projectDir) + writeNodePtyPatchFile(projectDir) writeFakePnpm(binDir) const result = spawnSync(process.execPath, [scriptPath, '--runtime=node'], { @@ -43,6 +44,7 @@ describe('ensure-native-runtime', () => { expect(result.status, result.stderr).toBe(0) const log = readFileSync(logPath, 'utf8') expect(log).toContain('pnpm rebuild node-pty\n') + expect(log).toContain('build-from-source true\n') expect(log.split('\n').filter((line) => line.startsWith('node-pty child '))).toEqual([ expect.stringMatching(/^node-pty child (?:conpty|pty) marker=false$/), expect.stringMatching(/^node-pty child (?:conpty|pty) marker=true$/) @@ -81,6 +83,7 @@ describe('ensure-native-runtime', () => { 'Patched node-pty build artifacts are missing; rebuilding native deps.' ) expect(readFileSync(logPath, 'utf8')).toContain('pnpm rebuild node-pty\n') + expect(readFileSync(logPath, 'utf8')).toContain('build-from-source true\n') } finally { rmSync(projectDir, { recursive: true, force: true }) } @@ -115,6 +118,7 @@ describe('ensure-native-runtime', () => { expect(result.status, result.stderr).toBe(0) expect(result.stderr).toContain("expected build/Release so Orca's node-pty patch is active") expect(readFileSync(logPath, 'utf8')).toContain('pnpm rebuild node-pty\n') + expect(readFileSync(logPath, 'utf8')).toContain('build-from-source true\n') } finally { rmSync(projectDir, { recursive: true, force: true }) } @@ -197,6 +201,14 @@ exports.loadNativeModule = function loadNativeModule(nativeName) { if (!markerExists) { throw new Error('ABI mismatch sentinel') } + return { + dir: '../build/Release/', + module: { + listJobProcessIds() {}, + terminateJob() {}, + assignCurrentProcessToJob() {} + } + } } ` ) @@ -260,6 +272,10 @@ function writeFakePnpm(binDir) { const { appendFileSync, writeFileSync } = require('node:fs') appendFileSync(process.env.ORCA_NATIVE_TEST_LOG, \`pnpm \${process.argv.slice(2).join(' ')}\\n\`) +appendFileSync( + process.env.ORCA_NATIVE_TEST_LOG, + \`build-from-source \${process.env.npm_config_build_from_source ?? 'unset'}\\n\` +) writeFileSync(process.env.ORCA_NATIVE_TEST_MARKER, 'rebuilt') ` ) diff --git a/config/scripts/install-node-dependencies-action.test.mjs b/config/scripts/install-node-dependencies-action.test.mjs index d989221c11a..6bc27de628f 100644 --- a/config/scripts/install-node-dependencies-action.test.mjs +++ b/config/scripts/install-node-dependencies-action.test.mjs @@ -47,6 +47,21 @@ function executeInstallScript(fixture) { } describe('install-node-dependencies action', () => { + it('skips the lockfile diff when a job container has no Git metadata', () => { + const fixture = createFixture() + try { + rmSync(join(fixture.workspace, '.git'), { recursive: true, force: true }) + + const result = executeInstallScript(fixture) + expect(result.status, result.stderr || result.stdout).toBe(0) + expect(`${result.stdout}\n${result.stderr}`).not.toContain( + 'diff --git a/package.json b/pnpm-lock.yaml' + ) + } finally { + rmSync(fixture.root, { recursive: true, force: true }) + } + }) + it.each([ ['package.json', '{"name":"changed"}\n'], ['pnpm-lock.yaml', 'lockfileVersion: 9\nchanged: true\n']