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