├── .gitignore ├── .zshrc ├── LICENSE.txt ├── README.md ├── defaults ├── homebrew.zsh ├── oh-my-zsh.gitrepo ├── oh-your-dotfiles.gitrepo ├── path.zsh └── zshrc.symlink ├── lib ├── apt.zsh ├── dotfiles.zsh ├── git.zsh ├── homebrew.zsh ├── install-arch.zsh ├── install.zsh ├── mas.zsh └── terminal.zsh ├── screenshot.png └── tools └── startlog.py /.gitignore: -------------------------------------------------------------------------------- 1 | oh-my-zsh 2 | .idea 3 | *.iml 4 | .zsh_history 5 | .zcompdump* 6 | -------------------------------------------------------------------------------- /.zshrc: -------------------------------------------------------------------------------- 1 | defaults/zshrc.symlink -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2009-2017 Robby Russell and contributors 4 | See the full list at https://github.com/robbyrussell/oh-my-zsh/contributors 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | *You got your Oh My Zsh in my dotfiles!* 2 | 3 | The flexibility of dotfiles meets the power of [Oh My Zsh](https://github.com/robbyrussell/oh-my-zsh) and [Homebrew](https://brew.sh/). 4 | 5 | Inspired by and compatible with [Zach Holman's dotfiles](https://github.com/holman/dotfiles). 6 | 7 | ![A screenshot of the oh-your-dotfiles update process output](screenshot.png?raw=true) 8 | 9 | ## Install ## 10 | 11 | The framework is only currently tested on macOS. 12 | 13 | 1. Clone this repository to `~/.oh-your-dotfiles` 14 | 2. Run `dotfiles_install` with: 15 | ``` 16 | ZDOTDIR=~/.oh-your-dotfiles zsh -ic dotfiles_install 17 | ``` 18 | 3. If your default shell isn't already `zsh`, change your default shell: 19 | ``` 20 | chsh -s /bin/zsh 21 | ``` 22 | 4. Start a new session 23 | 24 | You're good to go! 25 | 26 | Create yourself a dotfiles repository using the conventions below. See https://github.com/DanielThomas/dotfiles for an example of a dotfiles repository. 27 | 28 | ## Built-in Functions ## 29 | 30 | - `dotfiles` - list dotfiles locations 31 | - `dotfiles_find` - find files within dotfiles locations, for example `dotfiles_find \*.gitrepo` 32 | - `dotfiles_install` - install dotfiles 33 | - `dotfiles_update` - update dotfiles installed files. Equivalent to running `dotfiles_install` and choosing `S` to skip existing 34 | - `dotfiles_ignored` - show ignored file and directory patterns 35 | 36 | ## How it works ## 37 | 38 | Dotfiles sources are found using the pattern `$HOME/.*dotfiles*`. 39 | 40 | The files within are processed automatically by `.zshrc` or the installation process depending on their extension. 41 | 42 | Scripts set the environment, manage files, perform installation or enable plugins depending on the file name or extension. Bootstrap can be safely run repeatedly, you'll be prompted for the action you want to take if a destination file or directory already exists. 43 | 44 | ### Architecture ### 45 | 46 | The file conventions support an architecture suffix, for instance `path.zsh.x86_64` or `path.zsh.arm64` which will make the configuration apply conditionally to that architecture. 47 | 48 | Installers are run regardless of the prevailing architecture if the machine supports that architecture (i.e. `x86_64` on `arm64` via Rosetta 2) using `arch` to force the architecture. The `brew` command is also shimmed with a function to use the architecture specific location, `/usr/local` for `x86_64` and `/opt/homebrew` for `arm64` based on the prevailing architecture, run `brew` with `arch -x86_64 brew` in an `arm64` terminal to manually install Intel formulas/casks. 49 | 50 | Installer files without a suffix are assumed to be universal and are run using the native architecture for the machine. For files that are strictly compatible with a native architecture, add `-native`, for instance `x86_64-native` to indicate that it should be ignored even with `x86_64` translation. 51 | 52 | ### Environment ### 53 | 54 | These files set your shell's environment: 55 | 56 | - `oh-my-zsh.zsh` Loaded before oh my zsh is sourced, useful for configuration of a theme (ZSH_THEME) 57 | - `path.zsh`: Loaded first after oh my zsh is sourced, and expected to setup `$PATH` 58 | - `*.zsh`: Get loaded into your environment 59 | - `completion.zsh`: Loaded last, and expected to setup autocomplete 60 | 61 | ### Files ### 62 | 63 | The following extensions will cause files to be created in your home directory: 64 | 65 | - `*.symlink`: Automaticlly symlinked into your `$HOME` as a dot file during bootstrap. For example, a file `myfile.symlink` will be linked as `$HOME/.myfile`. If a directory the files within will be symlinked relatively, for instance `config.symlink/mytool/myconfig` will be linked as `$HOME/.config/mytool/myconfig` 66 | - `*.gitrepo`: Contains a URL to a Git repository to be cloned as a dotfile. For example `myrepo.gitrepo` will be cloned to `$HOME/.myrepo` 67 | - `*.themegitrepo`: Contains a URL to a Git repository to be cloned as a custom zsh theme. For example `mytheme.gitrepo` will be cloned to `$HOME/.oh-my-zsh/custom/themes/mytheme` 68 | - `*.gitpatch`: Name `repo-.gitpatch` to apply custom patches to a `gitrepo` repository 69 | - `*.otf`, `*.ttf`, `*.ttc`: Fonts are copied to `~/Library/Fonts` during bootstrap 70 | - `*.plist`: Preference lists are copied to `~/Library/Preferences` during bootstrap 71 | - `*.launchagent`: Files are copied to `~/Library/LaunchAgents` during bootstrap 72 | 73 | #### Ignoring Directories #### 74 | 75 | Hidden files and directories and `bin/` directories are ignored by default. To ignore a specific directory, add a `.dotfiles_ignore` file to a directory. 76 | 77 | Run `dotfiles_ignored` to see the list of ignored directories and `dotfiles_find \*` to see all candidate files. 78 | 79 | ### Installers ### 80 | 81 | Installation steps during bootstrap can be handled in several ways: 82 | 83 | - `install.sh`: An installation shellscript 84 | - `install.homebrew`: A list of Homebrew formulas to install. Use `install.linuxbrew` for Linux 85 | - `install.homebrew-cask`: A list of Homebrew casks to install 86 | - `install.homebrew-tap`: A list of Homebrew taps 87 | - `install.mas`: A list of App Store apps to install 88 | - `install.open`: A list of files to be handled by the default application association using the `open` command 89 | - `install.apt`: A list of apt packages to install 90 | 91 | #### Installing from the App Store with `install.mas` files #### 92 | 93 | Applications from the App Store are referenced by a numeric id rather than a name. 94 | In order to find out the id you can use the command `mas search `. 95 | Entries in `install.mas` should be in the format ` ` (the same format as the results of `mas search`). 96 | 97 | ### Plugins ### 98 | 99 | - All topic directory names are implicitly added to the plugin list, so you get `osx` and `brew` automatically 100 | - Plugins listed in `oh-my-zsh.plugins` files are read and added to this list 101 | 102 | ## Profiling Startup Time ## 103 | 104 | If your shell is taking an excessive amount of time to start, run `zsh` with the `DOTFILES_PROFILE_ZSHRC` environment variable: 105 | 106 | DOTFILES_PROFILE_ZSHRC=true zsh 107 | 108 | Then run `tools/startlog.py` against the output in `/tmp` to determine the contributors to startup time. For more details, see: 109 | 110 | [https://kev.inburke.com/kevin/profiling-zsh-startup-time/](https://kev.inburke.com/kevin/profiling-zsh-startup-time/) 111 | -------------------------------------------------------------------------------- /defaults/homebrew.zsh: -------------------------------------------------------------------------------- 1 | if [ "Darwin" = "$(uname)" ]; then 2 | case $(uname -m) in 3 | x86_64) 4 | HOMEBREW_PREFIX="/usr/local" 5 | ;; 6 | arm64) 7 | HOMEBREW_PREFIX="/opt/homebrew" 8 | ;; 9 | *) 10 | >&2 echo "Unknown architecture $(uname -m) unable to set HOMEBREW_PREFIX environment" 11 | ;; 12 | esac 13 | else 14 | HOMEBREW_PREFIX="$HOME/.linuxbrew" 15 | fi 16 | export HOMEBREW_PREFIX 17 | 18 | 19 | function brew() { 20 | if [ -z "$HOMEBREW_PREFIX" ]; then 21 | brew $@ 22 | else 23 | "$HOMEBREW_PREFIX/bin/brew" $@ 24 | fi 25 | } 26 | -------------------------------------------------------------------------------- /defaults/oh-my-zsh.gitrepo: -------------------------------------------------------------------------------- 1 | https://github.com/robbyrussell/oh-my-zsh.git -------------------------------------------------------------------------------- /defaults/oh-your-dotfiles.gitrepo: -------------------------------------------------------------------------------- 1 | https://github.com/DanielThomas/oh-your-dotfiles.git 2 | git@github.com:DanielThomas/oh-your-dotfiles.git 3 | -------------------------------------------------------------------------------- /defaults/path.zsh: -------------------------------------------------------------------------------- 1 | source "${0:a:h}/homebrew.zsh" 2 | BREW_PATH="" 3 | if [[ ":$PATH:" != *":$HOMEBREW_PREFIX/bin:"* ]]; then 4 | BREW_PATH="${HOMEBREW_PREFIX}/bin:" 5 | fi 6 | if [[ ":$PATH:" != *":$HOMEBREW_PREFIX/sbin:"* ]]; then 7 | BREW_PATH="${BREW_PATH}${HOMEBREW_PREFIX}/sbin:" 8 | fi 9 | if [[ ":$PATH:" != *":/usr/local/bin:"* ]]; then 10 | BREW_PATH="${BREW_PATH}/usr/local/bin:" 11 | fi 12 | if [[ ":$PATH:" != *":/usr/local/sbin:"* ]]; then 13 | BREW_PATH="${BREW_PATH}/usr/local/sbin:" 14 | fi 15 | if [ ! -z "$BREW_PATH" ]; then 16 | export PATH="${BREW_PATH}${PATH}" 17 | fi 18 | -------------------------------------------------------------------------------- /defaults/zshrc.symlink: -------------------------------------------------------------------------------- 1 | if [[ "$DOTFILES_PROFILE_ZSHRC" == true ]]; then 2 | PS4=$'%D{%M%S%.} %N:%i> ' 3 | exec 3>&2 2> /tmp/startlog.$$ 4 | setopt xtrace prompt_subst 5 | fi 6 | 7 | ## configure oh-your-dotfiles ## 8 | 9 | function realpath() { 10 | OURPWD=$PWD 11 | cd "$(dirname "$1")" 12 | LINK=$(readlink "$(basename "$1")") 13 | while [ "$LINK" ]; do 14 | cd "$(dirname "$LINK")" 15 | LINK=$(readlink "$(basename "$1")") 16 | done 17 | REALPATH="$PWD/$(basename "$1")" 18 | cd "$OURPWD" 19 | echo "$REALPATH" 20 | } 21 | 22 | export DOTFILES_DIR=$(dirname $(realpath $(echo ${(%):-%x})))/../ 23 | source "$DOTFILES_DIR/lib/dotfiles.zsh" 24 | 25 | # preserve the original path so we can restore it on reload 26 | if [ -z "$ZSHRC_PATH" ]; then 27 | ZSHRC_PATH="$PATH" 28 | else 29 | PATH="$ZSHRC_PATH" 30 | fi 31 | 32 | # find all zsh files 33 | typeset -U config_files 34 | config_files=($(dotfiles_find \*.zsh)) 35 | 36 | # use .localrc for things that need to be kept secret 37 | if [[ -a $HOME/.localrc ]] 38 | then 39 | source $HOME/.localrc 40 | fi 41 | 42 | ZSH=$DOTFILES_DIR/oh-my-zsh 43 | if [ ! -d "$ZSH" ]; then 44 | if [ ! -d "$HOME/.oh-my-zsh" ]; then 45 | echo "oh-my-zsh is not installed, run dotfiles_install" 46 | return 47 | else 48 | ZSH="$HOME/.oh-my-zsh" 49 | fi 50 | fi 51 | 52 | DEFAULT_USER=$(whoami) 53 | DISABLE_AUTO_UPDATE="true" 54 | 55 | # configure plugins 56 | plugins=("${(@f)$( 57 | find $(dotfiles) -maxdepth 1 -not -name '.git' -type d -exec basename {} \; | awk -v zsh=${ZSH} '{print zsh"/plugins/"$1}' | xargs ls -d 2>/dev/null | xargs -n 1 basename | sort | uniq 58 | 59 | find $(dotfiles) -maxdepth 2 -name oh-my-zsh.plugins -exec cat {} \; -exec echo \; 60 | )}") 61 | 62 | for file in ${(M)config_files:#*/oh-my-zsh.zsh} 63 | do 64 | source $file 65 | done 66 | 67 | OH_MY_ZSH="$ZSH/oh-my-zsh.sh" 68 | 69 | if [[ -a "$OH_MY_ZSH" ]] 70 | then 71 | source "$OH_MY_ZSH" 72 | fi 73 | 74 | ## load dotfiles ## 75 | 76 | # load the path files 77 | for file in ${(M)config_files:#*/path.zsh} 78 | do 79 | source $file 80 | done 81 | 82 | # put the bin/ directories first on the path 83 | for d in $(dotfiles) 84 | do 85 | export PATH=$d/bin:$PATH 86 | done 87 | 88 | # load everything else 89 | for file in ${${${config_files:#*/path.zsh}:#*/completion.zsh}:#*/oh-my-zsh.zsh} 90 | do 91 | source $file 92 | done 93 | 94 | # load every completion after autocomplete loads 95 | for file in ${(M)config_files:#*/completion.zsh} 96 | do 97 | source $file 98 | done 99 | 100 | unset config_files 101 | 102 | if [[ "$DOTFILES_PROFILE_ZSHRC" == true ]]; then 103 | unsetopt xtrace 104 | exec 2>&3 3>&- 105 | fi 106 | -------------------------------------------------------------------------------- /lib/apt.zsh: -------------------------------------------------------------------------------- 1 | apt_install_upgrade() { 2 | if [[ "Linux" != "$(uname)" ]]; then 3 | return 4 | fi 5 | run "updating apt indexes" "sudo apt update" 6 | local apt_upgradable=$(apt list --upgradeable) 7 | local apt_packages=() 8 | for aptfile in `dotfiles_find_installer install.apt`; do 9 | for package in $(cat "$aptfile"); do 10 | if ! dpkg -s "$package" 1> /dev/null 2>& 1; then 11 | apt_packages+="$package" 12 | fi 13 | if echo "$apt_upgradable" | grep "^${package}/" > /dev/null; then 14 | apt_packages+="$package" 15 | fi 16 | done 17 | done 18 | if [ ${#apt_packages[@]} -gt 0 ]; then 19 | run "installing ${apt_packages[*]}" "sudo apt --yes install ${apt_packages[*]}" 20 | fi 21 | } 22 | -------------------------------------------------------------------------------- /lib/dotfiles.zsh: -------------------------------------------------------------------------------- 1 | # install/update/reload 2 | function realpath() { 3 | OURPWD=$PWD 4 | cd "$(dirname "$1")" 5 | LINK=$(readlink "$(basename "$1")") 6 | while [ "$LINK" ]; do 7 | cd "$(dirname "$LINK")" 8 | LINK=$(readlink "$(basename "$1")") 9 | done 10 | REALPATH="$PWD/$(basename "$1")" 11 | cd "$OURPWD" 12 | echo "$REALPATH" 13 | } 14 | 15 | defaults=$(realpath "${0:a:h}/../defaults") 16 | 17 | function dotfiles_install() { 18 | $DOTFILES_DIR/lib/install.zsh $1 19 | } 20 | 21 | function dotfiles_update() { 22 | $DOTFILES_DIR/lib/install.zsh update 23 | } 24 | 25 | function dotfiles_reload() { 26 | source $HOME/.zshrc 27 | } 28 | 29 | function dotfiles_find() { 30 | local arch=$(uname -m) 31 | local arch_native="$arch" 32 | if [[ "Darwin" == "$(uname)" ]]; then 33 | if sysctl -n machdep.cpu.brand_string | grep "Apple" > /dev/null; then 34 | arch_native="arm64" 35 | fi 36 | fi 37 | if [ "$arch_native" = "$arch" ]; then 38 | find -L $(dotfiles) -type f $(dotfiles_find_ignore) -name "$1" -o -name "$1.${arch}" -o -name "$1.${arch}-native" 39 | else 40 | find -L $(dotfiles) -type f $(dotfiles_find_ignore) -name "$1" -o -name "$1.${arch}" 41 | fi 42 | } 43 | 44 | function dotfiles_find_installer() { 45 | local arch=$(uname -m) 46 | local arch_native="$arch" 47 | if [[ "Darwin" == "$(uname)" ]]; then 48 | if sysctl -n machdep.cpu.brand_string | grep "Apple" > /dev/null; then 49 | arch_native="arm64" 50 | fi 51 | fi 52 | # only return universal installers for the native architecture to avoid double-executing the installers 53 | if [ "$arch_native" = "$arch" ]; then 54 | find -L $(dotfiles) -type f $(dotfiles_find_ignore) -name "$1" -o -name "$1.${arch}" -o -name "$1.${arch}-native" 55 | else 56 | find -L $(dotfiles) -type f $(dotfiles_find_ignore) -name "$1.${arch}" 57 | fi 58 | } 59 | 60 | function dotfiles_find_symlink() { 61 | find -L $(dotfiles) $(dotfiles_find_ignore) -name "*.symlink" 62 | } 63 | 64 | function dotfiles_find_ignore() { 65 | for ignore in $(dotfiles_ignored); do 66 | printf "-not -path %s " "$ignore" 67 | done 68 | } 69 | 70 | function dotfiles_ignored() { 71 | find -L $(dotfiles) -type f -name ".dotfiles_ignore" -exec dirname {} \; | sed 's#$#/*#' 72 | for dotfile in $(dotfiles); do 73 | echo "$dotfile/bin/*" 74 | echo "$dotfile/.*" 75 | done 76 | } 77 | 78 | function dotfiles() { 79 | files=("$defaults") 80 | for file in $(find "$HOME" -maxdepth 1 -name '.*dotfiles*' -not -name '.oh-your-dotfiles' | sort); do 81 | if [ -d "$file" ]; then 82 | files+=($file) 83 | fi 84 | done 85 | echo "${files[@]}" 86 | } 87 | -------------------------------------------------------------------------------- /lib/git.zsh: -------------------------------------------------------------------------------- 1 | git_clone_or_pull() { 2 | if [ -d $2 ]; then 3 | git_pull $1 $2 4 | else 5 | git_clone $1 $2 6 | fi 7 | git_patch $1 8 | } 9 | 10 | git_clone() { 11 | fetch=$(head -n 1 $1) 12 | push=$(head -2 $1| tail -1) 13 | dest=$2 14 | 15 | if ! git clone --quiet $fetch $dest; then 16 | warn "clone for $fetch failed" 17 | return 18 | fi 19 | if [ "$fetch" != "$push" ]; then 20 | git -C "$dest" remote set-url origin --push $push 21 | fi 22 | 23 | success "cloned $fetch to `basename $dest`" 24 | } 25 | 26 | function git_pull() { 27 | fetch=$(head -n 1 $1) 28 | push=$(head -2 $1| tail -1) 29 | dest=$2 30 | 31 | git -C "$dest" remote set-url origin "$fetch" 32 | if [ "$fetch" != "$push" ]; then 33 | git -C "$dest" remote set-url origin --push $push 34 | fi 35 | 36 | current_sha=$(git -C "$dest" rev-parse --short HEAD) 37 | branch=$(git -C "$dest" rev-parse --abbrev-ref HEAD) 38 | remote=$(git -C "$dest" remote get-url origin) 39 | run "pulling $dest from $remote" "git -C $dest pull origin $branch --rebase --quiet" 40 | new_sha=$(git -C "$dest" rev-parse --short HEAD) 41 | if [ "$current_sha" != "$new_sha" ]; then 42 | success "updated $1 from $current_sha to $new_sha (branch $branch)" 43 | fi 44 | } 45 | 46 | git_patch() { 47 | dir=$(dirname $1) 48 | base=$(basename ${1%.*}) 49 | for patch in $(find $dir -maxdepth 2 -name $base\*.gitpatch); do 50 | run "applying $patch to $dest" "git -C "$dest" apply --quiet $patch" 51 | done 52 | } 53 | -------------------------------------------------------------------------------- /lib/homebrew.zsh: -------------------------------------------------------------------------------- 1 | brew_installed="" 2 | 3 | function brew_run() { 4 | HOMEBREW_NO_AUTO_UPDATE=1 $(brew_command) $@ 5 | } 6 | 7 | function brew_command() { 8 | echo "$(brew_prefix)/bin/brew" 9 | } 10 | 11 | function brew_prefix() { 12 | local arch=$(uname -m) 13 | if [[ "Darwin" == "$(uname)" ]]; then 14 | case $arch in 15 | x86_64) 16 | echo "/usr/local" 17 | ;; 18 | arm64) 19 | echo "/opt/homebrew" 20 | ;; 21 | *) 22 | >&2 echo "Cannot determine brew prefix, unknown architecture $arch" 23 | ;; 24 | esac 25 | else 26 | echo "$HOME/.linuxbrew" 27 | fi 28 | } 29 | 30 | function brew_install_upgrade_formulas() { 31 | brew_install_formulas 32 | brew_upgrade_formulas 33 | } 34 | 35 | function brew_install_formulas() { 36 | extension="homebrew" 37 | if [[ "Darwin" != "$(uname)" ]]; then 38 | extension="linuxbrew" 39 | fi 40 | formulas=$(dotfiles_find_installer "install.${extension}") 41 | casks=$(dotfiles_find_installer "install.${extension}-cask") 42 | 43 | if [[ -n "$casks" || -n "$formulas" ]]; then 44 | brew_check_and_install 45 | fi 46 | 47 | if [ -n "$formulas" ]; then 48 | brew_installed=$(brew_run ls --versions 2> /dev/null) 49 | for file in `dotfiles_find_installer install.${extension}`; do 50 | brew_install formula "$file" 51 | done 52 | fi 53 | 54 | if [ -n "$casks" ]; then 55 | brew_installed=$(brew_run ls --cask --versions 2> /dev/null) 56 | for file in `dotfiles_find_installer install.${extension}-cask`; do 57 | brew_install cask "$file" 58 | done 59 | fi 60 | } 61 | 62 | function brew_install() { 63 | type="$1" 64 | file="$2" 65 | missing_formulas="" 66 | for formula in $(cat "$file"); do 67 | if ! echo $brew_installed | grep -q "^$formula "; then 68 | missing_formulas+="$formula " 69 | fi 70 | done 71 | missing_formulas=$(echo "$missing_formulas" | xargs) 72 | if [ ! -z "$missing_formulas" ]; then 73 | run "installing ${type}s from $file ($(echo "$missing_formulas" | sed -e :a -e '$!N; s/\n/, /; ta'))" "brew_run install --${type} $missing_formulas" 74 | fi 75 | } 76 | 77 | function brew_update() { 78 | prefix=$(brew_prefix) 79 | run "updating homebrew (prefix $prefix)" "brew_run update" 80 | success "updated homebrew (prefix $prefix)" 81 | } 82 | 83 | function brew_upgrade_formulas() { 84 | if [ -f $(brew_command) ]; then 85 | brew_update 86 | brew_upgrade formula 87 | if [[ "Darwin" == "$(uname)" ]]; then 88 | brew_upgrade cask 89 | fi 90 | wait 91 | fi 92 | } 93 | 94 | function brew_upgrade() { 95 | type="$1" 96 | outdated=$(brew_run outdated --${type} | sed -e :a -e '$!N; s/\n/, /; ta') 97 | if [ -n "$outdated" ]; then 98 | run "upgrading brew $type ($outdated)" "brew_run upgrade --${type}" 99 | fi 100 | } 101 | 102 | function brew_check_and_install() { 103 | if [ ! -f $(brew_command) ]; then 104 | prefix=$(brew_prefix) 105 | info "homebrew is not installed in $prefix" 106 | run "creating directory $prefix" "sudo mkdir -p ${prefix}" 107 | owner="$(whoami):$(id -g -n)" 108 | run "changing ownership of $prefix to $owner" "sudo chown -R ${owner} ${prefix}" 109 | run "downloading homebrew and extracting to $prefix" "curl -L https://github.com/Homebrew/brew/tarball/master | tar xz --strip 1 -C ${prefix}" 110 | fi 111 | brew_taps 112 | } 113 | 114 | function brew_taps() { 115 | brew_tapped=$(brew_run tap 2> /dev/null) 116 | for tapfile in `dotfiles_find_installer install.homebrew-tap`; do 117 | while read -r LINE || [[ -n "$LINE" ]]; do 118 | args=( ${=LINE} ) 119 | tap="${args[1]}" 120 | if ! echo "$brew_tapped" | grep -q "$tap"; then 121 | run "tapping ${args[1]}" "brew_run tap ${args[1]} ${args[2]}" 122 | fi 123 | done < $tapfile 124 | done 125 | } 126 | -------------------------------------------------------------------------------- /lib/install-arch.zsh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env zsh 2 | libdir=${0:a:h} 3 | source $libdir/dotfiles.zsh 4 | source $libdir/terminal.zsh 5 | source $libdir/apt.zsh 6 | source $libdir/homebrew.zsh 7 | source $libdir/mas.zsh 8 | source $libdir/git.zsh 9 | 10 | function run_installers() { 11 | apt_install_upgrade 12 | brew_install_upgrade_formulas 13 | mas_install_upgrade_formulas 14 | 15 | dotfiles_find_installer install.sh | while read installer ; do run "running ${installer}" "${installer}" ; done 16 | 17 | for file_source in $(dotfiles_find_installer install.open); do 18 | OLD_IFS=$IFS 19 | IFS=$'\n' 20 | basedir="$(dirname $file_source)" 21 | for file in `cat $file_source`; do 22 | canonical_file="$basedir/$file" 23 | open_file "$canonical_file" 24 | done 25 | IFS=$OLD_IFS 26 | done 27 | } 28 | 29 | function run_postinstall() { 30 | dotfiles_find_installer post-install.sh | while read installer ; do run "running ${installer}" "${installer}" ; done 31 | } 32 | 33 | function main() { 34 | run_installers 35 | run_postinstall 36 | } 37 | 38 | main "$@" 39 | -------------------------------------------------------------------------------- /lib/install.zsh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env zsh 2 | libdir=${0:a:h} 3 | source $libdir/dotfiles.zsh 4 | source $libdir/terminal.zsh 5 | source $libdir/homebrew.zsh 6 | source $libdir/mas.zsh 7 | source $libdir/git.zsh 8 | 9 | function link_files() { 10 | case "$1" in 11 | link ) 12 | link_file $2 $3 13 | ;; 14 | copy ) 15 | copy_file $2 $3 16 | ;; 17 | git ) 18 | git_clone_or_pull $2 $3 19 | ;; 20 | * ) 21 | fail "Unknown link type: $1" 22 | ;; 23 | esac 24 | } 25 | 26 | function link_file() { 27 | mkdir -p $(dirname $2) 28 | run "linking $1 to $2" "ln -s -f $1 $2" 29 | success "linked $1 to $2" 30 | } 31 | 32 | function copy_file() { 33 | mkdir -p $(dirname $2) 34 | run "copying $1 to $2" "cp $1 $2" 35 | success "copied $1 to $2" 36 | } 37 | 38 | function open_file() { 39 | run "opening $1" "open $1" 40 | success "opened $1" 41 | } 42 | 43 | function install_file() { 44 | local file_type=$1 45 | local file_source=$2 46 | local file_dest=$3 47 | if [ -f $file_dest ] || [ -d $file_dest ]; then 48 | overwrite=false 49 | backup=false 50 | skip=false 51 | 52 | if [ "$overwrite_all" = "false" ] && [ "$backup_all" = "false" ] && [ "$skip_all" = "false" ] && [ "$skip_all_silent" = "false" ] && [ "$force_all" = "false" ] && [ "$force_all_silent" = "false" ]; then 53 | user "File already exists: `basename $file_dest`, what do you want to do? [s]kip, [S]kip all, [o]verwrite, [O]verwrite all, [b]ackup, [B]ackup all?" 54 | read -r action 55 | 56 | case "$action" in 57 | o ) 58 | overwrite=true;; 59 | O ) 60 | overwrite_all=true;; 61 | b ) 62 | backup=true;; 63 | B ) 64 | backup_all=true;; 65 | s ) 66 | skip=true;; 67 | S ) 68 | skip_all=true;; 69 | * ) 70 | ;; 71 | esac 72 | fi 73 | 74 | if [ "$overwrite" = "true" ] || [ "$overwrite_all" = "true" ]; then 75 | rm -rf "$file_dest" 76 | success "removed $file_dest" 77 | link_files $file_type $file_source $file_dest 78 | fi 79 | 80 | if [ "$backup" = "true" ] || [ "$backup_all" = "true" ]; then 81 | mv $file_dest $file_dest\.backup 82 | success "moved $file_dest to $file_dest.backup" 83 | link_files $file_type $file_source $file_dest 84 | fi 85 | 86 | if [[ "$force_all" = "true" ]] || [[ "$skip" = "false" && "$skip_all" = "false" && "$skip_all_silent" = "false" ]]; then 87 | link_files $file_type $file_source $file_dest 88 | elif [ "$skip_all_silent" = "false" ]; then 89 | success "skipped $file_source" 90 | fi 91 | else 92 | link_files $file_type $file_source $file_dest 93 | fi 94 | } 95 | 96 | function create_localrc() { 97 | LOCALRC=$HOME/.localrc 98 | if [ ! -f "$LOCALRC" ]; then 99 | echo "DEFAULT_USER=$USER" > $LOCALRC 100 | success "created $LOCALRC" 101 | fi 102 | } 103 | 104 | function dotfiles_install() { 105 | overwrite_all=false 106 | backup_all=false 107 | skip_all=false 108 | force_all=true 109 | 110 | # git repositories - dotfiles can be in nested gitrepo files, so we do multiple passes 111 | cloned_repos=() 112 | while true; do 113 | did_clone=false 114 | for file_source in $(dotfiles_find \*.gitrepo); do 115 | file_dest="$HOME/.`basename \"${file_source%.*}\"`" 116 | if [[ ! " ${cloned_repos[*]} " =~ " $file_source " ]]; then 117 | cloned_repos+=($file_source) 118 | install_file git $file_source $file_dest 119 | did_clone=true 120 | fi 121 | done 122 | wait 123 | if [ "$did_clone" = "false" ]; then 124 | break 125 | fi 126 | done 127 | 128 | for file_source in $(dotfiles_find \*.themegitrepo); do 129 | file_dest="$HOME/.oh-my-zsh/custom/themes/`basename \"${file_source%.*}\"`" 130 | install_file git $file_source $file_dest 131 | done 132 | wait 133 | 134 | if [ "$force_all_silent" != "true" ]; then 135 | force_all=false 136 | fi 137 | 138 | # symlinks 139 | for file_source in $(dotfiles_find_symlink); do 140 | file_dest="$HOME/.`basename \"${file_source%.*}\"`" 141 | if [ -d $file_source ]; then 142 | for directory_file_source in $(find "$file_source" -type f); do 143 | directory_file_dest=${file_dest}${directory_file_source#"$file_source"} 144 | if [ -L $directory_file_dest ]; then 145 | if [ "$(readlink "$directory_file_dest")" != "$directory_file_source" ]; then 146 | install_file link $directory_file_source $directory_file_dest 147 | fi 148 | else 149 | install_file link $directory_file_source $directory_file_dest 150 | fi 151 | done 152 | else 153 | if [ -L $file_dest ]; then 154 | if [ "$(readlink "$file_dest")" != "$file_source" ]; then 155 | install_file link $file_source $file_dest 156 | fi 157 | else 158 | install_file link $file_source $file_dest 159 | fi 160 | fi 161 | done 162 | 163 | if [[ "Darwin" == "$(uname)" ]]; then 164 | # preferences 165 | for file_source in $(dotfiles_find \*.plist); do 166 | file_dest="$HOME/Library/Preferences/`basename $file_source`" 167 | install_file copy $file_source $file_dest 168 | done 169 | 170 | # fonts 171 | for file_source in $(dotfiles_find \*.otf); do 172 | file_dest="$HOME/Library/Fonts/$(basename $file_source)" 173 | install_file copy $file_source $file_dest 174 | done 175 | for file_source in $(dotfiles_find \*.ttf); do 176 | file_dest="$HOME/Library/Fonts/$(basename $file_source)" 177 | install_file copy $file_source $file_dest 178 | done 179 | for file_source in $(dotfiles_find \*.ttc); do 180 | file_dest="$HOME/Library/Fonts/$(basename $file_source)" 181 | install_file copy $file_source $file_dest 182 | done 183 | 184 | # launch agents 185 | for file_source in $(dotfiles_find \*.launchagent); do 186 | file_dest="$HOME/Library/LaunchAgents/$(basename $file_source | sed 's/.launchagent//')" 187 | install_file copy $file_source $file_dest 188 | done 189 | fi 190 | } 191 | 192 | function install_arch_list() { 193 | if [[ "Darwin" == "$(uname)" ]]; then 194 | if sysctl -n machdep.cpu.brand_string | grep "Apple" > /dev/null; then 195 | echo "arm64e" 196 | fi 197 | fi 198 | echo "x86_64" 199 | } 200 | 201 | function install() { 202 | if [[ "Darwin" == "$(uname)" ]]; then 203 | if sysctl -n machdep.cpu.brand_string | grep "Apple" > /dev/null; then 204 | if [ $(uname -m) != "arm64" ]; then 205 | fail "this command must be run on an arm64 terminal on Apple Silicon" 206 | fi 207 | if [[ ! -f "/usr/libexec/rosetta/oahd" ]]; then 208 | run 'installing Rosetta' "/usr/sbin/softwareupdate --install-rosetta --agree-to-license" 209 | fi 210 | fi 211 | fi 212 | dotfiles_install 213 | for arch in $(install_arch_list); do 214 | info "running installers for $arch" 215 | if [[ "Darwin" == "$(uname)" ]]; then 216 | arch -arch "$arch" "$libdir/install-arch.zsh" 217 | else 218 | "$libdir/install-arch.zsh" 219 | fi 220 | done 221 | create_localrc 222 | } 223 | 224 | function main() { 225 | skip_all_silent=false 226 | force_all_silent=false 227 | if [ "$1" = "force" ]; then 228 | info 'installing dotfiles (force)' 229 | force_all_silent=true 230 | install 231 | success 'complete! use dotfiles_update to keep up to date. run dotfiles_reload or restart your session for environment changes to take effect' 232 | elif [ "$1" = "update" ]; then 233 | info 'updating dotfiles' 234 | skip_all_silent=true 235 | install 236 | success 'complete! run dotfiles_reload or restart your session for environment changes to take effect' 237 | else 238 | info 'installing dotfiles' 239 | install 240 | success 'complete! use dotfiles_update to keep up to date. run dotfiles_reload or restart your session for environment changes to take effect' 241 | fi 242 | 243 | echo '' 244 | } 245 | 246 | main "$@" 247 | -------------------------------------------------------------------------------- /lib/mas.zsh: -------------------------------------------------------------------------------- 1 | mas_installed="" 2 | 3 | function mas_install_upgrade_formulas() { 4 | mas_install_formulas 5 | mas_upgrade_formulas 6 | } 7 | 8 | function mas_install_formulas() { 9 | mas_files=`dotfiles_find install.mas` 10 | if [ -n "$mas_files" ]; then 11 | mas_check_and_install 12 | mas_installed=$(mas list 2> /dev/null) 13 | for file in $mas_files; do 14 | while read formula; do 15 | mas_install $formula 16 | done < <(grep ^ $file) 17 | done 18 | fi 19 | } 20 | 21 | function mas_upgrade_formulas() { 22 | if type mas > /dev/null; then 23 | outdated=$(mas outdated 2> /dev/null | cut -d ' ' -f2- | cut -d '(' -f1 | cut -d– -f1 | cut -d- -f1 | sed -e 's/ *$//' | sed -e :a -e '$!N; s/\n/, /; ta') 24 | if [ -n "$outdated" ]; then 25 | run "upgrading apps ($outdated)" "mas upgrade" 26 | fi 27 | fi 28 | } 29 | 30 | function mas_install() { 31 | id="${1%% *}" 32 | name="${1#* }" 33 | if ! echo $mas_installed | grep -q "^$id"; then 34 | if mas install $id > /dev/null 2>&1; then 35 | success "installed $name" 36 | else 37 | fail "failed to install $name" 38 | fi 39 | fi 40 | } 41 | 42 | function mas_check_and_install() { 43 | if ! type mas > /dev/null; then 44 | info "mas is not installed, installing" 45 | brew_check_and_install 46 | brew install mas 47 | login_to_Mac_App_Store 48 | fi 49 | } 50 | 51 | function login_to_Mac_App_Store () { 52 | # Attempt at workaround for High Sierra error that prevents logging into Mac App Store 53 | # See Mike Ratcliffe, https://github.com/mas-cli/mas/issues/107#issuecomment-335514316 54 | # Test if signed in. If not, launch MAS and sign in. 55 | 56 | until (mas account > /dev/null); # If signed in, drop to outer "done" 57 | do 58 | # If here, not logged in 59 | echo -e "You are not yet logged into the Mac App Store." 60 | echo -e "I will launch the Mac App Store now." 61 | echo -e "\nPlease log in to the Mac App Store..." 62 | open -a "/Applications/App Store.app" 63 | 64 | # until loop waits patiently until scriptrunner signs into Mac App Store 65 | until (mas account > /dev/null); 66 | do 67 | sleep 3 68 | echo -e "… zzz …." 69 | done 70 | done 71 | echo -e "You are signed into the Mac App Store." 72 | signed_in_user=$(mas account) 73 | echo -e "MAS user name: $signed_in_user" 74 | } 75 | -------------------------------------------------------------------------------- /lib/terminal.zsh: -------------------------------------------------------------------------------- 1 | function info() { 2 | printf " [ \033[00;34m..\033[0m ] %s\n" "$1" 3 | } 4 | 5 | function user() { 6 | printf "\r [ \033[0;33m??\033[0m ] %s " "$1" 7 | } 8 | 9 | function success() { 10 | printf "\r\033[2K [ \033[00;32mOK\033[0m ] %s\n" "$1" 11 | } 12 | 13 | function warn() { 14 | printf "\r\033[2K [ \033[0;31m!!\033[0m ] %s\n" "$1" 15 | } 16 | 17 | function fail() { 18 | printf "\r\033[2K [ \033[0;31m!!\033[0m ] %s\n" "$1" 19 | echo '' 20 | exit 1 21 | } 22 | 23 | function run() { 24 | set +e 25 | info "$1" 26 | output=$(eval $2 2>&1) 27 | if [ $? -ne 0 ]; then 28 | fail "failed to run '$1': $output" 29 | exit 1 30 | fi 31 | set -e 32 | } 33 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DanielThomas/oh-your-dotfiles/e2a16ff21d07b5f08aafe333e5f5bf5968609051/screenshot.png -------------------------------------------------------------------------------- /tools/startlog.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import datetime 3 | import sys 4 | 5 | SLOW_THRESHOLD = 7 6 | 7 | def parse_line(raw_line): 8 | nonewline = raw_line.strip('\n') 9 | timestr, rest = nonewline.split(' ', 1) 10 | return int(timestr), rest 11 | 12 | def main(filename): 13 | with open(filename) as f: 14 | count = 0 15 | start_time, rest = parse_line(f.readline()) 16 | print("0 {line}".format(line=rest)) 17 | 18 | prev_line = rest 19 | prev_line_start = start_time 20 | for line in f.readlines(): 21 | count += 1 22 | if len(line) == 0 or line == "\n": 23 | continue 24 | if not line[0].isdigit(): 25 | continue 26 | 27 | try: 28 | t, rest = parse_line(line) 29 | diff = t - prev_line_start 30 | if diff > SLOW_THRESHOLD: 31 | print("{since_start} {diff} {prev_line}".format( 32 | since_start=t-start_time, diff=diff, prev_line=prev_line)) 33 | prev_line_start = t 34 | prev_line = rest 35 | except ValueError: 36 | continue 37 | 38 | if __name__ == "__main__": 39 | main(sys.argv[1]) 40 | --------------------------------------------------------------------------------