この記事は、Infocom Advent Calendar 2020 15日目の記事です。
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
次に移行するリポジトリがあったら調べてみようかな。
以下スクリプトの全文です。
#!/usr/local/bin/bash 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
- 参考
フォーク
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^^}'" } }
以下スクリプトの全文です。
#!/usr/local/bin/bash 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 ← fork作ってからPushする #git lfs push --all new ← fork作ってからPushする 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
- 参考
機能差
移行後に気が付きましたが、細かい機能差があります。
どうやらクラウド版とオンプレ版では別製品のようですね。(クラウド版はPython実装で、オンプレ版はJava実装みたい)
パッと気がついたところで以下のような差異があります。だいたいクラウドでは出来ないという感じ・・・。今後に期待。
- Shift-JISが文字化けする
- Pull Requestで旧表示(See the old experience)にすると化けないこともある
.gitattribute
でdiffは直るようだが、ファイル表示では化けてる
- 要作業がない
- 昨日(2020/12/14)、
Request changes
がリリースされた!
- 昨日(2020/12/14)、
- Pull Request作成後、添付ファイルを追加できない
- Pull Requestを作るときは添付できる
- プロジェクト設定がない
- git lfsの容量で課金額が変わる
- むやみに使わないようにするしか無いかな
- Pull Requestの削除がない
- 却下のみ
- クラウド版のJIRAとの連携は強化されている
- JIRAも同じ時期にCloudへ移行したのでこれは嬉しかった
- SourceTreeがうまく使えない
- 特に社内のProxy環境下で・・・
- なんか意味不明な挙動をするので、早々に諦めて私はForkというアプリに移行しました。VS Codeに移行したメンバーも居るよう
- CI(pipeline)がついた
- まだ使ってない(既存のCIはJenkins)
- オンラインエディターがついてた
- 以前はAddonを買っていたので節約できた
- 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ページ取ればよかったのでループにはしてないですね。。。
以下スクリプトの概要です。
#!/usr/local/bin/bash #--- page 1 --- 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 #--- page 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\=2 | jq -r '.values[].name'` for R in $REPOS ; do ... done
- 参考
残課題
bitbucket.orgのミラーを社内に残ったBitbucket Serverに作って、1日1回くらい同期させておきたい。
バックアップにもなるし、メンテ落ちのときにソースも見られるし。