Skip to content

ptb/mac-setup

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

76 Commits
 
 
 
 
 
 

Repository files navigation

Mac Setup

From zero to fully installed and configured, in an hour.

Overview

Quick Start

case "${SHELL}" in
  (*zsh) ;;
  (*) chsh -s "$(which zsh)"; exit 1 ;;
esac
curl --location --silent \
  "https://github.com/ptb/mac-setup/raw/develop/mac-setup.command" | \
  source /dev/stdin 0
init && install && config
custom && personalize_all

init

  • Enter administrator account password only once
  • Select the cache folder for repeat installations
  • Turn off sleep and set computer name/hostname
  • Set write permission on destination folders
  • Cache software updates and App Store software
  • Install developer tools and macOS updates

install

  • Homebrew: The missing package mananger for macOS
  • Homebrew-Cask: “To install, drag this icon…” no more!
  • mas-cli/mas: Mac App Store command line interface
  • homebrew-bundle: List all Homebrew packages in Brewfile
  • Node.js: Cross-platform JavaScript run-time environment
  • Perl 5: Highly capable, feature-rich programming language
  • Python: Programming language that lets you work quickly
  • Ruby: Language with a focus on simplicity and productivity

config

  • Configure software requiring an administrator account
  • Optionally configure local Dovecot secure IMAP server
  • Create your primary non-administrator account
  • Remove password-less administrator account permission
  • Recommended that you log out of administrator account

custom

  • Log in with your new non-administrator account
  • Create or clone a git repository into your home folder
  • Install Atom packages and customize preferences
  • Set the desktop picture to a solid black color
  • Customize the dock with new default applications
  • Customize Emacs with Spacemacs: Emacs plus Vim!
  • Set all preferences automatically and consistently

Features

  • macOS High Sierra: Tested with macOS High Sierra 10.13 (17A365).
  • Completely Automated: Homebrew, Cask, and mas-cli install everything.
  • Latest Versions: Includes Node, Perl, Python, and Ruby separate from macOS.
  • Customized Terminal: Colors and fonts decyphered into editable preferences.
  • Idempotent: This script is intended to be safe to run more than once.

Walkthrough

Clone Internal Hard Disk to External Disk

Select the External Disk as Startup Disk

Download macOS Install from the App Store

macappstores://itunes.apple.com/app/id1209167288

Open /Applications/Utilities/Terminal.app

diskx="$(diskutil list internal physical | sed '/^\//!d;s/^\(.*\)\ (.*):/\1/')"
diskutil zeroDisk $diskx
diskutil partitionDisk $diskx 2 GPT \
  jhfs+ "Install" 6G \
  apfs $(ruby -e "print '$(hostname -s)'.capitalize") R
sudo "/Applications/Install macOS High Sierra.app/Contents/Resources/createinstallmedia" \
  --applicationpath "/Applications/Install macOS High Sierra.app" --nointeraction \
  --volume "/Volumes/Install"

Select the Install Disk as Startup Disk

License

Copyright 2017 Peter T Bosse II

Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

Initialize

Initialize New Terminal

if test -z "${1}"; then
  osascript - "${0}" << EOF > /dev/null 2>&1
<<new_term.applescript>>
EOF
fi

new_term.applescript

on run { _this }
  tell app "Terminal" to do script "source " & quoted form of _this & " 0"
end run

Define Function ask

ask () {
  osascript - "${1}" "${2}" "${3}" << EOF 2> /dev/null
<<ask.applescript>>
EOF
}

ask.applescript

on run { _title, _action, _default }
  tell app "System Events" to return text returned of (display dialog _title with title _title buttons { "Cancel", _action } default answer _default)
end run

Define Function ask2

ask2 () {
  osascript - "$1" "$2" "$3" "$4" "$5" "$6" << EOF 2> /dev/null
<<ask2.applescript>>
EOF
}

ask2.applescript

on run { _text, _title, _cancel, _action, _default, _hidden }
  tell app "Terminal" to return text returned of (display dialog _text with title _title buttons { _cancel, _action } cancel button _cancel default button _action default answer _default hidden answer _hidden)
end run

Define Function p

p () {
  printf "\n\033[1m\033[34m%s\033[0m\n\n" "${1}"
}

Define Function run

run () {
  osascript - "${1}" "${2}" "${3}" << EOF 2> /dev/null
<<run.applescript>>
EOF
}

run.applescript

on run { _title, _cancel, _action }
  tell app "Terminal" to return button returned of (display dialog _title with title _title buttons { _cancel, _action } cancel button 1 default button 2 giving up after 5)
end run

Define Function init

init () {
  init_sudo
  init_cache
  init_no_sleep
  init_hostname
  init_perms
  init_maskeep
  init_updates

  config_new_account
  config_rm_sudoers
}

if test "${1}" = 0; then
  printf "\n$(which init)\n"
fi

Define Function init_paths

init_paths () {
  test -x "/usr/libexec/path_helper" && \
    eval $(/usr/libexec/path_helper -s)
}

Eliminate Prompts for Password

init_sudo () {
  printf "%s\n" "%wheel ALL=(ALL) NOPASSWD: ALL" | \
  sudo tee "/etc/sudoers.d/wheel" > /dev/null && \
  sudo dscl /Local/Default append /Groups/wheel GroupMembership "$(whoami)"
}

Select Installation Cache Location

init_cache () {
  grep -q "CACHES" "/etc/zshenv" 2> /dev/null || \
  a=$(osascript << EOF 2> /dev/null
<<init_cache.applescript>>
EOF
) && \
  test -d "${a}" || \
    a="${HOME}/Library/Caches/"

  grep -q "CACHES" "/etc/zshenv" 2> /dev/null || \
  printf "%s\n" \
    "export CACHES=\"${a}\"" \
    "export HOMEBREW_CACHE=\"${a}/brew\"" \
    "export BREWFILE=\"${a}/brew/Brewfile\"" | \
  sudo tee -a "/etc/zshenv" > /dev/null
  . "/etc/zshenv"

  if test -d "${CACHES}/upd"; then
    sudo chown -R "$(whoami)" "/Library/Updates"
    rsync -a --delay-updates \
      "${CACHES}/upd/" "/Library/Updates/"
  fi
}

init_cache.applescript

on run
  return text 1 through -2 of POSIX path of (choose folder with prompt "Select Installation Cache Location")
end run

Set Defaults for Sleep

init_no_sleep () {
  sudo pmset -a sleep 0
  sudo pmset -a disksleep 0
}

Set Hostname from DNS

init_hostname () {
  a=$(ask2 "Set Computer Name and Hostname" "Set Hostname" "Cancel" "Set Hostname" $(ruby -e "print '$(hostname -s)'.capitalize") "false")
  if test -n $a; then
    sudo scutil --set ComputerName $(ruby -e "print '$a'.capitalize")
    sudo scutil --set HostName $(ruby -e "print '$a'.downcase")
  fi
}

Set Permissions on Install Destinations

init_perms () {
  printf "%s\n" "${_dest}" | \
  while IFS="$(printf '\t')" read d; do
    test -d "${d}" || sudo mkdir -p "${d}"
    sudo chgrp -R admin "${d}"
    sudo chmod -R g+w "${d}"
  done
}

_dest

LocationInstall Path
/usr/local/bin
/Library/Desktop Pictures
colorpickerdir/Library/ColorPickers
fontdir/Library/Fonts
input_methoddir/Library/Input Methods
prefpanedir/Library/PreferencePanes
qlplugindir/Library/QuickLook
screen_saverdir/Library/Screen Savers
/Library/User Pictures

Install Developer Tools

init_devtools () {
  p="${HOMEBREW_CACHE}/Cask/Command Line Tools (macOS High Sierra version 10.13).pkg"
  i="com.apple.pkg.CLTools_SDK_macOS1013"

  if test -f "${p}"; then
    if ! pkgutil --pkg-info "${i}" > /dev/null 2>&1; then
      sudo installer -pkg "${p}" -target /
    fi
  else
    xcode-select --install
  fi
}

Install Xcode

init_xcode () {
  if test -f ${HOMEBREW_CACHE}/Cask/xcode*.xip; then
    p "Installing Xcode"
    dest="${HOMEBREW_CACHE}/Cask/xcode"
    if ! test -d "$dest"; then
      pkgutil --expand ${HOMEBREW_CACHE}/Cask/xcode*.xip "$dest"
      curl --location --silent \
        "https://gist.githubusercontent.com/pudquick/ff412bcb29c9c1fa4b8d/raw/24b25538ea8df8d0634a2a6189aa581ccc6a5b4b/parse_pbzx2.py" | \
        python - "${dest}/Content"
      find "${dest}" -empty -name "*.xz" -type f -print0 | \
        xargs -0 -l 1 rm
      find "${dest}" -name "*.xz" -print0 | \
        xargs -0 -L 1 gunzip
      cat ${dest}/Content.part* > \
        ${dest}/Content.cpio
    fi
    cd /Applications && \
      sudo cpio -dimu --file=${dest}/Content.cpio
    for pkg in /Applications/Xcode*.app/Contents/Resources/Packages/*.pkg; do
      sudo installer -pkg "$pkg" -target /
    done
    x="$(find '/Applications' -maxdepth 1 -regex '.*/Xcode[^ ]*.app' -print -quit)"
    if test -n "${x}"; then
      sudo xcode-select -s "${x}"
      sudo xcodebuild -license accept
    fi
  fi
}

Install macOS Updates

init_updates () {
  sudo softwareupdate --install --all
}

Save Mac App Store Packages

sudo lsof -c softwareupdated -F -r 2 | sed '/^n\//!d;/com.apple.SoftwareUpdate/!d;s/^n//'
sudo lsof -c storedownloadd -F -r 2 | sed '/^n\//!d;/com.apple.appstore/!d;s/^n//'
init_maskeep () {
  sudo softwareupdate --reset-ignored > /dev/null

  cat << EOF > "/usr/local/bin/maskeep"
<<maskeep.sh>>
EOF

  chmod a+x "/usr/local/bin/maskeep"
  rehash

  config_launchd "/Library/LaunchDaemons/com.github.ptb.maskeep.plist" "$_maskeep_launchd" "sudo" ""
}

_maskeep_launchd

CommandEntryTypeValue
add:KeepAliveboolfalse
add:Labelstringcom.github.ptb.maskeep
add:ProcessTypestringBackground
add:Programstring/usr/local/bin/maskeep
add:RunAtLoadbooltrue
add:StandardErrorPathstring/dev/stderr
add:StandardOutPathstring/dev/stdout
add:UserNamestringroot
add:WatchPathsarray
add:WatchPaths:0string$(sudo find ‘/private/var/folders’ -name ‘com.apple.SoftwareUpdate’ -type d -user _softwareupdate -print -quit 2> /dev/null)
add:WatchPaths:1string$(sudo -u \#501 – sh -c ‘getconf DARWIN_USER_CACHE_DIR’ 2> /dev/null)com.apple.appstore
add:WatchPaths:2string$(sudo -u \#502 – sh -c ‘getconf DARWIN_USER_CACHE_DIR’ 2> /dev/null)com.apple.appstore
add:WatchPaths:3string$(sudo -u \#503 – sh -c ‘getconf DARWIN_USER_CACHE_DIR’ 2> /dev/null)com.apple.appstore
add:WatchPaths:4string/Library/Updates

/usr/local/bin/maskeep

#!/bin/sh

asdir="/Library/Caches/storedownloadd"
as1="\$(sudo -u \\#501 -- sh -c 'getconf DARWIN_USER_CACHE_DIR' 2> /dev/null)com.apple.appstore"
as2="\$(sudo -u \\#502 -- sh -c 'getconf DARWIN_USER_CACHE_DIR' 2> /dev/null)com.apple.appstore"
as3="\$(sudo -u \\#503 -- sh -c 'getconf DARWIN_USER_CACHE_DIR' 2> /dev/null)com.apple.appstore"
upd="/Library/Updates"
sudir="/Library/Caches/softwareupdated"
su="\$(sudo find '/private/var/folders' -name 'com.apple.SoftwareUpdate' -type d -user _softwareupdate 2> /dev/null)"

for i in 1 2 3 4 5; do
  mkdir -m a=rwxt -p "\$asdir"
  for as in "\$as1" "\$as2" "\$as3" "\$upd"; do
    test -d "\$as" && \
    find "\${as}" -type d -print | \\
    while read a; do
      b="\${asdir}/\$(basename \$a)"
      mkdir -p "\${b}"
      find "\${a}" -type f -print | \\
      while read c; do
        d="\$(basename \$c)"
        test -e "\${b}/\${d}" || \\
          ln "\${c}" "\${b}/\${d}" && \\
          chmod 666 "\${b}/\${d}"
      done
    done
  done

  mkdir -m a=rwxt -p "\${sudir}"
  find "\${su}" -name "*.tmp" -type f -print | \\
  while read a; do
    d="\$(basename \$a)"
    test -e "\${sudir}/\${d}.xar" ||
      ln "\${a}" "\${sudir}/\${d}.xar" && \\
      chmod 666 "\${sudir}/\${d}.xar"
  done

  sleep 1
done

exit 0

Install

Define Function install

install () {
  install_macos_sw
  install_node_sw
  install_perl_sw
  install_python_sw
  install_ruby_sw

  which config
}

Install macOS Software with brew

install_macos_sw () {
  p "Installing macOS Software"
  install_paths
  install_brew
  install_brewfile_taps
  install_brewfile_brew_pkgs
  install_brewfile_cask_args
  install_brewfile_cask_pkgs
  install_brewfile_mas_apps

  x=$(find '/Applications' -maxdepth 1 -regex '.*/Xcode[^ ]*.app' -print -quit)
  if test -n "$x"; then
    sudo xcode-select -s "$x"
    sudo xcodebuild -license accept
  fi

  brew bundle --file="${BREWFILE}"

  x=$(find '/Applications' -maxdepth 1 -regex '.*/Xcode[^ ]*.app' -print -quit)
  if test -n "$x"; then
    sudo xcode-select -s "$x"
    sudo xcodebuild -license accept
  fi

  install_links
  sudo xattr -rd "com.apple.quarantine" "/Applications" > /dev/null 2>&1
  sudo chmod -R go=u-w "/Applications" > /dev/null 2>&1
}

Add /usr/local/bin/sbin to Default Path

install_paths () {
  if ! grep -Fq "/usr/local/sbin" /etc/paths; then
    sudo sed -i "" -e "/\/usr\/sbin/{x;s/$/\/usr\/local\/sbin/;G;}" /etc/paths
  fi
}

Install Homebrew Package Manager

install_brew () {
  if ! which brew > /dev/null; then
    ruby -e \
      "$(curl -Ls 'https://github.com/Homebrew/install/raw/master/install')" \
      < /dev/null > /dev/null 2>&1
  fi
  printf "" > "${BREWFILE}"
  brew analytics off
  brew update
  brew doctor
  brew tap "homebrew/bundle"
}

Add Homebrew Taps to Brewfile

install_brewfile_taps () {
  printf "%s\n" "${_taps}" | \
  while IFS="$(printf '\t')" read tap; do
    printf 'tap "%s"\n' "${tap}" >> "${BREWFILE}"
  done
  printf "\n" >> "${BREWFILE}"
}

_taps

Homebrew Tap NameReference URL
caskroom/caskhttps://github.com/caskroom/homebrew-cask
caskroom/fontshttps://github.com/caskroom/homebrew-fonts
caskroom/versionshttps://github.com/caskroom/homebrew-versions
homebrew/bundlehttps://github.com/Homebrew/homebrew-bundle
homebrew/command-not-foundhttps://github.com/Homebrew/homebrew-command-not-found
homebrew/nginxhttps://github.com/Homebrew/homebrew-nginx
homebrew/phphttps://github.com/Homebrew/homebrew-php
homebrew/serviceshttps://github.com/Homebrew/homebrew-services
ptb/customhttps://github.com/ptb/homebrew-custom
railwaycat/emacsmacporthttps://github.com/railwaycat/homebrew-emacsmacport

Add Homebrew Packages to Brewfile

install_brewfile_brew_pkgs () {
  printf "%s\n" "${_pkgs}" | \
  while IFS="$(printf '\t')" read pkg; do
    # printf 'brew "%s", args: [ "force-bottle" ]\n' "${pkg}" >> "${BREWFILE}"
    printf 'brew "%s"\n' "${pkg}" >> "${BREWFILE}"
  done
  printf "\n" >> "${BREWFILE}"
}

_pkgs

Homebrew Package NameReference URL
aspellhttp://aspell.net/
bashhttps://www.gnu.org/software/bash/
certbothttps://certbot.eff.org/
chromedriverhttps://sites.google.com/a/chromium.org/chromedriver/
coreutilshttps://www.gnu.org/software/coreutils/
dashhttp://gondor.apana.org.au/~herbert/dash/
dutihttps://github.com/moretension/duti
e2fsprogshttps://e2fsprogs.sourceforge.io/
fasdhttps://github.com/clvv/fasd
fdupeshttps://github.com/adrianlopezroche/fdupes
gawkhttps://www.gnu.org/software/gawk/
getmailhttp://pyropus.ca/software/getmail/
githttps://git-scm.com/
git-flowhttp://nvie.com/posts/a-successful-git-branching-model/
git-lfshttps://git-lfs.github.com/
gnu-sedhttps://www.gnu.org/software/sed/
gnupghttps://www.gnupg.org/
gpachttps://gpac.wp.imt.fr/
httpiehttps://httpie.org/
hubhttps://hub.github.com/
ievmshttps://xdissent.github.io/ievms/
imagemagickhttps://www.imagemagick.org/
mashttps://github.com/argon/mas
mercurialhttps://www.mercurial-scm.org/
mp4v2https://code.google.com/archive/p/mp4v2/
mtrhttps://www.bitwizard.nl/mtr/
nmaphttps://nmap.org/
nodehttps://nodejs.org/
nodenvhttps://github.com/nodenv/nodenv
opensslhttps://www.openssl.org/
p7ziphttp://p7zip.sourceforge.net/
perl-buildhttps://github.com/tokuhirom/Perl-Build
pinentry-machttps://github.com/GPGTools/pinentry-mac
plenvhttps://github.com/tokuhirom/plenv
pyenvhttps://github.com/pyenv/pyenv
rbenvhttps://github.com/rbenv/rbenv
rsynchttps://rsync.samba.org/
selenium-server-standalonehttp://www.seleniumhq.org/
shellcheckhttps://github.com/koalaman/shellcheck
sleepwatcherhttp://www.bernhard-baehr.de/
sqlitehttps://sqlite.org
stowhttps://www.gnu.org/software/stow/
syncthinghttps://syncthing.net/
syncthing-inotifyhttps://github.com/syncthing/syncthing-inotify
taghttps://github.com/jdberry/tag
terminal-notifierhttps://github.com/julienXX/terminal-notifier
the_silver_searcherhttps://geoff.greer.fm/ag/
trashhttp://hasseg.org/trash/
unrarhttp://www.rarlab.com/
vcshhttps://github.com/RichiH/vcsh
vimhttps://vim.sourceforge.io/
yarnhttps://yarnpkg.com/
youtube-dlhttps://rg3.github.io/youtube-dl/
zshhttps://www.zsh.org/
zsh-syntax-highlightinghttps://github.com/zsh-users/zsh-syntax-highlighting
zsh-history-substring-searchhttps://github.com/zsh-users/zsh-history-substring-search
homebrew/php/php71https://github.com/Homebrew/homebrew-php
ptb/custom/dovecot
ptb/custom/ffmpeg
sdl2
zimg
x265
webp
wavpack
libvorbis
libvidstab
two-lame
theora
tesseract
speex
libssh
libsoxr
snappy
schroedinger
rubberband
rtmpdump
opus
openh264
opencore-amr
libmodplug
libgsm
game-music-emu
fontconfig
fdk-aac
libcaca
libbs2b
libbluray
libass
chromaprint
ptb/custom/nginx-full

Add Caskroom Options to Brewfile

install_brewfile_cask_args () {
  printf 'cask_args \' >> "${BREWFILE}"
  printf "%s\n" "${_args}" | \
  while IFS="$(printf '\t')" read arg dir; do
    printf '\n  %s: "%s",' "${arg}" "${dir}" >> "${BREWFILE}"
  done
  sed -i "" -e '$ s/,/\
/' "${BREWFILE}"
}

Add Homebrew Casks to Brewfile

install_brewfile_cask_pkgs () {
  printf "%s\n" "${_casks}" | \
  while IFS="$(printf '\t')" read cask; do
    printf 'cask "%s"\n' "${cask}" >> "${BREWFILE}"
  done
  printf "\n" >> "${BREWFILE}"
}

_casks

Caskroom Package NameReference URL
javahttps://www.oracle.com/technetwork/java/javase/
xquartzhttps://www.xquartz.org/
adiumhttps://www.adium.im/
alfredhttps://www.alfredapp.com/
arduinohttps://www.arduino.cc/
atomhttps://atom.io/
bbedithttps://www.barebones.com/products/bbedit/
betterziphttps://macitbetter.com/
bitbarhttps://getbitbar.com/
caffeinehttp://lightheadsw.com/caffeine/
carbon-copy-clonerhttps://bombich.com/
charleshttps://www.charlesproxy.com/
dashhttps://kapeli.com/dash
dropboxhttps://www.dropbox.com/
exifrenamerhttp://www.qdev.de/?location=mac/exifrenamer
find-empty-foldershttp://www.tempel.org/FindEmptyFolders
firefoxhttps://www.mozilla.org/firefox/
github-desktophttps://desktop.github.com/
gituphttp://gitup.co/
google-chromehttps://www.google.com/chrome/
hammerspoonhttp://www.hammerspoon.org/
handbrakehttps://handbrake.fr/
hermeshttp://hermesapp.org/
imageoptimhttps://imageoptim.com/mac
inkscapehttps://inkscape.org/
integrityhttp://peacockmedia.software/mac/integrity/
istat-menushttps://bjango.com/mac/istatmenus/
iterm2https://www.iterm2.com/
jublerhttp://www.jubler.org/
little-snitchhttps://www.obdev.at/products/littlesnitch/
machghttp://jasonfharris.com/machg/
menubar-countdownhttp://capablehands.net/menubarcountdown
meteorologisthttp://heat-meteo.sourceforge.net/
moomhttps://manytricks.com/moom/
mp4toolshttp://www.emmgunn.com/mp4tools-home/
musicbrainz-picardhttps://picard.musicbrainz.org/
namechangerhttps://mrrsoftware.com/namechanger/
nvalthttp://brettterpstra.com/projects/nvalt/
nzbgethttps://nzbget.net/
nzbvortexhttps://www.nzbvortex.com/
openemuhttp://openemu.org/
operahttps://www.opera.com/
pacifisthttps://www.charlessoft.com/
platypushttps://sveinbjorn.org/platypus
plex-media-serverhttps://www.plex.tv/
qlstephenhttps://whomwah.github.io/qlstephen/
quitterhttps://marco.org/apps#quitter
radarrhttps://radarr.video/
rescuetimehttps://www.rescuetime.com/
resilio-synchttps://www.resilio.com/individuals/
scrivenerhttps://literatureandlatte.com/scrivener.php
sizeuphttps://www.irradiatedsoftware.com/sizeup/
sketchhttps://www.sketchapp.com/
sketchuphttps://www.sketchup.com/
skitchhttps://evernote.com/products/skitch
skypehttps://www.skype.com/
slackhttps://slack.com/
sonarrhttps://sonarr.tv/
sonarr-menuhttps://github.com/jefbarn/Sonarr-Menu
sourcetreehttps://www.sourcetreeapp.com/
steermousehttp://plentycom.jp/en/steermouse/
sublerhttps://subler.org/
sublime-texthttps://www.sublimetext.com/3
the-unarchiverhttps://theunarchiver.com/
time-sinkhttps://manytricks.com/timesink/
torbrowserhttps://www.torproject.org/projects/torbrowser.html
towerhttps://www.git-tower.com/
unrarxhttp://www.unrarx.com/
vimrhttp://vimr.org/
vlchttps://www.videolan.org/vlc/
vmware-fusionhttps://www.vmware.com/products/fusion.html
wiresharkhttps://www.wireshark.org/
xldhttp://tmkk.undo.jp/xld/index_e.html
caskroom/fonts/font-inconsolata-lgchttps://github.com/DeLaGuardo/Inconsolata-LGC
caskroom/versions/transmit4https://panic.com/transmit/
ptb/custom/adobe-creative-cloud-2014https://www.adobe.com/creativecloud.html
ptb/custom/blankscreenhttp://www.wurst-wasser.net/wiki/index.php/Blank_Screen_Saver
ptb/custom/composerhttps://www.jamf.com/products/jamf-composer/
ptb/custom/enhanced-dictation
ptb/custom/ipmenulethttps://github.com/mcandre/IPMenulet
ptb/custom/pcalc-3http://www.pcalc.com/english/about.html
ptb/custom/sketchup-prohttps://www.sketchup.com/products/sketchup-pro
ptb/custom/text-to-speech-alex
ptb/custom/text-to-speech-allison
ptb/custom/text-to-speech-samantha
ptb/custom/text-to-speech-tom
railwaycat/emacsmacport/emacs-mac-spacemacs-iconhttps://github.com/railwaycat/homebrew-emacsmacport

Add App Store Packages to Brewfile

install_brewfile_mas_apps () {
  open "/Applications/App Store.app"
  run "Sign in to the App Store with your Apple ID" "Cancel" "OK"

  MASDIR="$(getconf DARWIN_USER_CACHE_DIR)com.apple.appstore"
  sudo chown -R "$(whoami)" "${MASDIR}"
  rsync -a --delay-updates \
    "${CACHES}/mas/" "${MASDIR}/"

  printf "%s\n" "${_mas}" | \
  while IFS="$(printf '\t')" read app id; do
    printf 'mas "%s", id: %s\n' "${app}" "${id}" >> "${BREWFILE}"
  done
}

_mas

App NameApp IDApp Store URL
1Password443987910https://itunes.apple.com/app/id443987910
Affinity Photo824183456https://itunes.apple.com/app/id824183456
Coffitivity659901392https://itunes.apple.com/app/id659901392
Duplicate Photos Fixer Pro963642514https://itunes.apple.com/app/id963642514
Growl467939042https://itunes.apple.com/app/id467939042
HardwareGrowler475260933https://itunes.apple.com/app/id475260933
I Love Stars402642760https://itunes.apple.com/app/id402642760
Icon Slate439697913https://itunes.apple.com/app/id439697913
Justnotes511230166https://itunes.apple.com/app/id511230166
Keynote409183694https://itunes.apple.com/app/id409183694
Metanota Pro515250764https://itunes.apple.com/app/id515250764
Numbers409203825https://itunes.apple.com/app/id409203825
Pages409201541https://itunes.apple.com/app/id409201541
WiFi Explorer494803304https://itunes.apple.com/app/id494803304
Xcode497799835https://itunes.apple.com/app/id497799835
macOS High Sierra1209167288https://itunes.apple.com/app/id1209167288

Link System Utilities to Applications

install_links () {
  printf "%s\n" "${_links}" | \
  while IFS="$(printf '\t')" read link; do
    find "${link}" -maxdepth 1 -name "*.app" -type d -print0 2> /dev/null | \
    xargs -0 -I {} -L 1 ln -s "{}" "/Applications" 2> /dev/null
  done
}

_links

Application Locations
/System/Library/CoreServices/Applications
/Applications/Xcode.app/Contents/Applications
/Applications/Xcode.app/Contents/Developer/Applications
/Applications/Xcode-beta.app/Contents/Applications
/Applications/Xcode-beta.app/Contents/Developer/Applications

Install Node.js with nodenv

install_node_sw () {
  if which nodenv > /dev/null; then
    NODENV_ROOT="/usr/local/node" && export NODENV_ROOT

    sudo mkdir -p "$NODENV_ROOT"
    sudo chown -R "$(whoami):admin" "$NODENV_ROOT"

    p "Installing Node.js with nodenv"
    git clone https://github.com/nodenv/node-build-update-defs.git \
      "$(nodenv root)"/plugins/node-build-update-defs
    nodenv update-version-defs > /dev/null

    nodenv install --skip-existing 8.7.0
    nodenv global 8.7.0

    grep -q "${NODENV_ROOT}" "/etc/paths" || \
    sudo sed -i "" -e "1i\\
${NODENV_ROOT}/shims
" "/etc/paths"

    init_paths
    rehash
  fi

  T=$(printf '\t')

  printf "%s\n" "$_npm" | \
  while IFS="$T" read pkg; do
    npm install --global "$pkg"
  done

  rehash
}
NPM Package NameReference URL
eslinthttps://eslint.org/
eslint-config-cleanjshttps://github.com/bodil/eslint-config-cleanjs
eslint-plugin-betterhttps://github.com/idmitriev/eslint-plugin-better
eslint-plugin-fphttps://github.com/jfmengels/eslint-plugin-fp
eslint-plugin-importhttps://github.com/benmosher/eslint-plugin-import
eslint-plugin-jsonhttps://github.com/azeemba/eslint-plugin-json
eslint-plugin-promisehttps://github.com/xjamundx/eslint-plugin-promise
eslint-plugin-standardhttps://github.com/xjamundx/eslint-plugin-standard
gatsby
jsonhttp://trentm.com/json/
sort-jsonhttps://github.com/kesla/sort-json

Install Perl 5 with plenv

install_perl_sw () {
  if which plenv > /dev/null; then
    PLENV_ROOT="/usr/local/perl" && export PLENV_ROOT

    sudo mkdir -p "$PLENV_ROOT"
    sudo chown -R "$(whoami):admin" "$PLENV_ROOT"

    p "Installing Perl 5 with plenv"
    plenv install 5.26.0 > /dev/null 2>&1
    plenv global 5.26.0

    grep -q "${PLENV_ROOT}" "/etc/paths" || \
    sudo sed -i "" -e "1i\\
${PLENV_ROOT}/shims
" "/etc/paths"

    init_paths
    rehash
  fi
}

Install Python with pyenv

install_python_sw () {
  if which pyenv > /dev/null; then
    CFLAGS="-I$(brew --prefix openssl)/include" && export CFLAGS
    LDFLAGS="-L$(brew --prefix openssl)/lib" && export LDFLAGS
    PYENV_ROOT="/usr/local/python" && export PYENV_ROOT

    sudo mkdir -p "$PYENV_ROOT"
    sudo chown -R "$(whoami):admin" "$PYENV_ROOT"

    p "Installing Python 2 with pyenv"
    pyenv install --skip-existing 2.7.13
    p "Installing Python 3 with pyenv"
    pyenv install --skip-existing 3.6.2
    pyenv global 2.7.13

    grep -q "${PYENV_ROOT}" "/etc/paths" || \
    sudo sed -i "" -e "1i\\
${PYENV_ROOT}/shims
" "/etc/paths"

    init_paths
    rehash

    pip install --upgrade "pip" "setuptools"

    # Reference: https://github.com/mdhiggins/sickbeard_mp4_automator
    pip install --upgrade "babelfish" "guessit<2" "qtfaststart" "requests" "stevedore==1.19.1" "subliminal<2"
    pip install --upgrade "requests-cache" "requests[security]"

    # Reference: https://github.com/pixelb/crudini
    pip install --upgrade "crudini"
  fi
}

Install Ruby with rbenv

install_ruby_sw () {
  if which rbenv > /dev/null; then
    RBENV_ROOT="/usr/local/ruby" && export RBENV_ROOT

    sudo mkdir -p "$RBENV_ROOT"
    sudo chown -R "$(whoami):admin" "$RBENV_ROOT"

    p "Installing Ruby with rbenv"
    rbenv install --skip-existing 2.4.2
    rbenv global 2.4.2

    grep -q "${RBENV_ROOT}" "/etc/paths" || \
    sudo sed -i "" -e "1i\\
${RBENV_ROOT}/shims
" "/etc/paths"

    init_paths
    rehash

    printf "%s\n" \
      "gem: --no-document" | \
    tee "${HOME}/.gemrc" > /dev/null

    gem update --system > /dev/null

    trash "$(which rdoc)"
    trash "$(which ri)"
    gem update

    gem install bundler
  fi
}

Configure

Define Function config

config () {
  config_admin_req
  config_bbedit
  config_certbot
  config_desktop
  config_dovecot
  config_emacs
  config_environment
  config_ipmenulet
  config_istatmenus
  config_nginx
  config_openssl
  config_sysprefs
  config_zsh
  config_guest

  which custom
}

Define Function config_defaults

config_defaults () {
  printf "%s\n" "${1}" | \
  while IFS="$(printf '\t')" read domain key type value host; do
    ${2} defaults ${host} write ${domain} "${key}" ${type} "${value}"
  done
}

Define Function config_plist

T="$(printf '\t')"

config_plist () {
  printf "%s\n" "$1" | \
  while IFS="$T" read command entry type value; do
    case "$value" in
      (\$*)
        $4 /usr/libexec/PlistBuddy "$2" \
          -c "$command '${3}${entry}' $type '$(eval echo \"$value\")'" 2> /dev/null ;;
      (*)
        $4 /usr/libexec/PlistBuddy "$2" \
          -c "$command '${3}${entry}' $type '$value'" 2> /dev/null ;;
    esac
  done
}

Define Function config_launchd

config_launchd () {
  test -d "$(dirname $1)" || \
    $3 mkdir -p "$(dirname $1)"

  test -f "$1" && \
    $3 launchctl unload "$1" && \
    $3 rm -f "$1"

  config_plist "$2" "$1" "$4" "$3" && \
    $3 plutil -convert xml1 "$1" && \
    $3 launchctl load "$1"
}

Mark Applications Requiring Administrator Account

config_admin_req () {
  printf "%s\n" "${_admin_req}" | \
  while IFS="$(printf '\t')" read app; do
    sudo tag -a "Red, admin" "/Applications/${app}"
  done
}

_admin_req

Admin Apps
Carbon Copy Cloner.app
Charles.app
Composer.app
Dropbox.app
iStat Menus.app
Moom.app
VMware Fusion.app
Wireshark.app

Configure BBEdit

config_bbedit () {
  if test -d "/Applications/BBEdit.app"; then
    test -f "/usr/local/bin/bbdiff" || \
    ln /Applications/BBEdit.app/Contents/Helpers/bbdiff /usr/local/bin/bbdiff && \
    ln /Applications/BBEdit.app/Contents/Helpers/bbedit_tool /usr/local/bin/bbedit && \
    ln /Applications/BBEdit.app/Contents/Helpers/bbfind /usr/local/bin/bbfind && \
    ln /Applications/BBEdit.app/Contents/Helpers/bbresults /usr/local/bin/bbresults
  fi
}

Configure Let’s Encrypt

config_certbot () {
  test -d "/etc/letsencrypt" || \
    sudo mkdir -p /etc/letsencrypt

  sudo tee "/etc/letsencrypt/cli.ini" << EOF > /dev/null
agree-tos = True
authenticator = standalone
eff-email = True
manual-public-ip-logging-ok = True
nginx-ctl = $(which nginx)
nginx-server-root = /usr/local/etc/nginx
preferred-challenges = tls-sni-01
keep-until-expiring = True
rsa-key-size = 4096
text = True
EOF

  if ! test -e "/etc/letsencrypt/.git"; then
    a=$(ask "Existing Let’s Encrypt Git Repository Path or URL?" "Clone Repository" "")
    test -n "$a" && \
    case "$a" in
      (/*)
        sudo tee "/etc/letsencrypt/.git" << EOF > /dev/null ;;
gitdir: $a
EOF
      (*)
        sudo git -C "/etc/letsencrypt" remote add origin "$a"
        sudo git -C "/etc/letsencrypt" fetch origin master ;;
    esac
    sudo git -C "/etc/letsencrypt" reset --hard
    sudo git checkout -f -b master HEAD
  fi

  sudo launchctl unload /Library/LaunchDaemons/org.nginx.nginx.plist 2> /dev/null
  sudo certbot renew

  while true; do
    test -n "$1" && server_name="$1" || \
      server_name="$(ask 'New SSL Server: Server Name?' 'Create Server' 'example.com')"
    test -n "$server_name" || break

    test -n "$2" && proxy_address="$2" || \
      proxy_address="$(ask "Proxy Address for $server_name?" 'Set Address' 'http://127.0.0.1:80')"

    sudo certbot certonly --domain $server_name

    key1="$(openssl x509 -pubkey < /etc/letsencrypt/live/$server_name/fullchain.pem | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | base64)"
    key2="$(curl -s https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem | openssl x509 -pubkey | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | base64)"
    key3="$(curl -s https://letsencrypt.org/certs/isrgrootx1.pem | openssl x509 -pubkey | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | base64)"

    pkp="$(printf "add_header Public-Key-Pins 'pin-sha256=\"%s\"; pin-sha256=\"%s\"; pin-sha256=\"%s\"; max-age=2592000;';\n" $key1 $key2 $key3)"

    cat << EOF > "/usr/local/etc/nginx/servers/$server_name.conf"
<<server_name.conf>>
EOF
    unset argv
  done

  sudo launchctl load /Library/LaunchDaemons/org.nginx.nginx.plist
}

/usr/local/etc/nginx/servers/server_name/server_name.conf

server {
  server_name $server_name;

  location / {
    proxy_pass $proxy_address;
  }

  ssl_certificate /etc/letsencrypt/live/$server_name/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/$server_name/privkey.pem;
  ssl_trusted_certificate /etc/letsencrypt/live/$server_name/chain.pem;

  $pkp

  add_header Content-Security-Policy "upgrade-insecure-requests;";
  add_header Referrer-Policy "strict-origin";
  add_header Strict-Transport-Security "max-age=15552000; includeSubDomains; preload" always;
  add_header X-Content-Type-Options nosniff;
  add_header X-Frame-Options DENY;
  add_header X-Robots-Tag none;
  add_header X-XSS-Protection "1; mode=block";

  listen 443 ssl http2;
  listen [::]:443 ssl http2;

  ssl_stapling on;
  ssl_stapling_verify on;

  # https://securityheaders.io/?q=https%3A%2F%2F$server_name&hide=on&followRedirects=on
  # https://www.ssllabs.com/ssltest/analyze.html?d=$server_name&hideResults=on&latest
}

Configure Default Apps

config_default_apps () {
  true
}

Configure Desktop Picture

config_desktop () {
  sudo rm -f "/Library/Caches/com.apple.desktop.admin.png"

  base64 -D << EOF > "/Library/Desktop Pictures/Solid Colors/Solid Black.png"
<<black.png.b64>>
EOF
}

black.png.b64

iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQAAAADrRVxmAAAAGElEQVR4AWOgMxgFo2AUjIJRMApGwSgAAAiAAAH3bJXBAAAAAElFTkSuQmCC

Configure Dovecot

config_dovecot () {
  if which /usr/local/sbin/dovecot > /dev/null; then
    if ! run "Configure Dovecot Email Server?" "Configure Server" "Cancel"; then
      sudo tee "/usr/local/etc/dovecot/dovecot.conf" << EOF > /dev/null
<<dovecot.conf>>
EOF

      MAILADM="$(ask 'Email: Postmaster Email?' 'Set Email' "$(whoami)@$(hostname -f | cut -d. -f2-)")"
      MAILSVR="$(ask 'Email: Server Hostname for DNS?' 'Set Hostname' "$(hostname -f)")"
      sudo certbot certonly --domain $MAILSVR
      printf "%s\n" \
        "postmaster_address = '${MAILADM}'" \
        "ssl_cert = </etc/letsencrypt/live/$MAILSVR/fullchain.pem" \
        "ssl_key = </etc/letsencrypt/live/$MAILSVR/privkey.pem" | \
      sudo tee -a "/usr/local/etc/dovecot/dovecot.conf" > /dev/null

      if test ! -f "/usr/local/etc/dovecot/cram-md5.pwd"; then
        while true; do
          MAILUSR="$(ask 'New Email Account: User Name?' 'Create Account' "$(whoami)")"
          test -n "${MAILUSR}" || break
          doveadm pw | \
          sed -e "s/^/${MAILUSR}:/" | \
          sudo tee -a "/usr/local/etc/dovecot/cram-md5.pwd"
        done
        sudo chown _dovecot "/usr/local/etc/dovecot/cram-md5.pwd"
        sudo chmod go= "/usr/local/etc/dovecot/cram-md5.pwd"
      fi

      sudo tee "/etc/pam.d/dovecot" << EOF > /dev/null
<<dovecot.pam>>
EOF

      sudo brew services start dovecot

      cat << EOF > "/usr/local/bin/imaptimefix.py"
<<imaptimefix.py>>
EOF
      chmod +x /usr/local/bin/imaptimefix.py
    fi
  fi
}

/usr/local/etc/dovecot/dovecot.conf

auth_mechanisms = cram-md5
default_internal_user = _dovecot
default_login_user = _dovenull
log_path = /dev/stderr
mail_location = maildir:~/.mail:INBOX=~/.mail/Inbox:LAYOUT=fs
mail_plugins = zlib
maildir_copy_with_hardlinks = no
namespace {
  inbox = yes
  mailbox Drafts {
    auto = subscribe
    special_use = \Drafts
  }
  mailbox Junk {
    auto = subscribe
    special_use = \Junk
  }
  mailbox Sent {
    auto = subscribe
    special_use = \Sent
  }
  mailbox "Sent Messages" {
    special_use = \Sent
  }
  mailbox Trash {
    auto = subscribe
    special_use = \Trash
  }
  separator = .
  type = private
}
passdb {
  args = scheme=cram-md5 /usr/local/etc/dovecot/cram-md5.pwd
  driver = passwd-file

  # driver = pam

  # args = nopassword=y
  # driver = static
}
plugin {
  sieve = file:/Users/%u/.sieve
  sieve_plugins = sieve_extprograms
  zlib_save = bz2
  zlib_save_level = 9
}
protocols = imap
service imap-login {
  inet_listener imap {
    port = 0
  }
}
ssl = required
ssl_cipher_list = ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS:!AES128
ssl_dh_parameters_length = 4096
ssl_prefer_server_ciphers = yes
ssl_protocols = !SSLv2 !SSLv3
userdb {
  driver = passwd
}
protocol lda {
  mail_plugins = sieve zlib
}

# auth_debug = yes
# auth_debug_passwords = yes
# auth_verbose = yes
# auth_verbose_passwords = plain
# mail_debug = yes
# verbose_ssl = yes

/etc/pam.d/dovecot

auth	required	pam_opendirectory.so	try_first_pass
account	required	pam_nologin.so
account	required	pam_opendirectory.so
password	required	pam_opendirectory.so

/usr/local/bin/imaptimefix.py

#!/usr/bin/env python

# Author: Zachary Cutlip <@zcutlip>
# http://shadow-file.blogspot.com/2012/06/parsing-email-and-fixing-timestamps-in.html
# Updated: Peter T Bosse II <@ptb>
# Purpose: A program to fix sorting of mail messages that have been POPed or
#          IMAPed in the wrong order. Compares time stamp sent and timestamp
#          received on an RFC822-formatted email message, and renames the
#          message file using the most recent timestamp that is no more than
#          24 hours after the date sent. Updates the file's atime/mtime with
#          the timestamp, as well. Does not modify the headers or contents of
#          the message.

from bz2 import BZ2File
from email import message_from_string
from email.utils import mktime_tz, parsedate_tz
from os import rename, utime, walk
from os.path import abspath, isdir, isfile, join
from re import compile, match
from sys import argv

if isdir(argv[1]):
  e = compile("([0-9]+)(\..*$)")

  for a, b, c in walk(argv[1]):
    for d in c:
      if e.match(d):
        f = message_from_string(BZ2File(join(a, d)).read())
        g = mktime_tz(parsedate_tz(f.get("Date")))

        h = 0
        for i in f.get_all("Received", []):
          j = i.split(";")[-1]
          if parsedate_tz(j):
            k = mktime_tz(parsedate_tz(j))
            if (k - g) > (60*60*24):
              continue

            h = k
          break

        if (h < 1):
          h = g

        l = e.match(d)

        if len(l.groups()) == 2:
          m = str(int(h)) + l.groups()[1]
          if not isfile(join(a, m)):
            rename(join(a, d), join(a, m))
          utime(join(a, m), (h, h))

Configure Emacs

config_emacs () {
  test -f "/usr/local/bin/vi" || \
  cat << EOF > "/usr/local/bin/vi"
<<vi.sh>>
EOF

  chmod a+x /usr/local/bin/vi
  rehash
}

/usr/local/bin/vi

#!/bin/sh

if [ -e "/Applications/Emacs.app" ]; then
  t=()

  if [ \${#@} -ne 0 ]; then
    while IFS= read -r file; do
      [ ! -f "\$file" ] && t+=("\$file") && /usr/bin/touch "\$file"
      file=\$(echo \$(cd \$(dirname "\$file") && pwd -P)/\$(basename "\$file"))
      \$(/usr/bin/osascript <<-END
        if application "Emacs.app" is running then
          tell application id (id of application "Emacs.app") to open POSIX file "\$file"
        else
          tell application ((path to applications folder as text) & "Emacs.app")
            activate
            open POSIX file "\$file"
          end tell
        end if
END
        ) &  # Note: END on the previous line may be indented with tabs but not spaces
    done <<<"\$(printf '%s\n' "\$@")"
  fi

  if [ ! -z "\$t" ]; then
    \$(/bin/sleep 10; for file in "\${t[@]}"; do
      [ ! -s "\$file" ] && /bin/rm "\$file";
    done) &
  fi
else
  vim -No "\$@"
fi

Configure Environment Variables

config_environment () {
  sudo tee "/etc/environment.sh" << 'EOF' > /dev/null
<<environment.sh>>
EOF
  sudo chmod a+x "/etc/environment.sh"
  rehash

  la="/Library/LaunchAgents/environment.user"
  ld="/Library/LaunchDaemons/environment"

  sudo mkdir -p "$(dirname $la)" "$(dirname $ld)"
  sudo launchctl unload "${la}.plist" "${ld}.plist" 2> /dev/null
  sudo rm -f "${la}.plist" "${ld}.plist"

  config_defaults "$_environment_defaults" "sudo"
  sudo plutil -convert xml1 "${la}.plist" "${ld}.plist"
  sudo launchctl load "${la}.plist" "${ld}.plist" 2> /dev/null
}

/etc/environment.sh

#!/bin/sh

set -e

if test -x /usr/libexec/path_helper; then
  export PATH=""
  eval `/usr/libexec/path_helper -s`
  launchctl setenv PATH $PATH
fi

osascript -e 'tell app "Dock" to quit'

_environment_defaults

DomainKeyTypeValueHost
/Library/LaunchAgents/environment.userKeepAlive-boolfalse
/Library/LaunchAgents/environment.userLabel-stringenvironment.user
/Library/LaunchAgents/environment.userProcessType-stringBackground
/Library/LaunchAgents/environment.userProgram-string/etc/environment.sh
/Library/LaunchAgents/environment.userRunAtLoad-booltrue
/Library/LaunchAgents/environment.userWatchPaths-array-add/etc/environment.sh
/Library/LaunchAgents/environment.userWatchPaths-array-add/etc/paths
/Library/LaunchAgents/environment.userWatchPaths-array-add/etc/paths.d
/Library/LaunchDaemons/environmentKeepAlive-boolfalse
/Library/LaunchDaemons/environmentLabel-stringenvironment
/Library/LaunchDaemons/environmentProcessType-stringBackground
/Library/LaunchDaemons/environmentProgram-string/etc/environment.sh
/Library/LaunchDaemons/environmentRunAtLoad-booltrue
/Library/LaunchDaemons/environmentWatchPaths-array-add/etc/environment.sh
/Library/LaunchDaemons/environmentWatchPaths-array-add/etc/paths
/Library/LaunchDaemons/environmentWatchPaths-array-add/etc/paths.d

Configure IPMenulet

config_ipmenulet () {
  _ipm="/Applications/IPMenulet.app/Contents/Resources"
  if test -d "$_ipm"; then
    rm "${_ipm}/icon-19x19-black.png"
    ln "${_ipm}/icon-19x19-white.png" "${_ipm}/icon-19x19-black.png"
  fi
}

Configure iStat Menus

config_istatmenus () {
  test -d "/Applications/iStat Menus.app" && \
  open "/Applications/iStat Menus.app"
}

Notes

client_max_body_size 0;

location / {
  if ($http_x_plex_device_name = "") {
    rewrite ^/$ https://$host/web/index.html permanent;
  }
}

Configure nginx

config_nginx () {
  cat << 'EOF' > /usr/local/etc/nginx/nginx.conf
<<nginx.conf>>
EOF

  ld="/Library/LaunchDaemons/org.nginx.nginx"

  sudo mkdir -p "$(dirname $ld)"
  sudo launchctl unload "${ld}.plist" 2> /dev/null
  sudo rm -f "${ld}.plist"

  config_defaults "$_nginx_defaults" "sudo"
  sudo plutil -convert xml1 "${ld}.plist"
  sudo launchctl load "${ld}.plist" 2> /dev/null
}

/usr/local/etc/nginx/nginx.conf

daemon off;

events {
  accept_mutex off;
  worker_connections 8000;
}

http {
  charset utf-8;
  charset_types
    application/javascript
    application/json
    application/rss+xml
    application/xhtml+xml
    application/xml
    text/css
    text/plain
    text/vnd.wap.wml;

  default_type application/octet-stream;

  error_log /dev/stderr;

  gzip on;
  gzip_comp_level 9;
  gzip_min_length 256;
  gzip_proxied any;
  gzip_static on;
  gzip_vary on;

  gzip_types
    application/atom+xml
    application/javascript
    application/json
    application/ld+json
    application/manifest+json
    application/rss+xml
    application/vnd.geo+json
    application/vnd.ms-fontobject
    application/x-font-ttf
    application/x-web-app-manifest+json
    application/xhtml+xml
    application/xml
    font/opentype
    image/bmp
    image/svg+xml
    image/x-icon
    text/cache-manifest
    text/css
    text/plain
    text/vcard
    text/vnd.rim.location.xloc
    text/vtt
    text/x-component
    text/x-cross-domain-policy;

  index index.html index.xhtml;

  log_format default '$host $status $body_bytes_sent "$request" "$http_referer"\n'
    '  $remote_addr "$http_user_agent"';

  map $http_upgrade $connection_upgrade {
    default upgrade;
    "" close;
  }

  proxy_http_version 1.1;
  proxy_set_header Upgrade $http_upgrade;
  proxy_set_header Connection $connection_upgrade;

  proxy_set_header Host $host;
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_set_header X-Forwarded-Proto $scheme;
  proxy_set_header X-Real-IP $remote_addr;

  proxy_buffering off;
  proxy_redirect off;

  sendfile on;
  sendfile_max_chunk 512k;

  server_tokens off;

  resolver 8.8.8.8 8.8.4.4 [2001:4860:4860::8888] [2001:4860:4860::8844] valid=300s;
  resolver_timeout 5s;

  # https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/
  ssl_ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS:!AES128;

  # openssl dhparam -out /etc/letsencrypt/ssl-dhparam.pem 4096
  ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

  ssl_ecdh_curve secp384r1;
  ssl_prefer_server_ciphers on;
  ssl_protocols TLSv1.2;
  ssl_session_cache shared:TLS:10m;

  types {
    application/atom+xml atom;
    application/font-woff woff;
    application/font-woff2 woff2;
    application/java-archive ear jar war;
    application/javascript js;
    application/json json map topojson;
    application/ld+json jsonld;
    application/mac-binhex40 hqx;
    application/manifest+json webmanifest;
    application/msword doc;
    application/octet-stream bin deb dll dmg exe img iso msi msm msp safariextz;
    application/pdf pdf;
    application/postscript ai eps ps;
    application/rss+xml rss;
    application/rtf rtf;
    application/vnd.geo+json geojson;
    application/vnd.google-earth.kml+xml kml;
    application/vnd.google-earth.kmz kmz;
    application/vnd.ms-excel xls;
    application/vnd.ms-fontobject eot;
    application/vnd.ms-powerpoint ppt;
    application/vnd.openxmlformats-officedocument.presentationml.presentation pptx;
    application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx;
    application/vnd.openxmlformats-officedocument.wordprocessingml.document docx;
    application/vnd.wap.wmlc wmlc;
    application/x-7z-compressed 7z;
    application/x-bb-appworld bbaw;
    application/x-bittorrent torrent;
    application/x-chrome-extension crx;
    application/x-cocoa cco;
    application/x-font-ttf ttc ttf;
    application/x-java-archive-diff jardiff;
    application/x-java-jnlp-file jnlp;
    application/x-makeself run;
    application/x-opera-extension oex;
    application/x-perl pl pm;
    application/x-pilot pdb prc;
    application/x-rar-compressed rar;
    application/x-redhat-package-manager rpm;
    application/x-sea sea;
    application/x-shockwave-flash swf;
    application/x-stuffit sit;
    application/x-tcl tcl tk;
    application/x-web-app-manifest+json webapp;
    application/x-x509-ca-cert crt der pem;
    application/x-xpinstall xpi;
    application/xhtml+xml xhtml;
    application/xml rdf xml;
    application/xslt+xml xsl;
    application/zip zip;
    audio/midi mid midi kar;
    audio/mp4 aac f4a f4b m4a;
    audio/mpeg mp3;
    audio/ogg oga ogg opus;
    audio/x-realaudio ra;
    audio/x-wav wav;
    font/opentype otf;
    image/bmp bmp;
    image/gif gif;
    image/jpeg jpeg jpg;
    image/png png;
    image/svg+xml svg svgz;
    image/tiff tif tiff;
    image/vnd.wap.wbmp wbmp;
    image/webp webp;
    image/x-icon cur ico;
    image/x-jng jng;
    text/cache-manifest appcache;
    text/css css;
    text/html htm html shtml;
    text/mathml mml;
    text/plain txt;
    text/vcard vcard vcf;
    text/vnd.rim.location.xloc xloc;
    text/vnd.sun.j2me.app-descriptor jad;
    text/vnd.wap.wml wml;
    text/vtt vtt;
    text/x-component htc;
    video/3gpp 3gp 3gpp;
    video/mp4 f4p f4v m4v mp4;
    video/mpeg mpeg mpg;
    video/ogg ogv;
    video/quicktime mov;
    video/webm webm;
    video/x-flv flv;
    video/x-mng mng;
    video/x-ms-asf asf asx;
    video/x-ms-wmv wmv;
    video/x-msvideo avi;
  }

  include servers/*.conf;
}

worker_processes auto;

_nginx_defaults

DomainKeyTypeValueHost
/Library/LaunchDaemons/org.nginx.nginxKeepAlive-booltrue
/Library/LaunchDaemons/org.nginx.nginxLabel-stringorg.nginx.nginx
/Library/LaunchDaemons/org.nginx.nginxProcessType-stringBackground
/Library/LaunchDaemons/org.nginx.nginxProgram-string/usr/local/bin/nginx
/Library/LaunchDaemons/org.nginx.nginxRunAtLoad-booltrue
/Library/LaunchDaemons/org.nginx.nginxStandardErrorPath-string/usr/local/var/log/nginx/error.log
/Library/LaunchDaemons/org.nginx.nginxStandardOutPath-string/usr/local/var/log/nginx/access.log
/Library/LaunchDaemons/org.nginx.nginxUserName-stringroot
/Library/LaunchDaemons/org.nginx.nginxWatchPaths-array-add/usr/local/etc/nginx

Configure OpenSSL

Create an intentionally invalid certificate for use with a DNS-based ad blocker, e.g. https://pi-hole.net

config_openssl () {
  _default="/etc/letsencrypt/live/default"
  test -d "$_default" || mkdir -p "$_default"

  cat << EOF > "${_default}/default.cnf"
<<openssl.cnf>>
EOF

  openssl req -days 1 -new -newkey rsa -x509 \
    -config "${_default}/default.cnf" \
    -out "${_default}/default.crt"

  cat << EOF > "/usr/local/etc/nginx/servers/default.conf"
<<default.conf>>
EOF
}

/etc/letsencrypt/live/default/default.cnf

[ req ]
default_bits = 4096
default_keyfile = ${_default}/default.key
default_md = sha256
distinguished_name = dn
encrypt_key = no
prompt = no
utf8 = yes
x509_extensions = v3_ca

[ dn ]
CN = *

[ v3_ca ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = CA:true

/usr/local/etc/nginx/servers/default.conf

server {
  server_name .$(hostname -f | cut -d. -f2-);

  listen 80;
  listen [::]:80;

  return 301 https://\$host\$request_uri;
}

server {
  listen 80 default_server;
  listen [::]:80 default_server;

  listen 443 default_server ssl http2;
  listen [::]:443 default_server ssl http2;

  ssl_certificate ${_default}/default.crt;
  ssl_certificate_key ${_default}/default.key;

  ssl_ciphers NULL;

  return 204;
}

Configure System Preferences

config_sysprefs () {
  config_energy
  config_loginwindow
  config_mas
}

Configure Energy Saver

config_energy () {
  printf "%s\n" "${_energy}" | \
  while IFS="$(printf '\t')" read flag setting value; do
    sudo pmset $flag ${setting} ${value}
  done
}
_energy
PreferenceFlagSettingValue
Power: Turn display off after: 20 min-cdisplaysleep20
Power: on Prevent computer from sleeping automatically when the display is off-csleep0
Power: 60 min Put hard disks to sleep when possible-cdisksleep60
Power: on Wake for network access-cwomp1
Power: on Start up automatically after a power failure-cautorestart1
Power: on Enable Power Nap-cpowernap1
UPS: Turn display off after: 2 min-udisplaysleep2
UPS: on Slightly dim the display when using this power source-ulessbright1
UPS: on Shut down the computer after using the UPS battery for: 5 min-uhaltafter5
UPS: off Shut down the computer when the time remaining on the UPS battery is:-uhaltremain-1
UPS: off Shut down the computer when the UPS battery level is below:-uhaltlevel-1

Configure Login Window

config_loginwindow () {
  config_defaults "${_loginwindow}" "sudo"
}

_loginwindow

PreferenceDomainKeyTypeValueHost
Display login window as: Name and password/Library/Preferences/com.apple.loginwindowSHOWFULLNAME-booltrue

Configure App Store

config_mas () {
  config_defaults "${_swupdate}" "sudo"
}

_swupdate

PreferenceDomainKeyTypeValueHost
on Install app updates/Library/Preferences/com.apple.commerceAutoUpdate-booltrue
on Install macOS updates/Library/Preferences/com.apple.commerceAutoUpdateRestartRequired-booltrue

Configure Z-Shell

config_zsh () {
  grep -q "$(which zsh)" /etc/shells ||
  print "$(which zsh)\n" | \
  sudo tee -a /etc/shells > /dev/null

  case "$SHELL" in
    ($(which zsh)) ;;
    (*)
      chsh -s "$(which zsh)"
      sudo chsh -s $(which zsh) ;;
  esac

  sudo tee -a /etc/zshenv << 'EOF' > /dev/null
<<etc-zshenv>>
EOF
  sudo chmod +x "/etc/zshenv"
  . "/etc/zshenv"

  sudo tee /etc/zshrc << 'EOF' > /dev/null
<<etc-zshrc>>
EOF
  sudo chmod +x "/etc/zshrc"
  . "/etc/zshrc"
}

/etc/zshenv

#-- Exports ----------------------------------------------------

export \
  ZDOTDIR="${HOME}/.zsh" \
  MASDIR="$(getconf DARWIN_USER_CACHE_DIR)com.apple.appstore" \
  NODENV_ROOT="/usr/local/node" \
  PLENV_ROOT="/usr/local/perl" \
  PYENV_ROOT="/usr/local/python" \
  RBENV_ROOT="/usr/local/ruby" \
  EDITOR="vi" \
  VISUAL="vi" \
  PAGER="less" \
  LANG="en_US.UTF-8" \
  LESS="-egiMQRS -x2 -z-2" \
  LESSHISTFILE="/dev/null" \
  HISTSIZE=50000 \
  SAVEHIST=50000 \
  KEYTIMEOUT=1

test -d "$ZDOTDIR" || \
  mkdir -p "$ZDOTDIR"

test -f "${ZDOTDIR}/.zshrc" || \
  touch "${ZDOTDIR}/.zshrc"

# Ensure path arrays do not contain duplicates.
typeset -gU cdpath fpath mailpath path

/etc/zshrc

#-- Exports ----------------------------------------------------

export \
  HISTFILE="${ZDOTDIR:-$HOME}/.zhistory"

#-- Changing Directories ---------------------------------------

setopt \
  autocd \
  autopushd \
  cdablevars \
  chasedots \
  chaselinks \
  NO_posixcd \
  pushdignoredups \
  no_pushdminus \
  pushdsilent \
  pushdtohome

#-- Completion -------------------------------------------------

setopt \
  ALWAYSLASTPROMPT \
  no_alwaystoend \
  AUTOLIST \
  AUTOMENU \
  autonamedirs \
  AUTOPARAMKEYS \
  AUTOPARAMSLASH \
  AUTOREMOVESLASH \
  no_bashautolist \
  no_completealiases \
  completeinword \
  no_globcomplete \
  HASHLISTALL \
  LISTAMBIGUOUS \
  no_LISTBEEP \
  no_listpacked \
  no_listrowsfirst \
  LISTTYPES \
  no_menucomplete \
  no_recexact

#-- Expansion and Globbing -------------------------------------

setopt \
  BADPATTERN \
  BAREGLOBQUAL \
  braceccl \
  CASEGLOB \
  CASEMATCH \
  NO_cshnullglob \
  EQUALS \
  extendedglob \
  no_forcefloat \
  GLOB \
  NO_globassign \
  no_globdots \
  no_globstarshort \
  NO_globsubst \
  no_histsubstpattern \
  NO_ignorebraces \
  no_ignoreclosebraces \
  NO_kshglob \
  no_magicequalsubst \
  no_markdirs \
  MULTIBYTE \
  NOMATCH \
  no_nullglob \
  no_numericglobsort \
  no_rcexpandparam \
  no_rematchpcre \
  NO_shglob \
  UNSET \
  no_warncreateglobal \
  no_warnnestedvar

#-- History ----------------------------------------------------

setopt \
  APPENDHISTORY \
  BANGHIST \
  extendedhistory \
  no_histallowclobber \
  no_HISTBEEP \
  histexpiredupsfirst \
  no_histfcntllock \
  histfindnodups \
  histignorealldups \
  histignoredups \
  histignorespace \
  histlexwords \
  no_histnofunctions \
  no_histnostore \
  histreduceblanks \
  HISTSAVEBYCOPY \
  histsavenodups \
  histverify \
  incappendhistory \
  incappendhistorytime \
  sharehistory

#-- Initialisation ---------------------------------------------

setopt \
  no_allexport \
  GLOBALEXPORT \
  GLOBALRCS \
  RCS

#-- Input/Output -----------------------------------------------

setopt \
  ALIASES \
  no_CLOBBER \
  no_correct \
  no_correctall \
  dvorak \
  no_FLOWCONTROL \
  no_ignoreeof \
  NO_interactivecomments \
  HASHCMDS \
  HASHDIRS \
  no_hashexecutablesonly \
  no_mailwarning \
  pathdirs \
  NO_pathscript \
  no_printeightbit \
  no_printexitvalue \
  rcquotes \
  NO_rmstarsilent \
  no_rmstarwait \
  SHORTLOOPS \
  no_sunkeyboardhack

#-- Job Control ------------------------------------------------

setopt \
  no_autocontinue \
  autoresume \
  no_BGNICE \
  CHECKJOBS \
  no_HUP \
  longlistjobs \
  MONITOR \
  NOTIFY \
  NO_posixjobs

#-- Prompting --------------------------------------------------

setopt \
  NO_promptbang \
  PROMPTCR \
  PROMPTSP \
  PROMPTPERCENT \
  promptsubst \
  transientrprompt

#-- Scripts and Functions --------------------------------------

setopt \
  NO_aliasfuncdef \
  no_cbases \
  no_cprecedences \
  DEBUGBEFORECMD \
  no_errexit \
  no_errreturn \
  EVALLINENO \
  EXEC \
  FUNCTIONARGZERO \
  no_localloops \
  NO_localoptions \
  no_localpatterns \
  NO_localtraps \
  MULTIFUNCDEF \
  MULTIOS \
  NO_octalzeroes \
  no_pipefail \
  no_sourcetrace \
  no_typesetsilent \
  no_verbose \
  no_xtrace

#-- Shell Emulation --------------------------------------------

setopt \
  NO_appendcreate \
  no_bashrematch \
  NO_bsdecho \
  no_continueonerror \
  NO_cshjunkiehistory \
  NO_cshjunkieloops \
  NO_cshjunkiequotes \
  NO_cshnullcmd \
  NO_ksharrays \
  NO_kshautoload \
  NO_kshoptionprint \
  no_kshtypeset \
  no_kshzerosubscript \
  NO_posixaliases \
  no_posixargzero \
  NO_posixbuiltins \
  NO_posixidentifiers \
  NO_posixstrings \
  NO_posixtraps \
  NO_shfileexpansion \
  NO_shnullcmd \
  NO_shoptionletters \
  NO_shwordsplit \
  no_trapsasync

#-- Zle --------------------------------------------------------

setopt \
  no_BEEP \
  combiningchars \
  no_overstrike \
  NO_singlelinezle

#-- Aliases ----------------------------------------------------

alias \
  ll="/bin/ls -aFGHhlOw"

#-- Functions --------------------------------------------------

autoload -Uz \
  add-zsh-hook \
  compaudit \
  compinit

compaudit 2> /dev/null | \
  xargs -L 1 chmod go-w 2> /dev/null

compinit -u

which nodenv > /dev/null && \
  eval "$(nodenv init - zsh)"

which plenv > /dev/null && \
  eval "$(plenv init - zsh)"

which pyenv > /dev/null && \
  eval "$(pyenv init - zsh)"

which rbenv > /dev/null && \
  eval "$(rbenv init - zsh)"

sf () {
  SetFile -P -d "$1 12:00:00" -m "$1 12:00:00" $argv[2,$]
}

ssh-add -A 2> /dev/null

#-- zsh-syntax-highlighting ------------------------------------

. "$(brew --prefix)/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh"

#-- zsh-history-substring-search -------------------------------

. "$(brew --prefix)/share/zsh-history-substring-search/zsh-history-substring-search.zsh"

HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_FOUND="fg=default,underline" && \
  export HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_FOUND
HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_NOT_FOUND="fg=red,underline" && \
  export HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_NOT_FOUND

#-- Zle --------------------------------------------------------

zmodload zsh/zle

bindkey -d
bindkey -v

for k in "vicmd" "viins"; do
  bindkey -M $k '\C-A' beginning-of-line
  bindkey -M $k '\C-E' end-of-line
  bindkey -M $k '\C-U' kill-whole-line
  bindkey -M $k '\e[3~' delete-char
  bindkey -M $k '\e[A' history-substring-search-up
  bindkey -M $k '\e[B' history-substring-search-down
  bindkey -M $k '\x7f' backward-delete-char
done

for f in \
  "zle-keymap-select" \
  "zle-line-finish" \
  "zle-line-init"
do
  eval "$f () {
    case \$TERM_PROGRAM in
      ('Apple_Terminal')
        test \$KEYMAP = 'vicmd' && \
          printf '%b' '\e[4 q' || \
          printf '%b' '\e[6 q' ;;
      ('iTerm.app')
        test \$KEYMAP = 'vicmd' && \
          printf '%b' '\e]Plf27f7f\e\x5c\e[4 q' || \
          printf '%b' '\e]Pl99cc99\e\x5c\e[6 q' ;;
    esac
  }"
  zle -N $f
done

#-- prompt_ptb_setup -------------------------------------------

prompt_ptb_setup () {
  I="$(printf '%b' '%{\e[3m%}')"
  i="$(printf '%b' '%{\e[0m%}')"
  PROMPT="%F{004}$I%d$i %(!.%F{001}.%F{002})%n %B❯%b%f " && \
  export PROMPT
}

prompt_ptb_setup

prompt_ptb_precmd () {
  if test "$(uname -s)" = "Darwin"; then
    print -Pn "\e]7;file://%M\${PWD// /%%20}\a"
    print -Pn "\e]2;%n@%m\a"
    print -Pn "\e]1;%~\a"
  fi

  test -n "$(git rev-parse --git-dir 2> /dev/null)" && \
  RPROMPT="%F{000}$(git rev-parse --abbrev-ref HEAD 2> /dev/null)%f" && \
  export RPROMPT
}

add-zsh-hook precmd \
  prompt_ptb_precmd

Configure New Account

config_new_account () {
  e="$(ask 'New macOS Account: Email Address?' 'OK' '')"
  curl --output "/Library/User Pictures/${e}.jpg" --silent \
    "https://www.gravatar.com/avatar/$(md5 -qs ${e}).jpg?s=512"

  g="$(curl --location --silent \
    "https://api.github.com/search/users?q=${e}" | \
    sed -n 's/^.*"url": "\(.*\)".*/\1/p')"
  g="$(curl --location --silent ${g})"

  n="$(printf ${g} | sed -n 's/^.*"name": "\(.*\)".*/\1/p')"
  n="$(ask 'New macOS Account: Real Name?' 'OK' ${n})"

  u="$(printf ${g} | sed -n 's/^.*"login": "\(.*\)".*/\1/p')"
  u="$(ask 'New macOS Account: User Name?' 'OK' ${u})"

  sudo defaults write \
    "/System/Library/User Template/Non_localized/Library/Preferences/.GlobalPreferences.plist" \
    "com.apple.swipescrolldirection" -bool false

  sudo sysadminctl -admin -addUser "${u}" -fullName "${n}" -password - \
    -shell "$(which zsh)" -picture "/Library/User Pictures/${e}.jpg"
}

Configure Guest Users

config_guest () {
  sudo sysadminctl -guestAccount off
}

Reinstate sudo Password

config_rm_sudoers () {
  sudo -- sh -c \
    "rm -f /etc/sudoers.d/wheel; dscl /Local/Default -delete /Groups/wheel GroupMembership $(whoami)"

  /usr/bin/read -n 1 -p "Press any key to continue.
" -s
  if run "Log Out Then Log Back In?" "Cancel" "Log Out"; then
    osascript -e 'tell app "loginwindow" to «event aevtrlgo»'
  fi
}

Customize

Define Function custom

custom () {
  custom_githome
  custom_atom
  custom_autoping
  custom_dropbox
  custom_duti
  custom_emacs
  custom_finder
  custom_getmail
  custom_git
  custom_gnupg
  custom_istatmenus
  custom_meteorologist
  custom_moom
  custom_mp4_automator
  custom_nvalt
  custom_nzbget
  custom_safari
  custom_sieve
  custom_sonarr
  custom_ssh
  custom_sysprefs
  custom_terminal
  custom_vim
  custom_vlc

  which personalize_all
}

Customize Home

custom_githome () {
  git -C "${HOME}" init

  test -f "${CACHES}/dbx/.zshenv" && \
    mkdir -p "${ZDOTDIR:-$HOME}" && \
    cp "${CACHES}/dbx/.zshenv" "${ZDOTDIR:-$HOME}" && \
    . "${ZDOTDIR:-$HOME}/.zshenv"

  a=$(ask "Existing Git Home Repository Path or URL" "Add Remote" "")
  if test -n "${a}"; then
    git -C "${HOME}" remote add origin "${a}"
    git -C "${HOME}" fetch origin master
  fi

  if run "Encrypt and commit changes to Git and push to GitHub, automatically?" "No" "Add AutoKeep"; then
    curl --location --silent \
      "https://github.com/ptb/autokeep/raw/master/autokeep.command" | \
      . /dev/stdin 0

    autokeep_remote
    autokeep_push
    autokeep_gitignore
    autokeep_post_commit
    autokeep_launchagent
    autokeep_crypt

    git reset --hard
    git checkout -f -b master FETCH_HEAD
  fi

  chmod -R go= "${HOME}" > /dev/null 2>&1
}

Customize Atom

custom_atom () {
  if which apm > /dev/null; then
    mkdir -p "${HOME}/.atom/.apm"

    cat << EOF > "${HOME}/.atom/.apmrc"
cache = ${CACHES}/apm
EOF

    cat << EOF > "${HOME}/.atom/.apm/.apmrc"
cache = ${CACHES}/apm
EOF

    printf "%s\n" "${_atom}" | \
    while IFS="$(printf '\t')" read pkg; do
      test -d "${HOME}/.atom/packages/${pkg}" ||
      apm install "${pkg}"
    done

    cat << EOF > "${HOME}/.atom/config.cson"
<<config.cson>>
EOF

    cat << EOF > "${HOME}/.atom/packages/tomorrow-night-eighties-syntax/styles/colors.less"
<<colors.less>>
EOF
  fi
}

_atom

Atom Package NameReference URL
atom-beautifyhttps://atom.io/packages/atom-beautify
atom-css-combhttps://atom.io/packages/atom-css-comb
atom-fuzzy-grephttps://atom.io/packages/atom-fuzzy-grep
atom-jadehttps://atom.io/packages/atom-jade
atom-wallabyhttps://atom.io/packages/atom-wallaby
autoclose-htmlhttps://atom.io/packages/autoclose-html
autocomplete-pythonhttps://atom.io/packages/autocomplete-python
busy-signalhttps://atom.io/packages/busy-signal
double-taghttps://atom.io/packages/double-tag
editorconfighttps://atom.io/packages/editorconfig
ex-modehttps://atom.io/packages/ex-mode
file-iconshttps://atom.io/packages/file-icons
git-plushttps://atom.io/packages/git-plus
git-time-machinehttps://atom.io/packages/git-time-machine
highlight-selectedhttps://atom.io/packages/highlight-selected
intentionshttps://atom.io/packages/intentions
language-dockerhttps://atom.io/packages/language-docker
language-jadehttps://atom.io/packages/language-jade
language-javascript-jsxhttps://atom.io/packages/language-javascript-jsx
language-lisphttps://atom.io/packages/language-lisp
language-slimhttps://atom.io/packages/language-slim
linterhttps://atom.io/packages/linter
linter-eslinthttps://atom.io/packages/linter-eslint
linter-rubocophttps://atom.io/packages/linter-rubocop
linter-shellcheckhttps://atom.io/packages/linter-shellcheck
linter-ui-defaulthttps://atom.io/packages/linter-ui-default
MagicPythonhttps://atom.io/packages/MagicPython
python-yapfhttps://atom.io/packages/python-yapf
reacthttps://atom.io/packages/react
riothttps://atom.io/packages/riot
sort-lineshttps://atom.io/packages/sort-lines
term3https://atom.io/packages/term3
tomorrow-night-eighties-syntaxhttps://atom.io/packages/tomorrow-night-eighties-syntax
tree-view-open-fileshttps://atom.io/packages/tree-view-open-files
vim-mode-plushttps://atom.io/packages/vim-mode-plus
vim-mode-zzhttps://atom.io/packages/vim-mode-zz

${HOME}/.atom/config.cson

"*":
  "autocomplete-python":
    useKite: false
  core:
    telemetryConsent: "limited"
    themes: [
      "one-dark-ui"
      "tomorrow-night-eighties-syntax"
    ]
  editor:
    fontFamily: "Inconsolata LGC"
    fontSize: 13
  welcome:
    showOnStartup: false

${HOME}/.atom/packages/tomorrow-night-eighties-syntax/styles/colors.less

@background: #222222;
@current-line: #333333;
@selection: #4c4c4c;
@foreground: #cccccc;
@comment: #999999;
@red: #f27f7f;
@orange: #ff994c;
@yellow: #ffcc66;
@green: #99cc99;
@aqua: #66cccc;
@blue: #6699cc;
@purple: #cc99cc;

Customize autoping

custom_autoping () {
  config_defaults "${_autoping}"
}

_autoping

PreferenceDomainKeyTypeValueHost
Host to Pingcom.memset.autopingHostname-stringgoogle.com
Slow Ping Threshold (ms) 100com.memset.autopingSlowPingLowThreshold-int100
Launch at Login oncom.memset.autopingLaunchAtLogin-booltrue
Display Icon and Textcom.memset.autopingShowIcon-booltrue
com.memset.autopingShowText-booltrue
Packet Loss Text oncom.memset.autopingShowPacketLossText-booltrue
Connection Up/Down Alerts oncom.memset.autopingShowNotifications-booltrue

Customize Dropbox

custom_dropbox () {
  test -d "/Applications/Dropbox.app" && \
    open "/Applications/Dropbox.app"
}

Customize Default UTIs

custom_duti () {
  if test -x "/usr/local/bin/duti"; then
    test -f "${HOME}/Library/Preferences/org.duti.plist" && \
      rm "${HOME}/Library/Preferences/org.duti.plist"

    printf "%s\n" "${_duti}" | \
    while IFS="$(printf '\t')" read id uti role; do
      defaults write org.duti DUTISettings -array-add \
        "{
          DUTIBundleIdentifier = '$a';
          DUTIUniformTypeIdentifier = '$b';
          DUTIRole = '$c';
        }"
    done

    duti "${HOME}/Library/Preferences/org.duti.plist" 2> /dev/null
  fi
}

_duti

Bundle IDUTIRole
com.apple.DiskImageMountercom.apple.disk-imageall
com.apple.DiskImageMounterpublic.disk-imageall
com.apple.DiskImageMounterpublic.iso-imageall
com.apple.QuickTimePlayerXcom.apple.coreaudio-formatall
com.apple.QuickTimePlayerXcom.apple.quicktime-movieall
com.apple.QuickTimePlayerXcom.microsoft.waveform-audioall
com.apple.QuickTimePlayerXpublic.aifc-audioall
com.apple.QuickTimePlayerXpublic.aiff-audioall
com.apple.QuickTimePlayerXpublic.audioall
com.apple.QuickTimePlayerXpublic.mp3all
com.apple.Safaricom.compuserve.gifall
com.apple.Terminalcom.apple.terminal.shell-scriptall
com.apple.iTunescom.apple.iTunes.audibleall
com.apple.iTunescom.apple.iTunes.ipgall
com.apple.iTunescom.apple.iTunes.ipswall
com.apple.iTunescom.apple.iTunes.iteall
com.apple.iTunescom.apple.iTunes.itlpall
com.apple.iTunescom.apple.iTunes.itmsall
com.apple.iTunescom.apple.iTunes.podcastall
com.apple.iTunescom.apple.m4a-audioall
com.apple.iTunescom.apple.mpeg-4-ringtoneall
com.apple.iTunescom.apple.protected-mpeg-4-audioall
com.apple.iTunescom.apple.protected-mpeg-4-videoall
com.apple.iTunescom.audible.aa-audioall
com.apple.iTunespublic.mpeg-4-audioall
com.apple.installercom.apple.installer-package-archiveall
com.github.atomcom.apple.binary-property-listeditor
com.github.atomcom.apple.crashreporteditor
com.github.atomcom.apple.dt.document.ascii-property-listeditor
com.github.atomcom.apple.dt.document.script-suite-property-listeditor
com.github.atomcom.apple.dt.document.script-terminology-property-listeditor
com.github.atomcom.apple.logeditor
com.github.atomcom.apple.property-listeditor
com.github.atomcom.apple.rez-sourceeditor
com.github.atomcom.apple.symbol-exporteditor
com.github.atomcom.apple.xcode.ada-sourceeditor
com.github.atomcom.apple.xcode.bash-scripteditor
com.github.atomcom.apple.xcode.configsettingseditor
com.github.atomcom.apple.xcode.csh-scripteditor
com.github.atomcom.apple.xcode.fortran-sourceeditor
com.github.atomcom.apple.xcode.ksh-scripteditor
com.github.atomcom.apple.xcode.lex-sourceeditor
com.github.atomcom.apple.xcode.make-scripteditor
com.github.atomcom.apple.xcode.mig-sourceeditor
com.github.atomcom.apple.xcode.pascal-sourceeditor
com.github.atomcom.apple.xcode.strings-texteditor
com.github.atomcom.apple.xcode.tcsh-scripteditor
com.github.atomcom.apple.xcode.yacc-sourceeditor
com.github.atomcom.apple.xcode.zsh-scripteditor
com.github.atomcom.apple.xml-property-listeditor
com.github.atomcom.barebones.bbedit.actionscript-sourceeditor
com.github.atomcom.barebones.bbedit.erb-sourceeditor
com.github.atomcom.barebones.bbedit.ini-configurationeditor
com.github.atomcom.barebones.bbedit.javascript-sourceeditor
com.github.atomcom.barebones.bbedit.json-sourceeditor
com.github.atomcom.barebones.bbedit.jsp-sourceeditor
com.github.atomcom.barebones.bbedit.lasso-sourceeditor
com.github.atomcom.barebones.bbedit.lua-sourceeditor
com.github.atomcom.barebones.bbedit.setext-sourceeditor
com.github.atomcom.barebones.bbedit.sql-sourceeditor
com.github.atomcom.barebones.bbedit.tcl-sourceeditor
com.github.atomcom.barebones.bbedit.tex-sourceeditor
com.github.atomcom.barebones.bbedit.textile-sourceeditor
com.github.atomcom.barebones.bbedit.vbscript-sourceeditor
com.github.atomcom.barebones.bbedit.vectorscript-sourceeditor
com.github.atomcom.barebones.bbedit.verilog-hdl-sourceeditor
com.github.atomcom.barebones.bbedit.vhdl-sourceeditor
com.github.atomcom.barebones.bbedit.yaml-sourceeditor
com.github.atomcom.netscape.javascript-sourceeditor
com.github.atomcom.sun.java-sourceeditor
com.github.atomdyn.ah62d4rv4ge80255drqall
com.github.atomdyn.ah62d4rv4ge80g55gq3w0nall
com.github.atomdyn.ah62d4rv4ge80g55sq2all
com.github.atomdyn.ah62d4rv4ge80y2xzrf0gk3pwall
com.github.atomdyn.ah62d4rv4ge81e3dtqqall
com.github.atomdyn.ah62d4rv4ge81e7kall
com.github.atomdyn.ah62d4rv4ge81g25xsqall
com.github.atomdyn.ah62d4rv4ge81g2pxsqall
com.github.atomnet.daringfireball.markdowneditor
com.github.atompublic.assembly-sourceeditor
com.github.atompublic.c-headereditor
com.github.atompublic.c-plus-plus-sourceeditor
com.github.atompublic.c-sourceeditor
com.github.atompublic.csh-scripteditor
com.github.atompublic.jsoneditor
com.github.atompublic.lex-sourceeditor
com.github.atompublic.logeditor
com.github.atompublic.mig-sourceeditor
com.github.atompublic.nasm-assembly-sourceeditor
com.github.atompublic.objective-c-plus-plus-sourceeditor
com.github.atompublic.objective-c-sourceeditor
com.github.atompublic.patch-fileeditor
com.github.atompublic.perl-scripteditor
com.github.atompublic.php-scripteditor
com.github.atompublic.plain-texteditor
com.github.atompublic.precompiled-c-headereditor
com.github.atompublic.precompiled-c-plus-plus-headereditor
com.github.atompublic.python-scripteditor
com.github.atompublic.ruby-scripteditor
com.github.atompublic.scripteditor
com.github.atompublic.shell-scripteditor
com.github.atompublic.source-codeeditor
com.github.atompublic.texteditor
com.github.atompublic.utf16-external-plain-texteditor
com.github.atompublic.utf16-plain-texteditor
com.github.atompublic.utf8-plain-texteditor
com.github.atompublic.xmleditor
com.kodlian.Icon-Slatecom.apple.icnsall
com.kodlian.Icon-Slatecom.microsoft.icoall
com.microsoft.Wordpublic.rtfall
com.panayotis.jublerdyn.ah62d4rv4ge81g6xyall
com.sketchup.SketchUp.2017com.sketchup.skpall
com.VortexApps.NZBVortex3dyn.ah62d4rv4ge8068xcall
com.vmware.fusioncom.microsoft.windows-executableall
cx.c3.theunarchivercom.alcohol-soft.mdf-imageall
cx.c3.theunarchivercom.allume.stuffit-archiveall
cx.c3.theunarchivercom.altools.alz-archiveall
cx.c3.theunarchivercom.amiga.adf-archiveall
cx.c3.theunarchivercom.amiga.adz-archiveall
cx.c3.theunarchivercom.apple.applesingle-archiveall
cx.c3.theunarchivercom.apple.binhex-archiveall
cx.c3.theunarchivercom.apple.bom-compressed-cpioall
cx.c3.theunarchivercom.apple.itunes.ipaall
cx.c3.theunarchivercom.apple.macbinary-archiveall
cx.c3.theunarchivercom.apple.self-extracting-archiveall
cx.c3.theunarchivercom.apple.xar-archiveall
cx.c3.theunarchivercom.apple.xip-archiveall
cx.c3.theunarchivercom.cyclos.cpt-archiveall
cx.c3.theunarchivercom.microsoft.cab-archiveall
cx.c3.theunarchivercom.microsoft.msi-installerall
cx.c3.theunarchivercom.nero.nrg-imageall
cx.c3.theunarchivercom.network172.pit-archiveall
cx.c3.theunarchivercom.nowsoftware.now-archiveall
cx.c3.theunarchivercom.nscripter.nsa-archiveall
cx.c3.theunarchivercom.padus.cdi-imageall
cx.c3.theunarchivercom.pkware.zip-archiveall
cx.c3.theunarchivercom.rarlab.rar-archiveall
cx.c3.theunarchivercom.redhat.rpm-archiveall
cx.c3.theunarchivercom.stuffit.archive.sitall
cx.c3.theunarchivercom.stuffit.archive.sitxall
cx.c3.theunarchivercom.sun.java-archiveall
cx.c3.theunarchivercom.symantec.dd-archiveall
cx.c3.theunarchivercom.winace.ace-archiveall
cx.c3.theunarchivercom.winzip.zipx-archiveall
cx.c3.theunarchivercx.c3.arc-archiveall
cx.c3.theunarchivercx.c3.arj-archiveall
cx.c3.theunarchivercx.c3.dcs-archiveall
cx.c3.theunarchivercx.c3.dms-archiveall
cx.c3.theunarchivercx.c3.ha-archiveall
cx.c3.theunarchivercx.c3.lbr-archiveall
cx.c3.theunarchivercx.c3.lha-archiveall
cx.c3.theunarchivercx.c3.lhf-archiveall
cx.c3.theunarchivercx.c3.lzx-archiveall
cx.c3.theunarchivercx.c3.packdev-archiveall
cx.c3.theunarchivercx.c3.pax-archiveall
cx.c3.theunarchivercx.c3.pma-archiveall
cx.c3.theunarchivercx.c3.pp-archiveall
cx.c3.theunarchivercx.c3.xmash-archiveall
cx.c3.theunarchivercx.c3.zoo-archiveall
cx.c3.theunarchivercx.c3.zoom-archiveall
cx.c3.theunarchiverorg.7-zip.7-zip-archiveall
cx.c3.theunarchiverorg.archive.warc-archiveall
cx.c3.theunarchiverorg.debian.deb-archiveall
cx.c3.theunarchiverorg.gnu.gnu-tar-archiveall
cx.c3.theunarchiverorg.gnu.gnu-zip-archiveall
cx.c3.theunarchiverorg.gnu.gnu-zip-tar-archiveall
cx.c3.theunarchiverorg.tukaani.lzma-archiveall
cx.c3.theunarchiverorg.tukaani.xz-archiveall
cx.c3.theunarchiverpublic.bzip2-archiveall
cx.c3.theunarchiverpublic.cpio-archiveall
cx.c3.theunarchiverpublic.tar-archiveall
cx.c3.theunarchiverpublic.tar-bzip2-archiveall
cx.c3.theunarchiverpublic.z-archiveall
cx.c3.theunarchiverpublic.zip-archiveall
cx.c3.theunarchiverpublic.zip-archive.first-partall
org.gnu.Emacsdyn.ah62d4rv4ge8086xhall
org.inkscape.Inkscapepublic.svg-imageeditor
org.videolan.vlccom.apple.m4v-videoall
org.videolan.vlccom.microsoft.windows-media-wmvall
org.videolan.vlcorg.videolan.3gpall
org.videolan.vlcorg.videolan.aacall
org.videolan.vlcorg.videolan.ac3all
org.videolan.vlcorg.videolan.aiffall
org.videolan.vlcorg.videolan.amrall
org.videolan.vlcorg.videolan.aoball
org.videolan.vlcorg.videolan.apeall
org.videolan.vlcorg.videolan.asfall
org.videolan.vlcorg.videolan.aviall
org.videolan.vlcorg.videolan.axaall
org.videolan.vlcorg.videolan.axvall
org.videolan.vlcorg.videolan.divxall
org.videolan.vlcorg.videolan.dtsall
org.videolan.vlcorg.videolan.dvall
org.videolan.vlcorg.videolan.flacall
org.videolan.vlcorg.videolan.flashall
org.videolan.vlcorg.videolan.gxfall
org.videolan.vlcorg.videolan.itall
org.videolan.vlcorg.videolan.midall
org.videolan.vlcorg.videolan.mkaall
org.videolan.vlcorg.videolan.mkvall
org.videolan.vlcorg.videolan.mlpall
org.videolan.vlcorg.videolan.modall
org.videolan.vlcorg.videolan.mpcall
org.videolan.vlcorg.videolan.mpeg-audioall
org.videolan.vlcorg.videolan.mpeg-streamall
org.videolan.vlcorg.videolan.mpeg-videoall
org.videolan.vlcorg.videolan.mxfall
org.videolan.vlcorg.videolan.nsvall
org.videolan.vlcorg.videolan.nuvall
org.videolan.vlcorg.videolan.ogg-audioall
org.videolan.vlcorg.videolan.ogg-videoall
org.videolan.vlcorg.videolan.omaall
org.videolan.vlcorg.videolan.opusall
org.videolan.vlcorg.videolan.quicktimeall
org.videolan.vlcorg.videolan.realmediaall
org.videolan.vlcorg.videolan.recall
org.videolan.vlcorg.videolan.rmiall
org.videolan.vlcorg.videolan.s3mall
org.videolan.vlcorg.videolan.spxall
org.videolan.vlcorg.videolan.todall
org.videolan.vlcorg.videolan.ttaall
org.videolan.vlcorg.videolan.voball
org.videolan.vlcorg.videolan.vocall
org.videolan.vlcorg.videolan.vqfall
org.videolan.vlcorg.videolan.vroall
org.videolan.vlcorg.videolan.wavall
org.videolan.vlcorg.videolan.webmall
org.videolan.vlcorg.videolan.wmaall
org.videolan.vlcorg.videolan.wmvall
org.videolan.vlcorg.videolan.wtvall
org.videolan.vlcorg.videolan.wvall
org.videolan.vlcorg.videolan.xaall
org.videolan.vlcorg.videolan.xescall
org.videolan.vlcorg.videolan.xmall
org.videolan.vlcpublic.ac3-audioall
org.videolan.vlcpublic.audiovisual-contentall
org.videolan.vlcpublic.aviall
org.videolan.vlcpublic.movieall
org.videolan.vlcpublic.mpegall
org.videolan.vlcpublic.mpeg-2-videoall
org.videolan.vlcpublic.mpeg-4all

Customize Emacs

custom_emacs () {
  mkdir -p "${HOME}/.emacs.d" && \
  curl --compressed --location --silent \
    "https://github.com/syl20bnr/spacemacs/archive/master.tar.gz" | \
  tar -C "${HOME}/.emacs.d" --strip-components 1 -xf -
  mkdir -p "${HOME}/.emacs.d/private/ptb"
  chmod -R go= "${HOME}/.emacs.d"

  cat << EOF > "${HOME}/.spacemacs"
<<.spacemacs>>
EOF

  cat << EOF > "${HOME}/.emacs.d/private/ptb/config.el"
<<config.el>>
EOF

  cat << EOF > "${HOME}/.emacs.d/private/ptb/funcs.el"
<<funcs.el>>
EOF

  cat << EOF > "${HOME}/.emacs.d/private/ptb/keybindings.el"
<<keybindings.el>>
EOF

  cat << EOF > "${HOME}/.emacs.d/private/ptb/packages.el"
<<packages.el>>
EOF
}

~/.spacemacs

(defun dotspacemacs/layers ()
  (setq-default
    dotspacemacs-configuration-layers '(
      auto-completion
      (colors :variables
        colors-colorize-identifiers 'variables)
      dash
      deft
      docker
      emacs-lisp
      evil-cleverparens
      git
      github
      helm
      html
      ibuffer
      imenu-list
      javascript
      markdown
      nginx
      (org :variables
        org-enable-github-support t)
      (osx :variables
        osx-use-option-as-meta nil)
      ptb
      react
      ruby
      ruby-on-rails
      search-engine
      semantic
      shell-scripts
      (spell-checking :variables
        spell-checking-enable-by-default nil)
      syntax-checking
      (version-control :variables
        version-control-diff-side 'left)
      vim-empty-lines
    )
    dotspacemacs-excluded-packages '(org-bullets)
  )
)

(defun dotspacemacs/init ()
  (setq-default
    dotspacemacs-startup-banner nil
    dotspacemacs-startup-lists nil
    dotspacemacs-scratch-mode 'org-mode
    dotspacemacs-themes '(sanityinc-tomorrow-eighties)
    dotspacemacs-default-font '(
      "Inconsolata LGC"
      :size 13
      :weight normal
      :width normal
      :powerline-scale 1.1)
    dotspacemacs-loading-progress-bar nil
    dotspacemacs-active-transparency 100
    dotspacemacs-inactive-transparency 100
    dotspacemacs-line-numbers t
    dotspacemacs-whitespace-cleanup 'all
  )
)

(defun dotspacemacs/user-init ())
(defun dotspacemacs/user-config ())

~/.emacs.d/private/ptb/config.el

(setq
  default-frame-alist '(
    (top . 22)
    (left . 1201)
    (height . 50)
    (width . 120)
    (vertical-scroll-bars . right))
  initial-frame-alist (copy-alist default-frame-alist)

  deft-directory "~/Dropbox/Notes"
  focus-follows-mouse t
  mouse-wheel-follow-mouse t
  mouse-wheel-scroll-amount '(1 ((shift) . 1))
  org-src-preserve-indentation t
  purpose-display-at-right 20
  recentf-max-saved-items 5
  scroll-step 1
  system-uses-terminfo nil

  ibuffer-formats '(
    (mark modified read-only " "
    (name 18 18 :left :elide)))

  ibuffer-shrink-to-minimum-size t
  ibuffer-always-show-last-buffer nil
  ibuffer-sorting-mode 'recency
  ibuffer-use-header-line nil
  x-select-enable-clipboard nil)

(global-linum-mode t)
(recentf-mode t)
(x-focus-frame nil)
(with-eval-after-load 'org
  (org-babel-do-load-languages
    'org-babel-load-languages '(
      (ruby . t)
      (shell . t)
    )
  )
)

~/.emacs.d/private/ptb/funcs.el

(defun is-useless-buffer (buffer)
  (let ((name (buffer-name buffer)))
    (and (= ?* (aref name 0))
        (string-match "^\\**" name))))

(defun kill-useless-buffers ()
  (interactive)
  (loop for buffer being the buffers
        do (and (is-useless-buffer buffer) (kill-buffer buffer))))

(defun org-babel-tangle-hook ()
  (add-hook 'after-save-hook 'org-babel-tangle))

(add-hook 'org-mode-hook #'org-babel-tangle-hook)

(defun ptb/new-untitled-buffer ()
  "Create a new untitled buffer in the current frame."
  (interactive)
  (let
    ((buffer "Untitled-") (count 1))
    (while
      (get-buffer (concat buffer (number-to-string count)))
      (setq count (1+ count)))
    (switch-to-buffer
    (concat buffer (number-to-string count))))
  (org-mode))

(defun ptb/previous-buffer ()
  (interactive)
  (kill-useless-buffers)
  (previous-buffer))

(defun ptb/next-buffer ()
  (interactive)
  (kill-useless-buffers)
  (next-buffer))

(defun ptb/kill-current-buffer ()
  (interactive)
  (kill-buffer (current-buffer))
  (kill-useless-buffers))

~/.emacs.d/private/ptb/keybindings.el

(define-key evil-insert-state-map (kbd "<return>") 'newline)

(define-key evil-normal-state-map (kbd "s-c") 'clipboard-kill-ring-save)
(define-key evil-insert-state-map (kbd "s-c") 'clipboard-kill-ring-save)
(define-key evil-visual-state-map (kbd "s-c") 'clipboard-kill-ring-save)

(define-key evil-ex-completion-map (kbd "s-v") 'clipboard-yank)
(define-key evil-ex-search-keymap (kbd "s-v") 'clipboard-yank)
(define-key evil-insert-state-map (kbd "s-v") 'clipboard-yank)

(define-key evil-normal-state-map (kbd "s-x") 'clipboard-kill-region)
(define-key evil-insert-state-map (kbd "s-x") 'clipboard-kill-region)
(define-key evil-visual-state-map (kbd "s-x") 'clipboard-kill-region)

(define-key evil-normal-state-map (kbd "<S-up>") 'evil-previous-visual-line)
(define-key evil-insert-state-map (kbd "<S-up>") 'evil-previous-visual-line)
(define-key evil-visual-state-map (kbd "<S-up>") 'evil-previous-visual-line)

(define-key evil-normal-state-map (kbd "<S-down>") 'evil-next-visual-line)
(define-key evil-insert-state-map (kbd "<S-down>") 'evil-next-visual-line)
(define-key evil-visual-state-map (kbd "<S-down>") 'evil-next-visual-line)

(global-set-key (kbd "C-l") 'evil-search-highlight-persist-remove-all)

(global-set-key (kbd "s-t") 'make-frame)
(global-set-key (kbd "s-n") 'ptb/new-untitled-buffer)
(global-set-key (kbd "s-w") 'ptb/kill-current-buffer)
(global-set-key (kbd "s-{") 'ptb/previous-buffer)
(global-set-key (kbd "s-}") 'ptb/next-buffer)

~/.emacs.d/private/ptb/packages.el

(setq ptb-packages '(adaptive-wrap auto-indent-mode))

(defun ptb/init-adaptive-wrap ()
  "Load the adaptive wrap package"
  (use-package adaptive-wrap
    :init
    (setq adaptive-wrap-extra-indent 2)
    :config
    (progn
      ;; http://stackoverflow.com/questions/13559061
      (when (fboundp 'adaptive-wrap-prefix-mode)
        (defun ptb/activate-adaptive-wrap-prefix-mode ()
          "Toggle 'visual-line-mode' and 'adaptive-wrap-prefix-mode' simultaneously."
          (adaptive-wrap-prefix-mode (if visual-line-mode 1 -1)))
        (add-hook 'visual-line-mode-hook 'ptb/activate-adaptive-wrap-prefix-mode)))))

(defun ptb/init-auto-indent-mode ()
  (use-package auto-indent-mode
    :init
    (setq
      auto-indent-delete-backward-char t
      auto-indent-fix-org-auto-fill t
      auto-indent-fix-org-move-beginning-of-line t
      auto-indent-fix-org-return t
      auto-indent-fix-org-yank t
      auto-indent-start-org-indent t
    )
  )
)

Customize Finder

custom_finder () {
  config_defaults "${_finder}"
  defaults write "com.apple.finder" "NSToolbar Configuration Browser" \
    '{
      "TB Display Mode" = 2;
      "TB Item Identifiers" = (
        "com.apple.finder.BACK",
        "com.apple.finder.PATH",
        "com.apple.finder.SWCH",
        "com.apple.finder.ARNG",
        "NSToolbarFlexibleSpaceItem",
        "com.apple.finder.SRCH",
        "com.apple.finder.ACTN"
      );
    }'
}

_finder

PreferenceDomainKeyTypeValueHost
Show these items on the desktop: off Hard diskscom.apple.finderShowHardDrivesOnDesktop-boolfalse
Show these items on the desktop: off External diskscom.apple.finderShowExternalHardDrivesOnDesktop-boolfalse
Show these items on the desktop: on CDs, DVDs, and iPodscom.apple.finderShowRemovableMediaOnDesktop-booltrue
Show these items on the desktop: on Connected servers:com.apple.finderShowMountedServersOnDesktop-booltrue
New Finder windows show: ${HOME}com.apple.finderNewWindowTarget-stringPfLo
com.apple.finderNewWindowTargetPath-stringfile://${HOME}/Dropbox/
on Show all filename extensions-globalDomainAppleShowAllExtensions-booltrue
off Show warning before changing an extensioncom.apple.finderFXEnableExtensionChangeWarning-boolfalse
on Show warning before removing from iCloud Drivecom.apple.finderFXEnableRemoveFromICloudDriveWarning-booltrue
off Show warning before emptying the Trashcom.apple.finderWarnOnEmptyTrash-boolfalse
ViewShow Path Barcom.apple.finderShowPathbar-booltrue
ViewShow Status Barcom.apple.finderShowStatusBar-booltrue

Customize getmail

custom_getmail () {
  test -d "${HOME}/.getmail" || \
    mkdir -m go= "${HOME}/.getmail"

  while true; do
    e=$(ask2 "To configure getmail, enter your email address." "Configure Getmail" "No More Addresses" "Create Configuration" "$(whoami)@$(hostname -f | cut -d. -f2-)" "false")
    test -n "$e" || break

    security find-internet-password -a "$e" -D "getmail password" > /dev/null || \
    p=$(ask2 "Enter your password for $e." "Configure Getmail" "Cancel" "Set Password" "" "true") && \
    security add-internet-password -a "$e" -s "imap.gmail.com" -r "imap" \
      -l "$e" -D "getmail password" -P 993 -w "$p"

    if which crudini > /dev/null; then
      gm="${HOME}/.getmail/${e}"
      printf "%s\n" "${_getmail_ini}" | \
      while IFS="$(printf '\t')" read section key value; do
        crudini --set "$gm" "$section" "$key" "$value"
      done
      crudini --set "$gm" "destination" "arguments" "('-c','/usr/local/etc/dovecot/dovecot.conf','-d','$(whoami)')"
      crudini --set "$gm" "destination" "path" "$(find '/usr/local/Cellar/dovecot' -name 'dovecot-lda' -print -quit)"
      crudini --set "$gm" "retriever" "username" "$e"
    fi

    la="${HOME}/Library/LaunchAgents/ca.pyropus.getmail.${e}"

    test -d "$(dirname $la)" || \
      mkdir -p "$(dirname $la)"
    launchctl unload "${la}.plist" 2> /dev/null
    rm -f "${la}.plist"

    config_plist "$_getmail_plist" "${la}.plist"
    config_defaults "$(printf "${la}\tLabel\t-string\tca.pyropus.getmail.${e}\t")"
    config_defaults "$(printf "${la}\tProgramArguments\t-array-add\t${e}\t")"
    config_defaults "$(printf "${la}\tWorkingDirectory\t-string\t${HOME}/.getmail\t")"

    plutil -convert xml1 "${la}.plist"
    launchctl load "${la}.plist" 2> /dev/null
  done
}

_getmail_ini

SectionKeyValue
destinationignore_stderrtrue
destinationtypeMDA_external
optionsdeletetrue
optionsdelivered_tofalse
optionsread_allfalse
optionsreceivedfalse
optionsverbose0
retrievermailboxes(“[Gmail]/All Mail”,)
retrievermove_on_delete“[Gmail]/Trash”
retrieverport993
retrieverserverimap.gmail.com
retrievertypeSimpleIMAPSSLRetriever

_getmail_plist

CommandEntryTypeValue
add:KeepAlivebooltrue
add:ProcessTypestringBackground
add:ProgramArgumentsarray
add:ProgramArguments:0string/usr/local/bin/getmail
add:ProgramArguments:1string–idle
add:ProgramArguments:2string[Gmail]/All Mail
add:ProgramArguments:3string–rcfile
add:RunAtLoadbooltrue
add:StandardOutPathstringgetmail.log
add:StandardErrorPathstringgetmail.err

Customize Git

custom_git () {
  if ! test -e "${HOME}/.gitconfig"; then
    true
  fi
}

Customize GnuPG

custom_gnupg () {
  if ! test -d "${HOME}/.gnupg"; then
    true
  fi
}

Customize iStat Menus

custom_istatmenus () {
  defaults delete com.bjango.istatmenus5.extras Time_MenubarFormat > /dev/null 2>&1
  defaults delete com.bjango.istatmenus5.extras Time_DropdownFormat > /dev/null 2>&1
  defaults delete com.bjango.istatmenus5.extras Time_Cities > /dev/null 2>&1
  config_defaults "${_istatmenus}"
}

_istatmenus

PreferenceDomainKeyTypeValueHost
com.bjango.istatmenus5.extrasMenubarSkinColor-int8
com.bjango.istatmenus5.extrasMenubarTheme-int0
com.bjango.istatmenus5.extrasDropdownTheme-int1
com.bjango.istatmenus5.extrasCPU_MenubarMode-string100,2,0
com.bjango.istatmenus5.extrasCPU_MenubarTextSize-int14
com.bjango.istatmenus5.extrasCPU_MenubarGraphShowBackground-int0
com.bjango.istatmenus5.extrasCPU_MenubarGraphWidth-int32
com.bjango.istatmenus5.extrasCPU_MenubarGraphBreakdowns-int0
com.bjango.istatmenus5.extrasCPU_MenubarGraphCustomColors-int0
com.bjango.istatmenus5.extrasCPU_MenubarGraphOverall-string0.40 0.60 0.40 1.00
com.bjango.istatmenus5.extrasCPU_MenubarCombineCores-int1
com.bjango.istatmenus5.extrasCPU_MenubarGroupItems-int0
com.bjango.istatmenus5.extrasCPU_MenubarSingleHistoryGraph-int0
com.bjango.istatmenus5.extrasCPU_CombineLogicalCores-int1
com.bjango.istatmenus5.extrasCPU_AppFormat-int0
com.bjango.istatmenus5.extrasMemory_MenubarMode-string100,2,6
com.bjango.istatmenus5.extrasMemory_MenubarPercentageSize-int14
com.bjango.istatmenus5.extrasMemory_MenubarGraphBreakdowns-int1
com.bjango.istatmenus5.extrasMemory_MenubarGraphCustomColors-int0
com.bjango.istatmenus5.extrasMemory_MenubarGraphOverall-string0.40 0.60 0.40 1.00
com.bjango.istatmenus5.extrasMemory_MenubarGraphWired-string0.40 0.60 0.40 1.00
com.bjango.istatmenus5.extrasMemory_MenubarGraphActive-string0.47 0.67 0.47 1.00
com.bjango.istatmenus5.extrasMemory_MenubarGraphCompressed-string0.53 0.73 0.53 1.00
com.bjango.istatmenus5.extrasMemory_MenubarGraphInactive-string0.60 0.80 0.60 1.00
com.bjango.istatmenus5.extrasMemory_IgnoreInactive-int0
com.bjango.istatmenus5.extrasMemory_AppFormat-int0
com.bjango.istatmenus5.extrasMemory_DisplayFormat-int1
com.bjango.istatmenus5.extrasDisks_MenubarMode-string100,9,8
com.bjango.istatmenus5.extrasDisks_MenubarGroupItems-int1
com.bjango.istatmenus5.extrasDisks_MenubarRWShowLabel-int1
com.bjango.istatmenus5.extrasDisks_MenubarRWBold-int0
com.bjango.istatmenus5.extrasDisks_MenubarGraphActivityWidth-int32
com.bjango.istatmenus5.extrasDisks_MenubarGraphActivityShowBackground-int0
com.bjango.istatmenus5.extrasDisks_MenubarGraphActivityCustomColors-int0
com.bjango.istatmenus5.extrasDisks_MenubarGraphActivityRead-string0.60 0.80 0.60 1.00
com.bjango.istatmenus5.extrasDisks_MenubarGraphActivityWrite-string0.40 0.60 0.40 1.00
com.bjango.istatmenus5.extrasDisks_SeperateFusion-int1
com.bjango.istatmenus5.extrasNetwork_MenubarMode-string4,0,1
com.bjango.istatmenus5.extrasNetwork_TextUploadColor-Dark-string1.00 1.00 1.00 1.00
com.bjango.istatmenus5.extrasNetwork_TextDownloadColor-Dark-string1.00 1.00 1.00 1.00
com.bjango.istatmenus5.extrasNetwork_GraphWidth-int32
com.bjango.istatmenus5.extrasNetwork_GraphShowBackground-int0
com.bjango.istatmenus5.extrasNetwork_GraphCustomColors-int0
com.bjango.istatmenus5.extrasNetwork_GraphUpload-string0.60 0.80 0.60 1.00
com.bjango.istatmenus5.extrasNetwork_GraphDownload-string0.40 0.60 0.40 1.00
com.bjango.istatmenus5.extrasNetwork_GraphMode-int1
com.bjango.istatmenus5.extrasBattery_MenubarMode-string5,0
com.bjango.istatmenus5.extrasBattery_ColorGraphCustomColors-int1
com.bjango.istatmenus5.extrasBattery_ColorGraphCharged-string0.40 0.60 0.40 1.00
com.bjango.istatmenus5.extrasBattery_ColorGraphCharging-string0.60 0.80 0.60 1.00
com.bjango.istatmenus5.extrasBattery_ColorGraphDraining-string1.00 0.60 0.60 1.00
com.bjango.istatmenus5.extrasBattery_ColorGraphLow-string1.00 0.20 0.20 1.00
com.bjango.istatmenus5.extrasBattery_PercentageSize-int14
com.bjango.istatmenus5.extrasBattery_MenubarCustomizeStates-int0
com.bjango.istatmenus5.extrasBattery_MenubarHideBluetooth-int1
com.bjango.istatmenus5.extrasTime_MenubarFormat-array-addEE
com.bjango.istatmenus5.extrasTime_MenubarFormat-array-add\040
com.bjango.istatmenus5.extrasTime_MenubarFormat-array-addMMM
com.bjango.istatmenus5.extrasTime_MenubarFormat-array-add\040
com.bjango.istatmenus5.extrasTime_MenubarFormat-array-addd
com.bjango.istatmenus5.extrasTime_MenubarFormat-array-add\040
com.bjango.istatmenus5.extrasTime_MenubarFormat-array-addh
com.bjango.istatmenus5.extrasTime_MenubarFormat-array-add:
com.bjango.istatmenus5.extrasTime_MenubarFormat-array-addmm
com.bjango.istatmenus5.extrasTime_MenubarFormat-array-add:
com.bjango.istatmenus5.extrasTime_MenubarFormat-array-addss
com.bjango.istatmenus5.extrasTime_MenubarFormat-array-add\040
com.bjango.istatmenus5.extrasTime_MenubarFormat-array-adda
com.bjango.istatmenus5.extrasTime_DropdownFormat-array-addEE
com.bjango.istatmenus5.extrasTime_DropdownFormat-array-add\040
com.bjango.istatmenus5.extrasTime_DropdownFormat-array-addh
com.bjango.istatmenus5.extrasTime_DropdownFormat-array-add:
com.bjango.istatmenus5.extrasTime_DropdownFormat-array-addmm
com.bjango.istatmenus5.extrasTime_DropdownFormat-array-add\040
com.bjango.istatmenus5.extrasTime_DropdownFormat-array-adda
com.bjango.istatmenus5.extrasTime_DropdownFormat-array-add\040\050
com.bjango.istatmenus5.extrasTime_DropdownFormat-array-addzzz
com.bjango.istatmenus5.extrasTime_DropdownFormat-array-add\051
com.bjango.istatmenus5.extrasTime_Cities-array-add4930956
com.bjango.istatmenus5.extrasTime_Cities-array-add4887398
com.bjango.istatmenus5.extrasTime_Cities-array-add5419384
com.bjango.istatmenus5.extrasTime_Cities-array-add5392171
com.bjango.istatmenus5.extrasTime_Cities-array-add5879400
com.bjango.istatmenus5.extrasTime_Cities-array-add5856195
com.bjango.istatmenus5.extrasTime_TextSize-int14

Customize Meteorologist

custom_meteorologist () {
  config_defaults "${_meteorologist}"
}

_meteorologist

PreferenceDomainKeyTypeValueHost
com.heat.meteorologistcontrolsInSubmenu-string0
com.heat.meteorologistcurrentWeatherInSubmenu-string0
com.heat.meteorologistdisplayCityName-string0
com.heat.meteorologistdisplayHumidity-string0
com.heat.meteorologistdisplayWeatherIcon-string1
com.heat.meteorologistextendedForecastIcons-string1
com.heat.meteorologistextendedForecastInSubmenu-string0
com.heat.meteorologistextendedForecastSingleLine-string1
com.heat.meteorologistforecastDays-int8
com.heat.meteorologistviewExtendedForecast-string1
com.heat.meteorologistweatherSource_1-int3

Customize Moom

custom_moom () {
  killall Moom > /dev/null 2>&1
  defaults delete com.manytricks.Moom "Custom Controls" > /dev/null 2>&1
  config_defaults "${_moom}"
  test -d "/Applications/Moom.app" && \
    open "/Applications/Moom.app"
}

_moom

PreferenceDomainKeyTypeValueHost
on Treat drawers as part of their parent windowscom.manytricks.MoomAllow For Drawers-booltrue
on Separate windows by 2 ptcom.manytricks.MoomGrid Spacing-booltrue
com.manytricks.MoomGrid Spacing: Gap-int2
off Apply to screen edgescom.manytricks.MoomGrid Spacing: Apply To Edges-boolfalse
Grid/keyboard control highlight: 25%com.manytricks.MoomTarget Window Highlight-float0.25
off Show preferences on launchcom.manytricks.MoomStealth Mode-booltrue
Run as faceless applicationcom.manytricks.MoomApplication Mode-int2
on Pop up controls when hovering over a Zoom buttoncom.manytricks.MoomMouse Controls-booltrue
Delay: 0.1 secondcom.manytricks.MoomMouse Controls Delay-float0.1
on Enable hexagon grid with 16 × 9 cellscom.manytricks.MoomMouse Controls Grid-booltrue
com.manytricks.MoomMouse Controls Grid: Mode-int3
com.manytricks.MoomMouse Controls Grid: Columns-int16
com.manytricks.MoomMouse Controls Grid: Rows-int9
on Enable access to custom controlscom.manytricks.MoomMouse Controls Include Custom Controls-booltrue
off Show on hovercom.manytricks.MoomMouse Controls Include Custom Controls: Show On Hover-boolfalse
on Bring moomed windows to the front automaticallycom.manytricks.MoomMouse Controls Auto-Activate Window-booltrue
off Move & Zoom when dragging a window to a display edge or cornercom.manytricks.MoomSnap-boolfalse
Move & Zoomcom.manytricks.MoomCustom Controls-array-add{ Action = 19; “Relative Frame” = “{{0, 0.5}, {0.375, 0.5}}”; }
com.manytricks.MoomCustom Controls-array-add{ Action = 19; “Relative Frame” = “{{0, 0}, {0.375, 0.5}}”; }
com.manytricks.MoomCustom Controls-array-add{ Action = 19; “Relative Frame” = “{{0, 0}, {0.375, 1}}”; }
com.manytricks.MoomCustom Controls-array-add{ Action = 19; “Relative Frame” = “{{0.125, 0}, {0.25, 0.33333}}”; }
com.manytricks.MoomCustom Controls-array-add{ Action = 19; “Relative Frame” = “{{0.375, 0.33333}, {0.3125, 0.66666}}”; }
com.manytricks.MoomCustom Controls-array-add{ Action = 19; “Relative Frame” = “{{0.375, 0}, {0.3125, 0.33333}}”; }
com.manytricks.MoomCustom Controls-array-add{ Action = 19; “Relative Frame” = “{{0.6875, 0.66666}, {0.3125, 0.66666}}”; }
com.manytricks.MoomCustom Controls-array-add{ Action = 19; “Relative Frame” = “{{0.6875, 0.33333}, {0.3125, 0.33333}}”; }
com.manytricks.MoomCustom Controls-array-add{ Action = 19; “Relative Frame” = “{{0.6875, 0}, {0.3125, 0.33333}}”; }
com.manytricks.MoomCustom Controls-array-add{ Action = 1001; “Apply to Overlapping Windows” = 1; Snapshot = ({ “Application Name” = Safari; “Bundle Identifier” = “com.apple.safari”; “Window Frame” = “{{0, 890}, {1199, 888}}”; “Window Subrole” = AXStandardWindow; }, { “Application Name” = Chrome; “Bundle Identifier” = “com.google.chrome”; “Window Frame” = “{{0, 0}, {1199, 888}}”; “Window Subrole” = AXStandardWindow; }, { “Application Name” = Firefox; “Bundle Identifier” = “org.mozilla.firefox”; “Window Frame” = “{{0, 0}, {1199, 888}}”; “Window Subrole” = AXStandardWindow; }, { “Application Name” = Emacs; “Bundle Identifier” = “org.gnu.emacs”; “Window Frame” = “{{1201, 597}, {991, 1181}}”; “Window Subrole” = AXStandardWindow; }, { “Application Name” = Code; “Bundle Identifier” = “com.microsoft.vscode”; “Window Frame” = “{{1201, 594}, {1999, 1184}}”; “Window Subrole” = AXStandardWindow; }, { “Application Name” = Mail; “Bundle Identifier” = “com.apple.mail”; “Window Frame” = “{{2201, 594}, {999, 1184}}”; “Window Subrole” = AXStandardWindow; }, { “Application Name” = nvALT; “Bundle Identifier” = “net.elasticthreads.nv”; “Window Frame” = “{{2201, 989}, {999, 789}}”; “Window Subrole” = AXStandardWindow; }, { “Application Name” = SimpleNote; “Bundle Identifier” = “bogdanf.osx.metanota.pro”; “Window Frame” = “{{2201, 989}, {999, 789}}”; “Window Subrole” = AXStandardWindow; }, { “Application Name” = Finder; “Bundle Identifier” = “com.apple.finder”; “Window Frame” = “{{2401, 1186}, {799, 592}}”; “Window Subrole” = AXStandardWindow; }, { “Application Name” = Messages; “Bundle Identifier” = “com.apple.ichat”; “Window Frame” = “{{401, 0}, {798, 591}}”; “Window Subrole” = AXStandardWindow; }, { “Application Name” = Slack; “Bundle Identifier” = “com.tinyspeck.slackmacgap”; “Window Frame” = “{{0, 0}, {999, 591}}”; “Window Subrole” = AXStandardWindow; }, { “Application Name” = Terminal; “Bundle Identifier” = “com.apple.terminal”; “Window Frame” = “{{1201, 20}, {993, 572}}”; “Window Subrole” = AXStandardWindow; }, { “Application Name” = iTerm2; “Bundle Identifier” = “com.googlecode.iterm2”; “Window Frame” = “{{1201, 17}, {993, 572}}”; “Window Subrole” = AXStandardWindow; }, { “Application Name” = QuickTime; “Bundle Identifier” = “com.apple.quicktimeplayerx”; “Window Frame” = “{{2201, 0}, {999, 592}}”; “Window Subrole” = AXStandardWindow; }, { “Application Name” = VLC; “Bundle Identifier” = “org.videolan.vlc”; “Window Frame” = “{{2201, 0}, {999, 592}}”; “Window Subrole” = AXStandardWindow; }); “Snapshot Screens” = ( “{{0, 0}, {3200, 1800}}” ); }
Define window sizes using 16 × 9 cellscom.manytricks.MoomConfiguration Grid: Columns-int16
com.manytricks.MoomConfiguration Grid: Rows-int9
on Check for updates automaticallycom.manytricks.MoomSUEnableAutomaticChecks-booltrue

Customize MP4 Automator

custom_mp4_automator () {
  mkdir -p "${HOME}/.config/mp4_automator" && \
  curl --compressed --location --silent \
    "https://github.com/mdhiggins/sickbeard_mp4_automator/archive/master.tar.gz" | \
  tar -C "${HOME}/.config/mp4_automator" --strip-components 1 -xf -
  printf "%s\n" "2.7.13" > "${HOME}/.config/mp4_automator/.python-version"

  if which crudini > /dev/null; then
    printf "%s\n" "${_mp4_automator}" | \
    while IFS="$(printf '\t')" read section key value; do
      crudini --set "${HOME}/.config/mp4_automator/autoProcess.ini" "${section}" "${key}" "${value}"
    done

    open "http://localhost:7878/settings/general"
    RADARRAPI="$(ask 'Radarr API Key?' 'Set API Key' '')"
    crudini --set "${HOME}/.config/mp4_automator/autoProcess.ini" "Radarr" "apikey" "$RADARRAPI"

    open "http://localhost:8989/settings/general"
    SONARRAPI="$(ask 'Sonarr API Key?' 'Set API Key' '')"
    crudini --set "${HOME}/.config/mp4_automator/autoProcess.ini" "Sonarr" "apikey" "$SONARRAPI"
  fi

  find "${HOME}/.config/mp4_automator" -name "*.py" -print0 | \
    xargs -0 -L 1 sed -i "" -e "s:/usr/bin/env python:/usr/local/python/versions/2.7.13/bin/python:"
}

_mp4_automator