prow/pjutil/pjutil.go
func JobURL(plank config.Plank, pj prowapi.ProwJob, log *logrus.Entry) string {
	if pj.Spec.DecorationConfig != nil && plank.GetJobURLPrefix(pj.Spec.Refs) != "" {
		spec := downwardapi.NewJobSpec(pj.Spec, pj.Status.BuildID, pj.Name)
		gcsConfig := pj.Spec.DecorationConfig.GCSConfiguration
		_, gcsPath, _ := gcsupload.PathsForJob(gcsConfig, &spec, "")

		prefix, _ := url.Parse(plank.GetJobURLPrefix(pj.Spec.Refs))
		prefix.Path = path.Join(prefix.Path, gcsConfig.Bucket, gcsPath)
		return prefix.String()
	}
	var b bytes.Buffer
	if err := plank.JobURLTemplate.Execute(&b, &pj); err != nil {
		log.WithFields(ProwJobFields(&pj)).Errorf("error executing URL template: %v", err)
	} else {
		return b.String()
	}
	return ""
}
func JobURL(plank config.Plank, pj prowapi.ProwJob, log *logrus.Entry) string {
	if pj.Spec.DecorationConfig != nil && plank.GetJobURLPrefix(pj.Spec.Refs) != "" {
		spec := downwardapi.NewJobSpec(pj.Spec, pj.Status.BuildID, pj.Name)
		gcsConfig := pj.Spec.DecorationConfig.GCSConfiguration
		_, gcsPath, _ := gcsupload.PathsForJob(gcsConfig, &spec, "")

		prefix, _ := url.Parse(plank.GetJobURLPrefix(pj.Spec.Refs))
		prefix.Path = path.Join(prefix.Path, gcsConfig.Bucket, gcsPath)
		return prefix.String()
	}
	var b bytes.Buffer
	if err := plank.JobURLTemplate.Execute(&b, &pj); err != nil {
		log.WithFields(ProwJobFields(&pj)).Errorf("error executing URL template: %v", err)
	} else {
		return b.String()
	}
	return ""
}
// JobURL returns the expected URL for ProwJobStatus.
//
// TODO(fejta): consider moving default JobURLTemplate and JobURLPrefix out of plank