OverviewFilesCommitsIssuesDocsReleasesPeers

favela-web / .pear-git / ci.yaml

83 lines · yaml ref:
# pear-git CI — auto-build + publish on `git push pear master`.
#
# Canonical bare-app distribution (path A): install the dependency closure, vendor the file: siblings
# (git-archive ships ONLY favela-web, so npm leaves broken symlinks), then `pgh publish .` content-
# addresses the app into the ReleaseRegistry → users `pgh app install favela-web`. Same pattern as
# atproto-bridge / hypernote.

steps:
  - name: install deps
    run: npm install --production
    timeout: 300

  # file:../ siblings — the CI git-archives ONLY favela-web into /tmp, so npm leaves broken symlinks and
  # the published tree lacks these. They're self-contained (own node_modules), so vendor dereferenced
  # copies (cp -rL follows the nested symlinks too — e.g. favela-community's hyper-rooms).
  - 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 this machine
      # ships to the fleet — reviewed or not. Checked by hand on 2026-08-16 and it was already true:
      # hyper-identity carried an uncommitted version bump and hyper-proto-peer an uncommitted publish
      # lock, both of which would have shipped silently. atproto-bridge learned this the harder way
      # (its ci.yaml records atproto-room shipping 27 unpushed commits on 2026-07-28).
      #
      # Makes git AUTHORITATIVE for what ships: refuse 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. Untracked files WARN rather than block (cp -rL
      # ships them too, but they are usually probe scripts). Escape hatch: PGV3_ALLOW_UNPUSHED_SIBLINGS=1.
      KB=/storage/projects/code/hypercore-kb/hyper-fleeky
      [ -d "$KB" ] || KB="$HOME/hyper-fleeky"
      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])=>k).join(" "))')
      [ -n "$SIBS" ] || { echo "no file: siblings found — package.json unreadable?"; exit 1; }
      rc=0
      for pkg in $SIBS; do
        SRC="$KB/$pkg"; [ -d "$SRC/.git" ] || SRC="$KB/packages/$pkg"
        [ -d "$SRC/.git" ] || { echo "  $pkg: not a git checkout — skipped"; continue; }
        GD="$HOME/git-repos/$pkg.git"
        [ -d "$GD" ] || { echo "  $pkg: NOT HOSTED ($GD) — refuse"; rc=1; continue; }
        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."; fi
      fi
      exit $rc
    timeout: 60

  - name: vendor sibling packages into the closure
    run: |
      # The list is DERIVED from package.json's file: deps — never hardcoded. It used to be spelled
      # out here, and package.json then gained atproto-room and hyper-proto-peer without this line
      # being touched, so every build published a tree with two BROKEN SYMLINKS. `pgh publish` caught
      # it ("refusing to publish a tree that would crash at import") — but only at the last step, and
      # only because that guard exists. Same drift the atproto-bridge ci.yaml documents; same fix.
      KB=/storage/projects/code/hypercore-kb/hyper-fleeky
      [ -d "$KB" ] || KB="$HOME/hyper-fleeky"
      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])=>k).join(" "))')
      [ -n "$SIBS" ] || { echo "no file: siblings found — package.json unreadable?"; exit 1; }
      echo "siblings (derived from package.json): $SIBS"
      rc=0
      for pkg in $SIBS; do
        # hyper-identity lives under packages/, the rest at the top level — resolve both.
        SRC="$KB/$pkg"; [ -d "$SRC" ] || SRC="$KB/packages/$pkg"
        [ -d "$SRC" ] || { echo "  $pkg: NO SOURCE at $KB/$pkg or $KB/packages/$pkg — refuse"; rc=1; continue; }
        rm -rf "node_modules/$pkg" && cp -rL "$SRC" "node_modules/$pkg" && echo "  vendored $pkg <- $SRC"
      done
      exit $rc
    timeout: 240

  - name: publish to the release registry
    # --accept-conflict: favela-web vendors the atproto-* siblings at their CURRENT versions (e.g.
    # atproto-identity 0.0.2 with the lens-export genesis entries), which differ from other apps'
    # already-published copies in the shared content-addressed store. Expected cross-app dep drift.
    run: pgh publish . --accept-conflict "favela-web vendors atproto-* siblings at current versions (lens-export); differs from other apps' published copies"
    timeout: 300