@0xsequence/catapult 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +29 -0
- package/.github/workflows/ci.yml +181 -0
- package/CONCEPT.md +24 -0
- package/README.md +772 -0
- package/contracts/checked-call.huff +65 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +16 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/common.d.ts +11 -0
- package/dist/commands/common.d.ts.map +1 -0
- package/dist/commands/common.js +73 -0
- package/dist/commands/common.js.map +1 -0
- package/dist/commands/dry.d.ts +3 -0
- package/dist/commands/dry.d.ts.map +1 -0
- package/dist/commands/dry.js +171 -0
- package/dist/commands/dry.js.map +1 -0
- package/dist/commands/etherscan.d.ts +3 -0
- package/dist/commands/etherscan.d.ts.map +1 -0
- package/dist/commands/etherscan.js +323 -0
- package/dist/commands/etherscan.js.map +1 -0
- package/dist/commands/index.d.ts +6 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +22 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/list.d.ts +3 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +259 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/run.d.ts +3 -0
- package/dist/commands/run.d.ts.map +1 -0
- package/dist/commands/run.js +96 -0
- package/dist/commands/run.js.map +1 -0
- package/dist/commands/utils.d.ts +3 -0
- package/dist/commands/utils.d.ts.map +1 -0
- package/dist/commands/utils.js +46 -0
- package/dist/commands/utils.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +58 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/__tests__/deployer-events.spec.d.ts +2 -0
- package/dist/lib/__tests__/deployer-events.spec.d.ts.map +1 -0
- package/dist/lib/__tests__/deployer-events.spec.js +260 -0
- package/dist/lib/__tests__/deployer-events.spec.js.map +1 -0
- package/dist/lib/__tests__/deployer.spec.d.ts +2 -0
- package/dist/lib/__tests__/deployer.spec.d.ts.map +1 -0
- package/dist/lib/__tests__/deployer.spec.js +884 -0
- package/dist/lib/__tests__/deployer.spec.js.map +1 -0
- package/dist/lib/__tests__/network-utils.spec.d.ts +2 -0
- package/dist/lib/__tests__/network-utils.spec.d.ts.map +1 -0
- package/dist/lib/__tests__/network-utils.spec.js +140 -0
- package/dist/lib/__tests__/network-utils.spec.js.map +1 -0
- package/dist/lib/contracts/__tests__/repository.spec.d.ts +2 -0
- package/dist/lib/contracts/__tests__/repository.spec.d.ts.map +1 -0
- package/dist/lib/contracts/__tests__/repository.spec.js +321 -0
- package/dist/lib/contracts/__tests__/repository.spec.js.map +1 -0
- package/dist/lib/contracts/repository.d.ts +27 -0
- package/dist/lib/contracts/repository.d.ts.map +1 -0
- package/dist/lib/contracts/repository.js +241 -0
- package/dist/lib/contracts/repository.js.map +1 -0
- package/dist/lib/core/__tests__/engine.spec.d.ts +2 -0
- package/dist/lib/core/__tests__/engine.spec.d.ts.map +1 -0
- package/dist/lib/core/__tests__/engine.spec.js +1212 -0
- package/dist/lib/core/__tests__/engine.spec.js.map +1 -0
- package/dist/lib/core/__tests__/graph.spec.d.ts +2 -0
- package/dist/lib/core/__tests__/graph.spec.d.ts.map +1 -0
- package/dist/lib/core/__tests__/graph.spec.js +116 -0
- package/dist/lib/core/__tests__/graph.spec.js.map +1 -0
- package/dist/lib/core/__tests__/json-integration.spec.d.ts +2 -0
- package/dist/lib/core/__tests__/json-integration.spec.d.ts.map +1 -0
- package/dist/lib/core/__tests__/json-integration.spec.js +300 -0
- package/dist/lib/core/__tests__/json-integration.spec.js.map +1 -0
- package/dist/lib/core/__tests__/loader.spec.d.ts +2 -0
- package/dist/lib/core/__tests__/loader.spec.d.ts.map +1 -0
- package/dist/lib/core/__tests__/loader.spec.js +288 -0
- package/dist/lib/core/__tests__/loader.spec.js.map +1 -0
- package/dist/lib/core/__tests__/multi-platform-verification.spec.d.ts +2 -0
- package/dist/lib/core/__tests__/multi-platform-verification.spec.d.ts.map +1 -0
- package/dist/lib/core/__tests__/multi-platform-verification.spec.js +342 -0
- package/dist/lib/core/__tests__/multi-platform-verification.spec.js.map +1 -0
- package/dist/lib/core/__tests__/resolver.spec.d.ts +2 -0
- package/dist/lib/core/__tests__/resolver.spec.d.ts.map +1 -0
- package/dist/lib/core/__tests__/resolver.spec.js +1367 -0
- package/dist/lib/core/__tests__/resolver.spec.js.map +1 -0
- package/dist/lib/core/__tests__/static-action.spec.d.ts +2 -0
- package/dist/lib/core/__tests__/static-action.spec.d.ts.map +1 -0
- package/dist/lib/core/__tests__/static-action.spec.js +136 -0
- package/dist/lib/core/__tests__/static-action.spec.js.map +1 -0
- package/dist/lib/core/context.d.ts +29 -0
- package/dist/lib/core/context.d.ts.map +1 -0
- package/dist/lib/core/context.js +88 -0
- package/dist/lib/core/context.js.map +1 -0
- package/dist/lib/core/engine.d.ts +25 -0
- package/dist/lib/core/engine.d.ts.map +1 -0
- package/dist/lib/core/engine.js +1191 -0
- package/dist/lib/core/engine.js.map +1 -0
- package/dist/lib/core/graph.d.ts +18 -0
- package/dist/lib/core/graph.d.ts.map +1 -0
- package/dist/lib/core/graph.js +158 -0
- package/dist/lib/core/graph.js.map +1 -0
- package/dist/lib/core/loader.d.ts +25 -0
- package/dist/lib/core/loader.d.ts.map +1 -0
- package/dist/lib/core/loader.js +248 -0
- package/dist/lib/core/loader.js.map +1 -0
- package/dist/lib/core/resolver.d.ts +20 -0
- package/dist/lib/core/resolver.d.ts.map +1 -0
- package/dist/lib/core/resolver.js +307 -0
- package/dist/lib/core/resolver.js.map +1 -0
- package/dist/lib/deployer.d.ts +39 -0
- package/dist/lib/deployer.d.ts.map +1 -0
- package/dist/lib/deployer.js +533 -0
- package/dist/lib/deployer.js.map +1 -0
- package/dist/lib/events/__tests__/event-system.spec.d.ts +2 -0
- package/dist/lib/events/__tests__/event-system.spec.d.ts.map +1 -0
- package/dist/lib/events/__tests__/event-system.spec.js +256 -0
- package/dist/lib/events/__tests__/event-system.spec.js.map +1 -0
- package/dist/lib/events/cli-adapter.d.ts +13 -0
- package/dist/lib/events/cli-adapter.d.ts.map +1 -0
- package/dist/lib/events/cli-adapter.js +244 -0
- package/dist/lib/events/cli-adapter.js.map +1 -0
- package/dist/lib/events/emitter.d.ts +11 -0
- package/dist/lib/events/emitter.d.ts.map +1 -0
- package/dist/lib/events/emitter.js +29 -0
- package/dist/lib/events/emitter.js.map +1 -0
- package/dist/lib/events/index.d.ts +4 -0
- package/dist/lib/events/index.d.ts.map +1 -0
- package/dist/lib/events/index.js +20 -0
- package/dist/lib/events/index.js.map +1 -0
- package/dist/lib/events/types.d.ts +368 -0
- package/dist/lib/events/types.d.ts.map +1 -0
- package/dist/lib/events/types.js +3 -0
- package/dist/lib/events/types.js.map +1 -0
- package/dist/lib/index.d.ts +5 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js +44 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/network-loader.d.ts +3 -0
- package/dist/lib/network-loader.d.ts.map +1 -0
- package/dist/lib/network-loader.js +80 -0
- package/dist/lib/network-loader.js.map +1 -0
- package/dist/lib/network-match.d.ts +3 -0
- package/dist/lib/network-match.d.ts.map +1 -0
- package/dist/lib/network-match.js +62 -0
- package/dist/lib/network-match.js.map +1 -0
- package/dist/lib/network-utils.d.ts +4 -0
- package/dist/lib/network-utils.d.ts.map +1 -0
- package/dist/lib/network-utils.js +39 -0
- package/dist/lib/network-utils.js.map +1 -0
- package/dist/lib/parsers/__tests__/buildinfo.spec.d.ts +2 -0
- package/dist/lib/parsers/__tests__/buildinfo.spec.d.ts.map +1 -0
- package/dist/lib/parsers/__tests__/buildinfo.spec.js +132 -0
- package/dist/lib/parsers/__tests__/buildinfo.spec.js.map +1 -0
- package/dist/lib/parsers/__tests__/job.spec.d.ts +2 -0
- package/dist/lib/parsers/__tests__/job.spec.d.ts.map +1 -0
- package/dist/lib/parsers/__tests__/job.spec.js +318 -0
- package/dist/lib/parsers/__tests__/job.spec.js.map +1 -0
- package/dist/lib/parsers/__tests__/template.spec.d.ts +2 -0
- package/dist/lib/parsers/__tests__/template.spec.d.ts.map +1 -0
- package/dist/lib/parsers/__tests__/template.spec.js +126 -0
- package/dist/lib/parsers/__tests__/template.spec.js.map +1 -0
- package/dist/lib/parsers/artifact/__tests__/artifact.spec.d.ts +2 -0
- package/dist/lib/parsers/artifact/__tests__/artifact.spec.d.ts.map +1 -0
- package/dist/lib/parsers/artifact/__tests__/artifact.spec.js +128 -0
- package/dist/lib/parsers/artifact/__tests__/artifact.spec.js.map +1 -0
- package/dist/lib/parsers/artifact/foundry-1.2.d.ts +3 -0
- package/dist/lib/parsers/artifact/foundry-1.2.d.ts.map +1 -0
- package/dist/lib/parsers/artifact/foundry-1.2.js +82 -0
- package/dist/lib/parsers/artifact/foundry-1.2.js.map +1 -0
- package/dist/lib/parsers/artifact/index.d.ts +3 -0
- package/dist/lib/parsers/artifact/index.d.ts.map +1 -0
- package/dist/lib/parsers/artifact/index.js +17 -0
- package/dist/lib/parsers/artifact/index.js.map +1 -0
- package/dist/lib/parsers/artifact/types.d.ts +3 -0
- package/dist/lib/parsers/artifact/types.d.ts.map +1 -0
- package/dist/lib/parsers/artifact/types.js +3 -0
- package/dist/lib/parsers/artifact/types.js.map +1 -0
- package/dist/lib/parsers/buildinfo.d.ts +5 -0
- package/dist/lib/parsers/buildinfo.d.ts.map +1 -0
- package/dist/lib/parsers/buildinfo.js +85 -0
- package/dist/lib/parsers/buildinfo.js.map +1 -0
- package/dist/lib/parsers/constants.d.ts +4 -0
- package/dist/lib/parsers/constants.d.ts.map +1 -0
- package/dist/lib/parsers/constants.js +45 -0
- package/dist/lib/parsers/constants.js.map +1 -0
- package/dist/lib/parsers/index.d.ts +5 -0
- package/dist/lib/parsers/index.d.ts.map +1 -0
- package/dist/lib/parsers/index.js +21 -0
- package/dist/lib/parsers/index.js.map +1 -0
- package/dist/lib/parsers/job.d.ts +3 -0
- package/dist/lib/parsers/job.d.ts.map +1 -0
- package/dist/lib/parsers/job.js +74 -0
- package/dist/lib/parsers/job.js.map +1 -0
- package/dist/lib/parsers/template.d.ts +3 -0
- package/dist/lib/parsers/template.d.ts.map +1 -0
- package/dist/lib/parsers/template.js +91 -0
- package/dist/lib/parsers/template.js.map +1 -0
- package/dist/lib/std/templates/assured-deployment.yaml +45 -0
- package/dist/lib/std/templates/erc-2470.yaml +67 -0
- package/dist/lib/std/templates/min-balance.yaml +32 -0
- package/dist/lib/std/templates/nano-universal-deployer.yaml +59 -0
- package/dist/lib/std/templates/raw-erc-2470.yaml +59 -0
- package/dist/lib/std/templates/raw-nano-universal-deployer.yaml +51 -0
- package/dist/lib/std/templates/raw-sequence-universal-deployer-2.yaml +48 -0
- package/dist/lib/std/templates/sequence-universal-deployer-2.yaml +57 -0
- package/dist/lib/types/__tests__/json-request-action.spec.d.ts +2 -0
- package/dist/lib/types/__tests__/json-request-action.spec.d.ts.map +1 -0
- package/dist/lib/types/__tests__/json-request-action.spec.js +219 -0
- package/dist/lib/types/__tests__/json-request-action.spec.js.map +1 -0
- package/dist/lib/types/__tests__/read-json-value.spec.d.ts +2 -0
- package/dist/lib/types/__tests__/read-json-value.spec.d.ts.map +1 -0
- package/dist/lib/types/__tests__/read-json-value.spec.js +233 -0
- package/dist/lib/types/__tests__/read-json-value.spec.js.map +1 -0
- package/dist/lib/types/actions.d.ts +74 -0
- package/dist/lib/types/actions.d.ts.map +1 -0
- package/dist/lib/types/actions.js +18 -0
- package/dist/lib/types/actions.js.map +1 -0
- package/dist/lib/types/artifacts.d.ts +15 -0
- package/dist/lib/types/artifacts.d.ts.map +1 -0
- package/dist/lib/types/artifacts.js +3 -0
- package/dist/lib/types/artifacts.js.map +1 -0
- package/dist/lib/types/buildinfo.d.ts +112 -0
- package/dist/lib/types/buildinfo.d.ts.map +1 -0
- package/dist/lib/types/buildinfo.js +3 -0
- package/dist/lib/types/buildinfo.js.map +1 -0
- package/dist/lib/types/conditions.d.ts +17 -0
- package/dist/lib/types/conditions.d.ts.map +1 -0
- package/dist/lib/types/conditions.js +21 -0
- package/dist/lib/types/conditions.js.map +1 -0
- package/dist/lib/types/contracts.d.ts +14 -0
- package/dist/lib/types/contracts.d.ts.map +1 -0
- package/dist/lib/types/contracts.js +3 -0
- package/dist/lib/types/contracts.js.map +1 -0
- package/dist/lib/types/definitions.d.ts +51 -0
- package/dist/lib/types/definitions.d.ts.map +1 -0
- package/dist/lib/types/definitions.js +3 -0
- package/dist/lib/types/definitions.js.map +1 -0
- package/dist/lib/types/index.d.ts +9 -0
- package/dist/lib/types/index.d.ts.map +1 -0
- package/dist/lib/types/index.js +25 -0
- package/dist/lib/types/index.js.map +1 -0
- package/dist/lib/types/network.d.ts +9 -0
- package/dist/lib/types/network.d.ts.map +1 -0
- package/dist/lib/types/network.js +3 -0
- package/dist/lib/types/network.js.map +1 -0
- package/dist/lib/types/project.d.ts +5 -0
- package/dist/lib/types/project.d.ts.map +1 -0
- package/dist/lib/types/project.js +3 -0
- package/dist/lib/types/project.js.map +1 -0
- package/dist/lib/types/task.d.ts +9 -0
- package/dist/lib/types/task.d.ts.map +1 -0
- package/dist/lib/types/task.js +3 -0
- package/dist/lib/types/task.js.map +1 -0
- package/dist/lib/types/values.d.ts +78 -0
- package/dist/lib/types/values.d.ts.map +1 -0
- package/dist/lib/types/values.js +3 -0
- package/dist/lib/types/values.js.map +1 -0
- package/dist/lib/utils/validation.d.ts +5 -0
- package/dist/lib/utils/validation.d.ts.map +1 -0
- package/dist/lib/utils/validation.js +77 -0
- package/dist/lib/utils/validation.js.map +1 -0
- package/dist/lib/validation/contract-references.d.ts +12 -0
- package/dist/lib/validation/contract-references.d.ts.map +1 -0
- package/dist/lib/validation/contract-references.js +112 -0
- package/dist/lib/validation/contract-references.js.map +1 -0
- package/dist/lib/validation/index.d.ts +1 -0
- package/dist/lib/validation/index.d.ts.map +1 -0
- package/dist/lib/validation/index.js +2 -0
- package/dist/lib/validation/index.js.map +1 -0
- package/dist/lib/verification/__tests__/etherscan.spec.d.ts +2 -0
- package/dist/lib/verification/__tests__/etherscan.spec.d.ts.map +1 -0
- package/dist/lib/verification/__tests__/etherscan.spec.js +565 -0
- package/dist/lib/verification/__tests__/etherscan.spec.js.map +1 -0
- package/dist/lib/verification/__tests__/sourcify.spec.d.ts +2 -0
- package/dist/lib/verification/__tests__/sourcify.spec.d.ts.map +1 -0
- package/dist/lib/verification/__tests__/sourcify.spec.js +212 -0
- package/dist/lib/verification/__tests__/sourcify.spec.js.map +1 -0
- package/dist/lib/verification/etherscan.d.ts +56 -0
- package/dist/lib/verification/etherscan.d.ts.map +1 -0
- package/dist/lib/verification/etherscan.js +340 -0
- package/dist/lib/verification/etherscan.js.map +1 -0
- package/dist/lib/verification/sourcify.d.ts +12 -0
- package/dist/lib/verification/sourcify.d.ts.map +1 -0
- package/dist/lib/verification/sourcify.js +227 -0
- package/dist/lib/verification/sourcify.js.map +1 -0
- package/eslint.config.js +48 -0
- package/examples/jobs/guards-v1.yaml +17 -0
- package/examples/jobs/sequence-seq-0001-patch.yaml +59 -0
- package/examples/jobs/sequence-v1.yaml +59 -0
- package/examples/templates/sequence-factory-v1.yaml +56 -0
- package/jest.config.js +25 -0
- package/package.json +68 -0
- package/src/cli.ts +17 -0
- package/src/commands/common.ts +61 -0
- package/src/commands/dry.ts +208 -0
- package/src/commands/etherscan.ts +360 -0
- package/src/commands/index.ts +5 -0
- package/src/commands/list.ts +249 -0
- package/src/commands/run.ts +136 -0
- package/src/commands/utils.ts +52 -0
- package/src/index.ts +67 -0
- package/src/lib/__tests__/deployer-events.spec.ts +338 -0
- package/src/lib/__tests__/deployer.spec.ts +1204 -0
- package/src/lib/__tests__/network-utils.spec.ts +181 -0
- package/src/lib/artifacts/__tests__/fixtures/contract1.json +19 -0
- package/src/lib/artifacts/__tests__/fixtures/contract2.json +19 -0
- package/src/lib/artifacts/__tests__/fixtures/duplicate-name.json +19 -0
- package/src/lib/artifacts/__tests__/fixtures/nested/nested-contract.json +18 -0
- package/src/lib/artifacts/__tests__/fixtures/not-an-artifact.json +8 -0
- package/src/lib/artifacts/__tests__/fixtures/readme.txt +2 -0
- package/src/lib/contracts/__tests__/repository.spec.ts +344 -0
- package/src/lib/contracts/repository.ts +313 -0
- package/src/lib/core/__tests__/engine.spec.ts +1514 -0
- package/src/lib/core/__tests__/graph.spec.ts +125 -0
- package/src/lib/core/__tests__/json-integration.spec.ts +360 -0
- package/src/lib/core/__tests__/loader.spec.ts +334 -0
- package/src/lib/core/__tests__/multi-platform-verification.spec.ts +406 -0
- package/src/lib/core/__tests__/resolver.spec.ts +1693 -0
- package/src/lib/core/__tests__/static-action.spec.ts +172 -0
- package/src/lib/core/context.ts +127 -0
- package/src/lib/core/engine.ts +1531 -0
- package/src/lib/core/graph.ts +252 -0
- package/src/lib/core/loader.ts +263 -0
- package/src/lib/core/resolver.ts +498 -0
- package/src/lib/deployer.ts +768 -0
- package/src/lib/events/__tests__/event-system.spec.ts +343 -0
- package/src/lib/events/cli-adapter.ts +325 -0
- package/src/lib/events/emitter.ts +62 -0
- package/src/lib/events/index.ts +3 -0
- package/src/lib/events/types.ts +469 -0
- package/src/lib/index.ts +14 -0
- package/src/lib/network-loader.ts +59 -0
- package/src/lib/network-utils.ts +64 -0
- package/src/lib/parsers/__tests__/buildinfo.spec.ts +122 -0
- package/src/lib/parsers/__tests__/fixtures/buildinfo/invalid-bytecode-buildinfo.json +62 -0
- package/src/lib/parsers/__tests__/fixtures/buildinfo/invalid-json.txt +2 -0
- package/src/lib/parsers/__tests__/fixtures/buildinfo/multi-contract-buildinfo.json +89 -0
- package/src/lib/parsers/__tests__/fixtures/buildinfo/no-contracts-buildinfo.json +17 -0
- package/src/lib/parsers/__tests__/fixtures/buildinfo/simple-buildinfo.json +63 -0
- package/src/lib/parsers/__tests__/fixtures/buildinfo/wrong-format.json +4 -0
- package/src/lib/parsers/__tests__/job.spec.ts +335 -0
- package/src/lib/parsers/__tests__/template.spec.ts +111 -0
- package/src/lib/parsers/artifact/__tests__/artifact.spec.ts +117 -0
- package/src/lib/parsers/artifact/__tests__/fixtures/empty-bytecode.json +5 -0
- package/src/lib/parsers/artifact/__tests__/fixtures/hardhat-artifact.json +67 -0
- package/src/lib/parsers/artifact/__tests__/fixtures/invalid-bytecode.json +5 -0
- package/src/lib/parsers/artifact/__tests__/fixtures/invalid-json.txt +11 -0
- package/src/lib/parsers/artifact/__tests__/fixtures/minimal-artifact.json +5 -0
- package/src/lib/parsers/artifact/__tests__/fixtures/missing-abi.json +4 -0
- package/src/lib/parsers/artifact/__tests__/fixtures/missing-bytecode.json +11 -0
- package/src/lib/parsers/artifact/__tests__/fixtures/missing-contract-name.json +11 -0
- package/src/lib/parsers/artifact/__tests__/fixtures/simple-artifact.json +40 -0
- package/src/lib/parsers/artifact/__tests__/fixtures/wrong-types.json +7 -0
- package/src/lib/parsers/artifact/foundry-1.2.ts +72 -0
- package/src/lib/parsers/artifact/index.ts +27 -0
- package/src/lib/parsers/artifact/types.ts +9 -0
- package/src/lib/parsers/buildinfo.ts +127 -0
- package/src/lib/parsers/constants.ts +56 -0
- package/src/lib/parsers/index.ts +5 -0
- package/src/lib/parsers/job.ts +101 -0
- package/src/lib/parsers/template.ts +131 -0
- package/src/lib/std/templates/assured-deployment.yaml +45 -0
- package/src/lib/std/templates/erc-2470.yaml +67 -0
- package/src/lib/std/templates/min-balance.yaml +32 -0
- package/src/lib/std/templates/nano-universal-deployer.yaml +59 -0
- package/src/lib/std/templates/raw-erc-2470.yaml +59 -0
- package/src/lib/std/templates/raw-nano-universal-deployer.yaml +51 -0
- package/src/lib/std/templates/raw-sequence-universal-deployer-2.yaml +48 -0
- package/src/lib/std/templates/sequence-universal-deployer-2.yaml +57 -0
- package/src/lib/types/__tests__/json-request-action.spec.ts +243 -0
- package/src/lib/types/__tests__/read-json-value.spec.ts +264 -0
- package/src/lib/types/actions.ts +127 -0
- package/src/lib/types/artifacts.ts +21 -0
- package/src/lib/types/buildinfo.ts +116 -0
- package/src/lib/types/conditions.ts +50 -0
- package/src/lib/types/contracts.ts +23 -0
- package/src/lib/types/definitions.ts +68 -0
- package/src/lib/types/index.ts +8 -0
- package/src/lib/types/network.ts +22 -0
- package/src/lib/types/project.ts +9 -0
- package/src/lib/types/task.ts +9 -0
- package/src/lib/types/values.ts +116 -0
- package/src/lib/utils/validation.ts +116 -0
- package/src/lib/validation/contract-references.ts +210 -0
- package/src/lib/validation/index.ts +1 -0
- package/src/lib/verification/__tests__/etherscan.spec.ts +710 -0
- package/src/lib/verification/__tests__/sourcify.spec.ts +288 -0
- package/src/lib/verification/etherscan.ts +546 -0
- package/src/lib/verification/sourcify.ts +248 -0
- package/test_validation/artifacts/TestContract.json +9 -0
- package/test_validation/jobs/test-missing.yaml +16 -0
- package/test_validation/networks.yaml +3 -0
- package/tsconfig.json +36 -0
|
@@ -0,0 +1,768 @@
|
|
|
1
|
+
import * as fs from 'fs/promises'
|
|
2
|
+
import * as path from 'path'
|
|
3
|
+
|
|
4
|
+
import { ProjectLoader, ProjectLoaderOptions } from './core/loader'
|
|
5
|
+
import { DependencyGraph } from './core/graph'
|
|
6
|
+
import { ExecutionEngine } from './core/engine'
|
|
7
|
+
import { createDefaultVerificationRegistry } from './verification/etherscan'
|
|
8
|
+
import { ExecutionContext } from './core/context'
|
|
9
|
+
import { Network, Job } from './types'
|
|
10
|
+
import { DeploymentEventEmitter, deploymentEvents } from './events'
|
|
11
|
+
import type { RunSummaryEvent } from './events'
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Options for configuring a Deployer instance.
|
|
15
|
+
*/
|
|
16
|
+
export interface DeployerOptions {
|
|
17
|
+
/** The root directory of the deployment project. */
|
|
18
|
+
projectRoot: string
|
|
19
|
+
|
|
20
|
+
/** The private key of the EOA to be used as the signer/relayer. Optional if an implicit sender from RPC is desired. */
|
|
21
|
+
privateKey?: string
|
|
22
|
+
|
|
23
|
+
/** An array of network configurations to use for deployment. */
|
|
24
|
+
networks: Network[]
|
|
25
|
+
|
|
26
|
+
/** Optional: An array of job names to execute. If not provided, all jobs are considered. */
|
|
27
|
+
runJobs?: string[]
|
|
28
|
+
|
|
29
|
+
/** Optional: An array of chain IDs to run on. If not provided, all configured networks are used. */
|
|
30
|
+
runOnNetworks?: number[]
|
|
31
|
+
|
|
32
|
+
/** Optional: Custom event emitter instance. If not provided, uses the global singleton. */
|
|
33
|
+
eventEmitter?: DeploymentEventEmitter
|
|
34
|
+
|
|
35
|
+
/** Optional: Project loader options (e.g., whether to load standard templates). */
|
|
36
|
+
loaderOptions?: ProjectLoaderOptions
|
|
37
|
+
|
|
38
|
+
/** Optional Etherscan API key for contract verification. */
|
|
39
|
+
etherscanApiKey?: string
|
|
40
|
+
|
|
41
|
+
/** Optional: Stop execution as soon as any job fails. Defaults to false. */
|
|
42
|
+
failEarly?: boolean
|
|
43
|
+
|
|
44
|
+
/** Optional: Skip post-execution check of skip conditions. Defaults to false (post-check enabled). */
|
|
45
|
+
noPostCheckConditions?: boolean
|
|
46
|
+
|
|
47
|
+
/** Optional: When true, write outputs in a flat directory instead of mirroring the jobs dir structure. */
|
|
48
|
+
flatOutput?: boolean
|
|
49
|
+
|
|
50
|
+
/** Optional: Allow running jobs marked as deprecated when true. */
|
|
51
|
+
runDeprecated?: boolean
|
|
52
|
+
|
|
53
|
+
/** Optional: Show end-of-run summary (default: true). */
|
|
54
|
+
showSummary?: boolean
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* The Deployer is the top-level orchestrator for the entire deployment process.
|
|
59
|
+
* It loads a project, builds the dependency graph, and executes jobs across
|
|
60
|
+
* specified networks in the correct order.
|
|
61
|
+
*/
|
|
62
|
+
export class Deployer {
|
|
63
|
+
private readonly options: DeployerOptions
|
|
64
|
+
public readonly events: DeploymentEventEmitter
|
|
65
|
+
private readonly loader: ProjectLoader
|
|
66
|
+
private readonly noPostCheckConditions: boolean
|
|
67
|
+
private readonly showSummary: boolean
|
|
68
|
+
|
|
69
|
+
// Store both successful and failed execution results
|
|
70
|
+
private readonly results = new Map<string, {
|
|
71
|
+
job: Job;
|
|
72
|
+
outputs: Map<number, { status: 'success' | 'error'; data: Map<string, unknown> | string }>
|
|
73
|
+
}>()
|
|
74
|
+
private graph?: DependencyGraph
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
constructor(options: DeployerOptions) {
|
|
78
|
+
this.options = options
|
|
79
|
+
this.events = options.eventEmitter || deploymentEvents
|
|
80
|
+
this.loader = new ProjectLoader(options.projectRoot, options.loaderOptions)
|
|
81
|
+
this.noPostCheckConditions = options.noPostCheckConditions ?? false
|
|
82
|
+
this.showSummary = options.showSummary !== false
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Runs the entire deployment process from loading to execution and outputting results.
|
|
88
|
+
*/
|
|
89
|
+
public async run(): Promise<void> {
|
|
90
|
+
this.events.emitEvent({
|
|
91
|
+
type: 'deployment_started',
|
|
92
|
+
level: 'info',
|
|
93
|
+
data: {
|
|
94
|
+
projectRoot: this.options.projectRoot
|
|
95
|
+
}
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
// 1. Load all project artifacts, templates, and jobs.
|
|
100
|
+
this.events.emitEvent({
|
|
101
|
+
type: 'project_loading_started',
|
|
102
|
+
level: 'info',
|
|
103
|
+
data: {
|
|
104
|
+
projectRoot: this.options.projectRoot
|
|
105
|
+
}
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
await this.loader.load()
|
|
109
|
+
|
|
110
|
+
this.events.emitEvent({
|
|
111
|
+
type: 'project_loaded',
|
|
112
|
+
level: 'info',
|
|
113
|
+
data: {
|
|
114
|
+
jobCount: this.loader.jobs.size,
|
|
115
|
+
templateCount: this.loader.templates.size
|
|
116
|
+
}
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
// 2. Build the dependency graph and determine execution order.
|
|
120
|
+
const graph = new DependencyGraph(this.loader.jobs, this.loader.templates)
|
|
121
|
+
this.graph = graph
|
|
122
|
+
const jobOrder = graph.getExecutionOrder()
|
|
123
|
+
|
|
124
|
+
// 3. Filter jobs and networks based on user options.
|
|
125
|
+
const jobsToRun = this.getJobExecutionPlan(jobOrder)
|
|
126
|
+
|
|
127
|
+
// Inform about skipped deprecated jobs (when applicable)
|
|
128
|
+
if (!this.options.runDeprecated) {
|
|
129
|
+
const skippedDeprecated = jobOrder.filter(name => {
|
|
130
|
+
const j = this.loader.jobs.get(name) as { deprecated?: boolean } | undefined
|
|
131
|
+
return !jobsToRun.includes(name) && j?.deprecated === true
|
|
132
|
+
})
|
|
133
|
+
if (skippedDeprecated.length > 0) {
|
|
134
|
+
this.events.emitEvent({
|
|
135
|
+
type: 'deprecated_jobs_skipped',
|
|
136
|
+
level: 'warn',
|
|
137
|
+
data: { jobs: skippedDeprecated }
|
|
138
|
+
})
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
const targetNetworks = this.getTargetNetworks()
|
|
142
|
+
|
|
143
|
+
this.events.emitEvent({
|
|
144
|
+
type: 'execution_plan',
|
|
145
|
+
level: 'info',
|
|
146
|
+
data: {
|
|
147
|
+
targetNetworks: targetNetworks.map(n => ({
|
|
148
|
+
name: n.name,
|
|
149
|
+
chainId: n.chainId
|
|
150
|
+
})),
|
|
151
|
+
jobExecutionOrder: jobsToRun
|
|
152
|
+
}
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
// 4. Execute the plan.
|
|
156
|
+
const verificationRegistry = createDefaultVerificationRegistry(this.options.etherscanApiKey)
|
|
157
|
+
const engine = new ExecutionEngine(this.loader.templates, this.events, verificationRegistry, this.noPostCheckConditions)
|
|
158
|
+
|
|
159
|
+
// Track if any jobs have failed
|
|
160
|
+
let hasFailures = false
|
|
161
|
+
|
|
162
|
+
for (const network of targetNetworks) {
|
|
163
|
+
this.events.emitEvent({
|
|
164
|
+
type: 'network_started',
|
|
165
|
+
level: 'info',
|
|
166
|
+
data: {
|
|
167
|
+
networkName: network.name,
|
|
168
|
+
chainId: network.chainId
|
|
169
|
+
}
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
for (const jobName of jobsToRun) {
|
|
173
|
+
const job = this.loader.jobs.get(jobName)!
|
|
174
|
+
|
|
175
|
+
if (this.shouldSkipJobOnNetwork(job, network)) {
|
|
176
|
+
this.events.emitEvent({
|
|
177
|
+
type: 'job_skipped',
|
|
178
|
+
level: 'warn',
|
|
179
|
+
data: {
|
|
180
|
+
jobName,
|
|
181
|
+
networkName: network.name,
|
|
182
|
+
reason: 'configuration'
|
|
183
|
+
}
|
|
184
|
+
})
|
|
185
|
+
continue
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Initialize results storage for this job if not exists
|
|
189
|
+
if (!this.results.has(job.name)) {
|
|
190
|
+
this.results.set(job.name, { job, outputs: new Map() })
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
let context: ExecutionContext | undefined
|
|
194
|
+
try {
|
|
195
|
+
context = new ExecutionContext(
|
|
196
|
+
network,
|
|
197
|
+
this.options.privateKey,
|
|
198
|
+
this.loader.contractRepository,
|
|
199
|
+
this.options.etherscanApiKey,
|
|
200
|
+
this.loader.constants
|
|
201
|
+
)
|
|
202
|
+
// Set job-level constants if present (guard for mocked contexts in tests)
|
|
203
|
+
if (typeof (context as unknown as { setJobConstants?: (constants: unknown) => void }).setJobConstants === 'function') {
|
|
204
|
+
(context as unknown as { setJobConstants: (constants: unknown) => void }).setJobConstants(job.constants)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Populate context with outputs from previously executed dependent jobs
|
|
208
|
+
this.populateContextWithDependentJobOutputs(job, context, network)
|
|
209
|
+
|
|
210
|
+
await engine.executeJob(job, context)
|
|
211
|
+
|
|
212
|
+
// Store successful results
|
|
213
|
+
this.results.get(job.name)!.outputs.set(network.chainId, {
|
|
214
|
+
status: 'success',
|
|
215
|
+
data: (context as { getOutputs(): Map<string, unknown> }).getOutputs()
|
|
216
|
+
})
|
|
217
|
+
} catch (error) {
|
|
218
|
+
// Store error results
|
|
219
|
+
const errorMessage = error instanceof Error ? error.message : String(error)
|
|
220
|
+
this.results.get(job.name)!.outputs.set(network.chainId, {
|
|
221
|
+
status: 'error',
|
|
222
|
+
data: errorMessage
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
this.events.emitEvent({
|
|
226
|
+
type: 'job_execution_failed',
|
|
227
|
+
level: 'error',
|
|
228
|
+
data: {
|
|
229
|
+
jobName: job.name,
|
|
230
|
+
networkName: network.name,
|
|
231
|
+
chainId: network.chainId,
|
|
232
|
+
error: errorMessage
|
|
233
|
+
}
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
// Mark that we have failures
|
|
237
|
+
hasFailures = true
|
|
238
|
+
|
|
239
|
+
// If fail-early is enabled, throw the error immediately
|
|
240
|
+
if (this.options.failEarly) {
|
|
241
|
+
throw error
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Otherwise, continue to next job/network
|
|
245
|
+
} finally {
|
|
246
|
+
// Clean up the context to prevent hanging connections
|
|
247
|
+
if (context) {
|
|
248
|
+
try {
|
|
249
|
+
await context.dispose()
|
|
250
|
+
} catch (disposeError) {
|
|
251
|
+
// Log disposal errors but don't let them interrupt the flow
|
|
252
|
+
this.events.emitEvent({
|
|
253
|
+
type: 'context_disposal_warning',
|
|
254
|
+
level: 'warn',
|
|
255
|
+
data: {
|
|
256
|
+
jobName: job.name,
|
|
257
|
+
networkName: network.name,
|
|
258
|
+
error: disposeError instanceof Error ? disposeError.message : String(disposeError)
|
|
259
|
+
}
|
|
260
|
+
})
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// 5. Write results to output files.
|
|
268
|
+
await this.writeOutputFiles()
|
|
269
|
+
|
|
270
|
+
// Emit end-of-run summary before final status
|
|
271
|
+
if (this.showSummary) {
|
|
272
|
+
this.emitRunSummary(hasFailures)
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Check if any jobs failed and exit with error if so
|
|
276
|
+
if (hasFailures) {
|
|
277
|
+
const error = new Error('One or more jobs failed during execution')
|
|
278
|
+
this.events.emitEvent({
|
|
279
|
+
type: 'deployment_failed',
|
|
280
|
+
level: 'error',
|
|
281
|
+
data: {
|
|
282
|
+
error: error.message,
|
|
283
|
+
stack: error.stack
|
|
284
|
+
}
|
|
285
|
+
})
|
|
286
|
+
throw error
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
this.events.emitEvent({
|
|
290
|
+
type: 'deployment_completed',
|
|
291
|
+
level: 'info'
|
|
292
|
+
})
|
|
293
|
+
} catch (error) {
|
|
294
|
+
this.events.emitEvent({
|
|
295
|
+
type: 'deployment_failed',
|
|
296
|
+
level: 'error',
|
|
297
|
+
data: {
|
|
298
|
+
error: error instanceof Error ? error.message : String(error),
|
|
299
|
+
stack: error instanceof Error ? error.stack : undefined
|
|
300
|
+
}
|
|
301
|
+
})
|
|
302
|
+
// Re-throw to allow CLI to exit with a non-zero code
|
|
303
|
+
throw error
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Emit a concise run summary event for the CLI to render at the end.
|
|
309
|
+
*/
|
|
310
|
+
private emitRunSummary(hasFailures: boolean): void {
|
|
311
|
+
// Compute counts
|
|
312
|
+
const jobCount = this.results.size
|
|
313
|
+
let successCount = 0
|
|
314
|
+
let failedCount = 0
|
|
315
|
+
const skippedCount = 0
|
|
316
|
+
|
|
317
|
+
// Detect skipped by comparing planned jobs across networks vs executed entries
|
|
318
|
+
// Here we approximate: an entry exists per job and per network outcome. We count
|
|
319
|
+
// successes/errors; skips were emitted as events during execution and are not persisted
|
|
320
|
+
// in results. We cannot perfectly reconstruct skipped count without tracking, so we
|
|
321
|
+
// expose it as 0 for now; could be improved by tracking per-network skip events.
|
|
322
|
+
|
|
323
|
+
for (const [, result] of this.results) {
|
|
324
|
+
for (const [, netResult] of result.outputs) {
|
|
325
|
+
if (netResult.status === 'success') successCount++
|
|
326
|
+
else failedCount++
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Collect key contract addresses from outputs for quick visibility
|
|
331
|
+
const keyContracts: Array<{ job: string; action: string; address: string }> = []
|
|
332
|
+
for (const [, result] of this.results) {
|
|
333
|
+
for (const [, netResult] of result.outputs) {
|
|
334
|
+
if (netResult.status !== 'success') continue
|
|
335
|
+
const outputs = netResult.data as Map<string, unknown>
|
|
336
|
+
for (const [k, v] of outputs) {
|
|
337
|
+
if (k.endsWith('.address') && typeof v === 'string') {
|
|
338
|
+
const action = k.split('.')[0]
|
|
339
|
+
keyContracts.push({ job: result.job.name, action, address: v })
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
const summaryEvent = {
|
|
346
|
+
type: 'run_summary',
|
|
347
|
+
level: (hasFailures ? 'warn' : 'info') as 'info' | 'warn',
|
|
348
|
+
data: {
|
|
349
|
+
networkCount: this.options.networks.length,
|
|
350
|
+
jobCount,
|
|
351
|
+
successCount,
|
|
352
|
+
failedCount,
|
|
353
|
+
skippedCount,
|
|
354
|
+
keyContracts: keyContracts.slice(0, 10)
|
|
355
|
+
}
|
|
356
|
+
} satisfies Omit<RunSummaryEvent, 'timestamp'>
|
|
357
|
+
|
|
358
|
+
this.events.emitEvent(summaryEvent)
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Determines the final, ordered list of jobs to execute based on user input.
|
|
363
|
+
* If a user requests specific jobs, this ensures all their dependencies are also included.
|
|
364
|
+
*/
|
|
365
|
+
private getJobExecutionPlan(fullOrder: string[]): string[] {
|
|
366
|
+
// Expand provided runJobs to concrete job names by supporting simple glob patterns
|
|
367
|
+
const expandRunJobs = (patterns: string[]): string[] => {
|
|
368
|
+
const allJobNames = Array.from(this.loader.jobs.keys())
|
|
369
|
+
|
|
370
|
+
const isPattern = (s: string): boolean => /[*?]/.test(s)
|
|
371
|
+
const escapeRegex = (s: string): string => s.replace(/[-\\^$+?.()|[\]{}*?]/g, '\\$&')
|
|
372
|
+
const patternToRegex = (pattern: string): RegExp => {
|
|
373
|
+
// Escape regex metacharacters, then translate wildcard tokens
|
|
374
|
+
const escaped = escapeRegex(pattern)
|
|
375
|
+
.replace(/\\\*/g, '.*') // escaped '*' -> '.*'
|
|
376
|
+
.replace(/\\\?/g, '.') // escaped '?' -> '.'
|
|
377
|
+
return new RegExp(`^${escaped}$`)
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
const expanded: string[] = []
|
|
381
|
+
const seen = new Set<string>()
|
|
382
|
+
|
|
383
|
+
for (const p of patterns) {
|
|
384
|
+
if (!isPattern(p)) {
|
|
385
|
+
// Exact name; validate exists
|
|
386
|
+
if (!this.loader.jobs.has(p)) {
|
|
387
|
+
throw new Error(`Specified job "${p}" not found in project.`)
|
|
388
|
+
}
|
|
389
|
+
if (!seen.has(p)) {
|
|
390
|
+
seen.add(p)
|
|
391
|
+
expanded.push(p)
|
|
392
|
+
}
|
|
393
|
+
continue
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
const re = patternToRegex(p)
|
|
397
|
+
const matches = allJobNames.filter(name => re.test(name))
|
|
398
|
+
if (matches.length === 0) {
|
|
399
|
+
throw new Error(`Job pattern "${p}" did not match any jobs in project.`)
|
|
400
|
+
}
|
|
401
|
+
for (const m of matches) {
|
|
402
|
+
if (!seen.has(m)) {
|
|
403
|
+
seen.add(m)
|
|
404
|
+
expanded.push(m)
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
return expanded
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// Helper to decide if a job is deprecated
|
|
413
|
+
const isDeprecated = (jobName: string): boolean => {
|
|
414
|
+
const j = this.loader.jobs.get(jobName)
|
|
415
|
+
return !!(j && (j as { deprecated?: boolean }).deprecated === true)
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// If user didn't specify jobs explicitly, include all non-deprecated jobs.
|
|
419
|
+
// Additionally, ALWAYS include deprecated jobs when they are dependencies of any non-deprecated job.
|
|
420
|
+
if (!this.options.runJobs || this.options.runJobs.length === 0) {
|
|
421
|
+
if (this.options.runDeprecated) {
|
|
422
|
+
return fullOrder
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
const nonDeprecatedJobs = new Set(fullOrder.filter(name => !isDeprecated(name)))
|
|
426
|
+
|
|
427
|
+
// Collect deprecated jobs that are required by any non-deprecated job
|
|
428
|
+
const requiredDeprecated = new Set<string>()
|
|
429
|
+
for (const jobName of nonDeprecatedJobs) {
|
|
430
|
+
const deps = this.graph?.getDependencies(jobName) || new Set<string>()
|
|
431
|
+
for (const dep of deps) {
|
|
432
|
+
if (isDeprecated(dep)) {
|
|
433
|
+
requiredDeprecated.add(dep)
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
const allowed = new Set<string>([...nonDeprecatedJobs, ...requiredDeprecated])
|
|
439
|
+
return fullOrder.filter(name => allowed.has(name))
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// Expand patterns to concrete names
|
|
443
|
+
const expandedRunJobs = expandRunJobs(this.options.runJobs)
|
|
444
|
+
const explicitlyRequested = new Set<string>(expandedRunJobs)
|
|
445
|
+
|
|
446
|
+
const jobsToRun = new Set<string>()
|
|
447
|
+
for (const jobName of expandedRunJobs) {
|
|
448
|
+
jobsToRun.add(jobName)
|
|
449
|
+
const dependencies = this.graph?.getDependencies(jobName) || new Set()
|
|
450
|
+
dependencies.forEach((dep: string) => jobsToRun.add(dep))
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// Deprecated dependencies must be kept even when --run-deprecated is not set.
|
|
454
|
+
// Only drop deprecated jobs that are neither explicitly requested nor required as a dependency.
|
|
455
|
+
const depsOfRequested = new Set<string>()
|
|
456
|
+
for (const jobName of expandedRunJobs) {
|
|
457
|
+
const deps = this.graph?.getDependencies(jobName) || new Set<string>()
|
|
458
|
+
deps.forEach(d => depsOfRequested.add(d))
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
const filtered = Array.from(jobsToRun).filter(name => {
|
|
462
|
+
if (!isDeprecated(name)) return true
|
|
463
|
+
if (explicitlyRequested.has(name)) return true
|
|
464
|
+
if (depsOfRequested.has(name)) return true // keep deprecated dependency
|
|
465
|
+
return this.options.runDeprecated === true
|
|
466
|
+
})
|
|
467
|
+
const allowedSet = new Set(filtered)
|
|
468
|
+
|
|
469
|
+
// Filter the original execution order to only include the required jobs, preserving the correct sequence.
|
|
470
|
+
return fullOrder.filter(jobName => allowedSet.has(jobName))
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Determines the final list of networks to run on based on user input.
|
|
475
|
+
*/
|
|
476
|
+
private getTargetNetworks(): Network[] {
|
|
477
|
+
if (!this.options.runOnNetworks || this.options.runOnNetworks.length === 0) {
|
|
478
|
+
return this.options.networks // Run on all configured networks
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
const targetChainIds = new Set(this.options.runOnNetworks)
|
|
482
|
+
const filteredNetworks = this.options.networks.filter(n => targetChainIds.has(n.chainId))
|
|
483
|
+
|
|
484
|
+
if (filteredNetworks.length !== this.options.runOnNetworks.length) {
|
|
485
|
+
const foundIds = new Set(filteredNetworks.map(n => n.chainId))
|
|
486
|
+
const missingIds = this.options.runOnNetworks.filter(id => !foundIds.has(id))
|
|
487
|
+
this.events.emitEvent({
|
|
488
|
+
type: 'missing_network_config_warning',
|
|
489
|
+
level: 'warn',
|
|
490
|
+
data: {
|
|
491
|
+
missingChainIds: missingIds
|
|
492
|
+
}
|
|
493
|
+
})
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
return filteredNetworks
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/**
|
|
500
|
+
* Checks a job's `only_networks` and `skip_networks` fields to see if it should run on the given network.
|
|
501
|
+
*/
|
|
502
|
+
private shouldSkipJobOnNetwork(job: Job, network: Network): boolean {
|
|
503
|
+
// Note: This relies on `only_networks` and `skip_networks` being present on the Job type.
|
|
504
|
+
const jobWithNetworkFilters = job as Job & { only_networks?: number[]; skip_networks?: number[] }
|
|
505
|
+
|
|
506
|
+
// Check only_networks: if present, the job only runs on these networks.
|
|
507
|
+
if (jobWithNetworkFilters.only_networks && jobWithNetworkFilters.only_networks.length > 0) {
|
|
508
|
+
return !jobWithNetworkFilters.only_networks.includes(network.chainId)
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// Check skip_networks: if present, the job skips these networks.
|
|
512
|
+
if (jobWithNetworkFilters.skip_networks && jobWithNetworkFilters.skip_networks.length > 0) {
|
|
513
|
+
return jobWithNetworkFilters.skip_networks.includes(network.chainId)
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
return false // Run by default
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* Populates the execution context with outputs from previously executed dependent jobs.
|
|
521
|
+
*/
|
|
522
|
+
private populateContextWithDependentJobOutputs(job: Job, context: ExecutionContext, network: Network): void {
|
|
523
|
+
if (!job.depends_on) return
|
|
524
|
+
|
|
525
|
+
for (const dependentJobName of job.depends_on) {
|
|
526
|
+
const dependentJobResults = this.results.get(dependentJobName)
|
|
527
|
+
if (!dependentJobResults) continue
|
|
528
|
+
|
|
529
|
+
const networkResult = dependentJobResults.outputs.get(network.chainId)
|
|
530
|
+
if (!networkResult || networkResult.status !== 'success') continue
|
|
531
|
+
|
|
532
|
+
// Add outputs with job name prefixes for cross-job access
|
|
533
|
+
const outputs = networkResult.data as Map<string, unknown>
|
|
534
|
+
for (const [key, value] of outputs.entries()) {
|
|
535
|
+
const prefixedKey = `${dependentJobName}.${key}`
|
|
536
|
+
context.setOutput(prefixedKey, value)
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+
|
|
543
|
+
/**
|
|
544
|
+
* Writes the collected deployment results to JSON files in the output directory.
|
|
545
|
+
* By default, mirrors the jobs directory structure under output/. When flatOutput
|
|
546
|
+
* is true, writes all job JSONs directly under output/.
|
|
547
|
+
*/
|
|
548
|
+
private async writeOutputFiles(): Promise<void> {
|
|
549
|
+
if (this.results.size === 0) {
|
|
550
|
+
this.events.emitEvent({
|
|
551
|
+
type: 'no_outputs',
|
|
552
|
+
level: 'warn'
|
|
553
|
+
})
|
|
554
|
+
return
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
const outputRoot = path.join(this.options.projectRoot, 'output')
|
|
558
|
+
await fs.mkdir(outputRoot, { recursive: true })
|
|
559
|
+
|
|
560
|
+
this.events.emitEvent({
|
|
561
|
+
type: 'output_writing_started',
|
|
562
|
+
level: 'info'
|
|
563
|
+
})
|
|
564
|
+
|
|
565
|
+
for (const [jobName, resultData] of this.results.entries()) {
|
|
566
|
+
// Determine relative subpath for this job based on its source path under jobs/
|
|
567
|
+
let relativeJobSubpath = `${jobName}.json`
|
|
568
|
+
if (!this.options.flatOutput && resultData.job._path) {
|
|
569
|
+
// Find jobs directory within project
|
|
570
|
+
const jobsDir = path.join(this.options.projectRoot, 'jobs')
|
|
571
|
+
const normalizedJobPath = path.normalize(resultData.job._path)
|
|
572
|
+
const normalizedJobsDir = path.normalize(jobsDir)
|
|
573
|
+
if (normalizedJobPath.startsWith(normalizedJobsDir)) {
|
|
574
|
+
// Compute relative path from jobs dir to the yaml file, and replace extension with .json
|
|
575
|
+
const relFromJobs = path.relative(normalizedJobsDir, normalizedJobPath)
|
|
576
|
+
const dirPart = path.dirname(relFromJobs)
|
|
577
|
+
const fileBase = path.basename(relFromJobs, path.extname(relFromJobs))
|
|
578
|
+
relativeJobSubpath = dirPart === '.' ? `${fileBase}.json` : path.join(dirPart, `${fileBase}.json`)
|
|
579
|
+
} else {
|
|
580
|
+
// Fallback to job name if path isn't within jobs dir
|
|
581
|
+
relativeJobSubpath = `${jobName}.json`
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
const outputFilePath = path.join(outputRoot, relativeJobSubpath)
|
|
586
|
+
const outputFileDir = path.dirname(outputFilePath)
|
|
587
|
+
await fs.mkdir(outputFileDir, { recursive: true })
|
|
588
|
+
|
|
589
|
+
// Group networks by identical status and outputs
|
|
590
|
+
const groupedResults = this.groupNetworkResults(resultData.outputs, resultData.job)
|
|
591
|
+
|
|
592
|
+
const fileContent = {
|
|
593
|
+
jobName: resultData.job.name,
|
|
594
|
+
jobVersion: resultData.job.version,
|
|
595
|
+
lastRun: new Date().toISOString(),
|
|
596
|
+
networks: groupedResults
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
await fs.writeFile(outputFilePath, JSON.stringify(fileContent, null, 2))
|
|
600
|
+
this.events.emitEvent({
|
|
601
|
+
type: 'output_file_written',
|
|
602
|
+
level: 'info',
|
|
603
|
+
data: {
|
|
604
|
+
relativePath: path.relative(this.options.projectRoot, outputFilePath)
|
|
605
|
+
}
|
|
606
|
+
})
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
/**
|
|
611
|
+
* Filters outputs according to job actions' output selection:
|
|
612
|
+
* - output: true -> include all outputs for that action
|
|
613
|
+
* - output: false -> exclude outputs for that action
|
|
614
|
+
* - output: object -> include ONLY the specified keys, resolved from context if they are placeholders
|
|
615
|
+
*
|
|
616
|
+
* If no actions have output: true or object (i.e., only false/undefined), includes all outputs (backward compatibility),
|
|
617
|
+
* but excludes dependency outputs when there are explicit dependencies defined.
|
|
618
|
+
*/
|
|
619
|
+
private filterOutputsByActionFlags(outputs: Map<string, unknown>, job: Job): Record<string, unknown> {
|
|
620
|
+
// Partition actions by output config
|
|
621
|
+
const actionsWithCustomMap = job.actions.filter(a => a.output && typeof a.output === 'object' && a.output !== null) as Array<Job['actions'][number] & { output: Record<string, unknown> }>
|
|
622
|
+
const actionsWithTrue = job.actions.filter(a => a.output === true)
|
|
623
|
+
const actionsWithFalse = new Set(job.actions.filter(a => a.output === false).map(a => a.name))
|
|
624
|
+
|
|
625
|
+
// If there are any custom maps, include only those mapped keys for those actions.
|
|
626
|
+
// Collect explicit inclusions here.
|
|
627
|
+
const result = new Map<string, unknown>()
|
|
628
|
+
|
|
629
|
+
// Helper to include by prefix
|
|
630
|
+
const includeAllForAction = (actionName: string) => {
|
|
631
|
+
for (const [key, value] of outputs) {
|
|
632
|
+
if (key.startsWith(`${actionName}.`)) {
|
|
633
|
+
result.set(key, value)
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
// 1) Handle custom output maps (highest precedence and explicit selection)
|
|
639
|
+
if (actionsWithCustomMap.length > 0) {
|
|
640
|
+
for (const action of actionsWithCustomMap) {
|
|
641
|
+
const prefix = `${action.name}.`
|
|
642
|
+
// For mapped keys, accept either fully qualified keys (e.g., "txHash") which we map to `${action.name}.txHash`
|
|
643
|
+
// or already-qualified keys (rare). We'll normalize to prefixed keys in the output.
|
|
644
|
+
for (const mappedKey of Object.keys(action.output)) {
|
|
645
|
+
// If user provided fully-qualified "action.key", strip if redundant
|
|
646
|
+
const normalizedKey = mappedKey.startsWith(prefix) ? mappedKey : `${prefix}${mappedKey}`
|
|
647
|
+
// Only include if present in outputs map
|
|
648
|
+
if (outputs.has(normalizedKey)) {
|
|
649
|
+
result.set(normalizedKey, outputs.get(normalizedKey)!)
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
// Note: when any custom maps exist, we DO NOT automatically include actionsWithTrue;
|
|
654
|
+
// the requirement states "if action specifies custom output, then the output is defined by them and not by the template".
|
|
655
|
+
// That means for those actions, only mapped keys are included. For other actions (without custom maps),
|
|
656
|
+
// they will be handled by output:true rules below.
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
// 2) Include all for actions marked output: true (that do not have a custom map)
|
|
660
|
+
const actionsWithTrueNames = new Set(actionsWithTrue.map(a => a.name))
|
|
661
|
+
for (const actionName of actionsWithTrueNames) {
|
|
662
|
+
// If this action also had a custom map, custom map already handled it and should be authoritative.
|
|
663
|
+
const hadCustom = actionsWithCustomMap.some(a => a.name === actionName)
|
|
664
|
+
if (!hadCustom) {
|
|
665
|
+
includeAllForAction(actionName)
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
// 3) Exclude any actions explicitly marked false (they won't be included by rules above anyway)
|
|
670
|
+
|
|
671
|
+
// If we have any inclusions (custom maps or trues), return them
|
|
672
|
+
if (result.size > 0) {
|
|
673
|
+
// Additionally, filter out any accidentally included outputs from actions marked false
|
|
674
|
+
for (const falseActionName of actionsWithFalse) {
|
|
675
|
+
for (const key of Array.from(result.keys())) {
|
|
676
|
+
if (key.startsWith(`${falseActionName}.`)) {
|
|
677
|
+
result.delete(key)
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
return Object.fromEntries(result)
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
// 4) Backward compatibility: include all outputs if no action opted-in via true/object.
|
|
685
|
+
// Exclude dependency outputs if the job has explicit dependencies.
|
|
686
|
+
if (job.depends_on && job.depends_on.length > 0) {
|
|
687
|
+
return this.filterOutDependencyOutputs(outputs, job)
|
|
688
|
+
}
|
|
689
|
+
return Object.fromEntries(outputs)
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
/**
|
|
693
|
+
* Filters out dependency outputs from the outputs map.
|
|
694
|
+
* Dependency outputs are identified by being prefixed with dependency job names.
|
|
695
|
+
*/
|
|
696
|
+
private filterOutDependencyOutputs(outputs: Map<string, unknown>, job: Job): Record<string, unknown> {
|
|
697
|
+
const filtered = new Map<string, unknown>()
|
|
698
|
+
|
|
699
|
+
// Get list of dependency job names
|
|
700
|
+
const dependencyNames = job.depends_on || []
|
|
701
|
+
|
|
702
|
+
for (const [key, value] of outputs) {
|
|
703
|
+
// Check if this output key starts with any dependency job name prefix
|
|
704
|
+
const isDependencyOutput = dependencyNames.some(depName => key.startsWith(`${depName}.`))
|
|
705
|
+
|
|
706
|
+
// Only include outputs that are NOT from dependencies
|
|
707
|
+
if (!isDependencyOutput) {
|
|
708
|
+
filtered.set(key, value)
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
return Object.fromEntries(filtered)
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
/**
|
|
716
|
+
* Groups network results by status and outputs.
|
|
717
|
+
* - Success states with identical outputs are grouped together with chainIds array
|
|
718
|
+
* - Error states are kept separate (one entry per network)
|
|
719
|
+
*/
|
|
720
|
+
private groupNetworkResults(outputs: Map<number, { status: 'success' | 'error'; data: Map<string, unknown> | string }>, job: Job): Array<{
|
|
721
|
+
status: 'success' | 'error';
|
|
722
|
+
chainIds?: string[];
|
|
723
|
+
chainId?: string;
|
|
724
|
+
outputs?: Record<string, unknown>;
|
|
725
|
+
error?: string;
|
|
726
|
+
}> {
|
|
727
|
+
const successGroups = new Map<string, { chainIds: string[], outputs: Record<string, unknown> }>()
|
|
728
|
+
const errorEntries: Array<{
|
|
729
|
+
status: 'error';
|
|
730
|
+
chainId: string;
|
|
731
|
+
error: string;
|
|
732
|
+
}> = []
|
|
733
|
+
|
|
734
|
+
for (const [chainId, result] of outputs.entries()) {
|
|
735
|
+
if (result.status === 'success') {
|
|
736
|
+
// Group successful results by identical outputs, filtered by action output flags
|
|
737
|
+
const outputsObj = result.data instanceof Map ? this.filterOutputsByActionFlags(result.data, job) : {}
|
|
738
|
+
const key = JSON.stringify(outputsObj)
|
|
739
|
+
|
|
740
|
+
if (!successGroups.has(key)) {
|
|
741
|
+
successGroups.set(key, {
|
|
742
|
+
chainIds: [],
|
|
743
|
+
outputs: outputsObj
|
|
744
|
+
})
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
successGroups.get(key)!.chainIds.push(chainId.toString())
|
|
748
|
+
} else {
|
|
749
|
+
// Keep error results separate - one entry per network
|
|
750
|
+
errorEntries.push({
|
|
751
|
+
status: 'error',
|
|
752
|
+
chainId: chainId.toString(),
|
|
753
|
+
error: result.data as string
|
|
754
|
+
})
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
// Convert success groups to array format
|
|
759
|
+
const successEntries = Array.from(successGroups.values()).map(group => ({
|
|
760
|
+
status: 'success' as const,
|
|
761
|
+
chainIds: group.chainIds.sort(), // Sort for consistent output
|
|
762
|
+
outputs: group.outputs
|
|
763
|
+
}))
|
|
764
|
+
|
|
765
|
+
// Return all entries: successes first, then errors
|
|
766
|
+
return [...successEntries, ...errorEntries]
|
|
767
|
+
}
|
|
768
|
+
}
|