[全自動]Macの環境構築Tips

この記事は[アラタナアドベントカレンダー2019の14日目](https://qiita.com/advent-calendar/2019/aratana)の記事です。

![https___qiita-image-store.s3.ap-northeast-1.amazonaws.com_0_217773_5ef318e0-bd49-8f89-bafa-50b68dc7ffeb.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107176/98c1de00-fa0e-c188-4bad-81bab7fb7545.jpeg)

# はじめに
修理に出して帰ってくるまでに時間がかかる、という理由だけで数年間無視してきた[バッテリー交換プログラム](https://support.apple.com/ja-jp/13inch-macbookpro-battery-replacement)に、ついに愛用してきたMacを出すことに決めました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107176/81d6d121-4470-cb84-b2e1-35415efb8246.png)


悲しいかな、交換プログラムに出すと内蔵されているデータはすべてなくなってしまいます。
バックアップディスクを使ってデータ復元してもいいけど、良い機会なので出荷状態で帰ってきたキレイなMacに現在使っている開発環境だけを移植することにしました。

そこで、今回交換に出す前のMacの開発環境をできるだけ楽に移植できるシェルを組んでみました。 
[bashの初見殺しっぷりがハンパない件](https://qiita.com/kugyu10/items/5c852e5c1b0f8c98da01)

今回のコードをまとめた[レポジトリ](https://github.com/naoyasugita/dotfiles)はこちら。

# 概要
`シェル(zsh)`, `エディタ(VSCode)`, `GUIアプリ` を1コマンドでインストール(一部設定まで)まで行います。

# 移行前に、まずはzsh関連のファイルを分割
今後、拡張していくことも考えてファイル分割を行いました。

```bash:.zshrc
ZSHHOME="${HOME}/.zsh.d"

if [ -d $ZSHHOME -a -r $ZSHHOME -a \
     -x $ZSHHOME ]; then
    for i in $ZSHHOME/*; do
        [[ ${i##*/} = *.zsh ]] &&
            [ \( -f $i -o -h $i \) -a -r $i ] && . $i
    done
fi
```

`.zshrc`内で`.zsh.d`以下のファイルの中身を呼び出すことでファイル分割を実現しています。
細かいオプションについては[こちら](https://linuxjm.osdn.jp/html/GNU_bash/man1/bash.1.html)を参考に

# 出力の色付
処理結果によって出力される文字列の色を切り替えます。

```bash:lib/echos.sh
#!/bin/bash

readonly RED=$(tput setaf 1)
readonly GREEN=$(tput setaf 2; tput bold)
readonly YELLOW=$(tput setaf 3)
readonly CYAN=$(tput setaf 6)
readonly NORMAL=$(tput sgr0)

function ok() {
  printf "%s\n" "$GREEN$1$NORMAL"
}

function info() {
  printf "%s\n" "$CYAN$1$NORMAL"
}

function warn() {
  printf "%s\n" "$YELLOW$1$NORMAL"
}

function fail() {
  printf "%s\n" "$RED$1$NORMAL"
}
```
出力するとこんな感じ
<img width="347" alt="スクリーンショット 2019-11-29 0.11.41.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107176/d86def8f-0b64-9a73-57b9-3bd19fdc41c6.png">


# 各種インストールと設定

## [brew](https://brew.sh/index_ja)

```bash:setup.sh
#!/bin/bash
set -euo pipefail

function command_exists() {
  type "$1" &> /dev/null ;
}

: "install brew" && {
  if ! command_exists brew; then
    info "installing brew..."
    /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
  else
    warn "brew is already installed"
  fi
}

: "install zsh by brew" && {
  # Catalinaからは標準shellがzshになっているので分岐を用意
  if ! command_exists zsh; then
    info "installing zsh..."
    brew install zsh zsh-completions
    sudo sh -c 'echo $(brew --prefix)/bin/zsh >> /etc/shells'
    chsh -s $(brew --prefix)/bin/zsh
  else
    warn "zsh is already installed"
  fi
}

: "install other packages by brew" && {
  # 使用したいパッケージをスペース区切りで書きます (カンマ区切りにするとエラーになる)
  # 後述で使用するcaskもここでインストール対象とします
  packages=( caskroom/cask/brew-cask jq tree )
  for package in ${packages[@]}; do
    if ! brew list | grep $package &> /dev/null; then
      info "installing ${package}..."
      brew install ${package}
    else
      warn "${package} is already installed"
    fi
  done
  brew cleanup
}
```
##[brew-cask](https://github.com/Homebrew/homebrew-cask)
[homebrew-caskとは](https://qiita.com/swallowtail62/items/61244ea3c7d00f692823)
`brew cask`の詳細については上記の記事を参考にするといいでしょう。

```bash:setup.sh
: "install brew cask" && {
  # 使用したいパッケージをスペース区切りで書きます (カンマ区切りにするとエラーになる)
  packages=( iterm2 visual-studio-code )
  for package in ${packages[@]}; do
    if ! brew cask list | grep $package &> /dev/null; then
      info "installing ${package}..."
      brew cask install ${package}
    else
      warn "${package} is already installed"
    fi
  done
}
```


## [oh-my-zsh](https://github.com/ohmyzsh/ohmyzsh)
```bash:setup.sh
: "install oh-my-zsh" && {
  if [ ! -e $HOME/.oh-my-zsh ]; then
    info "installing oh-my-zsh..."
    curl -L https://raw.github.com/robbyrussell/oh-my-zsh/master/tools/install.sh | sh
    git clone https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions
  else
    warn "oh-my-zsh is already installed"
  fi
  info "installing zsh theme..."
  git clone https://github.com/wesbos/Cobalt2-iterm.git
  cd Cobalt2-iterm && cp cobalt2.zsh-theme ~/.oh-my-zsh/themes/
  cd ../ && rm -rf Cobalt2-iterm
}
```

## VSCode
```bash:setup.sh
: "setting vscode" && {
  info "create symbolic..."
  SCRIPT_DIR=$(cd $(dirname $0) && pwd)
  VSCODE_SETTING_DIR=~/Library/Application\ Support/Code/User

  rm "$VSCODE_SETTING_DIR/settings.json"
  ln -s "$SCRIPT_DIR/settings.json" "${VSCODE_SETTING_DIR}/settings.json"
}
```
# ワンライナーでインストール
このままでは`git`が使えない場合、上記のコードを取得する方法が限られてしまうので、HTTP経由(`curl, wget`)で環境を構築できるようにします。

```bash:install.sh
DOTPATH=~/dotfiles

function command_exists() {
  type "$1" &> /dev/null ;
}

: "install dotfiles" && {
  if command_exists git; then
    git clone "https://github.com/{your_github_name}/dotfiles.git" "$DOTPATH"

  elif command_exists curl || command_exists wget; then
    tarball="https://github.com/{your_github_name}/dotfiles/archive/master.tar.gz"

    if command_exists curl; then
        curl -L "$tarball"

    elif command_exists wget; then
        wget -O - "$tarball"

    fi | tar zxv -C ~/
    mkdir -p ~/dotfiles
    mv -i ~/dotfiles-master/* "$DOTPATH" && rm -rf ~/dotfiles-master
  else
    echo "curl or wget required"
  fi
}

: "deploy and setup" && {
  cd ~/dotfiles
  if [ $? -ne 0 ]; then
    echo "not found: $DOTPATH"
  else
    sh setup.sh
  fi
}
```
自身の環境で使えるコマンド(`git`, `curl`, `wget`)によって処理を分けて、ローカル環境にソースを配置します。
その後は、先程の`setup.sh`を実行するだけです。

# 使い方
```bash:terminal
$ curl -L raw.github.com/{github_name}/dotfiles/master/install.sh | bash
```

# おわりに
先日、修理に出していたPCも無事手元に帰ってきました。
出荷時の状態から1コマンドで以前までの開発環境を再現することができたので満足です！

## 参考
- [dotfilesを整理した](https://1000ch.net/posts/2015/dotfiles.html)
- [優れた dotfiles を設計して、最速で環境構築する話](https://qiita.com/b4b4r07/items/24872cdcbec964ce2178)
- [ymm1x/dotfiles](https://github.com/ymm1x/dotfiles)