Configure Ubuntu to enable the execution of npm without sudo

Node.js Package Manager (NPM) , is an is an online repository for the publishing of open-source Node.js projects, and a command-line utility for interacting with said repository that aids in package installation, version management, and dependency management. Essentially what this tools enables you to do is easily and quickly make use of the plethora of nodejs packages being developed on a daily basis. NPM enables the community to share and contribute to reusable javascript code libraries to help others with common software development problems.

Why executing NPM as root is a bad idea

Although the npm is effectively community managed and for the most part is a great resource. The main drawback is that not everybody in the community can be trusted. There may be some members of the community that may publish a script for nefarious reasons. it is because of this you don’t necessarily want to give any script you download from the internet root permissions to your machine. Typically you would want to have NPM execute as user with a reduced permission level.

NB : Running npm as your user account also may not be the best solution, as typically the files that contain sensitive information are typically in your user account folders

[ebs_panel style=”panel-info”] [ebs_panel-header] [ebs_icon type=”glyphicon glyphicon-download-alt” color=”#1e73be”] Download
[/ebs_panel-header] [ebs_panel-content] [wpdm_package id=’6149′] [/ebs_panel-content] [ebs_panel-footer] [ebs_icon type=”glyphicon glyphicon-info-sign” color=”#1e73be”]  How to execute bash scripts
[/ebs_panel-footer] [/ebs_panel]
usage()
{
cat << EOF
usage: $0 [-d] [-v]

This script is intended to fix the common problem where npm users
are required to use sudo to install global packages.

It will backup a list of your installed packages remove all but npm,
then create a local directory, configure node to use this for global installs
whilst also fixing permissions on the .npm dir before, reinstalling the old packages.

OPTIONS:
   -h   Show this message
   -d   debug
   -v   Verbose
EOF
}


DEBUG=0
VERBOSE=0
while getopts "dv" OPTION
do
     case $OPTION in
         d)
             DEBUG=1
             ;;
         v)
             VERBOSE=1
             ;;
         ?)
             usage
             exit
             ;;
     esac
done

to_reinstall='/tmp/npm-reinstall.txt'

if [ 1 = ${VERBOSE} ];  then
    printf "\nSaving list of existing global npm packages\n"
fi

#Get a list of global packages (not deps)
#except for the npm package
#save in a temporary file.
npm -g list --depth=0 --parseable --long | cut -d: -f2 | grep -v '^npm@\|^$' >$to_reinstall

if [ 1 = ${VERBOSE} ];  then
    printf "\nRemoving existing packages temporarily - you might need your sudo password\n\n"
fi
#List the file
#replace the version numbers
#remove the newlines
#and pass to npm uninstall

uninstall='sudo npm -g uninstall'
if [ 1 = ${DEBUG} ];    then
    printf "Won't uninstall\n\n"
    uninstall='echo'
fi
if [ -s $to_reinstall ]; then
    cat $to_reinstall | sed -e 's/@.*//' | xargs $uninstall
fi

defaultnpmdir="${HOME}/.npm-packages"
npmdir=''

read -p "Choose your install directory. Default (${defaultnpmdir}) : " npmdir

if [ -z ${npmdir} ]; then
    npmdir=${defaultnpmdir}
else
    if [ ! -d ${npmdir} ]; then
        echo "${npmdir} is not a directory."
        exit
    fi
    npmdir="${npmdir}/.npm-packages"
fi

if [ 1 = ${VERBOSE} ];  then
    printf "\nMake a new directory ${npmdir} for our "-g" packages\n"
fi

if [ 0 = ${DEBUG} ];    then
    mkdir -p ${npmdir}
    npm config set prefix $npmdir
fi

if [ 1 = ${VERBOSE} ];  then
    printf "\nFix permissions on the .npm directories\n"
fi

me=`whoami`
sudo chown -R $me ~/.npm

if [ 1 = ${VERBOSE} ];  then
    printf "\nReinstall packages\n\n"
fi

#list the packages to install
#and pass to npm
install='npm -g install'
if [ 1 = ${DEBUG} ];    then
    install='echo'
fi
if [ -s $to_reinstall ]; then
    cat $to_reinstall | xargs $install
fi

envfix='
export NPM_PACKAGES="%s"
export NODE_PATH="$NPM_PACKAGES/lib/node_modules:$NODE_PATH"
export PATH="$NPM_PACKAGES/bin:$PATH"
# Unset manpath so we can inherit from /etc/manpath via the `manpath`
# command
unset MANPATH  # delete if you already modified MANPATH elsewhere in your config
export MANPATH="$NPM_PACKAGES/share/man:$(manpath)"
'

fix_env() {
    if [ -f "${HOME}/.bashrc" ];    then
        printf "${envfix}" ${npmdir} >> ~/.bashrc
        printf "\nDon't forget to run 'source ~/.bashrc'\n"
    fi
    if [ -f "${HOME}/.zshrc" ]; then
        printf "${envfix}" ${npmdir} >> ~/.zshrc
        printf "\nDon't forget to run 'source ~/.zshrc'\n"
    fi

}

echo_env() {
    printf "\nYou may need to add the following to your ~/.bashrc / .zshrc file(s)\n\n"
    printf "${envfix}\n\n" ${npmdir}
}

printf "\n\n"
read -p "Do you wish to update your .bashrc/.zshrc file(s) with the paths and manpaths? [yn] " yn
case $yn in
    [Yy]* ) fix_env;;
    [Nn]* ) echo_env;;
    * ) echo "Please answer 'y' or 'n'.";;
esac

rm $to_reinstall

printf "\nDone - current package list:\n\n"
npm -g list -depth=0

 

Gary Woodfine

Freelance Full Stack Developer at threenine.co.uk
Helps businesses by improving their technical proficiencies and eliminating waste from the software development pipelines.

A unique background as business owner, marketing, software development and business development ensures that he can offer the optimum business consultancy services across a wide spectrum of business challenges.

Latest posts by Gary Woodfine (see all)