Repository: openshift-pipelines/pipelines-as-code
Pull request #247 (2021-09-28 12:45:17)

Allow attaching a secret to a repository CRD
Allow user to attach a secret on a Repository.

This allows user to use their github repo with a direct webhook without webapp.





Patch:
diff --git a/.golangci.yml b/.golangci.yml
index 76e9e3ec..ea288bf1 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -42,7 +42,7 @@ linters:
     - unused
     - unparam
     - gochecknoinits
-    - goconst
+      # - goconst
       # - gocyclo
     # - goerr113
     - gofmt
diff --git a/INSTALL.md b/INSTALL.md
index d7512e98..653e75b9 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -111,6 +111,86 @@ Pipelines as Code supports Github Enterprise.
 You don't need to do anything special to get Pipelines as code working with GHE. Pipelines as code will automatically
 detects the header as set from GHE and use it the GHE API auth url instead of the public github.
 
+### Github Webhook
+
+You can as well use [Github Webhooks](https://docs.github.com/en/developers/webhooks-and-events/webhooks/creating-webhooks) directly from your repository instead of a Github application.
+
+Using Pipelines as Code via Github webhook does not allow access to the CheckRun API, the status of the tasks will be added as a Comment of the PR.
+
+You will need to create a personal token to be able to do operation on the Github API.
+
+Follow this guide to create a personal token :
+
+<https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token>
+
+The only permission needed is the *repo* permission. Make sure you note
+somewhere the generated token or otherwise you will have to recreate it.
+
+Go to you repository or organisation setting and click on *Hooks* and *"Add webhook"* links.
+
+Set the payload URL to the event listenner public url, on OpenShift you can get it like this :
+
+```shell
+echo https://$(oc get route -n pipelines-as-code el-pipelines-as-code-interceptor -o jsonpath='{.spec.host}')
+```
+
+Add a secret or generate a random one with this command  :
+
+```shell
+openssl rand -hex 20
+```
+
+- Click on select individual events and check these events :
+
+* Commit comments
+* Issue comments
+* Pull request reviews
+* Pull request reviews
+* Pushes
+
+On your cluster you need to add the webhook secret set previously in the *pipelines-as-code* namespace.
+
+```shell
+kubectl -n pipelines-as-code create secret generic github-app-secret \
+        --from-literal webhook.secret="WEBHOOK_SECRET_AS_SET"
+```
+
+Now to use this from the Repository CRD you first need to create a secret with
+the Github API token
+
+```shell
+kubectl -n target-namespace create secret generic github-token \
+        --from-literal token="TOKEN_AS_GENERATED_PREVIOUSLY"
+```
+
+And from your Repositry CRD you can add the secret field to reference this :
+
+For example :
+
+```yaml
+---
+apiVersion: "pipelinesascode.tekton.dev/v1alpha1"
+kind: Repository
+metadata:
+  name: my-repo
+  namespace: target-namespace
+spec:
+  url: "https://github.com/owner/repo"
+  branch: "main"
+  event_type: "pull_request"
+  namespace: "target-namespace"
+  # Set this if you run with Github Enteprise
+  # webvcs_api_url: "github.enteprise.com"
+  webvcs_secret:
+    name: "github-token"
+    # Set this if you have a different key in your secret
+    # key: "token"
+```
+
+`webvcs_secret` cannot reference a secret in another namespace, Pipelines as
+code assumes always it is in the same namespace where the repository has been
+created.
+
 ### Kubernetes
 
 Pipelines as Code should work directly on kubernetes/minikube/kind. You just need to install the release.yaml
diff --git a/README.md b/README.md
index a7077630..4501e145 100644
--- a/README.md
+++ b/README.md
@@ -85,6 +85,10 @@ The above command would create a `Repository` CRD in your `demo` namespace which
 PipelineRuns for your GitHub repository should run. It also generates an example pipeline in the `.tekton` folder.
 Commit and push the pipeline to your repo to start using pipelines as code.
 
+Note that even if Github application is the preferred method, Pipeline As Code
+supports Github Webhook as well, see the [INSTALL guide](INSTALL.md) for
+reference
+
 ## Usage Guide
 
 ### Pipelines As Code Configurations
diff --git a/config/200-role.yaml b/config/200-role.yaml
index 99a548b9..e79218b4 100644
--- a/config/200-role.yaml
+++ b/config/200-role.yaml
@@ -82,10 +82,11 @@ rules:
   - apiGroups: [""]
     resources: ["namespaces", "pods", "pods/log"]
     verbs: ["get", "list", "watch"]
-  # Allow creating and deleting secrets in namespace
+  # Allow getting, creating and deleting secrets in namespace
+  # This is to be able to attach secret to repository on webhook.
   - apiGroups: [""]
     resources: ["secrets"]
-    verbs: ["create", "delete"]
+    verbs: ["get", "create", "delete"]
   # Permissions to list repositories on cluster
   - apiGroups: ["pipelinesascode.tekton.dev"]
     resources: ["repositories"]
diff --git a/config/300-repositories.yaml b/config/300-repositories.yaml
index 41d7e493..02f65189 100644
--- a/config/300-repositories.yaml
+++ b/config/300-repositories.yaml
@@ -81,6 +81,19 @@ spec:
                 namespace:
                   description: Namespace
                   type: string
+                webvcs_api_url:
+                  description: The API URL for the WEBVCS
+                  type: string
+                webvcs_secret:
+                  type: object
+                  properties:
+                    key:
+                      type: string
+                      description: "Key inside the secret"
+                      default: "token"
+                    name:
+                      type: string
+                      description: "The secret name"
               type: object
           type: object
   scope: Namespaced
diff --git a/config/400-trigger-eventlistenner.yaml b/config/400-trigger-eventlistenner.yaml
index 60623224..6b1818b5 100644
--- a/config/400-trigger-eventlistenner.yaml
+++ b/config/400-trigger-eventlistenner.yaml
@@ -99,9 +99,7 @@ spec:
           params:
             - name: "filter"
               value: >-
-                'installation' in body &&
-                'pusher' in body &&
-                'installation' in body
+                'pusher' in body
       bindings:
         - ref: pipelines-as-code-bindings-push
       template:
@@ -150,8 +148,7 @@ spec:
           params:
             - name: "filter"
               value: >-
-                body.action in ['created', 'synchronize', 'opened'] &&
-                'installation' in body
+                body.action in ['created', 'synchronize', 'opened']
       bindings:
         - ref: pipelines-as-code-bindings-pullreq
       template:
diff --git a/config/401-trigger-issue-recheck.yaml b/config/401-trigger-issue-recheck.yaml
index 8e1faa74..51a0095a 100644
--- a/config/401-trigger-issue-recheck.yaml
+++ b/config/401-trigger-issue-recheck.yaml
@@ -71,6 +71,7 @@ spec:
     - name: pull_request_number
     - name: sender
     - name: installation_id
+      default: ""
     - name: trigger_target
     - name: ghe_host
       default: "api.github.com"
diff --git a/config/402-trigger-pull-request.yaml b/config/402-trigger-pull-request.yaml
index ff3839e8..2a1a6827 100644
--- a/config/402-trigger-pull-request.yaml
+++ b/config/402-trigger-pull-request.yaml
@@ -40,13 +40,16 @@ spec:
       value: $(body.pull_request.user.login)
     - name: "base_ref"
       value: $(body.pull_request.base.ref)
+    - name: "pull_request_number"
+      value: $(body.pull_request.number)
+    - name: "pull_request_html_url"
+      value: $(body.pull_request.html_url)
     - name: "sha"
       value: $(body.pull_request.head.sha)
     - name: "head_ref"
       value: $(body.pull_request.head.ref)
     - name: "installation_id"
       value: $(body.installation.id)
-
 ---
 apiVersion: triggers.tekton.dev/v1alpha1
 kind: TriggerTemplate
@@ -65,9 +68,12 @@ spec:
     - name: url
     - name: sender
     - name: base_ref
+    - name: pull_request_number
+    - name: pull_request_html_url
     - name: sha
     - name: head_ref
     - name: installation_id
+      default: ""
     - name: trigger_target
     - name: ghe_host
       default: "api.github.com"
@@ -100,6 +106,10 @@ spec:
             value: $(tt.params.sender)
           - name: base_ref
             value: $(tt.params.base_ref)
+          - name: pull_request_number
+            value: $(tt.params.pull_request_number)
+          - name: pull_request_html_url
+            value: $(tt.params.pull_request_html_url)
           - name: sha
             value: $(tt.params.sha)
           - name: head_ref
@@ -117,6 +127,8 @@ spec:
             - name: url
             - name: sender
             - name: base_ref
+            - name: pull_request_number
+            - name: pull_request_html_url
             - name: sha
             - name: head_ref
             - name: installation_id
@@ -156,6 +168,10 @@ spec:
                     type: string
                   - name: base_ref
                     type: string
+                  - name: pull_request_number
+                    type: string
+                  - name: pull_request_html_url
+                    type: string
                   - name: sha
                     type: string
                   - name: head_ref
@@ -195,6 +211,8 @@ spec:
                               "html_url": "$(params.url)"
                           },
                           "pull_request": {
+                              "number": $(params.pull_request_number),
+                              "html_url": "$(params.pull_request_html_url)",
                               "user": {
                                   "login": "$(params.sender)"
                               },
@@ -232,13 +250,16 @@ spec:
                   value: $(params.sender)
                 - name: base_ref
                   value: $(params.base_ref)
+                - name: pull_request_number
+                  value: $(params.pull_request_number)
+                - name: pull_request_html_url
+                  value: $(params.pull_request_html_url)
                 - name: sha
                   value: $(params.sha)
                 - name: head_ref
                   value: $(params.head_ref)
                 - name: token
                   value: "$(tasks.get-token.results.token)"
-
         workspaces:
           - name: secrets
             secret:
diff --git a/config/403-trigger-push.yaml b/config/403-trigger-push.yaml
index d3fe10af..4f1d0e95 100644
--- a/config/403-trigger-push.yaml
+++ b/config/403-trigger-push.yaml
@@ -70,6 +70,7 @@ spec:
     - name: sha
     - name: head_ref
     - name: installation_id
+      default: ""
     - name: trigger_target
   resourcetemplates:
     - apiVersion: tekton.dev/v1beta1
diff --git a/config/500-task-github-app-token.yaml b/config/500-task-github-app-token.yaml
index d54207e9..b0bcf4d2 100644
--- a/config/500-task-github-app-token.yaml
+++ b/config/500-task-github-app-token.yaml
@@ -94,10 +94,16 @@ spec:
         import json
         import os
         import time
+        import sys
 
         import requests
         from jwcrypto import jwk, jwt
 
+        if not os.environ.get("GITHUBAPP_INSTALLATION_ID"):
+          open(os.environ.get('GITHUBAPP_RESULT_PATH'), 'w').write("")
+          print("Skipping, since event not coming from a githup app installation")
+          sys.exit(0)
+
         EXPIRE_MINUTES_AS_SECONDS = int(os.environ.get('GITHUBAPP_TOKEN_EXPIRATION_MINUTES', 10)) * 60
         GITHUB_API_URL = "https://" + os.environ.get('GITHUB_API_URL')
         if GITHUB_API_URL != "https://api.github.com":
diff --git a/go.mod b/go.mod
index 67624bef..d6657d8e 100644
--- a/go.mod
+++ b/go.mod
@@ -5,6 +5,7 @@ go 1.15
 require (
 	github.com/AlecAivazis/survey/v2 v2.2.12
 	github.com/briandowns/spinner v1.16.0
+	github.com/davecgh/go-spew v1.1.1
 	github.com/gobwas/glob v0.2.3
 	github.com/google/go-cmp v0.5.6
 	github.com/google/go-github/v35 v35.3.0
diff --git a/pkg/apis/pipelinesascode/v1alpha1/types.go b/pkg/apis/pipelinesascode/v1alpha1/types.go
index 5257738b..fe0c7ea9 100644
--- a/pkg/apis/pipelinesascode/v1alpha1/types.go
+++ b/pkg/apis/pipelinesascode/v1alpha1/types.go
@@ -51,10 +51,17 @@ type RepositoryRunStatus struct {
 
 // RepositorySpec is the spec of a repo
 type RepositorySpec struct {
-	Namespace string `json:"namespace"`
-	URL       string `json:"url"`
-	EventType string `json:"event_type"`
-	Branch    string `json:"branch"`
+	Namespace    string            `json:"namespace"` // TODO: Remove, this should be implicit where it is installed
+	URL          string            `json:"url"`
+	EventType    string            `json:"event_type"`
+	Branch       string            `json:"branch"`
+	WebvcsAPIURL string            `json:"webvcs_api_url"`
+	WebvcsSecret *WebvcsSecretSpec `json:"webvcs_secret,omitempty"`
+}
+
+type WebvcsSecretSpec struct {
+	Name string `json:"name"`
+	Key  string `json:"key"`
 }
 
 // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
diff --git a/pkg/cmd/pipelineascode/pipelineascode.go b/pkg/cmd/pipelineascode/pipelineascode.go
index 05eb9445..fc6c7c9d 100644
--- a/pkg/cmd/pipelineascode/pipelineascode.go
+++ b/pkg/cmd/pipelineascode/pipelineascode.go
@@ -36,7 +36,7 @@ func Command(cs *params.Run) *cobra.Command {
 				return err
 			}
 			ctx := context.Background()
-			vcsintf, err := getVCS(ctx, cs.Info.Pac)
+			vcsintf, err := getVCS(cs.Info.Pac)
 			if err != nil {
 				return err
 			}
@@ -66,10 +66,11 @@ func getPayloadFromFile(opts info.PacOpts) (string, error) {
 	return string(payloadB), err
 }
 
-func getVCS(ctx context.Context, pacopts info.PacOpts) (webvcs.Interface, error) {
+func getVCS(pacopts info.PacOpts) (webvcs.Interface, error) {
 	switch pacopts.VCSType {
 	case "github":
-		return github.NewGithubVCS(ctx, pacopts), nil
+		g := &github.VCS{}
+		return g, nil
 	default:
 		return nil, fmt.Errorf("no supported VCS is detected")
 	}
@@ -84,15 +85,23 @@ func runWrap(ctx context.Context, cs *params.Run, vcx webvcs.Interface, kinterac
 		cs.Info.Pac.LogURL = defaultURL
 	}
 
+	// If we already have the Token (ie: github apps) set as soon as possible the client,
+	// There is more things supported when we already have a github apps and some that are not
+	// (ie: /ok-to-test or /rerequest)
+	if cs.Info.Pac.VCSToken != "" {
+		vcx.SetClient(ctx, cs.Info.Pac)
+	}
+
 	payload, err := getPayloadFromFile(cs.Info.Pac)
 	if err != nil {
 		return err
 	}
 
-	cs.Info.Event, err = vcx.ParsePayload(ctx, cs.Clients.Log, cs.Info.Event, payload)
+	cs.Info.Event, err = vcx.ParsePayload(ctx, cs, payload)
 	if err != nil {
 		return err
 	}
+
 	err = pipelineascode.Run(ctx, cs, vcx, kinteract)
 	if err != nil {
 		createStatusErr := vcx.CreateStatus(ctx, cs.Info.Event, cs.Info.Pac, webvcs.StatusOpts{
diff --git a/pkg/cmd/resolve/resolve.go b/pkg/cmd/resolve/resolve.go
index a5f93f16..d49d8c35 100644
--- a/pkg/cmd/resolve/resolve.go
+++ b/pkg/cmd/resolve/resolve.go
@@ -114,7 +114,7 @@ func resolveFilenames(cs *params.Run, filenames []string, params []string) (stri
 		SkipInlining: skipInlining,
 	}
 	// We use github here but since we don't do remotetask we would not care
-	vcsintf := github.NewGithubVCS(ctx, cs.Info.Pac)
+	vcsintf := &github.VCS{}
 	prun, err := resolve.Resolve(ctx, cs, vcsintf, allTemplates, ropt)
 	if err != nil {
 		return "", err
diff --git a/pkg/kubeinteraction/kubeinteraction.go b/pkg/kubeinteraction/kubeinteraction.go
index 863722c8..33d9f9f7 100644
--- a/pkg/kubeinteraction/kubeinteraction.go
+++ b/pkg/kubeinteraction/kubeinteraction.go
@@ -12,6 +12,12 @@ import (
 	tektonv1beta1client "github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1beta1"
 )
 
+type GetSecretOpt struct {
+	Namespace string
+	Name      string
+	Key       string
+}
+
 type Interface interface {
 	GetConsoleUI(context.Context, string, string) (string, error)
 	GetNamespace(context.Context, string) error
@@ -19,6 +25,7 @@ type Interface interface {
 	WaitForPipelineRunSucceed(context.Context, tektonv1beta1client.TektonV1beta1Interface, *v1beta1.PipelineRun, time.Duration) error
 	CleanupPipelines(context.Context, string, string, int) error
 	CreateBasicAuthSecret(context.Context, *info.Event, info.PacOpts, string) error
+	GetSecret(context.Context, GetSecretOpt) (string, error)
 }
 
 type Interaction struct {
diff --git a/pkg/kubeinteraction/secrets.go b/pkg/kubeinteraction/secrets.go
index 0cfa0eb1..fa186445 100644
--- a/pkg/kubeinteraction/secrets.go
+++ b/pkg/kubeinteraction/secrets.go
@@ -57,3 +57,12 @@ func (k Interaction) CreateBasicAuthSecret(ctx context.Context, runevent *info.E
 	k.Run.Clients.Log.Infof("Secret %s has been generated in namespace %s", secretName, targetNamespace)
 	return err
 }
+
+func (k Interaction) GetSecret(ctx context.Context, secretopt GetSecretOpt) (string, error) {
+	secret, err := k.Run.Clients.Kube.CoreV1().Secrets(secretopt.Namespace).Get(
+		ctx, secretopt.Name, metav1.GetOptions{})
+	if err != nil {
+		return "", err
+	}
+	return string(secret.Data[secretopt.Key]), nil
+}
diff --git a/pkg/params/info/pac.go b/pkg/params/info/pac.go
index cb706f32..b594041d 100644
--- a/pkg/params/info/pac.go
+++ b/pkg/params/info/pac.go
@@ -13,6 +13,7 @@ type PacOpts struct {
 	SecretAutoCreation bool   // secret auto creation in target namespace
 	VCSToken           string
 	VCSAPIURL          string
+	VCSInfoFromRepo    bool // wether the webvcs info come from the repository
 	VCSType            string
 	PayloadFile        string
 }
diff --git a/pkg/pipelineascode/pipelineascode.go b/pkg/pipelineascode/pipelineascode.go
index 0383d2d8..4bba8c44 100644
--- a/pkg/pipelineascode/pipelineascode.go
+++ b/pkg/pipelineascode/pipelineascode.go
@@ -13,6 +13,8 @@ import (
 	"github.com/openshift-pipelines/pipelines-as-code/pkg/params"
 	"github.com/openshift-pipelines/pipelines-as-code/pkg/resolve"
 	"github.com/openshift-pipelines/pipelines-as-code/pkg/webvcs"
+	"github.com/openshift-pipelines/pipelines-as-code/pkg/webvcs/github"
+	tektonv1beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
@@ -27,58 +29,64 @@ const (
 // The time to wait for a pipelineRun, maybe we should not restrict this?
 const pipelineRunTimeout = 2 * time.Hour
 
-func fmtDuration(d time.Duration) string {
-	d = d.Round(time.Minute)
-	m := d / time.Minute
-	return fmt.Sprintf("%02d", m)
-}
-
 func Run(ctx context.Context, cs *params.Run, vcsintf webvcs.Interface, k8int kubeinteraction.Interface) error {
 	var err error
 
-	// Check if the submitter is allowed to run this.
-	allowed, err := vcsintf.IsAllowed(ctx, cs.Info.Event)
+	// Match the Event to a Repository Resource,
+	// We are going to match on targetNamespace annotation later on in
+	// `MatchPipelinerunByAnnotation`
+	repo, err := matcher.GetRepoByCR(ctx, cs, "")
 	if err != nil {
 		return err
 	}
 
-	if !allowed {
-		msg := fmt.Sprintf("User %s is not allowed to run CI on this repo.", cs.Info.Event.Sender)
+	if repo == nil || repo.Spec.Namespace == "" {
+		msg := fmt.Sprintf("Could not find a namespace match for %s/%s on target-branch:%s event-type: %s", cs.Info.Event.Owner, cs.Info.Event.Repository, cs.Info.Event.BaseBranch, cs.Info.Event.EventType)
+
+		if cs.Info.Pac.VCSToken == "" {
+			cs.Clients.Log.Error(msg)
+			return nil
+		}
+
 		err = createStatus(ctx, vcsintf, cs, webvcs.StatusOpts{
 			Status:     "completed",
 			Conclusion: "skipped",
 			Text:       msg,
-			DetailsURL: "https://tenor.com/search/police-cat-gifs",
+			DetailsURL: "https://tenor.com/search/sad-cat-gifs",
 		}, true)
+
 		if err != nil {
 			return err
 		}
 		return nil
 	}
 
-	// Match the Event to a Repository Resource,
-	// We are going to match on targetNamespace annotation later on in
-	// `MatchPipelinerunByAnnotation`
-	repo, err := matcher.GetRepoByCR(ctx, cs, "")
+	if repo.Spec.WebvcsSecret != nil {
+		err := secretFromRepository(ctx, cs, k8int, repo)
+		if err != nil {
+			return err
+		}
+		// We already SetClient before ParseWebhook in case if we already set
+		// the token (ie: github apps) and not coming from the repository
+		vcsintf.SetClient(ctx, cs.Info.Pac)
+	}
+
+	// Check if the submitter is allowed to run this.
+	allowed, err := vcsintf.IsAllowed(ctx, cs.Info.Event)
 	if err != nil {
 		return err
 	}
 
-	if repo == nil || repo.Spec.Namespace == "" {
-		msg := fmt.Sprintf("Could not find a namespace match for %s/%s on target-branch:%s event-type: %s", cs.Info.Event.Owner, cs.Info.Event.Repository, cs.Info.Event.BaseBranch, cs.Info.Event.EventType)
-		if cs.Info.Event.EventType == "pull_request" || cs.Info.Event.TriggerTarget == "issue-recheck" {
-			err = createStatus(ctx, vcsintf, cs, webvcs.StatusOpts{
-				Status:     "completed",
-				Conclusion: "skipped",
-				Text:       msg,
-				DetailsURL: "https://tenor.com/search/sad-cat-gifs",
-			}, true)
-
-			if err != nil {
-				return err
-			}
-		} else {
-			cs.Clients.Log.Infof("Skipping creating status check: %s", msg)
+	if !allowed {
+		msg := fmt.Sprintf("User %s is not allowed to run CI on this repo.", cs.Info.Event.Sender)
+		err = createStatus(ctx, vcsintf, cs, webvcs.StatusOpts{
+			Status:     "completed",
+			Conclusion: "skipped",
+			Text:       msg,
+			DetailsURL: "https://tenor.com/search/police-cat-gifs",
+		}, true)
+		if err != nil {
+			return err
 		}
 		return nil
 	}
@@ -150,24 +158,14 @@ func Run(ctx context.Context, cs *params.Run, vcsintf webvcs.Interface, k8int ku
 		}
 	}
 
-	// Add labels on the soon to be created pipelinerun so UI/CLI can easily
-	// query them. Since K8s do not like slash in labels value and on push we
-	// have the full ref, we replace the "/" by "-". The tools probably need to
-	// be aware of it when querying.
-	refTomakeK8Happy := strings.ReplaceAll(cs.Info.Event.BaseBranch, "/", "-")
-	pipelineRun.Labels = map[string]string{
-		"app.kubernetes.io/managed-by":              "pipelines-as-code",
-		"pipelinesascode.tekton.dev/url-org":        cs.Info.Event.Owner,
-		"pipelinesascode.tekton.dev/url-repository": cs.Info.Event.Repository,
-		"pipelinesascode.tekton.dev/sha":            cs.Info.Event.SHA,
-		"pipelinesascode.tekton.dev/sender":         cs.Info.Event.Sender,
-		"pipelinesascode.tekton.dev/event-type":     cs.Info.Event.EventType,
-		"pipelinesascode.tekton.dev/branch":         refTomakeK8Happy,
-		"pipelinesascode.tekton.dev/repository":     repo.GetName(),
+	// Get the SHA commit info, we want to get the URL and commit title
+	err = vcsintf.GetCommitInfo(ctx, cs.Info.Event)
+	if err != nil {
+		return err
 	}
 
-	pipelineRun.Annotations["pipelinesascode.tekton.dev/sha-title"] = cs.Info.Event.SHATitle
-	pipelineRun.Annotations["pipelinesascode.tekton.dev/sha-url"] = cs.Info.Event.SHAURL
+	// Add labels and annotations to pipelinerun
+	addLabelsAndAnnotations(cs, pipelineRun, repo)
 
 	// Create the actual pipeline
 	pr, err := cs.Clients.Tekton.TektonV1beta1().PipelineRuns(repo.Spec.Namespace).Create(ctx, pipelineRun, metav1.CreateOptions{})
@@ -183,8 +181,7 @@ func Run(ctx context.Context, cs *params.Run, vcsintf webvcs.Interface, k8int ku
 	}
 
 	// Create status with the log url
-	msg := fmt.Sprintf(startingPipelineRunText,
-		pr.GetName(), repo.Spec.Namespace, repo.Spec.Namespace, pr.GetName())
+	msg := fmt.Sprintf(startingPipelineRunText, pr.GetName(), repo.Spec.Namespace, repo.Spec.Namespace, pr.GetName())
 	err = createStatus(ctx, vcsintf, cs, webvcs.StatusOpts{
 		Status:     "in_progress",
 		Conclusion: "",
@@ -254,3 +251,57 @@ func Run(ctx context.Context, cs *params.Run, vcsintf webvcs.Interface, k8int ku
 	cs.Clients.Log.Infof("Repository status of %s has been updated", nrepo.Name)
 	return err
 }
+
+func addLabelsAndAnnotations(cs *params.Run, pipelineRun *tektonv1beta1.PipelineRun, repo *apipac.Repository) {
+	// Add labels on the soon to be created pipelinerun so UI/CLI can easily
+	// query them. Since K8s do not like slash in labels value and on push we
+	// have the full ref, we replace the "/" by "-". The tools probably need to
+	// be aware of it when querying.
+	refTomakeK8Happy := strings.ReplaceAll(cs.Info.Event.BaseBranch, "/", "-")
+	pipelineRun.Labels = map[string]string{
+		"app.kubernetes.io/managed-by":              "pipelines-as-code",
+		"pipelinesascode.tekton.dev/url-org":        cs.Info.Event.Owner,
+		"pipelinesascode.tekton.dev/url-repository": cs.Info.Event.Repository,
+		"pipelinesascode.tekton.dev/sha":            cs.Info.Event.SHA,
+		"pipelinesascode.tekton.dev/sender":         cs.Info.Event.Sender,
+		"pipelinesascode.tekton.dev/event-type":     cs.Info.Event.EventType,
+		"pipelinesascode.tekton.dev/branch":         refTomakeK8Happy,
+		"pipelinesascode.tekton.dev/repository":     repo.GetName(),
+	}
+
+	pipelineRun.Annotations["pipelinesascode.tekton.dev/sha-title"] = cs.Info.Event.SHATitle
+	pipelineRun.Annotations["pipelinesascode.tekton.dev/sha-url"] = cs.Info.Event.SHAURL
+}
+
+func fmtDuration(d time.Duration) string {
+	d = d.Round(time.Minute)
+	m := d / time.Minute
+	return fmt.Sprintf("%02d", m)
+}
+
+func secretFromRepository(ctx context.Context, cs *params.Run, k8int kubeinteraction.Interface, repo *apipac.Repository) error {
+	var err error
+
+	if repo.Spec.WebvcsAPIURL == "" {
+		if cs.Info.Pac.VCSType == "github" {
+			repo.Spec.WebvcsAPIURL = github.PublicURL
+		}
+	}
+	cs.Info.Pac.VCSToken, err = k8int.GetSecret(
+		ctx,
+		kubeinteraction.GetSecretOpt{
+			Namespace: repo.GetNamespace(),
+			Name:      repo.Spec.WebvcsSecret.Name,
+			Key:       repo.Spec.WebvcsSecret.Key,
+		},
+	)
+
+	if err != nil {
+		return err
+	}
+	cs.Info.Pac.VCSInfoFromRepo = true
+	cs.Info.Pac.VCSAPIURL = repo.Spec.WebvcsAPIURL
+
+	cs.Clients.Log.Infof("Using token from secret %s in key %s", repo.Spec.WebvcsSecret.Name, repo.Spec.WebvcsSecret.Key)
+	return nil
+}
diff --git a/pkg/webvcs/github/events.go b/pkg/webvcs/github/events.go
index 79a5f4cc..84ecd08b 100644
--- a/pkg/webvcs/github/events.go
+++ b/pkg/webvcs/github/events.go
@@ -10,6 +10,7 @@ import (
 	"strings"
 
 	"github.com/google/go-github/v35/github"
+	"github.com/openshift-pipelines/pipelines-as-code/pkg/params"
 	"github.com/openshift-pipelines/pipelines-as-code/pkg/params/info"
 	"go.uber.org/zap"
 )
@@ -33,10 +34,10 @@ func (v VCS) payloadFix(payload string) []byte {
 
 // ParsePayload parse payload event
 // TODO: this piece of code is just plain silly
-func (v VCS) ParsePayload(ctx context.Context, log *zap.SugaredLogger, runevent *info.Event, payload string) (*info.Event, error) {
+func (v VCS) ParsePayload(ctx context.Context, run *params.Run, payload string) (*info.Event, error) {
 	var processedevent *info.Event
 	payloadTreated := v.payloadFix(payload)
-	event, err := github.ParseWebHook(runevent.EventType, payloadTreated)
+	event, err := github.ParseWebHook(run.Info.Event.EventType, payloadTreated)
 	if err != nil {
 		return &info.Event{}, err
 	}
@@ -48,14 +49,18 @@ func (v VCS) ParsePayload(ctx context.Context, log *zap.SugaredLogger, runevent
 
 	switch event := event.(type) {
 	case *github.CheckRunEvent:
-		if runevent.TriggerTarget == "issue-recheck" {
-			processedevent, err = v.handleReRequestEvent(ctx, log, event)
+		if run.Info.Event.TriggerTarget == "issue-recheck" {
+			processedevent, err = v.handleReRequestEvent(ctx, run.Clients.Log, event)
 			if err != nil {
 				return &info.Event{}, err
 			}
 		}
 	case *github.IssueCommentEvent:
-		processedevent, err = v.handleIssueCommentEvent(ctx, log, event)
+		if run.Info.Pac.VCSToken == "" {
+			return &info.Event{}, fmt.Errorf("gitops style comments operation is only supported with github apps" +
+				" integration")
+		}
+		processedevent, err = v.handleIssueCommentEvent(ctx, run.Clients.Log, event)
 		if err != nil {
 			return &info.Event{}, err
 		}
@@ -70,7 +75,7 @@ func (v VCS) ParsePayload(ctx context.Context, log *zap.SugaredLogger, runevent
 			SHATitle:      event.GetHeadCommit().GetMessage(),
 			Sender:        event.GetSender().GetLogin(),
 			BaseBranch:    event.GetRef(),
-			EventType:     runevent.TriggerTarget,
+			EventType:     run.Info.Event.TriggerTarget,
 		}
 
 		processedevent.HeadBranch = processedevent.BaseBranch // in push events Head Branch is the same as Basebranch
@@ -84,20 +89,15 @@ func (v VCS) ParsePayload(ctx context.Context, log *zap.SugaredLogger, runevent
 			BaseBranch:    event.GetPullRequest().Base.GetRef(),
 			HeadBranch:    event.GetPullRequest().Head.GetRef(),
 			Sender:        event.GetPullRequest().GetUser().GetLogin(),
-			EventType:     runevent.EventType,
+			EventType:     run.Info.Event.EventType,
 		}
 
 	default:
 		return &info.Event{}, errors.New("this event is not supported")
 	}
 
-	err = v.populateCommitInfo(ctx, processedevent)
-	if err != nil {
-		return &info.Event{}, err
-	}
-
 	processedevent.Event = event
-	processedevent.TriggerTarget = runevent.TriggerTarget
+	processedevent.TriggerTarget = run.Info.Event.TriggerTarget
 
 	return processedevent, nil
 }
diff --git a/pkg/webvcs/github/github.go b/pkg/webvcs/github/github.go
index b92efc71..93bdb8e4 100644
--- a/pkg/webvcs/github/github.go
+++ b/pkg/webvcs/github/github.go
@@ -6,21 +6,20 @@ import (
 	"fmt"
 	"net/http"
 	"strings"
-	"time"
 
 	"github.com/google/go-github/v35/github"
 	"github.com/openshift-pipelines/pipelines-as-code/pkg/params/info"
-	"github.com/openshift-pipelines/pipelines-as-code/pkg/webvcs"
 	"golang.org/x/oauth2"
 )
 
+const PublicURL = "https://api.github.com/"
+
 type VCS struct {
 	Client        *github.Client
-	Token, APIURL string
+	Token, APIURL *string
 }
 
-// NewGithubVCS Create a new GitHub VCS object for token
-func NewGithubVCS(ctx context.Context, info info.PacOpts) VCS {
+func (v *VCS) SetClient(ctx context.Context, info info.PacOpts) {
 	ts := oauth2.StaticTokenSource(
 		&oauth2.Token{AccessToken: info.VCSToken},
 	)
@@ -32,40 +31,25 @@ func NewGithubVCS(ctx context.Context, info info.PacOpts) VCS {
 		if !strings.HasPrefix(apiURL, "https") {
 			apiURL = "https://" + apiURL
 		}
+	}
+	if apiURL != "" && apiURL != PublicURL {
 		client, _ = github.NewEnterpriseClient(apiURL, apiURL, tc)
 	} else {
 		client = github.NewClient(tc)
+		apiURL = client.BaseURL.String()
 	}
 
-	return VCS{
-		Client: client,
-		Token:  info.VCSToken,
-		APIURL: apiURL,
-	}
-}
-
-// concatAllYamlFiles concat all yaml files from a directory as one big multi document yaml string
-func (v VCS) concatAllYamlFiles(ctx context.Context, objects []*github.RepositoryContent, runevent *info.Event) (string, error) {
-	var allTemplates string
-
-	for _, value := range objects {
-		if strings.HasSuffix(value.GetName(), ".yaml") ||
-			strings.HasSuffix(value.GetName(), ".yml") {
-			data, err := v.getObject(ctx, value.GetSHA(), runevent)
-			if err != nil {
-				return "", err
-			}
-			if allTemplates != "" && !strings.HasPrefix(string(data), "---") {
-				allTemplates += "---"
-			}
-			allTemplates += "\n" + string(data) + "\n"
-		}
+	// Make sure Client is not already set, so we don't override our fakeclient
+	// from unittesting.
+	if v.Client == nil {
+		v.Client = client
 	}
-	return allTemplates, nil
+	v.APIURL = &apiURL
+	v.Token = &info.VCSToken
 }
 
 // GetTektonDir Get all yaml files in tekton directory return as a single concated file
-func (v VCS) GetTektonDir(ctx context.Context, runevent *info.Event, path string) (string, error) {
+func (v *VCS) GetTektonDir(ctx context.Context, runevent *info.Event, path string) (string, error) {
 	fp, objects, resp, err := v.Client.Repositories.GetContents(ctx, runevent.Owner,
 		runevent.Repository, path, &github.RepositoryContentGetOptions{Ref: runevent.SHA})
 
@@ -83,29 +67,9 @@ func (v VCS) GetTektonDir(ctx context.Context, runevent *info.Event, path string
 	return v.concatAllYamlFiles(ctx, objects, runevent)
 }
 
-// getPullRequest get a pull request details
-func (v VCS) getPullRequest(ctx context.Context, runevent *info.Event, prNumber int) (*info.Event, error) {
-	pr, _, err := v.Client.PullRequests.Get(ctx, runevent.Owner, runevent.Repository, prNumber)
-	if err != nil {
-		return runevent, err
-	}
-	// Make sure to use the Base for Default BaseBranch or there would be a potential hijack
-	runevent.DefaultBranch = pr.GetBase().GetRepo().GetDefaultBranch()
-	runevent.URL = pr.GetBase().GetRepo().GetHTMLURL()
-	runevent.SHA = pr.GetHead().GetSHA()
-	runevent.SHAURL = fmt.Sprintf("%s/commit/%s", pr.GetHTMLURL(), pr.GetHead().GetSHA())
-	// TODO: Maybe if we wanted to allow rerequest from non approved user we
-	// would use the CheckRun Sender instead of the rerequest sender, could it
-	// be a room for abuse? 🤔
-	runevent.Sender = pr.GetUser().GetLogin()
-	runevent.HeadBranch = pr.GetHead().GetRef()
-	runevent.BaseBranch = pr.GetBase().GetRef()
-	runevent.EventType = "pull_request"
-	return runevent, nil
-}
-
-// populateCommitInfo get info on a commit in runevent
-func (v VCS) populateCommitInfo(ctx context.Context, runevent *info.Event) error {
+// GetCommitInfo get info (url and title) on a commit in runevent, this needs to
+// be run after parsewebhook while we already matched a token.
+func (v *VCS) GetCommitInfo(ctx context.Context, runevent *info.Event) error {
 	commit, _, err := v.Client.Git.GetCommit(ctx, runevent.Owner, runevent.Repository, runevent.SHA)
 	if err != nil {
 		return err
@@ -120,7 +84,7 @@ func (v VCS) populateCommitInfo(ctx context.Context, runevent *info.Event) error
 // GetFileInsideRepo Get a file via Github API using the runinfo information, we
 // branch is true, the user the branch as ref isntead of the SHA
 // TODO: merge GetFileInsideRepo amd GetTektonDir
-func (v VCS) GetFileInsideRepo(ctx context.Context, runevent *info.Event, path string, branch bool) (string, error) {
+func (v *VCS) GetFileInsideRepo(ctx context.Context, runevent *info.Event, path string, branch bool) (string, error) {
 	ref := runevent.SHA
 	if branch {
 		ref = runevent.BaseBranch
@@ -146,8 +110,49 @@ func (v VCS) GetFileInsideRepo(ctx context.Context, runevent *info.Event, path s
 	return string(getobj), nil
 }
 
+// concatAllYamlFiles concat all yaml files from a directory as one big multi document yaml string
+func (v *VCS) concatAllYamlFiles(ctx context.Context, objects []*github.RepositoryContent, runevent *info.Event) (string, error) {
+	var allTemplates string
+
+	for _, value := range objects {
+		if strings.HasSuffix(value.GetName(), ".yaml") ||
+			strings.HasSuffix(value.GetName(), ".yml") {
+			data, err := v.getObject(ctx, value.GetSHA(), runevent)
+			if err != nil {
+				return "", err
+			}
+			if allTemplates != "" && !strings.HasPrefix(string(data), "---") {
+				allTemplates += "---"
+			}
+			allTemplates += "\n" + string(data) + "\n"
+		}
+	}
+	return allTemplates, nil
+}
+
+// getPullRequest get a pull request details
+func (v *VCS) getPullRequest(ctx context.Context, runevent *info.Event, prNumber int) (*info.Event, error) {
+	pr, _, err := v.Client.PullRequests.Get(ctx, runevent.Owner, runevent.Repository, prNumber)
+	if err != nil {
+		return runevent, err
+	}
+	// Make sure to use the Base for Default BaseBranch or there would be a potential hijack
+	runevent.DefaultBranch = pr.GetBase().GetRepo().GetDefaultBranch()
+	runevent.URL = pr.GetBase().GetRepo().GetHTMLURL()
+	runevent.SHA = pr.GetHead().GetSHA()
+	runevent.SHAURL = fmt.Sprintf("%s/commit/%s", pr.GetHTMLURL(), pr.GetHead().GetSHA())
+	// TODO: Maybe if we wanted to allow rerequest from non approved user we
+	// would use the CheckRun Sender instead of the rerequest sender, could it
+	// be a room for abuse? 🤔
+	runevent.Sender = pr.GetUser().GetLogin()
+	runevent.HeadBranch = pr.GetHead().GetRef()
+	runevent.BaseBranch = pr.GetBase().GetRef()
+	runevent.EventType = "pull_request"
+	return runevent, nil
+}
+
 // getObject Get an object from a repository
-func (v VCS) getObject(ctx context.Context, sha string, runevent *info.Event) ([]byte, error) {
+func (v *VCS) getObject(ctx context.Context, sha string, runevent *info.Event) ([]byte, error) {
 	blob, _, err := v.Client.Git.GetBlob(ctx, runevent.Owner, runevent.Repository, sha)
 	if err != nil {
 		return nil, err
@@ -159,80 +164,3 @@ func (v VCS) getObject(ctx context.Context, sha string, runevent *info.Event) ([
 	}
 	return decoded, err
 }
-
-func (v VCS) createCheckRun(ctx context.Context, status string, runevent *info.Event, pacopts info.PacOpts) (*github.CheckRun, error) {
-	now := github.Timestamp{Time: time.Now()}
-	checkrunoption := github.CreateCheckRunOptions{
-		Name:       pacopts.ApplicationName,
-		HeadSHA:    runevent.SHA,
-		Status:     github.String(status),
-		DetailsURL: github.String(pacopts.LogURL),
-		StartedAt:  &now,
-	}
-
-	checkRun, _, err := v.Client.Checks.CreateCheckRun(ctx, runevent.Owner, runevent.Repository, checkrunoption)
-	return checkRun, err
-}
-
-func (v VCS) CreateStatus(ctx context.Context, runevent *info.Event, pacopts info.PacOpts, status webvcs.StatusOpts) error {
-	checkrunid := runevent.CheckRunID
-
-	// TODO: get console info ASAP,
-	// Create initial checkrun if not exist
-	if checkrunid == nil {
-		createCheckRun, err := v.createCheckRun(ctx, "in_progress", runevent, pacopts)
-		if err != nil {
-			return err
-		}
-		checkrunid = createCheckRun.ID
-	}
-
-	now := github.Timestamp{Time: time.Now()}
-
-	var summary, title string
-
-	switch status.Conclusion {
-	case "success":
-		title = "✅ Success"
-		summary = fmt.Sprintf("%s has successfully validated your commit.", pacopts.ApplicationName)
-	case "failure":
-		title = "❌ Failed"
-		summary = fmt.Sprintf("%s has <b>failed</b>.", pacopts.ApplicationName)
-	case "skipped":
-		title = "➖ Skipped"
-		summary = fmt.Sprintf("%s is skipping this commit.", pacopts.ApplicationName)
-	case "neutral":
-		title = "❓ Unknown"
-		summary = fmt.Sprintf("%s doesn't know what happened with this commit.", pacopts.ApplicationName)
-	}
-
-	if status.Status == "in_progress" {
-		title = "CI has Started"
-		summary = fmt.Sprintf("%s is running.", pacopts.ApplicationName)
-	}
-
-	checkRunOutput := &github.CheckRunOutput{
-		Title:   &title,
-		Summary: &summary,
-		Text:    &status.Text,
-	}
-
-	opts := github.UpdateCheckRunOptions{
-		Name:   pacopts.ApplicationName,
-		Status: &status.Status,
-		Output: checkRunOutput,
-	}
-
-	if status.DetailsURL != "" {
-		opts.DetailsURL = &status.DetailsURL
-	}
-
-	// Only set completed-at if conclusion is set (which means finished)
-	if status.Conclusion != "" {
-		opts.CompletedAt = &now
-		opts.Conclusion = &status.Conclusion
-	}
-
-	_, _, err := v.Client.Checks.UpdateCheckRun(ctx, runevent.Owner, runevent.Repository, *checkrunid, opts)
-	return err
-}
diff --git a/pkg/webvcs/github/status.go b/pkg/webvcs/github/status.go
new file mode 100644
index 00000000..36247144
--- /dev/null
+++ b/pkg/webvcs/github/status.go
@@ -0,0 +1,134 @@
+package github
+
+import (
+	"context"
+	"fmt"
+	"time"
+
+	"github.com/google/go-github/v35/github"
+	"github.com/openshift-pipelines/pipelines-as-code/pkg/params/info"
+	"github.com/openshift-pipelines/pipelines-as-code/pkg/webvcs"
+)
+
+// createCheckRunStatus create a status via the checkRun API, which is only
+// available with Github apps tokens.
+func (v *VCS) createCheckRunStatus(ctx context.Context, runevent *info.Event, pacopts info.PacOpts, status webvcs.StatusOpts) error {
+	now := github.Timestamp{Time: time.Now()}
+	if runevent.CheckRunID == nil {
+		now := github.Timestamp{Time: time.Now()}
+		checkrunoption := github.CreateCheckRunOptions{
+			Name:       pacopts.ApplicationName,
+			HeadSHA:    runevent.SHA,
+			Status:     github.String("in_progress"),
+			DetailsURL: github.String(pacopts.LogURL),
+			StartedAt:  &now,
+		}
+
+		checkRun, _, err := v.Client.Checks.CreateCheckRun(ctx, runevent.Owner, runevent.Repository, checkrunoption)
+		if err != nil {
+			return err
+		}
+		runevent.CheckRunID = checkRun.ID
+	}
+
+	checkRunOutput := &github.CheckRunOutput{
+		Title:   &status.Title,
+		Summary: &status.Summary,
+		Text:    &status.Text,
+	}
+
+	opts := github.UpdateCheckRunOptions{
+		Name:   pacopts.ApplicationName,
+		Status: &status.Status,
+		Output: checkRunOutput,
+	}
+
+	if status.DetailsURL != "" {
+		opts.DetailsURL = &status.DetailsURL
+	}
+
+	// Only set completed-at if conclusion is set (which means finished)
+	if status.Conclusion != "" {
+		opts.CompletedAt = &now
+		opts.Conclusion = &status.Conclusion
+	}
+
+	_, _, err := v.Client.Checks.UpdateCheckRun(ctx, runevent.Owner, runevent.Repository, *runevent.CheckRunID, opts)
+	return err
+}
+
+// createStatusCommit use the classic/old statuses API which is available when we
+// don't have a github app token
+func (v *VCS) createStatusCommit(ctx context.Context, runevent *info.Event, pacopts info.PacOpts, status webvcs.StatusOpts) error {
+	now := time.Now()
+	switch status.Conclusion {
+	case "skipped":
+		status.Conclusion = "success" // We don't have a choice than setting as succes, no pending here.
+	case "neutral":
+		status.Conclusion = "success" // We don't have a choice than setting as succes, no pending here.
+	}
+	if status.Status == "in_progress" {
+		status.Conclusion = "pending"
+	}
+
+	ghstatus := &github.RepoStatus{
+		State:       github.String(status.Conclusion),
+		TargetURL:   github.String(status.DetailsURL),
+		Description: github.String(status.Title),
+		Context:     github.String(pacopts.ApplicationName),
+		CreatedAt:   &now,
+	}
+
+	_, _, err := v.Client.Repositories.CreateStatus(ctx, runevent.Owner, runevent.Repository, runevent.SHA, ghstatus)
+	if err != nil {
+		return err
+	}
+	if status.Status == "completed" && status.Text != "" && runevent.EventType == "pull_request" {
+		payloadevent, ok := runevent.Event.(*github.PullRequestEvent)
+		if !ok {
+			return fmt.Errorf("bad event??? %w", err)
+		}
+		_, _, err = v.Client.Issues.CreateComment(ctx, runevent.Owner, runevent.Repository,
+			payloadevent.GetPullRequest().GetNumber(),
+			&github.IssueComment{
+				Body: github.String(fmt.Sprintf("%s<br>%s", status.Summary, status.Text)),
+			},
+		)
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+func (v *VCS) CreateStatus(ctx context.Context, runevent *info.Event, pacopts info.PacOpts, status webvcs.StatusOpts) error {
+	if pacopts.VCSAPIURL == "" || pacopts.VCSToken == "" {
+		return fmt.Errorf("cannot set status on github no token or url set")
+	}
+
+	switch status.Conclusion {
+	case "success":
+		status.Title = "✅ Success"
+		status.Summary = fmt.Sprintf("%s has <b>successfully</b> validated your commit.", pacopts.ApplicationName)
+	case "failure":
+		status.Title = "❌ Failed"
+		status.Summary = fmt.Sprintf("%s has <b>failed</b>.", pacopts.ApplicationName)
+	case "skipped":
+		status.Title = "➖ Skipped"
+		status.Summary = fmt.Sprintf("%s is skipping this commit.", pacopts.ApplicationName)
+	case "neutral":
+		status.Title = "❓ Unknown"
+		status.Summary = fmt.Sprintf("%s doesn't know what happened with this commit.", pacopts.ApplicationName)
+	}
+
+	if status.Status == "in_progress" {
+		status.Title = "CI has Started"
+		status.Summary = fmt.Sprintf("%s is running.", pacopts.ApplicationName)
+	}
+
+	if pacopts.VCSInfoFromRepo {
+		return v.createStatusCommit(ctx, runevent, pacopts, status)
+	}
+	return v.createCheckRunStatus(ctx, runevent, pacopts, status)
+}
diff --git a/pkg/webvcs/interface.go b/pkg/webvcs/interface.go
index fe13daaa..09b227be 100644
--- a/pkg/webvcs/interface.go
+++ b/pkg/webvcs/interface.go
@@ -3,8 +3,8 @@ package webvcs
 import (
 	"context"
 
+	"github.com/openshift-pipelines/pipelines-as-code/pkg/params"
 	"github.com/openshift-pipelines/pipelines-as-code/pkg/params/info"
-	"go.uber.org/zap"
 )
 
 type StatusOpts struct {
@@ -12,12 +12,16 @@ type StatusOpts struct {
 	Conclusion string
 	Text       string
 	DetailsURL string
+	Summary    string
+	Title      string
 }
 
 type Interface interface {
-	ParsePayload(context.Context, *zap.SugaredLogger, *info.Event, string) (*info.Event, error)
+	ParsePayload(context.Context, *params.Run, string) (*info.Event, error)
 	IsAllowed(context.Context, *info.Event) (bool, error)
 	CreateStatus(context.Context, *info.Event, info.PacOpts, StatusOpts) error
 	GetTektonDir(context.Context, *info.Event, string) (string, error)            // ctx, event, path
 	GetFileInsideRepo(context.Context, *info.Event, string, bool) (string, error) // ctx, event, path, branch
+	SetClient(context.Context, info.PacOpts)
+	GetCommitInfo(context.Context, *info.Event) error
 }
diff --git a/samples/repo.yaml b/samples/repo.yaml
deleted file mode 100644
index 289cb916..00000000
--- a/samples/repo.yaml
+++ /dev/null
@@ -1,21 +0,0 @@
----
-apiVersion: "pipelinesascode.tekton.dev/v1alpha1"
-kind: Repository
-metadata:
-  name: scratch-my-back
-spec:
-  url: "https://github.com/chmouel/scratchmyback"
-  branch: "main"
-  event_type: "pull_request"
-  namespace: "scratch-my-back"
-
----
-apiVersion: "pipelinesascode.tekton.dev/v1alpha1"
-kind: Repository
-metadata:
-  name: pipelines-catalog
-spec:
-  url: "https://github.com/openshift/pipelines-catalog"
-  branch: "release-next"
-  event_type: "pull_request"
-  namespace: "pipelines-catalog"
diff --git a/samples/repository-github-app.yaml b/samples/repository-github-app.yaml
new file mode 100644
index 00000000..13988179
--- /dev/null
+++ b/samples/repository-github-app.yaml
@@ -0,0 +1,17 @@
+# This is a sample application, which doesn't have a secret, if you use a
+# Github app installation (preferred) you don't have to specify a secret.
+---
+apiVersion: "pipelinesascode.tekton.dev/v1alpha1"
+kind: Repository
+metadata:
+  name: sample-app
+  namespace: "sample-ci"
+spec:
+  # which URL this Repository handles
+  url: "https://github.com/owner/sample-app"
+  # Which branch
+  branch: "main"
+  # Which event type
+  event_type: "pull_request"
+  # Target namespace, it needs to match where this CRD is installed.
+  namespace: "sample-ci"
diff --git a/samples/repository-secret.yaml b/samples/repository-secret.yaml
new file mode 100644
index 00000000..aa6502a4
--- /dev/null
+++ b/samples/repository-secret.yaml
@@ -0,0 +1,20 @@
+# This shows a repository which has a secret attached for webvcs operation.
+---
+apiVersion: "pipelinesascode.tekton.dev/v1alpha1"
+kind: Repository
+metadata:
+  name: sample-app-via-direct-webhook
+  namespace: "sample-ci"
+spec:
+  # which URL this Repository handles
+  url: "https://github.com/owner/sample-ci-direct-webhook"
+  # Which branch
+  branch: "main"
+  # Which event type
+  event_type: "pull_request"
+  # Target namespace, it needs to match where this CRD is installed.
+  namespace: "scratch-my-back"
+  webvcs_api_url: "github.enteprise.company.com"
+  webvcs_secret:
+    name: "github.enteprise.personal.access.token"
+    key: "tokenkey"
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 42e8c88b..6c2bd8dc 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -79,6 +79,7 @@ github.com/census-instrumentation/opencensus-proto/gen-go/trace/v1
 # github.com/cespare/xxhash/v2 v2.1.1
 github.com/cespare/xxhash/v2
 # github.com/davecgh/go-spew v1.1.1
+## explicit
 github.com/davecgh/go-spew/spew
 # github.com/emicklei/go-restful v2.15.0+incompatible
 github.com/emicklei/go-restful


Test patch:
diff --git a/config/404-trigger-retest-comment.yaml b/config/404-trigger-retest-comment.yaml
index a4ed6357..be8f388b 100644
--- a/config/404-trigger-retest-comment.yaml
+++ b/config/404-trigger-retest-comment.yaml
@@ -58,6 +58,7 @@ spec:
     - name: repository
     - name: pull_request_url
     - name: installation_id
+      default: ""
     - name: trigger_target
     - name: ghe_host
       default: "api.github.com"
diff --git a/hack/dev/e2e-tests-cleanup.sh b/hack/dev/e2e-tests-cleanup.sh
index efde18b9..b35aedb5 100755
--- a/hack/dev/e2e-tests-cleanup.sh
+++ b/hack/dev/e2e-tests-cleanup.sh
@@ -3,12 +3,14 @@ set -eu
 
 type -p gh >/dev/null || { echo "You need gh installed and configured for ${TEST_GITHUB_API_URL}"; exit 1 ;}
 
-export GH_REPO=${TEST_GITHUB_REPO_OWNER}
-export GH_HOST=$(echo ${TEST_GITHUB_API_URL}|sed 's,https://,,')
+for target in ${TEST_GITHUB_REPO_OWNER_GITHUBAPP} ${TEST_GITHUB_REPO_OWNER_WEBHOOK};do
+    export GH_REPO=${target}
+    export GH_HOST=$(echo ${TEST_GITHUB_API_URL}|sed 's,https://,,')
 
-echo "Closing lingering PR on ${TEST_GITHUB_API_URL}"
-for prn in $(gh pr list --jq .[].number --json number);do
-    gh pr close ${prn}
+    echo "Closing lingering PR on ${target}"
+    for prn in $(gh pr list --jq .[].number --json number);do
+        gh pr close ${prn}
+    done
 done
 
 echo "Cleaning Namespaces"
diff --git a/pkg/cmd/pipelineascode/pipelineascode_test.go b/pkg/cmd/pipelineascode/pipelineascode_test.go
index e685e5f9..fd74498d 100644
--- a/pkg/cmd/pipelineascode/pipelineascode_test.go
+++ b/pkg/cmd/pipelineascode/pipelineascode_test.go
@@ -29,17 +29,19 @@ func TestRunByVCS(t *testing.T) {
 		payloadFile          string
 		consoleURLErroring   bool
 		createStatusErroring bool
-		twebvcs              testwebvcs.TestWebVCSImp
+		twebvcs              *testwebvcs.TestWebVCSImp
 	}{
 		{
 			name:        "error on payload not here",
-			payloadFile: "/no/men/no/football",
+			payloadFile: "/no/men/no/war",
 			wantErr:     true,
+			twebvcs:     &testwebvcs.TestWebVCSImp{},
 		},
 		{
 			name:        "no payload passed",
 			payloadFile: "",
 			wantErr:     true,
+			twebvcs:     &testwebvcs.TestWebVCSImp{},
 		},
 		{
 			name: "run pull_request user not allowed",
@@ -54,7 +56,7 @@ func TestRunByVCS(t *testing.T) {
 				SHA:           "sha",
 				Sender:        "sender",
 			},
-			twebvcs:     testwebvcs.TestWebVCSImp{AllowIT: false},
+			twebvcs:     &testwebvcs.TestWebVCSImp{AllowIT: false},
 			payloadFile: "testdata/pull_request.json",
 		},
 		{
@@ -70,24 +72,14 @@ func TestRunByVCS(t *testing.T) {
 				SHA:           "sha",
 				Sender:        "sender",
 			},
-			twebvcs:            testwebvcs.TestWebVCSImp{AllowIT: false},
+			twebvcs:            &testwebvcs.TestWebVCSImp{AllowIT: false},
 			payloadFile:        "testdata/pull_request.json",
 			consoleURLErroring: true,
 		},
 		{
-			name: "create status error",
-			event: &info.Event{
-				EventType:     "pull_request",
-				TriggerTarget: "pull_request",
-				BaseBranch:    "testbranch",
-				DefaultBranch: "main",
-				HeadBranch:    "main",
-				Owner:         "owner",
-				Repository:    "repo",
-				SHA:           "sha",
-				Sender:        "sender",
-			},
-			twebvcs:     testwebvcs.TestWebVCSImp{AllowIT: false, CreateStatusErorring: true},
+			name:        "create status error",
+			event:       &info.Event{},
+			twebvcs:     &testwebvcs.TestWebVCSImp{AllowIT: false, CreateStatusErorring: true},
 			payloadFile: "testdata/pull_request.json",
 			wantErr:     true,
 		},
@@ -103,9 +95,12 @@ func TestRunByVCS(t *testing.T) {
 					Pac: info.PacOpts{
 						VCSType:     "github",
 						PayloadFile: test.payloadFile,
+						VCSToken:    "TOKEN",
+						VCSAPIURL:   "http://goninjago",
 					},
 				},
 			}
+
 			test.twebvcs.Event = test.event
 			kinteract := &kitesthelper.KinterfaceTest{
 				ConsoleURL:         "http://console",
@@ -144,8 +139,7 @@ func TestGetPayloadFromFile(t *testing.T) {
 			opts := info.PacOpts{
 				VCSType: tt.vcstype,
 			}
-			ctx, _ := rtesting.SetupFakeContext(t)
-			_, err := getVCS(ctx, opts)
+			_, err := getVCS(opts)
 			if (err != nil) != tt.wantErr {
 				t.Errorf("getPayloadFromFile() error = %v, wantErr %v", err, tt.wantErr)
 				return
diff --git a/pkg/matcher/annotation_matcher_test.go b/pkg/matcher/annotation_matcher_test.go
index f3d2c1e8..06cd2ce8 100644
--- a/pkg/matcher/annotation_matcher_test.go
+++ b/pkg/matcher/annotation_matcher_test.go
@@ -53,7 +53,7 @@ func TestMatchPipelinerunAnnotationAndRepositories(t *testing.T) {
 				runevent: info.Event{URL: targetURL, EventType: "pull_request", BaseBranch: mainBranch},
 				data: testclient.Data{
 					Repositories: []*v1alpha1.Repository{
-						testnewrepo.NewRepo("test-good", targetURL, mainBranch, targetNamespace, targetNamespace, "pull_request"),
+						testnewrepo.NewRepo("test-good", targetURL, mainBranch, targetNamespace, targetNamespace, "pull_request", "", ""),
 					},
 				},
 			},
@@ -67,8 +67,8 @@ func TestMatchPipelinerunAnnotationAndRepositories(t *testing.T) {
 				runevent: info.Event{URL: targetURL, EventType: "pull_request", BaseBranch: mainBranch},
 				data: testclient.Data{
 					Repositories: []*v1alpha1.Repository{
-						testnewrepo.NewRepo("test-other", targetURL, mainBranch, targetNamespace, targetNamespace, "pull_request"),
-						testnewrepo.NewRepo("test-good", targetURL, mainBranch, targetNamespace, targetNamespace, "pull_request"),
+						testnewrepo.NewRepo("test-other", targetURL, mainBranch, targetNamespace, targetNamespace, "pull_request", "", ""),
+						testnewrepo.NewRepo("test-good", targetURL, mainBranch, targetNamespace, targetNamespace, "pull_request", "", ""),
 					},
 				},
 			},
@@ -82,7 +82,7 @@ func TestMatchPipelinerunAnnotationAndRepositories(t *testing.T) {
 				runevent: info.Event{URL: targetURL, EventType: "pull_request", BaseBranch: mainBranch},
 				data: testclient.Data{
 					Repositories: []*v1alpha1.Repository{
-						testnewrepo.NewRepo("test-good", targetURL, mainBranch, "otherNS", "otherNS", "pull_request"),
+						testnewrepo.NewRepo("test-good", targetURL, mainBranch, "otherNS", "otherNS", "pull_request", "", ""),
 					},
 				},
 			},
diff --git a/pkg/matcher/annotation_tasks_install_test.go b/pkg/matcher/annotation_tasks_install_test.go
index cc6ba1d1..250dbf78 100644
--- a/pkg/matcher/annotation_tasks_install_test.go
+++ b/pkg/matcher/annotation_tasks_install_test.go
@@ -174,10 +174,9 @@ spec:
 				Run: cs,
 			}
 
-			vcsint := webvcs.TestWebVCSImp{
+			got, err := rt.GetTaskFromAnnotations(ctx, &webvcs.TestWebVCSImp{
 				FilesInsideRepo: tt.filesInsideRepo,
-			}
-			got, err := rt.GetTaskFromAnnotations(ctx, vcsint, tt.annotations)
+			}, tt.annotations)
 			if tt.wantErr != "" {
 				assert.ErrorContains(t, err, tt.wantErr, "We should have get an error with %v but we didn't", tt.wantErr)
 				return
diff --git a/pkg/matcher/repo_runinfo_matcher_test.go b/pkg/matcher/repo_runinfo_matcher_test.go
index 35e68a1b..693bd328 100644
--- a/pkg/matcher/repo_runinfo_matcher_test.go
+++ b/pkg/matcher/repo_runinfo_matcher_test.go
@@ -37,7 +37,7 @@ func Test_getRepoByCR(t *testing.T) {
 			args: args{
 				data: testclient.Data{
 					Repositories: []*v1alpha1.Repository{
-						testnewrepo.NewRepo("test-good", targetURL, mainBranch, targetNamespace, targetNamespace, "pull_request"),
+						testnewrepo.NewRepo("test-good", targetURL, mainBranch, targetNamespace, targetNamespace, "pull_request", "", ""),
 					},
 				},
 				runevent: info.Event{URL: targetURL, BaseBranch: mainBranch, EventType: "pull_request"},
@@ -50,7 +50,7 @@ func Test_getRepoByCR(t *testing.T) {
 			args: args{
 				data: testclient.Data{
 					Repositories: []*v1alpha1.Repository{
-						testnewrepo.NewRepo("test-good", targetURL, mainBranch, "paslebonns", targetNamespace, "pull_request"),
+						testnewrepo.NewRepo("test-good", targetURL, mainBranch, "paslebonns", targetNamespace, "pull_request", "", ""),
 					},
 				},
 				runevent: info.Event{URL: targetURL, BaseBranch: mainBranch, EventType: "pull_request"},
@@ -63,7 +63,7 @@ func Test_getRepoByCR(t *testing.T) {
 			args: args{
 				data: testclient.Data{
 					Repositories: []*v1alpha1.Repository{
-						testnewrepo.NewRepo("test-good", targetURL, mainBranch, targetNamespace, targetNamespace, "pull_request"),
+						testnewrepo.NewRepo("test-good", targetURL, mainBranch, targetNamespace, targetNamespace, "pull_request", "", ""),
 					},
 				},
 				runevent: info.Event{URL: targetURL, BaseBranch: mainBranch, EventType: "push"},
@@ -76,7 +76,7 @@ func Test_getRepoByCR(t *testing.T) {
 			args: args{
 				data: testclient.Data{
 					Repositories: []*v1alpha1.Repository{
-						testnewrepo.NewRepo("test-good", targetURL, mainBranch, targetNamespace, targetNamespace, "pull_request"),
+						testnewrepo.NewRepo("test-good", targetURL, mainBranch, targetNamespace, targetNamespace, "pull_request", "", ""),
 					},
 				},
 				runevent: info.Event{URL: targetURL, BaseBranch: "anotherBaseBranch", EventType: "pull_request"},
@@ -89,8 +89,7 @@ func Test_getRepoByCR(t *testing.T) {
 			args: args{
 				data: testclient.Data{
 					Repositories: []*v1alpha1.Repository{
-						testnewrepo.NewRepo("test-good", "http://nottarget.url", mainBranch,
-							targetNamespace, targetNamespace, "pull_request"),
+						testnewrepo.NewRepo("test-good", "http://nottarget.url", mainBranch, targetNamespace, targetNamespace, "pull_request", "", ""),
 					},
 				},
 				runevent: info.Event{URL: targetURL, BaseBranch: mainBranch, EventType: "pull_request"},
@@ -103,8 +102,7 @@ func Test_getRepoByCR(t *testing.T) {
 			args: args{
 				data: testclient.Data{
 					Repositories: []*v1alpha1.Repository{
-						testnewrepo.NewRepo("test-good", targetURL, mainBranch,
-							targetNamespace, targetNamespace, "pull_request"),
+						testnewrepo.NewRepo("test-good", targetURL, mainBranch, targetNamespace, targetNamespace, "pull_request", "", ""),
 					},
 				},
 				runevent: info.Event{
@@ -120,8 +118,7 @@ func Test_getRepoByCR(t *testing.T) {
 			args: args{
 				data: testclient.Data{
 					Repositories: []*v1alpha1.Repository{
-						testnewrepo.NewRepo("test-good", targetURL, "refs/tags/*",
-							targetNamespace, targetNamespace, "pull_request"),
+						testnewrepo.NewRepo("test-good", targetURL, "refs/tags/*", targetNamespace, targetNamespace, "pull_request", "", ""),
 					},
 				},
 				runevent: info.Event{
diff --git a/pkg/pipelineascode/pipelineascode_test.go b/pkg/pipelineascode/testrun_github_test.go
similarity index 76%
rename from pkg/pipelineascode/pipelineascode_test.go
rename to pkg/pipelineascode/testrun_github_test.go
index 857dbaaf..c89bd595 100644
--- a/pkg/pipelineascode/pipelineascode_test.go
+++ b/pkg/pipelineascode/testrun_github_test.go
@@ -11,18 +11,18 @@ import (
 	"strings"
 	"testing"
 
-	"github.com/openshift-pipelines/pipelines-as-code/pkg/params/clients"
-	"github.com/openshift-pipelines/pipelines-as-code/pkg/params/info"
-	testDynamic "github.com/openshift-pipelines/pipelines-as-code/pkg/test/dynamic"
-	"github.com/openshift-pipelines/pipelines-as-code/pkg/test/repository"
-
 	"github.com/google/go-github/v35/github"
 	"github.com/openshift-pipelines/pipelines-as-code/pkg/apis/pipelinesascode/v1alpha1"
 	"github.com/openshift-pipelines/pipelines-as-code/pkg/params"
+	"github.com/openshift-pipelines/pipelines-as-code/pkg/params/clients"
+	"github.com/openshift-pipelines/pipelines-as-code/pkg/params/info"
 	testclient "github.com/openshift-pipelines/pipelines-as-code/pkg/test/clients"
+	testDynamic "github.com/openshift-pipelines/pipelines-as-code/pkg/test/dynamic"
 	ghtesthelper "github.com/openshift-pipelines/pipelines-as-code/pkg/test/github"
 	kitesthelper "github.com/openshift-pipelines/pipelines-as-code/pkg/test/kubernetestint"
-	githubintf "github.com/openshift-pipelines/pipelines-as-code/pkg/webvcs/github"
+	"github.com/openshift-pipelines/pipelines-as-code/pkg/test/repository"
+	ghwebvcs "github.com/openshift-pipelines/pipelines-as-code/pkg/webvcs/github"
+	pipelinev1beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
 	"go.uber.org/zap"
 	zapobserver "go.uber.org/zap/zaptest/observer"
 	"gotest.tools/v3/assert"
@@ -42,6 +42,9 @@ func testSetupTektonDir(mux *http.ServeMux, runevent info.Event, directory strin
 	_ = filepath.Walk(directory, func(path string, info os.FileInfo, err error) error {
 		basename := filepath.Base(path)
 		trimmed := strings.TrimSuffix(basename, filepath.Ext(basename))
+		if info == nil {
+			return fmt.Errorf("should not be nil: %s", directory)
+		}
 		tektonDirContent += fmt.Sprintf(`{
 			"name": "%s",
 			"path": ".tekton/%s",
@@ -64,13 +67,26 @@ func testSetupTektonDir(mux *http.ServeMux, runevent info.Event, directory strin
 		fmt.Sprintf("[%s]", strings.TrimSuffix(tektonDirContent, ",")))
 }
 
-func testSetupCommonGhReplies(t *testing.T, mux *http.ServeMux, runevent info.Event, finalStatus, finalStatusText string,
-	noReplyOrgPublicMembers bool) {
+func testSetupCommonGhReplies(t *testing.T, mux *http.ServeMux, runevent info.Event,
+	finalStatus, finalStatusText string, noReplyOrgPublicMembers bool) {
 	// Take a directory and generate replies as Github for it
 	replyString(mux,
 		fmt.Sprintf("/repos/%s/%s/contents/internal/task", runevent.Owner, runevent.Repository),
 		`{"sha": "internaltasksha"}`)
 
+	replyString(mux,
+		fmt.Sprintf("/repos/%s/%s/statuses/%s", runevent.Owner, runevent.Repository, runevent.SHA),
+		"{}")
+
+	// using 666 as pull request number
+	replyString(mux,
+		fmt.Sprintf("/repos/%s/%s/issues/666/comments", runevent.Owner, runevent.Repository),
+		"{}")
+
+	replyString(mux,
+		fmt.Sprintf("/repos/%s/%s/git/commits/%s", runevent.Owner, runevent.Repository, runevent.SHA),
+		`{}`)
+
 	if !noReplyOrgPublicMembers {
 		mux.HandleFunc("/orgs/"+runevent.Owner+"/public_members", func(rw http.ResponseWriter, r *http.Request) {
 			fmt.Fprintf(rw, `[{"login": "%s"}]`, runevent.Sender)
@@ -105,13 +121,14 @@ func TestRun(t *testing.T) {
 		tektondir                    string
 		wantErr                      string
 		finalStatus                  string
-		finalLogText                 string
+		finalStatusText              string
 		repositories                 []*v1alpha1.Repository
 		skipReplyingOrgPublicMembers bool
 		expectedNumberofCleanups     int
+		VCSInfoFromRepo              bool
 	}{
 		{
-			name: "pull request",
+			name: "pull request/apps",
 			runevent: info.Event{
 				SHA:        "principale",
 				Owner:      "organizationes",
@@ -122,11 +139,32 @@ func TestRun(t *testing.T) {
 				Sender:     "fantasio",
 				EventType:  "pull_request",
 			},
-			tektondir:    "testdata/pull_request",
-			finalStatus:  "neutral",
-			finalLogText: "<th>Status</th><th>Duration</th><th>Name</th>",
+			tektondir:       "testdata/pull_request",
+			finalStatus:     "neutral",
+			finalStatusText: "<th>Status</th><th>Duration</th><th>Name</th>",
+		},
+		{
+			name: "pull request/with webhook",
+			runevent: info.Event{
+				Event: &github.PullRequestEvent{
+					PullRequest: &github.PullRequest{
+						Number: github.Int(666),
+					},
+				},
+				SHA:        "fromwebhook",
+				Owner:      "organizationes",
+				Repository: "lagaffe",
+				URL:        "https://service/documentation",
+				HeadBranch: "press",
+				BaseBranch: "main",
+				Sender:     "fantasio",
+				EventType:  "pull_request",
+			},
+			tektondir:       "testdata/pull_request",
+			finalStatus:     "neutral",
+			finalStatusText: "<th>Status</th><th>Duration</th><th>Name</th>",
+			VCSInfoFromRepo: true,
 		},
-
 		{
 			name: "No match",
 			runevent: info.Event{
@@ -187,9 +225,9 @@ func TestRun(t *testing.T) {
 				BaseBranch: "nomatch",
 				EventType:  "pull_request",
 			},
-			tektondir:    "",
-			finalStatus:  "skipped",
-			finalLogText: "directory for this repository",
+			tektondir:       "",
+			finalStatus:     "skipped",
+			finalStatusText: "directory for this repository",
 		},
 		// Skipped
 		{
@@ -205,9 +243,9 @@ func TestRun(t *testing.T) {
 				TriggerTarget: "check_run",
 				EventType:     "push",
 			},
-			tektondir:    "",
-			finalStatus:  "skipped",
-			finalLogText: "directory for this repository",
+			tektondir:       "",
+			finalStatus:     "skipped",
+			finalStatusText: "directory for this repository",
 		},
 		{
 			name: "Skipped/Test no repositories match on different event_type",
@@ -221,12 +259,11 @@ func TestRun(t *testing.T) {
 				BaseBranch: "nomatch",
 				EventType:  "push",
 			},
-			tektondir:    "",
-			finalStatus:  "skipped",
-			finalLogText: "Skipping creating status check",
+			tektondir:       "",
+			finalStatus:     "skipped",
+			finalStatusText: "not find a namespace match",
 			repositories: []*v1alpha1.Repository{
-				repository.NewRepo("test-run", "https://service/documentation",
-					"a branch", "namespace", "namespace", "pull_request"),
+				repository.NewRepo("test-run", "https://service/documentation", "a branch", "namespace", "namespace", "pull_request", "", ""),
 			},
 		},
 
@@ -242,12 +279,11 @@ func TestRun(t *testing.T) {
 				BaseBranch: "nomatch",
 				EventType:  "pull_request",
 			},
-			tektondir:    "",
-			finalStatus:  "skipped",
-			finalLogText: "not find a namespace match",
+			tektondir:       "",
+			finalStatus:     "skipped",
+			finalStatusText: "not find a namespace match",
 			repositories: []*v1alpha1.Repository{
-				repository.NewRepo("test-run", "https://nowhere.com",
-					"a branch", "namespace", "namespace", "pull_request"),
+				repository.NewRepo("test-run", "https://nowhere.com", "a branch", "namespace", "namespace", "pull_request", "", ""),
 			},
 		},
 
@@ -265,7 +301,7 @@ func TestRun(t *testing.T) {
 			},
 			tektondir:                    "testdata/pull_request",
 			finalStatus:                  "skipped",
-			finalLogText:                 "is not allowed to run CI on this repo",
+			finalStatusText:              "is not allowed to run CI on this repo",
 			skipReplyingOrgPublicMembers: true,
 		},
 		{
@@ -282,16 +318,26 @@ func TestRun(t *testing.T) {
 			},
 			tektondir:                "testdata/max-keep-runs",
 			finalStatus:              "neutral",
-			finalLogText:             "<th>Status</th><th>Duration</th><th>Name</th>",
+			finalStatusText:          "<th>Status</th><th>Duration</th><th>Name</th>",
 			expectedNumberofCleanups: 10,
 		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
 			ctx, _ := rtesting.SetupFakeContext(t)
+			fakeclient, mux, ghTestServerURL, teardown := ghtesthelper.SetupGH()
+			defer teardown()
+
+			var secretName, vcsURL string
+			if tt.VCSInfoFromRepo {
+				vcsURL = ghTestServerURL
+				secretName = "ziesecretee"
+			}
+
 			if tt.repositories == nil {
 				tt.repositories = []*v1alpha1.Repository{
-					repository.NewRepo("test-run", tt.runevent.URL, tt.runevent.BaseBranch, "namespace", "namespace", tt.runevent.EventType),
+					repository.NewRepo("test-run", tt.runevent.URL, tt.runevent.BaseBranch, "namespace", "namespace",
+						tt.runevent.EventType, secretName, vcsURL),
 				}
 			}
 			tdata := testclient.Data{
@@ -303,11 +349,16 @@ func TestRun(t *testing.T) {
 					},
 				},
 				Repositories: tt.repositories,
+				PipelineRuns: []*pipelinev1beta1.PipelineRun{
+					{
+						ObjectMeta: metav1.ObjectMeta{
+							Name: "force-me",
+						},
+					},
+				},
 			}
 
-			fakeclient, mux, _, teardown := ghtesthelper.SetupGH()
-			defer teardown()
-			testSetupCommonGhReplies(t, mux, tt.runevent, tt.finalStatus, tt.finalLogText, tt.skipReplyingOrgPublicMembers)
+			testSetupCommonGhReplies(t, mux, tt.runevent, tt.finalStatus, tt.finalStatusText, tt.skipReplyingOrgPublicMembers)
 			if tt.tektondir != "" {
 				testSetupTektonDir(mux, tt.runevent, tt.tektondir)
 			}
@@ -326,19 +377,23 @@ func TestRun(t *testing.T) {
 				Info: info.Info{
 					Event: &tt.runevent,
 					Pac: info.PacOpts{
+						VCSInfoFromRepo:    tt.VCSInfoFromRepo,
 						SecretAutoCreation: true,
+						VCSAPIURL:          ghTestServerURL,
+						VCSToken:           "NONE",
 					},
 				},
 			}
 			k8int := &kitesthelper.KinterfaceTest{
 				ConsoleURL:               "https://console.url",
 				ExpectedNumberofCleanups: tt.expectedNumberofCleanups,
+				GetSecretResult:          secretName,
 			}
 
-			vcsintf := githubintf.VCS{
+			err := Run(ctx, cs, &ghwebvcs.VCS{
 				Client: fakeclient,
-			}
-			err := Run(ctx, cs, vcsintf, k8int)
+				Token:  github.String("None"),
+			}, k8int)
 
 			if tt.wantErr != "" {
 				assert.ErrorContains(t, err, tt.wantErr)
diff --git a/pkg/resolve/resolve_test.go b/pkg/resolve/resolve_test.go
index 811a9ff8..108950f5 100644
--- a/pkg/resolve/resolve_test.go
+++ b/pkg/resolve/resolve_test.go
@@ -51,7 +51,7 @@ func readTDfile(t *testing.T, testname string, generateName bool) (*tektonv1beta
 		GenerateName: generateName,
 		RemoteTasks:  true,
 	}
-	tvcs := testwebvcs.TestWebVCSImp{}
+	tvcs := &testwebvcs.TestWebVCSImp{}
 	resolved, err := Resolve(ctx, cs, tvcs, string(data), ropt)
 	if err != nil {
 		return &tektonv1beta1.PipelineRun{}, nil, err
diff --git a/pkg/test/kubernetestint/kubernetesint.go b/pkg/test/kubernetestint/kubernetesint.go
index 158b0674..51da5958 100644
--- a/pkg/test/kubernetestint/kubernetesint.go
+++ b/pkg/test/kubernetestint/kubernetesint.go
@@ -6,6 +6,7 @@ import (
 	"fmt"
 	"time"
 
+	"github.com/openshift-pipelines/pipelines-as-code/pkg/kubeinteraction"
 	"github.com/openshift-pipelines/pipelines-as-code/pkg/params/info"
 	"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
 	tektonv1beta1client "github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1beta1"
@@ -16,6 +17,7 @@ type KinterfaceTest struct {
 	ConsoleURLErorring       bool
 	NamespaceError           bool
 	ExpectedNumberofCleanups int
+	GetSecretResult          string
 }
 
 func (k *KinterfaceTest) GetConsoleUI(ctx context.Context, ns string, pr string) (string, error) {
@@ -29,6 +31,10 @@ func (k *KinterfaceTest) CreateBasicAuthSecret(ctx context.Context, runevent *in
 	return nil
 }
 
+func (k *KinterfaceTest) GetSecret(ctx context.Context, secretopt kubeinteraction.GetSecretOpt) (string, error) {
+	return k.GetSecretResult, nil
+}
+
 func (k *KinterfaceTest) GetNamespace(ctx context.Context, ns string) error {
 	if k.NamespaceError {
 		return errors.New("cannot find namespace")
diff --git a/pkg/test/repository/repository.go b/pkg/test/repository/repository.go
index 7a7984ba..53104fd8 100644
--- a/pkg/test/repository/repository.go
+++ b/pkg/test/repository/repository.go
@@ -9,9 +9,9 @@ import (
 	"knative.dev/pkg/apis/duck/v1beta1"
 )
 
-func NewRepo(name, url, branch, installNamespace, namespace, eventtype string) *v1alpha1.Repository {
+func NewRepo(name, url, branch, installNamespace, namespace, eventtype, secretname, vcsurl string) *v1alpha1.Repository {
 	cw := clockwork.NewFakeClock()
-	return &v1alpha1.Repository{
+	repo := &v1alpha1.Repository{
 		TypeMeta: v1.TypeMeta{},
 		ObjectMeta: v1.ObjectMeta{
 			Name:      name,
@@ -56,4 +56,13 @@ func NewRepo(name, url, branch, installNamespace, namespace, eventtype string) *
 			},
 		},
 	}
+	if secretname != "" {
+		repo.Spec.WebvcsSecret = &v1alpha1.WebvcsSecretSpec{
+			Name: secretname,
+		}
+	}
+	if vcsurl != "" {
+		repo.Spec.WebvcsAPIURL = vcsurl
+	}
+	return repo
 }
diff --git a/pkg/test/webvcs/testwebvcs.go b/pkg/test/webvcs/testwebvcs.go
index 63c25309..56a638f2 100644
--- a/pkg/test/webvcs/testwebvcs.go
+++ b/pkg/test/webvcs/testwebvcs.go
@@ -4,9 +4,9 @@ import (
 	"context"
 	"fmt"
 
+	"github.com/openshift-pipelines/pipelines-as-code/pkg/params"
 	"github.com/openshift-pipelines/pipelines-as-code/pkg/params/info"
 	"github.com/openshift-pipelines/pipelines-as-code/pkg/webvcs"
-	"go.uber.org/zap"
 )
 
 type TestWebVCSImp struct {
@@ -17,31 +17,37 @@ type TestWebVCSImp struct {
 	FilesInsideRepo      map[string]string
 }
 
-func (v TestWebVCSImp) ParsePayload(ctx context.Context, logger *zap.SugaredLogger, event *info.Event,
-	s string) (*info.Event, error) {
+func (v *TestWebVCSImp) ParsePayload(ctx context.Context, run *params.Run, s string) (*info.Event, error) {
 	return v.Event, nil
 }
 
-func (v TestWebVCSImp) IsAllowed(ctx context.Context, event *info.Event) (bool, error) {
+func (v *TestWebVCSImp) GetCommitInfo(ctx context.Context, runevent *info.Event) error {
+	return nil
+}
+
+func (v *TestWebVCSImp) SetClient(ctx context.Context, pacopt info.PacOpts) {
+}
+
+func (v *TestWebVCSImp) IsAllowed(ctx context.Context, event *info.Event) (bool, error) {
 	if v.AllowIT {
 		return true, nil
 	}
 	return false, nil
 }
 
-func (v TestWebVCSImp) CreateStatus(ctx context.Context, event *info.Event, opts info.PacOpts,
-	opts2 webvcs.StatusOpts) error {
+func (v *TestWebVCSImp) CreateStatus(ctx context.Context, event *info.Event, opts info.PacOpts,
+	statusOpts webvcs.StatusOpts) error {
 	if v.CreateStatusErorring {
 		return fmt.Errorf("you want me to error I error for you")
 	}
 	return nil
 }
 
-func (v TestWebVCSImp) GetTektonDir(ctx context.Context, event *info.Event, s string) (string, error) {
+func (v *TestWebVCSImp) GetTektonDir(ctx context.Context, event *info.Event, s string) (string, error) {
 	return v.TektonDirTemplate, nil
 }
 
-func (v TestWebVCSImp) GetFileInsideRepo(ctx context.Context, event *info.Event, s string, b bool) (string, error) {
+func (v *TestWebVCSImp) GetFileInsideRepo(ctx context.Context, event *info.Event, s string, b bool) (string, error) {
 	if val, ok := v.FilesInsideRepo[s]; ok {
 		return val, nil
 	}
diff --git a/pkg/webvcs/github/events_test.go b/pkg/webvcs/github/events_test.go
index 3bacec9e..568ddeb6 100644
--- a/pkg/webvcs/github/events_test.go
+++ b/pkg/webvcs/github/events_test.go
@@ -7,6 +7,9 @@ import (
 	"strings"
 	"testing"
 
+	"github.com/google/go-github/v35/github"
+	"github.com/openshift-pipelines/pipelines-as-code/pkg/params"
+	"github.com/openshift-pipelines/pipelines-as-code/pkg/params/clients"
 	"github.com/openshift-pipelines/pipelines-as-code/pkg/params/info"
 	ghtesthelper "github.com/openshift-pipelines/pipelines-as-code/pkg/test/github"
 	"gotest.tools/v3/assert"
@@ -31,10 +34,21 @@ func TestPayLoadFix(t *testing.T) {
 	}
 
 	logger, _ := getLogger()
-	_, err = gvcs.ParsePayload(ctx, logger, &info.Event{
+
+	event := &info.Event{
 		EventType:     "pull_request",
 		TriggerTarget: "pull_request",
-	}, string(b))
+	}
+	run := &params.Run{
+		Clients: clients.Clients{
+			Log: logger,
+		},
+		Info: info.Info{
+			Event: event,
+		},
+	}
+	_, err = gvcs.ParsePayload(ctx, run, string(b))
+	assert.NilError(t, err)
 
 	// would bomb out on "assertion failed: error is not nil: invalid character
 	// '\n' in string literal" if we don't fix the payload
@@ -68,11 +82,19 @@ func TestParsePayloadRerequestFromPullRequest(t *testing.T) {
 		Client: fakeclient,
 	}
 	logger, observer := getLogger()
-	r := &info.Event{
+	event := &info.Event{
 		EventType:     "check_run",
 		TriggerTarget: "issue-recheck",
 	}
-	runinfo, err := gvcs.ParsePayload(ctx, logger, r, checkrunEvent)
+	run := &params.Run{
+		Clients: clients.Clients{
+			Log: logger,
+		},
+		Info: info.Info{
+			Event: event,
+		},
+	}
+	runinfo, err := gvcs.ParsePayload(ctx, run, checkrunEvent)
 	assert.NilError(t, err)
 
 	assert.Equal(t, prOwner, runinfo.Owner)
@@ -124,12 +146,21 @@ func TestParsePayloadRerequestFromPush(t *testing.T) {
 		_, _ = fmt.Fprint(w, `{"commit": {"message": "HELLO"}}`)
 	})
 
-	r := &info.Event{
+	event := &info.Event{
 		EventType:     "check_run",
 		TriggerTarget: "issue-recheck",
 	}
 	logger, _ := getLogger()
-	runinfo, err := gvcs.ParsePayload(ctx, logger, r, checkrunEvent)
+
+	run := &params.Run{
+		Clients: clients.Clients{
+			Log: logger,
+		},
+		Info: info.Info{
+			Event: event,
+		},
+	}
+	runinfo, err := gvcs.ParsePayload(ctx, run, checkrunEvent)
 	assert.NilError(t, err)
 
 	assert.Equal(t, runinfo.EventType, "push")
@@ -181,13 +212,24 @@ func TestParsePayLoadRetest(t *testing.T) {
 	gvcs := VCS{
 		Client: fakeclient,
 	}
-
 	// TODO
-	r := &info.Event{
+	event := &info.Event{
 		EventType:     "issue_comment",
 		TriggerTarget: "issue_comment",
 	}
-	runinfo, err := gvcs.ParsePayload(ctx, logger, r, issueEvent)
+	run := &params.Run{
+		Clients: clients.Clients{
+			Log: logger,
+		},
+		Info: info.Info{
+			Event: event,
+			Pac: info.PacOpts{
+				VCSToken: "TOKENSET",
+			},
+		},
+	}
+
+	runinfo, err := gvcs.ParsePayload(ctx, run, issueEvent)
 	assert.NilError(t, err)
 	assert.Equal(t, prOwner, runinfo.Owner)
 	// Make sure the PR owner is the runinfo.Owner and not the issueSender
@@ -195,6 +237,12 @@ func TestParsePayLoadRetest(t *testing.T) {
 	firstObservedMessage := observer.TakeAll()[0].Message
 	assert.Assert(t, strings.Contains(firstObservedMessage, "recheck"))
 	assert.Equal(t, runinfo.EventType, "pull_request")
+
+	// We cannot parse payload without a token when getting from issue_comment
+	run.Info.Pac = info.PacOpts{VCSToken: ""}
+	runinfo, err = gvcs.ParsePayload(ctx, run, issueEvent)
+	assert.ErrorContains(t, err, "gitops style comments operation is only supported with github apps")
+	assert.Assert(t, runinfo.Event == nil)
 }
 
 func TestParsePayload(t *testing.T) {
@@ -214,11 +262,19 @@ func TestParsePayload(t *testing.T) {
 	ctx, _ := rtesting.SetupFakeContext(t)
 	logger, _ := getLogger()
 
-	r := &info.Event{
+	event := &info.Event{
 		EventType:     "pull_request",
 		TriggerTarget: "pull_request",
 	}
-	runinfo, err := gvcs.ParsePayload(ctx, logger, r, string(b))
+	run := &params.Run{
+		Clients: clients.Clients{
+			Log: logger,
+		},
+		Info: info.Info{
+			Event: event,
+		},
+	}
+	runinfo, err := gvcs.ParsePayload(ctx, run, string(b))
 	assert.NilError(t, err)
 	assert.Assert(t, runinfo.BaseBranch == "master")
 	assert.Assert(t, runinfo.Owner == "chmouel")
@@ -229,45 +285,69 @@ func TestParsePayload(t *testing.T) {
 
 func TestParsePayloadInvalid(t *testing.T) {
 	ctx, _ := rtesting.SetupFakeContext(t)
-	gvcs := NewGithubVCS(ctx, info.PacOpts{
-		VCSToken:  "none",
-		VCSAPIURL: "nothing",
-	})
+	gvcs := VCS{
+		Token:  github.String("none"),
+		APIURL: github.String("nothing"),
+	}
 	logger, _ := getLogger()
-	r := &info.Event{
+	event := &info.Event{
 		EventType:     "pull_request",
 		TriggerTarget: "pull_request",
 	}
-	_, err := gvcs.ParsePayload(ctx, logger, r, "hello moto")
+	run := &params.Run{
+		Clients: clients.Clients{
+			Log: logger,
+		},
+		Info: info.Info{
+			Event: event,
+		},
+	}
+	_, err := gvcs.ParsePayload(ctx, run, "hello moto")
 	assert.ErrorContains(t, err, "invalid character")
 }
 
 func TestParsePayloadUnkownEvent(t *testing.T) {
 	ctx, _ := rtesting.SetupFakeContext(t)
-	gvcs := NewGithubVCS(ctx, info.PacOpts{
-		VCSToken:  "none",
-		VCSAPIURL: "",
-	})
+	gvcs := VCS{
+		Token:  github.String("none"),
+		APIURL: github.String(""),
+	}
 	logger, _ := getLogger()
-	r := &info.Event{
+	event := &info.Event{
 		EventType:     "foo",
 		TriggerTarget: "foo",
 	}
-	_, err := gvcs.ParsePayload(ctx, logger, r, "{\"hello\": \"moto\"}")
+	run := &params.Run{
+		Clients: clients.Clients{
+			Log: logger,
+		},
+		Info: info.Info{
+			Event: event,
+		},
+	}
+	_, err := gvcs.ParsePayload(ctx, run, "{\"hello\": \"moto\"}")
 	assert.ErrorContains(t, err, "unknown X-Github-Event")
 }
 
 func TestParsePayCannotParse(t *testing.T) {
 	ctx, _ := rtesting.SetupFakeContext(t)
-	gvcs := NewGithubVCS(ctx, info.PacOpts{
-		VCSToken:  "none",
-		VCSAPIURL: "",
-	})
+	gvcs := VCS{
+		Token:  github.String("none"),
+		APIURL: github.String(""),
+	}
 	logger, _ := getLogger()
-	r := &info.Event{
+	event := &info.Event{
 		EventType:     "gollum",
 		TriggerTarget: "gollum",
 	}
-	_, err := gvcs.ParsePayload(ctx, logger, r, "{}")
+	run := &params.Run{
+		Clients: clients.Clients{
+			Log: logger,
+		},
+		Info: info.Info{
+			Event: event,
+		},
+	}
+	_, err := gvcs.ParsePayload(ctx, run, "{}")
 	assert.Error(t, err, "this event is not supported")
 }
diff --git a/pkg/webvcs/github/github_test.go b/pkg/webvcs/github/github_test.go
index 7f9f12ab..4054eb5b 100644
--- a/pkg/webvcs/github/github_test.go
+++ b/pkg/webvcs/github/github_test.go
@@ -1,21 +1,16 @@
 package github
 
 import (
-	"encoding/json"
 	"fmt"
-	"io/ioutil"
 	"net/http"
 	"path/filepath"
-	"strings"
 	"testing"
 
 	"github.com/openshift-pipelines/pipelines-as-code/pkg/params/info"
 	ghtesthelper "github.com/openshift-pipelines/pipelines-as-code/pkg/test/github"
-	"github.com/openshift-pipelines/pipelines-as-code/pkg/webvcs"
 	rtesting "knative.dev/pkg/reconciler/testing"
 
 	"github.com/google/go-cmp/cmp"
-	"github.com/google/go-github/v35/github"
 	"go.uber.org/zap"
 	zapobserver "go.uber.org/zap/zaptest/observer"
 	"gotest.tools/v3/assert"
@@ -34,6 +29,10 @@ func setupFakesURLS() (client VCS, teardown func()) {
 		_, _ = fmt.Fprint(w, `{"id": 555}`)
 	})
 
+	mux.HandleFunc("/repos/check/info/check-runs/555", func(w http.ResponseWriter, r *http.Request) {
+		_, _ = fmt.Fprint(w, `{"id": 555}`)
+	})
+
 	mux.HandleFunc("/repos/check/info/check-runs/2026", func(w http.ResponseWriter, r *http.Request) {
 		_, _ = fmt.Fprint(w, `{"id": 666}`)
 	})
@@ -290,19 +289,6 @@ hello runyaml
 	}
 }
 
-func TestGithubVCS_CreateCheckRun(t *testing.T) {
-	ctx, _ := rtesting.SetupFakeContext(t)
-	gcvs, teardown := setupFakesURLS()
-	defer teardown()
-	event := &info.Event{
-		Owner:      "check",
-		Repository: "info",
-	}
-	cr, err := gcvs.createCheckRun(ctx, "hello moto", event, info.PacOpts{LogURL: "http://nowhere"})
-	assert.NilError(t, err)
-	assert.Equal(t, cr.GetID(), int64(555))
-}
-
 func TestCheckSenderOrgMembership(t *testing.T) {
 	tests := []struct {
 		name      string
@@ -411,134 +397,90 @@ func TestGetStringPullRequestComment(t *testing.T) {
 	}
 }
 
-func TestGithubVCS_CreateStatus(t *testing.T) {
-	checkrunid := int64(2026)
-	resultid := int64(666)
-	runEvent := info.Event{Owner: "check", Repository: "run", CheckRunID: &checkrunid}
-
-	type args struct {
-		runevent           info.Event
-		status             string
-		conclusion         string
-		text               string
-		detailsURL         string
-		titleSubstr        string
-		nilCompletedAtDate bool
-	}
+func TestVCS_GetCommitInfo(t *testing.T) {
 	tests := []struct {
-		name    string
-		args    args
-		want    *github.CheckRun
-		wantErr bool
+		name             string
+		event            *info.Event
+		wantErr          bool
+		shaurl, shatitle string
 	}{
 		{
-			name: "success",
-			args: args{
-				runevent:    runEvent,
-				status:      "completed",
-				conclusion:  "success",
-				text:        "Yay",
-				detailsURL:  "https://cireport.com",
-				titleSubstr: "Success",
-			},
-			want:    &github.CheckRun{ID: &resultid},
-			wantErr: false,
-		},
-		{
-			name: "in_progress",
-			args: args{
-				runevent:           runEvent,
-				status:             "in_progress",
-				conclusion:         "",
-				text:               "Yay",
-				detailsURL:         "https://cireport.com",
-				nilCompletedAtDate: true,
-			},
-			want:    &github.CheckRun{ID: &resultid},
-			wantErr: false,
-		},
-		{
-			name: "failure",
-			args: args{
-				runevent:    runEvent,
-				status:      "completed",
-				conclusion:  "failure",
-				text:        "Nay",
-				detailsURL:  "https://cireport.com",
-				titleSubstr: "Failed",
-			},
-			want:    &github.CheckRun{ID: &resultid},
-			wantErr: false,
-		},
-		{
-			name: "skipped",
-			args: args{
-				runevent:    runEvent,
-				status:      "completed",
-				conclusion:  "skipped",
-				text:        "Skipit",
-				detailsURL:  "https://cireport.com",
-				titleSubstr: "Skipped",
+			name: "good",
+			event: &info.Event{
+				Owner:      "owner",
+				Repository: "repository",
+				SHA:        "shacommitinfo",
 			},
-			want:    &github.CheckRun{ID: &resultid},
-			wantErr: false,
+			shaurl:   "https://git.vcs/commit/info",
+			shatitle: "My beautiful pony",
 		},
 		{
-			name: "unknown",
-			args: args{
-				runevent:    runEvent,
-				status:      "completed",
-				conclusion:  "neutral",
-				text:        "Je sais pas ce qui se passe wesh",
-				detailsURL:  "https://cireport.com",
-				titleSubstr: "Unknown",
+			name: "error",
+			event: &info.Event{
+				Owner:      "owner",
+				Repository: "repository",
+				SHA:        "shacommitinfo",
 			},
-			want:    &github.CheckRun{ID: &resultid},
-			wantErr: false,
+			wantErr: true,
 		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
 			fakeclient, mux, _, teardown := ghtesthelper.SetupGH()
 			defer teardown()
-
+			mux.HandleFunc(fmt.Sprintf("/repos/%s/%s/git/commits/%s",
+				tt.event.Owner, tt.event.Repository, tt.event.SHA), func(rw http.ResponseWriter, r *http.Request) {
+				if tt.wantErr {
+					fmt.Fprint(rw, "XXXXX")
+				} else {
+					fmt.Fprintf(rw, `{"html_url": "%s", "message": "%s"}`, tt.shaurl, tt.shatitle)
+				}
+			})
 			ctx, _ := rtesting.SetupFakeContext(t)
-			gcvs := VCS{
+			gvcs := &VCS{
 				Client: fakeclient,
 			}
-			mux.HandleFunc(fmt.Sprintf("/repos/check/run/check-runs/%d", checkrunid), func(rw http.ResponseWriter, r *http.Request) {
-				bit, _ := ioutil.ReadAll(r.Body)
-				checkRun := &github.CheckRun{}
-				err := json.Unmarshal(bit, checkRun)
-				assert.NilError(t, err)
-
-				if tt.args.nilCompletedAtDate {
-					// I guess that's the way you check for an undefined year,
-					// or maybe i don't understand fully how go works😅
-					assert.Assert(t, checkRun.GetCompletedAt().Year() == 0o001)
-				}
-				assert.Equal(t, checkRun.GetStatus(), tt.args.status)
-				assert.Equal(t, checkRun.GetConclusion(), tt.args.conclusion)
-				assert.Equal(t, checkRun.Output.GetText(), tt.args.text)
-				assert.Equal(t, checkRun.GetDetailsURL(), tt.args.detailsURL)
-				assert.Assert(t, strings.Contains(checkRun.Output.GetTitle(), tt.args.titleSubstr))
-				_, err = fmt.Fprintf(rw, `{"id": %d}`, resultid)
-				assert.NilError(t, err)
-			})
 
-			status := webvcs.StatusOpts{
-				Status:     tt.args.status,
-				Conclusion: tt.args.conclusion,
-				Text:       tt.args.text,
-				DetailsURL: tt.args.detailsURL,
-			}
-			pacopts := info.PacOpts{
-				LogURL: "https://log",
+			if err := gvcs.GetCommitInfo(ctx, tt.event); (err != nil) != tt.wantErr {
+				t.Errorf("GetCommitInfo() error = %v, wantErr %v", err, tt.wantErr)
 			}
-			err := gcvs.CreateStatus(ctx, &tt.args.runevent, pacopts, status)
-			if (err != nil) != tt.wantErr {
-				t.Errorf("GithubVCS.CreateStatus() error = %v, wantErr %v", err, tt.wantErr)
-				return
+			assert.Equal(t, tt.shatitle, tt.event.SHATitle)
+			assert.Equal(t, tt.shaurl, tt.event.SHAURL)
+		})
+	}
+}
+
+func TestVCS_SetClient(t *testing.T) {
+	tests := []struct {
+		name        string
+		info        info.PacOpts
+		expectedURL string
+		isGHE       bool
+	}{
+		{
+			name: "api url set",
+			info: info.PacOpts{
+				VCSAPIURL: "foo.com",
+			},
+			expectedURL: "https://foo.com",
+			isGHE:       true,
+		},
+		{
+			name:        "default to public github",
+			expectedURL: "https://api.github.com/",
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			ctx, _ := rtesting.SetupFakeContext(t)
+			v := VCS{}
+			v.SetClient(ctx, tt.info)
+			assert.Equal(t, tt.expectedURL, *v.APIURL)
+			assert.Equal(t, "https", v.Client.BaseURL.Scheme)
+			if tt.isGHE {
+				assert.Equal(t, "/api/v3/", v.Client.BaseURL.Path)
+			} else {
+				assert.Equal(t, "/", v.Client.BaseURL.Path)
 			}
 		})
 	}
diff --git a/pkg/webvcs/github/status_test.go b/pkg/webvcs/github/status_test.go
new file mode 100644
index 00000000..d6168297
--- /dev/null
+++ b/pkg/webvcs/github/status_test.go
@@ -0,0 +1,257 @@
+package github
+
+import (
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+	"strings"
+	"testing"
+
+	"github.com/google/go-github/v35/github"
+	"github.com/openshift-pipelines/pipelines-as-code/pkg/params/info"
+	ghtesthelper "github.com/openshift-pipelines/pipelines-as-code/pkg/test/github"
+	"github.com/openshift-pipelines/pipelines-as-code/pkg/webvcs"
+	"gotest.tools/v3/assert"
+	rtesting "knative.dev/pkg/reconciler/testing"
+)
+
+func TestGithubVCS_CreateCheckRun(t *testing.T) {
+	ctx, _ := rtesting.SetupFakeContext(t)
+	gcvs, teardown := setupFakesURLS()
+	defer teardown()
+	event := &info.Event{
+		Owner:      "check",
+		Repository: "info",
+	}
+
+	err := gcvs.createCheckRunStatus(ctx, event, info.PacOpts{LogURL: "http://nowhere"}, webvcs.StatusOpts{Status: "hello moto"})
+	assert.NilError(t, err)
+	assert.Equal(t, *event.CheckRunID, int64(555))
+}
+
+func TestGithubVCS_CreateStatus(t *testing.T) {
+	checkrunid := int64(2026)
+	resultid := int64(666)
+	runEvent := info.Event{Owner: "check", Repository: "run", CheckRunID: &checkrunid}
+
+	type args struct {
+		runevent           info.Event
+		status             string
+		conclusion         string
+		text               string
+		detailsURL         string
+		titleSubstr        string
+		nilCompletedAtDate bool
+	}
+	tests := []struct {
+		name    string
+		args    args
+		want    *github.CheckRun
+		wantErr bool
+		notoken bool
+	}{
+		{
+			name: "success",
+			args: args{
+				runevent:    runEvent,
+				status:      "completed",
+				conclusion:  "success",
+				text:        "Yay",
+				detailsURL:  "https://cireport.com",
+				titleSubstr: "Success",
+			},
+			want:    &github.CheckRun{ID: &resultid},
+			wantErr: false,
+		},
+		{
+			name: "in_progress",
+			args: args{
+				runevent:           runEvent,
+				status:             "in_progress",
+				conclusion:         "",
+				text:               "Yay",
+				detailsURL:         "https://cireport.com",
+				nilCompletedAtDate: true,
+			},
+			want:    &github.CheckRun{ID: &resultid},
+			wantErr: false,
+		},
+		{
+			name: "failure",
+			args: args{
+				runevent:    runEvent,
+				status:      "completed",
+				conclusion:  "failure",
+				text:        "Nay",
+				detailsURL:  "https://cireport.com",
+				titleSubstr: "Failed",
+			},
+			want:    &github.CheckRun{ID: &resultid},
+			wantErr: false,
+		},
+		{
+			name: "skipped",
+			args: args{
+				runevent:    runEvent,
+				status:      "completed",
+				conclusion:  "skipped",
+				text:        "Skipit",
+				detailsURL:  "https://cireport.com",
+				titleSubstr: "Skipped",
+			},
+			want:    &github.CheckRun{ID: &resultid},
+			wantErr: false,
+		},
+		{
+			name: "unknown",
+			args: args{
+				runevent:    runEvent,
+				status:      "completed",
+				conclusion:  "neutral",
+				text:        "Je sais pas ce qui se passe wesh",
+				detailsURL:  "https://cireport.com",
+				titleSubstr: "Unknown",
+			},
+			want:    &github.CheckRun{ID: &resultid},
+			wantErr: false,
+		},
+		{
+			name:    "no token set",
+			wantErr: true,
+			notoken: true,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			fakeclient, mux, _, teardown := ghtesthelper.SetupGH()
+			defer teardown()
+
+			ctx, _ := rtesting.SetupFakeContext(t)
+			gcvs := VCS{
+				Client: fakeclient,
+			}
+			mux.HandleFunc(fmt.Sprintf("/repos/check/run/check-runs/%d", checkrunid), func(rw http.ResponseWriter, r *http.Request) {
+				bit, _ := ioutil.ReadAll(r.Body)
+				checkRun := &github.CheckRun{}
+				err := json.Unmarshal(bit, checkRun)
+				assert.NilError(t, err)
+
+				if tt.args.nilCompletedAtDate {
+					// I guess that's the way you check for an undefined year,
+					// or maybe i don't understand fully how go works😅
+					assert.Assert(t, checkRun.GetCompletedAt().Year() == 0o001)
+				}
+				assert.Equal(t, checkRun.GetStatus(), tt.args.status)
+				assert.Equal(t, checkRun.GetConclusion(), tt.args.conclusion)
+				assert.Equal(t, checkRun.Output.GetText(), tt.args.text)
+				assert.Equal(t, checkRun.GetDetailsURL(), tt.args.detailsURL)
+				assert.Assert(t, strings.Contains(checkRun.Output.GetTitle(), tt.args.titleSubstr))
+				_, err = fmt.Fprintf(rw, `{"id": %d}`, resultid)
+				assert.NilError(t, err)
+			})
+
+			status := webvcs.StatusOpts{
+				Status:     tt.args.status,
+				Conclusion: tt.args.conclusion,
+				Text:       tt.args.text,
+				DetailsURL: tt.args.detailsURL,
+			}
+			pacopts := info.PacOpts{
+				LogURL: "https://log",
+			}
+			if !tt.notoken {
+				pacopts.VCSToken = "hello"
+				pacopts.VCSAPIURL = "moto"
+			}
+
+			err := gcvs.CreateStatus(ctx, &tt.args.runevent, pacopts, status)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("GithubVCS.CreateStatus() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+		})
+	}
+}
+
+func TestVCS_createStatusCommit(t *testing.T) {
+	issuenumber := 666
+	anevent := &info.Event{
+		Event:      &github.PullRequestEvent{PullRequest: &github.PullRequest{Number: github.Int(issuenumber)}},
+		Owner:      "owner",
+		Repository: "repository",
+		SHA:        "createStatusCommitSHA",
+		EventType:  "pull_request",
+	}
+	tests := []struct {
+		name               string
+		event              *info.Event
+		wantErr            bool
+		status             webvcs.StatusOpts
+		expectedConclusion string
+	}{
+		{
+			name:  "completed",
+			event: anevent,
+			status: webvcs.StatusOpts{
+				Status:     "completed",
+				Summary:    "I just wanna say",
+				Text:       "Finito amigo",
+				Conclusion: "completed",
+			},
+			expectedConclusion: "completed",
+		},
+		{
+			name:  "in_progress",
+			event: anevent,
+			status: webvcs.StatusOpts{
+				Status: "in_progress",
+			},
+			expectedConclusion: "pending",
+		},
+		{
+			name:  "pull_request status skipped",
+			event: anevent,
+			status: webvcs.StatusOpts{
+				Conclusion: "skipped",
+			},
+			expectedConclusion: "success",
+		},
+		{
+			name:  "pull_request status neutral",
+			event: anevent,
+			status: webvcs.StatusOpts{
+				Conclusion: "neutral",
+			},
+			expectedConclusion: "success",
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			fakeclient, mux, _, teardown := ghtesthelper.SetupGH()
+			defer teardown()
+			mux.HandleFunc(fmt.Sprintf("/repos/%s/%s/statuses/%s",
+				tt.event.Owner, tt.event.Repository, tt.event.SHA), func(rw http.ResponseWriter, r *http.Request) {
+				body, _ := ioutil.ReadAll(r.Body)
+				assert.Check(t, strings.Contains(string(body), fmt.Sprintf(`"state":"%s"`, tt.expectedConclusion)))
+			})
+			if tt.status.Status == "completed" {
+				mux.HandleFunc(fmt.Sprintf("/repos/%s/%s/issues/%d/comments",
+					tt.event.Owner, tt.event.Repository, issuenumber), func(rw http.ResponseWriter, r *http.Request) {
+					body, _ := ioutil.ReadAll(r.Body)
+					assert.Equal(t, fmt.Sprintf(`{"body":"%s<br>%s"}`, tt.status.Summary, tt.status.Text)+"\n", string(body))
+				})
+			}
+
+			ctx, _ := rtesting.SetupFakeContext(t)
+			gvcs := &VCS{
+				Client: fakeclient,
+			}
+
+			pacopts := info.PacOpts{}
+			if err := gvcs.createStatusCommit(ctx, tt.event, pacopts, tt.status); (err != nil) != tt.wantErr {
+				t.Errorf("GetCommitInfo() error = %v, wantErr %v", err, tt.wantErr)
+			}
+		})
+	}
+}
diff --git a/test/config_maxkeepruns_test.go b/test/config_maxkeepruns_test.go
index 6f78095a..57a80a86 100644
--- a/test/config_maxkeepruns_test.go
+++ b/test/config_maxkeepruns_test.go
@@ -23,7 +23,7 @@ import (
 func TestMaxKeepRuns(t *testing.T) {
 	targetNS := names.SimpleNameGenerator.RestrictLengthWithRandomSuffix("pac-e2e-ns")
 	ctx := context.Background()
-	runcnx, opts, ghcnx, err := setup(ctx)
+	runcnx, opts, ghcnx, err := setup(ctx, false)
 	assert.NilError(t, err)
 	maxKepRuns := 1
 
@@ -43,8 +43,10 @@ func TestMaxKeepRuns(t *testing.T) {
 			Branch:    mainBranch,
 		},
 	}
+	err = trepo.CreateNS(ctx, targetNS, runcnx)
+	assert.NilError(t, err)
 
-	err = trepo.CreateNSRepo(ctx, targetNS, runcnx, repository)
+	err = trepo.CreateRepo(ctx, targetNS, runcnx, repository)
 	assert.NilError(t, err)
 
 	for prRun := 1; prRun <= maxKepRuns+1; prRun++ {
diff --git a/test/init_test.go b/test/init_test.go
index 55d37878..32243970 100644
--- a/test/init_test.go
+++ b/test/init_test.go
@@ -26,7 +26,8 @@ var (
 )
 
 type E2EOptions struct {
-	Repo, Owner string
+	Repo, Owner   string
+	DirectWebhook bool
 }
 
 func tearDown(ctx context.Context, t *testing.T, runcnx *params.Run, ghvcs github.VCS,
@@ -55,31 +56,42 @@ func tearDown(ctx context.Context, t *testing.T, runcnx *params.Run, ghvcs githu
 	}
 }
 
-func setup(ctx context.Context) (*params.Run, E2EOptions, github.VCS, error) {
+func setup(ctx context.Context, viaDirectWebhook bool) (*params.Run, E2EOptions, github.VCS, error) {
 	githubURL := os.Getenv("TEST_GITHUB_API_URL")
 	githubToken := os.Getenv("TEST_GITHUB_TOKEN")
-	githubRepoOwner := os.Getenv("TEST_GITHUB_REPO_OWNER")
+	githubRepoOwnerGithubApp := os.Getenv("TEST_GITHUB_REPO_OWNER_GITHUBAPP")
+	githubRepoOwnerDirectWebhook := os.Getenv("TEST_GITHUB_REPO_OWNER_WEBHOOK")
 
 	for _, value := range []string{
 		"EL_URL", "GITHUB_API_URL", "GITHUB_TOKEN",
-		"GITHUB_REPO_OWNER", "EL_WEBHOOK_SECRET",
+		"GITHUB_REPO_OWNER_WEBHOOK", "GITHUB_REPO_OWNER_GITHUBAPP", "EL_WEBHOOK_SECRET",
 	} {
 		if env := os.Getenv("TEST_" + value); env == "" {
 			return nil, E2EOptions{}, github.VCS{}, fmt.Errorf("\"TEST_%s\" env variable is required, cannot continue", value)
 		}
 	}
 
-	if githubURL == "" || githubToken == "" || githubRepoOwner == "" {
-		return nil, E2EOptions{}, github.VCS{}, fmt.Errorf("TEST_GITHUB_API_URL TEST_GITHUB_TOKEN TEST_GITHUB_REPO_OWNER need to be set")
+	var splitted []string
+	if !viaDirectWebhook {
+		if githubURL == "" || githubToken == "" || githubRepoOwnerGithubApp == "" {
+			return nil, E2EOptions{}, github.VCS{}, fmt.Errorf("TEST_GITHUB_API_URL TEST_GITHUB_TOKEN TEST_GITHUB_REPO_OWNER_GITHUBAPP need to be set")
+		}
+		splitted = strings.Split(githubRepoOwnerGithubApp, "/")
+	}
+	if viaDirectWebhook {
+		if githubURL == "" || githubToken == "" || githubRepoOwnerDirectWebhook == "" {
+			return nil, E2EOptions{}, github.VCS{}, fmt.Errorf("TEST_GITHUB_API_URL TEST_GITHUB_TOKEN TEST_GITHUB_REPO_OWNER_WEBHOOK need to be set")
+		}
+		splitted = strings.Split(githubRepoOwnerDirectWebhook, "/")
 	}
 
-	splitted := strings.Split(githubRepoOwner, "/")
 	run := &params.Run{}
 	if err := run.Clients.NewClients(&run.Info); err != nil {
 		return nil, E2EOptions{}, github.VCS{}, err
 	}
-	e2eoptions := E2EOptions{Owner: splitted[0], Repo: splitted[1]}
-	gvcs := github.NewGithubVCS(ctx, info.PacOpts{
+	e2eoptions := E2EOptions{Owner: splitted[0], Repo: splitted[1], DirectWebhook: viaDirectWebhook}
+	gvcs := github.VCS{}
+	gvcs.SetClient(ctx, info.PacOpts{
 		VCSToken:  githubToken,
 		VCSAPIURL: githubURL,
 	})
diff --git a/test/paccli_test.go b/test/paccli_test.go
index 10c6483b..01217c8b 100644
--- a/test/paccli_test.go
+++ b/test/paccli_test.go
@@ -38,7 +38,7 @@ func execCommand(runcnx *params.Run, cmd func(*params.Run, *ui.IOStreams) *cobra
 func TestPacCli(t *testing.T) {
 	targetNS := names.SimpleNameGenerator.RestrictLengthWithRandomSuffix("pac-e2e-ns")
 	ctx := context.Background()
-	runcnx, opts, ghvcs, err := setup(ctx)
+	runcnx, opts, ghvcs, err := setup(ctx, false)
 	assert.NilError(t, err)
 
 	entries := map[string]string{
@@ -81,7 +81,10 @@ spec:
 		},
 	}
 
-	err = trepo.CreateNSRepo(ctx, targetNS, runcnx, repository)
+	err = trepo.CreateNS(ctx, targetNS, runcnx)
+	assert.NilError(t, err)
+
+	err = trepo.CreateRepo(ctx, targetNS, runcnx, repository)
 	assert.NilError(t, err)
 
 	output, err := execCommand(runcnx, repositorycmd.DescribeCommand, "-n", targetNS, targetNS)
diff --git a/test/pkg/repository/create.go b/test/pkg/repository/create.go
index 2ea0297f..040c46f6 100644
--- a/test/pkg/repository/create.go
+++ b/test/pkg/repository/create.go
@@ -9,13 +9,14 @@ import (
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
-func CreateNSRepo(ctx context.Context, targetNS string, cs *params.Run,
-	repository *pacv1alpha1.Repository) error {
+func CreateNS(ctx context.Context, targetNS string, cs *params.Run) error {
 	nsSpec := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: targetNS}}
 	_, err := cs.Clients.Kube.CoreV1().Namespaces().Create(ctx, nsSpec, metav1.CreateOptions{})
-	if err != nil {
-		return err
-	}
+	return err
+}
+
+func CreateRepo(ctx context.Context, targetNS string, cs *params.Run,
+	repository *pacv1alpha1.Repository) error {
 	repo, err := cs.Clients.PipelineAsCode.PipelinesascodeV1alpha1().Repositories(targetNS).Create(ctx, repository, metav1.CreateOptions{})
 	if err != nil {
 		return err
diff --git a/test/pullrequest_oktotest_test.go b/test/pullrequest_oktotest_test.go
index b567ddc2..28264858 100644
--- a/test/pullrequest_oktotest_test.go
+++ b/test/pullrequest_oktotest_test.go
@@ -27,7 +27,7 @@ import (
 func TestPullRequestOkToTest(t *testing.T) {
 	targetNS := names.SimpleNameGenerator.RestrictLengthWithRandomSuffix("pac-e2e-ns")
 	ctx := context.Background()
-	runcnx, opts, ghcnx, err := setup(ctx)
+	runcnx, opts, ghcnx, err := setup(ctx, false)
 	assert.NilError(t, err)
 
 	entries := map[string]string{
@@ -69,8 +69,10 @@ spec:
 			Branch:    mainBranch,
 		},
 	}
+	err = trepo.CreateNS(ctx, targetNS, runcnx)
+	assert.NilError(t, err)
 
-	err = trepo.CreateNSRepo(ctx, targetNS, runcnx, repository)
+	err = trepo.CreateRepo(ctx, targetNS, runcnx, repository)
 	assert.NilError(t, err)
 
 	targetRefName := fmt.Sprintf("refs/heads/%s",
diff --git a/test/pullrequest_privaterepository_test.go b/test/pullrequest_privaterepository_test.go
index 18d267c0..264cd42f 100644
--- a/test/pullrequest_privaterepository_test.go
+++ b/test/pullrequest_privaterepository_test.go
@@ -6,101 +6,63 @@ package test
 import (
 	"context"
 	"fmt"
-	"io/ioutil"
-	"net/http"
-	"path/filepath"
 	"testing"
 
-	pacv1alpha1 "github.com/openshift-pipelines/pipelines-as-code/pkg/apis/pipelinesascode/v1alpha1"
 	tgithub "github.com/openshift-pipelines/pipelines-as-code/test/pkg/github"
-	trepo "github.com/openshift-pipelines/pipelines-as-code/test/pkg/repository"
 	twait "github.com/openshift-pipelines/pipelines-as-code/test/pkg/wait"
 	"github.com/tektoncd/pipeline/pkg/names"
 	"gotest.tools/v3/assert"
-	corev1 "k8s.io/api/core/v1"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
 func TestPullRequestPrivateRepository(t *testing.T) {
-	targetNS := names.SimpleNameGenerator.RestrictLengthWithRandomSuffix("pac-e2e-ns")
-	ctx := context.Background()
-	runcnx, opts, ghcnx, err := setup(ctx)
-	assert.NilError(t, err)
-
-	prun, err := ioutil.ReadFile("testdata/pipelinerun_git_clone_private.yaml")
-	assert.NilError(t, err)
-
-	entries := map[string]string{
-		".tekton/info.yaml": fmt.Sprintf(string(prun), targetNS, mainBranch, pullRequestEvent),
+	for _, onWebhook := range []bool{false, true} {
+		targetNS := names.SimpleNameGenerator.RestrictLengthWithRandomSuffix("pac-e2e-ns")
+		ctx := context.Background()
+		runcnx, opts, ghcnx, err := setup(ctx, onWebhook)
+		assert.NilError(t, err)
+		if onWebhook {
+			runcnx.Clients.Log.Info("Testing with Direct Webhook integration")
+		} else {
+			runcnx.Clients.Log.Info("Testing with Github APPS integration")
+		}
+		entries, err := getEntries("testdata/pipelinerun_git_clone_private.yaml", targetNS, mainBranch, pullRequestEvent)
+		assert.NilError(t, err)
+
+		repoinfo, err := createRepoCRD(ctx, t, ghcnx, runcnx, opts, targetNS, pullRequestEvent, mainBranch, runcnx)
+		assert.NilError(t, err)
+
+		targetRefName := fmt.Sprintf("refs/heads/%s",
+			names.SimpleNameGenerator.RestrictLengthWithRandomSuffix("pac-e2e-test"))
+
+		title := "TestPullRequestPrivateRepository "
+		if onWebhook {
+			title += "OnWebhook"
+		}
+		title += "- " + targetRefName
+
+		sha, err := tgithub.PushFilesToRef(ctx, ghcnx.Client, title, repoinfo.GetDefaultBranch(), targetRefName,
+			opts.Owner, opts.Repo, entries)
+		assert.NilError(t, err)
+		runcnx.Clients.Log.Infof("Commit %s has been created and pushed to %s", sha, targetRefName)
+
+		number, err := tgithub.PRCreate(ctx, runcnx, ghcnx, opts.Owner, opts.Repo, targetRefName, repoinfo.GetDefaultBranch(), title)
+		assert.NilError(t, err)
+
+		defer tearDown(ctx, t, runcnx, ghcnx, number, targetRefName, targetNS, opts)
+
+		runcnx.Clients.Log.Infof("Waiting for Repository to be updated")
+		waitOpts := twait.Opts{
+			RepoName:        targetNS,
+			Namespace:       targetNS,
+			MinNumberStatus: 0,
+			PollTimeout:     defaultTimeout,
+			TargetSHA:       sha,
+		}
+		err = twait.UntilRepositoryUpdated(ctx, runcnx.Clients, waitOpts)
+		assert.NilError(t, err)
+
+		checkSuccess(ctx, t, runcnx, opts, pullRequestEvent, targetNS, sha, title)
 	}
-
-	repoinfo, resp, err := ghcnx.Client.Repositories.Get(ctx, opts.Owner, opts.Repo)
-	assert.NilError(t, err)
-	if resp != nil && resp.Response.StatusCode == http.StatusNotFound {
-		t.Errorf("Repository %s not found in %s", opts.Owner, opts.Repo)
-	}
-
-	repository := &pacv1alpha1.Repository{
-		ObjectMeta: metav1.ObjectMeta{
-			Name: targetNS,
-		},
-		Spec: pacv1alpha1.RepositorySpec{
-			Namespace: targetNS,
-			URL:       repoinfo.GetHTMLURL(),
-			EventType: pullRequestEvent,
-			Branch:    mainBranch,
-		},
-	}
-
-	err = trepo.CreateNSRepo(ctx, targetNS, runcnx, repository)
-	assert.NilError(t, err)
-
-	targetRefName := fmt.Sprintf("refs/heads/%s",
-		names.SimpleNameGenerator.RestrictLengthWithRandomSuffix("pac-e2e-test"))
-
-	sha, err := tgithub.PushFilesToRef(ctx, ghcnx.Client, "TestPullRequestPrivateRepository - "+targetRefName, repoinfo.GetDefaultBranch(), targetRefName, opts.Owner, opts.Repo, entries)
-	assert.NilError(t, err)
-	runcnx.Clients.Log.Infof("Commit %s has been created and pushed to %s", sha, targetRefName)
-
-	title := "TestPullRequestPrivateRepository - " + targetRefName
-	number, err := tgithub.PRCreate(ctx, runcnx, ghcnx, opts.Owner, opts.Repo, targetRefName, repoinfo.GetDefaultBranch(), title)
-	assert.NilError(t, err)
-
-	defer tearDown(ctx, t, runcnx, ghcnx, number, targetRefName, targetNS, opts)
-
-	runcnx.Clients.Log.Infof("Waiting for Repository to be updated")
-	waitOpts := twait.Opts{
-		RepoName:        targetNS,
-		Namespace:       targetNS,
-		MinNumberStatus: 0,
-		PollTimeout:     defaultTimeout,
-		TargetSHA:       sha,
-	}
-	err = twait.UntilRepositoryUpdated(ctx, runcnx.Clients, waitOpts)
-	assert.NilError(t, err)
-
-	runcnx.Clients.Log.Infof("Check if we have the repository set as succeeded")
-	repo, err := runcnx.Clients.PipelineAsCode.PipelinesascodeV1alpha1().Repositories(targetNS).Get(ctx, targetNS, metav1.GetOptions{})
-	assert.NilError(t, err)
-	laststatus := repo.Status[len(repo.Status)-1]
-	assert.Equal(t, corev1.ConditionTrue, laststatus.Conditions[0].Status)
-	assert.Equal(t, sha, *laststatus.SHA)
-	assert.Equal(t, sha, filepath.Base(*laststatus.SHAURL))
-	assert.Equal(t, title, *laststatus.Title)
-	assert.Assert(t, *laststatus.LogURL != "")
-
-	pr, err := runcnx.Clients.Tekton.TektonV1beta1().PipelineRuns(targetNS).Get(ctx, laststatus.PipelineRunName, metav1.GetOptions{})
-	assert.NilError(t, err)
-
-	assert.Equal(t, "pull_request", pr.Labels["pipelinesascode.tekton.dev/event-type"])
-	assert.Equal(t, repo.GetName(), pr.Labels["pipelinesascode.tekton.dev/repository"])
-	assert.Equal(t, opts.Owner, pr.Labels["pipelinesascode.tekton.dev/sender"])
-	assert.Equal(t, sha, pr.Labels["pipelinesascode.tekton.dev/sha"])
-	assert.Equal(t, opts.Owner, pr.Labels["pipelinesascode.tekton.dev/url-org"])
-	assert.Equal(t, opts.Repo, pr.Labels["pipelinesascode.tekton.dev/url-repository"])
-
-	assert.Equal(t, sha, filepath.Base(pr.Annotations["pipelinesascode.tekton.dev/sha-url"]))
-	assert.Equal(t, title, pr.Annotations["pipelinesascode.tekton.dev/sha-title"])
 }
 
 // Local Variables:
diff --git a/test/pullrequest_remote_annotations_test.go b/test/pullrequest_remote_annotations_test.go
index b453b1e6..d6d51eac 100644
--- a/test/pullrequest_remote_annotations_test.go
+++ b/test/pullrequest_remote_annotations_test.go
@@ -24,7 +24,7 @@ import (
 func TestPullRequestRemoteAnnotations(t *testing.T) {
 	targetNS := names.SimpleNameGenerator.RestrictLengthWithRandomSuffix("pac-e2e-ns")
 	ctx := context.Background()
-	runcnx, opts, ghcnx, err := setup(ctx)
+	runcnx, opts, ghcnx, err := setup(ctx, false)
 	assert.NilError(t, err)
 
 	prun, err := ioutil.ReadFile("testdata/pipelinerun_remote_annotations.yaml")
@@ -61,7 +61,10 @@ func TestPullRequestRemoteAnnotations(t *testing.T) {
 		},
 	}
 
-	err = trepo.CreateNSRepo(ctx, targetNS, runcnx, repository)
+	err = trepo.CreateNS(ctx, targetNS, runcnx)
+	assert.NilError(t, err)
+
+	err = trepo.CreateRepo(ctx, targetNS, runcnx, repository)
 	assert.NilError(t, err)
 
 	targetRefName := fmt.Sprintf("refs/heads/%s",
diff --git a/test/pullrequest_rerequest_test.go b/test/pullrequest_rerequest_test.go
index 8154af0f..bf4de66a 100644
--- a/test/pullrequest_rerequest_test.go
+++ b/test/pullrequest_rerequest_test.go
@@ -27,7 +27,7 @@ import (
 func TestPullRerequest(t *testing.T) {
 	targetNS := names.SimpleNameGenerator.RestrictLengthWithRandomSuffix("pac-e2e-ns")
 	ctx := context.Background()
-	runcnx, opts, ghcnx, err := setup(ctx)
+	runcnx, opts, ghcnx, err := setup(ctx, false)
 	assert.NilError(t, err)
 
 	entries := map[string]string{
@@ -70,7 +70,10 @@ spec:
 		},
 	}
 
-	err = trepo.CreateNSRepo(ctx, targetNS, runcnx, repository)
+	err = trepo.CreateNS(ctx, targetNS, runcnx)
+	assert.NilError(t, err)
+
+	err = trepo.CreateRepo(ctx, targetNS, runcnx, repository)
 	assert.NilError(t, err)
 
 	targetRefName := fmt.Sprintf("refs/heads/%s",
diff --git a/test/pullrequest_retest_test.go b/test/pullrequest_retest_test.go
index 0ff25924..51eb638e 100644
--- a/test/pullrequest_retest_test.go
+++ b/test/pullrequest_retest_test.go
@@ -23,7 +23,7 @@ import (
 func TestPullRequestRetest(t *testing.T) {
 	targetNS := names.SimpleNameGenerator.RestrictLengthWithRandomSuffix("pac-e2e-ns")
 	ctx := context.Background()
-	runcnx, opts, ghcnx, err := setup(ctx)
+	runcnx, opts, ghcnx, err := setup(ctx, false)
 	assert.NilError(t, err)
 
 	entries := map[string]string{
@@ -66,7 +66,10 @@ spec:
 		},
 	}
 
-	err = trepo.CreateNSRepo(ctx, targetNS, runcnx, repository)
+	err = trepo.CreateNS(ctx, targetNS, runcnx)
+	assert.NilError(t, err)
+
+	err = trepo.CreateRepo(ctx, targetNS, runcnx, repository)
 	assert.NilError(t, err)
 
 	targetRefName := fmt.Sprintf("refs/heads/%s",
diff --git a/test/pullrequest_test.go b/test/pullrequest_test.go
index 4a634e74..bd3d4aa5 100644
--- a/test/pullrequest_test.go
+++ b/test/pullrequest_test.go
@@ -6,11 +6,16 @@ package test
 import (
 	"context"
 	"fmt"
+	"io/ioutil"
 	"net/http"
+	"os"
 	"path/filepath"
 	"testing"
 
+	ghlib "github.com/google/go-github/v35/github"
 	pacv1alpha1 "github.com/openshift-pipelines/pipelines-as-code/pkg/apis/pipelinesascode/v1alpha1"
+	"github.com/openshift-pipelines/pipelines-as-code/pkg/params"
+	"github.com/openshift-pipelines/pipelines-as-code/pkg/webvcs/github"
 	tgithub "github.com/openshift-pipelines/pipelines-as-code/test/pkg/github"
 	trepo "github.com/openshift-pipelines/pipelines-as-code/test/pkg/repository"
 	twait "github.com/openshift-pipelines/pipelines-as-code/test/pkg/wait"
@@ -21,67 +26,48 @@ import (
 )
 
 func TestPullRequest(t *testing.T) {
-	targetNS := names.SimpleNameGenerator.RestrictLengthWithRandomSuffix("pac-e2e-ns")
-	ctx := context.Background()
-	runcnx, opts, ghvcs, err := setup(ctx)
-	assert.NilError(t, err)
+	for _, onWebhook := range []bool{false, true} {
+		targetNS := names.SimpleNameGenerator.RestrictLengthWithRandomSuffix("pac-e2e-ns")
+		ctx := context.Background()
 
-	entries := map[string]string{
-		".tekton/info.yaml": fmt.Sprintf(`---
-apiVersion: tekton.dev/v1beta1
-kind: PipelineRun
-metadata:
-  name: pipeline
-  annotations:
-    pipelinesascode.tekton.dev/target-namespace: "%s"
-    pipelinesascode.tekton.dev/on-target-branch: "[%s]"
-    pipelinesascode.tekton.dev/on-event: "[%s]"
-spec:
-  pipelineSpec:
-    tasks:
-      - name: task
-        taskSpec:
-          steps:
-            - name: task
-              image: gcr.io/google-containers/busybox
-              command: ["/bin/echo", "HELLOMOTO"]
-`, targetNS, mainBranch, pullRequestEvent),
-	}
+		runcnx, opts, ghvcs, err := setup(ctx, onWebhook)
+		assert.NilError(t, err)
+		if onWebhook {
+			runcnx.Clients.Log.Info("Testing with Direct Webhook integration")
+		} else {
+			runcnx.Clients.Log.Info("Testing with Github APPS integration")
+		}
 
-	repoinfo, resp, err := ghvcs.Client.Repositories.Get(ctx, opts.Owner, opts.Repo)
-	assert.NilError(t, err)
-	if resp != nil && resp.Response.StatusCode == http.StatusNotFound {
-		t.Errorf("Repository %s not found in %s", opts.Owner, opts.Repo)
-	}
+		repoinfo, err := createRepoCRD(ctx, t, ghvcs, runcnx, opts, targetNS, pullRequestEvent, mainBranch, runcnx)
+		assert.NilError(t, err)
 
-	repository := &pacv1alpha1.Repository{
-		ObjectMeta: metav1.ObjectMeta{
-			Name: targetNS,
-		},
-		Spec: pacv1alpha1.RepositorySpec{
-			Namespace: targetNS,
-			URL:       repoinfo.GetHTMLURL(),
-			EventType: pullRequestEvent,
-			Branch:    mainBranch,
-		},
-	}
+		entries, err := getEntries("testdata/pipelinerun.yaml", targetNS, mainBranch, pullRequestEvent)
+		assert.NilError(t, err)
 
-	err = trepo.CreateNSRepo(ctx, targetNS, runcnx, repository)
-	assert.NilError(t, err)
+		targetRefName := fmt.Sprintf("refs/heads/%s",
+			names.SimpleNameGenerator.RestrictLengthWithRandomSuffix("pac-e2e-test"))
 
-	targetRefName := fmt.Sprintf("refs/heads/%s",
-		names.SimpleNameGenerator.RestrictLengthWithRandomSuffix("pac-e2e-test"))
+		title := "TestPullRequest "
+		if onWebhook {
+			title += "OnWebhook"
+		}
+		title += "- " + targetRefName
 
-	sha, err := tgithub.PushFilesToRef(ctx, ghvcs.Client, "TestPullRequest - "+targetRefName, repoinfo.GetDefaultBranch(), targetRefName, opts.Owner, opts.Repo, entries)
-	assert.NilError(t, err)
-	runcnx.Clients.Log.Infof("Commit %s has been created and pushed to %s", sha, targetRefName)
+		sha, err := tgithub.PushFilesToRef(ctx, ghvcs.Client, title, repoinfo.GetDefaultBranch(), targetRefName,
+			opts.Owner, opts.Repo, entries)
+		assert.NilError(t, err)
+		runcnx.Clients.Log.Infof("Commit %s has been created and pushed to %s", sha, targetRefName)
 
-	title := "TestPullRequest - " + targetRefName
-	number, err := tgithub.PRCreate(ctx, runcnx, ghvcs, opts.Owner, opts.Repo, targetRefName, repoinfo.GetDefaultBranch(), title)
-	assert.NilError(t, err)
+		number, err := tgithub.PRCreate(ctx, runcnx, ghvcs, opts.Owner, opts.Repo, targetRefName, repoinfo.GetDefaultBranch(), title)
+		assert.NilError(t, err)
+
+		defer tearDown(ctx, t, runcnx, ghvcs, number, targetRefName, targetNS, opts)
 
-	defer tearDown(ctx, t, runcnx, ghvcs, number, targetRefName, targetNS, opts)
+		checkSuccess(ctx, t, runcnx, opts, pullRequestEvent, targetNS, sha, title)
+	}
+}
 
+func checkSuccess(ctx context.Context, t *testing.T, runcnx *params.Run, opts E2EOptions, onEvent, targetNS, sha, title string) {
 	runcnx.Clients.Log.Infof("Waiting for Repository to be updated")
 	waitOpts := twait.Opts{
 		RepoName:        targetNS,
@@ -90,7 +76,7 @@ spec:
 		PollTimeout:     defaultTimeout,
 		TargetSHA:       sha,
 	}
-	err = twait.UntilRepositoryUpdated(ctx, runcnx.Clients, waitOpts)
+	err := twait.UntilRepositoryUpdated(ctx, runcnx.Clients, waitOpts)
 	assert.NilError(t, err)
 
 	runcnx.Clients.Log.Infof("Check if we have the repository set as succeeded")
@@ -106,7 +92,7 @@ spec:
 	pr, err := runcnx.Clients.Tekton.TektonV1beta1().PipelineRuns(targetNS).Get(ctx, laststatus.PipelineRunName, metav1.GetOptions{})
 	assert.NilError(t, err)
 
-	assert.Equal(t, "pull_request", pr.Labels["pipelinesascode.tekton.dev/event-type"])
+	assert.Equal(t, onEvent, pr.Labels["pipelinesascode.tekton.dev/event-type"])
 	assert.Equal(t, repo.GetName(), pr.Labels["pipelinesascode.tekton.dev/repository"])
 	assert.Equal(t, opts.Owner, pr.Labels["pipelinesascode.tekton.dev/sender"])
 	assert.Equal(t, sha, pr.Labels["pipelinesascode.tekton.dev/sha"])
@@ -117,6 +103,68 @@ spec:
 	assert.Equal(t, title, pr.Annotations["pipelinesascode.tekton.dev/sha-title"])
 }
 
+func createSecret(ctx context.Context, runcnx *params.Run, secretData map[string]string, targetNamespace,
+	secretName string) error {
+	secret := &corev1.Secret{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:   secretName,
+			Labels: map[string]string{"app.kubernetes.io/managed-by": "pipelines-as-code"},
+		},
+	}
+	secret.StringData = secretData
+	_, err := runcnx.Clients.Kube.CoreV1().Secrets(targetNamespace).Create(ctx, secret, metav1.CreateOptions{})
+	return err
+}
+
+func createRepoCRD(ctx context.Context, t *testing.T, ghvcs github.VCS, run *params.Run, opts E2EOptions,
+	targetNS, targetEvent, targetBranch string, runcnx *params.Run) (*ghlib.Repository, error) {
+	repoinfo, resp, err := ghvcs.Client.Repositories.Get(ctx, opts.Owner, opts.Repo)
+	assert.NilError(t, err)
+
+	if resp != nil && resp.Response.StatusCode == http.StatusNotFound {
+		t.Errorf("Repository %s not found in %s", opts.Owner, opts.Repo)
+	}
+
+	repository := &pacv1alpha1.Repository{
+		ObjectMeta: metav1.ObjectMeta{
+			Name: targetNS,
+		},
+		Spec: pacv1alpha1.RepositorySpec{
+			Namespace: targetNS,
+			URL:       repoinfo.GetHTMLURL(),
+			EventType: targetEvent,
+			Branch:    targetBranch,
+		},
+	}
+
+	err = trepo.CreateNS(ctx, targetNS, runcnx)
+	assert.NilError(t, err)
+
+	if opts.DirectWebhook {
+		token, _ := os.LookupEnv("TEST_GITHUB_TOKEN")
+		apiURL, _ := os.LookupEnv("TEST_GITHUB_API_URL")
+		err := createSecret(ctx, run, map[string]string{"token": token}, targetNS, "webhook-token")
+		assert.NilError(t, err)
+		repository.Spec.WebvcsAPIURL = apiURL
+		repository.Spec.WebvcsSecret = &pacv1alpha1.WebvcsSecretSpec{Name: "webhook-token", Key: "token"}
+	}
+
+	err = trepo.CreateRepo(ctx, targetNS, runcnx, repository)
+	assert.NilError(t, err)
+	return repoinfo, err
+}
+
+func getEntries(yamlfile, targetNS, targetBranch, targetEvent string) (map[string]string, error) {
+	prun, err := ioutil.ReadFile(yamlfile)
+	if err != nil {
+		return nil, err
+	}
+
+	return map[string]string{
+		".tekton/pr.yaml": fmt.Sprintf(string(prun), targetNS, targetBranch, targetEvent),
+	}, nil
+}
+
 // Local Variables:
 // compile-command: "go test -tags=e2e -v -info TestPullRequest$ ."
 // End:
diff --git a/test/push_test.go b/test/push_test.go
index 4000402f..8512ea72 100644
--- a/test/push_test.go
+++ b/test/push_test.go
@@ -6,12 +6,9 @@ package test
 import (
 	"context"
 	"fmt"
-	"net/http"
 	"testing"
 
-	pacv1alpha1 "github.com/openshift-pipelines/pipelines-as-code/pkg/apis/pipelinesascode/v1alpha1"
 	tgithub "github.com/openshift-pipelines/pipelines-as-code/test/pkg/github"
-	trepo "github.com/openshift-pipelines/pipelines-as-code/test/pkg/repository"
 	twait "github.com/openshift-pipelines/pipelines-as-code/test/pkg/wait"
 	"github.com/tektoncd/pipeline/pkg/names"
 	"gotest.tools/v3/assert"
@@ -20,76 +17,53 @@ import (
 )
 
 func TestPush(t *testing.T) {
-	targetNS := names.SimpleNameGenerator.RestrictLengthWithRandomSuffix("pac-e2e-push")
-	targetBranch := targetNS
-	targetEvent := "push"
+	for _, onWebhook := range []bool{false, true} {
+		targetNS := names.SimpleNameGenerator.RestrictLengthWithRandomSuffix("pac-e2e-push")
+		targetBranch := targetNS
+		targetEvent := "push"
 
-	ctx := context.Background()
-	runcnx, opts, gvcs, err := setup(ctx)
-	assert.NilError(t, err)
+		ctx := context.Background()
+		runcnx, opts, gvcs, err := setup(ctx, onWebhook)
+		assert.NilError(t, err)
 
-	repoinfo, resp, err := gvcs.Client.Repositories.Get(ctx, opts.Owner, opts.Repo)
-	assert.NilError(t, err)
-	if resp != nil && resp.Response.StatusCode == http.StatusNotFound {
-		t.Errorf("Repository %s not found in %s", opts.Owner, opts.Repo)
-	}
+		if onWebhook {
+			runcnx.Clients.Log.Info("Testing with Direct Webhook integration")
+		} else {
+			runcnx.Clients.Log.Info("Testing with Github APPS integration")
+		}
 
-	repository := &pacv1alpha1.Repository{
-		ObjectMeta: metav1.ObjectMeta{
-			Name: targetNS,
-		},
-		Spec: pacv1alpha1.RepositorySpec{
-			Namespace: targetNS,
-			URL:       repoinfo.GetHTMLURL(),
-			EventType: targetEvent,
-			Branch:    targetBranch,
-		},
-	}
+		repoinfo, err := createRepoCRD(ctx, t, gvcs, runcnx, opts, targetNS, targetEvent, targetBranch, runcnx)
+		assert.NilError(t, err)
 
-	err = trepo.CreateNSRepo(ctx, targetNS, runcnx, repository)
-	assert.NilError(t, err)
+		entries, err := getEntries("testdata/pipelinerun-on-push.yaml", targetNS, targetBranch, targetEvent)
+		assert.NilError(t, err)
 
-	entries := map[string]string{
-		".tekton/info.yaml": fmt.Sprintf(`---
-apiVersion: tekton.dev/v1beta1
-kind: PipelineRun
-metadata:
-  name: pipeline
-  annotations:
-    pipelinesascode.tekton.dev/target-namespace: "%s"
-    pipelinesascode.tekton.dev/on-target-branch: "[%s]"
-    pipelinesascode.tekton.dev/on-event: "[%s]"
-spec:
-  pipelineSpec:
-    tasks:
-      - name: task
-        taskSpec:
-          steps:
-            - name: task
-              image: gcr.io/google-containers/busybox
-              command: ["/bin/echo", "HELLOMOTO"]
-`, targetNS, targetBranch, targetEvent),
-	}
+		title := "TestPush "
+		if onWebhook {
+			title += "OnWebhook"
+		}
+		title += "- " + targetBranch
 
-	targetRefName := fmt.Sprintf("refs/heads/%s", targetBranch)
-	sha, err := tgithub.PushFilesToRef(ctx, gvcs.Client, "TestPush - "+targetBranch, repoinfo.GetDefaultBranch(), targetRefName, opts.Owner, opts.Repo, entries)
-	runcnx.Clients.Log.Infof("Commit %s has been created and pushed to %s", sha, targetRefName)
-	assert.NilError(t, err)
-	defer tearDown(ctx, t, runcnx, gvcs, -1, targetRefName, targetNS, opts)
+		targetRefName := fmt.Sprintf("refs/heads/%s", targetBranch)
+		sha, err := tgithub.PushFilesToRef(ctx, gvcs.Client, title, repoinfo.GetDefaultBranch(), targetRefName, opts.Owner, opts.Repo, entries)
+		runcnx.Clients.Log.Infof("Commit %s has been created and pushed to %s", sha, targetRefName)
+		assert.NilError(t, err)
+		defer tearDown(ctx, t, runcnx, gvcs, -1, targetRefName, targetNS, opts)
 
-	runcnx.Clients.Log.Infof("Waiting for Repository to be updated")
-	waitOpts := twait.Opts{
-		RepoName:        targetNS,
-		Namespace:       targetNS,
-		MinNumberStatus: 0,
-		PollTimeout:     defaultTimeout,
-		TargetSHA:       sha,
-	}
-	err = twait.UntilRepositoryUpdated(ctx, runcnx.Clients, waitOpts)
-	assert.NilError(t, err)
+		runcnx.Clients.Log.Infof("Waiting for Repository to be updated")
+		waitOpts := twait.Opts{
+			RepoName:        targetNS,
+			Namespace:       targetNS,
+			MinNumberStatus: 0,
+			PollTimeout:     defaultTimeout,
+			TargetSHA:       sha,
+		}
+		err = twait.UntilRepositoryUpdated(ctx, runcnx.Clients, waitOpts)
+		assert.NilError(t, err)
 
-	runcnx.Clients.Log.Infof("Check if we have the repository set as succeeded")
-	repo, err := runcnx.Clients.PipelineAsCode.PipelinesascodeV1alpha1().Repositories(targetNS).Get(ctx, targetNS, metav1.GetOptions{})
-	assert.NilError(t, err)
-	assert.Assert(t, repo.Status[len(repo.Status)-1].Conditions[0].Status == corev1.ConditionTrue)
+		runcnx.Clients.Log.Infof("Check if we have the repository set as succeeded")
+		repo, err := runcnx.Clients.PipelineAsCode.PipelinesascodeV1alpha1().Repositories(targetNS).Get(ctx, targetNS, metav1.GetOptions{})
+		assert.NilError(t, err)
+		assert.Assert(t, repo.Status[len(repo.Status)-1].Conditions[0].Status == corev1.ConditionTrue)
+	}
 }
diff --git a/test/testdata/pipelinerun-on-push.yaml b/test/testdata/pipelinerun-on-push.yaml
new file mode 100644
index 00000000..0cfbd292
--- /dev/null
+++ b/test/testdata/pipelinerun-on-push.yaml
@@ -0,0 +1,18 @@
+---
+apiVersion: tekton.dev/v1beta1
+kind: PipelineRun
+metadata:
+  name: pipeline-on-push
+  annotations:
+    pipelinesascode.tekton.dev/target-namespace: "%s"
+    pipelinesascode.tekton.dev/on-target-branch: "[%s]"
+    pipelinesascode.tekton.dev/on-event: "[%s]"
+spec:
+  pipelineSpec:
+    tasks:
+      - name: task
+        taskSpec:
+          steps:
+            - name: task
+              image: gcr.io/google-containers/busybox
+              command: ["/bin/echo", "HELLOMOTO"]
diff --git a/test/testdata/pipelinerun.yaml b/test/testdata/pipelinerun.yaml
new file mode 100644
index 00000000..d383cad4
--- /dev/null
+++ b/test/testdata/pipelinerun.yaml
@@ -0,0 +1,18 @@
+---
+apiVersion: tekton.dev/v1beta1
+kind: PipelineRun
+metadata:
+  name: pipeline
+  annotations:
+    pipelinesascode.tekton.dev/target-namespace: "%s"
+    pipelinesascode.tekton.dev/on-target-branch: "[%s]"
+    pipelinesascode.tekton.dev/on-event: "[%s]"
+spec:
+  pipelineSpec:
+    tasks:
+      - name: task
+        taskSpec:
+          steps:
+            - name: task
+              image: gcr.io/google-containers/busybox
+              command: ["/bin/echo", "HELLOMOTO"]
\ No newline at end of file