package infra import ( "fmt" "sort" "strings" ) // GenerateDockerfile genera el texto de un Dockerfile multi-stage para una app Go. // Stage build: golang:1.23-alpine — descarga dependencias y compila. // Stage final: alpine:latest — copia el binario, expone el puerto y lo ejecuta. // Las envVars se inyectan como instrucciones ENV en el stage final. // Funcion pura: no realiza I/O, solo genera texto. func GenerateDockerfile(binaryName string, port int, envVars map[string]string) string { var sb strings.Builder // Stage build sb.WriteString("# Stage build\n") sb.WriteString("FROM golang:1.23-alpine AS builder\n\n") sb.WriteString("WORKDIR /app\n\n") sb.WriteString("COPY go.mod go.sum ./\n") sb.WriteString("RUN go mod download\n\n") sb.WriteString("COPY . .\n") sb.WriteString(fmt.Sprintf("RUN CGO_ENABLED=0 GOOS=linux go build -trimpath -ldflags=\"-s -w\" -o %s .\n\n", binaryName)) // Stage final sb.WriteString("# Stage final\n") sb.WriteString("FROM alpine:latest\n\n") sb.WriteString("RUN apk --no-cache add ca-certificates tzdata\n\n") sb.WriteString("WORKDIR /app\n\n") sb.WriteString(fmt.Sprintf("COPY --from=builder /app/%s .\n\n", binaryName)) // ENV vars (orden determinista) if len(envVars) > 0 { keys := make([]string, 0, len(envVars)) for k := range envVars { keys = append(keys, k) } sort.Strings(keys) for _, k := range keys { sb.WriteString(fmt.Sprintf("ENV %s=%s\n", k, envVars[k])) } sb.WriteString("\n") } if port > 0 { sb.WriteString(fmt.Sprintf("EXPOSE %d\n\n", port)) } sb.WriteString(fmt.Sprintf("ENTRYPOINT [\"./%s\"]\n", binaryName)) return sb.String() }