Macの環境をdotfilesでセットアップしてみた改
以前以下のブログで公開したMac用のdotfilesを色々改良してみたので、改良版として本記事で公開する。
なおdotfilesとは、ホームディレクトリに存在するドット(.)から始まる設定ファイル群の事を指す。
dotfiles の中身
$ tree
.
├── .bin
│ ├── .Brewfile
│ ├── .bash_profile
│ ├── .bashrc
│ ├── .gitconfig
│ ├── .gitignore_global
│ ├── brew.sh
│ ├── defaults.sh
│ ├── init.sh
│ └── link.sh
├── .github
│ └── workflows
│ └── main.yml
├── Makefile
├── README.md
├── chrome
│ └── extensions
├── raycast
│ └── Raycast.rayconfig
└── vscode
├── extensions
├── settings.json
└── sync.sh
dotfilesの詳細は、以下のGitHubのURLに上げている。
以下ではセットアップ用スクリプト、及びソフトウェア用設定ファイルに分けて解説する。
セットアップ用スクリプト
本項目では、Macにのdotfilesに格納されているファイルを用いてセットアップする際に使用するスクリプトについて説明する。
セットアップ自動化スクリプト(Makefile)
本dotfilesのルートディレクトリでmake
コマンドを叩く事で、Makefile
に記載のあるスクリプトが全て実行され、セットアップを自動で実施する。
なおmake init
、make link
、make defaults
、make brew
コマンドにより、各々のスクリプト(init.sh
, link.sh
, brew.sh
, defaults.sh
)を個別で実行できる。
初期設定スクリプト(.bin/init.sh)
init.sh
は、Macで一番最初に実行するべき初期設定を実施する。
本dotfilesでは、zshからbashへの切り替え、及びXcodeとbrewのインストールを実行する。
シンボリックリンク作成スクリプト(.bin/link.sh)
link.sh
は、ホームディレクトリに対してドットから始める設定ファイルのシンボリックリンクを作成する。
なお.git
や.github
、.DS_store
といったシンボリックリンクの作成の必要がないファイルについては、あらかじめ除外する。
追加コマンド/アプリ導入スクリプト(.bin/brew.sh)
brew.sh
は、Macに追加で必要なコマンドツールやデスクトップアプリをインストールする。
追加で必要なコマンド及びアプリを.Brewfile
に予め記載した上で、このスクリプトを実行すると一括インストールが可能。
.Brewfile
には、追加したいコマンド及びアプリを一覧で記載する。
Mac 環境設定スクリプト(.bin/defaults.sh)
defaults.sh
は、Mac自体の環境設定を操作する。
テスト自動化スクリプト(.github/workflows/main.yml)
main.yml
には、GitHub Actionsを用いて、Githubリポジトリにpushした際、自動で処理されるテストの内容を記載する。
本dotfilesではMacOSにおけるmake
コマンドのテストのみ実施しているが、他のコマンドを追記してテストする事も可能。
.github/workflows/main.yml
on:
push:
branches:
- main
permissions:
contents: read
jobs:
make:
runs-on: macos-latest
defaults:
run:
shell: bash
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Dotfiles
run: make
ソフトウェア設定ファイル
以下では、ソフトウェアの設定が記載されているファイルについて説明する。
Bash 設定ファイル(.bin/.bash_profile & .bin/.bashrc)
.bash_profile
及び.bashrc
には、ターミナルで使用するBashの設定が記載されている。
.bash_profile
には、主に環境変数を設定するexport
や、補完定義を設定するcomplete
/souce
を記載している。
対して、.bashrc
には主にターミナルで用いるコマンド群を、引数を持たないalias
と、引数を持つfunction
に分けて記載している。
なお.bash_profile
と.bashrc
の使い分け方は好みの問題なので、どちらに記載しても構わない。
.bin/.bashrc
# User specific aliases and functions
# User specific aliases and functions
alias ll='ls -lF'
alias la='ls -lAF'
alias ..='cd ..'
alias ...='cd ../..'
alias ....='cd ../../..'
alias relogin='exec $SHELL -l'
alias checkip='curl inet-ip.info'
alias rmtrash='rm -rf ${HOME}/.Trash/*'
alias delds='find . -name ".DS_Store" -type f -ls -delete'
alias bl='brew list --formula'
alias bo='brew upgrade --dry-run'
alias bg='brew upgrade'
alias cl='brew list --cask'
alias co='brew upgrade --cask --greedy --dry-run'
alias cg='brew upgrade --cask --greedy'
alias ml='mas list'
alias mo='mas outdated'
alias mg='mas upgrade'
alias al='echo "************brew***************"; brew list --formula; echo "************cask***************"; brew list --cask; echo "************mas***************"; mas list'
alias ao='echo "************brew***************"; brew upgrade --dry-run; echo "************cask***************"; brew upgrade --cask --greedy --dry-run; echo "************mas***************"; mas outdated'
alias ag='echo "************brew***************"; brew upgrade; echo "************cask***************"; brew upgrade --cask --greedy; echo "************mas***************"; mas upgrade'
alias bbl='brew bundle list --global --all'
alias bbc='brew bundle check --global --formula'
alias bbi='brew bundle install --global'
alias gb='git branch --all'
alias gbl='git branch'
alias gbr='git branch --remote'
function gbd (){
local LOCAL_BRANCH=$1
git branch -D "${LOCAL_BRANCH}"
}
alias gs='git status'
function gf (){
local REMOTE_BRANCH=${1:-HEAD}
git fetch origin "${REMOTE_BRANCH}"
}
alias gfa='git fetch --all'
function gm (){
local TRAKING_BRANCH=${1:-HEAD}
git merge origin "${TRAKING_BRANCH}"
}
function gpl (){
local REMOTE_BRANCH=${1:-`git branch --contains | cut -d " " -f 2`}
git pull origin "${REMOTE_BRANCH}"
}
alias gpla='git pull --all'
alias gch="git checkout"
alias gcb="git checkout -b"
function gcbo (){
local BRANCH=${1}
git checkout -b ${BRANCH} origin/${BRANCH}
}
function ga (){
local FILE=${1:-.}
git add "${FILE}"
}
alias gax='git reset HEAD'
function gr (){
local FILE=$1
git rm "${FILE}"
}
function gcm (){
local MESSAGE=${1:-fix}
git commit -m "${MESSAGE}"
}
function gcma (){
local MESSAGE=${1:-fix}
git commit -m --amend "${MESSAGE}"
}
alias gcx='git reset --hard HEAD^'
function gps (){
local REMOTE_BRANCH=${1:-HEAD}
git push origin "${REMOTE_BRANCH}"
}
function gss (){
local MESSAGE=$1
git stash save "${MESSAGE}"
}
alias gsu='git stash save -u'
alias gsl='git stash list'
alias gsc='git stash clear'
alias pci='pre-commit install'
alias pcr='pre-commit run -a'
alias pcu='pre-commit autoupdate'
alias av='anyenv versions'
alias au='anyenv update'
alias nrb='npm run build'
alias nrw='npm run watch'
alias nrt='npm run test'
alias nig='npm install -g'
alias nid='npm install -D'
alias ncu='npx -p npm-check-updates -c "ncu"'
alias ncuu='npx -p npm-check-updates -c "ncu -u"'
function eap (){
local PROFILE=${1:-tsukuboshi}
export AWS_PROFILE=${PROFILE}
}
alias enap='export -n AWS_PROFILE'
function awsume (){
local PROFILE=${1:-tsukuboshi}
source $(pyenv which awsume) ${PROFILE}
}
alias asg='aws sts get-caller-identity'
alias asl='aws sso login'
function asl (){
local PROFILE=$1
aws sso login --profile ${PROFILE}
}
function ec2exec (){
local INSTANCE_ID=$1
aws ssm start-session --target ${INSTANCE_ID}
}
function ecsexec (){
local CLUSTER_NAME=$1
local TASK_ID=$2
local CONTAINER_NAME=$3
aws ecs execute-command --cluster ${CLUSTER_NAME} --task ${TASK_ID} --container ${CONTAINER_NAME} --interactive --command "/bin/sh"
}
alias samv='sam validate'
alias samb='sam build'
alias samp='sam deploy'
alias samd='sam destroy'
function cdkin (){
local LANGUAGE=${1:-typescript}
cdk init --language ${LANGUAGE}
}
alias cdks='cdk synthesize'
alias cdki='cdk diff'
alias cdkp='cdk deploy'
alias cdkl='cdk list'
alias cdkd='cdk destroy'
function tin (){
local PROFILE=${1:-tsukuboshi}
aws-vault exec ${PROFILE} -- terraform init
}
function tf (){
local PROFILE=${1:-tsukuboshi}
aws-vault exec ${PROFILE} -- terraform fmt -recursive
}
function tg (){
local PROFILE=${1:-tsukuboshi}
aws-vault exec ${PROFILE} -- terraform get
}
function tc (){
local PROFILE=${1:-tsukuboshi}
aws-vault exec ${PROFILE} -- terraform console
}
function tim (){
local PROFILE=${1:-tsukuboshi}
aws-vault exec ${PROFILE} -- terraform import
}
function tv (){
local PROFILE=${1:-tsukuboshi}
aws-vault exec ${PROFILE} -- terraform validate
}
function tp (){
local PROFILE=${1:-tsukuboshi}
aws-vault exec ${PROFILE} -- terraform plan
}
function ta (){
local PROFILE=${1:-tsukuboshi}
aws-vault exec ${PROFILE} -- terraform apply
}
function ts (){
local PROFILE=${1:-tsukuboshi}
aws-vault exec ${PROFILE} -- terraform state
}
function td (){
local PROFILE=${1:-tsukuboshi}
aws-vault exec ${PROFILE} -- terraform destroy
}
function tdoc (){
local MODULE_PATH=${1:-.}
terraform-docs markdown table --output-file README.md --output-mode inject ${MODULE_PATH}
}
function dib (){
local IMAGE=${1:-itest}
docker image build --platform linux/x86_64 -t ${IMAGE} .
}
alias dil='docker image ls -a'
alias dip='docker image prune'
function dcr (){
local PORT=${1:-80}
local IMAGE=${2:-itest}
local CONTAINER=${3:-ctest}
docker container run --platform linux/x86_64 --name ${CONTAINER} -d -p ${PORT}:80 ${IMAGE}
}
function dce (){
local CONTAINER=${1:-ctest}
docker container exec -it ${CONTAINER} bash
}
function dcs () {
local CONTAINER=${1:-ctest}
docker container stop $(docker container ls -a --filter="name=${CONTAINER}" --format="{{.ID}}")
}
function dcrm () {
local CONTAINER=${1:-ctest}
docker container rm $(docker container ls -a --filter="name=${CONTAINER}" --format="{{.ID}}")
}
alias dcl='docker container ls -a'
alias dcp='docker container prune'
alias dsp='docker system prune --volumes'
alias dcu='docker compose up -d'
alias dcd='docker compose down'
alias gle="ls -l ${HOME}/Library/Application\ Support/Google/Chrome/Default/Extensions | awk '{print \$9}' | sed 's/^/https:\/\/chrome.google.com\/webstore\/detail\//g' | sed -e '1,2d'"
alias cle='code --list-extensions'
function otp (){
local ITEMID=${1:-AWS}
if [ "$(which op)" != "" ]; then
op item get ${ITEMID} --otp
elif [ "$(which bw)" != "" ]; then
bw get totp ${ITEMID}
else
echo "Install the password manager command to get your totp."
fi
}
Git 設定ファイル(.bin/.gitconfig & .bin/.gitignore_global)
.gitconfig
には、Gitの設定が記載されている。
もしEメールがそのまま書かれている場合は、以下を参考にコミットメールアドレスを別で設定すると良い。
.gitignore_global
には、Gitによる追跡から除外するファイルが記載されている。
GitHub公式から、.gitignoreのテンプレートがサービス毎に提供されているので、参考にしながら自分用にカスタマイズすると良さげ。
Chrome 設定ファイル(chrome/配下)
extensions
にはChrome拡張機能のURLが記載されており、どのChrome拡張機能が導入されているかを一目で確認できる。
ちなみに.bashrc
に記載されている以下のコマンドで、現在Chromeに導入されている拡張機能のURLを上記のファイルのように一覧で出力できる。
alias gle="ls -l ${HOME}/Library/Application\ Support/Google/Chrome/Default/Extensions | awk '{print \$9}' | sed 's/^/https:\/\/chrome.google.com\/webstore\/detail\//g' | sed -e '1,2d'"
VSCode 設定ファイル(vscode/配下)
settings.json
にはVSCode自体の設定が記載されている。
対してextension
には、VSCode拡張機能の名称が記載されており、どのvscode拡張機能が導入されているかを一目で確認できる。
そしてsync.sh
を実行する事で、settings.json
のシンボリックリンクの作成、及びextensions
に記載されている拡張機能のインストールを合わせて実施できる。
ちなみに.bashrc
に記載されている以下のコマンドで、現在VSCodeに導入されている拡張機能を上記のextensions
のように一覧で出力できる。
alias cle='code --list-extensions'
Raycast 設定ファイル(raycast/配下)
Raycast.rayconfig
にはRaycastの設定が記載されており、RaycastのImport機能で取り込み可能。
(バイナリファイルのためファイル内容は記載しない)
Importのやり方は以下を参照。
dotfilesを始めよう
万が一自身のPCに何かが起きた際の設定のバックアップになるので、ぜひ簡単なdotfilesを作ってみてほしい。
またスクリプトを作成する事でBashを学べたり、GitHubリポジトリにバックアップとしてアップロードする事でGitの勉強にもなるので、エンジニア初心者がこの辺りの技術を学ぶにはちょうど良い教材な気もする。
(2024/3/28追記)
スクリプトの内容が見やすいよう、可能な限りファイルをGitHubの埋め込みに変更。
Discussion