| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164 |
- package main
- import (
- "context"
- "crypto/rand"
- "encoding/hex"
- "fmt"
- "log"
- "os"
- "os/exec"
- "os/signal"
- "strings"
- "github.com/spf13/pflag"
- )
- type Builder struct {
- Name string
- }
- func (b *Builder) Shutdown(ctx context.Context) error {
- cmd := exec.CommandContext(
- ctx,
- "docker",
- "buildx",
- "rm",
- b.Name)
- cmd.Stdout = os.Stdout
- cmd.Stderr = os.Stderr
- return cmd.Run()
- }
- func StartBuilder(ctx context.Context) (*Builder, error) {
- var key [8]byte
- if _, err := rand.Read(key[:]); err != nil {
- return nil, err
- }
- name := fmt.Sprintf("go-build-%s", hex.EncodeToString(key[:]))
- cmd := exec.CommandContext(
- ctx,
- "docker",
- "buildx",
- "create",
- "--name",
- name)
- cmd.Stdout = os.Stdout
- cmd.Stderr = os.Stderr
- if err := cmd.Run(); err != nil {
- return nil, err
- }
- return &Builder{Name: name}, nil
- }
- func (b *Builder) Build(
- ctx context.Context,
- image string,
- tags []string,
- platforms []string,
- buildArgs []string,
- ) error {
- args := []string{
- "buildx",
- "build",
- "--builder",
- b.Name,
- "--platform",
- strings.Join(platforms, ","),
- }
- for _, arg := range buildArgs {
- args = append(args, "--build-arg", arg)
- }
- for _, tag := range tags {
- args = append(args,
- "-t",
- fmt.Sprintf("%s:%s", image, tag))
- }
- args = append(args, "--push", ".")
- cmd := exec.CommandContext(
- ctx,
- "docker",
- args...)
- cmd.Stdout = os.Stdout
- cmd.Stderr = os.Stderr
- return cmd.Run()
- }
- func run(
- ctx context.Context,
- image string,
- tags []string,
- platforms []string,
- buildArgs []string,
- ) error {
- builder, err := StartBuilder(ctx)
- if err != nil {
- return err
- }
- defer builder.Shutdown(context.Background())
- ctx, done := signal.NotifyContext(ctx, os.Interrupt)
- defer done()
- ch := make(chan error, 1)
- go func() {
- ch <- builder.Build(ctx, image, tags, platforms, buildArgs)
- }()
- select {
- case <-ctx.Done():
- return ctx.Err()
- case err := <-ch:
- return err
- }
- }
- func main() {
- var tags []string
- var platforms []string
- var buildArgs []string
- var image string
- pflag.StringSliceVar(
- &tags,
- "tag",
- []string{"latest"},
- "tags to apply to the image")
- pflag.StringSliceVar(
- &platforms,
- "platform",
- []string{"linux/amd64", "linux/arm64"},
- "platforms to build for")
- pflag.StringSliceVar(
- &buildArgs,
- "build-arg",
- []string{},
- "build arguments to pass to the image")
- pflag.StringVar(
- &image,
- "image",
- "kellegous/go",
- "the name of the image to build")
- pflag.Parse()
- if err := run(
- context.Background(),
- image, tags,
- platforms,
- buildArgs,
- ); err != nil {
- log.Panic(err)
- }
- }
|