#!/bin/bash ENVS="$HOME/envs" # Python environments - including required Ansible ANSIBLE="$ENVS/ansible/bin/ansible-playbook" # Core command TASKFILE="$HOME/backup/dot.$HOSTNAME.yaml" # Backup of localhost config DOTFILES="$HOME/dotfiles" # Where to find dotfiles LINKFILES="$DOTFILES/files" # Files to symlink INSTALLS="$DOTFILES/install" # Install scripts DOT="$HOME/.dot" # Touched files go here to track installs REQS="$DOTFILES/reqs" # Python env requirements.txt files read -d '' HELP << EOF dot - a (mostly) one script dotfiles manager Copyright 2026 Edward Delaporte V Licensed under AGPLv3 and later. dot setup install ansible CLI to $ANSIBLE dot link FILE updates ~/dot.HOSTNAME.yaml to include a symlink from ~/dotfiles/files/FILE to ~/.FILE dot check check setup against ~/backup/dot.HOSTNAME.yaml dot apply apply config in ~/backup/dot.HOSTNAME.yaml dot help FEATURE setup - more information on setup all - additional features EOF read -d '' SETUP_HELP << EOF ## Setup 1. Install this file on your path 2. Clone your (or my) 'dotfiles' repository to ~/dotfiles 3. Run 'dot help' to get started. EOF read -d '' EXTRA_HELP << EOF dot install SCRIPT updates ~/dot.HOSTNAME.yaml to run ~/dotfiles/SCRIPT.sh if it has not yet been run creates ~/dot/SCRIPT.installed after SCRIPT has been run dot env ENV create Python environment ~/envs/ENV based on ~/dotfiles/reqs/ENV.txt EOF if [ ! -n "$1" ] || [ "$1" == "help" ]; then echo "$HELP" fi if [ "$1" == "help" ] && [ "$2" == "all" ]; then echo "" echo "$EXTRA_HELP" echo "" echo "$SETUP_HELP" fi if [ "$1" == "help" ] && [ "$2" == "setup" ]; then echo "" echo "$SETUP_HELP" fi # set -v # read -d '' EMPTY_TASKFILE << EOF --- - name: Set up dotfiles on $HOSTNAME gather_facts: no hosts: localhost connection: local tasks: - name: Check on $TASKFILE ansible.builtin.stat: path: $TASKFILE EOF if [[ ! -f "$TASKFILE" ]]; then echo "Creating empty $TASKFILE" mkdir -p "$HOME/backup" echo "$EMPTY_TASKFILE" >> $TASKFILE fi if [ "$1" == "setup" ]; then if [[ ! -f "$ANSIBLE" ]]; then mkdir -p "$ENVS" echo "Setting up Ansible in ~/envs/ansible" cd $HOME/envs; python3 -m venv ansible $HOME/envs/ansible/bin/pip install ansible jinja2 fi if [[ -f "$ANSIBLE" ]]; then echo "[ ok ]: Ansible setup at $ANSIBLE" fi if [[ -f "$TASKFILE" ]]; then echo "[ ok ]: dot config at $TASKFILE" fi fi if [ "$1" == "reset" ]; then read -n1 -p "Reset $TASKFILE? [y,N]" input if [[ ! $input == "Y" && ! $input == "y" ]]; then echo "Aborted by user" exit 1 fi echo "$EMPTY_TASKFILE" > $TASKFILE echo "" fi if [ "$1" == "check" ]; then $ANSIBLE $TASKFILE --check --diff fi if [ "$1" == "link" ]; then if [ ! -n "$2" ]; then echo "Can symlink: (from $LINKFILES)" cd $LINKFILES ls -p | grep -v / exit 1 fi read -d '' LINK_THIS_FILE << EOF - name: symlink $2 ansible.builtin.file: src: "$LINKFILES/$2" dest: "$HOME/.$2" state: "link" EOF if [[ "$2" == *"."* ]];then # File already contains a dot read -d '' LINK_THIS_FILE << EOF - name: symlink $2 ansible.builtin.file: src: "$LINKFILES/$2" dest: "$HOME/$2" state: "link" EOF fi echo " $LINK_THIS_FILE" >> $TASKFILE echo "$2 added to symlinks in $TASKFILE" echo "Run 'dot apply' to create all configured symlinks" fi if [ "$1" == "apply" ]; then if [[ ! -f "$TASKFILE" ]]; then echo "$TASKFILE is missing. Run 'dot setup'" exit 1 fi $ANSIBLE $TASKFILE fi if [ "$1" == "install" ]; then if [ ! -n "$2" ]; then echo "Can install: (.sh scripts in $INSTALLS)" cd $INSTALLS ls -p | grep -v / | grep .sh exit 1 fi if [[ ! -f "$INSTALLS/$2.sh" ]]; then echo "No such install script $INSTALLS/$2.sh" echo "Run dot install with no params to list scripts." exit 1 fi mkdir -p $DOT read -d '' RUN_INSTALL << EOF - name: check $2 stat: path: "$DOT/$2.installed" register: $2_installed - name: install $2 ansible.builtin.shell: | "$INSTALLS/$2.sh" touch "$DOT/$2.installed" when: not $2_installed.stat.exists - name: verify $2 is installed ansible.builtin.debug: msg: "$2 is installed." when: $2_installed.stat.exists EOF echo " $RUN_INSTALL" >> $TASKFILE echo "Run 'dot apply' to run the install" fi if [ "$1" == "env" ]; then if [ ! -n "$2" ]; then echo "Can create: (.txt files in $REQS)" cd $REQS ls -p | grep -v / | grep .txt exit 1 fi if [[ ! -f "$REQS/$2.txt" ]]; then echo "No such requirements file script $REQS/$2.txt" echo "Run dot env with no params to list files." exit 1 fi read -d '' ADD_ENV << EOF - name: check env $2 stat: path: "$ENVS/$2" register: $2_path - name: install $2 ansible.builtin.shell: | cd $ENVS python3 -m venv $2 $ENVS/$2/bin/python -m pip install -r $REQS/$2.txt when: not $2_path.stat.exists - name: verify env $2 exists ansible.builtin.debug: msg: "env $2 exists" when: $2_path.stat.exists EOF echo " $ADD_ENV" >> $TASKFILE echo "Run 'dot apply' to run the install" fi