|
new file 100755
|
|
|
#!/bin/bash
|
|
|
#
|
|
|
# pip_check_requirements_upgrades.sh
|
|
|
#
|
|
|
# Copyright (C) 2017, Branko Majic <branko@majic.rs>
|
|
|
#
|
|
|
# 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.
|
|
|
#
|
|
|
# You should have received a copy of the GNU General Public License
|
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
#
|
|
|
|
|
|
program="pip_check_requirements_upgrades.sh"
|
|
|
|
|
|
function usage() {
|
|
|
cat <<EOF
|
|
|
$program, a non-interactive utility for checking for available upgrades in
|
|
|
Python pip requirements files based on pip-tools
|
|
|
|
|
|
Usage: $program [OPTIONS] configuration_directory
|
|
|
|
|
|
$program a non-interactive utility for checking for available upgrades in Python
|
|
|
pip requirements files based on pip-tools. Utility is written specifically with
|
|
|
pip-tools in mind, and pip-tools must be available when running the utility.
|
|
|
|
|
|
If you are unfamiliar with pip-tools, please read its documentation first, and
|
|
|
make sure you fully understand how it works.
|
|
|
|
|
|
Utility accepts a single positionl argument - path to configuration
|
|
|
directory. Configuration directory should contain one or more sub-directories
|
|
|
where each sub-directory is treated as describing a separate Python virtual
|
|
|
environment. This allows the checks to be run for multiple virtual environments
|
|
|
in a modular manner.
|
|
|
|
|
|
Each sub-directory should contain one or more .in files with corresponding .txt
|
|
|
file. Base names must match (i.e. if you have production.in, the requirements
|
|
|
file must be called production.txt).
|
|
|
|
|
|
Utility iterates over each .in/.txt pair, calculates new requirements based on
|
|
|
the .in file, and diffs this against existing .txt file.
|
|
|
|
|
|
Utility creates copy of existing requirements file, stripping it from all
|
|
|
comments, then calculates new requirements file (storing result in temporary
|
|
|
location as well), and runs a diff between them.
|
|
|
|
|
|
If newer pacakges are available that would satisfiy the provided .in file, a
|
|
|
diff is shown on standard output in addition to a warning message.
|
|
|
|
|
|
$program accepts the following options:
|
|
|
|
|
|
-V virtualenv
|
|
|
Path to virtual environment with pre-installed pip-tools. If specified,
|
|
|
the virtual environment will be activated prior to running the utility.
|
|
|
|
|
|
-q
|
|
|
Quiet mode. Output a message only if newer packages are available.
|
|
|
-d
|
|
|
Enable debug mode.
|
|
|
-v
|
|
|
Show script licensing information.
|
|
|
-h
|
|
|
Show usage help.
|
|
|
|
|
|
|
|
|
Please report bugs and send feature requests to <branko@majic.rs>.
|
|
|
EOF
|
|
|
}
|
|
|
|
|
|
function version() {
|
|
|
cat <<EOF
|
|
|
$program
|
|
|
|
|
|
+-----------------------------------------------------------------------+
|
|
|
| Copyright (C) 2017, Branko Majic <branko@majic.rs> |
|
|
|
| |
|
|
|
| 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. |
|
|
|
| |
|
|
|
| You should have received a copy of the GNU General Public License |
|
|
|
| along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
|
|
+-----------------------------------------------------------------------+
|
|
|
|
|
|
EOF
|
|
|
}
|
|
|
|
|
|
# Set-up colours for message printing if we're not piping and terminal is
|
|
|
# capable of outputting the colors.
|
|
|
_color_terminal=$(tput colors 2>&1)
|
|
|
if [[ -t 1 ]] && (( ${_color_terminal} > 0 )); then
|
|
|
_text_bold=$(tput bold)
|
|
|
_text_white=$(tput setaf 7)
|
|
|
_text_blue=$(tput setaf 6)
|
|
|
_text_green=$(tput setaf 2)
|
|
|
_text_yellow=$(tput setaf 3)
|
|
|
_text_red=$(tput setaf 1)
|
|
|
_text_reset=$(tput sgr0)
|
|
|
else
|
|
|
_text_bold=""
|
|
|
_text_white=""
|
|
|
_text_blue=""
|
|
|
_text_green=""
|
|
|
_text_yellow=""
|
|
|
_text_red=""
|
|
|
_text_reset=""
|
|
|
fi
|
|
|
|
|
|
# Set-up functions for printing coloured messages.
|
|
|
function debug() {
|
|
|
if [[ $debug != 0 ]]; then
|
|
|
echo "${_text_bold}${_text_blue}[DEBUG]${_text_reset}" "$@"
|
|
|
fi
|
|
|
}
|
|
|
|
|
|
function info() {
|
|
|
echo "${_text_bold}${_text_white}[INFO] ${_text_reset}" "$@"
|
|
|
}
|
|
|
|
|
|
function success() {
|
|
|
echo "${_text_bold}${_text_green}[OK] ${_text_reset}" "$@"
|
|
|
}
|
|
|
|
|
|
function warning() {
|
|
|
echo "${_text_bold}${_text_yellow}[WARN] ${_text_reset}" "$@"
|
|
|
}
|
|
|
|
|
|
function error() {
|
|
|
echo "${_text_bold}${_text_red}[ERROR]${_text_reset}" "$@" >&2
|
|
|
}
|
|
|
|
|
|
# Clean-up command for temporary files.
|
|
|
function on_exit() {
|
|
|
debug "Cleaning-up temporary file: $tmp_current"
|
|
|
[[ -f $tmp_current ]] && rm "$tmp_current"
|
|
|
|
|
|
debug "Cleaning-up temporayr file: $tmp_new"
|
|
|
[[ -f $tmp_new ]] && rm "$tmp_new"
|
|
|
}
|
|
|
trap on_exit EXIT
|
|
|
|
|
|
# Define error codes.
|
|
|
SUCCESS=0
|
|
|
ERROR_ARGUMENTS=1
|
|
|
ERROR_CONFIG_DIR=2
|
|
|
ERROR_PIP_TOOLS_MISSING=3
|
|
|
|
|
|
# Disable debug and quiet modes by default.
|
|
|
debug=0
|
|
|
quiet=0
|
|
|
|
|
|
# Set-up defaults.
|
|
|
virtualenv=""
|
|
|
|
|
|
# If no arguments were given, just show usage help.
|
|
|
if [[ -z $1 ]]; then
|
|
|
usage
|
|
|
exit "$SUCCESS"
|
|
|
fi
|
|
|
|
|
|
# Parse the arguments
|
|
|
while getopts "V:qdvh" opt; do
|
|
|
case "$opt" in
|
|
|
V) virtualenv="$OPTARG";;
|
|
|
q) quiet=1;;
|
|
|
d) debug=1;;
|
|
|
v) version
|
|
|
exit "$SUCCESS";;
|
|
|
h) usage
|
|
|
exit "$SUCCESS";;
|
|
|
*) usage
|
|
|
exit "$ERROR_ARGUMENTS";;
|
|
|
esac
|
|
|
done
|
|
|
i=$OPTIND
|
|
|
shift $(($i-1))
|
|
|
|
|
|
|
|
|
if [[ ${#@} == 0 ]]; then
|
|
|
error "Must pass configuration directory."
|
|
|
usage
|
|
|
exit "$ERROR_ARGUMENTS"
|
|
|
fi
|
|
|
|
|
|
# Verfiy positional arguments.
|
|
|
config_dir="$1"
|
|
|
if [[ ! -d $config_dir ]]; then
|
|
|
error "No such directory: $config_dir"
|
|
|
exit "$ERROR_ARGUMENTS"
|
|
|
fi
|
|
|
|
|
|
# Make sure virtual environment exists.
|
|
|
if [[ -n $virtualenv && ! -f $virtualenv/bin/activate ]]; then
|
|
|
error "Invalid virtual environment specified: $virtualenv"
|
|
|
exit "$ERROR_ARGUMENTS"
|
|
|
fi
|
|
|
|
|
|
# Activate the virtual environment if it was specified.
|
|
|
[[ -n $virtualenv ]] && source "$virtualenv/bin/activate"
|
|
|
|
|
|
# Verify pip-compile is available.
|
|
|
if ! which pip-compile >/dev/null 2>&1; then
|
|
|
error "Could not find command pip-compile from packagep pip-tools. Package might not be available in the virtual environment."
|
|
|
exit "$ERROR_PIP_TOOLS_MISSING"
|
|
|
fi
|
|
|
|
|
|
# Create temporary files where files where "normalised" outputs will be
|
|
|
# stored.
|
|
|
tmp_current=$(mktemp)
|
|
|
tmp_new=$(mktemp)
|
|
|
|
|
|
# Process each environment.
|
|
|
for environment in "$config_dir"/*; do
|
|
|
# Empty directory.
|
|
|
if [[ ! -e $environment ]]; then
|
|
|
error "Configuration directory is empty: $config_dir"
|
|
|
exit "$ERROR_CONFIG_DIR"
|
|
|
fi
|
|
|
|
|
|
# Process each .in file.
|
|
|
for req_in in "$environment"/*.in; do
|
|
|
# Directory without .in files.
|
|
|
if [[ ! -e $req_in ]]; then
|
|
|
error "No .in files in directory, skipping: $environment"
|
|
|
continue
|
|
|
fi
|
|
|
|
|
|
# Figure out if .txt file exists.
|
|
|
req_txt="${req_in%.in}.txt"
|
|
|
if [[ ! -f $req_txt ]]; then
|
|
|
warning "Missing .txt file for: $req_in"
|
|
|
continue
|
|
|
fi
|
|
|
|
|
|
# Deploy the existing requirements file and the new one.
|
|
|
sed -e 's/[[:blank:]]*#.*//' "$req_txt" | grep -v "^$" | sort -u > "$tmp_current"
|
|
|
if ! pip-compile --no-header --no-annotate --no-index --output-file "$tmp_new" "$req_in" > /dev/null; then
|
|
|
error "Failed while running pip-compile command against (see error stack trace above): $req_in"
|
|
|
continue
|
|
|
fi
|
|
|
|
|
|
# Run diff, storing the output and result.
|
|
|
diff=$(diff -u "$tmp_current" "$tmp_new")
|
|
|
result="$?"
|
|
|
|
|
|
# Show warning about available updates.
|
|
|
if [[ $result == 0 ]]; then
|
|
|
[[ $quiet == 0 ]] && info "No upgrades available for: $req_txt"
|
|
|
else
|
|
|
warning "Upgrades available for: $req_txt"
|
|
|
echo
|
|
|
echo "$diff"
|
|
|
echo
|
|
|
fi
|
|
|
done
|
|
|
done
|