diff --git a/src/utils/index.ts b/src/utils/index.ts index 17b77bb7..755d1ec0 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -7,6 +7,7 @@ import { getClientVersion } from './getClientVersion'; import { getRollupCreatorAddress } from './getRollupCreatorAddress'; import { getTokenBridgeCreatorAddress } from './getTokenBridgeCreatorAddress'; import { getWethAddress } from './getWethAddress'; +import { verifyFoundryBinaries } from './verifyFoundry'; export { generateChainId, @@ -18,4 +19,5 @@ export { getRollupCreatorAddress, getTokenBridgeCreatorAddress, getWethAddress, + verifyFoundryBinaries, }; diff --git a/src/utils/verifyFoundry.ts b/src/utils/verifyFoundry.ts new file mode 100644 index 00000000..e23ff037 --- /dev/null +++ b/src/utils/verifyFoundry.ts @@ -0,0 +1,47 @@ +import { execFile } from 'node:child_process'; + +const FOUNDRY_BINARIES: ['forge', 'cast'] = ['forge', 'cast']; + +function runVersionCommand(binary: 'forge' | 'cast'): Promise { + return new Promise((resolve, reject) => { + execFile(binary, ['--version'], (error, stdout) => { + if (error) { + reject(error); + return; + } + + resolve(stdout); + }); + }); +} + +export async function verifyFoundryBinaries(stableOnly = true) { + const results = await Promise.allSettled( + FOUNDRY_BINARIES.map((binary) => runVersionCommand(binary)), + ); + + const binariesPresent = results.every((result) => result.status === 'fulfilled'); + + const stableReleaseInstalled = results.every( + (result) => + result.status === 'fulfilled' && result.value && isStableFoundryRelease(result.value), + ); + + if (!binariesPresent) { + throw new Error( + 'Foundry is required to run this operation. Install Foundry and make sure forge and cast are available on PATH.', + ); + } + + if (stableOnly && !stableReleaseInstalled) { + throw new Error( + 'Foundry stable releases are required to run this operation. Please install the stable versions of forge and cast.', + ); + } +} + +function isStableFoundryRelease(version: string): boolean { + return !['nightly', 'dev', 'alpha', 'beta', 'rc', 'preview'].some((tag) => + version.toLowerCase().includes(tag), + ); +}