Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bookworm/Pi5 Compatibility: Upgrade to latest boilerplate #6

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Update packaging to latest boilerplate.
  • Loading branch information
Gadgetoid committed Dec 6, 2023
commit 03e7b832ab30d3ae9269b65016410672a2395fa2
1 change: 1 addition & 0 deletions check.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ NOPOST=$1
LIBRARY_NAME=`hatch project metadata name`
LIBRARY_VERSION=`hatch version | awk -F "." '{print $1"."$2"."$3}'`
POST_VERSION=`hatch version | awk -F "." '{print substr($4,0,length($4))}'`
TERM=${TERM:="xterm-256color"}

success() {
echo -e "$(tput setaf 2)$1$(tput sgr0)"
Expand Down
193 changes: 159 additions & 34 deletions install.sh
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
#!/bin/bash
LIBRARY_NAME=`grep -m 1 name pyproject.toml | awk -F" = " '{print substr($2,2,length($2)-2)}'`
CONFIG=/boot/config.txt
CONFIG_FILE=config.txt
CONFIG_DIR="/boot/firmware"
DATESTAMP=`date "+%Y-%m-%d-%H-%M-%S"`
CONFIG_BACKUP=false
APT_HAS_UPDATED=false
RESOURCES_TOP_DIR=$HOME/Pimoroni
RESOURCES_TOP_DIR="$HOME/Pimoroni"
VENV_BASH_SNIPPET="$RESOURCES_TOP_DIR/auto_venv.sh"
VENV_DIR="$HOME/.virtualenvs/pimoroni"
WD=`pwd`
USAGE="./install.sh (--unstable)"
POSITIONAL_ARGS=()
FORCE=false
UNSTABLE=false
PYTHON="/usr/bin/python3"
PYTHON="python"
CMD_ERRORS=false


user_check() {
if [ $(id -u) -eq 0 ]; then
printf "Script should not be run as root. Try './install.sh'\n"
exit 1
fatal "Script should not be run as root. Try './install.sh'\n"
fi
}

Expand Down Expand Up @@ -51,26 +54,99 @@ inform() {
}

warning() {
echo -e "$(tput setaf 1)$1$(tput sgr0)"
echo -e "$(tput setaf 1)⚠ WARNING:$(tput sgr0) $1"
}

fatal() {
echo -e "$(tput setaf 1)⚠ FATAL:$(tput sgr0) $1"
exit 1
}

find_config() {
if [ ! -f "$CONFIG_DIR/$CONFIG_FILE" ]; then
CONFIG_DIR="/boot"
if [ ! -f "$CONFIG_DIR/$CONFIG_FILE" ]; then
fatal "Could not find $CONFIG_FILE!"
fi
else
if [ -f "/boot/$CONFIG_FILE" ] && [ ! -L "/boot/$CONFIG_FILE" ]; then
warning "Oops! It looks like /boot/$CONFIG_FILE is not a link to $CONFIG_DIR/$CONFIG_FILE"
warning "You might want to fix this!"
fi
fi
inform "Using $CONFIG_FILE in $CONFIG_DIR"
}

venv_bash_snippet() {
inform "Checking for $VENV_BASH_SNIPPET\n"
if [ ! -f $VENV_BASH_SNIPPET ]; then
inform "Creating $VENV_BASH_SNIPPET\n"
mkdir -p $RESOURCES_TOP_DIR
cat << EOF > $VENV_BASH_SNIPPET
# Add "source $VENV_BASH_SNIPPET" to your ~/.bashrc to activate
# the Pimoroni virtual environment automagically!
VENV_DIR="$VENV_DIR"
if [ ! -f \$VENV_DIR/bin/activate ]; then
printf "Creating user Python environment in \$VENV_DIR, please wait...\n"
mkdir -p \$VENV_DIR
python3 -m venv --system-site-packages \$VENV_DIR
fi
printf " ↓ ↓ ↓ ↓ Hello, we've activated a Python venv for you. To exit, type \"deactivate\".\n"
source \$VENV_DIR/bin/activate
EOF
fi
}

venv_check() {
PYTHON_BIN=`which $PYTHON`
if [[ $VIRTUAL_ENV == "" ]] || [[ $PYTHON_BIN != $VIRTUAL_ENV* ]]; then
printf "This script should be run in a virtual Python environment.\n"
if confirm "Would you like us to create and/or use a default one?"; then
printf "\n"
if [ ! -f $VENV_DIR/bin/activate ]; then
inform "Creating a new virtual Python environment in $VENV_DIR, please wait...\n"
mkdir -p $VENV_DIR
/usr/bin/python3 -m venv $VENV_DIR --system-site-packages
venv_bash_snippet
source $VENV_DIR/bin/activate
else
inform "Activating existing virtual Python environment in $VENV_DIR\n"
printf "source $VENV_DIR/bin/activate\n"
source $VENV_DIR/bin/activate
fi
else
printf "\n"
fatal "Please create and/or activate a virtual Python environment and try again!\n"
fi
fi
printf "\n"
}

check_for_error() {
if [ $? -ne 0 ]; then
CMD_ERRORS=true
warning "^^^ 😬"
fi
}

function do_config_backup {
if [ ! $CONFIG_BACKUP == true ]; then
CONFIG_BACKUP=true
FILENAME="config.preinstall-$LIBRARY_NAME-$DATESTAMP.txt"
inform "Backing up $CONFIG to /boot/$FILENAME\n"
sudo cp $CONFIG /boot/$FILENAME
inform "Backing up $CONFIG_DIR/$CONFIG_FILE to $CONFIG_DIR/$FILENAME\n"
sudo cp $CONFIG_DIR/$CONFIG_FILE $CONFIG_DIR/$FILENAME
mkdir -p $RESOURCES_TOP_DIR/config-backups/
cp $CONFIG $RESOURCES_TOP_DIR/config-backups/$FILENAME
cp $CONFIG_DIR/$CONFIG_FILE $RESOURCES_TOP_DIR/config-backups/$FILENAME
if [ -f "$UNINSTALLER" ]; then
echo "cp $RESOURCES_TOP_DIR/config-backups/$FILENAME $CONFIG" >> $UNINSTALLER
echo "cp $RESOURCES_TOP_DIR/config-backups/$FILENAME $CONFIG_DIR/$CONFIG_FILE" >> $UNINSTALLER
fi
fi
}

function apt_pkg_install {
PACKAGES=()
PACKAGES_IN=("$@")
# Check the list of packages and only run update/install if we need to
for ((i = 0; i < ${#PACKAGES_IN[@]}; i++)); do
PACKAGE="${PACKAGES_IN[$i]}"
if [ "$PACKAGE" == "" ]; then continue; fi
Expand All @@ -82,20 +158,24 @@ function apt_pkg_install {
done
PACKAGES="${PACKAGES[@]}"
if ! [ "$PACKAGES" == "" ]; then
echo "Installing missing packages: $PACKAGES"
printf "\n"
inform "Installing missing packages: $PACKAGES"
if [ ! $APT_HAS_UPDATED ]; then
sudo apt update
APT_HAS_UPDATED=true
fi
sudo apt install -y $PACKAGES
check_for_error
if [ -f "$UNINSTALLER" ]; then
echo "apt uninstall -y $PACKAGES" >> $UNINSTALLER
fi
fi
}

function pip_pkg_install {
# A null Keyring prevents pip stalling in the background
PYTHON_KEYRING_BACKEND=keyring.backends.null.Keyring $PYTHON -m pip install --upgrade "$@"
check_for_error
}

while [[ $# -gt 0 ]]; do
Expand Down Expand Up @@ -125,29 +205,33 @@ while [[ $# -gt 0 ]]; do
esac
done

printf "Installing $LIBRARY_NAME...\n\n"

user_check
venv_check

if [ ! -f "$PYTHON" ]; then
printf "Python path $PYTHON not found!\n"
exit 1
if [ ! -f `which $PYTHON` ]; then
fatal "Python path $PYTHON not found!\n"
fi

PYTHON_VER=`$PYTHON --version`

printf "$LIBRARY_NAME Python Library: Installer\n\n"

inform "Checking Dependencies. Please wait..."

# Install toml and try to read pyproject.toml into bash variables

pip_pkg_install toml

CONFIG_VARS=`$PYTHON - <<EOF
import toml
config = toml.load("pyproject.toml")
p = dict(config['pimoroni'])
github_url = config['project']['urls']['GitHub']
p = dict(config['tool']['pimoroni'])
# Convert list config entries into bash arrays
for k, v in p.items():
v = "'\n\t'".join(v)
p[k] = f"('{v}')"
print(f'GITHUB_URL="{github_url}"')
print("""
APT_PACKAGES={apt_packages}
SETUP_CMDS={commands}
Expand All @@ -156,8 +240,8 @@ CONFIG_TXT={configtxt}
EOF`

if [ $? -ne 0 ]; then
warning "Error parsing configuration...\n"
exit 1
# This is bad, this should not happen in production!
fatal "Error parsing configuration...\n"
fi

eval $CONFIG_VARS
Expand All @@ -167,61 +251,83 @@ UNINSTALLER=$RESOURCES_DIR/uninstall.sh

RES_DIR_OWNER=`stat -c "%U" $RESOURCES_TOP_DIR`

# Previous install.sh scripts were run as root with sudo, which caused
# the ~/Pimoroni dir to be created with root ownership. Try and fix it.
if [[ "$RES_DIR_OWNER" == "root" ]]; then
warning "\n\nFixing $RESOURCES_TOP_DIR permissions!\n\n"
sudo chown -R $USER:$USER $RESOURCES_TOP_DIR
fi

mkdir -p $RESOURCES_DIR

# Create a stub uninstaller file, we'll try to add the inverse of every
# install command run to here, though it's not complete.
cat << EOF > $UNINSTALLER
printf "It's recommended you run these steps manually.\n"
printf "If you want to run the full script, open it in\n"
printf "an editor and remove 'exit 1' from below.\n"
exit 1
source $VIRTUAL_ENV/bin/activate
EOF

if $UNSTABLE; then
warning "Installing unstable library from source.\n\n"
else
printf "Installing stable library from pypi.\n\n"
fi
printf "\n"

inform "Installing for $PYTHON_VER...\n"

# Install apt packages from pyproject.toml / tool.pimoroni.apt_packages
apt_pkg_install "${APT_PACKAGES[@]}"

printf "\n"

if $UNSTABLE; then
warning "Installing unstable library from source.\n"
pip_pkg_install .
else
inform "Installing stable library from pypi.\n"
pip_pkg_install $LIBRARY_NAME
fi

if [ $? -eq 0 ]; then
success "Done!\n"
echo "$PYTHON -m pip uninstall $LIBRARY_NAME" >> $UNINSTALLER
fi

cd $WD

find_config

# Run the setup commands from pyproject.toml / tool.pimoroni.commands

for ((i = 0; i < ${#SETUP_CMDS[@]}; i++)); do
CMD="${SETUP_CMDS[$i]}"
# Attempt to catch anything that touches /boot/config.txt and trigger a backup
if [[ "$CMD" == *"raspi-config"* ]] || [[ "$CMD" == *"$CONFIG"* ]] || [[ "$CMD" == *"\$CONFIG"* ]]; then
# Attempt to catch anything that touches config.txt and trigger a backup
if [[ "$CMD" == *"raspi-config"* ]] || [[ "$CMD" == *"$CONFIG_DIR/$CONFIG_FILE"* ]] || [[ "$CMD" == *"\$CONFIG_DIR/\$CONFIG_FILE"* ]]; then
do_config_backup
fi
eval $CMD
check_for_error
done

printf "\n"

# Add the config.txt entries from pyproject.toml / tool.pimoroni.configtxt

for ((i = 0; i < ${#CONFIG_TXT[@]}; i++)); do
CONFIG_LINE="${CONFIG_TXT[$i]}"
if ! [ "$CONFIG_LINE" == "" ]; then
do_config_backup
inform "Adding $CONFIG_LINE to $CONFIG\n"
sudo sed -i "s/^#$CONFIG_LINE/$CONFIG_LINE/" $CONFIG
if ! grep -q "^$CONFIG_LINE" $CONFIG; then
printf "$CONFIG_LINE\n" | sudo tee --append $CONFIG
inform "Adding $CONFIG_LINE to $CONFIG_DIR/$CONFIG_FILE"
sudo sed -i "s/^#$CONFIG_LINE/$CONFIG_LINE/" $CONFIG_DIR/$CONFIG_FILE
if ! grep -q "^$CONFIG_LINE" $CONFIG_DIR/$CONFIG_FILE; then
printf "$CONFIG_LINE\n" | sudo tee --append $CONFIG_DIR/$CONFIG_FILE
fi
fi
done

printf "\n"

# Just a straight copy of the examples/ dir into ~/Pimoroni/board/examples

if [ -d "examples" ]; then
if confirm "Would you like to copy examples to $RESOURCES_DIR?"; then
inform "Copying examples to $RESOURCES_DIR"
Expand All @@ -233,9 +339,12 @@ fi

printf "\n"

# Use pdoc to generate basic documentation from the installed module

if confirm "Would you like to generate documentation?"; then
inform "Installing pdoc. Please wait..."
pip_pkg_install pdoc
printf "Generating documentation.\n"
inform "Generating documentation.\n"
$PYTHON -m pdoc $LIBRARY_NAME -o $RESOURCES_DIR/docs > /dev/null
if [ $? -eq 0 ]; then
inform "Documentation saved to $RESOURCES_DIR/docs"
Expand All @@ -245,6 +354,22 @@ if confirm "Would you like to generate documentation?"; then
fi
fi

success "\nAll done!"
inform "If this is your first time installing you should reboot for hardware changes to take effect.\n"
inform "Find uninstall steps in $UNINSTALLER\n"
printf "\n"

if [ "$CMD_ERRORS" = true ]; then
warning "One or more setup commands appear to have failed."
printf "This might prevent things from working properly.\n"
printf "Make sure your OS is up to date and try re-running this installer.\n"
printf "If things still don't work, report this or find help at $GITHUB_URL.\n\n"
else
success "\nAll done!"
fi

printf "If this is your first time installing you should reboot for hardware changes to take effect.\n"
printf "Find uninstall steps in $UNINSTALLER\n\n"

if [ "$CMD_ERRORS" = true ]; then
exit 1
else
exit 0
fi
7 changes: 5 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ skip = """
[tool.isort]
line_length = 200

[tool.black]
line-length = 200

[tool.check-manifest]
ignore = [
'.stickler.yml',
Expand All @@ -109,10 +112,10 @@ ignore = [
'requirements-dev.txt'
]

[pimoroni]
[tool.pimoroni]
apt_packages = []
configtxt = []
commands = [
"printf \"Setting up i2c...\n\"",
"raspi-config nonint do_i2c 0"
"sudo raspi-config nonint do_i2c 0"
]
2 changes: 1 addition & 1 deletion tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ commands =
python -m build --no-isolation
python -m twine check dist/*
isort --check .
ruff --format=github .
ruff .
codespell .
deps =
check-manifest
Expand Down
Loading