#!/usr/bin/env node import { access, lstat, mkdir, readFile, readdir, rename, rm } from 'node:fs/promises'; import { execFile, spawn } from 'node:child_process'; import { promisify } from 'node:util'; import { fileURLToPath } from 'node:url'; import path from 'node:path'; const execFileAsync = promisify(execFile); function run(command, args, options) { return new Promise((resolve, reject) => { const child = spawn(command, args, options); child.once('error', reject); child.once('exit', (code, signal) => { if (code === 0) resolve(); else reject(new Error(signal ? `husky terminated by ${signal}` : `husky exited ${code}`)); }); }); } async function pathExists(target) { try { await access(target); return true; } catch (error) { if (error.code === 'ENOENT') return false; throw error; } } async function directorySnapshot(root) { const snapshot = []; async function walk(current) { const children = await readdir(current, { withFileTypes: true }); for (const child of children.sort((left, right) => left.name.localeCompare(right.name))) { const target = path.join(current, child.name); const relative = path.relative(root, target); const stats = await lstat(target); if (child.isDirectory()) { snapshot.push([relative, 'directory', stats.mode & 0o777]); await walk(target); } else { snapshot.push([ relative, 'file', stats.mode & 0o777, (await readFile(target)).toString('base64'), ]); } } } await walk(root); return JSON.stringify(snapshot); } async function directoriesMatch(left, right) { return (await directorySnapshot(left)) === (await directorySnapshot(right)); } export async function installHooks({ root = process.cwd(), disabled = process.env.HUSKY === '0', quarantineRoot = path.join(root, '.mosaic-test-work', 'husky-quarantine'), runHusky = async (_stagingHooks, stagingRepo) => { await execFileAsync('git', ['init', '--quiet', stagingRepo]); await run(path.join(root, 'node_modules', '.bin', 'husky'), ['.husky'], { cwd: stagingRepo, stdio: 'inherit', }); }, activateHooks = async () => { await run('git', ['config', 'core.hooksPath', '.husky/_'], { cwd: root, stdio: 'inherit' }); }, } = {}) { if (disabled) return; const huskyDir = path.join(root, '.husky'); const active = path.join(huskyDir, '_'); const nonce = `${Date.now()}-${process.pid}`; const stagingRepo = path.join(root, '.mosaic-test-work', `husky-stage-${nonce}`); const stagingHooks = path.join(stagingRepo, '.husky'); const quarantined = path.join(quarantineRoot, `${path.basename(root)}-${nonce}`); await mkdir(huskyDir, { recursive: true }); await mkdir(quarantineRoot, { recursive: true }); const previousComplete = (await pathExists(active)) && (await pathExists(path.join(active, 'h'))); let previousQuarantined = false; try { if ((await pathExists(active)) && !previousComplete) { await rename(active, quarantined); previousQuarantined = true; } await mkdir(stagingRepo, { recursive: true }); await runHusky(stagingHooks, stagingRepo); const staged = path.join(stagingHooks, '_'); if (!(await pathExists(path.join(staged, 'h')))) { throw new Error('husky did not produce its required h shim'); } if (previousComplete) { if (!(await directoriesMatch(active, staged))) { throw new Error('existing complete hook set differs from the installed Husky version'); } await rm(stagingRepo, { recursive: true, force: true }); } else { await rename(staged, active); await rm(stagingRepo, { recursive: true, force: true }); } await activateHooks(); if (previousQuarantined) { try { await rm(quarantined, { recursive: true, force: true }); } catch { console.warn( `Previous hook state was deactivated but remains quarantined at ${quarantined}`, ); } } } catch (error) { const cleanupFailures = []; try { if (await pathExists(stagingRepo)) { await rename(stagingRepo, `${quarantined}-staging`); } } catch (cleanupError) { cleanupFailures.push(`staging hooks: ${cleanupError.message}`); } const cleanup = cleanupFailures.length === 0 ? 'No partial hook set was activated.' : `Automatic cleanup was incomplete (${cleanupFailures.join('; ')}).`; throw new Error( `Hook installation failed: ${error.message}. ${cleanup} Fix: rm -rf .husky/_ && git config core.hooksPath .husky/_ && pnpm install --frozen-lockfile`, { cause: error }, ); } } if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) { try { await installHooks(); } catch (error) { console.error(error.message); process.exitCode = 1; } }