📂

dotfilesの構成メモ

2024/01/25に公開

dotfilesとは端末環境を構成する設定ファイルをリモートリポジトリに集約管理することで環境の再現性を高めるものであり、その概念に基づいて各個人・グループで運用されているリポジトリの総称。dot(.)という名称を冠してはいるが、設定ファイルが隠しファイルの属性を持っている傾向にあるというだけでdot(.)ファイルであることが本質ではない。端末に設定ファイルをインストールする自前のスクリプトや特定の設定復元に特化したOSSツールとセットで運用することで復元を自動化している例も多く見られる。

それらの先例に倣いつつ、以下のコマンドを仮想端末(ターミナル)から実行すると設定を自動復元するdotfilesのスクリプトと設定ファイルの構成について整理してみた。

bash -c "$(curl -L https://raw.githubusercontent.com/takyshu98/dotfiles/master/install.sh)"

前提

  • macOS、Linux、WindowsのマルチOSを想定した例もあるが、macOS端末専用の構成とする
  • macOS端末にCommand Line Toolsがインストール済みである

スクリプトの構成

個別実行を考慮して設定ドメインを意識した責務分割を行う。
makeコマンドを利用した構成例も見られるが、さほど依存関係は複雑でないため採用は見送る。

install.sh
├──> scripts/defaults.sh
├──> scripts/symlink.sh
├──> brew bundle
└──> mackup restore

install.sh

依存関係の頂点としてHomebrewのインストールとdotfilesのgit cloneおよび各設定ドメイン単位の復元スクリプト(コマンド)をキックする。

#!//bin/bash

set -eu

readonly ARCH_TYPE="$(arch)"
readonly DOTPATH="${HOME}/share/dotfiles"

echo -e "\nInstallation has started...\n"
echo -e "My architecture: ${ARCH_TYPE}\n"

# Install Rosetta 2 for Apple silicon
if [ "${ARCH_TYPE}" = "arm64" ]; then
  softwareupdate --install-rosetta --agree-to-license
  echo
fi

# Install Homebrew
if ! command -v brew >/dev/null 2>&1; then
  /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
  echo
fi

# Export brew path to Homebrew Bundle
if [ "${ARCH_TYPE}" = "i386" ]; then
  export PATH="/usr/local/bin:${PATH}" # for Intel
elif [ "${ARCH_TYPE}" = "arm64" ]; then
  export PATH="/opt/homebrew/bin:${PATH}" # for Apple silicon
fi

# Clone repository
if [ ! -d "${DOTPATH}" ]; then
  git clone https://github.com/takyshu98/dotfiles.git "${DOTPATH}"
else
  echo "Already downloaded: ${DOTPATH}"
  echo "Stash local changes and updating..."
  git -C "${DOTPATH}" stash
  git -C "${DOTPATH}" switch master
  git -C "${DOTPATH}" pull origin master
  echo
fi

# Restore macOS settings
"${DOTPATH}/scripts/defaults.sh"
echo

# Make symbolic links and directories
"${DOTPATH}/scripts/symlink.sh"
echo

# Install command line tools and applications
brew bundle --file "${DOTPATH}/Brewfile"
echo

# Restore application settings
mackup -v restore
echo

echo "Installation completed!"

Homebrewのインストール

Homebrewは端末のチップ(Apple シリコン or Intel プロセッサ)によってPATHの通し方が異なるため制御を入れる。(どちらも所有しているため。将来的にはApple シリコンに統一したい)

dotfilesのgit clone

個人運用かつ日常利用は1台のみなので複数端末間の同期性はそこまで求めないが、形式的にリモートリポジトリのmasterブランチをSSOTとする。(mainブランチへ移行予定)

scripts/defaults.sh

macOSのシステム設定をdefaultsコマンドを利用して復元する。
defaultsコマンドのパラメータは公式には公開されておらず名前空間の見通しも良くないため、細かく設定し過ぎるとメンテンナンス性に不安が残るので必要最小限でスタートする方針を取る。(正直なくても良いかもしれないが操作系は即効性があるので採用した)

#!/bin/bash

set -e

echo "==> Configuring Trackpad options..."

# Change cursor speed faster
defaults write -g com.apple.trackpad.scaling -int 3

# Activate tap to click
defaults write com.apple.AppleMultitouchTrackpad Clicking -bool true
defaults write com.apple.driver.AppleBluetoothMultitouch.trackpad Clicking -bool true
defaults -currentHost write -g com.apple.mouse.tapBehavior -bool true

# Activate three finger drag
defaults write com.apple.AppleMultitouchTrackpad TrackpadThreeFingerDrag -bool true
defaults write com.apple.driver.AppleBluetoothMultitouch.trackpad TrackpadThreeFingerDrag -bool true

# Activate four finger app expose
defaults write com.apple.dock showMissionControlGestureEnabled -bool true
defaults write com.apple.dock showAppExposeGestureEnabled -bool true

echo "==> Configuring Mouse options..."

# Change cursor speed faster
defaults write -g com.apple.mouse.scaling 3

# Change scroll speed faster
defaults write -g com.apple.scrollwheel.scaling 1

echo "==> Configuring Other options..."

# Deactivate auto capitalization
defaults write NSGlobalDomain NSAutomaticCapitalizationEnabled -bool "false"

# Deactivate auto spell correction
defaults write NSGlobalDomain NSAutomaticSpellingCorrectionEnabled -bool "false"

scripts/symlink.sh

${HOME}/share/配下にgit cloneしたdotfiles各設定ファイルへのシンボリックリンク作成および独自ディレクトリの作成を行う。

#!/bin/bash

set -eu

readonly DOTHOME="${HOME}/share/dotfiles/home"

if [ ! -e "${DOTHOME}" ]; then
  echo "Error: Directory does not exist: ${DOTHOME}"
  exit 1
fi

# Make symbolic links from ~/.* to ~/share/dotfiles/home/.*
for file_path in "${DOTHOME}"/.??*; do
  file_name="$(basename "${file_path}")"
  if [[ "${file_name}" =~ ^(\.DS_Store|\.config|\.git|\.github|\.gitignore)$ ]]; then
    continue
  fi
  ln -fvns "${DOTHOME}/${file_name}" "${HOME}/${file_name}"
done

# Make symbolic links based on XDG Base Directory specification
[[ -z "${XDG_CONFIG_HOME}" ]] && XDG_CONFIG_HOME="${HOME}/.config"
[[ -z "${XDG_CACHE_HOME}" ]] && XDG_CACHE_HOME="${HOME}/.cache"
[[ -z "${XDG_DATA_HOME}" ]] && XDG_DATA_HOME="${HOME}/.local/share"
[[ -z "${XDG_STATE_HOME}" ]] && XDG_STATE_HOME="${HOME}/.local/state"

mkdir -p "${XDG_CONFIG_HOME}"
mkdir -p "${XDG_CACHE_HOME}"
mkdir -p "${XDG_DATA_HOME}"
mkdir -p "${XDG_STATE_HOME}"

find "${DOTHOME}/.config" -maxdepth 1 ! -name '.config' ! -name '.DS_Store' -exec ln -fvns {} "${XDG_CONFIG_HOME}" \;

# Make directories with reference to Filesystem Hierarchy Standard
mkdir -p "${HOME}/bin" # for original commands
mkdir -p "${HOME}/src" # for code repositories
mkdir -p "${HOME}/tmp" # for temporary workspace

XDG Base Directory Specification

設定ファイルはデフォルトで${HOME}ディレクトリ直下への配置を定められていることが多く、煩雑になる傾向にある。XDG Base Directory Specificationに対応したツールの場合は、${XDG_CONFIG_HOME}(≒ ${HOME}/.config)直下のツール名称に準拠したディレクトリまたはファイル名でそれぞれ配置することができるため見通しが良くなる。

Filesystem Hierarchy Standard

コードリポジトリの格納先をはじめとして、macOSデフォルトのディレクトリ構成ではリソースの収まりの悪さを感じるため、Filesystem Hierarchy Standardに一定の根拠を求めて運用する。

ディレクトリ名 用途
${HOME}/bin 独自ツールの格納先
${HOME}/src コードリポジトリの格納先

brew bundle

AppおよびCLIをHomebrew Bundleを利用してBrewfileからインストールすることで復元する。
App StoreからDLする必要があるAppについてはmas-cliを利用する。
機械的なメンテナンス性は低下するがbrew bundle dump の出力結果にコメントを追記することで用途を一定備忘する運用とする。

Brewfileは記載省略

mackup restore

エクスポート・インポートに手動操作が絡むAppの設定をmackupを利用して復元する。
設定はiCloud等のクラウドストレージに保存することも可能であるが、dotfilesで一元管理したいためengine = file_systemを選択してdotfiles内にエクスポートするようにしている。
当初はiTerm2の設定復元を主な導入目的としていたが、XDGで設定管理できるWezTermに乗り換えたため、それほど必要ではなくなった。

# @file ~/.mackup.cfg
# ref:
# https://github.com/lra/mackup/tree/master/doc

[storage]
engine = file_system
path = share/dotfiles
directory = home

[applications_to_sync]
# iTerm2
karabiner-elements

設定ファイルの構成

dotfiles/homeディレクトリを端末の${HOME}ディレクトリと対称関係に位置付けることで設定ファイルと直接関係のないインストール用途のファイル群との区別を明確にする。

dotfiles
└── home
   ├── .config
   │  ├── xxx   
   │  └── zsh
   │     └── .zshrc
   ├── .xxx
   └── .zshenv

ログインシェル(zsh)の設定

前述の${ZDOTIDIR}やプラグイン管理で分割が発生するため個別に整理する。
ログインシェルに求める設定をおおまかにカテゴライズし、ライフサイクルを考慮したうえでどの設定ファイルに定義を振り分けるかを記載した。
プラグイン管理にはXDG Base Directory Specificationに対応しており、かつ起動が高速であると評判のsheldonを採用した。

設定カテゴリ 設定ファイル プラグイン名
ロケール ${HOME}/.zshenv -
PATH ${HOME}/.zshenv -
プラグイン管理アクティベート ${HOME}/.config/zsh/.zshrc -
入力補完 ${HOME}/.config/zsh/.zshrc -
↑↑↑ ${HOME}/.config/sheldon/plugins.toml zsh-users/zsh-completions
↑↑↑ ${HOME}/.config/sheldon/plugins.toml zsh-users/zsh-syntax-highlighting
↑↑↑ ${HOME}/.config/sheldon/plugins.toml zsh-users/zsh-autosuggestions
ディレクトリ移動補助 ${HOME}/.config/sheldon/plugins.toml b4b4r07/enhancd
エイリアス ${HOME}/.config/sheldon/plugins.toml olets/zsh-abbr
プロンプト ${HOME}/.config/sheldon/plugins.toml starship
コマンド履歴管理 ${HOME}/.config/zsh/.zshrc -
仮想環境アクティベート ${HOME}/.config/zsh/.zshrc -

全体の構成

総合するとdotfiles全体の構成は以下の通りとなった。

dotfiles
├── home
│  ├── .config
│  │  ├── git
│  │  │  └── config
│  │  ├── karabiner
│  │  │  ├── assets
│  │  │  ├── automatic_backups
│  │  │  └── karabiner.json
│  │  ├── mise
│  │  │  ├── config.toml
│  │  │  └── settings.toml
│  │  ├── sheldon
│  │  │  └── plugins.toml
│  │  ├── starship.toml
│  │  ├── tmux
│  │  │  └── tmux.conf
│  │  ├── wezterm
│  │  │  └── wezterm.lua
│  │  └── zsh
│  │     └── .zshrc
│  ├── .mackup.cfg
│  └── .zshenv
│
├── install.sh
├── scripts
│  ├── defaults.sh
│  └── symlink.sh
└── Brewfile

Discussion