I am trying to setup a private build agent for Azure DevOps on Ubuntu. And I need to use npm task for builds.
I tried to use nvm to install the latest node, and the installation was successful:
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.1/install.sh | bash
nvm install 11.10.1
I can check node -v
and npm -v
. But when the npm task is executed from the pipeline - it fails with
Unable to locate executable file: 'npm'. Please verify either the file path exists or the file can be found within a directory specified by the PATH environment variable. Also check the file mode to verify the file is executable.
In my PATH I have /usr/local/nvm/versions/node/v11.10.1/bin
and ls -l
shows:
lrwxrwxrwx 1 500 500 38 Feb 28 06:00 /usr/local/nvm/versions/node/v11.10.1/bin/npm -> ../lib/node_modules/npm/bin/npm-cli.js
I also added 777 (just to try it!) for npm-cli.js, and still no luck.
I also found this similar question - https://github.com/Microsoft/azure-pipelines-agent/issues/1862
How to properly install node and npm using nvm on Ubuntu agent for Azure DevOps?
To get this to work I ended up creating a bash script that proxies to nvm, and then tells the agent to update the PATH variable to the current NVM.
Create a file at /usr/local/bin/nvm
containing this:
#!/usr/bin/env bash
# Store the current path so we can check to see if it changes
OLD_PATH=$PATH
# Normal NVM stuff
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
# Execute NVM and pass all the arguments
nvm $@
# Check to see if the path has changed, and if an azure agent is running the
# command by checking the existence of the $AGENT_VERSION variable.
if [[ "$OLD_PATH" != "$PATH" && ! -z ${AGENT_VERSION+x} ]]; then
# Grab the current node executable Something like:
# /Users/digitaldev/.nvm/versions/node/v10.16.3/bin
CURRENT_NODE=$(nvm which current)
# Resolve to directory of the node executable Something like
# /Users/digitaldev/.nvm/versions/node/v10.16.3
BIN_DIR=$(dirname "$CURRENT_NODE")
# Tell Azure Pipeline to update the PATH [1]
echo "##vso[task.prependpath]$BIN_DIR"
fi
Make the file executable:
chmod +x /usr/local/bin/nvm
Now when an Azure Pipeline runs nvm
it'll update the path to look for the nvm version of node.
[1] Logging Commands PrependPath
I create a template like this based on the instructions from nvm.sh and set the NVM_DIR and PATH accordingly.
steps:
- bash: |
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.36.0/install.sh | bash
export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
nvm install
nvm use
echo "##vso[task.setvariable variable=NVM_DIR;]$NVM_DIR"
echo "##vso[task.setvariable variable=PATH;]$PATH"
displayName: "Install Node.js"
I also add a .nvmrc
as documented in nvm.sh so that the version of node to use is specified.
Then I use it in my pipeline as follows
steps:
- template: templates/install-node-js.yml
- bash: |
node --version
This is my template for installing node and also doing caches to the packages in package-lock.json
.
steps:
- task: Cache@2
inputs:
key: 'nvm | "$(Agent.OS)" | .nvmrc'
path: $(Pipeline.Workspace)/.nvm
displayName: "Cache NVM"
- task: Cache@2
inputs:
key: 'npm | "$(Agent.OS)" | package-lock.json'
path: $(Build.SourcesDirectory)/node_modules
displayName: "Cache node dependencies"
- bash: |
set -e
if [ $(System.Debug) ]
then
set -x
fi
if [ ! -s $NVM_DIR/nvm.sh ]
then
mkdir -p $NVM_DIR
curl -so- https://raw.githubusercontent.com/nvm-sh/nvm/v0.36.0/install.sh | bash
fi
. $NVM_DIR/nvm.sh || true
nvm install
nvm use
npm ci
echo "##vso[task.setvariable variable=PATH;]$PATH"
env:
NVM_DIR: $(Pipeline.Workspace)/.nvm
displayName: Install Node and dependencies
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With