Edited at

goの静的解析系ツールをGitHub Actionsで動かしてみた


追記

バージョンアップしたので、その記事も書きました。

https://qiita.com/grandcolline/items/04c168844dfca3a8ede7

以下は、v1の頃の古い記事になります。


golangの静的解析ツールをGitHub Actionsで動かして、PRに通知してみた。GitHubTokenとかあまり考えなくていいからその辺の管理が楽そうですごくいい印象を受けた。

ただ、まだGithubActionsが理解できていない部分が多く、不備は多々ありそう・・・

TerraformのGithubActionsをかなり参考にした。(ほぼgolangに書き換えただけ)

hashicorp/terraform-github-actions

ほんと、ありがとうございます:bow:


TL;DL

結論から言うと、こんな感じファイルを .github/main.workflow に置くだけ。


.github/main.workflow

workflow "Golang Test Workflow" {

on = "pull_request"
resolves = [
"go imports",
"go vet",
"staticcheck",
]
}

action "filter to pr open synced" {
uses = "actions/bin/filter@master"
args = "action 'opened|synchronize'"
}

action "go imports" {
uses = "grandcolline/golang-github-actions/imports@v0.1.2"
needs = "filter to pr open synced"
secrets = ["GITHUB_TOKEN"]
}

action "go vet" {
uses = "grandcolline/golang-github-actions/vet@v0.1.2"
needs = "filter to pr open synced"
secrets = ["GITHUB_TOKEN"]
env = {
FLAGS = "-shadow"
}
}

action "staticcheck" {
uses = "grandcolline/golang-github-actions/staticcheck@v0.1.2"
needs = "filter to pr open synced"
secrets = ["GITHUB_TOKEN"]
}


これで、こんな感じでPRに通知してくれる。

imports.png

staticcheck.png


少し詳細

上記のワークフローの中で、下記のレポジトリを呼び出して、そのレポジトリ内にあるDockerfileから、Dockerコンテナを起動し、テストを実行している。

grandcolline/golang-github-actions

なので実際は、上記のレポジトリの部分を実装していった。

(ただ、実装といっても、静的解析ツールを入れたDockerfileと、テスト実行のシェルスクリプトのentrypoint.shを用意するだけ・・・)

例えば、goimportsを動かすジョブは、


goimports/Dockerfile

FROM golang:latest

LABEL "com.github.actions.name"="go imports"
LABEL "com.github.actions.description"="Run goimports"
LABEL "com.github.actions.icon"="terminal"
LABEL "com.github.actions.color"="purple"

LABEL "repository"="https://github.com/grandcolline/golang-github-actions"
LABEL "homepage"="https://github.com/grandcolline/golang-github-actions"
LABEL "maintainer"="grandcolline <grandcolline@gmail.com>"

RUN apt-get update && \
apt-get -y install jq && \
go get -u golang.org/x/tools/cmd/goimports

COPY entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]


jqgoimportsを取得するDockerfileを用意する。

LABELなどの仕様は、GitHubActionsの仕様に従う。詳しくは、ここ

実行するシェルは、こんな感じ。


goimports/entrypoint.sh

#!/bin/sh

set -e
cd "${GO_ACTION_WORKING_DIR:-.}"

set +e
UNFMT_FILES=$(sh -c "goimports -l . $*" 2>&1)
test -z "${UNFMT_FILES}"
SUCCESS=$?
echo "${UNFMT_FILES}"
set -e

if [ ${SUCCESS} -eq 0 ]; then
exit 0
fi

if [ "${GO_ACTION_COMMENT}" = "1" ] || [ "${GO_ACTION_COMMENT}" = "false" ]; then
exit ${SUCCESS}
fi

FMT_OUTPUT=""
for file in ${UNFMT_FILES}; do
FILE_DIFF=$(goimports -d -e "${file}" | sed -n '/@@.*/,//{/@@.*/d;p}')
FMT_OUTPUT="${FMT_OUTPUT}
<details><summary><code>
${file}</code></summary>

\`\`\`diff
${FILE_DIFF}
\`\`\`
</details>

"
done

COMMENT="## goimports Failed
${FMT_OUTPUT}
"

PAYLOAD=$(echo '{}' | jq --arg body "${COMMENT}" '.body = $body')
COMMENTS_URL=$(cat /github/workflow/event.json | jq -r .pull_request.comments_url)
curl -s -S -H "Authorization: token ${GITHUB_TOKEN}" --header "Content-Type: application/json" --data "${PAYLOAD}" "${COMMENTS_URL}" > /dev/null

exit ${SUCCESS}


自動でカレントディレクトリ(/github/workspace)にソースコードがcheckoutさてれいるので、goimports -l . でフォーマットがおかしいファイル一覧を取得し、ファイル毎にdiffを${FMT_OUTPUT}に整形しながら入れてあげて、最後にcurlでPRに通知する。

${GITHUB_TOKEN}などは、workflow側から入れてあげれば、自然な形で環境変数として使える。

環境変数などはここをみながら。