(installRoot, tempTarget, requiredVersion, successLabel)
| 497 | } |
| 498 | |
| 499 | function _installDownloadedTree(installRoot, tempTarget, requiredVersion, successLabel) { |
| 500 | var phase = 'parse'; |
| 501 | var backupRoot = null; |
| 502 | var movedEntries = []; |
| 503 | var copiedEntryNames = []; |
| 504 | var committedIndex = false; |
| 505 | var indexBackup = null; |
| 506 | var packageBackup = null; |
| 507 | try { |
| 508 | var tmpPkg = JSON.parse(fs.readFileSync(path.join(tempTarget, 'package.json'), 'utf8')); |
| 509 | if (!_isEvolverPackageName(tmpPkg && tmpPkg.name)) { |
| 510 | return _fail(FORCE_UPDATE_FAIL_CODES.DOWNLOADED_PACKAGE_NAME_MISMATCH, |
| 511 | 'downloaded package.json name="' + (tmpPkg && tmpPkg.name) + '", expected "@evomap/evolver"'); |
| 512 | } |
| 513 | if (!tmpPkg.version) { |
| 514 | return _fail(FORCE_UPDATE_FAIL_CODES.DOWNLOAD_INCOMPLETE, |
| 515 | 'downloaded package.json has no version field'); |
| 516 | } |
| 517 | if (tmpPkg.version !== requiredVersion) { |
| 518 | return _fail(FORCE_UPDATE_FAIL_CODES.DOWNLOADED_VERSION_MISMATCH, |
| 519 | 'downloaded version=' + JSON.stringify(tmpPkg.version) + ', expected ' + requiredVersion); |
| 520 | } |
| 521 | try { |
| 522 | if (!fs.statSync(path.join(tempTarget, 'index.js')).isFile()) { |
| 523 | return _fail(FORCE_UPDATE_FAIL_CODES.DOWNLOAD_INCOMPLETE, |
| 524 | 'downloaded index.js is not a file'); |
| 525 | } |
| 526 | } catch (indexReadErr) { |
| 527 | return _fail(FORCE_UPDATE_FAIL_CODES.DOWNLOAD_INCOMPLETE, |
| 528 | 'missing/unreadable index.js in downloaded tree: ' + _errStr(indexReadErr)); |
| 529 | } |
| 530 | |
| 531 | phase = 'delete'; |
| 532 | var entries = fs.readdirSync(installRoot, { withFileTypes: true }); |
| 533 | backupRoot = fs.mkdtempSync(path.join(installRoot, FORCE_UPDATE_BACKUP_PREFIX)); |
| 534 | _writeForceUpdateRecoveryJournal(backupRoot, requiredVersion, _readInstallPackageVersion(installRoot)); |
| 535 | var indexSrc = path.join(tempTarget, 'index.js'); |
| 536 | var indexDst = path.join(installRoot, 'index.js'); |
| 537 | var indexTmp = indexDst + '.' + process.pid + '.evolver-tmp'; |
| 538 | indexBackup = path.join(backupRoot, 'index.js'); |
| 539 | try { |
| 540 | phase = 'copy'; |
| 541 | _commitAtomicFileReplacement(indexSrc, indexDst, indexTmp, indexBackup); |
| 542 | committedIndex = true; |
| 543 | } catch (indexErr) { |
| 544 | _restoreFileBackupIfPresent(indexBackup, indexDst); |
| 545 | try { fs.rmSync(indexTmp, { force: true }); } catch (_) {} |
| 546 | console.warn('[ForceUpdate] index.js commit (atomic replace) failed: ' + (indexErr.message || indexErr)); |
| 547 | try { indexErr._evolverEntry = 'index.js commit'; } catch (_) {} |
| 548 | throw indexErr; |
| 549 | } |
| 550 | |
| 551 | for (var ei = 0; ei < entries.length; ei++) { |
| 552 | var eName = entries[ei].name; |
| 553 | // package.json is the install's commit marker: keep the OLD one in |
| 554 | // place through the entire delete+copy below and swap in the new one |
| 555 | // atomically at the very end. index.js was already atomically replaced |
| 556 | // after the recovery journal was written, before moving payload entries, |
no test coverage detected