以前、spanner.StatementのSQLの文字列をフォーマットするために、go-zetasqlfmtというCLIを作った。
便利に使えていたが、使用しているgo-zetasqlがCGOでzetasqlをBindingしている都合上、少々ビルドに時間がかかってしまう。
そのため、CI上で go run
を使って気軽に利用しようとするとCIの時間がかかってしまう問題があった。
使う側でキャシュをうまく使う等で回避はできる問題ではあったが、せっかくなので使う側がそれを気にせず気軽に使えるようにしたいと思い、以下のようにGitHub Actionsでコマンドを提供することにした。
https://github.com/nametake/go-zetasqlfmt-action
ついでにGitHub Actionsを作るにあたってやったことを作業ログとして残しておく。
やったこと Link to heading
色々試行錯誤はしたが、GitHub Actionsとして提供するにあたり、以下のような方針にした。
- go-zetasqlfmt単体でDockerfileを提供する
- go-zetasqlfmt-actionはDockerfileを指定したActionにする
まずはgo-zetasqlfmtのDockerfile作成から。
ライブラリ側のDockerfileを参考にして以下のようなDockerfileをgo-zetasqlfmtのリポジトリに作成。
FROM golang:1.22-bookworm as builder
ARG VERSION
RUN apt-get update \
&& apt-get install -y --no-install-recommends clang git \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
ENV CGO_ENABLED 1
ENV CXX clang++
WORKDIR /go-zetasqlfmt
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN go install ./cmd/zetasqlfmt
FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y \
wget \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*
ENV GO_VERSION 1.22.5
RUN wget https://golang.org/dl/go${GO_VERSION}.linux-amd64.tar.gz \
&& tar -C /usr/local -xzf go${GO_VERSION}.linux-amd64.tar.gz \
&& rm go${GO_VERSION}.linux-amd64.tar.gz
ENV PATH /usr/local/go/bin:$PATH
WORKDIR /app
COPY --from=builder /go/bin/zetasqlfmt /bin/zetasqlfmt
ENTRYPOINT ["/bin/zetasqlfmt"]
ビルドステージ側のImageはgo-zetasqlライブラリのDockerfileを参考にして、debian:bookwormを採用。
コマンド側のImageではslimを使用しつつ、golang.org/x/tools/go/packages.Load
でGoのバイナリが要求されるのでそれだけインストール(内部的に go list
コマンドを使っているっぽい)。
もうちょっと試行錯誤はできるかもしれないが、まずは動くの優先で最低限の軽量化のみしている。
Actions側は以下のようにGitHub Actions用のYAMLファイルを定義。
---
name: go-zetasqlfmt
description: run go-zetasqlfmt
inputs:
working-directory:
description: file paths
required: false
default: .
path:
description: file paths
required: true
nosemicolon:
description: no semicolon option
required: false
runs:
using: docker
image: Dockerfile
args:
- ${{ inputs.working-directory }}
- ${{ inputs.path }}
- ${{ inputs.nosemicolon }}
entrypoint.shもシンプルに以下のように定義。
#!/bin/sh
workingDir=$1
path=$2
nosemicolon=$3
cd "$workingDir" || exit
if [ "$nosemicolon" = "true" ]; then
zetasqlfmt -nosemicolon "$path"
exit 0
fi
zetasqlfmt "$path"
ハマった点として、GitHub ActionsでDockerfileを指定したとき、起動したDockerfileのWORKDIRにworkflow側で設定したjobs.run.working-directoryが反映されていないことがおきた。
go-zetasqlfmtで利用しているpackages.Loadはgo.modが配置されている場所を実行ディレクトリにする必要がある。
Actionを利用しようとしていたリポジトリはモノレポのプロジェクトで、$PROJECT_ROOT/server/go.mod
のようにROOTから一段階下がった場所にgo.modが配置されていたため、フォーマットがうまく効いてくれなかった。
この件の該当っぽいIssue も見つけたが、最適な回避方法もなさそうだったので、Action側で直接working-directoryを指定できるようにして回避した。
GitHub Actionsにしたことで、長いと10分ぐらいかかっていたgo-zetasqlfmtのCIの時間が30秒ぐらいまで削減できた。
もうちょっとImageを軽量化したり等で実行時間を短くしたいが、時間がかかりすぎてしまう問題自体は一旦回避できるようになったので、しばらくこの運用で回してみる。