Files

255 lines
7.1 KiB
Go

package artifacts
import (
"context"
"log/slog"
"os"
"path/filepath"
"dagger.io/dagger"
"github.com/grafana/grafana/pkg/build/daggerbuild/arguments"
"github.com/grafana/grafana/pkg/build/daggerbuild/backend"
"github.com/grafana/grafana/pkg/build/daggerbuild/flags"
"github.com/grafana/grafana/pkg/build/daggerbuild/packages"
"github.com/grafana/grafana/pkg/build/daggerbuild/pipeline"
)
var (
BackendArguments = []pipeline.Argument{
arguments.GrafanaDirectory,
arguments.EnterpriseDirectory,
arguments.GoVersion,
arguments.ViceroyVersion,
}
BackendFlags = flags.JoinFlags(
flags.PackageNameFlags,
flags.DistroFlags(),
)
)
var BackendInitializer = Initializer{
InitializerFunc: NewBackendFromString,
Arguments: BackendArguments,
}
type Backend struct {
// Name allows different backend compilations to be different even if all other factors are the same.
// For example, Grafana Enterprise, Grafana, and Grafana Pro may be built using the same options,
// but are fundamentally different because of the source code of the binary.
Name packages.Name
Src *dagger.Directory
Distribution backend.Distribution
BuildOpts *backend.BuildOpts
GoVersion string
ViceroyVersion string
GoBuildCache *dagger.CacheVolume
GoModCache *dagger.CacheVolume
// Version is embedded in the binary at build-time
Version string
}
func (b *Backend) Builder(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) {
return backend.Builder(
opts.Client,
opts.Log,
b.Distribution,
b.BuildOpts,
opts.Platform,
b.Src,
b.GoVersion,
b.ViceroyVersion,
b.GoBuildCache,
b.GoModCache,
)
}
func (b *Backend) Dependencies(ctx context.Context) ([]*pipeline.Artifact, error) {
return nil, nil
}
func (b *Backend) BuildFile(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.File, error) {
panic("not implemented") // TODO: Implement
}
func (b *Backend) BuildDir(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.Directory, error) {
f, err := b.Filename(ctx)
if err != nil {
return nil, err
}
return backend.Build(
opts.Client,
builder,
b.Src,
b.Distribution,
f,
b.BuildOpts,
), nil
}
func (b *Backend) Publisher(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) {
panic("not implemented") // TODO: Implement
}
func (b *Backend) PublishFile(ctx context.Context, opts *pipeline.ArtifactPublishFileOpts) error {
panic("not implemented") // TODO: Implement
}
func (b *Backend) PublishDir(ctx context.Context, opts *pipeline.ArtifactPublishDirOpts) error {
panic("not implemented") // TODO: Implement
}
// Filename should return a deterministic file or folder name that this build will produce.
// This filename is used as a map key for caching, so implementers need to ensure that arguments or flags that affect the output
// also affect the filename to ensure that there are no collisions.
// For example, the backend for `linux/amd64` and `linux/arm64` should not both produce a `bin` folder, they should produce a
// `bin/linux-amd64` folder and a `bin/linux-arm64` folder. Callers can mount this as `bin` or whatever if they want.
func (b *Backend) Filename(ctx context.Context) (string, error) {
return filepath.Join("bin", string(b.Name), string(b.Distribution)), nil
}
func (b *Backend) VerifyFile(ctx context.Context, client *dagger.Client, file *dagger.File) error {
// Not a file
return nil
}
func (b *Backend) VerifyDirectory(ctx context.Context, client *dagger.Client, dir *dagger.Directory) error {
// Nothing to do (yet)
return nil
}
type NewBackendOpts struct {
Name packages.Name
Enterprise bool
Src *dagger.Directory
Distribution backend.Distribution
GoVersion string
ViceroyVersion string
Version string
Experiments []string
Tags []string
Static bool
WireTag string
GoBuildCache *dagger.CacheVolume
GoModCache *dagger.CacheVolume
}
func NewBackendFromString(ctx context.Context, log *slog.Logger, artifact string, state pipeline.StateHandler) (*pipeline.Artifact, error) {
goVersion, err := state.String(ctx, arguments.GoVersion)
if err != nil {
return nil, err
}
viceroyVersion, err := state.String(ctx, arguments.ViceroyVersion)
if err != nil {
return nil, err
}
goModCache, err := state.CacheVolume(ctx, arguments.GoModCache)
if err != nil {
return nil, err
}
goBuildCache, err := state.CacheVolume(ctx, arguments.GoBuildCache)
if err != nil {
return nil, err
}
// 1. Figure out the options that were provided as part of the artifact string.
// For example, `linux/amd64:grafana`.
options, err := pipeline.ParseFlags(artifact, TargzFlags)
if err != nil {
return nil, err
}
static, err := options.Bool(flags.Static)
if err != nil {
return nil, err
}
wireTag, err := options.String(flags.WireTag)
if err != nil {
return nil, err
}
experiments, err := options.StringSlice(flags.GoExperiments)
if err != nil {
return nil, err
}
tags, err := options.StringSlice(flags.GoTags)
if err != nil {
return nil, err
}
p, err := GetPackageDetails(ctx, options, state)
if err != nil {
return nil, err
}
src, err := GrafanaDir(ctx, state, p.Enterprise)
if err != nil {
return nil, err
}
goCacheProg := ""
// If the caller has GOCACHEPROG set, then reuse it
if val, ok := os.LookupEnv("GOCACHEPROG"); ok {
goCacheProg = val
}
bopts := &backend.BuildOpts{
Version: p.Version,
Enterprise: p.Enterprise,
ExperimentalFlags: experiments,
GoCacheProg: goCacheProg,
Static: static,
WireTag: wireTag,
Tags: tags,
}
return pipeline.ArtifactWithLogging(ctx, log, &pipeline.Artifact{
ArtifactString: artifact,
Type: pipeline.ArtifactTypeDirectory,
Flags: BackendFlags,
Handler: &Backend{
Name: p.Name,
Distribution: p.Distribution,
BuildOpts: bopts,
GoVersion: goVersion,
ViceroyVersion: viceroyVersion,
Src: src,
GoModCache: goModCache,
GoBuildCache: goBuildCache,
},
})
}
func NewBackend(ctx context.Context, log *slog.Logger, artifact string, opts *NewBackendOpts) (*pipeline.Artifact, error) {
bopts := &backend.BuildOpts{
Version: opts.Version,
Enterprise: opts.Enterprise,
ExperimentalFlags: opts.Experiments,
Tags: opts.Tags,
Static: opts.Static,
WireTag: opts.WireTag,
}
log.Info("Initializing backend artifact with options", "static", opts.Static, "version", opts.Version, "name", opts.Name, "distro", opts.Distribution)
return pipeline.ArtifactWithLogging(ctx, log, &pipeline.Artifact{
ArtifactString: artifact,
Type: pipeline.ArtifactTypeDirectory,
Flags: BackendFlags,
Handler: &Backend{
Name: opts.Name,
Distribution: opts.Distribution,
BuildOpts: bopts,
GoVersion: opts.GoVersion,
ViceroyVersion: opts.ViceroyVersion,
Src: opts.Src,
GoModCache: opts.GoModCache,
GoBuildCache: opts.GoBuildCache,
},
})
}