func BuildEmail(cfg *Config, build *cbpb.Build) ([]byte, error) {
	var b bytes.Buffer
	mw := multipart.NewWriter(&b)

	// Write headers.
	writeHead := func(n, v string) { io.WriteString(&b, n+": "+v+"\r\n") }
	writeHead("From", cfg.emailFrom.String())
	// TODO: Preserve names instead of just using addresses?
	writeHead("To", strings.Join(cfg.emailRecipientsAddrs(), ", "))
	writeHead("Subject",
		fmt.Sprintf("[%s] %s %s (build %s)", build.ProjectId,
			buildSub(build, triggerNameSub, "[unknown]"),
			build.Status, strings.Split(build.Id, "-")[0]))
	writeHead("Date", time.Now().In(cfg.emailTimeZone).Format(time.RFC1123Z))
	writeHead("MIME-Version", "1.0")
	writeHead("Content-Type", "multipart/alternative; boundary="+mw.Boundary())
	io.WriteString(&b, "\r\n")

	writeBody := func(ctype string, f func(io.Writer) error) error {
		head := make(textproto.MIMEHeader)
		head.Add("Content-Type", ctype)
		pw, err := mw.CreatePart(head)
		if err != nil {
			return err
		}
		return f(pw)
	}

	const timeFmt = time.RFC1123Z // "Mon, 02 Jan 2006 15:04:05 -0700"
	start := build.StartTime.AsTime()
	end := build.FinishTime.AsTime()
	tdata := struct {
		BuildID     string
		LogURL      string
		TriggerID   string
		TriggerName string
		TriggerURL  string
		Status      string
		Repo        string
		Commit      string
		Branch      string
		Start       string
		End         string
		Duration    string
	}{
		BuildID:     build.Id,
		LogURL:      build.LogUrl,
		TriggerID:   build.BuildTriggerId,
		TriggerName: buildSub(build, triggerNameSub, ""),
		TriggerURL:  "https://console.cloud.google.com/cloud-build/triggers/edit/" + build.BuildTriggerId,
		Status:      build.Status.String(),
		Repo:        buildSub(build, repoSub, ""),
		Commit:      buildSub(build, commitSub, ""),
		Branch:      buildSub(build, branchSub, ""),
		Start:       start.In(cfg.emailTimeZone).Format(timeFmt),
		End:         end.In(cfg.emailTimeZone).Format(timeFmt),
		Duration:    formatDuration(end.Sub(start)),
	}

	// Add plain text part.
	if err := writeBody("text/plain; charset=UTF-8", func(w io.Writer) error {
		tmpl, err := ttemplate.New("").Parse(strings.TrimSpace(textTemplate))
		if err != nil {
			return err
		}
		return tmpl.Execute(w, tdata)
	}); err != nil {
		return nil, fmt.Errorf("text: %v", err)
	}

	// Add HTML part.
	if err := writeBody("text/html; charset=UTF-8", func(w io.Writer) error {
		tmpl, err := htemplate.New("").Parse(strings.TrimSpace(htmlTemplate))
		if err != nil {
			return err
		}
		return tmpl.Execute(w, tdata)
	}); err != nil {
		return nil, fmt.Errorf("HTML: %v", err)
	}

	// Finish up the message.
	if err := mw.Close(); err != nil {
		return nil, err
	}
	return b.Bytes(), nil
}
// BuildEmail constructs an email message describing build per cfg.
// It is exported so it can be used by the test_email program.
BuildEmail constructs an email message describing build per cfg.
It is exported so it can be used by the test_email program.
// BuildEmail constructs an email message describing build per cfg . // It is exported so it can be used by the test_email program .
func BuildEmail(cfg *Config, build *cbpb.Build) ([]byte, error) {
	var b bytes.Buffer
	mw := multipart.NewWriter(&b)
	writeHead := func(n, v string) { io.WriteString(&b, n+": "+v+"\r\n") }
	writeHead("From", cfg.emailFrom.String())
	writeHead("To", strings.Join(cfg.emailRecipientsAddrs(), ", "))
	writeHead("Subject",
		fmt.Sprintf("[%s] %s %s (build %s)", build.ProjectId,
			buildSub(build, triggerNameSub, "[unknown]"),
			build.Status, strings.Split(build.Id, "-")[0]))
	writeHead("Date", time.Now().In(cfg.emailTimeZone).Format(time.RFC1123Z))
	writeHead("MIME-Version", "1.0")
	writeHead("Content-Type", "multipart/alternative; boundary="+mw.Boundary())
	io.WriteString(&b, "\r\n")
	writeBody := func(ctype string, f func(io.Writer) error) error {
		head := make(textproto.MIMEHeader)
		head.Add("Content-Type", ctype)
		pw, err := mw.CreatePart(head)
		if err != nil {
			return err
		}
		return f(pw)
	}
	const timeFmt = time.RFC1123Z 
	start := build.StartTime.AsTime()
	end := build.FinishTime.AsTime()
	tdata := struct {
		BuildID     string
		LogURL      string
		TriggerID   string
		TriggerName string
		TriggerURL  string
		Status      string
		Repo        string
		Commit      string
		Branch      string
		Start       string
		End         string
		Duration    string
	}{
		BuildID:     build.Id,
		LogURL:      build.LogUrl,
		TriggerID:   build.BuildTriggerId,
		TriggerName: buildSub(build, triggerNameSub, ""),
		TriggerURL:  "https://console.cloud.google.com/cloud-build/triggers/edit/" + build.BuildTriggerId,
		Status:      build.Status.String(),
		Repo:        buildSub(build, repoSub, ""),
		Commit:      buildSub(build, commitSub, ""),
		Branch:      buildSub(build, branchSub, ""),
		Start:       start.In(cfg.emailTimeZone).Format(timeFmt),
		End:         end.In(cfg.emailTimeZone).Format(timeFmt),
		Duration:    formatDuration(end.Sub(start)),
	}
	if err := writeBody("text/plain; charset=UTF-8", func(w io.Writer) error {
		tmpl, err := ttemplate.New("").Parse(strings.TrimSpace(textTemplate))
		if err != nil {
			return err
		}
		return tmpl.Execute(w, tdata)
	}); err != nil {
		return nil, fmt.Errorf("text: %v", err)
	}
	if err := writeBody("text/html; charset=UTF-8", func(w io.Writer) error {
		tmpl, err := htemplate.New("").Parse(strings.TrimSpace(htmlTemplate))
		if err != nil {
			return err
		}
		return tmpl.Execute(w, tdata)
	}); err != nil {
		return nil, fmt.Errorf("HTML: %v", err)
	}
	if err := mw.Close(); err != nil {
		return nil, err
	}
	return b.Bytes(), nil
}
func BuildEmail ( cfg * Config , build * cbpb . Build ) ( [ ] byte , error ) { var b bytes . Buffer mw := multipart . NewWriter ( & b ) writeHead := func ( n , v string ) { io . WriteString ( & b , n + " " + v + " \r \n " ) } writeHead ( " " , cfg . emailFrom . String ( ) ) writeHead ( " " , strings . Join ( cfg . emailRecipientsAddrs ( ) , " " ) ) writeHead ( " " , fmt . Sprintf ( " " , build . ProjectId , buildSub ( build , triggerNameSub , " " ) , build . Status , strings . Split ( build . Id , " " ) [ 0 ] ) ) writeHead ( " " , time . Now ( ) . In ( cfg . emailTimeZone ) . Format ( time . RFC1123Z ) ) writeHead ( " " , " " ) writeHead ( " " , " " + mw . Boundary ( ) ) io . WriteString ( & b , " \r \n " ) writeBody := func ( ctype string , f func ( io . Writer ) error ) error { head := make ( textproto . MIMEHeader ) head . Add ( " " , ctype ) pw , err := mw . CreatePart ( head ) if err != nil { return err } return f ( pw ) } const timeFmt = time . RFC1123Z start := build . StartTime . AsTime ( ) end := build . FinishTime . AsTime ( ) tdata := struct { BuildID string LogURL string TriggerID string TriggerName string TriggerURL string Status string Repo string Commit string Branch string Start string End string Duration string } { BuildID : build . Id , LogURL : build . LogUrl , TriggerID : build . BuildTriggerId , TriggerName : buildSub ( build , triggerNameSub , " " ) , TriggerURL : " " + build . BuildTriggerId , Status : build . Status . String ( ) , Repo : buildSub ( build , repoSub , " " ) , Commit : buildSub ( build , commitSub , " " ) , Branch : buildSub ( build , branchSub , " " ) , Start : start . In ( cfg . emailTimeZone ) . Format ( timeFmt ) , End : end . In ( cfg . emailTimeZone ) . Format ( timeFmt ) , Duration : formatDuration ( end . Sub ( start ) ) , } if err := writeBody ( " " , func ( w io . Writer ) error { tmpl , err := ttemplate . New ( " " ) . Parse ( strings . TrimSpace ( textTemplate ) ) if err != nil { return err } return tmpl . Execute ( w , tdata ) } ) ; err != nil { return nil , fmt . Errorf ( " " , err ) } if err := writeBody ( " " , func ( w io . Writer ) error { tmpl , err := htemplate . New ( " " ) . Parse ( strings . TrimSpace ( htmlTemplate ) ) if err != nil { return err } return tmpl . Execute ( w , tdata ) } ) ; err != nil { return nil , fmt . Errorf ( " " , err ) } if err := mw . Close ( ) ; err != nil { return nil , err } return b . Bytes ( ) , nil }
BuildEmail constructs an email message describing build per cfg.
BuildEmail constructs an email message describing build per cfg .
// Write headers. // TODO: Preserve names instead of just using addresses? // "Mon, 02 Jan 2006 15:04:05 -0700" // Add plain text part. // Add HTML part. // Finish up the message.