プログラム内から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
に以下のように設定することだ。
host github-new
user git
hostname github.com
identityfile ~/.ssh/id_rsa_new
identitiesonly yes
その後、host
で指定した値をgithub.com(hostname
で指定する)の代わりに使えば、~/.ssh/id_rsa_new
を使ってアクセスできる。
$ git clone git@github-new:<AccountB>/<repository>.git
自分の環境で動かすだけならばこれで問題ないが、今回のプログラムは自分以外の環境でも動かす必要がある。そのため、設定よりも実行時オプションで指定できた方が良い。
GitはGIT_SSH
、GIT_SSH_COMMAND
(GIT_SSH_COMMAND
は2.3以降)という環境変数を持っている。これらはGitが使うSSHコマンドを指定することができる。実行するSSHコマンドに、上の設定ファイルと同じ内容のオプションを指定すれば、問題を解決できる。
# 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_SSH
、GIT_SSH_COMMAND
をサポートするコンテキストマネージャを定義している。
(参考)http://gitpython.readthedocs.org/en/stable/tutorial.html#handling-remotes
実行環境のGitのバージョンが2.3以上であることを前提とできなかったため、今回はGIT_SSH
を使用した。
また、SSH鍵の内容を設定に既に持っていたため、鍵とコマンドをファイルに書き出して指定している。
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):
...