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

add basic cli & devchain cmd #24

Merged
merged 15 commits into from
Mar 5, 2019
Merged
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
10 changes: 7 additions & 3 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
module.exports = {
// ENS is deterministically deployed to this address
ens: '0x5f6f7e8cc7346a11ca2def8f827b7a0b612c56a1'
}
// ENS is deterministically deployed to this address
ens: '0x5f6f7e8cc7346a11ca2def8f827b7a0b612c56a1',
ConsoleReporter: require('./src/reporters/ConsoleReporter'),
commands: {
devchain: require('./src/commands/devchain'),
},
}
17 changes: 16 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
{
"name": "@aragon/aragen",
"version": "3.0.0",
"version": "3.0.0-beta.1",
"description": "Generate Aragon local dev environment snapshots",
"main": "index.js",
"bin": {
"aragen": "./src/cli.js"
},
"scripts": {
"prepublishOnly": "npm run start",
"start": "npm run start-ganache-bg && npm run get-repos && npm run gen",
Expand Down Expand Up @@ -33,6 +36,18 @@
"url": "https://github.com/aragon/aragen/issues"
},
"homepage": "https://github.com/aragon/aragen#readme",
"dependencies": {
"@babel/polyfill": "^7.0.0",
"chalk": "^2.1.0",
"figures": "^2.0.0",
"ganache-core": "^2.2.1",
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's not pin this here, but pin it in aragonCLI so we can release that. Let's keep testing this after the aragonCLI release.

Copy link
Contributor

Choose a reason for hiding this comment

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

@sohkai I'm not sure about this one. You meant to leave v2.3.2 or do something else?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, hope is that everything will work with v2.3.2 (but maybe not D:). If it doesn't, then let's make sure to pin it to 2.2.1.

"listr": "^0.13.0",
"ncp": "^2.0.0",
"mkdirp": "^0.5.1",
"rimraf": "^2.6.2",
"web3": "^1.0.0-beta.34",
"yargs": "^12.0.2"
},
"devDependencies": {
"@aragon/cli": "^4.0.0-rc.14",
"ganache-cli": "^6.2.1",
Expand Down
2 changes: 1 addition & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ To use directly with ganache-cli:

```
npm install @aragon/aragen
ganache-cli --db node_modules/@aragon/aragen/aragon-ganache -m "explain tackle mirror kit van hammer degree position ginger unfair soup bonus" -i 15 -l 100000000
npx aragen devchain
```

If you wish to access from code, for example to run ganache-core directly:
Expand Down
6 changes: 5 additions & 1 deletion scripts/start-ganache
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
#!/bin/bash

cd $(dirname $0)/..
BASEPATH=$(pwd)
mnemonic=$(node -p "require(require('path').join(\"${BASEPATH}\", 'src/helpers/ganache-vars')).MNEMONIC")

rm -rf aragon-ganache
mkdir aragon-ganache
set -e;
npx ganache-cli -m "explain tackle mirror kit van hammer degree position ginger unfair soup bonus" -i 15 -l 100000000 --db aragon-ganache
npx ganache-cli -m "${mnemonic}" -i 15 -l 100000000 --db aragon-ganache
42 changes: 42 additions & 0 deletions src/cli.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#!/usr/bin/env node
require('@babel/polyfill')
const ConsoleReporter = require('./reporters/ConsoleReporter')

// Set up commands
const cmd = require('yargs').commandDir('./commands')

cmd.alias('h', 'help')
cmd.alias('v', 'version')

// Configure CLI behavior
cmd.demandCommand(1, 'You need to specify a command')

// Set global options
cmd.option('silent', {
description: 'Silence output to terminal',
default: false,
})

cmd.option('debug', {
description: 'Show more output to terminal',
default: false,
coerce: debug => {
if (debug || process.env.DEBUG) {
global.DEBUG_MODE = true
return true
}
},
})

// Run
const reporter = new ConsoleReporter()
// reporter.debug(JSON.stringify(process.argv))
cmd
.fail((msg, err, yargs) => {
if (!err) yargs.showHelp()
reporter.error(msg || err.message || 'An error occurred')
reporter.debug(err && err.stack)
})
.parse(process.argv.slice(2), {
reporter,
})
180 changes: 180 additions & 0 deletions src/commands/devchain.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
const TaskList = require('listr')
const ncp = require('ncp')
const ganache = require('ganache-core')
const Web3 = require('web3')
const { promisify } = require('util')
const os = require('os')
const path = require('path')
const rimraf = require('rimraf')
const mkdirp = require('mkdirp')
const chalk = require('chalk')
const fs = require('fs')
const listrOpts = require('../helpers/listr-options')
const pjson = require('../../package.json')

const { BLOCK_GAS_LIMIT, MNEMONIC } = require('../helpers/ganache-vars')

exports.command = 'devchain'
exports.describe =
'Open a test chain for development and pass arguments to ganache'
exports.builder = {
port: {
description: 'The port to run the local chain on',
default: 8545,
},
reset: {
type: 'boolean',
default: false,
description: 'Reset devchain to snapshot',
},
accounts: {
default: 2,
description: 'Number of accounts to print',
},
verbose: {
default: false,
type: 'boolean',
description: 'Enable verbose devchain output',
},
}

exports.task = async function({
port = 8545,
verbose = false,
reset = false,
showAccounts = 2,
reporter,
silent,
debug,
}) {
const removeDir = promisify(rimraf)
const mkDir = promisify(mkdirp)
const recursiveCopy = promisify(ncp)

const snapshotPath = path.join(
os.homedir(),
`.aragon/ganache-db-${pjson.version}`
)

const tasks = new TaskList(
[
{
title: 'Setting up a new chain from latest Aragon snapshot',
task: async (ctx, task) => {
await removeDir(snapshotPath)
await mkDir(path.resolve(snapshotPath, '..'))
const snapshot = path.join(__dirname, '../../aragon-ganache')
await recursiveCopy(snapshot, snapshotPath)
},
enabled: () => !fs.existsSync(snapshotPath) || reset,
},
{
title: 'Starting a local chain from snapshot',
task: async (ctx, task) => {
const server = ganache.server({
// Start on a different networkID every time to avoid Metamask nonce caching issue:
// https://github.com/aragon/aragon-cli/issues/156
network_id: parseInt(1e8 * Math.random()),
gasLimit: BLOCK_GAS_LIMIT,
mnemonic: MNEMONIC,
db_path: snapshotPath,
logger: verbose ? { log: reporter.info.bind(reporter) } : undefined,
})
const listen = () =>
new Promise((resolve, reject) => {
server.listen(port, err => {
if (err) return reject(err)

task.title = `Local chain started at port ${port}`
resolve()
})
})
await listen()

ctx.web3 = new Web3(
new Web3.providers.WebsocketProvider(`ws://localhost:${port}`)
)
const accounts = await ctx.web3.eth.getAccounts()

ctx.accounts = accounts.slice(0, parseInt(showAccounts))
ctx.mnemonic = MNEMONIC

const ganacheAccounts = server.provider.manager.state.accounts
ctx.privateKeys = ctx.accounts.map(address => ({
key: ganacheAccounts[address.toLowerCase()].secretKey.toString(
'hex'
),
address,
}))
},
},
],
listrOpts(silent, debug)
)

return tasks
}

exports.printAccounts = (reporter, privateKeys) => {
const firstAccountComment =
'(this account is used to deploy DAOs, it has more permissions)'

const formattedAccounts = privateKeys.map(
({ address, key }, i) =>
chalk.bold(
`Address #${i + 1}: ${address} ${
i === 0 ? firstAccountComment : ''
}\nPrivate key: `
) + key
)

reporter.info(`Here are some Ethereum accounts you can use.
The first one will be used for all the actions the CLI performs.
You can use your favorite Ethereum provider or wallet to import their private keys.
\n${formattedAccounts.join('\n')}`)
}

exports.printMnemonic = (reporter, mnemonic) => {
reporter.info(
`The accounts were generated from the following mnemonic phrase:\n${mnemonic}\n`
)
}

exports.printResetNotice = (reporter, reset) => {
if (reset) {
reporter.warning(`The devchain was reset, some steps need to be done to prevent issues:
- Reset the application cache in Aragon Core by going to Settings > Troubleshooting.
- If using Metamask: switch to a different network, and then switch back to the 'Private Network' (this will clear the nonce cache and prevent errors when sending transactions)
`)
}
}

exports.handler = async ({
reporter,
port,
reset,
verbose,
accounts,
silent,
debug,
}) => {
const task = await exports.task({
port,
reset,
verbose,
reporter,
showAccounts: accounts,
silent,
debug,
})
const { privateKeys, mnemonic } = await task.run()
exports.printAccounts(reporter, privateKeys)
exports.printMnemonic(reporter, mnemonic)
exports.printResetNotice(reporter, reset)

reporter.info(
`ENS instance deployed at 0x5f6f7e8cc7346a11ca2def8f827b7a0b612c56a1\n`
)

reporter.info(`Devchain running: ${chalk.bold('http://localhost:' + port)}.`)
}
4 changes: 4 additions & 0 deletions src/helpers/ganache-vars.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module.exports = {
BLOCK_GAS_LIMIT: 50e6,
MNEMONIC: 'explain tackle mirror kit van hammer degree position ginger unfair soup bonus'
}
19 changes: 19 additions & 0 deletions src/helpers/listr-options.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
const ListrRenderer = require('../reporters/ListrRenderer')

/**
* https://github.com/SamVerschueren/listr#options
* https://github.com/SamVerschueren/listr-update-renderer#options
* https://github.com/SamVerschueren/listr-verbose-renderer#options
*
* @param {boolean} silent Option silent
* @param {boolean} debug Option debug
* @returns {Object} listr options object
*/
function listrOpts(silent, debug) {
return {
renderer: ListrRenderer(silent, debug),
dateFormat: false,
}
}

module.exports = listrOpts
49 changes: 49 additions & 0 deletions src/reporters/ConsoleReporter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
const chalk = require('chalk')
const figures = require('figures')

module.exports = class ConsoleReporter {
constructor(opts = { silent: false }) {
this.silent = opts.silent
}

message(category = 'info', message) {
if (this.silent) return

const color = {
debug: 'magenta',
info: 'blue',
warning: 'yellow',
error: 'red',
success: 'green',
}[category]
const symbol = {
debug: figures.pointer,
info: figures.info,
warning: figures.warning,
error: figures.cross,
success: figures.tick,
}[category]
const icon = chalk[color](symbol)
console.log(` ${icon} ${message}`)
}

debug(message) {
if (global.DEBUG_MODE) this.message('debug', message)
}

info(message) {
this.message('info', message)
}

warning(message) {
this.message('warning', message)
}

error(message) {
this.message('error', message)
}

success(message) {
this.message('success', message)
}
}
9 changes: 9 additions & 0 deletions src/reporters/ListrRenderer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const VerboseRenderer = require('listr-verbose-renderer')
const SilentRenderer = require('listr-silent-renderer')
const UpdateRenderer = require('listr-update-renderer')

module.exports = function(silent, debug) {
if (debug) return VerboseRenderer
if (silent) return SilentRenderer
return UpdateRenderer
}