Golang + Sonarqube + Codebuild

https://qiita.com/fake-deli-ca/items/e01a42a9d04cf0bab8c4
の後にJenkinsじゃなくてCodeBuildも対応してみたんだけど、異様にわかりにくくて、時間かかった…。

# 結論
最初に結論から。
関係する箇所のみ抜粋してある。

```buildspec.yml
version: 0.2

env:
  variables:
   GO_VERSION      : ""  
   PROJECT_VERSION : ""  
   # 本来はParameter-Storeにすべき
   SONARQUBE_TOKEN  : "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"


phases:
  install:
    runtime-versions:
      golang: ${GO_VERSION}
    commands:
      - type go && go version
      - chmod +x ./gradlew
      - export REGOPATH=`echo $GOPATH | cut -c 5-`
      - export PATH=${GOPATH}/bin:${PATH} && echo ${PATH}

  pre_build:
    commands:
      - echo 'setup'
      - make setup

  build:
    commands:
      - echo 'make & test'
      - make
      - make test
      - ./gradlew sonarqube -Dsonar.projectBaseDir=${REGOPATH}/src -Dsonar.login=${SONARQUBE_TOKEN} -Dsonar.projectVersion=${PROJECT_VERSION} --info
```

下記のMakefileはBuildの記載がないので、このままでは動作しない。

```Makefile
.PHONY: all
all: build

.PHONY: setup
setup:
	go get -u github.com/ory/go-acc

.PHONY: test
test:
	go test ./... -json > reports/test.json
	go-acc ./...  -o reports/coverage.out
```

```build.gradle
plugins {
    id "org.sonarqube" version "3.0"
}

sonarqube {
    properties {
        //FIXME 
        property "sonar.host.url","https://sonarqube.foo.jp"

        property "sonar.projectKey", "hogehoge"
        property "sonar.projectName", "hogehoge Project"

        property "sonar.go.coverage.reportPaths", "(gitのpass)/reports/coverage.out"
        property "sonar.go.tests.reportPaths", "(gitのpass)/reports/test.json"
        //FIXME

        property "sonar.sources", "."

        property "sonar.exclusions", "**/*_test.go"
        property "sonar.tests", "."
        property "sonar.test.inclusions", "**/*_test.go"
        property "sonar.test.exclusions", "**/vendor/**"
        property "sonar.sourceEncoding", "UTF-8"
    }
}
```

# 確認すべき点

## is not included in the project, ignoring coverage
```
Sensor Go Cover sensor for Go coverage [go]
Load coverage report from '/codebuild/output/src532592822/src/(gitのpass)/coverage.txt'
File '/codebuild/output/src532592822/src/(gitのpass)/AAA/BBB.go' is not included in the project, ignoring coverage
```

とにかく、`is not included in the project, ignoring coverage`にやられた。どう設定していいのか全く分からない。
https://github.com/SonarSource/sonar-go/issues/218
を読むとGOPATHの設定が悪い、と書いてあるが、どうすれば治るのかは書いてない。
酷い…。

結論を言うと、`sonar.projectBaseDir`の設定が悪い。ところが、皆様ご存じの通り、Codebuildは`/codebuild/output/srcXXXXXXXXXX/src`の部分が毎回異なるため、何らかの利用できる変数で設定しなければならない。
普通に考えると`sonar.projectBaseDir=$GOPATH/src`とすればよいのだが、ところがである。Codebuildの`$GOPATH`は`/go:/codebuild/output/src532592822`となっており、**/go:** の部分が心底余計なのである。
上記のように設定すると、

```
* What went wrong:
Execution failed for task ':sonarqube'.
> The folder '/go:/codebuild/output/src179970974/src' does not exist for 'xxxxx' (base directory = /codebuild/output/src179970974/src/(gitのpass))
```

みたいなエラーになるのである。まあ当然だが。
というわけで強引にcutで削除して再設定した。本当にこれでいいのかどうかは知らない。
また、本来はbuild.gradleに全部書きたかったのだが、当然環境変数と展開できないので、`-Dsonar.projectBaseDir=${REGOPATH}/src`という設定とした。

### reportPathsの変更
上記projectBaseDirの変更に伴う対応である。
普通、この辺のPathは`$CODEBUILD_SRC_DIR`の直下に置かれるので、gitのpassは本来不要なのであるが、projectBaseDirを変更した関係上、ここも変更が必要である。

```
        property "sonar.go.coverage.reportPaths", "(gitのpass)/reports/coverage.out"
        property "sonar.go.tests.reportPaths", "(gitのpass)/reports/test.json"
```

## Gradleの利用
sonarqubeだけgradle使っててなんかおかしな感じではあるのだが、最初こんな感じでやってたらunzipのインストールが必要だったり、sonar-scannerのインストール位置が気持ち悪かったり、なんだか訳がわからなくなりそうだったので、あきらめて素直にgradle使うことにした。別に使わなくてもいいとは思う。ただ↓の設定でうまくいくところまでは試してない。少なくともGOPATHはこのままでは駄目だと思う。
また、gogradleで全部書き直すことも考えたが、なんかそこまでして綺麗にする理由もなかったのでやめ。

```buildspec.yml
phases:
  install:
    runtime-versions:
      golang: ${GO_VERSION}
    commands:
      - type go && go version
      # sonar-scanner install
      - yum -y install unzip
      - wget https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-4.4.0.2170-linux.zip
      - unzip ./sonar-scanner-cli-4.4.0.2170-linux.zip
  pre_build:
    commands:
      - echo 'setup'
      - export PATH=${GOPATH}/bin:./sonar-scanner-4.4.0.2170-linux/bin:${PATH} && echo ${PATH}
      - make setup

  build:
    commands:
      - echo 'make & test'
      - make
      - make test
      - sonar-scanner -X -Dsonar.login=${SONARQUBE_TOKEN}
```

## go-accの利用
ここまで実施してみて、どうもCoverage率が異様に低いことに気が付く。
これも結論だけをいうと、`go test -cover`のデフォルト挙動に問題があるということである。なので、`go-acc`を利用することとした。

> The reason for this was that go test -cover is per default only recording code coverage for the package that is currently tested, not for all the packages from the project. If you have a lot of integration tests, your code coverage might be higher than you think!

https://www.ory.sh/golang-go-code-coverage-accurate/

…と結論だけなら簡単だが、ここに辿り着くまでに大層時間かかった。

> I found a brutal hack of a workaround. I'm trying to report coverage on a project with a single golang module, and all of the module's files are below a single subdirectory in the git repo.  We use go-acc to measure coverage, it writes file coverage.txt. I added a step in the build script to rewrite the prefix on the paths in that file, change the module name to the directory name, kinda like this:
> `sed -i -e 's/^mymodule/ModuleSubdir/' coverage.txt`
> Then the sonar scanner is happy.  I don't know if the coverage numbers are correct, but they ain't zero no more

https://jira.sonarsource.com/browse/SONARSLANG-450
大本は、`is not included in the project, ignoring coverage`を解決すべく探していた際に見つけた上記であるが、結果的にソースコードのCoverage登録そのものとは関係なく、別の問題を解決することになった。

# うーん。
なんかもうちょっと綺麗にしたい気分ではあるんだけど…。
というのと、情報が少なくてきついですね。