OverviewFilesCommitsIssuesDocsReleasesPeers

godot-keet commits / 7e205b8

OTA: sync ALL app files from Hyperdrive, not just by-arch binary
pear-runtime only swaps by-arch/ (Godot binary+pck). Everything else (index.js, daemon/, node_modules/) stayed at install version — meaning any launcher or daemon fix required a full reinstall via pgh app install. After the binary swap, now iterates ALL Hyperdrive entries and writes non-by-arch files to the app directory. This means index.js, daemon code, dependency changes, etc. all get OTA'd alongside the game binary. Also adds mutex on the 'updated' handler to prevent concurrent race. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
7e205b8ddebc8e5ce33341de780c81a52fe5c099
by plankatron <fleeky@gmail.com> · 2026-02-22T00:42:50.000Z

Changed files (1) +29 -44

index.js +29 -44
……@@ -150,7 +150,7 @@ async function initPearRuntime () {
150150 })
151151
152152 runtime.on('updating-delta', (data) => {
153 console.log(`[launcher] delta: ${JSON.stringify({ key: data.key, op: data.op, bytes: data.bytesAdded })}`)
153 if (data.key) console.log(`[launcher] ${data.key}`)
154154 })
155155
156156 // Mutex: only one 'updated' handler runs at a time to prevent race conditions.
……@@ -159,60 +159,45 @@ async function initPearRuntime () {
159159 let applying = false
160160
161161 runtime.on('updated', async () => {
162 if (applying || runtime.applied) {
163 console.log(`[launcher] Update skipped (applying=${applying} applied=${runtime.applied})`)
164 return
165 }
162 if (applying || runtime.applied) return
166163 applying = true
167 console.log(`[launcher] Update ready — applying... (next=${runtime.next} app=${runtime.app})`)
168164
169 try {
170 // Log what's in the swap source directory (find the actual by-arch path)
171 try {
172 const nextDir = runtime.next
173 const byArch = path.join(nextDir, 'by-arch')
174 if (fs.existsSync(byArch)) {
175 const hosts = fs.readdirSync(byArch)
176 console.log(`[launcher] Swap next hosts: ${hosts.join(', ')}`)
177 for (const h of hosts) {
178 const appDir2 = path.join(byArch, h, 'app', 'godot-keet')
179 try {
180 const files = fs.readdirSync(appDir2)
181 console.log(`[launcher] Swap src ${h}: ${files.join(', ')}`)
182 } catch {}
183 }
184 } else {
185 console.log(`[launcher] No by-arch dir in ${nextDir}`)
186 }
187 } catch (e) {
188 console.log(`[launcher] Swap source check failed: ${e.message}`)
189 }
165 console.log('[launcher] Update ready — applying...')
190166
167 try {
191168 await runtime.applyUpdate()
192169
193170 if (runtime.applied) {
194 // Swap succeeded — update version files from Hyperdrive
171 // Binary swap succeeded (by-arch). Now also update ALL other app files
172 // from the Hyperdrive so index.js, daemon/, node_modules/ etc. stay current.
173 // Without this, only the Godot binary+pck get OTA'd — launcher code changes
174 // would require a full reinstall.
195175 const appDir = getAppDir()
196 const manifest = await runtime.drive.get('/package.json')
197 if (manifest) {
198 const remotePkg = JSON.parse(manifest)
199 fs.writeFileSync(path.join(appDir, 'package.json'),
200 JSON.stringify(Object.assign(pkg, { version: remotePkg.version }), null, 2) + '\n')
201 }
202 const remoteVersion = await runtime.drive.get('/version.txt')
203 if (remoteVersion) {
204 fs.writeFileSync(path.join(appDir, 'version.txt'), remoteVersion)
205 }
176 const co = runtime.drive.checkout(runtime.length)
177 let updated = 0
206178
207 // Verify swap target has expected files
208179 try {
209 const targetFiles = fs.readdirSync(runtime.app)
210 console.log(`[launcher] After swap, app dir: ${targetFiles.join(', ')}`)
211 } catch {}
180 for await (const entry of co.list('/')) {
181 const key = entry.key || entry
182 if (typeof key !== 'string') continue
183 // Skip by-arch (already swapped atomically) and build/ (duplicate of by-arch)
184 if (key.startsWith('/by-arch/') || key.startsWith('/build/')) continue
185
186 const data = await co.get(key)
187 if (data) {
188 const dst = path.join(appDir, key)
189 fs.mkdirSync(path.dirname(dst), { recursive: true })
190 fs.writeFileSync(dst, data)
191 updated++
192 }
193 }
194 console.log(`[launcher] Updated ${updated} app files from Hyperdrive`)
195 } catch (err) {
196 console.error('[launcher] App file sync partial:', err.message)
197 }
212198
199 await co.close()
213200 console.log('[launcher] Update applied — restart for new version')
214 } else {
215 console.log('[launcher] applyUpdate was no-op (applied still false)')
216201 }
217202 } catch (err) {
218203 console.error('[launcher] Failed to apply update:', err.message)