Git
bitbucket
api
backup
バックアップ

bitbucketのリポジトリのバックアップを取得するスクリプトを書いてみた

Bitbucketのgitリポジトリのバックアップを取得するスクリプトを書いてみました。

BitbucketのAPIを呼び出してアカウントに紐づけられているリポジトリの一覧を取得して、git clone --mirrorでリポジトリの複製を行ないます。
過去にリポジトリが複製済みだった場合にはgit fetch --allでリポジトリの内容を更新しています。
※BitbucketのAPIからのレスポンスのJSONを解析するのにjqを使っているのでjqがインストールされている必要があります。

mirror_bitbucket_repos.sh
#!/bin/bash

SCRIPT_DIR=$(cd $(dirname $0); pwd)

BITBUCKET_USER=bitbucket_user@email.address
BITBUCKET_PASSWORD=butbucket.password
BITBUCKET_OWNER=ownername

next_page_url="https://api.bitbucket.org/2.0/repositories/$BITBUCKET_OWNER"
while [ ! -z "$next_page_url" ] && [ "$next_page_url" != "null" ]; do
  bitbucket_response=`curl --user $BITBUCKET_USER:$BITBUCKET_PASSWORD $next_page_url`
  bitbucket_repositories=`echo $bitbucket_response | jq -r '.values[].links.clone[].href' | grep "^git"`
  for repository_path in $bitbucket_repositories; do
    dir=`echo "$(dirname $repository_path)" | sed -e 's/:/\//g' | sed -e 's/^git@//g'`
    if [ ! -d "$dir" ]; then
      mkdir -p "$dir"
    fi
    repo=$(basename $repository_path)
    if [ ! -d "$dir/$repo" ]; then
      (cd $dir; git clone --mirror $repository_path)
    else
      (cd "$dir/$repo"; git fetch --all)
    fi
  done
  next_page_url=`echo $bitbucket_response | jq -r '.next'`
done

JSONの解析にjqを使わずにColonel Richie氏が書かれたシェルスクリプト(sedやawkを利用)したparsrj.shで解析するなら、bitbucket_repositoriesを取得する部分を以下に差し替え

jqを使わずにparsrj.shを使う場合
bitbucket_repositories=`echo $bitbucket_response | $SCRIPT_DIR/parsrj.sh | \
 grep "\$\.values\[[0-9]*\]\.links\.clone\[[0-9*]\]\.href git" | \
 sed -e 's/\$\.values\[[0-9]*\]\.links\.clone\[[0-9*]\]\.href //g'`

その後…

上記シェルスクリプトを使ってBitbucketのリポジトリのミラーを作って、参照系の処理ではミラーを使ってネットトラフィックの削減などを行なっていたのですが、このスクリプトだとオーナーが所有するリポジトリが全部横並びに展開されてしまい使いづらいとの指摘があったので、Bitbucketのレスポンスからリポジトリが所属するプロジェクトの情報も取得してリポジトリを<オーナー>/<プロジェクト>/以下に展開する様に改良を加えたのが以下のスクリプトです。
※Bitbucketのパスワードをスクリプト中に暗号化して埋め込むためにdecrypt_password.shを呼び出しています。
decrypt_password.shについては「シェルスクリプトの中に安全にパスワードを埋め込む」を参照してください

mirror_bitbucket_repos.sh
#!/bin/bash

SCRIPT_DIR=$(cd $(dirname $0); pwd)

. $SCRIPT_DIR/decrypt_password.sh

BITBUCKET_USER=bitbucket_user@email.address
BITBUCKET_PASSWORD=`decrypt_password U2FsDGVkX1+tF5lcBC3+4/ztIYEpVr7jgThH9wYG9QE=`
BITBUCKET_OWNER= ownername

page=1
next_page_url="https://api.bitbucket.org/2.0/repositories/$BITBUCKET_OWNER"
while [ ! -z "$next_page_url" ] && [ "$next_page_url" != "null" ]; do
  bitbucket_response=`curl --user $BITBUCKET_USER:$BITBUCKET_PASSWORD $next_page_url 2> /dev/null`
  num_values=`echo $bitbucket_response | jq -r '.values' | jq length`
  for value_pos in $(seq 0 $((num_values - 1))); do
    repository_info=`echo $bitbucket_response | jq -r ".values[$value_pos]"`
    project_name=`echo $repository_info | jq -r ".project.name"`
    # たぶん、マッチするのは1件だけのはず…
    repository_path=`echo $repository_info | jq -r ".links.clone[].href" | grep "^git"`
    # 常に owner = BITBUCKET_OWNER なのではないか?
    owner=`echo "$(dirname $repository_path)" | sed -e 's/:/\//g' | sed -e 's/^git@//g'`
    # project.name が無いことはあるのか?
    if [ -z "$project_name" ]; then
      dir="$owner"
    else
      dir="$owner/$project_name"
    fi
    if [ ! -d "$dir" ]; then
      mkdir -p "$dir"
    fi
    repo=$(basename $repository_path)
    if [ ! -d "$dir/$repo" ]; then
      (cd $dir; git clone --mirror $repository_path)
    else
      (cd "$dir/$repo"; git fetch --all)
    fi
  done
  next_page_url=`echo $bitbucket_response | jq -r '.next'`
  echo next=$next_page_url
done