mac-setup
文件大小: unknow
源码售价: 5 个金币 积分规则     积分充值
资源说明:
* Mac Setup
:properties:
:header-args: :cache yes :comments org :padline yes :results silent
:header-args:sh: :noweb tangle :shebang "#!/bin/sh" :tangle mac-setup.command
:end:
#+startup: showall nohideblocks hidestars indent

#+begin_quote
From zero to fully installed and configured, in an hour.
#+end_quote

** Overview

*** Quick Start
#+begin_src sh
case "${SHELL}" in
  (*zsh) ;;
  (*) chsh -s "$(which zsh)"; exit 1 ;;
esac
#+end_src

#+begin_example sh
curl --location --silent \
  "https://github.com/ptb/mac-setup/raw/develop/mac-setup.command" | \
  source /dev/stdin 0
#+end_example

#+begin_example sh
init && install && config
#+end_example

#+begin_example sh
custom && personalize_all
#+end_example

**** =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=

- [[https://brew.sh/][Homebrew]]: The missing package mananger for macOS
- [[https://caskroom.github.io/][Homebrew-Cask]]: “To install, drag this icon…” no more!
- [[https://github.com/mas-cli/mas][mas-cli/mas]]: Mac App Store command line interface
- [[https://github.com/Homebrew/homebrew-bundle][homebrew-bundle]]: List all Homebrew packages in =Brewfile=
- [[https://nodejs.org/][Node.js]]: Cross-platform JavaScript run-time environment
- [[https://www.perl.org/][Perl 5]]: Highly capable, feature-rich programming language
- [[https://www.python.org/][Python]]: Programming language that lets you work quickly
- [[https://www.ruby-lang.org/][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 [[https://atom.io/][Atom]] [[https://atom.io/packages][packages]] and customize preferences
- Set the desktop picture to a solid black color
- Customize the dock with new default applications
- Customize Emacs with [[http://spacemacs.org/][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
#+begin_example sh
diskx="$(diskutil list internal physical | sed '/^\//!d;s/^\(.*\)\ (.*):/\1/')"
#+end_example

#+begin_example sh
diskutil zeroDisk $diskx
diskutil partitionDisk $diskx 2 GPT \
  jhfs+ "Install" 6G \
  apfs $(ruby -e "print '$(hostname -s)'.capitalize") R
#+end_example

#+begin_example sh
sudo "/Applications/Install macOS High Sierra.app/Contents/Resources/createinstallmedia" \
  --applicationpath "/Applications/Install macOS High Sierra.app" --nointeraction \
  --volume "/Volumes/Install"
#+end_example

**** Select the Install Disk as Startup Disk

*** License

#+begin_quote
Copyright 2017 [[https://github.com/ptb][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.
#+end_quote

** Initialize

*** Initialize New Terminal
#+begin_src sh
if test -z "${1}"; then
  osascript - "${0}" << EOF > /dev/null 2>&1
<>
EOF
fi
#+end_src

**** =new_term.applescript=
#+begin_src applescript :noweb-ref new_term.applescript
    on run { _this }
      tell app "Terminal" to do script "source " & quoted form of _this & " 0"
    end run
#+end_src

*** Define Function =ask=
#+begin_src sh
ask () {
  osascript - "${1}" "${2}" "${3}" << EOF 2> /dev/null
<>
EOF
}
#+end_src

**** =ask.applescript=
#+begin_src applescript :noweb-ref 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
#+end_src

*** Define Function =ask2=
#+begin_src sh
ask2 () {
  osascript - "$1" "$2" "$3" "$4" "$5" "$6" << EOF 2> /dev/null
<>
EOF
}
#+end_src

**** =ask2.applescript=
#+begin_src applescript :noweb-ref 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
#+end_src

*** Define Function =p=
#+begin_src sh
p () {
  printf "\n\033[1m\033[34m%s\033[0m\n\n" "${1}"
}
#+end_src

*** Define Function =run=
#+begin_src sh
run () {
  osascript - "${1}" "${2}" "${3}" << EOF 2> /dev/null
<>
EOF
}
#+end_src

**** =run.applescript=
#+begin_src applescript :noweb-ref 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
#+end_src

*** Define Function =init=
#+begin_src sh
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
#+end_src

*** Define Function =init_paths=
#+begin_src sh
init_paths () {
  test -x "/usr/libexec/path_helper" && \
    eval $(/usr/libexec/path_helper -s)
}
#+end_src

*** Eliminate Prompts for Password
#+begin_src sh
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)"
}
#+end_src

*** Select Installation Cache Location
#+begin_src sh
init_cache () {
  grep -q "CACHES" "/etc/zshenv" 2> /dev/null || \
  a=$(osascript << EOF 2> /dev/null
<>
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
}
#+end_src

**** =init_cache.applescript=
#+begin_src applescript :noweb-ref init_cache.applescript
    on run
      return text 1 through -2 of POSIX path of (choose folder with prompt "Select Installation Cache Location")
    end run
#+end_src

*** Set Defaults for Sleep
#+begin_src sh
init_no_sleep () {
  sudo pmset -a sleep 0
  sudo pmset -a disksleep 0
}
#+end_src

*** Set Hostname from DNS
#+begin_src sh
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
}
#+end_src

*** Set Permissions on Install Destinations
#+begin_src sh :var _dest=_dest[3:11,1]

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
}
#+end_src

**** _dest
#+name: _dest
|-----------------+---------------------------|
| Location        | Install 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
#+begin_src sh
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
}
#+end_src

*** Install Xcode
#+begin_src sh
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
}
#+end_src

*** Install macOS Updates
#+begin_src sh
init_updates () {
  sudo softwareupdate --install --all
}
#+end_src

*** Save Mac App Store Packages
#+begin_example sh
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//'
#+end_example
#+begin_src sh :var _maskeep_launchd=_maskeep_launchd[3:-2,0:3]

init_maskeep () {
  sudo softwareupdate --reset-ignored > /dev/null

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

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

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

**** _maskeep_launchd
#+name: _maskeep_launchd
|---------+--------------------+--------+-----------------------------------------------------------------------------------------------------------------|
| Command | Entry              | Type   | Value                                                                                                           |
|---------+--------------------+--------+-----------------------------------------------------------------------------------------------------------------|
| add     | :KeepAlive         | bool   | false                                                                                                           |
| add     | :Label             | string | com.github.ptb.maskeep                                                                                         |
| add     | :ProcessType       | string | Background                                                                                                      |
| add     | :Program           | string | /usr/local/bin/maskeep                                                                                          |
| add     | :RunAtLoad         | bool   | true                                                                                                            |
| add     | :StandardErrorPath | string | /dev/stderr                                                                                                     |
| add     | :StandardOutPath   | string | /dev/stdout                                                                                                     |
| add     | :UserName          | string | root                                                                                                            |
| add     | :WatchPaths        | array  |                                                                                                                 |
| add     | :WatchPaths:0      | string | $(sudo find '/private/var/folders' -name 'com.apple.SoftwareUpdate' -type d -user _softwareupdate -print -quit 2> /dev/null) |
| add     | :WatchPaths:1      | string | $(sudo -u \\#501 -- sh -c 'getconf DARWIN_USER_CACHE_DIR' 2> /dev/null)com.apple.appstore                       |
| add     | :WatchPaths:2      | string | $(sudo -u \\#502 -- sh -c 'getconf DARWIN_USER_CACHE_DIR' 2> /dev/null)com.apple.appstore                      |
| add     | :WatchPaths:3      | string | $(sudo -u \\#503 -- sh -c 'getconf DARWIN_USER_CACHE_DIR' 2> /dev/null)com.apple.appstore                       |
| add     | :WatchPaths:4      | string | /Library/Updates                                                                                                |
|---------+--------------------+--------+-----------------------------------------------------------------------------------------------------------------|

**** =/usr/local/bin/maskeep=
#+begin_src sh :noweb-ref maskeep.sh :tangle no
#!/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
#+end_src

** Install

*** Define Function =install=
#+begin_src sh
install () {
  install_macos_sw
  install_node_sw
  install_perl_sw
  install_python_sw
  install_ruby_sw

  which config
}
#+end_src

*** Install macOS Software with =brew=
#+begin_src sh
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
}
#+end_src

*** Add =/usr/local/bin/sbin= to Default Path
#+begin_src sh
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
}
#+end_src

*** Install Homebrew Package Manager
#+begin_src sh
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"
}
#+end_src

*** Add Homebrew Taps to Brewfile
#+begin_src sh :var _taps=_taps[3:-2,0]

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

**** _taps
#+name: _taps
|----------------------------+--------------------------------------------------------|
| Homebrew Tap Name          | Reference URL                                          |
|----------------------------+--------------------------------------------------------|
| caskroom/cask              | https://github.com/caskroom/homebrew-cask              |
| caskroom/fonts             | https://github.com/caskroom/homebrew-fonts             |
| caskroom/versions          | https://github.com/caskroom/homebrew-versions          |
| homebrew/bundle            | https://github.com/Homebrew/homebrew-bundle            |
| homebrew/command-not-found | https://github.com/Homebrew/homebrew-command-not-found |
| homebrew/nginx             | https://github.com/Homebrew/homebrew-nginx             |
| homebrew/php               | https://github.com/Homebrew/homebrew-php               |
| homebrew/services          | https://github.com/Homebrew/homebrew-services          |
| ptb/custom                 | https://github.com/ptb/homebrew-custom                 |
| railwaycat/emacsmacport    | https://github.com/railwaycat/homebrew-emacsmacport    |
|----------------------------+--------------------------------------------------------|

*** Add Homebrew Packages to Brewfile
#+begin_src sh :var _pkgs=_pkgs[3:-2,0]

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}"
}
#+end_src

**** _pkgs
#+name: _pkgs
|------------------------------+-----------------------------------------------------------|
| Homebrew Package Name        | Reference URL                                             |
|------------------------------+-----------------------------------------------------------|
| aspell                       | http://aspell.net/                                        |
| bash                         | https://www.gnu.org/software/bash/                        |
| certbot                      | https://certbot.eff.org/                                  |
| chromedriver                 | https://sites.google.com/a/chromium.org/chromedriver/     |
| coreutils                    | https://www.gnu.org/software/coreutils/                   |
| dash                         | http://gondor.apana.org.au/~herbert/dash/                 |
| duti                         | https://github.com/moretension/duti                       |
| e2fsprogs                    | https://e2fsprogs.sourceforge.io/                         |
| fasd                         | https://github.com/clvv/fasd                              |
| fdupes                       | https://github.com/adrianlopezroche/fdupes                |
| gawk                         | https://www.gnu.org/software/gawk/                        |
| getmail                      | http://pyropus.ca/software/getmail/                       |
| git                          | https://git-scm.com/                                      |
| git-flow                     | http://nvie.com/posts/a-successful-git-branching-model/   |
| git-lfs                      | https://git-lfs.github.com/                               |
| gnu-sed                      | https://www.gnu.org/software/sed/                         |
| gnupg                        | https://www.gnupg.org/                                    |
| gpac                         | https://gpac.wp.imt.fr/                                   |
| httpie                       | https://httpie.org/                                       |
| hub                          | https://hub.github.com/                                   |
| ievms                        | https://xdissent.github.io/ievms/                         |
| imagemagick                  | https://www.imagemagick.org/                              |
| mas                          | https://github.com/argon/mas                              |
| mercurial                    | https://www.mercurial-scm.org/                            |
| mp4v2                        | https://code.google.com/archive/p/mp4v2/                  |
| mtr                          | https://www.bitwizard.nl/mtr/                             |
| nmap                         | https://nmap.org/                                         |
| node                         | https://nodejs.org/                                       |
| nodenv                       | https://github.com/nodenv/nodenv                          |
| openssl                      | https://www.openssl.org/                                  |
| p7zip                        | http://p7zip.sourceforge.net/                             |
| perl-build                   | https://github.com/tokuhirom/Perl-Build                   |
| pinentry-mac                 | https://github.com/GPGTools/pinentry-mac                  |
| plenv                        | https://github.com/tokuhirom/plenv                        |
| pyenv                        | https://github.com/pyenv/pyenv                            |
| rbenv                        | https://github.com/rbenv/rbenv                            |
| rsync                        | https://rsync.samba.org/                                  |
| selenium-server-standalone   | http://www.seleniumhq.org/                                |
| shellcheck                   | https://github.com/koalaman/shellcheck                    |
| sleepwatcher                 | http://www.bernhard-baehr.de/                             |
| sqlite                       | https://sqlite.org                                        |
| stow                         | https://www.gnu.org/software/stow/                        |
| syncthing                    | https://syncthing.net/                                    |
| syncthing-inotify            | https://github.com/syncthing/syncthing-inotify            |
| tag                          | https://github.com/jdberry/tag                            |
| terminal-notifier            | https://github.com/julienXX/terminal-notifier             |
| the_silver_searcher          | https://geoff.greer.fm/ag/                                |
| trash                        | http://hasseg.org/trash/                                  |
| unrar                        | http://www.rarlab.com/                                    |
| vcsh                         | https://github.com/RichiH/vcsh                            |
| vim                          | https://vim.sourceforge.io/                               |
| yarn                         | https://yarnpkg.com/                                      |
| youtube-dl                   | https://rg3.github.io/youtube-dl/                         |
| zsh                          | https://www.zsh.org/                                      |
| zsh-syntax-highlighting      | https://github.com/zsh-users/zsh-syntax-highlighting      |
| zsh-history-substring-search | https://github.com/zsh-users/zsh-history-substring-search |
| homebrew/php/php71           | https://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
#+begin_src sh :var _args=_dest[5:10,0:1]

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}"
}
#+end_src

*** Add Homebrew Casks to Brewfile
#+begin_src sh :var _casks=_casks[3:-2,0]

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}"
}
#+end_src

**** _casks
#+name: _casks
|--------------------------------------------------+---------------------------------------------------------------|
| Caskroom Package Name                            | Reference URL                                                 |
|--------------------------------------------------+---------------------------------------------------------------|
| java                                             | https://www.oracle.com/technetwork/java/javase/               |
| xquartz                                          | https://www.xquartz.org/                                      |
| adium                                            | https://www.adium.im/                                         |
| alfred                                           | https://www.alfredapp.com/                                    |
| arduino                                          | https://www.arduino.cc/                                       |
| atom                                             | https://atom.io/                                              |
| bbedit                                           | https://www.barebones.com/products/bbedit/                    |
| betterzip                                        | https://macitbetter.com/                                      |
| bitbar                                           | https://getbitbar.com/                                        |
| caffeine                                         | http://lightheadsw.com/caffeine/                              |
| carbon-copy-cloner                               | https://bombich.com/                                          |
| charles                                          | https://www.charlesproxy.com/                                 |
| dash                                             | https://kapeli.com/dash                                       |
| dropbox                                          | https://www.dropbox.com/                                      |
| exifrenamer                                      | http://www.qdev.de/?location=mac/exifrenamer                  |
| find-empty-folders                               | http://www.tempel.org/FindEmptyFolders                        |
| firefox                                          | https://www.mozilla.org/firefox/                              |
| github-desktop                                   | https://desktop.github.com/                                   |
| gitup                                            | http://gitup.co/                                              |
| google-chrome                                    | https://www.google.com/chrome/                                |
| hammerspoon                                      | http://www.hammerspoon.org/                                   |
| handbrake                                        | https://handbrake.fr/                                         |
| hermes                                           | http://hermesapp.org/                                         |
| imageoptim                                       | https://imageoptim.com/mac                                    |
| inkscape                                         | https://inkscape.org/                                         |
| integrity                                        | http://peacockmedia.software/mac/integrity/                   |
| istat-menus                                      | https://bjango.com/mac/istatmenus/                            |
| iterm2                                           | https://www.iterm2.com/                                       |
| jubler                                           | http://www.jubler.org/                                        |
| little-snitch                                    | https://www.obdev.at/products/littlesnitch/                   |
| machg                                            | http://jasonfharris.com/machg/                                |
| menubar-countdown                                | http://capablehands.net/menubarcountdown                      |
| meteorologist                                    | http://heat-meteo.sourceforge.net/                            |
| moom                                             | https://manytricks.com/moom/                                  |
| mp4tools                                         | http://www.emmgunn.com/mp4tools-home/                         |
| musicbrainz-picard                               | https://picard.musicbrainz.org/                               |
| namechanger                                      | https://mrrsoftware.com/namechanger/                          |
| nvalt                                            | http://brettterpstra.com/projects/nvalt/                      |
| nzbget                                           | https://nzbget.net/                                           |
| nzbvortex                                        | https://www.nzbvortex.com/                                    |
| openemu                                          | http://openemu.org/                                           |
| opera                                            | https://www.opera.com/                                        |
| pacifist                                         | https://www.charlessoft.com/                                  |
| platypus                                         | https://sveinbjorn.org/platypus                               |
| plex-media-server                                | https://www.plex.tv/                                          |
| qlstephen                                        | https://whomwah.github.io/qlstephen/                          |
| quitter                                          | https://marco.org/apps#quitter                                |
| radarr                                           | https://radarr.video/                                         |
| rescuetime                                       | https://www.rescuetime.com/                                   |
| resilio-sync                                     | https://www.resilio.com/individuals/                          |
| scrivener                                        | https://literatureandlatte.com/scrivener.php                  |
| sizeup                                           | https://www.irradiatedsoftware.com/sizeup/                    |
| sketch                                           | https://www.sketchapp.com/                                    |
| sketchup                                         | https://www.sketchup.com/                                     |
| skitch                                           | https://evernote.com/products/skitch                          |
| skype                                            | https://www.skype.com/                                        |
| slack                                            | https://slack.com/                                            |
| sonarr                                           | https://sonarr.tv/                                            |
| sonarr-menu                                      | https://github.com/jefbarn/Sonarr-Menu                        |
| sourcetree                                       | https://www.sourcetreeapp.com/                                |
| steermouse                                       | http://plentycom.jp/en/steermouse/                            |
| subler                                           | https://subler.org/                                           |
| sublime-text                                     | https://www.sublimetext.com/3                                 |
| the-unarchiver                                   | https://theunarchiver.com/                                    |
| time-sink                                        | https://manytricks.com/timesink/                              |
| torbrowser                                       | https://www.torproject.org/projects/torbrowser.html           |
| tower                                            | https://www.git-tower.com/                                    |
| unrarx                                           | http://www.unrarx.com/                                        |
| vimr                                             | http://vimr.org/                                              |
| vlc                                              | https://www.videolan.org/vlc/                                 |
| vmware-fusion                                    | https://www.vmware.com/products/fusion.html                   |
| wireshark                                        | https://www.wireshark.org/                                    |
| xld                                              | http://tmkk.undo.jp/xld/index_e.html                          |
| caskroom/fonts/font-inconsolata-lgc              | https://github.com/DeLaGuardo/Inconsolata-LGC                 |
| caskroom/versions/transmit4                      | https://panic.com/transmit/                                   |
| ptb/custom/adobe-creative-cloud-2014             | https://www.adobe.com/creativecloud.html                      |
| ptb/custom/blankscreen                           | http://www.wurst-wasser.net/wiki/index.php/Blank_Screen_Saver |
| ptb/custom/composer                              | https://www.jamf.com/products/jamf-composer/                  |
| ptb/custom/enhanced-dictation                    |                                                               |
| ptb/custom/ipmenulet                             | https://github.com/mcandre/IPMenulet                          |
| ptb/custom/pcalc-3                               | http://www.pcalc.com/english/about.html                       |
| ptb/custom/sketchup-pro                          | https://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-icon | https://github.com/railwaycat/homebrew-emacsmacport           |
|--------------------------------------------------+---------------------------------------------------------------|

*** Add App Store Packages to Brewfile
#+begin_src sh :var _mas=_mas[3:-3,0:1]

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
}
#+end_src

**** _mas
#+name: _mas
|----------------------------+------------+-------------------------------------------|
| App Name                   |     App ID | App Store URL                             |
|----------------------------+------------+-------------------------------------------|
| 1Password                  |  443987910 | https://itunes.apple.com/app/id443987910  |
| Affinity Photo             |  824183456 | https://itunes.apple.com/app/id824183456  |
| Coffitivity                |  659901392 | https://itunes.apple.com/app/id659901392  |
| Duplicate Photos Fixer Pro |  963642514 | https://itunes.apple.com/app/id963642514  |
| Growl                      |  467939042 | https://itunes.apple.com/app/id467939042  |
| HardwareGrowler            |  475260933 | https://itunes.apple.com/app/id475260933  |
| I Love Stars               |  402642760 | https://itunes.apple.com/app/id402642760  |
| Icon Slate                 |  439697913 | https://itunes.apple.com/app/id439697913  |
| Justnotes                  |  511230166 | https://itunes.apple.com/app/id511230166  |
| Keynote                    |  409183694 | https://itunes.apple.com/app/id409183694  |
| Metanota Pro               |  515250764 | https://itunes.apple.com/app/id515250764  |
| Numbers                    |  409203825 | https://itunes.apple.com/app/id409203825  |
| Pages                      |  409201541 | https://itunes.apple.com/app/id409201541  |
| WiFi Explorer              |  494803304 | https://itunes.apple.com/app/id494803304  |
| Xcode                      |  497799835 | https://itunes.apple.com/app/id497799835  |
| macOS High Sierra          | 1209167288 | https://itunes.apple.com/app/id1209167288 |
|----------------------------+------------+-------------------------------------------|

*** Link System Utilities to Applications
#+begin_src sh :var _links=_links[3:-2,0]

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
}
#+end_src

**** _links
#+name: _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=
#+begin_src sh :var _npm=_npm[3:-2,0]

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
}
#+end_src

#+name: _npm
|------------------------+----------------------------------------------------|
| NPM Package Name       | Reference URL                                      |
|------------------------+----------------------------------------------------|
| eslint                 | https://eslint.org/                                |
| eslint-config-cleanjs  | https://github.com/bodil/eslint-config-cleanjs     |
| eslint-plugin-better   | https://github.com/idmitriev/eslint-plugin-better  |
| eslint-plugin-fp       | https://github.com/jfmengels/eslint-plugin-fp      |
| eslint-plugin-import   | https://github.com/benmosher/eslint-plugin-import  |
| eslint-plugin-json     | https://github.com/azeemba/eslint-plugin-json      |
| eslint-plugin-promise  | https://github.com/xjamundx/eslint-plugin-promise  |
| eslint-plugin-standard | https://github.com/xjamundx/eslint-plugin-standard |
| gatsby                 |                                                    |
| json                   | http://trentm.com/json/                            |
| sort-json              | https://github.com/kesla/sort-json                 |
|------------------------+----------------------------------------------------|

*** Install Perl 5 with =plenv=
#+begin_src sh
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
}
#+end_src

*** Install Python with =pyenv=
#+begin_src sh
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
}
#+end_src

*** Install Ruby with =rbenv=
#+begin_src sh
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
}
#+end_src

** Configure

*** Define Function =config=
#+begin_src sh
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
}
#+end_src

*** Define Function =config_defaults=
#+begin_src sh
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
}
#+end_src

*** Define Function =config_plist=
#+begin_src sh
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
}
#+end_src

*** Define Function =config_launchd=
#+begin_src sh
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"
}
#+end_src

*** Mark Applications Requiring Administrator Account
#+begin_src sh :var _admin_req=_admin_req[3:-2,0]

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

**** _admin_req
#+name: _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
#+begin_src sh
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
}
#+end_src

*** Configure Let’s Encrypt
#+begin_src sh
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"
<>
EOF
    unset argv
  done

  sudo launchctl load /Library/LaunchDaemons/org.nginx.nginx.plist
}
#+end_src

**** =/usr/local/etc/nginx/servers/server_name/server_name.conf=
#+begin_src conf :noweb-ref 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
}
#+end_src

*** Configure Default Apps
#+begin_src sh
config_default_apps () {
  true
}
#+end_src

*** Configure Desktop Picture
#+begin_src sh
config_desktop () {
  sudo rm -f "/Library/Caches/com.apple.desktop.admin.png"

  base64 -D << EOF > "/Library/Desktop Pictures/Solid Colors/Solid Black.png"
<>
EOF
}
#+end_src

**** =black.png.b64=
#+begin_src base64 :noweb-ref black.png.b64
iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQAAAADrRVxmAAAAGElEQVR4AWOgMxgFo2AUjIJRMApGwSgAAAiAAAH3bJXBAAAAAElFTkSuQmCC
#+end_src

*** Configure Dovecot
#+begin_src sh
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
<>
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 =  /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
<>
EOF

      sudo brew services start dovecot

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

**** =/usr/local/etc/dovecot/dovecot.conf=
#+begin_src conf :noweb-ref 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
#+end_src

**** =/etc/pam.d/dovecot=
#+begin_src conf :noweb-ref dovecot.pam
auth	required	pam_opendirectory.so	try_first_pass
account	required	pam_nologin.so
account	required	pam_opendirectory.so
password	required	pam_opendirectory.so
#+end_src

**** =/usr/local/bin/imaptimefix.py=
#+begin_src python :noweb-ref 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))
#+end_src

*** Configure Emacs
#+begin_src sh
config_emacs () {
  test -f "/usr/local/bin/vi" || \
  cat << EOF > "/usr/local/bin/vi"
<>
EOF

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

**** =/usr/local/bin/vi=
#+begin_src sh :noweb-ref vi.sh :tangle no
#!/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
#+end_src

*** Configure Environment Variables
#+begin_src sh :var _environment_defaults=_environment_defaults[3:-2,0:4]
config_environment () {
  sudo tee "/etc/environment.sh" << 'EOF' > /dev/null
<>
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
}
#+end_src

**** =/etc/environment.sh=
#+begin_src sh :noweb-ref environment.sh :tangle no
#!/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'
#+end_src

**** _environment_defaults
#+name: _environment_defaults
|----------------------------------------+-------------+------------+---------------------+------|
| Domain                                 | Key         | Type       | Value               | Host |
|----------------------------------------+-------------+------------+---------------------+------|
| /Library/LaunchAgents/environment.user | KeepAlive   | -bool      | false               |      |
| /Library/LaunchAgents/environment.user | Label       | -string    | environment.user    |      |
| /Library/LaunchAgents/environment.user | ProcessType | -string    | Background          |      |
| /Library/LaunchAgents/environment.user | Program     | -string    | /etc/environment.sh |      |
| /Library/LaunchAgents/environment.user | RunAtLoad   | -bool      | true                |      |
| /Library/LaunchAgents/environment.user | WatchPaths  | -array-add | /etc/environment.sh |      |
| /Library/LaunchAgents/environment.user | WatchPaths  | -array-add | /etc/paths          |      |
| /Library/LaunchAgents/environment.user | WatchPaths  | -array-add | /etc/paths.d        |      |
| /Library/LaunchDaemons/environment     | KeepAlive   | -bool      | false               |      |
| /Library/LaunchDaemons/environment     | Label       | -string    | environment         |      |
| /Library/LaunchDaemons/environment     | ProcessType | -string    | Background          |      |
| /Library/LaunchDaemons/environment     | Program     | -string    | /etc/environment.sh |      |
| /Library/LaunchDaemons/environment     | RunAtLoad   | -bool      | true                |      |
| /Library/LaunchDaemons/environment     | WatchPaths  | -array-add | /etc/environment.sh |      |
| /Library/LaunchDaemons/environment     | WatchPaths  | -array-add | /etc/paths          |      |
| /Library/LaunchDaemons/environment     | WatchPaths  | -array-add | /etc/paths.d        |      |
|----------------------------------------+-------------+------------+---------------------+------|

*** Configure IPMenulet
#+begin_src sh
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
}
#+end_src

*** Configure iStat Menus
#+begin_src sh
config_istatmenus () {
  test -d "/Applications/iStat Menus.app" && \
  open "/Applications/iStat Menus.app"
}
#+end_src

**** Notes
#+begin_example conf
  client_max_body_size 0;

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

*** Configure nginx
#+begin_src sh :var _nginx_defaults=_nginx_defaults[3:-2,0:4]
config_nginx () {
  cat << 'EOF' > /usr/local/etc/nginx/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
}
#+end_src

**** =/usr/local/etc/nginx/nginx.conf=
#+begin_src conf :noweb-ref 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_ti

本源码包内暂不包含可直接显示的源代码文件,请下载源码包。