diff --git a/.changeset/orange-planes-juggle.md b/.changeset/orange-planes-juggle.md new file mode 100644 index 0000000000..bdf517fe22 --- /dev/null +++ b/.changeset/orange-planes-juggle.md @@ -0,0 +1,6 @@ +--- +'@hyperlane-xyz/infra': minor +'@hyperlane-xyz/cli': minor +--- + +Added support for multiple registries in CLI with prioritization. diff --git a/typescript/cli/cli.ts b/typescript/cli/cli.ts index 1485a1ab9f..03330b09ce 100644 --- a/typescript/cli/cli.ts +++ b/typescript/cli/cli.ts @@ -17,7 +17,7 @@ import { logFormatCommandOption, logLevelCommandOption, overrideRegistryUriCommandOption, - registryUriCommandOption, + registryUrisCommandOption, skipConfirmationOption, strategyCommandOption, } from './src/commands/options.js'; @@ -43,7 +43,7 @@ try { .scriptName('hyperlane') .option('log', logFormatCommandOption) .option('verbosity', logLevelCommandOption) - .option('registry', registryUriCommandOption) + .option('registry', registryUrisCommandOption) .option('overrides', overrideRegistryUriCommandOption) .option('key', keyCommandOption) .option('disableProxy', disableProxyCommandOption) diff --git a/typescript/cli/src/commands/options.ts b/typescript/cli/src/commands/options.ts index baf0fa8472..6e55f5eb7e 100644 --- a/typescript/cli/src/commands/options.ts +++ b/typescript/cli/src/commands/options.ts @@ -8,6 +8,8 @@ import { ENV } from '../utils/env.js'; /* Global options */ +export const DEFAULT_LOCAL_REGISTRY = `${os.homedir()}/.hyperlane`; + export const demandOption = (option: Options): Options => ({ ...option, demandOption: true, @@ -25,17 +27,20 @@ export const logLevelCommandOption: Options = { choices: Object.values(LogLevel), }; -export const registryUriCommandOption: Options = { - type: 'string', - description: 'Registry URI, such as a Github repo URL or a local file path', +export const registryUrisCommandOption: Options = { + type: 'array', + string: true, + description: + 'List of Github or local path registries, later registry takes priority over previous', alias: 'r', - default: DEFAULT_GITHUB_REGISTRY, + default: [DEFAULT_GITHUB_REGISTRY, DEFAULT_LOCAL_REGISTRY], }; export const overrideRegistryUriCommandOption: Options = { type: 'string', description: 'Path to a local registry to override the default registry', - default: `${os.homedir()}/.hyperlane`, + default: '', + hidden: true, }; export const skipConfirmationOption: Options = { diff --git a/typescript/cli/src/commands/relayer.ts b/typescript/cli/src/commands/relayer.ts index c07a4d4120..72beb35bf7 100644 --- a/typescript/cli/src/commands/relayer.ts +++ b/typescript/cli/src/commands/relayer.ts @@ -12,14 +12,14 @@ import { tryReadJson, writeJson } from '../utils/files.js'; import { getWarpCoreConfigOrExit } from '../utils/warp.js'; import { + DEFAULT_LOCAL_REGISTRY, agentTargetsCommandOption, - overrideRegistryUriCommandOption, symbolCommandOption, warpCoreConfigCommandOption, } from './options.js'; import { MessageOptionsArgTypes } from './send.js'; -const DEFAULT_RELAYER_CACHE = `${overrideRegistryUriCommandOption.default}/relayer-cache.json`; +const DEFAULT_RELAYER_CACHE = `${DEFAULT_LOCAL_REGISTRY}/relayer-cache.json`; export const relayerCommand: CommandModuleWithContext< MessageOptionsArgTypes & { diff --git a/typescript/cli/src/context/context.ts b/typescript/cli/src/context/context.ts index 7bfe83a643..bbd5436833 100644 --- a/typescript/cli/src/context/context.ts +++ b/typescript/cli/src/context/context.ts @@ -37,8 +37,10 @@ export async function contextMiddleware(argv: Record) { const isDryRun = !isNullish(argv.dryRun); const requiresKey = isSignCommand(argv); const settings: ContextSettings = { - registryUri: argv.registry, - registryOverrideUri: argv.overrides, + registryUris: [ + ...argv.registry, + ...(argv.overrides ? [argv.overrides] : []), + ], key: argv.key, fromAddress: argv.fromAddress, requiresKey, @@ -99,15 +101,14 @@ export async function signerMiddleware(argv: Record) { * @returns context for the current command */ export async function getContext({ - registryUri, - registryOverrideUri, + registryUris, key, requiresKey, skipConfirmation, disableProxy = false, strategyPath, }: ContextSettings): Promise { - const registry = getRegistry(registryUri, registryOverrideUri, !disableProxy); + const registry = getRegistry(registryUris, !disableProxy); //Just for backward compatibility let signerAddress: string | undefined = undefined; @@ -137,8 +138,7 @@ export async function getContext({ */ export async function getDryRunContext( { - registryUri, - registryOverrideUri, + registryUris, key, fromAddress, skipConfirmation, @@ -146,7 +146,7 @@ export async function getDryRunContext( }: ContextSettings, chain?: ChainName, ): Promise { - const registry = getRegistry(registryUri, registryOverrideUri, !disableProxy); + const registry = getRegistry(registryUris, !disableProxy); const chainMetadata = await registry.getMetadata(); if (!chain) { @@ -189,12 +189,11 @@ export async function getDryRunContext( * @returns a new MergedRegistry */ export function getRegistry( - primaryRegistryUri: string, - overrideRegistryUri: string, + registryUris: string[], enableProxy: boolean, ): IRegistry { const logger = rootLogger.child({ module: 'MergedRegistry' }); - const registries = [primaryRegistryUri, overrideRegistryUri] + const registries = registryUris .map((uri) => uri.trim()) .filter((uri) => !!uri) .map((uri, index) => { diff --git a/typescript/cli/src/context/types.ts b/typescript/cli/src/context/types.ts index c320ff3cac..78244b05b2 100644 --- a/typescript/cli/src/context/types.ts +++ b/typescript/cli/src/context/types.ts @@ -10,8 +10,7 @@ import type { } from '@hyperlane-xyz/sdk'; export interface ContextSettings { - registryUri: string; - registryOverrideUri: string; + registryUris: string[]; key?: string; fromAddress?: string; requiresKey?: boolean; diff --git a/typescript/cli/src/tests/commands/helpers.ts b/typescript/cli/src/tests/commands/helpers.ts index 977cc89393..57e82a5513 100644 --- a/typescript/cli/src/tests/commands/helpers.ts +++ b/typescript/cli/src/tests/commands/helpers.ts @@ -245,8 +245,7 @@ export async function deployOrUseExistingCore( key: string, ) { const { registry } = await getContext({ - registryUri: REGISTRY_PATH, - registryOverrideUri: '', + registryUris: [REGISTRY_PATH], key, }); const addresses = (await registry.getChainAddresses(chain)) as ChainAddresses; @@ -264,8 +263,7 @@ export async function getDomainId( key: string, ): Promise { const { registry } = await getContext({ - registryUri: REGISTRY_PATH, - registryOverrideUri: '', + registryUris: [REGISTRY_PATH], key, }); const chainMetadata = await registry.getChainMetadata(chainName); @@ -279,8 +277,7 @@ export async function deployToken( symbol = 'TOKEN', ): Promise { const { multiProvider } = await getContext({ - registryUri: REGISTRY_PATH, - registryOverrideUri: '', + registryUris: [REGISTRY_PATH], key: privateKey, }); @@ -306,8 +303,7 @@ export async function deploy4626Vault( tokenAddress: string, ) { const { multiProvider } = await getContext({ - registryUri: REGISTRY_PATH, - registryOverrideUri: '', + registryUris: [REGISTRY_PATH], key: privateKey, }); diff --git a/typescript/cli/src/tests/commands/warp.ts b/typescript/cli/src/tests/commands/warp.ts index 6f3a5d56f3..c797525f83 100644 --- a/typescript/cli/src/tests/commands/warp.ts +++ b/typescript/cli/src/tests/commands/warp.ts @@ -19,10 +19,8 @@ $.verbose = true; * Deploys the Warp route to the specified chain using the provided config. */ export function hyperlaneWarpInit(warpCorePath: string): ProcessPromise { - // --overrides is " " to allow local testing to work return $`yarn workspace @hyperlane-xyz/cli run hyperlane warp init \ --registry ${REGISTRY_PATH} \ - --overrides " " \ --out ${warpCorePath} \ --key ${ANVIL_KEY} \ --verbosity debug \ @@ -47,7 +45,6 @@ export function hyperlaneWarpDeployRaw({ hypKey ? ['HYP_KEY=' + hypKey] : '' } yarn workspace @hyperlane-xyz/cli run hyperlane warp deploy \ --registry ${REGISTRY_PATH} \ - --overrides " " \ ${warpCorePath ? ['--config', warpCorePath] : ''} \ ${privateKey ? ['--key', privateKey] : ''} \ --verbosity debug \ @@ -75,7 +72,6 @@ export async function hyperlaneWarpApply( ) { return $`yarn workspace @hyperlane-xyz/cli run hyperlane warp apply \ --registry ${REGISTRY_PATH} \ - --overrides " " \ --config ${warpDeployPath} \ --warp ${warpCorePath} \ --key ${ANVIL_KEY} \ @@ -99,7 +95,6 @@ export function hyperlaneWarpReadRaw({ }): ProcessPromise { return $`yarn workspace @hyperlane-xyz/cli run hyperlane warp read \ --registry ${REGISTRY_PATH} \ - --overrides " " \ ${warpAddress ? ['--address', warpAddress] : ''} \ ${chain ? ['--chain', chain] : ''} \ ${symbol ? ['--symbol', symbol] : ''} \ @@ -136,7 +131,6 @@ export function hyperlaneWarpCheckRaw({ hypKey && !privateKey ? ['HYP_KEY=' + hypKey] : '' } yarn workspace @hyperlane-xyz/cli run hyperlane warp check \ --registry ${REGISTRY_PATH} \ - --overrides " " \ ${symbol ? ['--symbol', symbol] : ''} \ ${privateKey && !hypKey ? ['--key', privateKey] : ''} \ --verbosity debug \ @@ -164,7 +158,6 @@ export function hyperlaneWarpSendRelay( return $`yarn workspace @hyperlane-xyz/cli run hyperlane warp send \ ${relay ? '--relay' : ''} \ --registry ${REGISTRY_PATH} \ - --overrides " " \ --origin ${origin} \ --destination ${destination} \ --warp ${warpCorePath} \ diff --git a/typescript/infra/config/warp.ts b/typescript/infra/config/warp.ts index d2f2eda937..7838df469b 100644 --- a/typescript/infra/config/warp.ts +++ b/typescript/infra/config/warp.ts @@ -114,8 +114,7 @@ async function getConfigFromMergedRegistry( warpRouteId: string, ): Promise> { const warpRoute = await getRegistry( - DEFAULT_REGISTRY_URI, - '', + [DEFAULT_REGISTRY_URI], true, ).getWarpDeployConfig(warpRouteId); assert(warpRoute, `Warp route Config not found for ${warpRouteId}`); diff --git a/typescript/infra/test/warp-configs.test.ts b/typescript/infra/test/warp-configs.test.ts index e1533cdca8..cc2ef88c18 100644 --- a/typescript/infra/test/warp-configs.test.ts +++ b/typescript/infra/test/warp-configs.test.ts @@ -27,8 +27,7 @@ describe('Warp Configs', async function () { before(async function () { multiProvider = (await getHyperlaneCore(ENV)).multiProvider; configsFromGithub = await getRegistry( - DEFAULT_GITHUB_REGISTRY, - '', + [DEFAULT_GITHUB_REGISTRY], true, ).getWarpDeployConfigs(); }); diff --git a/typescript/infra/test/warpIds.test.ts b/typescript/infra/test/warpIds.test.ts index 7ced361c40..f8efa34aa0 100644 --- a/typescript/infra/test/warpIds.test.ts +++ b/typescript/infra/test/warpIds.test.ts @@ -7,7 +7,7 @@ import { WarpRouteIds } from '../config/environments/mainnet3/warp/warpIds.js'; describe('Warp IDs', () => { it('Has all warp IDs in the registry', () => { - const registry = getRegistry(DEFAULT_GITHUB_REGISTRY, '', true); + const registry = getRegistry([DEFAULT_GITHUB_REGISTRY], true); for (const warpId of Object.values(WarpRouteIds)) { // That's a long sentence! expect(