NPM call bash script with multiple arguments - bash

I have created a bash script to automate the git release flow. This script takes one or multiple branch names as argument.
I can call the script in the terminal with: ./releaseGit.sh -b branch1 -b branch2
But I want to use the script with npm. My current package.json contains:
"scripts": {
(other scripts..)
"git-release": "scripts/releaseGit.sh -b $*"
}
But with this configuration I can only pass one branch as argument to the script. How can I change that?
works:
npm run git-release -b only-one-branch
Doesn't work:
npm run git-release -b first-branch -b second-branch

Package.json:
scripts: {
test: "sh init.sh"
}
Pass args:
npm run test -- a b c

Related

Change value of shell variables as an argument of npm run command

I am trying to accomplish the following -> Right now I use cypress to run e2e tests. It is being launched by npm command. I have several environments and different user permission. I created a shell script, where I have stored value of environment and user permission rights. What I want to do is to have opportunity to run npm command with parameters to change the value of variable from shell script. Could someone give a clue, is it even possible? The expected behaviour is to write something like:
npm run cy dev3,full
And have the opportunity to change the value of shell script variable to launch necessary environament and change value of user permissions.
package.json command:
"scripts": {
"cy": "./scripts/cypress.sh",
}
cypress.sh file content
#!/usr/bin/env bash
DEV_ENV="${DEV_ENV:-"dev3"}"
USER_TYPE="${USER_TYPE:-"full"}"
COMMAND="cypress open \
--browser chrome \
--config baseUrl=https://environment-$DEV_ENV.com \
--env DEV_ENV=https://environment-$DEV_ENV.com,USER_TYPE=$USER_TYPE
"
eval $COMMAND
Are you trying to change the environment variables seen in cypress.sh? If so, you can just execute npm run like this:
DEV_ENV=dev2 USER_TYPE=empty npm run cy
and it will change the value of DEV_ENV and USER_TYPE.
If you want to strictly run it by using the format you gave (npm run cy dev3,full), the args dev3,full are passed on to cypress.sh, so you can just parse the arguments directly in cypress.sh:
if [ ! -z "$1" ]; then
# do whatever here...
fi

How execute bash Command Substitution in npm script

I want to execute linter only on staged files.
For this i wrote inside package.json the command(inside scripts):
"fix-lint": "tslint $(git diff --name-only --staged) "
Execute it:
npm run fix-lint
I have an error:
error: unknown option --name-only
I tried to figure out and try simple command:
"lss": "echo $(ls)"
And again execute it:
npm run lss
The result is printing $(ls) instead of printing all files in directory.
My question is how to fix this commands/scripts?
Or maybe in general how execute bash Command Substitution in npm script.
Thanks in advance.

How to define an environment variable that can be automatically used in multiple npm scripts?

Consider the following npm scripts.
$ npm run
available via `npm run-script`:
make
OUTPUT=dist/main.js bash -c 'elm make src/Main.js --output=$0 $1'
make:dev
npm run make -- '$OUTPUT' --debug
make:prod
npm run make -- '>(npm run uglify -- $OUTPUT)' --optimize
uglify
uglifyjs --compress 'pure_funcs="F2,F3,F4,F5,F6,F7,F8,F9,A2,A3,A4,A5,A6,A7,A8,A9",pure_getters,keep_fargs=false,unsafe_comps,unsafe' | uglifyjs --mangle --output=
I'd like to use it as follows:
$ npm run make -- '$OUTPUT' '--debug'
> experiment#0.1.0 experiment /Users/Adit/experiment
> OUTPUT=dist/main.js bash -c 'elm make src/Main.js --output=$0 $1' '$OUTPUT' '--debug'
This would correctly create the debug build of the Elm application. However, this is not what happens. Instead of using single quotes, npm run uses double quotes:
$ npm run make -- '$OUTPUT' '--debug'
> experiment#0.1.0 experiment /Users/Adit/experiment
> OUTPUT=dist/main.js bash -c 'elm make src/Main.js --output=$0 $1' "$OUTPUT" "--debug"
Due to this the output is not what I expect it to be. What's the best way to resolve this issue without writing a custom shell script? I want to use the OUTPUT variable in two different commands. However, I only want to define it in one place.
I solved the problem as follows.
{
"config": {
"input": "src/Main.elm",
"output": "dist/main.js"
},
"scripts": {
"make": "elm make $npm_package_config_input --output $npm_package_config_output",
"make:dev": "npm run make -- --debug",
"make:prod": "npm run make -- --optimize",
"postmake:prod": "uglifyjs $npm_package_config_output --compress 'pure_funcs=\"F2,F3,F4,F5,F6,F7,F8,F9,A2,A3,A4,A5,A6,A7,A8,A9\",pure_getters,keep_fargs=false,unsafe_comps,unsafe' | uglifyjs --mangle --output=$npm_package_config_output"
}
}
Hence, if you have a configuration variables that you'd like to use in multiple npm scripts, you can add them to the config dictionary of package.json. After that, you can access them as environment variables in the npm scripts via the name $npm_package_config_<name> where <name> is the name of your config variable.
I also used a post script instead of process substitution to uglify the output of the Elm compiler. Doing so was overall less of a hassle than using process substitution via bash -c.
Finally, you can run make, make:dev, or make:prod for different builds. The first one is a regular build. The second one is a development build with the Elm debugging tools. The third one is a regular build which is optimized and minified for production use.

How to check if npm script exists?

I am creating a bash script which runs through each of my projects and runs npm run test if the test script exists.
I know that if I get into a project and run npm run it will give me the list of available scripts as follows:
Lifecycle scripts included in www:
start
node server.js
test
mocha --require #babel/register --require dotenv/config --watch-extensions js **/*.test.js
available via `npm run-script`:
dev
node -r dotenv/config server.js
dev:watch
nodemon -r dotenv/config server.js
build
next build
However, I have no idea how to grab that information, see if test is available and then run it.
Here is my current code:
#!/bin/bash
ROOT_PATH="$(cd "$(dirname "$0")" && pwd)"
BASE_PATH="${ROOT_PATH}/../.."
while read MYAPP; do # reads from a list of projects
PROJECT="${MYAPP}"
FOLDER="${BASE_PATH}/${PROJECT}"
cd "$FOLDER"
if [ check here if the command exists ]; then
npm run test
echo ""
fi
done < "${ROOT_PATH}/../assets/apps-manifest"
EDIT:
As mentioned by Marie and James if you only want to run the command if it exists, npm has an option for that:
npm run test --if-present
This way you can have a generic script that work with multiple projects (that may or may not have an specific task) without having the risk of receiving an error.
Source: https://docs.npmjs.com/cli/run-script
EDIT
You could do a grep to check for the word test:
npm run | grep -q test
this return true if the result in npm run contains the word test
In your script it would look like this:
#!/bin/bash
ROOT_PATH="$(cd "$(dirname "$0")" && pwd)"
BASE_PATH="${ROOT_PATH}/../.."
while read MYAPP; do # reads from a list of projects
PROJECT="${MYAPP}"
FOLDER="${BASE_PATH}/${PROJECT}"
cd "$FOLDER"
if npm run | grep -q test; then
npm run test
echo ""
fi
done < "${ROOT_PATH}/../assets/apps-manifest"
It just would be a problem if the word test is in there with another meaning
Hope it helps
The right solution is using the if-present flag:
npm run test --if-present
--if-present doesn't allow you to "check if a npm script exists", but runs the script if it exists. If you have fallback logic this won't suffice. In my case, I want to run npm run test:ci if it exists and if not check for and run, npm run test. Using --if-present would run the test:ci AND test scripts if both exists. By checking if one exists first, we can decide which to run.
Because I have both "test" and "test:ci" scripts, the npm run | grep approach wasn't sufficient. As much as I'd like to do this with strictly npm, I have jq in my environments so I decided to go that route to have precision.
To check for a script named "test:ci":
if [[ $(jq '.scripts["test:ci"]' < package.json;) != null ]]; then
// script exists
fi

How to set shell for npm run-scripts in Windows

I'm running npm on Windows and would like to use & style parallel operations in run-scripts
but running in parallel in cmd is kind of messy
in my package.json file I'd like to write-
scripts: { "go": "cmd1 & cmd2"}
but npm executes the script under cmd.exe which does not know about ; I could change this to
scripts: { "go": "bats/bat1.bat") where bat1.bat is a cmd bat file that uses the windows style call or start commands to run commands in parallel. which works but gives me a script that only works on Windows.
It would be a lot simpler if I could get npm to run the script under a bash clone or cygwin.
I tried
config: { "shell": "bash"}
but that still ran cmd.exe
Is there any way to tell npm to run-scripts using a specific shell (not cmd.exe)?
Since npm 5.1
npm config set script-shell "C:\\Program Files (x86)\\git\\bin\\bash.exe"
or (64bit installation)
npm config set script-shell "C:\\Program Files\\git\\bin\\bash.exe"
Note that you need to have git for windows installed.
You can revert it by running:
npm config delete script-shell
Here's one way to do it:
Create a script, such as my_script.sh, in your project bin directory.
In your package.json file, add a line to run the script using bash. For example:
"scripts": {
"boogie": "bash bin/my_script.sh"
}
Now you can run your bash script from npm by:
npm run-script boogie
Not very elegant, but it works.
If you are developing in both Windows and Linux/Unix, then at least this approach is fairly portable to both environments.
Ideally, overriding the npm shell config parameter should work, but npm (at least version 1.4.14) seems in Windows to ignore the setting and use cmd.exe instead.
Use the following command in your bash or Git Bash shell to find out the shell setting:
$ npm config ls -l | grep shell
By default, the output will be:
shell = "C:\\WINDOWS\\system32\\cmd.exe"
However, to override the default shell parameter, you can add (or edit) an npmrc file to the \Users\yourusername\AppData\Roaming\npm\etc directory. Just add the following line:
shell = "C:\\Program Files (x86)\\git\\bin\\bash.exe"
The path you use can be any valid path to bash.exe. Now, if you run the above "npm config ls -l | grep shell" command, you will see the following output, indicating that the shell parameter has been overriden:
shell = "C:\\Program Files (x86)\\git\\bin\\bash.exe"
; shell = "C:\\WINDOWS\\system32\\cmd.exe" (overridden)
One day, perhaps, a new version of npm will pay attention to the overridden shell parameter.
You can also use cross-platform powershell https://github.com/powershell/powershell#get-powershell for npm scripts.
To set for single project, run this from project root folder:
npm config set script-shell pwsh --userconfig ./.npmrc
To globally set for all node projects:
npm config set script-shell pwsh [--global]
just using CMD's way to run .bat!
.json
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject",
"app": "cd build & browser-sync start --server --files 'index.html'",
"bat": "start start-browser.bat",
"starts": "start http://localhost:7777/datas/ && start http://localhost:7777/Info/"
},
.bat
start http://localhost:7777/datas/ && start http://localhost:7777/Info/
Use a specifically created node_module for this purpose. I suggest using npm-run-all, but others exists, such as parallelshell.
Parallelshell example is below for drop-in-replacement for your question.
"scripts": {
"parallelexample1": "parallelshell \"echo 1\" \"echo 2\" \"echo 3\""
},
following command:
npm run parallelexample1
works both on windows and unix(Linux/MacOS).
Interestingly npm-run-all does not support shell commands; therefore we need to put all shell commands to separate scripts like below.
"scripts": {
"parallelexample2": "npm-run-all echo*",
"echo1": "echo 1",
"echo2": "echo 2",
"echo3": "echo 3"
},
Following command:
npm run parallelexample2
works both on windows and unix(Linux/MacOS).
In my case I just needed to run npm start from inside Bash. I run cmd then I open bash by running "c:\Program Files\Git\bin\bash.exe". Under bash shell I then was able to call npm build and npm start succesfully.
You may already have bash if you are using Git. If not, you can install it.
Hope this may save someone's time.

Resources