# pear-git CI — auto-build + publish on `git push pear master`.
#
# Canonical bare-app distribution (path A): install the root dependency closure, then
# `pgh publish .` content-addresses the app into the ReleaseRegistry so it can be
# `pgh app install hyper-proto-peer`ed. NOT the tarball/`artifacts:` path (that only serves a
# blob over hyper-deploy and isn't installable via `pgh app install`) — same pattern as the
# atproto-bridge / hypernote / hyper-wallet ci.yaml.
#
# hyper-proto-peer runs under BARE (the hypercore/autobase/StateBaseV3 stack is Bare-native). Its
# lib uses bare-fs/path/os/process; lib/bare-globals.js polyfills TextEncoder/TextDecoder/process;
# scripts/bare-deps.mjs (npm postinstall) adds @noble's "bare" crypto condition across the tree.
# The pgh manifest is runtime: bare, launched as `bare index.js`. Swarm (multi-device) mode is
# selected at runtime by PEER_SWARM=1 + PEER_ROOT_MNEMONIC + PEER_DEVICE_MNEMONIC.
#
# The pure/substrate packages atproto-repo + atproto-identity + atproto-store are file:../
# siblings. CI git-archives ONLY hyper-proto-peer into /tmp, so npm leaves broken symlinks and
# the published tree would lack them — vendor DEREFERENCED copies into the closure. Requires the
# sibling repos present on the build machine (KB path or $HOME).
steps:
- name: install deps (root closure)
run: npm install --production
timeout: 300
- name: verify vendored siblings are in hosted git history
run: |
# A vendored sibling is copied from the WORKING TREE, so whatever is checked out on the build
# machine is what ships to the fleet — reviewed or not. On 2026-07-28 that meant atproto-room
# shipped 27 commits (incl. 0.2.14 projection-drift and 0.2.16 _isCreator) that were only on a
# feature branch, and hyper-svc shipped 2 unpushed commits. Nothing surfaced it.
#
# This makes git AUTHORITATIVE for what ships: refuse to build unless every sibling's HEAD is
# reachable from some hosted branch. Reachability, NOT equality with the default branch —
# atproto-room legitimately ships from statebase-fork-heal, and a default-branch check would
# block a repo that is perfectly fine.
#
# Untracked files WARN rather than block: cp -rL ships them too, but they are usually probe
# scripts, and blocking on them would make the build unusable. Escape hatch for an emergency:
# PGV3_ALLOW_UNPUSHED_SIBLINGS=1.
rc=0
for pkg in atproto-repo atproto-identity atproto-store atproto-room atproto-did-peer hyper-svc hyper-overlay; do
SRC=/storage/projects/code/hypercore-kb/hyper-fleeky/$pkg
[ -d "$SRC" ] || SRC="$HOME/hyper-fleeky/$pkg"
[ -d "$SRC/.git" ] || { echo " $pkg: not a git checkout — skipped"; continue; }
GD="$HOME/git-repos/$pkg.git"
if [ ! -d "$GD" ]; then echo " $pkg: NOT HOSTED ($GD) — refuse"; rc=1; continue; fi
LOCAL=$(git -C "$SRC" rev-parse HEAD 2>/dev/null)
SHORT=$(echo "$LOCAL" | cut -c1-7)
if [ -n "$(git -C "$SRC" status --porcelain --untracked-files=no 2>/dev/null | head -1)" ]; then
echo " $pkg: MODIFIED tracked files — refuse (commit or stash)"; rc=1; continue
fi
REFS=$(git --git-dir="$GD" for-each-ref --contains "$LOCAL" --format='%(refname:short)' refs/heads 2>/dev/null | tr '\n' ' ')
if [ -z "$REFS" ]; then echo " $pkg: $SHORT NOT in hosted history — refuse (push it)"; rc=1; continue; fi
U=$(git -C "$SRC" ls-files --others --exclude-standard 2>/dev/null | wc -l)
if [ "$U" -gt 0 ]; then echo " $pkg: $SHORT ok (on: $REFS) — WARN: $U untracked file(s) also vendored"
else echo " $pkg: $SHORT ok (on: $REFS)"; fi
done
if [ "$rc" -ne 0 ]; then
if [ -n "$PGV3_ALLOW_UNPUSHED_SIBLINGS" ]; then echo "PGV3_ALLOW_UNPUSHED_SIBLINGS set — continuing"; rc=0
else echo "REFUSING TO BUILD: a vendored sibling is not in hosted git history. Push it, or set PGV3_ALLOW_UNPUSHED_SIBLINGS=1."; fi
fi
exit $rc
timeout: 60
- name: vendor extracted atproto-repo + atproto-identity + atproto-store + atproto-room siblings
run: |
# cp -rL DEREFERENCES each sibling's nested node_modules (the git-archive drops the file:../
# symlinks), so the published closure is self-contained. atproto-room carries its own nested
# atproto-repo/identity/store copies the same way — resolution matches the dev symlink layout.
for pkg in atproto-repo atproto-identity atproto-store atproto-room atproto-did-peer hyper-svc hyper-overlay; do
SRC=/storage/projects/code/hypercore-kb/hyper-fleeky/$pkg
[ -d "$SRC" ] || SRC="$HOME/hyper-fleeky/$pkg"
rm -rf "node_modules/$pkg" && cp -rL "$SRC" "node_modules/$pkg" && echo "vendored $pkg from $SRC"
done
timeout: 120
- name: pgh publish (content-addressed → installable app)
run: pgh publish . --accept-conflict "automated CI publish; dep version-splits inherited from per-app lockfiles across the pgh ecosystem"
timeout: 300