Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion scripts/setup-loopback.sh
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ if [ "$COUNT" -lt 1 ] || [ "$COUNT" -gt "$MAX" ]; then
fi

END=$((START + COUNT - 1))
# Announce the range up front — also a tell when two checkouts run different
# package versions with different pool conventions (0.3.x started at 127.0.0.1).
echo "Configuring loopback addresses 127.0.0.$START-127.0.0.$END on lo0..."
for i in $(seq $START $END); do
# Use a host (/32) netmask, not the implicit class-A /8. Without an explicit netmask,
# macOS gives each 127.0.0.x alias a 255.0.0.0 mask, so every alias claims to own the
Expand All @@ -58,4 +61,23 @@ for i in $(seq $START $END); do
$SUDO ifconfig lo0 alias 127.0.0.$i netmask 255.255.255.255 up
done

echo "✓ Configured $COUNT loopback addresses (127.0.0.$START-127.0.0.$END)"
# Verify every alias actually landed before claiming success. A partial run
# (expired sudo credential mid-loop, interruption) previously still printed ✓,
# leaving the test runner failing later on the missing addresses.
MISSING=()
LO0_STATE=$(ifconfig lo0)
for i in $(seq $START $END); do
if ! echo "$LO0_STATE" | grep -q "inet 127\.0\.0\.$i "; then

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The success check can still accept the configuration this script is meant to repair. If an old 127.0.0.$i alias has a /8 netmask and either the delete or re-add fails (those statuses are currently ignored), the old alias remains in LO0_STATE; this IP-only grep passes and prints ✓ even though the overlapping-subnet/mDNS CPU problem remains. Could the verifier require the matching alias to have netmask 0xffffffff (macOS's representation of /32) before reporting success? Checking/reporting a failed add would also make the immediate error clearer. A fake-ifconfig regression test with an existing /8 alias would lock this down.

MISSING+=("127.0.0.$i")
fi
done
Comment thread
heskew marked this conversation as resolved.

if [ ${#MISSING[@]} -gt 0 ]; then
echo "✗ ${#MISSING[@]} loopback address(es) failed to configure. Fix with:"
for addr in "${MISSING[@]}"; do
echo " sudo ifconfig lo0 alias $addr netmask 255.255.255.255 up"
done
exit 1
fi

echo "✓ Configured and verified $COUNT loopback addresses (127.0.0.$START-127.0.0.$END)"
44 changes: 40 additions & 4 deletions src/harperLifecycle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ function getHarperScript(harperBinPath?: string): string {
// 1. Explicit option
if (harperBinPath) {
ok(existsSync(harperBinPath), `Harper script not found at provided harperBinPath: ${harperBinPath}`);
return harperBinPath;
return logResolvedHarperScript(harperBinPath, 'harperBinPath option');
}

// 2. Environment variable
Expand All @@ -232,15 +232,15 @@ function getHarperScript(harperBinPath?: string): string {
existsSync(envPath),
`Harper script not found at HARPER_INTEGRATION_TEST_INSTALL_SCRIPT path: ${envPath}`
);
return envPath;
return logResolvedHarperScript(envPath, 'HARPER_INTEGRATION_TEST_INSTALL_SCRIPT');
}

// 3. Auto-resolve from node_modules
try {
const require = createRequire(import.meta.url);
const resolved = require.resolve('harper/dist/bin/harper.js');
if (existsSync(resolved)) {
return resolved;
return logResolvedHarperScript(resolved, 'node_modules');
}
} catch {
// harper package not found in node_modules
Expand All @@ -251,7 +251,7 @@ function getHarperScript(harperBinPath?: string): string {
while (true) {
const potentialPath = join(currentDir, 'dist/bin/harper.js');
if (existsSync(potentialPath)) {
return potentialPath;
return logResolvedHarperScript(potentialPath, 'ancestor dist');
}
const parentDir = dirname(currentDir);
if (parentDir === currentDir) {
Expand All @@ -269,6 +269,42 @@ function getHarperScript(harperBinPath?: string): string {
);
}

const loggedHarperScripts = new Set<string>();

/**
* Logs which Harper binary was resolved and how, once per distinct path.
*
* Which binary runs is the single most consequential (and previously invisible)
* decision the harness makes: resolving a stale `harper` package from
* node_modules while the caller expected their local build produces confusing
* wholesale test failures with no hint of the cause. When the `node_modules`
* path wins while `./dist/bin/harper.js` also exists, warn with the override.
*/
function logResolvedHarperScript(scriptPath: string, source: string): string {
if (loggedHarperScripts.has(scriptPath)) return scriptPath;
loggedHarperScripts.add(scriptPath);
console.log(`[integration-testing] Using Harper at ${scriptPath} (via ${source})`);
if (source === 'node_modules') {
// Mirror resolution step 4's ancestor walk so the warning fires even when
// tests run from a subdirectory of the repo that holds the local build.
let currentDir = process.cwd();
while (true) {
const localDist = join(currentDir, 'dist/bin/harper.js');
if (existsSync(localDist) && localDist !== scriptPath) {
console.warn(
`[integration-testing] Warning: resolved the 'harper' package from node_modules, but ${localDist} also exists. ` +
`If you meant to test the local build, set HARPER_INTEGRATION_TEST_INSTALL_SCRIPT=${localDist}.`
);
break;
}
const parentDir = dirname(currentDir);
if (parentDir === currentDir) break;
currentDir = parentDir;
}
}
return scriptPath;
}

/**
* Strips ANSI escape sequences (colors, bold, underline, cursor movement, etc.) from a string.
*/
Expand Down