dotfiles における bootstrap.sh の対応 - M1 MacBook Air インストール覚書(3)

背景

新しいマシンを導入するたびにセットアップに時間をかけるのは大変です。このため、私の場合は、以下のように bootstrap.sh コマンドを実行するだけで環境構築ができるようにしています。

bash -c "$(curl -L https://bitbucket.org/hkob/dotfiles/raw/HEAD/bootstrap.sh)"

今回、M1 マシンが入ってきて、このままではこのスクリプトが利用できません。そこで、M1 MacBook Air をセッティングしながら、dotfiles のリポジトリも綺麗にしていこうと思います。この記事は後から逐次修正していきます。(12/17追記) 実際に実行したところ、かなりの修正がありました。 bootstrap.sh の実行と修正 - M1 MacBook Air インストール覚書(12) - hkob’s blogでインストール記録を書いています。こちらも参照してください。

bootstrap.sh における機種判定の導入

まだ、M1 MacBook Air が来ないので、ネットからわかる情報で判断しています。とりあえず uname -m でアーキテクチャが取得できるようなのですが、Rosetta2 で動作しているターミナルでは、x86_64 が帰ってきてしまうようです。そこで、uname -mの結果とarch -arm64 uname -m の結果を使うことで、Rosetta2 上で実行されたらエラーを出すようにしてみようと思います。Intel Mac の場合には、arm64 アーキテクチャがないので「x86_64-」となります。ネイティブターミナルでは、「arm64-arm64」という感じになります。Rosetta2 の場合だけ「x86_64-arm64」という形になるので、これを判定すれば Rosetta2 の場合だけ例外処理ができると考えています。また、スクリプトの中で M1 かどうかを判断するフラグとして、is_arm という関数を実装しておきます(下の方で使います)。

ここまでを踏まえて前半部分はこんな感じになりました。Intel Mac でこの部分が何もなく通過していることを確認しました。M1 Air が届いたらうまくいくか確認してみます。

#!/usr/bin/env bash

# エラーがあったらそこで即終了、設定していない変数を使ったらエラーにする
set -eu

# is_arm という関数を用意しておく。毎回 uname -m を実行するのは莫迦らしいので、UNAME 環境変数で判断
is_arm() { test "$UNAME" == "arm64"; }

# アーキテクチャ名は UNAME に入れておく
UNAME=`uname -m`

# dotfiles の場所を設定
DOTPATH=$HOME/dotfiles

# Rosetta2 でターミナルを動かしている時には強制終了させる
if [ "$UNAME-$(arch -arm64 uname -m)" == "x86_64-arm64" ]; then
  echo "This script can not exec in Rosetta2 terminal"
  exit 1
fi

(12/17、arm64e という情報が間違っていたので、arm64 に修正)

defaults 系の設定

一回しかやらないことなので、いつも検索しながら設定していました。どうせ一回だけやるならここで設定してしまうことにします。 思いついたものを追記していきます。一つはスクリーンショットの影を外す設定です。 授業資料でスクリーンショットを多用するので、全てのマシンでいつも設定していました。

# スクリーンショットに影をつけないようにする
defaults write com.apple.screencapture disable-shadow -boolean true
killall SystemUIServer

コマンドラインツールの自動インストールおよび Rosetta2 のインストール

これまでコマンドラインツールは事前インストールしていたのですが、これも組み入れてしまおうということで、git コマンドの有無でチェックすることにしました。git がなければ、コマンドラインツールをインストールします。インストールを待つことはできないので、再度実行してもらうように促します。M1 Mac の場合には、Rosetta2 も必要なのでここで一緒にインストールしてしまうことにします。これが同時に実行できるかは、実際にマシンが来てみないとわかりません。

# git が入っていなければ、コマンドラインツールをインストール
if ! command -v git > /dev/null 2>&1; then
  echo "Installing Command line tools ..."
  xcode-select --install
  # その場合、M1 Mac では Rosetta2 もインストールされていないと思われるので、こちらもインストール
  if is_arm; then
    # ソフトウェアアップデートで Rosetta2 をインストール。面倒なのでライセンス確認クリックをスキップ
    echo "Installing Rosetta2 ..."
    /usr/sbin/softwareupdate --install-rosetta --agree-to-license

  fi
  echo "Plaese exec ./bootstrap.sh again in $DOTPATH after installing command-line-tools and Rosetta2(M1 Mac only)."
  exit 1
fi

リポジトリアップデートの限定実行

これまでリポジトリは強制的にリポジトリのものにアップデートするようにしていました。ただ、テストしていてファイルを書き換えている時に git stash されて泣いたことがたくさんあったので、アップデート処理は引数を付けた時のみ実行するようにしました。アップデートしたい時は./bootstrap.sh update とすることで内部を綺麗にした上で、リポジトリを最新版に更新します。

# このフォルダの取り扱い
if [ ! -d "$DOTPATH" ]; then
  # 初回実行時はリポジトリがないので、clone してくる
  echo "Cloning dotfiles.git ..."
  git clone https://hkob@bitbucket.org/hkob/dotfiles.git "$DOTPATH"
else
  # すでにフォルダがある時はそのことを表示
  echo "$DOTPATH already downloaded."
  # 引数に update を付けた場合には、内部をきれいにして pull してくる
  if [ "X$*" == "Xupdate" ]; then
    # 内部で書き換えたものは廃棄して、リポジトリの最新版に差し替える
    echo "Update dotfiles.git ..."
    cd "$DOTPATH"
    git stash
    git checkout master
    git pull origin master
    echo
  fi
fi

dotfiles の展開

これまで、scripts/deploy.sh に分離していましたが、見通しが悪いのでここに列記することにしました。インストールなどで参照する可能性があるので、XDG_CONFIG_HOME は bootstrap.sh 内にも定義していたようです(すっかり忘れていました)。XDG_CONFIG_HOME についてはArchWiki の XDG Base Directoryに詳しく書いてあるようです。

# ここにある dotfiles をホームに展開 (.git, .DS_Store は除外。他に除外するものが増えたらここに追記)
echo "Deploying dotfiles ..."
for file in .??*; do
    [[ "$file" = ".git" ]] && continue
    [[ "$file" = ".DS_Store" ]] && continue
    ln -fvns "$DOTPATH/$file" "$HOME/$file"
done

# どのくらいのアプリが使うかわからないが、XDG_CONFIG_HOME の環境変数は定義しておく
# [[ -z $XDG_CONFIG_HOME ]] && XDG_CONFIG_HOME=$HOME/.config
# mkdir -p "$XDG_CONFIG_HOME"
# echo

(12/17追記) XDG_CONFIG_HOME はデフォルトで $HOME/.config になるので、わざわざ設定する必要はないとのことでした。この部分は削除しました。

homebrew システムのインストール

Homebrew は Intel Mac と Rosetta2 環境のものは /usr/local/bin にインストールされます。Homebrew 本家が紹介しているように M1 native のものは、/opt/homebrew に別途インストールすることにします。今後、基本的にはネイティブ版でインストールし、インストールできなかった場合には Rosetta2 版でインストールすることにします。ここで毎回パスを書くのは面倒なので、Rosetta2 版の brew は brew_x86 という名前で実行できるようにしておきます。このあたりがちゃんと動くかどうかは、Air が届いてから確認します。

# install homebrew
if ! command -v brew > /dev/null 2>&1; then
  # Install homebrew in Intel Mac or M1 Mac on Rosetta2
  echo "Installing homebrew in /usr/local for Intel or Rosetta2 ..."
  arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
  echo
  # M1 Mac では /opt/homebrew にネイティブ版をインストール
  if is_arm; then
    echo "Installing homebrew in /opt/homebrew for Arm ..."
    cd /opt
    sudo mkdir homebrew
    sudo chown $USER:admin homebrew
    curl -L https://github.com/Homebrew/brew/tarball/master | tar xz --strip 1 -C homebrew
    # M1 の場合には、Rosetta2 版の brew を brew_x86 という名前で実行できるようにしておく
    cd /usr/local/bin
    ln -s /usr/local/Homebrew/bin brew_x86
    # この順番でいくと、PATH は 「/opt/homebrew/bin:/usr/local/bin:それ以外」になることを期待している。これは実行してみないとわからない
  fi
fi

(12/17追記: brew_x86 のリンクはオリジナルの /usr/local/Homebrew/bin に張らないと動かなかったため修正しました)

Homebrew 自体のインストール

実際のアプリは brew bundle で一括インストールします。マシンごとにインストールできるものが異なるので以下のフォルダに Brewfile を分けて記述することにしました。

  • arm_brew/Brewfile: M1 native のアプリ
  • rosetta2_brew/Brewfile: Rosetta2 でしか動作しないアプリ
  • intel_brew/Brewfile: Intel Mac のアプリ

まず、現在の Brewfile を intel_brew/Brewfile に移動します。M1 Mac の方ではアプリの検証をした上で arm_brew/Brewfile または rosetta2_brew/Brewfile に振り分けていきます。App Store のアプリは mas、GUI アプリは brew cask でインストールしますが、これは arm_brew/Brewfile の方に記載しておけば良いと思います。これらの振り分けについては、この記事で説明していきます。

# homebrew and cask
if is_arm; then
  echo "brew bundle in Arm native ..."
  cd arm_brew
  brew bundle
  echo "brew bundle in Rosetta2 ..."
  cd ../rosetta2_brew
  brew bundle
  cd ..
else
  echo "brew bundle in Intel ..."
  cd intel_brew
  brew bundle
  cd ..
fi
echo

(12/17追記) コメントが逆になっていたので修正しました。

config の復元

mackup による config は機種依存しないと思うので、そのままにしておきます。

# config の共有の復元
echo "Restoring config files by mackup ..."
mackup restore
echo

MacTeX の環境設定

これまで頑張ってヒラギノフォントを PDF に埋め込むように設定していました。しかし、TeX Live 2020 でデフォルトで設定される「原の味フォント」がかなりよくなっているので、無理しないことにしました。このため、TeX Live をアップデートだけの処理になっています。

# MacTeX (TeX Live) のアップデート
echo "Updating TeX Live library ..."
TLMGR_REPOSITORY=http://ftp.jaist.ac.jp/pub/CTAN/systems/texlive/tlnet

sudo tlmgr option repository $TLMGR_REPOSITORY
sudo tlmgr update --self --all --no-persistent-download

Ruby のインストール

Ruby は rbenv で最新版をインストールします。インストールされていなければ、インストールするようになっています。この時、openssl は homebrew でインストールしたものを使うようにしています。

# Ruby のアップデート
RBENV_VERSION=2.7.2
RUBY_CONFIGURE_OPTS="--with-openssl-dir=$(brew --prefix openssl@1.1)"

now_rbenv=$(rbenv version | awk '{print $1}')
if [ "X${now_rbenv}" != "X${RBENV_VERSION}" ]; then
  echo "Installing ruby ..."
  rbenv install $RBENV_VERSION
  rbenv global $RBENV_VERSION
fi

終了

最後に Bootstrapping DONE が表示されておしまいです。

echo "Bootstrapping DONE!"

さて、これが M1 Mac で動作するかは、届いてからのお楽しみ。

次の記事: Big Sur の環境設定(1段目) - M1 MacBook Air インストール覚書(4) - hkob’s blog 前の記事: 既存環境の環境変更 - M1 MacBook Air インストール覚書(2) - hkob’s blog

hkob.hatenablog.com