Skip to content

Commit

Permalink
feat: add dagger CI
Browse files Browse the repository at this point in the history
Signed-off-by: Vasek - Tom C <[email protected]>
  • Loading branch information
TomChv committed Jan 23, 2024
1 parent 29affb3 commit be02b36
Show file tree
Hide file tree
Showing 10 changed files with 402 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .dagger/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/dagger.gen.go
/internal/querybuilder/
/querybuilder/
76 changes: 76 additions & 0 deletions .dagger/ci.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package main

import (
"context"

"golang.org/x/sync/errgroup"
)

type CI struct {
Project *Container
}

// Run runs all CI steps in parallel.
// It runs: unit tests, lint and build.
// Example usage: `dagger call ci run`
func (c *CI) Run(ctx context.Context) error {
wg, gctx := errgroup.WithContext(ctx)

wg.Go(func() error {
_, err := c.UnitTest(gctx)

return err
})

wg.Go(func() error {
_, err := c.Lint(gctx)

return err
})

wg.Go(func() error {
_, err := c.Build().Stdout(gctx)

return err
})

return wg.Wait()
}

// UnitTest runs go unit tests on the project.
// Example usage: `dagger call ci unit-test`
func (c *CI) UnitTest(ctx context.Context) (string, error) {
return c.
Project.
Pipeline("unit-test").
WithExec([]string{"go", "test", "-v", "-race", "./...",}).
Stdout(ctx)
}

// Lint runs golangci-lint on the project.
// Example usage: `dagger call ci lint`
func (c *CI) Lint(ctx context.Context) (string, error) {
// We cannot use golangci-lint official image because it doesn't support go
// 1.21.6 for now
return c.
Project.
Pipeline("lint").

// Add binutils-gold for golang
// See: https://github.com/golang/go/issues/52399
WithExec([]string{"apk", "add", "binutils-gold"}).

// Manually install golangci-lint binary
WithExec([]string{"go", "install", "github.com/golangci/golangci-lint/cmd/[email protected]"}).
WithExec([]string{"golangci-lint", "run", "-v", "--timeout", "5m"}).
Stdout(ctx)
}

// Build returns a container with Agate binary built in it.
// Example usage: `dagger call ci build`
func (c *CI) Build() *Container {
return c.
Project.
Pipeline("build").
WithExec([]string{"go", "build", "-o", "agate", "main.go"})
}
22 changes: 22 additions & 0 deletions .dagger/dagger.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"name": "agate",
"root": "..",
"sdk": "go",
"include": [
"**/*.go",
"db/**/*.sql",
"go.mod",
"go.sum",
".golangci.yaml",
".dagger"
],
"exclude": [
".github",
"**/*.md",
"**/*.env*",
"**/*.example",
"docker-compose*.yaml",
"Dockerfile",
"docs"
]
}
13 changes: 13 additions & 0 deletions .dagger/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module main

go 1.21.3

require (
github.com/99designs/gqlgen v0.17.31
github.com/Khan/genqlient v0.6.0
github.com/vektah/gqlparser/v2 v2.5.6
golang.org/x/exp v0.0.0-20231006140011-7918f672742d
golang.org/x/sync v0.4.0
)

require github.com/stretchr/testify v1.8.3 // indirect
35 changes: 35 additions & 0 deletions .dagger/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
github.com/99designs/gqlgen v0.17.31 h1:VncSQ82VxieHkea8tz11p7h/zSbvHSxSDZfywqWt158=
github.com/99designs/gqlgen v0.17.31/go.mod h1:i4rEatMrzzu6RXaHydq1nmEPZkb3bKQsnxNRHS4DQB4=
github.com/Khan/genqlient v0.6.0 h1:Bwb1170ekuNIVIwTJEqvO8y7RxBxXu639VJOkKSrwAk=
github.com/Khan/genqlient v0.6.0/go.mod h1:rvChwWVTqXhiapdhLDV4bp9tz/Xvtewwkon4DpWWCRM=
github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/vektah/gqlparser/v2 v2.5.6 h1:Ou14T0N1s191eRMZ1gARVqohcbe1e8FrcONScsq8cRU=
github.com/vektah/gqlparser/v2 v2.5.6/go.mod h1:z8xXUff237NntSuH8mLFijZ+1tjV1swDbpDqjJmk6ME=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
92 changes: 92 additions & 0 deletions .dagger/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package main

import (
"fmt"
"strconv"
)

const (
defaultGoImageVersion = "1.21.6-alpine3.19"
workdir = "/app"
envPrefix = "AGATE_INDEXER_"
)

type Agate struct {
// Image version of the Go image to use (default to: `1.21.6-alpine3.19`).
GoImageVersion string
}

func New(goImageVersion Optional[string]) *Agate {
return &Agate{
GoImageVersion: goImageVersion.GetOr(defaultGoImageVersion),
}
}

// CI gives access to CI commands.
func (m *Agate) CI() *CI {
return &CI{
Project: m.Project(),
}
}

// Database returns a PostgreSQL database.
// Example usage: `dagger up --native database`
func (m *Agate) Database(
username Optional[string],
password Optional[string],
dbName Optional[string],
port Optional[int],
) *Service {
var defaultPort int

if !port.isSet {
defaultPortStr := GetHostEnv("DATABASE_PORT")

defaultPortTmp, err := strconv.Atoi(defaultPortStr)
if err != nil {
fmt.Println(fmt.Errorf("invalid port: %w", err))
return nil
}

defaultPort = defaultPortTmp
}

return dag.
Container().
From("bitnami/postgresql:15.2.0").
WithEnvVariable("POSTGRES_USER", username.GetOr(GetHostEnv("DATABASE_USERNAME"))).
WithSecretVariable("POSTGRES_PASSWORD", dag.SetSecret("dbPassword", password.GetOr(GetHostEnv("DATABASE_PASSWORD")))).
WithEnvVariable("POSTGRES_DATABASE", dbName.GetOr(GetHostEnv("DBNAME"))).
WithMountedCache("/var/lib/postgresql/data", dag.CacheVolume("pg-data")).
WithExposedPort(port.GetOr(defaultPort)).
AsService()
}

// Binary returns Agate binary file to the host's current directory.
// Example usage: `dagger export binary`
func (m *Agate) Binary() *File {
return m.CI().Build().File("agate")
}

// Project returns the container that will be used in further Dagger command.
// Open a shell inside it using `dagger shell project --entrypoint /bin/sh`.
func (m *Agate) Project() *Container {
return dag.
Container().
From("golang:"+m.GoImageVersion).
WithWorkdir(workdir).
// Install build-base to build cgo packages.
WithExec([]string{"apk", "add", "build-base"}).

// Add cache for go modules and go build.
WithMountedCache("/go/pkg/mod", dag.CacheVolume("gomod")).
WithMountedCache("/root/.cache/go-build", dag.CacheVolume("gobuild")).

// Install go dependencies
WithFile("go.mod", source().File("go.mod")).
WithFile("go.sum", source().File("go.sum")).
WithExec([]string{"go", "mod", "download"}).

// Add source code
WithMountedDirectory(workdir, source())
}
26 changes: 26 additions & 0 deletions .dagger/source.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package main

import (
"os"
"path/filepath"
)

func source() *Directory {
return dag.Host().Directory(root(), HostDirectoryOpts{
Exclude: []string{
".dagger",
},
})
}

// Return the absolute path to the root of the project.
// This is required for now by Dagger but it should be fixed later.
// TODO: fix .. restriction when it's merge on dagger
func root() string {
workdir, err := os.Getwd()
if err != nil {
panic(err)
}

return filepath.Join(workdir, "..")
}
17 changes: 17 additions & 0 deletions .dagger/utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package main

import (
"fmt"
"os"
)


// GetHostEnv is like os.Getenv but ensures that the env var is set.
func GetHostEnv(name string) string {
value, ok := os.LookupEnv(envPrefix + name)
if !ok {
fmt.Fprintf(os.Stderr, "env var %s must be set\n", name)
os.Exit(1)
}
return value
}
28 changes: 28 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Continuous Integration

on:
push:
branches:
- main

pull_request:
branches:
- main

jobs:
ci:
runs-on: ubuntu-latest
env:
DAGGER_CLOUD_TOKEN: ${{ secrets.DAGGER_CLOUD_TOKEN }}

steps:
- name: Checkout
uses: actions/checkout@v3

- name: Install Dagger CLI
run: cd /usr/local && { curl -L https://dl.dagger.io/dagger/install.sh | sh; cd -; }

- name: Execute dagger run
run: |
cd .dagger
dagger call ci run
Loading

0 comments on commit be02b36

Please sign in to comment.