🦁

CI/CD も Go で書いてみた

2022/10/27に公開

Dagger が Go SDK を発表しました 🎉

https://dagger.io/blog/go-sdk

Dagger を使うことでローカルマシン、 CI ランナー、専用サーバー、その他任意のコンテナホスティングサービス上などで実行可能な CI/CD パイプラインを構築することができます。

以前「インフラもバックエンドもフロントエンドも Go で書いてみた」という記事を公開したのですが、 Dagger Go SDK ができたことで CI/CD までもが Go で書けるようになったので書いてみました。
インフラ・バックエンド・フロントエンドの方に関しては下記の記事をご参照ください。

https://zenn.dev/kou_pg_0131/articles/gogogo-introduction

作ったもの


GoGoGo

https://github.com/koki-develop/gogogo/tree/main/cicd

Dagger とは

https://dagger.io

Dagger はコンテナで実行される CI/CD パイプラインを構築するツールです。
GitHub Actions や CircleCI などといった特定のサービスに依存しないポータブルな CI/CD パイプラインを構築することができます。
ローカルでも実行することが可能なので、 CI/CD パイプラインの動作確認のためにわざわざコミット・プッシュする等の手間が省けるというメリットもあります。

以前は CUE 言語で記述する必要があったのですが、 Go SDK が公開されたことで Go で CI/CD パイプラインを構築することができるようになりました。

https://docs.dagger.io/sdk/go/

もちろん CUE 言語も引き続き利用することが可能です。

https://docs.dagger.io/sdk/cue

Dagger Go SDK の使い方

前提条件

  • Go 1.15 以上がインストールされている
  • Docker がインストール・起動されている

インストール

https://docs.dagger.io/sdk/go/371491/install

まず Go Module を初期化します

$ go mod init <任意のモジュール名>
# 例
$ go mod init github.com/koki-develop/dagger-go-sdk-example

次に Dagger をインストールします。

$ go get dagger.io/dagger@latest

これで Dagger Go SDK を使う準備が整いました。

サンプルコード

https://github.com/koki-develop/dagger-go-sdk-example

koki-develop/dagger-go-sdk-example をクローンして go build する例です。

main.go
package main

import (
	"context"
	"os"

	"dagger.io/dagger"
)

func main() {
	ctx := context.Background()

	// Dagger クライアントを初期化
	// dagger.WithLogOutput でログの出力先を設定することができる
	client, err := dagger.Connect(ctx, dagger.WithLogOutput(os.Stdout))
	if err != nil {
		panic(err)
	}
	defer client.Close()

	// リポジトリをクローン
	repo := client.Git("https://github.com/koki-develop/dagger-go-sdk-example.git")
	src, err := repo.Branch("main").Tree().ID(ctx)
	if err != nil {
		panic(err)
	}

	// Docker イメージを読み込む
	golang := client.Container().From("golang:1.19")

	// クローンしたリポジトリのソースをマウントする
	// WithWorkdir で作業ディレクトリを設定できる
	golang = golang.WithMountedDirectory("/app", src).WithWorkdir("/app")

	// 実行するコマンドを追加
	golang = golang.
		Exec(dagger.ContainerExecOpts{
			Args: []string{"go", "build", "-o", "build/"},
		}).
		Exec(dagger.ContainerExecOpts{
			Args: []string{"ls", "./build"},
		})

	// ExitCode でパイプラインが実行される
	if _, err := golang.ExitCode(ctx); err != nil {
		panic(err)
	}
}

これで go run ./main.go するだけで Dagger によるパイプラインが実行されます。

$ go run ./main.go
出力例
#1 resolve image config for docker.io/library/golang:1.19
#1 DONE 4.2s

#2 mkfile /Dockerfile
#2 DONE 0.0s

...省略

#15 go build -o build/
#15 0.150 go: downloading dagger.io/dagger v0.3.1
#15 0.831 go: downloading github.com/Khan/genqlient v0.5.0
#15 0.835 go: downloading github.com/iancoleman/strcase v0.2.0
#15 0.836 go: downloading github.com/vektah/gqlparser/v2 v2.5.1
...省略

#16 ls ./build
#16 0.175 dagger-go-sdk-example
#16 DONE 0.2s

GitHub Actions で使ってみる

例えば GitHub Actions で使う場合はこんな感じになります。
checkout して Go のセットアップをした後に go run ./main.go を実行するだけです。

.github/workflows/main.yml
name: build

on:
  push:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-go@v3
        with:
          go-version: 1.19

      - run: go run ./main.go

その他

他にもパイプラインの中で作成したファイルをホストマシン側にコピーしたり、 go routine を使用して複数パイプラインを並行に走らせたりなど、様々なことができます。
詳しくは公式の Get Startedpkg.go.dev をご参照ください。

https://docs.dagger.io/sdk/go/959738/get-started
https://pkg.go.dev/dagger.io/dagger

まとめ

なんでもかんでも Go で書けるようになりました。わーい。

参考

https://docs.dagger.io/sdk/go/

Discussion