Git HOME environment variables not being used with child-process.exec - node.js

The second optional argument to child-process's exec() method are an option's object with an env property. This should set the environmental variables for the executable that will run, but in some cases this is not the case.
What I'm trying to do is set the HOME variable for git to use to a different path so that the host alias MY_SSH_HOST_ALIAS reads the $HOME/.ssh/config file and chooses the appropriate key for that alias. I'm trying to do this because my system's default .ssh folder is not in HOME.
However, the below fails and reads the default key from $HOME/.ssh/
const exec = require('child-process').exec
var succeeds = 'echo "my home is $HOME"' // prints my home is ~/mypath/
var fail = 'git clone MY_SSH_HOST_ALIAS:repo.git'
var options = { env: { HOME: '~/mypath/' } }
exec(fail, options, function(e, out, err) {
console.out(e, out, err)
})

Related

Create a persistent bash shell session in Node.js, know when commands finish, and read and modify sourced/exported variables

Imagine this contrived scenario:
./main.sh
source ./config.sh
SOME_CONFIG="${SOME_CONFIG}bar"
./output.sh
./config.sh
export SOME_CONFIG='foo'
./output.sh
echo "Config is: ${SOME_CONFIG}"
I am trying to replace ./main.sh with a Node.js powered ./main.js WITHOUT replacing the other shell files. The exported ./config.sh functions/variables must also be fully available to ./output.sh
Here is a NON working ./main.js. I have written this for the sole purpose to explain what I want the final code to look like:
const terminal = require('child_process').spawn('bash')
terminal.stdin.write('source ./config.sh\n')
process.env.SOME_CONFIG = `${process.env.SOME_CONFIG}bar` // this must be done in JS
terminal.stdin.write('./output.sh\n') // this must be able to access all exported functions/variables in config.sh, including the JS modified SOME_CONFIG
How can I achieve this? Ideally if there's a library that can do this I'd prefer that.
While this doesn't fully answer my question, it solves the contrived problem I had at hand and could help others if need be.
In general, if bash scripts communicate with each other via environment variables (eg. using export/source), this will allow you to start moving bash code to Node.js.
./main.js
const child_process = require("child_process");
const os = require("os");
// Source config.sh and print the environment variables including SOME_CONFIG
const sourcedConfig = child_process
.execSync(". ./config.sh > /dev/null 2>&1 && env")
.toString();
// Convert ALL sourced environment variables into an object
const sourcedEnvVars = sourcedConfig
.split(os.EOL)
.map((line) => ({
env: `${line.substr(0, line.indexOf("="))}`,
val: `${line.substr(line.indexOf("=") + 1)}`,
}))
.reduce((envVarObject, envVarEntry) => {
envVarObject[envVarEntry.env] = envVarEntry.val;
return envVarObject;
}, {});
// Make changes
sourcedEnvVars["SOME_CONFIG"] = `${sourcedEnvVars["SOME_CONFIG"]}bar`;
// Run output.sh and pass in the environment variables we got from the previous command
child_process.execSync("./output.sh", {
env: sourcedEnvVars,
stdio: "inherit",
});

How to append values to the PATH environment variable in NodeJS?

Following the answer suggested in the question -
Is it possible to permanently set environment variables?
I was able to set new environment variables permanently with the command -
spawnSync('setx', ['-m', 'MyDownloads', 'H:\\temp\\downloads'])
But now my goal is to append new values to the PATH environment variable.
Is it possible?
Why don't you just get the environment variable and then append to it?
I.e.
const {spawnSync} = require("child_process");
const current_value = process.env.PATH;
const new_path_value = current_value.concat(";", "/some/new/path");
var result = spawnSync('setx', ['-m', 'PATH', new_path_value])
// STDOUT
var stdOut = result.stdout.toString();
console.log(stdOut)
// STDERR
var stdErr = result.stderr.toString();
if(stdErr === '') {
console.log('Successfully set environment variable')
} else {
console.log(`ERROR: ${stderr}`)
}
Update "/some/new/path" and run this as admin as the link you provided suggests and it should work.
Run your script with the admin permission:
Open cmd or PowerShell with admin
Run node your_script.js
To append PATH variable, you can set value is : %PATH%;your_new_value here (%PATH% get old value)
If you run with electron app, you should require admin permission.
Don't forget setx run on window
I don't have rights to modify my registry, and I also would rather not call an OS command such as setx.
The following adds an additional component to the Windows PATH. I then ran Selenium, which uses the new setting.
// Display current value of PATH
const current_value = process.env.PATH;
console.log("PREV VALUE:")
console.log(current_value)
// Add the additional entry
const addl_entry = String.raw`\my\new\path\component`
process.env["PATH"] = addl_entry + ";" + current_value
// Display the new value
console.log("NEW VALUE:")
console.log(process.env.PATH)

Node.js: How to pass environment variables with a shell command?

I'm trying to generate specific values in a Node.js script and then pass them as environment variables to a shell command, but I can't seem to get it working. What's the right way to execute a string as a shell command from Node.js while passing in my own environment variables?
The following doesn't seem to be working as I would expect:
const shell = require("shelljs");
const { findPort } = require("./find-port");
async function main() {
// imagine that `findPort(value)` starts at the provided `value` and
// increments it until it finds an available port
const PORT = await findPort(8000); // 8000, 8001, etc
const DB_PORT = await findPort(3306); // 3306, 3307, etc
shell.exec(`yarn run dev`, {
env: {
PORT,
DB_PORT,
},
async: true,
});
}
main();
When I try to run this, I get the following error:
env: node: No such file or directory
Important: I don't want any values to leak out of this specific script, which is why I'm trying to avoid the export FOO=bar syntax, but I may be misunderstanding how that works.
I'd prefer a solution that uses shelljs, but I'm open to other solutions that use child_process.exec, execa, etc.
The script does exactly what you ask for: it runs yarn run dev with your environment variables. Unfortunately, that means that it does not run with the system's environment variables that yarn depends on, like PATH.
You can instead run it with both your variables and the system's variables:
shell.exec(`yarn run dev`, {
env: {
...process.env,
PORT,
DB_PORT,
}
});

why i am not able to update env variable in node js

I want to update my env variable in node js, but i am not able to update its env variable, i tried with console.log(process.env.DB_HOST) but still i am getting old value, here i have added my whole code, can anyone please look in to it, and help me to resolve this issue,
function exec_api() {
return new Promise(async function (resolve) {
const execSync = require('child_process').exec;
//let child_process_obj = execSync('DB_HOST='+process.env.UNITTEST_DB_HOST+' DB_DATABASE='+process.env.UNITTEST_DB_DATABASE+' DB_USERNAME='+process.env.UNITTEST_DB_USERNAME+' DB_PASSWORD='+process.env.UNITTEST_DB_PASSWORD+' PORT='+process.env.UNITTEST_SERVICE_PORT+' ./node_modules/.bin/nodemon main.js');
await execSync('export DB_HOST=' + process.env.UNITTEST_DB_HOST);
await execSync('export DB_DATABASE=' + process.env.UNITTEST_DB_DATABASE);
await execSync('export DB_USERNAME=' + process.env.UNITTEST_DB_USERNAME);
await execSync('export DB_PASSWORD=' + process.env.UNITTEST_DB_PASSWORD);
await execSync('export PORT=' + process.env.UNITTEST_API_BACKEND_PORT);
let child_process_obj = await execSync('node main.js');
unittest_api_backend_process_id = child_process_obj.pid;
resolve(true);
});
}
TLDR: Just change process.env
To change, add or delete environment variables, use process.env. The following is test code showing how this works:
In main.js:
console.log(process.env.DB_DATABASE);
In exec.js:
const execSync = require ('child_process').execSync;
process.env.DB_DATABASE = 'foo'; // this is ALL you need to do
console.log(execSync('node main.js').toString('utf8'));
With the two files above, if you run node exec.js you will see foo printed out in the console. This is printed from main.js which inherits the environment from exec.js.
So all you need to do in your code is:
I want to update my env variable in node js, but i am not able to update its env variable, i tried with console.log(process.env.DB_HOST) but still i am getting old value, here i have added my whole code, can anyone please look in to it, and help me to resolve this issue,
function exec_api() {
return new Promise(function (resolve) {
const exec = require('child_process').exec;
// The following is node.js equivalent of bash "export":
process.env.DB_HOST = process.env.UNITTEST_DB_HOST;
process.env.DB_DATABASE = process.env.UNITTEST_DB_DATABASE;
process.env.DB_USERNAME = process.env.UNITTEST_DB_USERNAME;
process.env.DB_PASSWORD = process.env.UNITTEST_DB_PASSWORD;
process.env.PORT = process.env.UNITTEST_SERVICE_PORT;
let child_process_obj = exec('node main.js', {
stdio: ['inherit', 'inherit', 'inherit']
});
unittest_api_backend_process_id = child_process_obj.pid;
resolve(true);
});
}
Note that if you want the promise to return when the main.js ends you need to do:
function exec_api() {
return new Promise(function (resolve) {
const exec = require('child_process').exec;
// The following is node.js equivalent of bash "export":
process.env.DB_HOST = process.env.UNITTEST_DB_HOST;
process.env.DB_DATABASE = process.env.UNITTEST_DB_DATABASE;
process.env.DB_USERNAME = process.env.UNITTEST_DB_USERNAME;
process.env.DB_PASSWORD = process.env.UNITTEST_DB_PASSWORD;
process.env.PORT = process.env.UNITTEST_SERVICE_PORT;
let child_process_obj = exec('node main.js', {
stdio: ['inherit', 'inherit', 'inherit']
});
unittest_api_backend_process_id = child_process_obj.pid;
child_process_obj.on('exit', () => resolve(true));
// ^^^ Cannot use `await` as the API is not promise based
// but event based instead.
});
}
Long story: The full explanation of why export doesn't work
On unixen, environment variables, and indeed, the entire environment including current working directory, root directory (which can be changed via chroot) etc. are not features of shells. They are features of processes.
We may be familiar with the export syntax of some shells to set environment variables for child processes but that is the shell's syntax. It has nothing to do with environment variables themselves. C/C++ for example don't use export instead uses the setenv() function do set environment variables (indeed, internally that's what bash/sh/ksh etc do when implementing export).
In node.js, the mechanism for reading and setting environment variables is via process.env.
Why asking a shell to do it don't work
This is not merely a node.js issue. It also won't work in bash:
In exporter.sh:
#! /bin/bash
export DB_DATABASE=$1
In exec.sh:
#! /bin/bash
./exporter.sh foo
echo $DB_DATABASE ;# does not print "foo"
This is a core security feature of unixen: other users should not be allowed to mess with your process. The way this policy is enforced in the case of the environment is that only a parent process can set the environment of the child process. A child process is not allowed to set the environment of the parent process. The assumption is that the child process belongs to the parent process so you should be allowed to do what you want to a program - but since the parent process (you) don't belong to the child process the child is not allowed to mess with the parent's environment.
That's why your attempt to use export doesn't work. It actually works (the variables are indeed created in the subshell) but is not allowed to change the environment of it's parent (the node.js process)
When you use export in a terminal, it instructs the shell to set environment variables.
When you call exec from your code, you are not running such a shell, with the reason being that it would become a challenge to extract the output of every command.
This makes export an ignored command.
You can solve this by passing an option object to execSync:
execSync('node main.js', {
env: {
DB_HOST: 'localhost',
// More envs...
}
}

How to reset env for cucumber-reports through commands in karate

I have one runner file in karate to generate cucumber reports
public void genrateFinalReport() {
System.setProperty("karate.env", "pre_production"); // ensure reset if other tests (e.g. mock) had set env in CI
Results results = Runner.parallel(getClass(), 1);
generateReport(results.getReportDir());
assertTrue(results.getErrorMessages(), results.getFailCount() == 0);
}
in this i have set enviroment to pre_production.
I want to change or modify environment through the command line.
To change the environment through command line I'm using below command
mvn test -Dkarate.env=production -Dtest=PcadSanityTestReport
But by default pre_production environment is getting passed
com.intuit.karate - karate.env system property was: pre_production
can anyone help how to pass env in the command line or do I need to create a different runner for different env
I'm not sure I understand. You override the value with
System.setProperty("karate.env", "pre_production");
just before you launch the tests, yet you expect the value to be different?
Edit:
Since you seem to want a default value for env, you should do that in karate-config.js
var env = karate.env;
//here, you can override env with a default value if the value isn't an authorized one :
if (env != "pre_production" && env != "production"){
env = "pre_production";
}

Resources