execSync throws and error trying to run node - node.js

I am running some processes inside of an EC2 instance.
To run it I initiate it with an SSM command:
cd / && cd home/ec2-user && . .nvm/nvm.sh && cd ufo && npm run start
and inside of it, I have a method in app.ts which is initialized with ts-node app.ts
import { execSync } from 'node:child_process';
import { takeNextScheduledAudit } from './sqs-scheduler';
import { uploadResultsToBucket } from './s3-uploader';
import { AuditRunParams } from "./types";
import { sendAuditResults } from "./sendResults";
(async function conductor(): Promise<void> {
const nextAuditRunParams = await takeNextScheduledAudit();
if (!nextAuditRunParams) {
execSync("sudo shutdown -h now");
}
const { targetUrl, requesterId, endpoint } = nextAuditRunParams as AuditRunParams;
try {
execSync(`npx user-flow --url=${targetUrl} --open=false`);
const resultsUrl = await uploadResultsToBucket(targetUrl);
await sendAuditResults(requesterId, endpoint, resultsUrl);
} catch (error) {
console.log(error);
}
await conductor();
})();
If I log in manually and run npm run start the scripts works as intended but if I run it using the SSM command I get this output:
> start
> ts-node app.ts
Error: Command failed: npx user-flow --url=https://deep-blue.io/ --open=false
at checkExecSyncError (node:child_process:841:11)
at execSync (node:child_process:912:15)
at conductor (/home/ec2-user/ufo/app.ts:15:17)
at processTicksAndRejections (node:internal/process/task_queues:96:5) {
status: 243,
signal: null,
output: [ null, <Buffer >, <Buffer 0a> ],
pid: 2691,
stdout: <Buffer >,
stderr: <Buffer 0a>
}
and this error:
Error: Command failed: sudo shutdown -h now
at checkExecSyncError (node:child_process:841:11)
at execSync (node:child_process:912:15)
at conductor (/home/ec2-user/ufo/app.ts:10:17)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async conductor (/home/ec2-user/ufo/app.ts:21:5) {
status: null,
signal: 'SIGTERM',
output: [ null, Buffer(0) [Uint8Array] [], Buffer(0) [Uint8Array] [] ],
pid: 2705,
stdout: Buffer(0) [Uint8Array] [],
stderr: Buffer(0) [Uint8Array] []
}
failed to run commands: exit status 1
Moreover, if I run execSync("node -v && npx -v") it also throws an error.
Why can I run this script when i am logged in but if i run it via a SSM command it does not recognize node inside of node?
--- Edit - Added Info ---
When running execSync(node -v && npx -v,{shell: '/bin/bash'}) I get an error:
Error: Command failed: node -v && npx -v
When running execSync(ps -p $$ && echo $SHELL, {shell: '/bin/bash'}):
PID TTY TIME CMD
7817 ? 00:00:00 bash
/bin/bash
And when I loggin and do ps -p $$ && echo $SHELL I get:
PID TTY TIME CMD
6873 pts/0 00:00:00 bash
/bin/bash

By default, all of the child_process functions execute in the same environment as the process that launched them. I don't have an account handy to test with, but it's quite likely that SSM skips over a traditional shell and just executes certain runtimes directly.
You can use the exec options like this to set a particular shell in which to launch the process:
const output = execSync('echo "doing stuff"', {
shell: '/bin/bash',
})
console.log('***** output:', output.toString())
This is assuming the OS you're using for the EC2 instance has bash available. Most flavors of linux should, but for what you're doing there, /bin/sh is sufficient if not. To get a list of the available shells, you can run:
cat /etc/shells
## or possibly
sudo cat /etc/shells
EDIT: Since you say it works fine in a shell already, you have presumably already handled this, but user-flow would also have to be available. It's not a module from npmjs, so would need to already be present on the box as either a local dependency or a private repo to which the EC2 instance has access.

Related

I can't run commands from nodejs as child_process, or read the file system in an electron application with snap package configuration

I have working on an electron desktop application, the app is quite simple its about building a file browser for Linux. From Nodejs apis I use child_process, fs, path, os, and so on.
I am using electron-builder to package and build the application.
When I build for linux with target like "zip", "deb", "rpm", the application works as expected.
But when I compile for snap. I can't run commands like:
const util = require('util');
const exec = util.promisify(require('child_process').exec);
await exec('which code');
.....
await openExternalApp('code -n', `"${file.path}"`)
.....
async function openExternalApp(cmd, path) {
const util = require('util');
const exec = util.promisify(require('child_process').exec);
await exec(`${cmd} ${path}`);
}
.....
I got an error:
Error: Command failed: which code
at ChildProcess.exithandler (node:child_process:406:12)
at ChildProcess.emit (node:events:390:28)
at maybeClose (node:internal/child_process:1064:16)
at Socket.<anonymous> (node:internal/child_process:450:11)
at Socket.emit (node:events:390:28)
at Pipe.<anonymous> (node:net:687:12) {
killed: false,
code: 1,
signal: null,
cmd: 'which code',
stdout: '',
stderr: ''
}
Error: Command failed: which code
Also in my program when i have to read directories of the file system related with hard drive or another location simply i can't.
I've started to study a little bit the docs in snap store and I realized that you have to configure the slot, layout and slugs to access the whole file system, and perhaps to run bash or shell command like the one above.
After several configuration i continue with the same issues. Here is my configuration for snap in pakage.json
"snap": {
"plugs": [
"desktop",
"desktop-legacy",
"home",
"x11",
"unity7",
"browser-support",
"network",
"gsettings",
"opengl",
"block-devices",
"classic-support",
"hardware-observe",
"home",
"system-backup",
"system-observe",
"process-control",
"hostname-control",
"removable-media",
{
"system-files": {
"read": [
"/etc",
"/usr",
"/home",
"/media",
"/mnt",
"/var",
"/temp",
"/opt",
"/sys",
"/dev",
"/bin",
"/snap"
],
"write": [
"/home",
"/mnt"
]
}
}
],
"desktop": {
"Encoding": "UTF-8",
"Icon": "${SNAP}/icon.png"
}
}
snap image config in pakage.json

Linux screen dissapears after Jenkins job is done

I have made a Jenkins pipeline for my Angular application.
This Angular application uses SSR, so you have to run it in the background.
Thus I decided to use a screen for this, here is my JenkinsFile:
pipeline {
agent any
environment {
HOME = '.'
}
stages {
stage('build') {
steps {
sh 'npm i'
sh 'npm run build:ssr'
}
}
stage('move') {
steps {
script {
if (BRANCH_NAME == 'master') {
sh 'rm -R /var/www/AngularJenkinsTest_Master/client || true'
sh 'mkdir /var/www/AngularJenkinsTest_Master/client || true'
sh 'cp -R $WORKSPACE/dist/AngularJenkinsTest/* /var/www/AngularJenkinsTest_Master/client'
}
}
}
}
stage('publish') {
steps {
script {
if (BRANCH_NAME == 'master') {
sh 'screen -X -S AngularJenkinsTest_Master kill | true'
sh 'screen -dmS AngularJenkinsTest_Master'
sh 'screen -S AngularJenkinsTest_Master -X stuff "node /var/www/AngularJenkinsTest_Master/client/server/main.js\n"'
}
}
}
}
}
}
As you can see in "publish", I kill the screen (as it is running a node application), I then create a new screen and send a command to it.
I added a screen -ls to the end of it, and it did show that it existed, but when I go to my linux console.
jenkins#server:/root$ screen -ls
No Sockets found in /run/screen/S-jenkins.
This is the output that jenkins gives me:
Jenkins Output
I am new to Jenkins, so maybe I am just being dumb, but is there any reason for this to happen?

How to execute npm script using grunt-run?

I have a npm task in my package.json file as follows to execute jest testing:
"scripts": {
"test-jest": "jest",
"jest-coverage": "jest --coverage"
},
"jest": {
"testEnvironment": "jsdom"
},
I want to execute this task npm run test-jest using grunt. I installed grunt-run for the same and added the run task, but how do I invoke this npm task there?
run: {
options: {
// Task-specific options go here.
},
your_target: {
cmd: 'node'
}
}
Configure your Gruntfile.js similar to the example shown in the docs.
Set the value for the cmd to npm.
Set run and test-jest in the args Array.
Gruntfile.js
module.exports = function (grunt) {
grunt.loadNpmTasks('grunt-run');
grunt.initConfig({
run: {
options: {
// ...
},
npm_test_jest: {
cmd: 'npm',
args: [
'run',
'test-jest',
'--silent'
]
}
}
});
grunt.registerTask('default', [ 'run:npm_test_jest' ]);
};
Running
Running $ grunt via your CLI using the configuration shown above will invoke the npm run test-jest command.
Note: Adding --silent (or it's shorthand equivalent -s) to the args Array simply helps avoids the additional npm log to the console.
EDIT:
Cross Platform
Using the grunt-run solution shown above failed on Windows OS when running via cmd.exe. The following error was thrown:
Error: spawn npm ENOENT Warning: non-zero exit code -4058 Use --force to continue.
For a cross-platform solution consider installing and utlizing grunt-shell to invoke the npm run test-jest instead.
npm i -D grunt-shell
Gruntfile.js
module.exports = function (grunt) {
require('load-grunt-tasks')(grunt); // <-- uses `load-grunt-tasks`
grunt.initConfig({
shell: {
npm_test_jest: {
command: 'npm run test-jest --silent',
}
}
});
grunt.registerTask('default', [ 'shell:npm_test_jest' ]);
};
Notes
grunt-shell requires load-grunt-tasks for loading the Task instead of the typical grunt.loadNpmTasks(...), so you'll need to install that too:
npm i -D load-grunt-tasks
For older version of Windows I had to install an older version of grunt-shell, namely version 1.3.0, so I recommend installing an earlier version.
npm i -D grunt-shell#1.3.0
EDIT 2
grunt-run does seem to work on Windows if you use the exec key instead of the cmd and args keys...
For cross platform purposes... I found it necessary to specify the command as a single string using the exec key as per the documentation that reads:
If you would like to specify your command as a single string, useful
for specifying multiple commands in one task, use the exec: key
Gruntfile.js
module.exports = function (grunt) {
grunt.loadNpmTasks('grunt-run');
grunt.initConfig({
run: {
options: {
// ...
},
npm_test_jest: {
exec: 'npm run test-jest --silent' // <-- use the exec key.
}
}
});
grunt.registerTask('default', [ 'run:npm_test_jest' ]);
};

How to execute forever command remotely without using full path?

I am trying to execute “forever” command remotely using powershell but I am getting error
'forever' is not recognized as an internal or external command
Here is what I am doing. I have the node js script "MyNodeScript.js" which executes the forever command. The script is on the WebServer "MyWebServer1".
Node.Js and Forever is installed on MyWebServer1 globally.
var exec = require('child_process').exec;
var _ = require('underscore');
var winston = require('winston');
var async = require('async');
var mycommand = 'forever -p ./logs --sourceDir ../wf_manager --workingDir ../wf_manager start -a --uid "ServiceApp" ServiceApp.js'
function start(callback) {
async.waterfall([
function (cb) {
executeCommand(mycommand, false, cb);
}
], function done(err) {
if (err) {
winston.error('failed to start all instances by forever:' + err);
} else {
winston.info('successfully started all instances by forever');
}
callback();
});
}
function executeCommand(command, skip, callback) {
async.waterfall([
function (cb) {
exec(command, cb);
}
], function done(err) {
if (err) {
if (skip) {
// skip the error
callback(null);
} else {
callback(err);
}
} else {
callback(null);
}
});
}
module.exports = {
executeCommand: executeCommand,
start: start
}
start(function(){});
On the same MyWebServer1 under same folder i have a powershell script "MyPowerShellScript.ps1" which call this node script. The powershell script has only one line
node D:\myfolder\maintenance\MyNodeScript.js
I can run this powershell script locally on MyWebServer1 and it works fine. But when i try to execute this powershell script as below from remote machine
invoke-command -computername MyWebServer1 -filepath \\MyWebServer1\MyFolder\maintenance\MyPowerShellScript.ps1
i am getting error
error: failed to start all instances by forever:Error: Command failed:
C:\Windows\system32\cmd.exe /s /c "forever -p ./logs --sourceDir
../wf_manager --workingDir ../wf_manager start -a --uid "ServiceApp"
ServiceApp.js"
+ CategoryInfo : NotSpecified: (error: failed t...ServiceApp.js":String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
+ PSComputerName : MyWebServer1
'forever' is not recognized as an internal or external command, operable program or
batch file.
Note that i can execute the script remotely without any error if i update "MyNodeScript.js" and use full physical path for the forever command
var mycommand = 'C:\\Users\\admin\\AppData\\Roaming\\npm\\forever -p ./logs --sourceDir ../wf_manager --workingDir ../wf_manager start -a --uid "ServiceApp" ServiceApp.js'
However i would like to use just forever command. The path is already added as Environment variable on MyWebServer1
Try calling the ps1-file like this:
Invoke-Command -Scriptblock {powershell.exe -command {d:\blah\MyPowershellscript.ps1 } }
Via -filepath your specify a local scriptfile that should be executed remotely. As your script is present on the target system, you can omit that.

nodemon : Passing arguments to the executable when using as a required module

I'm trying to start a script with nodemon, using it as a required module, and I cannot pass arguments correctly.
For example, for
var args = [
process.argv[0], '--harmony',
'/path/to/script.js', '-i', 'logs'
];`
I'm expecting the script to be launched as :
node --harmony /path/to/script.js -i logs
But it doesn't work and all I can manage to get is
node --harmony /path/to/script.js -i logs /path/to/script.js
This is what I tried :
var app = require('nodemon')({
script: args[2],
exec: args.join(' ')
});
I know about execMap, but it's no good as I cannot pass arguments at the end anyway.
How can it be done?
Skimming through the source code, I found the args config options (undocumented...). It turns out to be what I needed.
var app = require('nodemon')({
exec: args.slice(0, 2),
script: args[2],
args: args.slice(3)
});
I recommend use gulp with nodemon
var argv = require('optimist').argv
gulp = require("gulp"),
nodemon = require("gulp-nodemon");
gulp.task("default", [], function(){
nodemon({
script: 'app.js',
ignore: ["public/*"],
env: {'NODE_ENV': 'development'},
args: ["--port="+argv.port],
exec: "node --harmony"
}).on("start");
});

Resources