Repository: shipwright-io/build
Pull request #871 (2021-09-21 04:01:29)

Surface Step Results in BuildRun Status
# Changes

Reference  https://github.com/shipwright-io/community/pull/13

Add git commit author result for git source step

Add bundle image digest result for local source code step

Add a function to surface TaksRun results to BuildRun

This PR only includes a subset of the [enhancements](https://github.com/shipwright-io/community/blob/main/ships/0023-surface-results-in-buildruns.md#strategy-results-api-proposal) called for, those are:
- [Build Results Sources API Proposal](https://github.com/shipwright-io/community/blob/main/ships/0023-surface-results-in-buildruns.md#build-results-sources-api-proposal) (excluded surfacing the http source type results since it is an MVP implementation)
- [Build Results Output API Proposal](https://github.com/shipwright-io/community/blob/main/ships/0023-surface-results-in-buildruns.md#build-results-output-api-proposal)

and not including the [Strategy Results API Proposal](https://github.com/shipwright-io/community/blob/main/ships/0023-surface-results-in-buildruns.md#strategy-results-api-proposal) that allows strategy authors to surface their desire result.

# Submitter Checklist

- [x] Includes tests if functionality changed/was added
- [x] Includes docs if changes are user-facing
- [x] [Set a kind label on this PR](https://prow.k8s.io/command-help#kind)  
- [x] Release notes block has been filled in, or marked NONE

See [the contributor guide](https://github.com/shipwright-io/build/blob/main/CONTRIBUTING.md)
for details on coding conventions, github and prow interactions, and the code review process.

# Release Notes

```release-note
You can now see the TaskRun results in BuildRun status
```


openshift-ci[bot]: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by:
To complete the [pull request process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process), please assign **otaviof** after the PR has been reviewed.
You can assign the PR to them by writing `/assign @otaviof` in a comment when ready.

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=shipwright-io%2Fbuild).

<details open>
Needs approval from an approver in each of these files:

- **[OWNERS](https://github.com/shipwright-io/build/blob/main/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["otaviof"]} -->
shahulsonhal: /kind api-change
gabemontero: ok @adambkaplan just told me there is a SHP :-) .... apologies 
openshift-ci[bot]: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by:
To complete the [pull request process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process), please ask for approval from **adambkaplan** after the PR has been reviewed.

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=shipwright-io%2Fbuild).

<details open>
Needs approval from an approver in each of these files:

- **[OWNERS](https://github.com/shipwright-io/build/blob/main/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["adambkaplan"]} -->
shahulsonhal: @gabemontero thanks

>And while it may be obvious to the uninitiated, I'm going to explicitly note (as I did not see an explicit note in the PR description) that this PR takes on a subset of the enhancements called for.

I have updated the description.

> Also, there was a BR result type noted in the EP that I did not see here. See my question below.

I assume you meant the `.status.results` field. It is not included in this PR.
openshift-ci[bot]: @shahulsonhal: PR needs rebase.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
</details>

Patch:
diff --git a/cmd/bundle/main.go b/cmd/bundle/main.go
index d414d089..878d0179 100644
--- a/cmd/bundle/main.go
+++ b/cmd/bundle/main.go
@@ -7,6 +7,7 @@ package main
 import (
 	"context"
 	"fmt"
+	"io/ioutil"
 	"log"
 	"os"
 	"path/filepath"
@@ -16,15 +17,17 @@ import (
 	"github.com/google/go-containerregistry/pkg/authn"
 	"github.com/google/go-containerregistry/pkg/name"
 	"github.com/google/go-containerregistry/pkg/v1/remote"
-	"github.com/shipwright-io/build/pkg/bundle"
 	"github.com/spf13/pflag"
+
+	"github.com/shipwright-io/build/pkg/bundle"
 )
 
 type settings struct {
-	help       bool
-	image      string
-	target     string
-	secretPath string
+	help                  bool
+	image                 string
+	target                string
+	secretPath            string
+	resultFileImageDigest string
 }
 
 var flagValues settings
@@ -36,6 +39,8 @@ func init() {
 	// Main flags of the bundle step
 	pflag.StringVar(&flagValues.image, "image", "", "Location of the bundle image (mandatory)")
 	pflag.StringVar(&flagValues.target, "target", "/workspace/source", "The target directory to place the code")
+	pflag.StringVar(&flagValues.resultFileImageDigest, "result-file-image-digest", "", "A file to write the image digest")
+
 	pflag.StringVar(&flagValues.secretPath, "secret-path", "", "A directory that contains access credentials (optional)")
 }
 
@@ -70,15 +75,29 @@ func Do(ctx context.Context) error {
 	}
 
 	log.Printf("Pulling image %q", ref)
-	if err := bundle.PullAndUnpack(
+
+	img, err := bundle.PullAndUnpack(
 		ref,
 		flagValues.target,
 		remote.WithContext(ctx),
-		remote.WithAuth(auth)); err != nil {
+		remote.WithAuth(auth))
+	if err != nil {
 		return err
 	}
 
 	log.Printf("Image content was extracted to %s\n", flagValues.target)
+
+	if flagValues.resultFileImageDigest != "" {
+		digest, err := (*img).Digest()
+		if err != nil {
+			return fmt.Errorf("retrieving digest of bundle image: %v", err)
+		}
+
+		if err = ioutil.WriteFile(flagValues.resultFileImageDigest, []byte(digest.String()), 0644); err != nil {
+			return err
+		}
+	}
+
 	return nil
 }
 
diff --git a/cmd/git/main.go b/cmd/git/main.go
index 51fd1f97..09a31138 100644
--- a/cmd/git/main.go
+++ b/cmd/git/main.go
@@ -40,14 +40,15 @@ func (e ExitError) Error() string {
 }
 
 type settings struct {
-	help                bool
-	url                 string
-	revision            string
-	depth               uint
-	target              string
-	resultFileCommitSha string
-	secretPath          string
-	skipValidation      bool
+	help                   bool
+	url                    string
+	revision               string
+	depth                  uint
+	target                 string
+	resultFileCommitSha    string
+	resultFileCommitAuthor string
+	secretPath             string
+	skipValidation         bool
 }
 
 var flagValues settings
@@ -68,6 +69,7 @@ func init() {
 	pflag.StringVar(&flagValues.revision, "revision", "", "The revision of the Git repository to be cloned. Optional, defaults to the default branch.")
 	pflag.StringVar(&flagValues.target, "target", "", "The target directory of the clone operation")
 	pflag.StringVar(&flagValues.resultFileCommitSha, "result-file-commit-sha", "", "A file to write the commit sha to.")
+	pflag.StringVar(&flagValues.resultFileCommitAuthor, "result-file-commit-author", "", "A file to write the commit author to.")
 	pflag.StringVar(&flagValues.secretPath, "secret-path", "", "A directory that contains a secret. Either username and password for basic authentication. Or a SSH private key and optionally a known hosts file. Optional.")
 
 	// Optional flag to be able to override the default shallow clone depth,
@@ -138,6 +140,17 @@ func runGitClone(ctx context.Context) error {
 		}
 	}
 
+	if flagValues.resultFileCommitAuthor != "" {
+		output, err := git(ctx, "-C", flagValues.target, "log", "-1", "--pretty=format:%an")
+		if err != nil {
+			return err
+		}
+
+		if err = ioutil.WriteFile(flagValues.resultFileCommitAuthor, []byte(output), 0644); err != nil {
+			return err
+		}
+	}
+
 	return nil
 }
 
diff --git a/cmd/mutate-image/main.go b/cmd/mutate-image/main.go
index ca4a0166..41ef4ab7 100644
--- a/cmd/mutate-image/main.go
+++ b/cmd/mutate-image/main.go
@@ -21,7 +21,7 @@ import (
 	"github.com/docker/cli/cli/config"
 	"github.com/google/go-containerregistry/pkg/crane"
 	"github.com/google/go-containerregistry/pkg/name"
-	v1 "github.com/google/go-containerregistry/pkg/v1"
+	containerreg "github.com/google/go-containerregistry/pkg/v1"
 	"github.com/google/go-containerregistry/pkg/v1/mutate"
 	"github.com/spf13/pflag"
 )
@@ -187,7 +187,7 @@ func runMutateImage(ctx context.Context) error {
 		return fmt.Errorf("mutating config: %v", err)
 	}
 
-	img = mutate.Annotations(img, annotations).(v1.Image)
+	img = mutate.Annotations(img, annotations).(containerreg.Image)
 
 	digest, err := img.Digest()
 	if err != nil {
@@ -241,7 +241,7 @@ func runMutateImage(ctx context.Context) error {
 // GetCompressedImageSize calculate the compressed size of the image.
 // By adding up the config and layer sizes we will get the
 // total compressed size of the image
-func GetCompressedImageSize(img v1.Image) (int64, error) {
+func GetCompressedImageSize(img containerreg.Image) (int64, error) {
 	manifest, err := img.Manifest()
 	if err != nil {
 		return 0, err
diff --git a/deploy/crds/shipwright.io_buildruns.yaml b/deploy/crds/shipwright.io_buildruns.yaml
index ec30e601..c6c9664c 100644
--- a/deploy/crds/shipwright.io_buildruns.yaml
+++ b/deploy/crds/shipwright.io_buildruns.yaml
@@ -309,6 +309,46 @@ spec:
               latestTaskRunRef:
                 description: "LatestTaskRunRef is the name of the TaskRun responsible for executing this BuildRun. \n TODO: This should be called something like \"TaskRunName\""
                 type: string
+              output:
+                description: Output holds the results emitted from step definition of an output
+                properties:
+                  digest:
+                    description: Digest holds the digest of output image
+                    type: string
+                  size:
+                    description: Size holds the compressed size of output image
+                    format: int64
+                    type: integer
+                type: object
+              sources:
+                description: Sources holds the results emitted from the step definition of different sources
+                items:
+                  description: SourceResult holds the results emitted from the different sources
+                  properties:
+                    bundle:
+                      description: Bundle holds the results emitted from from the step definition of bundle source
+                      properties:
+                        digest:
+                          description: Digest hold the image digest result
+                          type: string
+                      type: object
+                    git:
+                      description: Git holds the results emitted from from the step definition of a git source
+                      properties:
+                        commitAuthor:
+                          description: CommitAuthor holds the commit author of a git source
+                          type: string
+                        commitSha:
+                          description: CommitSha holds the commit sha of git source
+                          type: string
+                      type: object
+                    name:
+                      description: Name is the name of source
+                      type: string
+                  required:
+                  - name
+                  type: object
+                type: array
               startTime:
                 description: StartTime is the time the build is actually started.
                 format: date-time
diff --git a/docs/buildrun.md b/docs/buildrun.md
index 9bc0a3e2..3387dc6c 100644
--- a/docs/buildrun.md
+++ b/docs/buildrun.md
@@ -10,12 +10,14 @@ SPDX-License-Identifier: Apache-2.0
 - [BuildRun Controller](#buildrun-controller)
 - [Configuring a BuildRun](#configuring-a-buildrun)
   - [Defining the BuildRef](#defining-the-buildref)
-  - [Defining paramValues](#defining-paramvalues)
+  - [Defining ParamValues](#defining-paramvalues)
   - [Defining the ServiceAccount](#defining-the-serviceaccount)
-- [Canceling a `BuildRun`](#canceling-a-buildrun)  
+- [Canceling a `BuildRun`](#canceling-a-buildrun)
 - [BuildRun Status](#buildrun-status)
-  - [Understanding the state of a BuildRun](#understanding-the-state-of-a-BuildRun)
+  - [Understanding the state of a BuildRun](#understanding-the-state-of-a-buildrun)
   - [Understanding failed BuildRuns](#understanding-failed-buildruns)
+  - [Step Results in BuildRun Status](#step-results-in-buildrun-status)
+  - [Build Snapshot](#build-snapshot)
 - [Relationship with Tekton Tasks](#relationship-with-tekton-tasks)
 
 ## Overview
@@ -213,6 +215,48 @@ To make it easier for users to understand why did a BuildRun failed, users can i
 
 In addition, the `Status.Conditions` will host under the `Message` field a compacted message containing the `kubectl` command to trigger, in order to retrieve the logs.
 
+### Step Results in BuildRun Status
+
+After the successful completion of a `BuildRun`, the `.status` field contains the results (`.status.taskResults`) emitted from the `TaskRun` steps generate by the `BuildRun` controller as part of processing the `BuildRun`. These results contain valuable metadata for users, like the _image digest_ or the _commit sha_ of the source code used for building.
+The results from the source step will be surfaced to the `.status.sources` and the results from 
+the [output step](https://github.com/shipwright-io/build/blob/main/docs/buildstrategies.md#system-results) 
+will be surfaced to the `.status.output` field of a `BuildRun`.
+
+Example of a `BuildRun` with surfaced results for `git` source:
+
+```yaml
+# [...]
+status:
+  buildSpec:
+    # [...]
+  output:
+    digest: sha256:07626e3c7fdd28d5328a8d6df8d29cd3da760c7f5e2070b534f9b880ed093a53
+    size: "1989004"
+  sources:
+  - name: default
+    git:
+      commitAuthor: xxx xxxxxx
+      commitSha: f25822b85021d02059c9ac8a211ef3804ea8fdde
+```
+
+Another example of a `BuildRun` with surfaced results for local source code(`bundle`) source:
+
+```yaml
+# [...]
+status:
+  buildSpec:
+    # [...]
+  output:
+    digest: sha256:07626e3c7fdd28d5328a8d6df8d29cd3da760c7f5e2070b534f9b880ed093a53
+    size: "1989004"
+  sources:
+  - name: default
+    bundle:
+      digest: sha256:0f5e2070b534f9b880ed093a537626e3c7fdd28d5328a8d6df8d29cd3da760c7
+```
+
+**Note**: The digest and size of the output image are only included if the build strategy provides them. See [System results](buildstrategies.md#system-results).
+
 ### Build Snapshot
 
 For every BuildRun controller reconciliation, the `buildSpec` in the Status of the `BuildRun` is updated if an existing owned `TaskRun` is present. During this update, a `Build` resource snapshot is generated and embedded into the `status.buildSpec` path of the `BuildRun`. A `buildSpec` is just a copy of the original `Build` spec, from where the `BuildRun` executed a particular image build. The snapshot approach allows developers to see the original `Build` configuration.
diff --git a/docs/buildstrategies.md b/docs/buildstrategies.md
index fa2adf00..bd71ed73 100644
--- a/docs/buildstrategies.md
+++ b/docs/buildstrategies.md
@@ -277,7 +277,7 @@ Contrary to the strategy `spec.parameters`, you can use system parameters and th
 
 ## System results
 
-You can optionally store the size and digest of the image your build strategy created to some files. This information will eventually be made available in the status of the BuildRun.
+You can optionally store the size and digest of the image your build strategy created to a set of files.
 
 | Result file                        | Description                                     |
 | ---------------------------------- | ----------------------------------------------- |
@@ -286,6 +286,20 @@ You can optionally store the size and digest of the image your build strategy cr
 
 You can look at sample build strategies, such as [Kaniko](../samples/buildstrategy/kaniko/buildstrategy_kaniko_cr.yaml), or [Buildpacks](../samples/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3_cr.yaml), to see how they fill some or all of the results files.
 
+This information will be available in the `.status.output` field of the BuildRun.
+
+```yaml
+apiVersion: shipwright.io/v1alpha1
+kind: BuildRun
+# [...]
+status:
+ # [...]
+  output:
+    digest: sha256:07626e3c7fdd28d5328a8d6df8d29cd3da760c7f5e2070b534f9b880ed093a53
+    size: "1989004"
+  # [...]
+```
+
 ## Steps Resource Definition
 
 All strategies steps can include a definition of resources(_limits and requests_) for CPU, memory and disk. For strategies with more than one step, each step(_container_) could require more resources than others. Strategy admins are free to define the values that they consider the best fit for each step. Also, identical strategies with the same steps that are only different in their name and step resources can be installed on the cluster to allow users to create a build with smaller and larger resource requirements.
diff --git a/pkg/apis/build/v1alpha1/buildrun_types.go b/pkg/apis/build/v1alpha1/buildrun_types.go
index b6371b34..fb11e31c 100644
--- a/pkg/apis/build/v1alpha1/buildrun_types.go
+++ b/pkg/apis/build/v1alpha1/buildrun_types.go
@@ -64,8 +64,61 @@ const (
 	BuildRunStatePodEvicted = "PodEvicted"
 )
 
+// SourceResult holds the results emitted from the different sources
+type SourceResult struct {
+	// Name is the name of source
+	Name string `json:"name"`
+
+	// Git holds the results emitted from from the
+	// step definition of a git source
+	//
+	// +optional
+	Git *GitSourceResult `json:"git,omitempty"`
+
+	// Bundle holds the results emitted from from the
+	// step definition of bundle source
+	//
+	// +optional
+	Bundle *BundleSourceResult `json:"bundle,omitempty"`
+}
+
+// BundleSourceResult holds the results emitted from the bundle source
+type BundleSourceResult struct {
+	// Digest hold the image digest result
+	Digest string `json:"digest,omitempty"`
+}
+
+// GitSourceResult holds the results emitted from the git source
+type GitSourceResult struct {
+	// CommitSha holds the commit sha of git source
+	CommitSha string `json:"commitSha,omitempty"`
+
+	// CommitAuthor holds the commit author of a git source
+	CommitAuthor string `json:"commitAuthor,omitempty"`
+}
+
+// Output holds the results emitted from the output step (build-and-push)
+type Output struct {
+	// Digest holds the digest of output image
+	Digest string `json:"digest,omitempty"`
+
+	// Size holds the compressed size of output image
+	Size int64 `json:"size,omitempty"`
+}
+
 // BuildRunStatus defines the observed state of BuildRun
 type BuildRunStatus struct {
+	// Sources holds the results emitted from the step definition
+	// of different sources
+	//
+	// +optional
+	Sources []SourceResult `json:"sources"`
+
+	// Output holds the results emitted from step definition of an output
+	//
+	// +optional
+	Output *Output `json:"output"`
+
 	// Conditions holds the latest available observations of a resource's current state.
 	Conditions Conditions `json:"conditions,omitempty"`
 
diff --git a/pkg/bundle/bundle.go b/pkg/bundle/bundle.go
index b3b11eba..1b59a581 100644
--- a/pkg/bundle/bundle.go
+++ b/pkg/bundle/bundle.go
@@ -18,6 +18,7 @@ import (
 
 	"github.com/go-git/go-git/v5/plumbing/format/gitignore"
 	"github.com/google/go-containerregistry/pkg/name"
+	containerreg "github.com/google/go-containerregistry/pkg/v1"
 	"github.com/google/go-containerregistry/pkg/v1/empty"
 	"github.com/google/go-containerregistry/pkg/v1/mutate"
 	"github.com/google/go-containerregistry/pkg/v1/remote"
@@ -68,21 +69,25 @@ func PackAndPush(ref name.Reference, directory string, options ...remote.Option)
 // PullAndUnpack a container image layer content into a local directory. Analog
 // to the bundle.PackAndPush function, optional remote.Option can be used to
 // configure settings for the image pull, i.e. access credentials.
-func PullAndUnpack(ref name.Reference, targetPath string, options ...remote.Option) error {
+func PullAndUnpack(ref name.Reference, targetPath string, options ...remote.Option) (*containerreg.Image, error) {
 	desc, err := remote.Get(ref, options...)
 	if err != nil {
-		return err
+		return nil, err
 	}
 
 	image, err := desc.Image()
 	if err != nil {
-		return err
+		return nil, err
 	}
 
 	rc := mutate.Extract(image)
 	defer rc.Close()
 
-	return Unpack(rc, targetPath)
+	if err = Unpack(rc, targetPath); err != nil {
+		return nil, err
+	}
+
+	return &image, nil
 }
 
 // Pack reads a directory and creates a tar stream with its content by:
diff --git a/pkg/reconciler/buildrun/buildrun.go b/pkg/reconciler/buildrun/buildrun.go
index bbb68f39..dee37907 100644
--- a/pkg/reconciler/buildrun/buildrun.go
+++ b/pkg/reconciler/buildrun/buildrun.go
@@ -297,6 +297,11 @@ func (r *ReconcileBuildRun) Reconcile(request reconcile.Request) (reconcile.Resu
 			return reconcile.Result{}, nil
 		}
 
+		if len(lastTaskRun.Status.TaskRunResults) > 0 {
+			ctxlog.Info(ctx, "surfacing taskRun results to BuildRun status", namespace, request.Namespace, name, request.Name)
+			resources.UpdateBuildRunUsingTaskResults(ctx, buildRun, lastTaskRun.Status.TaskRunResults, request)
+		}
+
 		trCondition := lastTaskRun.Status.GetCondition(apis.ConditionSucceeded)
 		if trCondition != nil {
 			if err := resources.UpdateBuildRunUsingTaskRunCondition(ctx, r.client, buildRun, lastTaskRun, trCondition); err != nil {
diff --git a/pkg/reconciler/buildrun/resources/mutate.go b/pkg/reconciler/buildrun/resources/mutate.go
index cddc2306..0d012875 100644
--- a/pkg/reconciler/buildrun/resources/mutate.go
+++ b/pkg/reconciler/buildrun/resources/mutate.go
@@ -37,9 +37,9 @@ func mutateArgs(annotations, labels map[string]string) []string {
 		"--image",
 		fmt.Sprintf("$(params.%s-%s)", prefixParamsResultsVolumes, paramOutputImage),
 		"--result-file-image-digest",
-		fmt.Sprintf("$(results.%s-%s.path)", prefixParamsResultsVolumes, resultImageDigest),
+		fmt.Sprintf("$(results.%s-%s.path)", prefixParamsResultsVolumes, imageDigestResult),
 		"result-file-image-size",
-		fmt.Sprintf("$(results.%s-%s.path)", prefixParamsResultsVolumes, resultImageSize),
+		fmt.Sprintf("$(results.%s-%s.path)", prefixParamsResultsVolumes, imageSizeResult),
 	}
 
 	if len(annotations) > 0 {
diff --git a/pkg/reconciler/buildrun/resources/results.go b/pkg/reconciler/buildrun/resources/results.go
new file mode 100644
index 00000000..ecd12609
--- /dev/null
+++ b/pkg/reconciler/buildrun/resources/results.go
@@ -0,0 +1,73 @@
+// Copyright The Shipwright Contributors
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package resources
+
+import (
+	"context"
+	"fmt"
+	"strconv"
+
+	build "github.com/shipwright-io/build/pkg/apis/build/v1alpha1"
+	"github.com/shipwright-io/build/pkg/ctxlog"
+
+	pipeline "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
+	"sigs.k8s.io/controller-runtime/pkg/reconcile"
+)
+
+const (
+	imageDigestResult = "image-digest"
+	imageSizeResult   = "image-size"
+)
+
+// UpdateBuildRunUsingTaskResults surface the task results
+// to the buildrun
+func UpdateBuildRunUsingTaskResults(
+	ctx context.Context,
+	buildRun *build.BuildRun,
+	taskRunResult []pipeline.TaskRunResult,
+	request reconcile.Request,
+) {
+	// Set source results
+	updateBuildRunStatusWithSourceResult(buildRun, taskRunResult)
+
+	// Initializing output result
+	buildRun.Status.Output = &build.Output{}
+
+	// Set output results
+	updateBuildRunStatusWithOutputResult(ctx, buildRun, taskRunResult, request)
+}
+
+func updateBuildRunStatusWithOutputResult(ctx context.Context, buildRun *build.BuildRun, taskRunResult []pipeline.TaskRunResult, request reconcile.Request) {
+	for _, result := range taskRunResult {
+		switch result.Name {
+		case generateOutputResultName(imageDigestResult):
+			buildRun.Status.Output.Digest = result.Value
+
+		case generateOutputResultName(imageSizeResult):
+			if size, err := strconv.ParseInt(result.Value, 10, 64); err != nil {
+				ctxlog.Info(ctx, "invalid value for output image size from taskRun result", namespace, request.Namespace, name, request.Name, "error", err)
+			} else {
+				buildRun.Status.Output.Size = size
+			}
+		}
+	}
+}
+
+func generateOutputResultName(resultName string) string {
+	return fmt.Sprintf("%s-%s", prefixParamsResultsVolumes, resultName)
+}
+
+func getTaskSpecResults() []pipeline.TaskResult {
+	return []pipeline.TaskResult{
+		{
+			Name:        fmt.Sprintf("%s-%s", prefixParamsResultsVolumes, imageDigestResult),
+			Description: "The digest of the image",
+		},
+		{
+			Name:        fmt.Sprintf("%s-%s", prefixParamsResultsVolumes, imageSizeResult),
+			Description: "The compressed size of the image",
+		},
+	}
+}
diff --git a/pkg/reconciler/buildrun/resources/sources.go b/pkg/reconciler/buildrun/resources/sources.go
index 56f2c29d..22fd4474 100644
--- a/pkg/reconciler/buildrun/resources/sources.go
+++ b/pkg/reconciler/buildrun/resources/sources.go
@@ -8,22 +8,25 @@ import (
 	buildv1alpha1 "github.com/shipwright-io/build/pkg/apis/build/v1alpha1"
 	"github.com/shipwright-io/build/pkg/config"
 	"github.com/shipwright-io/build/pkg/reconciler/buildrun/resources/sources"
-	"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
+
+	pipeline "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
 )
 
+const defaultSourceName = "default"
+
 // AmendTaskSpecWithSources adds steps, results and volumes for spec.source and spec.sources
 func AmendTaskSpecWithSources(
 	cfg *config.Config,
-	taskSpec *v1beta1.TaskSpec,
+	taskSpec *pipeline.TaskSpec,
 	build *buildv1alpha1.Build,
 ) {
 	// create the step for spec.source, either Git or Bundle
 	switch {
 	case build.Spec.Source.BundleContainer != nil:
-		sources.AppendBundleStep(cfg, taskSpec, build.Spec.Source, "default")
+		sources.AppendBundleStep(cfg, taskSpec, build.Spec.Source, defaultSourceName)
 
 	case build.Spec.Source.URL != "":
-		sources.AppendGitStep(cfg, taskSpec, build.Spec.Source, "default")
+		sources.AppendGitStep(cfg, taskSpec, build.Spec.Source, defaultSourceName)
 	}
 
 	// create the step for spec.sources, this will eventually change into different steps depending on the type of the source
@@ -34,3 +37,17 @@ func AmendTaskSpecWithSources(
 		}
 	}
 }
+
+func updateBuildRunStatusWithSourceResult(buildrun *buildv1alpha1.BuildRun, results []pipeline.TaskRunResult) {
+	buildSpec := buildrun.Status.BuildSpec
+
+	switch {
+	case buildSpec.Source.BundleContainer != nil:
+		sources.AppendBundleResult(buildrun, defaultSourceName, results)
+
+	case buildSpec.Source.URL != "":
+		sources.AppendGitResult(buildrun, defaultSourceName, results)
+	}
+
+	// no results for HTTP sources yet
+}
diff --git a/pkg/reconciler/buildrun/resources/sources/bundle.go b/pkg/reconciler/buildrun/resources/sources/bundle.go
index 0c72b256..169b7a0b 100644
--- a/pkg/reconciler/buildrun/resources/sources/bundle.go
+++ b/pkg/reconciler/buildrun/resources/sources/bundle.go
@@ -6,11 +6,13 @@ package sources
 
 import (
 	"fmt"
+	"strings"
 
 	core "k8s.io/api/core/v1"
 
 	build "github.com/shipwright-io/build/pkg/apis/build/v1alpha1"
 	"github.com/shipwright-io/build/pkg/config"
+
 	pipeline "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
 )
 
@@ -21,6 +23,12 @@ func AppendBundleStep(
 	source build.Source,
 	name string,
 ) {
+	// append the result
+	taskSpec.Results = append(taskSpec.Results, pipeline.TaskResult{
+		Name:        fmt.Sprintf("%s-source-%s-image-digest", prefixParamsResultsVolumes, name),
+		Description: "The digest of the bundle image.",
+	})
+
 	// initialize the step from the template
 	bundleStep := pipeline.Step{
 		Container: *cfg.BundleContainerTemplate.DeepCopy(),
@@ -31,6 +39,7 @@ func AppendBundleStep(
 	bundleStep.Container.Args = []string{
 		"--image", source.BundleContainer.Image,
 		"--target", fmt.Sprintf("$(params.%s-%s)", prefixParamsResultsVolumes, paramSourceRoot),
+		"--result-file-image-digest", fmt.Sprintf("$(results.%s-source-%s-image-digest.path)", prefixParamsResultsVolumes, name),
 	}
 
 	// add credentials mount, if provided
@@ -54,3 +63,17 @@ func AppendBundleStep(
 
 	taskSpec.Steps = append(taskSpec.Steps, bundleStep)
 }
+
+// AppendBundleResult append bundle source result to build run
+func AppendBundleResult(buildRun *build.BuildRun, name string, results []pipeline.TaskRunResult) {
+	imageDigest := findResultValue(results, fmt.Sprintf("%s-source-%s-image-digest", prefixParamsResultsVolumes, name))
+
+	if strings.TrimSpace(imageDigest) != "" {
+		buildRun.Status.Sources = append(buildRun.Status.Sources, build.SourceResult{
+			Name: name,
+			Bundle: &build.BundleSourceResult{
+				Digest: imageDigest,
+			},
+		})
+	}
+}
diff --git a/pkg/reconciler/buildrun/resources/sources/git.go b/pkg/reconciler/buildrun/resources/sources/git.go
index 9f222b5f..9386ff59 100644
--- a/pkg/reconciler/buildrun/resources/sources/git.go
+++ b/pkg/reconciler/buildrun/resources/sources/git.go
@@ -6,6 +6,7 @@ package sources
 
 import (
 	"fmt"
+	"strings"
 
 	buildv1alpha1 "github.com/shipwright-io/build/pkg/apis/build/v1alpha1"
 	"github.com/shipwright-io/build/pkg/config"
@@ -13,6 +14,11 @@ import (
 	corev1 "k8s.io/api/core/v1"
 )
 
+const (
+	commitSHAResult    = "commit-sha"
+	commitAuthorResult = "commit-author"
+)
+
 // AppendGitStep appends the Git step and results and volume if needed to the TaskSpec
 func AppendGitStep(
 	cfg *config.Config,
@@ -22,8 +28,11 @@ func AppendGitStep(
 ) {
 	// append the result
 	taskSpec.Results = append(taskSpec.Results, tektonv1beta1.TaskResult{
-		Name:        fmt.Sprintf("%s-source-%s-commit-sha", prefixParamsResultsVolumes, name),
+		Name:        fmt.Sprintf("%s-source-%s-%s", prefixParamsResultsVolumes, name, commitSHAResult),
 		Description: "The commit SHA of the cloned source.",
+	}, tektonv1beta1.TaskResult{
+		Name:        fmt.Sprintf("%s-source-%s-%s", prefixParamsResultsVolumes, name, commitAuthorResult),
+		Description: "The author of the last commit of the cloned source.",
 	})
 
 	// initialize the step from the template
@@ -39,7 +48,9 @@ func AppendGitStep(
 		"--target",
 		fmt.Sprintf("$(params.%s-%s)", prefixParamsResultsVolumes, paramSourceRoot),
 		"--result-file-commit-sha",
-		fmt.Sprintf("$(results.%s-source-%s-commit-sha.path)", prefixParamsResultsVolumes, name),
+		fmt.Sprintf("$(results.%s-source-%s-%s.path)", prefixParamsResultsVolumes, name, commitSHAResult),
+		"--result-file-commit-author",
+		fmt.Sprintf("$(results.%s-source-%s-%s.path)", prefixParamsResultsVolumes, name, commitAuthorResult),
 	}
 
 	// Check if a revision is defined
@@ -76,3 +87,19 @@ func AppendGitStep(
 	// append the git step
 	taskSpec.Steps = append(taskSpec.Steps, gitStep)
 }
+
+// AppendGitResult append git source result to build run
+func AppendGitResult(buildRun *buildv1alpha1.BuildRun, name string, results []tektonv1beta1.TaskRunResult) {
+	commitAuthor := findResultValue(results, fmt.Sprintf("%s-source-%s-%s", prefixParamsResultsVolumes, name, commitAuthorResult))
+	commitSha := findResultValue(results, fmt.Sprintf("%s-source-%s-%s", prefixParamsResultsVolumes, name, commitSHAResult))
+
+	if strings.TrimSpace(commitAuthor) != "" || strings.TrimSpace(commitSha) != "" {
+		buildRun.Status.Sources = append(buildRun.Status.Sources, buildv1alpha1.SourceResult{
+			Name: name,
+			Git: &buildv1alpha1.GitSourceResult{
+				CommitAuthor: commitAuthor,
+				CommitSha:    commitSha,
+			},
+		})
+	}
+}
diff --git a/pkg/reconciler/buildrun/resources/sources/utils.go b/pkg/reconciler/buildrun/resources/sources/utils.go
index c7b8a45b..87e977c7 100644
--- a/pkg/reconciler/buildrun/resources/sources/utils.go
+++ b/pkg/reconciler/buildrun/resources/sources/utils.go
@@ -64,3 +64,13 @@ func SanitizeVolumeNameForSecretName(secretName string) string {
 
 	return sanitizedName
 }
+
+func findResultValue(results []tektonv1beta1.TaskRunResult, name string) string {
+	for _, result := range results {
+		if result.Name == name {
+			return result.Value
+		}
+	}
+
+	return ""
+}
diff --git a/pkg/reconciler/buildrun/resources/taskrun.go b/pkg/reconciler/buildrun/resources/taskrun.go
index c0979ab6..8d5d0c72 100644
--- a/pkg/reconciler/buildrun/resources/taskrun.go
+++ b/pkg/reconciler/buildrun/resources/taskrun.go
@@ -27,9 +27,6 @@ const (
 	paramSourceRoot    = "source-root"
 	paramSourceContext = "source-context"
 
-	resultImageDigest = "image-digest"
-	resultImageSize   = "image-size"
-
 	workspaceSource = "source"
 
 	inputParamBuilder    = "BUILDER_IMAGE"
@@ -107,16 +104,6 @@ func GenerateTaskSpec(
 				Type:        taskv1.ParamTypeString,
 			},
 		},
-		Results: []v1beta1.TaskResult{
-			{
-				Name:        fmt.Sprintf("%s-%s", prefixParamsResultsVolumes, resultImageDigest),
-				Description: "The digest of the image",
-			},
-			{
-				Name:        fmt.Sprintf("%s-%s", prefixParamsResultsVolumes, resultImageSize),
-				Description: "The compressed size of the image",
-			},
-		},
 		Workspaces: []v1beta1.WorkspaceDeclaration{
 			// workspace for the source files
 			{
@@ -125,6 +112,8 @@ func GenerateTaskSpec(
 		},
 	}
 
+	generatedTaskSpec.Results = getTaskSpecResults()
+
 	if build.Spec.Builder != nil {
 		InputBuilder := v1beta1.ParamSpec{
 			Description: "Image containing the build tools/logic",


Test patch:
diff --git a/cmd/bundle/main_test.go b/cmd/bundle/main_test.go
index 5efa6785..cdff9fd2 100644
--- a/cmd/bundle/main_test.go
+++ b/cmd/bundle/main_test.go
@@ -15,6 +15,10 @@ import (
 	. "github.com/onsi/gomega"
 
 	. "github.com/shipwright-io/build/cmd/bundle"
+
+	"github.com/google/go-containerregistry/pkg/name"
+	containerreg "github.com/google/go-containerregistry/pkg/v1"
+	"github.com/google/go-containerregistry/pkg/v1/remote"
 )
 
 var _ = Describe("Bundle Loader", func() {
@@ -32,6 +36,40 @@ var _ = Describe("Bundle Loader", func() {
 		f(path)
 	}
 
+	var withTempFile = func(pattern string, f func(filename string)) {
+		file, err := ioutil.TempFile(os.TempDir(), pattern)
+		Expect(err).ToNot(HaveOccurred())
+		defer os.Remove(file.Name())
+
+		f(file.Name())
+	}
+
+	var filecontent = func(path string) string {
+		data, err := ioutil.ReadFile(path)
+		Expect(err).ToNot(HaveOccurred())
+		return string(data)
+	}
+
+	var getImage = func(tag name.Tag) containerreg.Image {
+		ref, err := name.ParseReference(tag.String())
+		Expect(err).To(BeNil())
+
+		desc, err := remote.Get(ref)
+		Expect(err).To(BeNil())
+
+		img, err := desc.Image()
+		Expect(err).To(BeNil())
+
+		return img
+	}
+
+	var getImageDigest = func(tag name.Tag) containerreg.Hash {
+		digest, err := getImage(tag).Digest()
+		Expect(err).To(BeNil())
+
+		return digest
+	}
+
 	Context("validations and error cases", func() {
 		It("should succeed in case the help is requested", func() {
 			Expect(run("--help")).ToNot(HaveOccurred())
@@ -57,5 +95,23 @@ var _ = Describe("Bundle Loader", func() {
 				Expect(filepath.Join(target, "LICENSE")).To(BeAnExistingFile())
 			})
 		})
+
+		It("should store image digest into file specified in --result-file-image-digest flags", func() {
+			withTempDir(func(target string) {
+				withTempFile("image-digest", func(filename string) {
+					Expect(run(
+						"--image", exampleImage,
+						"--target", target,
+						"--result-file-image-digest",
+						filename,
+					)).ToNot(HaveOccurred())
+
+					tag, err := name.NewTag(exampleImage)
+					Expect(err).To(BeNil())
+
+					Expect(filecontent(filename)).To(Equal(getImageDigest(tag).String()))
+				})
+			})
+		})
 	})
 })
diff --git a/cmd/git/main_test.go b/cmd/git/main_test.go
index f2851a8c..6d542f9b 100644
--- a/cmd/git/main_test.go
+++ b/cmd/git/main_test.go
@@ -335,4 +335,39 @@ var _ = Describe("Git Resource", func() {
 			})
 		})
 	})
+
+	Context("store details in result files", func() {
+		const exampleRepo = "https://github.com/shipwright-io/sample-go"
+
+		It("should store commit-sha into file specified in --result-file-commit-sha flag", func() {
+			withTempFile("commit-sha", func(filename string) {
+				withTempDir(func(target string) {
+					Expect(run(
+						"--url", exampleRepo,
+						"--target", target,
+						"--revision", "v0.1.0",
+						"--result-file-commit-sha", filename,
+					)).ToNot(HaveOccurred())
+
+					Expect(filecontent(filename)).To(Equal("8016b0437a7a09079f961e5003e81e5ad54e6c26"))
+				})
+			})
+		})
+
+		It("should store commit-author into file specified in --result-file-commit-author flag", func() {
+			withTempFile("commit-author", func(filename string) {
+
+				withTempDir(func(target string) {
+					Expect(run(
+						"--url", exampleRepo,
+						"--target", target,
+						"--revision", "v0.1.0",
+						"--result-file-commit-author", filename,
+					)).ToNot(HaveOccurred())
+
+					Expect(filecontent(filename)).To(Equal("Enrique Encalada"))
+				})
+			})
+		})
+	})
 })
diff --git a/cmd/mutate-image/main_test.go b/cmd/mutate-image/main_test.go
index ffb5fe5d..e301ae41 100644
--- a/cmd/mutate-image/main_test.go
+++ b/cmd/mutate-image/main_test.go
@@ -14,7 +14,7 @@ import (
 
 	"github.com/google/go-containerregistry/pkg/authn"
 	"github.com/google/go-containerregistry/pkg/name"
-	v1 "github.com/google/go-containerregistry/pkg/v1"
+	containerreg "github.com/google/go-containerregistry/pkg/v1"
 	"github.com/google/go-containerregistry/pkg/v1/empty"
 	"github.com/google/go-containerregistry/pkg/v1/remote"
 	. "github.com/onsi/ginkgo"
@@ -130,7 +130,7 @@ var _ = Describe("Image Mutate Resource", func() {
 		return string(data)
 	}
 
-	getImage := func(tag name.Tag) v1.Image {
+	getImage := func(tag name.Tag) containerreg.Image {
 		auth := authn.FromConfig(authn.AuthConfig{
 			Username: os.Getenv(regUser),
 			Password: os.Getenv(regPass),
@@ -148,7 +148,7 @@ var _ = Describe("Image Mutate Resource", func() {
 		return img
 	}
 
-	getImageDigest := func(tag name.Tag) v1.Hash {
+	getImageDigest := func(tag name.Tag) containerreg.Hash {
 		digest, err := getImage(tag).Digest()
 		Expect(err).To(BeNil())
 
diff --git a/pkg/reconciler/buildrun/resources/results_test.go b/pkg/reconciler/buildrun/resources/results_test.go
new file mode 100644
index 00000000..7e4229d8
--- /dev/null
+++ b/pkg/reconciler/buildrun/resources/results_test.go
@@ -0,0 +1,140 @@
+// Copyright The Shipwright Contributors
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package resources_test
+
+import (
+	"context"
+
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+
+	build "github.com/shipwright-io/build/pkg/apis/build/v1alpha1"
+	"github.com/shipwright-io/build/pkg/reconciler/buildrun/resources"
+	"github.com/shipwright-io/build/test"
+
+	pipelinev1beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
+	"k8s.io/apimachinery/pkg/types"
+	"sigs.k8s.io/controller-runtime/pkg/reconcile"
+)
+
+var _ = Describe("TaskRun results to BuildRun", func() {
+	var ctl test.Catalog
+
+	Context("when a BuildRun complete successfully", func() {
+		var (
+			taskRunRequest reconcile.Request
+			br             *build.BuildRun
+			tr             *pipelinev1beta1.TaskRun
+		)
+
+		ctx := context.Background()
+
+		// returns a reconcile.Request based on an resource name and namespace
+		newReconcileRequest := func(name string, ns string) reconcile.Request {
+			return reconcile.Request{
+				NamespacedName: types.NamespacedName{
+					Name:      name,
+					Namespace: ns,
+				},
+			}
+		}
+
+		BeforeEach(func() {
+			taskRunRequest = newReconcileRequest("foo-p8nts", "bar")
+			tr = ctl.DefaultTaskRun("foo-p8nts", "bar")
+			br = ctl.DefaultBuildRun("foo", "bar")
+		})
+
+		It("should surface the TaskRun results emitting from default(git) source step", func() {
+			commitSha := "0e0583421a5e4bf562ffe33f3651e16ba0c78591"
+			br.Status.BuildSpec.Source.URL = "https://github.com/shipwright-io/sample-go"
+
+			tr.Status.TaskRunResults = append(tr.Status.TaskRunResults,
+				pipelinev1beta1.TaskRunResult{
+					Name:  "shp-source-default-commit-sha",
+					Value: commitSha,
+				},
+				pipelinev1beta1.TaskRunResult{
+					Name:  "shp-source-default-commit-author",
+					Value: "foo bar",
+				})
+
+			resources.UpdateBuildRunUsingTaskResults(ctx, br, tr.Status.TaskRunResults, taskRunRequest)
+
+			Expect(len(br.Status.Sources)).To(Equal(1))
+			Expect(br.Status.Sources[0].Git.CommitSha).To(Equal(commitSha))
+			Expect(br.Status.Sources[0].Git.CommitAuthor).To(Equal("foo bar"))
+		})
+
+		It("should surface the TaskRun results emitting from default(bundle) source step", func() {
+			bundleImageDigest := "sha256:fe1b73cd25ac3f11dec752755e2"
+			br.Status.BuildSpec.Source.BundleContainer = &build.BundleContainer{
+				Image: "quay.io/shipwright/source-bundle:latest",
+			}
+
+			tr.Status.TaskRunResults = append(tr.Status.TaskRunResults,
+				pipelinev1beta1.TaskRunResult{
+					Name:  "shp-source-default-image-digest",
+					Value: bundleImageDigest,
+				})
+
+			resources.UpdateBuildRunUsingTaskResults(ctx, br, tr.Status.TaskRunResults, taskRunRequest)
+
+			Expect(len(br.Status.Sources)).To(Equal(1))
+			Expect(br.Status.Sources[0].Bundle.Digest).To(Equal(bundleImageDigest))
+		})
+
+		It("should surface the TaskRun results emitting from output step", func() {
+			imageDigest := "sha256:fe1b73cd25ac3f11dec752755e2"
+
+			tr.Status.TaskRunResults = append(tr.Status.TaskRunResults,
+				pipelinev1beta1.TaskRunResult{
+					Name:  "shp-image-digest",
+					Value: imageDigest,
+				},
+				pipelinev1beta1.TaskRunResult{
+					Name:  "shp-image-size",
+					Value: "230",
+				})
+
+			resources.UpdateBuildRunUsingTaskResults(ctx, br, tr.Status.TaskRunResults, taskRunRequest)
+
+			Expect(br.Status.Output.Digest).To(Equal(imageDigest))
+			Expect(br.Status.Output.Size).To(Equal(int64(230)))
+		})
+
+		It("should surface the TaskRun results emitting from source and output step", func() {
+			commitSha := "0e0583421a5e4bf562ffe33f3651e16ba0c78591"
+			imageDigest := "sha256:fe1b73cd25ac3f11dec752755e2"
+			br.Status.BuildSpec.Source.URL = "https://github.com/shipwright-io/sample-go"
+
+			tr.Status.TaskRunResults = append(tr.Status.TaskRunResults,
+				pipelinev1beta1.TaskRunResult{
+					Name:  "shp-source-default-commit-sha",
+					Value: commitSha,
+				},
+				pipelinev1beta1.TaskRunResult{
+					Name:  "shp-source-default-commit-author",
+					Value: "foo bar",
+				},
+				pipelinev1beta1.TaskRunResult{
+					Name:  "shp-image-digest",
+					Value: imageDigest,
+				},
+				pipelinev1beta1.TaskRunResult{
+					Name:  "shp-image-size",
+					Value: "230",
+				})
+
+			resources.UpdateBuildRunUsingTaskResults(ctx, br, tr.Status.TaskRunResults, taskRunRequest)
+
+			Expect(len(br.Status.Sources)).To(Equal(1))
+			Expect(br.Status.Sources[0].Git.CommitSha).To(Equal(commitSha))
+			Expect(br.Status.Sources[0].Git.CommitAuthor).To(Equal("foo bar"))
+			Expect(br.Status.Output.Digest).To(Equal(imageDigest))
+			Expect(br.Status.Output.Size).To(Equal(int64(230)))
+		})
+	})
+})
diff --git a/pkg/reconciler/buildrun/resources/sources/git_test.go b/pkg/reconciler/buildrun/resources/sources/git_test.go
index 023e6da9..b6bd167f 100644
--- a/pkg/reconciler/buildrun/resources/sources/git_test.go
+++ b/pkg/reconciler/buildrun/resources/sources/git_test.go
@@ -34,9 +34,10 @@ var _ = Describe("Git", func() {
 			}, "default")
 		})
 
-		It("adds a result for the commit sha", func() {
-			Expect(len(taskSpec.Results)).To(Equal(1))
+		It("adds results for the commit sha and commit author", func() {
+			Expect(len(taskSpec.Results)).To(Equal(2))
 			Expect(taskSpec.Results[0].Name).To(Equal("shp-source-default-commit-sha"))
+			Expect(taskSpec.Results[1].Name).To(Equal("shp-source-default-commit-author"))
 		})
 
 		It("adds a step", func() {
@@ -50,6 +51,8 @@ var _ = Describe("Git", func() {
 				"$(params.shp-source-root)",
 				"--result-file-commit-sha",
 				"$(results.shp-source-default-commit-sha.path)",
+				"--result-file-commit-author",
+				"$(results.shp-source-default-commit-author.path)",
 			}))
 		})
 	})
@@ -71,9 +74,10 @@ var _ = Describe("Git", func() {
 			}, "default")
 		})
 
-		It("adds a result for the commit sha", func() {
-			Expect(len(taskSpec.Results)).To(Equal(1))
+		It("adds results for the commit sha and commit author", func() {
+			Expect(len(taskSpec.Results)).To(Equal(2))
 			Expect(taskSpec.Results[0].Name).To(Equal("shp-source-default-commit-sha"))
+			Expect(taskSpec.Results[1].Name).To(Equal("shp-source-default-commit-author"))
 		})
 
 		It("adds a volume for the secret", func() {
@@ -94,6 +98,8 @@ var _ = Describe("Git", func() {
 				"$(params.shp-source-root)",
 				"--result-file-commit-sha",
 				"$(results.shp-source-default-commit-sha.path)",
+				"--result-file-commit-author",
+				"$(results.shp-source-default-commit-author.path)",
 				"--secret-path",
 				"/workspace/shp-source-secret",
 			}))
diff --git a/pkg/reconciler/buildrun/resources/taskrun_test.go b/pkg/reconciler/buildrun/resources/taskrun_test.go
index 28a104a0..c437e9c3 100644
--- a/pkg/reconciler/buildrun/resources/taskrun_test.go
+++ b/pkg/reconciler/buildrun/resources/taskrun_test.go
@@ -84,6 +84,8 @@ var _ = Describe("GenerateTaskrun", func() {
 					"$(params.shp-source-root)",
 					"--result-file-commit-sha",
 					"$(results.shp-source-default-commit-sha.path)",
+					"--result-file-commit-author",
+					"$(results.shp-source-default-commit-author.path)",
 				}))
 			})
 
diff --git a/test/e2e/e2e_bundle_test.go b/test/e2e/e2e_bundle_test.go
index 9f8ed8ef..e0f7d264 100644
--- a/test/e2e/e2e_bundle_test.go
+++ b/test/e2e/e2e_bundle_test.go
@@ -77,6 +77,7 @@ var _ = Describe("For a Kubernetes cluster with Tekton and build installed", fun
 			Expect(err).ToNot(HaveOccurred())
 
 			validateBuildRunToSucceed(testBuild, buildRun)
+			validateBuildRunResultsFromBundleSource(buildRun)
 		})
 
 		It("should work with Buildpacks build strategy", func() {
@@ -99,6 +100,7 @@ var _ = Describe("For a Kubernetes cluster with Tekton and build installed", fun
 			Expect(err).ToNot(HaveOccurred())
 
 			validateBuildRunToSucceed(testBuild, buildRun)
+			validateBuildRunResultsFromBundleSource(buildRun)
 		})
 
 		It("should work with Buildah build strategy", func() {
@@ -122,6 +124,7 @@ var _ = Describe("For a Kubernetes cluster with Tekton and build installed", fun
 			Expect(err).ToNot(HaveOccurred())
 
 			validateBuildRunToSucceed(testBuild, buildRun)
+			validateBuildRunResultsFromBundleSource(buildRun)
 		})
 	})
 })
diff --git a/test/e2e/e2e_image_mutate_test.go b/test/e2e/e2e_image_mutate_test.go
index e9969ff4..3d1f8a61 100644
--- a/test/e2e/e2e_image_mutate_test.go
+++ b/test/e2e/e2e_image_mutate_test.go
@@ -11,7 +11,7 @@ import (
 
 	"github.com/google/go-containerregistry/pkg/authn"
 	"github.com/google/go-containerregistry/pkg/name"
-	v1 "github.com/google/go-containerregistry/pkg/v1"
+	containerreg "github.com/google/go-containerregistry/pkg/v1"
 	"github.com/google/go-containerregistry/pkg/v1/remote"
 	. "github.com/onsi/ginkgo"
 	. "github.com/onsi/gomega"
@@ -114,7 +114,7 @@ func getRegistryAuthentication(
 	return authn.FromConfig(authConfig.Auths[registryName])
 }
 
-func getImage(build *buildv1alpha1.Build) v1.Image {
+func getImage(build *buildv1alpha1.Build) containerreg.Image {
 	// In the GitHub action, we are using a registry inside the cluster to
 	// push the image created by `buildRun`. The registry inside the cluster
 	// is not directly accessible from the local, so that we have mapped
@@ -135,14 +135,14 @@ func getImage(build *buildv1alpha1.Build) v1.Image {
 	return img
 }
 
-func getImageAnnotation(img v1.Image, annotation string) string {
+func getImageAnnotation(img containerreg.Image, annotation string) string {
 	manifest, err := img.Manifest()
 	Expect(err).To(BeNil())
 
 	return manifest.Annotations[annotation]
 }
 
-func getImageLabel(img v1.Image, label string) string {
+func getImageLabel(img containerreg.Image, label string) string {
 	config, err := img.ConfigFile()
 	Expect(err).To(BeNil())
 
diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go
index c65e32b4..ea89b000 100644
--- a/test/e2e/e2e_test.go
+++ b/test/e2e/e2e_test.go
@@ -55,11 +55,12 @@ var _ = Describe("For a Kubernetes cluster with Tekton and build installed", fun
 			)
 		})
 
-		It("successfully runs a build", func() {
+		It("successfully runs a build and surface results to BuildRun", func() {
 			buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/buildrun/buildrun_buildah_cr.yaml")
 			Expect(err).ToNot(HaveOccurred(), "Error retrieving buildrun test data")
 
 			validateBuildRunToSucceed(testBuild, buildRun)
+			validateBuildRunResultsFromGitSource(buildRun)
 		})
 	})
 
@@ -125,11 +126,12 @@ var _ = Describe("For a Kubernetes cluster with Tekton and build installed", fun
 			)
 		})
 
-		It("successfully runs a build", func() {
+		It("successfully runs a build and surface results to BuildRun", func() {
 			buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/buildrun/buildrun_buildpacks-v3-heroku_namespaced_cr.yaml")
 			Expect(err).ToNot(HaveOccurred())
 
 			validateBuildRunToSucceed(testBuild, buildRun)
+			validateBuildRunResultsFromGitSource(buildRun)
 		})
 
 		AfterEach(func() {
@@ -151,11 +153,12 @@ var _ = Describe("For a Kubernetes cluster with Tekton and build installed", fun
 			)
 		})
 
-		It("successfully runs a build", func() {
+		It("successfully runs a build and surface results to BuildRun", func() {
 			buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/buildrun/buildrun_buildpacks-v3_cr.yaml")
 			Expect(err).ToNot(HaveOccurred(), "Error retrieving buildrun test data")
 
 			validateBuildRunToSucceed(testBuild, buildRun)
+			validateBuildRunResultsFromGitSource(buildRun)
 		})
 	})
 
@@ -325,11 +328,12 @@ var _ = Describe("For a Kubernetes cluster with Tekton and build installed", fun
 			)
 		})
 
-		It("successfully runs a build", func() {
+		It("successfully runs a build and surface results to BuildRun", func() {
 			buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/buildrun/buildrun_kaniko_cr.yaml")
 			Expect(err).ToNot(HaveOccurred())
 
 			validateBuildRunToSucceed(testBuild, buildRun)
+			validateBuildRunResultsFromGitSource(buildRun)
 		})
 	})
 
@@ -388,11 +392,12 @@ var _ = Describe("For a Kubernetes cluster with Tekton and build installed", fun
 			)
 		})
 
-		It("successfully runs a build", func() {
+		It("successfully runs a build and surface results to BuildRun", func() {
 			buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/buildrun/buildrun_kaniko-trivy-good_cr.yaml")
 			Expect(err).ToNot(HaveOccurred())
 
 			validateBuildRunToSucceed(testBuild, buildRun)
+			validateBuildRunResultsFromGitSource(buildRun)
 		})
 	})
 
@@ -430,11 +435,12 @@ var _ = Describe("For a Kubernetes cluster with Tekton and build installed", fun
 			)
 		})
 
-		It("successfully runs a build", func() {
+		It("successfully runs a build and surface results to BuildRun", func() {
 			buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/buildrun/buildrun_buildkit_cr.yaml")
 			Expect(err).ToNot(HaveOccurred())
 
 			validateBuildRunToSucceed(testBuild, buildRun)
+			validateBuildRunResultsFromGitSource(buildRun)
 		})
 	})
 
@@ -451,11 +457,12 @@ var _ = Describe("For a Kubernetes cluster with Tekton and build installed", fun
 			)
 		})
 
-		It("successfully runs a build", func() {
+		It("successfully runs a build and surface results to BuildRun", func() {
 			buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/buildrun/buildrun_source-to-image_cr.yaml")
 			Expect(err).ToNot(HaveOccurred(), "Error retrieving buildrun test data")
 
 			validateBuildRunToSucceed(testBuild, buildRun)
+			validateBuildRunResultsFromGitSource(buildRun)
 		})
 	})
 
diff --git a/test/e2e/validators_test.go b/test/e2e/validators_test.go
index 3da67f59..8a8067fc 100644
--- a/test/e2e/validators_test.go
+++ b/test/e2e/validators_test.go
@@ -147,6 +147,54 @@ func validateBuildRunToSucceed(testBuild *utils.TestBuild, testBuildRun *buildv1
 	Logf("Test build '%s' is completed after %v !", testBuildRun.GetName(), testBuildRun.Status.CompletionTime.Time.Sub(testBuildRun.Status.StartTime.Time))
 }
 
+func validateBuildRunResultsFromGitSource(testBuildRun *buildv1alpha1.BuildRun) {
+	testBuildRun, err := testBuild.GetBR(testBuildRun.Name)
+	Expect(err).ToNot(HaveOccurred())
+
+	tr, err := testBuild.GetTaskRunFromBuildRun(testBuildRun.Name)
+	Expect(err).ToNot(HaveOccurred())
+
+	Expect(len(testBuildRun.Status.Sources)).To(Equal(1))
+
+	for _, result := range tr.Status.TaskRunResults {
+		switch result.Name {
+		case "shp-source-default-commit-sha":
+			Expect(result.Value).To(Equal(testBuildRun.Status.Sources[0].Git.CommitSha))
+		case "shp-source-default-commit-author":
+			Expect(result.Value).To(Equal(testBuildRun.Status.Sources[0].Git.CommitAuthor))
+		case "shp-image-digest":
+			Expect(result.Value).To(Equal(testBuildRun.Status.Output.Digest))
+		case "shp-image-size":
+			size, err := strconv.ParseInt(result.Value, 10, 64)
+			Expect(err).To(BeNil())
+			Expect(size).To(Equal(testBuildRun.Status.Output.Size))
+		}
+	}
+}
+
+func validateBuildRunResultsFromBundleSource(testBuildRun *buildv1alpha1.BuildRun) {
+	testBuildRun, err := testBuild.GetBR(testBuildRun.Name)
+	Expect(err).ToNot(HaveOccurred())
+
+	tr, err := testBuild.GetTaskRunFromBuildRun(testBuildRun.Name)
+	Expect(err).ToNot(HaveOccurred())
+
+	Expect(len(testBuildRun.Status.Sources)).To(Equal(1))
+
+	for _, result := range tr.Status.TaskRunResults {
+		switch result.Name {
+		case "shp-source-default-image-digest":
+			Expect(result.Value).To(Equal(testBuildRun.Status.Sources[0].Bundle.Digest))
+		case "shp-image-digest":
+			Expect(result.Value).To(Equal(testBuildRun.Status.Output.Digest))
+		case "shp-image-size":
+			size, err := strconv.ParseInt(result.Value, 10, 64)
+			Expect(err).To(BeNil())
+			Expect(size).To(Equal(testBuildRun.Status.Output.Size))
+		}
+	}
+}
+
 // validateBuildRunToFail creates the build run and watches its flow until it fails.
 func validateBuildRunToFail(testBuild *utils.TestBuild, testBuildRun *buildv1alpha1.BuildRun) {
 	trueCondition := corev1.ConditionTrue