Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cli): mud init command #2853

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/great-peas-kick.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@latticexyz/cli": patch
---

Added the `mud init` command, which populates the current project with the MUD artefacts for a given World.

Currently it generates the `deploys` folder and `worlds.json` file.
2 changes: 2 additions & 0 deletions packages/cli/src/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import test from "./test";
import trace from "./trace";
import devContracts from "./dev-contracts";
import verify from "./verify";
import init from "./init";

// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Each command has different options
export const commands: CommandModule<any, any>[] = [
Expand All @@ -30,4 +31,5 @@ export const commands: CommandModule<any, any>[] = [
devContracts,
abiTs,
verify,
init,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prob need a changeset for this (and in a future PR, can edit/expand the changeset to include new behavior)

];
49 changes: 49 additions & 0 deletions packages/cli/src/commands/init.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import type { CommandModule, InferredOptionTypes } from "yargs";
import { loadConfig } from "@latticexyz/config/node";
import { World as WorldConfig } from "@latticexyz/world";
import { getRpcUrl } from "@latticexyz/common/foundry";
import { Hex, createWalletClient, http } from "viem";
import { getWorldDeploy } from "../deploy/getWorldDeploy";
import { writeDeploymentResult } from "../utils/writeDeploymentResult";

const verifyOptions = {
configPath: { type: "string", desc: "Path to the MUD config file" },
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the expected behavior if you already have a config?

I thought the goal of mud init was to generate a mud config given a world address?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sounds like this is mostly just setting up the scaffolding for the init command

either way, we should prob remove this config option since we're expecting to generate this from mud init?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, the config will ultimately be generated as part of this!

worldAddress: { type: "string", required: true, desc: "Verify an existing World at the given address" },
profile: { type: "string", desc: "The foundry profile to use" },
rpc: { type: "string", desc: "The RPC URL to use. Defaults to the RPC url from the local foundry.toml" },
} as const;

type Options = InferredOptionTypes<typeof verifyOptions>;

const commandModule: CommandModule<Options, Options> = {
command: "init",

describe: "Populates the local project with the MUD artefacts for a given World.",

builder(yargs) {
return yargs.options(verifyOptions);
},

async handler(opts) {
const profile = opts.profile ?? process.env.FOUNDRY_PROFILE;

const config = (await loadConfig(opts.configPath)) as WorldConfig;

const rpc = opts.rpc ?? (await getRpcUrl(profile));

const client = createWalletClient({
transport: http(rpc),
});

const worldDeploy = await getWorldDeploy(client, opts.worldAddress as Hex);

const deploymentInfo = {
worldAddress: worldDeploy.address,
blockNumber: Number(worldDeploy.deployBlock),
};

writeDeploymentResult({ client, config, deploymentInfo });
},
};

export default commandModule;
24 changes: 2 additions & 22 deletions packages/cli/src/runDeploy.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import path from "node:path";
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { InferredOptionTypes, Options } from "yargs";
import { deploy } from "./deploy/deploy";
import { createWalletClient, http, Hex, isHex } from "viem";
Expand All @@ -11,12 +9,12 @@ import { getOutDirectory, getRpcUrl, getSrcDirectory } from "@latticexyz/common/
import chalk from "chalk";
import { MUDError } from "@latticexyz/common/errors";
import { resolveConfig } from "./deploy/resolveConfig";
import { getChainId } from "viem/actions";
import { postDeploy } from "./utils/postDeploy";
import { WorldDeploy } from "./deploy/common";
import { build } from "./build";
import { kmsKeyToAccount } from "@latticexyz/common/kms";
import { configToModules } from "./deploy/configToModules";
import { writeDeploymentResult } from "./utils/writeDeploymentResult";

export const deployOptions = {
configPath: { type: "string", desc: "Path to the MUD config file" },
Expand Down Expand Up @@ -154,25 +152,7 @@ export async function runDeploy(opts: DeployOptions): Promise<WorldDeploy> {
};

if (opts.saveDeployment) {
const chainId = await getChainId(client);
const deploysDir = path.join(config.deploysDirectory, chainId.toString());
mkdirSync(deploysDir, { recursive: true });
writeFileSync(path.join(deploysDir, "latest.json"), JSON.stringify(deploymentInfo, null, 2));
writeFileSync(path.join(deploysDir, Date.now() + ".json"), JSON.stringify(deploymentInfo, null, 2));

const localChains = [1337, 31337];
const deploys = existsSync(config.worldsFile) ? JSON.parse(readFileSync(config.worldsFile, "utf-8")) : {};
deploys[chainId] = {
address: deploymentInfo.worldAddress,
// We expect the worlds file to be committed and since local deployments are often
// a consistent address but different block number, we'll ignore the block number.
blockNumber: localChains.includes(chainId) ? undefined : deploymentInfo.blockNumber,
};
writeFileSync(config.worldsFile, JSON.stringify(deploys, null, 2));

console.log(
chalk.bgGreen(chalk.whiteBright(`\n Deployment result (written to ${config.worldsFile} and ${deploysDir}): \n`)),
);
writeDeploymentResult({ client, config: configV2, deploymentInfo });
}

console.log(deploymentInfo);
Expand Down
44 changes: 44 additions & 0 deletions packages/cli/src/utils/writeDeploymentResult.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { World as WorldConfig } from "@latticexyz/world";
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
import path from "path";
import { WalletClient } from "viem";
import { getChainId } from "viem/actions";
import chalk from "chalk";

const localChains = [1337, 31337];

export async function writeDeploymentResult({
client,
config,
deploymentInfo,
}: {
client: WalletClient;
config: WorldConfig;
deploymentInfo: {
worldAddress: `0x${string}`;
blockNumber: number;
};
}) {
const chainId = await getChainId(client);
const deploysDir = path.join(config.deploy.deploysDirectory, chainId.toString());
mkdirSync(deploysDir, { recursive: true });
writeFileSync(path.join(deploysDir, "latest.json"), JSON.stringify(deploymentInfo, null, 2));
writeFileSync(path.join(deploysDir, Date.now() + ".json"), JSON.stringify(deploymentInfo, null, 2));

const deploys = existsSync(config.deploy.worldsFile)
? JSON.parse(readFileSync(config.deploy.worldsFile, "utf-8"))
: {};
deploys[chainId] = {
address: deploymentInfo.worldAddress,
// We expect the worlds file to be committed and since local deployments are often
// a consistent address but different block number, we'll ignore the block number.
blockNumber: localChains.includes(chainId) ? undefined : deploymentInfo.blockNumber,
};
writeFileSync(config.deploy.worldsFile, JSON.stringify(deploys, null, 2));

console.log(
chalk.bgGreen(
chalk.whiteBright(`\n Deployment result (written to ${config.deploy.worldsFile} and ${deploysDir}): \n`),
),
);
}
Loading