Repository: openshift-pipelines/pipelines-as-code
Pull request #1252 (2023-04-26 02:54:38)

Make pod logs configurable with config map.
<!-- 🎉🎉🎉 Thank you for the PR!!! 🎉🎉🎉 -->

# Changes
Make pipelines-as-code log level configurable with help of configmap. 

# Demo

https://youtu.be/pHId9-WT-Ds

<!-- Describe your changes here- ideally you can get that description straight from
your descriptive commit message(s)! -->

# Submitter Checklist

- [ ] ♽  Run `make test lint` before submitting a PR (ie: with [pre-commit](https://pipelinesascode.com/dev/tools), no need to waste CPU cycle on CI
- [ ] 📖 If you are adding a user facing feature or make a change of the behavior, please verify that you have documented it
- [ ] 🧪 100% coverage is not a target but most of the time we would rather have a unit test if you make a code change.
- [ ] 🎁 If that's something that is possible to do please ensure to check if we can add a e2e test.
- [ ] 🔎 If there is a flakiness in the CI tests then don't *necessary* ignore it, better get the flakyness fixed before merging or if that's not possible there is a good reason to bypass it. (token rate limitation may be a good reason to skip).


chmouel: /retest
pipelines-as-code[bot]: <!-- info:golang-cover-diff -->
# Golang test coverage difference report
Coverage increased by `0.01%`. :medal_sports: Keep it up :medal_sports:

<details>
<summary>Package report</summary>

```
package                                                                            before    after    delta
-------                                                                           -------  -------  -------
pkg/acl                                                                           100.00%  100.00%         
pkg/action                                                                         68.75%   68.75%         
pkg/adapter                                                                        72.41%   72.41%         
pkg/apis/features                                                                 100.00%  100.00%         
pkg/cli/info                                                                       88.23%   88.23%         
pkg/cli/prompt                                                                     55.38%   55.38%         
pkg/cli/status                                                                     95.23%   95.23%         
pkg/cli/webhook                                                                    49.40%   49.40%         
pkg/cmd/tknpac/bootstrap                                                            2.11%    2.11%         
pkg/cmd/tknpac/completion                                                          50.00%   50.00%         
pkg/cmd/tknpac/create                                                              44.14%   44.14%         
pkg/cmd/tknpac/describe                                                            46.31%   46.31%         
pkg/cmd/tknpac/generate                                                            62.20%   62.20%         
pkg/cmd/tknpac/info                                                                62.50%   62.50%         
pkg/cmd/tknpac/list                                                                46.47%   46.47%         
pkg/cmd/tknpac/resolve                                                             71.42%   71.42%         
pkg/cmd/tknpac/webhook                                                             52.47%   52.47%         
pkg/consoleui                                                                      82.60%   82.60%         
pkg/events                                                                         73.33%   73.33%         
pkg/formatting                                                                     98.71%   98.71%         
pkg/git                                                                            84.84%   84.84%         
pkg/hub                                                                            90.00%   90.00%         
pkg/kubeinteraction                                                                52.50%   52.50%         
pkg/kubeinteraction/status                                                         77.27%   77.27%         
pkg/matcher                                                                        86.47%   86.47%         
pkg/params/clients                                                                 14.86%   14.86%         
pkg/params/settings                                                                79.48%   79.48%         
pkg/pipelineascode                                                                 64.95%   64.95%         
pkg/provider                                                                       76.19%   76.19%         
pkg/provider/bitbucketcloud                                                        87.09%   87.09%         
pkg/provider/bitbucketserver                                                       88.88%   88.88%         
pkg/provider/gitea                                                                 34.14%   34.14%         
pkg/provider/github                                                                82.05%   82.05%         
pkg/provider/github/app                                                            78.33%   78.33%         
pkg/provider/gitlab                                                                86.64%   86.64%         
pkg/random                                                                        100.00%  100.00%         
pkg/reconciler                                                                     42.05%   42.19%   +0.14%
pkg/resolve                                                                        87.93%   87.93%         
pkg/secrets                                                                        92.85%   92.85%         
pkg/sort                                                                           51.20%   51.20%         
pkg/sync                                                                           91.13%   91.13%         
pkg/templates                                                                     100.00%  100.00%         
pkg/webhook                                                                        22.22%   22.22%         
                                                                          total:   65.25%   65.26%   +0.01%
```
</details>
savitaashture: > @savitaashture is it coherent with operator configuration?

Yes now its coherent with Operator, Pipeline, Triggers because now with this PR is uses config-logging cm for showing logs 


But i have a question to @AndrienkoAleksandr that once controller starts and if i edit cm `pac-config-logging` for configuration i see its not effected 
savitaashture: cc @vdemeester 
AndrienkoAleksandr: > But i have a question to @AndrienkoAleksandr that once controller starts and if i edit cm pac-config-logging for configuration i see its not effected

I provided doc with explanation and I attached demo. I think this stuff should work.

Patch:
diff --git a/.tekton/generate-coverage-release.yaml b/.tekton/generate-coverage-release.yaml
index 79063f51..9204aae9 100644
--- a/.tekton/generate-coverage-release.yaml
+++ b/.tekton/generate-coverage-release.yaml
@@ -106,6 +106,7 @@ spec:
           steps:
             - image: registry.access.redhat.com/ubi9/python-39
               name: release
+              workingDir: $(workspaces.source.path)
               env:
                 - name: HUB_TOKEN
                   valueFrom:
@@ -115,11 +116,11 @@ spec:
               script: |
                 #!/usr/bin/env bash
                 set -eux
+                ssa=$(git rev-parse --short HEAD)
                 bash hack/generate-releaseyaml.sh > release.k8s.yaml
                 env TARGET_OPENSHIFT=true bash hack/generate-releaseyaml.sh > release.yaml
                 set +x # Do not show TOKEN in logs
-                python hack/upload-file-to-github.py --message "Release yaml generated from {{repo_url}}/commit/{{revision}}" --from-ref refs/heads/main --to-ref refs/heads/nightly --owner-repository openshift-pipelines/pipelines-as-code --token ${HUB_TOKEN} -f release.k8s.yaml:release.k8s.yaml -f release.yaml:release.yaml -f <(echo nightly):docs/content/VERSION
-              workingDir: $(workspaces.source.path)
+                python hack/upload-file-to-github.py --message "Release yaml generated from {{repo_url}}/commit/{{revision}}" --from-ref refs/heads/main --to-ref refs/heads/nightly --owner-repository openshift-pipelines/pipelines-as-code --token ${HUB_TOKEN} -f release.k8s.yaml:release.k8s.yaml -f release.yaml:release.yaml -f nightly:docs/content/VERSION -f "nightly-$ssa-$(date +%Y%m%d):pkg/params/version/version.txt"
           workspaces:
             - name: source
         workspaces:
diff --git a/.tekton/release-pipeline.yaml b/.tekton/release-pipeline.yaml
index 71b9839b..d35884f3 100644
--- a/.tekton/release-pipeline.yaml
+++ b/.tekton/release-pipeline.yaml
@@ -77,8 +77,8 @@ spec:
                   env TARGET_OPENSHIFT=true bash hack/generate-releaseyaml.sh > /tmp/release.yaml
                   msg="pac release ${version} on branch ${target}"
                   set +x
-                  echo python hack/upload-file-to-github.py --message "Release yaml generated for ${msg}" --owner-repository openshift-pipelines/pipelines-as-code --token "TOKEN" --to-ref=refs/heads/${target} --from-ref=refs/tags/${version} -f release.k8s.yaml:release.k8s.yaml -f release.yaml:release.yaml -f <(echo ${TARGET_BRANCH}):docs/content/VERSION
-                  python hack/upload-file-to-github.py --message "Release yaml generated for ${msg}" --owner-repository openshift-pipelines/pipelines-as-code --token ${HUB_TOKEN} --to-ref=refs/heads/${target} --from-ref=refs/tags/${version} -f /tmp/release.k8s.yaml:release.k8s.yaml -f /tmp/release.yaml:release.yaml -f <(echo ${TARGET_BRANCH}):docs/content/VERSION
+                  echo python hack/upload-file-to-github.py --message "Release yaml generated for ${msg}" --owner-repository openshift-pipelines/pipelines-as-code --token "TOKEN" --to-ref=refs/heads/${target} --from-ref=refs/tags/${version} -f release.k8s.yaml:release.k8s.yaml -f release.yaml:release.yaml -f ${TARGET_BRANCH}:docs/content/VERSION -f "${version}:pkg/params/version/version.txt"
+                  python hack/upload-file-to-github.py --message "Release yaml generated for ${msg}" --owner-repository openshift-pipelines/pipelines-as-code --token ${HUB_TOKEN} --to-ref=refs/heads/${target} --from-ref=refs/tags/${version} -f /tmp/release.k8s.yaml:release.k8s.yaml -f /tmp/release.yaml:release.yaml -f ${TARGET_BRANCH}:docs/content/VERSION -f "${version}:pkg/params/version/version.txt"
                   set -x
                 done
       - name: gorelease
diff --git a/Makefile b/Makefile
index 333a9cf7..4df1b01e 100644
--- a/Makefile
+++ b/Makefile
@@ -102,7 +102,7 @@ lint-py: ${PY_FILES} ## runs pylint on all python files
 	@pylint $(PY_FILES)
 
 .PHONY: update-golden
-update-golden: ./vendor ## run unit tests (updating golden files)
+update-golden: ## run unit tests (updating golden files)
 	@echo "Running unit tests to update golden files..."
 	@./hack/update-golden.sh
 
@@ -110,7 +110,7 @@ update-golden: ./vendor ## run unit tests (updating golden files)
 generated: update-golden fumpt ## generate all files that needs to be generated
 
 .PHONY: html-coverage
-html-coverage: ./vendor ## generate html coverage
+html-coverage: ## generate html coverage
 	@mkdir -p tmp
 	@go test -coverprofile=tmp/c.out ./.../ && go tool cover -html=tmp/c.out
 
diff --git a/cmd/pipelines-as-code-controller/main.go b/cmd/pipelines-as-code-controller/main.go
index b2c7ec75..d1e92db9 100644
--- a/cmd/pipelines-as-code-controller/main.go
+++ b/cmd/pipelines-as-code-controller/main.go
@@ -1,13 +1,17 @@
 package main
 
 import (
+	"context"
 	"log"
 
 	"github.com/openshift-pipelines/pipelines-as-code/pkg/adapter"
 	"github.com/openshift-pipelines/pipelines-as-code/pkg/kubeinteraction"
 	"github.com/openshift-pipelines/pipelines-as-code/pkg/params"
 	evadapter "knative.dev/eventing/pkg/adapter/v2"
+	"knative.dev/pkg/client/injection/kube/client"
+	"knative.dev/pkg/logging"
 	"knative.dev/pkg/signals"
+	"knative.dev/pkg/system"
 )
 
 const (
@@ -28,5 +32,16 @@ func main() {
 		log.Fatal("failed to init kinit client : ", err)
 	}
 
+	loggerConfiguratorOpt := evadapter.WithLoggerConfiguratorConfigMapName(logging.ConfigMapName())
+	loggerConfigurator := evadapter.NewLoggerConfiguratorFromConfigMap(PACControllerLogKey, loggerConfiguratorOpt)
+	copt := evadapter.WithLoggerConfigurator(loggerConfigurator)
+	// put logger configurator to ctx
+	ctx = evadapter.WithConfiguratorOptions(ctx, []evadapter.ConfiguratorOption{copt})
+	// set up kubernetes interface to retrieve configmap with log configuration
+	ctx = context.WithValue(ctx, client.Key{}, run.Clients.Kube)
+
+	ctx = evadapter.WithNamespace(ctx, system.Namespace())
+	ctx = evadapter.WithConfigWatcherEnabled(ctx)
+
 	evadapter.MainWithContext(ctx, PACControllerLogKey, adapter.NewEnvConfig, adapter.New(run, kinteract))
 }
diff --git a/code-of-conduct.md b/code-of-conduct.md
index 1a30fdd2..f3cc0835 100644
--- a/code-of-conduct.md
+++ b/code-of-conduct.md
@@ -44,7 +44,7 @@ event. Representation of a project may be further defined and clarified by proje
 ## Enforcement
 
 Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at
-pipelines-dev@redhat.com. All complaints will be reviewed and investigated and will result in a response
+<pipelines-dev@redhat.com>. All complaints will be reviewed and investigated and will result in a response
 that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality
 with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
 
diff --git a/config/300-repositories.yaml b/config/300-repositories.yaml
index 4138c4b6..ac809fd4 100644
--- a/config/300-repositories.yaml
+++ b/config/300-repositories.yaml
@@ -75,6 +75,12 @@ spec:
                       items:
                         description: list of repositories where Github token can be scoped
                         type: string
+                    pipelinerun_provenance:
+                      description: From where the PipelineRun definitions will be coming from
+                      type: string
+                      enum:
+                        - source
+                        - default_branch
                 concurrency_limit:
                   description: Number of maximum pipelinerun running at any moment
                   type: integer
diff --git a/config/301-config-logging.yaml b/config/301-config-logging.yaml
new file mode 100644
index 00000000..a85849d7
--- /dev/null
+++ b/config/301-config-logging.yaml
@@ -0,0 +1,38 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: pac-config-logging
+  namespace: pipelines-as-code
+  labels:
+    app.kubernetes.io/instance: default
+    app.kubernetes.io/part-of: pipelines-as-code
+data:
+  zap-logger-config: |
+    {
+      "level": "info",
+      "development": false,
+      "sampling": {
+        "initial": 100,
+        "thereafter": 100
+      },
+      "outputPaths": ["stdout"],
+      "errorOutputPaths": ["stderr"],
+      "encoding": "json",
+      "encoderConfig": {
+        "timeKey": "ts",
+        "levelKey": "level",
+        "nameKey": "logger",
+        "callerKey": "caller",
+        "messageKey": "msg",
+        "stacktraceKey": "stacktrace",
+        "lineEnding": "",
+        "levelEncoder": "",
+        "timeEncoder": "iso8601",
+        "durationEncoder": "",
+        "callerEncoder": ""
+      }
+    }
+  # Log level overrides
+  loglevel.pipelinesascode: "info"
+  loglevel.pac-watcher: "info"
+  loglevel.pipelines-as-code-webhook: "info"
diff --git a/config/302-pac-configmap.yaml b/config/302-pac-configmap.yaml
index 6b1d8d1f..ba9aae6c 100644
--- a/config/302-pac-configmap.yaml
+++ b/config/302-pac-configmap.yaml
@@ -11,6 +11,9 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
+#
+# See https://pipelinesascode.com/docs/install/settings/ for the complete
+# documentation of all the settings.
 
 apiVersion: v1
 data:
@@ -88,25 +91,28 @@ data:
   # Add extra IPS (ie: 127.0.0.1) or networks (127.0.0.0/16) separated by commas.
   bitbucket-cloud-additional-source-ip: ""
 
-  # max-keep-run-upper-limit defines the upper limit for max-keep-run annotation value which a user can set on
-  # pipelineRun. the value set on annotation should be less than or equal to the upper limit otherwise
-  # the upper limit will be used while cleaning up
+  # max-keep-run-upper-limit defines the upper limit for max-keep-run annotation
+  # value which a user can set on pipelineRun. the value set on annotation
+  # should be less than or equal to the upper limit otherwise the upper limit
+  # will be used while cleaning up
   max-keep-run-upper-limit: ""
 
   # if defined then applies to all pipelineRun who doesn't have max-keep-runs annotation
   default-max-keep-runs: ""
 
-  # Whether to auto configure newly created repositories, this will create a new namespace
-  # and repository CR, supported only with GitHub App
+  # Whether to auto configure newly created repositories, this will create a new
+  # namespace and repository CR, supported only with GitHub App
   auto-configure-new-github-repo: "false"
 
-  # add a template to generate name for namespace for your auto configured github repo
-  # supported fields are repo_owner, repo_name
-  # eg. if defined as `{{repo_owner}}-{{repo_name}}-ci`, then namespace generated for repository
+  # add a template to generate name for namespace for your auto configured
+  # github repo supported fields are repo_owner, repo_name eg. if defined as
+  # `{{repo_owner}}-{{repo_name}}-ci`, then namespace generated for repository
   # https://github.com/owner/repo will be `owner-repo-ci`
   auto-configure-repo-namespace-template: ""
 
-  # Configure a custom console here
+  # Configure a custom console here, the driver support custom parameters from
+  # Repo CR along a few other template variable, see documentation for more
+  # details
   #
   # custom-console-name: Console Name
   # custom-console-url: https://url
diff --git a/config/400-controller.yaml b/config/400-controller.yaml
index 01a2ac1f..a69bf810 100644
--- a/config/400-controller.yaml
+++ b/config/400-controller.yaml
@@ -73,6 +73,8 @@ spec:
             successThreshold: 1
             timeoutSeconds: 1
           env:
+            - name: CONFIG_LOGGING_NAME
+              value: pac-config-logging
             - name: TLS_KEY
               value: "key"
             - name: TLS_CERT
diff --git a/config/500-watcher.yaml b/config/500-watcher.yaml
index fbdaafdb..f092c530 100644
--- a/config/500-watcher.yaml
+++ b/config/500-watcher.yaml
@@ -48,6 +48,8 @@ spec:
           image: "ko://github.com/openshift-pipelines/pipelines-as-code/cmd/pipelines-as-code-watcher"
           imagePullPolicy: Always
           env:
+          - name: CONFIG_LOGGING_NAME
+            value: pac-config-logging
           - name: SYSTEM_NAMESPACE
             valueFrom:
               fieldRef:
diff --git a/config/600-webhook.yaml b/config/600-webhook.yaml
index 7579d40b..47043cac 100644
--- a/config/600-webhook.yaml
+++ b/config/600-webhook.yaml
@@ -46,6 +46,8 @@ spec:
         - name: pac-webhook
           image: "ko://github.com/openshift-pipelines/pipelines-as-code/cmd/pipelines-as-code-webhook"
           env:
+            - name: CONFIG_LOGGING_NAME
+              value: pac-config-logging
             - name: SYSTEM_NAMESPACE
               valueFrom:
                 fieldRef:
diff --git a/docs/content/dev/_index.md b/docs/content/dev/_index.md
index 032a9aca..9bbb1cc5 100644
--- a/docs/content/dev/_index.md
+++ b/docs/content/dev/_index.md
@@ -60,6 +60,95 @@ override it you can set the `PAC_DIRS` environment variable.
   instead of ko. `-c` will only do the pac configuration (ie: creation of
   secrets/ingress etc..)
 
+## Configuring debug logging
+
+pipeline-as-code uses ConfigMap named `pac-config-logging` in the same namespace (`pipelines-as-code` by default) with controllers. To get configmap use the command:
+
+```bash
+$ kubectl get configmap pac-config-logging -n pipelines-as-code
+
+NAME                 DATA   AGE
+pac-config-logging   4      9m44s
+```
+
+To retrieve configmap content:
+
+```bash
+$ kubectl get configmap pac-config-logging -n pipelines-as-code -o yaml
+
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  labels:
+    app.kubernetes.io/instance: default
+    app.kubernetes.io/part-of: pipelines-as-code
+  name: pac-config-logging
+  namespace: pipelines-as-code
+data:
+  loglevel.pac-watcher: info
+  loglevel.pipelines-as-code-webhook: info
+  loglevel.pipelinesascode: info
+  zap-logger-config: |
+    {
+      "level": "info",
+      "development": false,
+      "sampling": {
+        "initial": 100,
+        "thereafter": 100
+      },
+      "outputPaths": ["stdout"],
+      "errorOutputPaths": ["stderr"],
+      "encoding": "json",
+      "encoderConfig": {
+        "timeKey": "ts",
+        "levelKey": "level",
+        "nameKey": "logger",
+        "callerKey": "caller",
+        "messageKey": "msg",
+        "stacktraceKey": "stacktrace",
+        "lineEnding": "",
+        "levelEncoder": "",
+        "timeEncoder": "iso8601",
+        "durationEncoder": "",
+        "callerEncoder": ""
+      }
+    }
+```
+
+Controllers log level defined in the `loglevel.*` fields:
+
+- loglevel.pipelinesascode - log level for pipelines-as-code-controller component
+- loglevel.pipelines-as-code-webhook - log level for pipelines-as-code-webhook component
+- loglevel.pac-watcher - log level for pipelines-as-code-watcher component
+
+You can change log level from `info` to `debug` or any other supported values. For example, set up log level `debug` for pipelines-as-code-watcher component:
+
+```bash
+kubectl patch configmap pac-config-logging -n pipelines-as-code --type json -p '[{"op": "replace", "path": "/data/loglevel.pac-watcher", "value":"debug"}]'
+```
+
+After that coresponding controller should get a new log level value.
+If you want to use the same log level for all pipelines-as-code components, then you have
+to delete `level.*` values from configmap:
+
+```bash
+kubectl patch configmap pac-config-logging -n pipelines-as-code --type json -p '[  {"op": "remove", "path": "/data/loglevel.pac-watcher"},  {"op": "remove", "path": "/data/loglevel.pipelines-as-code-webhook"},  {"op": "remove", "path": "/data/loglevel.pipelinesascode"}]'
+```
+
+In this case pipelines-as-code components should get common log level from `zap-logger-config` - `level` field from the json.
+
+List zap supported log level values:
+
+- debug - fine-grained debugging
+- info - normal logging
+- warn - unexpected but non-critical errors
+- error - critical errors; unexpected during normal operation
+- dpanic - in debug mode, trigger a panic (crash)
+- panic - trigger a panic (crash)
+- fatal - immediately exit with exit status 1 (failure)
+
+See more: <https://knative.dev/docs/serving/observability/logging/config-logging>
+
 ## Gitea
 
 Gitea is "unofficially" supported. You just need to configure Gitea the same way
diff --git a/docs/content/docs/guide/authoringprs.md b/docs/content/docs/guide/authoringprs.md
index cd31c632..3cea3df4 100644
--- a/docs/content/docs/guide/authoringprs.md
+++ b/docs/content/docs/guide/authoringprs.md
@@ -170,10 +170,10 @@ You can find more information about the CEL language spec here :
 
 <https://github.com/google/cel-spec/blob/master/doc/langdef.md>
 
-## Using the temporary Github APP Token for Github API operations
+## Using the temporary GitHub APP Token for GitHub API operations
 
 You can use the temporary installation token that is generated by Pipelines as
-Code from the Github App to access the Github API.
+Code from the GitHub App to access the GitHub API.
 
 The token value is stored into the temporary git-auth secret as generated for [private
 repositories](../privaterepo/) in the key `git-provider-token`.
diff --git a/docs/content/docs/guide/cli.md b/docs/content/docs/guide/cli.md
index b1ed23c5..f0017b3d 100644
--- a/docs/content/docs/guide/cli.md
+++ b/docs/content/docs/guide/cli.md
@@ -110,14 +110,23 @@ Bootstrap detect the OpenShift Route automatically associated to the Pipelines
 as code controller service and uses this as endpoint for the created Github
 application.
 
-On Kubernetes, tkn-pac will attempt to detect the tekton-dashboard Ingress URL.
-
-It will also ask you if you want to install a webhook forwarder called
-[gosmee](https://github.com/chmouel/gosmee) on <https://hook.pipelinesascode.com>.
-This allows you to connect the Pipelines as Code controller to GitHub even if
-it's is not directly accessible from the internet.
-
-You can override the URL with the flag `--route-url`
+You can use the `--route-url` flag to replace the OpenShift Route URL or specify
+a custom URL on an
+[Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/) in a
+Kubernetes cluster.
+
+The OpenShift console is automaticaly detected, on Kubernetes, `tkn-pac` will
+attempt to detect the tekton-dashboard Ingress URL and let you choose to use it
+as the endpoint for the created GitHub application.
+
+If your cluster is not accessible to the internet, Pipelines as Code provides an
+option to install a webhook forwarder called
+[gosmee](https://github.com/chmouel/gosmee). This forwarder enables connectivity
+between the Pipelines as Code controller and GitHub without requiring an
+internet connection. In this scenario, it will set up a forwarding URL on
+<https://hook.pipelinesascode.com> and set it up on Github. For OpenShift, it
+will not prompt you unless you explicitly specify the `--force-gosmee` flag
+(which can be useful if you are running [OpenShift Local](https://developers.redhat.com/products/openshift-local/overview) for instance).
 
 {{< /details >}}
 
@@ -328,9 +337,9 @@ of all Repositories CR created on the cluster and their associated URLs.
 
 If your have your installation set-up with a [GitHub App](../../install/github_apps),
 you will be able to the see details of the installed application along with
-other relevant information like the URL endpoint configured for your Github APP.
-By default, this will display information from the public Github API, but you
-can specify a custom Github API URL using the `--github-api-url` argument.
+other relevant information like the URL endpoint configured for your GitHub APP.
+By default, this will display information from the public GitHub API, but you
+can specify a custom GitHub API URL using the `--github-api-url` argument.
 
 It's important to note that only administrators with permission to read the
 `pipelines-as-code-secret` secret and list all Repository CR on the cluster are
diff --git a/docs/content/docs/guide/incoming_webhook.md b/docs/content/docs/guide/incoming_webhook.md
index ff3044d1..f35904bc 100644
--- a/docs/content/docs/guide/incoming_webhook.md
+++ b/docs/content/docs/guide/incoming_webhook.md
@@ -104,7 +104,7 @@ task can be added directly to the Pipeline, or the Repo CRD can be inspected
 using the tkn pac CLI. The [statuses](/docs/guide/statuses) documentation
 provides guidance on how to achieve this.
 
-### Webhook methods (Github Webhook, Gitlab, Bitbucket etc..)
+### Webhook methods (GitHub Webhook, Gitlab, Bitbucket etc..)
 
 Here is an example of a Repository CRD matching the target branch main:
 
diff --git a/docs/content/docs/guide/repositorycrd.md b/docs/content/docs/guide/repositorycrd.md
index ce4e0c5d..56ca220f 100644
--- a/docs/content/docs/guide/repositorycrd.md
+++ b/docs/content/docs/guide/repositorycrd.md
@@ -42,7 +42,7 @@ specific branch and event like a `push` or `pull_request`. It wil start the
 `PipelineRun` where the `Repository` CR has been created. You can only start the
 `PipelineRun` in the namespace where the Repository CR is located.
 
-## Additional Repository CR Security
+## Setting PipelineRun definition source
 
 An additional layer of security can be added by using a PipelineRun annotation
 to explicitly target a specific namespace. However, a Repository CRD must still
@@ -72,6 +72,45 @@ same URL. However, only the oldest created Repository CRD will be matched,
 unless you use the `target-namespace` annotation.
 {{< /hint >}}
 
+### PipelineRun definition provenance
+
+By default on a `Push` or a `Pull Request`, Pipelines as Code will fetch the
+PipelineRun definition from the branch of where the event has been triggered.
+
+This behavior can be changed by setting the setting `pipelinerun_provenance`.
+The setting currently accept two values:
+
+- `source`: The default behavior, the PipelineRun definition will be fetched
+  from the branch of where the event has been triggered.
+- `default_branch`: The PipelineRun definition will be fetched from the default
+  branch of the repository as configured on the git platform. For example
+  `main`, `master`, or `trunk`.
+
+Example:
+
+This configuration specifies a repository named my-repo with a URL of
+<https://github.com/my-org/my-repo>. It also sets the `pipelinerun_provenance`
+setting to `default_branch`, which means that the PipelineRun definition will be
+fetched from the default branch of the repository.
+
+```yaml
+apiVersion: "pipelinesascode.tekton.dev/v1alpha1"
+kind: Repository
+metadata:
+  name: my-repo
+spec:
+  url: "https://github.com/owner/repo"
+  settings:
+    pipelinerun_provenance: "default_branch"
+```
+
+{{< hint info >}}
+Letting the user specify the provenance of the PipelineRun definition to default
+branch is another layer of security. It ensures that only the one who has the
+right to merge commit to the default branch can change the PipelineRun and have
+access to the infrastrucutre.
+{{< /hint >}}
+
 ## Concurrency
 
 `concurrency_limit` allows you to define the maximum number of PipelineRuns running at any time for a Repository.
@@ -167,7 +206,7 @@ for all the variable exposed by default.
 
 The body of the payload is exposed inside the `body` prefix.
 
-For example if you are running a Pull Request on Github pac will receive a
+For example if you are running a Pull Request on GitHub pac will receive a
 payload which has this kind of json:
 
 ```json
@@ -202,33 +241,33 @@ and a pull request event.
 
 {{< hint info >}}
 
-- [Github Documentation for webhook events](https://docs.github.com/webhooks-and-events/webhooks/webhook-events-and-payloads?actionType=auto_merge_disabled#pull_request)
+- [GitHub Documentation for webhook events](https://docs.github.com/webhooks-and-events/webhooks/webhook-events-and-payloads?actionType=auto_merge_disabled#pull_request)
 - [Gitlab Documentation for webhook events](https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html)
 {{< /hint >}}
 
-## Scope Github token to a list of private and public repositories within and outside namespaces
+## Scope GitHub token to a list of private and public repositories within and outside namespaces
 
-By default, the Github token that Pipelines as Code generates is scoped only to the repository where the payload comes from.
+By default, the GitHub token that Pipelines as Code generates is scoped only to the repository where the payload comes from.
 However, in some cases, the developer team might want the token to allow control over additional repositories.
 For example, there might be a CI repository where the `.tekton/pr.yaml` file and source payload might be located, however the build process defined in `pr.yaml` might fetch tasks from a separate private CD repository.
 
-You can extend the scope of the Github token in two ways:
+You can extend the scope of the GitHub token in two ways:
 
-- _Global configuration_: extend the Github token to a list of repositories in different namespaces and admin have access to set this configuration.
+- _Global configuration_: extend the GitHub token to a list of repositories in different namespaces and admin have access to set this configuration.
 
-- _Repository level configuration_: extend the Github token to a list of repositories that exist in the same namespace as the original repository
+- _Repository level configuration_: extend the GitHub token to a list of repositories that exist in the same namespace as the original repository
 and both admin, non-admin have access to set this configuration.
 
-### Note
-
-Ignore Scoping of Github token for webhook based usecases.
+{{< hint info >}}
+when using a GitHub webhook, the scoping of the token is what you set when creating your [fine grained personal access token](https://github.blog/2022-10-18-introducing-fine-grained-personal-access-tokens-for-github/#creating-personal-access-tokens).
+{{</ hint >}}
 
-### Prerequisite
+Prerequisite
 
 - In the `pipelines-as-code` configmap, set the `secret-github-app-token-scoped` key to `false`.
-This setting enables the scoping of the Github token to private and public repositories listed under the Global and Repository level configuration.
+This setting enables the scoping of the GitHub token to private and public repositories listed under the Global and Repository level configuration.
 
-#### Scoping the Github token using Global configuration
+### Scoping the GitHub token using Global configuration
 
 You can use the global configuration to use as a list of repositories used from any Repository CR in any namespaces.
 
@@ -244,9 +283,9 @@ To set the global configuration, in the `pipelines-as-code` configmap, set the `
     secret-github-app-scope-extra-repos: "owner2/project2, owner3/project3"
   ```
 
-#### Scoping the Github token using Repository level configuration
+### Scoping the GitHub token using Repository level configuration
 
-You can use the `Repository` custom resource to scope the generated Github token to a list of repositories.
+You can use the `Repository` custom resource to scope the generated GitHub token to a list of repositories.
 The repositories can be public or private, but must reside in the same namespace as the repository with which the `Repository` resource is associated.
 
 Set the `github_app_token_scope_repos` spec configuration within the `Repository` custom resource, as in the following example:
@@ -266,17 +305,17 @@ Set the `github_app_token_scope_repos` spec configuration within the `Repository
   ```
 
 In this example, the `Repository` custom resource is associated with the `linda/project` repository in the `test-repo` namespace.
-The scope of the generated Github token is extended to the `owner/project` and `owner1/project1` repositories, as well as the `linda/project` repository. These repositories must exist under the `test-repo` namespace.
+The scope of the generated GitHub token is extended to the `owner/project` and `owner1/project1` repositories, as well as the `linda/project` repository. These repositories must exist under the `test-repo` namespace.
 
 **Note:**
 
-If any of the repositories does not exist in the namespace, the scoping of the Github token fails with an error message as in the following example:
+If any of the repositories does not exist in the namespace, the scoping of the GitHub token fails with an error message as in the following example:
 
 ```console
-failed to scope Github token as repo owner1/project1 does not exist in namespace test-repo
+failed to scope GitHub token as repo owner1/project1 does not exist in namespace test-repo
 ```
 
-### Scenarios for providing global and repository level configurations
+### Combining global and repository level configuration
 
 - When you provide both a `secret-github-app-scope-extra-repos` key in the `pipelines-as-code` configmap and
 a `github_app_token_scope_repos` spec configuration in the `Repository` custom resource, the token is scoped to all the repositories from both configurations, as in the following example:
@@ -309,23 +348,23 @@ a `github_app_token_scope_repos` spec configuration in the `Repository` custom r
          - "owner1/project1"
     ```
 
-    The Github token is scoped to the following repositories: `owner/project`, `owner1/project1`, `owner2/project2`, `owner3/project3`, `linda/project`.
+    The GitHub token is scoped to the following repositories: `owner/project`, `owner1/project1`, `owner2/project2`, `owner3/project3`, `linda/project`.
 
 - If you set only the global configuration in the `secret-github-app-scope-extra-repos` key in the `pipelines-as-code` configmap,
-the Github token is scoped to all the listed repositories, as well as the original repository from which the payload files come.
+the GitHub token is scoped to all the listed repositories, as well as the original repository from which the payload files come.
 
 - If you set only the `github_app_token_scope_repos` spec in the `Repository` custom resource,
-the Github token is scoped to all the listed repositories, as well as the original repository from which the payload files come.
+the GitHub token is scoped to all the listed repositories, as well as the original repository from which the payload files come.
 All the repositories must exist in the same namespace where the `Repository` custom resource is created.
 
-- If you did not install the Github app for any repositories that you list in the global or repository level configuration,
-creation of the Github token fails with the following error message:
+- If you did not install the GitHub app for any repositories that you list in the global or repository level configuration,
+creation of the GitHub token fails with the following error message:
 
     ```text
     failed to scope token to repositories in namespace test-repo with error : could not refresh installation id 36523992's token: received non 2xx response status \"422 Unprocessable Entity\" when fetching https://api.github.com/app/installations/36523992/access_tokens: Post \"https://api.github.com/repos/savitaashture/article/check-runs\
     ```
 
-- If the scoping of the Github token to the repositories set in global or repository level configuration fails for any reason,
+- If the scoping of the GitHub token to the repositories set in global or repository level configuration fails for any reason,
 the CI process does not run. This includes cases where the same repository is listed in the global or repository level configuration,
 and the scoping fails for the repository level configuration because the repository is not in the same namespace as the `Repository` custom resource.
 
@@ -354,8 +393,8 @@ and the scoping fails for the repository level configuration because the reposit
       - "owner5/project5"
   ```
 
-  In this example, if the `owner5/project5` repository is not under the `test-repo` namespace, scoping of the Github token fails with the following error message:
+  In this example, if the `owner5/project5` repository is not under the `test-repo` namespace, scoping of the GitHub token fails with the following error message:
 
   ```yaml
-  failed to scope Github token as repo owner5/project5 does not exist in namespace test-repo
+  failed to scope github token as repo owner5/project5 does not exist in namespace test-repo
   ```
diff --git a/docs/content/docs/guide/resolver.md b/docs/content/docs/guide/resolver.md
index f6b6724f..2f01c1c5 100644
--- a/docs/content/docs/guide/resolver.md
+++ b/docs/content/docs/guide/resolver.md
@@ -108,11 +108,11 @@ will fetch the task directly from that remote URL :
   pipelinesascode.tekton.dev/task: "[https://remote.url/task.yaml]"
 ```
 
-### Remote HTTP URL from a private Github repository
+### Remote HTTP URL from a private GitHub repository
 
 If you are using `GitHub` and If the remote task URL uses the same host as where
 the repo CRD is, PAC will use the  GitHub token and fetch the URL using the
-Github API.
+GitHub API.
 
 For example if you have a repo URL looking like this :
 
@@ -122,7 +122,7 @@ and the remote HTTP URLs is a referenced GitHub "blob" URL:
 
 <https://github.com/organization/repository/blob/mainbranch/path/file>
 
-or a Github rawURL (rawurl reference is only working on public GitHub):
+or a GitHub rawURL (rawurl reference is only working on public GitHub):
 
 <https://raw.githubusercontent.com/organization/repository/mainbranch/path/file>
 
@@ -130,7 +130,7 @@ It will be able to fetch the files from that private repository with the GitHub
 
 This allows you to reference a task or a pipeline from a private repository easily.
 
-Github app token are scoped to the owner or organization where the repository is located.
+GitHub app token are scoped to the owner or organization where the repository is located.
 If you are using the GitHub webhook method you are able to fetch any private or
 public repositories on any organization where the personal token is allowed.
 
diff --git a/docs/content/docs/guide/running.md b/docs/content/docs/guide/running.md
index 261efc47..95ad1444 100644
--- a/docs/content/docs/guide/running.md
+++ b/docs/content/docs/guide/running.md
@@ -36,10 +36,10 @@ another user that does meet these requirements can comment `/ok-to-test` on the
 to run the PipelineRun.
 
 {{< hint info >}}
-If you are using the Github Apps and have installed it on an Organization,
+If you are using the GitHub Apps and have installed it on an Organization,
 Pipelines as Code will only be initiated when a Repo CR matching one of the
 repositories in a URL is detected on a repository belonging to the organization
-where the Github App has been installed.
+where the GitHub App has been installed.
 Otherwise, Pipelines as Code will not be triggered.
 {{< /hint >}}
 
@@ -137,6 +137,6 @@ roses are red, violets are blue. why to run the pipeline when the infra is down.
 /cancel <pipelinerun-name>
 ```
 
-On Github App the status of the Pipeline will be set to `cancelled`.
+On GitHub App the status of the Pipeline will be set to `cancelled`.
 
 ![pipelinerun canceled](/images/pr-cancel.png)
diff --git a/docs/content/docs/guide/statuses.md b/docs/content/docs/guide/statuses.md
index 0f62d81e..ada30eab 100644
--- a/docs/content/docs/guide/statuses.md
+++ b/docs/content/docs/guide/statuses.md
@@ -49,7 +49,7 @@ sources.
 
 ![log snippet](/images/snippet-failure-message.png)
 
-### Error detection from containers logs as Github Annotation
+### Error detection from containers logs as GitHub Annotation
 
 If you enable the `error-detection-from-container-logs` option in the
 pipeline-as-code configuration map, Pipelines as Code will attempt to detect
diff --git a/docs/content/docs/install/github_apps.md b/docs/content/docs/install/github_apps.md
index 37adcb24..7508ea6d 100644
--- a/docs/content/docs/install/github_apps.md
+++ b/docs/content/docs/install/github_apps.md
@@ -19,7 +19,7 @@ There are 2 ways to set up GitHub App:
 
 You could use [`tkn pac bootstrap`](/docs/guide/cli) command which will a create GitHub App, provides
 steps to configure it with your Git repository and also creates required secrets.
-After creating the Github App, you must install it on the repositories you want to use for Pipelines as Code.
+After creating the GitHub App, you must install it on the repositories you want to use for Pipelines as Code.
 
 Alternatively, you could set up manually by following the steps [here](#setup-manually).
 
diff --git a/docs/content/docs/install/settings.md b/docs/content/docs/install/settings.md
index 9355d51a..cedcea26 100644
--- a/docs/content/docs/install/settings.md
+++ b/docs/content/docs/install/settings.md
@@ -21,7 +21,7 @@ There is a few things you can configure through the config map
 
 * `secret-github-app-token-scoped`
 
-  When using a Github app, `Pipelines as Code` will generate a temporary
+  When using a GitHub app, `Pipelines as Code` will generate a temporary
   installation token for every allowed event coming through the controller.
 
   The generated token will be scoped to the repository from the repository where
@@ -112,7 +112,7 @@ There is a few things you can configure through the config map
 
 {{< hint info >}}
  If you have a GitHub App already setup then verify if the `repository` event is
- subscribed into your Github App setting.
+ subscribed into your GitHub App setting.
 {{< /hint >}}
 
 * `auto-configure-repo-namespace-template`
@@ -136,7 +136,7 @@ There is a few things you can configure through the config map
 Pipelines as Code detect if the PipelineRun has failed and show a snippet of
 the last few lines of the error.
 
-On Github Aopps, It also try to detect and match the error messages in the container logs and expose them as annotations on Pull
+On GitHub Aopps, It also try to detect and match the error messages in the container logs and expose them as annotations on Pull
 Request.
 
 A few settings are available to configure this feature:
@@ -158,7 +158,7 @@ A few settings are available to configure this feature:
   Enable or disable the inspection of the container logs to detect error message
   and expose them as annotations on Pull Request.
 
-  Only Github apps is supported.
+  Only GitHub apps is supported.
 
 * `error-detection-max-number-of-lines`
 
@@ -186,55 +186,95 @@ A few settings are available to configure this feature:
 
    `<filename>`, `<line>`, `<error>`
 
-### Reporting logs URL to Tekton Dashboard or a custom Dashboard or Console
+### Reporting logs
 
-Pipelines as Code have the ability to automatically detect the OpenShift Console and link the logs of the tasks to the
-public URL of the OpenShift Console. If you are using the Tekton Dashboard, you can configure this feature using the
-`tekton-dashboard-url` setting. Simply set this to your dashboard URL, and the pipelinerun status and tasklog will be
-displayed there.
+  Pipeline as Code can report the logs of the tasks to the [OpenShift
+  Console](https://docs.openshift.com/container-platform/latest/web_console/web-console.html),
+  the [Tekton Dashboard](https://tekton.dev/docs/dashboard/) or if you have your
+  own give you flexibility to link to your custom Dashboard.
 
-Alternatively, you can also configure a custom console with the following settings:
+#### OpenShift Console
+
+  Pipelines as Code will automatically detect the OpenShift Console and link the logs of the tasks to the
+  public URL of the OpenShift Console.
+
+#### [Tekton Dashboard](https://tekton.dev/docs/dashboard/)
+
+  If you are using the Tekton Dashboard, you can configure this feature using the
+  `tekton-dashboard-url` setting. Simply set this to your dashboard URL, and the pipelinerun status and tasklog will be
+  displayed there.
+
+#### Custom Console (or dashboard)
+
+  Alternatively, you have the ability to configure the links to go to your custom
+  dashboard using the following settings:
 
 * `custom-console-name`
 
- Set this to the name of your custom console. example: `MyCorp Console`
+  Set this to the name of your custom console. example: `MyCorp Console`
 
 * `custom-console-url`
 
- Set this to the root URL of your custom console. example: `https://mycorp.com`
+  Set this to the root URL of your custom console. example: `https://mycorp.com`
 
 * `custom-console-url-pr-details`
 
- Set this to the URL where to view the details of the `PipelineRun`. This is
- shown when the PipelineRun is started so the user can follow execution on your
- console or when to see more details about the pipelinerun ion result.
+  Set this to the URL where to view the details of the `PipelineRun`. This is
+  shown when the PipelineRun is started so the user can follow execution on your
+  console or when to see more details about the pipelinerun on result.
 
- The URL suports templating for these value:
+  The URL suports all the standard variables as exposed on the Pipelinerun (refer to
+  the documentation on [Authoring PipelineRuns](../authoringprs)) with the added
+  variable:
 
-* `{{ namespace }}`: The target namespace where the pipelinerun is executed
-* `{{ pr }}`: The PipelineRun name.
+  * `{{ namespace }}`: The target namespace where the pipelinerun is executed
+  * `{{ pr }}`: The PipelineRun name.
+  
+  example: `https://mycorp.com/ns/{{ namespace }}/pipelinerun/{{ pr }}`
 
- example: `https://mycorp.com/ns/{{ namespace }}/pipelienrun/{{ pr }}`
+  Moreover it can access the [custom parameters](../guide/repositorycrd/#custom-parameter-expansion) from a
+  Repository CR. For example if the user has a parameter in their Repo CR like this :
 
-* `custom-console-url-pr-tasklog`
+   ```yaml
+   [...]
+   spec:
+    params:
+      - name: custom
+        value: value
+   ```
 
- Set this to the URL where to view the log of the taskrun of the `PipelineRun`. This is
- shown when we post a result of the task breakdown to link to the logs of the taskrun.
+  and the global configuration setting for `custom-console-url-pr-details` is:
+  
+  `https://mycorp.com/ns/{{ namespace }}/{{ custom }}`
+  
+  the `{{ custom }}` tag in the URL is expanded as `value`.
+  
+  This let operator to add specific informations like a `UUID` about a user as
+  parameter in their repo CR and let it link to the console.
+  
+* `custom-console-url-pr-tasklog`
+  
+  Set this to the URL where to view the log of the taskrun of the `PipelineRun`. This is
+  shown when we post a result of the task breakdown to link to the logs of the taskrun.
 
- The URL suports templating for these value:
+  The URL suports custom parameter on Repo CR and the standard parameters as
+  described in the `custom-console-url-pr-details` setting and as well those added
+  values:
 
-* `{{ namespace }}`: The target namespace where the pipelinerun is executed
-* `{{ pr }}`: The PipelineRun name.
-* `{{ task }}`: The Task name in the PR
-* `{{ pod }}`: The Pod name of the TaskRun
-* `{{ firstFailedStep }}`: The name of the first failed step in the TaskRun
+  * `{{ namespace }}`: The target namespace where the pipelinerun is executed
+  * `{{ pr }}`: The PipelineRun name.
+  * `{{ task }}`: The Task name in the PR
+  * `{{ pod }}`: The Pod name of the TaskRun
+  * `{{ firstFailedStep }}`: The name of the first failed step in the TaskRun
 
- example: `https://mycorp.com/ns/{{ namespace }}/pipelinerun/{{ pr }}/logs/{{ task }}#{{ pod }}-{{ firstFailedStep }}`
+  example: `https://mycorp.com/ns/{{ namespace }}/pipelinerun/{{ pr }}/logs/{{ task }}#{{ pod }}-{{ firstFailedStep }}`
 
 ## Pipelines-As-Code Info
 
   There are a settings exposed through a config map for which any authenticated
-  user can access to know about the Pipeline as Code status.
+  user can access to know about the Pipeline as Code status. This Configmap
+  will be automatically created with the [OpenShift Pipelines Operator](https://docs.openshift.com/container-platform/latest/cicd/pipelines/understanding-openshift-pipelines.html)
+  or when installing with [tkn pac boostrap](../../guide/cli/#bootstrap) command.
 
 * `version`
 
diff --git a/hack/dev/kind/install.sh b/hack/dev/kind/install.sh
index aad81cf2..b12f859c 100755
--- a/hack/dev/kind/install.sh
+++ b/hack/dev/kind/install.sh
@@ -250,4 +250,4 @@ while getopts "RGgpcrb" o; do
 done
 shift $((OPTIND-1))
 
-main
\ No newline at end of file
+main
diff --git a/hack/upload-file-to-github.py b/hack/upload-file-to-github.py
index 96506cdc..97d84c21 100755
--- a/hack/upload-file-to-github.py
+++ b/hack/upload-file-to-github.py
@@ -133,11 +133,22 @@ def upload_to_github(args):
     last_commit_sha = jeez["object"]["sha"]
 
     for entry in args.filename:
-        filename, dest = entry.split(":")
-        print(f"Upload file {filename} to {dest} based on {last_commit_sha}")
-
-        # pylint: disable=consider-using-with
-        base64content = base64.b64encode(open(filename, "rb").read())
+        left, dest = entry.split(":")
+
+        content: str = ""
+        if os.path.exists(left):
+            with open(left, "r", encoding="utf-8") as f:
+                content: str = f.read()
+            print(
+                f"Uploading file {left} to destination {dest} based on {last_commit_sha}"
+            )
+        else:
+            content: str = left.strip()
+            print(
+                f"Setting value {left} into the destionation {dest} based on {last_commit_sha}"
+            )
+
+        base64content = base64.b64encode(content.encode())
         _, jeez = github_request(
             args.token,
             "POST",
diff --git a/pkg/adapter/adapter.go b/pkg/adapter/adapter.go
index e8336765..33cd3186 100644
--- a/pkg/adapter/adapter.go
+++ b/pkg/adapter/adapter.go
@@ -8,6 +8,7 @@ import (
 	"log"
 	"net/http"
 	"os"
+	"strings"
 	"time"
 
 	cloudevents "github.com/cloudevents/sdk-go/v2"
@@ -66,7 +67,7 @@ func (l *listener) Start(ctx context.Context) error {
 	if envAdapterPort != "" {
 		adapterPort = envAdapterPort
 	}
-	l.logger.Infof("Starting Pipelines as Code version: %s", version.Version)
+	l.logger.Infof("Starting Pipelines as Code version: %s", strings.TrimSpace(version.Version))
 
 	mux := http.NewServeMux()
 
diff --git a/pkg/adapter/incoming.go b/pkg/adapter/incoming.go
index b7097516..ba3edb91 100644
--- a/pkg/adapter/incoming.go
+++ b/pkg/adapter/incoming.go
@@ -127,9 +127,9 @@ func (l *listener) processIncoming(targetRepo *v1alpha1.Repository) (provider.In
 		case "bitbucket-server":
 			provider = &bitbucketserver.Provider{}
 		default:
-			return l.processRes(false, nil, l.logger, "", fmt.Errorf("no supported Git provider has been detected"))
+			return l.processRes(false, nil, l.logger.With("namespace", targetRepo.Namespace), "", fmt.Errorf("no supported Git provider has been detected"))
 		}
 	}
 
-	return l.processRes(true, provider, l.logger.With("provider", "incoming"), "", nil)
+	return l.processRes(true, provider, l.logger.With("provider", "incoming", "namespace", targetRepo.Namespace), "", nil)
 }
diff --git a/pkg/apis/pipelinesascode/keys/keys.go b/pkg/apis/pipelinesascode/keys/keys.go
index bd6e7ae4..26cc147b 100644
--- a/pkg/apis/pipelinesascode/keys/keys.go
+++ b/pkg/apis/pipelinesascode/keys/keys.go
@@ -16,7 +16,11 @@ limitations under the License.
 
 package keys
 
-import "github.com/openshift-pipelines/pipelines-as-code/pkg/apis/pipelinesascode"
+import (
+	"regexp"
+
+	"github.com/openshift-pipelines/pipelines-as-code/pkg/apis/pipelinesascode"
+)
 
 const (
 	Task            = pipelinesascode.GroupName + "/task"
@@ -55,3 +59,5 @@ const (
 	GithubApplicationID = "github-application-id"
 	GithubPrivateKey    = "github-private-key"
 )
+
+var ParamsRe = regexp.MustCompile(`{{([^}]{2,})}}`)
diff --git a/pkg/apis/pipelinesascode/v1alpha1/types.go b/pkg/apis/pipelinesascode/v1alpha1/types.go
index 8ec46850..eae9cf2d 100644
--- a/pkg/apis/pipelinesascode/v1alpha1/types.go
+++ b/pkg/apis/pipelinesascode/v1alpha1/types.go
@@ -80,6 +80,7 @@ type RepositorySpec struct {
 
 type Settings struct {
 	GithubAppTokenScopeRepos []string `json:"github_app_token_scope_repos,omitempty"`
+	PipelineRunProvenance    string   `json:"pipelinerun_provenance,omitempty"`
 }
 
 type Params struct {
diff --git a/pkg/cli/prompt/stubber.go b/pkg/cli/prompt/stubber.go
index 13e8c50d..db76ffba 100644
--- a/pkg/cli/prompt/stubber.go
+++ b/pkg/cli/prompt/stubber.go
@@ -41,34 +41,6 @@ func InitAskStubber() (*AskStubber, func()) {
 		return nil
 	}
 
-	SurveyAsk = func(qs []*survey.Question, response interface{}, opts ...survey.AskOpt) error {
-		as.Asks = append(as.Asks, qs)
-		count := as.Count
-		as.Count++
-		if count >= len(as.Stubs) {
-			panic(fmt.Sprintf("more asks than stubs. most recent call: %#v", qs))
-		}
-
-		// actually set response
-		stubbedQuestions := as.Stubs[count]
-		if len(stubbedQuestions) != len(qs) {
-			panic(fmt.Sprintf("asked questions: %d; stubbed questions: %d", len(qs), len(stubbedQuestions)))
-		}
-		for i, sq := range stubbedQuestions {
-			q := qs[i]
-			if q.Name != sq.Name {
-				panic(fmt.Sprintf("stubbed question mismatch: %s != %s", q.Name, sq.Name))
-			}
-			if sq.Default {
-				defaultValue := reflect.ValueOf(q.Prompt).Elem().FieldByName("Default")
-				_ = core.WriteAnswer(response, q.Name, defaultValue)
-			} else {
-				_ = core.WriteAnswer(response, q.Name, sq.Value)
-			}
-		}
-
-		return nil
-	}
 	teardown := func() {
 		SurveyAsk = origSurveyAsk
 		SurveyAskOne = origSurveyAskOne
diff --git a/pkg/cmd/tknpac/bootstrap/bootstrap.go b/pkg/cmd/tknpac/bootstrap/bootstrap.go
index 9db391da..271be835 100644
--- a/pkg/cmd/tknpac/bootstrap/bootstrap.go
+++ b/pkg/cmd/tknpac/bootstrap/bootstrap.go
@@ -28,18 +28,19 @@ const (
 var providerTargets = []string{"github-app", "github-enterprise-app"}
 
 type bootstrapOpts struct {
-	providerType      string
-	installNightly    bool
-	skipInstall       bool
-	skipGithubAPP     bool
-	forceInstall      bool
-	webserverPort     int
-	cliOpts           *cli.PacCliOpts
-	ioStreams         *cli.IOStreams
-	targetNamespace   string
-	autoDetectedRoute bool
-	forwarderURL      string
-	dashboardURL      string
+	forceInstallGosmee bool
+	providerType       string
+	installNightly     bool
+	skipInstall        bool
+	skipGithubAPP      bool
+	forceInstall       bool
+	webserverPort      int
+	cliOpts            *cli.PacCliOpts
+	ioStreams          *cli.IOStreams
+	targetNamespace    string
+	autoDetectedRoute  bool
+	forwarderURL       string
+	dashboardURL       string
 
 	RouteName              string
 	GithubAPIURL           string
@@ -68,14 +69,14 @@ var successTmpl = fmt.Sprintf(`
 
 func install(ctx context.Context, run *params.Run, opts *bootstrapOpts) error {
 	if !opts.forceInstall {
-		fmt.Fprintln(opts.ioStreams.Out, "🏃 Checking if Pipelines as Code is installed.")
+		fmt.Fprintln(opts.ioStreams.Out, "=> Checking if Pipelines as Code is installed.")
 	}
 	tektonInstalled, err := checkPipelinesInstalled(run)
 	if err != nil {
 		return err
 	}
 	if !tektonInstalled {
-		return fmt.Errorf("a Tekton installation has not been found on this cluster, install Tekton first before launching this command")
+		return fmt.Errorf("no Tekton installation has been found on this kubernetes cluster, install Tekton first before launching this command")
 	}
 
 	// if we gt a ns back it means it has been detected in here so keep it as is.
@@ -93,7 +94,7 @@ func install(ctx context.Context, run *params.Run, opts *bootstrapOpts) error {
 	}
 
 	if !opts.forceInstall && err == nil && installed {
-		fmt.Fprintln(opts.ioStreams.Out, "👌 Pipelines as Code is already installed.")
+		fmt.Fprintln(opts.ioStreams.Out, "✓ Pipelines as Code is already installed.")
 	} else if err := installPac(ctx, run, opts); err != nil {
 		return err
 	}
@@ -173,6 +174,7 @@ func Command(run *params.Run, ioStreams *cli.IOStreams) *cobra.Command {
 	addGithubAppFlag(cmd, opts)
 
 	cmd.PersistentFlags().BoolVar(&opts.forceInstall, "force-install", false, "whether we should force pac install even if it's already installed")
+	cmd.PersistentFlags().BoolVar(&opts.forceInstallGosmee, "force-gosmee", false, "force install gosmee on OpenShift if your cluster is not reachable from the internet")
 	cmd.PersistentFlags().BoolVar(&opts.skipInstall, "skip-install", false, "skip Pipelines as Code installation")
 	cmd.PersistentFlags().BoolVar(&opts.skipGithubAPP, "skip-github-app", false, "skip creating github application")
 
@@ -203,7 +205,7 @@ func GithubApp(run *params.Run, ioStreams *cli.IOStreams) *cobra.Command {
 				return err
 			}
 			// installed but there is error for missing resources
-			if installed && err != nil && !opts.forceInstall {
+			if installed && !opts.forceInstall {
 				return err
 			}
 
diff --git a/pkg/cmd/tknpac/bootstrap/install.go b/pkg/cmd/tknpac/bootstrap/install.go
index 947bafe0..2c042c72 100644
--- a/pkg/cmd/tknpac/bootstrap/install.go
+++ b/pkg/cmd/tknpac/bootstrap/install.go
@@ -180,7 +180,7 @@ func installPac(ctx context.Context, run *params.Run, opts *bootstrapOpts) error
 
 	fmt.Fprintf(opts.ioStreams.Out, "✓ Pipelines-as-Code %s has been installed\n", latestVersion)
 
-	if !isOpenShift && opts.RouteName == "" {
+	if (!isOpenShift && opts.RouteName == "") || opts.forceInstallGosmee {
 		if err := installGosmeeForwarder(opts); err != nil {
 			return err
 		}
diff --git a/pkg/cmd/tknpac/version/version.go b/pkg/cmd/tknpac/version/version.go
index 6c517588..6a4b0dcb 100644
--- a/pkg/cmd/tknpac/version/version.go
+++ b/pkg/cmd/tknpac/version/version.go
@@ -2,6 +2,7 @@ package version
 
 import (
 	"fmt"
+	"strings"
 
 	"github.com/openshift-pipelines/pipelines-as-code/pkg/cli"
 	"github.com/openshift-pipelines/pipelines-as-code/pkg/params/settings"
@@ -14,7 +15,7 @@ func Command(ioStreams *cli.IOStreams) *cobra.Command {
 		Use:   "version",
 		Short: fmt.Sprintf("Print %s pac version", settings.TknBinaryName),
 		Run: func(cmd *cobra.Command, args []string) {
-			fmt.Fprintln(ioStreams.Out, version.Version)
+			fmt.Fprintln(ioStreams.Out, strings.TrimSpace(version.Version))
 		},
 		Annotations: map[string]string{
 			"commandType": "main",
diff --git a/pkg/consoleui/custom.go b/pkg/consoleui/custom.go
index 88b5e34c..cac1018a 100644
--- a/pkg/consoleui/custom.go
+++ b/pkg/consoleui/custom.go
@@ -3,7 +3,10 @@ package consoleui
 import (
 	"context"
 	"fmt"
+	"net/url"
+	"strings"
 
+	"github.com/openshift-pipelines/pipelines-as-code/pkg/apis/pipelinesascode/keys"
 	"github.com/openshift-pipelines/pipelines-as-code/pkg/params/info"
 	"github.com/openshift-pipelines/pipelines-as-code/pkg/params/settings"
 	"github.com/openshift-pipelines/pipelines-as-code/pkg/templates"
@@ -12,7 +15,12 @@ import (
 )
 
 type CustomConsole struct {
-	Info *info.Info
+	Info   *info.Info
+	params map[string]string
+}
+
+func (o *CustomConsole) SetParams(mt map[string]string) {
+	o.params = mt
 }
 
 func (o *CustomConsole) GetName() string {
@@ -29,14 +37,32 @@ func (o *CustomConsole) URL() string {
 	return o.Info.Pac.CustomConsoleURL
 }
 
+// generateURL will generate a URL from a template, trim some of the spaces and
+// \n we get from yaml
+// return the default URL if there it's not become a proper url or that it has
+// some of the templates like {{}} left
+func (o *CustomConsole) generateURL(urlTmpl string, dict map[string]string) string {
+	newurl := templates.ReplacePlaceHoldersVariables(urlTmpl, dict)
+	// trim new line because yaml parser adds new line at the end of the string
+	newurl = strings.TrimSpace(strings.TrimSuffix(newurl, "\n"))
+	if _, err := url.ParseRequestURI(newurl); err != nil {
+		return o.URL()
+	}
+	// detect if there is still some {{}} in the url
+	if keys.ParamsRe.MatchString(newurl) {
+		return o.URL()
+	}
+	return newurl
+}
+
 func (o *CustomConsole) DetailURL(pr *tektonv1.PipelineRun) string {
 	if o.Info.Pac.CustomConsolePRdetail == "" {
 		return fmt.Sprintf("https://detailurl.setting.%s.is.not.configured", settings.CustomConsolePRDetailKey)
 	}
-	return templates.ReplacePlaceHoldersVariables(o.Info.Pac.CustomConsolePRdetail, map[string]string{
-		"namespace": pr.GetNamespace(),
-		"pr":        pr.GetName(),
-	})
+	nm := o.params
+	nm["namespace"] = pr.GetNamespace()
+	nm["pr"] = pr.GetName()
+	return o.generateURL(o.Info.Pac.CustomConsolePRdetail, nm)
 }
 
 func (o *CustomConsole) TaskLogURL(pr *tektonv1.PipelineRun, taskRunStatus *tektonv1.PipelineRunTaskRunStatus) string {
@@ -51,13 +77,14 @@ func (o *CustomConsole) TaskLogURL(pr *tektonv1.PipelineRun, taskRunStatus *tekt
 			break
 		}
 	}
-	return templates.ReplacePlaceHoldersVariables(o.Info.Pac.CustomConsolePRTaskLog, map[string]string{
-		"namespace":       pr.GetNamespace(),
-		"pr":              pr.GetName(),
-		"task":            taskRunStatus.PipelineTaskName,
-		"pod":             taskRunStatus.Status.PodName,
-		"firstFailedStep": firstFailedStep,
-	})
+
+	nm := o.params
+	nm["namespace"] = pr.GetNamespace()
+	nm["pr"] = pr.GetName()
+	nm["task"] = taskRunStatus.PipelineTaskName
+	nm["pod"] = taskRunStatus.Status.PodName
+	nm["firstFailedStep"] = firstFailedStep
+	return o.generateURL(o.Info.Pac.CustomConsolePRTaskLog, nm)
 }
 
 func (o *CustomConsole) UI(_ context.Context, _ dynamic.Interface) error {
diff --git a/pkg/consoleui/interface.go b/pkg/consoleui/interface.go
index 31e22cf6..459fb9a9 100644
--- a/pkg/consoleui/interface.go
+++ b/pkg/consoleui/interface.go
@@ -16,6 +16,7 @@ type Interface interface {
 	UI(ctx context.Context, kdyn dynamic.Interface) error
 	URL() string
 	GetName() string
+	SetParams(mt map[string]string)
 }
 
 type FallBackConsole struct{}
@@ -40,6 +41,9 @@ func (f FallBackConsole) URL() string {
 	return consoleIsnotConfiguredURL
 }
 
+func (f FallBackConsole) SetParams(_ map[string]string) {
+}
+
 func New(ctx context.Context, kdyn dynamic.Interface, _ *info.Info) Interface {
 	oc := &OpenshiftConsole{}
 	if err := oc.UI(ctx, kdyn); err == nil {
diff --git a/pkg/consoleui/openshift.go b/pkg/consoleui/openshift.go
index 0afec6f8..ee7905d1 100644
--- a/pkg/consoleui/openshift.go
+++ b/pkg/consoleui/openshift.go
@@ -25,6 +25,9 @@ type OpenshiftConsole struct {
 	host string
 }
 
+func (o *OpenshiftConsole) SetParams(_ map[string]string) {
+}
+
 func (o *OpenshiftConsole) GetName() string {
 	return openshiftConsoleName
 }
diff --git a/pkg/consoleui/tektondashboard.go b/pkg/consoleui/tektondashboard.go
index e2fe3189..8bbf7b60 100644
--- a/pkg/consoleui/tektondashboard.go
+++ b/pkg/consoleui/tektondashboard.go
@@ -33,3 +33,6 @@ func (t *TektonDashboard) URL() string {
 func (t *TektonDashboard) UI(_ context.Context, _ dynamic.Interface) error {
 	return nil
 }
+
+func (t *TektonDashboard) SetParams(_ map[string]string) {
+}
diff --git a/pkg/customparams/cel.go b/pkg/customparams/cel.go
new file mode 100644
index 00000000..111496b4
--- /dev/null
+++ b/pkg/customparams/cel.go
@@ -0,0 +1,74 @@
+package customparams
+
+import (
+	"encoding/json"
+	"fmt"
+
+	"github.com/google/cel-go/cel"
+	"github.com/google/cel-go/checker/decls"
+	"github.com/google/cel-go/common/types"
+	"github.com/google/cel-go/common/types/ref"
+)
+
+func celEvaluate(expr string, env *cel.Env, data map[string]interface{}) (ref.Val, error) {
+	parsed, issues := env.Parse(expr)
+	if issues != nil && issues.Err() != nil {
+		return nil, fmt.Errorf("failed to parse expression %#v: %w", expr, issues.Err())
+	}
+
+	checked, issues := env.Check(parsed)
+	if issues != nil && issues.Err() != nil {
+		return nil, fmt.Errorf("expression %#v check failed: %w", expr, issues.Err())
+	}
+
+	prg, err := env.Program(checked, cel.EvalOptions(cel.OptOptimize))
+	if err != nil {
+		return nil, fmt.Errorf("expression %#v failed to create a Program: %w", expr, err)
+	}
+
+	out, _, err := prg.Eval(data)
+	if err != nil {
+		return nil, fmt.Errorf("expression %#v failed to evaluate: %w", expr, err)
+	}
+
+	return out, nil
+}
+
+// celFilter filters a query with cel, it sets two variables for the user to use
+// in the cel filter. body and pac.
+// body is the payload of the event, since it's already casted we un-marshall/and
+// marshall it as a map[string]interface{}
+func celFilter(query string, body any, pacParams map[string]string) (bool, error) {
+	nbody, err := json.Marshal(body)
+	if err != nil {
+		return false, err
+	}
+	var jsonMap map[string]interface{}
+	err = json.Unmarshal(nbody, &jsonMap)
+	if err != nil {
+		return false, err
+	}
+
+	mapStrDyn := decls.NewMapType(decls.String, decls.Dyn)
+	celDec, _ := cel.NewEnv(
+		cel.Declarations(
+			decls.NewVar("body", mapStrDyn),
+			decls.NewVar("pac", mapStrDyn),
+		))
+	val, err := celEvaluate(query, celDec, map[string]any{
+		"body": jsonMap,
+		"pac":  pacParams,
+	})
+	if err != nil {
+		return false, err
+	}
+
+	//nolint: gocritic
+	switch val.(type) {
+	case types.Bool:
+		if val.Value() == true {
+			return true, nil
+		}
+	}
+	return false, nil
+}
diff --git a/pkg/customparams/customparams.go b/pkg/customparams/customparams.go
new file mode 100644
index 00000000..52257e09
--- /dev/null
+++ b/pkg/customparams/customparams.go
@@ -0,0 +1,108 @@
+package customparams
+
+import (
+	"context"
+	"fmt"
+
+	"github.com/openshift-pipelines/pipelines-as-code/pkg/apis/pipelinesascode/v1alpha1"
+	"github.com/openshift-pipelines/pipelines-as-code/pkg/events"
+	"github.com/openshift-pipelines/pipelines-as-code/pkg/kubeinteraction"
+	"github.com/openshift-pipelines/pipelines-as-code/pkg/params"
+	"github.com/openshift-pipelines/pipelines-as-code/pkg/params/info"
+	sectypes "github.com/openshift-pipelines/pipelines-as-code/pkg/secrets/types"
+	"go.uber.org/zap"
+)
+
+type CustomParams struct {
+	event        *info.Event
+	run          *params.Run
+	k8int        kubeinteraction.Interface
+	eventEmitter *events.EventEmitter
+	repo         *v1alpha1.Repository
+}
+
+func NewCustomParams(event *info.Event, repo *v1alpha1.Repository, run *params.Run, k8int kubeinteraction.Interface, eventEmitter *events.EventEmitter) CustomParams {
+	return CustomParams{
+		event:        event,
+		repo:         repo,
+		run:          run,
+		k8int:        k8int,
+		eventEmitter: eventEmitter,
+	}
+}
+
+// GetParams will process the parameters as set in the repo.Spec CR.
+// value can come from a string or from a secretKeyRef or from a string value
+// if both is set we pick the value and issue a warning in the user namespace
+// we let the user specify a cel filter. If false then we skip the parameters.
+// if multiple params name has a filter we pick up the first one that has
+// matched true.
+func (p *CustomParams) GetParams(ctx context.Context) (map[string]string, error) {
+	stdParams := p.makeStandardParamsFromEvent()
+	if p.repo.Spec.Params == nil {
+		return stdParams, nil
+	}
+	ret := map[string]string{}
+	mapFilters := map[string]string{}
+
+	for index, value := range *p.repo.Spec.Params {
+		// if the name is empty we skip it
+		if value.Name == "" {
+			p.eventEmitter.EmitMessage(p.repo, zap.ErrorLevel,
+				"ParamsFilterSkipped", fmt.Sprintf("no name has been set in params[%d] of repo %s", index, p.repo.GetName()))
+			continue
+		}
+		if value.Filter != "" {
+			// if we already have a filter that has matched we skip it
+			if _, ok := mapFilters[value.Name]; ok {
+				p.eventEmitter.EmitMessage(p.repo, zap.WarnLevel,
+					"ParamsFilterSkipped", fmt.Sprintf("skipping params name %s, filter has already been matched previously", value.Name))
+				continue
+			}
+
+			// if the cel filter condition is false we skip it
+			cond, err := celFilter(value.Filter, p.event.Event, stdParams)
+			if err != nil {
+				p.eventEmitter.EmitMessage(p.repo, zap.ErrorLevel,
+					"ParamsFilterError", fmt.Sprintf("there is an error on the cel filter: %s: %s", value.Name, err.Error()))
+				return map[string]string{}, err
+			}
+			if !cond {
+				p.eventEmitter.EmitMessage(p.repo, zap.InfoLevel,
+					"ParamsFilterSkipped", fmt.Sprintf("skipping params name %s, filter condition is false", value.Name))
+				continue
+			}
+			mapFilters[value.Name] = value.Value
+		}
+
+		if value.SecretRef != nil && value.Value != "" {
+			p.eventEmitter.EmitMessage(p.repo, zap.InfoLevel,
+				"ParamsFilterUsedValue",
+				fmt.Sprintf("repo %s, param name %s has a value and secretref, picking value", p.repo.GetName(), value.Name))
+		}
+		if value.Value != "" {
+			ret[value.Name] = value.Value
+		} else if value.SecretRef != nil {
+			secretValue, err := p.k8int.GetSecret(ctx, sectypes.GetSecretOpt{
+				Namespace: p.repo.GetNamespace(),
+				Name:      value.SecretRef.Name,
+				Key:       value.SecretRef.Key,
+			})
+			if err != nil {
+				return ret, err
+			}
+			ret[value.Name] = secretValue
+		}
+	}
+
+	// TODO: Should we let the user override the standard params?
+	// we don't let them here
+	for k, v := range stdParams {
+		// check if not already there
+		if _, ok := ret[k]; !ok && v != "" {
+			ret[k] = v
+		}
+	}
+
+	return ret, nil
+}
diff --git a/pkg/customparams/standard.go b/pkg/customparams/standard.go
new file mode 100644
index 00000000..7df222ca
--- /dev/null
+++ b/pkg/customparams/standard.go
@@ -0,0 +1,29 @@
+package customparams
+
+import (
+	"strings"
+
+	"github.com/openshift-pipelines/pipelines-as-code/pkg/formatting"
+)
+
+// makeStandardParamsFromEvent will create a map of standard params out of the event
+func (p *CustomParams) makeStandardParamsFromEvent() map[string]string {
+	repoURL := p.event.URL
+	// On bitbucket server you are have a special url for checking it out, they
+	// seemed to fix it in 2.0 but i guess we have to live with this until then.
+	if p.event.CloneURL != "" {
+		repoURL = p.event.CloneURL
+	}
+
+	return map[string]string{
+		"revision":         p.event.SHA,
+		"repo_url":         repoURL,
+		"repo_owner":       strings.ToLower(p.event.Organization),
+		"repo_name":        strings.ToLower(p.event.Repository),
+		"target_branch":    formatting.SanitizeBranch(p.event.BaseBranch),
+		"source_branch":    formatting.SanitizeBranch(p.event.HeadBranch),
+		"sender":           strings.ToLower(p.event.Sender),
+		"target_namespace": p.repo.GetNamespace(),
+		"event_type":       p.event.EventType,
+	}
+}
diff --git a/pkg/formatting/k8labels.go b/pkg/formatting/k8labels.go
index 2033065f..45e33f94 100644
--- a/pkg/formatting/k8labels.go
+++ b/pkg/formatting/k8labels.go
@@ -12,5 +12,6 @@ import "strings"
 // '(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?')
 func CleanValueKubernetes(s string) string {
 	replasoeur := strings.NewReplacer("/", "-", " ", "_", "[", "__", "]", "__")
-	return replasoeur.Replace(strings.TrimRight(s, " -_[]"))
+	replaced := replasoeur.Replace(strings.TrimRight(s, " -_[]"))
+	return strings.TrimSpace(replaced)
 }
diff --git a/pkg/kubeinteraction/labels.go b/pkg/kubeinteraction/labels.go
index 7b927590..56bb371a 100644
--- a/pkg/kubeinteraction/labels.go
+++ b/pkg/kubeinteraction/labels.go
@@ -26,7 +26,7 @@ func AddLabelsAndAnnotations(event *info.Event, pipelineRun *tektonv1.PipelineRu
 		// These keys are used in LabelSelector query so we are keeping in Labels as it is.
 		// But adding same keys to Annotations so UI/CLI can fetch the actual value instead of modified value
 		"app.kubernetes.io/managed-by": pipelinesascode.GroupName,
-		"app.kubernetes.io/version":    version.Version,
+		"app.kubernetes.io/version":    formatting.CleanValueKubernetes(version.Version),
 		keys.URLOrg:                    formatting.CleanValueKubernetes(event.Organization),
 		keys.URLRepository:             formatting.CleanValueKubernetes(event.Repository),
 		keys.SHA:                       formatting.CleanValueKubernetes(event.SHA),
diff --git a/pkg/params/settings/validation.go b/pkg/params/settings/validation.go
index b61a401c..b55c069d 100644
--- a/pkg/params/settings/validation.go
+++ b/pkg/params/settings/validation.go
@@ -5,6 +5,7 @@ import (
 	"net/url"
 	"regexp"
 	"strconv"
+	"strings"
 )
 
 func Validate(config map[string]string) error {
@@ -71,14 +72,15 @@ func Validate(config map[string]string) error {
 	}
 
 	if v, ok := config[CustomConsolePRTaskLogKey]; ok && v != "" {
-		if _, err := url.ParseRequestURI(v); err != nil {
-			return fmt.Errorf("invalid value for key %v, invalid url: %w", CustomConsolePRTaskLogKey, err)
+		// check if custom console start with http:// or https://
+		if strings.HasPrefix(v, "http://") || !strings.HasPrefix(v, "https://") {
+			return fmt.Errorf("invalid value for key %v, must start with http:// or https://", CustomConsolePRTaskLogKey)
 		}
 	}
 
 	if v, ok := config[CustomConsolePRDetailKey]; ok && v != "" {
-		if _, err := url.ParseRequestURI(v); err != nil {
-			return fmt.Errorf("invalid value for key %v, invalid url: %w", CustomConsolePRDetailKey, err)
+		if strings.HasPrefix(v, "http://") || !strings.HasPrefix(v, "https://") {
+			return fmt.Errorf("invalid value for key %v, must start with http:// or https://", CustomConsolePRTaskLogKey)
 		}
 	}
 	return nil
diff --git a/pkg/params/version/version.go b/pkg/params/version/version.go
index a3b66b56..a58fb167 100644
--- a/pkg/params/version/version.go
+++ b/pkg/params/version/version.go
@@ -1,4 +1,6 @@
 package version
 
-// Version is dynamically set by the toolchain or overridden by the Makefile.
-var Version = "nightly"
+import _ "embed"
+
+//go:embed version.txt
+var Version string
diff --git a/pkg/params/version/version.txt b/pkg/params/version/version.txt
new file mode 100644
index 00000000..bf867e0a
--- /dev/null
+++ b/pkg/params/version/version.txt
@@ -0,0 +1,1 @@
+nightly
diff --git a/pkg/pipelineascode/match.go b/pkg/pipelineascode/match.go
index 2a274917..397fe9c3 100644
--- a/pkg/pipelineascode/match.go
+++ b/pkg/pipelineascode/match.go
@@ -60,6 +60,9 @@ func (p *PacRun) verifyRepoAndUser(ctx context.Context) (*v1alpha1.Repository, e
 		return nil, nil
 	}
 
+	p.logger = p.logger.With("namespace", repo.Namespace)
+	p.vcx.SetLogger(p.logger)
+	p.eventEmitter.SetLogger(p.logger)
 	// If we have a git_provider field in repository spec, then get all the
 	// information from there, including the webhook secret.
 	// otherwise get the secret from the current ns (i.e: pipelines-as-code/openshift-pipelines.)
@@ -147,7 +150,11 @@ is that what you want? make sure you use -n when generating the secret, eg: echo
 
 // getPipelineRunsFromRepo fetches pipelineruns from git repository and prepare them for creation
 func (p *PacRun) getPipelineRunsFromRepo(ctx context.Context, repo *v1alpha1.Repository) ([]matcher.Match, error) {
-	rawTemplates, err := p.vcx.GetTektonDir(ctx, p.event, tektonDir)
+	provenance := "source"
+	if repo.Spec.Settings != nil && repo.Spec.Settings.PipelineRunProvenance != "" {
+		provenance = repo.Spec.Settings.PipelineRunProvenance
+	}
+	rawTemplates, err := p.vcx.GetTektonDir(ctx, p.event, tektonDir, provenance)
 	if err != nil || rawTemplates == "" {
 		msg := fmt.Sprintf("cannot locate templates in %s/ directory for this repository in %s", tektonDir, p.event.HeadBranch)
 		if err != nil {
@@ -164,6 +171,11 @@ func (p *PacRun) getPipelineRunsFromRepo(ctx context.Context, repo *v1alpha1.Rep
 		return nil, fmt.Errorf(msg)
 	}
 
+	// This is for bitbucket
+	if p.event.CloneURL == "" {
+		p.event.AccountID = ""
+	}
+
 	// Replace those {{var}} placeholders user has in her template to the run.Info variable
 	allTemplates := p.makeTemplate(ctx, repo, rawTemplates)
 	pipelineRuns, err := resolve.Resolve(ctx, p.run, p.logger, p.vcx, p.event, allTemplates, &resolve.Opts{
diff --git a/pkg/pipelineascode/pipelineascode.go b/pkg/pipelineascode/pipelineascode.go
index 27a84537..11ea602f 100644
--- a/pkg/pipelineascode/pipelineascode.go
+++ b/pkg/pipelineascode/pipelineascode.go
@@ -7,6 +7,7 @@ import (
 
 	"github.com/openshift-pipelines/pipelines-as-code/pkg/action"
 	"github.com/openshift-pipelines/pipelines-as-code/pkg/apis/pipelinesascode/keys"
+	"github.com/openshift-pipelines/pipelines-as-code/pkg/customparams"
 	"github.com/openshift-pipelines/pipelines-as-code/pkg/events"
 	"github.com/openshift-pipelines/pipelines-as-code/pkg/kubeinteraction"
 	"github.com/openshift-pipelines/pipelines-as-code/pkg/matcher"
@@ -64,6 +65,15 @@ func (p *PacRun) Run(ctx context.Context) error {
 		p.manager.Enable()
 	}
 
+	// set params for the console driver, only used for the custom console ones
+	cp := customparams.NewCustomParams(p.event, repo, p.run, p.k8int, p.eventEmitter)
+	maptemplate, err := cp.GetParams(ctx)
+	if err != nil {
+		p.eventEmitter.EmitMessage(repo, zap.ErrorLevel, "ParamsError",
+			fmt.Sprintf("error processing repository CR custom params: %s", err.Error()))
+	}
+	p.run.Clients.ConsoleUI.SetParams(maptemplate)
+
 	var wg sync.WaitGroup
 	for _, match := range matchedPRs {
 		if match.Repo == nil {
diff --git a/pkg/pipelineascode/template.go b/pkg/pipelineascode/template.go
index b24c3072..b5d81b11 100644
--- a/pkg/pipelineascode/template.go
+++ b/pkg/pipelineascode/template.go
@@ -2,177 +2,25 @@ package pipelineascode
 
 import (
 	"context"
-	"encoding/json"
 	"fmt"
-	"strings"
 
-	"github.com/google/cel-go/cel"
-	"github.com/google/cel-go/checker/decls"
-	"github.com/google/cel-go/common/types"
-	"github.com/google/cel-go/common/types/ref"
 	"github.com/openshift-pipelines/pipelines-as-code/pkg/apis/pipelinesascode/v1alpha1"
-	"github.com/openshift-pipelines/pipelines-as-code/pkg/formatting"
-	sectypes "github.com/openshift-pipelines/pipelines-as-code/pkg/secrets/types"
+	"github.com/openshift-pipelines/pipelines-as-code/pkg/customparams"
 	"github.com/openshift-pipelines/pipelines-as-code/pkg/templates"
 	"go.uber.org/zap"
 )
 
-func celEvaluate(expr string, env *cel.Env, data map[string]interface{}) (ref.Val, error) {
-	parsed, issues := env.Parse(expr)
-	if issues != nil && issues.Err() != nil {
-		return nil, fmt.Errorf("failed to parse expression %#v: %w", expr, issues.Err())
-	}
-
-	checked, issues := env.Check(parsed)
-	if issues != nil && issues.Err() != nil {
-		return nil, fmt.Errorf("expression %#v check failed: %w", expr, issues.Err())
-	}
-
-	prg, err := env.Program(checked, cel.EvalOptions(cel.OptOptimize))
-	if err != nil {
-		return nil, fmt.Errorf("expression %#v failed to create a Program: %w", expr, err)
-	}
-
-	out, _, err := prg.Eval(data)
-	if err != nil {
-		return nil, fmt.Errorf("expression %#v failed to evaluate: %w", expr, err)
-	}
-
-	return out, nil
-}
-
-// celFilter filters a query with cel, it sets two variables for the user to use
-// in the cel filter. body and pac.
-// body is the payload of the event, since it's already casted we un-marshall/and
-// marshall it as a map[string]interface{}
-func celFilter(query string, body any, pacParams map[string]string) (bool, error) {
-	nbody, err := json.Marshal(body)
-	if err != nil {
-		return false, err
-	}
-	var jsonMap map[string]interface{}
-	err = json.Unmarshal(nbody, &jsonMap)
-	if err != nil {
-		return false, err
-	}
-
-	mapStrDyn := decls.NewMapType(decls.String, decls.Dyn)
-	celDec, _ := cel.NewEnv(
-		cel.Declarations(
-			decls.NewVar("body", mapStrDyn),
-			decls.NewVar("pac", mapStrDyn),
-		))
-	val, err := celEvaluate(query, celDec, map[string]any{
-		"body": jsonMap,
-		"pac":  pacParams,
-	})
-	if err != nil {
-		return false, err
-	}
-
-	//nolint: gocritic
-	switch val.(type) {
-	case types.Bool:
-		if val.Value() == true {
-			return true, nil
-		}
-	}
-	return false, nil
-}
-
-// processParams will process the parameters as set in the repo.Spec CR.
-// value can come from a string or from a secretKeyRef or from a string value
-// if both is set we pick the value and issue a warning in the user namespace
-// we let the user specify a cel filter. If false then we skip the parameters.
-// if multiple params name has a filter we pick up the first one that has
-// matched true.
-func (p *PacRun) processParams(ctx context.Context, repo *v1alpha1.Repository, maptemplate map[string]string) error {
-	mapFilters := map[string]string{}
-	if repo.Spec.Params == nil {
-		return nil
-	}
-	for index, value := range *repo.Spec.Params {
-		// if the name is empty we skip it
-		if value.Name == "" {
-			p.eventEmitter.EmitMessage(repo, zap.ErrorLevel,
-				"ParamsFilterSkipped", fmt.Sprintf("no name has been set in params[%d] of repo %s", index, repo.GetName()))
-			continue
-		}
-		if value.Filter != "" {
-			// if we already have a filter that has matched we skip it
-			if _, ok := mapFilters[value.Name]; ok {
-				p.eventEmitter.EmitMessage(repo, zap.WarnLevel,
-					"ParamsFilterSkipped", fmt.Sprintf("skipping params name %s, filter has already been matched previously", value.Name))
-				continue
-			}
-
-			// if the cel filter condition is false we skip it
-			cond, err := celFilter(value.Filter, p.event.Event, maptemplate)
-			if err != nil {
-				return err
-			}
-			if !cond {
-				p.eventEmitter.EmitMessage(repo, zap.InfoLevel,
-					"ParamsFilterSkipped", fmt.Sprintf("skipping params name %s, filter condition is false", value.Name))
-				continue
-			}
-			mapFilters[value.Name] = value.Value
-		}
-
-		if value.SecretRef != nil && value.Value != "" {
-			p.eventEmitter.EmitMessage(repo, zap.InfoLevel,
-				"ParamsFilterUsedValue",
-				fmt.Sprintf("repo %s, param name %s has a value and secretref, picking value", repo.GetName(), value.Name))
-		}
-		if value.Value != "" {
-			maptemplate[value.Name] = value.Value
-		} else if value.SecretRef != nil {
-			secretValue, err := p.k8int.GetSecret(ctx, sectypes.GetSecretOpt{
-				Namespace: repo.GetNamespace(),
-				Name:      value.SecretRef.Name,
-				Key:       value.SecretRef.Key,
-			})
-			if err != nil {
-				return err
-			}
-			maptemplate[value.Name] = secretValue
-		}
-	}
-	return nil
-}
-
 // makeTemplate will process all templates replacing the value from the event and from the
 // params as set on Repo CR
 func (p *PacRun) makeTemplate(ctx context.Context, repo *v1alpha1.Repository, template string) string {
-	repoURL := p.event.URL
-	// On bitbucket server you are have a special url for checking it out, they
-	// seemed to fix it in 2.0 but i guess we have to live with this until then.
-	if p.event.CloneURL != "" {
-		repoURL = p.event.CloneURL
-	}
-
-	if p.event.CloneURL == "" {
-		p.event.AccountID = ""
-	}
-
-	maptemplate := map[string]string{
-		"revision":         p.event.SHA,
-		"repo_url":         repoURL,
-		"repo_owner":       strings.ToLower(p.event.Organization),
-		"repo_name":        strings.ToLower(p.event.Repository),
-		"target_branch":    formatting.SanitizeBranch(p.event.BaseBranch),
-		"source_branch":    formatting.SanitizeBranch(p.event.HeadBranch),
-		"sender":           strings.ToLower(p.event.Sender),
-		"target_namespace": repo.GetNamespace(),
-		"event_type":       p.event.EventType,
-	}
-
-	if err := p.processParams(ctx, repo, maptemplate); err != nil {
+	cp := customparams.NewCustomParams(p.event, repo, p.run, p.k8int, p.eventEmitter)
+	maptemplate, err := cp.GetParams(ctx)
+	if err != nil {
 		p.eventEmitter.EmitMessage(repo, zap.ErrorLevel, "ParamsError",
 			fmt.Sprintf("error processing repository CR custom params: %s", err.Error()))
 	}
 
-	// convert to string
+	// convert pull request number to string
 	if p.event.PullRequestNumber != 0 {
 		maptemplate["pull_request_number"] = fmt.Sprintf("%d", p.event.PullRequestNumber)
 	}
diff --git a/pkg/provider/bitbucketcloud/bitbucket.go b/pkg/provider/bitbucketcloud/bitbucket.go
index afa606af..fd6c29d4 100644
--- a/pkg/provider/bitbucketcloud/bitbucket.go
+++ b/pkg/provider/bitbucketcloud/bitbucket.go
@@ -21,6 +21,7 @@ type Provider struct {
 	Logger        *zap.SugaredLogger
 	Token, APIURL *string
 	Username      *string
+	provenance    string
 }
 
 // GetTaskURI TODO: Implement ME
@@ -115,7 +116,8 @@ func (v *Provider) CreateStatus(_ context.Context, _ versioned.Interface, event
 	return nil
 }
 
-func (v *Provider) GetTektonDir(_ context.Context, event *info.Event, path string) (string, error) {
+func (v *Provider) GetTektonDir(_ context.Context, event *info.Event, path, provenance string) (string, error) {
+	v.provenance = provenance
 	repositoryFiles, err := v.getDir(event, path)
 	if err != nil {
 		return "", err
@@ -125,10 +127,18 @@ func (v *Provider) GetTektonDir(_ context.Context, event *info.Event, path strin
 }
 
 func (v *Provider) getDir(event *info.Event, path string) ([]bitbucket.RepositoryFile, error) {
+	// default set provenance from the SHA
+	revision := event.SHA
+	if v.provenance == "default_branch" {
+		revision = event.DefaultBranch
+		v.Logger.Infof("Using PipelineRun definition from default_branch: %s", event.DefaultBranch)
+	} else {
+		v.Logger.Infof("Using PipelineRun definition from source pull request SHA: %s", event.SHA)
+	}
 	repoFileOpts := &bitbucket.RepositoryFilesOptions{
 		Owner:    event.Organization,
 		RepoSlug: event.Repository,
-		Ref:      event.SHA,
+		Ref:      revision,
 		Path:     path,
 	}
 
@@ -139,8 +149,12 @@ func (v *Provider) getDir(event *info.Event, path string) ([]bitbucket.Repositor
 	return repositoryFiles, nil
 }
 
-func (v *Provider) GetFileInsideRepo(_ context.Context, runevent *info.Event, path, _ string) (string, error) {
-	return v.getBlob(runevent, runevent.SHA, path)
+func (v *Provider) GetFileInsideRepo(_ context.Context, event *info.Event, path, _ string) (string, error) {
+	revision := event.SHA
+	if v.provenance == "default_branch" {
+		revision = event.DefaultBranch
+	}
+	return v.getBlob(event, revision, path)
 }
 
 func (v *Provider) SetClient(_ context.Context, _ *params.Run, event *info.Event) error {
@@ -203,16 +217,20 @@ func (v *Provider) GetCommitInfo(_ context.Context, event *info.Event) error {
 	return nil
 }
 
-func (v *Provider) concatAllYamlFiles(objects []bitbucket.RepositoryFile, runevent *info.Event) (string, error) {
+func (v *Provider) concatAllYamlFiles(objects []bitbucket.RepositoryFile, event *info.Event) (string, error) {
 	var allTemplates string
 
+	revision := event.SHA
+	if v.provenance == "default_branch" {
+		revision = event.DefaultBranch
+	}
 	for _, value := range objects {
 		if value.Type == "commit_directory" {
-			objects, err := v.getDir(runevent, value.Path)
+			objects, err := v.getDir(event, value.Path)
 			if err != nil {
 				return "", err
 			}
-			subdirdata, err := v.concatAllYamlFiles(objects, runevent)
+			subdirdata, err := v.concatAllYamlFiles(objects, event)
 			if err != nil {
 				return "", err
 			}
@@ -222,7 +240,7 @@ func (v *Provider) concatAllYamlFiles(objects []bitbucket.RepositoryFile, runeve
 			allTemplates += fmt.Sprintf("\n%s\n", subdirdata)
 		} else if strings.HasSuffix(value.Path, ".yaml") ||
 			strings.HasSuffix(value.Path, ".yml") {
-			data, err := v.getBlob(runevent, runevent.SHA, value.Path)
+			data, err := v.getBlob(event, revision, value.Path)
 			if err != nil {
 				return "", err
 			}
diff --git a/pkg/provider/bitbucketserver/bitbucketserver.go b/pkg/provider/bitbucketserver/bitbucketserver.go
index 8f6ad55a..fdfcaef9 100644
--- a/pkg/provider/bitbucketserver/bitbucketserver.go
+++ b/pkg/provider/bitbucketserver/bitbucketserver.go
@@ -27,6 +27,7 @@ type Provider struct {
 	defaultBranchLatestCommit string
 	pullRequestNumber         int
 	apiURL                    string
+	provenance                string
 	projectKey                string
 }
 
@@ -153,9 +154,18 @@ func (v *Provider) getRaw(runevent *info.Event, revision, path string) (string,
 	return string(resp.Payload), nil
 }
 
-func (v *Provider) GetTektonDir(_ context.Context, event *info.Event, path string) (string, error) {
+func (v *Provider) GetTektonDir(_ context.Context, event *info.Event, path, provenance string) (string, error) {
+	v.provenance = provenance
 	allValues, err := paginate(func(nextPage int) (*bbv1.APIResponse, error) {
-		localVarOptionals := map[string]interface{}{"at": event.SHA}
+		// according to the docs, if no at parameters is specified it will default to the default branch
+		// cf: https://docs.atlassian.com/bitbucket-server/rest/4.1.0/bitbucket-rest.html#idp2425664
+		localVarOptionals := map[string]interface{}{}
+		if v.provenance == "source" {
+			localVarOptionals = map[string]interface{}{"at": event.SHA}
+			v.Logger.Infof("Using PipelineRun definition from source pull request SHA: %s", event.SHA)
+		} else {
+			v.Logger.Infof("Using PipelineRun definition from default_branch: %s", event.DefaultBranch)
+		}
 		if nextPage != 0 {
 			localVarOptionals["start"] = nextPage
 		}
diff --git a/pkg/provider/gitea/gitea.go b/pkg/provider/gitea/gitea.go
index 7ffe56eb..8c45acf2 100644
--- a/pkg/provider/gitea/gitea.go
+++ b/pkg/provider/gitea/gitea.go
@@ -175,9 +175,18 @@ func getCheckName(status provider.StatusOpts, pacopts *info.PacOpts) string {
 	return status.OriginalPipelineRunName
 }
 
-func (v *Provider) GetTektonDir(_ context.Context, event *info.Event, path string) (string, error) {
+func (v *Provider) GetTektonDir(_ context.Context, event *info.Event, path, provenance string) (string, error) {
+	// default set provenance from the SHA
+	revision := event.SHA
+	if provenance == "default_branch" {
+		revision = event.DefaultBranch
+		v.Logger.Infof("Using PipelineRun definition from default_branch: %s", event.DefaultBranch)
+	} else {
+		v.Logger.Infof("Using PipelineRun definition from source pull request SHA: %s", event.SHA)
+	}
+
 	tektonDirSha := ""
-	rootobjects, _, err := v.Client.GetTrees(event.Organization, event.Repository, event.SHA, false)
+	rootobjects, _, err := v.Client.GetTrees(event.Organization, event.Repository, revision, false)
 	if err != nil {
 		return "", err
 	}
diff --git a/pkg/provider/github/github.go b/pkg/provider/github/github.go
index 0a1e26af..3cd801fd 100644
--- a/pkg/provider/github/github.go
+++ b/pkg/provider/github/github.go
@@ -37,6 +37,7 @@ type Provider struct {
 	Token, APIURL *string
 	ApplicationID *int64
 	providerName  string
+	provenance    string
 	Run           *params.Run
 	RepositoryIDs []int64
 
@@ -253,10 +254,20 @@ func (v *Provider) SetClient(ctx context.Context, run *params.Run, event *info.E
 }
 
 // GetTektonDir Get all yaml files in tekton directory return as a single concated file
-func (v *Provider) GetTektonDir(ctx context.Context, runevent *info.Event, path string) (string, error) {
+func (v *Provider) GetTektonDir(ctx context.Context, runevent *info.Event, path, provenance string) (string, error) {
 	tektonDirSha := ""
 
-	rootobjects, _, err := v.Client.Git.GetTree(ctx, runevent.Organization, runevent.Repository, runevent.SHA, false)
+	v.provenance = provenance
+	// default set provenance from the SHA
+	revision := runevent.SHA
+	if provenance == "default_branch" {
+		revision = runevent.DefaultBranch
+		v.Logger.Infof("Using PipelineRun definition from default_branch: %s", runevent.DefaultBranch)
+	} else {
+		v.Logger.Infof("Using PipelineRun definition from source pull request SHA: %s", runevent.SHA)
+	}
+
+	rootobjects, _, err := v.Client.Git.GetTree(ctx, runevent.Organization, runevent.Repository, revision, false)
 	if err != nil {
 		return "", err
 	}
@@ -325,6 +336,8 @@ func (v *Provider) GetFileInsideRepo(ctx context.Context, runevent *info.Event,
 	ref := runevent.SHA
 	if target != "" {
 		ref = runevent.BaseBranch
+	} else if v.provenance == "default_branch" {
+		ref = runevent.DefaultBranch
 	}
 
 	fp, objects, _, err := v.Client.Repositories.GetContents(ctx, runevent.Organization,
diff --git a/pkg/provider/github/repository.go b/pkg/provider/github/repository.go
index 777acc82..22d78e5d 100644
--- a/pkg/provider/github/repository.go
+++ b/pkg/provider/github/repository.go
@@ -90,6 +90,7 @@ func createRepository(ctx context.Context, nsTemplate string, clients clients.Cl
 	if err != nil {
 		return fmt.Errorf("failed to create repository for repo: %v: %w", gitEvent.Repo.GetHTMLURL(), err)
 	}
+	logger = logger.With("namespace", repo.Namespace)
 	logger.Infof("github: repository created: %s/%s ", repo.Namespace, repo.Name)
 	return nil
 }
diff --git a/pkg/provider/gitlab/gitlab.go b/pkg/provider/gitlab/gitlab.go
index c2092f95..d43e3b25 100644
--- a/pkg/provider/gitlab/gitlab.go
+++ b/pkg/provider/gitlab/gitlab.go
@@ -202,15 +202,23 @@ func (v *Provider) CreateStatus(_ context.Context, _ versioned.Interface, event
 	return nil
 }
 
-func (v *Provider) GetTektonDir(_ context.Context, event *info.Event, path string) (string, error) {
+func (v *Provider) GetTektonDir(_ context.Context, event *info.Event, path, provenance string) (string, error) {
 	if v.Client == nil {
 		return "", fmt.Errorf("no gitlab client has been initiliazed, " +
 			"exiting... (hint: did you forget setting a secret on your repo?)")
 	}
+	// default set provenance from head
+	revision := event.HeadBranch
+	if provenance == "default_branch" {
+		revision = event.DefaultBranch
+		v.Logger.Infof("Using PipelineRun definition from default_branch: %s", event.DefaultBranch)
+	} else {
+		v.Logger.Infof("Using PipelineRun definition from source merge request SHA: %s", event.SHA)
+	}
 
 	opt := &gitlab.ListTreeOptions{
 		Path:      gitlab.String(path),
-		Ref:       gitlab.String(event.HeadBranch),
+		Ref:       gitlab.String(revision),
 		Recursive: gitlab.Bool(true),
 	}
 
diff --git a/pkg/provider/interface.go b/pkg/provider/interface.go
index 1a41007c..89b23f30 100644
--- a/pkg/provider/interface.go
+++ b/pkg/provider/interface.go
@@ -30,7 +30,7 @@ type Interface interface {
 	ParsePayload(context.Context, *params.Run, *http.Request, string) (*info.Event, error)
 	IsAllowed(context.Context, *info.Event) (bool, error)
 	CreateStatus(context.Context, versioned.Interface, *info.Event, *info.PacOpts, StatusOpts) error
-	GetTektonDir(context.Context, *info.Event, string) (string, error)              // ctx, event, path
+	GetTektonDir(context.Context, *info.Event, string, string) (string, error)      // ctx, event, path, provenance
 	GetFileInsideRepo(context.Context, *info.Event, string, string) (string, error) // ctx, event, path, branch
 	SetClient(context.Context, *params.Run, *info.Event) error
 	GetCommitInfo(context.Context, *info.Event) error
diff --git a/pkg/reconciler/controller.go b/pkg/reconciler/controller.go
index a8b739d6..2d68e646 100644
--- a/pkg/reconciler/controller.go
+++ b/pkg/reconciler/controller.go
@@ -2,7 +2,6 @@ package reconciler
 
 import (
 	"context"
-	"log"
 
 	"github.com/openshift-pipelines/pipelines-as-code/pkg/apis/pipelinesascode"
 	"github.com/openshift-pipelines/pipelines-as-code/pkg/apis/pipelinesascode/keys"
@@ -19,10 +18,12 @@ import (
 	"knative.dev/pkg/configmap"
 	"knative.dev/pkg/controller"
 	"knative.dev/pkg/kmeta"
+	"knative.dev/pkg/logging"
 )
 
 func NewController() func(context.Context, configmap.Watcher) *controller.Impl {
 	return func(ctx context.Context, cmw configmap.Watcher) *controller.Impl {
+		log := logging.FromContext(ctx)
 		run := params.New()
 		err := run.Clients.NewClients(ctx, &run.Info)
 		if err != nil {
diff --git a/pkg/reconciler/finalizer.go b/pkg/reconciler/finalizer.go
index 28914667..8199ed27 100644
--- a/pkg/reconciler/finalizer.go
+++ b/pkg/reconciler/finalizer.go
@@ -38,6 +38,7 @@ func (r *Reconciler) FinalizeKind(ctx context.Context, pr *tektonv1.PipelineRun)
 			return err
 		}
 
+		logger = logger.With("namespace", repo.Namespace)
 		next := r.qm.RemoveFromQueue(repo, pr)
 		if next != "" {
 			key := strings.Split(next, "/")
diff --git a/pkg/reconciler/reconciler.go b/pkg/reconciler/reconciler.go
index fd24a4a2..82e9928c 100644
--- a/pkg/reconciler/reconciler.go
+++ b/pkg/reconciler/reconciler.go
@@ -8,6 +8,7 @@ import (
 	"github.com/openshift-pipelines/pipelines-as-code/pkg/action"
 	"github.com/openshift-pipelines/pipelines-as-code/pkg/apis/pipelinesascode/keys"
 	"github.com/openshift-pipelines/pipelines-as-code/pkg/apis/pipelinesascode/v1alpha1"
+	"github.com/openshift-pipelines/pipelines-as-code/pkg/customparams"
 	"github.com/openshift-pipelines/pipelines-as-code/pkg/events"
 	pipelinesascode "github.com/openshift-pipelines/pipelines-as-code/pkg/generated/listers/pipelinesascode/v1alpha1"
 	"github.com/openshift-pipelines/pipelines-as-code/pkg/kubeinteraction"
@@ -45,7 +46,7 @@ var (
 
 // ReconcileKind is the main entry point for reconciling PipelineRun resources.
 func (r *Reconciler) ReconcileKind(ctx context.Context, pr *tektonv1.PipelineRun) pkgreconciler.Event {
-	logger := logging.FromContext(ctx)
+	logger := logging.FromContext(ctx).With("namespace", pr.GetNamespace())
 
 	// if pipelineRun is in completed or failed state then return
 	state, exist := pr.GetAnnotations()[keys.State]
@@ -99,6 +100,14 @@ func (r *Reconciler) reportFinalStatus(ctx context.Context, logger *zap.SugaredL
 		return nil, fmt.Errorf("reportFinalStatus: %w", err)
 	}
 
+	cp := customparams.NewCustomParams(event, repo, r.run, r.kinteract, r.eventEmitter)
+	maptemplate, err := cp.GetParams(ctx)
+	if err != nil {
+		r.eventEmitter.EmitMessage(repo, zap.ErrorLevel, "ParamsError",
+			fmt.Sprintf("error processing repository CR custom params: %s", err.Error()))
+	}
+	r.run.Clients.ConsoleUI.SetParams(maptemplate)
+
 	if event.InstallationID > 0 {
 		event.Provider.WebhookSecret, _ = pipelineascode.GetCurrentNSWebhookSecret(ctx, r.kinteract)
 	} else {
diff --git a/pkg/templates/templating.go b/pkg/templates/templating.go
index 36620018..ad8a4171 100644
--- a/pkg/templates/templating.go
+++ b/pkg/templates/templating.go
@@ -1,16 +1,15 @@
 package templates
 
 import (
-	"regexp"
 	"strings"
-)
 
-var reTemplate = regexp.MustCompile(`{{([^}]{2,})}}`)
+	"github.com/openshift-pipelines/pipelines-as-code/pkg/apis/pipelinesascode/keys"
+)
 
 // ReplacePlaceHoldersVariables Replace those {{var}} placeholders to the runinfo variable
 func ReplacePlaceHoldersVariables(template string, dico map[string]string) string {
-	return reTemplate.ReplaceAllStringFunc(template, func(s string) string {
-		parts := reTemplate.FindStringSubmatch(s)
+	return keys.ParamsRe.ReplaceAllStringFunc(template, func(s string) string {
+		parts := keys.ParamsRe.FindStringSubmatch(s)
 		key := strings.TrimSpace(parts[1])
 		if _, ok := dico[key]; !ok {
 			return s


Test patch:
diff --git a/pkg/cli/webhook/secret_test.go b/pkg/cli/webhook/secret_test.go
new file mode 100644
index 00000000..138627b5
--- /dev/null
+++ b/pkg/cli/webhook/secret_test.go
@@ -0,0 +1,90 @@
+package webhook
+
+import (
+	"fmt"
+	"strings"
+	"testing"
+
+	pacv1alpha1 "github.com/openshift-pipelines/pipelines-as-code/pkg/apis/pipelinesascode/v1alpha1"
+	"github.com/openshift-pipelines/pipelines-as-code/pkg/cli"
+	"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"
+	"github.com/openshift-pipelines/pipelines-as-code/pkg/params/settings"
+	"github.com/openshift-pipelines/pipelines-as-code/pkg/pipelineascode"
+	testclient "github.com/openshift-pipelines/pipelines-as-code/pkg/test/clients"
+	"github.com/openshift-pipelines/pipelines-as-code/pkg/test/logger"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/golden"
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	rtesting "knative.dev/pkg/reconciler/testing"
+)
+
+func TestWebHookSecret(t *testing.T) {
+	ctx, _ := rtesting.SetupFakeContext(t)
+	anURL := "https://hello.world/"
+	repoName := "test-repo"
+	repoNS := "test-ns"
+	secretName := "test-secret"
+	data := testclient.Data{
+		Secret: []*corev1.Secret{
+			{
+				ObjectMeta: metav1.ObjectMeta{
+					Name:      secretName,
+					Namespace: repoNS,
+				},
+				Data: map[string][]byte{
+					pipelineascode.DefaultGitProviderSecretKey: []byte("somethingsomething"),
+				},
+			},
+		},
+		Repositories: []*pacv1alpha1.Repository{
+			{
+				ObjectMeta: metav1.ObjectMeta{
+					Name:      repoName,
+					Namespace: repoNS,
+				},
+				Spec: pacv1alpha1.RepositorySpec{
+					URL: anURL,
+				},
+			},
+		},
+		Namespaces: []*corev1.Namespace{
+			{
+				ObjectMeta: metav1.ObjectMeta{
+					Name: repoNS,
+				},
+			},
+		},
+	}
+
+	cs, _ := testclient.SeedTestData(t, ctx, data)
+	logger, _ := logger.GetLogger()
+	io, _, out, _ := cli.IOTest()
+	w := &Options{
+		Run: &params.Run{
+			Clients: clients.Clients{
+				PipelineAsCode: cs.PipelineAsCode,
+				Log:            logger,
+				Kube:           cs.Kube,
+			},
+			Info: info.Info{
+				Pac: &info.PacOpts{
+					Settings: &settings.Settings{
+						AutoConfigureNewGitHubRepo: false,
+					},
+				},
+			},
+		},
+		IOStreams:           io,
+		RepositoryName:      repoName,
+		RepositoryNamespace: repoNS,
+		SecretName:          secretName,
+	}
+	response := &response{PersonalAccessToken: "pattest", WebhookSecret: "webtest", UserName: "testuser", APIURL: "https://api.github.com/"}
+	assert.NilError(t, w.createWebhookSecret(ctx, response))
+	assert.NilError(t, w.updateRepositoryCR(ctx, response))
+	assert.NilError(t, w.updateWebhookSecret(ctx, response))
+	golden.Assert(t, out.String(), strings.ReplaceAll(fmt.Sprintf("%s.golden", t.Name()), "/", "-"))
+}
diff --git a/pkg/cli/webhook/testdata/TestWebHookSecret.golden b/pkg/cli/webhook/testdata/TestWebHookSecret.golden
new file mode 100644
index 00000000..15faaf3b
--- /dev/null
+++ b/pkg/cli/webhook/testdata/TestWebHookSecret.golden
@@ -0,0 +1,3 @@
+🔑 Webhook Secret test-repo has been created in the test-ns namespace.
+🔑 Repository CR test-repo has been updated with webhook secret in the test-ns namespace
+🔑 Secret test-secret has been updated with webhook secert in the test-ns namespace.
diff --git a/pkg/cmd/tknpac/bootstrap/bootstrap_test.go b/pkg/cmd/tknpac/bootstrap/bootstrap_test.go
new file mode 100644
index 00000000..3d546a31
--- /dev/null
+++ b/pkg/cmd/tknpac/bootstrap/bootstrap_test.go
@@ -0,0 +1,48 @@
+package bootstrap
+
+import (
+	"bytes"
+	"io"
+	"testing"
+
+	"github.com/openshift-pipelines/pipelines-as-code/pkg/cli"
+	"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"
+	"github.com/openshift-pipelines/pipelines-as-code/pkg/test/logger"
+	"gotest.tools/v3/assert"
+	rtesting "knative.dev/pkg/reconciler/testing"
+)
+
+func newIOStream() (*cli.IOStreams, *bytes.Buffer) {
+	in := &bytes.Buffer{}
+	out := &bytes.Buffer{}
+	errOut := &bytes.Buffer{}
+	return &cli.IOStreams{
+		In:     io.NopCloser(in),
+		Out:    out,
+		ErrOut: errOut,
+	}, out
+}
+
+func TestInstall(t *testing.T) {
+	ctx, _ := rtesting.SetupFakeContext(t)
+	cs, _ := testclient.SeedTestData(t, ctx, testclient.Data{})
+	logger, _ := logger.GetLogger()
+
+	run := &params.Run{
+		Clients: clients.Clients{
+			PipelineAsCode: cs.PipelineAsCode,
+			Log:            logger,
+			Kube:           cs.Kube,
+		},
+		Info: info.Info{},
+	}
+	io, out := newIOStream()
+	opts := &bootstrapOpts{ioStreams: io}
+	err := install(ctx, run, opts)
+	// get an error because i need to figure out how to fake dynamic client
+	assert.Assert(t, err != nil)
+	assert.Equal(t, "=> Checking if Pipelines as Code is installed.\n", out.String())
+}
diff --git a/pkg/consoleui/custom_test.go b/pkg/consoleui/custom_test.go
index 95fc1e60..9643c179 100644
--- a/pkg/consoleui/custom_test.go
+++ b/pkg/consoleui/custom_test.go
@@ -15,8 +15,8 @@ import (
 func TestCustomGood(t *testing.T) {
 	consoleName := "MyCorp Console"
 	consoleURL := "https://mycorp.console"
-	consolePRdetail := "https://mycorp.console/{{ namespace }}/{{ pr }}"
-	consolePRtasklog := "https://mycorp.console/{{ namespace }}/{{ pr }}/{{ task }}/{{ pod }}/{{ firstFailedStep }}"
+	consolePRdetail := "https://mycorp.console/{{ namespace }}/{{ pr }}/params/{{ foo }}"
+	consolePRtasklog := "https://mycorp.console/{{ namespace }}/{{ pr }}/{{ task }}/{{ pod }}/{{ firstFailedStep }}/params/{{ foo }}/{{ nonewline }}"
 
 	c := CustomConsole{
 		Info: &info.Info{
@@ -30,6 +30,10 @@ func TestCustomGood(t *testing.T) {
 			},
 		},
 	}
+	c.SetParams(map[string]string{
+		"foo":       "bar",
+		"nonewline": "nonewline\n ",
+	})
 	pr := &tektonv1.PipelineRun{
 		ObjectMeta: metav1.ObjectMeta{
 			Namespace: "ns",
@@ -64,8 +68,25 @@ func TestCustomGood(t *testing.T) {
 	}
 	assert.Equal(t, c.GetName(), consoleName)
 	assert.Equal(t, c.URL(), consoleURL)
-	assert.Equal(t, c.DetailURL(pr), "https://mycorp.console/ns/pr")
-	assert.Equal(t, c.TaskLogURL(pr, trStatus), "https://mycorp.console/ns/pr/task/pod/failure")
+	assert.Equal(t, c.DetailURL(pr), "https://mycorp.console/ns/pr/params/bar")
+	assert.Equal(t, c.TaskLogURL(pr, trStatus), "https://mycorp.console/ns/pr/task/pod/failure/params/bar/nonewline")
+
+	// test if we fallback properly
+	f := CustomConsole{
+		Info: &info.Info{
+			Pac: &info.PacOpts{
+				Settings: &settings.Settings{
+					CustomConsoleName:      consoleName,
+					CustomConsoleURL:       consoleURL,
+					CustomConsolePRdetail:  "{{ notthere}}",
+					CustomConsolePRTaskLog: "{{ notthere}}",
+				},
+			},
+		},
+	}
+	f.SetParams(map[string]string{})
+	assert.Assert(t, strings.Contains(c.DetailURL(pr), consoleURL))
+	assert.Assert(t, strings.Contains(c.TaskLogURL(pr, trStatus), consoleURL))
 }
 
 func TestCustomBad(t *testing.T) {
diff --git a/pkg/customparams/customparams_test.go b/pkg/customparams/customparams_test.go
new file mode 100644
index 00000000..790c8d08
--- /dev/null
+++ b/pkg/customparams/customparams_test.go
@@ -0,0 +1,283 @@
+package customparams
+
+import (
+	"testing"
+
+	"github.com/google/go-github/v50/github"
+	"github.com/openshift-pipelines/pipelines-as-code/pkg/apis/pipelinesascode/v1alpha1"
+	"github.com/openshift-pipelines/pipelines-as-code/pkg/events"
+	"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"
+	kitesthelper "github.com/openshift-pipelines/pipelines-as-code/pkg/test/kubernetestint"
+	"go.uber.org/zap"
+	zapobserver "go.uber.org/zap/zaptest/observer"
+	"gotest.tools/v3/assert"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	rtesting "knative.dev/pkg/reconciler/testing"
+)
+
+func TestProcessTemplates(t *testing.T) {
+	ns := "there"
+	tests := []struct {
+		name               string
+		event              *info.Event
+		expected           map[string]string
+		repository         *v1alpha1.Repository
+		secretData         map[string]string
+		expectedLogSnippet string
+		expectedError      bool
+	}{
+		{
+			name:     "params/basic",
+			expected: map[string]string{"params": "batman"},
+			repository: &v1alpha1.Repository{
+				Spec: v1alpha1.RepositorySpec{
+					Params: &[]v1alpha1.Params{
+						{
+							Name:  "params",
+							Value: "batman",
+						},
+					},
+				},
+			},
+		},
+		{
+			name:     "params/from secret",
+			expected: map[string]string{"params": "gone", "target_namespace": ns},
+			secretData: map[string]string{
+				"name": "gone",
+			},
+			repository: &v1alpha1.Repository{
+				ObjectMeta: metav1.ObjectMeta{
+					Namespace: ns,
+				},
+				Spec: v1alpha1.RepositorySpec{
+					Params: &[]v1alpha1.Params{
+						{
+							Name: "params",
+							SecretRef: &v1alpha1.Secret{
+								Name: "name",
+								Key:  "key",
+							},
+						},
+					},
+				},
+			},
+		},
+		{
+			name:     "params/use last params when two values of the same name",
+			expected: map[string]string{"params": "robin"},
+			repository: &v1alpha1.Repository{
+				Spec: v1alpha1.RepositorySpec{
+					Params: &[]v1alpha1.Params{
+						{
+							Name:  "params",
+							Value: "batman",
+						},
+						{
+							Name:  "params",
+							Value: "robin",
+						},
+					},
+				},
+			},
+		},
+		{
+			name:     "params/fallback to stdparams",
+			expected: map[string]string{"event_type": "pull_request"},
+			event:    &info.Event{EventType: "pull_request"},
+			repository: &v1alpha1.Repository{
+				Spec: v1alpha1.RepositorySpec{
+					Params: &[]v1alpha1.Params{},
+				},
+			},
+		},
+		{
+			name:               "params/skip with no name",
+			expectedLogSnippet: "no name has been set in params[0] of repo",
+			repository: &v1alpha1.Repository{
+				Spec: v1alpha1.RepositorySpec{
+					Params: &[]v1alpha1.Params{
+						{
+							Value: "batman",
+						},
+					},
+				},
+			},
+		},
+		{
+			name:               "params/pick value when value and secret set",
+			expected:           map[string]string{"params": "batman"},
+			expectedLogSnippet: "repo repo, param name params has a value and secretref, picking value",
+			repository: &v1alpha1.Repository{
+				ObjectMeta: metav1.ObjectMeta{
+					Name: "repo",
+				},
+				Spec: v1alpha1.RepositorySpec{
+					Params: &[]v1alpha1.Params{
+						{
+							Name:  "params",
+							Value: "batman",
+							SecretRef: &v1alpha1.Secret{
+								Name: "name",
+								Key:  "key",
+							},
+						},
+					},
+				},
+			},
+		},
+		{
+			name:     "params/filter",
+			expected: map[string]string{"event_type": "pull_request", "params": "batman"},
+			event:    &info.Event{EventType: "pull_request"},
+			repository: &v1alpha1.Repository{
+				Spec: v1alpha1.RepositorySpec{
+					Params: &[]v1alpha1.Params{
+						{
+							Name:   "params",
+							Value:  "batman",
+							Filter: `pac.event_type == "pull_request"`,
+						},
+					},
+				},
+			},
+		},
+		{
+			name:     "params/filter on body",
+			expected: map[string]string{"params": "batman", "event_type": "pull_request"},
+			event:    &info.Event{EventType: "pull_request", Event: github.PullRequestEvent{Number: github.Int(42)}},
+			repository: &v1alpha1.Repository{
+				Spec: v1alpha1.RepositorySpec{
+					Params: &[]v1alpha1.Params{
+						{
+							Name:   "params",
+							Value:  "batman",
+							Filter: `body.number == 42`,
+						},
+					},
+				},
+			},
+		},
+		{
+			name:          "params/filter on body with bad filter",
+			event:         &info.Event{EventType: "pull_request", Event: github.PullRequestEvent{Number: github.Int(42)}},
+			expectedError: true,
+			repository: &v1alpha1.Repository{
+				Spec: v1alpha1.RepositorySpec{
+					Params: &[]v1alpha1.Params{
+						{
+							Name:   "params",
+							Value:  "batman",
+							Filter: `body.BADDDADA == 42`,
+						},
+					},
+				},
+			},
+		},
+		{
+			name:          "params/bad filter skipped",
+			event:         &info.Event{EventType: "push"},
+			expectedError: true,
+			repository: &v1alpha1.Repository{
+				Spec: v1alpha1.RepositorySpec{
+					Params: &[]v1alpha1.Params{
+						{
+							Name:   "params",
+							Value:  "batman",
+							Filter: `BADDADADDADA`,
+						},
+					},
+				},
+			},
+		},
+		{
+			name:          "params/bad payload skipped",
+			event:         &info.Event{EventType: "push", Event: "BADADDADA"},
+			expectedError: true,
+			repository: &v1alpha1.Repository{
+				Spec: v1alpha1.RepositorySpec{
+					Params: &[]v1alpha1.Params{
+						{
+							Name:   "params",
+							Value:  "batman",
+							Filter: `body.number == 42`,
+						},
+					},
+				},
+			},
+		},
+		{
+			name:               "params/no filter match",
+			expectedLogSnippet: "skipping params name params, filter condition is false",
+			event:              &info.Event{EventType: "push"},
+			repository: &v1alpha1.Repository{
+				Spec: v1alpha1.RepositorySpec{
+					Params: &[]v1alpha1.Params{
+						{
+							Name:   "params",
+							Value:  "batman",
+							Filter: `pac.event_type == "pull_request"`,
+						},
+					},
+				},
+			},
+		},
+		{
+			name:               "params/two filters same name, match first",
+			expected:           map[string]string{"params": "batman1", "event_type": "pull_request"},
+			event:              &info.Event{EventType: "pull_request"},
+			expectedLogSnippet: "skipping params name params, filter has already been matched previously",
+			repository: &v1alpha1.Repository{
+				Spec: v1alpha1.RepositorySpec{
+					Params: &[]v1alpha1.Params{
+						{
+							Name:   "params",
+							Value:  "batman1",
+							Filter: `pac.event_type == "pull_request"`,
+						},
+						{
+							Name:   "params",
+							Value:  "batman2",
+							Filter: `pac.event_type == "pull_request"`,
+						},
+					},
+				},
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			observer, log := zapobserver.New(zap.InfoLevel)
+			logger := zap.New(observer).Sugar()
+			repo := tt.repository
+			if repo == nil {
+				repo = &v1alpha1.Repository{}
+			}
+			ctx, _ := rtesting.SetupFakeContext(t)
+			run := &params.Run{Clients: clients.Clients{}}
+			if tt.event == nil {
+				tt.event = &info.Event{}
+			}
+			p := NewCustomParams(tt.event, repo, run, &kitesthelper.KinterfaceTest{GetSecretResult: tt.secretData}, nil)
+			stdata, _ := testclient.SeedTestData(t, ctx, testclient.Data{})
+			p.eventEmitter = events.NewEventEmitter(stdata.Kube, logger)
+			ret, err := p.GetParams(ctx)
+			if tt.expectedError {
+				assert.Assert(t, err != nil, "expected error but got nil")
+			} else {
+				assert.NilError(t, err)
+			}
+			if len(tt.expected) > 0 {
+				assert.DeepEqual(t, tt.expected, ret)
+			}
+			if tt.expectedLogSnippet != "" {
+				logmsg := log.FilterMessageSnippet(tt.expectedLogSnippet).TakeAll()
+				assert.Assert(t, len(logmsg) > 0, "log message filtered %s expected %s all logs: %+v", logmsg,
+					tt.expectedLogSnippet, log.TakeAll())
+			}
+		})
+	}
+}
diff --git a/pkg/customparams/standard_test.go b/pkg/customparams/standard_test.go
new file mode 100644
index 00000000..31d60a9a
--- /dev/null
+++ b/pkg/customparams/standard_test.go
@@ -0,0 +1,54 @@
+package customparams
+
+import (
+	"testing"
+
+	"github.com/openshift-pipelines/pipelines-as-code/pkg/apis/pipelinesascode/v1alpha1"
+	"github.com/openshift-pipelines/pipelines-as-code/pkg/params/info"
+	"gotest.tools/v3/assert"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+func TestMakeStandardParamsFromEvent(t *testing.T) {
+	event := &info.Event{
+		SHA:          "1234567890",
+		Organization: "Org",
+		Repository:   "Repo",
+		BaseBranch:   "main",
+		HeadBranch:   "foo",
+		EventType:    "pull_request",
+		Sender:       "SENDER",
+		URL:          "https://paris.com",
+	}
+
+	result := map[string]string{
+		"event_type":       "pull_request",
+		"repo_name":        "repo",
+		"repo_owner":       "org",
+		"repo_url":         "https://paris.com",
+		"revision":         "1234567890",
+		"sender":           "sender",
+		"source_branch":    "foo",
+		"target_branch":    "main",
+		"target_namespace": "myns",
+	}
+
+	repo := &v1alpha1.Repository{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      "myname",
+			Namespace: "myns",
+		},
+	}
+
+	p := NewCustomParams(event, repo, nil, nil, nil)
+	params := p.makeStandardParamsFromEvent()
+	assert.DeepEqual(t, params, result)
+
+	nevent := &info.Event{}
+	event.DeepCopyInto(nevent)
+	nevent.CloneURL = "https://blahblah"
+	p.event = nevent
+	nparams := p.makeStandardParamsFromEvent()
+	result["repo_url"] = nevent.CloneURL
+	assert.DeepEqual(t, nparams, result)
+}
diff --git a/pkg/formatting/k8labels_test.go b/pkg/formatting/k8labels_test.go
index 37f56af7..ffe282f6 100644
--- a/pkg/formatting/k8labels_test.go
+++ b/pkg/formatting/k8labels_test.go
@@ -28,6 +28,11 @@ func TestK8LabelsCleanup(t *testing.T) {
 			str:  "MBAPPEvsMESSI--",
 			want: "MBAPPEvsMESSI",
 		},
+		{
+			name: "remove new line",
+			str:  "foo\n",
+			want: "foo",
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
diff --git a/pkg/provider/bitbucketcloud/acl_test.go b/pkg/provider/bitbucketcloud/acl_test.go
index f6ba3d33..5ad7ff02 100644
--- a/pkg/provider/bitbucketcloud/acl_test.go
+++ b/pkg/provider/bitbucketcloud/acl_test.go
@@ -177,7 +177,7 @@ func TestIsAllowed(t *testing.T) {
 
 			bbcloudtest.MuxOrgMember(t, mux, tt.event, tt.fields.workspaceMembers)
 			bbcloudtest.MuxComments(t, mux, tt.event, tt.fields.comments)
-			bbcloudtest.MuxFiles(t, mux, tt.event, tt.fields.filescontents)
+			bbcloudtest.MuxFiles(t, mux, tt.event, tt.fields.filescontents, "")
 
 			v := &Provider{Client: bbclient}
 
diff --git a/pkg/provider/bitbucketcloud/bitbucket_test.go b/pkg/provider/bitbucketcloud/bitbucket_test.go
index d06b53fd..f77db19f 100644
--- a/pkg/provider/bitbucketcloud/bitbucket_test.go
+++ b/pkg/provider/bitbucketcloud/bitbucket_test.go
@@ -10,6 +10,8 @@ import (
 	"github.com/openshift-pipelines/pipelines-as-code/pkg/provider"
 	bbcloudtest "github.com/openshift-pipelines/pipelines-as-code/pkg/provider/bitbucketcloud/test"
 	"github.com/openshift-pipelines/pipelines-as-code/pkg/provider/bitbucketcloud/types"
+	"go.uber.org/zap"
+	zapobserver "go.uber.org/zap/zaptest/observer"
 	"gotest.tools/v3/assert"
 	rtesting "knative.dev/pkg/reconciler/testing"
 )
@@ -22,18 +24,29 @@ func TestGetConfig(t *testing.T) {
 
 func TestGetTektonDir(t *testing.T) {
 	tests := []struct {
-		name            string
-		event           *info.Event
-		testDirPath     string
-		contentContains string
-		wantErr         bool
-		removeSuffix    bool
+		name                 string
+		event                *info.Event
+		testDirPath          string
+		contentContains      string
+		wantErr              bool
+		removeSuffix         bool
+		provenance           string
+		filterMessageSnippet string
 	}{
 		{
-			name:            "Get Tekton Directory",
-			event:           bbcloudtest.MakeEvent(nil),
-			testDirPath:     "../../pipelineascode/testdata/pull_request/.tekton",
-			contentContains: "kind: PipelineRun",
+			name:                 "Get Tekton Directory",
+			event:                bbcloudtest.MakeEvent(nil),
+			testDirPath:          "../../pipelineascode/testdata/pull_request/.tekton",
+			contentContains:      "kind: PipelineRun",
+			filterMessageSnippet: "Using PipelineRun definition from source pull request SHA",
+		},
+		{
+			name:                 "Get Tekton Directory Mainbranch",
+			event:                bbcloudtest.MakeEvent(&info.Event{DefaultBranch: "main"}),
+			testDirPath:          "../../pipelineascode/testdata/pull_request/.tekton",
+			contentContains:      "kind: PipelineRun",
+			provenance:           "default_branch",
+			filterMessageSnippet: "Using PipelineRun definition from default_branch: main",
 		},
 		{
 			name:            "Get Tekton Directory and subdirectory",
@@ -50,12 +63,14 @@ func TestGetTektonDir(t *testing.T) {
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
+			observer, exporter := zapobserver.New(zap.InfoLevel)
+			fakelogger := zap.New(observer).Sugar()
 			ctx, _ := rtesting.SetupFakeContext(t)
 			bbclient, mux, tearDown := bbcloudtest.SetupBBCloudClient(t)
 			defer tearDown()
-			v := &Provider{Client: bbclient}
-			bbcloudtest.MuxDirContent(t, mux, tt.event, tt.testDirPath)
-			content, err := v.GetTektonDir(ctx, tt.event, ".tekton")
+			v := &Provider{Logger: fakelogger, Client: bbclient}
+			bbcloudtest.MuxDirContent(t, mux, tt.event, tt.testDirPath, tt.provenance)
+			content, err := v.GetTektonDir(ctx, tt.event, ".tekton", tt.provenance)
 			if tt.wantErr {
 				assert.Assert(t, err != nil, "GetTektonDir() error = %v, wantErr %v", err, tt.wantErr)
 				return
@@ -65,6 +80,11 @@ func TestGetTektonDir(t *testing.T) {
 				return
 			}
 			assert.Assert(t, strings.Contains(content, tt.contentContains), "content %s doesn't have %s", content, tt.contentContains)
+
+			if tt.filterMessageSnippet != "" {
+				gotcha := exporter.FilterMessageSnippet(tt.filterMessageSnippet)
+				assert.Assert(t, gotcha.Len() > 0, "expected to find %s in logs, found %v", tt.filterMessageSnippet, exporter.All())
+			}
 		})
 	}
 }
diff --git a/pkg/provider/bitbucketcloud/test/bbcloudtest.go b/pkg/provider/bitbucketcloud/test/bbcloudtest.go
index 1ced4200..598afc50 100644
--- a/pkg/provider/bitbucketcloud/test/bbcloudtest.go
+++ b/pkg/provider/bitbucketcloud/test/bbcloudtest.go
@@ -84,11 +84,15 @@ func MuxOrgMember(t *testing.T, mux *http.ServeMux, event *info.Event, members [
 		})
 }
 
-func MuxFiles(t *testing.T, mux *http.ServeMux, event *info.Event, filescontents map[string]string) {
+func MuxFiles(t *testing.T, mux *http.ServeMux, event *info.Event, filescontents map[string]string, provenance string) {
 	t.Helper()
 
+	sha := event.SHA
+	if provenance == "default_branch" {
+		sha = event.DefaultBranch
+	}
 	for key := range filescontents {
-		target := fmt.Sprintf("/repositories/%s/%s/src/%s", event.Organization, event.Repository, event.SHA)
+		target := fmt.Sprintf("/repositories/%s/%s/src/%s", event.Organization, event.Repository, sha)
 		mux.HandleFunc(target+"/"+key, func(rw http.ResponseWriter, r *http.Request) {
 			s := strings.ReplaceAll(r.URL.String(), target, "")
 			s = strings.TrimPrefix(s, "/")
@@ -97,11 +101,15 @@ func MuxFiles(t *testing.T, mux *http.ServeMux, event *info.Event, filescontents
 	}
 }
 
-func MuxListDirFiles(t *testing.T, mux *http.ServeMux, event *info.Event, dirs map[string][]bitbucket.RepositoryFile) {
+func MuxListDirFiles(t *testing.T, mux *http.ServeMux, event *info.Event, dirs map[string][]bitbucket.RepositoryFile, provenance string) {
 	t.Helper()
+	sha := event.SHA
+	if provenance == "default_branch" {
+		sha = event.DefaultBranch
+	}
 
 	for key, value := range dirs {
-		urlp := "/repositories/" + event.Organization + "/" + event.Repository + "/src/" + event.SHA + "/" + key + "/"
+		urlp := "/repositories/" + event.Organization + "/" + event.Repository + "/src/" + sha + "/" + key + "/"
 		mux.HandleFunc(urlp, func(rw http.ResponseWriter, r *http.Request) {
 			dircontents := map[string][]bitbucket.RepositoryFile{
 				"values": value,
@@ -181,7 +189,7 @@ func MuxCreateComment(t *testing.T, mux *http.ServeMux, event *info.Event, expec
 	})
 }
 
-func MuxDirContent(t *testing.T, mux *http.ServeMux, event *info.Event, testdir string) {
+func MuxDirContent(t *testing.T, mux *http.ServeMux, event *info.Event, testdir, provenance string) {
 	t.Helper()
 	files, err := os.ReadDir(testdir)
 	assert.NilError(t, err)
@@ -201,7 +209,7 @@ func MuxDirContent(t *testing.T, mux *http.ServeMux, event *info.Event, testdir
 				Type: btype,
 			})
 
-			MuxDirContent(t, mux, event, fpath)
+			MuxDirContent(t, mux, event, fpath, provenance)
 		} else {
 			btype := "file"
 			brfiles[relativenamedir] = append(brfiles[relativenamedir], bitbucket.RepositoryFile{
@@ -213,8 +221,8 @@ func MuxDirContent(t *testing.T, mux *http.ServeMux, event *info.Event, testdir
 			filecontents[relativename] = string(content)
 		}
 	}
-	MuxListDirFiles(t, mux, event, brfiles)
-	MuxFiles(t, mux, event, filecontents)
+	MuxListDirFiles(t, mux, event, brfiles, provenance)
+	MuxFiles(t, mux, event, filecontents, provenance)
 }
 
 func MakePREvent(accountid, nickname, sha, comment string) types.PullRequestEvent {
diff --git a/pkg/provider/bitbucketserver/bitbucketserver_test.go b/pkg/provider/bitbucketserver/bitbucketserver_test.go
index 76fd08fc..5dc260a8 100644
--- a/pkg/provider/bitbucketserver/bitbucketserver_test.go
+++ b/pkg/provider/bitbucketserver/bitbucketserver_test.go
@@ -20,6 +20,8 @@ import (
 	"github.com/openshift-pipelines/pipelines-as-code/pkg/params/settings"
 	"github.com/openshift-pipelines/pipelines-as-code/pkg/provider"
 	bbtest "github.com/openshift-pipelines/pipelines-as-code/pkg/provider/bitbucketserver/test"
+	"go.uber.org/zap"
+	zapobserver "go.uber.org/zap/zaptest/observer"
 	"gotest.tools/v3/assert"
 	rtesting "knative.dev/pkg/reconciler/testing"
 )
@@ -51,12 +53,14 @@ func TestGetTektonDir(t *testing.T) {
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
+			observer, _ := zapobserver.New(zap.InfoLevel)
+			logger := zap.New(observer).Sugar()
 			ctx, _ := rtesting.SetupFakeContext(t)
 			client, mux, tearDown := bbtest.SetupBBServerClient(ctx)
 			defer tearDown()
-			v := &Provider{Client: client, projectKey: tt.event.Organization}
+			v := &Provider{Logger: logger, Client: client, projectKey: tt.event.Organization}
 			bbtest.MuxDirContent(t, mux, tt.event, tt.testDirPath, tt.path)
-			content, err := v.GetTektonDir(ctx, tt.event, tt.path)
+			content, err := v.GetTektonDir(ctx, tt.event, tt.path, "")
 			if tt.wantErr {
 				assert.Assert(t, err != nil,
 					"GetTektonDir() error = %v, wantErr %v", err, tt.wantErr)
diff --git a/pkg/provider/github/github_test.go b/pkg/provider/github/github_test.go
index 4c32cea1..91b1603e 100644
--- a/pkg/provider/github/github_test.go
+++ b/pkg/provider/github/github_test.go
@@ -27,6 +27,8 @@ import (
 	testclient "github.com/openshift-pipelines/pipelines-as-code/pkg/test/clients"
 	ghtesthelper "github.com/openshift-pipelines/pipelines-as-code/pkg/test/github"
 	"github.com/openshift-pipelines/pipelines-as-code/pkg/test/logger"
+	"go.uber.org/zap"
+	zapobserver "go.uber.org/zap/zaptest/observer"
 	"gotest.tools/v3/assert"
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -135,10 +137,12 @@ func TestGithubSplitURL(t *testing.T) {
 
 func TestGetTektonDir(t *testing.T) {
 	testGetTektonDir := []struct {
-		treepath       string
-		event          *info.Event
-		name           string
-		expectedString string
+		treepath             string
+		event                *info.Event
+		name                 string
+		expectedString       string
+		provenance           string
+		filterMessageSnippet string
 	}{
 		{
 			name: "test no subtree",
@@ -147,8 +151,21 @@ func TestGetTektonDir(t *testing.T) {
 				Repository:   "cat",
 				SHA:          "123",
 			},
-			expectedString: "PipelineRun",
-			treepath:       "testdata/tree/simple",
+			expectedString:       "PipelineRun",
+			treepath:             "testdata/tree/simple",
+			filterMessageSnippet: "Using PipelineRun definition from source pull request SHA",
+		},
+		{
+			name: "test provenance default_branch ",
+			event: &info.Event{
+				Organization:  "tekton",
+				Repository:    "cat",
+				DefaultBranch: "main",
+			},
+			expectedString:       "FROMDEFAULTBRANCH",
+			treepath:             "testdata/tree/defaultbranch",
+			provenance:           "default_branch",
+			filterMessageSnippet: "Using PipelineRun definition from default_branch: main",
 		},
 		{
 			name: "test with subtree",
@@ -163,19 +180,30 @@ func TestGetTektonDir(t *testing.T) {
 	}
 	for _, tt := range testGetTektonDir {
 		t.Run(tt.name, func(t *testing.T) {
+			observer, exporter := zapobserver.New(zap.InfoLevel)
+			fakelogger := zap.New(observer).Sugar()
 			ctx, _ := rtesting.SetupFakeContext(t)
 			fakeclient, mux, _, teardown := ghtesthelper.SetupGH()
 			defer teardown()
 			gvcs := Provider{
 				Client: fakeclient,
+				Logger: fakelogger,
+			}
+			if tt.provenance == "default_branch" {
+				tt.event.SHA = tt.event.DefaultBranch
+			} else {
+				shaDir := fmt.Sprintf("%x", sha256.Sum256([]byte(tt.treepath)))
+				tt.event.SHA = shaDir
 			}
-			shaDir := fmt.Sprintf("%x", sha256.Sum256([]byte(tt.treepath)))
-			tt.event.SHA = shaDir
 			ghtesthelper.SetupGitTree(t, mux, tt.treepath, tt.event, false)
 
-			got, err := gvcs.GetTektonDir(ctx, tt.event, ".tekton")
+			got, err := gvcs.GetTektonDir(ctx, tt.event, ".tekton", tt.provenance)
 			assert.NilError(t, err)
 			assert.Assert(t, strings.Contains(got, tt.expectedString), "expected %s, got %s", tt.expectedString, got)
+			if tt.filterMessageSnippet != "" {
+				gotcha := exporter.FilterMessageSnippet(tt.filterMessageSnippet)
+				assert.Assert(t, gotcha.Len() > 0, "expected to find %s in logs, found %v", tt.filterMessageSnippet, exporter.All())
+			}
 		})
 	}
 }
diff --git a/pkg/provider/github/testdata/tree/defaultbranch/.tekton/pipeline.yaml b/pkg/provider/github/testdata/tree/defaultbranch/.tekton/pipeline.yaml
new file mode 100644
index 00000000..7a2ad0b8
--- /dev/null
+++ b/pkg/provider/github/testdata/tree/defaultbranch/.tekton/pipeline.yaml
@@ -0,0 +1,11 @@
+---
+apiVersion: tekton.dev/v1beta1
+kind: PipelineRun
+spec:
+  tasks:
+    - name: task
+      taskSpec:
+        steps:
+          - name: task
+            image: registry.access.redhat.com/ubi9/ubi-micro
+            command: ["/bin/echo", "FROMDEFAULTBRANCH"]
diff --git a/pkg/provider/github/testdata/tree/defaultbranch/.tekton/pipelinerun.yaml b/pkg/provider/github/testdata/tree/defaultbranch/.tekton/pipelinerun.yaml
new file mode 100644
index 00000000..2db81456
--- /dev/null
+++ b/pkg/provider/github/testdata/tree/defaultbranch/.tekton/pipelinerun.yaml
@@ -0,0 +1,11 @@
+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:
+  pipelineRef:
+    name: pipeline
diff --git a/pkg/provider/gitlab/gitlab_test.go b/pkg/provider/gitlab/gitlab_test.go
index b303e9dd..d7a92189 100644
--- a/pkg/provider/gitlab/gitlab_test.go
+++ b/pkg/provider/gitlab/gitlab_test.go
@@ -14,6 +14,8 @@ import (
 	"github.com/openshift-pipelines/pipelines-as-code/pkg/provider"
 	thelp "github.com/openshift-pipelines/pipelines-as-code/pkg/provider/gitlab/test"
 	"github.com/xanzy/go-gitlab"
+	"go.uber.org/zap"
+	zapobserver "go.uber.org/zap/zaptest/observer"
 	"gotest.tools/v3/assert"
 	rtesting "knative.dev/pkg/reconciler/testing"
 )
@@ -246,17 +248,19 @@ func TestGetTektonDir(t *testing.T) {
 		userID          int
 	}
 	type args struct {
-		event *info.Event
-		path  string
+		event      *info.Event
+		path       string
+		provenance string
 	}
 	tests := []struct {
-		name       string
-		fields     fields
-		args       args
-		wantStr    string
-		wantErr    bool
-		wantClient bool
-		prcontent  string
+		name                 string
+		fields               fields
+		args                 args
+		wantStr              string
+		wantErr              bool
+		wantClient           bool
+		prcontent            string
+		filterMessageSnippet string
 	}{
 		{
 			name:    "no client set",
@@ -288,6 +292,23 @@ func TestGetTektonDir(t *testing.T) {
 			fields: fields{
 				sourceProjectID: 100,
 			},
+			wantClient:           true,
+			wantStr:              "kind: PipelineRun",
+			filterMessageSnippet: `Using PipelineRun definition from source merge request SHA`,
+		},
+		{
+			name:      "list tekton dir on default_branch",
+			prcontent: string(samplePR),
+			args: args{
+				provenance: "default_branch",
+				path:       ".tekton",
+				event: &info.Event{
+					DefaultBranch: "main",
+				},
+			},
+			fields: fields{
+				sourceProjectID: 100,
+			},
 			wantClient: true,
 			wantStr:    "kind: PipelineRun",
 		},
@@ -311,21 +332,28 @@ func TestGetTektonDir(t *testing.T) {
 		t.Run(tt.name, func(t *testing.T) {
 			ctx, _ := rtesting.SetupFakeContext(t)
 
+			observer, exporter := zapobserver.New(zap.InfoLevel)
+			fakelogger := zap.New(observer).Sugar()
 			v := &Provider{
 				targetProjectID: tt.fields.targetProjectID,
 				sourceProjectID: tt.fields.sourceProjectID,
 				userID:          tt.fields.userID,
+				Logger:          fakelogger,
 			}
 			if tt.wantClient {
 				client, mux, tearDown := thelp.Setup(t)
 				v.Client = client
+				muxbranch := tt.args.event.HeadBranch
+				if tt.args.provenance == "default_branch" {
+					muxbranch = tt.args.event.DefaultBranch
+				}
 				if tt.args.path != "" && tt.prcontent != "" {
-					thelp.MuxListTektonDir(t, mux, tt.fields.sourceProjectID, tt.args.event.HeadBranch, tt.prcontent)
+					thelp.MuxListTektonDir(t, mux, tt.fields.sourceProjectID, muxbranch, tt.prcontent)
 				}
 				defer tearDown()
 			}
 
-			got, err := v.GetTektonDir(ctx, tt.args.event, tt.args.path)
+			got, err := v.GetTektonDir(ctx, tt.args.event, tt.args.path, tt.args.provenance)
 			if (err != nil) != tt.wantErr {
 				t.Errorf("GetTektonDir() error = %v, wantErr %v", err, tt.wantErr)
 				return
@@ -333,6 +361,10 @@ func TestGetTektonDir(t *testing.T) {
 			if tt.wantStr != "" {
 				assert.Assert(t, strings.Contains(got, tt.wantStr), "%s is not in %s", tt.wantStr, got)
 			}
+			if tt.filterMessageSnippet != "" {
+				gotcha := exporter.FilterMessageSnippet(tt.filterMessageSnippet)
+				assert.Assert(t, gotcha.Len() > 0, "expected to find %s in logs, found %v", tt.filterMessageSnippet, exporter.All())
+			}
 		})
 	}
 }
diff --git a/pkg/reconciler/controller_test.go b/pkg/reconciler/controller_test.go
new file mode 100644
index 00000000..c9cd24f3
--- /dev/null
+++ b/pkg/reconciler/controller_test.go
@@ -0,0 +1,82 @@
+package reconciler
+
+import (
+	"context"
+	"testing"
+
+	"github.com/openshift-pipelines/pipelines-as-code/pkg/apis/pipelinesascode"
+	"github.com/openshift-pipelines/pipelines-as-code/pkg/apis/pipelinesascode/keys"
+	tektontest "github.com/openshift-pipelines/pipelines-as-code/pkg/test/tekton"
+	pipelinev1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1"
+	"go.uber.org/zap"
+	zapobserver "go.uber.org/zap/zaptest/observer"
+	"gotest.tools/v3/assert"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"knative.dev/pkg/controller"
+)
+
+type fakeReconciler struct{}
+
+func (r *fakeReconciler) Reconcile(_ context.Context, _ string) error {
+	return nil
+}
+
+func TestCheckStateAndEnqueue(t *testing.T) {
+	observer, catcher := zapobserver.New(zap.DebugLevel)
+	logger := zap.New(observer).Sugar()
+	// set debug level
+	wh := &fakeReconciler{}
+	// Create a new controller implementation.
+	impl := controller.NewContext(context.TODO(), wh, controller.ControllerOptions{
+		WorkQueueName: "ValidationWebhook",
+		Logger:        logger.Named("ValidationWebhook"),
+	})
+
+	// Create a new PipelineRun object with the "started" state label.
+	testPR := tektontest.MakePR("namespace", "force-me", []pipelinev1.ChildStatusReference{
+		tektontest.MakeChildStatusReference("first"),
+		tektontest.MakeChildStatusReference("last"),
+		tektontest.MakeChildStatusReference("middle"),
+	}, nil)
+	testPR.SetAnnotations(map[string]string{
+		keys.State: "started",
+	})
+
+	// Call the checkStateAndEnqueue function with the PipelineRun object.
+	checkStateAndEnqueue(impl)(testPR)
+	assert.Equal(t, impl.Name, "ValidationWebhook")
+	assert.Equal(t, impl.Concurrency, 2)
+	assert.Equal(t, catcher.FilterMessageSnippet("Adding to queue namespace/force-me").Len(), 1)
+}
+
+func TestCtrlOpts(t *testing.T) {
+	observer, _ := zapobserver.New(zap.DebugLevel)
+	logger := zap.New(observer).Sugar()
+	// Create a new controller implementation.
+	wh := &fakeReconciler{}
+	// Create a new controller implementation.
+	impl := controller.NewContext(context.TODO(), wh, controller.ControllerOptions{
+		WorkQueueName: "ValidationWebhook",
+		Logger:        logger.Named("ValidationWebhook"),
+	})
+	// Call the ctrlOpts function to get the controller options.
+	opts := ctrlOpts()(impl)
+
+	// Assert that the finalizer name is set correctly.
+	assert.Equal(t, pipelinesascode.GroupName, opts.FinalizerName)
+
+	// Create a new PipelineRun object with the "started" state label.
+	pr := &pipelinev1.PipelineRun{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:        "test-pipeline-run",
+			Namespace:   "test-namespace",
+			Annotations: map[string]string{keys.State: "started"},
+		},
+	}
+
+	// Call the promote filter function with the PipelineRun object.
+	promote := opts.PromoteFilterFunc(pr)
+
+	// Assert that the promote filter function returns true.
+	assert.Assert(t, promote)
+}
diff --git a/pkg/test/provider/testwebvcs.go b/pkg/test/provider/testwebvcs.go
index 773132bd..0ab28525 100644
--- a/pkg/test/provider/testwebvcs.go
+++ b/pkg/test/provider/testwebvcs.go
@@ -68,7 +68,7 @@ func (v *TestProviderImp) CreateStatus(_ context.Context, _ versioned.Interface,
 	return nil
 }
 
-func (v *TestProviderImp) GetTektonDir(_ context.Context, _ *info.Event, _ string) (string, error) {
+func (v *TestProviderImp) GetTektonDir(_ context.Context, _ *info.Event, _, _ string) (string, error) {
 	return v.TektonDirTemplate, nil
 }
 
diff --git a/test/gitea_custom_console_dashboard_test.go b/test/gitea_custom_console_dashboard_test.go
deleted file mode 100644
index 1f272626..00000000
--- a/test/gitea_custom_console_dashboard_test.go
+++ /dev/null
@@ -1,88 +0,0 @@
-//go:build e2e
-// +build e2e
-
-package test
-
-import (
-	"context"
-	"testing"
-	"time"
-
-	"github.com/openshift-pipelines/pipelines-as-code/pkg/apis/pipelinesascode/keys"
-	"github.com/openshift-pipelines/pipelines-as-code/test/pkg/configmap"
-	tgitea "github.com/openshift-pipelines/pipelines-as-code/test/pkg/gitea"
-	"github.com/openshift-pipelines/pipelines-as-code/test/pkg/options"
-	twait "github.com/openshift-pipelines/pipelines-as-code/test/pkg/wait"
-	"gotest.tools/v3/assert"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-)
-
-const (
-	customConsoleDashboardName        = "custom-console-name"
-	customConsoleDashboardURL         = "custom-console-url"
-	customConsoleDashboardPRDetailURL = "custom-console-url-pr-details"
-	customConsoleDashboardPRTaskLog   = "custom-console-url-pr-tasklog"
-)
-
-func TestGiteaCustomConsoleDashboard(t *testing.T) {
-	ctx := context.TODO()
-	topts := &tgitea.TestOpts{
-		Regexp:      successRegexp,
-		TargetEvent: options.PullRequestEvent,
-		YAMLFiles: map[string]string{
-			".tekton/pr.yaml": "testdata/pipelinerun-max-keep-run-1.yaml",
-		},
-		NoCleanup:      true,
-		CheckForStatus: "success",
-		ExpectEvents:   false,
-	}
-	defer tgitea.TestPR(t, topts)()
-
-	prs, err := topts.ParamsRun.Clients.Tekton.TektonV1().PipelineRuns(topts.TargetNS).List(ctx, metav1.ListOptions{})
-
-	assert.NilError(t, err)
-	// asserting with non empty because we are not sure the URL for K8S and Openshift
-	assert.Assert(t, prs.Items[0].Annotations[keys.LogURL] != "")
-
-	data := map[string]string{
-		customConsoleDashboardName:        "My custom console",
-		customConsoleDashboardURL:         "https://custom-console.test.com",
-		customConsoleDashboardPRDetailURL: "https://custom-console.test.com/foo-pr",
-		customConsoleDashboardPRTaskLog:   "https://custom-console.test.com/foo-pr/logs/task",
-	}
-	defer configmap.ChangeGlobalConfig(ctx, t, topts.ParamsRun, data)()
-
-	tgitea.PostCommentOnPullRequest(t, topts, "/retest")
-	tgitea.WaitForStatus(t, topts, topts.TargetRefName)
-
-	waitOpts := twait.Opts{
-		RepoName:        topts.TargetNS,
-		Namespace:       topts.TargetNS,
-		MinNumberStatus: 1, // 1 means 2 🙃
-		PollTimeout:     twait.DefaultTimeout,
-		TargetSHA:       topts.PullRequest.Head.Sha,
-	}
-	err = twait.UntilRepositoryUpdated(ctx, topts.ParamsRun.Clients, waitOpts)
-	assert.NilError(t, err)
-
-	time.Sleep(15 * time.Second) // “Evil does not sleep. It waits.” - Galadriel
-
-	prs, err = topts.ParamsRun.Clients.Tekton.TektonV1().PipelineRuns(topts.TargetNS).List(ctx, metav1.ListOptions{})
-	assert.NilError(t, err)
-
-	assert.Equal(t, len(prs.Items), 1, "should have only one pipelinerun, but we have: %d", len(prs.Items))
-
-	prs, err = topts.ParamsRun.Clients.Tekton.TektonV1().PipelineRuns(topts.TargetNS).List(ctx, metav1.ListOptions{})
-	assert.NilError(t, err)
-
-	logURL := map[string]string{}
-	for _, v := range prs.Items {
-		logURL[v.Annotations[keys.LogURL]] = v.Annotations[keys.LogURL]
-	}
-	// Check that latest create Pipeline Run will have the console dashboard URL
-	assert.Assert(t, logURL["https://custom-console.test.com/foo-pr"] == "https://custom-console.test.com/foo-pr")
-}
-
-// Local Variables:
-// compile-command: "go test -tags=e2e -v -run TestGiteaCustomConsoleDashboard$ ."
-// End:
diff --git a/test/gitea_test.go b/test/gitea_test.go
index 18ad9bc3..7b2e9384 100644
--- a/test/gitea_test.go
+++ b/test/gitea_test.go
@@ -22,6 +22,7 @@ import (
 	tknpacgenerate "github.com/openshift-pipelines/pipelines-as-code/pkg/cmd/tknpac/generate"
 	tknpaclist "github.com/openshift-pipelines/pipelines-as-code/pkg/cmd/tknpac/list"
 	tknpacresolve "github.com/openshift-pipelines/pipelines-as-code/pkg/cmd/tknpac/resolve"
+	"github.com/openshift-pipelines/pipelines-as-code/test/pkg/configmap"
 	pacrepo "github.com/openshift-pipelines/pipelines-as-code/test/pkg/repository"
 
 	"github.com/openshift-pipelines/pipelines-as-code/pkg/git"
@@ -208,7 +209,7 @@ func TestGiteaConcurrencyExclusivenessMultipleRuns(t *testing.T) {
 	tgitea.WaitForStatus(t, topts, topts.TargetRefName)
 
 	topts.Regexp = successRegexp
-	tgitea.WaitForPullRequestCommentMatch(context.Background(), t, topts)
+	tgitea.WaitForPullRequestCommentMatch(t, topts)
 }
 
 func TestGiteaRetestAfterPush(t *testing.T) {
@@ -267,7 +268,7 @@ func TestGiteaACLOrgSkipped(t *testing.T) {
 	topts.CheckForStatus = "success"
 	tgitea.WaitForStatus(t, topts, topts.PullRequest.Head.Sha)
 	topts.Regexp = regexp.MustCompile(`.*is skipping this commit.*`)
-	tgitea.WaitForPullRequestCommentMatch(context.Background(), t, topts)
+	tgitea.WaitForPullRequestCommentMatch(t, topts)
 }
 
 func TestGiteaACLCommentsAllowing(t *testing.T) {
@@ -305,11 +306,11 @@ func TestGiteaACLCommentsAllowing(t *testing.T) {
 			topts.CheckForStatus = "success"
 			tgitea.WaitForStatus(t, topts, topts.PullRequest.Head.Sha)
 			topts.Regexp = regexp.MustCompile(`.*is skipping this commit.*`)
-			tgitea.WaitForPullRequestCommentMatch(context.Background(), t, topts)
+			tgitea.WaitForPullRequestCommentMatch(t, topts)
 
 			tgitea.PostCommentOnPullRequest(t, topts, tt.comment)
 			topts.Regexp = successRegexp
-			tgitea.WaitForPullRequestCommentMatch(context.Background(), t, topts)
+			tgitea.WaitForPullRequestCommentMatch(t, topts)
 		})
 	}
 }
@@ -648,7 +649,7 @@ func TestGiteaErrorSnippet(t *testing.T) {
 	defer tgitea.TestPR(t, topts)()
 
 	topts.Regexp = regexp.MustCompile(`Hey man i just wanna to say i am not such a failure, i am useful in my failure`)
-	tgitea.WaitForPullRequestCommentMatch(context.Background(), t, topts)
+	tgitea.WaitForPullRequestCommentMatch(t, topts)
 }
 
 func TestGiteaErrorSnippetWithSecret(t *testing.T) {
@@ -670,7 +671,7 @@ func TestGiteaErrorSnippetWithSecret(t *testing.T) {
 	defer tgitea.TestPR(t, topts)()
 
 	topts.Regexp = regexp.MustCompile(`I WANT TO SAY \*\*\*\*\* OUT LOUD BUT NOBODY UNDERSTAND ME`)
-	tgitea.WaitForPullRequestCommentMatch(context.Background(), t, topts)
+	tgitea.WaitForPullRequestCommentMatch(t, topts)
 }
 
 // TestGiteaNotExistingClusterTask checks that the pipeline run fails if the clustertask does not exist
@@ -778,6 +779,76 @@ func TestGiteaParamsOnRepoCR(t *testing.T) {
 					" payload match"), 2))
 }
 
+func TestGiteaParamsOnRepoCRWithCustomConsole(t *testing.T) {
+	ctx := context.Background()
+	topts := &tgitea.TestOpts{
+		CheckForStatus:  "success",
+		SkipEventsCheck: true,
+		TargetEvent:     options.PullRequestEvent,
+		YAMLFiles: map[string]string{
+			".tekton/pr.yaml": "testdata/params.yaml",
+		},
+		RepoCRParams: &[]v1alpha1.Params{
+			{
+				Name:  "custom",
+				Value: "myconsole",
+			},
+		},
+	}
+	topts.TargetRefName = names.SimpleNameGenerator.RestrictLengthWithRandomSuffix("pac-e2e-test")
+	topts.TargetNS = topts.TargetRefName
+	topts.ParamsRun, topts.Opts, topts.GiteaCNX, _ = tgitea.Setup(ctx)
+	assert.NilError(t, topts.ParamsRun.Clients.NewClients(ctx, &topts.ParamsRun.Info))
+	assert.NilError(t, pacrepo.CreateNS(ctx, topts.TargetNS, topts.ParamsRun))
+
+	cfgMapData := map[string]string{
+		"custom-console-name":           "Custom Console",
+		"custom-console-url":            "https://url",
+		"custom-console-url-pr-details": "https://url/detail/{{ custom }}",
+		"custom-console-url-pr-tasklog": "https://url/log/{{ custom }}",
+		"tekton-dashboard-url":          "",
+	}
+	defer configmap.ChangeGlobalConfig(ctx, t, topts.ParamsRun, cfgMapData)()
+	defer tgitea.TestPR(t, topts)()
+	// topts.Regexp = regexp.MustCompile(`(?m).*Custom Console.*https://url/detail/myconsole.*https://url/log/myconsole`)
+	topts.Regexp = regexp.MustCompile(`(?m).*Custom Console.*https://url/detail/myconsole`)
+	tgitea.WaitForPullRequestCommentMatch(t, topts)
+	topts.Regexp = regexp.MustCompile(`(?m).*https://url/log/myconsole`)
+	tgitea.WaitForPullRequestCommentMatch(t, topts)
+}
+
+func TestGiteaProvenance(t *testing.T) {
+	topts := &tgitea.TestOpts{
+		SkipEventsCheck:       true,
+		TargetEvent:           options.PullRequestEvent,
+		Settings:              &v1alpha1.Settings{PipelineRunProvenance: "default_branch"},
+		NoPullRequestCreation: true,
+	}
+	tgitea.TestPR(t, topts)
+	branch := topts.TargetRefName
+	prmap := map[string]string{".tekton/pr.yaml": "testdata/pipelinerun.yaml"}
+	entries, err := payload.GetEntries(prmap, topts.TargetNS, topts.DefaultBranch, topts.TargetEvent, map[string]string{})
+	assert.NilError(t, err)
+	topts.TargetRefName = topts.DefaultBranch
+	tgitea.PushFilesToRefGit(t, topts, entries, topts.DefaultBranch)
+	topts.TargetRefName = branch
+	prmap = map[string]string{"notgonnatobetested.yaml": "testdata/pipelinerun.yaml"}
+	entries, err = payload.GetEntries(prmap, topts.TargetNS, topts.DefaultBranch, topts.TargetEvent, map[string]string{})
+	assert.NilError(t, err)
+	tgitea.PushFilesToRefGit(t, topts, entries, topts.DefaultBranch)
+
+	pr, _, err := topts.GiteaCNX.Client.CreatePullRequest(topts.Opts.Organization, topts.TargetRefName, gitea.CreatePullRequestOption{
+		Title: "Test Pull Request - " + topts.TargetRefName,
+		Head:  topts.TargetRefName,
+		Base:  options.MainBranch,
+	})
+	assert.NilError(t, err)
+	topts.PullRequest = pr
+	topts.ParamsRun.Clients.Log.Infof("PullRequest %s has been created", pr.HTMLURL)
+	topts.CheckForStatus = "success"
+	tgitea.WaitForStatus(t, topts, topts.TargetRefName)
+}
+
 // Local Variables:
 // compile-command: "go test -tags=e2e -v -run TestGiteaPush ."
 // End:
diff --git a/test/config_maxkeepruns_test.go b/test/github_config_maxkeepruns_test.go
similarity index 100%
rename from test/config_maxkeepruns_test.go
rename to test/github_config_maxkeepruns_test.go
diff --git a/test/pkg/gitea/crd.go b/test/pkg/gitea/crd.go
index f1cbd297..343eb02d 100644
--- a/test/pkg/gitea/crd.go
+++ b/test/pkg/gitea/crd.go
@@ -45,6 +45,7 @@ func CreateCRD(ctx context.Context, topts *TestOpts) error {
 			},
 			ConcurrencyLimit: topts.ConcurrencyLimit,
 			Params:           topts.RepoCRParams,
+			Settings:         topts.Settings,
 		},
 	}
 
diff --git a/test/pkg/gitea/scm.go b/test/pkg/gitea/scm.go
index 97ca6fe0..4386b560 100644
--- a/test/pkg/gitea/scm.go
+++ b/test/pkg/gitea/scm.go
@@ -54,6 +54,9 @@ func PushFilesToRefGit(t *testing.T, topts *TestOpts, entries map[string]string,
 	_, err = git.RunGit(path, "remote", "add", "-f", "origin", topts.GitCloneURL)
 	assert.NilError(t, err)
 
+	_, err = git.RunGit(path, "fetch", "-a", "origin")
+	assert.NilError(t, err)
+
 	_, err = git.RunGit(path, "checkout", "-B", topts.TargetRefName, "origin/"+baseRefFrom)
 	assert.NilError(t, err)
 
diff --git a/test/pkg/gitea/test.go b/test/pkg/gitea/test.go
index d6d347ef..89bd7848 100644
--- a/test/pkg/gitea/test.go
+++ b/test/pkg/gitea/test.go
@@ -25,29 +25,31 @@ import (
 )
 
 type TestOpts struct {
-	SkipEventsCheck      bool
-	NoCleanup            bool
-	TargetNS             string
-	TargetEvent          string
-	Regexp               *regexp.Regexp
-	YAMLFiles            map[string]string
-	ExtraArgs            map[string]string
-	RepoCRParams         *[]v1alpha1.Params
-	CheckForStatus       string
-	TargetRefName        string
-	CheckForNumberStatus int
-	ConcurrencyLimit     *int
-	ParamsRun            *params.Run
-	GiteaCNX             pgitea.Provider
-	Opts                 options.E2E
-	PullRequest          *gitea.PullRequest
-	DefaultBranch        string
-	GitCloneURL          string
-	GitHTMLURL           string
-	GiteaAPIURL          string
-	GiteaPassword        string
-	ExpectEvents         bool
-	InternalGiteaURL     string
+	NoPullRequestCreation bool
+	SkipEventsCheck       bool
+	NoCleanup             bool
+	TargetNS              string
+	TargetEvent           string
+	Settings              *v1alpha1.Settings
+	Regexp                *regexp.Regexp
+	YAMLFiles             map[string]string
+	ExtraArgs             map[string]string
+	RepoCRParams          *[]v1alpha1.Params
+	CheckForStatus        string
+	TargetRefName         string
+	CheckForNumberStatus  int
+	ConcurrencyLimit      *int
+	ParamsRun             *params.Run
+	GiteaCNX              pgitea.Provider
+	Opts                  options.E2E
+	PullRequest           *gitea.PullRequest
+	DefaultBranch         string
+	GitCloneURL           string
+	GitHTMLURL            string
+	GiteaAPIURL           string
+	GiteaPassword         string
+	ExpectEvents          bool
+	InternalGiteaURL      string
 }
 
 func PostCommentOnPullRequest(t *testing.T, topt *TestOpts, body string) {
@@ -101,6 +103,14 @@ func TestPR(t *testing.T, topts *TestOpts) func() {
 	err = CreateCRD(ctx, topts)
 	assert.NilError(t, err)
 
+	url, err := MakeGitCloneURL(repoInfo.CloneURL, os.Getenv("TEST_GITEA_USERNAME"), os.Getenv("TEST_GITEA_PASSWORD"))
+	assert.NilError(t, err)
+	topts.GitCloneURL = url
+
+	if topts.NoPullRequestCreation {
+		return cleanup
+	}
+
 	entries, err := payload.GetEntries(topts.YAMLFiles,
 		topts.TargetNS,
 		repoInfo.DefaultBranch,
@@ -108,9 +118,6 @@ func TestPR(t *testing.T, topts *TestOpts) func() {
 		topts.ExtraArgs)
 	assert.NilError(t, err)
 
-	url, err := MakeGitCloneURL(repoInfo.CloneURL, os.Getenv("TEST_GITEA_USERNAME"), os.Getenv("TEST_GITEA_PASSWORD"))
-	assert.NilError(t, err)
-	topts.GitCloneURL = url
 	PushFilesToRefGit(t, topts, entries, topts.DefaultBranch)
 	pr, _, err := topts.GiteaCNX.Client.CreatePullRequest(topts.Opts.Organization, repoInfo.Name, gitea.CreatePullRequestOption{
 		Title: "Test Pull Request - " + topts.TargetRefName,
@@ -130,7 +137,7 @@ func TestPR(t *testing.T, topts *TestOpts) func() {
 	}
 
 	if topts.Regexp != nil {
-		WaitForPullRequestCommentMatch(ctx, t, topts)
+		WaitForPullRequestCommentMatch(t, topts)
 	}
 
 	events, err := topts.ParamsRun.Clients.Kube.CoreV1().Events(topts.TargetNS).List(ctx, metav1.ListOptions{
@@ -231,13 +238,12 @@ func WaitForSecretDeletion(t *testing.T, topts *TestOpts, _ string) {
 	}
 }
 
-func WaitForPullRequestCommentMatch(ctx context.Context, t *testing.T, topts *TestOpts) {
+func WaitForPullRequestCommentMatch(t *testing.T, topts *TestOpts) {
 	i := 0
 	for {
-		tls, err := GetIssueTimeline(ctx, topts)
+		comments, _, err := topts.GiteaCNX.Client.ListRepoIssueComments(topts.PullRequest.Base.Repository.Owner.UserName, topts.PullRequest.Base.Repository.Name, gitea.ListIssueCommentOptions{})
 		assert.NilError(t, err)
-		for _, v := range tls {
-			// look for the regexp "Pipelines as Code CI.*has.*successfully" in v.body
+		for _, v := range comments {
 			if topts.Regexp.MatchString(v.Body) {
 				topts.ParamsRun.Clients.Log.Infof("Found regexp \"%s\" in PR comments", topts.Regexp.String())
 				return