別のGitLabインスタンスにユーザの関係性を保持したままプロジェクトをインポートする方法


この記事は [GitLab Advent Calendar 2019](https://qiita.com/advent-calendar/2019/gitlab) の24日目です

# GitLab のプロジェクトお引越し問題

GitLab のお引越しをされたことがある方はご存知かもしれませんが、GitLab の丸ごと移動ではなく、プロジェクトごとの Import/Export による移動は何かと問題が起こるようです。

公式の Import/Export に関するドキュメントは[こちら](https://docs.gitlab.com/ee/user/project/settings/import_export.html)ですが、それでも問題に遭遇する方はいらっしゃるようで、Qiitaでも[@hellfire さん](https://qiita.com/hellfire/items/a56b299d5433309f82db)とか、[@tetsukay さん](https://qiita.com/tetsukay/items/2441d61c62c90bb34282)が記事を書いてらっしゃいますね。

対策として移動元と移動先でバージョンを合わせるなどありますが、それでも問題が残っているようです。最近でもこんな [Issue が修正](https://gitlab.com/gitlab-org/gitlab/issues/32106)されていますが、それでも Import できない大きいプロジェクトが手元にあってどうしたものかと思っています。何か対策をご存じの方は教えて下さい。


# ユーザー情報問題

それはそれとして、無事 Import できるプロジェクトであったとしても「移動元と移動先でアカウントの情報が違うと、ユーザ間の連携が削除されて残念な結果になる」という問題があります。まぁアカウントの情報が違うので「別人」とされて処理されるのは当然なのですが、例えばアカウントのデータソースを変更した（個別登録→LDAP に移行したとか）際にそれを反映しつつなんとかスマートに移行したいという案件はあるのではないかと思います。レアケースでしょうけれど。

# 強引に import してみるとどうなるか

## 一般ユーザで import

Export したユーザを何も考えずに一般ユーザーで import すると[このような表示](https://gitlab.com/gitlab-org/gitlab-foss/issues/58066)になってしまい、issue や MergeRequest でユーザ間連携が崩れるような問題（不在ユーザ処理と呼ぶことにします）が発生します。

* import した人以外の発言した全てのコメントのオーナーが、import したユーザに置き換わる（アイコンも置き換わる）
* そのコメントの末尾に「By ～ (imported from GitLab project)」が全てのコメントに付く（「～」は元の発言者のユーザー名が入る）
* ユーザ間のリンクが全て消える（普通のテキストになる）

リンク先にもある通り、「import した人以外の発言が書き換わる」問題は admin ユーザで import することで解決できます。これはセキュリティの問題に起因する仕様であり、これ自体は致し方ないかなと思うのですが...

## admin で import

しかしながら、admin で import してセキュリティ上の問題がなかったとしても、移動先と元とでユーザの情報が違っていると同じ現象が発生します。GitLab は Import 時にある程度 import するプロジェクトの情報と、import 先の GitLab 上でマッチングするユーザを検索して見つけてくれるのですが、それに失敗すると上記の「不在ユーザ処理」がなされるようです。

ユーザのデータソースが同じであればそれでも良いのですが、そうでない場合にはどうしても「不在ユーザ処理」が入ってしまい、わざわざ Issue や MergeRequest を移行する意味がなくなってしまいます。

# 雑なツールを作ったよ

そんなわけで、export したデータを書き換えるツールを雑に作ってみました。いくつかの条件はありますが、移行先と移行元とでユーザ情報が違う場合でもこれを通したデータを使うと元のまま import できるようになります。現時点で 12.4 と 12.5 でしか確認していませんが、大きくフォーマットが変わらない限りは使えるのではないかという気がします。

https://gitlab.com/kilaebi/exported-json-user-rewriter

## 必要なもの

* 上記のリポジトリにある gitlab-exported-json-user-rewriter.sh
* export した GitLab プロジェクトのアーカイブファイル (tar.gz)
   * Settings -> General -> Advanced -> Export project メニューから生成できます
   * ``YYYY-MM-DD_HH-MM-SSS_[PJ_NAME].tar.gz`` のようなファイル名です
* jq (ver 1.5.1 でしか動作確認していません)
* bash
* user.tsv (下の方の書き方を参照してください)
   * カレントディレクトリに配置してください

## 使い方

README.md を見ればすぐわかりますが、

``gitlab-exported-json-user-rewriter.sh [options] [target] [target] ...``

* Target:
    * GitLab から export したプロジェクトアーカイブファイルへの相対パス
* Options:
    * --debug, -d       デバッグモード (中間生成ファイルを残します)
    * --help, -h        ヘルプ表示

成功するとユーザ情報が書き換えられた新しいプロジェクトアーカイブファイル（～_rewrited.tar.gz）が生成されます。

# user.tsv

新旧 GitLab 間のユーザマッピングファイルです。以下のようなタブ区切りのtsvフォーマットで作成してカレントディレクトリに配置してください。
ファイル名は「user.tsv」固定です。

## フォーマット

``[Full Name(移行元)]<TAB>[Full Name(移行先)]<TAB>[Email(移行元)]<TAB>[Email(移行先)]<TAB>[User Name(移行元)]<TAB>[User Name(移行先)]``

* 1行1ユーザで記述します
* 空行と「#」で始まる行は無視されます

## 記述例

```tsv
# --------------------------
# Format:
# ---
# [Full Name(src)]<TAB>[Full Name(dst)]<TAB>[Email(src)]<TAB>[Email(dst)]<TAB>[User Name(src)]<TAB>[User Name(dst)]
# --------------------------

# A Group Member
John Smith   JohnSmith@example.com JohnSmith@example.com JohnSmith@example.com John Smith John.Smith
Mary William  MaryWilliam@example.com MaryWilliam@example.com MaryWilliam@example.com Mary William Mary.William

# B Group Member
Fred Bloggs  FredBloggs@example.com FredBloggs@example.com FredBloggs@example.com Fred Bloggs Fred.Bloggs
```

# インポートの仕方

上記のツールで生成した新しいアーカイブファイルを新しい GitLab 上で import してください。ただし、移行先の GitLab に対して以下の条件があります。これに一致しないユーザの情報には「不在ユーザ処理」が入りますのでご注意ください。

* admin ユーザで import すること
    * 一般ユーザの場合には自分以外の発言には全て「不在ユーザ処理」が入ります
* import する project に含まれる、user.tsv で定義されたユーザが、全て移行先に存在する（アカウントが作成されている）こと
    * 存在しない場合には「不在ユーザ処理」が入ります

# おわりに

今後、同一フォルダ内のアーカイブのtsv一括処理を実装しようと思っています。
なにか不具合などあれば教えていただけると嬉しいです。

# 参考記事

* [公式 Import/Export 方法](https://docs.gitlab.com/ee/user/project/settings/import_export.html)
* [GitLabからGitLabへのプロジェクトexport/import時にハマった話](https://qiita.com/hellfire/items/a56b299d5433309f82db)
* [続：GitLabのプロジェクト import に苦労した話](https://qiita.com/tetsukay/items/2441d61c62c90bb34282)
* [Import error: "Validation failed: Notes is invalid"](https://gitlab.com/gitlab-org/gitlab/issues/32106)
* [Imported projects do not maintain ownership of issues and merge requests](https://gitlab.com/gitlab-org/gitlab-foss/issues/58066)