Skip to content

Commit

Permalink
feat: Implement system/local config + support default/dev account (#62)
Browse files Browse the repository at this point in the history
Co-authored-by: Igor Papandinas <igor.papandinas@posteo.net>
  • Loading branch information
prxgr4mm3r and ipapandinas authored Feb 27, 2024
1 parent e28c7c6 commit e315716
Show file tree
Hide file tree
Showing 22 changed files with 589 additions and 210 deletions.
40 changes: 34 additions & 6 deletions src/commands/account/create.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,37 @@
import { Flags } from "@oclif/core";
import chalk from "chalk";
import { ChainAccount, encrypt } from "../../lib/index.js";
import { ChainAccount, encrypt, getSwankyConfig, isLocalConfigCheck } from "../../lib/index.js";
import { AccountData } from "../../types/index.js";
import inquirer from "inquirer";
import { SwankyCommand } from "../../lib/swankyCommand.js";
import { FileError } from "../../lib/errors.js";
import { ConfigBuilder } from "../../lib/config-builder.js";
import { SwankyAccountCommand } from "./swankyAccountCommands.js";

export class CreateAccount extends SwankyAccountCommand<typeof CreateAccount> {
static description = "Create a new dev account in config";

static flags = {
generate: Flags.boolean({
global: Flags.boolean({
char: "g",
description: "Create account globally stored in Swanky system config.",

}),
new: Flags.boolean({
char: "n",
description: "Generate a brand new account.",
}),
dev: Flags.boolean({
char: "d",
description: "Make this account a dev account for local network usage.",
}),
};

constructor(argv: string[], baseConfig: any) {
super(argv, baseConfig);
(this.constructor as typeof SwankyCommand).ENSURE_SWANKY_CONFIG = false;
}

async run(): Promise<void> {
const { flags } = await this.parse(CreateAccount);

Expand All @@ -36,8 +51,8 @@ export class CreateAccount extends SwankyAccountCommand<typeof CreateAccount> {
);
}

let tmpMnemonic: string;
if (flags.generate) {
let tmpMnemonic = "";
if (flags.new) {
tmpMnemonic = ChainAccount.generate();
console.log(
`${
Expand Down Expand Up @@ -76,9 +91,22 @@ export class CreateAccount extends SwankyAccountCommand<typeof CreateAccount> {
accountData.mnemonic = tmpMnemonic;
}

this.swankyConfig.accounts.push(accountData);
const configType = flags.global ? "global" : isLocalConfigCheck() ? "local" : "global";
const config = configType === "global" ? getSwankyConfig("global") : getSwankyConfig("local");

const configBuilder = new ConfigBuilder(config).addAccount(accountData);

await this.storeConfig();
if (config.defaultAccount === null) {
configBuilder.setDefaultAccount(accountData.alias);
}

try {
await this.storeConfig(configBuilder.build(), configType);
} catch (cause) {
throw new FileError(`Error storing created account in ${configType} config`, {
cause,
});
}

this.log(
`${chalk.greenBright("✔")} Account with alias ${chalk.yellowBright(
Expand Down
82 changes: 82 additions & 0 deletions src/commands/account/default.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { Args, Flags } from "@oclif/core";
import chalk from "chalk";
import { SwankySystemConfig } from "../../types/index.js";
import inquirer from "inquirer";
import { SwankyCommand } from "../../lib/swankyCommand.js";
import { ConfigError, FileError } from "../../lib/errors.js";
import { getSwankyConfig, isLocalConfigCheck } from "../../lib/index.js";
import { ConfigBuilder } from "../../lib/config-builder.js";

export class DefaultAccount extends SwankyCommand<typeof DefaultAccount> {
static description = "Set default account to use";

static flags = {
global: Flags.boolean({
char: "g",
description: "Set default account globally in Swanky system config.",
}),
};

static args = {
accountAlias: Args.string({
name: "accountAlias",
required: false,
description: "Alias of account to be used as default",
}),
};

constructor(argv: string[], baseConfig: any) {
super(argv, baseConfig);
(this.constructor as typeof SwankyCommand).ENSURE_SWANKY_CONFIG = false;
}

async run(): Promise<void> {
const { args, flags } = await this.parse(DefaultAccount);

const configType = flags.global ? "global" : isLocalConfigCheck() ? "local" : "global";
const config = configType === "global" ? getSwankyConfig("global") : getSwankyConfig("local");

const accountAlias = args.accountAlias ?? (await this.promptForAccountAlias(config));
this.ensureAccountExists(config, accountAlias);

const newConfig = new ConfigBuilder(config).setDefaultAccount(accountAlias).build();

try {
await this.storeConfig(newConfig, configType);
} catch (cause) {
throw new FileError(`Error storing default account in ${configType} config`, {
cause,
});
}

this.log(
`${chalk.greenBright("✔")} Account with alias ${chalk.yellowBright(
accountAlias
)} set as default in ${configType} config`
);
}

private async promptForAccountAlias(config: SwankySystemConfig): Promise<string> {
const choices = config.accounts.map((account) => ({
name: `${account.alias} (${account.address})`,
value: account.alias,
}));

const answer = await inquirer.prompt([
{
type: "list",
name: "defaultAccount",
message: "Select default account",
choices: choices,
},
]);

return answer.defaultAccount;
}

private ensureAccountExists(config: SwankySystemConfig, alias: string) {
const isSomeAccount = config.accounts.some((account) => account.alias === alias);
if (!isSomeAccount)
throw new ConfigError(`Provided account alias ${chalk.yellowBright(alias)} not found`);
}
}
31 changes: 28 additions & 3 deletions src/commands/account/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,36 @@ export class ListAccounts extends SwankyCommand<typeof ListAccounts> {
static description = "List dev accounts stored in config";
static aliases = [`account:ls`];

constructor(argv: string[], baseConfig: any) {
super(argv, baseConfig);
(this.constructor as typeof SwankyCommand).ENSURE_SWANKY_CONFIG = false;
}

async run(): Promise<void> {
this.log(`${chalk.greenBright("✔")} Stored dev accounts:`);
const countOfDevAccounts = this.swankyConfig.accounts.filter((account) => account.isDev).length;

if(countOfDevAccounts !== 0) {
this.log(`${chalk.greenBright("✔")} Stored dev accounts:`);

for (const account of this.swankyConfig.accounts) {
if(account.isDev){
this.log(`\t${chalk.yellowBright("Alias: ")} ${account.alias} \
${chalk.yellowBright("Address: ")} ${account.address} ${this.swankyConfig.defaultAccount === account.alias ? chalk.greenBright("<- Default") : ""}`);
}
}
}

const countOfProdAccounts = this.swankyConfig.accounts.length - countOfDevAccounts;

if(countOfProdAccounts !== 0) {
this.log(`${chalk.greenBright("✔")} Stored prod accounts:`);

for (const account of this.swankyConfig.accounts) {
this.log(`\t${chalk.yellowBright("Alias: ")} ${account.alias}`);
for (const account of this.swankyConfig.accounts) {
if(!account.isDev){
this.log(`\t${chalk.yellowBright("Alias: ")} ${account.alias} \
${chalk.yellowBright("Address: ")} ${account.address} ${this.swankyConfig.defaultAccount === account.alias ? chalk.greenBright("<- Default") : ""}`);
}
}
}
}
}
20 changes: 12 additions & 8 deletions src/commands/check/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Listr } from "listr2";
import { commandStdoutOrNull, extractCargoContractVersion } from "../../lib/index.js";
import { SwankyConfig } from "../../types/index.js";
import { pathExistsSync, readJSON, writeJson } from "fs-extra/esm";
import { pathExistsSync, writeJson } from "fs-extra/esm";
import { readFileSync } from "fs";
import path from "node:path";
import TOML from "@iarna/toml";
Expand Down Expand Up @@ -31,7 +31,7 @@ interface Ctx {
contracts: Record<string, Record<string, string>>;
swankyNode: string | null;
};
swankyConfig?: SwankyConfig;
swankyConfig: SwankyConfig;
mismatchedVersions: Record<string, string>;
looseDefinitionDetected: boolean;
}
Expand All @@ -46,11 +46,16 @@ export default class Check extends SwankyCommand<typeof Check> {
}),
};

constructor(argv: string[], baseConfig: any) {
super(argv, baseConfig);
(this.constructor as typeof SwankyCommand).ENSURE_SWANKY_CONFIG = false;
}

public async run(): Promise<void> {
const { flags } = await this.parse(Check);
const swankyNodeVersion = this.swankyConfig.node.version;
const isSwankyNodeInstalled = !!swankyNodeVersion;
const anyContracts = Object.keys(this.swankyConfig?.contracts).length > 0;
const anyContracts = Object.keys(this.swankyConfig.contracts ?? {}).length > 0;
const tasks = new Listr<Ctx>([
{
title: "Check OS",
Expand Down Expand Up @@ -136,11 +141,9 @@ export default class Check extends SwankyCommand<typeof Check> {
{
title: "Read ink dependencies",
enabled: anyContracts,
skip: (ctx) => Object.keys(ctx.swankyConfig.contracts).length == 0,
task: async (ctx) => {
const swankyConfig = await readJSON("swanky.config.json");
ctx.swankyConfig = swankyConfig;

for (const contract in swankyConfig.contracts) {
for (const contract in ctx.swankyConfig.contracts) {
const tomlPath = path.resolve(`contracts/${contract}/Cargo.toml`);
const doesCargoTomlExist = pathExistsSync(tomlPath);
if (!doesCargoTomlExist) {
Expand Down Expand Up @@ -168,7 +171,7 @@ export default class Check extends SwankyCommand<typeof Check> {
skip: (ctx) => Object.keys(ctx.versions.contracts).length === 0,
enabled: anyContracts && isSwankyNodeInstalled,
task: async (ctx) => {
const supportedInk = ctx.swankyConfig!.node.supportedInk;
const supportedInk = ctx.swankyConfig.node.supportedInk;
const mismatched: Record<string, string> = {};
Object.entries(ctx.versions.contracts).forEach(([contract, inkDependencies]) => {
Object.entries(inkDependencies).forEach(([depName, version]) => {
Expand Down Expand Up @@ -256,6 +259,7 @@ export default class Check extends SwankyCommand<typeof Check> {
contracts: {},
swankyNode: swankyNodeVersion || null,
},
swankyConfig: this.swankyConfig,
looseDefinitionDetected: false,
mismatchedVersions: {}
});
Expand Down
29 changes: 18 additions & 11 deletions src/commands/contract/compile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import path from "node:path";
import { spawn } from "node:child_process";
import { pathExists } from "fs-extra/esm";
import { SwankyCommand } from "../../lib/swankyCommand.js";
import { ensureCargoContractVersionCompatibility, extractCargoContractVersion, Spinner, storeArtifacts } from "../../lib/index.js";
import { ensureCargoContractVersionCompatibility, extractCargoContractVersion, Spinner, storeArtifacts, configName, getSwankyConfig } from "../../lib/index.js";
import { ConfigError, InputError, ProcessError } from "../../lib/errors.js";
import { BuildMode } from "../../index.js";
import { BuildMode, SwankyConfig } from "../../index.js";
import { ConfigBuilder } from "../../lib/config-builder.js";

export class CompileContract extends SwankyCommand<typeof CompileContract> {
static description = "Compile the smart contract(s) in your contracts directory";
Expand Down Expand Up @@ -41,6 +42,8 @@ export class CompileContract extends SwankyCommand<typeof CompileContract> {
async run(): Promise<void> {
const { args, flags } = await this.parse(CompileContract);

const localConfig = getSwankyConfig("local") as SwankyConfig;

if (args.contractName === undefined && !flags.all) {
throw new InputError("No contracts were selected to compile", { winston: { stack: true } });
}
Expand All @@ -55,7 +58,7 @@ export class CompileContract extends SwankyCommand<typeof CompileContract> {
const contractInfo = this.swankyConfig.contracts[contractName];
if (!contractInfo) {
throw new ConfigError(
`Cannot find contract info for ${contractName} contract in swanky.config.json`,
`Cannot find contract info for ${contractName} contract in "${configName()}"`
);
}
const contractPath = path.resolve("contracts", contractInfo.name);
Expand Down Expand Up @@ -130,14 +133,18 @@ export class CompileContract extends SwankyCommand<typeof CompileContract> {
return storeArtifacts(artifactsPath, contractInfo.name, contractInfo.moduleName);
}, "Moving artifacts");

this.swankyConfig.contracts[contractName].build = {
timestamp: Date.now(),
artifactsPath,
buildMode,
isVerified: false,
};

await this.storeConfig();
await this.spinner.runCommand(async () => {
const buildData = {
timestamp: Date.now(),
artifactsPath,
buildMode,
isVerified: false,
};
const newLocalConfig = new ConfigBuilder(localConfig)
.addContractBuild(args.contractName, buildData)
.build();
await this.storeConfig(newLocalConfig, "local");
}, "Writing config");
}
}
}
Loading

0 comments on commit e315716

Please sign in to comment.