Last active
November 10, 2018 22:19
-
-
Save agwells/a9bed57c59430ef35e6328d3f93933fe to your computer and use it in GitHub Desktop.
Multi-package-management-system updater
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
#### | |
# A script to run updates or check for updates, in many different package management systems | |
# on Ubuntu. | |
# | |
# When I was installing everything I use through apt alone, it was easy to keep all my | |
# packages up to date with "apt-get update && apt-get upgrade", or the GUI update manager. | |
# But now every programming language has its own separate package management system, and | |
# I often use these to install packages at a "global" level on my computer. It's a pain | |
# to keep all of those up to date, because they all require different commands to update | |
# them. | |
# | |
# So, I made this script, and I periodically run it manually. | |
# | |
# SECURITY: Most package management systems allow a package to execute arbitrary code as part | |
# of the installation and upgrade process. This is inherently insecure, whether you're running | |
# the upgrade with this script or without it. Most package management systems allow you to | |
# reduce that risk by installing "global" packages in a directory owned by your own user account, | |
# rather than root. This script is built to support that, and only uses root permissions (via sudo) | |
# to update things that are installed as root. Even so, you can still get harmed by a malicious | |
# package. | |
# | |
# REQUIREMENTS: This script is hand-tailored for my workstation. I've tried to remove any | |
# specific usernames or paths, so it's somewhat generic. But it does not check for the | |
# existence of any of these programs before running them, so it probably will not work | |
# perfectly unless you have all the same things installed that I do. | |
# | |
# COPYRIGHT: Aaron Wells, 2018 | |
# | |
# LICENSE: | |
# This program is free software: you can redistribute it and/or modify | |
# it under the terms of the GNU General Public License as published by | |
# the Free Software Foundation, either version 3 of the License, or | |
# (at your option) any later version. | |
# | |
# This program is distributed in the hope that it will be useful, | |
# but WITHOUT ANY WARRANTY; without even the implied warranty of | |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
# GNU General Public License for more details: | |
# <https://www.gnu.org/licenses/>. | |
####################### | |
# Don't run this as root. :( | |
if [[ $(whoami) == "root" ]] | |
then | |
echo | |
echo "******" | |
echo "ERROR! Don't run this script as root! When it needs root privileges, it asks for them." | |
echo "******" | |
exit 1 | |
fi | |
echo | |
echo "**********************" | |
echo "SUDO: Update apt packages..." | |
echo "**********************" | |
sudo apt update -qq | |
sudo apt upgrade -q -y | |
sudo apt autoremove -q -y | |
echo | |
echo "*************************" | |
echo "SUDO: Update Rubygems (Ruby)..." | |
echo "*************************" | |
sudo gem update | |
echo | |
echo "*********************************" | |
echo "SUDO: Update system-level R packages..." | |
echo "*********************************" | |
# Go to my home directory first, so that we'll have the normal user libs, | |
# not project-specific packrat libs. | |
pushd ~ > /dev/null | |
# Nearly the same as the script for user-level R packages, except it looks for | |
# libraries owned by root. Since we're not doing "sudo -H", it'll use my home | |
# directory, and hence have the same set of library directories as the user-level, | |
# command, so I don't need to install magrittr and remotes as root packages. | |
# | |
# Note that these are generally installed via apt packages, so hopefully apt | |
# will already have updated them! But it's possible that the CRAN version is | |
# ahead of the debian repo. | |
echo 'Checking for outdated packages...' | |
CMD=$(cat <<EOM | |
user <- 'root'; | |
rootLibPaths <- .libPaths() %>% | |
file.info() %>% | |
use_series("uname") %>% | |
equals(user) %>% | |
extract(.libPaths(), .); | |
.libPaths(rootLibPaths); | |
rootPkgs <- installed.packages() %>% | |
rownames(); | |
pkgsToUpdate <- remotes::package_deps(rootPkgs); | |
if (any(pkgsToUpdate\$diff < 0)) { | |
print(pkgsToUpdate[pkgsToUpdate\$diff < 0, ]); | |
remotes::update_packages(rootPkgs); | |
} else { | |
cat('No packages to update for user ', user, '\\n') | |
} | |
EOM | |
) | |
CMD=$(echo $CMD | cat) | |
sudo -- Rscript --default-packages="utils,magrittr,remotes" -e "${CMD}" | |
popd > /dev/null | |
echo | |
echo "******************************" | |
echo "SUDO: Update system-installed NPM..." | |
echo "******************************" | |
source ~/.nvm/nvm.sh | |
currentNodeVersion=$(node --version) | |
echo "Currently on Node $currentNodeVersion. Switching via nvm to system node..." | |
if nvm use system | |
then | |
sudo npm install -g npm@latest | |
fi | |
if [[ $(ls /usr/lib/node_modules/ | wc -l) -gt 1 ]] | |
then | |
echo "PROBLEM: You've got some global NPM packages installed in the " | |
echo "(root-owned system directory! You should probably delete those.)" | |
ls /usr/lib/node_modules | grep -v '^npm$' | sort | |
fi | |
echo "Done! Switching back to Node $currentNodeVersion." | |
nvm use --silent $currentNodeVersion | |
nvm unload | |
echo | |
echo "********************" | |
echo "Relinquishing SUDO privileges..." | |
echo "********************" | |
sudo -k | |
echo | |
echo "****************************************" | |
echo "Update composer (PHP) global packages..." | |
echo "****************************************" | |
composer global update | |
composer selfupdate | |
echo | |
echo "*******************************" | |
echo "Update pip (Python) packages..." | |
echo "*******************************" | |
# Go to home directory to avoid project-specific python venv stuff | |
pushd ~ > /dev/null | |
if which pip | |
then | |
pip list --user --outdated --format=columns | \ | |
tail -n "+3" | \ | |
cut -s -d " " -f 1 | \ | |
sed -e 's/^\|$/"/g' | \ | |
xargs -r pip install --user -U | |
pip check | |
else | |
echo "No pip packages on this system! :)" | |
fi | |
popd > /dev/null | |
echo | |
echo "*******************************" | |
echo "Update user-level R packages..." | |
echo "*******************************" | |
# Go to my home directory first, so that we'll have the normal user libs, | |
# not project-specific packrat libs. | |
pushd ~ > /dev/null | |
# It gets the current set of libraries, then it finds out which Unix user | |
# owns each one. It extracts only those libraries owned by me. Then it finds | |
# all the installed packages in those libraries. Then it checks if any of those | |
# need to be updated. | |
echo 'Checking for outdated packages...' | |
CMD=$(cat <<EOM | |
user <- '$(whoami)'; | |
pkgs <- .libPaths() %>% | |
file.info() %>% | |
use_series("uname") %>% | |
equals(user) %>% | |
extract(.libPaths(), .) %>% | |
installed.packages() %>% | |
rownames(); | |
pkgsToUpdate <- remotes::package_deps(pkgs); | |
if (any(pkgsToUpdate\$diff < 0)) { | |
print(pkgsToUpdate[pkgsToUpdate\$diff < 0, ]); | |
remotes::update_packages(pkgs); | |
} else { | |
cat('No packages to update for user ', user, '\\n') | |
} | |
EOM | |
) | |
CMD=$(echo $CMD | cat) | |
Rscript --default-packages="utils,magrittr,remotes" -e "${CMD}" | |
popd > /dev/null | |
echo | |
echo "*******************************************" | |
echo "Update Node and NPM (JS) global packages..." | |
echo "*******************************************" | |
source ~/.nvm/nvm.sh | |
currentNodeVersion=$(node --version) | |
echo "Currently on Node $currentNodeVersion. Switching via nvm..." | |
for nodeversion in $(nvm ls | grep -vF N/A | grep -oP 'v[0-9]+\.[0-9]+' | grep -oP '[0-9]+\.[0-9]+' | cut -d '.' -f 1 | sort | uniq) | |
do | |
localversion=$(nvm ls $nodeversion | tail -n 1 | grep -oP 'v[0-9]+\.[0-9]+\.[0-9]+') | |
remoteversion=$(nvm ls-remote $nodeversion | tail -n 1 | grep -oP 'v[0-9]+\.[0-9]+\.[0-9]+') | |
if [[ "$localversion" == "$remoteversion" ]] | |
then | |
echo " - Node $nodeversion: up-to-date at $localversion" | |
else | |
echo " - Node $nodeversion: upgrade from $localversion to $remoteversion" | |
nvm install $nodeversion --reinstall-packages-from=$localversion | |
nvm uninstall $localversion | |
if [[ "$localversion" == "$currentNodeVersion" ]] | |
then | |
# Can't switch back to our "current" version anymore because we've uninstalled it! | |
# So switch back to the upgraded version at the end, instead. | |
currentNodeVersion=$remoteversion | |
fi | |
fi | |
nvm use --silent $nodeversion | |
if [[ $(npm -g outdated --parseable npm | wc -c ) -eq 0 ]] | |
then | |
echo " -- and npm up-to-date at $(npm --version)" | |
else | |
echo " -- and updating npm..." | |
nvm install-latest-npm | |
fi | |
echo " -- Checking global packages for Node $nodeversion..." | |
# You can't do "npm upgrade" or "npm install" to check for updates to global-installed | |
# npm packages, because those rely on having a project package.json file with version | |
# specifiers. | |
# | |
# So instead I use the "npm-check-updates" utility, which can check that each global | |
# package is at its latest version in the npm repo. | |
# | |
# But first, npm-check-updates must *itself* be installed as a global package, in the | |
# current node version. | |
if [[ -x $(npm -g bin)/npm-check-updates ]]; | |
then | |
echo " --- Installing npm-check-updates..." | |
npm -g install npm-check-updates | |
fi | |
# Now, check for updates to global packages. | |
for package in $($(npm -g bin)/npm-check-updates -g 2> /dev/null | grep -oP '^ [^ ]+ ') | |
do | |
echo " --- Upgrading package: $package" | |
npm install -g $package | |
done | |
done | |
echo "Done! Switching back to Node $currentNodeVersion." | |
nvm use --silent $currentNodeVersion | |
nvm unload | |
#echo | |
#echo "***************************" | |
#echo "Update Android SDK packages" | |
#echo "***************************" | |
#sdkmanager --update | |
echo | |
echo "***********************************" | |
echo "Check for outdated vagrant boxes..." | |
echo "***********************************" | |
vagrant box outdated --global | |
echo | |
echo "***********************************" | |
echo "Check whether reboot is required..." | |
echo "***********************************" | |
if [ -f /var/run/reboot-required ]; then | |
echo "REBOOT REQUIRED! :)" | |
else | |
echo "No reboot required today." | |
fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment