()
| 98 | // Download + cache the platform bundle from GitHub Releases. Returns |
| 99 | // {command, args}; exits the process with guidance if it can't. |
| 100 | async function selfHealBundle() { |
| 101 | var version = readVersion(); |
| 102 | var bundlesDir = path.join(process.env.CODEGRAPH_INSTALL_DIR || path.join(os.homedir(), '.codegraph'), 'bundles'); |
| 103 | var dest = path.join(bundlesDir, target + '-' + version); |
| 104 | |
| 105 | // Already downloaded by a previous run? Use it even when downloads are |
| 106 | // disabled — CODEGRAPH_NO_DOWNLOAD blocks fetching, not a cached bundle. |
| 107 | var cached = launcherIn(dest); |
| 108 | if (cached) { pruneOldBundles(bundlesDir, dest); return cached; } |
| 109 | |
| 110 | if (process.env.CODEGRAPH_NO_DOWNLOAD) { |
| 111 | fail('the network fallback is disabled (CODEGRAPH_NO_DOWNLOAD is set).'); |
| 112 | } |
| 113 | |
| 114 | var asset = 'codegraph-' + target + (isWindows ? '.zip' : '.tar.gz'); |
| 115 | var base = process.env.CODEGRAPH_DOWNLOAD_BASE || ('https://github.com/' + REPO + '/releases/download'); |
| 116 | var url = base + '/v' + version + '/' + asset; |
| 117 | |
| 118 | process.stderr.write( |
| 119 | 'codegraph: platform bundle missing (registry did not provide ' + pkg + ').\n' + |
| 120 | 'codegraph: downloading ' + asset + ' from GitHub Releases (' + version + ')...\n' |
| 121 | ); |
| 122 | |
| 123 | // Stage inside bundlesDir so the final rename is on the same filesystem (atomic, |
| 124 | // no EXDEV across tmpfs). Strip the archive's top-level codegraph-<target>/ dir. |
| 125 | fs.mkdirSync(bundlesDir, { recursive: true }); |
| 126 | var stage = fs.mkdtempSync(path.join(bundlesDir, '.dl-')); |
| 127 | try { |
| 128 | var archivePath = path.join(stage, asset); |
| 129 | await download(url, archivePath, 6); |
| 130 | await verifyChecksum(archivePath, asset, base, version); |
| 131 | var extracted = path.join(stage, 'bundle'); |
| 132 | fs.mkdirSync(extracted); |
| 133 | extract(archivePath, extracted); |
| 134 | |
| 135 | var raced = launcherIn(dest); // another process may have finished meanwhile |
| 136 | if (raced) { rmrf(stage); return raced; } |
| 137 | try { |
| 138 | fs.renameSync(extracted, dest); |
| 139 | } catch (e) { |
| 140 | var other = launcherIn(dest); // lost the race but theirs is valid |
| 141 | if (other) { rmrf(stage); return other; } |
| 142 | throw e; |
| 143 | } |
| 144 | } catch (e) { |
| 145 | rmrf(stage); |
| 146 | fail('download failed (' + e.message + ').\n URL: ' + url); |
| 147 | } |
| 148 | rmrf(stage); |
| 149 | |
| 150 | var ready = launcherIn(dest); |
| 151 | if (!ready) fail('downloaded bundle is missing its launcher under ' + dest + '.'); |
| 152 | pruneOldBundles(bundlesDir, dest); |
| 153 | process.stderr.write('codegraph: bundle ready.\n'); |
| 154 | return ready; |
| 155 | } |
| 156 | |
| 157 | function readVersion() { |
no test coverage detected