Skip to content

Commit 2aaa82b

Browse files
kcsongornvsriram
andauthored
cli: check solana-cli version (wormhole-foundation#645)
* cli: check solana-cli version This vastly improves the error message when the wrong solana-cli version is installed. Before this change, it just prints a cryptic file not found error. Also changed the `checkAnchorVersion` function to read the expected version from the Anchor.toml of the relevant worktree. This makes the function robust in the future in case future ntt versions are upgraded to newer anchor versions. * Update cli/src/index.ts Co-authored-by: Sriram Nurani Viswanathan <[email protected]> * cli: switch solana version detect the appropriate version picker and run it --------- Co-authored-by: Sriram Nurani Viswanathan <[email protected]>
1 parent e339a96 commit 2aaa82b

File tree

1 file changed

+114
-13
lines changed

1 file changed

+114
-13
lines changed

cli/src/index.ts

Lines changed: 114 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ function setNestedValue(obj: any, path: string[], value: any): void {
4747
}, obj);
4848
target[lastKey] = value;
4949
}
50+
5051
import { NTT, SolanaNtt } from "@wormhole-foundation/sdk-solana-ntt";
5152
import type { EvmNtt, EvmNttWormholeTranceiver } from "@wormhole-foundation/sdk-evm-ntt";
5253
import type { EvmChains, EvmNativeSigner, EvmUnsignedTransaction } from "@wormhole-foundation/sdk-evm";
@@ -1657,6 +1658,8 @@ async function deploySolana<N extends Network, C extends SolanaChains>(
16571658
): Promise<ChainAddress<C>> {
16581659
ensureNttRoot(pwd);
16591660

1661+
checkSolanaVersion(pwd);
1662+
16601663
// TODO: if the binary is provided, we should not check addresses in the source tree. (so we should move around the control flow a bit)
16611664
// TODO: factor out some of this into separate functions to help readability of this function (maybe even move to a different file)
16621665

@@ -1790,7 +1793,7 @@ async function deploySolana<N extends Network, C extends SolanaChains>(
17901793
} else {
17911794
// build the program
17921795
// TODO: build with docker
1793-
checkAnchorVersion();
1796+
checkAnchorVersion(pwd);
17941797
const proc = Bun.spawn(
17951798
["anchor",
17961799
"build",
@@ -2413,22 +2416,120 @@ export function ensureNttRoot(pwd: string = ".") {
24132416
}
24142417
}
24152418

2416-
function checkAnchorVersion() {
2417-
const expected = "0.29.0";
2419+
// Check Solana toolchain version against Anchor.toml requirements
2420+
function checkSolanaVersion(pwd: string): void {
24182421
try {
2419-
execSync("which anchor");
2420-
} catch {
2421-
console.error("Anchor CLI is not installed.\nSee https://www.anchor-lang.com/docs/installation")
2422-
process.exit(1);
2422+
// Read required version from Anchor.toml
2423+
const anchorToml = fs.readFileSync(`${pwd}/solana/Anchor.toml`, 'utf8');
2424+
const versionMatch = anchorToml.match(/solana_version = "(.+)"/);
2425+
2426+
if (!versionMatch) {
2427+
console.warn(chalk.yellow("Warning: Could not find solana_version in Anchor.toml"));
2428+
return;
2429+
}
2430+
2431+
const requiredVersion = versionMatch[1];
2432+
2433+
// Get current Solana version and detect client type
2434+
let currentVersion: string;
2435+
let clientType: 'agave' | 'solanalabs';
2436+
try {
2437+
const output = execSync('solana --version', { encoding: 'utf8', stdio: 'pipe' });
2438+
const versionMatch = output.match(/solana-cli (\d+\.\d+\.\d+)/);
2439+
if (!versionMatch) {
2440+
console.error(chalk.red("Error: Could not parse solana CLI version"));
2441+
process.exit(1);
2442+
}
2443+
currentVersion = versionMatch[1];
2444+
2445+
// Detect client type
2446+
if (output.includes('Agave')) {
2447+
clientType = 'agave';
2448+
} else if (output.includes('SolanaLabs')) {
2449+
clientType = 'solanalabs';
2450+
} else {
2451+
// Default to agave if we can't detect
2452+
clientType = 'agave';
2453+
}
2454+
} catch (error) {
2455+
console.error(chalk.red("Error: solana CLI not found. Please install the Solana toolchain."));
2456+
console.error(chalk.yellow("Install with: sh -c \"$(curl -sSfL https://release.anza.xyz/stable/install)\""));
2457+
process.exit(1);
2458+
}
2459+
2460+
if (currentVersion !== requiredVersion) {
2461+
console.log(chalk.yellow(`Solana version mismatch detected:`));
2462+
console.log(chalk.yellow(` Required: ${requiredVersion} (from Anchor.toml)`));
2463+
console.log(chalk.yellow(` Current: ${currentVersion}`));
2464+
console.log(chalk.yellow(`\nSwitching to required version...`));
2465+
2466+
// Run the appropriate version switch command
2467+
const installCommand = clientType === 'agave'
2468+
? `agave-install init ${requiredVersion}`
2469+
: `solana-install init ${requiredVersion}`;
2470+
2471+
try {
2472+
execSync(installCommand, { stdio: 'inherit' });
2473+
console.log(chalk.green(`Successfully switched to Solana version ${requiredVersion}`));
2474+
} catch (error) {
2475+
console.error(chalk.red(`Failed to switch Solana version using ${installCommand}`));
2476+
console.error(chalk.red(`Please run manually: ${installCommand}`));
2477+
process.exit(1);
2478+
}
2479+
}
2480+
} catch (error) {
2481+
if (error instanceof Error && 'code' in error && error.code === 'ENOENT') {
2482+
console.warn(chalk.yellow("Warning: Could not read Anchor.toml file"));
2483+
} else {
2484+
console.warn(chalk.yellow(`Warning: Failed to check Solana version: ${error instanceof Error ? error.message : error}`));
2485+
}
24232486
}
2424-
const version = execSync("anchor --version").toString().trim();
2425-
// version looks like "anchor-cli 0.14.0"
2426-
const [_, v] = version.split(" ");
2427-
if (v !== expected) {
2428-
console.error(`Anchor CLI version must be ${expected} but is ${v}`);
2429-
process.exit(1);
2487+
}
2488+
2489+
function checkAnchorVersion(pwd: string) {
2490+
try {
2491+
// Read required version from Anchor.toml
2492+
const anchorToml = fs.readFileSync(`${pwd}/solana/Anchor.toml`, 'utf8');
2493+
const versionMatch = anchorToml.match(/anchor_version = "(.+)"/);
2494+
2495+
if (!versionMatch) {
2496+
console.error(chalk.red("Error: Could not find anchor_version in Anchor.toml"));
2497+
process.exit(1);
2498+
}
2499+
2500+
const expected = versionMatch[1];
2501+
2502+
// Check if Anchor CLI is installed
2503+
try {
2504+
execSync("which anchor");
2505+
} catch {
2506+
console.error("Anchor CLI is not installed.\nSee https://www.anchor-lang.com/docs/installation")
2507+
process.exit(1);
2508+
}
2509+
2510+
// Get current Anchor version
2511+
const version = execSync("anchor --version").toString().trim();
2512+
// version looks like "anchor-cli 0.14.0"
2513+
const [_, v] = version.split(" ");
2514+
if (v !== expected) {
2515+
console.error(chalk.red(`Anchor CLI version mismatch!`));
2516+
console.error(chalk.red(` Required: ${expected} (from Anchor.toml)`));
2517+
console.error(chalk.red(` Current: ${v}`));
2518+
console.error(chalk.yellow(`\nTo fix this, install the correct version of Anchor`));
2519+
console.error(chalk.gray("See https://www.anchor-lang.com/docs/installation"));
2520+
process.exit(1);
2521+
}
2522+
} catch (error) {
2523+
if (error instanceof Error && 'code' in error && error.code === 'ENOENT') {
2524+
console.error(chalk.red("Error: Could not read Anchor.toml file"));
2525+
console.error(chalk.yellow(`Expected file at: ${pwd}/solana/Anchor.toml`));
2526+
process.exit(1);
2527+
} else {
2528+
throw error;
2529+
}
24302530
}
24312531
}
2532+
24322533
function loadConfig(path: string): Config {
24332534
if (!fs.existsSync(path)) {
24342535
console.error(`File not found: ${path}`);

0 commit comments

Comments
 (0)