diff --git a/run b/run new file mode 100755 index 000000000..90363c00b --- /dev/null +++ b/run @@ -0,0 +1,214 @@ +#!/usr/bin/env bash +# +# usage: ./run.sh command [argument ...] +# +# Commands used during development / CI. +# Also, executable documentation for project dev practices. +# +# See https://death.andgravity.com/run-sh +# for an explanation of how it works and why it's useful. + +# First, set up the environment. +# (Check the notes at the end when changing this.) + +set -o nounset +set -o pipefail +set -o errexit + +# Enable this to echo commands as they are executed. +#set -o xtrace + +# Change the current directory to the project root. +PROJECT_ROOT=${0%/*} +if [[ $0 != $PROJECT_ROOT && $PROJECT_ROOT != "" ]]; then + cd "$PROJECT_ROOT" +fi +readonly PROJECT_ROOT=$(pwd) + +# Store the absolute path to this script (useful for recursion). +readonly SCRIPT="$PROJECT_ROOT/$(basename "$0")" + +################################################################################ +# Commands follow. + +function all { + checks +} + +function install_pio { + echo "๐Ÿ“ฆ Installing PlatformIO project" + pio project init +} + +function install_frontend { + echo "๐Ÿ“ฆ Installing frontend dependencies" + ( cd web && npm install ) +} + +function install { + echo "๐Ÿ“ฆ Installing project and dependencies" + install_pio + install_frontend +} + +function clean { + echo "๐Ÿงน Cleaning up" + rm -rf .pio/ + rm -rf web/node_modules/ +} + +function build_pio { + echo "๐Ÿ—๏ธ Building the PlatformIO project" + pio run +} + +function build_frontend { + echo "๐Ÿ—๏ธ Building the frontend" + pio run -t webUI +} + +function build { + echo "๐Ÿ—๏ธ Building the project" + build_frontend + build_pio +} + +function upload { + echo "๐Ÿš€ Uploading the project" + build + pio run -t upload +} + +function upload_monitor { + echo "๐Ÿš€ ๐Ÿ–ฅ๏ธ Uploading the project and opening the serial monitor" + build + pio run -t upload --target monitor +} + +function dev { + echo "๐Ÿ‘ฉโ€๐Ÿ’ป Starting development environment" + # TODO +} + +function format { + echo "๐Ÿ“ Formatting code" + # TODO +} + +function lint { + echo "๐Ÿ” Linting code" + # TODO +} + +function test { + echo "๐Ÿงช Running unit tests" + # Run tests +} + +function checks { + echo "๐Ÿ” Running all checks" + # Run all checks + format + lint + test +} + +function ci-test { + echo "๐Ÿงช Running tests in CI" + +} + + +function help { + # list all "public" functions (those not beginning with an underscore) + # defined in this file + printf "%s [args]\n\nTasks:\n" "${0}" + compgen -A function | grep -v "^_" | cat -n + + printf "\nExtended help:\n Each task has comments for general usage\n" +} + +################################################################################ +# Helper functions + +once_hash_array=() +function _once { + # Run a command only once during the execution of this script, even if it's + # called multiple times. + # + # Usage: + # _once [argument ...] + # + # Example: + # _once echo "Hello" + # _once echo "Hello" # won't be executed + + local command="$*" + local hash=$(echo "$command" | shasum | cut -d' ' -f1) + if [[ ! " ${once_hash_array[@]} " =~ " ${hash} " ]]; then + once_hash_array+=("$hash") + eval "$command" + fi +} + +compose_flags="" +function _dc { + docker compose $compose_flags "$@" +} + +function _env { + echo "๐Ÿ—๏ธ Setting environment from .env and .env.defaults" + # Go through the files and export all variables not already present in + # the environment. First file has precedence! + if [ -f .env ]; then + _export_unset .env + else + # Make sure a .env file exists, otherwise docker-compose will complain + cp .env.defaults .env + fi + _export_unset .env.defaults +} + +function _export_unset { + local file="$1" + + # Need to use a temp file to avoid a subshell + local tmpfile=$(mktemp) + grep -v '^#' $file > $tmpfile + + while read -r line; do + if [[ ! "$line" =~ ^[[:space:]]*$ ]]; then + varname=$(echo "$line" | cut -d= -f1) + if [[ -z "${!varname:-}" ]]; then + eval $line + export $varname + fi + fi + done < $tmpfile + rm $tmpfile +} + +function _test_export_unset { + _export_unset .env + env | sort +} + +################################################################################ +# Commands end. + +# Dispatch to command. A simpler version would be just "$@" (with the quotes!). + +TIMEFORMAT=$'\nTask completed in %3lR' +time "${@:-help}" + +# Some dev notes for this script. +# +# The commands *require*: +# +# * The current working directory is the project root. +# * The shell options and globals are set as they are. +# +# Inspired by the following: +# - https://death.andgravity.com/run-sh +# - http://www.oilshell.org/blog/2020/02/good-parts-sketch.html +# - https://www.youtube.com/watch?v=SdmYd5hJISM&t=7s