複数GitHubアカウント運用方法

こんにちは。

複数GitHubアカウントをディレクトリごとに自動で切り替える運用方法が固まってきたので既出感ありますが、備忘録として残しておきます。

:::note info
文中の`privateusername`などのメールアドレス、ユーザー名等は適宜任意のものに読みかえて下さい。
:::

# 背景
業務時は仕事用GitHubアカウントを使っています。
しかしdotfilesだけ個人アカウントのものを使い回したくて、個人用GitHubアカウントにもfetch/pushできるようにしたかったのでいくつか設定しました。

# どういうことができるようになるか
## 1. ディレクトリに応じた自動アカウント切り替え
設定後、作業ディレクトリが`~/github/`以下であれば個人用のGitHubアカウント（private）、それ以外のディレクトリでは会社用のGitHubアカウント（company）が自動的に使用されます。
これにより、リポジトリごとに手動でアカウントを切り替える必要がなくなります。

##### 実行例: 個人用ディレクトリでの操作
```shell
# 個人用ディレクトリに移動
cd ~/github/my-personal-repo

# ディレクトリでリポジトリを自動判別して適切な認証をする
git add .
git commit -m "個人用ディレクトリでの操作は個人用GitHubアカウントSSHキーと紐づくよう設定"
git push

# リポジトリのクローン
# `git clone git@private:privateusername/my-personal-repo.git`が実行される
gitclone git@github.com:privateusername/my-personal-repo.git
```

## 2. クローン時の自動切り替え
新しくリポジトリをクローンする際も、シェルスクリプトを使用することで、ディレクトリに応じて自動的にホストエイリアスが適用されます。
##### 実行例: 個人用ディレクトリでの新規リポジトリクローン
```shell
# 個人用ディレクトリに移動
cd ~/github/

# リポジトリのクローン
# `git clone git@github.com:privateusername/new-private-repo.git`が実行される
gitclone git@github.com:privateusername/new-private-repo.git
```

# 設定方法
方針としては、まず`~/.gitconfig`でディレクトリごとのホストを設定します。
次に`~/.ssh/config`でホスト名とSSHキーのパスを紐づけます。

GitHubアカウントはSSHキーによって認証されるので、これによってディレクトリごとに異なるGitHubアカウントを使えるようになります。

ちなみにホストとはドメイン名やIPアドレスのことで、今回はデフォルトホスト名が`github.com`ですがSSHキーとの紐づけを切り替えるためにホストエイリアスを使用しています。

## 1. SSH設定の準備
まず、個人用と会社用のSSHキーをそれぞれ生成し、GitHubアカウントに登録します。


既にキーを生成している場合はディレクトリを移動させるだけで大丈夫です。
ドキュメント通りに設定していると`~/.ssh/`配下に`~/.ssh/id_rsa`, `~/.ssh/id_rsa.pub`があると思いますのでそれを移動させてください。

```shell
# 会社用SSHキーの生成
ssh-keygen -t ed25519 -C "your_company_email@example.com" -f ~/.ssh/company

# 個人用SSHキーの生成
ssh-keygen -t ed25519 -C "your_private_email@example.com" -f ~/.ssh/private
```
新規に生成した場合は生成された公開鍵 (~/.ssh/company.pub と ~/.ssh/private.pub) を、それぞれのGitHubアカウントのSSHキー設定にウェブページ上から追加します。

## 2. SSH設定ファイルの編集
次に、`~/.ssh/config` ファイルを編集して、ホストエイリアスを定義します。これにより、特定のエイリアスを使用して異なるSSHキーを自動的に選択できます。

```shell
# ~/.ssh/config

# 会社用GitHubアカウント
Host company
    HostName github.com
    User git
    IdentityFile ~/.ssh/company
    Port 22
    TCPKeepAlive yes
    IdentitiesOnly yes
    AddKeysToAgent yes

# 個人用GitHubアカウント
Host private
    HostName github.com
    User git
    IdentityFile ~/.ssh/private
    Port 22
    TCPKeepAlive yes
    IdentitiesOnly yes
    AddKeysToAgent yes
```
    
## 3. Git設定ファイルの編集
Gitの設定ファイル (`~/.gitconfig`) を編集して、条件付きインクルードを利用し、ディレクトリごとに異なる設定ファイルを読み込むようにします。

```
# ~/.gitconfig

[user]
    name = companyusername
    email = your_company_email@example.com

[credential "https://github.com"]
    useHttpPath = true

[url "git@company:"]
    insteadOf = git@github.com:

[includeIf "gitdir:~/github/**"]
    path = ~/.gitconfig_private
```

個人用Git設定ファイルの作成
`~/.gitconfig_private`ファイルを作成し、個人用の設定を追加します。

```shell
# ~/.gitconfig_private

[user]
    name = privatename
    email = your_private_email@example.com

[url "git@private:"]
    insteadOf = git@github.com:
```

## 4. GitリモートURLの設定
既存のリポジトリのリモートURLを、ホストエイリアスを使用するように変更します。
`git remote -v`した結果が既に下記のようになっていれば不要です。

会社用リポジトリの場合
```shell
cd /path/to/company-repo
git remote set-url origin git@company:companyusername/company-repo.git
```

個人用リポジトリの場合
```shell
cd ~/github/private-repo
git remote set-url origin git@private:privateusername/private-repo.git
```

## 5. SSHエージェントの設定
SSHエージェントにSSHキーを追加します。これにより、パスフレーズの入力を省略できます。
```shell
# SSHエージェントの起動
eval "$(ssh-agent -s)"

# 会社用SSHキーの追加
ssh-add ~/.ssh/company

# 個人用SSHキーの追加
ssh-add ~/.ssh/private

```

# git clone
git cloneコマンドだけは自動で切り替えてくれず、通常GitHubページ上でコピーしたURLは`git@github.com:~~`という形式ですが`git@company:~~`のように書き換えなければならず面倒です。

そこで今回はシェルスクリプトを書いて対応しました。
```.zshrc
function git_clone_auto() {
    local repo_url="$1"
    local host_alias="company"  # デフォルトは 'company'

    # ホストエイリアスの決定
    case "$PWD" in
        "$HOME"/github/**)
            host_alias="private"
            ;;
    esac

    if [[ "$repo_url" == git@github.com:* ]]; then
        local repo_path="${repo_url#git@github.com:}"
        echo "実行コマンド: git clone git@${host_alias}:${repo_path}"
        git clone "git@${host_alias}:${repo_path}"
    else
        echo "実行コマンド: git clone $repo_url"
        git clone "$repo_url"
    fi
}

alias gitclone='git_clone_auto'
```

```shell
$ gitclone git@github.com:~~~/~~~~~.git
```

これでコピーしたURLをそのまま使えます。

# 確認
設定が正しく動作しているか確認します。

## 1. SSH接続の確認
それぞれのホストエイリアスを使用して、GitHubとのSSH接続が正しく行われているか確認します。

```shell
# 会社用アカウントの確認
$ ssh -T git@company
Hi companyusername! You've successfully authenticated, but GitHub does not provide shell access.

# 個人用アカウントの確認
$ ssh -T git@private
Hi your-private-username! You've successfully authenticated, but GitHub does not provide shell access.
```
## 2. リモートURLの確認
各リポジトリのリモートURLが正しく設定されているか確認します。
```shell
$ cd /path/to/repository
$ git remote -v
origin  git@company:companyusername/company-repo.git (fetch)
origin  git@company:companyusername/company-repo.git (push)

# または
origin  git@private:privateusername/private-repo.git (fetch)
origin  git@private:privateusername/private-repo.git (push)
```
## 3. git push のテスト
各リポジトリからのgit pushが正しく動作するか確認します。

```shell
cd /path/to/repository
git push origin main
```
エラーが発生せず、正しいアカウントにプッシュされることを確認します。

## 4. gitcloneシェルスクリプト の動作確認
新規リポジトリをクローンする際に、シェルスクリプトが正しくホストエイリアスを適用しているか確認します。

```shell
# 個人用ディレクトリでのクローン
cd ~/github/
gitclone git@github.com:privateusername/new-private-repo.git

# 会社用ディレクトリでのクローン
cd ~/work/
gitclone git@github.com:companyusername/new-company-repo.git
```

# まとめ
そもそも一つのアカウントで運用しろという声も聞こえてきますが、複数アカウント使うならこんな感じでしょう。

もう一つ罠があるとすればGitHubのCLIツール`gh`コマンドは今回対応していないので、`gh`コマンド仕様の際は`gh switch`でアカウントを切り替えて使うようにしています。

それでは✋️