Repository: shipwright-io/build
Pull request #770 (2021-05-06 11:48:37)

Remove usage of Tekton resources
# Changes

Related to [Remove usage of Tekton resources #696](https://github.com/shipwright-io/build/issues/696).

Reviewers can go through the changes by commit:

1. **Extend deployment configuration to include template for Git step** extends the deployment configuration with a template for a Git step. I chose JSON as serialization format as this is easier to provide as a one-liner in a deployment yaml. Once we switch to a ConfigMap, we can revisit how we store the data there. The image URL can be configured in the template but is also available as separate environment variable. That's needed as our release build sets only the image URL based on the git image that we build (in commit 5)
2. **Add custom gomega matcher** adds a Gomega matcher to check for an element in an array or slice to contain an item with a certain name, is used in the unit tests in the next commit.
3. **Remove Git and Image resources, introduce common handling of sources, use results** is the main code change. The resources are removed, results are added. I introduced a structure for our different source types so that as we evolve that, we have a nice structure in our source code. Remote artifacts were moved to sources/http to fit into that structure. The runtime image code was extended to correctly fill the image results in the same way as the Kaniko sample strategy (see commit 6).
4. **Stop adding source secret to the service account** stops adding the source secret to the service account as this is directly mounted by the previous commit.
5. **Build git image as part of the release build** extends our deployment with the environment variable for the git image in ko-style. In the release build, I had to workaround the ko limitation that only one image name can be specified (see comment in the file). I just notice that the release.sh here references the `GIT_IMAGE` environment variable, this is set in the Makefile in commit 7. @qu1queee please do not forget to create the git repository in quay as discussed, so that the image can be pushed by the build.
6. **Update sample build strategies to write to results** updates the sample build strategies to write the image sha and size where possible. The EP also suggests solutions for BuildKit and maybe Buildah, those strategies can be extended in a follow-on PR. I focused on those that already supported that feature plus Buildpacks where it is trivial to access the image SHA. The strategy changes also outline that less `chown` in prepare steps is necessary for non-root build strategies. Remaining is the `chown` for the Tekton home directory that we will get rid of once we also move away from the service account and creds-init for the output image credential.
7. **Update integration test** makes the necessary changes to the integration test cases and updates our CI to build the git image and load it into the KinD cluster.
8. **Update documentation** adds the two results that the build strategy author can use to the build strategy documentation. The authentication documentation is updated to remove the Tekton annotation as that is not needed anymore. I am also removing the link to Tekton's authentication documentation. There's nothing for our users to learn there and we evolve to our own secret handling anyway.
9. **Update EP with information on how the non-root git clone is implemented** updates the EP with information about how to the non-root Git clone is done, and adjusts some naming for the volume mount to prevent possible conflicts with what build strategy authors could use by using the `shp-` prefix there.








# 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

# Release Notes

```release-note
Remove usage of Tekton's pipelines resources in favor of a shipwright-managed step to clone a Git repository, and Tekton results to store information such as Git commit SHA and image digest and size
```


openshift-ci-robot: [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 **saschaschwarze0** 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/master/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":["saschaschwarze0"]} -->
openshift-ci[bot]: @SaschaSchwarze0: 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/.github/workflows/ci.yml b/.github/workflows/ci.yml
index fc677c05..fb2fb028 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -45,6 +45,8 @@ jobs:
           uses: actions/setup-go@v2
           with:
             go-version: ${{ matrix.go-version }}
+        - name: Install Ko
+          uses: imjasonh/setup-ko@20b7695b536c640edfafdd378d96c760460f29d6
         - name: Check out code
           uses: actions/checkout@v2
         - name: Install kubectl
@@ -68,7 +70,9 @@ jobs:
           run: |
             make kind-tekton
         - name: Test
-          run: make test-integration
+          run: |
+            export GIT_CONTAINER_IMAGE="$(KO_DOCKER_REPO=kind.local ko publish ./cmd/git)"
+            make test-integration
     e2e:
         strategy:
           fail-fast: false
diff --git a/Makefile b/Makefile
index cc9447ad..88239a09 100644
--- a/Makefile
+++ b/Makefile
@@ -65,7 +65,7 @@ TEST_SOURCE_SECRET ?=
 
 # Image settings for building and pushing images
 IMAGE_HOST ?= quay.io
-IMAGE ?= shipwright/shipwright-build-controller
+IMAGE_NAMESPACE ?= shipwright
 TAG ?= latest
 
 # options for generating crds with controller-gen
@@ -84,19 +84,19 @@ vendor: go.mod go.sum
 build: $(CONTROLLER)
 
 $(CONTROLLER): vendor
-	go build -trimpath $(GO_FLAGS) -o $(CONTROLLER) cmd/manager/main.go
+	go build -trimpath $(GO_FLAGS) -o $(CONTROLLER) cmd/shipwright-build-controller/main.go
 
 .PHONY: build-plain
 build-plain: 
-	go build -trimpath $(GO_FLAGS) -o $(CONTROLLER) cmd/manager/main.go
+	go build -trimpath $(GO_FLAGS) -o $(CONTROLLER) cmd/shipwright-build-controller/main.go
 
 .PHONY: build-image
 build-image:
-	KO_DOCKER_REPO="$(IMAGE_HOST)/$(IMAGE)" GOFLAGS="$(GO_FLAGS)" ko publish --bare ./cmd/manager
+	KO_DOCKER_REPO="$(IMAGE_HOST)/$(IMAGE_NAMESPACE)" GOFLAGS="$(GO_FLAGS)" ko publish --base-import-paths ./cmd/shipwright-build-controller
 
 .PHONY: build-image-with-pprof
 build-image-with-pprof:
-	KO_DOCKER_REPO="$(IMAGE_HOST)/$(IMAGE)" GOFLAGS="$(GO_FLAGS) -tags=pprof_enabled" ko publish --bare --tags=pprof ./cmd/manager
+	KO_DOCKER_REPO="$(IMAGE_HOST)/$(IMAGE_NAMESPACE)" GOFLAGS="$(GO_FLAGS) -tags=pprof_enabled" ko publish --base-import-paths --tags=pprof ./cmd/shipwright-build-controller
 
 .PHONY: release
 release:
@@ -247,7 +247,7 @@ test-e2e-kind-with-prereq-install: ginkgo install-controller-kind install-strate
 
 install:
 	@echo "Building Shipwright Build controller for platform ${GO_OS}/${GO_ARCH}"
-	GOOS=$(GO_OS) GOARCH=$(GO_ARCH) KO_DOCKER_REPO="$(IMAGE_HOST)/$(IMAGE)" GOFLAGS="$(GO_FLAGS)" ko apply --bare -R -f deploy/
+	GOOS=$(GO_OS) GOARCH=$(GO_ARCH) KO_DOCKER_REPO="$(IMAGE_HOST)/$(IMAGE_NAMESPACE)" GOFLAGS="$(GO_FLAGS)" ko apply --base-import-paths -R -f deploy/
 
 install-with-pprof:
 	GOOS=$(GO_OS) GOARCH=$(GO_ARCH) GOFLAGS="$(GO_FLAGS) -tags=pprof_enabled" ko apply -R -f deploy/
@@ -259,7 +259,7 @@ install-apis:
 
 install-controller: install-apis
 	@echo "Building Shipwright Build controller for platform ${GO_OS}/${GO_ARCH}"
-	GOOS=$(GO_OS) GOARCH=$(GO_ARCH) KO_DOCKER_REPO="$(IMAGE_HOST)/$(IMAGE)" GOFLAGS="$(GO_FLAGS)" ko apply --bare -f deploy/
+	GOOS=$(GO_OS) GOARCH=$(GO_ARCH) KO_DOCKER_REPO="$(IMAGE_HOST)/$(IMAGE_NAMESPACE)" GOFLAGS="$(GO_FLAGS)" ko apply --base-import-paths -f deploy/
 
 install-controller-kind: install-apis
 	KO_DOCKER_REPO=kind.local GOFLAGS="$(GO_FLAGS)" ko apply -f deploy/
@@ -269,11 +269,11 @@ install-strategies: install-apis
 
 local: vendor install-strategies
 	CONTROLLER_NAME=shipwright-build-controller \
-	go run cmd/manager/main.go $(ZAP_FLAGS)
+	go run cmd/shipwright-build-controller/main.go $(ZAP_FLAGS)
 
 local-plain: vendor
 	CONTROLLER_NAME=shipwright-build-controller \
-	go run cmd/manager/main.go $(ZAP_FLAGS)
+	go run cmd/shipwright-build-controller/main.go $(ZAP_FLAGS)
 
 clean:
 	rm -rf $(OUTPUT_DIR)
diff --git a/cmd/manager/main.go b/cmd/shipwright-build-controller/main.go
similarity index 100%
rename from cmd/manager/main.go
rename to cmd/shipwright-build-controller/main.go
diff --git a/deploy/500-controller.yaml b/deploy/500-controller.yaml
index c0339122..3dd2c27f 100644
--- a/deploy/500-controller.yaml
+++ b/deploy/500-controller.yaml
@@ -16,7 +16,7 @@ spec:
       serviceAccountName: shipwright-build-controller
       containers:
         - name: shipwright-build
-          image: ko://github.com/shipwright-io/build/cmd/manager
+          image: ko://github.com/shipwright-io/build/cmd/shipwright-build-controller
           env:
             - name: WATCH_NAMESPACE
               valueFrom:
@@ -32,6 +32,8 @@ spec:
                   fieldPath: metadata.name
             - name: CONTROLLER_NAME
               value: "shipwright-build"
+            - name: GIT_CONTAINER_IMAGE
+              value: ko://github.com/shipwright-io/build/cmd/git
           ports:
             - containerPort: 8383
               name: metrics-port
diff --git a/docs/buildstrategies.md b/docs/buildstrategies.md
index f3745268..ec6c3232 100644
--- a/docs/buildstrategies.md
+++ b/docs/buildstrategies.md
@@ -18,12 +18,15 @@ SPDX-License-Identifier: Apache-2.0
 - [BuildKit](#buildkit)
   - [Cache Exporters](#cache-exporters)
   - [Known Limitations](#known-limitations)
+  - [Usage in Clusters with Pod Security Standards](#usage-in-clusters-with-pod-security-standards)
   - [Installing BuildKit Strategy](#installing-buildkit-strategy)
 - [ko](#ko)
   - [Installing ko Strategy](#installing-ko-strategy)
 - [Source to Image](#source-to-image)
   - [Installing Source to Image Strategy](#installing-source-to-image-strategy)
   - [Build Steps](#build-steps)
+- [System parameters](#system-parameters)
+- [System results](#system-results)
 - [Steps Resource Definition](#steps-resource-definition)
   - [Strategies with different resources](#strategies-with-different-resources)
   - [How does Tekton Pipelines handle resources](#how-does-tekton-pipelines-handle-resources)
@@ -209,6 +212,17 @@ You can use parameters when defining the steps of a build strategy to access sys
 | `$(params.shp-source-context)` | The absolute path to the context directory of the user's sources. If the user specified no value for `spec.source.contextDir` in his Build, then this value will equal the value for `$(params.shp-source-root)`. Note that this directory is not guaranteed to exist at the time the container for your step is started, you can therefore not use this parameter as a step's working directory. |
 | `$(params.shp-output-image)`      | The URL of the image that the user wants to push as specified in the Build's `spec.output.image`, or the override from the BuildRun's `spec.output.image`. |
 
+## 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.
+
+| Result file                       | Description                                     |
+| --------------------------------- | ----------------------------------------------- |
+| `$(results.shp-image-digest.path) | File to store the digest of the image.          |
+| `$(results.shp-image-size.path)   | File to store the compressed size of the image. |
+
+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.
+
 ## 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.
@@ -253,7 +267,6 @@ spec:
         - --dockerfile=$(build.dockerfile)
         - --context=$(params.shp-source-context)
         - --destination=$(params.shp-output-image)
-        - --oci-layout-path=/workspace/output/image
         - --snapshotMode=redo
         - --push-retry=3
       resources:
@@ -298,7 +311,6 @@ spec:
         - --dockerfile=$(build.dockerfile)
         - --context=$(params.shp-source-context)
         - --destination=$(params.shp-output-image)
-        - --oci-layout-path=/workspace/output/image
         - --snapshotMode=redo
         - --push-retry=3
       resources:
diff --git a/docs/configuration.md b/docs/configuration.md
index 50d1a31a..5a9663b7 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -15,6 +15,8 @@ The following environment variables are available:
 | `CTX_TIMEOUT` | Override the default context timeout used for all Custom Resource Definition reconciliation operations. |
 | `KANIKO_CONTAINER_IMAGE` | Specify the Kaniko container image to be used for the runtime image build instead of the default, for example `gcr.io/kaniko-project/executor:v1.6.0`. |
 | `REMOTE_ARTIFACTS_CONTAINER_IMAGE` | Specify the container image used for the `.spec.sources` remote artifacts download, by default it uses `busybox:latest`. |
+| `GIT_CONTAINER_TEMPLATE` | JSON representation of a [Container](https://pkg.go.dev/k8s.io/api/core/v1#Container) template that is used for steps that clone a Git repository. Default is `{"image":"quay.io/shipwright/git:latest", "command":["/ko-app/git"], "securityContext":{"runAsUser":1000,"runAsGroup":1000}}`. The following properties are ignored as they are set by the controller: `args`, `name`. |
+| `GIT_CONTAINER_IMAGE` | Custom container image for Git clone steps. If `GIT_CONTAINER_TEMPLATE` is also specifying an image, then the value for `GIT_CONTAINER_IMAGE` has precedence. |
 | `BUILD_CONTROLLER_LEADER_ELECTION_NAMESPACE` |  Set the namespace to be used to store the `shipwright-build-controller` lock, by default it is in the same namespace as the controller itself. |
 | `BUILD_CONTROLLER_LEASE_DURATION` |  Override the `LeaseDuration`, which is the duration that non-leader candidates will wait to force acquire leadership. |
 | `BUILD_CONTROLLER_RENEW_DEADLINE` |  Override the `RenewDeadline`, which is the duration that the acting leader will retry refreshing leadership before giving up. |
diff --git a/docs/proposals/removal-tekton-resources.md b/docs/proposals/removal-tekton-resources.md
index 89a404b4..66e10bfc 100644
--- a/docs/proposals/removal-tekton-resources.md
+++ b/docs/proposals/removal-tekton-resources.md
@@ -15,18 +15,18 @@ approvers:
   - "@qu1queee"
 creation-date: 2021-04-10
 last-updated: 2021-04-20
-status: provisional
+status: implementable
 ---
 
 # Removing Tekton resource usages
 
 ## Release Signoff Checklist
 
-- [ ] Enhancement is `implementable`
-- [ ] Design details are appropriately documented from clear requirements
-- [ ] Test plan is defined
-- [X] Graduation criteria for dev preview, tech preview, GA
-- [ ] User-facing documentation is created in [docs](/docs/)
+- [x] Enhancement is `implementable`
+- [x] Design details are appropriately documented from clear requirements
+- [x] Test plan is defined
+- [x] Graduation criteria for dev preview, tech preview, GA
+- [x] User-facing documentation is created in [docs](/docs/)
 
 ## Open Questions [optional]
 
@@ -158,7 +158,9 @@ taskSpec:
         - --revision=develop                                                     # from the user's Build, omitted if not specified which will load the default branch
         - --target=$(params.shp-source-root)                                     # resolves to /workspace/source
         - --result-file-commit-sha=$(results.shp-source-default-commit-sha.path)
-      resources: [...]
+      securityContext:
+        runAsUser: 1000
+        runAsGroup: 1000
     [...]
 ```
 
@@ -175,23 +177,27 @@ taskSpec:
         - --revision=develop                                                     # from the user's Build, omitted if not specified which will load the default branch
         - --target=$(params.shp-source-root)                                     # resolves to /workspace/source
         - --result-file-commit-sha=$(results.shp-source-default-commit-sha.path)
-        - --secret-path=/workspace/source-secrets/default
-      resources: [...]
+        - --secret-path=/workspace/shp-source-secret
+      securityContext:
+        runAsUser: 1000
+        runAsGroup: 1000
       volumeMounts:
-        - mountPath: /workspace/source-secrets/default
-          name: ssh-secret                                                       # from the user's build
+        - mountPath: /workspace/shp-source-secret
+          name: shp-ssh-secret
           readOnly: true
   volumes:
-    - name: ssh-secret                                                           # name of the secret
+    - name: shp-ssh-secret                                                       # 'shp-' + name of the secret
       secret:
         secretName: ssh-secret                                                   # from the user's build
-        defaultMode: 256                                                         # this is 0400 in octal
+        defaultMode: 0444
     [...]
 ```
 
 The image URL is a new configuration option of the build controller deployment (similar to how Tekton allows to specify its supporting images). In our release process, we need to make sure we point to the right image in our yaml.
 
-The resources of the step are also configurable at the build controller deployment level with reasonable defaults.
+The `securityContext` of the step can be configured through a new configuration option of the build controller deployment which is using the [Container](https://pkg.go.dev/k8s.io/api/core/v1#Container) type as a template. The deployment administrator can use it to also specify other attributes, such as `resources`. By default, we will run as user/group 1000. This will also cause the sources to be available as such a user. This choice was made based on the experience that many tools run as this user, such as BuildKit or Buildpacks. Build strategies then do not anymore need to `chown` the source directory to this user. Instead, if a build strategy needs the sources as a different user (such as root), those will need the prepare step for it. This will eventually allow rootless strategies to run completely without any supporting prepare step that must run as root.
+
+The secret volume is mounted using 0444 as access mask because Kubernetes mounts secrets as root user. For SSH secrets, the implementation of the Git command will need to create a copy of the private key file with 0400 as permission to consume it. For reference, the long-standing Kubernetes issue that would simplifying this: [Allow setting ownership on mounted secrets #81089](https://github.com/kubernetes/kubernetes/issues/81089).
 
 The way we run the download logic is something we can implement in a staged approach and start with the simple approach: Just a container with the necessary command-line tools (like `git`), and a simple executable implemented by us that parses the arguments and calls the `git` binary similar to [today's logic in Tekton](https://github.com/tektoncd/pipeline/blob/v0.21.0/pkg/git/git.go).
 
@@ -216,15 +222,6 @@ To address the complexity, I suggest that the implementation is done in a staged
 7. Improve the implementation for HTTP sources to make it consistent with this design.
 8. Define and implement a well-defined set of exit codes per resource kind and use them in the BuildRun controller for an enhanced error reporting.
 
-The goal to run as non-root is not covered in great detail in this proposal as some items are not 100 % clear to me yet, for example:
-
-- Will we need to define a `podTemplate` in the Task spec to set a global `securityContext` to run also Tekton init containers as a non-root user?
-- Is for example the `/tekton/results` directory already writable for every user?
-- Can we decide what the id of the non-root user is or will different build strategies need to use different users?
-- Do we need to give the build strategy author flexibility as which user the init containers and our source downloads run and subsequently as which user the source files are stored on disk?
-
-Trying to mitigate it by listing it here to gather feedback/ideas/suggestions on this area.
-
 ## Design Details
 
 ### Test Plan
diff --git a/docs/tutorials/README.md b/docs/tutorials/README.md
index 60c940b5..a402ad47 100644
--- a/docs/tutorials/README.md
+++ b/docs/tutorials/README.md
@@ -58,7 +58,7 @@ strategy to choose:
 | A go app | [shipwright-io/sample-go](https://github.com/shipwright-io/sample-go) | `/source-build` | Dockerfile-less | buildpacks-v3, buildpacks-v3-heroku |
 | A ruby app | [shipwright-io/sample-ruby](https://github.com/shipwright-io/sample-ruby) | `/source-build` | Dockerfile-less | buildpacks-v3, buildpacks-v3-heroku |
 | A java app with a Dockerfile | [shipwright-io/sample-java](https://github.com/shipwright-io/sample-java) | `/docker-build` | Dockerfile-based | Kaniko, BuildKit, Buildah |
-| Shipwright/Build | [shipwright-io/build](https://github.com/shipwright-io/build) |  `/cmd/manager` | Dockerfile-less | ko |
+| Shipwright/Build | [shipwright-io/build](https://github.com/shipwright-io/build) |  `/cmd/shipwright-build-controller` | Dockerfile-less | ko |
 
 _Note_: `ContextDir` is the path under the repository where the source code is located.
 
diff --git a/hack/release.sh b/hack/release.sh
index 29303f54..f9370ff3 100755
--- a/hack/release.sh
+++ b/hack/release.sh
@@ -14,6 +14,16 @@ echo "Building container image"
 echo "Adding io.shipwright.vcs-ref label with value: ${GITHUB_SHA}"
 
 # Using defaults, this pushes to:
-# quay.io/shipwright/shipwright-build-controller:latest
-KO_DOCKER_REPO="$IMAGE_HOST/$IMAGE" GOFLAGS="${GO_FLAGS}" ko resolve -t "$TAG" --image-label "io.shipwright.vcs-ref=${GITHUB_SHA}" --bare --platform=all -R -f deploy/ > release.yaml
-KO_DOCKER_REPO="$IMAGE_HOST/$IMAGE" GOFLAGS="${GO_FLAGS} -tags=pprof_enabled" ko resolve -t "$TAG-debug" --image-label "io.shipwright.vcs-ref=${GITHUB_SHA}" --bare --platform=all -R -f deploy/ > release-debug.yaml
+# - quay.io/shipwright/git:latest
+# - quay.io/shipwright/shipwright-build-controller:latest
+KO_DOCKER_REPO="${IMAGE_HOST}/${IMAGE_NAMESPACE}" GOFLAGS="${GO_FLAGS}" ko resolve \
+  --base-import-paths \
+  --tags "${TAG}" \
+  --image-label "io.shipwright.vcs-ref=${GITHUB_SHA}" \
+  --platform=all -R -f deploy/ > release.yaml
+
+KO_DOCKER_REPO="${IMAGE_HOST}/${IMAGE_NAMESPACE}" GOFLAGS="${GO_FLAGS} -tags=pprof_enabled" ko resolve \
+  --base-import-paths \
+  --tags "${TAG}-debug" \
+  --image-label "io.shipwright.vcs-ref=${GITHUB_SHA}" \
+  --platform=all -R -f deploy/ > release-debug.yaml
diff --git a/pkg/config/config.go b/pkg/config/config.go
index ebe30cd9..5b28fa51 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -5,12 +5,15 @@
 package config
 
 import (
+	"encoding/json"
 	"os"
 	"strconv"
 	"strings"
 	"time"
 
 	"github.com/prometheus/client_golang/prometheus"
+	corev1 "k8s.io/api/core/v1"
+	"k8s.io/utils/pointer"
 )
 
 const (
@@ -25,6 +28,13 @@ const (
 	remoteArtifactsDefaultImage = "quay.io/quay/busybox:latest"
 	remoteArtifactsEnvVar       = "REMOTE_ARTIFACTS_CONTAINER_IMAGE"
 
+	// the Git image is built using ko which can replace environment variable values in the deployment, so once we decide to move
+	// from environment variables to a ConfigMap, then we should move the container template, but retain the environment variable
+	// (or make it an argument like Tekton)
+	gitDefaultImage            = "quay.io/shipwright/git:latest"
+	gitImageEnvVar             = "GIT_CONTAINER_IMAGE"
+	gitContainerTemplateEnvVar = "GIT_CONTAINER_TEMPLATE"
+
 	// environment variable to override the buckets
 	metricBuildRunCompletionDurationBucketsEnvVar = "PROMETHEUS_BR_COMP_DUR_BUCKETS"
 	metricBuildRunEstablishDurationBucketsEnvVar  = "PROMETHEUS_BR_EST_DUR_BUCKETS"
@@ -56,12 +66,15 @@ var (
 	metricBuildRunCompletionDurationBuckets = prometheus.LinearBuckets(50, 50, 10)
 	metricBuildRunEstablishDurationBuckets  = []float64{0, 1, 2, 3, 5, 7, 10, 15, 20, 30}
 	metricBuildRunRampUpDurationBuckets     = prometheus.LinearBuckets(0, 1, 10)
+
+	nonRoot = pointer.Int64Ptr(1000)
 )
 
 // Config hosts different parameters that
 // can be set to use on the Build controllers
 type Config struct {
 	CtxTimeOut                    time.Duration
+	GitContainerTemplate          corev1.Container
 	KanikoContainerImage          string
 	RemoteArtifactsContainerImage string
 	Prometheus                    PrometheusConfig
@@ -99,7 +112,7 @@ type ControllerOptions struct {
 	MaxConcurrentReconciles int
 }
 
-// KubeAPIOptions contains configrable options for the kube API client
+// KubeAPIOptions contains configurable options for the kube API client
 type KubeAPIOptions struct {
 	QPS   int
 	Burst int
@@ -108,7 +121,17 @@ type KubeAPIOptions struct {
 // NewDefaultConfig returns a new Config, with context timeout and default Kaniko image.
 func NewDefaultConfig() *Config {
 	return &Config{
-		CtxTimeOut:                    contextTimeout,
+		CtxTimeOut: contextTimeout,
+		GitContainerTemplate: corev1.Container{
+			Image: gitDefaultImage,
+			Command: []string{
+				"/ko-app/git",
+			},
+			SecurityContext: &corev1.SecurityContext{
+				RunAsUser:  nonRoot,
+				RunAsGroup: nonRoot,
+			},
+		},
 		KanikoContainerImage:          kanikoDefaultImage,
 		RemoteArtifactsContainerImage: remoteArtifactsDefaultImage,
 		Prometheus: PrometheusConfig{
@@ -150,6 +173,21 @@ func (c *Config) SetConfigFromEnv() error {
 		c.CtxTimeOut = time.Duration(i) * time.Second
 	}
 
+	if gitContainerTemplate := os.Getenv(gitContainerTemplateEnvVar); gitContainerTemplate != "" {
+		c.GitContainerTemplate = corev1.Container{}
+		if err := json.Unmarshal([]byte(gitContainerTemplate), &c.GitContainerTemplate); err != nil {
+			return err
+		}
+		if c.GitContainerTemplate.Image == "" {
+			c.GitContainerTemplate.Image = gitDefaultImage
+		}
+	}
+
+	// the dedicated environment variable for the image overwrites what is defined in the git container template
+	if gitImage := os.Getenv(gitImageEnvVar); gitImage != "" {
+		c.GitContainerTemplate.Image = gitImage
+	}
+
 	if kanikoImage := os.Getenv(kanikoImageEnvVar); kanikoImage != "" {
 		c.KanikoContainerImage = kanikoImage
 	}
diff --git a/pkg/reconciler/buildrun/resources/credentials.go b/pkg/reconciler/buildrun/resources/credentials.go
index 12b8e7a9..2836acbf 100644
--- a/pkg/reconciler/buildrun/resources/credentials.go
+++ b/pkg/reconciler/buildrun/resources/credentials.go
@@ -19,12 +19,6 @@ func ApplyCredentials(ctx context.Context, build *buildv1alpha1.Build, buildRun
 
 	modified := false
 
-	// credentials of the source/git repo
-	sourceSecret := build.Spec.Source.Credentials
-	if sourceSecret != nil {
-		modified = updateServiceAccountIfSecretNotLinked(ctx, sourceSecret, serviceAccount) || modified
-	}
-
 	// credentials of the 'Builder' image registry
 	builderImage := build.Spec.Builder
 	if builderImage != nil && builderImage.Credentials != nil {
@@ -38,9 +32,9 @@ func ApplyCredentials(ctx context.Context, build *buildv1alpha1.Build, buildRun
 	} else {
 		// otherwise, if buildrun does not override the output credentials,
 		// we should use the ones provided by the build
-		sourceSecret = build.Spec.Output.Credentials
-		if sourceSecret != nil {
-			modified = updateServiceAccountIfSecretNotLinked(ctx, sourceSecret, serviceAccount) || modified
+		outputSecret := build.Spec.Output.Credentials
+		if outputSecret != nil {
+			modified = updateServiceAccountIfSecretNotLinked(ctx, outputSecret, serviceAccount) || modified
 		}
 	}
 
diff --git a/pkg/reconciler/buildrun/resources/remote_artifacts.go b/pkg/reconciler/buildrun/resources/remote_artifacts.go
deleted file mode 100644
index 7294f50e..00000000
--- a/pkg/reconciler/buildrun/resources/remote_artifacts.go
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright The Shipwright Contributors
-//
-// SPDX-License-Identifier: Apache-2.0
-
-package resources
-
-import (
-	"fmt"
-	"strings"
-
-	buildv1alpha1 "github.com/shipwright-io/build/pkg/apis/build/v1alpha1"
-	"github.com/shipwright-io/build/pkg/config"
-	"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
-	v1 "k8s.io/api/core/v1"
-)
-
-// renderRemoteArtifactsDownloadScript returns a slice of commands, a shell script, based on informed
-// BuildSources slice. Scripting lines are bind together with "&&".
-func renderRemoteArtifactsDownloadScript(sources []buildv1alpha1.BuildSource) []string {
-	script := []string{}
-	for _, source := range sources {
-		cmd := fmt.Sprintf("wget %s", source.URL)
-		script = append(script, cmd)
-	}
-	return script
-}
-
-// remoteArtifactStep generates a Tekton Step to execute the remote artifacts download script.
-func remoteArtifactStep(b *buildv1alpha1.Build, image string) v1beta1.Step {
-	script := renderRemoteArtifactsDownloadScript(*b.Spec.Sources)
-	container := v1.Container{
-		Name:       "remote-artifacts",
-		Image:      image,
-		WorkingDir: fmt.Sprintf("$(params.%s%s)", prefixParamsResults, paramSourceRoot),
-		Command:    []string{"/bin/sh"},
-		Args:       append([]string{"-e", "-x", "-c"}, strings.Join(script, " ; ")),
-	}
-	return v1beta1.Step{Container: container}
-}
-
-// AmendTaskSpecWithRemoteArtifacts will amend a TaskSpec reference with remote-artifacts download
-// step, taking place before all others.
-func AmendTaskSpecWithRemoteArtifacts(
-	cfg *config.Config,
-	spec *v1beta1.TaskSpec,
-	b *buildv1alpha1.Build,
-) {
-	step := remoteArtifactStep(b, cfg.RemoteArtifactsContainerImage)
-	steps := append([]v1beta1.Step{step}, spec.Steps...)
-	spec.Steps = steps
-}
-
-// IsSourcesDefined checks if ``spec.sources` is defined, returns a boolean.
-func IsSourcesDefined(b *buildv1alpha1.Build) bool {
-	return b.Spec.Sources != nil && len(*b.Spec.Sources) > 0
-}
diff --git a/pkg/reconciler/buildrun/resources/runtime_image.go b/pkg/reconciler/buildrun/resources/runtime_image.go
index 9600c98d..0d66046a 100644
--- a/pkg/reconciler/buildrun/resources/runtime_image.go
+++ b/pkg/reconciler/buildrun/resources/runtime_image.go
@@ -67,6 +67,13 @@ ENTRYPOINT [ {{ renderEntrypoint .Spec.Runtime.Entrypoint }} ]
 
 	// defultShellImage default image for a simple shell instance.
 	defultShellImage = "busybox:latest"
+
+	resultShellScript = `
+# Store the image digest
+grep digest /kaniko/shp-runtime-image-layout/index.json | sed -E 's/.*sha256([^"]*).*/sha256\1/' | tr -d '\n' > "$(results.shp-image-digest.path)"
+
+# Store the image size
+du -b -c /kaniko/shp-runtime-image-layout/blobs/sha256/* | tail -1 | sed 's/\s*total//' | tr -d '\n' > "$(results.shp-image-size.path)"`
 )
 
 // rootUserID root's UID
@@ -126,7 +133,7 @@ func renderRuntimeDockerfile(b *buildv1alpha1.Build) (*bytes.Buffer, error) {
 // runtimeDockerfileTransformations search and replace special variables `$()` in informed string.
 func runtimeDockerfileTransformations(b *buildv1alpha1.Build, str string) string {
 	transformations := map[string]string{
-		"$(workspace)": fmt.Sprintf("$(params.%s%s)", prefixParamsResults, paramSourceContext),
+		"$(workspace)": fmt.Sprintf("$(params.%s-%s)", prefixParamsResultsVolumes, paramSourceContext),
 	}
 	for k, v := range transformations {
 		str = strings.ReplaceAll(str, k, v)
@@ -152,12 +159,12 @@ func runtimeDockerfileStep(b *buildv1alpha1.Build) (*v1beta1.Step, error) {
 	}
 
 	container := v1.Container{
-		Name:  "runtime-dockerfile",
+		Name:  "runtime-image-dockerfile",
 		Image: imageURL,
 		SecurityContext: &v1.SecurityContext{
 			RunAsUser: &rootUserID,
 		},
-		WorkingDir: fmt.Sprintf("$(params.%s%s)", prefixParamsResults, paramSourceRoot),
+		WorkingDir: fmt.Sprintf("$(params.%s-%s)", prefixParamsResultsVolumes, paramSourceRoot),
 		Command:    []string{"/bin/sh"},
 		Args: []string{
 			"-x",
@@ -171,9 +178,9 @@ func runtimeDockerfileStep(b *buildv1alpha1.Build) (*v1beta1.Step, error) {
 // runtimeBuildAndPushStep returns a Task step to build the Dockerfile.runtime with kaniko.
 func runtimeBuildAndPushStep(b *buildv1alpha1.Build, kanikoImage string) *v1beta1.Step {
 	container := v1.Container{
-		Name:       "kaniko-build-and-push",
+		Name:       "runtime-image-build-and-push",
 		Image:      kanikoImage,
-		WorkingDir: fmt.Sprintf("$(params.%s%s)", prefixParamsResults, paramSourceRoot),
+		WorkingDir: fmt.Sprintf("$(params.%s-%s)", prefixParamsResultsVolumes, paramSourceRoot),
 		SecurityContext: &v1.SecurityContext{
 			RunAsUser: &rootUserID,
 			Capabilities: &v1.Capabilities{
@@ -197,14 +204,40 @@ func runtimeBuildAndPushStep(b *buildv1alpha1.Build, kanikoImage string) *v1beta
 		Args: []string{
 			"--skip-tls-verify=true",
 			fmt.Sprintf("--dockerfile=%s", runtimeDockerfile),
-			fmt.Sprintf("--destination=$(params.%s%s)", prefixParamsResults, paramOutputImage),
+			fmt.Sprintf("--destination=$(params.%s-%s)", prefixParamsResultsVolumes, paramOutputImage),
 			"--snapshotMode=redo",
-			"--oci-layout-path=/workspace/output/image",
+			"--oci-layout-path=/kaniko/shp-runtime-image-layout",
+		},
+		VolumeMounts: []v1.VolumeMount{
+			{
+				Name:      prefixParamsResultsVolumes + "-runtime-image-layout",
+				MountPath: "/kaniko/shp-runtime-image-layout",
+			},
 		},
 	}
 	return &v1beta1.Step{Container: container}
 }
 
+func resultStep() *v1beta1.Step {
+	return &v1beta1.Step{
+		Container: v1.Container{
+			Name:    "runtime-image-results",
+			Image:   defultShellImage,
+			Command: []string{"/bin/sh"},
+			Args: []string{
+				"-c",
+				resultShellScript,
+			},
+			VolumeMounts: []v1.VolumeMount{
+				{
+					Name:      prefixParamsResultsVolumes + "-runtime-image-layout",
+					MountPath: "/kaniko/shp-runtime-image-layout",
+				},
+			},
+		},
+	}
+}
+
 // AmendTaskSpecWithRuntimeImage add more steps to Tekton's Task in order to create the
 // runtime-image.
 func AmendTaskSpecWithRuntimeImage(
@@ -212,14 +245,29 @@ func AmendTaskSpecWithRuntimeImage(
 	spec *v1beta1.TaskSpec,
 	b *buildv1alpha1.Build,
 ) error {
+	// append a volume for the OCI image layout to be passed from the Kaniko to the Results step
+	spec.Volumes = append(spec.Volumes, v1.Volume{
+		Name: prefixParamsResultsVolumes + "-runtime-image-layout",
+		VolumeSource: v1.VolumeSource{
+			EmptyDir: &v1.EmptyDirVolumeSource{},
+		},
+	})
+
+	// append the step that generates the Dockerfile
 	step, err := runtimeDockerfileStep(b)
 	if err != nil {
 		return err
 	}
 	spec.Steps = append(spec.Steps, *step)
 
+	// append the step that runs the Dockerfile build using Kaniko
 	step = runtimeBuildAndPushStep(b, cfg.KanikoContainerImage)
 	spec.Steps = append(spec.Steps, *step)
+
+	// append the step that fills the image results
+	step = resultStep()
+	spec.Steps = append(spec.Steps, *step)
+
 	return nil
 }
 
diff --git a/pkg/reconciler/buildrun/resources/sources.go b/pkg/reconciler/buildrun/resources/sources.go
new file mode 100644
index 00000000..85a49eed
--- /dev/null
+++ b/pkg/reconciler/buildrun/resources/sources.go
@@ -0,0 +1,30 @@
+// Copyright The Shipwright Contributors
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package resources
+
+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"
+)
+
+// AmendTaskSpecWithSources adds steps, results and volumes for spec.source and spec.sources
+func AmendTaskSpecWithSources(
+	cfg *config.Config,
+	taskSpec *v1beta1.TaskSpec,
+	build *buildv1alpha1.Build,
+) {
+	// create the step for spec.source, this is always Git
+	sources.AppendGitStep(cfg, taskSpec, build.Spec.Source, "default")
+
+	// create the step for spec.sources, this will eventually change into different steps depending on the type of the source
+	if build.Spec.Sources != nil {
+		for _, source := range *build.Spec.Sources {
+			// today, we only have HTTP sources
+			sources.AppendHTTPStep(cfg, taskSpec, source)
+		}
+	}
+}
diff --git a/pkg/reconciler/buildrun/resources/sources/git.go b/pkg/reconciler/buildrun/resources/sources/git.go
new file mode 100644
index 00000000..a8dbd808
--- /dev/null
+++ b/pkg/reconciler/buildrun/resources/sources/git.go
@@ -0,0 +1,78 @@
+// Copyright The Shipwright Contributors
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package sources
+
+import (
+	"fmt"
+
+	buildv1alpha1 "github.com/shipwright-io/build/pkg/apis/build/v1alpha1"
+	"github.com/shipwright-io/build/pkg/config"
+	tektonv1beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
+	corev1 "k8s.io/api/core/v1"
+)
+
+// AppendGitStep appends the Git step and results and volume if needed to the TaskSpec
+func AppendGitStep(
+	cfg *config.Config,
+	taskSpec *tektonv1beta1.TaskSpec,
+	source buildv1alpha1.Source,
+	name string,
+) {
+	// append the result
+	taskSpec.Results = append(taskSpec.Results, tektonv1beta1.TaskResult{
+		Name:        fmt.Sprintf("%s-source-%s-commit-sha", prefixParamsResultsVolumes, name),
+		Description: "The commit SHA of the cloned source.",
+	})
+
+	// initialize the step from the template
+	gitStep := tektonv1beta1.Step{
+		Container: *cfg.GitContainerTemplate.DeepCopy(),
+	}
+
+	// add the build-specific details
+	gitStep.Container.Name = fmt.Sprintf("source-%s", name)
+	gitStep.Container.Args = []string{
+		"--url",
+		source.URL,
+		"--target",
+		fmt.Sprintf("$(params.%s-%s)", prefixParamsResultsVolumes, paramSourceRoot),
+		"--result-file-commit-sha",
+		fmt.Sprintf("$(results.%s-source-%s-commit-sha.path)", prefixParamsResultsVolumes, name),
+	}
+
+	// Check if a revision is defined
+	if source.Revision != nil {
+		// append the argument
+		gitStep.Container.Args = append(
+			gitStep.Container.Args,
+			"--revision",
+			*source.Revision,
+		)
+	}
+
+	if source.Credentials != nil {
+		// ensure the value is there
+		AppendSecretVolume(taskSpec, source.Credentials.Name)
+
+		secretMountPath := fmt.Sprintf("/workspace/%s-source-secret", prefixParamsResultsVolumes)
+
+		// define the volume mount on the container
+		gitStep.VolumeMounts = append(gitStep.VolumeMounts, corev1.VolumeMount{
+			Name:      fmt.Sprintf("%s-%s", prefixParamsResultsVolumes, source.Credentials.Name),
+			MountPath: secretMountPath,
+			ReadOnly:  true,
+		})
+
+		// append the argument
+		gitStep.Container.Args = append(
+			gitStep.Container.Args,
+			"--secret-path",
+			secretMountPath,
+		)
+	}
+
+	// append the git step
+	taskSpec.Steps = append(taskSpec.Steps, gitStep)
+}
diff --git a/pkg/reconciler/buildrun/resources/sources/http.go b/pkg/reconciler/buildrun/resources/sources/http.go
new file mode 100644
index 00000000..86acd4e1
--- /dev/null
+++ b/pkg/reconciler/buildrun/resources/sources/http.go
@@ -0,0 +1,57 @@
+// Copyright The Shipwright Contributors
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package sources
+
+import (
+	"fmt"
+
+	buildv1alpha1 "github.com/shipwright-io/build/pkg/apis/build/v1alpha1"
+	"github.com/shipwright-io/build/pkg/config"
+	tektonv1beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
+	corev1 "k8s.io/api/core/v1"
+)
+
+// AppendHTTPStep appends the step for a HTTP source to the TaskSpec
+func AppendHTTPStep(
+	cfg *config.Config,
+	taskSpec *tektonv1beta1.TaskSpec,
+	source buildv1alpha1.BuildSource,
+) {
+	// HTTP is done currently all in a single step, see if there is already one
+	httpStep := findExistingHTTPSourcesStep(taskSpec)
+	if httpStep != nil {
+		httpStep.Container.Args[3] = fmt.Sprintf("%s ; wget \"%s\"", httpStep.Container.Args[3], source.URL)
+	} else {
+		httpStep := tektonv1beta1.Step{
+			Container: corev1.Container{
+				Name:       "sources-http",
+				Image:      cfg.RemoteArtifactsContainerImage,
+				WorkingDir: fmt.Sprintf("$(params.%s-%s)", prefixParamsResultsVolumes, paramSourceRoot),
+				Command: []string{
+					"/bin/sh",
+				},
+				Args: []string{
+					"-e",
+					"-x",
+					"-c",
+					fmt.Sprintf("wget \"%s\"", source.URL),
+				},
+			},
+		}
+
+		// append the git step
+		taskSpec.Steps = append(taskSpec.Steps, httpStep)
+	}
+}
+
+func findExistingHTTPSourcesStep(taskSpec *tektonv1beta1.TaskSpec) *tektonv1beta1.Step {
+	for _, candidateStep := range taskSpec.Steps {
+		if candidateStep.Name == "sources-http" {
+			return &candidateStep
+		}
+	}
+
+	return nil
+}
diff --git a/pkg/reconciler/buildrun/resources/sources/utils.go b/pkg/reconciler/buildrun/resources/sources/utils.go
new file mode 100644
index 00000000..86ccb337
--- /dev/null
+++ b/pkg/reconciler/buildrun/resources/sources/utils.go
@@ -0,0 +1,66 @@
+// Copyright The Shipwright Contributors
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package sources
+
+import (
+	"fmt"
+	"regexp"
+
+	tektonv1beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
+	corev1 "k8s.io/api/core/v1"
+	"k8s.io/utils/pointer"
+)
+
+const (
+	prefixParamsResultsVolumes = "shp"
+
+	paramSourceRoot = "source-root"
+)
+
+var (
+	dnsLabel1123Forbidden = regexp.MustCompile("[^a-zA-Z0-9-]+")
+
+	// secrets are volumes and volumes are mounted as root, as we run as non-root, we must use 0444 to allow non-root to read it
+	secretMountMode = pointer.Int32Ptr(0444)
+)
+
+// AppendSecretVolume checks if a volume for a secret already exists, if not it appends it to the TaskSpec
+func AppendSecretVolume(
+	taskSpec *tektonv1beta1.TaskSpec,
+	secretName string,
+) {
+	volumeName := fmt.Sprintf("%s-%s", prefixParamsResultsVolumes, secretName)
+
+	// ensure we do not add the secret twice
+	for _, volume := range taskSpec.Volumes {
+		if volume.VolumeSource.Secret != nil && volume.Name == volumeName {
+			return
+		}
+	}
+
+	// append volume for secret
+	taskSpec.Volumes = append(taskSpec.Volumes, corev1.Volume{
+		Name: volumeName,
+		VolumeSource: corev1.VolumeSource{
+			Secret: &corev1.SecretVolumeSource{
+				SecretName:  secretName,
+				DefaultMode: secretMountMode,
+			},
+		},
+	})
+}
+
+// SanitizeVolumeName ensures that there are no forbidden names in the volume name and that its name is not too long
+func SanitizeVolumeName(name string) string {
+	// remove forbidden characters
+	sanitizedName := dnsLabel1123Forbidden.ReplaceAllString(name, "-")
+
+	// ensure maximum length
+	if len(sanitizedName) > 63 {
+		sanitizedName = sanitizedName[:63]
+	}
+
+	return sanitizedName
+}
diff --git a/pkg/reconciler/buildrun/resources/taskrun.go b/pkg/reconciler/buildrun/resources/taskrun.go
index e09cccb6..9d853a93 100644
--- a/pkg/reconciler/buildrun/resources/taskrun.go
+++ b/pkg/reconciler/buildrun/resources/taskrun.go
@@ -21,20 +21,20 @@ import (
 )
 
 const (
-	prefixParamsResults = "shp-"
+	prefixParamsResultsVolumes = "shp"
 
 	paramOutputImage   = "output-image"
 	paramSourceRoot    = "source-root"
 	paramSourceContext = "source-context"
 
-	inputSourceResourceName = "source"
-	inputGitSourceURL       = "url"
-	inputGitSourceRevision  = "revision"
-	inputParamBuilder       = "BUILDER_IMAGE"
-	inputParamDockerfile    = "DOCKERFILE"
-	inputParamContextDir    = "CONTEXT_DIR"
-	outputImageResourceName = "image"
-	outputImageResourceURL  = "url"
+	resultImageDigest = "image-digest"
+	resultImageSize   = "image-size"
+
+	workspaceSource = "source"
+
+	inputParamBuilder    = "BUILDER_IMAGE"
+	inputParamDockerfile = "DOCKERFILE"
+	inputParamContextDir = "CONTEXT_DIR"
 )
 
 // getStringTransformations gets us MANDATORY replacements using
@@ -43,7 +43,7 @@ func getStringTransformations(fullText string) string {
 
 	stringTransformations := map[string]string{
 		// this will be removed, build strategy author should use $(params.shp-output-image) directly
-		"$(build.output.image)": fmt.Sprintf("$(params.%s%s)", prefixParamsResults, paramOutputImage),
+		"$(build.output.image)": fmt.Sprintf("$(params.%s-%s)", prefixParamsResultsVolumes, paramOutputImage),
 
 		"$(build.builder.image)": fmt.Sprintf("$(inputs.params.%s)", inputParamBuilder),
 		"$(build.dockerfile)":    fmt.Sprintf("$(inputs.params.%s)", inputParamDockerfile),
@@ -69,24 +69,6 @@ func GenerateTaskSpec(
 ) (*v1beta1.TaskSpec, error) {
 
 	generatedTaskSpec := v1beta1.TaskSpec{
-		Resources: &v1beta1.TaskResources{
-			Inputs: []v1beta1.TaskResource{
-				{
-					ResourceDeclaration: taskv1.ResourceDeclaration{
-						Name: inputSourceResourceName,
-						Type: taskv1.PipelineResourceTypeGit,
-					},
-				},
-			},
-			Outputs: []v1beta1.TaskResource{
-				{
-					ResourceDeclaration: taskv1.ResourceDeclaration{
-						Name: outputImageResourceName, // mapped from {{ .Build.OutputImage }}
-						Type: taskv1.PipelineResourceTypeImage,
-					},
-				},
-			},
-		},
 		Params: []v1beta1.ParamSpec{
 			{
 				Description: "Path to the Dockerfile",
@@ -107,22 +89,37 @@ func GenerateTaskSpec(
 				},
 			},
 			{
-				Name:        prefixParamsResults + paramOutputImage,
+				Name:        fmt.Sprintf("%s-%s", prefixParamsResultsVolumes, paramOutputImage),
 				Description: "The URL of the image that the build produces",
 				Type:        taskv1.ParamTypeString,
 			},
 			{
-				Name:        prefixParamsResults + paramSourceContext,
+				Name:        fmt.Sprintf("%s-%s", prefixParamsResultsVolumes, paramSourceContext),
 				Description: "The context directory inside the source directory",
 				Type:        taskv1.ParamTypeString,
 			},
 			{
-				Name:        prefixParamsResults + paramSourceRoot,
+				Name:        fmt.Sprintf("%s-%s", prefixParamsResultsVolumes, paramSourceRoot),
 				Description: "The source directory",
 				Type:        taskv1.ParamTypeString,
 			},
 		},
-		Steps: []v1beta1.Step{},
+		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
+			{
+				Name: workspaceSource,
+			},
+		},
 	}
 
 	if build.Spec.Builder != nil {
@@ -137,8 +134,10 @@ func GenerateTaskSpec(
 		generatedTaskSpec.Params = append(generatedTaskSpec.Params, InputBuilder)
 	}
 
-	var vols []corev1.Volume
+	// define results, steps and volumes for sources
+	AmendTaskSpecWithSources(cfg, &generatedTaskSpec, build)
 
+	// define the steps coming from the build strategy
 	for _, containerValue := range buildSteps {
 
 		var taskCommand []string
@@ -172,22 +171,19 @@ func GenerateTaskSpec(
 		// Get volumeMounts added to Task's spec.Volumes
 		for _, volumeInBuildStrategy := range containerValue.VolumeMounts {
 			newVolume := true
-			for _, volumeInTask := range vols {
+			for _, volumeInTask := range generatedTaskSpec.Volumes {
 				if volumeInTask.Name == volumeInBuildStrategy.Name {
 					newVolume = false
 				}
 			}
 			if newVolume {
-				vols = append(vols, corev1.Volume{
+				generatedTaskSpec.Volumes = append(generatedTaskSpec.Volumes, corev1.Volume{
 					Name: volumeInBuildStrategy.Name,
 				})
 			}
-
 		}
 	}
 
-	generatedTaskSpec.Volumes = vols
-
 	// checking for runtime-image settings, and appending more steps to the strategy
 	if IsRuntimeDefined(build) {
 		if err := AmendTaskSpecWithRuntimeImage(cfg, &generatedTaskSpec, build); err != nil {
@@ -195,12 +191,6 @@ func GenerateTaskSpec(
 		}
 	}
 
-	// when sources is defined on `spec.build` it will prepend the step to handle remote artifacts,
-	// before all other steps
-	if IsSourcesDefined(build) {
-		AmendTaskSpecWithRemoteArtifacts(cfg, &generatedTaskSpec, build)
-	}
-
 	return &generatedTaskSpec, nil
 }
 
@@ -213,14 +203,6 @@ func GenerateTaskRun(
 	strategy buildv1alpha1.BuilderStrategy,
 ) (*v1beta1.TaskRun, error) {
 
-	// Set revision to empty if the field is not specified in the Build.
-	// This will force Tekton Controller to do a git symbolic-link to HEAD
-	// giving back the default branch of the repository
-	revision := ""
-	if build.Spec.Source.Revision != nil {
-		revision = *build.Spec.Source.Revision
-	}
-
 	// retrieve expected imageURL form build or buildRun
 	var image string
 	if buildRun.Spec.Output != nil {
@@ -248,42 +230,11 @@ func GenerateTaskRun(
 		Spec: v1beta1.TaskRunSpec{
 			ServiceAccountName: serviceAccountName,
 			TaskSpec:           taskSpec,
-			Resources: &v1beta1.TaskRunResources{
-				Inputs: []v1beta1.TaskResourceBinding{
-					{
-						PipelineResourceBinding: v1beta1.PipelineResourceBinding{
-							Name: inputSourceResourceName,
-							ResourceSpec: &taskv1.PipelineResourceSpec{
-								Type: taskv1.PipelineResourceTypeGit,
-								Params: []taskv1.ResourceParam{
-									{
-										Name:  inputGitSourceURL,
-										Value: build.Spec.Source.URL,
-									},
-									{
-										Name:  inputGitSourceRevision,
-										Value: revision,
-									},
-								},
-							},
-						},
-					},
-				},
-				Outputs: []v1beta1.TaskResourceBinding{
-					{
-						PipelineResourceBinding: v1beta1.PipelineResourceBinding{
-							Name: outputImageResourceName,
-							ResourceSpec: &taskv1.PipelineResourceSpec{
-								Type: taskv1.PipelineResourceTypeImage,
-								Params: []taskv1.ResourceParam{
-									{
-										Name:  outputImageResourceURL,
-										Value: image,
-									},
-								},
-							},
-						},
-					},
+			Workspaces: []v1beta1.WorkspaceBinding{
+				// workspace for the source files
+				{
+					Name:     workspaceSource,
+					EmptyDir: &corev1.EmptyDirVolumeSource{},
 				},
 			},
 		},
@@ -309,14 +260,14 @@ func GenerateTaskRun(
 	params := []v1beta1.Param{
 		{
 			// shp-output-image
-			Name: prefixParamsResults + paramOutputImage,
+			Name: fmt.Sprintf("%s-%s", prefixParamsResultsVolumes, paramOutputImage),
 			Value: v1beta1.ArrayOrString{
 				Type:      v1beta1.ParamTypeString,
 				StringVal: image,
 			},
 		},
 		{
-			Name: prefixParamsResults + paramSourceRoot,
+			Name: fmt.Sprintf("%s-%s", prefixParamsResultsVolumes, paramSourceRoot),
 			Value: v1beta1.ArrayOrString{
 				Type:      v1beta1.ParamTypeString,
 				StringVal: "/workspace/source",
@@ -350,7 +301,7 @@ func GenerateTaskRun(
 			},
 		})
 		params = append(params, v1beta1.Param{
-			Name: prefixParamsResults + paramSourceContext,
+			Name: fmt.Sprintf("%s-%s", prefixParamsResultsVolumes, paramSourceContext),
 			Value: v1beta1.ArrayOrString{
 				Type:      v1beta1.ParamTypeString,
 				StringVal: path.Join("/workspace/source", *build.Spec.Source.ContextDir),
@@ -358,7 +309,7 @@ func GenerateTaskRun(
 		})
 	} else {
 		params = append(params, v1beta1.Param{
-			Name: prefixParamsResults + paramSourceContext,
+			Name: fmt.Sprintf("%s-%s", prefixParamsResultsVolumes, paramSourceContext),
 			Value: v1beta1.ArrayOrString{
 				Type:      v1beta1.ParamTypeString,
 				StringVal: "/workspace/source",
diff --git a/samples/build/build_ko_cr.yaml b/samples/build/build_ko_cr.yaml
index 0eb1f834..88eaf7ce 100644
--- a/samples/build/build_ko_cr.yaml
+++ b/samples/build/build_ko_cr.yaml
@@ -8,7 +8,7 @@ metadata:
 spec:
   source:
     url: https://github.com/shipwright-io/build
-    contextDir: cmd/manager
+    contextDir: cmd/shipwright-build-controller
   strategy:
     name: ko
     kind: ClusterBuildStrategy
diff --git a/samples/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3-heroku_cr.yaml b/samples/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3-heroku_cr.yaml
index c927c3fb..09592e5c 100644
--- a/samples/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3-heroku_cr.yaml
+++ b/samples/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3-heroku_cr.yaml
@@ -13,14 +13,11 @@ spec:
           add: 
             - CHOWN
       command:
-        - /bin/bash
+        - chown
       args:
-        - -c
-        - >
-          chown -R "1000:1000" "$(params.shp-source-root)" &&
-          chown -R "1000:1000" /tekton/home &&
-          chown -R "1000:1000" /cache &&
-          chown -R "1000:1000" /layers
+        - -R
+        - "1000:1000"
+        - /tekton/home
       resources:
         limits:
           cpu: 500m
@@ -28,23 +25,29 @@ spec:
         requests:
           cpu: 250m
           memory: 65Mi
-      volumeMounts:
-        - name: cache-dir
-          mountPath: /cache
-        - name: layers-dir
-          mountPath: /layers
     - name: build-and-push
       image: heroku/buildpacks:18
       securityContext:
         runAsUser: 1000
         runAsGroup: 1000
       command:
-        - /cnb/lifecycle/creator
+        - /bin/bash
       args:
-        - -app=$(params.shp-source-context)
-        - -cache-dir=/cache
-        - -layers=/layers
-        - $(params.shp-output-image)
+        - -c
+        - |
+          set -euo pipefail
+
+          mkdir /tmp/cache /tmp/layers
+
+          /cnb/lifecycle/creator \
+            '-app=$(params.shp-source-context)' \
+            -cache-dir=/tmp/cache \
+            -layers=/tmp/layers \
+            -report=/tmp/report.toml \
+            '$(params.shp-output-image)'
+
+          # Store the image digest
+          grep digest /tmp/report.toml | tr -d ' \"\n' | sed s/digest=// > "$(results.shp-image-digest.path)"
       resources:
         limits:
           cpu: 500m
@@ -52,8 +55,3 @@ spec:
         requests:
           cpu: 250m
           memory: 65Mi
-      volumeMounts:
-        - name: cache-dir
-          mountPath: /cache
-        - name: layers-dir
-          mountPath: /layers
diff --git a/samples/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3-heroku_namespaced_cr.yaml b/samples/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3-heroku_namespaced_cr.yaml
index 87a394da..b26fccf7 100644
--- a/samples/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3-heroku_namespaced_cr.yaml
+++ b/samples/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3-heroku_namespaced_cr.yaml
@@ -13,14 +13,11 @@ spec:
           add: 
             - CHOWN
       command:
-        - /bin/bash
+        - chown
       args:
-        - -c
-        - >
-          chown -R "1000:1000" "$(params.shp-source-root)" &&
-          chown -R "1000:1000" /tekton/home &&
-          chown -R "1000:1000" /cache &&
-          chown -R "1000:1000" /layers
+        - -R
+        - "1000:1000"
+        - /tekton/home
       resources:
         limits:
           cpu: 500m
@@ -28,23 +25,29 @@ spec:
         requests:
           cpu: 250m
           memory: 65Mi
-      volumeMounts:
-        - name: cache-dir
-          mountPath: /cache
-        - name: layers-dir
-          mountPath: /layers
     - name: build-and-push
       image: heroku/buildpacks:18
       securityContext:
         runAsUser: 1000
         runAsGroup: 1000
       command:
-        - /cnb/lifecycle/creator
+        - /bin/bash
       args:
-        - -app=$(params.shp-source-context)
-        - -cache-dir=/cache
-        - -layers=/layers
-        - $(params.shp-output-image)
+        - -c
+        - |
+          set -euo pipefail
+
+          mkdir /tmp/cache /tmp/layers
+
+          /cnb/lifecycle/creator \
+            '-app=$(params.shp-source-context)' \
+            -cache-dir=/tmp/cache \
+            -layers=/tmp/layers \
+            -report=/tmp/report.toml \
+            '$(params.shp-output-image)'
+
+          # Store the image digest
+          grep digest /tmp/report.toml | tr -d ' \"\n' | sed s/digest=// > "$(results.shp-image-digest.path)"
       resources:
         limits:
           cpu: 500m
@@ -52,8 +55,3 @@ spec:
         requests:
           cpu: 250m
           memory: 65Mi
-      volumeMounts:
-        - name: cache-dir
-          mountPath: /cache
-        - name: layers-dir
-          mountPath: /layers
diff --git a/samples/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3_cr.yaml b/samples/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3_cr.yaml
index 848669f2..de59553c 100644
--- a/samples/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3_cr.yaml
+++ b/samples/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3_cr.yaml
@@ -13,14 +13,11 @@ spec:
           add: 
             - CHOWN
       command:
-        - /bin/bash
+        - chown
       args:
-        - -c
-        - >
-          chown -R "1000:1000" "$(params.shp-source-root)" &&
-          chown -R "1000:1000" /tekton/home &&
-          chown -R "1000:1000" /cache &&
-          chown -R "1000:1000" /layers
+        - -R
+        - "1000:1000"
+        - /tekton/home
       resources:
         limits:
           cpu: 500m
@@ -28,23 +25,29 @@ spec:
         requests:
           cpu: 250m
           memory: 65Mi
-      volumeMounts:
-        - name: cache-dir
-          mountPath: /cache
-        - name: layers-dir
-          mountPath: /layers
     - name: build-and-push
       image: docker.io/paketobuildpacks/builder:full
       securityContext:
         runAsUser: 1000
         runAsGroup: 1000
       command:
-        - /cnb/lifecycle/creator
+        - /bin/bash
       args:
-        - -app=$(params.shp-source-context)
-        - -cache-dir=/cache
-        - -layers=/layers
-        - $(params.shp-output-image)
+        - -c
+        - |
+          set -euo pipefail
+
+          mkdir /tmp/cache /tmp/layers
+
+          /cnb/lifecycle/creator \
+            '-app=$(params.shp-source-context)' \
+            -cache-dir=/tmp/cache \
+            -layers=/tmp/layers \
+            -report=/tmp/report.toml \
+            '$(params.shp-output-image)'
+
+          # Store the image digest
+          grep digest /tmp/report.toml | tr -d ' \"\n' | sed s/digest=// > "$(results.shp-image-digest.path)"
       resources:
         limits:
           cpu: 500m
@@ -52,8 +55,3 @@ spec:
         requests:
           cpu: 250m
           memory: 65Mi
-      volumeMounts:
-        - name: cache-dir
-          mountPath: /cache
-        - name: layers-dir
-          mountPath: /layers
diff --git a/samples/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3_namespaced_cr.yaml b/samples/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3_namespaced_cr.yaml
index e9383090..54972e9f 100644
--- a/samples/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3_namespaced_cr.yaml
+++ b/samples/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3_namespaced_cr.yaml
@@ -13,14 +13,11 @@ spec:
           add: 
             - CHOWN
       command:
-        - /bin/bash
+        - chown
       args:
-        - -c
-        - >
-          chown -R "1000:1000" "$(params.shp-source-root)" &&
-          chown -R "1000:1000" /tekton/home &&
-          chown -R "1000:1000" /cache &&
-          chown -R "1000:1000" /layers
+        - -R
+        - "1000:1000"
+        - /tekton/home
       resources:
         limits:
           cpu: 500m
@@ -28,23 +25,29 @@ spec:
         requests:
           cpu: 250m
           memory: 65Mi
-      volumeMounts:
-        - name: cache-dir
-          mountPath: /cache
-        - name: layers-dir
-          mountPath: /layers
     - name: build-and-push
       image: docker.io/paketobuildpacks/builder:full
       securityContext:
         runAsUser: 1000
         runAsGroup: 1000
       command:
-        - /cnb/lifecycle/creator
+        - /bin/bash
       args:
-        - -app=$(params.shp-source-context)
-        - -cache-dir=/cache
-        - -layers=/layers
-        - $(params.shp-output-image)
+        - -c
+        - |
+          set -euo pipefail
+
+          mkdir /tmp/cache /tmp/layers
+
+          /cnb/lifecycle/creator \
+            '-app=$(params.shp-source-context)' \
+            -cache-dir=/tmp/cache \
+            -layers=/tmp/layers \
+            -report=/tmp/report.toml \
+            '$(params.shp-output-image)'
+
+          # Store the image digest
+          grep digest /tmp/report.toml | tr -d ' \"\n' | sed s/digest=// > "$(results.shp-image-digest.path)"
       resources:
         limits:
           cpu: 500m
@@ -52,8 +55,3 @@ spec:
         requests:
           cpu: 250m
           memory: 65Mi
-      volumeMounts:
-        - name: cache-dir
-          mountPath: /cache
-        - name: layers-dir
-          mountPath: /layers
diff --git a/samples/buildstrategy/kaniko/buildstrategy_kaniko_cr.yaml b/samples/buildstrategy/kaniko/buildstrategy_kaniko_cr.yaml
index b62ab9cc..d61f4a42 100644
--- a/samples/buildstrategy/kaniko/buildstrategy_kaniko_cr.yaml
+++ b/samples/buildstrategy/kaniko/buildstrategy_kaniko_cr.yaml
@@ -33,7 +33,7 @@ spec:
         - --dockerfile=$(build.dockerfile)
         - --context=$(params.shp-source-context)
         - --destination=$(params.shp-output-image)
-        - --oci-layout-path=/workspace/output/image
+        - --oci-layout-path=/kaniko/oci-image-layout
         - --snapshotMode=redo
         - --push-retry=3
       resources:
@@ -43,3 +43,31 @@ spec:
         requests:
           cpu: 250m
           memory: 65Mi
+      volumeMounts:
+        - name: layout
+          mountPath: /kaniko/oci-image-layout
+    - name: results
+      image: registry.access.redhat.com/ubi8/ubi-minimal
+      command:
+        - /bin/bash
+      args:
+        - -c
+        - |
+          set -euo pipefail
+          
+          # Store the image digest
+          grep digest /kaniko/oci-image-layout/index.json | sed -E 's/.*sha256([^"]*).*/sha256\1/' | tr -d '\n' > "$(results.shp-image-digest.path)"
+
+          # Store the image size
+          du -b -c /kaniko/oci-image-layout/blobs/sha256/* | tail -1 | sed 's/\s*total//' | tr -d '\n' > "$(results.shp-image-size.path)"
+      resources:
+        limits:
+          cpu: 100m
+          memory: 128Mi
+        requests:
+          cpu: 100m
+          memory: 128Mi
+      volumeMounts:
+        - name: layout
+          mountPath: /kaniko/oci-image-layout
+
diff --git a/samples/buildstrategy/ko/buildstrategy_ko_cr.yaml b/samples/buildstrategy/ko/buildstrategy_ko_cr.yaml
index df4fe480..7b7ff0ae 100644
--- a/samples/buildstrategy/ko/buildstrategy_ko_cr.yaml
+++ b/samples/buildstrategy/ko/buildstrategy_ko_cr.yaml
@@ -13,15 +13,11 @@ spec:
           add: 
             - CHOWN
       command:
-        - /bin/bash
+        - chown
       args:
-        - -c
-        - |
-          set -euo pipefail
-
-          chown -R 1000:1000 /workspace/output
-          chown -R 1000:1000 "$(params.shp-source-root)"
-          chown -R 1000:1000 /tekton/home
+        - -R
+        - "1000:1000"
+        - /tekton/home
       resources:
         limits:
           cpu: 100m
@@ -85,10 +81,16 @@ spec:
           export KO_DOCKER_REPO="${REPO}"
 
           if [ "${TAG}" == "" ]; then
-            /tmp/ko publish "./$(build.source.contextDir)" --bare --oci-layout-path=/workspace/output/image --platform="${PLATFORM}"
+            /tmp/ko publish "./$(build.source.contextDir)" --bare --oci-layout-path=/tmp/layout --platform="${PLATFORM}"
           else
-            /tmp/ko publish "./$(build.source.contextDir)" --bare --oci-layout-path=/workspace/output/image --platform="${PLATFORM}" --tags="${TAG}"
+            /tmp/ko publish "./$(build.source.contextDir)" --bare --oci-layout-path=/tmp/layout --platform="${PLATFORM}" --tags="${TAG}"
           fi
+
+          # Store the image digest
+          grep digest /tmp/layout/index.json | sed -E 's/.*sha256([^"]*).*/sha256\1/' | tr -d '\n' > "$(results.shp-image-digest.path)"
+
+          # Store the image size
+          du -b -c /tmp/layout/blobs/sha256/* | tail -1 | sed 's/\s*total//' | tr -d '\n' > "$(results.shp-image-size.path)"
       resources:
         limits:
           cpu: 500m
diff --git a/samples/buildstrategy/source-to-image/buildstrategy_source-to-image_cr.yaml b/samples/buildstrategy/source-to-image/buildstrategy_source-to-image_cr.yaml
index 59031aba..f9b97cf8 100644
--- a/samples/buildstrategy/source-to-image/buildstrategy_source-to-image_cr.yaml
+++ b/samples/buildstrategy/source-to-image/buildstrategy_source-to-image_cr.yaml
@@ -22,7 +22,7 @@ spec:
         - '--dockerfile=/gen-source/Dockerfile.gen'
         - '--context=/gen-source'
         - '--destination=$(params.shp-output-image)'
-        - '--oci-layout-path=/workspace/output/image'
+        - '--oci-layout-path=/kaniko/oci-image-layout'
         - '--snapshotMode=redo'
         - '--push-retry=3'
       command:
@@ -47,4 +47,30 @@ spec:
       volumeMounts:
         - mountPath: /gen-source
           name: gen-source
+        - name: layout
+          mountPath: /kaniko/oci-image-layout
       workingDir: /gen-source
+    - name: results
+      image: registry.access.redhat.com/ubi8/ubi-minimal
+      command:
+        - /bin/bash
+      args:
+        - -c
+        - |
+          set -euo pipefail
+          
+          # Store the image digest
+          grep digest /kaniko/oci-image-layout/index.json | sed -E 's/.*sha256([^"]*).*/sha256\1/' | tr -d '\n' > "$(results.shp-image-digest.path)"
+
+          # Store the image size
+          du -b -c /kaniko/oci-image-layout/blobs/sha256/* | tail -1 | sed 's/\s*total//' | tr -d '\n' > "$(results.shp-image-size.path)"
+      resources:
+        limits:
+          cpu: 100m
+          memory: 128Mi
+        requests:
+          cpu: 100m
+          memory: 128Mi
+      volumeMounts:
+        - name: layout
+          mountPath: /kaniko/oci-image-layout


Test patch:
diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go
index 7d3d7d4b..3718d417 100644
--- a/pkg/config/config_test.go
+++ b/pkg/config/config_test.go
@@ -10,6 +10,9 @@ import (
 
 	. "github.com/onsi/ginkgo"
 	. "github.com/onsi/gomega"
+	corev1 "k8s.io/api/core/v1"
+	"k8s.io/apimachinery/pkg/api/resource"
+	"k8s.io/utils/pointer"
 
 	. "github.com/shipwright-io/build/pkg/config"
 )
@@ -104,6 +107,63 @@ var _ = Describe("Config", func() {
 				Expect(config.KubeAPIOptions.QPS).To(Equal(300))
 			})
 		})
+
+		It("should allow for an override of the Git container template", func() {
+			var overrides = map[string]string{
+				"GIT_CONTAINER_TEMPLATE": "{\"image\":\"myregistry/custom/git-image\",\"resources\":{\"requests\":{\"cpu\":\"0.5\",\"memory\":\"128Mi\"}}}",
+			}
+
+			configWithEnvVariableOverrides(overrides, func(config *Config) {
+				Expect(config.GitContainerTemplate).To(Equal(corev1.Container{
+					Image: "myregistry/custom/git-image",
+					Resources: corev1.ResourceRequirements{
+						Requests: corev1.ResourceList{
+							corev1.ResourceCPU:    resource.MustParse("0.5"),
+							corev1.ResourceMemory: resource.MustParse("128Mi"),
+						},
+					},
+				}))
+			})
+		})
+
+		It("should allow for an override of the Git container image", func() {
+			var overrides = map[string]string{
+				"GIT_CONTAINER_IMAGE": "myregistry/custom/git-image",
+			}
+
+			configWithEnvVariableOverrides(overrides, func(config *Config) {
+				nonRoot := pointer.Int64Ptr(1000)
+				Expect(config.GitContainerTemplate).To(Equal(corev1.Container{
+					Image: "myregistry/custom/git-image",
+					Command: []string{
+						"/ko-app/git",
+					},
+					SecurityContext: &corev1.SecurityContext{
+						RunAsUser:  nonRoot,
+						RunAsGroup: nonRoot,
+					},
+				}))
+			})
+		})
+
+		It("should allow for an override of the Git container template and image", func() {
+			var overrides = map[string]string{
+				"GIT_CONTAINER_TEMPLATE": `{"image":"myregistry/custom/git-image","resources":{"requests":{"cpu":"0.5","memory":"128Mi"}}}`,
+				"GIT_CONTAINER_IMAGE":    "myregistry/custom/git-image:override",
+			}
+
+			configWithEnvVariableOverrides(overrides, func(config *Config) {
+				Expect(config.GitContainerTemplate).To(Equal(corev1.Container{
+					Image: "myregistry/custom/git-image:override",
+					Resources: corev1.ResourceRequirements{
+						Requests: corev1.ResourceList{
+							corev1.ResourceCPU:    resource.MustParse("0.5"),
+							corev1.ResourceMemory: resource.MustParse("128Mi"),
+						},
+					},
+				}))
+			})
+		})
 	})
 })
 
diff --git a/pkg/reconciler/buildrun/resources/credentials_test.go b/pkg/reconciler/buildrun/resources/credentials_test.go
index 5dec802a..f8b52af7 100644
--- a/pkg/reconciler/buildrun/resources/credentials_test.go
+++ b/pkg/reconciler/buildrun/resources/credentials_test.go
@@ -71,10 +71,10 @@ var _ = Describe("Credentials", func() {
 			}
 
 			expectedAfterServiceAccount = &corev1.ServiceAccount{
+				// source credential is not added to the service account
 				Secrets: []corev1.ObjectReference{
 					{Name: "secret_b"},
 					{Name: "secret_c"},
-					{Name: "secret_a"},
 					{Name: "secret_docker.io"},
 					{Name: "secret_buildrun.io"},
 				},
@@ -97,6 +97,8 @@ var _ = Describe("Credentials", func() {
 				Spec: buildv1alpha1.BuildSpec{
 					Source: buildv1alpha1.Source{
 						URL: "a/b/c",
+					},
+					Output: buildv1alpha1.Image{
 						Credentials: &corev1.LocalObjectReference{
 							Name: "secret_b",
 						},
diff --git a/pkg/reconciler/buildrun/resources/remote_artifacts_test.go b/pkg/reconciler/buildrun/resources/remote_artifacts_test.go
deleted file mode 100644
index c1d0a717..00000000
--- a/pkg/reconciler/buildrun/resources/remote_artifacts_test.go
+++ /dev/null
@@ -1,80 +0,0 @@
-// Copyright The Shipwright Contributors
-//
-// SPDX-License-Identifier: Apache-2.0
-package resources
-
-import (
-	"fmt"
-	"reflect"
-	"testing"
-
-	buildv1alpha1 "github.com/shipwright-io/build/pkg/apis/build/v1alpha1"
-	"github.com/shipwright-io/build/pkg/config"
-	"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
-)
-
-func TestRemoteArtifacts_renderRemoteArtifactsDownloadScript(t *testing.T) {
-	url1 := "http://host/path/file.ext"
-	url2 := "http://another/path/file.ext"
-
-	tests := []struct {
-		description string
-		sources     []buildv1alpha1.BuildSource
-		want        []string
-	}{{
-		description: "empty should not render a script",
-		sources:     []buildv1alpha1.BuildSource{},
-		want:        []string{},
-	}, {
-		description: "single build-source on default location",
-		sources: []buildv1alpha1.BuildSource{{
-			Name: "example",
-			URL:  url1,
-		}},
-		want: []string{
-			fmt.Sprintf("wget %s", url1),
-		},
-	}, {
-		description: "multiple build-sources",
-		sources: []buildv1alpha1.BuildSource{{
-			Name: "example-1",
-			URL:  url1,
-		}, {
-			Name: "example-2",
-			URL:  url2,
-		}},
-		want: []string{
-			fmt.Sprintf("wget %s", url1),
-			fmt.Sprintf("wget %s", url2),
-		},
-	}}
-
-	for _, tt := range tests {
-		t.Run(tt.description, func(t *testing.T) {
-			got := renderRemoteArtifactsDownloadScript(tt.sources)
-			if !reflect.DeepEqual(got, tt.want) {
-				t.Errorf("renderRemoteArtifactsDownloadScript() = '%v', want '%v'", got, tt.want)
-			}
-		})
-	}
-}
-
-func TestRemoteArtifacts_AmendTaskSpecWithRemoteArtifacts(t *testing.T) {
-	cfg := &config.Config{RemoteArtifactsContainerImage: "busybox:latest"}
-	spec := &v1beta1.TaskSpec{Steps: []v1beta1.Step{}}
-
-	exampleURL := "http://host/path/file.ext"
-
-	b := &buildv1alpha1.Build{Spec: buildv1alpha1.BuildSpec{
-		Sources: &[]buildv1alpha1.BuildSource{{
-			Name: "example",
-			URL:  exampleURL,
-		}},
-	}}
-
-	AmendTaskSpecWithRemoteArtifacts(cfg, spec, b)
-
-	if len(spec.Steps) != 1 {
-		t.Fatalf("remote-artifacts download step is not present")
-	}
-}
diff --git a/pkg/reconciler/buildrun/resources/runtime_image_test.go b/pkg/reconciler/buildrun/resources/runtime_image_test.go
index 6354c6a9..cbd063d6 100644
--- a/pkg/reconciler/buildrun/resources/runtime_image_test.go
+++ b/pkg/reconciler/buildrun/resources/runtime_image_test.go
@@ -115,7 +115,7 @@ ENTRYPOINT [ "/bin/bash", "-x", "-c" ]`,
 		It("expect to have Tekton's Task amended", func() {
 			err := AmendTaskSpecWithRuntimeImage(config.NewDefaultConfig(), taskSpec, b)
 			Expect(err).ToNot(HaveOccurred())
-			Expect(len(taskSpec.Steps)).To(Equal(2))
+			Expect(len(taskSpec.Steps)).To(Equal(3))
 		})
 	})
 })
diff --git a/pkg/reconciler/buildrun/resources/sources/git_test.go b/pkg/reconciler/buildrun/resources/sources/git_test.go
new file mode 100644
index 00000000..48ebfd35
--- /dev/null
+++ b/pkg/reconciler/buildrun/resources/sources/git_test.go
@@ -0,0 +1,106 @@
+// Copyright The Shipwright Contributors
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package sources_test
+
+import (
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+
+	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"
+
+	tektonv1beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
+	corev1 "k8s.io/api/core/v1"
+)
+
+var _ = Describe("Git", func() {
+
+	cfg := config.NewDefaultConfig()
+
+	Context("when adding a public Git source", func() {
+
+		var taskSpec *tektonv1beta1.TaskSpec
+
+		BeforeEach(func() {
+			taskSpec = &tektonv1beta1.TaskSpec{}
+		})
+
+		JustBeforeEach(func() {
+			sources.AppendGitStep(cfg, taskSpec, buildv1alpha1.Source{
+				URL: "https://github.com/shipwright-io/build",
+			}, "default")
+		})
+
+		It("adds a result for the commit sha", func() {
+			Expect(len(taskSpec.Results)).To(Equal(1))
+			Expect(taskSpec.Results[0].Name).To(Equal("shp-source-default-commit-sha"))
+		})
+
+		It("adds a step", func() {
+			Expect(len(taskSpec.Steps)).To(Equal(1))
+			Expect(taskSpec.Steps[0].Name).To(Equal("source-default"))
+			Expect(taskSpec.Steps[0].Image).To(Equal(cfg.GitContainerTemplate.Image))
+			Expect(taskSpec.Steps[0].Args).To(Equal([]string{
+				"--url",
+				"https://github.com/shipwright-io/build",
+				"--target",
+				"$(params.shp-source-root)",
+				"--result-file-commit-sha",
+				"$(results.shp-source-default-commit-sha.path)",
+			}))
+		})
+	})
+
+	Context("when adding a private Git source", func() {
+
+		var taskSpec *tektonv1beta1.TaskSpec
+
+		BeforeEach(func() {
+			taskSpec = &tektonv1beta1.TaskSpec{}
+		})
+
+		JustBeforeEach(func() {
+			sources.AppendGitStep(cfg, taskSpec, buildv1alpha1.Source{
+				URL: "git@github.com:shipwright-io/build.git",
+				Credentials: &corev1.LocalObjectReference{
+					Name: "a-secret",
+				},
+			}, "default")
+		})
+
+		It("adds a result for the commit sha", func() {
+			Expect(len(taskSpec.Results)).To(Equal(1))
+			Expect(taskSpec.Results[0].Name).To(Equal("shp-source-default-commit-sha"))
+		})
+
+		It("adds a volume for the secret", func() {
+			Expect(len(taskSpec.Volumes)).To(Equal(1))
+			Expect(taskSpec.Volumes[0].Name).To(Equal("shp-a-secret"))
+			Expect(taskSpec.Volumes[0].VolumeSource.Secret).NotTo(BeNil())
+			Expect(taskSpec.Volumes[0].VolumeSource.Secret.SecretName).To(Equal("a-secret"))
+		})
+
+		It("adds a step", func() {
+			Expect(len(taskSpec.Steps)).To(Equal(1))
+			Expect(taskSpec.Steps[0].Name).To(Equal("source-default"))
+			Expect(taskSpec.Steps[0].Image).To(Equal(cfg.GitContainerTemplate.Image))
+			Expect(taskSpec.Steps[0].Args).To(Equal([]string{
+				"--url",
+				"git@github.com:shipwright-io/build.git",
+				"--target",
+				"$(params.shp-source-root)",
+				"--result-file-commit-sha",
+				"$(results.shp-source-default-commit-sha.path)",
+				"--secret-path",
+				"/workspace/shp-source-secret",
+			}))
+			Expect(len(taskSpec.Steps[0].VolumeMounts)).To(Equal(1))
+			Expect(taskSpec.Steps[0].VolumeMounts[0].Name).To(Equal("shp-a-secret"))
+			Expect(taskSpec.Steps[0].VolumeMounts[0].MountPath).To(Equal("/workspace/shp-source-secret"))
+			Expect(taskSpec.Steps[0].VolumeMounts[0].ReadOnly).To(BeTrue())
+		})
+	})
+})
diff --git a/pkg/reconciler/buildrun/resources/sources/http_test.go b/pkg/reconciler/buildrun/resources/sources/http_test.go
new file mode 100644
index 00000000..fb0150ba
--- /dev/null
+++ b/pkg/reconciler/buildrun/resources/sources/http_test.go
@@ -0,0 +1,115 @@
+// Copyright The Shipwright Contributors
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package sources_test
+
+import (
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+
+	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"
+
+	tektonv1beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
+	corev1 "k8s.io/api/core/v1"
+)
+
+var _ = Describe("HTTP", func() {
+
+	cfg := config.NewDefaultConfig()
+
+	Context("when a TaskSpec does not contain an step", func() {
+
+		var taskSpec *tektonv1beta1.TaskSpec
+
+		BeforeEach(func() {
+			taskSpec = &tektonv1beta1.TaskSpec{}
+		})
+
+		It("adds the first step", func() {
+			sources.AppendHTTPStep(cfg, taskSpec, buildv1alpha1.BuildSource{
+				Name: "logo",
+				URL:  "https://shipwright.io/icons/logo.svg",
+			})
+
+			Expect(len(taskSpec.Steps)).To(Equal(1))
+			Expect(taskSpec.Steps[0].Name).To(Equal("sources-http"))
+			Expect(taskSpec.Steps[0].Image).To(Equal(cfg.RemoteArtifactsContainerImage))
+			Expect(taskSpec.Steps[0].WorkingDir).To(Equal("$(params.shp-source-root)"))
+			Expect(taskSpec.Steps[0].Args[3]).To(Equal("wget \"https://shipwright.io/icons/logo.svg\""))
+		})
+	})
+
+	Context("when a TaskSpec already contains the http step", func() {
+
+		var taskSpec *tektonv1beta1.TaskSpec
+
+		BeforeEach(func() {
+			taskSpec = &tektonv1beta1.TaskSpec{
+				Steps: []tektonv1beta1.Step{
+					{
+						Container: corev1.Container{
+							Name:       "sources-http",
+							Image:      cfg.RemoteArtifactsContainerImage,
+							WorkingDir: "$(params.shp-source-root)",
+							Command: []string{
+								"/bin/sh",
+							},
+							Args: []string{
+								"-e",
+								"-x",
+								"-c",
+								"wget \"https://tekton.dev/images/tekton-horizontal-color.png\"",
+							},
+						},
+					},
+				},
+			}
+		})
+
+		It("updates the existing step", func() {
+			sources.AppendHTTPStep(cfg, taskSpec, buildv1alpha1.BuildSource{
+				Name: "logo",
+				URL:  "https://shipwright.io/icons/logo.svg",
+			})
+
+			Expect(len(taskSpec.Steps)).To(Equal(1))
+			Expect(taskSpec.Steps[0].Name).To(Equal("sources-http"))
+			Expect(taskSpec.Steps[0].Image).To(Equal(cfg.RemoteArtifactsContainerImage))
+			Expect(taskSpec.Steps[0].WorkingDir).To(Equal("$(params.shp-source-root)"))
+			Expect(taskSpec.Steps[0].Args[3]).To(Equal("wget \"https://tekton.dev/images/tekton-horizontal-color.png\" ; wget \"https://shipwright.io/icons/logo.svg\""))
+		})
+	})
+
+	Context("when a TaskSpec already another source step step", func() {
+
+		var taskSpec *tektonv1beta1.TaskSpec
+
+		BeforeEach(func() {
+			taskSpec = &tektonv1beta1.TaskSpec{
+				Steps: []tektonv1beta1.Step{
+					{
+						Container: corev1.Container{
+							Name: "source-something",
+						},
+					},
+				},
+			}
+		})
+
+		It("appends the http step", func() {
+			sources.AppendHTTPStep(cfg, taskSpec, buildv1alpha1.BuildSource{
+				Name: "logo",
+				URL:  "https://shipwright.io/icons/logo.svg",
+			})
+
+			Expect(len(taskSpec.Steps)).To(Equal(2))
+			Expect(taskSpec.Steps[1].Name).To(Equal("sources-http"))
+			Expect(taskSpec.Steps[1].Image).To(Equal(cfg.RemoteArtifactsContainerImage))
+			Expect(taskSpec.Steps[1].WorkingDir).To(Equal("$(params.shp-source-root)"))
+			Expect(taskSpec.Steps[1].Args[3]).To(Equal("wget \"https://shipwright.io/icons/logo.svg\""))
+		})
+	})
+})
diff --git a/pkg/reconciler/buildrun/resources/sources/sources_suite_test.go b/pkg/reconciler/buildrun/resources/sources/sources_suite_test.go
new file mode 100644
index 00000000..1ed37125
--- /dev/null
+++ b/pkg/reconciler/buildrun/resources/sources/sources_suite_test.go
@@ -0,0 +1,17 @@
+// Copyright The Shipwright Contributors
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package sources_test
+
+import (
+	"testing"
+
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+)
+
+func TestResources(t *testing.T) {
+	RegisterFailHandler(Fail)
+	RunSpecs(t, "Sources Suite")
+}
diff --git a/pkg/reconciler/buildrun/resources/sources/utils_test.go b/pkg/reconciler/buildrun/resources/sources/utils_test.go
new file mode 100644
index 00000000..dd66c838
--- /dev/null
+++ b/pkg/reconciler/buildrun/resources/sources/utils_test.go
@@ -0,0 +1,85 @@
+// Copyright The Shipwright Contributors
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package sources_test
+
+import (
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+	"github.com/shipwright-io/build/pkg/reconciler/buildrun/resources/sources"
+
+	tektonv1beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
+	corev1 "k8s.io/api/core/v1"
+)
+
+var _ = Describe("Utils", func() {
+
+	Context("for different candidate volume names", func() {
+
+		It("retains a name that is okay", func() {
+			Expect(sources.SanitizeVolumeName("okay-name")).To(Equal("okay-name"))
+		})
+
+		It("replaces characters that are not allowed", func() {
+			Expect(sources.SanitizeVolumeName("bad.name")).To(Equal("bad-name"))
+		})
+
+		It("reduces the length if needed", func() {
+			Expect(sources.SanitizeVolumeName("long-name-long-name-long-name-long-name-long-name-long-name-long-name-")).To(Equal("long-name-long-name-long-name-long-name-long-name-long-name-lon"))
+		})
+	})
+
+	Context("when a TaskSpec does not contain any volume", func() {
+
+		var taskSpec *tektonv1beta1.TaskSpec
+
+		BeforeEach(func() {
+			taskSpec = &tektonv1beta1.TaskSpec{}
+		})
+
+		It("adds the first volume", func() {
+			sources.AppendSecretVolume(taskSpec, "a-secret")
+
+			Expect(len(taskSpec.Volumes)).To(Equal(1))
+			Expect(taskSpec.Volumes[0].Name).To(Equal("shp-a-secret"))
+			Expect(taskSpec.Volumes[0].VolumeSource.Secret).NotTo(BeNil())
+			Expect(taskSpec.Volumes[0].VolumeSource.Secret.SecretName).To(Equal("a-secret"))
+		})
+	})
+
+	Context("when a TaskSpec already contains a volume secret", func() {
+
+		var taskSpec *tektonv1beta1.TaskSpec
+
+		BeforeEach(func() {
+			taskSpec = &tektonv1beta1.TaskSpec{
+				Volumes: []corev1.Volume{
+					{
+						Name: "shp-a-secret",
+						VolumeSource: corev1.VolumeSource{
+							Secret: &corev1.SecretVolumeSource{
+								SecretName: "a-secret",
+							},
+						},
+					},
+				},
+			}
+		})
+
+		It("adds another one when the name does not match", func() {
+			sources.AppendSecretVolume(taskSpec, "b-secret")
+
+			Expect(len(taskSpec.Volumes)).To(Equal(2))
+			Expect(taskSpec.Volumes[1].Name).To(Equal("shp-b-secret"))
+			Expect(taskSpec.Volumes[1].VolumeSource.Secret).NotTo(BeNil())
+			Expect(taskSpec.Volumes[1].VolumeSource.Secret.SecretName).To(Equal("b-secret"))
+		})
+
+		It("keeps the volume list unchanged if the same secret is appended", func() {
+			sources.AppendSecretVolume(taskSpec, "a-secret")
+
+			Expect(len(taskSpec.Volumes)).To(Equal(1))
+		})
+	})
+})
diff --git a/pkg/reconciler/buildrun/resources/taskrun_test.go b/pkg/reconciler/buildrun/resources/taskrun_test.go
index f7b603ac..dda5f62b 100644
--- a/pkg/reconciler/buildrun/resources/taskrun_test.go
+++ b/pkg/reconciler/buildrun/resources/taskrun_test.go
@@ -21,22 +21,22 @@ import (
 	"github.com/shipwright-io/build/pkg/config"
 	"github.com/shipwright-io/build/pkg/reconciler/buildrun/resources"
 	"github.com/shipwright-io/build/test"
+	"github.com/shipwright-io/build/test/utils"
 )
 
 var _ = Describe("GenerateTaskrun", func() {
 
 	var (
-		build                       *buildv1alpha1.Build
-		buildRun                    *buildv1alpha1.BuildRun
-		buildStrategy               *buildv1alpha1.BuildStrategy
-		builderImage                *buildv1alpha1.Image
-		dockerfile, buildpacks, url string
-		ctl                         test.Catalog
+		build                  *buildv1alpha1.Build
+		buildRun               *buildv1alpha1.BuildRun
+		buildStrategy          *buildv1alpha1.BuildStrategy
+		builderImage           *buildv1alpha1.Image
+		dockerfile, buildpacks string
+		ctl                    test.Catalog
 	)
 
 	BeforeEach(func() {
 		buildpacks = "buildpacks-v3"
-		url = "https://github.com/shipwright-io/sample-go"
 		dockerfile = "Dockerfile"
 	})
 
@@ -73,24 +73,46 @@ var _ = Describe("GenerateTaskrun", func() {
 				Expect(err).To(BeNil())
 			})
 
+			It("should contain a step to clone the Git sources", func() {
+				Expect(got.Steps[0].Name).To(Equal("source-default"))
+				Expect(got.Steps[0].Command[0]).To(Equal("/ko-app/git"))
+				Expect(got.Steps[0].Args).To(Equal([]string{
+					"--url",
+					build.Spec.Source.URL,
+					"--target",
+					"$(params.shp-source-root)",
+					"--result-file-commit-sha",
+					"$(results.shp-source-default-commit-sha.path)",
+				}))
+			})
+
+			It("should contain results for the image", func() {
+				Expect(got.Results).To(utils.ContainNamedElement("shp-image-digest"))
+				Expect(got.Results).To(utils.ContainNamedElement("shp-image-size"))
+			})
+
+			It("should contain a result for the Git commit SHA", func() {
+				Expect(got.Results).To(utils.ContainNamedElement("shp-source-default-commit-sha"))
+			})
+
 			It("should ensure IMAGE is replaced by builder image when needed.", func() {
-				Expect(got.Steps[0].Container.Image).To(Equal("quay.io/containers/buildah:v1.20.1"))
+				Expect(got.Steps[1].Container.Image).To(Equal("quay.io/containers/buildah:v1.20.1"))
 			})
 
 			It("should ensure command replacements happen when needed", func() {
-				Expect(got.Steps[0].Container.Command[0]).To(Equal("/usr/bin/buildah"))
+				Expect(got.Steps[1].Container.Command[0]).To(Equal("/usr/bin/buildah"))
 			})
 
 			It("should ensure resource replacements happen for the first step", func() {
-				Expect(got.Steps[0].Container.Resources).To(Equal(ctl.LoadCustomResources("500m", "1Gi")))
+				Expect(got.Steps[1].Container.Resources).To(Equal(ctl.LoadCustomResources("500m", "1Gi")))
 			})
 
 			It("should ensure resource replacements happen for the second step", func() {
-				Expect(got.Steps[1].Container.Resources).To(Equal(ctl.LoadCustomResources("100m", "65Mi")))
+				Expect(got.Steps[2].Container.Resources).To(Equal(ctl.LoadCustomResources("100m", "65Mi")))
 			})
 
 			It("should ensure arg replacements happen when needed", func() {
-				Expect(got.Steps[0].Container.Args).To(Equal(expectedCommandOrArg))
+				Expect(got.Steps[1].Container.Args).To(Equal(expectedCommandOrArg))
 			})
 
 			It("should ensure top level volumes are populated", func() {
@@ -98,60 +120,27 @@ var _ = Describe("GenerateTaskrun", func() {
 			})
 
 			It("should contain the shipwright system parameters", func() {
-				params := got.Params
-
-				paramSourceRootFound := false
-				paramSourceContextFound := false
-				paramOutputImageFound := false
+				Expect(got.Params).To(utils.ContainNamedElement("shp-source-root"))
+				Expect(got.Params).To(utils.ContainNamedElement("shp-source-context"))
+				Expect(got.Params).To(utils.ContainNamedElement("shp-output-image"))
 
 				// legacy params
-				paramBuilderImageFound := false
-				paramDockerfileFound := false
-				paramContextDirFound := false
-
-				for _, param := range params {
-					switch param.Name {
-					case "shp-source-root":
-						paramSourceRootFound = true
-
-					case "shp-source-context":
-						paramSourceContextFound = true
-
-					case "shp-output-image":
-						paramOutputImageFound = true
-
-					case "BUILDER_IMAGE":
-						paramBuilderImageFound = true
+				Expect(got.Params).ToNot(utils.ContainNamedElement("BUILDER_IMAGE")) // test build has no builder image
+				Expect(got.Params).To(utils.ContainNamedElement("CONTEXT_DIR"))
+				Expect(got.Params).To(utils.ContainNamedElement("DOCKERFILE"))
 
-					case "DOCKERFILE":
-						paramDockerfileFound = true
-
-					case "CONTEXT_DIR":
-						paramContextDirFound = true
-
-					default:
-						Fail(fmt.Sprintf("Unexpected param found: %s", param.Name))
-					}
-				}
-
-				Expect(paramSourceRootFound).To(BeTrue())
-				Expect(paramSourceContextFound).To(BeTrue())
-				Expect(paramOutputImageFound).To(BeTrue())
-
-				Expect(paramBuilderImageFound).To(BeFalse()) // test build has no builder image
-				Expect(paramDockerfileFound).To(BeTrue())
-				Expect(paramContextDirFound).To(BeTrue())
+				Expect(len(got.Params)).To(Equal(5))
 			})
 		})
 	})
 
 	Describe("Generate the TaskRun", func() {
 		var (
-			k8sDuration30s                                                                      *metav1.Duration
-			k8sDuration1m                                                                       *metav1.Duration
-			namespace, contextDir, revision, outputPath, outputPathBuildRun, serviceAccountName string
-			got                                                                                 *v1beta1.TaskRun
-			err                                                                                 error
+			k8sDuration30s                                                            *metav1.Duration
+			k8sDuration1m                                                             *metav1.Duration
+			namespace, contextDir, outputPath, outputPathBuildRun, serviceAccountName string
+			got                                                                       *v1beta1.TaskRun
+			err                                                                       error
 		)
 		BeforeEach(func() {
 			duration, err := time.ParseDuration("30s")
@@ -167,7 +156,6 @@ var _ = Describe("GenerateTaskrun", func() {
 
 			namespace = "build-test"
 			contextDir = "docker-build"
-			revision = ""
 			builderImage = &buildv1alpha1.Image{
 				Image: "heroku/buildpacks:18",
 			}
@@ -210,31 +198,8 @@ var _ = Describe("GenerateTaskrun", func() {
 				Expect(got.Annotations["kubernetes.io/ingress-bandwidth"]).To(Equal("1M"))
 			})
 
-			It("should ensure generated TaskRun's input and output resources are correct", func() {
-				inputResources := got.Spec.Resources.Inputs
-				for _, inputResource := range inputResources {
-					Expect(inputResource.ResourceSpec.Type).To(Equal(v1beta1.PipelineResourceTypeGit))
-					params := inputResource.ResourceSpec.Params
-					for _, param := range params {
-						if param.Name == "url" {
-							Expect(param.Value).To(Equal(url))
-						}
-						if param.Name == "revision" {
-							Expect(param.Value).To(Equal(revision))
-						}
-					}
-				}
-
-				outputResources := got.Spec.Resources.Outputs
-				for _, outputResource := range outputResources {
-					Expect(outputResource.ResourceSpec.Type).To(Equal(v1beta1.PipelineResourceTypeImage))
-					params := outputResource.ResourceSpec.Params
-					for _, param := range params {
-						if param.Name == "url" {
-							Expect(param.Value).To(Equal(outputPath))
-						}
-					}
-				}
+			It("should ensure generated TaskRun has no resources", func() {
+				Expect(got.Spec.Resources).To(BeNil())
 			})
 
 			It("should ensure resource replacements happen when needed", func() {
@@ -248,7 +213,7 @@ var _ = Describe("GenerateTaskrun", func() {
 						corev1.ResourceMemory: resource.MustParse("2Gi"),
 					},
 				}
-				Expect(got.Spec.TaskSpec.Steps[0].Resources).To(Equal(expectedResourceOrArg))
+				Expect(got.Spec.TaskSpec.Steps[1].Resources).To(Equal(expectedResourceOrArg))
 			})
 
 			It("should have no timeout set", func() {
@@ -344,7 +309,7 @@ var _ = Describe("GenerateTaskrun", func() {
 						corev1.ResourceMemory: resource.MustParse("2Gi"),
 					},
 				}
-				Expect(got.Spec.TaskSpec.Steps[0].Resources).To(Equal(expectedResourceOrArg))
+				Expect(got.Spec.TaskSpec.Steps[1].Resources).To(Equal(expectedResourceOrArg))
 			})
 
 			It("should have the timeout set correctly", func() {
@@ -392,19 +357,6 @@ var _ = Describe("GenerateTaskrun", func() {
 				Expect(err).To(BeNil())
 			})
 
-			It("should use the imageURL from the BuildRun in the resource", func() {
-				outputResources := got.Spec.Resources.Outputs
-				for _, outputResource := range outputResources {
-					Expect(outputResource.ResourceSpec.Type).To(Equal(v1beta1.PipelineResourceTypeImage))
-					params := outputResource.ResourceSpec.Params
-					for _, param := range params {
-						if param.Name == "url" {
-							Expect(param.Value).To(Equal(outputPathBuildRun))
-						}
-					}
-				}
-			})
-
 			It("should use the imageURL from the BuildRun for the param", func() {
 				params := got.Spec.Params
 
diff --git a/test/clusterbuildstrategy_samples.go b/test/clusterbuildstrategy_samples.go
index cd6e63ab..d82e7985 100644
--- a/test/clusterbuildstrategy_samples.go
+++ b/test/clusterbuildstrategy_samples.go
@@ -150,7 +150,6 @@ spec:
         - --dockerfile=$(build.dockerfile)
         - --context=$(params.shp-source-context)
         - --destination=$(params.shp-output-image)
-        - --oci-layout-path=/workspace/output/image
         - --snapshotMode=redo
         - --push-retry=3
       resources:
@@ -200,7 +199,6 @@ spec:
         - --dockerfile=$(build.dockerfile)
         - --context=$(params.shp-source-context)
         - --destination=$(params.shp-output-image)
-        - --oci-layout-path=/workspace/output/image
         - --snapshotMode=redo
         - --push-retry=3
       resources:
@@ -315,7 +313,6 @@ spec:
         - --dockerfile=$(build.dockerfile)
         - --context=$(params.shp-source-root)
         - --destination=$(params.shp-output-image)
-        - --oci-layout-path=/workspace/output/image
         - --snapshotMode=redo
         - --push-retry=3
       resources:
diff --git a/test/integration/build_to_buildruns_test.go b/test/integration/build_to_buildruns_test.go
index 744f12b3..bfcf8655 100644
--- a/test/integration/build_to_buildruns_test.go
+++ b/test/integration/build_to_buildruns_test.go
@@ -142,7 +142,7 @@ var _ = Describe("Integration tests Build and BuildRuns", func() {
 			tr, err := tb.GetTaskRunFromBuildRun(buildRun.Name)
 			Expect(err).To(BeNil())
 
-			Expect(tr.Spec.Resources.Outputs[0].PipelineResourceBinding.ResourceSpec.Params[0].Value).To(Equal("foobar.registry.com"))
+			Expect(tr.Spec.Params[0].Value.StringVal).To(Equal("foobar.registry.com"))
 
 		})
 	})
@@ -338,11 +338,11 @@ var _ = Describe("Integration tests Build and BuildRuns", func() {
 
 			tr01, err := tb.GetTaskRunFromBuildRun(buildRun01.Name)
 			Expect(err).To(BeNil())
-			Expect(tr01.Spec.Resources.Inputs[0].PipelineResourceBinding.ResourceSpec.Params[0].Value).To(Equal("https://github.com/shipwright-io/sample-go"))
+			Expect(tr01.Spec.TaskSpec.Steps[0].Args[1]).To(Equal("https://github.com/shipwright-io/sample-go"))
 
 			tr02, err := tb.GetTaskRunFromBuildRun(buildRun02.Name)
 			Expect(err).To(BeNil())
-			Expect(tr02.Spec.Resources.Inputs[0].PipelineResourceBinding.ResourceSpec.Params[0].Value).To(Equal("https://github.com/shipwright-io/sample-go"))
+			Expect(tr02.Spec.TaskSpec.Steps[0].Args[1]).To(Equal("https://github.com/shipwright-io/sample-go"))
 
 		})
 	})
diff --git a/test/utils/controllers.go b/test/utils/controllers.go
index 6b94a164..277b09dc 100644
--- a/test/utils/controllers.go
+++ b/test/utils/controllers.go
@@ -17,6 +17,9 @@ import (
 func (t *TestBuild) StartBuildControllers() (chan struct{}, error) {
 	c := buildconfig.NewDefaultConfig()
 
+	// read configuration from environment variables, especially the GIT_CONTAINER_IMAGE
+	c.SetConfigFromEnv()
+
 	mgr, err := controller.NewManager(t.Context, c, t.KubeConfig, manager.Options{
 		Namespace:          t.Namespace,
 		LeaderElection:     false,
diff --git a/test/utils/gomega.go b/test/utils/gomega.go
new file mode 100644
index 00000000..f84edf4b
--- /dev/null
+++ b/test/utils/gomega.go
@@ -0,0 +1,52 @@
+// Copyright The Shipwright Contributors
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package utils
+
+import (
+	"reflect"
+
+	"github.com/onsi/gomega/format"
+	"github.com/onsi/gomega/types"
+)
+
+type ContainNamedElementMatcher struct {
+	Name string
+}
+
+func (matcher *ContainNamedElementMatcher) FailureMessage(actual interface{}) (message string) {
+	return format.Message(actual, "to contain element with name", matcher.Name)
+}
+
+func (matcher *ContainNamedElementMatcher) NegatedFailureMessage(actual interface{}) (message string) {
+	return format.Message(actual, "not to contain element with name", matcher.Name)
+}
+
+func (matcher *ContainNamedElementMatcher) Match(actual interface{}) (success bool, err error) {
+	if actual == nil {
+		return false, nil
+	}
+
+	kind := reflect.TypeOf(actual).Kind()
+	if kind == reflect.Array || kind == reflect.Slice {
+		value := reflect.ValueOf(actual)
+
+		for i := 0; i < value.Len(); i++ {
+			vItem := value.Index(i)
+			vName := vItem.FieldByName("Name")
+			if matcher.Name == vName.String() {
+				return true, nil
+			}
+		}
+	}
+
+	return false, nil
+}
+
+// ContainNamedElement can be applied for an array or slice of objects which have a Name field, to check if any item has a matching name
+func ContainNamedElement(name string) types.GomegaMatcher {
+	return &ContainNamedElementMatcher{
+		Name: name,
+	}
+}