UNPKG

@solarity/hardhat-migrate

Version:
327 lines 13.5 kB
import { isAbsolute } from "path"; import { validateUserConfigZodType } from "@nomicfoundation/hardhat-zod-utils"; import { z } from "zod"; import { PLUGIN_ID } from "./constants.js"; export default async () => ({ validateUserConfig, resolveUserConfig, }); const userConfigType = z.object({ migrate: z .object({ filter: z .object({ from: z.number().int().optional(), to: z.number().int().optional(), only: z.number().int().optional(), skip: z.number().int().optional(), }) .optional(), verification: z .object({ verify: z.boolean().optional(), verificationDelay: z.number().int().optional(), verifyParallel: z.number().int().optional(), verifyAttempts: z.number().int().optional(), }) .optional(), paths: z .object({ pathToMigrations: z .string() .refine((p) => !isAbsolute(p), "Expected a relative path") .optional(), namespace: z.string().optional(), reportPath: z .string() .refine((p) => !isAbsolute(p), "Expected a relative path") .optional(), reportFormat: z.enum(["json", "md"]).optional(), }) .optional(), execution: z .object({ force: z.boolean().optional(), continue: z.boolean().optional(), wait: z.number().int().optional(), transactionStatusCheckInterval: z.number().int().optional(), withoutCLIReporting: z.boolean().optional(), }) .optional(), castWallet: z .object({ passwordFile: z.string().optional(), keystore: z.string().optional(), account: z.string().optional(), }) .optional(), trezorWallet: z .object({ enabled: z.boolean().optional(), mnemonicIndex: z.number().int().optional(), }) .optional(), }) .optional(), }); export async function validateUserConfig(userConfig) { return validateUserConfigZodType(userConfig, userConfigType); } export async function resolveUserConfig(userConfig, resolveConfigurationVariable, next) { const resolvedConfig = await next(userConfig, resolveConfigurationVariable); const migrate = await resolveMigrateConfig(userConfig.migrate, resolveConfigurationVariable); return { ...resolvedConfig, migrate, }; } async function resolveMigrateConfig(externalConfig, _resolveConfigurationVariable) { const defaults = { filter: { from: -1, to: -1, only: -1, skip: -1, }, verification: { verify: false, verificationDelay: 5000, verifyParallel: 1, verifyAttempts: 3, }, paths: { pathToMigrations: "./deploy", namespace: "", reportPath: "cache", reportFormat: "md", }, execution: { force: false, continue: false, wait: 1, transactionStatusCheckInterval: 2000, withoutCLIReporting: false, }, castWallet: {}, trezorWallet: { enabled: false, mnemonicIndex: 0, }, }; if (externalConfig === undefined) { return defaults; } const resolved = JSON.parse(JSON.stringify(defaults)); // Filter if (externalConfig.filter) { if (typeof externalConfig.filter.from === "number") resolved.filter.from = externalConfig.filter.from; if (typeof externalConfig.filter.to === "number") resolved.filter.to = externalConfig.filter.to; if (typeof externalConfig.filter.only === "number") resolved.filter.only = externalConfig.filter.only; if (typeof externalConfig.filter.skip === "number") resolved.filter.skip = externalConfig.filter.skip; } // Verification if (externalConfig.verification) { const v = externalConfig.verification; if (typeof v.verify === "boolean") resolved.verification.verify = v.verify; if (typeof v.verificationDelay === "number") resolved.verification.verificationDelay = v.verificationDelay; if (typeof v.verifyParallel === "number") resolved.verification.verifyParallel = v.verifyParallel; if (typeof v.verifyAttempts === "number") resolved.verification.verifyAttempts = v.verifyAttempts; } // Paths if (externalConfig.paths) { const p = externalConfig.paths; if (typeof p.pathToMigrations === "string") resolved.paths.pathToMigrations = p.pathToMigrations; if (typeof p.namespace === "string") resolved.paths.namespace = p.namespace; if (typeof p.reportPath === "string") resolved.paths.reportPath = p.reportPath; if (typeof p.reportFormat === "string") { resolved.paths.reportFormat = p.reportFormat; } } // Execution if (externalConfig.execution) { const e = externalConfig.execution; if (typeof e.force === "boolean") resolved.execution.force = e.force; if (typeof e.continue === "boolean") resolved.execution.continue = e.continue; if (typeof e.wait === "number") resolved.execution.wait = e.wait; if (typeof e.transactionStatusCheckInterval === "number") resolved.execution.transactionStatusCheckInterval = e.transactionStatusCheckInterval; if (typeof e.withoutCLIReporting === "boolean") resolved.execution.withoutCLIReporting = e.withoutCLIReporting; } // Cast wallet if (externalConfig.castWallet) { if (typeof externalConfig.castWallet.passwordFile === "string") { resolved.castWallet.passwordFile = externalConfig.castWallet.passwordFile; } if (typeof externalConfig.castWallet.keystore === "string") { resolved.castWallet.keystore = externalConfig.castWallet.keystore; } if (typeof externalConfig.castWallet.account === "string") { resolved.castWallet.account = externalConfig.castWallet.account; } } // Trezor wallet if (externalConfig.trezorWallet) { const t = externalConfig.trezorWallet; if (typeof t.enabled === "boolean") resolved.trezorWallet.enabled = t.enabled; if (typeof t.mnemonicIndex === "number") resolved.trezorWallet.mnemonicIndex = t.mnemonicIndex; } return resolved; } export const mergeConfigs = (externalConfig, migrateConfig) => { if (externalConfig) { return deepMerge(migrateConfig, externalConfig); } return migrateConfig; }; export function convertFlatToNested(flatConfig) { const result = {}; // NOTE: Using truthy checks here means CLI flags that explicitly set `0` (e.g. `--from 0`) // will be ignored because `0` is falsy. If zero should be a valid override we need // to switch to `"key" in flatConfig` style checks. if (flatConfig.from || flatConfig.to || flatConfig.only || flatConfig.skip) { result.filter = {}; } if (flatConfig.from) result.filter.from = flatConfig.from; if (flatConfig.to) result.filter.to = flatConfig.to; if (flatConfig.only) result.filter.only = flatConfig.only; if (flatConfig.skip) result.filter.skip = flatConfig.skip; if (flatConfig.verify || flatConfig.verificationDelay || flatConfig.verifyParallel || flatConfig.verifyAttempts) { result.verification = {}; } if (flatConfig.verify) result.verification.verify = flatConfig.verify; if (flatConfig.verificationDelay) result.verification.verificationDelay = flatConfig.verificationDelay; if (flatConfig.verifyParallel) result.verification.verifyParallel = flatConfig.verifyParallel; if (flatConfig.verifyAttempts) result.verification.verifyAttempts = flatConfig.verifyAttempts; if (flatConfig.pathToMigrations || flatConfig.namespace || flatConfig.reportPath || flatConfig.reportFormat) { result.paths = {}; } if (flatConfig.pathToMigrations) result.paths.pathToMigrations = flatConfig.pathToMigrations; if (flatConfig.namespace) result.paths.namespace = flatConfig.namespace; if (flatConfig.reportPath) result.paths.reportPath = flatConfig.reportPath; if (flatConfig.reportFormat) result.paths.reportFormat = flatConfig.reportFormat; if (flatConfig.force || flatConfig.continue || flatConfig.wait || flatConfig.transactionStatusCheckInterval) { result.execution = {}; } if (flatConfig.force) result.execution.force = flatConfig.force; if (flatConfig.continue) result.execution.continue = flatConfig.continue; if (flatConfig.wait) result.execution.wait = flatConfig.wait; if (flatConfig.transactionStatusCheckInterval) result.execution.transactionStatusCheckInterval = flatConfig.transactionStatusCheckInterval; if (flatConfig.passwordFile || flatConfig.keystore || flatConfig.account) { result.castWallet = {}; } if (flatConfig.passwordFile) result.castWallet.passwordFile = flatConfig.passwordFile; if (flatConfig.keystore) result.castWallet.keystore = flatConfig.keystore; if (flatConfig.account) result.castWallet.account = flatConfig.account; if (flatConfig.trezorEnabled || flatConfig.trezorMnemonicIndex) { result.trezorWallet = {}; } if (flatConfig.trezorEnabled) result.trezorWallet.enabled = flatConfig.trezorEnabled; if (flatConfig.trezorMnemonicIndex) result.trezorWallet.mnemonicIndex = flatConfig.trezorMnemonicIndex; return result; } // Deep merge utility for nested objects function deepMerge(target, source) { const output = { ...target }; if (isObject(target) && isObject(source)) { Object.keys(source).forEach((key) => { if (isObject(source[key])) { if (!(key in target)) { Object.assign(output, { [key]: source[key] }); } else { output[key] = deepMerge(target[key], source[key]); } } else { Object.assign(output, { [key]: source[key] }); } }); } return output; } function isObject(item) { return item && typeof item === "object" && !Array.isArray(item); } export const validateConfig = (config) => { if (config.execution.wait !== undefined && config.execution.wait < 1) { throw new Error(`[${PLUGIN_ID}] config.migrate.execution.wait must be greater than 0`); } if (config.verification.verifyParallel !== undefined && config.verification.verifyParallel < 1) { throw new Error(`[${PLUGIN_ID}] config.migrate.verification.verifyParallel must be greater than 0`); } if (config.verification.verifyAttempts !== undefined && config.verification.verifyAttempts < 1) { throw new Error(`[${PLUGIN_ID}] config.migrate.verification.verifyAttempts must be greater than 0`); } if (config.paths.pathToMigrations !== undefined && !isRelativePath(config.paths.pathToMigrations)) { throw new Error(`[${PLUGIN_ID}] config.migrate.paths.pathToMigrations must be a relative path`); } if (config.paths.reportFormat !== undefined && !["json", "md"].includes(config.paths.reportFormat)) { throw new Error(`[${PLUGIN_ID}] config.migrate.paths.reportFormat must be either 'json' or 'md'`); } if (config.trezorWallet.enabled && (config.castWallet.account || config.castWallet.keystore)) { throw new Error(`[${PLUGIN_ID}] config.migrate.trezorWallet.enabled cannot be used with config.migrate.castWallet.account or config.migrate.castWallet.keystore at the same time`); } if (config.castWallet.account && config.castWallet.keystore) { throw new Error(`[${PLUGIN_ID}] config.migrate.castWallet.account and config.migrate.castWallet.keystore cannot be enabled at the same time`); } if (config.execution.transactionStatusCheckInterval !== undefined && config.execution.transactionStatusCheckInterval < 1000) { throw new Error(`[${PLUGIN_ID}] config.migrate.execution.transactionStatusCheckInterval must be greater or equal to 1000`); } }; export const extendVerifyConfigs = (cliArgs) => { const defaultVerifyConfig = { inputFile: undefined, parallel: 1, attempts: 3, }; const config = cliArgs === undefined ? defaultVerifyConfig : { ...defaultVerifyConfig, ...definedProps(cliArgs) }; if (config.parallel !== undefined && config.parallel < 1) { throw new Error(`[${PLUGIN_ID}] parallel must be greater than 0`); } if (config.attempts !== undefined && config.attempts < 1) { throw new Error(`[${PLUGIN_ID}] attempts must be greater than 0`); } return config; }; const definedProps = (obj) => Object.fromEntries(Object.entries(obj).filter(([, v]) => v !== undefined)); const isRelativePath = (path) => path === undefined || !isAbsolute(path); //# sourceMappingURL=config.js.map