mirror of
https://github.com/EvernodeXRPL/hp-devkit.git
synced 2026-04-29 15:37:58 +00:00
Cross platform support by migrating to npm (#10)
This commit is contained in:
1
npm/.gitignore
vendored
Normal file
1
npm/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
node_modules
|
||||
43
npm/README.md
Normal file
43
npm/README.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# HotPocket developer kit
|
||||
Evernode uses HotPocket as its smart contract engine. HotPocket smart contracts can be developed using any POSIX-compliant language/framework. To make it easy to develop and test HotPocket smart contracts on your local PC, you can use HotPocket developer kit.
|
||||
|
||||
## Installation
|
||||
|
||||
### Prerequisites
|
||||
HotPocket developer kit requires you to install [Docker Engine](https://docs.docker.com/engine/install/) and [NodeJs](https://nodejs.org/en/) on your development machine.
|
||||
|
||||
### Supports cross platform
|
||||
This is a npm global package which supports both Linux and Windows
|
||||
1. Install [prerequisites](#prerequisites).
|
||||
2. Run the following command to install hpdevkit on your machine.
|
||||
```
|
||||
npm i -g hpdevkit
|
||||
```
|
||||
|
||||
## Updates
|
||||
Update `hpdevkit` to the latest and update the supporting docker images.
|
||||
|
||||
Run one of following commands to update hpdevkit.
|
||||
- Method 1 - Using hpdevkit CLI
|
||||
```
|
||||
hpdevkit update
|
||||
```
|
||||
|
||||
- Method 2 - Using npm
|
||||
```
|
||||
npm update -g hpdevkit
|
||||
```
|
||||
|
||||
**NOTE: You need to re-deploy your contracts to make the new changes effective.**
|
||||
|
||||
## Uninstall
|
||||
Uninstall `hpdevkit` and the supporting docker images and containers.
|
||||
|
||||
- Using hpdevkit CLI
|
||||
```
|
||||
hpdevkit uninstall
|
||||
```
|
||||
|
||||
**NOTE: Uninstalling from hpdevkit CLI is recommended. If you uninstall using npm you'll have to clean hpdevkit supporting docker images and containers manually.**
|
||||
|
||||
_**NOTE:** In Linux platforms, for Installation, Update and Uninstallation you'll need root privileges. Add `sudo` to above commands._
|
||||
15
npm/appenv.js
Normal file
15
npm/appenv.js
Normal file
@@ -0,0 +1,15 @@
|
||||
const process = require('process');
|
||||
|
||||
const appenv = {
|
||||
cluster: 'default',
|
||||
clusterSize: process.env.HP_CLUSTER_SIZE || 3,
|
||||
defaultNode: process.env.HP_DEFAULT_NODE || 1,
|
||||
devkitImage: process.env.HP_DEVKIT_IMAGE || 'evernodedev/hpdevkit',
|
||||
instanceImage: process.env.HP_INSTANCE_IMAGE || 'evernodedev/hotpocket:latest-ubt.20.04-njs.16',
|
||||
hpUserPortBegin: process.env.HP_USER_PORT_BEGIN || 8081,
|
||||
hpPeerPortBegin: process.env.HP_PEER_PORT_BEGIN || 22861,
|
||||
}
|
||||
|
||||
Object.freeze(appenv);
|
||||
|
||||
module.exports = appenv
|
||||
59
npm/index.js
Executable file
59
npm/index.js
Executable file
@@ -0,0 +1,59 @@
|
||||
#! /usr/bin/env node
|
||||
|
||||
const { program } = require('commander');
|
||||
const { version, codeGen, deploy, clean, logs, start, stop, update, uninstall } = require('./lib/command-handler');
|
||||
|
||||
program
|
||||
.command('version')
|
||||
.description('hpdevkit version')
|
||||
.action(version);
|
||||
|
||||
program
|
||||
.command('gen <platform> <app-type> <project-name>')
|
||||
.description('hpdevkit gen <platform> <app-type> <project-name>')
|
||||
.action(codeGen);
|
||||
|
||||
program
|
||||
.command('deploy <contract-path>')
|
||||
.description('hpdevkit deploy <contract-path>')
|
||||
.action(deploy);
|
||||
|
||||
program
|
||||
.command('clean')
|
||||
.description('hpdevkit clean')
|
||||
.action(clean);
|
||||
|
||||
program
|
||||
.command('logs <node-number>')
|
||||
.description('hpdevkit logs <node-number>')
|
||||
.action(logs);
|
||||
|
||||
program
|
||||
.command('start <node-number>')
|
||||
.description('hpdevkit start <node-number>')
|
||||
.action(start);
|
||||
|
||||
program
|
||||
.command('stop <node-number>')
|
||||
.description('hpdevkit stop <node-number>')
|
||||
.action(stop);
|
||||
|
||||
program
|
||||
.command('update')
|
||||
.description('hpdevkit update')
|
||||
.action(update);
|
||||
|
||||
program
|
||||
.command('uninstall')
|
||||
.description('uninstall')
|
||||
.action(uninstall);
|
||||
|
||||
try {
|
||||
program.parse();
|
||||
}
|
||||
catch (e) {
|
||||
// Console outputs will be handled inside command functions.
|
||||
// Log the exception if not a console output.
|
||||
if (!('stdout' in e) && !('stderr' in e))
|
||||
console.error(e);
|
||||
}
|
||||
9
npm/lib/child-proc.js
Normal file
9
npm/lib/child-proc.js
Normal file
@@ -0,0 +1,9 @@
|
||||
const { execSync } = require('child_process');
|
||||
|
||||
function exec(commad, streamOut = false) {
|
||||
return execSync(commad, streamOut ? { stdio: 'inherit' } : {stdio : 'pipe' });
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
exec
|
||||
};
|
||||
158
npm/lib/command-handler.js
Normal file
158
npm/lib/command-handler.js
Normal file
@@ -0,0 +1,158 @@
|
||||
const fs = require('fs');
|
||||
const appenv = require('../appenv');
|
||||
const { exec } = require('./child-proc');
|
||||
const {
|
||||
CONSTANTS,
|
||||
initializeDeploymentCluster,
|
||||
runOnContainer,
|
||||
executeOnContainer,
|
||||
teardownDeploymentCluster,
|
||||
isExists,
|
||||
updateDockerImages
|
||||
} = require('./common');
|
||||
const { success, error, info, warn } = require('./logger');
|
||||
|
||||
function version() {
|
||||
info(`command: version`);
|
||||
|
||||
try {
|
||||
const res = exec(`npm -g list ${CONSTANTS.npmPackageName} --depth=0`);
|
||||
const splitted = res.toString().split('\n');
|
||||
if (splitted.length > 1) {
|
||||
success(`\n${splitted[1].split('@')[1]}\n`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (e) { }
|
||||
|
||||
error(`\n${CONSTANTS.npmPackageName} is not installed.`);
|
||||
}
|
||||
|
||||
function codeGen(platform, apptype, projName) {
|
||||
info("Code generator\n");
|
||||
|
||||
if (fs.existsSync(projName)) {
|
||||
error(`Directory '${projName}' already exists.`);
|
||||
return;
|
||||
}
|
||||
|
||||
let containerStarted = false;
|
||||
try {
|
||||
runOnContainer(CONSTANTS.codegenContainerName, null, null, null, null, `${platform} ${apptype} ${projName}`, 'codegen');
|
||||
containerStarted = true;
|
||||
exec(`docker cp ${CONSTANTS.codegenContainerName}:${CONSTANTS.codegenOutputDir} ./${projName}`);
|
||||
success(`Project '${projName}' created.`);
|
||||
}
|
||||
catch (e) {
|
||||
error(`Project '${projName}' generation failed.`);
|
||||
}
|
||||
finally {
|
||||
if (containerStarted)
|
||||
exec(`docker rm ${CONSTANTS.codegenContainerName}`, false);
|
||||
}
|
||||
}
|
||||
|
||||
function deploy(contractPath) {
|
||||
info(`command: deploy (cluster: ${appenv.cluster})`);
|
||||
|
||||
initializeDeploymentCluster();
|
||||
|
||||
// If copying a directory, delete target bundle directory. If not create empty target bundle directory to copy a file.
|
||||
const prepareBundleDir = contractPath ?
|
||||
`rm -rf ${CONSTANTS.bundleMount}` :
|
||||
`mkdir -p ${CONSTANTS.bundleMount} && rm -rf ${CONSTANTS.bundleMount}/* ${CONSTANTS.bundleMount}/.??*`;
|
||||
|
||||
executeOnContainer(CONSTANTS.deploymentContainerName, prepareBundleDir);
|
||||
exec(`docker cp ${contractPath} "${CONSTANTS.deploymentContainerName}:${CONSTANTS.bundleMount}"`);
|
||||
|
||||
// Sync contract bundle to all instance directories in the cluster.
|
||||
executeOnContainer(CONSTANTS.deploymentContainerName, 'cluster stop ; cluster sync ; cluster start');
|
||||
|
||||
if (appenv.defaultNode > 0) {
|
||||
info(`Streaming logs of node ${appenv.defaultNode}:`);
|
||||
executeOnContainer(CONSTANTS.deploymentContainerName, `cluster logs ${appenv.defaultNode}`);
|
||||
}
|
||||
}
|
||||
|
||||
function clean() {
|
||||
info(`command: clean (cluster: ${appenv.cluster})`);
|
||||
|
||||
teardownDeploymentCluster();
|
||||
}
|
||||
|
||||
function logs(nodeNumber) {
|
||||
info(`command: logs (cluster: ${appenv.cluster})`);
|
||||
|
||||
runOnContainer(null, null, true, true, null, `logs ${nodeNumber}`, 'cluster');
|
||||
}
|
||||
|
||||
function start(nodeNumber) {
|
||||
info(`command: start (cluster: ${appenv.cluster})`);
|
||||
|
||||
runOnContainer(null, null, true, true, null, `start ${nodeNumber}`, 'cluster');
|
||||
}
|
||||
|
||||
function stop(nodeNumber) {
|
||||
info(`command: stop (cluster: ${appenv.cluster})`);
|
||||
|
||||
runOnContainer(null, null, true, true, null, `stop ${nodeNumber}`, 'cluster');
|
||||
}
|
||||
|
||||
function update() {
|
||||
info(`command: update`);
|
||||
|
||||
// Update npm package if outdated (Docker images will be updated from there). Otherwise only update the docker images.
|
||||
try {
|
||||
exec(`npm -g outdated ${CONSTANTS.npmPackageName}`);
|
||||
info('\nUpdating docker images...');
|
||||
updateDockerImages();
|
||||
}
|
||||
catch (e) {
|
||||
const splitted = e.stdout.toString().trim().split('\n').map(l => l.trim().split(/\s+/));
|
||||
if (splitted.length > 1) {
|
||||
info(`\nUpdating ${CONSTANTS.npmPackageName} npm package...`);
|
||||
exec(`npm -g install ${CONSTANTS.npmPackageName}@${splitted[1][3]}`, true);
|
||||
}
|
||||
}
|
||||
|
||||
success('\nUpdate Completed !!');
|
||||
warn('NOTE: You need to re-deploy your contracts to make the new changes effective.');
|
||||
}
|
||||
|
||||
function uninstall() {
|
||||
info(`command: uninstall`);
|
||||
|
||||
info(`\nUninstalling ${CONSTANTS.npmPackageName} npm package...`);
|
||||
exec(`npm -g uninstall ${CONSTANTS.npmPackageName}`, true);
|
||||
|
||||
// Remove deployment cluster if exist.
|
||||
if (isExists(CONSTANTS.deploymentContainerName)) {
|
||||
info('\nCleaning the deployed contracts...');
|
||||
teardownDeploymentCluster();
|
||||
}
|
||||
|
||||
// Remove docker images if exist.
|
||||
if (isExists(appenv.devkitImage, 'image')) {
|
||||
info('\nRemoving devkit docker image...');
|
||||
exec(`docker image rm ${appenv.devkitImage}`, true);
|
||||
}
|
||||
|
||||
if (isExists(appenv.instanceImage, 'image')) {
|
||||
info('\nRemoving instance docker image...');
|
||||
exec(`docker image rm ${appenv.instanceImage}`, true);
|
||||
}
|
||||
|
||||
success('\nUninstalled hpdevkit !!');
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
version,
|
||||
codeGen,
|
||||
deploy,
|
||||
clean,
|
||||
logs,
|
||||
start,
|
||||
stop,
|
||||
update,
|
||||
uninstall
|
||||
};
|
||||
122
npm/lib/common.js
Normal file
122
npm/lib/common.js
Normal file
@@ -0,0 +1,122 @@
|
||||
const appenv = require("../appenv");
|
||||
const { exec } = require("./child-proc");
|
||||
const { log, info } = require("./logger");
|
||||
|
||||
const GLOBAL_PREFIX = "hpdevkit";
|
||||
const VERSION = "0.1.0";
|
||||
|
||||
const CONSTANTS = {
|
||||
npmPackageName: `hpdevkit`,
|
||||
volumeMount: `/${GLOBAL_PREFIX}_vol`,
|
||||
volume: `${GLOBAL_PREFIX}_${appenv.cluster}_vol`,
|
||||
network: `${GLOBAL_PREFIX}_${appenv.cluster}_net`,
|
||||
containerPrefix: `${GLOBAL_PREFIX}_${appenv.cluster}_node`,
|
||||
bundleMount: `${GLOBAL_PREFIX}_vol/contract_bundle`,
|
||||
deploymentContainerName: `${GLOBAL_PREFIX}_${appenv.cluster}_deploymgr`,
|
||||
confOverrideFile: "hp.cfg.override",
|
||||
codegenOutputDir: "/codegen-output",
|
||||
codegenContainerName: `${GLOBAL_PREFIX}_codegen`
|
||||
};
|
||||
|
||||
function runOnContainer(name, detached, autoRemove, mountStock, mountVolume, entryCmd, entryPoint, interactive = true) {
|
||||
command = `docker run`;
|
||||
|
||||
if (interactive)
|
||||
command += " -it";
|
||||
|
||||
if (name)
|
||||
command += ` --name ${name}`;
|
||||
|
||||
if (detached)
|
||||
command += " -d";
|
||||
|
||||
if (autoRemove)
|
||||
command += " --rm";
|
||||
|
||||
if (mountStock)
|
||||
command += " --mount type=bind,src=/var/run/docker.sock,dst=/var/run/docker.sock";
|
||||
|
||||
if (mountVolume)
|
||||
command += ` --mount type=volume,src=${CONSTANTS.volume},dst=${CONSTANTS.volumeMount}`;
|
||||
|
||||
if (entryPoint)
|
||||
command += ` --entrypoint ${entryPoint}`;
|
||||
else
|
||||
command += " --entrypoint /bin/bash";
|
||||
|
||||
command += ` -e CLUSTER=${appenv.cluster} -e CLUSTER_SIZE=${appenv.clusterSize} -e DEFAULT_NODE=${appenv.defaultNode} -e VOLUME=${CONSTANTS.volume} -e NETWORK=${CONSTANTS.network}`;
|
||||
command += ` -e CONTAINER_PREFIX=${CONSTANTS.containerPrefix} -e VOLUME_MOUNT=${CONSTANTS.volumeMount} -e BUNDLE_MOUNT=${CONSTANTS.bundleMount} -e HOTPOCKET_IMAGE=${appenv.instanceImage}`;
|
||||
command += ` -e CONFIG_OVERRIDES_FILE=${CONSTANTS.confOverrideFile} -e CODEGEN_OUTPUT=${CONSTANTS.codegenOutputDir}`;
|
||||
command += ` -e HP_USER_PORT_BEGIN=${appenv.hpUserPortBegin} -e HP_PEER_PORT_BEGIN=${appenv.hpPeerPortBegin}`;
|
||||
|
||||
command += ` ${appenv.devkitImage}`;
|
||||
|
||||
if (entryCmd) {
|
||||
if (entryPoint)
|
||||
command += ` ${entryCmd}`;
|
||||
else
|
||||
command += ` -c "${entryCmd}"`;
|
||||
}
|
||||
|
||||
exec(command, true);
|
||||
}
|
||||
|
||||
function executeOnContainer(name, cmd) {
|
||||
if (name)
|
||||
exec(`docker exec ${name} /bin/bash -c "${cmd}"`, true);
|
||||
}
|
||||
|
||||
function isExists(name, type = null) {
|
||||
try {
|
||||
const res = exec(`docker ${type === 'image' ? 'image ' : ''}inspect ${name}`);
|
||||
if (!res)
|
||||
return false;
|
||||
const resJson = JSON.parse(res.toString().trim());
|
||||
return !!(resJson && resJson.length);
|
||||
}
|
||||
catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function initializeDeploymentCluster() {
|
||||
if (!isExists(CONSTANTS.deploymentContainerName)) {
|
||||
log("\nInitializing deployment cluster");
|
||||
|
||||
// Stop cluster if running. Create cluster if not exists.
|
||||
runOnContainer(CONSTANTS.deploymentContainerName, null, true, true, null, 'cluster stop ; cluster create', null);
|
||||
|
||||
// Spin up management container.
|
||||
runOnContainer(CONSTANTS.deploymentContainerName, true, false, true, true, null, null);
|
||||
|
||||
// Bind the instance mesh network config together.
|
||||
executeOnContainer(CONSTANTS.deploymentContainerName, 'cluster bindmesh');
|
||||
}
|
||||
}
|
||||
|
||||
function teardownDeploymentCluster() {
|
||||
exec(`docker stop ${CONSTANTS.deploymentContainerName}`);
|
||||
exec(`docker rm ${CONSTANTS.deploymentContainerName}`);
|
||||
runOnContainer(null, null, true, true, null, "cluster stop ; cluster destroy", null, false);
|
||||
}
|
||||
|
||||
function updateDockerImages() {
|
||||
exec(`docker pull ${appenv.devkitImage}`);
|
||||
exec(`docker pull ${appenv.instanceImage}`, true);
|
||||
|
||||
// Clear if there's already deployed cluster since they are outdated now.
|
||||
if (isExists(CONSTANTS.deploymentContainerName)) {
|
||||
info('\nCleaning the deployed contracts...');
|
||||
teardownDeploymentCluster();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
runOnContainer,
|
||||
executeOnContainer,
|
||||
isExists,
|
||||
initializeDeploymentCluster,
|
||||
teardownDeploymentCluster,
|
||||
updateDockerImages,
|
||||
CONSTANTS
|
||||
};
|
||||
53
npm/lib/logger.js
Normal file
53
npm/lib/logger.js
Normal file
@@ -0,0 +1,53 @@
|
||||
const Reset = "\x1b[0m";
|
||||
const Bright = "\x1b[1m";
|
||||
const Dim = "\x1b[2m";
|
||||
const Underscore = "\x1b[4m";
|
||||
const Blink = "\x1b[5m";
|
||||
const Reverse = "\x1b[7m";
|
||||
const Hidden = "\x1b[8m";
|
||||
|
||||
const FgBlack = "\x1b[30m";
|
||||
const FgRed = "\x1b[31m";
|
||||
const FgGreen = "\x1b[32m";
|
||||
const FgYellow = "\x1b[33m";
|
||||
const FgBlue = "\x1b[34m";
|
||||
const FgMagenta = "\x1b[35m";
|
||||
const FgCyan = "\x1b[36m";
|
||||
const FgWhite = "\x1b[37m";
|
||||
|
||||
const BgBlack = "\x1b[40m";
|
||||
const BgRed = "\x1b[41m";
|
||||
const BgGreen = "\x1b[42m";
|
||||
const BgYellow = "\x1b[43m";
|
||||
const BgBlue = "\x1b[44m";
|
||||
const BgMagenta = "\x1b[45m";
|
||||
const BgCyan = "\x1b[46m";
|
||||
const BgWhite = "\x1b[47m";
|
||||
|
||||
function success(...args) {
|
||||
console.log(`${FgGreen}%s${Reset}`, ...args);
|
||||
}
|
||||
|
||||
function info(...args) {
|
||||
console.log(`${FgBlue}%s${Reset}`, ...args);
|
||||
}
|
||||
|
||||
function warn(...args) {
|
||||
console.log(`${FgYellow}%s${Reset}`, ...args);
|
||||
}
|
||||
|
||||
function error(...args) {
|
||||
console.log(`${FgRed}%s${Reset}`, ...args);
|
||||
}
|
||||
|
||||
function log(...args) {
|
||||
console.log(...args);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
success,
|
||||
info,
|
||||
warn,
|
||||
error,
|
||||
log
|
||||
};
|
||||
35
npm/package-lock.json
generated
Normal file
35
npm/package-lock.json
generated
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "hpdevkit",
|
||||
"version": "0.5.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "hpdevkit",
|
||||
"version": "0.5.0",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"commander": "9.4.0"
|
||||
},
|
||||
"bin": {
|
||||
"hpdevkit": "index.js"
|
||||
}
|
||||
},
|
||||
"node_modules/commander": {
|
||||
"version": "9.4.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-9.4.0.tgz",
|
||||
"integrity": "sha512-sRPT+umqkz90UA8M1yqYfnHlZA7fF6nSphDtxeywPZ49ysjxDQybzk13CL+mXekDRG92skbcqCLVovuCusNmFw==",
|
||||
"engines": {
|
||||
"node": "^12.20.0 || >=14"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"commander": {
|
||||
"version": "9.4.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-9.4.0.tgz",
|
||||
"integrity": "sha512-sRPT+umqkz90UA8M1yqYfnHlZA7fF6nSphDtxeywPZ49ysjxDQybzk13CL+mXekDRG92skbcqCLVovuCusNmFw=="
|
||||
}
|
||||
}
|
||||
}
|
||||
22
npm/package.json
Normal file
22
npm/package.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "hpdevkit",
|
||||
"version": "0.5.0",
|
||||
"description": "Developer toolkit for HotPocket smart contract development",
|
||||
"scripts": {
|
||||
"install": "node scripts/install.js"
|
||||
},
|
||||
"keywords": [
|
||||
"HotPocket",
|
||||
"toolkit",
|
||||
"hpdevkit",
|
||||
"smart contract"
|
||||
],
|
||||
"homepage": "https://github.com/HotPocketDev/evernode-sdk",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"commander": "9.4.0"
|
||||
},
|
||||
"bin": {
|
||||
"hpdevkit": "./index.js"
|
||||
}
|
||||
}
|
||||
3
npm/scripts/install.js
Normal file
3
npm/scripts/install.js
Normal file
@@ -0,0 +1,3 @@
|
||||
const { updateDockerImages } = require("../lib/common");
|
||||
|
||||
updateDockerImages();
|
||||
Reference in New Issue
Block a user