OverviewFilesCommitsIssuesDocsReleasesPeers

favela-peer commits / eb159b4

ci: build, test and publish favela-peer through pear-git CI
favela-peer had no ci.yaml, so a push built nothing and every release was published by hand. Same steps as ap-bridge (install, vendor, guard, tests against the vendored closure, publish), except the vendor step resolves each sibling from its file: spec: hyper-identity2 and hyperident live under packages/ and repos/, which ap-bridge's name-based lookup cannot find. Declares hyperdht and streamx as devDependencies: three tests import them and they resolved only through hoisting. Already in the lockfile at these versions, so the lock root is updated by hand, nothing re-resolved. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
eb159b4333d45849ff42756673e9ace469bbf574
by plankatron <fleeky@gmail.com> · 2026-10-13T01:19:51.000Z

Changed files (3) +104 -2

.pear-git/ci.yaml +98 -0
……@@ -0,0 +1,98 @@
1# pear-git CI — auto-build + publish on `git push pear master`.
2#
3# Same shape as ap-bridge's ci.yaml (install -> vendor -> guard -> tests -> publish), which is the
4# only order where every precondition holds. Before this file favela-peer had no ci.yaml, so a push
5# built nothing and every release (0.0.57 and earlier) was published by hand.
6#
7# NO daemon-boot smoke here: CI steps are synchronous with a hard timeout, and a step that boots a
8# long-running daemon orphans the child when the timeout fires.
9
10steps:
11 - name: install deps (root closure)
12 run: npm install --production
13 timeout: 300
14
15 # Vendor LAST among the npm operations. CI git-archives only this repo, so each file: dep is a
16 # dangling symlink until it is copied in from the build machine's working tree, and whatever is
17 # checked out there ships to the fleet. Refuse unless each sibling HEAD is reachable from a hosted
18 # branch.
19 #
20 # ⛔ The SOURCE comes from the file: spec, not the package name. Two siblings live off the flat
21 # layout: hyper-identity2 is `file:../packages/hyper-identity2` and hyperident is
22 # `file:../repos/hyperident`. Looking them up as hyper-fleeky/<name> (ap-bridge's rule) finds
23 # neither. The spec is resolved against the canonical checkout, because the export in /tmp has no
24 # meaningful `..`.
25 - name: "vendor file: siblings into the closure"
26 run: |
27 HF=/storage/projects/code/hypercore-kb/hyper-fleeky
28 [ -d "$HF" ] || HF="$HOME/hyper-fleeky"
29 SIBS=$(node -e 'const d=require("./package.json");const a={...d.dependencies,...d.devDependencies};console.log(Object.entries(a).filter(([,v])=>String(v).startsWith("file:../")).map(([k,v])=>k+"="+v.slice(8)).join(" "))')
30 echo "siblings (derived from package.json): $SIBS"
31 [ -n "$SIBS" ] || { echo "no file: siblings found — package.json unreadable?"; exit 1; }
32 rc=0
33 for pair in $SIBS; do
34 pkg=${pair%%=*}; rel=${pair#*=}
35 SRC="$HF/$rel"
36 if [ ! -d "$SRC" ]; then echo " $pkg: $SRC NOT FOUND on the build machine — refuse"; rc=1; continue; fi
37 GD="$HOME/git-repos/$(basename "$rel").git"
38 if [ ! -d "$GD" ]; then echo " $pkg: no hosted repo at $GD — refuse"; rc=1; continue; fi
39 LOCAL=$(git -C "$SRC" rev-parse HEAD 2>/dev/null)
40 REFS=$(git --git-dir="$GD" for-each-ref --contains "$LOCAL" --format='%(refname:short)' refs/heads 2>/dev/null | tr '\n' ' ')
41 if [ -z "$REFS" ]; then echo " $pkg: $(echo $LOCAL | cut -c1-7) NOT in hosted history — refuse (push it)"; rc=1; continue; fi
42 echo " $pkg ($rel): $(echo $LOCAL | cut -c1-7) ok (on: $REFS)"
43 rm -rf "node_modules/$pkg" && cp -rL "$SRC" "node_modules/$pkg"
44 done
45 exit $rc
46 timeout: 180
47
48 # Declare-your-deps guard: an undeclared runtime import resolves locally through hoisting and then
49 # crash-loops the --production closure with MODULE_NOT_FOUND. Checks the DECLARATION, not the
50 # disk. Runtime code is index.js, bin/ and lib/; `#node:` specifiers are package imports and are
51 # skipped by the pattern.
52 - name: every direct import is declared
53 run: |
54 rc=0
55 for spec in $(grep -hoE "from '[a-z0-9@][^']*'" index.js bin/*.js lib/*.js \
56 | sed -E "s/.*from '([^']*)'/\1/" | sort -u); do
57 case "$spec" in
58 @*) m=$(echo "$spec" | cut -d/ -f1,2) ;;
59 *) m=$(echo "$spec" | cut -d/ -f1) ;;
60 esac
61 if node -e "const d=require('./package.json');process.exit(({...d.dependencies,...d.devDependencies})['$m']?0:1)"; then echo " ok: $m"
62 else echo " UNDECLARED: $m (from '$spec') — resolves on disk but is not in package.json"; rc=1; fi
63 done
64 exit $rc
65 timeout: 60
66
67 # Tests AFTER vendoring, against the exact tree about to be published. npm must not touch this
68 # tree now (it re-resolves and deletes hand-vendored directories), so registry devDependencies go
69 # into a SEPARATE prefix, only what the tree lacks is copied in, the suite runs with the tree's own
70 # brittle (brittle-bare runs tests in-process), and exactly those additions are removed again.
71 - name: tests (offline, against the vendored closure)
72 run: |
73 B=$(mktemp -d)
74 SPECS=$(node -p 'Object.entries(require("./package.json").devDependencies||{}).filter(([,v])=>!String(v).startsWith("file:")).map(([k,v])=>k+"@"+v).join(" ")')
75 echo "test-only devDependencies: $SPECS"
76 npm install --prefix "$B" --no-audit --no-fund --no-save $SPECS >/dev/null 2>&1 || { echo "could not install: $SPECS"; exit 1; }
77 ADDED=""
78 for p in $(cd "$B/node_modules" && ls -A | grep -v -E '^\.'); do
79 case "$p" in
80 @*) for q in $(ls -A "$B/node_modules/$p"); do
81 if [ ! -e "node_modules/$p/$q" ]; then mkdir -p "node_modules/$p" && cp -r "$B/node_modules/$p/$q" "node_modules/$p/$q" && ADDED="$ADDED $p/$q"; fi
82 done ;;
83 *) if [ ! -e "node_modules/$p" ]; then cp -r "$B/node_modules/$p" "node_modules/$p" && ADDED="$ADDED $p"; fi ;;
84 esac
85 done
86 echo "overlaid for the test run only:$ADDED"
87 # The runner's path comes from brittle's own bin map: brittle 4 moved it from bin/bare.js
88 # (ap-bridge's 3.x) to brittle-bare.js at the package root.
89 RUNNER=node_modules/brittle/$(node -p 'require("./node_modules/brittle/package.json").bin["brittle-bare"]')
90 bare "$RUNNER" test/*.test.js; rc=$?
91 for p in $ADDED; do rm -rf "node_modules/$p"; done
92 rm -rf "$B"
93 exit $rc
94 timeout: 900
95
96 - name: pgh publish (content-addressed -> installable app)
97 run: pgh publish . --accept-conflict "automated CI publish; dep version-splits inherited from per-app lockfiles across the pgh ecosystem"
98 timeout: 300
package-lock.json +3 -1
……@@ -38,7 +38,9 @@
3838 "favela-peer": "index.js"
3939 },
4040 "devDependencies": {
41 "brittle": "^4.0.2"
41 "brittle": "^4.0.2",
42 "hyperdht": "^6.34.0",
43 "streamx": "^2.28.1"
4244 }
4345 },
4446 "../anproto": {
package.json +3 -1
……@@ -56,7 +56,9 @@
5656 "protomux-rpc": "^1.10.0"
5757 },
5858 "devDependencies": {
59 "brittle": "^4.0.2"
59 "brittle": "^4.0.2",
60 "hyperdht": "^6.34.0",
61 "streamx": "^2.28.1"
6062 },
6163 "main": "index.js",
6264 "bin": {