On previous post, we have built Triggers that will react to push events.
Problem
However, if we were using monorepo, we would like to trigger different Pipeline build based on the path change
For example the repository like this
|-- ui/
|-- backend/
|-- tools/
When changes on ui
are detected, it will only trigger the Pipeline for ui
Solution 1: use another CI to trigger tekton
We could use another CI that supports this, for example
Write the path changes on the CI, and trigger Tekton
For example, on GitHub Actions
on:
push:
branches:
- 'main'
paths:
- 'my-project/**'
jobs:
trigger-project:
runs-on: ubuntu-latest
steps:
- name: Trigger Tekton
run: curl -X POST --data '{}' https://trigger-tekton.example.com
On Tekton, we need to add a listener
apiVersion: triggers.tekton.dev/v1beta1
kind: EventListener
metadata:
name: general-listener
spec:
serviceAccountName: tekton-triggers-example-sa
triggers:
- name: push-trigger
bindings:
- ref: push-binding
template:
ref: simple-pipeline-template
Add TriggerBinding with hard-coded values
branch can be dynamically set, but that requires writing custom ClusterInterceptor, more on solution 2
apiVersion: triggers.tekton.dev/v1beta1
kind: TriggerBinding
metadata:
name: push-binding
namespace: tekton
spec:
params:
- name: git_branch
value: main
- name: git_url
value: https://github.com/tektoncd/triggers
this only handles one project inside monorepo, we need to repeat the process (some files can be reused, eg TriggerBinding
) if we have more than one projects to be deployed on a monorepo
Solution 2: implement custom ClusterInterceptor
This solution will not depend on another CI service but will need to write ClusterInterceptor. A ClusterInterceptor must meet the following requirements
I have written a ClusterInterceptor, which will get the file changes based on the last commit, and match with the glob pattern on the config
Check out the
examples
directory for Tekton complete example
How it works
The ClusterInterceptor will do the following when it is called by Tekton Triggers
1. clone
It will clone the repository with --depth 2
to save time and network bandwidth
2. diff
It will run git diff
, to get the file changes from the last commit
git --no-pager diff --name-only HEAD^
# example output
README.md
examples/01_deployment.yaml
examples/04_cluster-interceptor.yaml
examples/06_trigger.yaml
main.go
3. match
It will match the file changes based on glob pattern on config
example of config.yaml
repos:
demo:
# can be http format or ssh format
url: https://github.com/WLun001/path-change
paths:
# only match of if files inside examples directory
- "examples/**"
response
if match: set the continue field in the response to true.
{
"extensions": {
"paths": "MATCH"
},
"continue": true,
"status": {}
}
if not match: set the continue field to false
{
"extensions": {
"paths": "NOT_MATCH"
},
"continue": false,
"status": {}
}
The response will be interpreted by Tekton
How to use
Check out the
examples
directory for Tekton complete example
Deploy path-change
Check out the example deployment on examples
directory.
For git credentials, it works with any git credentials, just need to bind the volume to the deployment. The example is showing ssh credentials
After deploying path-change, we need to add ClusterInterceptor to reference it
apiVersion: triggers.tekton.dev/v1alpha1
kind: ClusterInterceptor
metadata:
name: path-change-interceptor
spec:
clientConfig:
service:
name: path-change
namespace: tekton
And we can use it on EventListener, and set the repo
that we are interested in at params
field
For example, if we specify demo
as our repo, based on the config on the previous example, it will check against https://github.com/WLun001/path-change
and with the path that match "examples/**"
pattern
apiVersion: triggers.tekton.dev/v1beta1
kind: EventListener
spec:
triggers:
- name: push-trigger
interceptors:
...
- ref:
# here we can use our custom ClusterInterceptor
name: path-change-interceptor
kind: ClusterInterceptor
params:
# on params put the repo we would like to check with
- name: repo
value: demo # this will map the values in config.yaml
...
After deploying everything, we could do a test
Test it out
Do a port forward
kubectl port-forward -n tekton service/el-simple-listener 8080
Test it locally
Send example payload
The response status code should be 202 Accepted
HMAC tool used to create X-Hub-Signature.
curl -v \
-H 'X-GitHub-Event: push' \
-H 'X-Hub-Signature: sha1=87b1adbb9aca10522739f9f94d372afd1542e498' \
-H 'Content-Type: application/json' \
-d '{"ref": "refs/heads/main", "repository": {"git_url": "https://github.com/WLun001/cncf-demo.git"}}' \
http://localhost:8080
You should see a new PipelineRun is running
Test with GitHub
Create a tunnel using tools like ngrok
ngrok http 8080
Add the generated URL to Payload URL
Make a commit that matches the path pattern and push, you should see a new PipelineRun is running
Dealing with monorepo
Back to our problem, to solve it, we can create a config.yaml
that specify all the paths pattern and git URL, and create different EventListener and Webhook for each app
For example, we would create 3 EventListeners and Webhooks in order to trigger the correct Pipelines for ui
, backend
and tools
.
|-- ui
|-- backend/
|-- tools/
Conclusion
Tekton is very powerful with its extensibility, it might feel complex at the first sight, but in a long run really worth investing time on it,
We have built a custom ClusterInterceptor that works well with Tekton Triggers, and solve our problems with monorepo.
Next, we will look at the advanced use case of Tekton Pipelines, running Docker-Compose on Pipelines