Redmine のプロジェクトを GitLab Issues へ移行する

この投稿は [Fujitsu Advent Calendar 2017](http://qiita.com/advent-calendar/2017/fujitsu) と [GitLab Advent Calendar 2017](http://qiita.com/advent-calendar/2017/gitlab) の 10日目 の記事です。(大幅遅刻です。ごめんなさい、ごめんなさい、ごめんなさい。)
この記事に書かれた見解は、個人のものであり、所属する会社・組織を代表するものではありません。

---

前フリを書いたら長くなってしまったので、それはばっさり後にまわして、いきなり本題からいきます。

# migrate-redmine-to-gitlab
[migrate-redmine-to-gitlab](https://github.com/ultreia-io/migrate-redmine-to-gitlab)を使います。

## どれくらいちゃんと移行できるのか
[公式ドキュメント](https://ultreia-io.github.io/migrate-redmine-to-gitlab/) の記述に、実際にやった結果を補足しながらまとめてみます。

「移行可否」欄の凡例:

- ◎:問題なく移行できる。
- ○:移行できるが若干の問題あり。
- △:公式ドキュメントには移行できると書いてあるがやってみたらだめだった。
- ×:移行できない。

|Redmine                  |GitLab       |移行可否|○または△の詳細                                |
|-------------------------|---------------|:--:|--------------------------------------------------|
|**チケット(issue)**      |**Issue**      |    |                                                  |
|　説明                   |Description    |○  |TextileがMarkdownに変換されない(後述)             |
|　著者                   |Opened by ~    |△  |移行を実行したユーザー(Access Tokenの持ち主)になる|
|　追加日/終了日          |               |○  |メタデータではなくIssue本文に挿入される<br/>例:(from redmine issue 1 created on 2017-12-10, closed on 2017-12-10)|
|　コメント               |Comment        |○  |TextileがMarkdownに変換されない(後述)             |
|　コメントの著者         |~ Commented    |△  |移行を実行したユーザー(Access Tokenの持ち主)になる|
|　コメント日             |               |○  |メタデータではなくComment本文に挿入される<br/>例:(from redmine: written on 2017-12-12)|
|　トラッカー             |Label          |◎  |                                                  |
|　優先度                 |Label          |◎  |                                                  |
|　ステータス             |Open/Closed    |○  |open(未完了)かclosed(完了)かのみ                  |
|　担当者                 |Assignee       |◎  |                                                  |
|　チケット番号           |Issue ID       |◎  |                                                  |
|　関連チケット           |Related Issue  |◎? |Related Issue は GitLab EE の機能のため確認できず |
|　添付ファイル           |Files          |◎  |                                                  |
|　ロードマップ/バージョン|Milestone      |◎  |                                                  |
|　ウォッチャー           |Subscribe      |×  |                                                  |
|**ユーザー/グループ/ロール**|**User/Group/Role**|×  |予め GitLab 側に手で作成が必要                    |
|**リポジトリ**           |**Repository** |×  |                                                  |
|**Wiki**                 |**Wiki**       |×  |                                                  |
|**タグ**                 |**Label**      |×  |                                                  |

## 実行環境
今回はお試しなので、Redmine と GitLab はこれ用に Docker でさくっと立てました。

- [migrate-redmine-to-gitlab](https://github.com/ultreia-io/migrate-redmine-to-gitlab) v2.0.2
- Python 3.6 (3.5以上)
- Redmine 3.4.2: [sameersbn/redmine](https://hub.docker.com/r/sameersbn/redmine/)
- GitLab CE 10.2.4: [sameersbn/gitlab](https://hub.docker.com/r/sameersbn/gitlab/)
- Docker 17.05.0-ce
- CentOS 7.4

# 手順
[公式ドキュメント](https://ultreia-io.github.io/migrate-redmine-to-gitlab/)通りやれば OK なのですが、ハマりどころ、注意点などを補足しながら説明します。

## 1. 移行先の GitLab の準備
1. プロジェクトを作る  
  Redmine のプロジェクトと同じ名前でなくてもかまいません。
1. ユーザーを登録してプロジェクトのメンバーに追加する  
  [issue を作成する権限を持っていれば](https://docs.gitlab.com/ce/user/permissions.html)、プロジェクトが属するグループのメンバーでも、プロジェクトのメンバーでもかまいません。  
  少なくとも、移行するチケットの担当者になっているユーザーは全員必要です。  
  公式ドキュメントには「GitLab 側に対応するユーザーが見つからない場合は、admin ユーザー (root?) が担当者になります」と書いてありますが、実際には `ERROR: Required users presence... FAILED` というエラーになりました。  
  GitLab の `Username` が Redmine の `login` (ログインID) と同じである必要はありませんが、同じにしておいた方が後で楽です。  

**重要**: GitLab プロジェクトには、Milestone も Issue も無いこと。

## 2. Redmine to Gitlab migrator のインストール

```console
$ sudo pip install migrate-redmine-to-gitlab
```

## 3. config.json を書く

```config.json
{
  "redmine": { "host":"https://forge.codelutin.com", "path":"projects/observe", "key": "XXX" },
  "gitlab": { "host":"https://gitlab.com", "path":"ultreia.io/ird-observe", "key": "XXX" }
}
```

### Redmine の key
個人設定画面の右側のサイドバーになる「APIアクセスキー」です。
可能なら、システム管理者権限を持ったユーザーのキーを使った方が後で楽です。

![redmine-get-key.png](https://qiita-image-store.s3.amazonaws.com/0/152517/702c85dd-f7cb-2404-5616-a4ba4e3e5ff5.png)

ここに「APIアクセスキー」が出ないときは、[システム管理者の「管理」画面の、「設定」の「API」タブで「RESTによるWebサービスを有効にする」をONにします](http://redmine.jp/glossary/r/rest-api/)。

### GitLab の key
「User Settings」の「Access Tokens」で「Personal Access Token」を生成します。
![gitlab-token-before.png](https://qiita-image-store.s3.amazonaws.com/0/152517/e21ff6f7-e388-647f-8fc5-f9adcec2ed19.png)

**生成したトークンは生成直後の画面にしか表示されません**ので、忘れずに控えておきましょう。
![gitlab-token-after.png](https://qiita-image-store.s3.amazonaws.com/0/152517/7b31e1ca-f57a-c0a9-cdc1-b90a665815fe.png)

## 4. 初期化

```console
$ migrate-redmine-to-gitlab init
INFO: Create cache dir: ./redmine
INFO: Init <migrate_redmine_to_gitlab.commands.Init object at 0x7fee....7860>
INFO: Got redmine project: 1
INFO: Go gitlab project 1
INFO: Redmine Cache dir: ./redmine
INFO: Project file project to ./redmine/project.json
INFO: Run <migrate_redmine_to_gitlab.commands.Init object at 0x7fee....7860>
:
INFO: End <migrate_redmine_to_gitlab.commands.Init object at 0x7fee....7860>
```

この段階で、Redmine 側の情報を全て持ってくるようです。
完了すると、こういうディレクトリ構造ができていると思います。

```
.
├── config.json
└── redmine
    ├── attachments
    ├── issues
    ├── project.json
    ├── users
    └── versions
```

前述の「Textile が Markdown に変換されない」問題は、この段階で [Pandoc](https://pandoc.org/) 等で変換しておくことができそうです。
今回はそこまでやりませんでした。そのうちやって、続編を書きたい。

## 5. ユーザーの対応づけ
`redmine/users` に `1.json`, `2.json` のようなファイルが Redmine のユーザーの数だけ入っています。
これをひとつひとつ編集して、`login` (ログインID) の値を、対応する GitLab の `Username` に書き換えます。
前述のように、

- `config.json` に設定する Redmine の「APIアクセスキー」をシステム管理者権限を持ったユーザーの物にしておくと、全ユーザの `login` が取得できているはずです。一般ユーザーの物だとそのユーザー以外の `login` が取得できないので、全部手で入力しないといけません。
- Redmine の `login` と GitLabの `Username` を同じにしておけば、この書き換えは必要ありません。ただし、最低限 `admin` を `root` に変える必要はあるでしょう。

```5.json
{
	"id": 5,
	"login": "jeffi7",
	"firstname": "jeff",
	"lastname": "inoue",
	"mail": "jeffi7@example.com",
	"created_on": "2017-12-12T08:05:34Z",
	"last_login_on": "2017-12-12T09:45:48Z",
	"api_key": "d9617fd..............7608eeb",
	"status": 1
}
```
上記は見やすくするために整形しましたが、実際にはインラインで入っています。

ここで不足があると、後のチケットの移行のときに `ERROR: Required users presence... FAILED` というエラーになります。

## 6. ロードマップ(バージョン) の移行
Redmine のロードマップ(バージョン) を GitLab の Milestone に移行します。

```console
$ migrate-redmine-to-gitlab roadmap
INFO: Init <migrate_redmine_to_gitlab.commands.Versions object at 0x7fe09a748860>
:
INFO: 2 version(s) created on GitLab
INFO: End <migrate_redmine_to_gitlab.commands.Versions object at 0x7fe09a748860>
```

## 7. 添付ファイルの移行

```console
$ migrate-redmine-to-gitlab attachments
INFO: Init <migrate_redmine_to_gitlab.commands.Attachments object at 0x7fa3b3244828>
:
INFO: 1 attachments(s) created on GitLab
INFO: End <migrate_redmine_to_gitlab.commands.Attachments object at 0x7fa3b3244828>
```

## 8. チケットの移行
Redmine のチケットを GitLab の Issue に移行します。
タイトルに Redmine のチケットID を付けない方法と、付ける方法とがあります。
チケットID を付けておくと、次のステップで GitLab の Issue ID を Redmine の チケットID と同じにすることができます。

### チケットIDを付けない方法

```console
$ migrate-redmine-to-gitlab issues
:
INFO: 5 issue(s) created on GitLab
INFO: End <migrate_redmine_to_gitlab.commands.Issues object at 0x7fe20289c828>
```

### チケットIDを付ける方法

```console
$ migrate-redmine-to-gitlab issues-with-id
:
INFO: 5 issue(s) created on GitLab
INFO: End <migrate_redmine_to_gitlab.commands.IssuesWithId object at 0x7fe20289c828>
```

## 9. チケットIDの移行
前述のように、タイトルに Redmine のチケットID を付けて Issue を移行しておくと、ID を Redmine と同じにできます。
ただし、GitLab API ではなく直接 GitLab のデータベースにアクセスして書き換えますので、GitLab が動いているマシン上で `migrate-redmine-to-gitlab` を実行する必要があります。
(今回はそこまでやりませんでした。そのうちやって、続編を(以下同文)

## ここまでで、移行は実質的に完了です。

下記のスクリーンショットでは、移行後に Label に色をつけたり、[優先順位をつけたり](https://docs.gitlab.com/ce/user/project/labels.html#prioritize-labels)してあります。
![gitlab-completed.png](https://qiita-image-store.s3.amazonaws.com/0/152517/858fc789-5a61-fa04-60af-1983f3ac1777.png)
![gitlab-imported-issue#1.png](https://qiita-image-store.s3.amazonaws.com/0/152517/eafba224-edc1-8473-abaf-58811693a39a.png)

## 10. Redmine から GitLab にリンクを張る
移行元の Redmine をもう使わないなら、以降の処理は必要ないと思います。

### Redmine のバージョンから GitLab Milestone にリンク

```console
$ migrate-redmine-to-gitlab link-roadmap
:
INFO: Link redmine version 1 (Ver1) to gitlab milestone 1
:
INFO: Link redmine version 2 (Ver2) to gitlab milestone 2
:
INFO: End <migrate_redmine_to_gitlab.commands.LinkRedmineRoadmap object at 0x7ff2ba0d8828>
```

こんな風になります。
![redmine-roadmap-after.png](https://qiita-image-store.s3.amazonaws.com/0/152517/193d2589-6029-1196-e7b6-8614590af5c2.png)

### Redmine のチケットから GitLab Milestone にリンク

実行してみたらエラーになってしまったのですが、使わないと思ったので原因は調べていません。
(そのうち...これはやらないかな。)

```console
$ migrate-redmine-to-gitlab link-issues
INFO: Init <migrate_redmine_to_gitlab.commands.LinkRedmineIssue object at 0x7fd680b3e898>
INFO: Got redmine project: 1
INFO: Go gitlab project 1
INFO: Redmine Cache dir: ./redmine
INFO: Project file project to ./redmine/project.json
INFO: Run <migrate_redmine_to_gitlab.commands.LinkRedmineIssue object at 0x7fd680b3e898>
INFO: Start <migrate_redmine_to_gitlab.commands.LinkRedmineIssue object at 0x7fd680b3e898>
INFO: Got 5 issue(s) from redmine.
INFO: Got 5 issue(s) from gitlab.
Traceback (most recent call last):
  File "/usr/bin/migrate-redmine-to-gitlab", line 11, in <module>
    load_entry_point('migrate-redmine-to-gitlab==2.0.2', 'console_scripts', 'migrate-redmine-to-gitlab')()
  File "/usr/lib/python3.6/site-packages/migrate_redmine_to_gitlab/commands.py", line 91, in main
    args.command(config, args).run()
  File "/usr/lib/python3.6/site-packages/migrate_redmine_to_gitlab/commands.py", line 107, in run
    self.execute()
  File "/usr/lib/python3.6/site-packages/migrate_redmine_to_gitlab/commands.py", line 575, in execute
    gitlab_issue = gitlab_issues_index[subject]
KeyError: 'シンプルなチケット'
```

# 前フリに書くはずだった後書き

私のチームでは、社内の開発者向けに、[GitLab で「コードホスティングサービス」](https://www.slideshare.net/inosuke/gitlab-meetup-tokyo-1-lt-gitlab)や、アジャイル開発の現場支援を提供しています。
(詳しく知りたい社内の人は[グローバルコミュニケーション基盤](http://www.fujitsu.com/jp/services/application-services/application-development-integration/global-communication/)で私のプロフィールを見てください。)
[外部イシュートラッカー](https://docs.gitlab.com/ce/integration/external-issue-tracker.html)として Redmine を GitLab と連携させて使っているチームが多いのですが、[Backlogs プラグインの開発が本家で止まって久しい](https://github.com/backlogs/redmine_backlogs)ところへ、最近 GitLab が、

- [タイムトラッキング](https://docs.gitlab.com/ce/workflow/time_tracking.html)
- [イシューボード](https://docs.gitlab.com/ce/user/project/issue_board.html)(カンバンボード)と[優先順位付きラベル](https://docs.gitlab.com/ce/user/project/labels.html#prioritize-labels)
- [バーンダウンチャート](https://docs.gitlab.com/ee/user/project/milestones/burndown_charts.html) (現状 EE のみ。[CEにも搭載予定](https://gitlab.com/gitlab-org/gitlab-ce/issues/33093)。)
- [関連イシュー](https://docs.gitlab.com/ee/user/project/issues/related_issues.html) (EE のみ)

等の機能が追加されるなど、イシュートラッカーとしても Redmine と肩を並べるようになってきたので、これからは GitLab だけで完結させたい機運が高まってきました。できれば Redmine に溜まった過去のチケットも GitLab に移行したいと思いました。

同じ課題を抱えている人が他にもいるに違いないと思ってググってみると見つかるのがオープンソースのすばらしいところ。このふたつが見つかりました。

- [Redmine to Gitlab migrator](https://github.com/oasiswork/redmine-gitlab-migrator)
- [Migrate Redmine to GitLab](https://github.com/ultreia-io/migrate-redmine-to-gitlab)

よく見てみると、前者は約2年前で更新が止まっており、これをフォークして開発を継続しているのが後者のようです。後者の Migrate Redmine to Gitlab を試してみることにしました。

# ToDo

- [ ] Textile から Markdown に変換してみる
- [ ] チケット ID の移行もやってみる
- [ ] 実際の Redmine プロジェクトを移行してみる