やりたいこと
- いろいろな事情によりGitPythonを使って、他のリポジトリをcloneしたい
- そのリポジトリはprivateである
- さらに、そのリポジトリの中にprivateなsubmoduleが存在する
要はprivateにリポジトリだろうがなんだろうが
git clone git@github.com:hoge/fuga.git
git submodule update --init
したいということ
やったこと
とりあえず普通にcloneする
この段階ではまだsubmoduleは空の状態になる。
import git
GITHUB_TOKEN = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
REPO_OWNER = 'hoge'
REPO_NAME = 'fuga'
DIST_PATH = 'piyo' # リポジトリをcloneするときの名前
GIT_CLONE_URL = f'https://{GITHUB_TOKEN}@github.com/{REPO_OWNER}/{REPO_NAME}.git'
# 単にmasterをcloneするだけのとき
cloned_repo = git.Repo.clone_from(
GIT_CLONE_URL,
DIST_PATH)
# branch指定、sshのhost key verification無視設定、
# commit履歴深さ設定などを追加するとこんなかんじ
cloned_repo = git.Repo.clone_from(
GIT_CLONE_URL,
DIST_PATH
env={'GIT_SSH_COMMAND': 'ssh -o "StrictHostKeyChecking=no"'},
branch='poyo',
depth=1)
submoduleがpublicであれば、 clone_from()
の引数に recursive=True
を追加するだけでよい。
gitmoduleの上書き
submoduleのcloneは、具体的には .gitmodules
に記述されたurl
が使われる。
昨今わざわざID/passでgitを利用する人間は稀だと思われるので、以下のように書かれていると思われる。
[submodule "poyo"]
path = piyo
url = git@github.com:hoge/fuga.git
つまり、このurlをtoken付きのhttpsに書き換えてあげることでsubmoduleを取得できるようになる。
clone_from()
でrecursiveオプションを付与しないのは、.gitmodules
の書き換えより先に接続しようとしてしまい、権限不足でエラーが発生するためである。
単純にファイルを開いて置換処理を行い、そして保存すればよい。
# submoduleの取得に用いるURLを、sshからtoken付httpsに書き換える
gitmodules_path = os.path.join(DIST_PATH, '.gitmodules')
with open(gitmodules_path, 'r') as f:
gitmodules = f.read()
gitmodules = gitmodules.replace('git@github.com:', f'https://{GITHUB_TOKEN}@github.com/')
with open(gitmodules_path, 'w') as f:
f.write(gitmodules)
git submodule update --init
する
"gitpython submodule"でググるとわかるが、リリースされて10年くらいの間に実装が二転三転しており(しかも全部生きてるっぽい)、submodule周りの実装がなんか4つくらいある。
動作確認できてシンプルに実現できたやつを紹介しておく。
for submodule in cloned_repo.submodules:
cloned_repo.git.submodule('init', submodule.name)
cloned_repo.git.submodule('update', submodule.name)
うまくゆけばsubmoduleの中身が反映されているはず。
その他躓いたこと
公式のGitPythonリポジトリにはrequirementsとして下記のように書かれている(2021年2月5日現在)。
- Git (1.7.x or newer)
- Python >= 3.4
ただし、実際のところgitコマンドがsshなど他にちょいちょい依存しているので、それらも忘れないようにインストールしなければならない。
特にGitPythonを使うケースというのはdockerコンテナ内などであることが多いと思うが、そういう環境だとapk add openssh
しておくみたいな「基礎的すぎて思いも寄らない箇所」への依存でコケたりするので注意が必要(一敗)。