From 3edb2ef2ac9ff96c8280ef0a2b65fe23451fe96c Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Tue, 5 Dec 2023 13:12:31 +0000 Subject: [PATCH 1/9] Repackage to hatch/pyproject.toml --- library/.coveragerc => .coveragerc | 0 library/CHANGELOG.txt => CHANGELOG.md | 0 library/MANIFEST.in => MANIFEST.in | 0 Makefile | 90 ++++---- README.md | 8 +- check.sh | 88 ++++++++ install-legacy.sh | 256 ---------------------- install.sh | 260 +++++++++++++++++------ library/LICENSE.txt | 21 -- library/README.md | 59 ----- library/pyproject.toml | 3 - library/setup.cfg | 51 ----- library/setup.py | 33 --- library/tox.ini | 25 --- {library/pa1010d => pa1010d}/__init__.py | 4 +- pyproject.toml | 122 +++++++++++ {library/tests => tests}/conftest.py | 0 {library/tests => tests}/test_setup.py | 0 tox.ini | 34 +++ uninstall.sh | 77 +++++-- 20 files changed, 544 insertions(+), 587 deletions(-) rename library/.coveragerc => .coveragerc (100%) rename library/CHANGELOG.txt => CHANGELOG.md (100%) rename library/MANIFEST.in => MANIFEST.in (100%) create mode 100755 check.sh delete mode 100755 install-legacy.sh delete mode 100644 library/LICENSE.txt delete mode 100644 library/README.md delete mode 100644 library/pyproject.toml delete mode 100644 library/setup.cfg delete mode 100755 library/setup.py delete mode 100644 library/tox.ini rename {library/pa1010d => pa1010d}/__init__.py (99%) create mode 100644 pyproject.toml rename {library/tests => tests}/conftest.py (100%) rename {library/tests => tests}/test_setup.py (100%) create mode 100644 tox.ini diff --git a/library/.coveragerc b/.coveragerc similarity index 100% rename from library/.coveragerc rename to .coveragerc diff --git a/library/CHANGELOG.txt b/CHANGELOG.md similarity index 100% rename from library/CHANGELOG.txt rename to CHANGELOG.md diff --git a/library/MANIFEST.in b/MANIFEST.in similarity index 100% rename from library/MANIFEST.in rename to MANIFEST.in diff --git a/Makefile b/Makefile index a289fed..9e0c15c 100644 --- a/Makefile +++ b/Makefile @@ -1,70 +1,60 @@ -LIBRARY_VERSION=$(shell grep version library/setup.cfg | awk -F" = " '{print $$2}') -LIBRARY_NAME=$(shell grep name library/setup.cfg | awk -F" = " '{print $$2}') +LIBRARY_NAME := $(shell hatch project metadata name 2> /dev/null) +LIBRARY_VERSION := $(shell hatch version 2> /dev/null) -.PHONY: usage install uninstall +.PHONY: usage install uninstall check pytest qa build-deps check tag wheel sdist clean dist testdeploy deploy usage: +ifdef LIBRARY_NAME @echo "Library: ${LIBRARY_NAME}" @echo "Version: ${LIBRARY_VERSION}\n" +else + @echo "WARNING: You should 'make dev-deps'\n" +endif @echo "Usage: make , where target is one of:\n" - @echo "install: install the library locally from source" - @echo "uninstall: uninstall the local library" - @echo "check: peform basic integrity checks on the codebase" - @echo "python-readme: generate library/README.md from README.md + library/CHANGELOG.txt" - @echo "python-wheels: build python .whl files for distribution" - @echo "python-sdist: build python source distribution" - @echo "python-clean: clean python build and dist directories" - @echo "python-dist: build all python distribution files" - @echo "python-testdeploy: build all and deploy to test PyPi" - @echo "tag: tag the repository with the current version" + @echo "install: install the library locally from source" + @echo "uninstall: uninstall the local library" + @echo "dev-deps: install Python dev dependencies" + @echo "check: perform basic integrity checks on the codebase" + @echo "qa: run linting and package QA" + @echo "pytest: run Python test fixtures" + @echo "clean: clean Python build and dist directories" + @echo "build: build Python distribution files" + @echo "testdeploy: build and upload to test PyPi" + @echo "deploy: build and upload to PyPi" + @echo "tag: tag the repository with the current version\n" install: - ./install.sh + ./install.sh --unstable uninstall: ./uninstall.sh -check: - @echo "Checking for trailing whitespace" - @! grep -IUrn --color "[[:blank:]]$$" --exclude-dir=sphinx --exclude-dir=.tox --exclude-dir=.git --exclude=PKG-INFO - @echo "Checking for DOS line-endings" - @! grep -IUrn --color " " --exclude-dir=sphinx --exclude-dir=.tox --exclude-dir=.git --exclude=Makefile - @echo "Checking library/CHANGELOG.txt" - @cat library/CHANGELOG.txt | grep ^${LIBRARY_VERSION} - @echo "Checking library/${LIBRARY_NAME}/__init__.py" - @cat library/${LIBRARY_NAME}/__init__.py | grep "^__version__ = '${LIBRARY_VERSION}'" - -tag: - git tag -a "v${LIBRARY_VERSION}" -m "Version ${LIBRARY_VERSION}" +dev-deps: + python3 -m pip install -r requirements-dev.txt + sudo apt install dos2unix -python-readme: library/README.md - -python-license: library/LICENSE.txt +check: + @bash check.sh -library/README.md: README.md library/CHANGELOG.txt - cp README.md library/README.md - printf "\n# Changelog\n" >> library/README.md - cat library/CHANGELOG.txt >> library/README.md +qa: + tox -e qa -library/LICENSE.txt: LICENSE - cp LICENSE library/LICENSE.txt +pytest: + tox -e py -python-wheels: python-readme python-license - cd library; python3 setup.py bdist_wheel - cd library; python setup.py bdist_wheel +nopost: + @bash check.sh --nopost -python-sdist: python-readme python-license - cd library; python setup.py sdist +tag: + git tag -a "v${LIBRARY_VERSION}" -m "Version ${LIBRARY_VERSION}" -python-clean: - -rm -r library/dist - -rm -r library/build - -rm -r library/*.egg-info +build: check + @hatch build -python-dist: python-clean python-wheels python-sdist - ls library/dist +clean: + -rm -r dist -python-testdeploy: python-dist - twine upload --repository-url https://test.pypi.org/legacy/ library/dist/* +testdeploy: build + twine upload --repository testpypi dist/* -python-deploy: check python-dist - twine upload library/dist/* +deploy: nopost build + twine upload dist/* diff --git a/README.md b/README.md index 1af3031..cd6d44c 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Pimoroni PA1010D GPS Breakout -[![Build Status](https://img.shields.io/github/actions/workflow/status/pimoroni/pa1010d-python/test.yml?branch=master)](https://github.com/pimoroni/pa1010d-python/actions/workflows/test.yml) -[![Coverage Status](https://coveralls.io/repos/github/pimoroni/pa1010d-python/badge.svg?branch=master)](https://coveralls.io/github/pimoroni/pa1010d-python?branch=master) +[![Build Status](https://img.shields.io/github/actions/workflow/status/pimoroni/pa1010d-python/test.yml?branch=main)](https://github.com/pimoroni/pa1010d-python/actions/workflows/test.yml) +[![Coverage Status](https://coveralls.io/repos/github/pimoroni/pa1010d-python/badge.svg?branch=main)](https://coveralls.io/github/pimoroni/pa1010d-python?branch=main) [![PyPi Package](https://img.shields.io/pypi/v/pa1010d.svg)](https://pypi.python.org/pypi/pa1010d) [![Python Versions](https://img.shields.io/pypi/pyversions/pa1010d.svg)](https://pypi.python.org/pypi/pa1010d) @@ -19,13 +19,13 @@ Stable library and dependencies from GitHub: * `git clone https://github.com/pimoroni/pa1010d-python` * `cd pa1010d-python` -* `sudo ./install.sh` +* `./install.sh` Latest/development library and dependencies from GitHub: * `git clone https://github.com/pimoroni/pa1010d-python` * `cd pa1010d-python` -* `sudo ./install.sh --unstable` +* `./install.sh --unstable` Stable (library only) from PyPi: diff --git a/check.sh b/check.sh new file mode 100755 index 0000000..4395d89 --- /dev/null +++ b/check.sh @@ -0,0 +1,88 @@ +#!/bin/bash + +# This script handles some basic QA checks on the source + +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)" +} + +inform() { + echo -e "$(tput setaf 6)$1$(tput sgr0)" +} + +warning() { + echo -e "$(tput setaf 1)$1$(tput sgr0)" +} + +while [[ $# -gt 0 ]]; do + K="$1" + case $K in + -p|--nopost) + NOPOST=true + shift + ;; + *) + if [[ $1 == -* ]]; then + printf "Unrecognised option: $1\n"; + exit 1 + fi + POSITIONAL_ARGS+=("$1") + shift + esac +done + +inform "Checking $LIBRARY_NAME $LIBRARY_VERSION\n" + +inform "Checking for trailing whitespace..." +grep -IUrn --color "[[:blank:]]$" --exclude-dir=dist --exclude-dir=.tox --exclude-dir=.git --exclude=PKG-INFO +if [[ $? -eq 0 ]]; then + warning "Trailing whitespace found!" + exit 1 +else + success "No trailing whitespace found." +fi +printf "\n" + +inform "Checking for DOS line-endings..." +grep -lIUrn --color $'\r' --exclude-dir=dist --exclude-dir=.tox --exclude-dir=.git --exclude=Makefile +if [[ $? -eq 0 ]]; then + warning "DOS line-endings found!" + exit 1 +else + success "No DOS line-endings found." +fi +printf "\n" + +inform "Checking CHANGELOG.md..." +cat CHANGELOG.md | grep ^${LIBRARY_VERSION} > /dev/null 2>&1 +if [[ $? -eq 1 ]]; then + warning "Changes missing for version ${LIBRARY_VERSION}! Please update CHANGELOG.md." + exit 1 +else + success "Changes found for version ${LIBRARY_VERSION}." +fi +printf "\n" + +inform "Checking for git tag ${LIBRARY_VERSION}..." +git tag -l | grep -E "${LIBRARY_VERSION}$" +if [[ $? -eq 1 ]]; then + warning "Missing git tag for version ${LIBRARY_VERSION}" +fi +printf "\n" + +if [[ $NOPOST ]]; then + inform "Checking for .postN on library version..." + if [[ "$POST_VERSION" != "" ]]; then + warning "Found .$POST_VERSION on library version." + inform "Please only use these for testpypi releases." + exit 1 + else + success "OK" + fi +fi diff --git a/install-legacy.sh b/install-legacy.sh deleted file mode 100755 index 3572e50..0000000 --- a/install-legacy.sh +++ /dev/null @@ -1,256 +0,0 @@ -#!/bin/bash - -CONFIG=/boot/config.txt -DATESTAMP=`date "+%Y-%m-%d-%H-%M-%S"` -CONFIG_BACKUP=false -APT_HAS_UPDATED=false -USER_HOME=/home/$SUDO_USER -RESOURCES_TOP_DIR=$USER_HOME/Pimoroni -WD=`pwd` -USAGE="sudo ./install.sh (--unstable)" -POSITIONAL_ARGS=() -FORCE=false -UNSTABLE=false -CODENAME=`lsb_release -sc` - -if [[ $CODENAME == "bullseye" ]]; then - bash ./install.sh $@ - exit $? -fi - -user_check() { - if [ $(id -u) -ne 0 ]; then - printf "Script must be run as root. Try 'sudo ./install.sh'\n" - exit 1 - fi -} - -confirm() { - if $FORCE; then - true - else - read -r -p "$1 [y/N] " response < /dev/tty - if [[ $response =~ ^(yes|y|Y)$ ]]; then - true - else - false - fi - fi -} - -prompt() { - read -r -p "$1 [y/N] " response < /dev/tty - if [[ $response =~ ^(yes|y|Y)$ ]]; then - true - else - false - fi -} - -success() { - echo -e "$(tput setaf 2)$1$(tput sgr0)" -} - -inform() { - echo -e "$(tput setaf 6)$1$(tput sgr0)" -} - -warning() { - echo -e "$(tput setaf 1)$1$(tput sgr0)" -} - -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" - cp $CONFIG /boot/$FILENAME - mkdir -p $RESOURCES_TOP_DIR/config-backups/ - cp $CONFIG $RESOURCES_TOP_DIR/config-backups/$FILENAME - if [ -f "$UNINSTALLER" ]; then - echo "cp $RESOURCES_TOP_DIR/config-backups/$FILENAME $CONFIG" >> $UNINSTALLER - fi - fi -} - -function apt_pkg_install { - PACKAGES=() - PACKAGES_IN=("$@") - for ((i = 0; i < ${#PACKAGES_IN[@]}; i++)); do - PACKAGE="${PACKAGES_IN[$i]}" - if [ "$PACKAGE" == "" ]; then continue; fi - printf "Checking for $PACKAGE\n" - dpkg -L $PACKAGE > /dev/null 2>&1 - if [ "$?" == "1" ]; then - PACKAGES+=("$PACKAGE") - fi - done - PACKAGES="${PACKAGES[@]}" - if ! [ "$PACKAGES" == "" ]; then - echo "Installing missing packages: $PACKAGES" - if [ ! $APT_HAS_UPDATED ]; then - apt update - APT_HAS_UPDATED=true - fi - apt install -y $PACKAGES - if [ -f "$UNINSTALLER" ]; then - echo "apt uninstall -y $PACKAGES" - fi - fi -} - -while [[ $# -gt 0 ]]; do - K="$1" - case $K in - -u|--unstable) - UNSTABLE=true - shift - ;; - -f|--force) - FORCE=true - shift - ;; - *) - if [[ $1 == -* ]]; then - printf "Unrecognised option: $1\n"; - printf "Usage: $USAGE\n"; - exit 1 - fi - POSITIONAL_ARGS+=("$1") - shift - esac -done - -user_check - -apt_pkg_install python-configparser - -CONFIG_VARS=`python - < $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 -EOF - -printf "$LIBRARY_NAME $LIBRARY_VERSION Python Library: Installer\n\n" - -if $UNSTABLE; then - warning "Installing unstable library from source.\n\n" -else - printf "Installing stable library from pypi.\n\n" -fi - -cd library - -printf "Installing for Python 2..\n" -apt_pkg_install "${PY2_DEPS[@]}" -if $UNSTABLE; then - python setup.py install > /dev/null -else - pip install --upgrade $LIBRARY_NAME -fi -if [ $? -eq 0 ]; then - success "Done!\n" - echo "pip uninstall $LIBRARY_NAME" >> $UNINSTALLER -fi - -if [ -f "/usr/bin/python3" ]; then - printf "Installing for Python 3..\n" - apt_pkg_install "${PY3_DEPS[@]}" - if $UNSTABLE; then - python3 setup.py install > /dev/null - else - pip3 install --upgrade $LIBRARY_NAME - fi - if [ $? -eq 0 ]; then - success "Done!\n" - echo "pip3 uninstall $LIBRARY_NAME" >> $UNINSTALLER - fi -fi - -cd $WD - -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 - do_config_backup - fi - eval $CMD -done - -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" - sed -i "s/^#$CONFIG_LINE/$CONFIG_LINE/" $CONFIG - if ! grep -q "^$CONFIG_LINE" $CONFIG; then - printf "$CONFIG_LINE\n" >> $CONFIG - fi - fi -done - -if [ -d "examples" ]; then - if confirm "Would you like to copy examples to $RESOURCES_DIR?"; then - inform "Copying examples to $RESOURCES_DIR" - cp -r examples/ $RESOURCES_DIR - echo "rm -r $RESOURCES_DIR" >> $UNINSTALLER - success "Done!" - fi -fi - -printf "\n" - -if [ -f "/usr/bin/pydoc" ]; then - printf "Generating documentation.\n" - pydoc -w $LIBRARY_NAME > /dev/null - if [ -f "$LIBRARY_NAME.html" ]; then - cp $LIBRARY_NAME.html $RESOURCES_DIR/docs.html - rm -f $LIBRARY_NAME.html - inform "Documentation saved to $RESOURCES_DIR/docs.html" - success "Done!" - else - warning "Error: Failed to generate documentation." - 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" diff --git a/install.sh b/install.sh index 28d3d46..d42c48d 100755 --- a/install.sh +++ b/install.sh @@ -1,22 +1,25 @@ #!/bin/bash -CONFIG=/boot/config.txt +LIBRARY_NAME=`grep -m 1 name pyproject.toml | awk -F" = " '{print substr($2,2,length($2)-2)}'` +CONFIG_FILE=config.txt +CONFIG_DIR="/boot/firmware" DATESTAMP=`date "+%Y-%m-%d-%H-%M-%S"` CONFIG_BACKUP=false APT_HAS_UPDATED=false -USER_HOME=/home/$SUDO_USER -RESOURCES_TOP_DIR=$USER_HOME/Pimoroni +RESOURCES_TOP_DIR="$HOME/Pimoroni" +VENV_BASH_SNIPPET="$RESOURCES_TOP_DIR/auto_venv.sh" +VENV_DIR="$HOME/.virtualenvs/pimoroni" WD=`pwd` -USAGE="sudo ./install.sh (--unstable)" +USAGE="./install.sh (--unstable)" POSITIONAL_ARGS=() FORCE=false UNSTABLE=false -PYTHON="/usr/bin/python3" +PYTHON="python" +CMD_ERRORS=false user_check() { - if [ $(id -u) -ne 0 ]; then - printf "Script must be run as root. Try 'sudo ./install.sh'\n" - exit 1 + if [ $(id -u) -eq 0 ]; then + fatal "Script should not be run as root. Try './install.sh'\n" fi } @@ -51,19 +54,91 @@ 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" - 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 } @@ -71,6 +146,7 @@ function do_config_backup { 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 @@ -82,18 +158,26 @@ 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 - apt update + sudo apt update APT_HAS_UPDATED=true fi - apt install -y $PACKAGES + sudo apt install -y $PACKAGES + check_for_error if [ -f "$UNINSTALLER" ]; then - echo "apt uninstall -y $PACKAGES" + 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 K="$1" case $K in @@ -121,46 +205,43 @@ 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` -inform "Installing. Please wait..." +inform "Checking Dependencies. Please wait..." + +# Install toml and try to read pyproject.toml into bash variables -$PYTHON -m pip install --upgrade configparser +pip_pkg_install toml CONFIG_VARS=`$PYTHON - < $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 -printf "$LIBRARY_NAME $LIBRARY_VERSION Python Library: Installer\n\n" +printf "\n" -if $UNSTABLE; then - warning "Installing unstable library from source.\n\n" -else - printf "Installing stable library from pypi.\n\n" -fi +inform "Installing for $PYTHON_VER...\n" + +# Install apt packages from pyproject.toml / tool.pimoroni.apt_packages +apt_pkg_install "${APT_PACKAGES[@]}" -cd library +printf "\n" -printf "Installing for $PYTHON_VER...\n" -apt_pkg_install "${PY3_DEPS[@]}" if $UNSTABLE; then - $PYTHON setup.py install > /dev/null + warning "Installing unstable library from source.\n" + pip_pkg_install . else - $PYTHON -m pip install --upgrade $LIBRARY_NAME + 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 @@ -201,27 +294,40 @@ 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" - sed -i "s/^#$CONFIG_LINE/$CONFIG_LINE/" $CONFIG - if ! grep -q "^$CONFIG_LINE" $CONFIG; then - printf "$CONFIG_LINE\n" >> $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" @@ -233,19 +339,37 @@ fi printf "\n" -if [ -f "/usr/bin/pydoc" ]; then - printf "Generating documentation.\n" - pydoc -w $LIBRARY_NAME > /dev/null - if [ -f "$LIBRARY_NAME.html" ]; then - cp $LIBRARY_NAME.html $RESOURCES_DIR/docs.html - rm -f $LIBRARY_NAME.html - inform "Documentation saved to $RESOURCES_DIR/docs.html" +# 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 + 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" success "Done!" else warning "Error: Failed to generate documentation." 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 \ No newline at end of file diff --git a/library/LICENSE.txt b/library/LICENSE.txt deleted file mode 100644 index aed751a..0000000 --- a/library/LICENSE.txt +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2018 Pimoroni Ltd. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/library/README.md b/library/README.md deleted file mode 100644 index d567df5..0000000 --- a/library/README.md +++ /dev/null @@ -1,59 +0,0 @@ -# Pimoroni PA1010D GPS Breakout - -[![Build Status](https://img.shields.io/github/actions/workflow/status/pimoroni/pa1010d-python/test.yml?branch=master)](https://github.com/pimoroni/pa1010d-python/actions/workflows/test.yml) -[![Coverage Status](https://coveralls.io/repos/github/pimoroni/pa1010d-python/badge.svg?branch=master)](https://coveralls.io/github/pimoroni/pa1010d-python?branch=master) -[![PyPi Package](https://img.shields.io/pypi/v/pa1010d.svg)](https://pypi.python.org/pypi/pa1010d) -[![Python Versions](https://img.shields.io/pypi/pyversions/pa1010d.svg)](https://pypi.python.org/pypi/pa1010d) - -# Pre-requisites - -You must enable: - -* i2c: `sudo raspi-config nonint do_i2c 0` - -You can optionally run `sudo raspi-config` or the graphical Raspberry Pi Configuration UI to enable interfaces. - -# Installing - -Stable library and dependencies from GitHub: - -* `git clone https://github.com/pimoroni/pa1010d-python` -* `cd pa1010d-python` -* `sudo ./install.sh` - -Latest/development library and dependencies from GitHub: - -* `git clone https://github.com/pimoroni/pa1010d-python` -* `cd pa1010d-python` -* `sudo ./install.sh --unstable` - -Stable (library only) from PyPi: - -* Just run `python3 -m pip install pa1010d` - -# Changelog - -0.0.4 ------ - -* Fix read/write for Python 3.9 - -0.0.3 ------ - -* Add support for geoidal separation -* Add support for lat/long direction -* Require Python >= 3.6 - -0.0.2 ------ - -* Add support for disabling PPS LED -* Handle spurious newlines in sentences -* Add PDOP, VDOP and HDOP output -* Handle GLL messages - -0.0.1 ------ - -* Initial Release diff --git a/library/pyproject.toml b/library/pyproject.toml deleted file mode 100644 index 2f21011..0000000 --- a/library/pyproject.toml +++ /dev/null @@ -1,3 +0,0 @@ -[build-system] -requires = ["setuptools>=40.8.0", "wheel"] -build-backend = "setuptools.build_meta" diff --git a/library/setup.cfg b/library/setup.cfg deleted file mode 100644 index 9ac11ec..0000000 --- a/library/setup.cfg +++ /dev/null @@ -1,51 +0,0 @@ -# -*- coding: utf-8 -*- -[metadata] -name = pa1010d -version = 0.0.4 -author = Philip Howard -author_email = phil@pimoroni.com -description = Python library for the PA1010D i2c GPS module -long_description = file: README.md -long_description_content_type = text/markdown -keywords = Raspberry Pi -url = https://www.pimoroni.com -project_urls = - GitHub=https://www.github.com/pimoroni/pa1010d-python -license = MIT -# This includes the license file(s) in the wheel. -# https://wheel.readthedocs.io/en/stable/user_guide.html#including-license-files-in-the-generated-wheel-file -license_files = LICENSE.txt -classifiers = - Development Status :: 4 - Beta - Operating System :: POSIX :: Linux - License :: OSI Approved :: MIT License - Intended Audience :: Developers - Programming Language :: Python :: 3 - Topic :: Software Development - Topic :: Software Development :: Libraries - Topic :: System :: Hardware - -[options] -python_requires = >= 3.6 -packages = pa1010d -install_requires = pynmea2 - -[flake8] -exclude = - .tox, - .eggs, - .git, - __pycache__, - build, - dist -ignore = - E501 - -[pimoroni] -py3only = true -py2deps = -py3deps = -configtxt = -commands = - printf "Setting up i2c...\n" - raspi-config nonint do_i2c 0 diff --git a/library/setup.py b/library/setup.py deleted file mode 100755 index afb1ee1..0000000 --- a/library/setup.py +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -""" -Copyright (c) 2016 Pimoroni - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -""" - -from setuptools import setup, __version__ -from pkg_resources import parse_version - -minimum_version = parse_version('30.4.0') - -if parse_version(__version__) < minimum_version: - raise RuntimeError("Package setuptools must be at least version {}".format(minimum_version)) - -setup() diff --git a/library/tox.ini b/library/tox.ini deleted file mode 100644 index 60d3665..0000000 --- a/library/tox.ini +++ /dev/null @@ -1,25 +0,0 @@ -[tox] -envlist = py,qa -skip_missing_interpreters = True - -[testenv] -commands = - python setup.py install - coverage run -m pytest -v -r wsx - coverage report -deps = - mock - pytest>=3.1 - pytest-cov - -[testenv:qa] -commands = - check-manifest --ignore tox.ini,tests/*,.coveragerc - python setup.py sdist bdist_wheel - twine check dist/* - flake8 --ignore E501 -deps = - check-manifest - flake8 - twine - diff --git a/library/pa1010d/__init__.py b/pa1010d/__init__.py similarity index 99% rename from library/pa1010d/__init__.py rename to pa1010d/__init__.py index a38240d..8ab8e96 100644 --- a/library/pa1010d/__init__.py +++ b/pa1010d/__init__.py @@ -1,5 +1,5 @@ import time -import smbus +import smbus2 import pynmea2 @@ -39,7 +39,7 @@ class PA1010D(): def __init__(self, i2c_addr=PA1010D_ADDR, debug=False): self._i2c_addr = i2c_addr - self._i2c = smbus.SMBus(1) + self._i2c = smbus2.SMBus(1) self._debug = debug diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..adf4672 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,122 @@ +[build-system] +requires = ["hatchling", "hatch-fancy-pypi-readme"] +build-backend = "hatchling.build" + +[project] +name = "pa1010d" +dynamic = ["version", "readme"] +description = "Python library for the PA1010D i2c GPS module" +license = {file = "LICENSE"} +requires-python = ">= 3.7" +authors = [ + { name = "Philip Howard", email = "phil@pimoroni.com" }, +] +maintainers = [ + { name = "Philip Howard", email = "phil@pimoroni.com" }, +] +keywords = [ + "Pi", + "Raspberry", +] +classifiers = [ + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Operating System :: POSIX :: Linux", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3 :: Only", + "Topic :: Software Development", + "Topic :: Software Development :: Libraries", + "Topic :: System :: Hardware", +] +dependencies = [ + "pynmea2", + "smbus2" +] + +[project.urls] +GitHub = "https://www.github.com/pimoroni/pa1010d-python" +Homepage = "https://www.pimoroni.com" + +[tool.hatch.version] +path = "pa1010d/__init__.py" + +[tool.hatch.build] +include = [ + "pa1010d", + "README.md", + "CHANGELOG.md", + "LICENSE" +] + +[tool.hatch.build.targets.sdist] +include = [ + "*" +] +exclude = [ + ".*", + "dist" +] + +[tool.hatch.metadata.hooks.fancy-pypi-readme] +content-type = "text/markdown" +fragments = [ + { path = "README.md" }, + { text = "\n" }, + { path = "CHANGELOG.md" } +] + +[tool.ruff] +exclude = [ + '.tox', + '.egg', + '.git', + '__pycache__', + 'build', + 'dist' +] +line-length = 200 + +[tool.codespell] +skip = """ +./.tox,\ +./.egg,\ +./.git,\ +./__pycache__,\ +./build,\ +./dist.\ +""" + +[tool.isort] +line_length = 200 + +[tool.black] +line-length = 200 + +[tool.check-manifest] +ignore = [ + '.stickler.yml', + 'boilerplate.md', + 'check.sh', + 'install.sh', + 'uninstall.sh', + 'Makefile', + 'tox.ini', + 'tests/*', + 'examples/*', + '.coveragerc', + 'requirements-dev.txt' +] + +[tool.pimoroni] +apt_packages = [] +configtxt = [] +commands = [ + "printf \"Setting up i2c...\n\"", + "sudo raspi-config nonint do_i2c 0" +] diff --git a/library/tests/conftest.py b/tests/conftest.py similarity index 100% rename from library/tests/conftest.py rename to tests/conftest.py diff --git a/library/tests/test_setup.py b/tests/test_setup.py similarity index 100% rename from library/tests/test_setup.py rename to tests/test_setup.py diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..44c8654 --- /dev/null +++ b/tox.ini @@ -0,0 +1,34 @@ +[tox] +envlist = py,qa +skip_missing_interpreters = True +isolated_build = true +minversion = 4.0.0 + +[testenv] +commands = + coverage run -m pytest -v -r wsx + coverage report +deps = + mock + pytest>=3.1 + pytest-cov + build + +[testenv:qa] +commands = + check-manifest + python -m build --no-isolation + python -m twine check dist/* + isort --check . + ruff . + codespell . +deps = + check-manifest + ruff + codespell + isort + twine + build + hatch + hatch-fancy-pypi-readme + diff --git a/uninstall.sh b/uninstall.sh index 4848039..f213fc5 100755 --- a/uninstall.sh +++ b/uninstall.sh @@ -1,25 +1,72 @@ #!/bin/bash -LIBRARY_VERSION=`cat library/setup.cfg | grep version | awk -F" = " '{print $2}'` -LIBRARY_NAME=`cat library/setup.cfg | grep name | awk -F" = " '{print $2}'` +FORCE=false +LIBRARY_NAME=`grep -m 1 name pyproject.toml | awk -F" = " '{print substr($2,2,length($2)-2)}'` +RESOURCES_DIR=$HOME/Pimoroni/$LIBRARY_NAME +PYTHON="python" -printf "$LIBRARY_NAME $LIBRARY_VERSION Python Library: Uninstaller\n\n" -if [ $(id -u) -ne 0 ]; then - printf "Script must be run as root. Try 'sudo ./uninstall.sh'\n" - exit 1 -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" + exit 1 + fi +} -cd library +user_check() { + if [ $(id -u) -eq 0 ]; then + printf "Script should not be run as root. Try './uninstall.sh'\n" + exit 1 + fi +} -printf "Unnstalling for Python 2..\n" -pip uninstall $LIBRARY_NAME +confirm() { + if $FORCE; then + true + else + read -r -p "$1 [y/N] " response < /dev/tty + if [[ $response =~ ^(yes|y|Y)$ ]]; then + true + else + false + fi + fi +} -if [ -f "/usr/bin/pip3" ]; then - printf "Uninstalling for Python 3..\n" - pip3 uninstall $LIBRARY_NAME -fi +prompt() { + read -r -p "$1 [y/N] " response < /dev/tty + if [[ $response =~ ^(yes|y|Y)$ ]]; then + true + else + false + fi +} + +success() { + echo -e "$(tput setaf 2)$1$(tput sgr0)" +} + +inform() { + echo -e "$(tput setaf 6)$1$(tput sgr0)" +} + +warning() { + echo -e "$(tput setaf 1)$1$(tput sgr0)" +} -cd .. +printf "$LIBRARY_NAME Python Library: Uninstaller\n\n" + +user_check +venv_check + +printf "Uninstalling for Python 3...\n" +$PYTHON -m pip uninstall $LIBRARY_NAME + +if [ -d $RESOURCES_DIR ]; then + if confirm "Would you like to delete $RESOURCES_DIR?"; then + rm -r $RESOURCES_DIR + fi +fi printf "Done!\n" From 190478248573ba3be50544e1da5063b72ecab10e Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Tue, 5 Dec 2023 13:16:10 +0000 Subject: [PATCH 2/9] QA: Apply isort suggestions. --- examples/cmd.py | 3 +-- examples/latlon.py | 1 - examples/pps.py | 1 - pa1010d/__init__.py | 4 +--- tests/conftest.py | 5 +++-- 5 files changed, 5 insertions(+), 9 deletions(-) diff --git a/examples/cmd.py b/examples/cmd.py index c80d71a..b603fd7 100755 --- a/examples/cmd.py +++ b/examples/cmd.py @@ -1,10 +1,9 @@ #!/usr/bin/env python3 -import time import sys +import time from pa1010d import PA1010D - """ Run raw commands against the PA1010D GPS and return the respones. diff --git a/examples/latlon.py b/examples/latlon.py index a3286d4..61ab695 100755 --- a/examples/latlon.py +++ b/examples/latlon.py @@ -3,7 +3,6 @@ from pa1010d import PA1010D - gps = PA1010D() while True: diff --git a/examples/pps.py b/examples/pps.py index f4d812b..c514c5e 100755 --- a/examples/pps.py +++ b/examples/pps.py @@ -3,7 +3,6 @@ from pa1010d import PA1010D - if len(sys.argv) < 2: print(f"Usage: {sys.argv[0]} ") sys.exit() diff --git a/pa1010d/__init__.py b/pa1010d/__init__.py index 8ab8e96..30eb37a 100644 --- a/pa1010d/__init__.py +++ b/pa1010d/__init__.py @@ -1,9 +1,7 @@ import time -import smbus2 - import pynmea2 - +import smbus2 __version__ = '0.0.4' diff --git a/tests/conftest.py b/tests/conftest.py index c119e3f..2a59e26 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,7 +1,8 @@ -import pytest -import mock import sys +import mock +import pytest + class SMBus: def __init__(self, bus): From 68f01e52c034d531b541ceabdd984659633383b7 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Tue, 5 Dec 2023 13:19:33 +0000 Subject: [PATCH 3/9] QA: Apply ruff suggestions. Change type comparisons to isinstance. --- pa1010d/__init__.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/pa1010d/__init__.py b/pa1010d/__init__.py index 30eb37a..2235a33 100644 --- a/pa1010d/__init__.py +++ b/pa1010d/__init__.py @@ -79,7 +79,7 @@ def send_command(self, command, add_checksum=True): If add_checksum is True (the default) a NMEA checksum will automatically be computed and added. """ - if type(command) is not bytes: + if isinstance(command, bytes): command = command.encode("ascii") # TODO replace with pynmea2 functionality @@ -152,7 +152,7 @@ def update(self, wait_for="GGA", timeout=5): continue # Time, position and fix - if type(result) == pynmea2.GGA: + if isinstance(result, pynmea2.GGA): if result.gps_qual is None: self.num_sats = 0 self.gps_qual = 0 @@ -170,11 +170,11 @@ def update(self, wait_for="GGA", timeout=5): return True # Geographic Lat/Lon (Loran holdover) - elif type(result) == pynmea2.GLL: + elif isinstance(result, pynmea2.GLL): pass # GPS DOP and active satellites - elif type(result) == pynmea2.GSA: + elif isinstance(result, pynmea2.GSA): self.mode_fix_type = result.mode_fix_type self.pdop = result.pdop self.hdop = result.hdop @@ -183,23 +183,23 @@ def update(self, wait_for="GGA", timeout=5): return True # Position, velocity and time - elif type(result) == pynmea2.RMC: + elif isinstance(result, pynmea2.RMC): self.speed_over_ground = result.spd_over_grnd if wait_for == "RMC": return True # Track made good and speed over ground - elif type(result) == pynmea2.VTG: + elif isinstance(result, pynmea2.VTG): if wait_for == "VTG": return True # SVs in view, PRN, elevation, azimuth and SNR - elif type(result) == pynmea2.GSV: + elif isinstance(result, pynmea2.GSV): if wait_for == "GSV": return True # ProprietarySentence handles boot up output such as "$PMTK011,MTKGPS*08" - elif type(result) == pynmea2.ProprietarySentence: + elif isinstance(result, pynmea2.ProprietarySentence): # TODO If we implement sending commands *to* the GPS, # they should not be permitted until after receiving this sequence # $PMTK011,MTKGPS*08 Successful bootup @@ -213,10 +213,10 @@ def update(self, wait_for="GGA", timeout=5): # requires merge and release of: https://github.com/Knio/pynmea2/pull/111 # TODO Drop this special case when #111 is merged & released try: - if type(result) in ( + if isinstance(result, ( pynmea2.types.proprietary.mtk.MTK011, pynmea2.types.proprietary.mtk.MTK010 - ): + )): return True except AttributeError: pass From 3f4e04ad55d0417f7c384f4e8597ab7ee41e6c52 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Tue, 5 Dec 2023 13:21:38 +0000 Subject: [PATCH 4/9] QA: Apply codespell suggestions. --- examples/cmd.py | 2 +- pa1010d/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/cmd.py b/examples/cmd.py index b603fd7..5cc3e73 100755 --- a/examples/cmd.py +++ b/examples/cmd.py @@ -5,7 +5,7 @@ from pa1010d import PA1010D """ -Run raw commands against the PA1010D GPS and return the respones. +Run raw commands against the PA1010D GPS and return the responses. Eg: PMTK605 = Query Firmware Release Info diff --git a/pa1010d/__init__.py b/pa1010d/__init__.py index 2235a33..ea187a3 100644 --- a/pa1010d/__init__.py +++ b/pa1010d/__init__.py @@ -128,7 +128,7 @@ def update(self, wait_for="GGA", timeout=5): Returns true if a sentence has been successfully parsed. - Returns false if an error has occured. + Returns false if an error has occurred. Will wait 5 seconds for a GGA message by default. From 15cbf73321aeb6e01c2b8d331b4177d1c7647357 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Tue, 5 Dec 2023 13:26:23 +0000 Subject: [PATCH 5/9] QA: Apply some black suggestions. --- examples/cmd.py | 3 +-- pa1010d/__init__.py | 12 ++++++------ tests/conftest.py | 2 +- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/examples/cmd.py b/examples/cmd.py index 5cc3e73..49b795b 100755 --- a/examples/cmd.py +++ b/examples/cmd.py @@ -25,8 +25,7 @@ def timeout(err=None, timeout=5.0): raise TimeoutError(err) -responses = { -} +responses = {} if len(sys.argv) < 2: print(f"Usage: {sys.argv[0]} ") diff --git a/pa1010d/__init__.py b/pa1010d/__init__.py index ea187a3..2a5ac81 100644 --- a/pa1010d/__init__.py +++ b/pa1010d/__init__.py @@ -3,7 +3,7 @@ import pynmea2 import smbus2 -__version__ = '0.0.4' +__version__ = "0.0.4" PA1010D_ADDR = 0x10 @@ -14,7 +14,7 @@ PPS_ALWAYS = 4 -class PA1010D(): +class PA1010D: __slots__ = ( "timestamp", "latitude", @@ -32,7 +32,7 @@ class PA1010D(): "vdop", "_i2c_addr", "_i2c", - "_debug" + "_debug", ) def __init__(self, i2c_addr=PA1010D_ADDR, debug=False): @@ -89,7 +89,7 @@ def send_command(self, command, add_checksum=True): command = command[:-1] buf = bytearray() - buf += b'$' + buf += b"$" buf += command if add_checksum: checksum = 0 @@ -97,9 +97,9 @@ def send_command(self, command, add_checksum=True): # so `for char in commaud` iterates through char ordinals for char in command: checksum ^= char - buf += b'*' # Delimits checksum value + buf += b"*" # Delimits checksum value buf += "{checksum:02X}".format(checksum=checksum).encode("ascii") - buf += b'\r\n' + buf += b"\r\n" self._write_sentence(buf) def read_sentence(self, timeout=5): diff --git a/tests/conftest.py b/tests/conftest.py index 2a59e26..3881efb 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -22,7 +22,7 @@ def write_byte(self, address, data): pass -@pytest.fixture(scope='function', autouse=False) +@pytest.fixture(scope="function", autouse=False) def smbus(): smbus = mock.MagicMock() smbus.SMBus = SMBus From 7291060421a6655866ea0e22730f9f4f98a49557 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Tue, 5 Dec 2023 13:26:59 +0000 Subject: [PATCH 6/9] CI: Update GitHub workflows. --- .github/workflows/build.yml | 41 +++++++++++++++++++++++++++++++++++++ .github/workflows/qa.yml | 36 ++++++++++++++++++++++++++++++++ .github/workflows/test.yml | 22 ++++++++++++-------- 3 files changed, 90 insertions(+), 9 deletions(-) create mode 100644 .github/workflows/build.yml create mode 100644 .github/workflows/qa.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..87200ef --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,41 @@ +name: Build + +on: + pull_request: + push: + branches: + - main + +jobs: + test: + name: Python ${{ matrix.python }} + runs-on: ubuntu-latest + strategy: + matrix: + python: ['3.9', '3.10', '3.11'] + + env: + RELEASE_FILE: ${{ github.event.repository.name }}-${{ github.event.release.tag_name || github.sha }}-py${{ matrix.python }} + + steps: + - name: Checkout Code + uses: actions/checkout@v3 + + - name: Set up Python ${{ matrix.python }} + uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python }} + + - name: Install Dependencies + run: | + make dev-deps + + - name: Build Packages + run: | + make build + + - name: Upload Packages + uses: actions/upload-artifact@v3 + with: + name: ${{ env.RELEASE_FILE }} + path: dist/ diff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml new file mode 100644 index 0000000..4f85883 --- /dev/null +++ b/.github/workflows/qa.yml @@ -0,0 +1,36 @@ +name: QA + +on: + pull_request: + push: + branches: + - main + +jobs: + test: + name: linting & spelling + runs-on: ubuntu-latest + + env: + TERM: xterm-256color + + steps: + - name: Checkout Code + uses: actions/checkout@v2 + + - name: Set up Python '3,11' + uses: actions/setup-python@v3 + with: + python-version: '3.11' + + - name: Install Dependencies + run: | + make dev-deps + + - name: Run Quality Assurance + run: | + make qa + + - name: Run Code Checks + run: | + make check diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e8e6358..016a678 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,37 +1,41 @@ -name: Python Tests +name: Tests on: pull_request: push: branches: - - master + - main jobs: test: + name: Python ${{ matrix.python }} runs-on: ubuntu-latest strategy: matrix: python: ['3.9', '3.10', '3.11'] steps: - - uses: actions/checkout@v2 + - name: Checkout Code + uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v3 with: python-version: ${{ matrix.python }} + - name: Install Dependencies run: | - python -m pip install --upgrade setuptools tox + make dev-deps + - name: Run Tests - working-directory: library run: | - tox -e py + make pytest + - name: Coverage + if: ${{ matrix.python == '3.9' }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - working-directory: library run: | python -m pip install coveralls coveralls --service=github - if: ${{ matrix.python == '3.11' }} From 85fe89a1e42c7b3ed9d3becb2333b0414bfae72e Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Tue, 5 Dec 2023 13:31:18 +0000 Subject: [PATCH 7/9] CI: Add missing requirements-dev. --- requirements-dev.txt | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 requirements-dev.txt diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 0000000..525b042 --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,9 @@ +check-manifest +ruff +codespell +isort +twine +hatch +hatch-fancy-pypi-readme +tox +pdoc From 00224ee7859269611f5a6aba5e57797b8aaf87dd Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Tue, 5 Dec 2023 13:52:08 +0000 Subject: [PATCH 8/9] CI: Fix tests and command encoding bug. --- pa1010d/__init__.py | 2 +- tests/conftest.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pa1010d/__init__.py b/pa1010d/__init__.py index 2a5ac81..98b014e 100644 --- a/pa1010d/__init__.py +++ b/pa1010d/__init__.py @@ -79,7 +79,7 @@ def send_command(self, command, add_checksum=True): If add_checksum is True (the default) a NMEA checksum will automatically be computed and added. """ - if isinstance(command, bytes): + if not isinstance(command, bytes): command = command.encode("ascii") # TODO replace with pynmea2 functionality diff --git a/tests/conftest.py b/tests/conftest.py index 3881efb..0ab7518 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -26,6 +26,6 @@ def write_byte(self, address, data): def smbus(): smbus = mock.MagicMock() smbus.SMBus = SMBus - sys.modules["smbus"] = smbus + sys.modules["smbus2"] = smbus yield smbus - del sys.modules["smbus"] + del sys.modules["smbus2"] From d6ae4aaf9a2adc548e94452c665b5bfcb85103d5 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Wed, 10 Jan 2024 16:18:59 +0000 Subject: [PATCH 9/9] Sync with latest boilerplate. * CI: Update GitHub Action versions. * QA: Add shellcheck and fix/ignore all issues. * install.sh: slightly better feedback for setup commands. * install.sh: fix quoting bug in do_config_backup. * install.sh: don't output printf commands. --- .github/workflows/build.yml | 6 +- .github/workflows/qa.yml | 9 ++- .github/workflows/test.yml | 2 +- Makefile | 5 +- check.sh | 20 +++--- install.sh | 121 ++++++++++++++++++------------------ uninstall.sh | 14 ++--- 7 files changed, 89 insertions(+), 88 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 87200ef..07620e3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,10 +19,10 @@ jobs: steps: - name: Checkout Code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python }} - uses: actions/setup-python@v3 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python }} @@ -35,7 +35,7 @@ jobs: make build - name: Upload Packages - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ${{ env.RELEASE_FILE }} path: dist/ diff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml index 4f85883..ac672a5 100644 --- a/.github/workflows/qa.yml +++ b/.github/workflows/qa.yml @@ -10,16 +10,15 @@ jobs: test: name: linting & spelling runs-on: ubuntu-latest - env: TERM: xterm-256color steps: - name: Checkout Code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Set up Python '3,11' - uses: actions/setup-python@v3 + uses: actions/setup-python@v5 with: python-version: '3.11' @@ -34,3 +33,7 @@ jobs: - name: Run Code Checks run: | make check + + - name: Run Bash Code Checks + run: | + make shellcheck diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 016a678..6f8cff7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -19,7 +19,7 @@ jobs: uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python }} - uses: actions/setup-python@v3 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python }} diff --git a/Makefile b/Makefile index 9e0c15c..34f4a7d 100644 --- a/Makefile +++ b/Makefile @@ -30,11 +30,14 @@ uninstall: dev-deps: python3 -m pip install -r requirements-dev.txt - sudo apt install dos2unix + sudo apt install dos2unix shellcheck check: @bash check.sh +shellcheck: + shellcheck *.sh + qa: tox -e qa diff --git a/check.sh b/check.sh index 4395d89..38dfc3a 100755 --- a/check.sh +++ b/check.sh @@ -3,9 +3,9 @@ # This script handles some basic QA checks on the source 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))}'` +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() { @@ -29,7 +29,7 @@ while [[ $# -gt 0 ]]; do ;; *) if [[ $1 == -* ]]; then - printf "Unrecognised option: $1\n"; + printf "Unrecognised option: %s\n" "$1"; exit 1 fi POSITIONAL_ARGS+=("$1") @@ -40,8 +40,7 @@ done inform "Checking $LIBRARY_NAME $LIBRARY_VERSION\n" inform "Checking for trailing whitespace..." -grep -IUrn --color "[[:blank:]]$" --exclude-dir=dist --exclude-dir=.tox --exclude-dir=.git --exclude=PKG-INFO -if [[ $? -eq 0 ]]; then +if grep -IUrn --color "[[:blank:]]$" --exclude-dir=dist --exclude-dir=.tox --exclude-dir=.git --exclude=PKG-INFO; then warning "Trailing whitespace found!" exit 1 else @@ -50,8 +49,7 @@ fi printf "\n" inform "Checking for DOS line-endings..." -grep -lIUrn --color $'\r' --exclude-dir=dist --exclude-dir=.tox --exclude-dir=.git --exclude=Makefile -if [[ $? -eq 0 ]]; then +if grep -lIUrn --color $'\r' --exclude-dir=dist --exclude-dir=.tox --exclude-dir=.git --exclude=Makefile; then warning "DOS line-endings found!" exit 1 else @@ -60,8 +58,7 @@ fi printf "\n" inform "Checking CHANGELOG.md..." -cat CHANGELOG.md | grep ^${LIBRARY_VERSION} > /dev/null 2>&1 -if [[ $? -eq 1 ]]; then +if ! grep "^${LIBRARY_VERSION}" CHANGELOG.md > /dev/null 2>&1; then warning "Changes missing for version ${LIBRARY_VERSION}! Please update CHANGELOG.md." exit 1 else @@ -70,8 +67,7 @@ fi printf "\n" inform "Checking for git tag ${LIBRARY_VERSION}..." -git tag -l | grep -E "${LIBRARY_VERSION}$" -if [[ $? -eq 1 ]]; then +if ! git tag -l | grep -E "${LIBRARY_VERSION}$"; then warning "Missing git tag for version ${LIBRARY_VERSION}" fi printf "\n" diff --git a/install.sh b/install.sh index d42c48d..bb671f2 100755 --- a/install.sh +++ b/install.sh @@ -1,14 +1,13 @@ #!/bin/bash -LIBRARY_NAME=`grep -m 1 name pyproject.toml | awk -F" = " '{print substr($2,2,length($2)-2)}'` +LIBRARY_NAME=$(grep -m 1 name pyproject.toml | awk -F" = " '{print substr($2,2,length($2)-2)}') CONFIG_FILE=config.txt CONFIG_DIR="/boot/firmware" -DATESTAMP=`date "+%Y-%m-%d-%H-%M-%S"` +DATESTAMP=$(date "+%Y-%m-%d-%H-%M-%S") CONFIG_BACKUP=false APT_HAS_UPDATED=false 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 @@ -18,7 +17,7 @@ CMD_ERRORS=false user_check() { - if [ $(id -u) -eq 0 ]; then + if [ "$(id -u)" -eq 0 ]; then fatal "Script should not be run as root. Try './install.sh'\n" fi } @@ -36,15 +35,6 @@ confirm() { fi } -prompt() { - read -r -p "$1 [y/N] " response < /dev/tty - if [[ $response =~ ^(yes|y|Y)$ ]]; then - true - else - false - fi -} - success() { echo -e "$(tput setaf 2)$1$(tput sgr0)" } @@ -79,10 +69,10 @@ find_config() { venv_bash_snippet() { inform "Checking for $VENV_BASH_SNIPPET\n" - if [ ! -f $VENV_BASH_SNIPPET ]; then + if [ ! -f "$VENV_BASH_SNIPPET" ]; then inform "Creating $VENV_BASH_SNIPPET\n" - mkdir -p $RESOURCES_TOP_DIR - cat << EOF > $VENV_BASH_SNIPPET + 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" @@ -98,21 +88,23 @@ EOF } venv_check() { - PYTHON_BIN=`which $PYTHON` + 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 + 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 + mkdir -p "$VENV_DIR" + /usr/bin/python3 -m venv "$VENV_DIR" --system-site-packages venv_bash_snippet - source $VENV_DIR/bin/activate + # shellcheck disable=SC1091 + 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 + printf "source \"%s/bin/activate\"\n" "$VENV_DIR" + # shellcheck disable=SC1091 + source "$VENV_DIR/bin/activate" fi else printf "\n" @@ -125,7 +117,7 @@ venv_check() { check_for_error() { if [ $? -ne 0 ]; then CMD_ERRORS=true - warning "^^^ 😬" + warning "^^^ 😬 previous command did not exit cleanly!" fi } @@ -134,29 +126,29 @@ function do_config_backup { CONFIG_BACKUP=true FILENAME="config.preinstall-$LIBRARY_NAME-$DATESTAMP.txt" 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_DIR/$CONFIG_FILE $RESOURCES_TOP_DIR/config-backups/$FILENAME + sudo cp "$CONFIG_DIR/$CONFIG_FILE" "$CONFIG_DIR/$FILENAME" + mkdir -p "$RESOURCES_TOP_DIR/config-backups/" + cp $CONFIG_DIR/$CONFIG_FILE "$RESOURCES_TOP_DIR/config-backups/$FILENAME" if [ -f "$UNINSTALLER" ]; then - echo "cp $RESOURCES_TOP_DIR/config-backups/$FILENAME $CONFIG_DIR/$CONFIG_FILE" >> $UNINSTALLER + echo "cp $RESOURCES_TOP_DIR/config-backups/$FILENAME $CONFIG_DIR/$CONFIG_FILE" >> "$UNINSTALLER" fi fi } function apt_pkg_install { - PACKAGES=() + PACKAGES_NEEDED=() 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 - printf "Checking for $PACKAGE\n" - dpkg -L $PACKAGE > /dev/null 2>&1 + printf "Checking for %s\n" "$PACKAGE" + dpkg -L "$PACKAGE" > /dev/null 2>&1 if [ "$?" == "1" ]; then - PACKAGES+=("$PACKAGE") + PACKAGES_NEEDED+=("$PACKAGE") fi done - PACKAGES="${PACKAGES[@]}" + PACKAGES="${PACKAGES_NEEDED[*]}" if ! [ "$PACKAGES" == "" ]; then printf "\n" inform "Installing missing packages: $PACKAGES" @@ -164,10 +156,10 @@ function apt_pkg_install { sudo apt update APT_HAS_UPDATED=true fi - sudo apt install -y $PACKAGES + sudo apt install -y "$PACKAGES" check_for_error if [ -f "$UNINSTALLER" ]; then - echo "apt uninstall -y $PACKAGES" >> $UNINSTALLER + echo "apt uninstall -y $PACKAGES" >> "$UNINSTALLER" fi fi } @@ -196,8 +188,8 @@ while [[ $# -gt 0 ]]; do ;; *) if [[ $1 == -* ]]; then - printf "Unrecognised option: $1\n"; - printf "Usage: $USAGE\n"; + printf "Unrecognised option: %s\n" "$1"; + printf "Usage: %s\n" "$USAGE"; exit 1 fi POSITIONAL_ARGS+=("$1") @@ -205,16 +197,16 @@ while [[ $# -gt 0 ]]; do esac done -printf "Installing $LIBRARY_NAME...\n\n" +printf "Installing %s...\n\n" "$LIBRARY_NAME" user_check venv_check -if [ ! -f `which $PYTHON` ]; then - fatal "Python path $PYTHON not found!\n" +if [ ! -f "$(which "$PYTHON")" ]; then + fatal "Python path %s not found!\n" "$PYTHON" fi -PYTHON_VER=`$PYTHON --version` +PYTHON_VER=$($PYTHON --version) inform "Checking Dependencies. Please wait..." @@ -222,7 +214,8 @@ inform "Checking Dependencies. Please wait..." pip_pkg_install toml -CONFIG_VARS=`$PYTHON - < $UNINSTALLER +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" @@ -284,27 +279,32 @@ if $UNSTABLE; then pip_pkg_install . else inform "Installing stable library from pypi.\n" - pip_pkg_install $LIBRARY_NAME + pip_pkg_install "$LIBRARY_NAME" fi +# shellcheck disable=SC2181 # One of two commands run, depending on --unstable flag if [ $? -eq 0 ]; then success "Done!\n" - echo "$PYTHON -m pip uninstall $LIBRARY_NAME" >> $UNINSTALLER + echo "$PYTHON -m pip uninstall $LIBRARY_NAME" >> "$UNINSTALLER" fi -cd $WD - find_config +printf "\n" + # Run the setup commands from pyproject.toml / tool.pimoroni.commands +inform "Running setup commands...\n" for ((i = 0; i < ${#SETUP_CMDS[@]}; i++)); do CMD="${SETUP_CMDS[$i]}" # 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 + if [[ ! "$CMD" == printf* ]]; then + printf "Running: \"%s\"\n" "$CMD" + fi + eval "$CMD" check_for_error done @@ -319,7 +319,7 @@ for ((i = 0; i < ${#CONFIG_TXT[@]}; i++)); do 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 + printf "%s \n" "$CONFIG_LINE" | sudo tee --append $CONFIG_DIR/$CONFIG_FILE fi fi done @@ -331,8 +331,8 @@ printf "\n" if [ -d "examples" ]; then if confirm "Would you like to copy examples to $RESOURCES_DIR?"; then inform "Copying examples to $RESOURCES_DIR" - cp -r examples/ $RESOURCES_DIR - echo "rm -r $RESOURCES_DIR" >> $UNINSTALLER + cp -r examples/ "$RESOURCES_DIR" + echo "rm -r $RESOURCES_DIR" >> "$UNINSTALLER" success "Done!" fi fi @@ -345,8 +345,7 @@ if confirm "Would you like to generate documentation?"; then inform "Installing pdoc. Please wait..." pip_pkg_install pdoc inform "Generating documentation.\n" - $PYTHON -m pdoc $LIBRARY_NAME -o $RESOURCES_DIR/docs > /dev/null - if [ $? -eq 0 ]; then + if $PYTHON -m pdoc "$LIBRARY_NAME" -o "$RESOURCES_DIR/docs" > /dev/null; then inform "Documentation saved to $RESOURCES_DIR/docs" success "Done!" else @@ -360,16 +359,16 @@ 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" + printf "If things still don't work, report this or find help at %s.\n\n" "$GITHUB_URL" 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" +printf "Find uninstall steps in %s\n\n" "$UNINSTALLER" if [ "$CMD_ERRORS" = true ]; then exit 1 else exit 0 -fi \ No newline at end of file +fi diff --git a/uninstall.sh b/uninstall.sh index f213fc5..3314b7f 100755 --- a/uninstall.sh +++ b/uninstall.sh @@ -1,13 +1,13 @@ #!/bin/bash FORCE=false -LIBRARY_NAME=`grep -m 1 name pyproject.toml | awk -F" = " '{print substr($2,2,length($2)-2)}'` +LIBRARY_NAME=$(grep -m 1 name pyproject.toml | awk -F" = " '{print substr($2,2,length($2)-2)}') RESOURCES_DIR=$HOME/Pimoroni/$LIBRARY_NAME PYTHON="python" venv_check() { - PYTHON_BIN=`which $PYTHON` + PYTHON_BIN=$(which $PYTHON) if [[ $VIRTUAL_ENV == "" ]] || [[ $PYTHON_BIN != $VIRTUAL_ENV* ]]; then printf "This script should be run in a virtual Python environment.\n" exit 1 @@ -15,7 +15,7 @@ venv_check() { } user_check() { - if [ $(id -u) -eq 0 ]; then + if [ "$(id -u)" -eq 0 ]; then printf "Script should not be run as root. Try './uninstall.sh'\n" exit 1 fi @@ -55,17 +55,17 @@ warning() { echo -e "$(tput setaf 1)$1$(tput sgr0)" } -printf "$LIBRARY_NAME Python Library: Uninstaller\n\n" +printf "%s Python Library: Uninstaller\n\n" "$LIBRARY_NAME" user_check venv_check printf "Uninstalling for Python 3...\n" -$PYTHON -m pip uninstall $LIBRARY_NAME +$PYTHON -m pip uninstall "$LIBRARY_NAME" -if [ -d $RESOURCES_DIR ]; then +if [ -d "$RESOURCES_DIR" ]; then if confirm "Would you like to delete $RESOURCES_DIR?"; then - rm -r $RESOURCES_DIR + rm -r "$RESOURCES_DIR" fi fi