diff --git a/.github/workflows/validate_pr.yaml b/.github/workflows/validate_pr.yaml new file mode 100644 index 00000000..7377eed --- /dev/null +++ b/.github/workflows/validate_pr.yaml @@ -0,0 +1,17 @@ +--- +name: Validate Pull Request +on: # yamllint disable-line rule:truthy + pull_request: + branches: [main] + +jobs: + shellcheck: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: shellcheck + uses: ludeeus/action-shellcheck@master + env: + SHELLCHECK_OPTS: -x + with: + ignore_names: .zshrc .zprofile diff --git a/bootstrap.sh b/bootstrap.sh index 220335c..27754e4 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -4,7 +4,7 @@ # UPDATE DOTFILES # ############################################################################### -cd "$(dirname "${BASH_SOURCE}")" || exit 1; +cd "$(dirname "${BASH_SOURCE:-$0}")" || exit 1; git pull origin main; @@ -16,7 +16,8 @@ function bootstrap() { --exclude ".gitignore" \ -f"- */" \ -avh --no-perms . ~; - source ~/.bash_profile; + # shellcheck source=/dev/null + source "$HOME"/.zprofile; } if [ "$1" == "--force" ] || [ "$1" == "-f" ]; then diff --git a/dock.sh b/dock.sh index b678b3f..04f8503 100755 --- a/dock.sh +++ b/dock.sh @@ -1,31 +1,7 @@ #!/usr/bin/env bash -set -o errexit # stop the script each time a command fails -set -o nounset # stop if you attempt to use an undef variable -function bash_traceback() { - local lasterr="$?" - set +o xtrace - local code="-1" - local bash_command=${BASH_COMMAND} - echo "Error in ${BASH_SOURCE[1]}:${BASH_LINENO[0]} ('$bash_command' exited with status $lasterr)" >&2 - if [ ${#FUNCNAME[@]} -gt 2 ]; then - # Print out the stack trace described by $function_stack - echo "Traceback of ${BASH_SOURCE[1]} (most recent call last):" >&2 - for ((i=0; i < ${#FUNCNAME[@]} - 1; i++)); do - local funcname="${FUNCNAME[$i]}" - [ "$i" -eq "0" ] && funcname=$bash_command - echo -e " ${BASH_SOURCE[$i+1]}:${BASH_LINENO[$i]}\\t$funcname" >&2 - done - fi - echo "Exiting with status ${code}" >&2 - exit "${code}" -} - -# provide an error handler whenever a command exits nonzero -trap 'bash_traceback' ERR - -# propagate ERR trap handler functions, expansions and subshells -set -o errtrace +# Source the bash_traceback.sh file +source "$(dirname "$0")/bash_traceback.sh" ############################################################################### # FUNCTIONS FOR MANIPULATING MACOS DOCK # diff --git a/install.sh b/install.sh index 8dd1839..fec11d4 100755 --- a/install.sh +++ b/install.sh @@ -93,18 +93,24 @@ install () { } brew_sync() { - local toml_apps=$(yq eval 'to_entries | map(.value | to_entries | map(select(.value == "cask" or .value == "formula") | .key)) | flatten | .[]' apps.toml) + local toml_apps + toml_apps=$(yq eval 'to_entries | map(.value | to_entries | map(select(.value == "cask" or .value == "formula") | .key)) | flatten | .[]' apps.toml) toml_apps=$(echo "$toml_apps" | sed -E 's|.*/||') # get name from tapped apps (slashes in name) - local missing_formulae=$(comm -23 <(brew leaves | sort) <(echo "$toml_apps" | sort)) - local missing_casks=$(comm -23 <(brew list --cask | sort) <(echo "$toml_apps" | sort)) - local missing_apps=$(echo -e "$missing_formulae\n$missing_casks" | sort -u) + local missing_formulae + missing_formulae=$(comm -23 <(brew leaves | sort) <(echo "$toml_apps" | sort)) + local missing_casks + missing_casks=$(comm -23 <(brew list --cask | sort) <(echo "$toml_apps" | sort)) + local missing_apps + missing_apps=$(echo -e "$missing_formulae\n$missing_casks" | sort -u) if [[ -n "$missing_apps" ]]; then echo -e "❗️ \033[1;31mThe following Homebrew-installed formulae and casks are missing from apps.toml:\033[0m" + # shellcheck disable=SC2001 echo "$missing_formulae" | sed 's/^/ /' + # shellcheck disable=SC2001 echo "$missing_casks" | sed 's/^/ /' - read -rp $'❓ \e[1;31mDo you want to uninstall these apps? (y/n) \e[0m ' choice + read -rp $'❓ \e[1;31mDo you want to uninstall these apps? (y/n)\e[0m ' choice if [[ "$choice" == "y" ]]; then for app in $missing_apps; do brew uninstall "$app" @@ -119,14 +125,17 @@ brew_sync() { } uv_sync() { - local toml_apps=$(yq eval 'to_entries | map(.value | to_entries | map(select(.value == "uv") | .key)) | flatten | .[]' apps.toml) + local toml_apps + toml_apps=$(yq eval 'to_entries | map(.value | to_entries | map(select(.value == "uv") | .key)) | flatten | .[]' apps.toml) - local missing_uv_apps=$(comm -23 <(uv tool list | awk '{print $1}' | grep -v '^-*$' | sort) <(echo "$toml_apps" | sort)) + local missing_uv_apps + missing_uv_apps=$(comm -23 <(uv tool list | awk '{print $1}' | grep -v '^-*$' | sort) <(echo "$toml_apps" | sort)) if [[ -n "$missing_uv_apps" ]]; then echo -e "❗️ \033[1;31mThe following uv-installed apps are missing from apps.toml:\033[0m" + # shellcheck disable=SC2001 echo "$missing_uv_apps" | sed 's/^/ /' - read -rp $'❓ \e[1;31mDo you want to uninstall these apps? (y/n) \e[0m ' choice + read -rp $'❓ \e[1;31mDo you want to uninstall these apps? (y/n \e[0m ' choice if [[ "$choice" == "y" ]]; then for app in $missing_uv_apps; do uv tool uninstall "$app" @@ -141,9 +150,11 @@ uv_sync() { } mas_sync() { - local toml_apps=$(yq eval 'to_entries | map(.value | to_entries | map(select(.value == "mas") | .key)) | flatten | .[]' apps.toml) + local toml_apps + toml_apps=$(yq eval 'to_entries | map(.value | to_entries | map(select(.value == "mas") | .key)) | flatten | .[]' apps.toml) - local installed_mas_apps=$(mas list | sed -E 's/^([0-9]+)[[:space:]]+(.*)[[:space:]]+\(.*/\1 \2/' | sort) + local installed_mas_apps + installed_mas_apps=$(mas list | sed -E 's/^([0-9]+)[[:space:]]+(.*)[[:space:]]+\(.*/\1 \2/' | sort) # `-A` requires bash 4+, can't use Apple-provided bash which is 3.2 declare -A missing_mas_apps=() # Ensure it's initialized as an empty associative array @@ -159,7 +170,7 @@ mas_sync() { for id in "${!missing_mas_apps[@]}"; do echo -e " ${missing_mas_apps[$id]} ($id)" done - read -rp $'❓ \e[1;31mDo you want to uninstall these apps? (y/n) \e[0m ' choice + read -rp $'❓ \e[1;31mDo you want to uninstall these apps? (y/n)\e[0m ' choice if [[ "$choice" == "y" ]]; then for id in "${!missing_mas_apps[@]}"; do name="${missing_mas_apps[$id]}" @@ -187,6 +198,7 @@ fi populate_installed_apps # Use yq to parse the TOML file and store the output in a variable +# shellcheck disable=2016 parsed_toml=$(yq e 'to_entries | .[] | .key as $category | .value | to_entries[] | [$category, .key, .value] | @tsv' apps.toml) # Install apps from each category in the apps.toml file @@ -210,7 +222,7 @@ echo -e "\n🔼 \033[1;35mUpdating existing apps and packages...\033[0m" brew update brew upgrade uv tool upgrade --all -read -rp $'❓ \e[1;31mUpdate Mac App Store apps (may be slightly buggy)? (y/n) \e[0m ' choice +read -rp $'❓ \e[1;31mUpdate Mac App Store apps (may be slightly buggy)? (y/n)\e[0m ' choice if [[ "$choice" == "y" ]]; then mas upgrade fi diff --git a/local.sh b/local.sh index 52840f8..78aacdb 100755 --- a/local.sh +++ b/local.sh @@ -23,7 +23,7 @@ confirm_set () { } # Set computer name -read -rp $'❓ \e[1;31mDo you want to (re)set the name for this computer? (y/n) \e[0m ' SETNAME +read -rp $'❓ \e[1;31mDo you want to (re)set the name for this computer? (y/n)\e[0m ' SETNAME if [[ $SETNAME =~ ^[Yy]$ ]]; then confirm_set "💻 Set the name for this computer: " COMPUTERNAME sudo scutil --set ComputerName "$COMPUTERNAME" @@ -36,17 +36,17 @@ fi echo -e "📝 \033[1;34mSetting up .gitconfig.private...\033[0m" USERNAME=$(op user get --me | grep 'Name:' | sed 's/Name: *//') if [ -n "$USERNAME" ]; then - git config --file=$HOME/.gitconfig.private user.name "$USERNAME" + git config --file="$HOME"/.gitconfig.private user.name "$USERNAME" else echo "Error: User name is empty." exit 1 fi -git config --file=$HOME/.gitconfig.private user.email "$(op read "op://Private/Github/email")" -git config --file=$HOME/.gitconfig.private user.signingKey "$(op read "op://Private/Github SSH Commit Signing Key/public key")" -git config --file=$HOME/.gitconfig.private github.user "$(op read "op://Private/Github/username")" +git config --file="$HOME"/.gitconfig.private user.email "$(op read "op://Private/Github/email")" +git config --file="$HOME"/.gitconfig.private user.signingKey "$(op read "op://Private/Github SSH Commit Signing Key/public key")" +git config --file="$HOME"/.gitconfig.private github.user "$(op read "op://Private/Github/username")" # Copy all files from manual/ to ~/ -read -rp $'❓ \e[1;31mDo you want to copy and overwrite all files from manual/ to $HOME? (y/n) \e[0m ' COPYMANUAL +read -rp $'❓ \e[1;31mDo you want to copy and overwrite all files from manual/ to $HOME? (y/n)\e[0m ' COPYMANUAL if [[ $COPYMANUAL =~ ^[Yy]$ ]]; then cp -r manual/ ~/ fi @@ -54,8 +54,8 @@ fi # iStat Menus if [[ -z "$(defaults read com.bjango.istatmenus license6 2>/dev/null || echo '')" ]]; then echo -e "📝 \033[1;34mRegistering iStat Menus...\033[0m" - defaults write com.bjango.istatmenus _modelid -string $(sysctl hw.model | sed 's/hw.model: //') - defaults write com.bjango.istatmenus installDateV6 -int $(date -v +14d +%s) + defaults write com.bjango.istatmenus _modelid -string "$(sysctl hw.model | sed 's/hw.model: //')" + defaults write com.bjango.istatmenus installDateV6 -int "$(date -v +14d +%s)" ISTAT_EMAIL=$(op read "op://Private/iStat Menus 6/registered email") ISTAT_KEY=$(op read "op://Private/iStat Menus 6/license key") diff --git a/macos.sh b/macos.sh index cb73bba..deb81e4 100755 --- a/macos.sh +++ b/macos.sh @@ -132,11 +132,11 @@ while true; do sudo -n true; sleep 60; kill -0 "$$" || exit; done 2>/dev/null & ############################################################################### # Set computer name (as done via System Preferences → Sharing) -read -p "Set your computer name: " ComputerName -sudo scutil --set ComputerName $ComputerName -sudo scutil --set HostName $ComputerName -sudo scutil --set LocalHostName $ComputerName -sudo defaults write /Library/Preferences/SystemConfiguration/com.apple.smb.server NetBIOSName -string $ComputerName +read -rp "Set your computer name: " ComputerName +sudo scutil --set ComputerName "$ComputerName" +sudo scutil --set HostName "$ComputerName" +sudo scutil --set LocalHostName "$ComputerName" +sudo defaults write /Library/Preferences/SystemConfiguration/com.apple.smb.server NetBIOSName -string "$ComputerName" @@ -246,7 +246,7 @@ defaults write NSGlobalDomain ApplePressAndHoldEnabled -bool false # defaults write com.apple.screensaver askForPasswordDelay -int 0 # Save screenshots to the Downloads folder -defaults write com.apple.screencapture location -string "~/Downloads" +defaults write com.apple.screencapture location -string "$HOME/Downloads" # Save screenshots in PNG format (other options: BMP, GIF, JPG, PDF, TIFF) # defaults write com.apple.screencapture type -string "png" diff --git a/setup.sh b/setup.sh index 60b06fa..12ef19b 100755 --- a/setup.sh +++ b/setup.sh @@ -1,5 +1,8 @@ #!/usr/bin/env bash +# Source the bash_traceback.sh file +source "$(dirname "$0")/bash_traceback.sh" + # Ask for the administrator password upfront and keep alive until script has finished sudo -v while true; do sudo -n true; sleep 60; kill -0 "$$" || exit; done 2>/dev/null &