main.go 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. package main
  2. import (
  3. "context"
  4. "crypto/rand"
  5. "encoding/hex"
  6. "fmt"
  7. "log"
  8. "os"
  9. "os/exec"
  10. "strings"
  11. "github.com/spf13/pflag"
  12. )
  13. type Builder struct {
  14. Name string
  15. }
  16. func (b *Builder) Shutdown(ctx context.Context) error {
  17. cmd := exec.CommandContext(
  18. ctx,
  19. "docker",
  20. "buildx",
  21. "rm",
  22. b.Name)
  23. cmd.Stdout = os.Stdout
  24. cmd.Stderr = os.Stderr
  25. return cmd.Run()
  26. }
  27. func StartBuilder(ctx context.Context) (*Builder, error) {
  28. var key [8]byte
  29. if _, err := rand.Read(key[:]); err != nil {
  30. return nil, err
  31. }
  32. name := fmt.Sprintf("go-build-%s", hex.EncodeToString(key[:]))
  33. cmd := exec.CommandContext(
  34. ctx,
  35. "docker",
  36. "buildx",
  37. "create",
  38. "--name",
  39. name)
  40. cmd.Stdout = os.Stdout
  41. cmd.Stderr = os.Stderr
  42. if err := cmd.Run(); err != nil {
  43. return nil, err
  44. }
  45. return &Builder{Name: name}, nil
  46. }
  47. func (b *Builder) Build(
  48. ctx context.Context,
  49. image string,
  50. tags []string,
  51. platforms []string,
  52. buildArgs []string,
  53. ) error {
  54. args := []string{
  55. "buildx",
  56. "build",
  57. "--builder",
  58. b.Name,
  59. "--platform",
  60. strings.Join(platforms, ","),
  61. }
  62. for _, arg := range buildArgs {
  63. args = append(args, "--build-arg", arg)
  64. }
  65. for _, tag := range tags {
  66. args = append(args,
  67. "-t",
  68. fmt.Sprintf("%s:%s", image, tag))
  69. }
  70. args = append(args, "--push", ".")
  71. cmd := exec.CommandContext(
  72. ctx,
  73. "docker",
  74. args...)
  75. cmd.Stdout = os.Stdout
  76. cmd.Stderr = os.Stderr
  77. return cmd.Run()
  78. }
  79. func run(
  80. ctx context.Context,
  81. image string,
  82. tags []string,
  83. platforms []string,
  84. buildArgs []string,
  85. ) error {
  86. builder, err := StartBuilder(ctx)
  87. if err != nil {
  88. return err
  89. }
  90. defer builder.Shutdown(ctx)
  91. if err := builder.Build(ctx, image, tags, platforms, buildArgs); err != nil {
  92. return err
  93. }
  94. return nil
  95. }
  96. func main() {
  97. var tags []string
  98. var platforms []string
  99. var buildArgs []string
  100. var image string
  101. pflag.StringSliceVar(
  102. &tags,
  103. "tag",
  104. []string{"latest"},
  105. "tags to apply to the image")
  106. pflag.StringSliceVar(
  107. &platforms,
  108. "platform",
  109. []string{"linux/amd64", "linux/arm64"},
  110. "platforms to build for")
  111. pflag.StringSliceVar(
  112. &buildArgs,
  113. "build-arg",
  114. []string{},
  115. "build arguments to pass to the image")
  116. pflag.StringVar(
  117. &image,
  118. "image",
  119. "kellegous/go",
  120. "the name of the image to build")
  121. pflag.Parse()
  122. if err := run(
  123. context.Background(),
  124. image, tags,
  125. platforms,
  126. buildArgs,
  127. ); err != nil {
  128. log.Panic(err)
  129. }
  130. }