Last active
November 26, 2024 00:00
-
-
Save pythoninthegrass/3bfb8ed6b8b1dd130a865f6207829c8d to your computer and use it in GitHub Desktop.
(Un)install pw cli password manager
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
set -e | |
BASE_URL="https://github.com" | |
ORG="sschmid" | |
REPO="pw-terminal-password-manager" | |
BIN_NAME="pw" | |
SUBDIR="src" | |
# Executes a command with sudo if NEED_SUDO is true, otherwise executes normally | |
escalate() { | |
if [[ $NEED_SUDO == true ]]; then | |
sudo "$@" | |
else | |
"$@" | |
fi | |
} | |
# Checks if write permissions exist for a file/directory, sets NEED_SUDO if sudo is required | |
check_permissions() { | |
local file parent | |
file="$1" | |
if [[ -L "$file" ]] || [[ ! -e "$file" ]]; then | |
parent="$(dirname "$file")" | |
else | |
parent="$file" | |
fi | |
if [[ ! -w "$parent" ]]; then | |
echo "No write permission for ${parent}. Prompting for sudo." | |
NEED_SUDO=true | |
if ! sudo -v; then | |
echo "Error: Sudo authentication failed." | |
exit 1 | |
fi | |
fi | |
} | |
# Creates a directory with proper permissions, using sudo if necessary | |
create_directory() { | |
local dir="$1" | |
check_permissions "$(dirname "$dir")" | |
escalate mkdir -p "$dir" | |
} | |
# Removes a file, symlink, or directory with proper permissions, using sudo if necessary | |
remove() { | |
local file="$1" | |
check_permissions "$file" | |
if [[ -f "$file" ]] || [[ -L "$file" ]]; then | |
escalate rm -f "$file" | |
elif [[ -d "$file" ]]; then | |
escalate rm -rf "$file" | |
fi | |
} | |
# Creates a symbolic link with proper permissions, using sudo if necessary | |
symlink() { | |
local src="$1" | |
local target="$2" | |
check_permissions "$(dirname "$target")" | |
escalate ln -sf "$src" "$target" | |
} | |
# Clears the command hash in both current and root shell to ensure command path is updated | |
clear_command_hash() { | |
local cmd="$1" | |
# Clear hash in current shell | |
hash -r 2>/dev/null || true | |
# If using sudo, clear hash in root shell | |
if [[ $NEED_SUDO == true ]]; then | |
sudo bash -c 'hash -r 2>/dev/null || true' | |
fi | |
# Force shell to look up the command path again | |
if type "$cmd" >/dev/null 2>&1; then | |
hash "$cmd" 2>/dev/null || true | |
fi | |
} | |
# Prints usage instructions for the install script | |
help() { | |
cat <<-DESCRIPTION >&2 | |
USAGE | |
$(basename "$0") [COMMAND] [TARGET_DIR] | |
ARGUMENTS | |
install [TARGET_DIR] Install ${BIN_NAME} to TARGET_DIR (default: /usr/local/bin) | |
--uninstall [TARGET_DIR] Uninstall ${BIN_NAME} from TARGET_DIR (default: /usr/local/bin) | |
DESCRIPTION | |
} | |
# Normalizes a file path by expanding ~ and removing double slashes and resolving .. | |
normalize_path() { | |
local path="$1" | |
# Expand ~ to $HOME | |
path="${path/#\~/$HOME}" | |
# Remove double slashes and resolve .. | |
path="$(cd "$(dirname "$path")" 2>/dev/null && pwd -P || echo "$(dirname "$path")")/$(basename "$path")" | |
echo "$path" | |
} | |
# Parses command line arguments and sets target directory and uninstall flag | |
parse_arguments() { | |
local -n _target_dir=$1 | |
local -n _uninstall=$2 | |
local arg1="$3" | |
local arg2="$4" | |
case "$arg1" in | |
--help|-h) | |
help | |
exit 0 | |
;; | |
--uninstall) | |
_uninstall=true | |
[[ -n "$arg2" ]] && _target_dir="$arg2" | |
;; | |
install) | |
[[ -n "$arg2" ]] && _target_dir="$arg2" | |
;; | |
*) | |
[[ -n "$arg1" ]] && _target_dir="$arg1" | |
;; | |
esac | |
_target_dir="$(normalize_path "$_target_dir")" | |
} | |
# Handles the uninstallation by removing files and clearing command cache | |
handle_uninstall() { | |
local target_dir source_dir bin_path | |
target_dir="$1" | |
source_dir="${target_dir}/${REPO}" | |
bin_path="${target_dir}/${BIN_NAME}" | |
if [[ ! -L "${bin_path}" ]] && [[ ! -d "${source_dir}" ]]; then | |
echo "${BIN_NAME} is not installed at ${target_dir}" | |
exit 1 | |
fi | |
remove "${source_dir}" | |
remove "${bin_path}" | |
clear_command_hash "${BIN_NAME}" | |
echo "${BIN_NAME} has been uninstalled successfully from ${target_dir}" | |
echo "Please restart your shell or run 'hash -r' to clear the command path cache" | |
} | |
# Installs by cloning the repository and creating necessary symlinks | |
install() { | |
local target_dir source_dir repo_url bin_path bin_source | |
target_dir="$1" | |
source_dir="${target_dir}/${REPO}" | |
repo_url="${BASE_URL}/${ORG}/${REPO}" | |
bin_path="${target_dir}/${BIN_NAME}" | |
bin_source="${source_dir}/${SUBDIR}/${BIN_NAME}" | |
# First remove any existing installation | |
if [[ -L "${bin_path}" ]] || [[ -d "${source_dir}" ]]; then | |
handle_uninstall "$target_dir" | |
fi | |
create_directory "$target_dir" | |
escalate git clone "${repo_url}" "${source_dir}" | |
symlink "${bin_source}" "${bin_path}" | |
clear_command_hash "${BIN_NAME}" | |
echo "${BIN_NAME} has been installed successfully to ${target_dir}" | |
} | |
main() { | |
local target_dir | |
local uninstall | |
target_dir="/usr/local/bin" | |
uninstall=false | |
parse_arguments target_dir uninstall "$@" | |
if $uninstall; then | |
handle_uninstall "$target_dir" | |
else | |
install "$target_dir" | |
fi | |
} | |
main "$@" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment