Created
September 25, 2019 06:57
-
-
Save chrismaes87/698c8d8e3eaf7e9ba924bd68b64f1c3c to your computer and use it in GitHub Desktop.
bash script to create a relative virtualenv that can be packaged in an rpm
This file contains hidden or 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 | |
me=$(basename $0) | |
set -e # exit on error | |
function print_help { | |
echo | |
echo $me - create a virtualenv with relative paths in a certain buildroot and fix the relative path to make it work after install | |
echo | |
echo OPTIONS | |
echo " -h / --help" | |
echo " show this help summary" | |
echo " -p / --path" | |
echo " the path where the virtualenv should be installed" | |
echo " -r / --requirements" | |
echo " requirements.txt or Pipfile (Pipfile.lock will be used)" | |
echo " DEFAULT=requirements.txt" | |
echo " -v / --version" | |
echo " pip version number to use (2 by default)" | |
echo | |
echo "EXAMPLE" | |
echo | |
echo "$me -p \$RPM_BUILD_ROOT/usr/lib/application" | |
echo "$me -p \$RPM_BUILD_ROOT/usr/lib/application -r requirements.txt" | |
exit | |
} | |
TEMP=$(getopt -o hp:r:v: --long help,path:,requirements:,version: -- "$@") | |
eval set -- "$TEMP" | |
VENVPATH= | |
REQUIREMENTS=requirements.txt | |
VERSION=2 | |
while true | |
do | |
case "$1" in | |
-h | --help ) print_help ; exit 0 ;; | |
-p | --path ) VENVPATH="$2"; shift 2;; | |
-r | --requirements ) REQUIREMENTS="$2"; shift 2;; | |
-v | --version ) VERSION="$2"; shift 2;; | |
-- ) if [ -n "$2" ] | |
then | |
shift | |
echo "Unexpected options: \"$@\" . exiting." | |
exit 1; | |
fi | |
shift; break;; | |
* ) break ;; | |
esac | |
done | |
# check if we have the correct variables set | |
[ -z "$VENVPATH" ] && echo "ERROR: you need to specify the virtualenv path" && exit 1 | |
[ -z "$REQUIREMENTS" ] && echo "ERROR: you need to specify the requirements file" && exit 1 | |
if [ -e $VENVPATH/virtualenv ] | |
then | |
echo "removing pre-existing $VENVPATH/virtualenv" | |
rm -rf $VENVPATH/virtualenv | |
fi | |
# Note: Pipenv expects a path to a given Pipfile, thus it is impossible for him to | |
# read a Pipfile.lock, either it will fail because it is expecting a .toml formatted file. | |
if [ "$REQUIREMENTS" == "Pipfile" ] | |
then | |
# Installing pipenv and upgrading setuptools on user level | |
ABSPATH_PIPFILE=$(realpath $REQUIREMENTS) | |
PIPFILE_LOCK="${ABSPATH_PIPFILE}.lock" | |
if [ ! -f $PIPFILE_LOCK ] | |
then | |
echo "ERROR: The given Pipfile has no locked dependencies (Pipfile.lock not found)" | |
exit 1 | |
else | |
python$VERSION -m pip install --user --upgrade setuptools pipenv | |
# Give the path to the pipfile, avoid locale related errors and (most important one) | |
# tell pipenv to create the virtualenvironment inside the project directory, under .venv dir. | |
LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 PIPENV_PIPFILE=$ABSPATH_PIPFILE PIPENV_VENV_IN_PROJECT=1 python$VERSION -m pipenv --python $VERSION sync | |
# rename to virtualenv for backwards compatibility | |
mkdir -p $VENVPATH | |
mv .venv $VENVPATH/virtualenv | |
fi | |
else | |
# Installing virtualenv and upgrading setuptools on user level | |
python$VERSION -m pip install --user --upgrade setuptools virtualenv | |
# Create virtualenv | |
python$VERSION -m virtualenv --python=python$VERSION $VENVPATH/virtualenv | |
# this is to have a distribute virtualenv | |
python$VERSION -m virtualenv --python=python$VERSION --relocatable --distribute $VENVPATH/virtualenv | |
# activate virtualenv to install requirements | |
source $VENVPATH/virtualenv/bin/activate | |
# a first pass to upgrade the setuptools since this caused trouble for some dependencies. | |
# We suppose that upgraded setuptools won't cause any trouble for other packages. | |
$VENVPATH/virtualenv/bin/pip install "setuptools>=24.3" | |
# install the requirements | |
$VENVPATH/virtualenv/bin/pip install -r $REQUIREMENTS | |
fi | |
# for some reason there is a 'local' directory on ubuntu with symbolic links. | |
# this creates problems: upon installation these still point to the temporary build directories | |
# the postinstall scripts where we change file permissions then change file permissions in the temporary build directories which cannot be removed | |
# for the moment let's just remove this local directory. It is not created on other environments anyways | |
rm -rf $VENVPATH/virtualenv/local | |
# remove all pycaches | |
find $VENVPATH/virtualenv/ -name __pycache__ -type d -prune -exec rm -rf {} + | |
# make sure this is a full path to avoid problems when replacing this in virtualenv/bin | |
VENVPATH=$(realpath $VENVPATH) | |
# replace paths in RPM_BUILD_ROOT with the relative paths | |
find $VENVPATH/virtualenv/lib/python$VERSION*/site-packages/ -type f -exec sed -i "s|$VENVPATH/virtualenv/|/../|g" {} \; | |
# seek pyo file and pyc file to delete it | |
find $VENVPATH/virtualenv -name '*.py[co]' -delete | |
# remove pip from the virtualenv | |
for file in pip activate easy_install python-config | |
do | |
rm -f $VENVPATH/virtualenv/bin/$file* | |
done | |
for package in pip easy_install | |
do | |
rm -rf $VENVPATH/virtualenv/lib*/python*/site-packages/$package* | |
done | |
# prepare a script that will be called to run any generated binary with the local python | |
# we do that because we can't just modify the shebang and replace it with relative path | |
# as shebang can't execute relative path | |
# scripts have extension 'script.pyw' because the generated python inside strips this extension (why ?) | |
# and this is nicer when the python script print its own name (for example for help) | |
cat <<END >$VENVPATH/virtualenv/bin/run-with-local-python | |
#!/usr/bin/env bash | |
FILE=\$0-script.pyw | |
BIN=\$(dirname \$0)/python | |
exec \$BIN \$FILE "\$@" | |
END | |
chmod 0755 $VENVPATH/virtualenv/bin/run-with-local-python | |
# replace binaries that have the virtualenv python in their shebang to a symlink to run-with-local-python | |
for binary in $(find $VENVPATH/virtualenv/bin/ -maxdepth 1 -type f -executable ! -name "python*") | |
do | |
if head -n 1 $binary | grep -q -e '^#!.*python*' | |
then | |
tail -n +2 $binary > $binary-script.pyw | |
elif head -n 1 $binary | grep -q -e '^#!/bin/sh$' | |
then | |
# when the filepath is too long, python-virtualenv creates a multiline shebang in this form: | |
# #!/bin/sh | |
# '''exec' /home/chris/rpmbuild/BUILDROOT/application-1.2.3-0/usr/lib/application/virtualenv/bin/python3.6 "$0" "$@" | |
# ' ''' | |
if [ -z "$(head -n 3 $binary | grep -ve '^#!/bin/sh' | grep -ve "^'''" | grep -ve "'''$")" ] | |
then | |
# we made the matching pattern very strict; so normally we shouldn't have any wrong matches... now just remove the top three lines | |
tail -n +4 $binary > $binary-script.pyw | |
fi | |
fi | |
if [ -e $binary-script.pyw ] | |
then | |
chmod 0755 $binary-script.pyw | |
rm $binary | |
ln -s run-with-local-python $binary | |
fi | |
done |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment