Compare commits

..

49 Commits

Author SHA1 Message Date
efdb80ddba 2.0.27 2021-07-26 21:24:13 +02:00
db712b2223 fix(core): update 2021-07-26 21:24:13 +02:00
0199160013 2.0.26 2020-05-22 01:23:27 +00:00
199fa19899 fix(core): update 2020-05-22 01:23:27 +00:00
23a3230f07 2.0.25 2019-08-27 20:15:23 +02:00
ad0b7ac21c fix(core): update 2019-08-27 20:15:22 +02:00
c8c0cbd47a 2.0.24 2019-08-27 19:41:25 +02:00
213dac474e fix(core): update 2019-08-27 19:41:25 +02:00
a4c188051a 2.0.23 2019-05-29 10:56:46 +02:00
62195b63d6 fix(core): update 2019-05-29 10:56:45 +02:00
16ba3803a4 2.0.22 2019-05-28 11:14:33 +02:00
613241cdcb fix(core): update 2019-05-28 11:14:32 +02:00
f5df21e144 2.0.21 2019-05-28 10:43:55 +02:00
ba8c2d06d8 fix(core): update 2019-05-28 10:43:54 +02:00
9bb0999f0e 2.0.20 2019-05-27 15:31:15 +02:00
1beb3a0bbe 2.0.19 2019-05-27 15:30:52 +02:00
c1133e9131 fix(core): update 2019-05-27 15:30:52 +02:00
48dd3c3cca 2.0.18 2019-05-27 15:25:30 +02:00
f0a5be41ea fix(core): update 2019-05-27 15:25:30 +02:00
970d28a4ad 2.0.17 2019-05-23 23:29:36 +02:00
76f2126eb3 fix(core): update 2019-05-23 23:29:36 +02:00
a6f164602c 2.0.16 2019-05-22 15:45:09 +02:00
f8457ad58d fix(core): update 2019-05-22 15:45:08 +02:00
8ceac74a1f 2.0.15 2019-05-19 22:41:20 +02:00
f63c4456bf fix(core): update 2019-05-19 22:41:20 +02:00
32c33cad5b 2.0.14 2019-05-14 17:28:38 +02:00
0758e58fa4 fix(core): update 2019-05-14 17:28:38 +02:00
e981ca6bdd 2.0.13 2018-12-14 18:36:23 +01:00
2853a48708 fix(core): update 2018-12-14 18:36:23 +01:00
389f8f4a52 2.0.12 2018-12-14 18:35:31 +01:00
c1a537f052 fix(core): update 2018-12-14 18:35:31 +01:00
4701341c0c ensure WSL compatibility 2018-12-13 16:50:32 +01:00
d5d347721a 2.0.11 2018-11-26 17:55:16 +01:00
6a047d6333 fix(core): update 2018-11-26 17:55:15 +01:00
6e7b4dd18e 2.0.10 2018-11-26 17:46:32 +01:00
debc5f1524 fix(core): update 2018-11-26 17:46:31 +01:00
8d03561762 2.0.9 2018-11-26 17:45:59 +01:00
f516a6e753 fix(core): update 2018-11-26 17:45:58 +01:00
1c4f6a8bf9 2.0.8 2018-10-28 19:18:42 +01:00
4fe3775904 fix(child processes): now run detached 2018-10-28 19:18:42 +01:00
dcee2d6233 2.0.7 2018-10-28 19:12:16 +01:00
39d457382f fix(exec): processes now run detached 2018-10-28 19:12:15 +01:00
e6ed457501 2.0.6 2018-08-05 23:27:38 +02:00
0a6484762c fix(ci): update testing framework 2018-08-05 23:27:37 +02:00
baa3e4e6e9 2.0.5 2018-08-04 16:43:02 +02:00
0d68361381 fix(PATH): add proper PATH handling for child processes 2018-08-04 16:43:02 +02:00
286d80328c 2.0.4 2018-07-30 16:34:45 +02:00
2a71f00ab0 2.0.3 2018-07-30 16:19:38 +02:00
80a44a43cb fix(ci): update build step to be faster 2018-07-30 16:19:38 +02:00
14 changed files with 25396 additions and 604 deletions

20
.gitignore vendored
View File

@ -1,4 +1,20 @@
.nogit/
# artifacts
coverage/ coverage/
node_modules/
pages/
public/ public/
pages/
# installs
node_modules/
# caches
.yarn/
.cache/
.rpt2_cache
# builds
dist/
dist_*/
# custom

View File

@ -1,10 +1,10 @@
# gitzone standard # gitzone ci_default
image: hosttoday/ht-docker-node:npmci image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
cache: cache:
paths: paths:
- .npmci_cache/ - .npmci_cache/
key: "$CI_BUILD_STAGE" key: '$CI_BUILD_STAGE'
stages: stages:
- security - security
@ -19,56 +19,62 @@ mirror:
stage: security stage: security
script: script:
- npmci git mirror - npmci git mirror
only:
- tags
tags: tags:
- lossless
- docker - docker
- notpriv - notpriv
snyk: auditProductionDependencies:
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
stage: security stage: security
script: script:
- npmci command npm install -g snyk - npmci npm prepare
- npmci command npm install --ignore-scripts - npmci command npm install --production --ignore-scripts
- npmci command snyk test - npmci command npm config set registry https://registry.npmjs.org
- npmci command npm audit --audit-level=high --only=prod --production
tags: tags:
- docker - docker
- notpriv allow_failure: true
auditDevDependencies:
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
stage: security
script:
- npmci npm prepare
- npmci command npm install --ignore-scripts
- npmci command npm config set registry https://registry.npmjs.org
- npmci command npm audit --audit-level=high --only=dev
tags:
- docker
allow_failure: true
# ==================== # ====================
# test stage # test stage
# ==================== # ====================
testLEGACY:
stage: test
script:
- npmci node install legacy
- npmci npm install
- npmci npm test
coverage: /\d+.?\d+?\%\s*coverage/
tags:
- docker
- notpriv
allow_failure: true
testLTS: testStable:
stage: test
script:
- npmci node install lts
- npmci npm install
- npmci npm test
coverage: /\d+.?\d+?\%\s*coverage/
tags:
- docker
- notpriv
testSTABLE:
stage: test stage: test
script: script:
- npmci npm prepare
- npmci node install stable - npmci node install stable
- npmci npm install - npmci npm install
- npmci npm test - npmci npm test
coverage: /\d+.?\d+?\%\s*coverage/ coverage: /\d+.?\d+?\%\s*coverage/
tags: tags:
- docker - docker
- notpriv
testBuild:
stage: test
script:
- npmci npm prepare
- npmci node install stable
- npmci npm install
- npmci command npm run build
coverage: /\d+.?\d+?\%\s*coverage/
tags:
- docker
release: release:
stage: release stage: release
@ -78,6 +84,7 @@ release:
only: only:
- tags - tags
tags: tags:
- lossless
- docker - docker
- notpriv - notpriv
@ -86,20 +93,16 @@ release:
# ==================== # ====================
codequality: codequality:
stage: metadata stage: metadata
image: docker:stable
allow_failure: true allow_failure: true
services: only:
- docker:stable-dind - tags
script: script:
- export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/') - npmci command npm install -g tslint typescript
- docker run - npmci npm prepare
--env SOURCE_CODE="$PWD" - npmci npm install
--volume "$PWD":/code - npmci command "tslint -c tslint.json ./ts/**/*.ts"
--volume /var/run/docker.sock:/var/run/docker.sock
"registry.gitlab.com/gitlab-org/security-products/codequality:$SP_VERSION" /code
artifacts:
paths: [codeclimate.json]
tags: tags:
- lossless
- docker - docker
- priv - priv
@ -110,17 +113,20 @@ trigger:
only: only:
- tags - tags
tags: tags:
- lossless
- docker - docker
- notpriv - notpriv
pages: pages:
image: hosttoday/ht-docker-node:npmci
stage: metadata stage: metadata
script: script:
- npmci command npm install -g typedoc typescript - npmci node install lts
- npmci command npm install -g @gitzone/tsdoc
- npmci npm prepare
- npmci npm install - npmci npm install
- npmci command typedoc --module "commonjs" --target "ES2016" --out public/ ts/ - npmci command tsdoc
tags: tags:
- lossless
- docker - docker
- notpriv - notpriv
only: only:
@ -130,13 +136,3 @@ pages:
paths: paths:
- public - public
allow_failure: true allow_failure: true
windowsCompatibility:
image: stefanscherer/node-windows:10-build-tools
stage: metadata
script:
- npm install & npm test
coverage: /\d+.?\d+?\%\s*coverage/
tags:
- windows
allow_failure: true

29
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,29 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "current file",
"type": "node",
"request": "launch",
"args": [
"${relativeFile}"
],
"runtimeArgs": ["-r", "@gitzone/tsrun"],
"cwd": "${workspaceRoot}",
"protocol": "inspector",
"internalConsoleOptions": "openOnSessionStart"
},
{
"name": "test.ts",
"type": "node",
"request": "launch",
"args": [
"test/test.ts"
],
"runtimeArgs": ["-r", "@gitzone/tsrun"],
"cwd": "${workspaceRoot}",
"protocol": "inspector",
"internalConsoleOptions": "openOnSessionStart"
}
]
}

26
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,26 @@
{
"json.schemas": [
{
"fileMatch": ["/npmextra.json"],
"schema": {
"type": "object",
"properties": {
"npmci": {
"type": "object",
"description": "settings for npmci"
},
"gitzone": {
"type": "object",
"description": "settings for gitzone",
"properties": {
"projectType": {
"type": "string",
"enum": ["website", "element", "service", "npm", "wcc"]
}
}
}
}
}
}
]
}

View File

@ -1,32 +0,0 @@
# smartshell
shell actions designed as promises
## Availabililty
[![npm](https://pushrocks.gitlab.io/assets/repo-button-npm.svg)](https://www.npmjs.com/package/smartshell)
[![git](https://pushrocks.gitlab.io/assets/repo-button-git.svg)](https://GitLab.com/pushrocks/smartshell)
[![git](https://pushrocks.gitlab.io/assets/repo-button-mirror.svg)](https://github.com/pushrocks/smartshell)
[![docs](https://pushrocks.gitlab.io/assets/repo-button-docs.svg)](https://pushrocks.gitlab.io/smartshell/)
## Status for master
[![build status](https://GitLab.com/pushrocks/smartshell/badges/master/build.svg)](https://GitLab.com/pushrocks/smartshell/commits/master)
[![coverage report](https://GitLab.com/pushrocks/smartshell/badges/master/coverage.svg)](https://GitLab.com/pushrocks/smartshell/commits/master)
[![npm downloads per month](https://img.shields.io/npm/dm/smartshell.svg)](https://www.npmjs.com/package/smartshell)
[![Dependency Status](https://david-dm.org/pushrocks/smartshell.svg)](https://david-dm.org/pushrocks/smartshell)
[![bitHound Dependencies](https://www.bithound.io/github/pushrocks/smartshell/badges/dependencies.svg)](https://www.bithound.io/github/pushrocks/smartshell/master/dependencies/npm)
[![bitHound Code](https://www.bithound.io/github/pushrocks/smartshell/badges/code.svg)](https://www.bithound.io/github/pushrocks/smartshell)
[![TypeScript](https://img.shields.io/badge/TypeScript-2.x-blue.svg)](https://nodejs.org/dist/latest-v6.x/docs/api/)
[![node](https://img.shields.io/badge/node->=%206.x.x-blue.svg)](https://nodejs.org/dist/latest-v6.x/docs/api/)
[![JavaScript Style Guide](https://img.shields.io/badge/code%20style-standard-brightgreen.svg)](http://standardjs.com/)
## Usage
Use TypeScript for best in class instellisense.
For further information read the linked docs at the top of this README.
> MIT licensed | **©** [Lossless GmbH](https://lossless.gmbh)
[![repo-footer](https://pushrocks.gitlab.io/assets/repo-footer.svg)](https://push.rocks)

View File

@ -1,9 +1,18 @@
{ {
"npmci": { "npmci": {
"npmGlobalTools": [ "npmGlobalTools": [],
"npmts", "npmAccessLevel": "public",
"ts-node" "npmRegistryUrl": "registry.npmjs.org"
], },
"npmAccessLevel": "public" "gitzone": {
"projectType": "npm",
"module": {
"githost": "gitlab.com",
"gitscope": "pushrocks",
"gitrepo": "smartshell",
"shortDescription": "shell actions designed as promises",
"npmPackagename": "@pushrocks/smartshell",
"license": "MIT"
}
} }
} }

25432
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,13 @@
{ {
"name": "@pushrocks/smartshell", "name": "@pushrocks/smartshell",
"private": false, "private": false,
"version": "2.0.2", "version": "2.0.27",
"description": "shell actions designed as promises", "description": "shell actions designed as promises",
"main": "dist/index.js", "main": "dist_ts/index.js",
"typings": "dist/index.d.ts", "typings": "dist_ts/index.d.ts",
"scripts": { "scripts": {
"test": "(tsrun test/test.ts)", "test": "(tstest test/)",
"build": "(npmts)" "build": "(tsbuild --web)"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@ -24,13 +24,35 @@
}, },
"homepage": "https://gitlab.com/pushrocks/smartshell#README", "homepage": "https://gitlab.com/pushrocks/smartshell#README",
"devDependencies": { "devDependencies": {
"@gitzone/tsrun": "^1.1.9", "@gitzone/tsbuild": "^2.1.25",
"@pushrocks/tapbundle": "^3.0.1", "@gitzone/tsrun": "^1.2.17",
"@types/node": "^10.5.2" "@gitzone/tstest": "^1.0.54",
"@pushrocks/tapbundle": "^3.2.14",
"@types/node": "^16.4.3",
"tslint": "^6.1.3",
"tslint-config-prettier": "^1.18.0"
}, },
"dependencies": { "dependencies": {
"@pushrocks/smartpromise": "^2.0.5", "@pushrocks/smartdelay": "^2.0.13",
"@types/which": "^1.3.1", "@pushrocks/smartexit": "^1.0.15",
"which": "^1.3.1" "@pushrocks/smartpromise": "^3.1.6",
} "@types/which": "^2.0.1",
"tree-kill": "^1.2.2",
"which": "^2.0.2"
},
"files": [
"ts/**/*",
"ts_web/**/*",
"dist/**/*",
"dist_*/**/*",
"dist_ts/**/*",
"dist_ts_web/**/*",
"assets/**/*",
"cli.js",
"npmextra.json",
"readme.md"
],
"browserslist": [
"last 1 chrome versions"
]
} }

View File

@ -8,7 +8,7 @@ let testSmartshell: smartshell.Smartshell;
tap.test('smartshell should create a Smartshell instance', async () => { tap.test('smartshell should create a Smartshell instance', async () => {
testSmartshell = new smartshell.Smartshell({ testSmartshell = new smartshell.Smartshell({
executor: 'bash', executor: 'bash',
sourceFilePaths: [] sourceFilePaths: [],
}); });
expect(testSmartshell).to.be.instanceof(smartshell.Smartshell); expect(testSmartshell).to.be.instanceof(smartshell.Smartshell);
}); });
@ -26,7 +26,7 @@ tap.test('smartshell should run async and silent', async () => {
tap.test('smartshell should stream a shell execution', async () => { tap.test('smartshell should stream a shell execution', async () => {
let done = smartpromise.defer(); let done = smartpromise.defer();
let execStreamingResponse = await testSmartshell.execStreaming('npm -v'); let execStreamingResponse = await testSmartshell.execStreaming('npm -v');
execStreamingResponse.childProcess.stdout.on('data', data => { execStreamingResponse.childProcess.stdout.on('data', (data) => {
done.resolve(data); done.resolve(data);
}); });
let data = await done.promise; let data = await done.promise;
@ -39,15 +39,19 @@ tap.test('it should execute and wait for a line in the output', async () => {
}); });
tap.test('smartshell should run async', async () => { tap.test('smartshell should run async', async () => {
return testSmartshell.execSilent('sleep 1 && npm -v').then(async execResult => { return testSmartshell.execSilent('sleep 1 && npm -v').then(async (execResult) => {
console.log(execResult.stdout); console.log(execResult.stdout);
}); });
}); });
tap.test('should be able to find git', async () => { tap.test('should be able to find git', async () => {
testSmartshell.exec('git --version'); await testSmartshell.exec('git --version');
});
tap.test('should spawn an interactive cli', async () => {
// await testSmartshell.execInteractive('echo "hi"');
}); });
tap.start({ tap.start({
throwOnError: true throwOnError: true,
}); });

View File

@ -2,21 +2,58 @@ export type TExecutor = 'sh' | 'bash';
export interface IShellEnvContructorOptions { export interface IShellEnvContructorOptions {
executor: TExecutor; executor: TExecutor;
sourceFilePaths: string[]; sourceFilePaths?: string[];
pathDirectories?: string[];
} }
export class ShellEnv { export class ShellEnv {
executor: TExecutor; executor: TExecutor;
sourceFileArray: string[] = []; sourceFileArray: string[] = [];
pathDirArray: string[] = [];
/** /**
* constructor for the shellenv * constructor for the shellenv
*/ */
constructor(optionsArg: IShellEnvContructorOptions) { constructor(optionsArg: IShellEnvContructorOptions) {
this.executor = optionsArg.executor; this.executor = optionsArg.executor;
for (let sourceFilePath of optionsArg.sourceFilePaths) {
this.sourceFileArray.push(sourceFilePath); // add sourcefiles
if (optionsArg.sourceFilePaths) {
this.sourceFileArray = this.sourceFileArray.concat(optionsArg.sourceFilePaths);
} }
// add pathDirectories
if (optionsArg.pathDirectories) {
this.pathDirArray = this.pathDirArray.concat(optionsArg.pathDirectories);
}
}
/**
* imports path into the shell from env if available and returns it with
*/
private _setPath(commandStringArg: string): string {
let commandResult = commandStringArg;
let commandPaths: string[] = [];
commandPaths = commandPaths.concat(process.env.PATH.split(':'));
if (process.env.SMARTSHELL_PATH) {
commandPaths = commandPaths.concat(process.env.SMARTSHELL_PATH.split(':'));
}
// lets filter for unwanted paths
// Windows WSL
commandPaths = commandPaths.filter((commandPathArg) => {
const filterResult =
!commandPathArg.startsWith('/mnt/c/') &&
!commandPathArg.startsWith('Files/1E') &&
!commandPathArg.includes(' ');
if (!filterResult) {
// console.log(`${commandPathArg} will be filtered!`);
}
return filterResult;
});
commandResult = `PATH=${commandPaths.join(':')} && ${commandStringArg}`;
return commandResult;
} }
/** /**
@ -36,15 +73,31 @@ export class ShellEnv {
this.sourceFileArray = []; this.sourceFileArray = [];
} }
createEnvExecString(commandArg): string { public createEnvExecString(commandArg: string): string {
if (this.executor === 'bash') { let commandResult = '';
let sourceString = ''; let sourceString = '';
for (let sourceFilePath of this.sourceFileArray) {
// deal with sourcestring
for (const sourceFilePath of this.sourceFileArray) {
sourceString = sourceString + `source ${sourceFilePath} && `; sourceString = sourceString + `source ${sourceFilePath} && `;
} }
return `bash -c '${sourceString} ${commandArg}'`;
} else { // deal with avaiable path
return commandArg; let pathString = 'PATH=$PATH';
} for (const pathDir of this.pathDirArray) {
pathString += `:${pathDir}`;
}
pathString += ` && `;
switch (this.executor) {
case 'bash':
commandResult = `bash -c '${pathString}${sourceString}${commandArg}'`;
break;
case 'sh':
commandResult = `${pathString}${sourceString}${commandArg}`;
break;
}
commandResult = this._setPath(commandResult);
return commandResult;
} }
} }

View File

@ -5,28 +5,22 @@ import * as plugins from './smartshell.plugins';
* making sure the process doesn't run out of memory * making sure the process doesn't run out of memory
*/ */
export class ShellLog { export class ShellLog {
logStore = Buffer.from(''); public logStore = Buffer.from('');
/** /**
* log data to console * log data to console
* @param dataArg * @param dataArg
*/ */
logToConsole(dataArg: string | Buffer): void { public writeToConsole(dataArg: string | Buffer): void {
// make sure we have the data as string // make sure we have the data as string
const dataString: string = (() => { process.stdout.write(dataArg);
if (Buffer.isBuffer(dataArg)) {
return dataArg.toString();
}
return dataArg;
})();
console.log(dataString);
} }
/** /**
* add data to Buffer for later consumption * add data to Buffer for later consumption
* @param dataArg * @param dataArg
*/ */
addToBuffer(dataArg: string | Buffer): void { public addToBuffer(dataArg: string | Buffer): void {
// make sure we have the data as Buffer // make sure we have the data as Buffer
const dataBuffer: Buffer = (() => { const dataBuffer: Buffer = (() => {
if (!Buffer.isBuffer(dataArg)) { if (!Buffer.isBuffer(dataArg)) {
@ -37,8 +31,8 @@ export class ShellLog {
this.logStore = Buffer.concat([this.logStore, dataBuffer]); this.logStore = Buffer.concat([this.logStore, dataBuffer]);
} }
logAndAdd(dataArg: string | Buffer): void { public logAndAdd(dataArg: string | Buffer): void {
this.logToConsole(dataArg); this.writeToConsole(dataArg);
this.addToBuffer(dataArg); this.addToBuffer(dataArg);
} }
} }

View File

@ -21,29 +21,18 @@ export interface IExecResult {
export interface IExecResultStreaming { export interface IExecResultStreaming {
childProcess: cp.ChildProcess; childProcess: cp.ChildProcess;
finalPromise: Promise<IExecResult>; finalPromise: Promise<IExecResult>;
kill: () => void;
} }
// -- SmartShell -- // -- SmartShell --
export class Smartshell { export class Smartshell {
shellEnv: ShellEnv; public shellEnv: ShellEnv;
public smartexit = new plugins.smartexit.SmartExit();
constructor(optionsArg: IShellEnvContructorOptions) { constructor(optionsArg: IShellEnvContructorOptions) {
this.shellEnv = new ShellEnv(optionsArg); this.shellEnv = new ShellEnv(optionsArg);
} }
/**
* imports path into the shell from env if available and returns it with
*/
private _importEnvVarPath(stringArg): string {
if (process.env.SMARTSHELL_PATH) {
let commandResult = `PATH=${process.env.SMARTSHELL_PATH} && ${stringArg}`;
// console.log(commandResult)
return commandResult;
} else {
return stringArg;
}
}
/** /**
* executes a given command async * executes a given command async
* @param commandStringArg * @param commandStringArg
@ -60,41 +49,37 @@ export class Smartshell {
// build commandToExecute // build commandToExecute
let commandToExecute = commandStringArg; let commandToExecute = commandStringArg;
commandToExecute = this.shellEnv.createEnvExecString(commandStringArg); commandToExecute = this.shellEnv.createEnvExecString(commandStringArg);
commandToExecute = this._importEnvVarPath(commandToExecute);
const spawnlogInstance = new ShellLog(); const spawnlogInstance = new ShellLog();
const execChildProcess = cp.spawn(commandToExecute, [], { const execChildProcess = cp.spawn(commandToExecute, [], {
shell: true, shell: true,
env: process.env env: process.env,
detached: false,
}); });
execChildProcess.stdout.on('data', data => { this.smartexit.addProcess(execChildProcess);
execChildProcess.stdout.on('data', (data) => {
if (!silentArg) { if (!silentArg) {
spawnlogInstance.logToConsole(data); spawnlogInstance.writeToConsole(data);
} }
spawnlogInstance.addToBuffer(data); spawnlogInstance.addToBuffer(data);
}); });
execChildProcess.stderr.on('data', data => { execChildProcess.stderr.on('data', (data) => {
if (!silentArg) { if (!silentArg) {
spawnlogInstance.logToConsole(data); spawnlogInstance.writeToConsole(data);
} }
spawnlogInstance.addToBuffer(data); spawnlogInstance.addToBuffer(data);
}); });
if (streamingArg) {
done.resolve({
childProcess: execChildProcess,
finalPromise: childProcessEnded.promise
});
}
execChildProcess.on('exit', (code, signal) => { execChildProcess.on('exit', (code, signal) => {
this.smartexit.removeProcess(execChildProcess);
if (strictArg && code === 1) { if (strictArg && code === 1) {
done.reject(); done.reject();
} }
const execResult = { const execResult = {
exitCode: code, exitCode: code,
stdout: spawnlogInstance.logStore.toString() stdout: spawnlogInstance.logStore.toString(),
}; };
if (!streamingArg) { if (!streamingArg) {
@ -103,11 +88,23 @@ export class Smartshell {
childProcessEnded.resolve(execResult); childProcessEnded.resolve(execResult);
}); });
if (streamingArg) {
done.resolve({
childProcess: execChildProcess,
finalPromise: childProcessEnded.promise,
kill: () => {
// this notation with the - kills the whole process group
console.log(`running tree kill on process ${execChildProcess.pid}`);
plugins.treeKill(execChildProcess.pid);
},
});
}
const result = await done.promise; const result = await done.promise;
return result; return result;
} }
async exec(commandStringArg: string): Promise<IExecResult> { public async exec(commandStringArg: string): Promise<IExecResult> {
return (await this._exec(commandStringArg, false)) as IExecResult; return (await this._exec(commandStringArg, false)) as IExecResult;
} }
@ -115,28 +112,32 @@ export class Smartshell {
* executes a given command async and silent * executes a given command async and silent
* @param commandStringArg * @param commandStringArg
*/ */
async execSilent(commandStringArg: string): Promise<IExecResult> { public async execSilent(commandStringArg: string): Promise<IExecResult> {
return (await this._exec(commandStringArg, true)) as IExecResult; return (await this._exec(commandStringArg, true)) as IExecResult;
} }
/** /**
* executes a command async and strict, meaning it rejects the promise if something happens * executes a command async and strict, meaning it rejects the promise if something happens
*/ */
async execStrict(commandStringArg: string): Promise<IExecResult> { public async execStrict(commandStringArg: string): Promise<IExecResult> {
return (await this._exec(commandStringArg, false, true)) as IExecResult;
}
public async execStrictSilent(commandStringArg: string): Promise<IExecResult> {
return (await this._exec(commandStringArg, true, true)) as IExecResult; return (await this._exec(commandStringArg, true, true)) as IExecResult;
} }
/** /**
* executes a command and allows you to stream output * executes a command and allows you to stream output
*/ */
async execStreaming( public async execStreaming(
commandStringArg: string, commandStringArg: string,
silentArg: boolean = false silentArg: boolean = false
): Promise<IExecResultStreaming> { ): Promise<IExecResultStreaming> {
return (await this._exec(commandStringArg, silentArg, false, true)) as IExecResultStreaming; return (await this._exec(commandStringArg, silentArg, false, true)) as IExecResultStreaming;
} }
async execStreamingSilent(commandStringArg: string) { public async execStreamingSilent(commandStringArg: string) {
return (await this.execStreaming(commandStringArg, true)) as IExecResultStreaming; return (await this.execStreaming(commandStringArg, true)) as IExecResultStreaming;
} }
@ -145,7 +146,11 @@ export class Smartshell {
* @param commandStringArg * @param commandStringArg
* @param regexArg * @param regexArg
*/ */
async execAndWaitForLine(commandStringArg: string, regexArg: RegExp, silentArg: boolean = false) { public async execAndWaitForLine(
commandStringArg: string,
regexArg: RegExp,
silentArg: boolean = false
) {
let done = plugins.smartpromise.defer(); let done = plugins.smartpromise.defer();
let execStreamingResult = await this.execStreaming(commandStringArg, silentArg); let execStreamingResult = await this.execStreaming(commandStringArg, silentArg);
execStreamingResult.childProcess.stdout.on('data', (stdOutChunk: string) => { execStreamingResult.childProcess.stdout.on('data', (stdOutChunk: string) => {
@ -156,7 +161,42 @@ export class Smartshell {
return done.promise; return done.promise;
} }
async execAndWaitForLineSilent(commandStringArg: string, regexArg: RegExp) { public async execAndWaitForLineSilent(commandStringArg: string, regexArg: RegExp) {
this.execAndWaitForLine(commandStringArg, regexArg, true); this.execAndWaitForLine(commandStringArg, regexArg, true);
} }
/**
* execs an command and then enters interactive CLI
* @param commandStringArg
* @param regexArg
*/
public async execInteractive(commandStringArg: string) {
if (process.env.CI) {
return;
}
const done = plugins.smartpromise.defer();
const shell = cp.spawn('sh', [], { stdio: 'pipe' });
this.smartexit.addProcess(shell);
const shellLog = new ShellLog();
const stdInStream = process.stdin.pipe(shell.stdin);
const stdOutStream = shell.stdout.pipe(process.stdout);
shell.on('close', (code) => {
console.log(`interactive shell terminated with code ${code}`);
stdInStream.removeAllListeners();
stdInStream.uncork();
stdOutStream.removeAllListeners();
stdOutStream.unpipe();
shell.kill('SIGTERM');
process.stdin.pause();
done.resolve();
});
let commandString = commandStringArg;
if (process.env.CI) {
commandString += ' && exit';
}
commandString += '\n';
shell.stdin.write(commandString);
await done.promise;
}
} }

View File

@ -1,4 +1,11 @@
import * as smartdelay from '@pushrocks/smartdelay';
import * as smartexit from '@pushrocks/smartexit';
import * as smartpromise from '@pushrocks/smartpromise'; import * as smartpromise from '@pushrocks/smartpromise';
import * as which from 'which'; import * as which from 'which';
export { smartpromise, which }; export { smartdelay, smartexit, smartpromise, which };
// third party
import treeKill from 'tree-kill';
export { treeKill };

View File

@ -1,3 +1,17 @@
{ {
"extends": "tslint-config-standard" "extends": ["tslint:latest", "tslint-config-prettier"],
"rules": {
"semicolon": [true, "always"],
"no-console": false,
"ordered-imports": false,
"object-literal-sort-keys": false,
"member-ordering": {
"options":{
"order": [
"static-method"
]
}
}
},
"defaultSeverity": "warning"
} }