diff --git a/README.md b/README.md index 07ef50e..abf1fd0 100755 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ -# CLI application based on ENM (Ericsson Network Manager) REST API +# 💻 CLI application based on ENM (Ericsson Network Manager) REST API [![Github version](https://img.shields.io/github/package-json/version/vvsviridov/enm-cli?label=enm-cli&color=brightgreen&logo=github)](https://github.com/vvsviridov/enm-cli) [![Npm version](https://img.shields.io/npm/v/enm-cli?color=red&logo=npm&label=enm-cli)](https://www.npmjs.com/package/enm-cli) -## Key Features +## 🛠 Key Features - 🚲 Simple and easy to use - 📟 Does not require GUI @@ -11,7 +11,7 @@ - 🏗 Allows to make changes in config mode - 🤯 No needs to remember nodes IP addresses -## Installation +## 💾 Installation First you need **node.js** which can be downloaded from official site [nodejs.org](https://nodejs.org/en/download/) and installed as described in the docs. @@ -41,7 +41,7 @@ npm link ... to add application to your OS $PATH variable if you want to run it from anywhere. -## Store your credentials +## 🔏 Store your credentials ### Dotenv file @@ -56,7 +56,7 @@ PASSWORD=YourPassword By default you'll be prompted for username and password -## Launch apllication +## 🚀 Launch apllication ```shell > enm-cli -l USERNAME -p PASSWORD -u https://enm.your.company.domain.com @@ -67,7 +67,7 @@ By default you'll be prompted for username and password Shell Terminal ``` -## Usage +## 🕹 Usage Recommended environment is Windows Terminal (not _cmd.exe_) or any shell with rich formatting support. After application successfully launched youll see root content and available commands. @@ -86,7 +86,7 @@ Options: -h, --help display help for command ``` -## AutoProvisioning Application +## 🗂 AutoProvisioning Application ![AutoProvisioning](img/render1673699059489.gif?raw=true 'AutoProvisioning') @@ -197,7 +197,7 @@ Available commands are: - `[back]` - Return to project's nodes. - `[exit]` - Exit this app. -## TopologyBrowser Application +## 🔭 TopologyBrowser Application ![TopologyBrowser](img/render1673690475520.gif?raw=true 'TopologyBrowser') @@ -516,15 +516,15 @@ CONSTRAINTS Value Resolution: null ``` -## Bulk Import +## 🗃 Bulk Import ![BulkImport](img/render1673699800882.gif?raw=true 'BulkImport') -## Shell Terminal +## ⌨️ Shell Terminal ![ShellTerminal](img/render1673702182559.gif?raw=true 'ShellTerminal') -## Contribution +## 📝 Contribution 1. [Fork it] 2. Create your feature branch (`git checkout -b my-new-feature`) @@ -532,7 +532,7 @@ CONSTRAINTS 4. Push to the branch (`git push origin my-new-feature`) 5. Create new Pull Request -## Known Issues +## 🦠 Known Issues ### flickering issue @@ -544,11 +544,11 @@ Add 1 to _clearLine()_ on this line this.stream.clearLine(1); ``` -## Credits +## 💵 Credits [Contact Me](https://github.com/vvsviridov/) to request new feature or bugs reporting. -## Changes +## 💣 Changes 1.0.0 - Is released diff --git a/bin/enm-cli.js b/bin/enm-cli.js index 09e7dc9..e4a7b2d 100755 --- a/bin/enm-cli.js +++ b/bin/enm-cli.js @@ -4,6 +4,10 @@ const { Command, Option } = require('commander') const pkg = require('../package.json') const inquirer = require('inquirer') const path = require('path') +const { exec } = require('child_process') +const { promisify } = require('util') +const semver = require('semver') +const chalk = require('chalk') const { isEmpty } = require('../util/validation') @@ -17,106 +21,130 @@ const ShellTerminal = require('../lib/applications/ShellTerminal/ShellTerminal') const logError = require('../util/logError') if (process.env.NODE_ENV === 'development') { - process.on('uncaughtException', (exception) => console.log(exception)) + process.on('uncaughtException', (exception) => console.log(exception)) } + +const executeCommand = promisify(exec) + +async function checkVersion() { + try { + const { stdout } = await executeCommand(`npm view ${pkg.name} version`) + if (semver.lt(pkg.version, stdout.trim())) { + console.log(` + The version ${chalk.red(pkg.version)} of ${chalk.bold(pkg.name)} is out of date. + The latest version is ${chalk.green(stdout.trim())} + To update, run: + + ${chalk.bold('npm update -g ' + pkg.name)} + `) + } + } catch (error) { + console.error(`Error occurred while check for update: ${error.message}`) + } +} + + const applications = [ - { - id: 'tplg', - appClass: TopologyBrowser, - name: 'Topology Browser', - }, - { - id: 'prvn', - appClass: AutoProvisioning, - name: 'Auto Provisioning', - }, - { - id: 'bulk', - appClass: BulkImport, - name: 'Bulk Import', - }, - { - id: 'shll', - appClass: ShellTerminal, - name: 'Shell Terminal', - }, + { + id: 'tplg', + appClass: TopologyBrowser, + name: 'Topology Browser', + }, + { + id: 'prvn', + appClass: AutoProvisioning, + name: 'Auto Provisioning', + }, + { + id: 'bulk', + appClass: BulkImport, + name: 'Bulk Import', + }, + { + id: 'shll', + appClass: ShellTerminal, + name: 'Shell Terminal', + }, ] const appIds = applications.map(item => item.id) const program = new Command() program - .version(pkg.version) - .addOption(new Option('-l, --login ', 'ENM User Login').env('LOGIN')) - .addOption(new Option('-p, --password ', 'ENM User Password').env('PASSWORD')) - .addOption(new Option('-a, --application ', 'Start specified application') - .choices(appIds) - ) - .requiredOption('-u, --url ', 'ENM Url') - .parse(process.argv) + .version(pkg.version) + .addOption(new Option('-l, --login ', 'ENM User Login').env('LOGIN')) + .addOption(new Option('-p, --password ', 'ENM User Password').env('PASSWORD')) + .addOption(new Option('-a, --application ', 'Start specified application') + .choices(appIds) + ) + .requiredOption('-u, --url ', 'ENM Url') + .parse(process.argv) const options = program.opts() async function promptUsername() { - const input = await inquirer.prompt([ - { - type: 'input', - name: 'value', - prefix: '👤', - message: 'Type ENM login:', - validate: isEmpty, - } - ]) - return input.value + const input = await inquirer.prompt([ + { + type: 'input', + name: 'value', + prefix: '👤', + message: 'Type ENM login:', + validate: isEmpty, + } + ]) + return input.value } async function promptPassword() { - const input = await inquirer.prompt([ - { - type: 'password', - name: 'value', - prefix: '🔑', - message: 'Type ENM password:', - validate: isEmpty, - } - ]) - return input.value + const input = await inquirer.prompt([ + { + type: 'password', + name: 'value', + prefix: '🔑', + message: 'Type ENM password:', + validate: isEmpty, + } + ]) + return input.value } async function selectApplication() { - let selectedApp - if (options.application && appIds.includes(options.application)) { - selectedApp = options.application - } else { - const input = await inquirer.prompt([ - { - type: 'list', - name: 'application', - prefix: '💾', - message: 'Select Application:', - choices: applications.map(item => ({ name: item.name, value: item.id, short: item.id })) - }]) - selectedApp = input.application - } - const { appClass } = applications.find(item => item.id === selectedApp) - const login = options.login || await promptUsername() - const password = options.password || await promptPassword() - return new appClass(login, password, options.url) + let selectedApp + if (options.application && appIds.includes(options.application)) { + selectedApp = options.application + } else { + const input = await inquirer.prompt([ + { + type: 'list', + name: 'application', + prefix: '💾', + message: 'Select Application:', + choices: applications.map(item => ({ name: item.name, value: item.id, short: item.id })) + }]) + selectedApp = input.application + } + const { appClass } = applications.find(item => item.id === selectedApp) + const login = options.login || await promptUsername() + const password = options.password || await promptPassword() + return new appClass(login, password, options.url) } async function main() { - try { - const app = await selectApplication() - await app.login() - await app.inputHandler() - await app.logout() - } catch (error) { - logError(error) - } + try { + if (!process.env.DISABLE_UPDATE_CHECK) { + await checkVersion() + } + const app = await selectApplication() + await app.login() + await app.inputHandler() + await app.logout() + } catch (error) { + logError(error) + } } main() diff --git a/package-lock.json b/package-lock.json index 8f99409..75b178a 100755 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "enm-cli", - "version": "1.0.6a", + "version": "1.0.6c", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "enm-cli", - "version": "1.0.6a", + "version": "1.0.6c", "license": "MIT", "dependencies": { "axios": "^0.21.2", @@ -21,6 +21,7 @@ "inquirer-date-prompt": "^2.0.1", "inquirer-file-tree-selection-prompt": "^1.0.18", "ora": "^5.4.1", + "semver": "^7.3.8", "tough-cookie": "^4.0.0", "ws": "^8.11.0" }, @@ -528,6 +529,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -698,6 +710,20 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", @@ -847,6 +873,11 @@ "optional": true } } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" } }, "dependencies": { @@ -1171,6 +1202,14 @@ "is-unicode-supported": "^0.1.0" } }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, "mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -1285,6 +1324,14 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "requires": { + "lru-cache": "^6.0.0" + } + }, "signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", @@ -1390,6 +1437,11 @@ "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", "requires": {} + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" } } } diff --git a/package.json b/package.json index 3710e4e..1d6c41b 100755 --- a/package.json +++ b/package.json @@ -1,10 +1,10 @@ { "name": "enm-cli", - "version": "1.0.6c", + "version": "1.0.6-c", "description": "This is a cli application for Ericsson Network Manager (ENM)", "bin": "./bin/enm-cli.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "example": "node ./bin/enm-cli.js -u https://example.com/api/v1/" }, "keywords": [ "Ericsson", @@ -41,6 +41,7 @@ "inquirer-date-prompt": "^2.0.1", "inquirer-file-tree-selection-prompt": "^1.0.18", "ora": "^5.4.1", + "semver": "^7.3.8", "tough-cookie": "^4.0.0", "ws": "^8.11.0" },