From aa0c7e5134071188d4e430a35420644f664e6d15 Mon Sep 17 00:00:00 2001 From: dbeal Date: Mon, 23 Dec 2024 13:13:12 +0900 Subject: [PATCH] feat(cli): ability to print many different types of data with inspect (#1567) --- packages/cli/src/commands/config/index.ts | 6 +- packages/cli/src/commands/inspect.test.ts | 8 +- packages/cli/src/commands/inspect.ts | 32 ++-- packages/cli/src/index.ts | 142 +++++++++--------- .../cli/src/write-script/render-foundry.ts | 6 +- 5 files changed, 106 insertions(+), 88 deletions(-) diff --git a/packages/cli/src/commands/config/index.ts b/packages/cli/src/commands/config/index.ts index 5f6c22392..24b791efe 100644 --- a/packages/cli/src/commands/config/index.ts +++ b/packages/cli/src/commands/config/index.ts @@ -551,7 +551,11 @@ export const commandsConfig: CommandsConfig = { }, { flags: '-j --json', - description: 'Output as JSON', + description: 'DEPRECATED. use `--out json`. Output as JSON', + }, + { + flags: '-o --out ', + description: 'Output the given data. Options: `json` or `misc-json`', }, { flags: '-w --write-deployments ', diff --git a/packages/cli/src/commands/inspect.test.ts b/packages/cli/src/commands/inspect.test.ts index 72de46508..642726c55 100644 --- a/packages/cli/src/commands/inspect.test.ts +++ b/packages/cli/src/commands/inspect.test.ts @@ -139,7 +139,7 @@ describe('inspect', () => { }); test('should inspect package deployment', async () => { - const result = await inspect(packageName, cliSettings, chainId, false, '', false); + const result = await inspect(packageName, cliSettings, chainId, 'overview', '', false); expect(result).toEqual(testPkgData); expect(mockedFallBackRegistry.getUrl).toHaveBeenCalledWith(packageName, chainId); @@ -152,7 +152,7 @@ describe('inspect', () => { test('should write deployment files', async () => { const writeDeployments = 'contracts'; - const result = await inspect(packageName, cliSettings, chainId, false, writeDeployments, false); + const result = await inspect(packageName, cliSettings, chainId, 'overview', writeDeployments, false); expect(result).toEqual(testPkgData); expect(mockedFallBackRegistry.getUrl).toHaveBeenCalledWith(packageName, chainId); @@ -161,7 +161,7 @@ describe('inspect', () => { }); test('should call inspect with sources flag ', async () => { - const result = await inspect(packageName, cliSettings, chainId, false, '', true); + const result = await inspect(packageName, cliSettings, chainId, 'overview', '', true); expect(result).toEqual(testPkgData); expect(mockedFallBackRegistry.getUrl).toHaveBeenCalledWith(packageName, chainId); @@ -173,7 +173,7 @@ describe('inspect', () => { }); test('should call inspect with json flag ', async () => { - const result = await inspect(packageName, cliSettings, chainId, true, '', false); + const result = await inspect(packageName, cliSettings, chainId, 'deploy-json', '', false); expect(result).toEqual(testPkgData); expect(mockedFallBackRegistry.getUrl).toHaveBeenCalledWith(packageName, chainId); diff --git a/packages/cli/src/commands/inspect.ts b/packages/cli/src/commands/inspect.ts index 9a2c1964a..50f5bf8ed 100644 --- a/packages/cli/src/commands/inspect.ts +++ b/packages/cli/src/commands/inspect.ts @@ -4,6 +4,7 @@ import { ContractData, DeploymentState, fetchIPFSAvailability, + getArtifacts, PackageReference, } from '@usecannon/builder'; import { bold, cyan, green, yellow } from 'chalk'; @@ -23,7 +24,7 @@ export async function inspect( packageRef: string, cliSettings: CliSettings, chainId: number, - json: boolean, + out: 'overview' | 'deploy-json' | 'misc-json' | 'artifact-json', writeDeployments: string, sources: boolean ) { @@ -39,7 +40,7 @@ export async function inspect( // Mute all build outputs when printing the result to json, this is so it // doesn't break the result. - if (json) { + if (out && out !== 'overview') { // eslint-disable-next-line no-console console.log = debug; // eslint-disable-next-line no-console @@ -65,15 +66,17 @@ export async function inspect( ); } - if (json) { - // use process.stdout.write and write in chunks because bash piping seems to have some sort of - // a problem with outputting huge amounts of data all at once while using pipes - const toOutput = JSON.stringify(deployData, null, 2); - - const chunkSize = 16; - for (let i = 0; i < toOutput.length; i += chunkSize) { - process.stdout.write(toOutput.slice(i, i + chunkSize)); + if (out === 'deploy-json') { + _outputJson(deployData); + } else if (out === 'misc-json') { + if (!deployData.miscUrl) { + log('null'); + return; } + const miscData = await loader[deployData.miscUrl.split(':')[0] as 'ipfs'].read(deployData.miscUrl); + _outputJson(miscData); + } else if (out === 'artifact-json') { + _outputJson(getArtifacts(chainDefinition, deployData.state)); } else { const metaUrl = await resolver.getMetaUrl(fullPackageRef, chainId); const packageOwner = deployData.def.setting?.owner?.defaultValue; @@ -166,3 +169,12 @@ function _getNestedStateFiles(artifacts: ChainArtifacts, pathname: string, resul function _listSourceCodeContracts(miscData: any) { return Object.keys(_.pickBy(miscData.artifacts, (v) => v.source)); } + +function _outputJson(obj: object) { + const toOutput = JSON.stringify(obj, null, 2); + + const chunkSize = 16; + for (let i = 0; i < toOutput.length; i += chunkSize) { + process.stdout.write(toOutput.slice(i, i + chunkSize)); + } +} diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 72c744f62..938a798f5 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -101,7 +101,7 @@ function configureRun(program: Command) { return applyCommandsConfig(program, commandsConfig.run).action(async function ( packages: PackageSpecification[], options, - program + program, ) { log(bold('Starting local node...\n')); @@ -258,74 +258,67 @@ applyCommandsConfig(program.command('verify'), commandsConfig.verify).action(asy await verify(fullPackageRef, cliSettings, chainId); }); -applyCommandsConfig(program.command('diff'), commandsConfig.diff).action(async function ( - packageRef, - projectDirectory, - options -) { - const { diff } = await import('./commands/diff'); - - const cliSettings = resolveCliSettings(options); - const { fullPackageRef, chainId } = await getPackageInfo(packageRef, options.chainId, cliSettings.rpcUrl); - - const foundDiffs = await diff( - fullPackageRef, - cliSettings, - chainId, - projectDirectory, - options.matchContract, - options.matchSource - ); +applyCommandsConfig(program.command('diff'), commandsConfig.diff).action( + async function (packageRef, projectDirectory, options) { + const { diff } = await import('./commands/diff'); - // exit code is the number of differences found--useful for CI checks - process.exit(foundDiffs); -}); + const cliSettings = resolveCliSettings(options); + const { fullPackageRef, chainId } = await getPackageInfo(packageRef, options.chainId, cliSettings.rpcUrl); + + const foundDiffs = await diff( + fullPackageRef, + cliSettings, + chainId, + projectDirectory, + options.matchContract, + options.matchSource, + ); -applyCommandsConfig(program.command('alter'), commandsConfig.alter).action(async function ( - packageName, - command, - options, - flags -) { - const { alter } = await import('./commands/alter'); + // exit code is the number of differences found--useful for CI checks + process.exit(foundDiffs); + }, +); - const cliSettings = resolveCliSettings(flags); +applyCommandsConfig(program.command('alter'), commandsConfig.alter).action( + async function (packageName, command, options, flags) { + const { alter } = await import('./commands/alter'); - // throw an error if the chainId is not consistent with the provider's chainId - await ensureChainIdConsistency(cliSettings.rpcUrl, flags.chainId); + const cliSettings = resolveCliSettings(flags); - // note: for command below, pkgInfo is empty because forge currently supplies no package.json or anything similar - const newUrl = await alter( - packageName, - flags.subpkg ? flags.subpkg.split(',') : [], - parseInt(flags.chainId), - cliSettings, - {}, - command, - options, - {} - ); + // throw an error if the chainId is not consistent with the provider's chainId + await ensureChainIdConsistency(cliSettings.rpcUrl, flags.chainId); + + // note: for command below, pkgInfo is empty because forge currently supplies no package.json or anything similar + const newUrl = await alter( + packageName, + flags.subpkg ? flags.subpkg.split(',') : [], + parseInt(flags.chainId), + cliSettings, + {}, + command, + options, + {}, + ); - log(newUrl); -}); + log(newUrl); + }, +); -applyCommandsConfig(program.command('fetch'), commandsConfig.fetch).action(async function ( - packageRef, - givenIpfsUrl, - options -) { - const { fetch } = await import('./commands/fetch'); +applyCommandsConfig(program.command('fetch'), commandsConfig.fetch).action( + async function (packageRef, givenIpfsUrl, options) { + const { fetch } = await import('./commands/fetch'); - const { fullPackageRef, chainId } = await getPackageReference(packageRef, options.chainId); - const ipfsUrl = getIpfsUrl(givenIpfsUrl); - const metaIpfsUrl = getIpfsUrl(options.metaHash) || undefined; + const { fullPackageRef, chainId } = await getPackageReference(packageRef, options.chainId); + const ipfsUrl = getIpfsUrl(givenIpfsUrl); + const metaIpfsUrl = getIpfsUrl(options.metaHash) || undefined; - if (!ipfsUrl) { - throw new Error('IPFS URL is required.'); - } + if (!ipfsUrl) { + throw new Error('IPFS URL is required.'); + } - await fetch(fullPackageRef, chainId, ipfsUrl, metaIpfsUrl); -}); + await fetch(fullPackageRef, chainId, ipfsUrl, metaIpfsUrl); + }, +); applyCommandsConfig(program.command('pin'), commandsConfig.pin).action(async function (packageRef, options) { const cliSettings = resolveCliSettings(options); @@ -351,7 +344,7 @@ applyCommandsConfig(program.command('pin'), commandsConfig.pin).action(async fun applyCommandsConfig(program.command('publish'), commandsConfig.publish).action(async function ( packageRef, - options: { [opt: string]: string } + options: { [opt: string]: string }, ) { const { publish } = await import('./commands/publish'); @@ -428,8 +421,8 @@ applyCommandsConfig(program.command('publish'), commandsConfig.publish).action(a log(); log( gray( - `Package "${pkgRef.name}" not yet registered, please use "cannon register" to register your package first.\nYou need enough gas on Ethereum Mainnet to register the package on Cannon Registry` - ) + `Package "${pkgRef.name}" not yet registered, please use "cannon register" to register your package first.\nYou need enough gas on Ethereum Mainnet to register the package on Cannon Registry`, + ), ); log(); @@ -482,7 +475,7 @@ applyCommandsConfig(program.command('publish'), commandsConfig.publish).action(a }\n - Max Priority Fee Per Gas: ${ overrides.maxPriorityFeePerGas ? overrides.maxPriorityFeePerGas.toString() : 'default' }\n - Gas Limit: ${overrides.gasLimit ? overrides.gasLimit : 'default'}\n` + - " - To alter these settings use the parameters '--max-fee-per-gas', '--max-priority-fee-per-gas', '--gas-limit'.\n" + " - To alter these settings use the parameters '--max-fee-per-gas', '--max-priority-fee-per-gas', '--gas-limit'.\n", ); await publish({ @@ -528,7 +521,14 @@ applyCommandsConfig(program.command('inspect'), commandsConfig.inspect).action(a const cliSettings = resolveCliSettings(options); const { fullPackageRef, chainId } = await getPackageInfo(packageRef, options.chainId, cliSettings.rpcUrl); - await inspect(fullPackageRef, cliSettings, chainId, options.json, options.writeDeployments, options.sources); + await inspect( + fullPackageRef, + cliSettings, + chainId, + options.json ? 'json' : options.out, + options.writeDeployments, + options.sources, + ); }); applyCommandsConfig(program.command('prune'), commandsConfig.prune).action(async function (options) { @@ -548,7 +548,7 @@ applyCommandsConfig(program.command('prune'), commandsConfig.prune).action(async storage, options.filterPackage?.split(',') || '', options.filterVariant?.split(',') || '', - options.keepAge + options.keepAge, ); if (pruneUrls.length) { @@ -635,9 +635,9 @@ applyCommandsConfig(program.command('test'), commandsConfig.test).action(async f warn( yellowBright( bold( - '⚠️ The `--` syntax for passing options to forge or anvil is deprecated. Please use `--forge.*` or `--anvil.*` instead.' - ) - ) + '⚠️ The `--` syntax for passing options to forge or anvil is deprecated. Please use `--forge.*` or `--anvil.*` instead.', + ), + ), ); log(); } @@ -702,14 +702,14 @@ applyCommandsConfig(program.command('interact'), commandsConfig.interact).action priorityGasFee: options.maxPriorityFee, }, resolver, - getMainLoader(cliSettings) + getMainLoader(cliSettings), ); const deployData = await runtime.readDeploy(fullPackageRef, runtime.chainId); if (!deployData) { throw new Error( - `deployment not found for package: ${fullPackageRef} with chaindId ${chainId}. please make sure it exists for the given preset and current network.` + `deployment not found for package: ${fullPackageRef} with chaindId ${chainId}. please make sure it exists for the given preset and current network.`, ); } @@ -717,7 +717,7 @@ applyCommandsConfig(program.command('interact'), commandsConfig.interact).action if (!outputs) { throw new Error( - `no cannon build found for ${fullPackageRef} with chaindId ${chainId}. Did you mean to run the package instead?` + `no cannon build found for ${fullPackageRef} with chaindId ${chainId}. Did you mean to run the package instead?`, ); } diff --git a/packages/cli/src/write-script/render-foundry.ts b/packages/cli/src/write-script/render-foundry.ts index 2b0733bdb..49b320f00 100644 --- a/packages/cli/src/write-script/render-foundry.ts +++ b/packages/cli/src/write-script/render-foundry.ts @@ -1,6 +1,8 @@ import { EOL } from 'node:os'; import { Transform } from 'node:stream'; +import { getAddress } from 'viem'; + /** * This script is used to deploy contracts using Foundry Cast. * It outputs a bash script that can be used to deploy contracts and execute transactions. @@ -47,7 +49,7 @@ export const createRenderer = () => // Loggin txn types for (const c in line.result.contracts) { this.push(`${indent}// > CONTRACT DEPLOYED: ${line.result.contracts[c].address}\n`); - this.push(`${indent}getAddress[keccak256("${c}")] = address(${line.result.contracts[c].address});\n`); + this.push(`${indent}getAddress[keccak256("${c}")] = address(${getAddress(line.result.contracts[c].address)});\n`); } for (const t in line.result.txns) { @@ -57,7 +59,7 @@ export const createRenderer = () => for (const { to, from, input, value } of line.txns) { if (from) { - this.push(`${indent}vm.broadcast(address(${from}));\n`); + this.push(`${indent}vm.broadcast(${getAddress(from)});\n`); this.push(`${indent}data = hex"${input.slice(2)}";\n`); }