Pass values from cmd to npm script - powershell

I've searched for days but did not find an answer that worked for my problem.
I want to run a npm script through cmd or Powershell in Windows and pass values for script variables.
I would like the bellow script in package.json:
"scripts": {
"happy-birthday": "echo Happy birthday $NAME and many returns!"
}
To output:
Happy birthday Danny and many returns!
With a command like:
npm run happy-birthday --NAME=Danny
Everything I tested so far gives me:
Happy birthday $NAME and many returns!
It feels like npm does not recognize this as a variable and prints it like it is a string. I also tested %NAME%.
Npm version - 6.12.1

You can't pass arguments to the middle of npm scripts, argument(s) can only be passed to the end of them. See my answer here for further explanation.
Given your example, consider the following solution which will work successfully across all platforms:
In your package.json file define your happy-birthday npm script as follows:
"scripts": {
"happy-birthday": "node -e \"console.log('Happy birthday %s and many returns!', process.argv[1] || 'Jane')\""
}
Then run the following command via cmd or Powershell (or any other command line tool).
npm run happy-birthday -- Danny
This will print:
Happy birthday Danny and many returns!
Note: If you just run the following command, i.e. without passing an argument:
npm run happy-birthday
It will print the default name instead:
Happy birthday Jane and many returns!
Explanation:
The npm script utilizes the nodejs command line option -e to evaluate the inline JavaScript as follows:
console.log('Happy birthday %s and many returns!', process.argv[1] || 'Jane')
The arguments passed via the CLI, e.g. Danny, are read using process.argv - whereby we reference the Array element at index 1.
The Logical OR Operator, i,e. || is utilized to return Jane when no argument is passed.
Edit: Setting environment variables instead
Alternatively may want to consider setting an environment variable and referencing it in your npm script.
In your npm script define your happy-birthday npm script as follows:
"happy-birthday": "echo Happy birthday %NAME% and many returns!"
Note the %NAME% notation used by Windows only to reference the variable.
Using cmd
When using cmd (i.e. Command Prompt) you'll need to run the following command:
set NAME=Danny&& npm run happy-birthday
Using Powershell
When using Powershell you'll need to run the following command instead:
$env:NAME="Danny" ; npm run happy-birthday
Note: The default shell that npm utilizes for npm scripts is sh on *nix and cmd on windows. Therefore the aforementioned methods defined in steps 1 and 2 will fail on *nix platforms.
If cross-platform support is requirement and you do want to take this approach of setting environment variables and referencing them via npm scripts, then consider utilizing the cross-env package.

Related

How to switch NVM environments within perl

I am writing a perl script, and I want to run a simple shell command to use a certain version of NVM:
Here is my code snippet:
print "\n*** Switching to correct nvm environment for dashboard builds\n";
system("nvm use 8.12.0") == 0 or die $?;
But I am getting the following error:
Can't exec "nvm": No such file or directory
Can someone help?
Update (June 30, 2021):
I also tried adding the command:
my $nvm_version = "8.12.0";
system ("bash", "-lic", "nvm use $nvm_version");
But nothing happens:
I'm not familiar with nwm, but I think I get the gist of what it does. And if so, the attempt is fundamentally flawed. Even if you fixed this to run the proper shell so that nvm could run, I believe all the tool does is change the shell's environment variables, a shell you immediately exit. This means it would have no effect even if if it ran successfully.
Again, it this tool does what I think it does, such tool are meant to be used in interactive shells. In other instances, you simply use the path the to correct executable instead of relying on the PATH.
With that in mind, you can use the following to run the command in bash:
# Non-interactive shell.
system("bash", "-c", "nvm use 8.12.0")
or
# Interactive shell.
# This is improper and fragile as interactive shells
# often create aliases that override basic commands.
system("bash", "-ic", "nvm use 8.12.0")
Just to reiterate, at least one of these will allow the command to run (if it normally works from bash), but I believe it's unlikely this will produce the results you expect.
The nvm command is shell function which is different from a shell command. Also the nvm command is not an exported function so it will not be seen by sub shells. For example, in Bash shell:
$ nvm ls
-> v15.0.1
$ my-test-script.sh
./my-test-script.sh: line 3: nvm: command not found
where my-test-script.sh is:
#! /bin/bash
nvm use 16.4
The error nvm: command not found is because nvm is not exported. I can source the script in the current shell context to make it work:
$ source my-test-script.sh
Now using node v16.4.0 (npm v7.18.1)
$ node --version
v16.4.0
So a Perl script cannot change the node version of the current shell, but it can calculate the version and pass it back to shell, which can set the version. For example:
$ nvm use $(perl -E'$v=15.0; print $v')
Now using node v15.0.1 (npm v7.0.3)

NPM custom arguments produces Powershell error

I am trying to launch a npm script with a custom argument:
"publish-local": "ng build $PROJECT && cd dist/$PROJECT && npm publish --registry=http://my.local.npm.registry"
This is how I am trying to call it from the prompt:
PROJECT=my-lib npm run publish-local
This is how I have seen it should work on different web sources (for example:here)
Anyway, trying to do that, I get this error:
PROJECT=my-lib: The term 'PROJECT=my-lib' is not recognized as the name of a cmdlet, function, script file, or
operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try
again.
At line:1 char:1
What to do?
Short answer: The example(s) that you've seen that "should work" will only work on *nix. They do not work via PowerShell, nor via Command Prompt on Windows.
Given that you're wanting to pass an argument to a npm-script, whereby that argument is consumed two times in the middle of that script I suggest you consider the following approach instead:
The following suggested approach is very similar to my answer here.
Solution - Cross-platform:
For a cross-platform solution, (one which works successfully with *nix, Windows Command Prompt and PowerShell etc..), you'll need to utilize a nodejs helper script.
Let's name the nodejs script publish-local.js and save it in the projects root directory, at the same level as package.json.
publish-local.js
const execSync = require('child_process').execSync;
const arg = process.argv[2] || 'my-lib'; // Default value `my-lib` if no args provided via CLI.
execSync('ng build ' + arg + ' && cd dist/' + arg +
' && npm publish --registry=http://my.local.npm.registry', {stdio:[0, 1, 2]});
package.json
Configure your publish-local script to invoke publish-local.js as follows:
...
"scripts": {
"publish-local": "node publish-local",
},
...
Running publish-local script:
To invoke publish-local via your CLI you'll need to run:
npm run publish-local -- my-lib
Notes:
Inside publish-local.js take note of the line that reads:
const arg = process.argv[2] || 'my-lib'; // Default value `my-lib` if no args provided via CLI.
It specifies a default value to use when no argument is provide via the CLI.
So, If you were to currently run the npm script without passing an argument:
npm run publish-local
or run it with passing an argument:
npm run publish-local -- my-lib
They are essentially the same. However if you were to provide an argument that is different to my-lib, i.e. one that is different to the default specified in publish-local.js, it will take precedence. For example:
npm run publish-local -- some-other-lib
For a further understanding of this solution I suggest you read my answer that I previously linked to.
The default shell used by npm is cmd.exe on Windows, and sh on *nix - this given solution will work successfully with either.
If you only intend to use/support more recent versions of node.js that support ecmascript-6 features, such as destructuring, template literals then you could refactor publish-local.js as follows:
publish-local.js (refactored using ES6 features)
const { execSync: shell } = require('child_process');
const [ , , projectName='my-lib' ] = process.argv;
shell(`ng build ${projectName} && cd dist/${projectName} && npm publish --registry=http://my.local.npm.registry`, {stdio:[0, 1, 2]});

Recommended way to run commands after installing dependencies in the virtualenv

I would like to use tox to run py.test on a project which needs additional setup in addition to installing packages into the virtualenv. After creating the virtualenv and installing dependencies, some commands need to be run.
Specifically I'm talking about setting up a node and npm environment using nodeenv:
nodeenv --prebuilt -p
I see that tox allows me to provide a custom command used for installing dependencies by setting install_command in tox.ini. But I don't think this is what I want because that replaces the command (I assume pip) used to install dependencies.
I thought about using a py.test fixture with session scope to handle setting up nodeenv but that seems hacky to me as I don't want this to happen when py.test is run directly, not via tox.
What is the least insane way of achieving this?
You can do all necessary setup after the creation of the virtualenv and the dependency installation in commands. Yes, it says "the commands to be called for testing." but if you need to do extra work to prepare for testing you can just do it right there.
It works through whatever you throw at it in the order it is given - e.g.:
[testenv:someenv]
deps =
nodeenv
pytest
flexmock
commands =
nodeenv --prebuilt -p
; ... and whatever else you might need to do
py.test path/to/my/tests
If you have commands/scripts or whatever else that produces the right result but it returns a non zero exit status you can ignore that by prepending - (as in - naughty-command).
If you need more steps to happen you can wrap them in a little (Python) script and call that script instead as outlined in https://stackoverflow.com/a/47834447/2626627.
There is also an issue to add the ability to use more than one install command: https://github.com/tox-dev/tox/issues/715 is implemented.
I had the same issue, and as it was important for me to be able to create the environment without invoking the tests (via --notest), I wanted the install to happen in the install phase and not the run phase, so I did something slightly differently. First, I created a create-env script:
#!/usr/bin/env sh
set -e
pip install $#
nodeenv --prebuilt --python-virtualenv --node=8.2.1
Made it executable, Then in tox.ini:
[tox]
skipsdist = True
[testenv]
install_command = ./create-env {opts} {packages}
deps = nodeenv
commands = node --version
This complete example runs and outputs the following:
$ tox
python create: .../.tox/python
python installdeps: nodeenv
python installed: nodeenv==1.3.0
python runtests: PYTHONHASHSEED='1150209523'
python runtests: commands[0] | node --version
v8.2.1
_____________________________________________________________________ summary ______________________________________________________________________
python: commands succeeded
congratulations :)
This approach has the downside that it would only work on Unix.
In tox 715, I propose the possibility of native support for multiple install commands.

Change shell within Perl script

I am looking for a nice way to get the following done:
So I have a script that I need to run in Python in Unix by calling from a Perl script that was, in turn, called from my Excel VBA macro in Windows using Plink. The Python script, due to dependency issues, has to run in either csh or bash, and I will need to use export/setenv to add a few libraries before running the script. However by default, perl runs in sh shell and as such, there is no way I can add in all the dependencies and have the Python script to run.
So, I am just wondering if there is EITHER: 1. a way for me to add dependencies to sh shell in the perl script, OR 2. force my perl script to run in csh (preferred, since for some reason .bashrc for the account runs into permission issues).
Thanks a lot!
How about "3. Set the appropriate environment variable in the Perl or Python scripts"?
$ENV{'PATH'} = ...
...
os.environ['PATH'] = os.pathsep.join(newpaths + os.environ['PATH'].split(os.pathsep))
(dunno how to get the path separator in Perl, sorz)
To force the shell to csh, try the following in Perl :
`/bin/csh -c "command_name"`;
Edit:
You can use ENV variable, like this. Try that :
$s = `/bin/bash -c 'VAR_FOO=753; echo \$VAR_FOO'`;
print $s;
I ended up just change the .cshrc script, apparently the addition to PATH, for some reason, did not work for me. After that, everything runs smoothly by putting all into one line
so basically it looks something like this
/path/to/.cshrc && /python/path/to/python
Hope that helps!

Perl script works but not via CRON

I have a perl script (which syncs delicious to wp) which:
runs via the shell but
does not run via cron (and i dont get an error)
The only thing I can think of is that it read the config file wrongly but... it is defined via the full path (i think).
I read my config file as:
my $config = Config::Simple->import_from('/home/12345/data/scripts/delicious/wpds.ini',
\my %config);
(I am hosted on mediatemple)
Does anybody have a clue?
update 1: HERE is the complete code: http://plugins.svn.wordpress.org/wordpress-23-compatible-wordpress-delicious-daily-synchronization-script/trunk/ (but I have added the path as above to the configuration file location as difference)
update 2: crossposted on https://forums.mediatemple.net/viewtopic.php?pid=31563#p31563
update 3: the full path did the trick, solved
The difference between a cron job and a job run from the shell is 'environment'. The primary difference is that your profile and the like are not run for a cron job, so any environment variables you have set in your normal shell environment are not set the same in the cron environment - no extensions to PATH, no environment variables identifying where Delicious and/or WP are hosted, etc.
Suggestion: create a cron job that simply reports the environment to a known file:
env > /home/27632/tmp/env.27632
Then see what is set in your own shell environment in comparison. Chances are, that will reveal the trouble.
Failing that, other environmental differences are that a cron job has no terminal, and has /dev/null for input and output - so interactive stuff does not work well.
it seems the problem is not in running perl, but locating the Config library
you should try:
perl -e "print #INC"
and run a similar perl script in cron, and read the output
it possible that they differ
I suggest looking at my answer to How to simulate the environment cron executes a script with?
This is an similar Jonathan's answer but goes a bit further.
Based on your crontab, and depending on your installation, the problem might be the "perl". As others note the environment, particularly the $PATH variable, is different for cron. perl may not be in the path so you need to put the full path to perl in the cron command.
You can determine the path with the command $ type perl
I run into the same problem ...
Perl script works but not via CRON => error: "perl: command not found"
... after an update from Plesk 12.0 to Plesk 12.5. But the existing answers were not very helpful for me.
It took some time, but than I found this thread in the Odin forum which helps me: https://talk.plesk.com/threads/scheduled-tasks-always-fail.331821/
They suggest the following:
/usr/local/psa/bin/server_pref -u -crontab-secure-shell ""
That deletes in the /var/spool/cron/crontabs files the line:
SHELL="/opt/psa/bin/chrootsh"
After that, my cron jobs run with out any error.
(Ubuntu 14.04 with Plesk 12.5)
If the perl script runs fine manually, but not from crontab, then
there is some environment path needed by the some package that is not
getting through `cron`. Run your command as follows:
suppose your cron entry like:
* 13 * * * /usr/bin/perl /home/username/public_html/cron.pl >/dev/null 2>&1
env - /home/username/public_html/cron.pl
The output will show you the missing package. export that package path in
$PATH variables