※ この記事は、個人で2つ以上の無料GitHubアカウントを保持することを意図したものではありませんし、私自身無料アカウントを複数持つことはしておりません。無料アカウントを複数持つことは Github の規約違反になりますのでご注意ください(詳細は こちらのコメント にて)。
結論
- git configの
urlの設定を利用して、リポの中では自動的に目的のsshホストが使われるようにする。 -
git cloneが叩かれたことをtrap DEBUGで検知してコマンド実行前にフラグを立て、Githooksのpost-checkout内で、clone先ディレクトリの位置に応じて利用するgitアカウント情報をgitconfigに書き込む。
問題
一つのマシン上で複数のGithubアカウントを使い分けている人がどのくらいいるのかわかりませんが、私は仕事とプライベートのGithubアカウントを分けています。
案件によっては求めに応じてその会社用のアカウントを作ったりします。
ただ、複数持っていても運用がつらくなるのは避けたいです。
- アカウントの切り替えやリポの設定を楽にしたい。全部自動にしたい。
- SSHの設定を覚えてなくてもいいようにしたい。
- 間違えて仕事のリポにプライベートのアカウントでコミットしてしたり、プライベートのリポに仕事のアカウントでコミットしたりするのを防ぎたい。(gitフックを使って職場のレポジトリに趣味のアカウントでcommitするのを防ぐという記事もあるんですが、コミットのときに実行されるのではなく、クローンのときに自動で設定したい)
ということで、
- 複数アカウントの設定のしかた
- 複数アカウントをどうやって楽に運用するか
を書きます。これによって、最初から最後まで(git clone前からpush後まで)アカウントの切り替えを手動でやる必要も、アカウントを意識する必要も無くなりました。何らかのGitクライアントとか ghq みたいなツールでもちょっといじればこういうことができるものはあると思いますが、シェルスクリプトでもある程度できそうだよという記事です。
環境
macOS
sw_vers
ProductName: Mac OS X
ProductVersion: 10.14.5
BuildVersion: 18F132
OpenSSH
ssh -V
OpenSSH_7.9p1, LibreSSL 2.7.3
Git
git --version
git version 2.23.0
仕事用のコードはworkディレクトリ配下、個人用のコードはprivateディレクトリ配下で管理しているとします。
設定のしかた
知っている方は飛ばしてください。
SSHキーを複数用意し、それぞれのGithubアカウントで公開鍵を設定
GithubではSSHキーを使い回せないのでアカウントの数分作ります。Githubじゃなくてもキーの使い回しは避けたほうがいいです。キーが流出しても被害が広がりにくいので。キーの作成方法がわからなければ、タイトルで煽ってるだけのお前らのSSH Keysの作り方は間違っている等を参考に。
~/.ssh/configの設定
Host github_p
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519_github_private
IdentitiesOnly yes
UseKeychain yes
AddKeysToAgent yes
Host github_w
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519_github_work
IdentitiesOnly yes
UseKeychain yes
AddKeysToAgent yes
-
Host
値が一意である必要があるので、それぞれ別の名前にします。 -
HostName,User
ここはいつも通りgithub.comとgitです。 -
IdentityFile
作成した秘密鍵のファイルパスです。適切に変更します。 -
IdentitiesOnly
ssh-agentが複数のアイデンティティを保持しているときでも、ssh_configファイル内で明示的に設定されたアイデンティティのみを使用するかどうかを指定します。まさに今回のようなユースケースのためにある設定項目で、これを忘れると、github_pで接続したいのに、勝手に忖度してgithub_wで接続してしまう、みたいなことが起きます。 -
UseKeychain
macOSのみ設定可能。パスワードやアカウント情報を管理するアプリであるKeychainにパスフレーズを保存するかどうか、そこから探すかどうかを指定します。macOSでない場合や、macOSでも古いバージョンのOpenSSHでは動かないらしいので、そんなオプションないって言われたらUseKeychainの行を消してください。 -
IgnoreUnknown
ちなみに、そんなオプションないと言われるのを避けるためにこの設定の最初のほうに入れて、認識されないオプションを無視するという手もあるのですが、万が一、有効な設定なのにタイポしたために無視されるという危険もあるので、この設定は利用していません。 -
AddKeysToAgent
キーとパスフレーズをssh-agentへ自動的に追加するかどうかを指定します。何度もssh-addするのは面倒くさいのでyesにします。この設定を使うのはやめようみたいな記事を見かけましたが、私はトラブったことはないです。その記事ではホントにこの設定が原因なのか検証されてなかったので、あまり気にしていません。
詳しくはman ssh_configを見てください。
どうやって運用するか
グローバルのユーザ情報を設定しない
git config --global --unset user.name
git config --global --unset user.email
[user]
# nameが設定されていないことを確認
# emailが設定されていないことを確認
[url "github_p"]
# 件のSSHキーに関するURL短縮の設定がされていないことを確認
[url "github_w"]
# 件のSSHキーに関するURL短縮の設定がされていないことを確認
誤ったアカウントで操作したくないので、どのユーザ情報もデフォルトにはしないよう、globalにはユーザ情報は設定しません。git config --localのほうで適切に設定されない限り、git@github.comでアクセスできないようになります。安心。
ローカルで user.name, user.email, url..insteadOfの設定をする
cd 個人の/既存の/リポジトリの/パス
git config --local user.name "個人ユーザ名"
git config --local user.email "個人メールアドレス"
# url."ssh_configで設定したHostの値".insteadOf という形式にします
git config --local url."github_p".insteadOf "git@github.com"
cd 仕事の/既存の/リポジトリの/パス
git config --local user.name "仕事ユーザ名"
git config --local user.email "仕事メールアドレス"
# url."ssh_configで設定したHostの値".insteadOf という形式にします
git config --local url."github_w".insteadOf "git@github.com"
[user]
user = "個人ユーザ名"
email = "個人メールアドレス"
[url "github_p"]
insteadOf = "git@github.com"
すでにクローンされてローカルマシンにあるリポジトリについては、リポごとにユーザ情報を設定します。設定し直す必要があるリポジトリが多かったらスクリプト書いたほうがいいです。
ポイントはurlの設定です。
-
url.<base>.insteadOf
gitコマンドを実行する際、insteadOfで指定した文字列で始まるURLについて、insteadOfで指定した文字列の部分が<base>で指定した文字列に書き換えられた状態で実行されます。
例えば、上記のgithub_pの設定をしてそのディレクトリ内でリモートとやり取りするgitコマンドを実行すると、
git@github.com:個人ユーザ/あるリポジトリ.git->github_p:個人ユーザ/あるリポジトリ.git
というふうに書き換えられて実行されます。git pushとか、明示的にURLを指定しないときもこの設定は有効なので便利です。git@github.com以外にも、任意の文字列を複数登録しておけます。 -
sshCommandという設定項目もあるのですが、SSHの設定は~/.sshにまとめたく、散り散りに管理したくないので使いません。
noreplyメールアドレスの利用(オプショナル)
ちなみに、これはオプショナルな設定ですが、色々な理由で個人のメールアドレスをさらしたくないケースがあると思います。その場合、登録したメールアドレスではなく、Githubから提供されるno-replyメールアドレスを利用することもできます。
Setting -> Emails -> Keep my email addresses privateをチェックするだけです。Primary email addressの説明のところにno-replyメールアドレスが表示されて、利用できるようになります。
参考: https://help.github.com/en/articles/setting-your-commit-email-address
git cloneのSSH設定を自動で切り替える
もうひと押し。
上記のリポごとの.git/configの設定は一度設定してしまえば良いのですが、最初のgit cloneのときにはそもそもローカルにリポがないので設定できません。毎回クローン後にいちいち設定するのも面倒ですし、間違いの元です。
方法1: 単純にgit cloneをラップするコマンドを作って、git colneを使わずにそれを使う
一番簡単な方法ですが、間違ってgit cloneを使ってしまうのが嫌ですし、仮に別のプラグインを入れてクローンしたりするとダメです。
方法2: cdとgitコマンドのラッパー関数を作ってエイリアスとして設定する
そこで以下のGistを作りました。Gist冒頭部のコメントの通りに設定して、rcファイル等に貼り付けてロードし直すと動きます。cdとgitコマンドをエイリアスで上書きしています。直接cdやgitという名前で関数を定義しないのは、上書き前の挙動をさせたいときに\cdや\gitだけでできるからです。
https://gist.github.com/KeyAmam/2cde31a9c2ca4b1af44251c3ac628dec
このGistが何をしてくれるかというと、
-
git clone実行時のSSH設定の自動切り替え。workディレクトリ配下で実行した場合(直下ではなく、配下ならどこでもいい)はgithub_w、privateディレクトリ配下で実行した場合はgithub_p、それ以外のディレクトリでは切り替えなしでcloneするようになります。 -
git clone後の、上記のuser.name・user.email・url.<base>.insteadOfの自動設定 -
--force-urlオプションでgit clone時の自動切り替えOFF。 - GPGキーのキー生成とgitconfigへの自動設定(
git clone実行前に、上記Gistの65行目をアンコメントし、別のGist https://gist.github.com/KeyAmam/74d4aefcd161410bf1487714b829a6ce を~/.scripts/git-set-gpg-key.shに保存しておいた場合)
コードが散らばらないので良いのですが、こちらも別のプラグイン等を入れてcd以外でもディレクトリ移動したりすると具合が悪いので結局方法3にしました。
方法3: GitHooksでgit cloneのみをフックする
記事を書きました: GitHooksでgit cloneだけをフックする
GPGキーの設定(オプショナル)
これはオプショナルな設定ですが、自分のコミットであることの信頼性を高めるためにGPGキーを設定します。基本的に、リポごとではなく、アカウントごとに1つのGPGキーを設定してます。
git clone実行前に、上記Gistの65行目をアンコメントし、別のGist https://gist.github.com/KeyAmam/74d4aefcd161410bf1487714b829a6ce を~/.scripts/git-set-gpg-key.shに保存しておくと、GPGキーの設定が走るようになります。
設定したいリポに行って、git-set-gpg-key.shを単体で実行することもできます。
GPGキー生成時に名前とメールアドレスを聞かれますが、Githubのアカウント名とメールアドレスを入れておきます。本名を聞かれますが本名である必要はありません。
ローカルの設定後、gpg --armor --export メールアドレスやGPGキーIDなどキーを特定できる文字列 | pbcopyでクリップボードにGPG公開鍵をコピーし、GithubのSettings -> SSH and GPG keys -> New GPG Keysで貼り付け保存で完了です。これでコミットするとGithub上でverifiedアイコンが表示されるのが確認できます。
まとめ
まとめると、以下2つの対策によって、複数のGithubアカウントを持っていても、ほとんどアカウントについて意識する必要がなくなりました。気にするのは、どのディレクトリの中でクローンするかだけです。
- git configの
urlの設定を利用して、リポの中では自動的に目的のsshホストが使われるようにする。 - GitHooksでgit cloneのみをフックしてsshホストを自動的に書き込む。