-
Notifications
You must be signed in to change notification settings - Fork 10
/
venvpatch
executable file
·151 lines (138 loc) · 4.84 KB
/
venvpatch
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
#!/bin/sh
# venvpatch (part of ossobv/vcutil) // wdoekes/2014-2015 // Public Domain
#
# Automatically applies one or more patches to the current python environment.
#
# Use this to apply a set of patches to a python virtualenv and optionally the
# system packages. Group the patches you want to apply in a single directory
# and venvpatch will apply all unapplied ones at once. Specify --apply to
# actually do it.
#
# Usage:
#
# venvpatch PATH_WITH_PATCH_FILES [--apply]
# venvpatch SINGLE_PATCH_FILE [--apply]
#
# Common python project deployment workflow goes like this:
#
# - Create a virtual environment:
# $ mkvirtualenv PROJECT
# - Install the requirements:
# $ pip install -r requirements.txt
# - Apply a set of patches to the requirements:
# $ cdsitepackages
# $ patch -p1 < /path/to/project/patches/somepatch.patch
# $ patch -p0 < /path/to/project/patches/anotherpatch.patch
#
# The venvpatch utility will take care of:
#
# - Applying multiple patch files at once.
# - Checking whether a patch is applied already, and ignoring it otherwise.
# - Autodetecting -p0 and -p1.
# - Autodetecting the destination path, be it in the virtualenv site-packages
# or in the system-global-packages.
#
# In the following example, the first patch has been applied already:
#
# # ls ./docs/patches/1.4/
# issue16211+14029-query-expression-extra-operators-1.4.patch
# pisa3-reportlab-version.patch
#
# # venvpatch ./docs/patches/1.4/
# source = /srv/django-projects/test/docs/patches/1.4/
# [X] issue16211+14029-query-expression-extra-operators-1.4.patch => \
# /srv/virtualenvs/test/lib/python2.7/site-packages
# [ ] pisa3-reportlab-version.patch => /usr/lib/pymodules/python2.7
#
# # venvpatch ./docs/patches/1.4/ --apply
# source = /srv/django-projects/test/docs/patches/1.4/
# [X] issue16211+14029-query-expression-extra-operators-1.4.patch => \
# /srv/virtualenvs/test/lib/python2.7/site-packages
# [N] pisa3-reportlab-version.patch => /usr/lib/pymodules/python2.7
# patching file sx/pisa3/pisa_util.py
#
# A re-run of venvpatch would now show [X] next to both patches. In case of
# errors, an [E] would be shown.
#
patchpath="$1"
test -z "$patchpath" &&
echo "venvpatch: Need SOURCE_PATCH_PATH as first argument" >&2 &&
exit 1
apply="$2"
# Init globals.
local_packages=$(
python -c 'import distutils;print(distutils.sysconfig.get_python_lib())' \
2>/dev/null)
other_packages=$(python -c 'import sys; print(" ".join(sys.path))')
cwd=$(pwd)/
patchpath=$(echo "$patchpath" | sed -e 's#^\([^/]\)#'$cwd'\1#')
# Fetch patch arguments.
patchver=$(patch --version | sed -e '1!d;s/.* //')
if test $(printf '%s\n2.7\n' $patchver | sort -V | head -n1) = 2.7; then
patchargs="--follow-symlinks --forward"
else
patchargs="--forward"
fi
# Echo source path so manual intervention is eased.
if test -f "$patchpath"; then
echo "source = $(dirname "$patchpath")"
else
echo "source = $patchpath"
fi
# Collect possible patches, and loop over them:
ret=0
find "$patchpath" -maxdepth 1 -type f -name '*.patch' | sort |
while read patch; do
patch_basename=$(basename "$patch")
# Get the first file to be patched.
path1=$(grep ^+++ "$patch" | head -n1 | awk '{print $2}')
# Does it begin with a/ or b/ ? Then we should check the second path
# element instead.
# NOTE: $dir may turn out to be a plain file.
if echo "$path1" | grep -q '^[ab]/'; then
dir=$(echo "$path1" | sed -e 's#^[ab]/\([^/]*\)/.*#\1#')
level=1
else
dir=$(echo "$path1" | sed -e 's#^\([^/]*\)/.*#\1#')
level=0
fi
# Where do we patch? In VIRTUAL_ENV or in global?
dest=
for path in $local_packages $other_packages; do
if test -d "$path/$dir" || test -r "$path/$dir"; then
dest="$path"
break
fi
done
if test -z "$dest"; then
cat >&2 <<EOF
venvpatch: No sensible patch location found for '$dir' in $patch
venvpatch: Forgot to enable virtualenv?
EOF
exit 1
fi
# Very well. Let's see what a dry-run does. And if --apply is
# passed as arg2, let's rock.
cd "$dest"
output=$(patch -p$level -i$patch --dry-run $patchargs \
</dev/null 2>&1)
if test $? -eq 0; then
if test "$apply" = "--apply"; then
echo " [N] $patch_basename => $dest"
patch -p$level -i$patch $patchargs || exit 1
else
echo " [ ] $patch_basename => $dest"
fi
elif echo "$output" | grep -q ^Reversed; then
echo " [X] $patch_basename => $dest"
else
echo " [E] $patch_basename => $dest"
echo " Output: "
echo "$output" | sed -e 's/^/ /'
echo " Permission issue? Try:"
echo " sudo env PATH=\$PATH $0 $patch"
ret=1
fi
test $ret = 0 # set status code
done
# vim: set ts=8 sw=4 sts=4 et ai tw=79: