main.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. package main
  2. import (
  3. "flag"
  4. "fmt"
  5. "log"
  6. "log/slog"
  7. "os"
  8. "os/exec"
  9. "path/filepath"
  10. "strings"
  11. "github.com/TecharoHQ/anubis/internal"
  12. "github.com/facebookgo/flagenv"
  13. )
  14. var (
  15. dockerAnnotations = flag.String("docker-annotations", os.Getenv("DOCKER_METADATA_OUTPUT_ANNOTATIONS"), "Docker image annotations")
  16. dockerLabels = flag.String("docker-labels", os.Getenv("DOCKER_METADATA_OUTPUT_LABELS"), "Docker image labels")
  17. dockerRepo = flag.String("docker-repo", "registry.int.xeserv.us/techaro/anubis", "Docker image repository for Anubis")
  18. dockerTags = flag.String("docker-tags", os.Getenv("DOCKER_METADATA_OUTPUT_TAGS"), "newline separated docker tags including the registry name")
  19. githubEventName = flag.String("github-event-name", "", "GitHub event name")
  20. pullRequestID = flag.Int("pull-request-id", -1, "GitHub pull request ID")
  21. slogLevel = flag.String("slog-level", "INFO", "logging level (see https://pkg.go.dev/log/slog#hdr-Levels)")
  22. )
  23. func main() {
  24. flagenv.Parse()
  25. flag.Parse()
  26. slog.SetDefault(internal.InitSlog(*slogLevel, os.Stderr))
  27. koDockerRepo := strings.TrimSuffix(*dockerRepo, "/"+filepath.Base(*dockerRepo))
  28. if *githubEventName == "pull_request" && *pullRequestID != -1 {
  29. *dockerRepo = fmt.Sprintf("ttl.sh/techaro/pr-%d/anubis", *pullRequestID)
  30. *dockerTags = fmt.Sprintf("ttl.sh/techaro/pr-%d/anubis:24h", *pullRequestID)
  31. koDockerRepo = fmt.Sprintf("ttl.sh/techaro/pr-%d", *pullRequestID)
  32. slog.Info(
  33. "Building image for pull request",
  34. "docker-repo", *dockerRepo,
  35. "docker-tags", *dockerTags,
  36. "github-event-name", *githubEventName,
  37. "pull-request-id", *pullRequestID,
  38. )
  39. }
  40. if strings.Contains(*dockerTags, ",") {
  41. newTags := strings.Join(strings.Split(*dockerTags, ","), "\n")
  42. dockerTags = &newTags
  43. }
  44. setOutput("docker_image", strings.SplitN(*dockerTags, "\n", 2)[0])
  45. version, err := run("git describe --tags --always --dirty")
  46. if err != nil {
  47. log.Fatal(err)
  48. }
  49. commitTimestamp, err := run("git log -1 --format='%ct'")
  50. if err != nil {
  51. log.Fatal(err)
  52. }
  53. slog.Debug(
  54. "ko env",
  55. "KO_DOCKER_REPO", koDockerRepo,
  56. "SOURCE_DATE_EPOCH", commitTimestamp,
  57. "VERSION", version,
  58. )
  59. os.Setenv("KO_DOCKER_REPO", koDockerRepo)
  60. os.Setenv("SOURCE_DATE_EPOCH", commitTimestamp)
  61. os.Setenv("VERSION", version)
  62. setOutput("version", version)
  63. if *dockerTags == "" {
  64. log.Fatal("Must set --docker-tags or DOCKER_METADATA_OUTPUT_TAGS")
  65. }
  66. images, err := parseImageList(*dockerTags)
  67. if err != nil {
  68. log.Fatalf("can't parse images: %v", err)
  69. }
  70. for _, img := range images {
  71. if img.repository != *dockerRepo {
  72. slog.Error(
  73. "Something weird is going on. Wanted docker repo differs from contents of --docker-tags. Did a flag get set incorrectly?",
  74. "wanted", *dockerRepo,
  75. "got", img.repository,
  76. "docker-tags", *dockerTags,
  77. )
  78. os.Exit(2)
  79. }
  80. }
  81. var tags []string
  82. for _, img := range images {
  83. tags = append(tags, img.tag)
  84. }
  85. output, err := run(fmt.Sprintf("ko build --platform=all --base-import-paths --tags=%q --image-user=1000 --image-annotation=%q --image-label=%q ./cmd/anubis | tail -n1", strings.Join(tags, ","), *dockerAnnotations, *dockerLabels))
  86. if err != nil {
  87. log.Fatalf("can't run ko build, check stderr: %v", err)
  88. }
  89. sp := strings.SplitN(output, "@", 2)
  90. setOutput("digest", sp[1])
  91. }
  92. type image struct {
  93. repository string
  94. tag string
  95. }
  96. func parseImageList(imageList string) ([]image, error) {
  97. images := strings.Split(imageList, "\n")
  98. var result []image
  99. for _, img := range images {
  100. if img == "" {
  101. continue
  102. }
  103. // reg.xeiaso.net/techaro/anubis:latest
  104. // repository: reg.xeiaso.net/techaro/anubis
  105. // tag: latest
  106. index := strings.LastIndex(img, ":")
  107. result = append(result, image{
  108. repository: img[:index],
  109. tag: img[index+1:],
  110. })
  111. }
  112. if len(result) == 0 {
  113. return nil, fmt.Errorf("no images provided, bad flags")
  114. }
  115. return result, nil
  116. }
  117. // run executes a command and returns the trimmed output.
  118. func run(command string) (string, error) {
  119. bin, err := exec.LookPath("sh")
  120. if err != nil {
  121. return "", err
  122. }
  123. slog.Debug("running command", "command", command)
  124. cmd := exec.Command(bin, "-c", command)
  125. cmd.Stderr = os.Stderr
  126. out, err := cmd.Output()
  127. if err != nil {
  128. return "", err
  129. }
  130. return strings.TrimSpace(string(out)), nil
  131. }
  132. func setOutput(key, val string) {
  133. github_output := os.Getenv("GITHUB_OUTPUT")
  134. f, _ := os.OpenFile(github_output, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644)
  135. fmt.Fprintf(f, "%s=%s\n", key, val)
  136. f.Close()
  137. }