Posted at

GitPythonでSSH鍵を指定してGithubにアクセスする

More than 3 years have passed since last update.

プログラム内からGithub上のリポジトリにアクセスする必要がでたため、GitPythonを使用した。

大部分はチュートリアルを見て解決できたが、SSH鍵を指定しての操作で少し手間取った箇所があるため記載する。


GitでSSH鍵を指定する

今回取り組んだケースでは、まず以下の前提・要求が存在する。


  • 2つのGithubアカウントを持っている。以降、それぞれをアカウントA、アカウントBと表記する。

  • それぞれのアカウントに異なる鍵が紐付いている。以降、アカウントAの鍵を~/.ssh/id_rsaが、アカウントBの鍵を~/.ssh/id_rsa_newとする。

  • プログラム内でアカウントBのリポジトリをClone、幾つかの変更を加えた後にPushする。なおPublicリポジトリであるため、Cloneは誰でもできる。

この場合、環境に依るので一概には言えないが、アカウントAの持つリポジトリへのアクセスは問題ないことが多い。アカウントAはデフォルトのSSH鍵(~/.ssh/id_rsa)に紐付いており、Gitはデフォルトでこの鍵を使用するからだ。

一方、アカウントBを使用する場合、~/.ssh/id_rsa_newを使用することをGitに教えてやる必要がある。そのための一つの方法は、~/.ssh/configに以下のように設定することだ。


~/.ssh/config

host github-new

user git
hostname github.com
identityfile ~/.ssh/id_rsa_new
identitiesonly yes

その後、hostで指定した値をgithub.com(hostnameで指定する)の代わりに使えば、~/.ssh/id_rsa_newを使ってアクセスできる。


~/.ssh/id_rsa_newを使ってcloneする

$ git clone git@github-new:<AccountB>/<repository>.git


自分の環境で動かすだけならばこれで問題ないが、今回のプログラムは自分以外の環境でも動かす必要がある。そのため、設定よりも実行時オプションで指定できた方が良い。

GitはGIT_SSHGIT_SSH_COMMAND(GIT_SSH_COMMANDは2.3以降)という環境変数を持っている。これらはGitが使うSSHコマンドを指定することができる。実行するSSHコマンドに、上の設定ファイルと同じ内容のオプションを指定すれば、問題を解決できる。


GIT_SSH,GIT_SSH_COMMAND

# GIT_SSH

$ GIT_SSH='~/ssh_cmd' git push origin master
$ cat ~/ssh_cmd
ssh -i ~/.ssh/id_rsa_new -oIdentitiesOnly=yes "$@"

# GIT_SSH_COMMAND
$ GIT_SSH_COMMAND='ssh -i ~/.ssh/id_rsa_new -oIdentitiesOnly=yes' git push origin master


(参考)http://git-scm.com/docs/git#_other


GitPythonでSSH鍵を指定する

GitPythonは、GIT_SSHGIT_SSH_COMMANDをサポートするコンテキストマネージャを定義している。

(参考)http://gitpython.readthedocs.org/en/stable/tutorial.html#handling-remotes

実行環境のGitのバージョンが2.3以上であることを前提とできなかったため、今回はGIT_SSHを使用した。

また、SSH鍵の内容を設定に既に持っていたため、鍵とコマンドをファイルに書き出して指定している。


GitPythonでのGIT_SSH,GIT_SSH_COMMAND

import git

# SSH鍵の書き出し
with open('./id_rsa_tmp', 'w') as f:
f.write('<SSH Key>')
os.chmod('./id_rsa_tmp', 0o600)
# SSHコマンドの書き出し
with open('./ssh_cmd', 'w') as f:
f.write('ssh -i ../id_rsa_tmp -oIdentitiesOnly=yes "$@"')
os.chmod('./ssh_cmd', 0o777)

# リポジトリのClone
repo = git.Repo.clone_from('accountB_repo')

# リポジトリのPush
ssh_executable = '../ssh_cmd'
with repo.git.custom_environment(GIT_SSH=ssh_executable):
repo.remote().push('master')


注意したいのが、repo.gitcustom_environment実行時は、リポジトリのワーキングディレクトリにいるということ。

上記のコードでは、Cloneしたリポジトリ(accountB_repo)、鍵(id_rsa_tmp)、コマンド(ssh_cmd)が同一ディレクトリにある。

repo.gitcustom_environment実行時は、accountB_repoディレクトリ以下にいるため、鍵とコマンドは親ディレクトリにあることを意識して指定する。


鍵とコマンドの指定

with open('./ssh_cmd', 'w') as f:

# 鍵は親ディクレトリにある
f.write('ssh -i ../id_rsa_tmp -oIdentitiesOnly=yes "$@"')

# コマンドは親ディレクトリにある
ssh_executable = '../ssh_cmd'
with repo.git.custom_environment(GIT_SSH=ssh_executable):
...