aesthetic terminal experience
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

279 lines
8.1 KiB

#!/bin/sh
# Bare Metal Alchemist, 2022
#############################################
# Lead - ♄ #
#############################################
# The most basic ingredient in an Alchemy recipe, lead is used in all
# recipes in this repository to standardize some simple things that I
# rely on to make scripts concise and pleasant to use
# --------------- Escape Codes ---------------
# These constants are used to add color and text formatting to the
# terminal's output -- Further reading: ANSI Escape Codes
RED="\e[0;31m"
GREEN="\e[0;32m"
BLUE="\e[0;34m"
BOLD="\e[1m"
ITALIC="\e[3m"
ULINE="\e[4m"
RESET="\e[0m"
# TODO -- there are actually a lot of ANSI codes that can be used
# 0 - Reset (normal attributes)
# 1 - Bold or increased intensity
# 2 - Faint (opposite of bold)
# 3 - Italic
# 4 - Underline
# 5 - Blink slowly
# 6 - Blink quickly
# 7 - Swap foreground and background
# 8 - Conceal
# 9 - Cross-out
# 10 - Default font
# 11-19 - Alternate fonts
# 20 - Fraktur font
# 21 - Bold off or double underline
# 22 - Bold and faint off
# 23 - Italic and fraktur off
# 24 - Underline off
# 25 - Blink off
# 27 - Undo foreground and background swapping
# 28 - Conceal off
# 29 - Cross-out off
# 39 - Default text color
# 49 - Default background color
# 51 - Framed
# 52 - Encircled
# 53 - Overlined
# 54 - Frame and encircle off
# 55 - Overline off
# TODO -- these should be implemented as part of the syntax for "say"
# --------------- Traps ---------------
# Traps, in the case of shell scripting, listen for certain signals
# that are broadcast by the system and execute a command in response
# We only want these to activate if we're running a recipe, so we
# check to see if we're running a "shell inside of a shell"
if [ $SHLVL -gt 1 ]; then
# Make sure that ctrl+C consistently exits the script
trap "exit" INT
# Give informative error messages when we receive unguarded EXIT codes
set -e
handle_exit() {
case $? in
0)
exit
;;
*)
printf "${RED}Oops...${RESET} Something went wrong on line ${LINENO} of this script. Exit code was $?\n"
;;
esac
}
trap handle_exit EXIT
fi
# --------------- Functions ---------------
# Coding Moment: generally, whenever you see something with brackets
# at the end of it, like this() or like(this), it's a function!
# It takes inputs and gives (or does) something as an output
# --------------- Input/Output ---------------
# 'say' is a simple function that imitates "echo"
# This needs to be built out way more! Replace echo for POSIX compatibility
say() {
printf "%b\n" "${1}"
}
ask_for() {
if [ ${#} -eq 0 ]; then
say "To use this command, you need to pass the variable you want,"
say "and then add as string of text to ask for it if you want. Example:\n"
say "ask_for RESPONSE \"Could you give me a RESPONSE please?\""
say ""
say "Afterwards, you'll be able to use \$RESPONSE as a variable,"
say "and ${ITALIC}say \$RESPONSE${RESET} will respond with your entry"
return 0
fi
if [ -n "${2}" ]; then
printf "%b" "${2}"
fi
read ${1}
}
# --------------- Environment Setup ---------------
# If there's an env file, export it's contents to the environment
source_env() {
if [ -f .env ]; then
export $(grep -v '^#' .env | xargs)
else
if [ -z $ALCHEMY ]; then
say "No .env file, this might cause some issues..."
fi
fi
}
# This is how we call a function that we have already defined.
# This one takes no arguments (words after the function name
source_env
# --------------- Program Installation ---------------
3 years ago
# Checks to see if we can use a command or not
check_for() {
command -v "$1" > /dev/null
3 years ago
}
if [ "$(id -u)" -eq 0 ]; then
say "${RED}Woah there!${RESET} Seems you're running this script as a superuser."
say ""
say "That might cause some issues with permissions and whatnot. Run this script as your default user (without sudo) and I'll ask you when I need superuser permissions"
say ""
exit 1
fi
# I like using source as a keyword!
if ! check_for source; then
source() {
. ${1}
}
fi
3 years ago
# This one installs utilities to your OS (If you need them!)
install_if_needed() {
for package in "$@" # $@ means "all the arguments you passed
do
case $DISTRO in
"debian")
# TODO Better installation detection than check_for
if dpkg -l | grep -q "$package"; then
say "$package already installed!"
else
say "installing $package"
sudo apt install -y $package
fi
;;
"arch")
if pacman -Qi $package &>/dev/null; then
say "$package already installed!"
else
say "installing $package"
sudo pacman -S $package --noconfirm --needed
fi
;;
"fedora")
# TODO Better installation detection than "check_for"
if [ -z $(check_for $package) ]; then
say "installing $package"
sudo dnf install -y $package
else
say "$package already installed!"
fi
;;
"mac")
# TODO Better installation detection than "check_for"
if [ -z $(check_for $package) ]; then
say "installing $package"
brew install $package
else
say "$package already installed!"
fi
;;
esac
done
}
# --------------- Memory ---------------
# These two functions might look like gibberish because we're using regex,
# short for REGular EXpression. It's a form of matching text to a pattern.
# It takes values and stores them away in the env for later reference
forget() {
DOTENV_ENTRY=$(cat .env | grep ^${1}\=)
if [ -n "$DOTENV_ENTRY" ]; then
unset ${1}
sed -i "/^${1}.*$/d" .env
else
say "I already don't remember ${BLUE}${1}${RESET}..."
fi
# Once we've made changes, we need to make sure we're up to date
source_env
}
remember() {
# Optionally choose to output somewhere other than Alchemy
if [ "${2}" = "to" ]; then
ENV_LOCATION=${3}
else
ENV_LOCATION="$ALCHEMY/.env"
fi
KEY=$(say ${1} | cut -d'=' -f 1)
VALUE=$(say ${1} | cut -d'=' -f 2)
if say ${KEY} | grep -Eqv "^[A-Z_]+$"; then
say "Keys must consist only of capital letters and underscores"
return 1
fi
VALID_CHARS='A-Za-z0-9/_.:=-'
if say ${VALUE} | grep -Eqv "^[${VALID_CHARS}]+$"; then
say "illegal VALUE: $VALUE"
say "for key $KEY"
say "Valid characters for env values: letters, numbers, \".\",\"/\",\"_\"",\":\", \"-\"
fi
# If we're setting a valid key/value pair
if say ${1} | grep -Eq "^[A-Z_]+\=[$VALID_CHARS]+$"; then
DOTENV_ENTRY=$(cat $ENV_LOCATION | grep "^${KEY}" )
# If something already exists and we're trying to set it to something new
if [ -n "$DOTENV_ENTRY" ] && [ "$DOTENV_ENTRY" != "$1" ]; then
say "I'm trying to remember ${BLUE}${1}${RESET}, but..."
say ""
say "${BLUE}${DOTENV_ENTRY}${RESET}"
say "This has already been defined in the $ENV_LOCATION file!"
say ""
ask_for overwrite "would you like to overwrite it? ${BLUE}(y/n)${RESET} "
case $overwrite in
"y" | "Y")
unset ${KEY}
sed -i "/^${KEY}.*$/d" $ENV_LOCATION
echo "${1}" >> $ENV_LOCATION
export ${1}
;;
esac
elif [ -z "$DOTENV_ENTRY" ]; then
unset ${KEY}
echo "${1}" >> $ENV_LOCATION
export ${1}
fi
fi
# Once we've made changes to the Alchemy .env,
# we need to make sure we're up to date
if [ -z "$2" ]; then
source_env
fi
}
3 years ago
LEAD=1
# Confirm? Something to automate the y/n dialog