11#! /bin/bash
22LIBRARY_NAME=` grep -m 1 name pyproject.toml | awk -F" = " ' {print substr($2,2,length($2)-2)}' `
3- CONFIG=/boot/config.txt
3+ CONFIG_FILE=config.txt
4+ CONFIG_DIR=" /boot/firmware"
45DATESTAMP=` date " +%Y-%m-%d-%H-%M-%S" `
56CONFIG_BACKUP=false
67APT_HAS_UPDATED=false
7- RESOURCES_TOP_DIR=$HOME /Pimoroni
8- VENV_BASH_SNIPPET=$RESOURCES_DIR /auto_venv.sh
9- VENV_DIR=$HOME /.virtualenvs/pimoroni
8+ RESOURCES_TOP_DIR=" $HOME /Pimoroni"
9+ VENV_BASH_SNIPPET=" $RESOURCES_TOP_DIR /auto_venv.sh"
10+ VENV_DIR=" $HOME /.virtualenvs/pimoroni"
1011WD=` pwd`
1112USAGE=" ./install.sh (--unstable)"
1213POSITIONAL_ARGS=()
1314FORCE=false
1415UNSTABLE=false
1516PYTHON=" python"
17+ CMD_ERRORS=false
1618
1719
1820user_check () {
1921 if [ $( id -u) -eq 0 ]; then
20- printf " Script should not be run as root. Try './install.sh'\n"
21- exit 1
22+ fatal " Script should not be run as root. Try './install.sh'\n"
2223 fi
2324}
2425
@@ -53,13 +54,36 @@ inform() {
5354}
5455
5556warning () {
56- echo -e " $( tput setaf 1) $1 $( tput sgr0) "
57+ echo -e " $( tput setaf 1) ⚠ WARNING:$( tput sgr0) $1 "
58+ }
59+
60+ fatal () {
61+ echo -e " $( tput setaf 1) ⚠ FATAL:$( tput sgr0) $1 "
62+ exit 1
63+ }
64+
65+ find_config () {
66+ if [ ! -f " $CONFIG_DIR /$CONFIG_FILE " ]; then
67+ CONFIG_DIR=" /boot"
68+ if [ ! -f " $CONFIG_DIR /$CONFIG_FILE " ]; then
69+ fatal " Could not find $CONFIG_FILE !"
70+ fi
71+ else
72+ if [ -f " /boot/$CONFIG_FILE " ] && [ ! -L " /boot/$CONFIG_FILE " ]; then
73+ warning " Oops! It looks like /boot/$CONFIG_FILE is not a link to $CONFIG_DIR /$CONFIG_FILE "
74+ warning " You might want to fix this!"
75+ fi
76+ fi
77+ inform " Using $CONFIG_FILE in $CONFIG_DIR "
5778}
5879
5980venv_bash_snippet () {
81+ inform " Checking for $VENV_BASH_SNIPPET \n"
6082 if [ ! -f $VENV_BASH_SNIPPET ]; then
83+ inform " Creating $VENV_BASH_SNIPPET \n"
84+ mkdir -p $RESOURCES_TOP_DIR
6185 cat << EOF > $VENV_BASH_SNIPPET
62- # Add ` source $RESOURCES_DIR /auto_venv.sh ` to your ~/.bashrc to activate
86+ # Add " source $VENV_BASH_SNIPPET " to your ~/.bashrc to activate
6387# the Pimoroni virtual environment automagically!
6488VENV_DIR="$VENV_DIR "
6589if [ ! -f \$ VENV_DIR/bin/activate ]; then
@@ -77,42 +101,52 @@ venv_check() {
77101 PYTHON_BIN=` which $PYTHON `
78102 if [[ $VIRTUAL_ENV == " " ]] || [[ $PYTHON_BIN != $VIRTUAL_ENV * ]]; then
79103 printf " This script should be run in a virtual Python environment.\n"
80- if confirm " Would you like us to create one for you?" ; then
104+ if confirm " Would you like us to create and/or use a default one?" ; then
105+ printf " \n"
81106 if [ ! -f $VENV_DIR /bin/activate ]; then
82- inform " Creating virtual Python environment in $VENV_DIR , please wait...\n"
107+ inform " Creating a new virtual Python environment in $VENV_DIR , please wait...\n"
83108 mkdir -p $VENV_DIR
84109 /usr/bin/python3 -m venv $VENV_DIR --system-site-packages
85110 venv_bash_snippet
111+ source $VENV_DIR /bin/activate
86112 else
87- inform " Found existing virtual Python environment in $VENV_DIR \n"
113+ inform " Activating existing virtual Python environment in $VENV_DIR \n"
114+ printf " source $VENV_DIR /bin/activate\n"
115+ source $VENV_DIR /bin/activate
88116 fi
89- inform " Activating virtual Python environment in $VENV_DIR ..."
90- inform " source $VENV_DIR /bin/activate\n"
91- source $VENV_DIR /bin/activate
92-
93117 else
94- exit 1
118+ printf " \n"
119+ fatal " Please create and/or activate a virtual Python environment and try again!\n"
95120 fi
96121 fi
122+ printf " \n"
123+ }
124+
125+ check_for_error () {
126+ if [ $? -ne 0 ]; then
127+ CMD_ERRORS=true
128+ warning " ^^^ 😬"
129+ fi
97130}
98131
99132function do_config_backup {
100133 if [ ! $CONFIG_BACKUP == true ]; then
101134 CONFIG_BACKUP=true
102135 FILENAME=" config.preinstall-$LIBRARY_NAME -$DATESTAMP .txt"
103- inform " Backing up $CONFIG to /boot /$FILENAME \n"
104- sudo cp $CONFIG /boot /$FILENAME
136+ inform " Backing up $CONFIG_DIR / $CONFIG_FILE to $CONFIG_DIR /$FILENAME \n"
137+ sudo cp $CONFIG_DIR / $CONFIG_FILE $CONFIG_DIR /$FILENAME
105138 mkdir -p $RESOURCES_TOP_DIR /config-backups/
106- cp $CONFIG $RESOURCES_TOP_DIR /config-backups/$FILENAME
139+ cp $CONFIG_DIR / $CONFIG_FILE $RESOURCES_TOP_DIR /config-backups/$FILENAME
107140 if [ -f " $UNINSTALLER " ]; then
108- echo " cp $RESOURCES_TOP_DIR /config-backups/$FILENAME $CONFIG " >> $UNINSTALLER
141+ echo " cp $RESOURCES_TOP_DIR /config-backups/$FILENAME $CONFIG_DIR / $CONFIG_FILE " >> $UNINSTALLER
109142 fi
110143 fi
111144}
112145
113146function apt_pkg_install {
114147 PACKAGES=()
115148 PACKAGES_IN=(" $@ " )
149+ # Check the list of packages and only run update/install if we need to
116150 for (( i = 0 ; i < ${# PACKAGES_IN[@]} ; i++ )) ; do
117151 PACKAGE=" ${PACKAGES_IN[$i]} "
118152 if [ " $PACKAGE " == " " ]; then continue ; fi
@@ -124,20 +158,24 @@ function apt_pkg_install {
124158 done
125159 PACKAGES=" ${PACKAGES[@]} "
126160 if ! [ " $PACKAGES " == " " ]; then
127- echo " Installing missing packages: $PACKAGES "
161+ printf " \n"
162+ inform " Installing missing packages: $PACKAGES "
128163 if [ ! $APT_HAS_UPDATED ]; then
129164 sudo apt update
130165 APT_HAS_UPDATED=true
131166 fi
132167 sudo apt install -y $PACKAGES
168+ check_for_error
133169 if [ -f " $UNINSTALLER " ]; then
134170 echo " apt uninstall -y $PACKAGES " >> $UNINSTALLER
135171 fi
136172 fi
137173}
138174
139175function pip_pkg_install {
176+ # A null Keyring prevents pip stalling in the background
140177 PYTHON_KEYRING_BACKEND=keyring.backends.null.Keyring $PYTHON -m pip install --upgrade " $@ "
178+ check_for_error
141179}
142180
143181while [[ $# -gt 0 ]]; do
@@ -167,30 +205,33 @@ while [[ $# -gt 0 ]]; do
167205 esac
168206done
169207
208+ printf " Installing $LIBRARY_NAME ...\n\n"
209+
170210user_check
171211venv_check
172212
173213if [ ! -f ` which $PYTHON ` ]; then
174- printf " Python path $PYTHON not found!\n"
175- exit 1
214+ fatal " Python path $PYTHON not found!\n"
176215fi
177216
178217PYTHON_VER=` $PYTHON --version`
179218
180- printf " $LIBRARY_NAME Python Library: Installer\n\n"
181-
182219inform " Checking Dependencies. Please wait..."
183220
221+ # Install toml and try to read pyproject.toml into bash variables
222+
184223pip_pkg_install toml
185224
186225CONFIG_VARS=` $PYTHON - << EOF
187226import toml
188227config = toml.load("pyproject.toml")
228+ github_url = config['project']['urls']['GitHub']
189229p = dict(config['tool']['pimoroni'])
190230# Convert list config entries into bash arrays
191231for k, v in p.items():
192232 v = "'\n\t'".join(v)
193233 p[k] = f"('{v}')"
234+ print(f'GITHUB_URL="{github_url}"')
194235print("""
195236APT_PACKAGES={apt_packages}
196237SETUP_CMDS={commands}
@@ -199,8 +240,8 @@ CONFIG_TXT={configtxt}
199240EOF`
200241
201242if [ $? -ne 0 ]; then
202- warning " Error parsing configuration...\n "
203- exit 1
243+ # This is bad, this should not happen in production!
244+ fatal " Error parsing configuration...\n "
204245fi
205246
206247eval $CONFIG_VARS
@@ -210,13 +251,17 @@ UNINSTALLER=$RESOURCES_DIR/uninstall.sh
210251
211252RES_DIR_OWNER=` stat -c "%U" $RESOURCES_TOP_DIR `
212253
254+ # Previous install.sh scripts were run as root with sudo, which caused
255+ # the ~/Pimoroni dir to be created with root ownership. Try and fix it.
213256if [[ " $RES_DIR_OWNER " == " root" ]]; then
214257 warning " \n\nFixing $RESOURCES_TOP_DIR permissions!\n\n"
215258 sudo chown -R $USER :$USER $RESOURCES_TOP_DIR
216259fi
217260
218261mkdir -p $RESOURCES_DIR
219262
263+ # Create a stub uninstaller file, we'll try to add the inverse of every
264+ # install command run to here, though it's not complete.
220265cat << EOF > $UNINSTALLER
221266printf "It's recommended you run these steps manually.\n"
222267printf "If you want to run the full script, open it in\n"
@@ -225,47 +270,64 @@ exit 1
225270source $VIRTUAL_ENV /bin/activate
226271EOF
227272
228- if $UNSTABLE ; then
229- warning " Installing unstable library from source.\n\n"
230- else
231- printf " Installing stable library from pypi.\n\n"
232- fi
273+ printf " \n"
233274
234275inform " Installing for $PYTHON_VER ...\n"
276+
277+ # Install apt packages from pyproject.toml / tool.pimoroni.apt_packages
235278apt_pkg_install " ${APT_PACKAGES[@]} "
279+
280+ printf " \n"
281+
236282if $UNSTABLE ; then
283+ warning " Installing unstable library from source.\n"
237284 pip_pkg_install .
238285else
286+ inform " Installing stable library from pypi.\n"
239287 pip_pkg_install $LIBRARY_NAME
240288fi
289+
241290if [ $? -eq 0 ]; then
242291 success " Done!\n"
243292 echo " $PYTHON -m pip uninstall $LIBRARY_NAME " >> $UNINSTALLER
244293fi
245294
246295cd $WD
247296
297+ find_config
298+
299+ # Run the setup commands from pyproject.toml / tool.pimoroni.commands
300+
248301for (( i = 0 ; i < ${# SETUP_CMDS[@]} ; i++ )) ; do
249302 CMD=" ${SETUP_CMDS[$i]} "
250- # Attempt to catch anything that touches /boot/ config.txt and trigger a backup
251- if [[ " $CMD " == * " raspi-config" * ]] || [[ " $CMD " == * " $CONFIG " * ]] || [[ " $CMD " == * " \$ CONFIG " * ]]; then
303+ # Attempt to catch anything that touches config.txt and trigger a backup
304+ if [[ " $CMD " == * " raspi-config" * ]] || [[ " $CMD " == * " $CONFIG_DIR / $CONFIG_FILE " * ]] || [[ " $CMD " == * " \$ CONFIG_DIR/ \$ CONFIG_FILE " * ]]; then
252305 do_config_backup
253306 fi
254307 eval $CMD
308+ check_for_error
255309done
256310
311+ printf " \n"
312+
313+ # Add the config.txt entries from pyproject.toml / tool.pimoroni.configtxt
314+
257315for (( i = 0 ; i < ${# CONFIG_TXT[@]} ; i++ )) ; do
258316 CONFIG_LINE=" ${CONFIG_TXT[$i]} "
259317 if ! [ " $CONFIG_LINE " == " " ]; then
260318 do_config_backup
261- inform " Adding $CONFIG_LINE to $CONFIG \n "
262- sudo sed -i " s/^#$CONFIG_LINE /$CONFIG_LINE /" $CONFIG
263- if ! grep -q " ^$CONFIG_LINE " $CONFIG ; then
264- printf " $CONFIG_LINE \n" | sudo tee --append $CONFIG
319+ inform " Adding $CONFIG_LINE to $CONFIG_DIR / $CONFIG_FILE "
320+ sudo sed -i " s/^#$CONFIG_LINE /$CONFIG_LINE /" $CONFIG_DIR / $CONFIG_FILE
321+ if ! grep -q " ^$CONFIG_LINE " $CONFIG_DIR / $CONFIG_FILE ; then
322+ printf " $CONFIG_LINE \n" | sudo tee --append $CONFIG_DIR / $CONFIG_FILE
265323 fi
266324 fi
267325done
268326
327+ printf " \n"
328+
329+ # Just a straight copy of the examples/ dir into ~/Pimoroni/board/examples
330+
269331if [ -d " examples" ]; then
270332 if confirm " Would you like to copy examples to $RESOURCES_DIR ?" ; then
271333 inform " Copying examples to $RESOURCES_DIR "
277339
278340printf " \n"
279341
342+ # Use pdoc to generate basic documentation from the installed module
343+
280344if confirm " Would you like to generate documentation?" ; then
345+ inform " Installing pdoc. Please wait..."
281346 pip_pkg_install pdoc
282- printf " Generating documentation.\n"
347+ inform " Generating documentation.\n"
283348 $PYTHON -m pdoc $LIBRARY_NAME -o $RESOURCES_DIR /docs > /dev/null
284349 if [ $? -eq 0 ]; then
285350 inform " Documentation saved to $RESOURCES_DIR /docs"
@@ -289,6 +354,22 @@ if confirm "Would you like to generate documentation?"; then
289354 fi
290355fi
291356
292- success " \nAll done!"
293- inform " If this is your first time installing you should reboot for hardware changes to take effect.\n"
294- inform " Find uninstall steps in $UNINSTALLER \n"
357+ printf " \n"
358+
359+ if [ " $CMD_ERRORS " = true ]; then
360+ warning " One or more setup commands appear to have failed."
361+ printf " This might prevent things from working properly.\n"
362+ printf " Make sure your OS is up to date and try re-running this installer.\n"
363+ printf " If things still don't work, report this or find help at $GITHUB_URL .\n\n"
364+ else
365+ success " \nAll done!"
366+ fi
367+
368+ printf " If this is your first time installing you should reboot for hardware changes to take effect.\n"
369+ printf " Find uninstall steps in $UNINSTALLER \n\n"
370+
371+ if [ " $CMD_ERRORS " = true ]; then
372+ exit 1
373+ else
374+ exit 0
375+ fi
0 commit comments