| … | … | @@ -107,7 +107,17 @@ steps: |
| 107 | 107 | # without a 153 MB sideload — pgh-runtime fetches this file and verifies it against tree.json |
| 108 | 108 | # (written by `npm run gen:tree` in the prebuild step). Native code still needs a new APK. |
| 109 | 109 | cp android/app/src/main/assets/worklet.bundle worklet.bundle |
| 110 | ls -l favela-android.apk worklet.bundle tree.json | |
| 110 | # The APK gets a tree too, and a SEPARATE one. pgh-runtime installs a TREE — it hashes what it is | |
| 111 | # handed and refuses bytes that do not reproduce the root hash — so until now the 154 MB APK was | |
| 112 | # the one artifact nothing verified, whoever fetched it. This is the precondition for ever fetching | |
| 113 | # it over the swarm instead of as a single unresumable HTTPS GET. | |
| 114 | # | |
| 115 | # ⛔ NOT merged into tree.json. A pgh tree holds many entries, so it would encode fine and would | |
| 116 | # brick every phone already in the field: the deployed bundleStore.materialize keeps the LAST file | |
| 117 | # entry it writes as the bundle to boot, so a two-entry tree stages a 154 MB APK as the worklet | |
| 118 | # bundle. The OTA client on a handset is exactly what cannot be fixed remotely. | |
| 119 | npm run gen:apk-tree | |
| 120 | ls -l favela-android.apk worklet.bundle tree.json apk.tree.json | |
| 111 | 121 | timeout: 2700 |
| 112 | 122 | |
| 113 | 123 | artifacts: |
| … | … | @@ -121,3 +131,7 @@ artifacts: |
| 121 | 131 | # existing and nothing scheduling it (hyper-social #47 §2); this makes that trajectory steeper. |
| 122 | 132 | - worklet.bundle |
| 123 | 133 | - tree.json |
| 134 | # The APK's manifest — 289 bytes, and the only thing that makes that 154 MB fetchable-and-verifiable | |
| 135 | # by anything other than a browser. Nothing consumes it yet; publishing it first is what lets a | |
| 136 | # swarm-side fetcher be written without another build round-trip. | |
| 137 | - apk.tree.json |
| … | … | @@ -1,6 +1,6 @@ |
| 1 | 1 | { |
| 2 | 2 | "name": "favela-android", |
| 3 | "version": "0.0.8", | |
| 3 | "version": "0.0.9", | |
| 4 | 4 | "description": "favela-android — a React Native client over a bare-kit worklet running a FULL favela node (hyper-proto-peer + appview + hyperswarm) on the phone. favela-web is reference for the logic, NOT the rendering: this is a different rendering surface. See spec/architecture.md.", |
| 5 | 5 | "private": true, |
| 6 | 6 | "type": "module", |
| … | … | @@ -11,7 +11,8 @@ |
| 11 | 11 | "prebuild:android": "npm run pack:worklet && npm run stamp:worklet && npm run gen:tree && npm run stage:addons", |
| 12 | 12 | "test": "bare test/adopt.test.mjs && bare test/ipc.test.mjs && bare test/session.test.mjs && bare test/favela-api.test.mjs && bare test/lifecycle.test.mjs && bare test/roster.test.mjs && bare test/describe.test.mjs && bare test/shell.test.mjs && bare test/ota.test.mjs && bare test/ota-e2e.test.mjs", |
| 13 | 13 | "stamp:worklet": "node -e \"const fs=require('fs');const p='android/app/src/main/assets/worklet.bundle';fs.writeFileSync(p+'.size',String(fs.statSync(p).size))\"", |
| 14 | "gen:tree": "bare scripts/gen-tree-manifest.mjs" | |
| 14 | "gen:tree": "bare scripts/gen-tree-manifest.mjs", | |
| 15 | "gen:apk-tree": "bare scripts/gen-tree-manifest.mjs --in favela-android.apk --out apk.tree.json --name favela-android.apk" | |
| 15 | 16 | }, |
| 16 | 17 | "dependencies": { |
| 17 | 18 | "atproto-identity": "file:../atproto-identity", |
| … | … | @@ -16,21 +16,38 @@ |
| 16 | 16 | // |
| 17 | 17 | // Run under BARE, not node: hyper-overlay/tree resolves #fs through bare's import map. |
| 18 | 18 | // bare scripts/gen-tree-manifest.mjs |
| 19 | // ⛔ ONE FILE PER MANIFEST, AND THE APK GETS ITS OWN. It is tempting to put both artifacts in tree.json | |
| 20 | // — a pgh tree is a set of entries, so it would encode fine. It would also brick every phone already in | |
| 21 | // the field: the deployed bundleStore.materialize loops the entries and keeps the LAST file it writes as | |
| 22 | // the bundle to boot, so a two-entry tree would stage a 154 MB APK as the worklet bundle. The OTA client | |
| 23 | // on a handset is the thing we cannot fix remotely, so its manifest keeps exactly the shape it has | |
| 24 | // always had, and the APK is described in a SEPARATE file that only new code looks for. | |
| 25 | // | |
| 26 | // --in <file> what to hash (default: the worklet bundle) | |
| 27 | // --out <file> where to write (default: tree.json) | |
| 28 | // --name <name> the entry's name (default: treeFor's own, worklet.bundle) | |
| 19 | 29 | import fs from 'bare-fs' |
| 20 | 30 | import path from 'bare-path' |
| 21 | 31 | import { treeFor } from '../lib/ota.js' |
| 22 | 32 | |
| 23 | 33 | const ROOT = path.join(path.dirname(import.meta.url.replace('file://', '')), '..') |
| 24 | const BUNDLE = path.join(ROOT, 'android', 'app', 'src', 'main', 'assets', 'worklet.bundle') | |
| 25 | const OUT = path.join(ROOT, 'tree.json') | |
| 26 | 34 | |
| 27 | if (!fs.existsSync(BUNDLE)) { | |
| 28 | console.error('no worklet.bundle at ' + BUNDLE + ' — run `npm run pack:worklet` first') | |
| 35 | function arg (flag, fallback) { | |
| 36 | const i = Bare.argv.indexOf(flag) | |
| 37 | return i === -1 ? fallback : Bare.argv[i + 1] | |
| 38 | } | |
| 39 | ||
| 40 | const IN = path.resolve(ROOT, arg('--in', path.join(ROOT, 'android', 'app', 'src', 'main', 'assets', 'worklet.bundle'))) | |
| 41 | const OUT = path.resolve(ROOT, arg('--out', path.join(ROOT, 'tree.json'))) | |
| 42 | const NAME = arg('--name', null) | |
| 43 | ||
| 44 | if (!fs.existsSync(IN)) { | |
| 45 | console.error('nothing to hash at ' + IN + ' — run `npm run pack:worklet` first (or point --in at a built artifact)') | |
| 29 | 46 | Bare.exit(1) |
| 30 | 47 | } |
| 31 | 48 | |
| 32 | const bytes = fs.readFileSync(BUNDLE) | |
| 33 | const { entries, treeHash, contentHash, size } = treeFor(bytes) | |
| 49 | const bytes = fs.readFileSync(IN) | |
| 50 | const { entries, treeHash, contentHash, size } = NAME ? treeFor(bytes, NAME) : treeFor(bytes) | |
| 34 | 51 | |
| 35 | 52 | fs.writeFileSync(OUT, JSON.stringify({ treeHash, size, entries }, null, 2) + '\n') |
| 36 | console.log(`tree.json — treeHash ${treeHash.slice(0, 16)}… content ${contentHash.slice(0, 16)}… ${size} bytes`) | |
| 53 | console.log(`${path.basename(OUT)} — treeHash ${treeHash.slice(0, 16)}… content ${contentHash.slice(0, 16)}… ${size} bytes`) |