Repository: openshift-pipelines/pipelines-as-code
Pull request #60 (2021-05-19 11:29:09)

Add support for push events.
Add support for push events

where if you have a pipelinerun with this annotation in your tekton
directory.

    pipelinesascode.tekton.dev/on-target-branch: [refs/heads/main]
    pipelinesascode.tekton.dev/on-event: [push]

Pipeline as code will run with it.

Refactor the TestRun giant test to make it more flexible to test
multiple use cases.

Signed-off-by: Chmouel Boudjnah <chmouel@redhat.com>


chmouel: Figured why the CI wasn't working, the annotations wasnt set properly.

![image](https://user-images.githubusercontent.com/98980/118808064-5762ab00-b8a9-11eb-9007-acc74cfeb8e8.png)

which make me things, how come tekton/kube is not bugging on arbitrary metadata :-D 
chmouel: There was some bugs from the previous commit, the last two commits fixes it.
chmouel: Ran the CI manually with the fixes, which made it green..


Patch:
diff --git a/.tekton/run.yaml b/.tekton/run.yaml
index 992c6cdb..d86fa47e 100644
--- a/.tekton/run.yaml
+++ b/.tekton/run.yaml
@@ -3,9 +3,10 @@ apiVersion: tekton.dev/v1beta1
 kind: PipelineRun
 metadata:
   name: pipelines-as-code-test-run
-  pipelinesascode.tekton.dev/on-target-branch: "[main]"
-  pipelinesascode.tekton.dev/on-event: "[pull_request]"
-  pipelinesascode.tekton.dev/task: "[git-clone, codecov]"
+  annotations:
+    pipelinesascode.tekton.dev/on-target-branch: "[main]"
+    pipelinesascode.tekton.dev/on-event: "[pull_request]"
+    pipelinesascode.tekton.dev/task: "[git-clone, codecov]"
 spec:
   pipelineRef:
     name: pipelines-as-code-test
diff --git a/README.md b/README.md
index 0d6fabfd..7d1fc072 100644
--- a/README.md
+++ b/README.md
@@ -110,19 +110,48 @@ on the `PipelineRun`. For example when you have these metadatas in your `Pipelin
     pipelinesascode.tekton.dev/on-event: "[pull_request]"
 ```
 
-  `Pipelines as Code` will match the piplinerun `pipeline-pr-main` if the VCS
-  events target the branch `main`.
+`Pipelines as Code` will match the piplinerun `pipeline-pr-main` if the VCS
+events target the branch `main` and it's coming from a `[pull_request]`
 
-  Multiple event or target branch can be specified separated by comma, i.e:
+Multiple target branch can be specified separated by comma, i.e:
 
-  `[main, release-nightly]`
+`[main, release-nightly]`
 
-  If there is multiple pipeline matching the event, it will match the first one.
+If there is multiple pipeline matching the event, it will match the first one.
 
-  matching annotations are currently mandated or `Pipelines asCode` will not
-  match your `PiplineRun`.
+You can match on `pull_request` events as above and on git `push` events to a
+repo.
 
-#### Pipelines asCode resolver
+For example this will match the pipeline when there is a push to a commit in
+the `main` branch :
+
+```yaml
+ metadata:
+  name: pipeline-push-on-main
+  annotations:
+    pipelinesascode.tekton.dev/on-target-branch: "[refs/heads/main]"
+    pipelinesascode.tekton.dev/on-event: "[push]"
+```
+
+
+You need to specify the refs and not just the branch, this allows to as well
+match on tags. For example :
+
+```yaml
+ metadata:
+ name: pipeline-push-on-1.0-tags
+ annotations:
+    pipelinesascode.tekton.dev/on-target-branch: "[refs/tags/1.0]"
+    pipelinesascode.tekton.dev/on-event: "[push]"
+```
+
+will match the pipeline `pipeline-push-on-1.0-tags` when you push the 1.0 tags
+into your repository.
+
+Matching annotations are currently mandated or `Pipelines as Code` will not
+match your `PiplineRun`.
+
+#### Pipelines as Code resolver
 
 If `Pipelines as Code` sees multiple documents, it tries to *resolves* it as a
   single PiplineSpec embedded to a `PipelineRun`. It will add a `generateName`
@@ -136,7 +165,7 @@ it will fails before applying the pipelinerun onto the cluster.
 
 #### Remote Task support
 
-`Pipelines asCode` support fetching remote tasks from remote location via  annotation on PipelineRun.
+`Pipelines as Code` support fetching remote tasks from remote location via  annotation on PipelineRun.
 
 If the resolver sees a PipelineRun referencing a remote task via its name in a  Pipeline or a PipelineSpec it will automatically inlines it.
 
diff --git a/config/400-triggers.yaml b/config/400-triggers.yaml
index 19ae20f9..69c7a459 100644
--- a/config/400-triggers.yaml
+++ b/config/400-triggers.yaml
@@ -10,6 +10,8 @@ metadata:
 spec:
   serviceAccountName: openshift-pipelines-ascode-sa-el
   triggers:
+
+    # When you have a /retest in a comment to reste a PR
     - name: github-issue-comment-retest
       bindings:
         - ref: openshift-pipelines-ascode-bindings
@@ -25,10 +27,29 @@ spec:
               body.action == 'created' &&
               'pull_request' in body.issue &&
               body.issue.state == 'open' &&
+              'installation' in body &&
               body.comment.body.matches('(^|\\r\\n)/retest([ ]*$|$|\\r\\n)')
       template:
         ref: openshift-pipelines-ascode-template
 
+    # Branch push
+    - name: github-branch-push
+      bindings:
+        - ref: openshift-pipelines-ascode-bindings
+      interceptors:
+        - github:
+            secretRef:
+              secretName: "github-app-secret"
+              secretKey: "webhook.secret"
+            eventTypes:
+              - push
+        - cel:
+            filter: >-
+              'installation' in body &&
+              'pusher' in body
+      template:
+        ref: openshift-pipelines-ascode-template
+
     # When using the UI and clicking on Re-run failed test
     - name: github-check-run-recheck
       bindings:
diff --git a/hack/replay-gh-events.py b/hack/replay-gh-events.py
index 4d30cba1..6fe18fb5 100755
--- a/hack/replay-gh-events.py
+++ b/hack/replay-gh-events.py
@@ -64,12 +64,16 @@ def main(args):
                      digestmod=hashlib.sha1).hexdigest()
 
     print("Replay event for repo " + jeez["repository"]["full_name"])
-    if jeez["action"] in ("opened", "synchronize") and "pull_request" in jeez:
+    if 'action' in jeez and jeez["action"] in (
+            "opened", "synchronize") and "pull_request" in jeez:
         event_type = "pull_request"
-    elif jeez["action"] in ("rerequested") and "check_run" in jeez:
+    elif 'action' in jeez and jeez[
+            "action"] == "rerequested" and "check_run" in jeez:
         event_type = "check_run"
-    elif jeez["action"] in ("created") and "issue" in jeez:
+    elif 'action' in jeez and jeez["action"] == "created" and "issue" in jeez:
         event_type = "issue_comment"
+    elif 'pusher' in jeez:
+        event_type = "push"
     else:
         raise Exception("Unknown event_type")
 
diff --git a/pkg/cmd/pipelineascode/pipelineascode.go b/pkg/cmd/pipelineascode/pipelineascode.go
index 86a4b2f8..cc5085b4 100644
--- a/pkg/cmd/pipelineascode/pipelineascode.go
+++ b/pkg/cmd/pipelineascode/pipelineascode.go
@@ -120,10 +120,12 @@ func runWrap(ctx context.Context, opts *pacpkg.Options, cs *cli.Clients, kintera
 	runinfo.WebConsoleURL = url
 
 	err = pacpkg.Run(ctx, cs, kinteract, runinfo)
-	if err != nil && runinfo.CheckRunID != nil && !strings.Contains(err.Error(), "403 Resource not accessible by integration") {
-		_, _ = cs.GithubClient.CreateStatus(ctx, runinfo, "completed", "failure",
-			fmt.Sprintf("There was an issue validating the commit: %q", err),
-			runinfo.WebConsoleURL)
+	if err != nil {
+		if runinfo.CheckRunID != nil && !strings.Contains(err.Error(), "403 Resource not accessible by integration") {
+			_, _ = cs.GithubClient.CreateStatus(ctx, runinfo, "completed", "failure",
+				fmt.Sprintf("There was an issue validating the commit: %q", err),
+				runinfo.WebConsoleURL)
+		}
 	}
 	return err
 }
diff --git a/pkg/config/annotation_matcher.go b/pkg/config/annotation_matcher.go
index 790dda1c..2757bcd5 100644
--- a/pkg/config/annotation_matcher.go
+++ b/pkg/config/annotation_matcher.go
@@ -24,12 +24,18 @@ func getAnnotationValues(annotation string) ([]string, error) {
 	if !match {
 		return nil, errors.New("annotations in pipeline are in wrong format")
 	}
+
+	// Split all tasks by comma and make sure to trim spaces in there
 	splitted := strings.Split(re.FindStringSubmatch(annotation)[1], ",")
+	for i := range splitted {
+		splitted[i] = strings.TrimSpace(splitted[i])
+	}
+
 	if splitted[0] == "" {
 		return nil, errors.New("annotations in pipeline are empty")
 	}
 
-	return strings.Split(re.FindStringSubmatch(annotation)[1], ","), nil
+	return splitted, nil
 }
 
 func MatchPipelinerunByAnnotation(pruns []*v1beta1.PipelineRun, cs *cli.Clients,
diff --git a/pkg/config/annotation_tasks_install.go b/pkg/config/annotation_tasks_install.go
index 89a925e7..682f0805 100644
--- a/pkg/config/annotation_tasks_install.go
+++ b/pkg/config/annotation_tasks_install.go
@@ -39,6 +39,8 @@ func (rt RemoteTasks) convertTotask(data string) (*tektonv1beta1.Task, error) {
 
 func (rt RemoteTasks) getTask(ctx context.Context, task string) (*tektonv1beta1.Task, error) {
 	var ret *tektonv1beta1.Task
+
+	// TODO: print a log info when getting the task from which location
 	switch {
 	case strings.HasPrefix(task, "https://"), strings.HasPrefix(task, "http://"):
 		res, err := rt.Clients.HTTPClient.Get(task)
@@ -62,15 +64,17 @@ func (rt RemoteTasks) getTask(ctx context.Context, task string) (*tektonv1beta1.
 			version = split[len(split)-1]
 			taskName = split[0]
 		}
-		resource := rt.Clients.Hub.GetResource(hub.ResourceOption{
+		resourceoption := hub.ResourceOption{
 			Name:    taskName,
 			Catalog: tektonCatalogHubName,
 			Kind:    "task",
 			Version: version,
-		})
+		}
+
+		resource := rt.Clients.Hub.GetResource(resourceoption)
 		data, err := resource.Manifest()
 		if err != nil {
-			return ret, err
+			return ret, fmt.Errorf("task %s hasn't been found: %s", taskName, err)
 		}
 		return rt.convertTotask(string(data))
 	}
diff --git a/pkg/pipelineascode/pipelineascode.go b/pkg/pipelineascode/pipelineascode.go
index f72b7e1c..f7c31459 100644
--- a/pkg/pipelineascode/pipelineascode.go
+++ b/pkg/pipelineascode/pipelineascode.go
@@ -3,6 +3,7 @@ package pipelineascode
 import (
 	"context"
 	"fmt"
+	"strings"
 
 	apipac "github.com/openshift-pipelines/pipelines-as-code/pkg/apis/pipelinesascode/v1alpha1"
 	"github.com/openshift-pipelines/pipelines-as-code/pkg/cli"
@@ -53,28 +54,46 @@ func getRepoByCR(ctx context.Context, cs *cli.Clients, url, branch, forceNamespa
 	return repository, nil
 }
 
+func createStatus(ctx context.Context, cs *cli.Clients, runinfo *webvcs.RunInfo, status, conclusion, text, detailsURL string, logit bool) error {
+	if logit {
+		cs.Log.Infof(text)
+	}
+	// Do not create status on push, there is maybe going other event type we
+	// would run CI on but that are not tight to a PR
+	if runinfo.EventType == "push" {
+		return nil
+	}
+	_, err := cs.GithubClient.CreateStatus(ctx, runinfo, status, conclusion, text, detailsURL)
+	return err
+}
+
 // Run over the main loop
 func Run(ctx context.Context, cs *cli.Clients, k8int cli.KubeInteractionIntf, runinfo *webvcs.RunInfo) error {
 	var err error
 
 	// Create first check run to let know the user we have started the pipeline
-	checkRun, err := cs.GithubClient.CreateCheckRun(ctx, "in_progress", runinfo)
-	if err != nil {
-		return err
+	// TODO: Refactor this bit in a function
+	if runinfo.EventType != "push" {
+		checkRun, err := cs.GithubClient.CreateCheckRun(ctx, "in_progress", runinfo)
+		if err != nil {
+			return err
+		}
+		// Set the runId on runInfo so if we have an error we can report it on UI (GH checks UI for GH PR)
+		runinfo.CheckRunID = checkRun.ID
 	}
-	// Set the runId on runInfo so if we have an error we can report it on UI (GH checks UI for GH PR)
-	runinfo.CheckRunID = checkRun.ID
 
 	// Check if submitted is allowed to run this.
 	allowed, err := aclCheck(ctx, cs, runinfo)
 	if err != nil {
 		return err
 	}
+
 	if !allowed {
-		_, _ = cs.GithubClient.CreateStatus(ctx, runinfo, "completed", "skipped",
-			fmt.Sprintf("User %s is not allowed to run CI on this repo.", runinfo.Sender),
-			"https://tenor.com/search/police-cat-gifs")
-		cs.Log.Infof("User %s is not allowed to run CI on this repo", runinfo.Sender)
+		msg := fmt.Sprintf("User %s is not allowed to run CI on this repo.", runinfo.Sender)
+		err = createStatus(ctx, cs, runinfo, "completed", "skipped", msg, "https://tenor.com/search/police-cat-gifs", true)
+		if err != nil {
+			return err
+		}
 		return nil
 	}
 
@@ -85,17 +104,23 @@ func Run(ctx context.Context, cs *cli.Clients, k8int cli.KubeInteractionIntf, ru
 		return err
 	}
 	if repo.Spec.Namespace == "" {
-		_, _ = cs.GithubClient.CreateStatus(ctx, runinfo, "completed", "skipped",
-			"Could not find a configuration for this repository", "https://tenor.com/search/sad-cat-gifs")
-		cs.Log.Infof("Could not find a namespace match for %s/%s on %s", runinfo.Owner, runinfo.Repository, runinfo.BaseBranch)
+		msg := fmt.Sprintf("Could not find a namespace match for %s/%s on %s", runinfo.Owner, runinfo.Repository, runinfo.BaseBranch)
+		err = createStatus(ctx, cs, runinfo, "completed", "skipped", msg, "https://tenor.com/search/sad-cat-gifs", true)
+		if err != nil {
+			return err
+		}
 		return nil
 	}
 
 	// Get everything in tekton directory
 	objects, err := cs.GithubClient.GetTektonDir(ctx, tektonDir, runinfo)
-	if err != nil {
-		_, _ = cs.GithubClient.CreateStatus(ctx, runinfo, "completed", "skipped",
-			"😿 Could not find a <b>.tekton/</b> directory for this repository", "https://tenor.com/search/sad-cat-gifs")
+	if len(objects) == 0 || err != nil {
+		msg := "😿 Could not find a <b>.tekton/</b> directory for this repository"
+		err2 := createStatus(ctx, cs, runinfo, "completed", "skipped",
+			msg, "https://tenor.com/search/sad-cat-gifs", true)
+		if err2 != nil {
+			return err
+		}
 		return err
 	}
 	cs.Log.Infow("Loading payload",
@@ -113,9 +138,9 @@ func Run(ctx context.Context, cs *cli.Clients, k8int cli.KubeInteractionIntf, ru
 	}
 
 	// Update status in UI
-	_, err = cs.GithubClient.CreateStatus(ctx, runinfo, "in_progress", "",
+	err = createStatus(ctx, cs, runinfo, "in_progress", "",
 		fmt.Sprintf("Getting pipelinerun configuration in namespace <b>%s</b>", repo.Spec.Namespace),
-		"https://tenor.com/search/sad-cat-gifs")
+		"https://tenor.com/search/sad-cat-gifs", true)
 	if err != nil {
 		return err
 	}
@@ -145,13 +170,16 @@ func Run(ctx context.Context, cs *cli.Clients, k8int cli.KubeInteractionIntf, ru
 	}
 
 	// Add labels on the soon to be created pipelinerun so UI/CLI can easily
-	// query them.
+	// 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(runinfo.BaseBranch, "/", "-")
 	pipelineRun.Labels = map[string]string{
 		"tekton.dev/pipeline-ascode-owner":      runinfo.Owner,
 		"tekton.dev/pipeline-ascode-repository": runinfo.Repository,
 		"tekton.dev/pipeline-ascode-sha":        runinfo.SHA,
 		"tekton.dev/pipeline-ascode-sender":     runinfo.Sender,
-		"tekton.dev/pipeline-ascode-branch":     runinfo.BaseBranch,
+		"tekton.dev/pipeline-ascode-branch":     refTomakeK8Happy,
 	}
 
 	// Create the actual pipeline
@@ -168,10 +196,10 @@ func Run(ctx context.Context, cs *cli.Clients, k8int cli.KubeInteractionIntf, ru
 	}
 
 	// Create status with the log url
-	_, err = cs.GithubClient.CreateStatus(ctx, runinfo, "in_progress", "",
+	err = createStatus(ctx, cs, runinfo, "in_progress", "",
 		fmt.Sprintf(`Starting Pipelinerun <b>%s</b> in namespace <b>%s</b><br><br>You can follow the execution on the command line with : <br><br><code>tkn pr logs -f -n %s %s</code>`,
 			pr.GetName(), repo.Spec.Namespace, repo.Spec.Namespace, pr.GetName()),
-		consoleURL)
+		consoleURL, false)
 	if err != nil {
 		return nil
 	}
@@ -189,7 +217,6 @@ func Run(ctx context.Context, cs *cli.Clients, k8int cli.KubeInteractionIntf, ru
 	if err != nil {
 		return err
 	}
-
 	repoStatus := apipac.RepositoryRunStatus{
 		Status:          newPr.Status.Status,
 		PipelineRunName: newPr.Name,
diff --git a/pkg/pipelineascode/status.go b/pkg/pipelineascode/status.go
index 7612d596..508e8598 100644
--- a/pkg/pipelineascode/status.go
+++ b/pkg/pipelineascode/status.go
@@ -162,9 +162,8 @@ func postFinalStatus(ctx context.Context, cs *cli.Clients, k8int cli.KubeInterac
 		return pr, err
 	}
 
-	_, err = cs.GithubClient.CreateStatus(ctx, runinfo,
-		"completed", pipelineRunStatus(pr),
-		outputBuffer.String(), consoleURL)
+	err = createStatus(ctx, cs, runinfo, "completed", pipelineRunStatus(pr),
+		outputBuffer.String(), consoleURL, false)
 
 	return pr, err
 }
diff --git a/pkg/webvcs/github.go b/pkg/webvcs/github.go
index 70d631a7..0e0ebae7 100644
--- a/pkg/webvcs/github.go
+++ b/pkg/webvcs/github.go
@@ -157,6 +157,17 @@ func (v GithubVCS) ParsePayload(ctx context.Context, log *zap.SugaredLogger, eve
 		if err != nil {
 			return &runinfo, err
 		}
+	case *github.PushEvent:
+		runinfo = RunInfo{
+			Owner:         event.GetRepo().GetOwner().GetLogin(),
+			Repository:    event.GetRepo().GetName(),
+			DefaultBranch: event.GetRepo().GetDefaultBranch(),
+			SHA:           event.GetHeadCommit().GetID(),
+			URL:           event.GetRepo().GetHTMLURL(),
+			Sender:        event.GetSender().GetLogin(),
+			BaseBranch:    event.GetRef(),
+		}
+		runinfo.HeadBranch = runinfo.BaseBranch // in push events Head Branch is the same as Basebranch
 	case *github.PullRequestEvent:
 		runinfo = RunInfo{
 			Owner:         event.GetRepo().Owner.GetLogin(),


Test patch:
diff --git a/pkg/cmd/pipelineascode/pipelineascode_test.go b/pkg/cmd/pipelineascode/pipelineascode_test.go
index c0b59162..5831643f 100644
--- a/pkg/cmd/pipelineascode/pipelineascode_test.go
+++ b/pkg/cmd/pipelineascode/pipelineascode_test.go
@@ -30,6 +30,9 @@ func TestRunWrapOld(t *testing.T) {
 	mux.HandleFunc("/repos/chmouel/scratchmyback/check-runs", func(w http.ResponseWriter, r *http.Request) {
 		fmt.Fprintf(w, `{"id": %d}`, checkid)
 	})
+	mux.HandleFunc(fmt.Sprintf("/repos/chmouel/scratchmyback/check-runs/%d", checkid), func(w http.ResponseWriter, r *http.Request) {
+		fmt.Fprintf(w, `{"id": %d}`, checkid)
+	})
 	cs := &cli.Clients{
 		GithubClient: webvcs.GithubVCS{
 			Client: fakeghclient,
@@ -172,6 +175,9 @@ func TestRunWrap(t *testing.T) {
 	mux.HandleFunc("/repos/chmouel/scratchmyback/check-runs", func(w http.ResponseWriter, r *http.Request) {
 		fmt.Fprintf(w, `{"id": %d}`, checkid)
 	})
+	mux.HandleFunc(fmt.Sprintf("/repos/chmouel/scratchmyback/check-runs/%d", checkid), func(w http.ResponseWriter, r *http.Request) {
+		fmt.Fprintf(w, `{"id": %d}`, checkid)
+	})
 	kinteract := &kitesthelper.KinterfaceTest{
 		ConsoleURL: "https://console.url",
 	}
diff --git a/pkg/config/annotation_matcher_test.go b/pkg/config/annotation_matcher_test.go
index 0672e275..60f568ab 100644
--- a/pkg/config/annotation_matcher_test.go
+++ b/pkg/config/annotation_matcher_test.go
@@ -1,6 +1,7 @@
 package config
 
 import (
+	"reflect"
 	"strings"
 	"testing"
 
@@ -175,3 +176,58 @@ func TestMatchPipelinerunByAnnotation(t *testing.T) {
 		})
 	}
 }
+
+func Test_getAnnotationValues(t *testing.T) {
+	type args struct {
+		annotation string
+	}
+	tests := []struct {
+		name    string
+		args    args
+		want    []string
+		wantErr bool
+	}{
+		{
+			name: "get-annotation-simple",
+			args: args{
+				annotation: "[foo]",
+			},
+			want:    []string{"foo"},
+			wantErr: false,
+		},
+		{
+			name: "get-annotation-multiples",
+			args: args{
+				annotation: "[foo, bar]",
+			},
+			want:    []string{"foo", "bar"},
+			wantErr: false,
+		},
+		{
+			name: "get-annotation-bad-syntax",
+			args: args{
+				annotation: "foo]",
+			},
+			wantErr: true,
+		},
+		{
+			name: "get-annotation-error-empty",
+			args: args{
+				annotation: "[]",
+			},
+			wantErr: true,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got, err := getAnnotationValues(tt.args.annotation)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("getAnnotationValues() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+			if !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("getAnnotationValues() got = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
diff --git a/pkg/pipelineascode/pipelineascode_test.go b/pkg/pipelineascode/pipelineascode_test.go
index aa156c6d..fe97344b 100644
--- a/pkg/pipelineascode/pipelineascode_test.go
+++ b/pkg/pipelineascode/pipelineascode_test.go
@@ -6,6 +6,9 @@ import (
 	"fmt"
 	"io/ioutil"
 	"net/http"
+	"os"
+	"path/filepath"
+	"strings"
 	"testing"
 	"time"
 
@@ -224,85 +227,45 @@ func TestRunDeniedFromForcedNamespace(t *testing.T) {
 			forcedNamespace))
 }
 
-func TestRun(t *testing.T) {
-	ctx, _ := rtesting.SetupFakeContext(t)
-	fakeclient, mux, _, teardown := ghtesthelper.SetupGH()
-	defer teardown()
-	runinfo := &webvcs.RunInfo{
-		SHA:        "principale",
-		Owner:      "organizationes",
-		Repository: "lagaffe",
-		URL:        "https://service/documentation",
-		HeadBranch: "press",
-		BaseBranch: "main",
-		Sender:     "fantasio",
-		EventType:  "pull_request",
-	}
+func testSetupTektonDir(mux *http.ServeMux, runinfo *webvcs.RunInfo, directory string) {
+	var tektonDirContent string
+	_ = filepath.Walk(directory, func(path string, info os.FileInfo, err error) error {
+		basename := filepath.Base(path)
+		trimmed := strings.TrimSuffix(basename, filepath.Ext(basename))
+		tektonDirContent += fmt.Sprintf(`{
+			"name": "%s",
+			"path": ".tekton/%s",
+			"sha": "shaof%s",
+			"size": %d,
+			"type": "file"
+		},`, basename, basename, trimmed, info.Size())
+
+		contentB, _ := ioutil.ReadFile(path)
+		replyString(mux,
+			fmt.Sprintf("/repos/%s/%s/git/blobs/shaof%s", runinfo.Owner, runinfo.Repository, trimmed),
+			fmt.Sprintf(`{"encoding": "base64","content": "%s"}`,
+				base64.StdEncoding.EncodeToString(contentB)))
+
+		return nil
+	})
 
 	replyString(mux,
 		fmt.Sprintf("/repos/%s/%s/contents/.tekton", runinfo.Owner, runinfo.Repository),
-		`[{
-
-				  "name": "pipeline.yaml",
-				  "path": ".tekton/pipeline.yaml",
-				  "sha": "5f44631b24c740288924767c608af932756d6c1a",
-				  "size": 1186,
-				  "type": "file"
-				},
-				{
-				  "name": "run.yaml",
-				  "path": ".tekton/run.yaml",
-				  "sha": "9085026cd00516d1db7101191d61a4371933c735",
-				  "size": 464,
-				  "type": "file"
-				},
-				{
-				  "name": "tekton.yaml",
-				  "path": ".tekton/tekton.yaml",
-				  "sha": "eacad9fa044f3d9039bb04c9452eadf0c43e3195",
-				  "size": 233,
-				  "type": "file"
-		 }]`)
+		fmt.Sprintf("[%s]", strings.TrimSuffix(tektonDirContent, ",")))
+}
 
+func testSetupCommonGhReplies(t *testing.T, mux *http.ServeMux, runinfo *webvcs.RunInfo, finalStatus, finalStatusText string,
+	noReplyOrgPublicMembers bool) {
+	// Take a directory and geneate replies as Github for it
 	replyString(mux,
 		fmt.Sprintf("/repos/%s/%s/contents/internal/task", runinfo.Owner, runinfo.Repository),
 		`{"sha": "internaltasksha"}`)
 
-	mux.HandleFunc("/orgs/"+runinfo.Owner+"/public_members", func(rw http.ResponseWriter, r *http.Request) {
-		fmt.Fprintf(rw, `[{"login": "%s"}]`, runinfo.Sender)
-	})
-
-	// Internal task referenced in tekton.yaml
-	taskB, err := ioutil.ReadFile("testdata/task.yaml")
-	assert.NilError(t, err)
-	replyString(mux,
-		fmt.Sprintf("/repos/%s/%s/git/blobs/internaltasksha", runinfo.Owner, runinfo.Repository),
-		fmt.Sprintf(`{"encoding": "base64","content": "%s"}`,
-			base64.StdEncoding.EncodeToString(taskB)))
-
-	// Tekton.yaml
-	tlB, err := ioutil.ReadFile("testdata/tekton.yaml")
-	assert.NilError(t, err)
-	replyString(mux,
-		fmt.Sprintf("/repos/%s/%s/git/blobs/eacad9fa044f3d9039bb04c9452eadf0c43e3195", runinfo.Owner, runinfo.Repository),
-		fmt.Sprintf(`{"encoding": "base64","content": "%s"}`,
-			base64.StdEncoding.EncodeToString(tlB)))
-
-	// Run.yaml
-	prB, err := ioutil.ReadFile("testdata/run.yaml")
-	assert.NilError(t, err)
-	replyString(mux,
-		fmt.Sprintf("/repos/%s/%s/git/blobs/9085026cd00516d1db7101191d61a4371933c735", runinfo.Owner, runinfo.Repository),
-		fmt.Sprintf(`{"encoding": "base64","content": "%s"}`,
-			base64.StdEncoding.EncodeToString(prB)))
-
-	// Pipeline.yaml
-	pB, err := ioutil.ReadFile("testdata/pipeline.yaml")
-	assert.NilError(t, err)
-	replyString(mux,
-		fmt.Sprintf("/repos/%s/%s/git/blobs/5f44631b24c740288924767c608af932756d6c1a", runinfo.Owner, runinfo.Repository),
-		fmt.Sprintf(`{"encoding": "base64","content": "%s"}`,
-			base64.StdEncoding.EncodeToString(pB)))
+	if !noReplyOrgPublicMembers {
+		mux.HandleFunc("/orgs/"+runinfo.Owner+"/public_members", func(rw http.ResponseWriter, r *http.Request) {
+			fmt.Fprintf(rw, `[{"login": "%s"}]`, runinfo.Sender)
+		})
+	}
 
 	replyString(mux,
 		fmt.Sprintf("/repos/%s/%s/check-runs", runinfo.Owner, runinfo.Repository),
@@ -314,64 +277,207 @@ func TestRun(t *testing.T) {
 			created := github.CreateCheckRunOptions{}
 			err := json.Unmarshal(body, &created)
 			assert.NilError(t, err)
-
 			// We created multiple status but the last one should be completed.
 			// TODO: we could maybe refine this test
 			if created.GetStatus() == "completed" {
-				assert.Equal(t, created.GetConclusion(), "neutral")
+				assert.Equal(t, created.GetConclusion(), finalStatus)
+				assert.Assert(t, strings.Contains(created.GetOutput().GetText(), finalStatusText), "GetStatus/CheckRun %s != %s", created.GetOutput().GetText(), finalStatusText)
 			}
 		})
+}
 
-	gcvs := webvcs.GithubVCS{
-		Client: fakeclient,
-	}
+func TestRun(t *testing.T) {
+	observer, log := zapobserver.New(zap.InfoLevel)
+	logger := zap.New(observer).Sugar()
+	tests := []struct {
+		name                         string
+		runinfo                      *webvcs.RunInfo
+		tektondir                    string
+		wantErr                      string
+		finalStatus                  string
+		finalStatusText              string
+		repositories                 []*v1alpha1.Repository
+		skipReplyingOrgPublicMembers bool
+	}{
+		{
+			name: "pull request",
+			runinfo: &webvcs.RunInfo{
+				SHA:        "principale",
+				Owner:      "organizationes",
+				Repository: "lagaffe",
+				URL:        "https://service/documentation",
+				HeadBranch: "press",
+				BaseBranch: "main",
+				Sender:     "fantasio",
+				EventType:  "pull_request",
+			},
+			tektondir:       "testdata/pull_request",
+			finalStatus:     "neutral",
+			finalStatusText: "More detailed status",
+		},
+		{
+			name: "No match",
+			runinfo: &webvcs.RunInfo{
+				SHA:        "principale",
+				Owner:      "organizationes",
+				Repository: "lagaffe",
+				URL:        "https://service/documentation",
+				HeadBranch: "press",
+				Sender:     "fantasio",
+				BaseBranch: "nomatch",
+				EventType:  "pull_request",
+			},
+			tektondir:   "testdata/pull_request",
+			wantErr:     "cannot match any pipeline",
+			finalStatus: "neutral",
+		},
+		{
+			name: "Push/branch",
+			runinfo: &webvcs.RunInfo{
+				SHA:        "principale",
+				Owner:      "organizationes",
+				Repository: "lagaffe",
+				URL:        "https://service/documentation",
+				Sender:     "fantasio",
+				HeadBranch: "refs/heads/main",
+				BaseBranch: "refs/heads/main",
+				EventType:  "push",
+			},
+			tektondir:   "testdata/push_branch",
+			finalStatus: "neutral",
+		},
+		{
+			name: "Push/tags",
+			runinfo: &webvcs.RunInfo{
+				SHA:        "principale",
+				Owner:      "organizationes",
+				Repository: "lagaffe",
+				URL:        "https://service/documentation",
+				Sender:     "fantasio",
+				HeadBranch: "refs/tags/0.1",
+				BaseBranch: "refs/tags/0.1",
+				EventType:  "push",
+			},
+			tektondir:   "testdata/push_tags",
+			finalStatus: "neutral",
+		},
 
-	repo := testclient.Data{
-		Namespaces: []*corev1.Namespace{
-			{
-				ObjectMeta: metav1.ObjectMeta{
-					Name: "namespace",
-				},
+		// Skipped
+		{
+			name: "Skipped/Test no tekton dir",
+			runinfo: &webvcs.RunInfo{
+				SHA:        "principale",
+				Owner:      "organizationes",
+				Repository: "lagaffe",
+				URL:        "https://service/documentation",
+				HeadBranch: "press",
+				Sender:     "fantasio",
+				BaseBranch: "nomatch",
+				EventType:  "pull_request",
 			},
+			tektondir:       "",
+			finalStatus:     "skipped",
+			finalStatusText: "directory for this repository",
 		},
-		Repositories: []*v1alpha1.Repository{
-			newRepo(
-				"test-run",
-				runinfo.URL,
-				runinfo.BaseBranch,
-				"namespace",
-				"namespace"),
+		{
+			name: "Skipped/Test no repositories match",
+			runinfo: &webvcs.RunInfo{
+				SHA:        "principale",
+				Owner:      "organizationes",
+				Repository: "lagaffe",
+				URL:        "https://service/documentation",
+				HeadBranch: "press",
+				Sender:     "fantasio",
+				BaseBranch: "nomatch",
+				EventType:  "pull_request",
+			},
+			tektondir:       "",
+			finalStatus:     "skipped",
+			finalStatusText: "not find a namespace match",
+			repositories: []*v1alpha1.Repository{
+				newRepo("test-run", "https://nowhere.com",
+					"a branch",
+					"namespace",
+					"namespace"),
+			},
+		},
+		{
+			name: "Skipped/User is not allowed",
+			runinfo: &webvcs.RunInfo{
+				SHA:        "principale",
+				Owner:      "organizationes",
+				Repository: "lagaffe",
+				URL:        "https://service/documentation",
+				HeadBranch: "press",
+				Sender:     "evilbro",
+				BaseBranch: "nomatch",
+				EventType:  "pull_request",
+			},
+			tektondir:                    "testdata/pull_request",
+			finalStatus:                  "skipped",
+			finalStatusText:              "is not allowed to run CI on this repo",
+			skipReplyingOrgPublicMembers: true,
 		},
 	}
-	stdata, _ := testclient.SeedTestData(t, ctx, repo)
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			ctx, _ := rtesting.SetupFakeContext(t)
+			if tt.repositories == nil {
+				tt.repositories = []*v1alpha1.Repository{
+					newRepo("test-run", tt.runinfo.URL, tt.runinfo.BaseBranch, "namespace", "namespace"),
+				}
+			}
+			tdata := testclient.Data{
+				Namespaces: []*corev1.Namespace{
+					{
+						ObjectMeta: metav1.ObjectMeta{
+							Name: "namespace",
+						},
+					},
+				},
+				Repositories: tt.repositories,
+			}
 
-	observer, log := zapobserver.New(zap.InfoLevel)
-	logger := zap.New(observer).Sugar()
+			fakeclient, mux, _, teardown := ghtesthelper.SetupGH()
+			defer teardown()
 
-	tdc := testDynamic.Options{}
-	dc, _ := tdc.Client()
+			testSetupCommonGhReplies(t, mux, tt.runinfo, tt.finalStatus, tt.finalStatusText, tt.skipReplyingOrgPublicMembers)
+			if tt.tektondir != "" {
+				testSetupTektonDir(mux, tt.runinfo, tt.tektondir)
+			}
 
-	cs := &cli.Clients{
-		GithubClient:   gcvs,
-		PipelineAsCode: stdata.PipelineAsCode,
-		Log:            logger,
-		Kube:           stdata.Kube,
-		Tekton:         stdata.Pipeline,
-		Dynamic:        dc,
-	}
+			stdata, _ := testclient.SeedTestData(t, ctx, tdata)
+			tdc := testDynamic.Options{}
+			dc, _ := tdc.Client()
+			cs := &cli.Clients{
+				GithubClient: webvcs.GithubVCS{
+					Client: fakeclient,
+				},
+				PipelineAsCode: stdata.PipelineAsCode,
+				Log:            logger,
+				Kube:           stdata.Kube,
+				Tekton:         stdata.Pipeline,
+				Dynamic:        dc,
+			}
+			k8int := &kitesthelper.KinterfaceTest{
+				ConsoleURL: "https://console.url",
+			}
+			err := Run(ctx, cs, k8int, tt.runinfo)
 
-	k8int := kitesthelper.KinterfaceTest{
-		ConsoleURL: "https://console.url",
-	}
+			if tt.wantErr != "" {
+				assert.ErrorContains(t, err, tt.wantErr)
+				return
+			}
 
-	// TODO: I am not sure why querying stdata.Tekton.PipelineRun.List doesn't
-	// give back anything while it works for repo, we will need to investigate
-	// this to make better testing.
-	err = Run(ctx, cs, &k8int, runinfo)
-	assert.NilError(t, err)
-	assert.Assert(t, len(log.TakeAll()) > 0)
-	got, err := stdata.PipelineAsCode.PipelinesascodeV1alpha1().Repositories("namespace").Get(
-		ctx, "test-run", metav1.GetOptions{})
-	assert.NilError(t, err)
-	assert.Assert(t, got.Status[len(got.Status)-1].PipelineRunName != "pipelinerun1")
+			assert.NilError(t, err)
+			assert.Assert(t, len(log.TakeAll()) > 0)
+
+			if tt.finalStatus != "skipped" {
+				got, err := stdata.PipelineAsCode.PipelinesascodeV1alpha1().Repositories("namespace").Get(
+					ctx, "test-run", metav1.GetOptions{})
+				assert.NilError(t, err)
+				assert.Assert(t, got.Status[len(got.Status)-1].PipelineRunName != "pipelinerun1")
+			}
+		})
+	}
 }
diff --git a/pkg/pipelineascode/testdata/pipeline.yaml b/pkg/pipelineascode/testdata/pull_request/pipeline.yaml
similarity index 100%
rename from pkg/pipelineascode/testdata/pipeline.yaml
rename to pkg/pipelineascode/testdata/pull_request/pipeline.yaml
diff --git a/pkg/pipelineascode/testdata/run.yaml b/pkg/pipelineascode/testdata/pull_request/run.yaml
similarity index 89%
rename from pkg/pipelineascode/testdata/run.yaml
rename to pkg/pipelineascode/testdata/pull_request/run.yaml
index ea6d7c29..a32239e7 100644
--- a/pkg/pipelineascode/testdata/run.yaml
+++ b/pkg/pipelineascode/testdata/pull_request/run.yaml
@@ -2,7 +2,7 @@
 apiVersion: tekton.dev/v1beta1
 kind: PipelineRun
 metadata:
-  name: hello-test-pipeline
+  name: pull_request
   annotations:
     pipelinesascode.tekton.dev/on-target-branch: "[main]"
     pipelinesascode.tekton.dev/on-event: "[pull_request]"
diff --git a/pkg/pipelineascode/testdata/task.yaml b/pkg/pipelineascode/testdata/pull_request/task.yaml
similarity index 100%
rename from pkg/pipelineascode/testdata/task.yaml
rename to pkg/pipelineascode/testdata/pull_request/task.yaml
diff --git a/pkg/pipelineascode/testdata/push_branch/run.yaml b/pkg/pipelineascode/testdata/push_branch/run.yaml
new file mode 100644
index 00000000..15b40d8e
--- /dev/null
+++ b/pkg/pipelineascode/testdata/push_branch/run.yaml
@@ -0,0 +1,17 @@
+---
+apiVersion: tekton.dev/v1beta1
+kind: PipelineRun
+metadata:
+  annotations:
+    pipelinesascode.tekton.dev/on-target-branch: "[refs/heads/main]"
+    pipelinesascode.tekton.dev/on-event: "[push]"
+  name: push_branch
+spec:
+  pipelineSpec:
+    tasks:
+      - name: hello1
+        taskSpec:
+          steps:
+            - name: hello-moto
+              image: alpine:3.7
+              script: "echo hello moto"
diff --git a/pkg/pipelineascode/testdata/push_tags/run.yaml b/pkg/pipelineascode/testdata/push_tags/run.yaml
new file mode 100644
index 00000000..453d6ce2
--- /dev/null
+++ b/pkg/pipelineascode/testdata/push_tags/run.yaml
@@ -0,0 +1,17 @@
+---
+apiVersion: tekton.dev/v1beta1
+kind: PipelineRun
+metadata:
+  annotations:
+    pipelinesascode.tekton.dev/on-target-branch: "[refs/tags/0.1]"
+    pipelinesascode.tekton.dev/on-event: "[push]"
+  name: push_branch
+spec:
+  pipelineSpec:
+    tasks:
+      - name: hello1
+        taskSpec:
+          steps:
+            - name: hello-moto
+              image: alpine:3.7
+              script: "echo hello moto"
diff --git a/pkg/pipelineascode/testdata/tekton.yaml b/pkg/pipelineascode/testdata/tekton.yaml
deleted file mode 100644
index 252333aa..00000000
--- a/pkg/pipelineascode/testdata/tekton.yaml
+++ /dev/null
@@ -1,3 +0,0 @@
----
-tasks:
-  - internal/task
diff --git a/test/fixtures/push-ref-main.json b/test/fixtures/push-ref-main.json
new file mode 100644
index 00000000..e10a9d11
--- /dev/null
+++ b/test/fixtures/push-ref-main.json
@@ -0,0 +1,198 @@
+{
+  "ref": "refs/heads/main",
+  "before": "00bacbb7fc5f39197bab744c9c53d275d1828f5c",
+  "after": "6fe4cf6bdee23d427228ccac175e288c07c1c95f",
+  "repository": {
+    "id": 359117785,
+    "node_id": "MDEwOlJlcG9zaXRvcnkzNTkxMTc3ODU=",
+    "name": "scratchmyback",
+    "full_name": "chmouel/scratchmyback",
+    "private": false,
+    "owner": {
+      "name": "chmouel",
+      "email": "chmouel@chmouel.com",
+      "login": "chmouel",
+      "id": 98980,
+      "node_id": "MDQ6VXNlcjk4OTgw",
+      "avatar_url": "https://avatars.githubusercontent.com/u/98980?v=4",
+      "gravatar_id": "",
+      "url": "https://api.github.com/users/chmouel",
+      "html_url": "https://github.com/chmouel",
+      "followers_url": "https://api.github.com/users/chmouel/followers",
+      "following_url": "https://api.github.com/users/chmouel/following{/other_user}",
+      "gists_url": "https://api.github.com/users/chmouel/gists{/gist_id}",
+      "starred_url": "https://api.github.com/users/chmouel/starred{/owner}{/repo}",
+      "subscriptions_url": "https://api.github.com/users/chmouel/subscriptions",
+      "organizations_url": "https://api.github.com/users/chmouel/orgs",
+      "repos_url": "https://api.github.com/users/chmouel/repos",
+      "events_url": "https://api.github.com/users/chmouel/events{/privacy}",
+      "received_events_url": "https://api.github.com/users/chmouel/received_events",
+      "type": "User",
+      "site_admin": false
+    },
+    "html_url": "https://github.com/chmouel/scratchmyback",
+    "description": "Back scratching",
+    "fork": false,
+    "url": "https://github.com/chmouel/scratchmyback",
+    "forks_url": "https://api.github.com/repos/chmouel/scratchmyback/forks",
+    "keys_url": "https://api.github.com/repos/chmouel/scratchmyback/keys{/key_id}",
+    "collaborators_url": "https://api.github.com/repos/chmouel/scratchmyback/collaborators{/collaborator}",
+    "teams_url": "https://api.github.com/repos/chmouel/scratchmyback/teams",
+    "hooks_url": "https://api.github.com/repos/chmouel/scratchmyback/hooks",
+    "issue_events_url": "https://api.github.com/repos/chmouel/scratchmyback/issues/events{/number}",
+    "events_url": "https://api.github.com/repos/chmouel/scratchmyback/events",
+    "assignees_url": "https://api.github.com/repos/chmouel/scratchmyback/assignees{/user}",
+    "branches_url": "https://api.github.com/repos/chmouel/scratchmyback/branches{/branch}",
+    "tags_url": "https://api.github.com/repos/chmouel/scratchmyback/tags",
+    "blobs_url": "https://api.github.com/repos/chmouel/scratchmyback/git/blobs{/sha}",
+    "git_tags_url": "https://api.github.com/repos/chmouel/scratchmyback/git/tags{/sha}",
+    "git_refs_url": "https://api.github.com/repos/chmouel/scratchmyback/git/refs{/sha}",
+    "trees_url": "https://api.github.com/repos/chmouel/scratchmyback/git/trees{/sha}",
+    "statuses_url": "https://api.github.com/repos/chmouel/scratchmyback/statuses/{sha}",
+    "languages_url": "https://api.github.com/repos/chmouel/scratchmyback/languages",
+    "stargazers_url": "https://api.github.com/repos/chmouel/scratchmyback/stargazers",
+    "contributors_url": "https://api.github.com/repos/chmouel/scratchmyback/contributors",
+    "subscribers_url": "https://api.github.com/repos/chmouel/scratchmyback/subscribers",
+    "subscription_url": "https://api.github.com/repos/chmouel/scratchmyback/subscription",
+    "commits_url": "https://api.github.com/repos/chmouel/scratchmyback/commits{/sha}",
+    "git_commits_url": "https://api.github.com/repos/chmouel/scratchmyback/git/commits{/sha}",
+    "comments_url": "https://api.github.com/repos/chmouel/scratchmyback/comments{/number}",
+    "issue_comment_url": "https://api.github.com/repos/chmouel/scratchmyback/issues/comments{/number}",
+    "contents_url": "https://api.github.com/repos/chmouel/scratchmyback/contents/{+path}",
+    "compare_url": "https://api.github.com/repos/chmouel/scratchmyback/compare/{base}...{head}",
+    "merges_url": "https://api.github.com/repos/chmouel/scratchmyback/merges",
+    "archive_url": "https://api.github.com/repos/chmouel/scratchmyback/{archive_format}{/ref}",
+    "downloads_url": "https://api.github.com/repos/chmouel/scratchmyback/downloads",
+    "issues_url": "https://api.github.com/repos/chmouel/scratchmyback/issues{/number}",
+    "pulls_url": "https://api.github.com/repos/chmouel/scratchmyback/pulls{/number}",
+    "milestones_url": "https://api.github.com/repos/chmouel/scratchmyback/milestones{/number}",
+    "notifications_url": "https://api.github.com/repos/chmouel/scratchmyback/notifications{?since,all,participating}",
+    "labels_url": "https://api.github.com/repos/chmouel/scratchmyback/labels{/name}",
+    "releases_url": "https://api.github.com/repos/chmouel/scratchmyback/releases{/id}",
+    "deployments_url": "https://api.github.com/repos/chmouel/scratchmyback/deployments",
+    "created_at": 1618743465,
+    "updated_at": "2021-05-10T13:18:37Z",
+    "pushed_at": 1621344346,
+    "git_url": "git://github.com/chmouel/scratchmyback.git",
+    "ssh_url": "git@github.com:chmouel/scratchmyback.git",
+    "clone_url": "https://github.com/chmouel/scratchmyback.git",
+    "svn_url": "https://github.com/chmouel/scratchmyback",
+    "homepage": null,
+    "size": 25,
+    "stargazers_count": 0,
+    "watchers_count": 0,
+    "language": null,
+    "has_issues": true,
+    "has_projects": true,
+    "has_downloads": true,
+    "has_wiki": true,
+    "has_pages": false,
+    "forks_count": 0,
+    "mirror_url": null,
+    "archived": false,
+    "disabled": false,
+    "open_issues_count": 1,
+    "license": {
+      "key": "gpl-3.0",
+      "name": "GNU General Public License v3.0",
+      "spdx_id": "GPL-3.0",
+      "url": "https://api.github.com/licenses/gpl-3.0",
+      "node_id": "MDc6TGljZW5zZTk="
+    },
+    "forks": 0,
+    "open_issues": 1,
+    "watchers": 0,
+    "default_branch": "main",
+    "stargazers": 0,
+    "master_branch": "main"
+  },
+  "pusher": {
+    "name": "chmouel",
+    "email": "chmouel@chmouel.com"
+  },
+  "sender": {
+    "login": "chmouel",
+    "id": 98980,
+    "node_id": "MDQ6VXNlcjk4OTgw",
+    "avatar_url": "https://avatars.githubusercontent.com/u/98980?v=4",
+    "gravatar_id": "",
+    "url": "https://api.github.com/users/chmouel",
+    "html_url": "https://github.com/chmouel",
+    "followers_url": "https://api.github.com/users/chmouel/followers",
+    "following_url": "https://api.github.com/users/chmouel/following{/other_user}",
+    "gists_url": "https://api.github.com/users/chmouel/gists{/gist_id}",
+    "starred_url": "https://api.github.com/users/chmouel/starred{/owner}{/repo}",
+    "subscriptions_url": "https://api.github.com/users/chmouel/subscriptions",
+    "organizations_url": "https://api.github.com/users/chmouel/orgs",
+    "repos_url": "https://api.github.com/users/chmouel/repos",
+    "events_url": "https://api.github.com/users/chmouel/events{/privacy}",
+    "received_events_url": "https://api.github.com/users/chmouel/received_events",
+    "type": "User",
+    "site_admin": false
+  },
+  "installation": {
+    "id": 16556382,
+    "node_id": "MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMTY1NTYzODI="
+  },
+  "created": false,
+  "deleted": false,
+  "forced": false,
+  "base_ref": null,
+  "compare": "https://github.com/chmouel/scratchmyback/compare/00bacbb7fc5f...6fe4cf6bdee2",
+  "commits": [
+    {
+      "id": "6fe4cf6bdee23d427228ccac175e288c07c1c95f",
+      "tree_id": "f81571691a70613b79e197fedbada5224584a6fc",
+      "distinct": true,
+      "message": "update\n\nSigned-off-by: Chmouel Boudjnah <chmouel@chmouel.com>",
+      "timestamp": "2021-05-18T15:25:39+02:00",
+      "url": "https://github.com/chmouel/scratchmyback/commit/6fe4cf6bdee23d427228ccac175e288c07c1c95f",
+      "author": {
+        "name": "Chmouel Boudjnah",
+        "email": "chmouel@chmouel.com",
+        "username": "chmouel"
+      },
+      "committer": {
+        "name": "Chmouel Boudjnah",
+        "email": "chmouel@chmouel.com",
+        "username": "chmouel"
+      },
+      "added": [
+
+      ],
+      "removed": [
+
+      ],
+      "modified": [
+        "OWNERS"
+      ]
+    }
+  ],
+  "head_commit": {
+    "id": "6fe4cf6bdee23d427228ccac175e288c07c1c95f",
+    "tree_id": "f81571691a70613b79e197fedbada5224584a6fc",
+    "distinct": true,
+    "message": "update\n\nSigned-off-by: Chmouel Boudjnah <chmouel@chmouel.com>",
+    "timestamp": "2021-05-18T15:25:39+02:00",
+    "url": "https://github.com/chmouel/scratchmyback/commit/6fe4cf6bdee23d427228ccac175e288c07c1c95f",
+    "author": {
+      "name": "Chmouel Boudjnah",
+      "email": "chmouel@chmouel.com",
+      "username": "chmouel"
+    },
+    "committer": {
+      "name": "Chmouel Boudjnah",
+      "email": "chmouel@chmouel.com",
+      "username": "chmouel"
+    },
+    "added": [
+
+    ],
+    "removed": [
+
+    ],
+    "modified": [
+      "OWNERS"
+    ]
+  }
+}
diff --git a/test/fixtures/push-tag-0.1.json b/test/fixtures/push-tag-0.1.json
new file mode 100644
index 00000000..98cdc3a7
--- /dev/null
+++ b/test/fixtures/push-tag-0.1.json
@@ -0,0 +1,172 @@
+{
+  "ref": "refs/tags/0.1",
+  "before": "0000000000000000000000000000000000000000",
+  "after": "172a108023444bfb48851c0b08123b02afbe4ed2",
+  "repository": {
+    "id": 359117785,
+    "node_id": "MDEwOlJlcG9zaXRvcnkzNTkxMTc3ODU=",
+    "name": "scratchmyback",
+    "full_name": "chmouel/scratchmyback",
+    "private": false,
+    "owner": {
+      "name": "chmouel",
+      "email": "chmouel@chmouel.com",
+      "login": "chmouel",
+      "id": 98980,
+      "node_id": "MDQ6VXNlcjk4OTgw",
+      "avatar_url": "https://avatars.githubusercontent.com/u/98980?v=4",
+      "gravatar_id": "",
+      "url": "https://api.github.com/users/chmouel",
+      "html_url": "https://github.com/chmouel",
+      "followers_url": "https://api.github.com/users/chmouel/followers",
+      "following_url": "https://api.github.com/users/chmouel/following{/other_user}",
+      "gists_url": "https://api.github.com/users/chmouel/gists{/gist_id}",
+      "starred_url": "https://api.github.com/users/chmouel/starred{/owner}{/repo}",
+      "subscriptions_url": "https://api.github.com/users/chmouel/subscriptions",
+      "organizations_url": "https://api.github.com/users/chmouel/orgs",
+      "repos_url": "https://api.github.com/users/chmouel/repos",
+      "events_url": "https://api.github.com/users/chmouel/events{/privacy}",
+      "received_events_url": "https://api.github.com/users/chmouel/received_events",
+      "type": "User",
+      "site_admin": false
+    },
+    "html_url": "https://github.com/chmouel/scratchmyback",
+    "description": "Back scratching",
+    "fork": false,
+    "url": "https://github.com/chmouel/scratchmyback",
+    "forks_url": "https://api.github.com/repos/chmouel/scratchmyback/forks",
+    "keys_url": "https://api.github.com/repos/chmouel/scratchmyback/keys{/key_id}",
+    "collaborators_url": "https://api.github.com/repos/chmouel/scratchmyback/collaborators{/collaborator}",
+    "teams_url": "https://api.github.com/repos/chmouel/scratchmyback/teams",
+    "hooks_url": "https://api.github.com/repos/chmouel/scratchmyback/hooks",
+    "issue_events_url": "https://api.github.com/repos/chmouel/scratchmyback/issues/events{/number}",
+    "events_url": "https://api.github.com/repos/chmouel/scratchmyback/events",
+    "assignees_url": "https://api.github.com/repos/chmouel/scratchmyback/assignees{/user}",
+    "branches_url": "https://api.github.com/repos/chmouel/scratchmyback/branches{/branch}",
+    "tags_url": "https://api.github.com/repos/chmouel/scratchmyback/tags",
+    "blobs_url": "https://api.github.com/repos/chmouel/scratchmyback/git/blobs{/sha}",
+    "git_tags_url": "https://api.github.com/repos/chmouel/scratchmyback/git/tags{/sha}",
+    "git_refs_url": "https://api.github.com/repos/chmouel/scratchmyback/git/refs{/sha}",
+    "trees_url": "https://api.github.com/repos/chmouel/scratchmyback/git/trees{/sha}",
+    "statuses_url": "https://api.github.com/repos/chmouel/scratchmyback/statuses/{sha}",
+    "languages_url": "https://api.github.com/repos/chmouel/scratchmyback/languages",
+    "stargazers_url": "https://api.github.com/repos/chmouel/scratchmyback/stargazers",
+    "contributors_url": "https://api.github.com/repos/chmouel/scratchmyback/contributors",
+    "subscribers_url": "https://api.github.com/repos/chmouel/scratchmyback/subscribers",
+    "subscription_url": "https://api.github.com/repos/chmouel/scratchmyback/subscription",
+    "commits_url": "https://api.github.com/repos/chmouel/scratchmyback/commits{/sha}",
+    "git_commits_url": "https://api.github.com/repos/chmouel/scratchmyback/git/commits{/sha}",
+    "comments_url": "https://api.github.com/repos/chmouel/scratchmyback/comments{/number}",
+    "issue_comment_url": "https://api.github.com/repos/chmouel/scratchmyback/issues/comments{/number}",
+    "contents_url": "https://api.github.com/repos/chmouel/scratchmyback/contents/{+path}",
+    "compare_url": "https://api.github.com/repos/chmouel/scratchmyback/compare/{base}...{head}",
+    "merges_url": "https://api.github.com/repos/chmouel/scratchmyback/merges",
+    "archive_url": "https://api.github.com/repos/chmouel/scratchmyback/{archive_format}{/ref}",
+    "downloads_url": "https://api.github.com/repos/chmouel/scratchmyback/downloads",
+    "issues_url": "https://api.github.com/repos/chmouel/scratchmyback/issues{/number}",
+    "pulls_url": "https://api.github.com/repos/chmouel/scratchmyback/pulls{/number}",
+    "milestones_url": "https://api.github.com/repos/chmouel/scratchmyback/milestones{/number}",
+    "notifications_url": "https://api.github.com/repos/chmouel/scratchmyback/notifications{?since,all,participating}",
+    "labels_url": "https://api.github.com/repos/chmouel/scratchmyback/labels{/name}",
+    "releases_url": "https://api.github.com/repos/chmouel/scratchmyback/releases{/id}",
+    "deployments_url": "https://api.github.com/repos/chmouel/scratchmyback/deployments",
+    "created_at": 1618743465,
+    "updated_at": "2021-05-18T15:22:38Z",
+    "pushed_at": 1621352226,
+    "git_url": "git://github.com/chmouel/scratchmyback.git",
+    "ssh_url": "git@github.com:chmouel/scratchmyback.git",
+    "clone_url": "https://github.com/chmouel/scratchmyback.git",
+    "svn_url": "https://github.com/chmouel/scratchmyback",
+    "homepage": null,
+    "size": 25,
+    "stargazers_count": 0,
+    "watchers_count": 0,
+    "language": null,
+    "has_issues": true,
+    "has_projects": true,
+    "has_downloads": true,
+    "has_wiki": true,
+    "has_pages": false,
+    "forks_count": 0,
+    "mirror_url": null,
+    "archived": false,
+    "disabled": false,
+    "open_issues_count": 1,
+    "license": {
+      "key": "gpl-3.0",
+      "name": "GNU General Public License v3.0",
+      "spdx_id": "GPL-3.0",
+      "url": "https://api.github.com/licenses/gpl-3.0",
+      "node_id": "MDc6TGljZW5zZTk="
+    },
+    "forks": 0,
+    "open_issues": 1,
+    "watchers": 0,
+    "default_branch": "main",
+    "stargazers": 0,
+    "master_branch": "main"
+  },
+  "pusher": {
+    "name": "chmouel",
+    "email": "chmouel@chmouel.com"
+  },
+  "sender": {
+    "login": "chmouel",
+    "id": 98980,
+    "node_id": "MDQ6VXNlcjk4OTgw",
+    "avatar_url": "https://avatars.githubusercontent.com/u/98980?v=4",
+    "gravatar_id": "",
+    "url": "https://api.github.com/users/chmouel",
+    "html_url": "https://github.com/chmouel",
+    "followers_url": "https://api.github.com/users/chmouel/followers",
+    "following_url": "https://api.github.com/users/chmouel/following{/other_user}",
+    "gists_url": "https://api.github.com/users/chmouel/gists{/gist_id}",
+    "starred_url": "https://api.github.com/users/chmouel/starred{/owner}{/repo}",
+    "subscriptions_url": "https://api.github.com/users/chmouel/subscriptions",
+    "organizations_url": "https://api.github.com/users/chmouel/orgs",
+    "repos_url": "https://api.github.com/users/chmouel/repos",
+    "events_url": "https://api.github.com/users/chmouel/events{/privacy}",
+    "received_events_url": "https://api.github.com/users/chmouel/received_events",
+    "type": "User",
+    "site_admin": false
+  },
+  "installation": {
+    "id": 16556382,
+    "node_id": "MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMTY1NTYzODI="
+  },
+  "created": true,
+  "deleted": false,
+  "forced": false,
+  "base_ref": "refs/heads/main",
+  "compare": "https://github.com/chmouel/scratchmyback/compare/0.1",
+  "commits": [
+
+  ],
+  "head_commit": {
+    "id": "172a108023444bfb48851c0b08123b02afbe4ed2",
+    "tree_id": "a7f00fa35663a623df63e694e517f7d49a5b4028",
+    "distinct": true,
+    "message": "Add run on push\n\nSigned-off-by: Chmouel Boudjnah <chmouel@chmouel.com>",
+    "timestamp": "2021-05-18T17:22:31+02:00",
+    "url": "https://github.com/chmouel/scratchmyback/commit/172a108023444bfb48851c0b08123b02afbe4ed2",
+    "author": {
+      "name": "Chmouel Boudjnah",
+      "email": "chmouel@chmouel.com",
+      "username": "chmouel"
+    },
+    "committer": {
+      "name": "Chmouel Boudjnah",
+      "email": "chmouel@chmouel.com",
+      "username": "chmouel"
+    },
+    "added": [
+      ".tekton/run-on-push.yaml"
+    ],
+    "removed": [
+
+    ],
+    "modified": [
+
+    ]
+  }
+}
diff --git a/test/fixtures/push.json b/test/fixtures/push.json
new file mode 100644
index 00000000..e10a9d11
--- /dev/null
+++ b/test/fixtures/push.json
@@ -0,0 +1,198 @@
+{
+  "ref": "refs/heads/main",
+  "before": "00bacbb7fc5f39197bab744c9c53d275d1828f5c",
+  "after": "6fe4cf6bdee23d427228ccac175e288c07c1c95f",
+  "repository": {
+    "id": 359117785,
+    "node_id": "MDEwOlJlcG9zaXRvcnkzNTkxMTc3ODU=",
+    "name": "scratchmyback",
+    "full_name": "chmouel/scratchmyback",
+    "private": false,
+    "owner": {
+      "name": "chmouel",
+      "email": "chmouel@chmouel.com",
+      "login": "chmouel",
+      "id": 98980,
+      "node_id": "MDQ6VXNlcjk4OTgw",
+      "avatar_url": "https://avatars.githubusercontent.com/u/98980?v=4",
+      "gravatar_id": "",
+      "url": "https://api.github.com/users/chmouel",
+      "html_url": "https://github.com/chmouel",
+      "followers_url": "https://api.github.com/users/chmouel/followers",
+      "following_url": "https://api.github.com/users/chmouel/following{/other_user}",
+      "gists_url": "https://api.github.com/users/chmouel/gists{/gist_id}",
+      "starred_url": "https://api.github.com/users/chmouel/starred{/owner}{/repo}",
+      "subscriptions_url": "https://api.github.com/users/chmouel/subscriptions",
+      "organizations_url": "https://api.github.com/users/chmouel/orgs",
+      "repos_url": "https://api.github.com/users/chmouel/repos",
+      "events_url": "https://api.github.com/users/chmouel/events{/privacy}",
+      "received_events_url": "https://api.github.com/users/chmouel/received_events",
+      "type": "User",
+      "site_admin": false
+    },
+    "html_url": "https://github.com/chmouel/scratchmyback",
+    "description": "Back scratching",
+    "fork": false,
+    "url": "https://github.com/chmouel/scratchmyback",
+    "forks_url": "https://api.github.com/repos/chmouel/scratchmyback/forks",
+    "keys_url": "https://api.github.com/repos/chmouel/scratchmyback/keys{/key_id}",
+    "collaborators_url": "https://api.github.com/repos/chmouel/scratchmyback/collaborators{/collaborator}",
+    "teams_url": "https://api.github.com/repos/chmouel/scratchmyback/teams",
+    "hooks_url": "https://api.github.com/repos/chmouel/scratchmyback/hooks",
+    "issue_events_url": "https://api.github.com/repos/chmouel/scratchmyback/issues/events{/number}",
+    "events_url": "https://api.github.com/repos/chmouel/scratchmyback/events",
+    "assignees_url": "https://api.github.com/repos/chmouel/scratchmyback/assignees{/user}",
+    "branches_url": "https://api.github.com/repos/chmouel/scratchmyback/branches{/branch}",
+    "tags_url": "https://api.github.com/repos/chmouel/scratchmyback/tags",
+    "blobs_url": "https://api.github.com/repos/chmouel/scratchmyback/git/blobs{/sha}",
+    "git_tags_url": "https://api.github.com/repos/chmouel/scratchmyback/git/tags{/sha}",
+    "git_refs_url": "https://api.github.com/repos/chmouel/scratchmyback/git/refs{/sha}",
+    "trees_url": "https://api.github.com/repos/chmouel/scratchmyback/git/trees{/sha}",
+    "statuses_url": "https://api.github.com/repos/chmouel/scratchmyback/statuses/{sha}",
+    "languages_url": "https://api.github.com/repos/chmouel/scratchmyback/languages",
+    "stargazers_url": "https://api.github.com/repos/chmouel/scratchmyback/stargazers",
+    "contributors_url": "https://api.github.com/repos/chmouel/scratchmyback/contributors",
+    "subscribers_url": "https://api.github.com/repos/chmouel/scratchmyback/subscribers",
+    "subscription_url": "https://api.github.com/repos/chmouel/scratchmyback/subscription",
+    "commits_url": "https://api.github.com/repos/chmouel/scratchmyback/commits{/sha}",
+    "git_commits_url": "https://api.github.com/repos/chmouel/scratchmyback/git/commits{/sha}",
+    "comments_url": "https://api.github.com/repos/chmouel/scratchmyback/comments{/number}",
+    "issue_comment_url": "https://api.github.com/repos/chmouel/scratchmyback/issues/comments{/number}",
+    "contents_url": "https://api.github.com/repos/chmouel/scratchmyback/contents/{+path}",
+    "compare_url": "https://api.github.com/repos/chmouel/scratchmyback/compare/{base}...{head}",
+    "merges_url": "https://api.github.com/repos/chmouel/scratchmyback/merges",
+    "archive_url": "https://api.github.com/repos/chmouel/scratchmyback/{archive_format}{/ref}",
+    "downloads_url": "https://api.github.com/repos/chmouel/scratchmyback/downloads",
+    "issues_url": "https://api.github.com/repos/chmouel/scratchmyback/issues{/number}",
+    "pulls_url": "https://api.github.com/repos/chmouel/scratchmyback/pulls{/number}",
+    "milestones_url": "https://api.github.com/repos/chmouel/scratchmyback/milestones{/number}",
+    "notifications_url": "https://api.github.com/repos/chmouel/scratchmyback/notifications{?since,all,participating}",
+    "labels_url": "https://api.github.com/repos/chmouel/scratchmyback/labels{/name}",
+    "releases_url": "https://api.github.com/repos/chmouel/scratchmyback/releases{/id}",
+    "deployments_url": "https://api.github.com/repos/chmouel/scratchmyback/deployments",
+    "created_at": 1618743465,
+    "updated_at": "2021-05-10T13:18:37Z",
+    "pushed_at": 1621344346,
+    "git_url": "git://github.com/chmouel/scratchmyback.git",
+    "ssh_url": "git@github.com:chmouel/scratchmyback.git",
+    "clone_url": "https://github.com/chmouel/scratchmyback.git",
+    "svn_url": "https://github.com/chmouel/scratchmyback",
+    "homepage": null,
+    "size": 25,
+    "stargazers_count": 0,
+    "watchers_count": 0,
+    "language": null,
+    "has_issues": true,
+    "has_projects": true,
+    "has_downloads": true,
+    "has_wiki": true,
+    "has_pages": false,
+    "forks_count": 0,
+    "mirror_url": null,
+    "archived": false,
+    "disabled": false,
+    "open_issues_count": 1,
+    "license": {
+      "key": "gpl-3.0",
+      "name": "GNU General Public License v3.0",
+      "spdx_id": "GPL-3.0",
+      "url": "https://api.github.com/licenses/gpl-3.0",
+      "node_id": "MDc6TGljZW5zZTk="
+    },
+    "forks": 0,
+    "open_issues": 1,
+    "watchers": 0,
+    "default_branch": "main",
+    "stargazers": 0,
+    "master_branch": "main"
+  },
+  "pusher": {
+    "name": "chmouel",
+    "email": "chmouel@chmouel.com"
+  },
+  "sender": {
+    "login": "chmouel",
+    "id": 98980,
+    "node_id": "MDQ6VXNlcjk4OTgw",
+    "avatar_url": "https://avatars.githubusercontent.com/u/98980?v=4",
+    "gravatar_id": "",
+    "url": "https://api.github.com/users/chmouel",
+    "html_url": "https://github.com/chmouel",
+    "followers_url": "https://api.github.com/users/chmouel/followers",
+    "following_url": "https://api.github.com/users/chmouel/following{/other_user}",
+    "gists_url": "https://api.github.com/users/chmouel/gists{/gist_id}",
+    "starred_url": "https://api.github.com/users/chmouel/starred{/owner}{/repo}",
+    "subscriptions_url": "https://api.github.com/users/chmouel/subscriptions",
+    "organizations_url": "https://api.github.com/users/chmouel/orgs",
+    "repos_url": "https://api.github.com/users/chmouel/repos",
+    "events_url": "https://api.github.com/users/chmouel/events{/privacy}",
+    "received_events_url": "https://api.github.com/users/chmouel/received_events",
+    "type": "User",
+    "site_admin": false
+  },
+  "installation": {
+    "id": 16556382,
+    "node_id": "MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMTY1NTYzODI="
+  },
+  "created": false,
+  "deleted": false,
+  "forced": false,
+  "base_ref": null,
+  "compare": "https://github.com/chmouel/scratchmyback/compare/00bacbb7fc5f...6fe4cf6bdee2",
+  "commits": [
+    {
+      "id": "6fe4cf6bdee23d427228ccac175e288c07c1c95f",
+      "tree_id": "f81571691a70613b79e197fedbada5224584a6fc",
+      "distinct": true,
+      "message": "update\n\nSigned-off-by: Chmouel Boudjnah <chmouel@chmouel.com>",
+      "timestamp": "2021-05-18T15:25:39+02:00",
+      "url": "https://github.com/chmouel/scratchmyback/commit/6fe4cf6bdee23d427228ccac175e288c07c1c95f",
+      "author": {
+        "name": "Chmouel Boudjnah",
+        "email": "chmouel@chmouel.com",
+        "username": "chmouel"
+      },
+      "committer": {
+        "name": "Chmouel Boudjnah",
+        "email": "chmouel@chmouel.com",
+        "username": "chmouel"
+      },
+      "added": [
+
+      ],
+      "removed": [
+
+      ],
+      "modified": [
+        "OWNERS"
+      ]
+    }
+  ],
+  "head_commit": {
+    "id": "6fe4cf6bdee23d427228ccac175e288c07c1c95f",
+    "tree_id": "f81571691a70613b79e197fedbada5224584a6fc",
+    "distinct": true,
+    "message": "update\n\nSigned-off-by: Chmouel Boudjnah <chmouel@chmouel.com>",
+    "timestamp": "2021-05-18T15:25:39+02:00",
+    "url": "https://github.com/chmouel/scratchmyback/commit/6fe4cf6bdee23d427228ccac175e288c07c1c95f",
+    "author": {
+      "name": "Chmouel Boudjnah",
+      "email": "chmouel@chmouel.com",
+      "username": "chmouel"
+    },
+    "committer": {
+      "name": "Chmouel Boudjnah",
+      "email": "chmouel@chmouel.com",
+      "username": "chmouel"
+    },
+    "added": [
+
+    ],
+    "removed": [
+
+    ],
+    "modified": [
+      "OWNERS"
+    ]
+  }
+}