mirror of
https://github.com/Matir/skel.git
synced 2026-05-25 13:19:07 -07:00
Refactor
This commit is contained in:
137
bin/install_ansible.sh
Executable file
137
bin/install_ansible.sh
Executable file
@@ -0,0 +1,137 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Installs Ansible, trying user-space methods first before falling back to sudo.
|
||||
# This script is designed to be idempotent and safe to run multiple times.
|
||||
|
||||
set -e # Exit immediately if a command exits with a non-zero status.
|
||||
|
||||
# --- Helper Functions ---
|
||||
info() { echo "[INFO] $1"; }
|
||||
warn() { echo "[WARN] $1"; }
|
||||
error() { echo "[ERROR] $1" >&2; exit 1; }
|
||||
|
||||
# --- Main Logic ---
|
||||
|
||||
# 1. Check if Ansible is already installed
|
||||
if command -v ansible >/dev/null 2>&1; then
|
||||
info "Ansible is already installed at $(command -v ansible)."
|
||||
exit 0
|
||||
fi
|
||||
info "Ansible not found. Attempting installation..."
|
||||
|
||||
# 2. Try user-space installation (no sudo)
|
||||
info "--- Attempting user-space installation (no sudo required) ---"
|
||||
|
||||
# Try pipx first, as it's the cleanest user-space method
|
||||
if command -v pipx >/dev/null 2>&1; then
|
||||
info "Found pipx. Trying to install Ansible with it..."
|
||||
if pipx install ansible;
|
||||
then
|
||||
# pipx requires adding ~/.local/bin to PATH, which might not be sourced yet.
|
||||
# Check the executable directly.
|
||||
if [[ -x "${HOME}/.local/bin/ansible" ]]; then
|
||||
info "Ansible installed successfully with pipx."
|
||||
info "Please ensure '${HOME}/.local/bin' is in your PATH."
|
||||
info "You may need to restart your shell or run: export PATH=\"$HOME/.local/bin:$PATH\""
|
||||
exit 0
|
||||
else
|
||||
warn "pipx install seemed to succeed, but ansible executable not found where expected."
|
||||
fi
|
||||
else
|
||||
warn "pipx install ansible failed."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Try Python's venv module if pipx failed or wasn't present
|
||||
VENV_PATH="${HOME}/.local/share/ansible_venv"
|
||||
# Create a temp path to avoid clobbering a failed install
|
||||
VENV_TEST_PATH="/tmp/ansible_venv_test_$$"
|
||||
if python3 -m venv "${VENV_TEST_PATH}" >/dev/null 2>&1; then
|
||||
rm -rf "${VENV_TEST_PATH}" # Clean up test
|
||||
info "Python's venv module is available. Creating a virtual environment at ${VENV_PATH}..."
|
||||
python3 -m venv "${VENV_PATH}"
|
||||
if "${VENV_PATH}/bin/pip" install --quiet ansible;
|
||||
then
|
||||
info "Ansible installed successfully into a virtual environment."
|
||||
info "To use it, run: '${VENV_PATH}/bin/ansible'"
|
||||
info "To make it available everywhere, add its bin directory to your PATH:"
|
||||
info " echo 'export PATH="${VENV_PATH}/bin:$PATH"' >> ~/.profile"
|
||||
info "(You may need to source ~/.profile or restart your shell)."
|
||||
exit 0
|
||||
else
|
||||
warn "Failed to install ansible into the virtual environment."
|
||||
rm -rf "${VENV_PATH}" # Clean up failed attempt
|
||||
fi
|
||||
else
|
||||
info "Python's venv module not available or failed to create a test environment."
|
||||
fi
|
||||
|
||||
# 3. Fallback to sudo installation
|
||||
info "--- User-space installation failed. Falling back to system-wide installation (sudo required) ---"
|
||||
|
||||
if ! command -v sudo >/dev/null 2>&1; then
|
||||
error "sudo command not found. Cannot attempt system-wide installation. Aborting."
|
||||
fi
|
||||
|
||||
# Prompt for sudo password upfront so it doesn't happen in the middle of the script
|
||||
info "Sudo privileges are required. You may be prompted for your password."
|
||||
if ! sudo -v; then
|
||||
error "Failed to acquire sudo privileges. Aborting."
|
||||
fi
|
||||
|
||||
# Detect package manager and install
|
||||
if command -v apt-get >/dev/null 2>&1; then
|
||||
info "Detected Debian-based system (apt)."
|
||||
sudo apt-get update -y
|
||||
info "Attempting to install 'ansible' package..."
|
||||
if sudo apt-get install -y ansible;
|
||||
then
|
||||
info "System package 'ansible' installed successfully."
|
||||
else
|
||||
warn "Failed to install 'ansible' package directly. Trying to install prerequisites for user-space install..."
|
||||
if sudo apt-get install -y pipx;
|
||||
then
|
||||
info "Installed pipx. Attempting to install Ansible with it..."
|
||||
if pipx install ansible;
|
||||
then
|
||||
info "Ansible installed successfully with pipx."
|
||||
info "Please ensure '${HOME}/.local/bin' is in your PATH."
|
||||
exit 0
|
||||
fi
|
||||
else
|
||||
error "Failed to install 'ansible' or 'pipx' via apt. Aborting."
|
||||
fi
|
||||
fi
|
||||
elif command -v dnf >/dev/null 2>&1; then
|
||||
info "Detected Red Hat-based system (dnf)."
|
||||
info "Attempting to install 'ansible-core' package..."
|
||||
if ! sudo dnf install -y ansible-core;
|
||||
then
|
||||
error "Failed to install ansible-core via dnf. Aborting."
|
||||
fi
|
||||
elif command -v pacman >/dev/null 2>&1; then
|
||||
info "Detected Arch-based system (pacman)."
|
||||
info "Attempting to install 'ansible' package..."
|
||||
if ! sudo pacman -Syu --noconfirm ansible;
|
||||
then
|
||||
error "Failed to install ansible via pacman. Aborting."
|
||||
fi
|
||||
elif command -v brew >/dev/null 2>&1; then
|
||||
info "Detected macOS (brew)."
|
||||
info "Attempting to install 'ansible' package..."
|
||||
if ! brew install ansible;
|
||||
then
|
||||
error "Failed to install ansible via brew. Aborting."
|
||||
fi
|
||||
else
|
||||
error "Could not detect a known package manager (apt, dnf, pacman, brew). Aborting."
|
||||
fi
|
||||
|
||||
# 4. Final verification
|
||||
info "--- Verifying final installation ---"
|
||||
if command -v ansible >/dev/null 2>&1; then
|
||||
info "Ansible successfully installed at $(command -v ansible)."
|
||||
exit 0
|
||||
else
|
||||
error "Installation attempted but the 'ansible' command is still not available. Please check the output for errors."
|
||||
fi
|
||||
@@ -47,7 +47,7 @@ alias cdgr='cd $(git rev-parse --show-toplevel || echo .)'
|
||||
alias sshanon="ssh -oUserKnownHostsFile=/dev/null -oStrictHostKeyChecking=no"
|
||||
|
||||
# Straight to ipython
|
||||
alias ipy="ipython3"
|
||||
alias ipy="ipython3 --no-banner"
|
||||
|
||||
# Skip the header on bc
|
||||
alias bc="command bc -q"
|
||||
|
||||
57
dotfiles/ipython/profile_default/startup/05-venv-manager.py
Normal file
57
dotfiles/ipython/profile_default/startup/05-venv-manager.py
Normal file
@@ -0,0 +1,57 @@
|
||||
import os
|
||||
import sys
|
||||
from IPython import prompts
|
||||
|
||||
def _setup_environment():
|
||||
ip = get_ipython()
|
||||
if not ip:
|
||||
return
|
||||
|
||||
# 1. SEARCH LOGIC: Walk up the tree for a venv
|
||||
active_venv_name = None
|
||||
curr = os.getcwd()
|
||||
|
||||
while True:
|
||||
for venv_name in ['.venv', 'env', 'venv']:
|
||||
venv_path = os.path.join(curr, venv_name)
|
||||
if os.path.isdir(venv_path):
|
||||
# Platform-specific site-packages path
|
||||
py_ver = f"python{sys.version_info.major}.{sys.version_info.minor}"
|
||||
site_pkgs = os.path.join(venv_path, 'lib', py_ver, 'site-packages')
|
||||
|
||||
if os.path.exists(site_pkgs):
|
||||
if site_pkgs not in sys.path:
|
||||
sys.path.insert(0, site_pkgs)
|
||||
sys.prefix = venv_path
|
||||
active_venv_name = os.path.basename(venv_path)
|
||||
break
|
||||
|
||||
if active_venv_name or os.path.exists(os.path.join(curr, '.git')):
|
||||
break
|
||||
|
||||
parent = os.path.dirname(curr)
|
||||
if parent == curr:
|
||||
break
|
||||
curr = parent
|
||||
|
||||
# 2. PROMPT LOGIC: Inject venv name into the UI
|
||||
class VenvPrompts(prompts.Prompts):
|
||||
def in_prompt_tokens(self):
|
||||
tokens = []
|
||||
if active_venv_name:
|
||||
tokens.append((prompts.Token.Prompt, f'({active_venv_name}) '))
|
||||
|
||||
tokens.extend([
|
||||
(prompts.Token.Prompt, 'In ['),
|
||||
(prompts.Token.PromptNum, str(self.shell.execution_count)),
|
||||
(prompts.Token.Prompt, ']: '),
|
||||
])
|
||||
return tokens
|
||||
|
||||
ip.prompts = VenvPrompts(ip)
|
||||
|
||||
# Execute the setup
|
||||
_setup_environment()
|
||||
|
||||
# cleanup
|
||||
del _setup_environment, prompts
|
||||
91
install.sh
91
install.sh
@@ -6,7 +6,6 @@ set -o nounset
|
||||
set -o errexit
|
||||
set -o shwordsplit 2>/dev/null || true # Make zsh behave like bash
|
||||
|
||||
USER=${USER:-$(id -un)}
|
||||
HOME=${HOME:-$(cd ~ && pwd)}
|
||||
|
||||
case $(uname) in
|
||||
@@ -27,11 +26,12 @@ have_command() {
|
||||
}
|
||||
|
||||
prerequisites() {
|
||||
local USER=${USER:-$(id -un)}
|
||||
if have_command zsh ; then
|
||||
case $- in
|
||||
*i*)
|
||||
local shell_path
|
||||
if [ "$(uname)" = "Darwin" ]; then
|
||||
if [[ "$(uname)" = "Darwin" ]]; then
|
||||
# dscl output is "UserShell: /bin/zsh"
|
||||
shell_path="$(dscl . -read "/Users/${USER}" UserShell | awk '{print $2}')"
|
||||
else
|
||||
@@ -62,7 +62,6 @@ install_dotfile_dir() {
|
||||
done)"
|
||||
# shellcheck disable=SC2086
|
||||
find "${SRCDIR}" \( -name .git -o \
|
||||
-path "${SRCDIR}/private_dotfiles" -o \
|
||||
-name install.sh -o \
|
||||
-name README.md -o \
|
||||
-name .gitignore \
|
||||
@@ -79,11 +78,11 @@ install_dotfile_dir() {
|
||||
local FULLNAME="${BASEDIR}/${submodule}"
|
||||
local TARGET="${HOME}/.${FULLNAME#"${SRCDIR}"/}"
|
||||
mkdir -p "$(dirname "${TARGET}")"
|
||||
if test -L "${TARGET}" ; then
|
||||
if [ "$(readlink "${TARGET}")" != "${FULLNAME}" ] ; then
|
||||
if [[ -L "${TARGET}" ]] ; then
|
||||
if [[ "$(readlink "${TARGET}")" != "${FULLNAME}" ]] ; then
|
||||
echo "${TARGET} points to $(readlink "${TARGET}") not ${FULLNAME}!" >/dev/stderr
|
||||
fi
|
||||
elif test -d "${TARGET}" ; then
|
||||
elif [[ -d "${TARGET}" ]] ; then
|
||||
echo "rm -rf ${TARGET}" >/dev/stderr
|
||||
else
|
||||
ln -s -f "${FULLNAME}" "${TARGET}"
|
||||
@@ -106,13 +105,13 @@ install_basic_dir() {
|
||||
ssh_key_already_installed() {
|
||||
# Return 1 if the key isn't already installed, 0 if it is
|
||||
local AK="${HOME}/.ssh/authorized_keys"
|
||||
if [ ! -f "$AK" ] ; then
|
||||
if [[ ! -f "$AK" ]] ; then
|
||||
return 1
|
||||
fi
|
||||
# Extract the key data (field 2) from the key file, ignoring comments
|
||||
local key_data
|
||||
key_data=$(awk '/^ssh-/ {print $2}' "$1")
|
||||
if [ -z "${key_data}" ]; then
|
||||
if [[ -z "${key_data}" ]]; then
|
||||
# Not a valid key file
|
||||
return 1
|
||||
fi
|
||||
@@ -127,13 +126,13 @@ install_ssh_keys() {
|
||||
local AK="${HOME}/.ssh/authorized_keys"
|
||||
local key
|
||||
local keydir
|
||||
if test "${TRUST_ALL_KEYS}" = 1 ; then
|
||||
if [[ "${TRUST_ALL_KEYS}" = 1 ]] ; then
|
||||
keydir="${BASEDIR}/keys/ssh"
|
||||
else
|
||||
keydir="${BASEDIR}/keys/ssh/trusted"
|
||||
fi
|
||||
for key in "${keydir}"/* ; do
|
||||
if [ ! -f "${key}" ] ; then
|
||||
if [[ ! -f "${key}" ]] ; then
|
||||
continue
|
||||
fi
|
||||
if ssh_key_already_installed "${key}" ; then
|
||||
@@ -156,11 +155,11 @@ install_gpg_keys() {
|
||||
|
||||
install_known_hosts() {
|
||||
verbose 'Installing known hosts...' >&2
|
||||
if [ ! -f "${BASEDIR}/keys/known_hosts" ] ; then
|
||||
if [[ ! -f "${BASEDIR}/keys/known_hosts" ]] ; then
|
||||
return 0
|
||||
fi
|
||||
mkdir -p "${HOME}/.ssh"
|
||||
if [ -f "${HOME}/.ssh/known_hosts" ] ; then
|
||||
if [[ -f "${HOME}/.ssh/known_hosts" ]] ; then
|
||||
local tmpf="$(mktemp)"
|
||||
cat "${BASEDIR}"/keys/known_hosts "${HOME}"/.ssh/known_hosts \
|
||||
| sort -u > "$tmpf"
|
||||
@@ -180,21 +179,21 @@ setup_git_email() {
|
||||
local gc_local="${HOME}/.gitconfig.local"
|
||||
local current_email=""
|
||||
|
||||
if [ -f "${gc_local}" ]; then
|
||||
if [[ -f "${gc_local}" ]]; then
|
||||
current_email=$(git config -f "${gc_local}" user.email || true)
|
||||
fi
|
||||
|
||||
if [ -n "${GIT_EMAIL:-}" ]; then
|
||||
if [[ -n "${GIT_EMAIL:-}" ]]; then
|
||||
# Use environment variable
|
||||
git config -f "${gc_local}" user.email "${GIT_EMAIL}"
|
||||
elif [ -n "${current_email}" ]; then
|
||||
elif [[ -n "${current_email}" ]]; then
|
||||
# Already has an email set
|
||||
GIT_EMAIL="${current_email}"
|
||||
else
|
||||
# Prompt the user
|
||||
echo -n "Enter git email (leave blank to skip): " >&2
|
||||
read -r GIT_EMAIL || true
|
||||
if [ -n "${GIT_EMAIL}" ]; then
|
||||
if [[ -n "${GIT_EMAIL}" ]]; then
|
||||
git config -f "${gc_local}" user.email "${GIT_EMAIL}"
|
||||
fi
|
||||
fi
|
||||
@@ -204,7 +203,7 @@ setup_git_email() {
|
||||
read_saved_prefs() {
|
||||
# Can't use basedir here as we don't have it yet
|
||||
local pref_file="$(dirname "$0")/.installed-prefs"
|
||||
if [ -f "${pref_file}" ] ; then
|
||||
if [[ -f "${pref_file}" ]] ; then
|
||||
verbose "Loading saved skel preferences from ${pref_file}"
|
||||
# source is a bashism
|
||||
# shellcheck disable=SC1090
|
||||
@@ -213,46 +212,38 @@ read_saved_prefs() {
|
||||
}
|
||||
|
||||
save_prefs() {
|
||||
test "$SAVE" = 1 || return 0
|
||||
[[ "$SAVE" = 1 ]] || return 0
|
||||
local pref_file=${BASEDIR}/.installed-prefs
|
||||
(echo_pref BASEDIR
|
||||
echo_pref MINIMAL
|
||||
echo_pref INSTALL_KEYS
|
||||
echo_pref TRUST_ALL_KEYS
|
||||
echo_pref VERBOSE) > "$pref_file"
|
||||
}
|
||||
|
||||
echo_pref() {
|
||||
eval "local val=\${$1}"
|
||||
# shellcheck disable=SC2154
|
||||
echo ": \${$1:=${val}}"
|
||||
{
|
||||
echo "BASEDIR=\"${BASEDIR}\""
|
||||
echo "MINIMAL=\"${MINIMAL}\""
|
||||
echo "INSTALL_KEYS=\"${INSTALL_KEYS}\""
|
||||
echo "TRUST_ALL_KEYS=\"${TRUST_ALL_KEYS}\""
|
||||
echo "VERBOSE=\"${VERBOSE}\""
|
||||
} > "$pref_file"
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
if [ -x "${BASEDIR}/bin/prune-broken-symlinks.sh" ]; then
|
||||
if [[ -x "${BASEDIR}/bin/prune-broken-symlinks.sh" ]]; then
|
||||
"${BASEDIR}/bin/prune-broken-symlinks.sh" -y "${HOME}/.zshrc.d"
|
||||
"${BASEDIR}/bin/prune-broken-symlinks.sh" -y "${HOME}/bin"
|
||||
fi
|
||||
}
|
||||
|
||||
verbose() {
|
||||
test "${VERBOSE:-0}" = 1 && echo "$@" >&2 || return 0
|
||||
[[ "${VERBOSE:-0}" = 1 ]] && echo "$@" >&2 || return 0
|
||||
}
|
||||
|
||||
# Operations
|
||||
|
||||
install_dotfiles() {
|
||||
install_dotfile_dir "${BASEDIR}/dotfiles"
|
||||
if test -d "${BASEDIR}/private_dotfiles" && \
|
||||
test -d "${BASEDIR}/.git/git-crypt" ; then
|
||||
install_dotfile_dir "${BASEDIR}/private_dotfiles"
|
||||
fi
|
||||
if test -d "${BASEDIR}/local_dotfiles" ; then
|
||||
if [[ -d "${BASEDIR}/local_dotfiles" ]] ; then
|
||||
install_dotfile_dir "${BASEDIR}/local_dotfiles"
|
||||
fi
|
||||
if test -d "${BASEDIR}/dotfile_overlays" ; then
|
||||
if [[ -d "${BASEDIR}/dotfile_overlays" ]] ; then
|
||||
for dotfiledir in "${BASEDIR}/dotfile_overlays/"* ; do
|
||||
if test -d "${dotfiledir}" ; then
|
||||
if [[ -d "${dotfiledir}" ]] ; then
|
||||
install_dotfile_dir "${dotfiledir}"
|
||||
fi
|
||||
done
|
||||
@@ -260,21 +251,21 @@ install_dotfiles() {
|
||||
}
|
||||
|
||||
install_main() {
|
||||
if test -d "${BASEDIR}/.git" && have_command git ; then
|
||||
if [ -z "$(git -C "${BASEDIR}" status --porcelain)" ]; then
|
||||
if [[ -d "${BASEDIR}/.git" && have_command git ]] ; then
|
||||
if [[ -z "$(git -C "${BASEDIR}" status --porcelain)" ]]; then
|
||||
git -C "${BASEDIR}" pull --ff-only || true
|
||||
test "$MINIMAL" = 1 || \
|
||||
[[ "$MINIMAL" = 1 ]] || \
|
||||
git -C "${BASEDIR}" submodule update --init --recursive --depth 1
|
||||
else
|
||||
echo "Skipping self-update: repository has local changes." >&2
|
||||
fi
|
||||
fi
|
||||
test "$MINIMAL" = 1 || {
|
||||
[[ "$MINIMAL" = 1 ]] || {
|
||||
prerequisites
|
||||
# try to update dotfile overlays
|
||||
if test -d "${BASEDIR}/dotfile_overlays" ; then
|
||||
if [[ -d "${BASEDIR}/dotfile_overlays" ]] ; then
|
||||
for dotfiledir in "${BASEDIR}/dotfile_overlays/"* ; do
|
||||
if test -d "${dotfiledir}/.git" ; then
|
||||
if [[ -d "${dotfiledir}/.git" ]] ; then
|
||||
git -C "${dotfiledir}" pull --ff-only || true
|
||||
git -C "${dotfiledir}" submodule update --init --recursive --depth 1 || true
|
||||
fi
|
||||
@@ -283,7 +274,7 @@ install_main() {
|
||||
}
|
||||
install_dotfiles
|
||||
install_basic_dir "${BASEDIR}/bin" "${HOME}/bin"
|
||||
test "$INSTALL_KEYS" = 1 && install_keys
|
||||
[[ "$INSTALL_KEYS" = 1 ]] && install_keys
|
||||
save_prefs
|
||||
setup_git_email
|
||||
cleanup
|
||||
@@ -293,8 +284,8 @@ install_vim_extra() {
|
||||
local DEST="${HOME}/.vim/pack/matir-extra"
|
||||
local REPO="https://github.com/Matir/vim-extra.git"
|
||||
|
||||
if test -d "${DEST}" ; then
|
||||
if test -d "${DEST}/.git" ; then
|
||||
if [[ -d "${DEST}" ]] ; then
|
||||
if [[ -d "${DEST}/.git" ]] ; then
|
||||
# do update
|
||||
git -C "${DEST}" pull --ff-only
|
||||
git -C "${DEST}" submodule update --init
|
||||
@@ -321,7 +312,7 @@ read_saved_prefs
|
||||
: ${SAVE:=1}
|
||||
|
||||
# Check prerequisites
|
||||
if [ ! -d "$BASEDIR" ] ; then
|
||||
if [[ ! -d "$BASEDIR" ]] ; then
|
||||
echo "Please install to $BASEDIR!" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
@@ -335,10 +326,6 @@ case $OPERATION in
|
||||
dotfiles)
|
||||
install_dotfiles
|
||||
;;
|
||||
test)
|
||||
# Do nothing, just sourcing
|
||||
set +o errexit
|
||||
;;
|
||||
vim-extra)
|
||||
# Install/update extra vim modules
|
||||
install_vim_extra
|
||||
|
||||
Reference in New Issue
Block a user