From 510d299276fd79c3efb442e85a5e83dfe29e7163 Mon Sep 17 00:00:00 2001 From: Ian Wijma Date: Sun, 19 Nov 2023 13:28:59 +1100 Subject: [PATCH] Created Keep Running (kr) command --- .github/workflows/build.yaml | 18 +++ .gitignore | 3 + bin/run.sh | 11 ++ package-lock.json | 213 +++++++++++++++++++++++++++++++++++ package.json | 27 +++++ src/index.js | 115 +++++++++++++++++++ 6 files changed, 387 insertions(+) create mode 100644 .github/workflows/build.yaml create mode 100644 .gitignore create mode 100755 bin/run.sh create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 src/index.js diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml new file mode 100644 index 0000000..64a60df --- /dev/null +++ b/.github/workflows/build.yaml @@ -0,0 +1,18 @@ +name: Bonsai CI +run-name: ${{ gitea.actor }} is running spotify.local CI pipeline 🚀 +on: [release] +concurrency: 'true' + +jobs: + Build: + runs-on: ubuntu-latest + steps: + - name: Check out repository code + uses: actions/checkout@v4 + - name: Download dependencies + run: npm ci + - name: Build binairy files + run: npm run build + - uses: actions/upload-artifact@v3 + with: + path: bin diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..16a5eaf --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.idea/ +node_modules/ +out/ \ No newline at end of file diff --git a/bin/run.sh b/bin/run.sh new file mode 100755 index 0000000..664e3c4 --- /dev/null +++ b/bin/run.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +SLEEP=${1:-'10'} + +echo "Sleeping for $SLEEP seconds..."; + +sleep $SLEEP; + +echo "Waking up!"; + +exit 1; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..b72eaa1 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,213 @@ +{ + "name": "kr", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "kr", + "version": "1.0.0", + "dependencies": { + "yargs": "^17.7.2" + }, + "bin": { + "kr": "src/index.js" + }, + "devDependencies": { + "@types/node": "^20.8.10", + "@types/yargs": "^17.0.29" + } + }, + "node_modules/@types/node": { + "version": "20.9.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.2.tgz", + "integrity": "sha512-WHZXKFCEyIUJzAwh3NyyTHYSR35SevJ6mZ1nWwJafKtiQbqRTIKSRcw3Ma3acqgsent3RRDqeVwpHntMk+9irg==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/yargs": { + "version": "17.0.31", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.31.tgz", + "integrity": "sha512-bocYSx4DI8TmdlvxqGpVNXOgCNR1Jj0gNPhhAY+iz1rgKDAaYrAYdFYnhDV1IFuiuVc9HkOwyDcFxaTElF3/wg==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "engines": { + "node": ">=12" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..7283594 --- /dev/null +++ b/package.json @@ -0,0 +1,27 @@ +{ + "name": "kr", + "version": "1.0.0", + "description": "", + "bin": "src/index.js", + "main": "src/index.js", + "scripts": { + "build": "pkg package.json" + }, + "pkg": { + "scripts": "src/**/*.js", + "targets": [ + "node18-linux-x64", + "node18-macos-x64", + "node18-win-x64" + ], + "outputPath": "out" + }, + "author": "Ian Wijma", + "dependencies": { + "yargs": "^17.7.2" + }, + "devDependencies": { + "@types/node": "^20.8.10", + "@types/yargs": "^17.0.29" + } +} diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..ef45515 --- /dev/null +++ b/src/index.js @@ -0,0 +1,115 @@ +const Yargs = require('yargs'); +const {spawn} = require("child_process"); + +const SECONDS_IN_A_MINUTE = 60; +const SECONDS_IN_A_HOUR = 60 * 60; + +const yargs = Yargs(process.argv.splice(2)) + .scriptName('kr') + .usage('kr [args] ') + .option('rpm', { + default: 0, + description: 'The amount of Retries Per Minute, before we stop retrying', + type: 'number' + }) + .option('rph', { + default: 0, + description: 'The amount of Retries Per Hour, before we stop retrying', + type: 'number' + }) + .help(); + +const { _ = [], rpm, rph } = yargs.argv; +const [command, ...args ] = _; + +if (!command) { + return yargs.showHelp(); +} + +let historyMax = 4; +let seconds = SECONDS_IN_A_MINUTE; +let restartName = 'minute'; + +if (rpm && rph) { + return console.error('Currently, can not define both --rpm and --rph, please choose only one.'); +} else if (rpm) { + historyMax = rpm +} else if (rph) { + historyMax = rph + seconds = SECONDS_IN_A_HOUR + restartName = 'hour' +} + +/** + * @type {Object} + */ +const history = {}; + +/** + * @param {string} logs + */ +const pushHistory = (logs) => history[(new Date).getTime()] = logs; + +const updateHistory = () => { + const clearKeys = []; + + const maxAge = (new Date).getTime() + seconds; + for (const historyTime in history) { + if (historyTime > maxAge) { + clearKeys.push(historyTime) + } + } + + clearKeys.forEach((key) => delete history[key]); +} + +const checkHistory = () => Object.keys(history).length <= historyMax; + +const runCommand = () => { + console.log(`Running ${command} ${args.join(' ')}`); + const runner = spawn(command, args); + + const commandLogs = []; + + const pushLog = (logEntry) => commandLogs.push(logEntry); + + const trimLog = () => { + if(commandLogs.length > 5000) commandLogs.shift() + }; + + const clearLogs = () => commandLogs.length = 0; + + const getLogs = () => commandLogs.join('\n'); + + const handleOut = (type, message) => { + pushLog(`[${type}]\t${message}`); + trimLog(); + }; + + const handleExit = (exitCode) => { + console.log(`[CRASH] exit code: ${exitCode}`) + if (exitCode !== 0) { + pushHistory(getLogs()); + clearLogs(); + updateHistory(); + if (checkHistory()) { + console.log('Restarting...'); + runCommand(); + } else { + console.error(`The process crashed more then ${historyMax} times in the past ${restartName}, stop retrying.`); + console.error(`See below the past ${historyMax} crashes:`); + for (const time in history) { + console.error(`Crash @ ${time}:\n${history[time]}`); + } + } + } + } + + runner.on('close', (exitCode) => handleExit(exitCode)) + runner.stdout.on('data', (data) => console.log(data.toString().trim())) + runner.stdout.on('data', (data) => handleOut('stdout', data.toString().trim())) + runner.stderr.on('data', (data) => handleOut('stderr', data.toString().trim())) +} + +// Command goes BRRRR +runCommand();