OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-sync commits / cb5fab7

0.1.3: _ensureRemoteHelper resolves the COMPLETE (hoisted) git-remote-punch
Pre-existing latent bug (surfaced 2026-07-18, pear-git #3): in content-addressed pgh bundles, hyper-sync's NESTED node_modules/git-remote-punch is deduped/pruned — the package dir lingers but its bin/ is gone. _ensureRemoteHelper picked that path blindly (existsSync on the dir path passed at write time, went stale after OTA), so the shim exec'd a missing file and bare aborted (SIGABRT) on EVERY punch fetch. That — not the connect timeout — is why org joiners got refs but ZERO objects. Fix: resolve git-remote-punch via require.resolve + a bare-safe ancestor walk to the HOISTED top-level (complete) copy, and verify the actual bin FILE exists (isFile) so a pruned package dir is never selected — falling through to null (git uses PATH) at worst, never shimming a missing path. Proven: cloning through the top-level helper pulls 7rnx-proxy fully (objects + files). Existing helper test still green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
cb5fab7f94094725f160840521bf6bfa0cfd1e30
by plankatron <fleeky@gmail.com> · 2026-07-18T20:17:54.000Z

Changed files (2) +49 -14

index.js +28 -8
……@@ -499,15 +499,35 @@ module.exports = class HyperSync extends ReadyResource {
499499 if (this._remoteHelperDir !== undefined) return this._remoteHelperDir;
500500 this._remoteHelperDir = null;
501501 try {
502 // git-remote-punch sits as a SIBLING of hyper-sync — in the dev tree
503 // (hyper-fleeky/{hyper-sync,git-remote-punch}) and when flattened into
504 // node_modules (hoisted next to us). Also check the nested layout.
505 const candidates = [
506 path.join(__dirname, '..', 'git-remote-punch', 'bin', 'git-remote-punch'),
507 path.join(__dirname, 'node_modules', 'git-remote-punch', 'bin', 'git-remote-punch')
508 ];
502 // Resolve git-remote-punch's bin. ORDER MATTERS: prefer the MODULE-RESOLVED
503 // (hoisted top-level) copy. In content-addressed pgh bundles, hyper-sync's
504 // NESTED node_modules/git-remote-punch is deduped/pruned — its `bin/` can be
505 // ABSENT while the package dir lingers, so a shim pointing at that missing
506 // file makes bare abort (SIGABRT) on EVERY fetch (org joiners then get refs
507 // but zero objects). The hoisted top-level copy is the complete one.
508 const candidates = [];
509 try {
510 // Follows node_modules resolution to the hoisted top-level copy.
511 // (Available under Node; may be absent under bare — hence the walk below.)
512 const pkg = require.resolve('git-remote-punch/package.json');
513 candidates.push(path.join(path.dirname(pkg), 'bin', 'git-remote-punch'));
514 } catch {}
515 // bare-safe fallback: walk ancestors for a HOISTED node_modules/git-remote-punch
516 // (the top-level, COMPLETE copy). Does not rely on require.resolve.
517 for (let d = __dirname, prev = null; d !== prev; prev = d, d = path.dirname(d)) {
518 candidates.push(path.join(d, 'node_modules', 'git-remote-punch', 'bin', 'git-remote-punch'));
519 }
520 // Dev tree (sibling) + immediate-nested fallbacks.
521 candidates.push(path.join(__dirname, '..', 'git-remote-punch', 'bin', 'git-remote-punch'));
522 candidates.push(path.join(__dirname, 'node_modules', 'git-remote-punch', 'bin', 'git-remote-punch'));
523
509524 let helperBin = null;
510 for (const c of candidates) { try { if (fs.existsSync(c)) { helperBin = c; break; } } catch {} }
525 for (const c of candidates) {
526 // Verify the actual BIN FILE exists (isFile) — a pruned package dir must
527 // NOT be selected, or we shim a missing path and crash. Fall through, and
528 // finally to null (git uses whatever git-remote-punch is already on PATH).
529 try { if (fs.existsSync(c) && fs.statSync(c).isFile()) { helperBin = c; break; } } catch {}
530 }
511531 if (!helperBin) return null;
512532
513533 // git-remote-punch is `#!/usr/bin/env bare`, so the shim runs it under the
package.json +21 -6
……@@ -1,14 +1,29 @@
11{
22 "name": "hyper-sync",
3 "version": "0.1.2",
3 "version": "0.1.3",
44 "description": "Manage and sync bare git repos over Hyperswarm",
55 "main": "index.js",
66 "imports": {
7 "fs": { "bare": "bare-fs", "default": "fs" },
8 "path": { "bare": "bare-path", "default": "path" },
9 "os": { "bare": "bare-os", "default": "os" },
10 "child_process": { "bare": "bare-subprocess", "default": "child_process" },
11 "process": { "bare": "bare-process", "default": "process" }
7 "fs": {
8 "bare": "bare-fs",
9 "default": "fs"
10 },
11 "path": {
12 "bare": "bare-path",
13 "default": "path"
14 },
15 "os": {
16 "bare": "bare-os",
17 "default": "os"
18 },
19 "child_process": {
20 "bare": "bare-subprocess",
21 "default": "child_process"
22 },
23 "process": {
24 "bare": "bare-process",
25 "default": "process"
26 }
1227 },
1328 "scripts": {
1429 "test": "brittle test/*.test.js"