#!/bin/bash # dot - a (mostly) one script dotfiles manager # # Copyright 2026 Edward Delaporte V # Licensed under AGPLv3 and later. # # Features # symlinks - any file in $HOME/dotfiles/files may be symlinked # # dot setup # install the necessary tools to $HOME/envs/dot # # dot link FILE # updates ~/dot.HOSTNAME.yaml # to include a symlink from ~/dotfiles/files/FILE to ~/.FILE # # dot check # check setup against ~/dot.HOSTNAME.yaml # # dot apply # apply config in ~/dot.HOSTNAME.yaml # set -v ENVS="$HOME/envs" ANSIBLE="$ENVS/ansible/bin/ansible-playbook" TASKFILE="$HOME/dot.$HOSTNAME.yaml" LINKFILES="$HOME/dotfiles/files" 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 [ "$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 "Creating $TASKFILE" echo "$EMPTY_TASKFILE" >> $TASKFILE 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 "May symlink:" cd $LINKFILES ls -p | grep -v / exit 1 fi read -d '' LINK_THIS_FILE << EOF - name: Create a symbolic link ansible.builtin.file: src: "$LINKFILES/$2" dest: "$HOME/.$2" state: "link" EOF echo " $LINK_THIS_FILE" >> $TASKFILE echo "$2 added to symlinks in $TASKFILE" echo "Run `dot apply` to create all configured symlinks" fi