From 3d96f4fe42106988fa22434911fcef9deca9de47 Mon Sep 17 00:00:00 2001 From: Daniel Beal Date: Sat, 7 Sep 2024 12:02:30 +0900 Subject: [PATCH 1/2] error when a dependency input cannot be found. explicit dependency enforcement will significantly reduce the chance of typos or other mistake leading to unexpected empty values. --- packages/builder/src/definition.ts | 5 ++++- packages/builder/src/index.ts | 2 +- packages/cli/src/util/build.ts | 12 +++++++++++- packages/hardhat-cannon/src/tasks/build.ts | 12 +++++++++++- .../src/features/Deploy/QueueFromGitOpsPage.tsx | 17 +++++++++++++++++ 5 files changed, 44 insertions(+), 4 deletions(-) diff --git a/packages/builder/src/definition.ts b/packages/builder/src/definition.ts index 045f52e99..8619443c8 100644 --- a/packages/builder/src/definition.ts +++ b/packages/builder/src/definition.ts @@ -67,6 +67,8 @@ export class ChainDefinition { readonly dependencyFor = new Map(); readonly resolvedDependencies = new Map(); + readonly danglingDependencies = new Set<`${string}:${string}`>(); + constructor(def: RawChainDefinition, sensitiveDependencies = false) { debug('begin chain def init'); this.raw = def; @@ -355,8 +357,9 @@ export class ChainDefinition { debug(`deps: ${node} consumes ${input}`); if (this.dependencyFor.has(input)) { deps.push(this.dependencyFor.get(input)!); - } else if (!input.startsWith('settings.')) { + } else { debug(`WARNING: dependency ${input} not found for operation ${node}`); + this.danglingDependencies.add(`${input}:${node}`); } } } diff --git a/packages/builder/src/index.ts b/packages/builder/src/index.ts index 67c1b8272..aa1ce2d0b 100644 --- a/packages/builder/src/index.ts +++ b/packages/builder/src/index.ts @@ -8,7 +8,7 @@ export type { CannonLoader } from './loader'; export { IPFSLoader, InMemoryLoader } from './loader'; export { decodeTxError, renderTrace, findContract } from './trace'; export type { TraceEntry } from './trace'; -export { traceActions } from './error'; +export { traceActions, CannonError } from './error'; export { prepareMulticall } from './multicall'; // prevent dumb bugs diff --git a/packages/cli/src/util/build.ts b/packages/cli/src/util/build.ts index 23c46d006..5e1861999 100644 --- a/packages/cli/src/util/build.ts +++ b/packages/cli/src/util/build.ts @@ -14,7 +14,8 @@ import { parseSettings } from './params'; import { pickAnvilOptions } from './anvil'; import { setDebugLevel } from './debug-level'; import { ProviderAction, resolveProvider, isURL, getChainIdFromRpcUrl } from './provider'; - +import { CannonError } from '@usecannon/builder'; +import { bold, italic } from 'chalk'; const debug = Debug('cannon:cli'); /** @@ -227,6 +228,15 @@ async function prepareBuildConfig( ) { const { name, version, preset, def } = await loadCannonfile(cannonfile); + if (def.danglingDependencies.size) { + const neededDeps = Array.from(def.danglingDependencies).map((v) => v.split(':')); + throw new CannonError( + `Unknown template access found. Please ensure the following references are defined:\n${neededDeps + .map(([input, node]) => `${bold(input)} in ${italic(node)}`) + .join('\n')}` + ); + } + const packageSpecification = { name, version, diff --git a/packages/hardhat-cannon/src/tasks/build.ts b/packages/hardhat-cannon/src/tasks/build.ts index 01c7324f8..72b14c62e 100644 --- a/packages/hardhat-cannon/src/tasks/build.ts +++ b/packages/hardhat-cannon/src/tasks/build.ts @@ -3,7 +3,7 @@ import { CANNON_CHAIN_ID, CannonSigner } from '@usecannon/builder'; import { build, createDryRunRegistry, loadCannonfile, parseSettings, resolveCliSettings } from '@usecannon/cli'; import { getChainById } from '@usecannon/cli/dist/src/chains'; import { getProvider } from '@usecannon/cli/dist/src/rpc'; -import { bold, yellow, yellowBright } from 'chalk'; +import { bold, italic, yellow, yellowBright } from 'chalk'; import { TASK_COMPILE } from 'hardhat/builtin-tasks/task-names'; import { task } from 'hardhat/config'; import { HttpNetworkConfig } from 'hardhat/types'; @@ -14,6 +14,7 @@ import { loadPackageJson } from '../internal/load-pkg-json'; import { parseAnvilOptions } from '../internal/parse-anvil-options'; import { SubtaskRunAnvilNodeResult } from '../subtasks/run-anvil-node'; import { SUBTASK_GET_ARTIFACT, SUBTASK_RUN_ANVIL_NODE, TASK_BUILD } from '../task-names'; +import { CannonError } from '@usecannon/builder'; task(TASK_BUILD, 'Assemble a defined chain and save it to to a state which can be used later') .addPositionalParam('cannonfile', 'Path to a cannonfile to build', 'cannonfile.toml') @@ -75,6 +76,15 @@ task(TASK_BUILD, 'Assemble a defined chain and save it to to a state which can b const { name, version, def, preset } = await loadCannonfile(path.join(hre.config.paths.root, cannonfile)); + if (def.danglingDependencies.size) { + const neededDeps = Array.from(def.danglingDependencies).map((v) => v.split(':')); + throw new CannonError( + `Unknown template access found. Please ensure the following references are defined:\n${neededDeps + .map(([input, node]) => `${bold(input)} in ${italic(node)}`) + .join('\n')}` + ); + } + if (hre.network.name === 'hardhat' && dryRun) { throw new Error('You cannot use --dry-run param when using the "hardhat" network'); } diff --git a/packages/website/src/features/Deploy/QueueFromGitOpsPage.tsx b/packages/website/src/features/Deploy/QueueFromGitOpsPage.tsx index ce1490e37..88744fe67 100644 --- a/packages/website/src/features/Deploy/QueueFromGitOpsPage.tsx +++ b/packages/website/src/features/Deploy/QueueFromGitOpsPage.tsx @@ -446,6 +446,22 @@ function QueueFromGitOps() { ); } + if (cannonDefInfo.def && cannonDefInfo.def.danglingDependencies.size > 0) { + alertMessage = ( + <> + The cannonfile contains invalid dependencies. Please ensure the + following references are defined: + {Array.from(cannonDefInfo.def.danglingDependencies).map( + ([input, node]) => ( + + {input} in {node} + + ) + )} + + ); + } + return alertMessage ? ( @@ -459,6 +475,7 @@ function QueueFromGitOps() { const disablePreviewButton = chainId !== currentSafe?.chainId || !cannonDefInfo.def || + cannonDefInfo.def.danglingDependencies.size > 0 || cannonPkgPreviousInfo.isFetching || partialDeployInfo.isFetching || cannonPkgVersionInfo.isFetching || From ad03bbae2cc86074bbe1724deb320807cd83765d Mon Sep 17 00:00:00 2001 From: Daniel Beal Date: Tue, 17 Sep 2024 02:45:22 +0900 Subject: [PATCH 2/2] fix test failure caused by mock --- packages/cli/src/commands/build.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/src/commands/build.test.ts b/packages/cli/src/commands/build.test.ts index 95c329ba9..4a516acb7 100644 --- a/packages/cli/src/commands/build.test.ts +++ b/packages/cli/src/commands/build.test.ts @@ -79,7 +79,7 @@ describe('build', () => { let provider: viem.PublicClient & viem.WalletClient & viem.TestClient; beforeEach(() => { - jest.spyOn(helpers, 'loadCannonfile').mockResolvedValue({} as any); + jest.spyOn(helpers, 'loadCannonfile').mockResolvedValue({ def: { danglingDependencies: {} } } as any); provider = makeFakeProvider(); jest.spyOn(buildCommand, 'build').mockResolvedValue({ outputs: {}, provider, runtime: {} as any }); jest.spyOn(utilProvider, 'resolveProvider').mockResolvedValue({ provider: provider as any, signers: [] });