Repository: jenkins-x/lighthouse (go)

Issue:
Support git-batch-merge Tekton task
We should ideally be merging PRs onto their target branch before building them - Jenkins X does this automatically, but with Tekton, we currently just support the `git-clone` task. We should support `git-batch-merge` (i.e., https://github.com/tektoncd/catalog/tree/master/task/git-batch-merge/0.1/) as well.

Pull request description:
feat(tekton): Support git-batch-merge Task parameters
Detect the `git-batch-merge` catalog `Task` and replace parameters as appropriate for this task as well as `git-clone`.

fixes #889

Signed-off-by: Andrew Bayer <andrew.bayer@gmail.com>

Patch:
diff --git a/pkg/engines/tekton/controller.go b/pkg/engines/tekton/controller.go
index 56aa7f29..522d1c2e 100644
--- a/pkg/engines/tekton/controller.go
+++ b/pkg/engines/tekton/controller.go
@@ -49,6 +49,8 @@ const (
 	gitCloneCatalogTaskName = "git-clone"
 	gitCloneURLParam        = "url"
 	gitCloneRevisionParam   = "revision"
+	gitMergeCatalogTaskName = "git-batch-merge"
+	gitMergeBatchRefsParam  = "batchedRefs"
 )
 
 // Controller listens for changes to PipelineRuns and updates the corresponding LighthouseJobs with their activity
@@ -509,18 +511,30 @@ func (c *Controller) makePipelineRun(lj v1alpha1.LighthouseJob) (*tektonv1beta1.
 	// Add parameters instead of env vars.
 	env := lj.Spec.GetEnvVars()
 	env[v1alpha1.BuildIDEnv] = buildID
-	urlParamKey, revParamKey, err := determineGitCloneTaskParams(&p, c.tektonClient)
+	paramNames, err := determineGitCloneOrMergeTaskParams(&p, c.tektonClient)
 	if err != nil {
 		return nil, err
 	}
-	if urlParamKey == "" || revParamKey == "" {
-		c.logger.Warnf("git-clone task parameters not found in Pipeline for PipelineRun, so skipping setting PipelineRun parameters for revision")
+	if paramNames == nil {
+		c.logger.Warnf("git-clone and/or git-batch-merge task parameters not found in Pipeline for PipelineRun, so skipping setting PipelineRun parameters for revision")
 	} else {
-		env[urlParamKey] = lj.Spec.Refs.CloneURI
-		if len(lj.Spec.Refs.Pulls) > 0 {
-			env[revParamKey] = lj.Spec.Refs.Pulls[0].SHA
-		} else {
-			env[revParamKey] = "master"
+		env[paramNames.urlParam] = lj.Spec.Refs.CloneURI
+		if paramNames.revParam != "" {
+			if len(lj.Spec.Refs.Pulls) > 0 {
+				env[paramNames.revParam] = lj.Spec.Refs.Pulls[0].SHA
+			} else {
+				env[paramNames.revParam] = "master"
+			}
+		}
+		if paramNames.baseRevisionParam != "" {
+			env[paramNames.baseRevisionParam] = lj.Spec.Refs.BaseRef
+		}
+		if paramNames.batchedRefsParam != "" {
+			var batchedRefsVals []string
+			for _, pull := range lj.Spec.Refs.Pulls {
+				batchedRefsVals = append(batchedRefsVals, pull.Ref)
+			}
+			env[paramNames.batchedRefsParam] = strings.Join(batchedRefsVals, " ")
 		}
 	}
 	for _, key := range sets.StringKeySet(env).List() {
@@ -538,13 +552,20 @@ func (c *Controller) makePipelineRun(lj v1alpha1.LighthouseJob) (*tektonv1beta1.
 	return &p, nil
 }
 
-func determineGitCloneTaskParams(pr *tektonv1beta1.PipelineRun, tektonClient tektonclient.Interface) (string, string, error) {
+type gitTaskParamNames struct {
+	urlParam          string
+	revParam          string
+	batchedRefsParam  string
+	baseRevisionParam string
+}
+
+func determineGitCloneOrMergeTaskParams(pr *tektonv1beta1.PipelineRun, tektonClient tektonclient.Interface) (*gitTaskParamNames, error) {
 	if pr == nil {
-		return "", "", errors.New("provided PipelineRun is nil")
+		return nil, errors.New("provided PipelineRun is nil")
 	}
 
 	if pr.Spec.PipelineSpec == nil && pr.Spec.PipelineRef == nil {
-		return "", "", errors.New("neither PipelineSpec nor PipelineRef specified for PipelineRun")
+		return nil, errors.New("neither PipelineSpec nor PipelineRef specified for PipelineRun")
 	}
 	var pipelineSpec *tektonv1beta1.PipelineSpec
 
@@ -553,31 +574,51 @@ func determineGitCloneTaskParams(pr *tektonv1beta1.PipelineRun, tektonClient tek
 	} else {
 		pipeline, err := tektonClient.TektonV1beta1().Pipelines(pr.Namespace).Get(pr.Spec.PipelineRef.Name, metav1.GetOptions{})
 		if err != nil {
-			return "", "", errors.Wrapf(err, "failed to find Pipeline %s for PipelineRun", pr.Spec.PipelineRef.Name)
+			return nil, errors.Wrapf(err, "failed to find Pipeline %s for PipelineRun", pr.Spec.PipelineRef.Name)
 		}
 		pipelineSpec = &pipeline.Spec
 	}
 
+	paramNames := &gitTaskParamNames{}
+
 	for _, task := range pipelineSpec.Tasks {
-		if task.TaskRef != nil && task.TaskRef.Name == gitCloneCatalogTaskName {
-			urlParam := ""
-			revParam := ""
-			for _, p := range task.Params {
-				if p.Name == gitCloneURLParam && p.Value.Type == tektonv1beta1.ParamTypeString {
-					urlParam = extractPipelineParamFromTaskParamValue(p.Value.StringVal)
+		if task.TaskRef != nil {
+			if task.TaskRef.Name == gitCloneCatalogTaskName {
+				for _, p := range task.Params {
+					if p.Name == gitCloneURLParam && p.Value.Type == tektonv1beta1.ParamTypeString {
+						paramNames.urlParam = extractPipelineParamFromTaskParamValue(p.Value.StringVal)
+					}
+					if p.Name == gitCloneRevisionParam && p.Value.Type == tektonv1beta1.ParamTypeString {
+						paramNames.revParam = extractPipelineParamFromTaskParamValue(p.Value.StringVal)
+					}
 				}
-				if p.Name == gitCloneRevisionParam && p.Value.Type == tektonv1beta1.ParamTypeString {
-					revParam = extractPipelineParamFromTaskParamValue(p.Value.StringVal)
+
+				if paramNames.urlParam != "" && paramNames.revParam != "" {
+					return paramNames, nil
 				}
 			}
+			if task.TaskRef.Name == gitMergeCatalogTaskName {
+				for _, p := range task.Params {
+					if p.Name == gitCloneURLParam && p.Value.Type == tektonv1beta1.ParamTypeString {
+						paramNames.urlParam = extractPipelineParamFromTaskParamValue(p.Value.StringVal)
+					}
+					if p.Name == gitCloneRevisionParam && p.Value.Type == tektonv1beta1.ParamTypeString {
+						paramNames.baseRevisionParam = extractPipelineParamFromTaskParamValue(p.Value.StringVal)
+					}
+					if p.Name == gitMergeBatchRefsParam && p.Value.Type == tektonv1beta1.ParamTypeString {
+						paramNames.batchedRefsParam = extractPipelineParamFromTaskParamValue(p.Value.StringVal)
+					}
+				}
+
+				if paramNames.urlParam != "" && paramNames.batchedRefsParam != "" {
+					return paramNames, nil
+				}
 
-			if urlParam != "" && revParam != "" {
-				return urlParam, revParam, nil
 			}
 		}
 	}
 
-	return "", "", nil
+	return nil, nil
 }
 
 func extractPipelineParamFromTaskParamValue(taskParam string) string {
diff --git a/pkg/jobutil/jobutil.go b/pkg/jobutil/jobutil.go
index 07c9fcb8..b7a06af8 100644
--- a/pkg/jobutil/jobutil.go
+++ b/pkg/jobutil/jobutil.go
@@ -54,7 +54,7 @@ func NewLighthouseJob(spec v1alpha1.LighthouseJobSpec, extraLabels, extraAnnotat
 	}
 }
 
-func createRefs(pr *scm.PullRequest, baseSHA string) v1alpha1.Refs {
+func createRefs(pr *scm.PullRequest, baseSHA string, prRefFmt string) v1alpha1.Refs {
 	org := pr.Base.Repo.Namespace
 	repo := pr.Base.Repo.Name
 	number := pr.Number
@@ -77,6 +77,7 @@ func createRefs(pr *scm.PullRequest, baseSHA string) v1alpha1.Refs {
 				Link:       pr.Link,
 				AuthorLink: pr.Author.Link,
 				CommitLink: fmt.Sprintf("%s/pull/%d/commits/%s", repoLink, number, pr.Head.Sha),
+				Ref:        fmt.Sprintf(prRefFmt, number),
 			},
 		},
 	}
@@ -85,8 +86,8 @@ func createRefs(pr *scm.PullRequest, baseSHA string) v1alpha1.Refs {
 // NewPresubmit converts a config.Presubmit into a builder.PipelineOptions.
 // The builder.Refs are configured correctly per the pr, baseSHA.
 // The eventGUID becomes a gitprovider.EventGUID label.
-func NewPresubmit(pr *scm.PullRequest, baseSHA string, job config.Presubmit, eventGUID string) v1alpha1.LighthouseJob {
-	refs := createRefs(pr, baseSHA)
+func NewPresubmit(pr *scm.PullRequest, baseSHA string, job config.Presubmit, eventGUID string, prRefFmt string) v1alpha1.LighthouseJob {
+	refs := createRefs(pr, baseSHA, prRefFmt)
 	labels := make(map[string]string)
 	for k, v := range job.Labels {
 		labels[k] = v
diff --git a/pkg/keeper/keeper.go b/pkg/keeper/keeper.go
index db6b823e..395e770a 100644
--- a/pkg/keeper/keeper.go
+++ b/pkg/keeper/keeper.go
@@ -67,6 +67,7 @@ type scmProviderClient interface {
 	Query(context.Context, interface{}, map[string]interface{}) error
 	SupportsGraphQL() bool
 	ProviderType() string
+	PRRefFmt() string
 	GetRepositoryByFullName(string) (*scm.Repository, error)
 	ListAllPullRequestsForFullNameRepo(string, scm.PullRequestListOptions) ([]*scm.PullRequest, error)
 	CreateComment(owner, repo string, number int, isPR bool, comment string) error
@@ -1113,6 +1114,7 @@ func (c *DefaultController) trigger(sp subpool, presubmits map[int][]config.Pres
 				Number: int(pr.Number),
 				Author: string(pr.Author.Login),
 				SHA:    string(pr.HeadRefOID),
+				Ref:    fmt.Sprintf(c.spc.PRRefFmt(), int(pr.Number)),
 			},
 		)
 	}
diff --git a/pkg/plugins/override/override.go b/pkg/plugins/override/override.go
index 1e0dba8c..861b3694 100644
--- a/pkg/plugins/override/override.go
+++ b/pkg/plugins/override/override.go
@@ -51,6 +51,7 @@ type scmProviderClient interface {
 	HasPermission(org, repo, user string, role ...string) (bool, error)
 	ListStatuses(org, repo, ref string) ([]*scm.Status, error)
 	ProviderType() string
+	PRRefFmt() string
 	IsOrgAdmin(string, string) (bool, error)
 	QuoteAuthorForComment(string) string
 }
@@ -81,6 +82,10 @@ func (c client) ProviderType() string {
 	return c.spc.ProviderType()
 }
 
+func (c client) PRRefFmt() string {
+	return c.spc.PRRefFmt()
+}
+
 func (c client) IsOrgAdmin(org, user string) (bool, error) {
 	return c.spc.IsOrgAdmin(org, user)
 }
@@ -270,7 +275,7 @@ Only the following contexts were expected:
 				return oc.CreateComment(org, repo, number, e.IsPR, plugins.FormatResponseRaw(e.Body, e.Link, oc.QuoteAuthorForComment(user), resp))
 			}
 
-			pj := jobutil.NewPresubmit(pr, baseSHA, *pre, e.GUID)
+			pj := jobutil.NewPresubmit(pr, baseSHA, *pre, e.GUID, oc.PRRefFmt())
 			now := metav1.Now()
 			pj.Status = v1alpha1.LighthouseJobStatus{
 				State:          v1alpha1.SuccessState,
diff --git a/pkg/plugins/trigger/trigger.go b/pkg/plugins/trigger/trigger.go
index 0d3dc321..cb130dc7 100644
--- a/pkg/plugins/trigger/trigger.go
+++ b/pkg/plugins/trigger/trigger.go
@@ -108,6 +108,7 @@ type scmProviderClient interface {
 	DeleteStaleComments(org, repo string, number int, comments []*scm.Comment, pr bool, isStale func(*scm.Comment) bool) error
 	GetIssueLabels(org, repo string, number int, pr bool) ([]*scm.Label, error)
 	QuoteAuthorForComment(string) string
+	PRRefFmt() string
 }
 
 type launcher interface {
@@ -255,7 +256,7 @@ func runRequested(c Client, pr *scm.PullRequest, requestedJobs []config.Presubmi
 	var errors []error
 	for _, job := range requestedJobs {
 		c.Logger.Infof("Starting %s build.", job.Name)
-		pj := jobutil.NewPresubmit(pr, baseSHA, job, eventGUID)
+		pj := jobutil.NewPresubmit(pr, baseSHA, job, eventGUID, c.SCMProviderClient.PRRefFmt())
 		c.Logger.WithFields(jobutil.LighthouseJobFields(&pj)).Info("Creating a new LighthouseJob.")
 		if _, err := c.LauncherClient.Launch(&pj); err != nil {
 			c.Logger.WithError(err).Error("Failed to create LighthouseJob.")
diff --git a/pkg/scmprovider/client.go b/pkg/scmprovider/client.go
index abe02091..33312525 100644
--- a/pkg/scmprovider/client.go
+++ b/pkg/scmprovider/client.go
@@ -22,6 +22,7 @@ type SCMClient interface {
 	SetBotName(string)
 	SupportsGraphQL() bool
 	ProviderType() string
+	PRRefFmt() string
 	SupportsPRLabels() bool
 	ServerURL() *url.URL
 	QuoteAuthorForComment(string) string
@@ -159,6 +160,18 @@ func (c *Client) ProviderType() string {
 	return c.client.Driver.String()
 }
 
+// PRRefFmt returns the "refs/(something)/%d/(something)" sprintf format used for constructing PR refs for this provider
+func (c *Client) PRRefFmt() string {
+	switch c.client.Driver {
+	case scm.DriverStash:
+		return "refs/pull-requests/%d/from"
+	case scm.DriverGitlab:
+		return "refs/merge-requests/%d/head"
+	default:
+		return "refs/pull/%d/head"
+	}
+}
+
 func (c *Client) repositoryName(owner string, repo string) string {
 	return fmt.Sprintf("%s/%s", owner, repo)
 }
diff --git a/pkg/scmprovider/fake/fake.go b/pkg/scmprovider/fake/fake.go
index aaa90b3e..186029eb 100644
--- a/pkg/scmprovider/fake/fake.go
+++ b/pkg/scmprovider/fake/fake.go
@@ -98,6 +98,11 @@ func (f *SCMClient) ProviderType() string {
 	return providerType
 }
 
+// PRRefFmt returns the PR ref format for the provider
+func (f *SCMClient) PRRefFmt() string {
+	return "refs/pull/%d/head"
+}
+
 // SupportsGraphQL returns whether the provider supports GraphQL
 func (f *SCMClient) SupportsGraphQL() bool {
 	return false


Test patch:
diff --git a/pkg/engines/tekton/controller_test.go b/pkg/engines/tekton/controller_test.go
index 33343860..e234f34d 100644
--- a/pkg/engines/tekton/controller_test.go
+++ b/pkg/engines/tekton/controller_test.go
@@ -43,6 +43,10 @@ func TestSyncHandler(t *testing.T) {
 			name:       "update-job",
 			inputIsJob: false,
 		},
+		{
+			name:       "start-batch-pullrequest",
+			inputIsJob: true,
+		},
 	}
 
 	for _, tc := range testCases {
diff --git a/pkg/engines/tekton/test_data/controller/start-batch-pullrequest/expected-lhjob.yml b/pkg/engines/tekton/test_data/controller/start-batch-pullrequest/expected-lhjob.yml
new file mode 100644
index 00000000..eca03236
--- /dev/null
+++ b/pkg/engines/tekton/test_data/controller/start-batch-pullrequest/expected-lhjob.yml
@@ -0,0 +1,57 @@
+apiVersion: lighthouse.jenkins.io/v1alpha1
+kind: LighthouseJob
+metadata:
+  annotations:
+    lighthouse.jenkins-x.io/job: github
+  labels:
+    created-by-lighthouse: "true"
+    lighthouse.jenkins-x.io/branch: PR-813
+    lighthouse.jenkins-x.io/buildNum: "7828158075477027098"
+    lighthouse.jenkins-x.io/context: github
+    lighthouse.jenkins-x.io/id: f46327af-b47e-11ea-b797-9256b7b8d9b0
+    lighthouse.jenkins-x.io/job: github
+    lighthouse.jenkins-x.io/refs.org: jenkins-x
+    lighthouse.jenkins-x.io/refs.pull: "813"
+    lighthouse.jenkins-x.io/refs.repo: lighthouse
+    lighthouse.jenkins-x.io/type: presubmit
+  name: f46327af-b47e-11ea-b797-9256b7b8d9b0
+  namespace: jx
+spec:
+  agent: tekton-pipeline
+  context: github
+  job: github
+  namespace: jx
+  pipeline_run_spec:
+    pipelineRef:
+      apiVersion: tekton.dev/v1beta1
+      name: jenkins-x-charts-jx-build-templ-wbbx6-7
+    podTemplate:
+      schedulerName: ""
+    serviceAccountName: tekton-bot
+  refs:
+    base_link: https://github.com/jenkins-x/lighthouse/commit/e8d56b5ee9671599c75644af574a251dd3b94a5c
+    base_ref: master
+    base_sha: e8d56b5ee9671599c75644af574a251dd3b94a5c
+    clone_uri: https://github.com/jenkins-x/lighthouse.git
+    org: jenkins-x
+    pulls:
+      - author: abayer
+        author_link: https://github.com/abayer
+        commit_link: https://github.com/jenkins-x/lighthouse/pull/813/commits/dd64c739442d505cf5381e2a14b60968e8a0d86e
+        link: https://github.com/jenkins-x/lighthouse/pull/813.diff
+        number: 813
+        sha: dd64c739442d505cf5381e2a14b60968e8a0d86e
+        ref: refs/pull/813/head
+      - author: abayer
+        author_link: https://github.com/abayer
+        commit_link: https://github.com/jenkins-x/lighthouse/pull/814/commits/abcdefg
+        link: https://github.com/jenkins-x/lighthouse/pull/814.diff
+        number: 814
+        sha: abcdefg
+        ref: refs/pull/814/head
+    repo: lighthouse
+    repo_link: https://github.com/jenkins-x/lighthouse
+  rerun_command: /test github
+  type: presubmit
+status:
+  state: pending
diff --git a/pkg/engines/tekton/test_data/controller/start-batch-pullrequest/expected-pr.yml b/pkg/engines/tekton/test_data/controller/start-batch-pullrequest/expected-pr.yml
new file mode 100644
index 00000000..6bcb7bf3
--- /dev/null
+++ b/pkg/engines/tekton/test_data/controller/start-batch-pullrequest/expected-pr.yml
@@ -0,0 +1,61 @@
+metadata:
+  annotations:
+    lighthouse.jenkins-x.io/cloneURI: https://github.com/jenkins-x/lighthouse.git
+    lighthouse.jenkins-x.io/job: github
+  labels:
+    created-by-lighthouse: "true"
+    lighthouse.jenkins-x.io/baseSHA: e8d56b5ee9671599c75644af574a251dd3b94a5c
+    lighthouse.jenkins-x.io/branch: PR-813
+    lighthouse.jenkins-x.io/buildNum: "7828158075477027098"
+    lighthouse.jenkins-x.io/context: github
+    lighthouse.jenkins-x.io/id: f46327af-b47e-11ea-b797-9256b7b8d9b0
+    lighthouse.jenkins-x.io/job: github
+    lighthouse.jenkins-x.io/lastCommitSHA: dd64c739442d505cf5381e2a14b60968e8a0d86e
+    lighthouse.jenkins-x.io/refs.org: jenkins-x
+    lighthouse.jenkins-x.io/refs.pull: "813"
+    lighthouse.jenkins-x.io/refs.repo: lighthouse
+    lighthouse.jenkins-x.io/type: presubmit
+  generateName: github-
+  namespace: jx
+  ownerReferences:
+    - apiVersion: lighthouse.jenkins.io/v1
+      kind: LighthouseJob
+      name: f46327af-b47e-11ea-b797-9256b7b8d9b0
+spec:
+  params:
+    - name: BUILD_ID
+      value: "7828158075477027098"
+    - name: JOB_NAME
+      value: github
+    - name: JOB_SPEC
+      value: type:presubmit
+    - name: JOB_TYPE
+      value: presubmit
+    - name: PULL_BASE_REF
+      value: master
+    - name: PULL_BASE_SHA
+      value: e8d56b5ee9671599c75644af574a251dd3b94a5c
+    - name: PULL_NUMBER
+      value: "813"
+    - name: PULL_PULL_SHA
+      value: dd64c739442d505cf5381e2a14b60968e8a0d86e
+    - name: PULL_REFS
+      value: master:e8d56b5ee9671599c75644af574a251dd3b94a5c,813:dd64c739442d505cf5381e2a14b60968e8a0d86e:refs/pull/813/head,814:abcdefg:refs/pull/814/head
+    - name: REPO_NAME
+      value: lighthouse
+    - name: REPO_OWNER
+      value: jenkins-x
+    - name: batch-refs
+      value: refs/pull/813/head refs/pull/814/head
+    - name: branch-name
+      value: master
+    - name: repo-url
+      value: https://github.com/jenkins-x/lighthouse.git
+  pipelineRef:
+    apiVersion: tekton.dev/v1beta1
+    name: jenkins-x-charts-jx-build-templ-wbbx6-7
+  podTemplate:
+    schedulerName: ""
+  serviceAccountName: tekton-bot
+  timeout: 24h0m0s
+status: {}
diff --git a/pkg/engines/tekton/test_data/controller/start-batch-pullrequest/observed-lhjob.yml b/pkg/engines/tekton/test_data/controller/start-batch-pullrequest/observed-lhjob.yml
new file mode 100644
index 00000000..fff3300c
--- /dev/null
+++ b/pkg/engines/tekton/test_data/controller/start-batch-pullrequest/observed-lhjob.yml
@@ -0,0 +1,55 @@
+apiVersion: lighthouse.jenkins.io/v1alpha1
+kind: LighthouseJob
+metadata:
+  annotations:
+    lighthouse.jenkins-x.io/job: github
+  labels:
+    created-by-lighthouse: "true"
+    lighthouse.jenkins-x.io/branch: PR-813
+    lighthouse.jenkins-x.io/context: github
+    lighthouse.jenkins-x.io/job: github
+    lighthouse.jenkins-x.io/refs.org: jenkins-x
+    lighthouse.jenkins-x.io/refs.pull: "813"
+    lighthouse.jenkins-x.io/refs.repo: lighthouse
+    lighthouse.jenkins-x.io/type: presubmit
+  name: f46327af-b47e-11ea-b797-9256b7b8d9b0
+  namespace: jx
+spec:
+  agent: tekton-pipeline
+  context: github
+  job: github
+  namespace: jx
+  pipeline_run_spec:
+    pipelineRef:
+      apiVersion: tekton.dev/v1beta1
+      name: jenkins-x-charts-jx-build-templ-wbbx6-7
+    podTemplate:
+      schedulerName: ""
+    serviceAccountName: tekton-bot
+  refs:
+    base_link: https://github.com/jenkins-x/lighthouse/commit/e8d56b5ee9671599c75644af574a251dd3b94a5c
+    base_ref: master
+    base_sha: e8d56b5ee9671599c75644af574a251dd3b94a5c
+    clone_uri: https://github.com/jenkins-x/lighthouse.git
+    org: jenkins-x
+    pulls:
+    - author: abayer
+      author_link: https://github.com/abayer
+      commit_link: https://github.com/jenkins-x/lighthouse/pull/813/commits/dd64c739442d505cf5381e2a14b60968e8a0d86e
+      link: https://github.com/jenkins-x/lighthouse/pull/813.diff
+      number: 813
+      sha: dd64c739442d505cf5381e2a14b60968e8a0d86e
+      ref: refs/pull/813/head
+    - author: abayer
+      author_link: https://github.com/abayer
+      commit_link: https://github.com/jenkins-x/lighthouse/pull/814/commits/abcdefg
+      link: https://github.com/jenkins-x/lighthouse/pull/814.diff
+      number: 814
+      sha: abcdefg
+      ref: refs/pull/814/head
+    repo: lighthouse
+    repo_link: https://github.com/jenkins-x/lighthouse
+  rerun_command: /test github
+  type: presubmit
+status:
+  state: triggered
diff --git a/pkg/engines/tekton/test_data/controller/start-batch-pullrequest/observed-pipeline.yml b/pkg/engines/tekton/test_data/controller/start-batch-pullrequest/observed-pipeline.yml
new file mode 100644
index 00000000..c0d64a37
--- /dev/null
+++ b/pkg/engines/tekton/test_data/controller/start-batch-pullrequest/observed-pipeline.yml
@@ -0,0 +1,48 @@
+# Note that this doesn't need to match the run we're actually expecting, just has to have the git-clone task.
+apiVersion: tekton.dev/v1beta1
+kind: Pipeline
+metadata:
+  name: jenkins-x-charts-jx-build-templ-wbbx6-7
+  namespace: jx
+spec:
+  params:
+    - name: repo-url
+      type: string
+      description: The git repository URL to clone from.
+    - name: branch-name
+      type: string
+      description: The git branch to clone.
+    - name: batch-refs
+      type: string
+  workspaces:
+    - name: shared-data
+      description: |
+        This workspace will receive the cloned git repo and be passed
+        to the next Task for the repo's README.md file to be read.
+  tasks:
+    - name: fetch-repo
+      taskRef:
+        name: git-batch-merge
+      workspaces:
+        - name: output
+          workspace: shared-data
+      params:
+        - name: url
+          value: $(params.repo-url)
+        - name: revision
+          value: $(params.branch-name)
+        - name: batchedRefs
+          value: $(params.batch-refs)
+    - name: cat-readme
+      runAfter: ["fetch-repo"]  # Wait until the clone is done before reading the readme.
+      workspaces:
+        - name: source
+          workspace: shared-data
+      taskSpec:
+        workspaces:
+          - name: source
+        steps:
+          - image: zshusers/zsh:4.3.15
+            script: |
+              #!/usr/bin/env zsh
+              cat $(workspaces.source.path)/README.md
diff --git a/pkg/jobutil/jobutil_test.go b/pkg/jobutil/jobutil_test.go
index ac4ce436..835a4fe1 100644
--- a/pkg/jobutil/jobutil_test.go
+++ b/pkg/jobutil/jobutil_test.go
@@ -559,10 +559,11 @@ func TestCreateRefs(t *testing.T) {
 				Link:       "https://github.example.com/kubernetes/Hello-World/pull/42",
 				AuthorLink: "https://github.example.com/ibzib",
 				CommitLink: "https://github.example.com/kubernetes/Hello-World/pull/42/commits/123456",
+				Ref:        "refs/pull/42/head",
 			},
 		},
 	}
-	if actual := createRefs(pr, "abcdef"); !reflect.DeepEqual(expected, actual) {
+	if actual := createRefs(pr, "abcdef", "refs/pull/%d/head"); !reflect.DeepEqual(expected, actual) {
 		t.Errorf("diff between expected and actual refs:%s", diff.ObjectReflectDiff(expected, actual))
 	}
 }
diff --git a/pkg/keeper/keeper_test.go b/pkg/keeper/keeper_test.go
index 11e92b60..cf753cfb 100644
--- a/pkg/keeper/keeper_test.go
+++ b/pkg/keeper/keeper_test.go
@@ -523,6 +523,10 @@ func (f *fgc) ProviderType() string {
 	return "fake"
 }
 
+func (f *fgc) PRRefFmt() string {
+	return "refs/pull/%d/head"
+}
+
 func (f *fgc) GetRepositoryByFullName(string) (*scm.Repository, error) {
 	return nil, scm.ErrNotSupported
 }
diff --git a/pkg/plugins/override/override_test.go b/pkg/plugins/override/override_test.go
index e61c4d1a..82e77f07 100644
--- a/pkg/plugins/override/override_test.go
+++ b/pkg/plugins/override/override_test.go
@@ -52,6 +52,10 @@ func (c *fakeClient) ProviderType() string {
 	return "fake"
 }
 
+func (c *fakeClient) PRRefFmt() string {
+	return "refs/pull/%d/head"
+}
+
 func (c *fakeClient) IsOrgAdmin(org, user string) (bool, error) {
 	return false, nil
 }
diff --git a/test/e2e/tekton_test.go b/test/e2e/tekton_test.go
index 632a14bd..eda59307 100644
--- a/test/e2e/tekton_test.go
+++ b/test/e2e/tekton_test.go
@@ -102,7 +102,7 @@ func ChatOpsTests() bool {
 			err = applyPipelineAndTask()
 			Expect(err).ShouldNot(HaveOccurred())
 			ExpectCommandExecution(localClone.Dir, 1, 0, "kubectl", "apply", "-f",
-				"https://raw.githubusercontent.com/tektoncd/catalog/master/task/git-clone/0.1/git-clone.yaml")
+				"https://raw.githubusercontent.com/tektoncd/catalog/master/task/git-batch-merge/0.1/git-batch-merge.yaml")
 
 			By(fmt.Sprintf("creating and populating Lighthouse config for %s", repo.Clone))
 			cfg, pluginCfg, err := ProcessConfigAndPlugins(repo.Namespace, repo.Name, ns, config.TektonPipelineAgent)
diff --git a/test/e2e/test_data/tekton/pipelineAndTask.tmpl.yaml b/test/e2e/test_data/tekton/pipelineAndTask.tmpl.yaml
index f0eeb46d..b457bcfd 100644
--- a/test/e2e/test_data/tekton/pipelineAndTask.tmpl.yaml
+++ b/test/e2e/test_data/tekton/pipelineAndTask.tmpl.yaml
@@ -24,22 +24,24 @@ spec:
   params:
     - name: git-repo-url
       type: string
-    - name: git-revision
+    - name: batch-refs
       type: string
   workspaces:
     - name: shared-data
   tasks:
     - name: fetch-repo
       taskRef:
-        name: git-clone
+        name: git-batch-merge
       workspaces:
         - name: output
           workspace: shared-data
       params:
         - name: url
           value: $(params.git-repo-url)
-        - name: revision
-          value: $(params.git-revision)
+        - name: batchedRefs
+          value: $(params.batch-refs)
+        - name: subdirectory
+          value: ""
     - name: run-script
       runAfter: ["fetch-repo"]
       taskRef: