Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to source shell script with npm scripts?

I have a script in my package.json:

{
  "scripts": {
    "start": "source run-nvm.sh && ..."
  }
}

But running source run-nvm.sh && ... is different to yarn start (or npm run start).

Why? It creates a subshell. So I can't change environment of the original shell, I can't export constants for it or manipulate the state of nvm (I can't change node version for the parent shell)

So the real question

Can I execute yarn/npm script by not creating a subshell? (and use the current shell)

OR

How to source shell script with npm scripts?

Eventually, you may try to change the discourse by asking: "why don't you just source run-nvm.sh && yarn start" but I don't want to just add some custom scripts and complexity, I want it to be automatically executed on yarn start / npm start (to change node version automatically)

And the real problem

It works currently (the script changes the version of node and runs the app) but since it's a subshell it does not save the state of nvm. So on every yarn start it's using default version initially, then changes the version, then starts the app, so it adds ~3-4 seconds for the yarn start command for the version change. While it shouldn't set the version every time, but should set it just once, for the first time.

like image 855
Jerry Green Avatar asked Apr 16 '19 06:04

Jerry Green


2 Answers

I faced this problem today and I found a simple solution that works for me.

  1. Create a env file with the vars

    # cat > .env << EOF
    PORT=8080
    DB_HOST=my-db.host
    DB_PORT=3306
    DB_USER=mysql
    DB_PASS=123456
    EOF
    
  2. Create a new entry on the package.json file

    {
      [...]
      "start": "export $(cat .env | egrep -v '#|^$' | xargs) && node production-server/server.js",
      [...]
    }
    
  3. Start the app as usual

    # npm run start
    
    > [email protected] start /usr/local/myapp
    > export $(cat .env | egrep -v '#|^$' | xargs) && 
    node production-server/server.js
    
    > Ready on http://localhost:8080
    

Thats all.

If you want to know what export $(cat .env | egrep -v '#|^$' | xargs) does, keep reading.

export $(cat .env | egrep -v '#|^$' | xargs)
   |      |           |                 |
   |      |           |                 transform the output in "PORT=8080 DB_HOST=my-db.host DB_PORT=3306 DB_USER=mysql DB_PASS=123456"
   |      |           |
   |      |           filter lines starting with comment or blank line
   |      |
   |      cat the .env file
   |
   save the env on subshell before start the node
like image 129
Charles Santos Avatar answered Nov 09 '22 21:11

Charles Santos


Spotted your comment about using a vscode plugin If all your looking for is to change your nvm version for all terminals when the version differes on one of your projects then check out my overkill answer.

I will probably put all of the logic in a bash script and just share the link. 👍

Usually version of node is stored in the package.json and causing an incorrect version of node or npm to cause a failure.

// package.json

 "engines": {
    "node": ">=10.0.0"
  },
  "scripts": {

Usually version logic is in bashrc sourced by the shell in the $HOME/.bashrc file. when new window windows are created they re-trigger nvm use.

Two possibilities:
both use script: script-to-replace-nvm-version.sh
# Which replaces in bashrc the "nvm use " line with "nvm use $correctProjectVersion"

  1. Use start script in package json to run script-to-replace-nvm-version

  2. Use PROMPT_COMMAND to run script-to-replace-nvm-version:

a check is used here to make sure we are on an npm project.

be careful with PROMPT_COMMAND as it will run every time before the ps1 renders.

 PROMPT_COMMAND="[ -f ./package.json ] && $HOME/.scriptsourcedbybashrc.sh"

Script Summary: Nvm is usually sourced with the particular version in $HOME/.bashrc. Therefore we could the npm package rewrite the line containing nvm use ... and update the version. We could even prompt the user with do you wish to update?

check node version from package.json

projectVersion=$(cat ./package.json | grep 'node":' | tr -dc '0-9.')
echo $projectVersion

check node version from $HOME/.bashrc

nodeVersion=$(cat $HOME/.bashrc | grep 'nvm use' | tr -dc '0-9.')
echo $nodeVersion

check if node and package.json versions differ

# bash script, to be sourced by npm run start;

# WIP, needs a few more error checks
# such as, nvm used in more than one place in your bashrc.
if
  versions_differ_tell_and_prompt
fi
versions_differ_tell_and_prompt() {
  echo "nvm versions differ: ";
  found in project: $projectVersion;
  found in '$HOME/.bashrc': $nodeVersion;
  echo ;
  echo "ignore with any keypress or [uU] to update";
  old_stty_cfg=$(stty -g);
  stty raw -echo ; answer=$(head -c 1) ; stty $old_stty_cfg;
}
if echo "$answer" | grep -iq "^u" ;then
    update_file 'nvm use' "nvm use $projectVersion" && source $HOME.bashrc;
else
    echo "No changes made";
fi
update_file() {
  searchLine=$1
  replacementLine=$2
  lineNumber=$(cat $HOME/.bashrc | grep -n $searchLine | cut -f1 -d:)
  cp "$HOME/.bashrc" "$HOME/.bashrc.backup"
  sed -i "$lineNumbers/.*/$replacementLine/" "$HOME/.bashrc"
  source "$HOME/.bashrc"
}
unset searchLine
unset replacementLine
unset answer
unset old_stty_cfg
unset nodeVersion
unset projectVersion

Some other helpful links: https://github.com/md-command-line/ERRORSCREAM/issues/2

took most of the logic from here: https://github.com/MichaelDimmitt/git_check_computer/blob/master/git_check_computer.sh

correct the yarn start command to run the correct yarn or npm based on the lockfile found. https://github.com/MichaelDimmitt/know-your-package-manager

like image 44
Michael Dimmitt Avatar answered Nov 09 '22 19:11

Michael Dimmitt