feat: remove deprecated shell scripts and add Python alternatives for all of them
Some checks failed
Nix Format Check / check-format (push) Failing after 37s
Some checks failed
Nix Format Check / check-format (push) Failing after 37s
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@ -7,4 +7,7 @@ logs/*
|
|||||||
secrets/**/*.*
|
secrets/**/*.*
|
||||||
|
|
||||||
# SHA256 hashes of the encrypted secrets
|
# SHA256 hashes of the encrypted secrets
|
||||||
*.sha256
|
*.sha256
|
||||||
|
|
||||||
|
# python cache
|
||||||
|
**/__pycache__/
|
78
bin/actions/auto-start.py
Executable file
78
bin/actions/auto-start.py
Executable file
@ -0,0 +1,78 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
# Import helper functions
|
||||||
|
sys.path.append(os.path.join(os.path.expanduser("~/.dotfiles"), "bin"))
|
||||||
|
from helpers.functions import printfe, run_command
|
||||||
|
|
||||||
|
def check_command_exists(command):
|
||||||
|
"""Check if a command is available in the system"""
|
||||||
|
try:
|
||||||
|
subprocess.run(["which", command],
|
||||||
|
check=True,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE)
|
||||||
|
return True
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def list_screen_sessions():
|
||||||
|
"""List all screen sessions"""
|
||||||
|
success, output = run_command(["screen", "-ls"])
|
||||||
|
return output
|
||||||
|
|
||||||
|
def wipe_dead_sessions():
|
||||||
|
"""Check and clean up dead screen sessions"""
|
||||||
|
screen_list = list_screen_sessions()
|
||||||
|
if "Dead" in screen_list:
|
||||||
|
print("Found dead sessions, cleaning up...")
|
||||||
|
run_command(["screen", "-wipe"])
|
||||||
|
|
||||||
|
def is_app_running(app_name):
|
||||||
|
"""Check if an app is already running in a screen session"""
|
||||||
|
screen_list = list_screen_sessions()
|
||||||
|
return app_name in screen_list
|
||||||
|
|
||||||
|
def start_app(app_name, command):
|
||||||
|
"""Start an application in a screen session"""
|
||||||
|
printfe("green", f"Starting {app_name} with command: {command}...")
|
||||||
|
run_command(["screen", "-dmS", app_name] + command.split())
|
||||||
|
time.sleep(1) # Give it a moment to start
|
||||||
|
|
||||||
|
def main():
|
||||||
|
# Define dictionary with app_name => command mapping
|
||||||
|
apps = {
|
||||||
|
"vesktop": "vesktop",
|
||||||
|
"ktailctl": "flatpak run org.fkoehler.KTailctl",
|
||||||
|
"ulauncher": "ulauncher --no-window-shadow --hide-window"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Clean up dead sessions if any
|
||||||
|
wipe_dead_sessions()
|
||||||
|
|
||||||
|
print("Starting auto-start applications...")
|
||||||
|
for app_name, command in apps.items():
|
||||||
|
# Get the binary name (first part of the command)
|
||||||
|
command_binary = command.split()[0]
|
||||||
|
|
||||||
|
# Check if the command exists
|
||||||
|
if check_command_exists(command_binary):
|
||||||
|
# Check if the app is already running
|
||||||
|
if is_app_running(app_name):
|
||||||
|
printfe("yellow", f"{app_name} is already running. Skipping...")
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Start the application
|
||||||
|
start_app(app_name, command)
|
||||||
|
|
||||||
|
# Display screen sessions
|
||||||
|
print(list_screen_sessions())
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
@ -1,33 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
source $DOTFILES_PATH/bin/helpers/functions.sh
|
|
||||||
|
|
||||||
# Define associative array with app_name => command mapping
|
|
||||||
declare -A apps=(
|
|
||||||
["vesktop"]="vesktop"
|
|
||||||
["ktailctl"]="flatpak run org.fkoehler.KTailctl"
|
|
||||||
["ulauncher"]="ulauncher --no-window-shadow --hide-window"
|
|
||||||
)
|
|
||||||
|
|
||||||
# check if screen has any dead sessions
|
|
||||||
if screen -list | grep -q "Dead"; then
|
|
||||||
screen -wipe
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Starting auto-start applications..."
|
|
||||||
for app_name in "${!apps[@]}"; do
|
|
||||||
command="${apps[$app_name]}"
|
|
||||||
command_binary=$(echo $command | awk '{print $1}')
|
|
||||||
|
|
||||||
if [ -x "$(command -v $command_binary)" ]; then
|
|
||||||
if screen -list | grep -q $app_name; then
|
|
||||||
printfe "%s\n" "yellow" "$app_name is already running. Skipping..."
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
|
|
||||||
printfe "%s\n" "green" "Starting $app_name with command: $command..."
|
|
||||||
screen -dmS $app_name $command
|
|
||||||
sleep 1
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
screen -ls
|
|
30
bin/actions/hello.py
Executable file
30
bin/actions/hello.py
Executable file
@ -0,0 +1,30 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# Import helper functions
|
||||||
|
sys.path.append(os.path.join(os.path.expanduser("~/.dotfiles"), "bin"))
|
||||||
|
from helpers.functions import printfe, logo, _rainbow_color
|
||||||
|
|
||||||
|
def welcome():
|
||||||
|
"""Display welcome message with hostname and username"""
|
||||||
|
print()
|
||||||
|
|
||||||
|
# Get hostname and username
|
||||||
|
hostname = os.uname().nodename
|
||||||
|
username = os.environ.get("USER", os.environ.get("USERNAME", "user"))
|
||||||
|
|
||||||
|
print("\033[36mYou're logged in on [", end="")
|
||||||
|
print(_rainbow_color(hostname), end="")
|
||||||
|
print("\033[36m] as [", end="")
|
||||||
|
print(_rainbow_color(username), end="")
|
||||||
|
print("\033[36m]\033[0m")
|
||||||
|
|
||||||
|
def main():
|
||||||
|
logo(continue_after=True)
|
||||||
|
welcome()
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
@ -1,20 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
source $HOMEsource $DOTFILES_PATH/bin/helpers/functions.sh
|
|
||||||
|
|
||||||
welcome() {
|
|
||||||
echo
|
|
||||||
tput setaf 6
|
|
||||||
printf "You're logged in on ["
|
|
||||||
printf $HOSTNAME | lolcat
|
|
||||||
tput setaf 6
|
|
||||||
printf "] as "
|
|
||||||
printf "["
|
|
||||||
printf $USER | lolcat
|
|
||||||
tput setaf 6
|
|
||||||
printf "]\n"
|
|
||||||
tput sgr0
|
|
||||||
}
|
|
||||||
|
|
||||||
logo continue
|
|
||||||
welcome
|
|
28
bin/actions/help.py
Executable file
28
bin/actions/help.py
Executable file
@ -0,0 +1,28 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# Import helper functions
|
||||||
|
sys.path.append(os.path.join(os.path.expanduser("~/.dotfiles"), "bin"))
|
||||||
|
from helpers.functions import printfe, println, logo
|
||||||
|
|
||||||
|
def main():
|
||||||
|
# Print logo
|
||||||
|
logo(continue_after=True)
|
||||||
|
|
||||||
|
# Print help
|
||||||
|
dotfiles_path = os.environ.get("DOTFILES_PATH", os.path.expanduser("~/.dotfiles"))
|
||||||
|
try:
|
||||||
|
with open(f"{dotfiles_path}/bin/resources/help.txt", "r") as f:
|
||||||
|
help_text = f.read()
|
||||||
|
print(help_text)
|
||||||
|
except Exception as e:
|
||||||
|
printfe("red", f"Error reading help file: {e}")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
println(" ", "cyan")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
@ -1,10 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
source $DOTFILES_PATH/bin/helpers/functions.sh
|
|
||||||
|
|
||||||
# Print logo
|
|
||||||
logo
|
|
||||||
|
|
||||||
# Print help
|
|
||||||
cat $DOTFILES_PATH/bin/resources/help.txt
|
|
||||||
println " " "cyan"
|
|
168
bin/actions/secrets.py
Executable file
168
bin/actions/secrets.py
Executable file
@ -0,0 +1,168 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
import hashlib
|
||||||
|
import glob
|
||||||
|
|
||||||
|
# Import helper functions
|
||||||
|
sys.path.append(os.path.join(os.path.expanduser("~/.dotfiles"), "bin"))
|
||||||
|
from helpers.functions import printfe, run_command
|
||||||
|
|
||||||
|
def is_wsl():
|
||||||
|
"""Check if running under WSL"""
|
||||||
|
try:
|
||||||
|
with open('/proc/version', 'r') as f:
|
||||||
|
return 'microsoft' in f.read().lower()
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_password():
|
||||||
|
"""Get password from 1Password"""
|
||||||
|
# Choose the appropriate op command based on WSL status
|
||||||
|
op_cmd = "op.exe" if is_wsl() else "op"
|
||||||
|
|
||||||
|
# Try to get the password
|
||||||
|
success, output = run_command([op_cmd, "item", "get", "Dotfiles Secrets", "--fields", "password"])
|
||||||
|
|
||||||
|
if not success:
|
||||||
|
printfe("red", "Failed to fetch password from 1Password.")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Check if we need to use a token
|
||||||
|
if "use 'op item get" in output:
|
||||||
|
# Extract the token
|
||||||
|
token = output.split("use 'op item get ")[1].split(" --")[0]
|
||||||
|
printfe("cyan", f"Got fetch token: {token}")
|
||||||
|
|
||||||
|
# Use the token to get the actual password
|
||||||
|
success, password = run_command(
|
||||||
|
[op_cmd, "item", "get", token, "--reveal", "--fields", "password"]
|
||||||
|
)
|
||||||
|
if not success:
|
||||||
|
return None
|
||||||
|
return password
|
||||||
|
else:
|
||||||
|
# We already got the password
|
||||||
|
return output
|
||||||
|
|
||||||
|
def prompt_for_password():
|
||||||
|
"""Ask for password manually"""
|
||||||
|
import getpass
|
||||||
|
printfe("cyan", "Enter the password manually: ")
|
||||||
|
password = getpass.getpass("")
|
||||||
|
|
||||||
|
if not password:
|
||||||
|
printfe("red", "Password cannot be empty.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
printfe("green", "Password entered successfully.")
|
||||||
|
return password
|
||||||
|
|
||||||
|
def calculate_checksum(file_path):
|
||||||
|
"""Calculate SHA256 checksum of a file"""
|
||||||
|
sha256_hash = hashlib.sha256()
|
||||||
|
with open(file_path, "rb") as f:
|
||||||
|
for byte_block in iter(lambda: f.read(4096), b""):
|
||||||
|
sha256_hash.update(byte_block)
|
||||||
|
return sha256_hash.hexdigest()
|
||||||
|
|
||||||
|
def encrypt_folder(folder_path, password):
|
||||||
|
"""Recursively encrypt files in a folder"""
|
||||||
|
for item in glob.glob(os.path.join(folder_path, "*")):
|
||||||
|
# Skip .gpg and .sha256 files
|
||||||
|
if item.endswith(".gpg") or item.endswith(".sha256"):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Handle directories recursively
|
||||||
|
if os.path.isdir(item):
|
||||||
|
encrypt_folder(item, password)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Calculate current checksum
|
||||||
|
current_checksum = calculate_checksum(item)
|
||||||
|
checksum_file = f"{item}.sha256"
|
||||||
|
|
||||||
|
# Check if file changed since last encryption
|
||||||
|
if os.path.exists(checksum_file):
|
||||||
|
with open(checksum_file, 'r') as f:
|
||||||
|
previous_checksum = f.read().strip()
|
||||||
|
|
||||||
|
if current_checksum == previous_checksum:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Remove existing .gpg file if it exists
|
||||||
|
gpg_file = f"{item}.gpg"
|
||||||
|
if os.path.exists(gpg_file):
|
||||||
|
os.remove(gpg_file)
|
||||||
|
|
||||||
|
# Encrypt the file
|
||||||
|
printfe("cyan", f"Encrypting {item}...")
|
||||||
|
cmd = [
|
||||||
|
"gpg", "--quiet", "--batch", "--yes", "--symmetric",
|
||||||
|
"--cipher-algo", "AES256", "--armor",
|
||||||
|
"--passphrase", password,
|
||||||
|
"--output", gpg_file, item
|
||||||
|
]
|
||||||
|
|
||||||
|
success, _ = run_command(cmd)
|
||||||
|
if success:
|
||||||
|
printfe("cyan", f"Staging {item} for commit...")
|
||||||
|
run_command(["git", "add", "-f", gpg_file])
|
||||||
|
|
||||||
|
# Update checksum file
|
||||||
|
with open(checksum_file, 'w') as f:
|
||||||
|
f.write(current_checksum)
|
||||||
|
else:
|
||||||
|
printfe("red", f"Failed to encrypt {item}")
|
||||||
|
|
||||||
|
def decrypt_folder(folder_path, password):
|
||||||
|
"""Recursively decrypt files in a folder"""
|
||||||
|
for item in glob.glob(os.path.join(folder_path, "*")):
|
||||||
|
# Handle .gpg files
|
||||||
|
if item.endswith(".gpg"):
|
||||||
|
output_file = item[:-4] # Remove .gpg extension
|
||||||
|
printfe("cyan", f"Decrypting {item}...")
|
||||||
|
|
||||||
|
cmd = [
|
||||||
|
"gpg", "--quiet", "--batch", "--yes", "--decrypt",
|
||||||
|
"--passphrase", password,
|
||||||
|
"--output", output_file, item
|
||||||
|
]
|
||||||
|
|
||||||
|
success, _ = run_command(cmd)
|
||||||
|
if not success:
|
||||||
|
printfe("red", f"Failed to decrypt {item}")
|
||||||
|
|
||||||
|
# Process directories recursively
|
||||||
|
elif os.path.isdir(item):
|
||||||
|
printfe("cyan", f"Decrypting folder {item}...")
|
||||||
|
decrypt_folder(item, password)
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if len(sys.argv) != 2 or sys.argv[1] not in ["encrypt", "decrypt"]:
|
||||||
|
printfe("red", "Usage: secrets.py [encrypt|decrypt]")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
# Get the dotfiles path
|
||||||
|
dotfiles_path = os.environ.get("DOTFILES_PATH", os.path.expanduser("~/.dotfiles"))
|
||||||
|
secrets_path = os.path.join(dotfiles_path, "secrets")
|
||||||
|
|
||||||
|
# Get the password
|
||||||
|
password = get_password()
|
||||||
|
if not password:
|
||||||
|
password = prompt_for_password()
|
||||||
|
|
||||||
|
# Perform the requested action
|
||||||
|
if sys.argv[1] == "encrypt":
|
||||||
|
printfe("cyan", "Encrypting secrets...")
|
||||||
|
encrypt_folder(secrets_path, password)
|
||||||
|
else: # decrypt
|
||||||
|
printfe("cyan", "Decrypting secrets...")
|
||||||
|
decrypt_folder(secrets_path, password)
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
@ -1,118 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
source $DOTFILES_PATH/bin/helpers/functions.sh
|
|
||||||
|
|
||||||
if is_wsl; then
|
|
||||||
output=$(op.exe item get "Dotfiles Secrets" --fields password)
|
|
||||||
else
|
|
||||||
output=$(op item get "Dotfiles Secrets" --fields password)
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check if command was a success
|
|
||||||
if [[ $? -ne 0 ]]; then
|
|
||||||
printfe "%s\n" "red" "Failed to fetch password from 1Password."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# In case the output does not contain use 'op item get, it means the password was fetched successfully
|
|
||||||
# Without having to reveal the password using an external command
|
|
||||||
if [[ ! $output == *"use 'op item get"* ]]; then
|
|
||||||
password=$output
|
|
||||||
else
|
|
||||||
token=$(echo "$output" | grep -oP "(?<=\[use 'op item get ).*(?= --)")
|
|
||||||
printfe "%s\n" "cyan" "Got fetch token: $token"
|
|
||||||
|
|
||||||
if is_wsl; then
|
|
||||||
password=$(op.exe item get $token --reveal --field password)
|
|
||||||
else
|
|
||||||
password=$(op item get $token --reveal --fields password)
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# only continue if password isn't empty
|
|
||||||
if [[ -z "$password" ]]; then
|
|
||||||
printfe "%s\n" "red" "Something went wrong while fetching the password from 1Password."
|
|
||||||
|
|
||||||
# Ask for manual input
|
|
||||||
printfe "%s" "cyan" "Enter the password manually: "
|
|
||||||
read -s password
|
|
||||||
echo
|
|
||||||
|
|
||||||
if [[ -z "$password" ]]; then
|
|
||||||
printfe "%s\n" "red" "Password cannot be empty."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
printfe "%s\n" "green" "Password entered successfully."
|
|
||||||
fi
|
|
||||||
|
|
||||||
encrypt_folder() {
|
|
||||||
for file in $1/*; do
|
|
||||||
# Skip if the current file is a .gpg file
|
|
||||||
if [[ $file == *.gpg ]]; then
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Skip if the current file is a .sha256 file
|
|
||||||
if [[ $file == *.sha256 ]]; then
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
|
|
||||||
# If the file is a directory, call this function recursively
|
|
||||||
if [[ -d $file ]]; then
|
|
||||||
encrypt_folder $file
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
|
|
||||||
current_checksum=$(sha256sum "$file" | awk '{ print $1 }')
|
|
||||||
checksum_file="$file.sha256"
|
|
||||||
|
|
||||||
if [[ -f $checksum_file ]]; then
|
|
||||||
previous_checksum=$(cat $checksum_file)
|
|
||||||
|
|
||||||
if [[ $current_checksum == $previous_checksum ]]; then
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# If the file has an accompanying .gpg file, remove it
|
|
||||||
if [[ -f $file.gpg ]]; then
|
|
||||||
rm "$file.gpg"
|
|
||||||
fi
|
|
||||||
|
|
||||||
printfe "%s\n" "cyan" "Encrypting $file..."
|
|
||||||
gpg --quiet --batch --yes --symmetric --cipher-algo AES256 --armor --passphrase="$password" --output "$file.gpg" "$file"
|
|
||||||
|
|
||||||
printfe "%s\n" "cyan" "Staging $file for commit..."
|
|
||||||
git add -f "$file.gpg"
|
|
||||||
|
|
||||||
# Update checksum file
|
|
||||||
echo $current_checksum > "$checksum_file"
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
# Recursively decrypt all .gpg files under the folder specified, recursively call this function for sub folders!
|
|
||||||
# Keep the original file name minus the .gpg extension
|
|
||||||
decrypt_folder() {
|
|
||||||
for file in $1/*; do
|
|
||||||
# Skip if current file is a .gpg file
|
|
||||||
if [[ $file == *.gpg ]]; then
|
|
||||||
filename=$(basename $file .gpg)
|
|
||||||
printfe "%s\n" "cyan" "Decrypting $file..."
|
|
||||||
gpg --quiet --batch --yes --decrypt --passphrase="$password" --output $1/$filename $file
|
|
||||||
fi
|
|
||||||
|
|
||||||
# If file is actually a folder, call this function recursively
|
|
||||||
if [[ -d $file ]]; then
|
|
||||||
printfe "%s\n" "cyan" "Decrypting folder $file..."
|
|
||||||
decrypt_folder $file
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
if [[ "$1" == "decrypt" ]]; then
|
|
||||||
printfe "%s\n" "cyan" "Decrypting secrets..."
|
|
||||||
decrypt_folder $DOTFILES_PATH/secrets
|
|
||||||
elif [[ "$1" == "encrypt" ]]; then
|
|
||||||
printfe "%s\n" "cyan" "Encrypting secrets..."
|
|
||||||
encrypt_folder $DOTFILES_PATH/secrets
|
|
||||||
fi
|
|
163
bin/actions/update.py
Executable file
163
bin/actions/update.py
Executable file
@ -0,0 +1,163 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
# Import helper functions
|
||||||
|
sys.path.append(os.path.join(os.path.expanduser("~/.dotfiles"), "bin"))
|
||||||
|
from helpers.functions import printfe, run_command
|
||||||
|
|
||||||
|
def help_message():
|
||||||
|
"""Print help message and exit"""
|
||||||
|
printfe("green", "Usage: upgrade.py [options]")
|
||||||
|
printfe("green", "Options:")
|
||||||
|
printfe("green", " --ha, -H Upgrade Home Manager packages.")
|
||||||
|
printfe("green", " --ansible, -A Upgrade Ansible packages.")
|
||||||
|
printfe("green", " --ansible-verbose Upgrade Ansible packages with verbose output. (-vvv)")
|
||||||
|
printfe("green", " --full-speed, -F Upgrade packages and use all available cores for compilation. (Default: 8 cores)")
|
||||||
|
printfe("green", " --help, -h Display this help message.")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def ensure_ansible_collections():
|
||||||
|
"""Ensure required Ansible collections are installed"""
|
||||||
|
# List of required collections that can be expanded in the future
|
||||||
|
required_collections = [
|
||||||
|
"community.general",
|
||||||
|
]
|
||||||
|
|
||||||
|
printfe("cyan", "Checking for required Ansible collections...")
|
||||||
|
status, output = run_command(["ansible-galaxy", "collection", "list"], shell=False)
|
||||||
|
|
||||||
|
if not status:
|
||||||
|
printfe("red", "Failed to list Ansible collections")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Check each required collection and install if missing
|
||||||
|
for collection in required_collections:
|
||||||
|
if collection not in output:
|
||||||
|
printfe("yellow", f"Installing {collection} collection...")
|
||||||
|
status, install_output = run_command(["ansible-galaxy", "collection", "install", collection], shell=False)
|
||||||
|
if not status:
|
||||||
|
printfe("yellow", f"Warning: Failed to install {collection} collection: {install_output}")
|
||||||
|
printfe("yellow", f"Continuing anyway, but playbook might fail if it requires {collection}")
|
||||||
|
else:
|
||||||
|
printfe("green", f"Successfully installed {collection} collection")
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def main():
|
||||||
|
# Parse arguments
|
||||||
|
parser = argparse.ArgumentParser(add_help=False)
|
||||||
|
parser.add_argument("--ha", "-H", action="store_true", help="Upgrade Home Manager packages")
|
||||||
|
parser.add_argument("--ansible", "-A", action="store_true", help="Upgrade Ansible packages")
|
||||||
|
parser.add_argument("--ansible-verbose", action="store_true", help="Upgrade Ansible packages with verbose output")
|
||||||
|
parser.add_argument("--full-speed", "-F", action="store_true", help="Use all available cores")
|
||||||
|
parser.add_argument("--help", "-h", action="store_true", help="Display help message")
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.help:
|
||||||
|
return help_message()
|
||||||
|
|
||||||
|
# If no specific option provided, run both
|
||||||
|
if not args.ha and not args.ansible and not args.ansible_verbose:
|
||||||
|
args.ha = True
|
||||||
|
args.ansible = True
|
||||||
|
|
||||||
|
# If ansible_verbose is set, also set ansible
|
||||||
|
if args.ansible_verbose:
|
||||||
|
args.ansible = True
|
||||||
|
|
||||||
|
# Set cores and jobs based on full-speed flag
|
||||||
|
if args.full_speed:
|
||||||
|
import multiprocessing
|
||||||
|
cores = jobs = multiprocessing.cpu_count()
|
||||||
|
else:
|
||||||
|
cores = 8
|
||||||
|
jobs = 1
|
||||||
|
|
||||||
|
printfe("cyan", f"Limiting to {cores} cores with {jobs} jobs.")
|
||||||
|
|
||||||
|
# Home Manager update
|
||||||
|
if args.ha:
|
||||||
|
dotfiles_path = os.environ.get("DOTFILES_PATH", os.path.expanduser("~/.dotfiles"))
|
||||||
|
hostname = os.uname().nodename
|
||||||
|
|
||||||
|
printfe("cyan", "Updating Home Manager flake...")
|
||||||
|
os.chdir(f"{dotfiles_path}/config/home-manager")
|
||||||
|
status, output = run_command(["nix", "--extra-experimental-features", "nix-command",
|
||||||
|
"--extra-experimental-features", "flakes", "flake", "update"],
|
||||||
|
shell=False)
|
||||||
|
if not status:
|
||||||
|
printfe("red", f"Failed to update Home Manager flake: {output}")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
# Check if home-manager is installed
|
||||||
|
status, _ = run_command(["which", "home-manager"], shell=False)
|
||||||
|
if status:
|
||||||
|
printfe("cyan", "Cleaning old backup files...")
|
||||||
|
backup_file = os.path.expanduser("~/.config/mimeapps.list.backup")
|
||||||
|
if os.path.exists(backup_file):
|
||||||
|
os.remove(backup_file)
|
||||||
|
|
||||||
|
printfe("cyan", "Upgrading Home Manager packages...")
|
||||||
|
env = os.environ.copy()
|
||||||
|
env["NIXPKGS_ALLOW_UNFREE"] = "1"
|
||||||
|
|
||||||
|
cmd = ["home-manager", "--extra-experimental-features", "nix-command",
|
||||||
|
"--extra-experimental-features", "flakes", "switch", "-b", "backup",
|
||||||
|
f"--flake", f".#{hostname}", "--impure", "--cores", str(cores), "-j", str(jobs)]
|
||||||
|
|
||||||
|
result = subprocess.run(cmd, env=env)
|
||||||
|
if result.returncode != 0:
|
||||||
|
printfe("red", "Failed to upgrade Home Manager packages.")
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
printfe("red", "Home Manager is not installed.")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
# Ansible update
|
||||||
|
if args.ansible:
|
||||||
|
dotfiles_path = os.environ.get("DOTFILES_PATH", os.path.expanduser("~/.dotfiles"))
|
||||||
|
hostname = os.uname().nodename
|
||||||
|
username = os.environ.get("USER", os.environ.get("USERNAME", "user"))
|
||||||
|
|
||||||
|
# Check if ansible is installed
|
||||||
|
status, _ = run_command(["which", "ansible-playbook"], shell=False)
|
||||||
|
if not status:
|
||||||
|
printfe("yellow", "Ansible is not installed, installing it with pipx...")
|
||||||
|
status, output = run_command(["pipx", "install", "--include-deps", "ansible", "ansible-lint"], shell=False)
|
||||||
|
if not status:
|
||||||
|
printfe("red", f"Failed to install Ansible: {output}")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
# Ensure required collections are installed
|
||||||
|
if not ensure_ansible_collections():
|
||||||
|
printfe("red", "Failed to ensure required Ansible collections are installed")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
printfe("cyan", "Running Ansible playbook...")
|
||||||
|
ansible_cmd = [
|
||||||
|
"ansible-playbook",
|
||||||
|
"-i", f"{dotfiles_path}/config/ansible/inventory.ini",
|
||||||
|
f"{dotfiles_path}/config/ansible/main.yml",
|
||||||
|
"--extra-vars", f"hostname={hostname}",
|
||||||
|
"--extra-vars", f"ansible_user={username}",
|
||||||
|
"--limit", hostname,
|
||||||
|
"--ask-become-pass"
|
||||||
|
]
|
||||||
|
|
||||||
|
if args.ansible_verbose:
|
||||||
|
ansible_cmd.append("-vvv")
|
||||||
|
|
||||||
|
result = subprocess.run(ansible_cmd)
|
||||||
|
if result.returncode != 0:
|
||||||
|
printfe("red", "Failed to upgrade Ansible packages.")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
@ -1,85 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
source $DOTFILES_PATH/bin/helpers/functions.sh
|
|
||||||
|
|
||||||
help() {
|
|
||||||
printfe "%s\n" "green" "Usage: upgrade.sh [options]"
|
|
||||||
printfe "%s\n" "green" "Options:"
|
|
||||||
printfe "%s\n" "green" " --ha, -H Upgrade Home Manager packages."
|
|
||||||
printfe "%s\n" "green" " --ansible, -A Upgrade Ansible packages."
|
|
||||||
printfe "%s\n" "green" " --ansible-verbose Upgrade Ansible packages with verbose output. (-vvv)"
|
|
||||||
printfe "%s\n" "green" " --full-speed, -F Upgrade packages and use all available cores for compilation. (Default: 8 cores)"
|
|
||||||
printfe "%s\n" "green" " --help, -h Display this help message."
|
|
||||||
exit 0
|
|
||||||
}
|
|
||||||
|
|
||||||
while [[ "$#" -gt 0 ]]; do
|
|
||||||
case $1 in
|
|
||||||
--ha|-H) RUN_HA=true ;;
|
|
||||||
--ansible|-A) RUN_ANSIBLE=true ;;
|
|
||||||
--ansible-verbose)
|
|
||||||
RUN_ANSIBLE=true
|
|
||||||
ANSIBLE_VERBOSE=true ;;
|
|
||||||
--full-speed|-F) FULL_SPEED=true ;;
|
|
||||||
--help|-h) help ;;
|
|
||||||
*) echo "Unknown parameter passed: $1";
|
|
||||||
help ;;
|
|
||||||
esac
|
|
||||||
shift
|
|
||||||
done
|
|
||||||
|
|
||||||
if [[ -z "$RUN_HA" && -z "$RUN_ANSIBLE" ]]; then
|
|
||||||
RUN_HA=true
|
|
||||||
RUN_ANSIBLE=true
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check if --full-speed flag is passed, otherwise use --cores 8 -j 1
|
|
||||||
if [[ "$FULL_SPEED" == true ]]; then
|
|
||||||
CORES=$(nproc)
|
|
||||||
JOBS=$(nproc)
|
|
||||||
else
|
|
||||||
CORES=8
|
|
||||||
JOBS=1
|
|
||||||
fi
|
|
||||||
|
|
||||||
printfe "%s\n" "cyan" "Limiting to $CORES cores with $JOBS jobs."
|
|
||||||
|
|
||||||
if [[ "$RUN_HA" == true ]]; then
|
|
||||||
printfe "%s\n" "cyan" "Updating Home Manager flake..."
|
|
||||||
cd $DOTFILES_PATH/config/home-manager && nix --extra-experimental-features nix-command --extra-experimental-features flakes flake update
|
|
||||||
|
|
||||||
if command -v home-manager &> /dev/null; then
|
|
||||||
printfe "%s\n" "cyan" "Cleaning old backup files..."
|
|
||||||
rm -rf $HOME/.config/mimeapps.list.backup
|
|
||||||
|
|
||||||
printfe "%s\n" "cyan" "Upgrading Home Manager packages..."
|
|
||||||
cd $DOTFILES_PATH/config/home-manager && NIXPKGS_ALLOW_UNFREE=1 home-manager --extra-experimental-features nix-command --extra-experimental-features flakes switch -b backup --flake .#$HOSTNAME --impure --cores $CORES -j $JOBS
|
|
||||||
|
|
||||||
if [[ $? -ne 0 ]]; then
|
|
||||||
printfe "%s\n" "red" "Failed to upgrade Home Manager packages."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
printfe "%s\n" "red" "Home Manager is not installed."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "$RUN_ANSIBLE" == true ]]; then
|
|
||||||
if ! command -v ansible-playbook &> /dev/null; then
|
|
||||||
printfe "%s\n" "yellow" "Ansible is not installed, installing it with pipx..."
|
|
||||||
pipx install --include-deps ansible ansible-lint
|
|
||||||
|
|
||||||
if [[ $? -ne 0 ]]; then
|
|
||||||
printfe "%s\n" "red" "Failed to install Ansible."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
printfe "%s\n" "cyan" "Running Ansible playbook..."
|
|
||||||
cd $DOTFILES_PATH/config/ansible && ansible-playbook -i $DOTFILES_PATH/config/ansible/inventory.ini $DOTFILES_PATH/config/ansible/main.yml --extra-vars "hostname=$HOSTNAME" --extra-vars "ansible_user=$USER" --limit $HOSTNAME --ask-become-pass ${ANSIBLE_VERBOSE:+-vvv}
|
|
||||||
|
|
||||||
if [[ $? -ne 0 ]]; then
|
|
||||||
printfe "%s\n" "red" "Failed to upgrade Ansible packages."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
172
bin/dotf
172
bin/dotf
@ -1,122 +1,108 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
# strict mode
|
import os
|
||||||
set -euo pipefail
|
import sys
|
||||||
IFS=$'\n\t'
|
import subprocess
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
# Script constants
|
# Script constants
|
||||||
readonly DOTFILES_ROOT="$HOME/.dotfiles"
|
DOTFILES_ROOT = os.path.expanduser("~/.dotfiles")
|
||||||
readonly DOTFILES_BIN="$DOTFILES_ROOT/bin"
|
DOTFILES_BIN = os.path.join(DOTFILES_ROOT, "bin")
|
||||||
|
DOTFILES_PATH = DOTFILES_ROOT # For compatibility with the original scripts
|
||||||
|
|
||||||
# Source helper functions
|
# Import helper functions
|
||||||
if [[ ! -f "$DOTFILES_BIN/helpers/functions.sh" ]]; then
|
sys.path.append(DOTFILES_BIN)
|
||||||
echo "Error: Required helper functions not found"
|
from helpers.functions import printfe, logo
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
source "$DOTFILES_BIN/helpers/functions.sh"
|
|
||||||
|
|
||||||
# Command functions
|
def run_script(script_path, args):
|
||||||
update() {
|
"""Run an action script with the given arguments"""
|
||||||
local update_script="$DOTFILES_BIN/actions/update.sh"
|
if not os.path.isfile(script_path) or not os.access(script_path, os.X_OK):
|
||||||
if [[ ! -x "$update_script" ]]; then
|
printfe("red", f"Error: Script not found or not executable: {script_path}")
|
||||||
printfe "%s\n" "red" "Error: Update script not found or not executable"
|
|
||||||
return 1
|
return 1
|
||||||
fi
|
|
||||||
"$update_script" $@
|
result = subprocess.run([script_path] + args, env={**os.environ, "DOTFILES_PATH": DOTFILES_PATH})
|
||||||
}
|
return result.returncode
|
||||||
|
|
||||||
hello() {
|
def update(args):
|
||||||
local term_script="$DOTFILES_BIN/actions/hello.sh"
|
"""Run the update action"""
|
||||||
if [[ ! -x "$term_script" ]]; then
|
return run_script(f"{DOTFILES_BIN}/actions/update.py", args)
|
||||||
printfe "%s\n" "red" "Error: Terminal script not found or not executable"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
"$term_script" "$@"
|
|
||||||
}
|
|
||||||
|
|
||||||
help() {
|
def hello(args):
|
||||||
local help_script="$DOTFILES_BIN/actions/help.sh"
|
"""Run the hello action"""
|
||||||
if [[ ! -x "$help_script" ]]; then
|
return run_script(f"{DOTFILES_BIN}/actions/hello.py", args)
|
||||||
printfe "%s\n" "red" "Error: Help script not found or not executable"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
"$help_script" "$@"
|
|
||||||
}
|
|
||||||
|
|
||||||
secrets() {
|
def help(args):
|
||||||
local secrets_script="$DOTFILES_BIN/actions/secrets.sh"
|
"""Run the help action"""
|
||||||
if [[ ! -x "$secrets_script" ]]; then
|
return run_script(f"{DOTFILES_BIN}/actions/help.py", args)
|
||||||
printfe "%s\n" "red" "Error: Secrets script not found or not executable"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
"$secrets_script" "$@"
|
|
||||||
}
|
|
||||||
|
|
||||||
auto_start() {
|
def secrets(args):
|
||||||
local auto_start_script="$DOTFILES_BIN/actions/auto-start.sh"
|
"""Run the secrets action"""
|
||||||
if [[ ! -x "$auto_start_script" ]]; then
|
return run_script(f"{DOTFILES_BIN}/actions/secrets.py", args)
|
||||||
printfe "%s\n" "red" "Error: Auto-start script not found or not executable"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
"$auto_start_script" "$@"
|
|
||||||
}
|
|
||||||
|
|
||||||
ensure_git_hooks() {
|
def auto_start(args):
|
||||||
local hooks_dir="$DOTFILES_ROOT/.git/hooks"
|
"""Run the auto-start action"""
|
||||||
local target_link="$DOTFILES_BIN/actions/git"
|
return run_script(f"{DOTFILES_BIN}/actions/auto-start.py", args)
|
||||||
|
|
||||||
|
def ensure_git_hooks():
|
||||||
|
"""Ensure git hooks are correctly set up"""
|
||||||
|
hooks_dir = os.path.join(DOTFILES_ROOT, ".git/hooks")
|
||||||
|
target_link = os.path.join(DOTFILES_BIN, "actions/git")
|
||||||
|
|
||||||
# Validate target directory exists
|
# Validate target directory exists
|
||||||
if [[ ! -d "$target_link" ]]; then
|
if not os.path.isdir(target_link):
|
||||||
printfe "%s\n" "red" "Error: Git hooks source directory does not exist: $target_link"
|
printfe("red", f"Error: Git hooks source directory does not exist: {target_link}")
|
||||||
return 1
|
return 1
|
||||||
fi
|
|
||||||
|
|
||||||
# Handle existing symlink
|
# Handle existing symlink
|
||||||
if [[ -L "$hooks_dir" ]]; then
|
if os.path.islink(hooks_dir):
|
||||||
local current_link
|
current_link = os.readlink(hooks_dir)
|
||||||
current_link=$(readlink "$hooks_dir")
|
if current_link != target_link:
|
||||||
if [[ "$current_link" != "$target_link" ]]; then
|
printfe("yellow", "Incorrect git hooks symlink found. Removing and recreating...")
|
||||||
printfe "%s\n" "yellow" "Incorrect git hooks symlink found. Removing and recreating..."
|
os.remove(hooks_dir)
|
||||||
rm "$hooks_dir"
|
else:
|
||||||
else
|
|
||||||
return 0
|
return 0
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Handle existing directory
|
# Handle existing directory
|
||||||
if [[ -d "$hooks_dir" ]]; then
|
if os.path.isdir(hooks_dir) and not os.path.islink(hooks_dir):
|
||||||
printfe "%s\n" "yellow" "Removing existing hooks directory..."
|
printfe("yellow", "Removing existing hooks directory...")
|
||||||
rm -rf "$hooks_dir"
|
import shutil
|
||||||
fi
|
shutil.rmtree(hooks_dir)
|
||||||
|
|
||||||
# Create new symlink
|
# Create new symlink
|
||||||
if ln -s "$target_link" "$hooks_dir"; then
|
try:
|
||||||
printfe "%s\n" "green" "Git hooks successfully configured!"
|
os.symlink(target_link, hooks_dir)
|
||||||
else
|
printfe("green", "Git hooks successfully configured!")
|
||||||
printfe "%s\n" "red" "Failed to create git hooks symlink"
|
return 0
|
||||||
|
except Exception as e:
|
||||||
|
printfe("red", f"Failed to create git hooks symlink: {e}")
|
||||||
return 1
|
return 1
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
main() {
|
def main():
|
||||||
# Ensure we're in the correct directory
|
# Ensure we're in the correct directory
|
||||||
if [[ ! -d "$DOTFILES_ROOT" ]]; then
|
if not os.path.isdir(DOTFILES_ROOT):
|
||||||
printfe "%s\n" "red" "Error: Dotfiles directory not found"
|
printfe("red", "Error: Dotfiles directory not found")
|
||||||
exit 1
|
return 1
|
||||||
fi
|
|
||||||
|
|
||||||
# Setup git hooks
|
# Setup git hooks
|
||||||
ensure_git_hooks || exit 1
|
if ensure_git_hooks() != 0:
|
||||||
|
return 1
|
||||||
|
|
||||||
# Parse commands
|
# Parse commands
|
||||||
case "${1:-help}" in
|
command = sys.argv[1] if len(sys.argv) > 1 else "help"
|
||||||
update) shift; update "$@" ;;
|
args = sys.argv[2:]
|
||||||
help) shift; help "$@" ;;
|
|
||||||
hello) shift; hello "$@" ;;
|
|
||||||
secrets) shift; secrets "$@" ;;
|
|
||||||
auto-start) shift; auto_start "$@" ;;
|
|
||||||
*) help ;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
|
|
||||||
main "$@"
|
commands = {
|
||||||
|
"update": update,
|
||||||
|
"help": help,
|
||||||
|
"hello": hello,
|
||||||
|
"secrets": secrets,
|
||||||
|
"auto-start": auto_start
|
||||||
|
}
|
||||||
|
|
||||||
|
if command in commands:
|
||||||
|
return commands[command](args)
|
||||||
|
else:
|
||||||
|
return help([])
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
|
97
bin/helpers/functions.py
Normal file
97
bin/helpers/functions.py
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
import math
|
||||||
|
import random
|
||||||
|
try:
|
||||||
|
import pyfiglet
|
||||||
|
except ImportError:
|
||||||
|
pyfiglet = None
|
||||||
|
|
||||||
|
# Color codes for terminal output
|
||||||
|
COLORS = {
|
||||||
|
"black": "\033[0;30m",
|
||||||
|
"red": "\033[0;31m",
|
||||||
|
"green": "\033[0;32m",
|
||||||
|
"yellow": "\033[0;33m",
|
||||||
|
"blue": "\033[0;34m",
|
||||||
|
"purple": "\033[0;35m",
|
||||||
|
"cyan": "\033[0;36m",
|
||||||
|
"white": "\033[0;37m",
|
||||||
|
"reset": "\033[0m"
|
||||||
|
}
|
||||||
|
|
||||||
|
def printfe(color, message):
|
||||||
|
"""Print a formatted message with the specified color"""
|
||||||
|
color_code = COLORS.get(color.lower(), COLORS["reset"])
|
||||||
|
print(f"{color_code}{message}{COLORS['reset']}")
|
||||||
|
|
||||||
|
def println(message, color=None):
|
||||||
|
"""Print a line with optional color"""
|
||||||
|
if color:
|
||||||
|
printfe(color, message)
|
||||||
|
else:
|
||||||
|
print(message)
|
||||||
|
|
||||||
|
def _rainbow_color(text, freq=0.1, offset=0):
|
||||||
|
"""Apply rainbow colors to text similar to lolcat"""
|
||||||
|
colored_text = ""
|
||||||
|
for i, char in enumerate(text):
|
||||||
|
if char.strip(): # Only color non-whitespace characters
|
||||||
|
# Calculate RGB values using sine waves with phase shifts
|
||||||
|
r = int(127 * math.sin(freq * i + offset + 0) + 128)
|
||||||
|
g = int(127 * math.sin(freq * i + offset + 2 * math.pi / 3) + 128)
|
||||||
|
b = int(127 * math.sin(freq * i + offset + 4 * math.pi / 3) + 128)
|
||||||
|
|
||||||
|
# Apply the RGB color to the character
|
||||||
|
colored_text += f"\033[38;2;{r};{g};{b}m{char}\033[0m"
|
||||||
|
else:
|
||||||
|
colored_text += char
|
||||||
|
|
||||||
|
return colored_text
|
||||||
|
|
||||||
|
def logo(continue_after=False):
|
||||||
|
"""Display the dotfiles logo"""
|
||||||
|
try:
|
||||||
|
# Try to read logo file first for backward compatibility
|
||||||
|
logo_path = f"{os.environ.get('DOTFILES_PATH', os.path.expanduser('~/.dotfiles'))}/bin/resources/logo.txt"
|
||||||
|
if os.path.exists(logo_path):
|
||||||
|
with open(logo_path, "r") as f:
|
||||||
|
logo_text = f.read()
|
||||||
|
print(logo_text)
|
||||||
|
elif pyfiglet:
|
||||||
|
# Generate ASCII art with pyfiglet and rainbow colors
|
||||||
|
ascii_art = pyfiglet.figlet_format("Menno's Dotfiles", font='slant')
|
||||||
|
print("\n") # Add some space before the logo
|
||||||
|
|
||||||
|
# Use a random offset to vary the rainbow start position
|
||||||
|
random_offset = random.random() * 2 * math.pi
|
||||||
|
line_offset = 0
|
||||||
|
|
||||||
|
for line in ascii_art.splitlines():
|
||||||
|
# Add a little variation to each line
|
||||||
|
print(_rainbow_color(line, offset=random_offset + line_offset))
|
||||||
|
line_offset += 0.1
|
||||||
|
|
||||||
|
print("\n") # Add some space after the logo
|
||||||
|
else:
|
||||||
|
# Fallback if pyfiglet is not available
|
||||||
|
printfe("yellow", "\n\n *** Menno's Dotfiles ***\n\n")
|
||||||
|
printfe("cyan", " Note: Install pyfiglet for better logo display")
|
||||||
|
printfe("cyan", " (pip install pyfiglet)\n")
|
||||||
|
|
||||||
|
if not continue_after:
|
||||||
|
sys.exit(0)
|
||||||
|
except Exception as e:
|
||||||
|
printfe("red", f"Error displaying logo: {e}")
|
||||||
|
|
||||||
|
def run_command(command, shell=False):
|
||||||
|
"""Run a shell command and return the result"""
|
||||||
|
try:
|
||||||
|
result = subprocess.run(command, shell=shell, check=True, text=True,
|
||||||
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
|
return True, result.stdout.strip()
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
return False, e.stderr.strip()
|
@ -1,262 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
#Color print function, usage: println "message" "color"
|
|
||||||
println() {
|
|
||||||
color=$2
|
|
||||||
printfe "%s\n" $color "$1"
|
|
||||||
}
|
|
||||||
|
|
||||||
is_wsl() {
|
|
||||||
if [ -f "/proc/sys/fs/binfmt_misc/WSLInterop" ]; then
|
|
||||||
return 0
|
|
||||||
else
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
logo() {
|
|
||||||
echo "Menno's Dotfiles" | figlet | lolcat
|
|
||||||
|
|
||||||
if [[ $(trash-list | wc -l) -gt 0 ]]; then
|
|
||||||
printfe "%s" "yellow" "[!] $(trash-list | wc -l | tr -d ' ') file(s) in trash - "
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Print if repo is dirty and the count of untracked files, modified files and staged files
|
|
||||||
if [[ $(git -C $DOTFILES_PATH status --porcelain) ]]; then
|
|
||||||
printfe "%s" "yellow" "dotfiles is dirty "
|
|
||||||
printfe "%s" "red" "[$(git -C $DOTFILES_PATH status --porcelain | grep -c '^??')] untracked "
|
|
||||||
printfe "%s" "yellow" "[$(git -C $DOTFILES_PATH status --porcelain | grep -c '^ M')] modified "
|
|
||||||
printfe "%s" "green" "[$(git -C $DOTFILES_PATH status --porcelain | grep -c '^M ')] staged "
|
|
||||||
fi
|
|
||||||
|
|
||||||
printfe "%s" "blue" "[$(git -C $DOTFILES_PATH rev-parse --short HEAD)] "
|
|
||||||
if [[ $(git -C $DOTFILES_PATH log origin/master..HEAD) ]]; then
|
|
||||||
printfe "%s" "yellow" "[!] You have $(git -C $DOTFILES_PATH log origin/master..HEAD --oneline | wc -l | tr -d ' ') commit(s) to push"
|
|
||||||
fi
|
|
||||||
|
|
||||||
println "" "normal"
|
|
||||||
}
|
|
||||||
|
|
||||||
# print colored with printf (args: format, color, message ...)
|
|
||||||
printfe() {
|
|
||||||
format=$1
|
|
||||||
color=$2
|
|
||||||
shift 2
|
|
||||||
|
|
||||||
red=$(tput setaf 1)
|
|
||||||
green=$(tput setaf 2)
|
|
||||||
yellow=$(tput setaf 3)
|
|
||||||
blue=$(tput setaf 4)
|
|
||||||
magenta=$(tput setaf 5)
|
|
||||||
cyan=$(tput setaf 6)
|
|
||||||
normal=$(tput sgr0)
|
|
||||||
|
|
||||||
case $color in
|
|
||||||
"red")
|
|
||||||
color=$red
|
|
||||||
;;
|
|
||||||
"green")
|
|
||||||
color=$green
|
|
||||||
;;
|
|
||||||
"yellow")
|
|
||||||
color=$yellow
|
|
||||||
;;
|
|
||||||
"blue")
|
|
||||||
color=$blue
|
|
||||||
;;
|
|
||||||
"magenta")
|
|
||||||
color=$magenta
|
|
||||||
;;
|
|
||||||
"cyan")
|
|
||||||
color=$cyan
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
color=$normal
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
printf "$color$format$normal" "$@"
|
|
||||||
}
|
|
||||||
|
|
||||||
ensure_package_installed() {
|
|
||||||
if ! command -v $1 &>/dev/null; then
|
|
||||||
println "$1 is not installed. Please install it." "red"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
println " - $1 is available." "green"
|
|
||||||
}
|
|
||||||
|
|
||||||
ensure_sudo_privileges() {
|
|
||||||
if sudo -n true 2>/dev/null; then
|
|
||||||
return
|
|
||||||
else
|
|
||||||
println "$1" "yellow"
|
|
||||||
sudo true
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function exesudo ()
|
|
||||||
{
|
|
||||||
### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ##
|
|
||||||
#
|
|
||||||
# LOCAL VARIABLES:
|
|
||||||
#
|
|
||||||
### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ##
|
|
||||||
|
|
||||||
#
|
|
||||||
# I use underscores to remember it's been passed
|
|
||||||
local _funcname_="$1"
|
|
||||||
|
|
||||||
local params=( "$@" ) ## array containing all params passed here
|
|
||||||
local tmpfile="/dev/shm/$RANDOM" ## temporary file
|
|
||||||
local content ## content of the temporary file
|
|
||||||
local regex ## regular expression
|
|
||||||
|
|
||||||
|
|
||||||
### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ##
|
|
||||||
#
|
|
||||||
# MAIN CODE:
|
|
||||||
#
|
|
||||||
### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ##
|
|
||||||
|
|
||||||
#
|
|
||||||
# WORKING ON PARAMS:
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
#
|
|
||||||
# Shift the first param (which is the name of the function)
|
|
||||||
unset params[0] ## remove first element
|
|
||||||
# params=( "${params[@]}" ) ## repack array
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# WORKING ON THE TEMPORARY FILE:
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
content="#!/bin/bash\n\n"
|
|
||||||
|
|
||||||
#
|
|
||||||
# Write the params array
|
|
||||||
content="${content}params=(\n"
|
|
||||||
|
|
||||||
regex="\s+"
|
|
||||||
for param in "${params[@]}"
|
|
||||||
do
|
|
||||||
if [[ "$param" =~ $regex ]]
|
|
||||||
then
|
|
||||||
content="${content}\t\"${param}\"\n"
|
|
||||||
else
|
|
||||||
content="${content}\t${param}\n"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
content="$content)\n"
|
|
||||||
echo -e "$content" > "$tmpfile"
|
|
||||||
|
|
||||||
#
|
|
||||||
# Append the function source
|
|
||||||
echo "#$( type "$_funcname_" )" >> "$tmpfile"
|
|
||||||
|
|
||||||
#
|
|
||||||
# Append the call to the function
|
|
||||||
echo -e "\n$_funcname_ \"\${params[@]}\"\n" >> "$tmpfile"
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# DONE: EXECUTE THE TEMPORARY FILE WITH SUDO
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
sudo bash "$tmpfile"
|
|
||||||
rm "$tmpfile"
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve_path() {
|
|
||||||
echo "$(cd "$(dirname "$1")"; pwd)/$(basename "$1")"
|
|
||||||
}
|
|
||||||
|
|
||||||
check_or_make_symlink() {
|
|
||||||
source /home/menno/dotfiles/bin/helpers/functions.sh
|
|
||||||
|
|
||||||
SOURCE="$1"
|
|
||||||
TARGET="$2"
|
|
||||||
|
|
||||||
# Take any ~ and replace it with $HOME
|
|
||||||
SOURCE="${SOURCE/#\~/$HOME}"
|
|
||||||
TARGET="${TARGET/#\~/$HOME}"
|
|
||||||
|
|
||||||
# Ensure the parent directory of the target exists
|
|
||||||
mkdir -p "$(dirname "$TARGET")"
|
|
||||||
|
|
||||||
# if source doesn't exist it's likely a secret that hasn't been decrypted yet
|
|
||||||
if [ ! -e "$SOURCE" ]; then
|
|
||||||
printfe "%s\n" "yellow" " - Source $SOURCE doesn't exist"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
SOURCE=$(resolve_path "$SOURCE")
|
|
||||||
TARGET=$(resolve_path "$TARGET")
|
|
||||||
|
|
||||||
# Check if we have permissions to create the symlink
|
|
||||||
if [ ! -w "$(dirname "$TARGET")" ]; then
|
|
||||||
# Check if link exists
|
|
||||||
if [ -L "$TARGET" ]; then
|
|
||||||
# Check if it points to the correct location
|
|
||||||
if [ "$(readlink "$TARGET")" != "$SOURCE" ]; then
|
|
||||||
exesudo check_or_make_symlink "$SOURCE" "$TARGET"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
# Link doesn't exist but we don't have permissions to create it, so we should try to create it with sudosudo
|
|
||||||
exesudo check_or_make_symlink "$SOURCE" "$TARGET"
|
|
||||||
fi
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
# If target is already a symlink, we should check if it points to the correct location
|
|
||||||
if [ -L "$TARGET" ]; then
|
|
||||||
if [ "$(readlink "$TARGET")" != "$SOURCE" ]; then
|
|
||||||
printfe "%s\n" "yellow" " - Symlink $TARGET exists but points to the wrong location"
|
|
||||||
printfe "%s\n" "yellow" " Expected: $SOURCE"
|
|
||||||
printfe "%s\n" "yellow" " Actual: $(readlink "$TARGET")"
|
|
||||||
printfe "%s\n" "yellow" " Fixing symlink"
|
|
||||||
rm "$TARGET"
|
|
||||||
mkdir -p "$(dirname "$TARGET")"
|
|
||||||
ln -s "$SOURCE" "$TARGET"
|
|
||||||
printfe "%s\n" "green" " Created symlink $TARGET -> $SOURCE"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# If target is a file and it's not a symlink, we should back it up
|
|
||||||
if [ -f "$TARGET" ] && [ ! -L "$TARGET" ]; then
|
|
||||||
printfe "%s\n" "yellow" " - File $TARGET exists, backing up and creating symlink"
|
|
||||||
mv "$TARGET" "$TARGET.bak"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# If the target is already a symlink, and it points to the correct location, we should return and be happy
|
|
||||||
if [ -L "$TARGET" ]; then
|
|
||||||
printfe "%s" "green" " - OK: "
|
|
||||||
printfe "%-30s" "blue" "$SOURCE"
|
|
||||||
printfe "%s" "cyan" " -> "
|
|
||||||
printfe "%-30s\n" "blue" "$TARGET"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Create the symlink
|
|
||||||
mkdir -p "$(dirname "$TARGET")"
|
|
||||||
ln -s "$SOURCE" "$TARGET"
|
|
||||||
|
|
||||||
# Check if the symlink was created successfully
|
|
||||||
if [ ! -L "$TARGET" ]; then
|
|
||||||
printfe "%s\n" "red" " - Failed to create symlink $TARGET -> $SOURCE"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
printfe "%s" "green" " - Added new symlink: "
|
|
||||||
printfe "%-30s" "blue" "$SOURCE"
|
|
||||||
printfe "%s" "cyan" " -> "
|
|
||||||
printfe "%-30s\n" "blue" "$TARGET"
|
|
||||||
}
|
|
||||||
|
|
||||||
clear_line() {
|
|
||||||
echo -en "\r"
|
|
||||||
}
|
|
@ -2,21 +2,6 @@
|
|||||||
hosts: all
|
hosts: all
|
||||||
gather_facts: true
|
gather_facts: true
|
||||||
|
|
||||||
pre_tasks:
|
|
||||||
- name: Check if community.general collection is installed
|
|
||||||
ansible.builtin.command: ansible-galaxy collection list community.general
|
|
||||||
register: collection_check
|
|
||||||
changed_when: false
|
|
||||||
ignore_errors: true
|
|
||||||
delegate_to: localhost
|
|
||||||
|
|
||||||
- name: Install required collections
|
|
||||||
ansible.builtin.command: ansible-galaxy collection install -r "{{ playbook_dir }}/requirements.yml"
|
|
||||||
delegate_to: localhost
|
|
||||||
when: collection_check.rc != 0 or "community.general" not in collection_check.stdout
|
|
||||||
changed_when: collection_check.rc != 0 or "community.general" not in collection_check.stdout
|
|
||||||
run_once: true
|
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
- name: Include global tasks
|
- name: Include global tasks
|
||||||
ansible.builtin.import_tasks: tasks/global/global.yml
|
ansible.builtin.import_tasks: tasks/global/global.yml
|
||||||
|
@ -18,5 +18,6 @@
|
|||||||
set -o pipefail
|
set -o pipefail
|
||||||
/tmp/rustup.sh -y
|
/tmp/rustup.sh -y
|
||||||
args:
|
args:
|
||||||
|
executable: /bin/bash
|
||||||
creates: ~/.cargo/bin/rustc
|
creates: ~/.cargo/bin/rustc
|
||||||
when: rust_check.rc != 0
|
when: rust_check.rc != 0
|
||||||
|
8
setup.sh
8
setup.sh
@ -414,11 +414,11 @@ main() {
|
|||||||
export PATH=$PATH:$DOTFILES_PATH/bin
|
export PATH=$PATH:$DOTFILES_PATH/bin
|
||||||
|
|
||||||
# Create new .bashrc with exports for initial `dotf update` command
|
# Create new .bashrc with exports for initial `dotf update` command
|
||||||
cat << 'EOF' >> $HOME/.bashrc
|
cat << EOF >> $HOME/.bashrc
|
||||||
export NIXPKGS_ALLOW_INSECURE=1
|
export NIXPKGS_ALLOW_INSECURE=1
|
||||||
export DOTFILES_PATH=$DOTFILES_PATH
|
export DOTFILES_PATH=${DOTFILES_PATH}
|
||||||
export PATH=$PATH:$DOTFILES_PATH/bin
|
export PATH=\$PATH:\$DOTFILES_PATH/bin
|
||||||
export PATH=$PATH:$HOME/.local/bin
|
export PATH=\$PATH:\$HOME/.local/bin
|
||||||
EOF
|
EOF
|
||||||
cp $HOME/.bashrc $HOME/.profile
|
cp $HOME/.bashrc $HOME/.profile
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user