package main import ( "bufio" "fmt" "os" "os/exec" "strings" "time" gha "github.com/sethvargo/go-githubactions" ) func getShortHash() string { // Get short git commit hash gitCmd := exec.Command("git", "rev-parse", "--short", "HEAD") gitCmd.Dir = "." gitCmd.Stderr = os.Stderr hashBytes, err := gitCmd.Output() if err != nil { fmt.Fprintf(os.Stderr, "Failed to get git hash: %v\n", err) os.Exit(1) } shortHash := string(hashBytes) shortHash = shortHash[:len(shortHash)-1] // remove trailing newline return shortHash } func parseSecrets(secretsInput string) map[string]string { secrets := make(map[string]string) if secretsInput != "" { scanner := bufio.NewScanner(strings.NewReader(secretsInput)) for scanner.Scan() { line := strings.TrimSpace(scanner.Text()) if line == "" || strings.HasPrefix(line, "#") { continue } parts := strings.SplitN(line, "=", 2) if len(parts) == 2 { key := strings.TrimSpace(parts[0]) val := strings.Trim(strings.TrimSpace(parts[1]), `"`) secrets[key] = val } } } return secrets } func buildDockerCommand(context string, dockerFile string, secrets map[string]string, tagLatest string, tagNightly string) *exec.Cmd { // Parse secrets and write to .secrets file buildArgs := []string{} if len(secrets) > 0 { f, err := os.CreateTemp("", ".secrets") if err != nil { fmt.Fprintf(os.Stderr, "Failed to create .secrets file: %v\n", err) os.Exit(1) } defer f.Close() for secretKey, secretVal := range secrets { _, err := fmt.Fprintf(f, "%s=%s\n", secretKey, secretVal) if err != nil { fmt.Fprintf(os.Stderr, "Failed to write secret: %v\n", err) os.Exit(1) } } buildArgs = append(buildArgs, "--secret", "id=build_secrets,src="+f.Name()) } // Build command buildArgsExpanded := append([]string{"build", context, "-f", dockerFile, "-t", tagLatest, "-t", tagNightly}, buildArgs...) buildCmd := exec.Command("docker", buildArgsExpanded...) buildCmd.Env = append(os.Environ(), "DOCKER_BUILDKIT=1") buildCmd.Stdout = os.Stdout buildCmd.Stderr = os.Stderr return buildCmd } func pushDockerCommand(tag string) *exec.Cmd { // Push nightly tag pushNightlyCmd := exec.Command("docker", "push", tag) pushNightlyCmd.Stdout = os.Stdout pushNightlyCmd.Stderr = os.Stderr return pushNightlyCmd } func main() { basePath := gha.GetInput("path") imageName := gha.GetInput("image-name") dockerFile := gha.GetInput("dockerfile") context := gha.GetInput("context") secrets := parseSecrets(gha.GetInput("secrets")) date := time.Now().Format("2006-01-02") shortHash := getShortHash() if basePath == "" { fmt.Fprintln(os.Stderr, "Error: 'path' input is required") os.Exit(1) } if imageName == "" { fmt.Fprintln(os.Stderr, "Error: 'image-name' input is required") os.Exit(1) } if basePath[len(basePath)-1] != '/' { basePath += "/" } // Construct image names imageBase := fmt.Sprintf("%s%s", basePath, imageName) tagLatest := fmt.Sprintf("%s:latest", imageBase) tagNightly := fmt.Sprintf("%s:nightly-%s.%s", imageBase, date, shortHash) // Build Docker command buildCmd := buildDockerCommand(context, dockerFile, secrets, tagLatest, tagNightly) fmt.Println("Running:", buildCmd.String()) if err := buildCmd.Run(); err != nil { fmt.Fprintf(os.Stderr, "Build failed: %v\n", err) os.Exit(1) } // Push nightly tag pushNightlyCmd := pushDockerCommand(tagNightly) fmt.Println("Running:", pushNightlyCmd.String()) if err := pushNightlyCmd.Run(); err != nil { fmt.Fprintf(os.Stderr, "Push nightly failed: %v\n", err) os.Exit(1) } // Push latest tag pushLatestCmd := pushDockerCommand(tagLatest) fmt.Println("Running:", pushLatestCmd.String()) if err := pushLatestCmd.Run(); err != nil { fmt.Fprintf(os.Stderr, "Push latest failed: %v\n", err) os.Exit(1) } gha.SetOutput("image-tag", fmt.Sprintf("nightly-%s.%s", date, shortHash)) gha.SetOutput("full-image-name", tagLatest) }