( stagedBinaryPath: string, installPath: string, )
| 298 | } |
| 299 | |
| 300 | async function atomicMoveToInstallPath( |
| 301 | stagedBinaryPath: string, |
| 302 | installPath: string, |
| 303 | ) { |
| 304 | // Create installation directory if it doesn't exist |
| 305 | await mkdir(dirname(installPath), { recursive: true }) |
| 306 | |
| 307 | // Move from staging to final location atomically |
| 308 | const tempInstallPath = `${installPath}.tmp.${process.pid}.${Date.now()}` |
| 309 | |
| 310 | try { |
| 311 | // Copy to temp next to install path, then rename. A direct rename from staging |
| 312 | // would fail with EXDEV if staging and install are on different filesystems. |
| 313 | await copyFile(stagedBinaryPath, tempInstallPath) |
| 314 | await chmod(tempInstallPath, 0o755) |
| 315 | await rename(tempInstallPath, installPath) |
| 316 | logForDebugging(`Atomically installed binary to ${installPath}`) |
| 317 | } catch (error) { |
| 318 | // Clean up temp file if it exists |
| 319 | try { |
| 320 | await unlink(tempInstallPath) |
| 321 | } catch { |
| 322 | // Ignore cleanup errors |
| 323 | } |
| 324 | throw error |
| 325 | } |
| 326 | } |
| 327 | |
| 328 | async function installVersionFromPackage( |
| 329 | stagingPath: string, |
no test coverage detected