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.
278 lines
8.1 KiB
278 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 --------------- |
|
|
|
# Checks to see if we can use a command or not |
|
check_for() { |
|
command -v "$1" > /dev/null |
|
} |
|
|
|
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 |
|
|
|
# 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 |
|
} |
|
|
|
LEAD=1 |
|
|
|
# Confirm? Something to automate the y/n dialog
|
|
|