この記事は、Infocom Advent Calendar 2020 15日目の記事です。
qiita.com
2020年の9月くらいからBitbucket Serverの移行をしたときの話です。
まえがき
自分が所属している部署では2014年に自分が導入したBitbucket Serverを使っています。(その前はSubversion)
COVID-19以降は基本在宅勤務になり、制約はあまりないが接続が面倒なSSL-VPNから接続を意識しないブラウザベースのPre App VPN主体へと切り替わり、CloneやPush/Pull出来ないオンプレのGitサーバが使いにくいと思う事が増えてきてました。
また、20年以上その時々に所属する部署の開発環境を整備してきたのだけど、いつまでも自分がやれないこともあるし、残してきたシステムが古くなるさまを見ると、マネージドなサービスに移行すべきなんだろうなと考えていたりして、移行に踏み切れたのかもしれないですね。
移行直後に、AtlassianからオンプレミスのServer製品EOLが発表されたのでタイミングとしては良かったのかなと思ってます。
事前調査
中身はGitなのでリポジトリが移行出来るというのはわかってました。これはbitbucket.orgじゃなくて別サービス、例えばgithub.comでも一緒です。
Pull Requestは移行できないというのが公式サイトにも書いてあったのでそこは考えた末に、過去分はオンプレで見れるからいいやということにしました。
やってみて、気がついたのがフォーク(fork)でした。今までフォークしたリポジトリ間でPull Requestを出すようなワークフローでした。
が、普通にリポジトリを移行しただけではフォーク関係が切れてしまいました。
これでは今のワークフローが回らなくなるので、フォークを維持しながら移行できるか調べたら、なんとかなりました。
支払いはドル建てのクレジットカード払いになるということはわかっていたので、そこは社内で精算方法を確認しておきました。
移行
大まかな手順はこんな感じです。
git clone --mirror
で社内リポジトリのミラーを作成
- APIでクラウドにリポジトリ作成
- 社内のミラーに、クラウドリポジトリをremoteとして登録
- 社内ミラーをクラウドリポジトリへpush
フォークがあるともうちょっと複雑になります。
git clone --mirror
で社内リポジトリのミラーを作成
- APIでクラウドにリポジトリ作成
- 社内のミラーに、クラウドリポジトリをremoteとして登録
- 1のforkリポジトリのミラーを作成
- APIでクラウドに2のforkリポジトリ作成
- 社内のforkミラーに、クラウドforkをremoteとして登録
- 社内forkミラーをクラウドforkへpush
- 社内ミラーをクラウドリポジトリへpush
という感じです。
Bitbucket Serverとbitbucket.orgのリポジトリはURLが違います。
Bitbucket Serverは、 https://git.intra.example.com/scm/$PROJECT/$REPO
という構成になっています。
bitbucket.orgは、 https://bitbucket.org/$WORKSPACE/$REPO
です。
概念としてはクラウドにもプロジェクトはありますが、URLで識別するものではなくなっています。
単純なリポジトリの移行ならこんなスクリプトでやってます。
Macでやってますが、デフォルトのbashでは使えない機能を使ってしまったので、homebrewで入れたbashを使っています。
#!/usr/local/bin/bash
リポジトリは --mirror
オプションを付けてクローンしてます。
git clone --mirror https://userid@git.intra.example.com/scm/$ORG_PROJECT/$REPO
クラウドにリポジトリを作るAPIは、割と単純ですが、プロジェクトを指定するために指定するJSONは結構試行錯誤しました。
消してから作るようにしているので、移行後に流すのは厳禁です。
(※実際にはコマンドは改行なしの1行です)
curl -v -X DELETE -u userid:passwd https://api.bitbucket.org/2.0/repositories/$WORKSPACE/${R,,}
curl -v -X POST -u userid:passwd -H "Content-type: application/json"
https://api.bitbucket.org/2.0/repositories/$WORKSPACE/${R,,}
-d {
"scm": "git",
"is_private": true,
"fork_policy": "no_public_forks",
"name": "'$R'",
"project": {
"type": "project",
"name": "'$PROJECT'",
"key": "'${PROJECT^^}'"
}
}
Git-LFSは使っているところが少ないのですが、もれないようにスクリプトの中で発行してしまってます。
git lfs fetch --all
...
git lfs push --all new
remoteにpushするのは --mirror
オプションを付けていますが、 git push --all new && git push --tags
の方がいいのかもしれない?
git push --mirror new
次に移行するリポジトリがあったら調べてみようかな。
以下スクリプトの全文です。
BASE_DIR="/Users/gozu/projects/migration"
WORKSPACE="example_ws"
ORG_PROJECT="Project1"
PROJECT="Project1"
REPOS="repo1 repo2"
rm -rf $PROJECT
mkdir $PROJECT
cd $PROJECT
for R in $REPOS ; do
REPO=$R.git
rm -rf $REPO
git clone --mirror https://userid@git.intra.example.com/scm/$ORG_PROJECT/$REPO
curl -v -X DELETE -u userid:passwd https://api.bitbucket.org/2.0/repositories/$WORKSPACE/${R,,}
curl -v -X POST -u userid:passwd -H "Content-type: application/json" https://api.bitbucket.org/2.0/repositories/$WORKSPACE/${R,,} -d '{"scm":"git", "is_private":true, "fork_policy":"no_public_forks", "name":"'$R'", "project": {"type":"project", "name":"'$PROJECT'", "key":"'${PROJECT^^}'"} }'
cd $REPO
git lfs fetch --all
git remote add --mirror=push new https://userid@bitbucket.org/$WORKSPACE/$REPO
git push --mirror new
git lfs push --all new
cd ..
done
cd $BASE_DIR
developer.atlassian.com
qiita.com
www.seeds-std.co.jp
フォーク
bitbucket.orgのリポジトリはURLにプロジェクトが入らなくなったので、forkして同じリポジトリ名を別プロジェクトに作ることが出来ません。
なので、forkを移行するときはリポジトリ名の後ろに -fork
を付加して移行しました。
https://git.intra.example.com/scm/$PROJECT/$REPO
→ https://bitbucket.org/$WORKSPACE/$REPO-fork
という感じですね。
以下のスクリプトは、repo1, repo2にそれぞれ別プロジェクトでフォークがあった場合です。
forkを含めて空のリポジトリを作ってからpushしてます。
フォークはリポジトリ名に -fork
を付加してAPIで作ってます。
(※実際にはコマンドは改行なしの1行です)
curl -v -X DELETE -u userid:passwd https://api.bitbucket.org/2.0/repositories/$WORKSPACE/${FORK_R,,}-fork
curl -v -X POST -u userid:passwd
https://api.bitbucket.org/2.0/repositories/$WORKSPACE/${FORK_R,,}/forks
-H 'Content-Type: application/json'
-d {
"name": "'$FORK_R'-fork",
"workspace": {
"slug": "$WORKSPACE"
},
"project": {
"type": "project",
"name": "'$FORK_PROJECT'",
"key": "'${FORK_PROJECT^^}'"
}
}
以下スクリプトの全文です。
BASE_DIR="/Users/gozu/projects/migration"
WORKSPACE="example_ws"
ORG_PROJECT="Project1"
PROJECT="Project1"
REPOS="repo1 repo2"
rm -rf $PROJECT
mkdir $PROJECT
cd $PROJECT
for R in $REPOS ; do
REPO=$R.git
rm -rf $REPO
git clone --mirror https://userid@git.intra.example.com/scm/$ORG_PROJECT/$REPO
curl -v -X DELETE -u userid:passwd https://api.bitbucket.org/2.0/repositories/$WORKSPACE/${R,,}
curl -v -X POST -u userid:passwd -H "Content-type: application/json" https://api.bitbucket.org/2.0/repositories/$WORKSPACE/${R,,} -d '{"scm":"git", "is_private":true, "fork_policy":"no_public_forks", "name":"'$R'", "project": {"type":"project", "name":"'$PROJECT'", "key":"'${PROJECT^^}'"} }'
cd $REPO
git lfs fetch --all
git remote add --mirror=push new https://userid@bitbucket.org/$WORKSPACE/$REPO
cd ..
done
cd $BASE_DIR
FORK_PROJECT="ForkProject"
FORK_ORG_PROJECT="fp"
FORK_REPOS="repo1 repo2"
rm -rf $FORK_PROJECT
mkdir $FORK_PROJECT
cd $FORK_PROJECT
for FORK_R in $FORK_REPOS ; do
FORK_REPO=$FORK_R.git
rm -rf $FORK_REPO
git clone --mirror https://userid@git.intra.example.com/scm/$FORK_ORG_PROJECT/$FORK_REPO
curl -v -X DELETE -u userid:passwd https://api.bitbucket.org/2.0/repositories/$WORKSPACE/${FORK_R,,}-fork
curl -v -X POST -u userid:passwd https://api.bitbucket.org/2.0/repositories/$WORKSPACE/${FORK_R,,}/forks -H 'Content-Type: application/json' -d '{ "name": "'$FORK_R'-fork", "workspace": { "slug": "$WORKSPACE" }, "project": {"type":"project", "name":"'$FORK_PROJECT'", "key":"'${FORK_PROJECT^^}'"} }'
cd $FORK_REPO
git lfs fetch --all
git remote add --mirror=push new https://userid@bitbucket.org/$WORKSPACE/$FORK_R-fork.git
git push --mirror new
git lfs push --all new
cd ..
done
cd $BASE_DIR
cd $PROJECT
for R in $REPOS ; do
REPO=$R.git
cd $REPO
git push --mirror new
git lfs push --all new
cd ..
done
developer.atlassian.com
機能差
移行後に気が付きましたが、細かい機能差があります。
どうやらクラウド版とオンプレ版では別製品のようですね。(クラウド版はPython実装で、オンプレ版はJava実装みたい)
パッと気がついたところで以下のような差異があります。だいたいクラウドでは出来ないという感じ・・・。今後に期待。
- Shift-JISが文字化けする
- Pull Requestで旧表示(See the old experience)にすると化けないこともある
.gitattribute
でdiffは直るようだが、ファイル表示では化けてる
- 要作業がない
- 昨日(2020/12/14)、
Request changes
がリリースされた!
- Pull Request作成後、添付ファイルを追加できない
- プロジェクト設定がない
- URLでもわかるようにリポジトリがフラットに管理されているようなので仕方がないか
- APIでまとめて設定した→※後述
- git lfsの容量で課金額が変わる
- Pull Requestの削除がない
- クラウド版のJIRAとの連携は強化されている
- JIRAも同じ時期にCloudへ移行したのでこれは嬉しかった
- SourceTreeがうまく使えない
- 特に社内のProxy環境下で・・・
- なんか意味不明な挙動をするので、早々に諦めて私はForkというアプリに移行しました。VS Codeに移行したメンバーも居るよう
- CI(pipeline)がついた
- オンラインエディターがついてた
- Markdownのレンダリング結果が違う
- 自動マージングがなくなった
- これはすごい不便。git-flowでやってるので、releaseブランチをmasterとdevelopの両方に自動でマージしたいのに・・・
- Addonだけど、Auto Unapprove for Bitbucket Serverに相当するものがない
APIでまとめて設定
プロジェクト設定がなくなったので、リポジトリ毎に設定をしないといけないが1つづやってられない・・・
APIでやりたいことは一応出来た。
とりあえずやったことは、ブランチのアクセス許可の設定です。
masterとdevelopの削除禁止と履歴の書き換え禁止、プルリクエストでマージ(全員)です。(他もやればできそう)
(※実際にはコマンドは改行なしの1行です)
curl -v -X POST -u userid:passwd -H "Content-type: application/json"
https://api.bitbucket.org/2.0/repositories/$WORKSPACE/${R,,}/branch-restrictions
-d '{
"kind": "force",
"pattern": "master",
"branch_match_kind": "glob"
}'
curl -v -X POST -u userid:passwd -H "Content-type: application/json"
https://api.bitbucket.org/2.0/repositories/$WORKSPACE/${R,,}/branch-restrictions
-d '{
"kind": "delete",
"pattern": "master",
"branch_match_kind": "glob"
}'
curl -v -X POST -u userid:passwd -H "Content-type: application/json"
https://api.bitbucket.org/2.0/repositories/$WORKSPACE/${R,,}/branch-restrictions
-d '{
"kind": "push", "user": [],
"pattern": "master",
"branch_match_kind": "glob", "groups": []
}'
全リポジトリに強制的に付与したので、リポジトリの一覧を取得してます。
APIでリポジトリの一覧を取得しているのですが、一回のリクエストに付き最大100個しか取れないので以下のようにしてます。
2ページ取ればよかったのでループにはしてないですね。。。
以下スクリプトの概要です。
REPOS=`curl -v -X GET -u userid:passwd -H "Content-type: application/json" https://api.bitbucket.org/2.0/repositories/$WORKSPACE\?pagelen\=100\&page\=1 | jq -r '.values[].name'`
for R in $REPOS ; do
...
done
REPOS=`curl -v -X GET -u userid:passwd -H "Content-type: application/json" https://api.bitbucket.org/2.0/repositories/$WORKSPACE\?pagelen\=100\&page\=2 | jq -r '.values[].name'`
for R in $REPOS ; do
...
done
developer.atlassian.com
残課題
bitbucket.orgのミラーを社内に残ったBitbucket Serverに作って、1日1回くらい同期させておきたい。
バックアップにもなるし、メンテ落ちのときにソースも見られるし。