In Argo Workflows, you can have a Sensor trigger only when specific paths in a repo have changed, but it isn’t well documented. Here’s how it works.
Trigger on any change
You’ll want to make sure this is working first.
-
Deploy a WorkflowTemplate; here we’ll assume called
build-example
is already created. -
A webhook EventSource
apiVersion: argoproj.io/v1alpha1 kind: EventSource metadata: name: example-webhook namespace: argowf spec: replicas: 2 service: ports: - name: http port: 12000 targetPort: 12000 webhook: gitea-webhook: endpoint: /gitea method: POST port: "12000" # Has to be a string because lol
-
Configure a repository in Gitea to call that webhook on every push
-
Configure a Sensor. Replace
REPO_OWNER/REPO_NAME
with the owner and name of the repo in Gitea. Note: you’ll also need to define a trigger,apiVersion: argoproj.io/v1alpha1 kind: Sensor metadata: name: workflow-build-example-project namespace: argowf spec: template: serviceAccountName: argowf-sensor container: env: - name: LOG_LEVEL value: "debug" dependencies: - name: gitea-webhook-dep eventSourceName: example-webhook eventName: gitea-webhook filters: data: - path: body.repository.full_name type: string value: - "REPO_OWNER/REPO_NAME" triggers: - template: name: argo-workflow-trigger argoWorkflow: operation: submit source: resource: apiVersion: argoproj.io/v1alpha1 kind: Workflow metadata: generateName: wf-build-example- namespace: argowf spec: workflowTemplateRef: name: build-example
-
This will trigger your
build-example
WorkflowTemplate any time you push to theREPO_OWNER/REPO_NAME
git repository in your Gitea server.
Trigger for specific path
It’s possible to trigger only on specific paths, but it isn’t well documented and the syntax is really confusing.
To make this work, you need to add another data filter.
For instance, this will match on any file starting with some/path/.*
(including subfolders):
filters:
# Require that all of the data filters match; boolean AND
dataLogicalOperator: "and"
data:
# The same filter we had above, matching on the specific repo
- path: body.repository.full_name
type: string
value:
- "REPO_OWNER/REPO_NAME"
# A new filter, requiring that at least one of the files added/changed/removed in the commit is under some/path/ in the repo
- path: "[body.commits.#.modified.@flatten,body.commits.#.added.@flatten,body.commits.#.removed.@flatten].@flatten.@flatten"
type: string
value:
- '"some/path/.*'
How does this work?
First, note that how this works depends entirely on the webhook payload from your Git server. I’m using Gitea, which is supposedly compatible with Github, but this isn’t a standard. If you’re using some other Git server, you’ll need to figure out what its webhook payload body looks like. (One way to do this is by creating a Sensor and reading the logs of the Sensor container while pushing a commit.)
Gitea (and GitHub) send(s) a rather large JSON payload, including a list of commits
and a list of files modified/added/removed in each.
For instance, a commit that changes some/path/README.md
and adds some/path/main.c
will include this (heavily truncated):
{
"body": {
"commits": [
{
"added": [
"some/path/main.c"
],
"modified": [
"some/path/README.md"
]
}
]
}
}
In the path
field of a data filter,
Argo Workflows supports something called
GJSON Path syntax,
which we use to collapse all modified, added, and removed lists into a SINGLE list
containing all the files that were added, modified, or removed, like this:
["some/path/main.c", "some/path/README.md"]
The value
field of the data filter is a regexp that operates
on that JSON string, as a string.
To be clear, it doesn’t understand JSON at all,
and does not operate on individual list items.
For non-pathological repositories you can just match on double quotes "
as the beginning/end of file names;
in theory you could have a repo with files that contain quotes,
but my advice is: do not do that.
Here’s a simple case matching a single sub-path:
- path: "[body.commits.#.modified.@flatten,body.commits.#.added.@flatten,body.commits.#.removed.@flatten].@flatten.@flatten"
type: string
value:
- '"some/path/.*'
Note that the whole thing is wrapped in single quotes '
;
this is so the double quote "
character is parsed as part of the string.
Trigger for a list of paths
Here’s a more complicated case,
matching any of several sub-paths,
separated by pipes.
Note the double quote "
as the first character,
indicating that all of these path fragments must be at the beginning.
- path: "[body.commits.#.modified.@flatten,body.commits.#.added.@flatten,body.commits.#.removed.@flatten].@flatten.@flatten"
type: string
value:
- '"(path/one|path/two/that/is/deeper|path3|etc)'
See also
I’ve adapted this from my labnotes, which I used for general documentation of my kubernasty homelab cluster.
References
- argoproj/argo-events#1127: Unsure of the best practice for a filter with multiple paths)
- argoproj/argo-events#1097: Allow for JSON string to be processed in the Sensor DataFilter
- Documentation was written with information about GJSON, but somehow that documentation has been lost from the current docs?
- argoproj/argo-events#1130: docs: Enhance the filters tutorial for #1097, the PR that contains GJSON documentation
- argoproj/argo-events#3525: Example documentation for filtering webhooks based on repo and path, a ticket I opened to address this