package main import ( "fmt" "io/ioutil" "os" "os/exec" "path/filepath" "strings" "time" "gopkg.in/yaml.v2" ) type Tag struct { Tag string `yaml:"tag"` Platforms []string `yaml:"platforms"` } type Image struct { Name string `yaml:"name"` From string `yaml:"from,omitempty"` // `from` 为可选字段 Tags []Tag `yaml:"tags"` } type Config struct { Target string `yaml:"target"` Images []Image `yaml:"images"` } func main() { // 记录开始时间 startTime := time.Now() // 创建日志文件名 logFileName := fmt.Sprintf("build_%s.log", startTime.Format("20060102")) logFile, err := os.OpenFile(logFileName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) if err != nil { logErrorInfo(logFile, time.Now(), fmt.Sprintf("Error opening log file: %v", err)) return } defer logFile.Close() // 记录程序启动时间 logStartTime(logFile, startTime) // 读取 YAML 文件 data, err := ioutil.ReadFile("images.yaml") if err != nil { logErrorInfo(logFile, time.Now(), fmt.Sprintf("Error reading YAML file: %v", err)) return } // 解析 YAML 文件 var config Config if err := yaml.Unmarshal(data, &config); err != nil { logErrorInfo(logFile, time.Now(), fmt.Sprintf("Error parsing YAML: %v", err)) return } totalImages := 0 totalTags := 0 // 遍历每个镜像信息 for _, image := range config.Images { // 如果 `from` 字段为空,则将其设置为 `name`(Docker Hub 官方镜像) if image.From == "" { image.From = image.Name } imageBuilt := false // 记录是否构建过该镜像 for _, tag := range image.Tags { totalTags++ // 根据镜像生成 Dockerfile dockerfilePath := filepath.Join(".", fmt.Sprintf("Dockerfile.%s:%s", image.Name, tag.Tag)) if err := generateDockerfile(image.From, tag.Tag, dockerfilePath); err != nil { logErrorInfo(logFile, time.Now(), fmt.Sprintf("Error generating Dockerfile for %s:%s: %v", image.Name, tag.Tag, err)) continue } // 合并所有平台为一个字符串 platforms := strings.Join(tag.Platforms, ",") // 构建 Docker 镜像命令,添加 target buildCmd := exec.Command("docker", "buildx", "build", "--push", "--platform", platforms, "-t", fmt.Sprintf("%s/%s:%s", config.Target, image.Name, tag.Tag), "-f", dockerfilePath, ".") buildCmd.Stdout = os.Stdout buildCmd.Stderr = os.Stderr // 记录开始构建时间 buildStartTime := time.Now() if err := buildCmd.Run(); err != nil { logErrorInfo(logFile, time.Now(), fmt.Sprintf("Error building image %s:%s for platforms %s: %v", image.Name, tag.Tag, platforms, err)) continue } // 记录构建结束时间 buildEndTime := time.Now() // 记录构建日志 logBuildInfo(logFile, buildStartTime, image.Name, tag.Tag, platforms, config.Target, buildEndTime.Sub(buildStartTime)) imageBuilt = true // 标记该镜像已成功构建 // 删除生成的 Dockerfile if err := os.Remove(dockerfilePath); err != nil { logErrorInfo(logFile, time.Now(), fmt.Sprintf("Error deleting Dockerfile %s: %v", dockerfilePath, err)) } } if imageBuilt { totalImages++ // 仅在成功构建过一次时增加镜像计数 } } // 计算总时间 elapsedTime := time.Since(startTime) logCompletion(logFile, elapsedTime, totalImages, totalTags) } // 记录程序启动时间 func logStartTime(logFile *os.File, startTime time.Time) { logFile.WriteString(fmt.Sprintf("Process started at: %s\n", startTime.Format(time.RFC3339))) } // 记录构建信息 func logBuildInfo(logFile *os.File, startTime time.Time, imageName, tag string, platforms string, target string, duration time.Duration) { logFile.WriteString(fmt.Sprintf("%s | Image: %s | Tag: %s | Platforms: %s | Pushed to: %s/%s:%s | Duration: %v\n", startTime.Format(time.RFC3339), imageName, tag, platforms, target, imageName, tag, duration)) } // 记录报错 func logErrorInfo(logFile *os.File, startTime time.Time, errorMessage string) { logFile.WriteString(fmt.Sprintf("%s | %s\n", startTime.Format(time.RFC3339), errorMessage)) } // 记录完成时间和统计信息 func logCompletion(logFile *os.File, elapsedTime time.Duration, totalImages, totalTags int) { logFile.WriteString(fmt.Sprintf("Build completed at: %s\n", time.Now().Format(time.RFC3339))) logFile.WriteString(fmt.Sprintf("Total images built: %d\n", totalImages)) logFile.WriteString(fmt.Sprintf("Total tags processed: %d\n", totalTags)) logFile.WriteString(fmt.Sprintf("Total execution time: %v\n", elapsedTime)) } // 生成 Dockerfile func generateDockerfile(fromImage, tag, filePath string) error { dockerfileContent := fmt.Sprintf("FROM %s:%s\n", fromImage, tag) return ioutil.WriteFile(filePath, []byte(dockerfileContent), 0644) }