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