Literate programming dotfiles
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
.gitignore
LICENSE.txt
README.org

README.org

dotfiles

A collection of literate programming dotfiles created and maintained in Emacs with Org mode.

Build Configuration Files

Open this Org document in Emacs and tangle it (C-c C-v t). Configuration files, e.g., .bashrc, will be generated.

Shell

Change Default Shell

This should be the Homebrew installed bash not the system bash path.

chsh -s /usr/local/bin/bash
export SHELL=/usr/local/bin/bash
echo ${SHELL}

On OS X:

sudo dscl . -create ${HOME} UserShell /usr/local/bin/bash
dscl . -read ${HOME} UserShell

Make the Bash configuration directory:

mkdir -p "${HOME}/.bashrc.d"

.inputrc

set bell-style none
set colored-completion-prefix on
set colored-stats on
set completion-ignore-case off
set convert-meta off
set expand-tilde on
set input-meta on
set output-meta on
set show-all-if-ambiguous on
set visible-stats on

.bash_profile

source "${HOME}/.bashrc"

.bashrc

Load all configuration:

for f in "${HOME}/.bashrc.d/"*.bash; do
    source "${f}"
done
unset -v config

Custom

Interactive shell options.

Secret Information

Define a directory to keep secret information in. Make sure that it exists in =.gitignore=.

export K20E_SECRET_HOME=${HOME}/.bashrc.d/secret

Create it if necessary.

mkdir -p ${K20E_SECRET_HOME}

Adjust permissions.

chmod 0700 ${K20E_SECRET_HOME}
chmod -Rf 0600 ${K20E_SECRET_HOME}/*.sh

Define files to source in the following sections.

export K20E_SECRET_VARIABLES=${K20E_SECRET_HOME}/variables.sh
export K20E_SECRET_ALIASES=${K20E_SECRET_HOME}/aliases.sh
export K20E_SECRET_PATH=${K20E_SECRET_HOME}/path.sh
export K20E_SECRET_FUNCTIONS=${K20E_SECRET_HOME}/functions.sh

Options

shopt -s \
      autocd \
      cdspell \
      checkjobs \
      checkwinsize \
      dirspell \
      no_empty_cmd_completion

Variables

Bash variables.

LANG=en_US.UTF-8

HISTSIZE=100000
HISTCONTROL=erasedups
HISTTIMEFORMAT='%F %T '

Base variables that I use to organize the file system.

export CODE_HOME=${HOME}/Code
export GOOGLE=${HOME}/Google

File system variables.

export CARGO_HOME=${HOME}/.cargo

# https://github.com/rust-lang-nursery/rustfmt#tips
export DYLD_LIBRARY_PATH=$(${CARGO_HOME}/bin/rustc --print sysroot)/lib:${DYLD_LIBRARY_PATH}

export CHICKEN_HOME=$(/usr/local/bin/csi -print '(chicken-home)' | cut -d/ -f 1-8)
export GROOVY_HOME=/usr/local/opt/groovy/libexec
export JAVA_HOME=$(/usr/libexec/java_home)
export VLC_HOME=/Applications/VLC.app/Contents/MacOS

export DESKTOP=${HOME}/Desktop
export DOWNLOADS=${HOME}/Downloads
export GOROOT=/usr/local/opt/go/libexec
export HOMEBREW_CELLAR=$(/usr/local/bin/brew --prefix)/Cellar

export GOPATH=${CODE_HOME}/go
export MANPATH=/usr/local/share/man:${MANPATH}
export NODE_PATH=/usr/local/lib/node_modules:${NODE_PATH}

Non file system variables.

export EDITOR=emacsclient
export EMACS_VERSION="HEAD"
export LANG=en_US.UTF-8
export PIPENV_SHELL_FANCY=1
export TERM=xterm-256color

… use the -u/--unquoted option to specify that any result that is a string will be printed without quotes. … If this is a common enough occurance for you, you can set the JP_UNQUOTED environment variable to make this the default behavior

https://github.com/jmespath/jp

export JP_UNQUOTED=true
if [ -f ${K20E_SECRET_VARIABLES} ]; then
    source ${K20E_SECRET_VARIABLES}
fi

CDPATH

The cdpath variable sets the search path for the cd command. If you do not specify . somewhere in the path, it is assumed to be the first component.

export CDPATH="${CODE_HOME}:${GOOGLE}"

PATH

pathmunge () {
    case ":${PATH}:" in
        *:"$1":*)
            ;;
        *)
            if [ "$2" = "after" ] ; then
                PATH=$PATH:$1
            else
                PATH=$1:$PATH
            fi
    esac
}
pathmunge /usr/local/opt/python/libexec/bin
pathmunge /usr/local/sbin
pathmunge /usr/local/bin
pathmunge "${HOME}/bin"
pathmunge "${CARGO_HOME}/bin"
pathmunge "${GOPATH}/bin"
pathmunge "${GOROOT}/bin"
pathmunge "${VLC_HOME}"
pathmunge "$(/usr/local/bin/brew --prefix git)/share/git-core/contrib/diff-highlight"
pathmunge "${CHICKEN_HOME}/bin"
if [ -f ${K20E_SECRET_PATH} ]; then
    source ${K20E_SECRET_PATH}
fi

PYTHON_USER_BASE

Add Python site.USER_BASE for user site-packages and pip install --user installations.

export PYTHON_USER_BASE=$(python -m site --user-base)
pathmunge "${PYTHON_USER_BASE}/bin"

PROMPT_COMMAND

TODO try out https://github.com/magicmonty/bash-git-prompt

[ -e /usr/local/etc/bash_completion.d/git-prompt.sh ] && source /usr/local/etc/bash_completion.d/git-prompt.sh

GIT_PS1_SHOWCOLORHINTS=1
GIT_PS1_SHOWDIRTYSTATE=1
GIT_PS1_SHOWUNTRACKEDFILES=1
function k20e_working_directory() {
    declare -r YELLOW='\[\e[0;33m\]'
    declare -r RESET='\[\e[0m\]'
    echo "${YELLOW}\w${RESET}"
}
function k20e_virtual_env() {
    declare -r BLUE='\[\e[0;34m\]'
    declare -r RESET='\[\e[0m\]'
    local venv=''
    if [ -n "${VIRTUAL_ENV}" ] && [ -d "${VIRTUAL_ENV}" ]; then
        venv=$(basename "${VIRTUAL_ENV}")
    fi
    echo "${BLUE}venv ${venv}${RESET}"
}
function k20e_prompt_command_aws_profile() {
    declare -r CYAN='\[\e[0;36m\]'
    declare -r RED='\[\e[0;31m\]'
    declare -r RESET='\[\e[0m\]'
    declare profile="${AWS_PROFILE}"
    declare token="${AWS_SECURITY_TOKEN}"
    if [ "${profile}" = 'default' ]; then
        profile=''
    elif [[ "${profile}" =~ ^prod.* ]]; then
        profile="${RED}${AWS_PROFILE}"
    elif [ -n "${token}" ]; then
        profile='🔑'
    else
        profile=''
    fi
    echo "${CYAN}aws ${profile}${RESET}"
}
PROMPT_COMMAND='__git_ps1 \
"\n$(k20e_prompt_command_aws_profile)  $(k20e_virtual_env)
$(k20e_working_directory)" \
"\n\$ " \
" %s"'

Aliases

alias ..="cd ../"
alias ...="cd ../../"
alias ....="cd ../../.."
alias emacs="/usr/local/bin/emacs --no-window-system"
alias emacsclient="/usr/local/bin/emacsclient --no-wait"
alias ec=emacsclient
alias g="git"
alias gh="github.py"
alias j="jobs -l"
alias l.l='ls -1A | grep "^\." | xargs ls -lhGF'
alias ll="ls -lhF"
alias ls="ls -GF"
alias top="top -ocpu -Orsize"
alias v="TERM=ansi vagrant"
if [ -f ${K20E_SECRET_ALIASES} ]; then
    source ${K20E_SECRET_ALIASES}
fi

ssh TERM

My Emacs multi-term with bash has TERM=xterm-256color. On many remote hosts, primarily Amazon Linux, any xterm sets PROMPT_COMMAND and garbles the prompt by attempting to set the window title. /etc/bashrc usually contains something like this:

if [ -z "$PROMPT_COMMAND" ]; then
  case $TERM in
  xterm*)
      if [ -e /etc/sysconfig/bash-prompt-xterm ]; then
          PROMPT_COMMAND=/etc/sysconfig/bash-prompt-xterm
      else
          PROMPT_COMMAND='printf "\033]0;%s@%s:%s\007" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/~}"'
      fi
      ;;
  screen)
      if [ -e /etc/sysconfig/bash-prompt-screen ]; then
          PROMPT_COMMAND=/etc/sysconfig/bash-prompt-screen
      else
          PROMPT_COMMAND='printf "\033]0;%s@%s:%s\033\\" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/~}"'
      fi
      ;;
  *)
      [ -e /etc/sysconfig/bash-prompt-default ] && PROMPT_COMMAND=/etc/sysconfig/bash-prompt-default
      ;;
    esac
fi

Hacking that to some other reasonable value avoids prompt garbling and muscle memory typing unset PROMPT_COMMAND.

alias ssh="TERM=ansi ssh"

Completions

[ -e /usr/local/share/bash-completion/bash_completion ] && source /usr/local/share/bash-completion/bash_completion

ag

[ -e /usr/local/etc/bash_completion.d/ag.bashcomp.sh ] && source /usr/local/etc/bash_completion.d/ag.bashcomp.sh

Git

[ -e /usr/local/etc/bash_completion.d/git-completion.bash ] && source /usr/local/etc/bash_completion.d/git-completion.bash

Add completion for my muscle memory alias of g for git:

__git_complete g __git_main

pip

eval "$(pip completion --bash)"

Functions

if [ -f ${K20E_SECRET_FUNCTIONS} ]; then
    source ${K20E_SECRET_FUNCTIONS}
fi

AWS

Credentials

Unset the many current and legacy AWS CLI environment variables:

alias aws-list="env | grep AWS_"
alias aws-unset="unset AWS_SECRET_ACCESS_KEY AWS_ACCESS_KEY_ID AWS_PROFILE AWS_PROFILE AWS_CREDENTIAL_FILE AWS_SECURITY_TOKEN EC2_CERT EC2_PRIVATE_KEY"
aws-unset

Set default profile:

export AWS_PROFILE="default"

List stacks by StackName

function k20e-aws-stacks-list()
{
    declare output='table'
    declare show_policies=false

    declare OPTIND OPTARG opt
    while getopts ':o:p' opt; do
        case ${opt} in
            o)
                output="${OPTARG}"
                ;;
            p)
                show_policies=true
                ;;
        esac
    done
    shift $((OPTIND-1))

    declare -r NAME="${1}"

    # DELETE_COMPLETE
    declare -ar STATUSES=(
        'CREATE_IN_PROGRESS'
        'CREATE_FAILED'
        'CREATE_COMPLETE'
        'ROLLBACK_IN_PROGRESS'
        'ROLLBACK_FAILED'
        'ROLLBACK_COMPLETE'
        'DELETE_IN_PROGRESS'
        'DELETE_FAILED'
        'UPDATE_IN_PROGRESS'
        'UPDATE_COMPLETE_CLEANUP_IN_PROGRESS'
        'UPDATE_COMPLETE'
        'UPDATE_ROLLBACK_IN_PROGRESS'
        'UPDATE_ROLLBACK_FAILED'
        'UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS'
        'UPDATE_ROLLBACK_COMPLETE'
    )

    declare -a query=(
        'StackSummaries[*].StackName'
        "| [?contains(@, \`${NAME}\`) == \`true\`]"
    )

    if [ "${show_policies}" = false ]; then
        query+=('| [?contains(@, `Policy`) == `false`]')
    fi

    query+=('| sort(@)')

    aws --output "${output}" cloudformation list-stacks --stack-status-filter "${STATUSES[@]}" --query "'${query[*]}'"
}

List instances by tag Name

function k20e-aws-instances-describe()
{
    zparseopts -D -E -A opts -- o:
    output=${opts[-o]:-"table"}

    name=${1}
    query=(
        "Reservations[].Instances[]"
        ".{"
        "Name             : Tags[?Key == \`Name\`].Value | [0],"
        "State            : State.Name,"
        "LaunchTime       : LaunchTime,"
        "PublicIpAddress  : PublicIpAddress,"
        "PrivateIpAddress : PrivateIpAddress,"
        "ImageId          : ImageId,"
        "InstanceType     : InstanceType"
        "}"
    )

    aws --output ${output} \
        ec2 describe-instances \
        --filters "Name=tag:Name,Values=*${name}*" \
        --query "${query}"
}

Get instance public IP by tag Name

function k20e-aws-instance-public-ip()
{
    name=${1}
    query="Reservations[].Instances[].PublicIpAddress"

    aws --output text \
        ec2 describe-instances \
        --filters "Name=tag:Name,Values=*${name}*" \
        --query "${query}"
}

Get instance private IP by tag Name

function k20e-aws-instance-private-ip()
{
    name=${1}
    query="Reservations[].Instances[].PrivateIpAddress"

    aws --output text \
        ec2 describe-instances \
        --filters "Name=tag:Name,Values=*${name}*" \
        --query "${query}"
}

Terminate instance by tag Name

function k20e-aws-instance-terminate()
{
    zparseopts -D -E -A opts -- : f

    name=${1}
    query=(
        "Reservations[].Instances[].InstanceId"
    )

    id=$(
        aws --output text \
            ec2 describe-instances \
            --filters "Name=tag:Name,Values=*${name}*" \
            --query "${query}"
      )

    dry_run="--dry-run"
    if (( ${+opts[-f]} == 1 )); then
        dry_run=""
    fi

    aws --output "text" \
        ec2 terminate-instances \
        --instance-ids ${id} \
        ${dry_run}
}

List images by id

function k20e-aws-images-describe()
{
    zparseopts -D -E -A opts -- o:
    output=${opts[-o]:-"table"}

    id=${1:-ami-e3106686}
    aws --output ${output} \
        ec2 describe-images \
        --image-ids "${id}"
}

List EMR clusters

function k20e-aws-emr-list-clusters()
{
    query=(
        "Clusters[].Id"
    )

    aws --output text \
        emr list-clusters \
        --cluster-states "WAITING" "RUNNING" \
        --query "${query}"
}

RDS

Print a mysql command to connect to an RDS instance given an instance id:

function k20e-aws-rds-mysql-command()
{
    zparseopts -D -E -A opts -- i: # Require db instance id
    id=${1}

    query=(
        "DBInstances[0]"        # The first since id is required
        ".["                    # Select the values mysql requires
        "Endpoint.Address",     # Host
        "Endpoint.Port",        # Port
        "MasterUsername"        # User
        "]"
    )

    prog=(
        '{ print'
        '"mysql",'
        '"-h", $1,'             # Host
        '"-P", $2,'             # Port
        '"-u", $3,'             # User
        '"-p"'                  # Ask for password from tty
        '}'
    )

    aws --output text \
        rds describe-db-instances \
        --db-instance-identifier ${id} \
        --query "${query}" \
        | awk "${prog}"
}

Old Boxes

# aws --output text ec2 describe-instances --query 'Reservations[].Instances[].[LaunchTime,Tags[?Key==`Name`].Value|[0]]' | sort | head

aws-cli Completion

if [ -e ${PYTHON_USER_BASE}/bin/aws_bash_completer ]; then
    source ${PYTHON_USER_BASE}/bin/aws_bash_completer
fi

Python

function k20e-pip-upgrade() {
    if [[ $(which deactivate) == "deactivate: function" && -n ${VIRTUAL_ENV} ]]; then
        echo "Deactivating current virtual environment ${VIRTUAL_ENV}"
        deactivate
    fi
    pip install --user --upgrade --requirement ${HOME}/requirements-to-freeze.txt
    pip freeze > ${HOME}/requirements.txt
}

terminal-notifier

if [ -e "/Applications/terminal-notifier.app" ]; then
    alias notify="/Applications/terminal-notifier.app/Contents/MacOS/terminal-notifier"
fi

virtualenvwrapper

if (( ${PIPENV_ACTIVE:-0} != 1 )); then
   source virtualenvwrapper.sh
fi

Git

.gitconfig

[user]
      name = Kris
      email = krismolendyke@users.noreply.github.com
      useconfigonly = true
[color]
      ui = auto
[core]
      excludesfile = ~/.gitignore-global
      whitespace = -trailing-space,-space-before-tab
      editor = emacsclient
[credential]
      helper = osxkeychain
[apply]
      whitespace = nowarn
[alias]
      diff = diff --color-moved
      stache = stash
      st = status -sb
      a = add -p
      l = log --color-moved --stat --no-merges
      lp = log --color-moved --patch --stat --no-merges
      wlp = log --color-moved --patch --stat --color-words --no-merges
      lo = log --color-moved --oneline --decorate --no-merges
      lf = log --color-moved --pretty=format: --name-only -z --max-count 1 --no-merges
      co = checkout
      br = branch -vv
      wdiff = diff --color-moved --color-words
      ds = diff --color-moved --staged
[advice]
      statusHints = true
[rebase]
      autosquash = true
[diff]
      algorithm = histogram
      compactionHeuristic = 1
      colorMoved = zebra
[help]
      autocorrect = 1
[pager]
      diff = diff-highlight | less
      log = diff-highlight | less
      show = diff-highlight | less
[interactive]
      diffFilter = diff-highlight

.gitignore-global

# -*- mode: gitignore; -*-

##################################################################################
# Below from:                                                                    #
#                                                                                #
# https://raw.githubusercontent.com/github/gitignore/master/Global/OSX.gitignore #
##################################################################################

.DS_Store
.AppleDouble
.LSOverride

# Icon must end with two \r
Icon


# Thumbnails
._*

# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns

# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk

Python

See also Python functions.

requirements-to-freeze.txt

Use A Better Pip Workflow™ to specify packages that I do actually want installed to the user’s packages.

# User packages
awscli
jmespath-terminal
keyring
pew
pipdeptree[graphviz]
pipenv
restview
twine
virtualenv
virtualenvwrapper

Homebrew

brew doesn’t have a great way to manage dependencies that I’ve found outside of a Brewfile and the bundle subcommand.

Security

From https://www.davd.eu/securing-macos/#homebrew

export HOMEBREW_NO_INSECURE_REDIRECT=1
export HOMEBREW_CASK_OPTS=--require-sha

Bundle

Install:

brew tap Homebrew/bundle

Programs currently installed by brew can be dumped to a global Brewfile, which defaults to ${HOME}/.Brewfile:

brew bundle dump --force --global --verbose

Install all programs specified in the global ${HOME}./Brewfile:

brew bundle --global

${HOME}/.Brewfile

tap "homebrew/cask-versions"
tap "homebrew/core"
tap "homebrew/cask"
tap "homebrew/bundle"
tap "homebrew/services"
tap "jmespath/jmespath"
cask "xquartz"
cask "homebrew/cask-versions/java8"
brew "apr"
brew "openssl"
brew "apr-util"
brew "aspell"
brew "automake"
brew "bash"
brew "bash-completion@2"
brew "bison"
brew "freetype"
brew "fontconfig"
brew "gettext", link: true
brew "pixman"
brew "cairo"
brew "emacs", args: ["HEAD", "with-cocoa"]
brew "cask"
brew "chicken"
brew "cmake"
brew "faac"
brew "lame"
brew "xvid"
brew "ffmpeg"
brew "figlet"
brew "flex"
brew "gawk"
brew "gcc"
brew "gd"
brew "gdk-pixbuf"
brew "gflags"
brew "git"
brew "go"
brew "gprof2dot"
brew "gradle"
brew "grafana"
brew "groovy"
brew "harfbuzz"
brew "hunspell"
brew "imagemagick"
brew "influxdb"
brew "ipcalc"
brew "ispell"
brew "less"
brew "libav"
brew "libcroco"
brew "libdvdcss"
brew "librsvg"
brew "libssh"
brew "libyaml"
brew "lz4"
brew "make"
brew "makedepend"
brew "mariadb"
brew "mas"
brew "maven"
brew "ninja"
brew "node"
brew "oniguruma"
brew "packer"
brew "parallel"
brew "perl"
brew "privoxy"
brew "protobuf"
brew "pstree"
brew "pv"
brew "pyenv"
brew "rocksdb"
brew "ruby"
brew "rustup-init"
brew "scons"
brew "shared-mime-info"
brew "sshtrix"
brew "subversion"
brew "terminal-notifier"
brew "texi2html"
brew "the_silver_searcher"
brew "tree"
brew "watch"
brew "wget"
brew "yarn"
brew "yasm"
brew "youtube-dl"
brew "yubico-piv-tool"
brew "jmespath/jmespath/jp"
cask "1password"
cask "alfred"
cask "amethyst"
cask "android-platform-tools"
cask "devdocs"
cask "etcher"
cask "firefox"
cask "google-backup-and-sync"
cask "google-chrome"
cask "google-featured-photos"
cask "handbrake"
cask "inkscape"
cask "iterm2"
cask "keepingyouawake"
cask "mactex-no-gui"
cask "pocket-casts"
cask "racket"
cask "slack"
cask "vagrant"
cask "virtualbox"
cask "viscosity"
cask "vlc"
cask "zoomus"
cask "homebrew/cask-versions/istat-menus5"
mas "GarageBand", id: 682658836
mas "iMovie", id: 408981434
mas "Keynote", id: 409183694
mas "Marked", id: 448925439
mas "Numbers", id: 409203825
mas "Pages", id: 409201541
mas "Pixelmator", id: 407963104
mas "The Unarchiver", id: 425424353

mas

A simple command line interface for the Mac App Store. Designed for scripting and automation.

The Bundlefile above should install the App Store programs listed with in it using mas.

SSH

Create a configuration directory:

mkdir -p ${HOME}/.ssh/config.d

config

ServerAliveCountMax 5
ServerAliveInterval 60
UseKeychain yes

Host *
    IdentityFile ~/.ssh/id_rsa
    PreferredAuthentications publickey,password

Include ~/.ssh/config.d/*