| … | … | @@ -140,7 +140,7 @@ async function initPearRuntime () { |
| 140 | 140 | |
| 141 | 141 | const runtime = new PearRuntime({ |
| 142 | 142 | dir, |
| 143 | | app: path.join(appDir, 'build', getPlatformDir(), 'godot-keet'), |
| 143 | app: appDir, |
| 144 | 144 | version: runtimeVersion, |
| 145 | 145 | upgrade: pkg.upgrade |
| 146 | 146 | }) |
| … | … | @@ -153,82 +153,18 @@ async function initPearRuntime () { |
| 153 | 153 | if (data.key) console.log(`[launcher] ${data.key}`) |
| 154 | 154 | }) |
| 155 | 155 | |
| 156 | | // Mutex: only one 'updated' handler runs at a time to prevent race conditions. |
| 157 | | // Multiple 'updated' events fire as Hyperdrive replicates — without this lock, |
| 158 | | // concurrent async handlers can write version files without an actual swap. |
| 159 | | let applying = false |
| 160 | | |
| 161 | 156 | runtime.on('updated', async () => { |
| 162 | | if (applying || runtime.applied) return |
| 163 | | applying = true |
| 157 | if (runtime.applied) return |
| 164 | 158 | |
| 165 | 159 | console.log('[launcher] Update ready — applying...') |
| 166 | 160 | |
| 167 | 161 | try { |
| 168 | 162 | await runtime.applyUpdate() |
| 169 | | |
| 170 | 163 | if (runtime.applied) { |
| 171 | | // Binary swap succeeded (by-arch). Sync only the app files that change |
| 172 | | // between deploys — NOT all of node_modules (thousands of static files). |
| 173 | | // pear-runtime only swaps by-arch/, so without this, launcher/daemon |
| 174 | | // changes would require a full reinstall. |
| 175 | | const appDir = getAppDir() |
| 176 | | const co = runtime.drive.checkout(runtime.length) |
| 177 | | let updated = 0 |
| 178 | | |
| 179 | | // Only sync files that actually change between deploys: |
| 180 | | // - Top-level app files (index.js, package.json, version.txt) |
| 181 | | // - daemon/ (our app code) |
| 182 | | // - node_modules/keet-daemon/ (our modified daemon) |
| 183 | | // - node_modules/pear-runtime/ (CI-patched) |
| 184 | | const syncPrefixes = [ |
| 185 | | '/index.js', |
| 186 | | '/package.json', |
| 187 | | '/version.txt', |
| 188 | | '/daemon/', |
| 189 | | '/node_modules/keet-daemon/', |
| 190 | | '/node_modules/pear-runtime/' |
| 191 | | ] |
| 192 | | |
| 193 | | try { |
| 194 | | for (const prefix of syncPrefixes) { |
| 195 | | if (!prefix.endsWith('/')) { |
| 196 | | // Single file |
| 197 | | const data = await co.get(prefix) |
| 198 | | if (data) { |
| 199 | | const dst = path.join(appDir, prefix) |
| 200 | | fs.writeFileSync(dst, data) |
| 201 | | console.log(`[launcher] synced ${prefix}`) |
| 202 | | updated++ |
| 203 | | } |
| 204 | | continue |
| 205 | | } |
| 206 | | // Directory — list entries under prefix |
| 207 | | for await (const entry of co.list(prefix)) { |
| 208 | | const key = entry.key || entry |
| 209 | | if (typeof key !== 'string') continue |
| 210 | | const data = await co.get(key) |
| 211 | | if (data) { |
| 212 | | const dst = path.join(appDir, key) |
| 213 | | fs.mkdirSync(path.dirname(dst), { recursive: true }) |
| 214 | | fs.writeFileSync(dst, data) |
| 215 | | updated++ |
| 216 | | } |
| 217 | | } |
| 218 | | if (updated > 0) console.log(`[launcher] synced ${prefix} (${updated} files so far)`) |
| 219 | | } |
| 220 | | console.log(`[launcher] Synced ${updated} app files from Hyperdrive`) |
| 221 | | } catch (err) { |
| 222 | | console.error('[launcher] App file sync partial:', err.message) |
| 223 | | } |
| 224 | | |
| 225 | | await co.close() |
| 226 | 164 | console.log('[launcher] Update applied — restart for new version') |
| 227 | 165 | } |
| 228 | 166 | } catch (err) { |
| 229 | 167 | console.error('[launcher] Failed to apply update:', err.message) |
| 230 | | } finally { |
| 231 | | applying = false |
| 232 | 168 | } |
| 233 | 169 | }) |
| 234 | 170 | |