Skip to content

Commit

Permalink
Merge pull request #15
Browse files Browse the repository at this point in the history
added some non-interactive commands
  • Loading branch information
gfusee authored Mar 28, 2023
2 parents e9d6b42 + f33d961 commit ac3f2d3
Show file tree
Hide file tree
Showing 5 changed files with 179 additions and 108 deletions.
2 changes: 1 addition & 1 deletion cli/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@gfusee/xnetwork",
"version": "0.0.2",
"version": "0.1.1",
"description": "An all-in-one tool for creating and managing your own MultiversX network",
"type": "module",
"scripts": {
Expand Down
87 changes: 69 additions & 18 deletions cli/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,58 @@
#!/usr/bin/env node

import {getDefaultConfig} from "./config/config.js"
import {DockerPrerequisites} from "./prerequisites/dockerPrerequisites.js"
import {CLIConfig, getDefaultConfig} from "./config/config.js"
import {dontIndent} from "./utils/strings/dontIndent.js";
import chalk from "chalk";
import {GitRepoPrerequisites} from "./prerequisites/gitRepoPrerequisites.js"
import { program } from 'commander'
import {StartQuestion} from "./questions/startQuestion.js";
import fs from "fs/promises";
import {checkPrerequisites} from "./utils/host/checkPrerequisites.js";
import {createNetwork} from "./utils/docker/createNetwork.js";
import {removeExistingNetwork} from "./utils/docker/removeExistingNetwork.js";
import {readLatestConfig} from "./utils/config/readLatestConfig.js";

async function main() {

program
const defaultCommand = program
.name('xnetwork')
.version('0.0.1')
.description('An all-in-one tool for creating and managing your own MultiversX network')
.option('--no-cache', 'Do not use cache when downloading files')
.option('--custom-repo-path <path>', 'Fetch files from a custom repository')
.option('--custom-repo-branch <branch>', 'Fetch files from a custom repository branch')
.action(startInteractiveSetup)

await program.parse(process.argv)
defaultCommand
.command('create')
.description('Create a new network, non-interactive')
.argument('<config-path>', 'Path to the config file')
.action((configPath) => {createNetworkAction(configPath)})

console.log('Checking prerequisites...')
defaultCommand
.command('remove')
.description('Remove a network, non-interactive')
.action(removeNetworkAction)

try {
await (new DockerPrerequisites()).check()
await (new GitRepoPrerequisites()).check()
} catch (e) {
if (typeof e === 'string') {
console.log(e)
process.exit(1)
} else {
throw e
}
}
const configCommand = defaultCommand
.command('config')
.description('Config utils')

configCommand
.command('generate')
.description('Generate a config file')
.argument('<output-path>', 'Path to the output file')
.action((outputPath) => {generateConfigAction(outputPath)})

configCommand
.command('latest')
.description('Get the latest config file used to create a network, if exists')
.action(getLatestConfigAction)

await program.parse(process.argv)
}

async function startInteractiveSetup() {
await checkPrerequisites()

const welcomeMessage = `
Welcome to ${chalk.bold('xNetwork')} CLI! 🔥
Expand All @@ -47,4 +67,35 @@ async function main() {
await (new StartQuestion()).process(config)
}

main().then(() => console.log('\nDone'))
async function createNetworkAction(configPath: string) {
await checkPrerequisites()

const configRaw = await fs.readFile(configPath, 'utf-8')
const config = JSON.parse(configRaw) as CLIConfig

await createNetwork(config)
}

async function removeNetworkAction() {
await checkPrerequisites()

await removeExistingNetwork()
}

async function generateConfigAction(outputPath: string) {
const config = getDefaultConfig()
const configString = JSON.stringify(config, null, 4)

await fs.writeFile(outputPath, configString)
}

async function getLatestConfigAction() {
try {
const latestConfig = await readLatestConfig()
console.log(JSON.stringify(latestConfig, null, 4))
} catch (e) {
console.log('No latest config found')
}
}

main().then(() => console.log('Done'))
91 changes: 2 additions & 89 deletions cli/src/questions/runner/runnerQuestion.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,7 @@
import {CLIQuestion} from "../question.js"
import {Answers, ListQuestion, Question} from "inquirer"
import {CLIConfig} from "../../config/config.js"
import {execCustomInRepo, ExecError} from "../../utils/exec.js"
import ora from "ora"
import {waitForVMQueryToBeReady} from "../../utils/healthchecks/waitForVMQueryToBeReady.js";
import {waitForAPIToBeReady} from "../../utils/healthchecks/waitForAPIToBeReady.js";
import {removeExistingNetwork} from "../../utils/docker/removeExistingNetwork.js";
import {ResultLogger} from "../../result/resultLogger.js";
import {saveLatestConfig} from "../../utils/config/saveLatestConfig.js";
import {upContainer} from "../../utils/docker/upContainer.js";
import {Constants} from "../../config/constants.js";
import {createNetwork} from "../../utils/docker/createNetwork.js";

export class RunnerQuestion extends CLIQuestion {

Expand All @@ -29,88 +21,9 @@ export class RunnerQuestion extends CLIQuestion {

override async handleAnswer(answers: Answers, config: CLIConfig): Promise<CLIQuestion[]> {
if (answers.choice === RunnerQuestion.yesChoice) {
await this.run(config)
await createNetwork(config)
}

return []
}

private async run(config: CLIConfig) {
try {
await removeExistingNetwork()

if (config.shouldHaveElasticSearch) {
const startingElasticSearchSpinner = ora('Starting ElasticSearch container...').start()
await upContainer(Constants.ELASTIC_CONTAINER.name)
startingElasticSearchSpinner.succeed('Started ElasticSearch container')
}

if (config.shouldHaveMySQL) {
const startingMySQLSpinner = ora('Starting MySQL container...').start()
await upContainer(Constants.MYSQL_CONTAINER.name)
startingMySQLSpinner.succeed('Started MySQL container')
}

if (config.shouldHaveRedis) {
const startingRedisSpinner = ora('Starting Redis container...').start()
await upContainer(Constants.REDIS_CONTAINER.name)
startingRedisSpinner.succeed('Started Redis container')
}

if (config.shouldHaveRabbitMQ) {
const startingRabbitMQSpinner = ora('Starting RabbitMQ container...').start()
await upContainer(Constants.RABBITMQ_CONTAINER.name)
startingRabbitMQSpinner.succeed('Started RabbitMQ container')
}

const startingNetworkSpinner = ora('Starting network...').start()
await upContainer(Constants.TESTNET_CONTAINER.name, {
...process.env,
"MX_LT_NUM_SHARDS": config.numberOfShards.toString(),
"MX_LT_ELASTIC_ENABLED": config.shouldHaveElasticSearch.toString(),
"MX_LT_CUSTOM_EGLD_ADDRESS": config.initialEGLDAddress ?? ""
})
startingNetworkSpinner.succeed('Started network successfully')

await saveLatestConfig(config)

await waitForVMQueryToBeReady()

if (config.shouldHaveApi) {
const startingApiSpinner = ora('Starting API container...').start()
await upContainer(Constants.API_CONTAINER.name)
startingApiSpinner.succeed('Started API container')

const startingApiHealthCheckSpinner = ora('Waiting for API to be ready').start()
await waitForAPIToBeReady()
startingApiHealthCheckSpinner.succeed('API is ready')
}

if (config.mxOpsScenesPath) {
const copyingScenesSpinner = ora('Copying mxops scenes...').start()
await execCustomInRepo(`docker-compose cp ${config.mxOpsScenesPath} testnet:/home/ubuntu/mxops`)
copyingScenesSpinner.succeed('Copied mxops scenes')

const runningScenesSpinner = ora('Running mxops scenes...').start()
await execCustomInRepo(`docker-compose exec testnet python3 run_mxops.py`)
runningScenesSpinner.succeed('Ran mxops scenes')
}

const resultLogger = new ResultLogger()
await resultLogger.printResults(config)
} catch (e) {
try {
const error = e as ExecError
console.log("Error while running network...")
console.log("Command : ", error.error.cmd)
console.log("Exit code : ", error.error.code)
console.log("Message : ", error.stderr)
} catch (e2) {
console.log("Error while running network...")
console.log(e)
}

throw e
}
}
}
89 changes: 89 additions & 0 deletions cli/src/utils/docker/createNetwork.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import {CLIConfig} from "../../config/config.js";
import {removeExistingNetwork} from "./removeExistingNetwork.js";
import ora from "ora";
import {upContainer} from "./upContainer.js";
import {Constants} from "../../config/constants.js";
import {saveLatestConfig} from "../config/saveLatestConfig.js";
import {waitForVMQueryToBeReady} from "../healthchecks/waitForVMQueryToBeReady.js";
import {waitForAPIToBeReady} from "../healthchecks/waitForAPIToBeReady.js";
import {execCustomInRepo, ExecError} from "../exec.js";
import {ResultLogger} from "../../result/resultLogger.js";

export async function createNetwork(config: CLIConfig) {
try {
await removeExistingNetwork()

if (config.shouldHaveElasticSearch) {
const startingElasticSearchSpinner = ora('Starting ElasticSearch container...').start()
await upContainer(Constants.ELASTIC_CONTAINER.name)
startingElasticSearchSpinner.succeed('Started ElasticSearch container')
}

if (config.shouldHaveMySQL) {
const startingMySQLSpinner = ora('Starting MySQL container...').start()
await upContainer(Constants.MYSQL_CONTAINER.name)
startingMySQLSpinner.succeed('Started MySQL container')
}

if (config.shouldHaveRedis) {
const startingRedisSpinner = ora('Starting Redis container...').start()
await upContainer(Constants.REDIS_CONTAINER.name)
startingRedisSpinner.succeed('Started Redis container')
}

if (config.shouldHaveRabbitMQ) {
const startingRabbitMQSpinner = ora('Starting RabbitMQ container...').start()
await upContainer(Constants.RABBITMQ_CONTAINER.name)
startingRabbitMQSpinner.succeed('Started RabbitMQ container')
}

const startingNetworkSpinner = ora('Starting network...').start()
await upContainer(Constants.TESTNET_CONTAINER.name, {
...process.env,
"MX_LT_NUM_SHARDS": config.numberOfShards.toString(),
"MX_LT_ELASTIC_ENABLED": config.shouldHaveElasticSearch.toString(),
"MX_LT_CUSTOM_EGLD_ADDRESS": config.initialEGLDAddress ?? ""
})
startingNetworkSpinner.succeed('Started network successfully')

await saveLatestConfig(config)

await waitForVMQueryToBeReady()

if (config.shouldHaveApi) {
const startingApiSpinner = ora('Starting API container...').start()
await upContainer(Constants.API_CONTAINER.name)
startingApiSpinner.succeed('Started API container')

const startingApiHealthCheckSpinner = ora('Waiting for API to be ready').start()
await waitForAPIToBeReady()
startingApiHealthCheckSpinner.succeed('API is ready')
}

if (config.mxOpsScenesPath) {
const copyingScenesSpinner = ora('Copying mxops scenes...').start()
await execCustomInRepo(`docker-compose cp ${config.mxOpsScenesPath} testnet:/home/ubuntu/mxops`)
copyingScenesSpinner.succeed('Copied mxops scenes')

const runningScenesSpinner = ora('Running mxops scenes...').start()
await execCustomInRepo(`docker-compose exec testnet python3 run_mxops.py`)
runningScenesSpinner.succeed('Ran mxops scenes')
}

const resultLogger = new ResultLogger()
await resultLogger.printResults(config)
} catch (e) {
try {
const error = e as ExecError
console.log("Error while running network...")
console.log("Command : ", error.error.cmd)
console.log("Exit code : ", error.error.code)
console.log("Message : ", error.stderr)
} catch (e2) {
console.log("Error while running network...")
console.log(e)
}

throw e
}
}
18 changes: 18 additions & 0 deletions cli/src/utils/host/checkPrerequisites.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import {DockerPrerequisites} from "../../prerequisites/dockerPrerequisites.js";
import {GitRepoPrerequisites} from "../../prerequisites/gitRepoPrerequisites.js";

export async function checkPrerequisites() {
console.log('Checking prerequisites...')

try {
await (new DockerPrerequisites()).check()
await (new GitRepoPrerequisites()).check()
} catch (e) {
if (typeof e === 'string') {
console.log(e)
process.exit(1)
} else {
throw e
}
}
}

0 comments on commit ac3f2d3

Please sign in to comment.