-
Notifications
You must be signed in to change notification settings - Fork 16
/
Copy pathupdate_package.sh
executable file
·283 lines (239 loc) · 9.68 KB
/
update_package.sh
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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
#!/usr/bin/env bash
INFO='Rebuild and update the local-source Python package at given path in the implied or specified Python virtual env.
Defaults to all local packages in the event no single directory is provided.'
SCRIPT_PARENT_DIR="$(cd "$(dirname "${0}")"; pwd)"
# Set SHARED_FUNCS_DIR (as needed by default_script_setup.sh) to the correct path before using it to source its contents
SHARED_FUNCS_DIR="${SCRIPT_PARENT_DIR}/shared"
if [ ! -d "${SHARED_FUNCS_DIR}" ]; then
>&2 echo "Error: could not find shared script functions script at expected location:"
>&2 echo " ${SHARED_FUNCS_DIR}"
exit 1
fi
# Import shared default script startup source
. ${SHARED_FUNCS_DIR}/default_script_setup.sh
# Import shared functions used for python-dev-related scripts
. ${SHARED_FUNCS_DIR}/py_dev_func.sh
# Import bash-only shared functions used for python-dev-related scripts
. ${SHARED_FUNCS_DIR}/py_dev_bash_func.sh
# Statically set this for now
_REQ_FILE="${PROJECT_ROOT:?}/requirements.txt"
usage()
{
local _O="${NAME:?}:
${INFO:?}
Usage:
${NAME:?} -h|-help|--help
${NAME:?} [opts] [<directory>]
Options:
--dependencies | -d
Install Python dependency packages defined in the
requirements.txt file in the project root
--dependencies-only | -D
Like the --dependencies option, except only do this
step, without performing any other package building or
updating
--libraries-only | --no-service-packages | -l
Include only library packages when default installing
all local-source packages (ignored if single package
directory is specified)
--print-directories | -pd
Do not install, but rather just print the directories
of the local packages that will be installed (cannot be
run with --dependencies, --dependencies-only, or
--print-packages).
--print-packages | -pp
Print just the names of the packages to be installed,
without actually installing (cannot be run with
--print-directories).
--venv <dir>
Set the directory of the virtual environment to use.
By default, the following directories will be checked,
with the first apparently valid virtual env being used:
- ./venv/
- ./.venv/
- ${SCRIPT_PARENT_DIR:-?}/venv/
- ${SCRIPT_PARENT_DIR:-?}/.venv/
"
echo "${_O}" 2>&1
}
init_package_dirs_array_when_empty()
{
# Only initialize if nothing was provided (i.e., via command line args)
if [ -z ${PACKAGE_DIRS+x} ]; then
# First, get all them, in the separate arrays for lib and service packages.
py_dev_bash_get_package_directories
spi=0
for i in ${LIB_PACKAGE_DIRS[@]}; do
# Include package directory, as long as there is a pyproject.toml for the package
if [ -e "${i}/pyproject.toml" ]; then
PACKAGE_DIRS[${spi}]="${i}"
spi=$((spi+1))
fi
done
# Though check for option indicating only library packages should be installed.
if [ -z "${NO_SERVICE_PACKAGES:-}" ]; then
for i in ${SERVICE_PACKAGE_DIRS[@]}; do
# Include package directory, as long as there is a pyproject.toml for the package
if [ -e "${i}/pyproject.toml" ]; then
PACKAGE_DIRS[${spi}]="${i}"
spi=$((spi+1))
fi
done
fi
fi
[ ${#PACKAGE_DIRS[@]} -le 0 ] && >&2 echo "Error: Invalid package empty directory list." && exit 1
}
print_package_names()
{
init_package_dirs_array_when_empty
for pd in ${PACKAGE_DIRS[@]}; do
cd "${pd}"
py_dev_extract_package_dist_name_from_setup
cd "${STARTING_DIR:?}"
done
if [ -n "${DO_DEPS:-}" ]; then
cat "${_REQ_FILE:?}"
fi
}
print_package_directories()
{
init_package_dirs_array_when_empty
for pd in ${PACKAGE_DIRS[@]}; do
echo "${pd}"
done
}
while [ ${#} -gt 0 ]; do
case "${1}" in
--dependencies|-d)
[ -n "${DO_DEPS:-}" ] && usage && exit 1
DO_DEPS='true'
;;
--dependencies-only|-D)
[ -n "${DO_DEPS:-}" ] && usage && exit 1
# Also, this shouldn't be set, as it assumes we will be building, which conflicts with this option
[ -n "${NO_SERVICE_PACKAGES:-}" ] && usage && exit 1
DO_DEPS='true'
DEPS_ONLY='true'
;;
-h|--help|-help)
usage
exit
;;
--libraries-only|--no-service-packages|-l)
[ -n "${NO_SERVICE_PACKAGES:-}" ] && usage && exit 1
# Also, make sure we aren't marked for dependencies only
[ -n "${DEPS_ONLY:-}" ] && usage && exit 1
NO_SERVICE_PACKAGES='true'
;;
--print-directories|-pd)
[ -n "${DO_PRINT_DIRS:-}" ] && usage && exit 1
[ -n "${DO_PRINT_PACKAGES:-}" ] && usage && exit 1
[ -n "${DO_DEPS:-}" ] && usage && exit 1
DO_PRINT_DIRS='true'
;;
--print-packages|-pp)
[ -n "${DO_PRINT_PACKAGES:-}" ] && usage && exit 1
[ -n "${DO_PRINT_DIRS:-}" ] && usage && exit 1
DO_PRINT_PACKAGES='true'
;;
--venv)
[ -n "${VENV_DIR:-}" ] && usage && exit 1
VENV_DIR="$(py_dev_validate_venv_dir "${2}")"
[ -z "${VENV_DIR:-}" ] && echo "Error: provided arg ${2} is not a valid virtual env directory" && exit 1
shift
;;
*)
# Checks that PACKAGE_DIRS is, in fact, set (if it is, it'll get replaced with x)
[ ${#PACKAGE_DIRS[@]} -gt 0 ] && usage && exit 1
[ ! -d "${1}" ] && >&2 echo "Error: directory arg '${1}' is not an existing directory" && usage && exit 1
PACKAGE_DIRS[0]="${1}"
;;
esac
shift
done
# If a virtual environment isn't already activated, make sure a the variable for a venv directory gets set
if [ -z "${VIRTUAL_ENV:-}" ]; then
# This function will try to detect a valid venv directory from defaults, unless one has already been set
py_dev_detect_default_venv_directory
# Bail if, at this point, a valid venv directory was neither provided nor detected
[ -z "${VENV_DIR:-}" ] && echo "Error: no valid virtual env directory could be determined or was given" && exit 1
fi
# Sanity check the requirements file exists and can be read if it is needed
if [ -n "${DO_DEPS:-}" ]; then
if [ ! -f "${_REQ_FILE:?}" ]; then
>&2 echo "Error: unable to find valid Python requirements file at ${_REQ_FILE}"
exit 1
elif [ ! -r "${_REQ_FILE:?}" ]; then
>&2 echo "Error: Python requirements file '${_REQ_FILE}' is not readable"
exit 1
fi
fi
# If args given to just print, then do just those things
if [ -n "${DO_PRINT_PACKAGES:-}" ]; then
print_package_names
exit ${?}
elif [ -n "${DO_PRINT_DIRS:-}" ]; then
print_package_directories
#exit 0
exit ${?}
fi
# Take appropriate action to activate a virtual environment if needed
py_dev_activate_venv
# Trap to make sure we "clean up" script activity before exiting
trap cleanup_before_exit 0 1 2 3 6 15
# Install build dependencies if not present
py_dev_maybe_install_build_deps
# After setting VENV, if set to get dependencies, do that, optionally exiting after if that's all we are set to do
if [ -n "${DO_DEPS:-}" ]; then
pip install --upgrade -r "${_REQ_FILE}"
# Also, if set to only get dependencies, exit here
[ -n "${DEPS_ONLY:-}" ] && exit
fi
# If unset, meaning no single package directory was specified, assume all packages should be installed.
init_package_dirs_array_when_empty
PACKAGE_DIST_NAMES=()
# The --find-links=.../dist/ arguments needed for the dist/ directories when doing the local pip instal
PACKAGE_DIST_DIR_LINK_ARGS=()
build_package_and_collect_dist_details()
{
if [ ${#} -lt 1 ]; then
>&2 echo "Error: unable to build package without package directory argument"
exit 1
elif [ ${#} -lt 2 ]; then
>&2 echo "Error: unable to build package without starting directory argument"
exit 1
fi
# Go into the package directory, build new dists, and install them
cd "${1}"
# Collect dist names and dist link args as we go.
# Of course, this means we need to figure out the index of the next array values.
# Fortunately, this should just be the current size of the arrays
local _N=${#PACKAGE_DIST_NAMES[@]}
PACKAGE_DIST_NAMES[${_N}]="$(py_dev_extract_package_dist_name_from_setup)"
# Bail if we can't detect the appropriate package dist name
if [ -z "${PACKAGE_DIST_NAMES[${_N}]}" ]; then
>&2 echo "Error: unable to determine package dist name from ${1}/pyproject.toml"
exit 1
fi
# Then add the generated dist directory pip arg value to that array
PACKAGE_DIST_DIR_LINK_ARGS[${_N}]="--find-links=${1}/dist"
# Clean any previous artifacts and build
py_dev_clean_dist && python -m build
# Return to starting directory if one was given
cd "${2}"
}
cd "${STARTING_DIR:?}"
# Build the packages, and build lists/arrays of dist names and '--find-links=' pip arg values as we go
for pd in ${PACKAGE_DIRS[@]}; do
build_package_and_collect_dist_details "${pd}" "${STARTING_DIR:?}"
done
# Uninstall all existing package dists
pip uninstall -y ${PACKAGE_DIST_NAMES[@]}
# Install new dists, using the generated '--find-links=' args so we can find the local copies of build package dists
pip install --upgrade ${PACKAGE_DIST_DIR_LINK_ARGS[@]} ${PACKAGE_DIST_NAMES[@]}
# Finally, clean up all the created build artifacts in the package directories
for pd in ${PACKAGE_DIRS[@]}; do
cd "${pd}"
py_dev_clean_dist
cd "${STARTING_DIR:?}"
done