diff --git a/src/commands/node/chopsticks/init.ts b/src/commands/node/chopsticks/init.ts new file mode 100644 index 00000000..fd3ab620 --- /dev/null +++ b/src/commands/node/chopsticks/init.ts @@ -0,0 +1,39 @@ +import path from "node:path"; +import { SwankyCommand } from "../../../lib/swankyCommand.js"; +import { + copyChopsticksTemplateFile, + getSwankyConfig, + getTemplates, +} from "../../../lib/index.js"; +import { ConfigBuilder } from "../../../lib/config-builder.js"; +import { SwankyConfig } from "../../../types/index.js"; + +export const chopsticksConfig = "dev.yml"; + +export class InitChopsticks extends SwankyCommand { + static description = "Initialize chopsticks config"; + + async run(): Promise { + const localConfig = getSwankyConfig("local") as SwankyConfig; + const projectPath = path.resolve(); + + const chopsticksTemplatePath = getTemplates().chopsticksTemplatesPath; + const configPath = path.resolve(projectPath, "node", "config"); + + await this.spinner.runCommand( + () => + copyChopsticksTemplateFile(chopsticksTemplatePath, configPath), + "Copying Chopsticks template files...", + ); + + await this.spinner.runCommand(async () => { + const newLocalConfig = new ConfigBuilder(localConfig) + .addChopsticks(path.resolve(projectPath, "node", "config", chopsticksConfig)) + .build(); + await this.storeConfig(newLocalConfig, "local"); + }, "Updating Swanky configuration with Chopsticks settings..."); + + this.log("Chopsticks config initialized successfully"); + } +} + diff --git a/src/commands/node/chopsticks/start.ts b/src/commands/node/chopsticks/start.ts new file mode 100644 index 00000000..bde4a77f --- /dev/null +++ b/src/commands/node/chopsticks/start.ts @@ -0,0 +1,40 @@ +import { Flags } from "@oclif/core"; +import { execaCommand } from "execa"; +import { SwankyCommand } from "../../../lib/swankyCommand.js"; +import { ensureSwankyNodeInstalled } from "../../../lib/index.js"; +import { pathExists } from "fs-extra/esm"; +import { ConfigError, FileError } from "../../../lib/errors.js"; +export class StartChopsticks extends SwankyCommand { + static description = "Start chopsticks"; + + static flags = { + "config": Flags.string({ + description: "Path to the chopsticks config file", + }) + } + + async run(): Promise { + const { flags } = await this.parse(StartChopsticks); + + ensureSwankyNodeInstalled(this.swankyConfig); + + const chopsticksConfigPath = flags.config ?? this.swankyConfig.node.chopsticks?.configPath; + + if(!chopsticksConfigPath) { + throw new ConfigError("Chopsticks config not set in swanky config. Please set it in swanky config or provide the path to the chopsticks config file using --config flag."); + } + + if (!(await pathExists(chopsticksConfigPath))) { + throw new FileError(`Chopsticks config file not found at ${flags.config}`); + } + + await execaCommand( + `npx @acala-network/chopsticks@latest --config=${chopsticksConfigPath}`, + { + stdio: "inherit", + } + ); + + this.log("Chopsticks started successfully."); + } +} diff --git a/src/commands/node/start.ts b/src/commands/node/start.ts index 53a85590..89155083 100644 --- a/src/commands/node/start.ts +++ b/src/commands/node/start.ts @@ -2,6 +2,7 @@ import { Flags } from "@oclif/core"; import { execaCommand } from "execa"; import { SwankyCommand } from "../../lib/swankyCommand.js"; import semver from "semver"; +import { ensureSwankyNodeInstalled } from "../../lib/index.js"; export class StartNode extends SwankyCommand { static description = "Start a local node"; @@ -29,9 +30,8 @@ export class StartNode extends SwankyCommand { async run(): Promise { const { flags } = await this.parse(StartNode); - if(this.swankyConfig.node.localPath === "") { - this.error("Swanky node is not installed. Please run `swanky node:install` first."); - } + ensureSwankyNodeInstalled(this.swankyConfig); + // Run persistent mode by default. non-persistent mode in case flag is provided. // Non-Persistent mode (`--dev`) allows all CORS origin, without `--dev`, users need to specify origins by `--rpc-cors`. await execaCommand( diff --git a/src/lib/command-utils.ts b/src/lib/command-utils.ts index 5284c4ad..1d89ac48 100644 --- a/src/lib/command-utils.ts +++ b/src/lib/command-utils.ts @@ -43,7 +43,6 @@ export function getSwankyConfig(configType: "local" | "global"): SwankyConfig | return config; } - export function getSystemConfigDirectoryPath(): string { const homeDir = userInfo().homedir; const configPath = homeDir + `/${DEFAULT_CONFIG_FOLDER_NAME}`; @@ -148,12 +147,14 @@ export async function generateTypes(contractName: string) { ); } export function ensureAccountIsSet(account: string | undefined, config: SwankyConfig) { - if(!account && config.defaultAccount === null) { - throw new ConfigError("No default account set. Please set one or provide an account alias with --account"); + if (!account && config.defaultAccount === null) { + throw new ConfigError( + "No default account set. Please set one or provide an account alias with --account" + ); } } -export function buildSwankyConfig() { +export function buildSwankyConfig() { return { node: { localPath: "", @@ -164,16 +165,16 @@ export function buildSwankyConfig() { defaultAccount: DEFAULT_ACCOUNT, accounts: [ { - "alias": "alice", - "mnemonic": "//Alice", - "isDev": true, - "address": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY" + alias: "alice", + mnemonic: "//Alice", + isDev: true, + address: "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", }, { - "alias": "bob", - "mnemonic": "//Bob", - "isDev": true, - "address": "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty" + alias: "bob", + mnemonic: "//Bob", + isDev: true, + address: "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", }, ], networks: { @@ -249,5 +250,8 @@ export function extractCargoDylintVersion() { } export function extractCargoContractVersion() { - return extractVersion("cargo contract -V", /cargo-contract-contract (\d+\.\d+\.\d+(?:-[\w.]+)?)(?:-unknown-[\w-]+)/); -} \ No newline at end of file + return extractVersion( + "cargo contract -V", + /cargo-contract-contract (\d+\.\d+\.\d+(?:-[\w.]+)?)(?:-unknown-[\w-]+)/ + ); +} diff --git a/src/lib/config-builder.ts b/src/lib/config-builder.ts index 9a4f4b0b..8a2d5e3b 100644 --- a/src/lib/config-builder.ts +++ b/src/lib/config-builder.ts @@ -78,6 +78,15 @@ export class ConfigBuilder { return this; } + addChopsticks(path: string): ConfigBuilder { + if("node" in this.config) { + this.config.node.chopsticks ={ + configPath: path, + } + } + return this; + } + build(): T { return this.config; } diff --git a/src/lib/config.ts b/src/lib/config.ts new file mode 100644 index 00000000..fd98d235 --- /dev/null +++ b/src/lib/config.ts @@ -0,0 +1,8 @@ +import { SwankyConfig } from "../index.js"; +import { FileError } from "./errors.js"; + +export function ensureSwankyNodeInstalled(config: SwankyConfig) { + if (config.node.localPath === "") { + throw new FileError('Swanky node is not installed. Please run `swanky node:install` first.'); + } +} diff --git a/src/lib/index.ts b/src/lib/index.ts index b14b6f9c..2d961da5 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -1,5 +1,6 @@ export * from "./account.js"; export * from "./command-utils.js"; +export * from "./config.js"; export * as consts from "./consts.js"; export * from "./crypto.js"; export * from "./nodeInfo.js"; diff --git a/src/lib/tasks.ts b/src/lib/tasks.ts index f023c567..a8de45f1 100644 --- a/src/lib/tasks.ts +++ b/src/lib/tasks.ts @@ -17,6 +17,7 @@ import { zombienetConfig } from "../commands/zombienet/init.js"; import { readFileSync } from "fs"; import TOML from "@iarna/toml"; import { writeFileSync } from "node:fs"; +import { chopsticksConfig } from "../commands/node/chopsticks/init.js"; export async function checkCliDependencies(spinner: Spinner) { const dependencyList = [ @@ -218,6 +219,14 @@ export async function copyZombienetTemplateFile(templatePath: string, configPath ); } +export async function copyChopsticksTemplateFile(templatePath: string, configPath: string) { + await ensureDir(configPath); + await copy( + path.resolve(templatePath, chopsticksConfig), + path.resolve(configPath, chopsticksConfig), + ); +} + export async function downloadZombienetBinaries(binaries: string[], projectPath: string, swankyConfig: SwankyConfig, spinner: Spinner) { const binPath = path.resolve(projectPath, "zombienet", "bin"); await ensureDir(binPath); diff --git a/src/lib/templates.ts b/src/lib/templates.ts index 90996e34..225278ca 100644 --- a/src/lib/templates.ts +++ b/src/lib/templates.ts @@ -9,6 +9,7 @@ export function getTemplates() { const templatesPath = path.resolve(__dirname, "..", "templates"); const contractTemplatesPath = path.resolve(templatesPath, "contracts"); const zombienetTemplatesPath = path.resolve(templatesPath, "zombienet"); + const chopsticksTemplatesPath = path.resolve(templatesPath, "chopsticks"); const fileList = readdirSync(contractTemplatesPath, { withFileTypes: true, }); @@ -21,5 +22,6 @@ export function getTemplates() { contractTemplatesPath, contractTemplatesList, zombienetTemplatesPath, + chopsticksTemplatesPath, }; } diff --git a/src/templates/chopsticks/dev.yml b/src/templates/chopsticks/dev.yml new file mode 100644 index 00000000..bfa3ee29 --- /dev/null +++ b/src/templates/chopsticks/dev.yml @@ -0,0 +1,4 @@ +endpoint: ws://127.0.0.1:9944 +mock-signature-host: true +block: 1 +db: ./db.sqlite \ No newline at end of file diff --git a/src/types/index.ts b/src/types/index.ts index 8c243e2e..b5e27a42 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -58,6 +58,9 @@ export interface SwankyConfig extends SwankySystemConfig{ localPath: string; supportedInk: string; version: string; + chopsticks?: { + configPath: string; + }; }; contracts: Record | Record; zombienet?: ZombienetData;