TODOコメントを埋もれさせない

# はじめに

古いプロジェクトであればあるほど見つかるものがあります...
そう，**「忘れ去られたTODOコメント」**です...

```golang:main.go
// TODO: 後で共通化する
```

やろうとは思っていたものの工数が取れなかったり，単純に忘れてしまったりした結果，今日を迎えてしまったんでしょう．

そんな悲しいTODOコメントを生み出さないために，TODOコメントからGithubの
issueを自動生成するツールを作成しました．

https://github.com/masibw/gifc

（Githubのissueが作られても埋もれるだろ！という反論は受け付けます）

## 概要

```main.go
// TODO: implement this
```
というTODOコメントがある場合，その.gitに登録されている１つ目のremoteに（多くの場合 origin）このようなissueが生成されます．

![スクリーンショット 2021-12-06 23.17.07.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379424/88ac0c4d-9be6-bdbc-c156-6923b4841dd9.jpeg)

同じTODOコメントで複数回issueを作成しないように，作成後は以下のようなコメントへ置き換えられます．

```
// TODO-#2{https://github.com/masibw/is-test/issues/2}: implement this
```

ちなみに，`.git`の中は探索しないように設定されています．
# 使い方
Go言語で作られていますが，実行は言語を問わず行えます．ただし，現在は`//`から始まる行をコメントと判断しています．
## Github アクセストークンの作成

https://github.com/settings/tokens 

からrepoスコープを持つアクセストークンを作成してください．
![スクリーンショット 2021-12-06 14.59.19.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379424/f7d3365a-6d37-555c-10fe-fe8b3baf0348.jpeg)

## インストール

Goを使う場合配下のコマンドを実行して下さい．

```
go install github.com/masibw/gifc@latest
```

バイナリはreleasesからダウンロードすることもできます．

https://github.com/masibw/gifc/releases

## 実行

issueを自動生成したい`.git`があるディレクトリで以下のコマンドを実行します．

```
GITHUB_TOKEN=your_token gifc
```

# 実装

フローチャートで表すとこんな感じです．
![gifc.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379424/c031e5dd-fd9b-a045-9a90-1acc090a42aa.jpeg)

## .gitからリモートリポジトリの情報を取得する
[go-git](https://github.com/go-git/go-git)を使用して，.gitの情報を取得しています．

```golang:
	repository, err := git.PlainOpen(".git")
	if err != nil {
		err = errors.Wrap(err, "failed to git PlainOpen")
		return nil, err
	}
    remotes, err := repository.Remotes()
	if err != nil {
		return nil, errors.Wrap(err, "failed to get Remotes")
	}
```

## ファイルの走査
ファイルの一部を上書きしたいだけなのに，一旦ファイルの中身を消して全部上書きする形になるのは少し無駄な気もするんですが，どうしようもないんですかね？何か良い方法をおまちの方は教えてください:bow: 
(もちろんPRを出してくれても嬉しいです:eyes:)


```golang:
func (c *CommentUseCase) InspectFile(filePath string) (err error) {
	var file *os.File
	file, err = os.Open(filePath)
	defer func() {
		err = file.Close()
		if err != nil {
			err = errors.Wrap(err, "failed to Close")
		}
	}()

	if err != nil {
		err = errors.Wrap(err, "failed to open file")
		return
	}

	var bs []byte
	buf := bytes.NewBuffer(bs)
	fileScanner := bufio.NewScanner(file)

	for fileScanner.Scan() {
		line := fileScanner.Text()
		if isTodoComment(line) && notCreated(extractCommentContent(line)) {
			commentContent := extractCommentContent(line)
			todoContent := extractTodoContent(commentContent)
			var createdIssue *entity.Issue
			createdIssue, err = c.issueRepository.Create(entity.NewIssue(todoContent, todoContent), c.git)
			if err != nil {
				err = errors.Wrap(err, "failed to create issue")
				return
			}

			newComment := createdIssue.GenerateTodoCommentWithGithubInfo()
			err = writeLine(buf, newComment)
			if err != nil {
				err = errors.Wrap(err, "failed to writeLine")
				return
			}
		} else {
			err = writeLine(buf, line)
			if err != nil {
				err = errors.Wrap(err, "failed to writeLine")
				return
			}
		}
	}

	if err = fileScanner.Err(); err != nil {
		err = errors.Wrap(err, "error while reading file")
		return
	}

	err = os.WriteFile(filePath, buf.Bytes(), 0644)
	if err != nil {
		err = errors.Wrap(err, "failed to write file")
		return
	}

	return
}
```

## issueの作成
[go-github](https://github.com/google/go-github)を使ってgithubAPIを操作します．LabelやAssigneeも設定できるので，まだまだ便利に発展させられそうです．(gifcで自動生成したissueには同じラベルをつけるとか)

```golang:
	ctx := context.Background()
	issueRequest := &github.IssueRequest{
		Title:     &issue.Title,
		Body:      &issue.Content,
		Labels:    nil,
		Assignee:  nil,
		State:     nil,
		Milestone: nil,
		Assignees: nil,
	}

	var gIssue *github.Issue
	gIssue, _, err = i.client.Issues.Create(ctx, git.Owner, git.Repository, issueRequest)
	if err != nil {
		err = errors.Wrap(err, "failed to create issues")
		return
	}
```


# 今後の展望
まだ実装できてないんですが，あったら自分も嬉しいなぁ〜という機能を書いておきます．
## ignore機能
`node_modules`などの依存ファイルやテストコードなどのTODOからissueを作成して欲しくないファイルを登録できるようにしたいですね．

## 複数行のコメントに対応

```main.go
/*
TODO: 並行処理する
各ファイルごとの処理は並行処理して，最終結果を待機する
*/
```
みたいな複数行のコメントがあった場合に，1行目をissueのタイトル・2行目以降を内容にするなど，複数行のコメントにも対応したいですね．

## 複数言語対応
現在は`//`をコメントの始まりとする言語にしか対応してませんが，`#`などがコメントを表す言語もあるので対応したいです．

## dry-run機能の追加
どのようなissueが生成されるのか実行前に確認できた方が良いと思うので，dry-run機能も追加したいですね...

## コメントとissueの紐付け
現在は `コメント→issue` はURLで辿れますが， `issue→コメント` は辿れません．issueがcloseされたらコメントも削除するとか，何かしらで実装できたら便利そうですが良い案は思いつきません...

# さいごに
思いついたツールをガッと作ってしまったので，まだまだ改善点は多くあると思います．
issue作成，PR作成大歓迎ですので，是非 gifc をよろしくお願いします:tada:

https://github.com/masibw/gifc