package main import ( "flag" "fmt" "log" "log/slog" "os" "os/exec" "path/filepath" "strings" "github.com/TecharoHQ/anubis/internal" "github.com/facebookgo/flagenv" ) var ( dockerAnnotations = flag.String("docker-annotations", os.Getenv("DOCKER_METADATA_OUTPUT_ANNOTATIONS"), "Docker image annotations") dockerLabels = flag.String("docker-labels", os.Getenv("DOCKER_METADATA_OUTPUT_LABELS"), "Docker image labels") dockerRepo = flag.String("docker-repo", "registry.int.xeserv.us/techaro/anubis", "Docker image repository for Anubis") dockerTags = flag.String("docker-tags", os.Getenv("DOCKER_METADATA_OUTPUT_TAGS"), "newline separated docker tags including the registry name") githubActor = flag.String("github-actor", "", "GitHub actor") githubEventName = flag.String("github-event-name", "", "GitHub event name") pullRequestID = flag.Int("pull-request-id", -1, "GitHub pull request ID") slogLevel = flag.String("slog-level", "INFO", "logging level (see https://pkg.go.dev/log/slog#hdr-Levels)") knownContributors = []string{ "Xe", } ) func inList(needle string, haystack []string) bool { for _, h := range haystack { if h == needle { return true } } return false } func main() { flagenv.Parse() flag.Parse() internal.InitSlog(*slogLevel) koDockerRepo := strings.TrimRight(*dockerRepo, "/"+filepath.Base(*dockerRepo)) if *githubEventName == "pull_request" && !inList(*githubActor, knownContributors) { if *pullRequestID == -1 { log.Fatal("Must set --pull-request-id when --github-event-name=pull_request") } *dockerRepo = fmt.Sprintf("ttl.sh/techaro/pr-%d/anubis", *pullRequestID) *dockerTags = fmt.Sprintf("ttl.sh/techaro/pr-%d/anubis:24h", *pullRequestID) koDockerRepo = fmt.Sprintf("ttl.sh/techaro/pr-%d", *pullRequestID) slog.Info( "Building image for pull request", "docker-repo", *dockerRepo, "docker-tags", *dockerTags, "github-event-name", *githubEventName, "pull-request-id", *pullRequestID, ) } setOutput("docker_image", strings.SplitN(*dockerTags, "\n", 2)[0]) version, err := run("git describe --tags --always --dirty") if err != nil { log.Fatal(err) } commitTimestamp, err := run("git log -1 --format='%ct'") if err != nil { log.Fatal(err) } slog.Debug( "ko env", "KO_DOCKER_REPO", koDockerRepo, "SOURCE_DATE_EPOCH", commitTimestamp, "VERSION", version, ) os.Setenv("KO_DOCKER_REPO", koDockerRepo) os.Setenv("SOURCE_DATE_EPOCH", commitTimestamp) os.Setenv("VERSION", version) setOutput("version", version) if *dockerTags == "" { log.Fatal("Must set --docker-tags or DOCKER_METADATA_OUTPUT_TAGS") } images, err := parseImageList(*dockerTags) if err != nil { log.Fatalf("can't parse images: %v", err) } for _, img := range images { if img.repository != *dockerRepo { slog.Error( "Something weird is going on. Wanted docker repo differs from contents of --docker-tags. Did a flag get set incorrectly?", "wanted", *dockerRepo, "got", img.repository, "docker-tags", *dockerTags, ) os.Exit(2) } } var tags []string for _, img := range images { tags = append(tags, img.tag) } 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)) if err != nil { log.Fatalf("can't run ko build, check stderr: %v", err) } sp := strings.SplitN(output, "@", 2) setOutput("digest", sp[1]) } type image struct { repository string tag string } func newlineSep2Comma(inp string) string { lines := strings.Split(inp, "\n") return strings.Join(lines, ",") } func parseImageList(imageList string) ([]image, error) { images := strings.Split(imageList, "\n") var result []image for _, img := range images { if img == "" { continue } // reg.xeiaso.net/techaro/anubis:latest // repository: reg.xeiaso.net/techaro/anubis // tag: latest parts := strings.SplitN(img, ":", 2) result = append(result, image{ repository: parts[0], tag: parts[1], }) } if len(result) == 0 { return nil, fmt.Errorf("no images provided, bad flags??") } return result, nil } // run executes a command and returns the trimmed output. func run(command string) (string, error) { bin, err := exec.LookPath("sh") if err != nil { return "", err } slog.Debug("running command", "command", command) cmd := exec.Command(bin, "-c", command) cmd.Stderr = os.Stderr out, err := cmd.Output() if err != nil { return "", err } return strings.TrimSpace(string(out)), nil } func setOutput(key, val string) { fmt.Printf("::set-output name=%s::%s\n", key, val) }