Skip to content

Instantly share code, notes, and snippets.

@LevZaplatin
Forked from chrismaes87/create-relative-virtualenv
Last active December 1, 2019 17:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save LevZaplatin/82bead581e37e7d02bcf5fb3fb1124f8 to your computer and use it in GitHub Desktop.
Save LevZaplatin/82bead581e37e7d02bcf5fb3fb1124f8 to your computer and use it in GitHub Desktop.
bash script to create a relative virtualenv that can be packaged in an rpm
#!/usr/bin/env 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