Files
orca/config/scripts/shared-electron-dist-cache.mjs
T
Neil df0c44571b fix(build): shared native packages get one immutable build instead of in-place rebuilds (#30226)
* refactor(build): extract the lock-free shared tree publish from the Electron dist cache

Native builds in package dirs shared across worktrees need the same
stage, validate, rename-into-place publish; one implementation keeps the
never-replace-a-good-entry and quarantine-restore rules in one place.

* fix(build): never rebuild a native addon in a package dir other worktrees load

Under pnpm's global virtual store node_modules/node-pty is one store slot
shared by every worktree, so an in-place node-gyp or @electron/rebuild run
in one worktree replaced the pty.node another was running. A shared
package dir now gets an immutable per-ABI build/orca-napi-<platform>-<arch>
compiled in a scratch copy, load-checked in a fresh process, and published
atomically; a usable entry is reused, never rebuilt. The patched node-pty
and windows-process-tree loaders try that dir before build/Release, and
packaging renames it to build/Release so packaged layouts are unchanged.
Private (non-GVS, CI) installs keep the existing in-place path.

* fix(build): find shared native packages through the project's own node_modules link

require.resolve with paths fell back to another install under Bun, so a
test fixture's private node-pty was judged shared.

* ci: run native checks when the shared native build modules change

* fix(build): pin the shared native slot and keep sibling publishes intact

Resolve the package's physical dir once so a concurrent relink cannot publish
one slot's build into another, verify the quarantined tree is the one judged
unusable before replacing it, and keep in-flight staging/quarantine trees out
of the packaged resource copy.

* fix(build): never move a sibling's fresh native entry, and refuse off-platform builds

Re-check the entry's identity right before quarantining it, and fail instead
of publishing a host-OS binary under another platform's entry name.
2026-10-18 20:21:50 -07:00

140 lines
5.1 KiB
JavaScript

import { execFileSync } from 'node:child_process'
import { randomUUID } from 'node:crypto'
import { existsSync, lstatSync, mkdirSync, readFileSync, rmSync } from 'node:fs'
import path from 'node:path'
import { makeTreeReadOnly, shareTree } from './space-sharing-copy.mjs'
import { publishSharedTree, sharedTreeStagingPath } from './shared-tree-publish.mjs'
const IDENTITY_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]*$/
// Sibling of path.txt, never inside dist: an install replaces dist wholesale.
const MARKER_FILENAME = '.orca-shared-dist'
/**
* Where sibling worktrees of one repository keep their shared extracted Electron.
*
* Null means "install normally" -- every caller treats a missing entry as "do what you did before".
*/
export function resolveSharedElectronDistEntry(options) {
const { repoRoot, version, targetPlatform, targetArch } = options
const env = options.env ?? process.env
// Packaging jobs get a fresh checkout per run, so a cache only adds a failure mode.
if (env.CI === '1' || env.CI === 'true') {
return null
}
if (![version, targetPlatform, targetArch].every((part) => IDENTITY_PATTERN.test(part ?? ''))) {
return null
}
let gitCommonDir
try {
gitCommonDir = resolveGitCommonDir(repoRoot, options.execFile ?? execFileSync)
} catch {
return null // Folder workspace, or no Git on PATH.
}
const cacheRoot = path.join(gitCommonDir, 'orca-cache', 'electron')
return {
cacheRoot,
entryPath: path.join(cacheRoot, `${version}-${targetPlatform}-${targetArch}`),
markerPath: path.join(options.electronPackageDir, MARKER_FILENAME)
}
}
export function resolveGitCommonDir(repoRoot, execFile = execFileSync) {
const rawPath = execFile('git', ['-C', repoRoot, 'rev-parse', '--git-common-dir'], {
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'ignore']
}).trim()
if (!rawPath) {
throw new Error('Git returned an empty common directory')
}
return path.resolve(repoRoot, rawPath)
}
/** True once this worktree's dist is already a clone of the current cache entry. */
export function hasAdoptedSharedElectronDist(entry) {
try {
return readFileSync(entry.markerPath, 'utf8') === path.basename(entry.entryPath)
} catch {
return false
}
}
export function recordAdoptedSharedElectronDist(entry, write) {
try {
write(entry.markerPath, path.basename(entry.entryPath))
} catch {
// The marker is only an optimization: a missing one costs one extra clone.
}
}
/**
* Share a validated cache entry into `stagePath`. Deliberately never falls back to a byte copy --
* with no storage to share the caller is better off with its normal install, which this must not
* slow down.
*/
export function shareElectronDistFromCache(entry, stagePath, options) {
const { version, platformPath } = options
if (!isUsableElectronDist(entry.entryPath, version, platformPath)) {
return false
}
try {
;(options.share ?? shareTree)(entry.entryPath, stagePath)
} catch {
return false
}
return isUsableElectronDist(stagePath, version, platformPath)
}
/**
* Publish this worktree's dist as the shared entry, best effort.
*
* No lock: staging names are unique and `rename` onto a populated directory fails with ENOTEMPTY,
* so a concurrent publisher either wins the rename or cleans up its own staging tree. Neither can
* observe a half-written entry, and a usable entry already in place is never overwritten.
*/
export function publishSharedElectronDist(distPath, entry, options = {}) {
const { version, platformPath } = options
const uuid = options.uuid ?? randomUUID
const canValidate = Boolean(version) && Boolean(platformPath)
// Without an identity to check against, "unusable" is unknowable -- never discard on a guess.
if (existsSync(entry.entryPath) && (!canValidate || isUsable(entry, version, platformPath))) {
return false
}
const stagePath = sharedTreeStagingPath(entry.entryPath, uuid)
try {
mkdirSync(entry.cacheRoot, { recursive: true })
;(options.share ?? shareTree)(distPath, stagePath)
// Before publishing, not after: an entry is visible the instant the rename lands, and under
// hardlink sharing this is the only thing standing between a stray write and every worktree.
;(options.protect ?? makeTreeReadOnly)(stagePath)
} catch {
rmSync(stagePath, { recursive: true, force: true })
return false
}
// An entry we cannot judge counts as good: another worktree got there first, so keep theirs.
return publishSharedTree(stagePath, entry.entryPath, {
isUsable: () => !canValidate || isUsable(entry, version, platformPath),
uuid,
rename: options.rename
})
}
function isUsable(entry, version, platformPath) {
return isUsableElectronDist(entry.entryPath, version, platformPath)
}
export function isUsableElectronDist(distPath, version, platformPath) {
try {
if (!lstatSync(distPath).isDirectory()) {
return false
}
const installedVersion = readFileSync(path.join(distPath, 'version'), 'utf8')
.trim()
.replace(/^v/, '')
return installedVersion === version && existsSync(path.join(distPath, platformPath))
} catch {
return false
}
}