OverviewFilesCommitsIssuesDocsReleasesPeers

godot-keet commits / 8c63a92

OTA file sync: only sync app files that change, not all of node_modules
The full Hyperdrive iteration was downloading thousands of static node_modules files on every OTA. Instead, target only the files that actually change between deploys: index.js, daemon/, package.json, version.txt, and the two patched node_modules (keet-daemon, pear-runtime). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
8c63a92b1ab9c1295ec20ad07dc931b275d75d69
by plankatron <fleeky@gmail.com> · 2026-02-22T01:02:03.000Z

Changed files (1) +43 -17

index.js +43 -17
……@@ -168,30 +168,56 @@ async function initPearRuntime () {
168168 await runtime.applyUpdate()
169169
170170 if (runtime.applied) {
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.
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.
175175 const appDir = getAppDir()
176176 const co = runtime.drive.checkout(runtime.length)
177177 let updated = 0
178178
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
179193 try {
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++
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 }
192217 }
218 if (updated > 0) console.log(`[launcher] synced ${prefix} (${updated} files so far)`)
193219 }
194 console.log(`[launcher] Updated ${updated} app files from Hyperdrive`)
220 console.log(`[launcher] Synced ${updated} app files from Hyperdrive`)
195221 } catch (err) {
196222 console.error('[launcher] App file sync partial:', err.message)
197223 }