Code Monkey home page Code Monkey logo

deploy-dotfiles's Introduction

deploy-dotfiles.sh

Simplified multi-platform dotfile management.

(WORK IN PROGRESS—​very much a rough draft)

General Information

What this isn’t

A good idea.

What this is

A useful, robust CLI tool, written entirely in Bash, using non-traditional features & methodologies.

I have oft found myself needing to maintain 3-4 versions of my dotfiles. A .bashrc on macOS, linux, a work machine, etc. All of which have subtle differences in the prompts, aliases, or defaults.

This projects allows for a single file with variables (denoted as {{…​}}) to be used. Key:value pairs are defined in a second file. If a matching value is found, it is substituted into the text. After parsing it’s linked to the destination.

Usage

Installation

# Assuming an ubuntu system...

#  1. General deps:
$ sudo apt-get update -y
$ sudo apt-get upgrade -y
$ sudo apt-get install gawk sed git

#  2. My deps:
$ git clone https://github.com/hre-utils/mk-conf
$ chmod +x ./mk-conf/mk-conf.sh
$ mv ./mk-conf/mk-conf.sh /usr/local/bin

#  3. This script:
$ git clone https://github.com/hre-utils/deploy-dotfiles
$ chmod +x ./deploy-dotfiles/deploy-dotfiles.sh
$ mv ./deploy-dotfiles/deploy-dotfiles.sh /usr/local/bin

Definitions

base (file)

The contents of a dotfile, containing key:value pairs substituted during compilation. Located at ./files/$NAME/base.

base (section)

The leading section of a local configuration file, specifying both per-directory options & variable definitions.

global config

Configuration settings set for compilation & deployment. It is loaded prior to per-directory local options. Located at ./config.cfg

local config

Per-directory configuration file. It is loaded after global options, thus you may override a global default for a single file. Located at ./files/$NAME/config.cfg.

.cfg (format)

A file format commonly used for config files. Characterized by bracket enclosed headings, and use of multiple brackets to denote sub-heading levels. Mine may be parsed non-traditionally, see @hre-utils/mk-conf for more info.

Directory Structure

The requisite directory structure is created at either:

  1. $XDG_DATA_HOME/hre-utils/deploy-dotfiles/, or

  2. ~/.local/share/hre_utils/deploy-dotfiles/

deploy-dotfiles/
 ├── config.cfg                  # <- global config
 ├── dist/                       # <- compiled output
 └── files/
      ├── bashrc/
      │    ├── base
      │    └── config.cfg
      └── vimrc/
           ├── base             # <- base dotfile
           └── config.cfg       # <- local config

Base files

A ‘base’ dotfile will look nearly identical to the original. An example will demonstrate the differences effectively. Given a .bashrc used on both macOS and Linux, an ls alias needs to account for the BSD variant. Below we alias ‘ll’ to the output of a variable called ‘ls_long’:

alias ll={{ls_long}}

This variable is expanded based on established keys in the local ‘config.cfg’ file.

Local config

An example can be found here. Under the [classes] heading, supply subheading(s) named for each distinct platform grouping. Given the example above, perhaps [[macos]] and [[ubuntu]]:

[classes]
[[macos]]
ls_long=ls -l -G

[[ubuntu]]
ls_long=ls -l --color --group-directories-first

Global config

An example can be found here. The global configuration file is well documented via in-line comments, though there are several features of note.

‘class’

The class= variable defines which subset of variables should be substituted on this machine from the local config. Due to this, the global config.cfg should be untracked.

‘backup_mode’

By default, if a file is found at the deployment destination it will be moved to ./backup/. This prevents unintentionally overwriting a file when experimenting.

Backed up files are named after their last modification time.

Useful opts

Adding a new file

$ ./deploy-dotfiles.sh --new PATH
  1. Creates new directory under files/

  2. Default local config.cfg is written

  3. Copies file from $PATH, or creates empty ‘base’ file

Build only

$ ./deploy-dotfiles.sh --build-only
  1. Compiles all ‘base’ files

  2. Moves into dist/, named after seconds since epoch

  3. Does not deploy to final destination

Pruning dist/

$ ./deploy-dotfiles.sh --clean [NUM]
  1. Deletes all files from dist/, save for the most recent $NUM (default 3)

What else

Safety

Decent error checking.

Sane defaults.

It should be difficult for one to accidentally nuke a config file. If an existing dotfile is found at the deployment location, it is backed up via one of several methods:

  1. Moved (default): re-located to the backup directory, renamed to last modification time

  2. In-place: given .bak suffix

  3. Removed: rm -i to provide confirmation & interactively remove

Should the user choose to not back up a potentially overwritten file, the default copy command is cp -i. There’s plenty opportunity to prevent data loss, unless specifically chosen not to.

Portability

Very few dependencies.

Aside from a couple bash scripts you can easily clone, you’ll probably have everything installed already. Anyone with bash >=4.2 and gawk/sed should be set. You don’t have to download the entirety of Python3, or nonsense ruby gems.

You’re welcome.

Logging & Troubleshooting

Fairly comprehensive log output.

Turn on log levels by passing --debug LOW[,HIGH]. Levels go from -1 (for absolute noise), to 3 (critical errors).

Each run initially generates a ‘RUN_ID’ (seconds since the epoch). The compiled files in dist/ are each named after the $RUN_ID, to match against specific logfile output. Allows for easier troubleshooting.

Why it do

bash == best

Using the language for things it was unequivocally not intended is a wonderful way to gain a deeper understanding of it. No one in their right mind would make a lexer in bash…​ so I had to.

It incidentally keeps the footprint & dependencies small.

To do

Features
  • ❏ Re-work type :multiline and :text in mk-conf.sh, such that we can specify longer sections of text to drop in. While specifying files in ./files/$NAME/additions/ may be a more elegant solution for long additions, 4-5 line chunks seem best via a :multiline entry.

  • ❏ Reporting. Compile information during the run into a final report. Use a trap to ensure the report is actually written on exits or failure. Report should contain: 1) exit status, 2) run summary, 3) operations performed, 4) errors encountered. Use less -r to show with color escapes enabled.

  • ❏ Easier option for files that don’t have any processing required. If it it something that’s as simple as a 'cp' with no variables.

  • ❏ Add write function. Similar to debug. For writing necessary output to the terminal. Will need to be quieted by -q|--quiet.

  • ❏ When stripping newlines, also consider situations of $'\n', $' '. Need a lookahead +2, or a lookbehind.

  • ❏ Maybe set up fswatch for auto-compiling files from base?

Done
  • ✓ Create deployment script, move data to XDG_DATA_HOME or .local/share

  • ✓ CLI options:

    • --new Automatically create the requisite directory structure

    • --clean Remove >3 files from each dir in ./dist.

    • --find Echo path to the ‘base’ of a specified search term

  • ✓ ‘Library’ files contain too many conflicting global variables when sourcing. PROGDIR ends up being set to the path of the last-sourced file. Several proposed solutions noted in the comments.

  • ✓ Require --new flag has a parameter

  • ✓ Use new & improved import.sh for dependencies

  • ✓ Tokenize new text that’s entered from the config.cfg file, such that we can properly strip newlines.

  • ✓ Diff previously generated files. If there’s no differences, no need to compile them again. Best way to do this might be a dotfile within each ./dist/$NAME with a md5sum of the base file, and the filename it’s created. Before running, we md5sum the ‘base’ file, grep the list to see if there’s an existing entry.

  • ✓ Make consistent global variables for common paths. The names should be straightforward, memorable, and obviously distinct to which directory they refer.

  • ✓ Clean up terminology. We’re referring to ‘base’ in like 3 different ways. As with variables, things should have one (and only one) clear name.

deploy-dotfiles's People

Contributors

carlinigraphy avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.