Compare commits
55 Commits
Author | SHA1 | Date | |
---|---|---|---|
d7247add7e | |||
b304b01ab6 | |||
9ba5447e72 | |||
6cb7ab8f19 | |||
efdb80ddba | |||
db712b2223 | |||
0199160013 | |||
199fa19899 | |||
23a3230f07 | |||
ad0b7ac21c | |||
c8c0cbd47a | |||
213dac474e | |||
a4c188051a | |||
62195b63d6 | |||
16ba3803a4 | |||
613241cdcb | |||
f5df21e144 | |||
ba8c2d06d8 | |||
9bb0999f0e | |||
1beb3a0bbe | |||
c1133e9131 | |||
48dd3c3cca | |||
f0a5be41ea | |||
970d28a4ad | |||
76f2126eb3 | |||
a6f164602c | |||
f8457ad58d | |||
8ceac74a1f | |||
f63c4456bf | |||
32c33cad5b | |||
0758e58fa4 | |||
e981ca6bdd | |||
2853a48708 | |||
389f8f4a52 | |||
c1a537f052 | |||
4701341c0c | |||
d5d347721a | |||
6a047d6333 | |||
6e7b4dd18e | |||
debc5f1524 | |||
8d03561762 | |||
f516a6e753 | |||
1c4f6a8bf9 | |||
4fe3775904 | |||
dcee2d6233 | |||
39d457382f | |||
e6ed457501 | |||
0a6484762c | |||
baa3e4e6e9 | |||
0d68361381 | |||
286d80328c | |||
2a71f00ab0 | |||
80a44a43cb | |||
5ea780acda | |||
7c9cf6e70d |
20
.gitignore
vendored
20
.gitignore
vendored
@ -1,4 +1,20 @@
|
||||
.nogit/
|
||||
|
||||
# artifacts
|
||||
coverage/
|
||||
node_modules/
|
||||
pages/
|
||||
public/
|
||||
pages/
|
||||
|
||||
# installs
|
||||
node_modules/
|
||||
|
||||
# caches
|
||||
.yarn/
|
||||
.cache/
|
||||
.rpt2_cache
|
||||
|
||||
# builds
|
||||
dist/
|
||||
dist_*/
|
||||
|
||||
# custom
|
151
.gitlab-ci.yml
151
.gitlab-ci.yml
@ -1,16 +1,16 @@
|
||||
# gitzone standard
|
||||
image: hosttoday/ht-docker-node:npmci
|
||||
# gitzone ci_default
|
||||
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
|
||||
|
||||
cache:
|
||||
paths:
|
||||
- .npmci_cache/
|
||||
key: "$CI_BUILD_STAGE"
|
||||
- .npmci_cache/
|
||||
key: '$CI_BUILD_STAGE'
|
||||
|
||||
stages:
|
||||
- security
|
||||
- test
|
||||
- release
|
||||
- metadata
|
||||
- security
|
||||
- test
|
||||
- release
|
||||
- metadata
|
||||
|
||||
# ====================
|
||||
# security stage
|
||||
@ -18,108 +18,115 @@ stages:
|
||||
mirror:
|
||||
stage: security
|
||||
script:
|
||||
- npmci git mirror
|
||||
- npmci git mirror
|
||||
only:
|
||||
- tags
|
||||
tags:
|
||||
- docker
|
||||
- notpriv
|
||||
- lossless
|
||||
- docker
|
||||
- notpriv
|
||||
|
||||
snyk:
|
||||
auditProductionDependencies:
|
||||
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
|
||||
stage: security
|
||||
script:
|
||||
- npmci command npm install -g snyk
|
||||
- npmci command npm install --ignore-scripts
|
||||
- npmci command snyk test
|
||||
- npmci npm prepare
|
||||
- npmci command npm install --production --ignore-scripts
|
||||
- npmci command npm config set registry https://registry.npmjs.org
|
||||
- npmci command npm audit --audit-level=high --only=prod --production
|
||||
tags:
|
||||
- docker
|
||||
- notpriv
|
||||
- docker
|
||||
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
|
||||
# ====================
|
||||
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
|
||||
- npmci npm prepare
|
||||
- npmci node install stable
|
||||
- npmci npm install
|
||||
- npmci npm test
|
||||
coverage: /\d+.?\d+?\%\s*coverage/
|
||||
tags:
|
||||
- docker
|
||||
- notpriv
|
||||
|
||||
testSTABLE:
|
||||
- docker
|
||||
|
||||
testBuild:
|
||||
stage: test
|
||||
script:
|
||||
- npmci node install stable
|
||||
- npmci npm install
|
||||
- npmci npm test
|
||||
- npmci npm prepare
|
||||
- npmci node install stable
|
||||
- npmci npm install
|
||||
- npmci command npm run build
|
||||
coverage: /\d+.?\d+?\%\s*coverage/
|
||||
tags:
|
||||
- docker
|
||||
- notpriv
|
||||
- docker
|
||||
|
||||
release:
|
||||
stage: release
|
||||
script:
|
||||
- npmci node install stable
|
||||
- npmci npm publish
|
||||
- npmci node install stable
|
||||
- npmci npm publish
|
||||
only:
|
||||
- tags
|
||||
- tags
|
||||
tags:
|
||||
- docker
|
||||
- notpriv
|
||||
- lossless
|
||||
- docker
|
||||
- notpriv
|
||||
|
||||
# ====================
|
||||
# metadata stage
|
||||
# ====================
|
||||
codequality:
|
||||
stage: metadata
|
||||
image: docker:stable
|
||||
allow_failure: true
|
||||
services:
|
||||
- docker:stable-dind
|
||||
only:
|
||||
- tags
|
||||
script:
|
||||
- export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')
|
||||
- docker run
|
||||
--env SOURCE_CODE="$PWD"
|
||||
--volume "$PWD":/code
|
||||
--volume /var/run/docker.sock:/var/run/docker.sock
|
||||
"registry.gitlab.com/gitlab-org/security-products/codequality:$SP_VERSION" /code
|
||||
artifacts:
|
||||
paths: [codeclimate.json]
|
||||
- npmci command npm install -g tslint typescript
|
||||
- npmci npm prepare
|
||||
- npmci npm install
|
||||
- npmci command "tslint -c tslint.json ./ts/**/*.ts"
|
||||
tags:
|
||||
- docker
|
||||
- priv
|
||||
- lossless
|
||||
- docker
|
||||
- priv
|
||||
|
||||
trigger:
|
||||
stage: metadata
|
||||
script:
|
||||
- npmci trigger
|
||||
- npmci trigger
|
||||
only:
|
||||
- tags
|
||||
- tags
|
||||
tags:
|
||||
- docker
|
||||
- notpriv
|
||||
- lossless
|
||||
- docker
|
||||
- notpriv
|
||||
|
||||
pages:
|
||||
image: hosttoday/ht-docker-node:npmci
|
||||
stage: metadata
|
||||
script:
|
||||
- npmci command npm install -g npmpage
|
||||
- npmci command npmpage
|
||||
- npmci node install lts
|
||||
- npmci command npm install -g @gitzone/tsdoc
|
||||
- npmci npm prepare
|
||||
- npmci npm install
|
||||
- npmci command tsdoc
|
||||
tags:
|
||||
- lossless
|
||||
- docker
|
||||
- notpriv
|
||||
only:
|
||||
@ -127,15 +134,5 @@ pages:
|
||||
artifacts:
|
||||
expire_in: 1 week
|
||||
paths:
|
||||
- public
|
||||
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
|
||||
- public
|
||||
allow_failure: true
|
||||
|
29
.vscode/launch.json
vendored
Normal file
29
.vscode/launch.json
vendored
Normal 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
26
.vscode/settings.json
vendored
Normal 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"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
32
README.md
32
README.md
@ -1,32 +0,0 @@
|
||||
# smartshell
|
||||
|
||||
shell actions designed as promises
|
||||
|
||||
## Availabililty
|
||||
|
||||
[](https://www.npmjs.com/package/smartshell)
|
||||
[](https://GitLab.com/pushrocks/smartshell)
|
||||
[](https://github.com/pushrocks/smartshell)
|
||||
[](https://pushrocks.gitlab.io/smartshell/)
|
||||
|
||||
## Status for master
|
||||
|
||||
[](https://GitLab.com/pushrocks/smartshell/commits/master)
|
||||
[](https://GitLab.com/pushrocks/smartshell/commits/master)
|
||||
[](https://www.npmjs.com/package/smartshell)
|
||||
[](https://david-dm.org/pushrocks/smartshell)
|
||||
[](https://www.bithound.io/github/pushrocks/smartshell/master/dependencies/npm)
|
||||
[](https://www.bithound.io/github/pushrocks/smartshell)
|
||||
[](https://nodejs.org/dist/latest-v6.x/docs/api/)
|
||||
[](https://nodejs.org/dist/latest-v6.x/docs/api/)
|
||||
[](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)
|
||||
|
||||
[](https://push.rocks)
|
@ -1,9 +1,18 @@
|
||||
{
|
||||
"npmci": {
|
||||
"npmGlobalTools": [
|
||||
"npmts",
|
||||
"ts-node"
|
||||
],
|
||||
"npmAccessLevel": "public"
|
||||
"npmGlobalTools": [],
|
||||
"npmAccessLevel": "public",
|
||||
"npmRegistryUrl": "registry.npmjs.org"
|
||||
},
|
||||
"gitzone": {
|
||||
"projectType": "npm",
|
||||
"module": {
|
||||
"githost": "gitlab.com",
|
||||
"gitscope": "pushrocks",
|
||||
"gitrepo": "smartshell",
|
||||
"shortDescription": "shell actions designed as promises",
|
||||
"npmPackagename": "@pushrocks/smartshell",
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
}
|
26372
package-lock.json
generated
26372
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
46
package.json
46
package.json
@ -1,13 +1,13 @@
|
||||
{
|
||||
"name": "@pushrocks/smartshell",
|
||||
"private": false,
|
||||
"version": "2.0.1",
|
||||
"version": "2.0.29",
|
||||
"description": "shell actions designed as promises",
|
||||
"main": "dist/index.js",
|
||||
"typings": "dist/index.d.ts",
|
||||
"main": "dist_ts/index.js",
|
||||
"typings": "dist_ts/index.d.ts",
|
||||
"scripts": {
|
||||
"test": "(tsrun test/test.ts)",
|
||||
"build": "(npmts)"
|
||||
"test": "(tstest test/)",
|
||||
"build": "(tsbuild --web)"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -24,13 +24,35 @@
|
||||
},
|
||||
"homepage": "https://gitlab.com/pushrocks/smartshell#README",
|
||||
"devDependencies": {
|
||||
"@gitzone/tsrun": "^1.1.9",
|
||||
"@pushrocks/tapbundle": "^3.0.1",
|
||||
"@types/node": "^10.5.2"
|
||||
"@gitzone/tsbuild": "^2.1.28",
|
||||
"@gitzone/tsrun": "^1.2.18",
|
||||
"@gitzone/tstest": "^1.0.59",
|
||||
"@pushrocks/tapbundle": "^3.2.14",
|
||||
"@types/node": "^16.11.6",
|
||||
"tslint": "^6.1.3",
|
||||
"tslint-config-prettier": "^1.18.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@pushrocks/smartpromise": "^2.0.5",
|
||||
"@types/which": "^1.3.1",
|
||||
"which": "^1.3.1"
|
||||
}
|
||||
"@pushrocks/smartdelay": "^2.0.13",
|
||||
"@pushrocks/smartexit": "^1.0.20",
|
||||
"@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"
|
||||
]
|
||||
}
|
||||
|
40
readme.md
Normal file
40
readme.md
Normal file
@ -0,0 +1,40 @@
|
||||
# @pushrocks/smartshell
|
||||
shell actions designed as promises
|
||||
|
||||
## Availabililty and Links
|
||||
* [npmjs.org (npm package)](https://www.npmjs.com/package/@pushrocks/smartshell)
|
||||
* [gitlab.com (source)](https://gitlab.com/pushrocks/smartshell)
|
||||
* [github.com (source mirror)](https://github.com/pushrocks/smartshell)
|
||||
* [docs (typedoc)](https://pushrocks.gitlab.io/smartshell/)
|
||||
|
||||
## Status for master
|
||||
|
||||
Status Category | Status Badge
|
||||
-- | --
|
||||
GitLab Pipelines | [](https://lossless.cloud)
|
||||
GitLab Pipline Test Coverage | [](https://lossless.cloud)
|
||||
npm | [](https://lossless.cloud)
|
||||
Snyk | [](https://lossless.cloud)
|
||||
TypeScript Support | [](https://lossless.cloud)
|
||||
node Support | [](https://nodejs.org/dist/latest-v10.x/docs/api/)
|
||||
Code Style | [](https://lossless.cloud)
|
||||
PackagePhobia (total standalone install weight) | [](https://lossless.cloud)
|
||||
PackagePhobia (package size on registry) | [](https://lossless.cloud)
|
||||
BundlePhobia (total size when bundled) | [](https://lossless.cloud)
|
||||
Platform support | [](https://lossless.cloud) [](https://lossless.cloud)
|
||||
|
||||
## Usage
|
||||
|
||||
Use TypeScript for best in class instellisense.
|
||||
|
||||
|
||||
## Contribution
|
||||
|
||||
We are always happy for code contributions. If you are not the code contributing type that is ok. Still, maintaining Open Source repositories takes considerable time and thought. If you like the quality of what we do and our modules are useful to you we would appreciate a little monthly contribution: You can [contribute one time](https://lossless.link/contribute-onetime) or [contribute monthly](https://lossless.link/contribute). :)
|
||||
|
||||
For further information read the linked docs at the top of this readme.
|
||||
|
||||
> MIT licensed | **©** [Lossless GmbH](https://lossless.gmbh)
|
||||
| By using this npm module you agree to our [privacy policy](https://lossless.gmbH/privacy)
|
||||
|
||||
[](https://maintainedby.lossless.com)
|
16
test/test.ts
16
test/test.ts
@ -8,7 +8,7 @@ let testSmartshell: smartshell.Smartshell;
|
||||
tap.test('smartshell should create a Smartshell instance', async () => {
|
||||
testSmartshell = new smartshell.Smartshell({
|
||||
executor: 'bash',
|
||||
sourceFilePaths: []
|
||||
sourceFilePaths: [],
|
||||
});
|
||||
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 () => {
|
||||
let done = smartpromise.defer();
|
||||
let execStreamingResponse = await testSmartshell.execStreaming('npm -v');
|
||||
execStreamingResponse.childProcess.stdout.on('data', data => {
|
||||
execStreamingResponse.childProcess.stdout.on('data', (data) => {
|
||||
done.resolve(data);
|
||||
});
|
||||
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 () => {
|
||||
return testSmartshell.execSilent('sleep 1 && npm -v').then(async execResult => {
|
||||
return testSmartshell.execSilent('sleep 1 && npm -v').then(async (execResult) => {
|
||||
console.log(execResult.stdout);
|
||||
});
|
||||
});
|
||||
|
||||
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({
|
||||
throwOnError: true
|
||||
throwOnError: true,
|
||||
});
|
||||
|
@ -1 +1,2 @@
|
||||
export * from './smartshell.classes.smartshell';
|
||||
export { which } from './smartshell.plugins';
|
@ -2,26 +2,63 @@ export type TExecutor = 'sh' | 'bash';
|
||||
|
||||
export interface IShellEnvContructorOptions {
|
||||
executor: TExecutor;
|
||||
sourceFilePaths: string[];
|
||||
sourceFilePaths?: string[];
|
||||
pathDirectories?: string[];
|
||||
}
|
||||
|
||||
export class ShellEnv {
|
||||
executor: TExecutor;
|
||||
sourceFileArray: string[] = [];
|
||||
|
||||
pathDirArray: string[] = [];
|
||||
|
||||
/**
|
||||
* constructor for the shellenv
|
||||
*/
|
||||
constructor(optionsArg: IShellEnvContructorOptions) {
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* add files that are going to be sourced when running a command
|
||||
* @param sourceFilePathsArray
|
||||
* @param sourceFilePathsArray
|
||||
*/
|
||||
addSourceFiles(sourceFilePathsArray: string[]) {
|
||||
for (let sourceFilePath of sourceFilePathsArray) {
|
||||
@ -36,15 +73,31 @@ export class ShellEnv {
|
||||
this.sourceFileArray = [];
|
||||
}
|
||||
|
||||
createEnvExecString(commandArg): string {
|
||||
if (this.executor === 'bash') {
|
||||
let sourceString = '';
|
||||
for (let sourceFilePath of this.sourceFileArray) {
|
||||
sourceString = sourceString + `source ${sourceFilePath} && `;
|
||||
}
|
||||
return `bash -c '${sourceString} ${commandArg}'`;
|
||||
} else {
|
||||
return commandArg;
|
||||
public createEnvExecString(commandArg: string): string {
|
||||
let commandResult = '';
|
||||
let sourceString = '';
|
||||
|
||||
// deal with sourcestring
|
||||
for (const sourceFilePath of this.sourceFileArray) {
|
||||
sourceString = sourceString + `source ${sourceFilePath} && `;
|
||||
}
|
||||
|
||||
// deal with avaiable path
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,28 +5,22 @@ import * as plugins from './smartshell.plugins';
|
||||
* making sure the process doesn't run out of memory
|
||||
*/
|
||||
export class ShellLog {
|
||||
logStore = Buffer.from('');
|
||||
public logStore = Buffer.from('');
|
||||
|
||||
/**
|
||||
* 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
|
||||
const dataString: string = (() => {
|
||||
if (Buffer.isBuffer(dataArg)) {
|
||||
return dataArg.toString();
|
||||
}
|
||||
return dataArg;
|
||||
})();
|
||||
console.log(dataString);
|
||||
process.stdout.write(dataArg);
|
||||
}
|
||||
|
||||
/**
|
||||
* add data to Buffer for later consumption
|
||||
* @param dataArg
|
||||
*/
|
||||
addToBuffer(dataArg: string | Buffer): void {
|
||||
public addToBuffer(dataArg: string | Buffer): void {
|
||||
// make sure we have the data as Buffer
|
||||
const dataBuffer: Buffer = (() => {
|
||||
if (!Buffer.isBuffer(dataArg)) {
|
||||
@ -34,11 +28,11 @@ export class ShellLog {
|
||||
}
|
||||
return dataArg;
|
||||
})();
|
||||
this.logStore = Buffer.concat([this.logStore,dataBuffer]);
|
||||
this.logStore = Buffer.concat([this.logStore, dataBuffer]);
|
||||
}
|
||||
|
||||
logAndAdd(dataArg: string | Buffer): void {
|
||||
this.logToConsole(dataArg);
|
||||
public logAndAdd(dataArg: string | Buffer): void {
|
||||
this.writeToConsole(dataArg);
|
||||
this.addToBuffer(dataArg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,10 @@
|
||||
// -- imports --
|
||||
import * as plugins from './smartshell.plugins';
|
||||
import * as smartshellWrap from './smartshell.wrap';
|
||||
import { ShellEnv, IShellEnvContructorOptions, TExecutor, } from './smartshell.classes.shellenv';
|
||||
import { ShellEnv, IShellEnvContructorOptions, TExecutor } from './smartshell.classes.shellenv';
|
||||
import { ShellLog } from './smartshell.classes.shelllog';
|
||||
|
||||
import * as cp from "child_process";
|
||||
import { Deferred } from "@pushrocks/smartpromise";
|
||||
import * as cp from 'child_process';
|
||||
import { Deferred } from '@pushrocks/smartpromise';
|
||||
|
||||
// -- interfaces --
|
||||
/**
|
||||
@ -22,34 +21,30 @@ export interface IExecResult {
|
||||
export interface IExecResultStreaming {
|
||||
childProcess: cp.ChildProcess;
|
||||
finalPromise: Promise<IExecResult>;
|
||||
/**
|
||||
* sends SIGKILL
|
||||
*/
|
||||
kill: () => void;
|
||||
/**
|
||||
* sends SIGTERM
|
||||
*/
|
||||
terminate: () => void;
|
||||
}
|
||||
|
||||
// -- SmartShell --
|
||||
export class Smartshell {
|
||||
shellEnv: ShellEnv;
|
||||
public shellEnv: ShellEnv;
|
||||
public smartexit = new plugins.smartexit.SmartExit();
|
||||
|
||||
constructor(optionsArg: IShellEnvContructorOptions) {
|
||||
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
|
||||
* @param commandStringArg
|
||||
*/
|
||||
private async _exec (
|
||||
private async _exec(
|
||||
commandStringArg: string,
|
||||
silentArg: boolean = false,
|
||||
strictArg = false,
|
||||
@ -59,119 +54,161 @@ export class Smartshell {
|
||||
const done = plugins.smartpromise.defer<IExecResult | IExecResultStreaming>();
|
||||
const childProcessEnded = plugins.smartpromise.defer<IExecResult>();
|
||||
// build commandToExecute
|
||||
let commandToExecute = commandStringArg
|
||||
let commandToExecute = commandStringArg;
|
||||
commandToExecute = this.shellEnv.createEnvExecString(commandStringArg);
|
||||
commandToExecute = this._importEnvVarPath(commandToExecute);
|
||||
const spawnlogInstance = new ShellLog();
|
||||
const execChildProcess = cp.spawn(commandToExecute, [], {
|
||||
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) {
|
||||
spawnlogInstance.logToConsole(data);
|
||||
spawnlogInstance.writeToConsole(data);
|
||||
}
|
||||
spawnlogInstance.addToBuffer(data);
|
||||
});
|
||||
execChildProcess.stderr.on("data", data => {
|
||||
execChildProcess.stderr.on('data', (data) => {
|
||||
if (!silentArg) {
|
||||
spawnlogInstance.logToConsole(data);
|
||||
spawnlogInstance.writeToConsole(data);
|
||||
}
|
||||
spawnlogInstance.addToBuffer(data);
|
||||
});
|
||||
|
||||
if(streamingArg) {
|
||||
done.resolve({
|
||||
childProcess: execChildProcess,
|
||||
finalPromise: childProcessEnded.promise
|
||||
});
|
||||
}
|
||||
|
||||
execChildProcess.on("exit", (code, signal) => {
|
||||
if(strictArg && code === 1) {
|
||||
execChildProcess.on('exit', (code, signal) => {
|
||||
this.smartexit.removeProcess(execChildProcess);
|
||||
if (strictArg && code === 1) {
|
||||
done.reject();
|
||||
}
|
||||
|
||||
const execResult = {
|
||||
exitCode: code,
|
||||
stdout: spawnlogInstance.logStore.toString()
|
||||
}
|
||||
stdout: spawnlogInstance.logStore.toString(),
|
||||
};
|
||||
|
||||
if(!streamingArg) {
|
||||
if (!streamingArg) {
|
||||
done.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 with SIGKILL on process ${execChildProcess.pid}`);
|
||||
plugins.treeKill(execChildProcess.pid, 'SIGKILL');
|
||||
},
|
||||
terminate: () => {
|
||||
// this notation with the - kills the whole process group
|
||||
console.log(`running tree kill with SIGTERM on process ${execChildProcess.pid}`);
|
||||
plugins.treeKill(execChildProcess.pid, 'SIGTERM');
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const result = await done.promise;
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
async exec (
|
||||
commandStringArg: string
|
||||
): Promise<IExecResult> {
|
||||
public async exec(commandStringArg: string): Promise<IExecResult> {
|
||||
return (await this._exec(commandStringArg, false)) as IExecResult;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* executes a given command async and silent
|
||||
* @param commandStringArg
|
||||
*/
|
||||
async execSilent (
|
||||
commandStringArg: string
|
||||
): Promise<IExecResult> {
|
||||
public async execSilent(commandStringArg: string): Promise<IExecResult> {
|
||||
return (await this._exec(commandStringArg, true)) as IExecResult;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* executes a command async and strict, meaning it rejects the promise if something happens
|
||||
*/
|
||||
async execStrict (
|
||||
commandStringArg: string
|
||||
): Promise<IExecResult> {
|
||||
return (await this._exec(commandStringArg, true, true)) as 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* executes a command and allows you to stream output
|
||||
*/
|
||||
async execStreaming (
|
||||
public async execStreaming(
|
||||
commandStringArg: string,
|
||||
silentArg: boolean = false
|
||||
): Promise<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;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* executes a command and returns promise that will be fullfilled once an putput line matches RegexArg
|
||||
* @param commandStringArg
|
||||
* @param regexArg
|
||||
*/
|
||||
async execAndWaitForLine (
|
||||
public async execAndWaitForLine(
|
||||
commandStringArg: string,
|
||||
regexArg: RegExp,
|
||||
silentArg: boolean = false
|
||||
) {
|
||||
let done = plugins.smartpromise.defer();
|
||||
let execStreamingResult = await this.execStreaming(commandStringArg, silentArg);
|
||||
execStreamingResult.childProcess.stdout.on("data", (stdOutChunk: string) => {
|
||||
execStreamingResult.childProcess.stdout.on('data', (stdOutChunk: string) => {
|
||||
if (regexArg.test(stdOutChunk)) {
|
||||
done.resolve();
|
||||
}
|
||||
});
|
||||
return done.promise;
|
||||
};
|
||||
}
|
||||
|
||||
async execAndWaitForLineSilent (
|
||||
commandStringArg: string,
|
||||
regexArg: RegExp
|
||||
) {
|
||||
public async execAndWaitForLineSilent(commandStringArg: string, regexArg: RegExp) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,11 @@
|
||||
import * as smartdelay from '@pushrocks/smartdelay';
|
||||
import * as smartexit from '@pushrocks/smartexit';
|
||||
import * as smartpromise from '@pushrocks/smartpromise';
|
||||
import * as which from 'which';
|
||||
import which from 'which';
|
||||
|
||||
export { smartpromise, which };
|
||||
export { smartdelay, smartexit, smartpromise, which };
|
||||
|
||||
// third party
|
||||
import treeKill from 'tree-kill';
|
||||
|
||||
export { treeKill };
|
||||
|
@ -1,59 +0,0 @@
|
||||
import * as plugins from "./smartshell.plugins";
|
||||
import { ShellLog } from "./smartshell.classes.shelllog";
|
||||
|
||||
// interfaces
|
||||
import * as cp from "child_process";
|
||||
import { Deferred } from "@pushrocks/smartpromise";
|
||||
|
||||
/**
|
||||
* interface for ExecResult
|
||||
*/
|
||||
export interface IExecResult {
|
||||
exitCode: number;
|
||||
stdout: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* interface for streaming ExecResult
|
||||
*/
|
||||
export interface IExecResultStreaming {
|
||||
childProcess: cp.ChildProcess;
|
||||
finalPromise: Promise<IExecResult>;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* get a path
|
||||
*/
|
||||
export let which = (cmd: string): Promise<string> => {
|
||||
let done = plugins.smartpromise.defer<string>();
|
||||
plugins.which(cmd, (err, path: string) => {
|
||||
if (err) {
|
||||
done.reject(err);
|
||||
}
|
||||
done.resolve(path);
|
||||
});
|
||||
return done.promise;
|
||||
};
|
||||
|
||||
|
||||
/* /////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* executes silently and returns IExecResult
|
||||
* @param commandArg
|
||||
|
||||
async execSilent(commandArg: string) {
|
||||
let execCommand = this..createExecString(commandArg);
|
||||
return await smartshellWrap.execSilent(execCommand);
|
||||
}
|
||||
|
||||
/**
|
||||
* executes and returns IExecResult
|
||||
* @param commandArg
|
||||
|
||||
async exec(commandArg: string) {
|
||||
let execCommand = this.createExecString(commandArg);
|
||||
return await smartshellWrap.exec(execCommand);
|
||||
} */
|
16
tslint.json
16
tslint.json
@ -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"
|
||||
}
|
||||
|
Reference in New Issue
Block a user