Repository: shipwright-io/build
Pull request #1008 (2022-03-09 09:09:26)

Shipwright Triggers API
# Changes

Adding the API for [Shipwright Triggers (EP)](https://github.com/shipwright-io/community/blob/main/ships/0031-shipwright-trigger.md) with validation and initial documentation.

# Submitter Checklist

- [x] Includes tests if functionality changed/was added
- [x] Includes docs if changes are user-facing
- [x] [Set a kind label on this PR](https://prow.k8s.io/command-help#kind)  
- [x] Release notes block has been filled in, or marked NONE

# Release Notes

```release-note
Adding the API for Shipwright Triggers, a event driven approach to instantiate new builds
```

openshift-ci[bot]: @otaviof: Adding the "do-not-merge/release-note-label-needed" label because no release-note block was detected, please follow our [release note process](https://git.k8s.io/community/contributors/guide/release-notes.md) to remove it.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
</details>
openshift-ci[bot]: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by:
To complete the [pull request process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process), please ask for approval from **otaviof** after the PR has been reviewed.

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=shipwright-io%2Fbuild).

<details open>
Needs approval from an approver in each of these files:

- **[OWNERS](https://github.com/shipwright-io/build/blob/main/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["otaviof"]} -->
openshift-ci[bot]: @otaviof: PR needs rebase.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
</details>
lookis: Hi @otaviof , thanks for your works, any update on this PR?
otaviof: /retittle Shipwright Triggers API
otaviof: /retitle Shipwright Triggers API
otaviof: /assign @SaschaSchwarze0 @adambkaplan 

/cc @coreydaley
coreydaley: @otaviof I don't see the use of the `trigger.when`, why not just have `triggers`? Do you expect to add additional adverbs in the future?
otaviof: > Hi @otaviof , thanks for your works, any update on this PR?

Hello! Sorry for the delay, we are moving forward with this feature again 👍 And thanks for the heads up too.
otaviof: > @otaviof I don't see the use of the `trigger.when`, why not just have `triggers`? Do you expect to add additional adverbs in the future? I would like some clarification on that before commenting on the specifics of the pull request since a lot of my thoughts pertain to that.

I'm implementing the changes we've discussed during the EP review without changes, so that's why we have the nested attribute approach.

Personally, the `trigger.when` sounds straightforward to explain the concept behind the API, thus a contender to `.spec.triggers` could be `.spec.triggerWhen`.

openshift-ci[bot]: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by:
To complete the [pull request process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process), please ask for approval from **adambkaplan** after the PR has been reviewed.

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=shipwright-io%2Fbuild).

<details open>
Needs approval from an approver in each of these files:

- **[OWNERS](https://github.com/shipwright-io/build/blob/main/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["adambkaplan"]} -->
lookis: I have a question, If we watch github event, how to apply carried information to Build result?
for example, I have tag a commit in github repo, and trigger the Build. how to apply the tag to the output Image
SaschaSchwarze0: > I have a question, If we watch github event, how to apply carried information to Build result? for example, I have tag a commit in github repo, and trigger the Build. how to apply the tag to the output Image

Hi @lookis, qq, are you:

* pushing a tag to your GitHub repository and expect a build to start for this tag ? This is not covered of the [Triggers SHIP](https://github.com/shipwright-io/community/blob/main/ships/0031-shipwright-trigger.md), but would be an interesting scenario @otaviof.
* interested in getting the tag name or commit sha that is built somewhere into the image (as a label or annotation), or as its image tag ? Or as annotation of an image signature ? That's all future.
lookis: @SaschaSchwarze0 Thanks, looking forward to it.
openshift-ci[bot]: @otaviof: GitHub didn't allow me to request PR reviews from the following users: ricardomaraschini.

Note that only [shipwright-io members](https://github.com/orgs/shipwright-io/people) and repo collaborators can review this PR, and authors cannot review their own PRs.

<details>

In response to [this](https://github.com/shipwright-io/build/pull/1008#discussion_r902402373):

>/cc @ricardomaraschini


Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
</details>
otaviof: > I suggest to add validation to https://github.com/shipwright-io/build/blob/v0.10.0/pkg/validate/validate.go#L83-L119. Basically a BuildRun with an inline spec must not have triggers set.

Thanks for the review, please consider the latest changes.
openshift-ci[bot]: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/shipwright-io/build/pull/1008#pullrequestreview-1015341704" title="Approved">SaschaSchwarze0</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=shipwright-io%2Fbuild).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/shipwright-io/build/blob/main/OWNERS)~~ [SaschaSchwarze0]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->

Patch:
diff --git a/deploy/crds/shipwright.io_buildruns.yaml b/deploy/crds/shipwright.io_buildruns.yaml
index a9beb612..73c3f55b 100644
--- a/deploy/crds/shipwright.io_buildruns.yaml
+++ b/deploy/crds/shipwright.io_buildruns.yaml
@@ -466,6 +466,87 @@ spec:
                       should take to execute.
                     format: duration
                     type: string
+                  trigger:
+                    description: Trigger defines the scenarios where a new build should
+                      be triggered.
+                    properties:
+                      secretRef:
+                        description: SecretRef points to a local object carrying the
+                          secret token to validate webhook request.
+                        properties:
+                          name:
+                            description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+                              TODO: Add other useful fields. apiVersion, kind, uid?'
+                            type: string
+                        type: object
+                      when:
+                        description: When the list of scenarios when a new build should
+                          take place.
+                        items:
+                          description: TriggerWhen a given scenario where the webhook
+                            trigger is applicable.
+                          properties:
+                            github:
+                              description: GitHub describes how to trigger builds
+                                based on GitHub (SCM) events.
+                              properties:
+                                branches:
+                                  description: Branches slice of branch names where
+                                    the event applies.
+                                  items:
+                                    type: string
+                                  type: array
+                                events:
+                                  description: Events GitHub event names.
+                                  items:
+                                    description: GitHubEventName set of WhenGitHub
+                                      valid event names.
+                                    type: string
+                                  minItems: 1
+                                  type: array
+                              type: object
+                            image:
+                              description: Image slice of image names where the event
+                                applies.
+                              properties:
+                                names:
+                                  description: Names fully qualified image names.
+                                  items:
+                                    type: string
+                                  type: array
+                              type: object
+                            name:
+                              description: Name name or the short description of the
+                                trigger condition.
+                              type: string
+                            objectRef:
+                              description: ObjectRef describes how to match a foreign
+                                resource, either using the name or the label selector,
+                                plus the current resource status.
+                              properties:
+                                name:
+                                  description: Name target object name.
+                                  type: string
+                                selector:
+                                  additionalProperties:
+                                    type: string
+                                  description: Selector label selector.
+                                  type: object
+                                status:
+                                  description: Status object status.
+                                  items:
+                                    type: string
+                                  type: array
+                              type: object
+                            type:
+                              description: Type the event type
+                              type: string
+                          required:
+                          - name
+                          - type
+                          type: object
+                        type: array
+                    type: object
                   volumes:
                     description: Volumes contains volume Overrides of the BuildStrategy
                       volumes in case those are allowed to be overridden. Must only
@@ -4190,6 +4271,87 @@ spec:
                       should take to execute.
                     format: duration
                     type: string
+                  trigger:
+                    description: Trigger defines the scenarios where a new build should
+                      be triggered.
+                    properties:
+                      secretRef:
+                        description: SecretRef points to a local object carrying the
+                          secret token to validate webhook request.
+                        properties:
+                          name:
+                            description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+                              TODO: Add other useful fields. apiVersion, kind, uid?'
+                            type: string
+                        type: object
+                      when:
+                        description: When the list of scenarios when a new build should
+                          take place.
+                        items:
+                          description: TriggerWhen a given scenario where the webhook
+                            trigger is applicable.
+                          properties:
+                            github:
+                              description: GitHub describes how to trigger builds
+                                based on GitHub (SCM) events.
+                              properties:
+                                branches:
+                                  description: Branches slice of branch names where
+                                    the event applies.
+                                  items:
+                                    type: string
+                                  type: array
+                                events:
+                                  description: Events GitHub event names.
+                                  items:
+                                    description: GitHubEventName set of WhenGitHub
+                                      valid event names.
+                                    type: string
+                                  minItems: 1
+                                  type: array
+                              type: object
+                            image:
+                              description: Image slice of image names where the event
+                                applies.
+                              properties:
+                                names:
+                                  description: Names fully qualified image names.
+                                  items:
+                                    type: string
+                                  type: array
+                              type: object
+                            name:
+                              description: Name name or the short description of the
+                                trigger condition.
+                              type: string
+                            objectRef:
+                              description: ObjectRef describes how to match a foreign
+                                resource, either using the name or the label selector,
+                                plus the current resource status.
+                              properties:
+                                name:
+                                  description: Name target object name.
+                                  type: string
+                                selector:
+                                  additionalProperties:
+                                    type: string
+                                  description: Selector label selector.
+                                  type: object
+                                status:
+                                  description: Status object status.
+                                  items:
+                                    type: string
+                                  type: array
+                              type: object
+                            type:
+                              description: Type the event type
+                              type: string
+                          required:
+                          - name
+                          - type
+                          type: object
+                        type: array
+                    type: object
                   volumes:
                     description: Volumes contains volume Overrides of the BuildStrategy
                       volumes in case those are allowed to be overridden. Must only
diff --git a/deploy/crds/shipwright.io_builds.yaml b/deploy/crds/shipwright.io_builds.yaml
index db2f0d37..eee61b10 100644
--- a/deploy/crds/shipwright.io_builds.yaml
+++ b/deploy/crds/shipwright.io_builds.yaml
@@ -444,6 +444,87 @@ spec:
                   should take to execute.
                 format: duration
                 type: string
+              trigger:
+                description: Trigger defines the scenarios where a new build should
+                  be triggered.
+                properties:
+                  secretRef:
+                    description: SecretRef points to a local object carrying the secret
+                      token to validate webhook request.
+                    properties:
+                      name:
+                        description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+                          TODO: Add other useful fields. apiVersion, kind, uid?'
+                        type: string
+                    type: object
+                  when:
+                    description: When the list of scenarios when a new build should
+                      take place.
+                    items:
+                      description: TriggerWhen a given scenario where the webhook
+                        trigger is applicable.
+                      properties:
+                        github:
+                          description: GitHub describes how to trigger builds based
+                            on GitHub (SCM) events.
+                          properties:
+                            branches:
+                              description: Branches slice of branch names where the
+                                event applies.
+                              items:
+                                type: string
+                              type: array
+                            events:
+                              description: Events GitHub event names.
+                              items:
+                                description: GitHubEventName set of WhenGitHub valid
+                                  event names.
+                                type: string
+                              minItems: 1
+                              type: array
+                          type: object
+                        image:
+                          description: Image slice of image names where the event
+                            applies.
+                          properties:
+                            names:
+                              description: Names fully qualified image names.
+                              items:
+                                type: string
+                              type: array
+                          type: object
+                        name:
+                          description: Name name or the short description of the trigger
+                            condition.
+                          type: string
+                        objectRef:
+                          description: ObjectRef describes how to match a foreign
+                            resource, either using the name or the label selector,
+                            plus the current resource status.
+                          properties:
+                            name:
+                              description: Name target object name.
+                              type: string
+                            selector:
+                              additionalProperties:
+                                type: string
+                              description: Selector label selector.
+                              type: object
+                            status:
+                              description: Status object status.
+                              items:
+                                type: string
+                              type: array
+                          type: object
+                        type:
+                          description: Type the event type
+                          type: string
+                      required:
+                      - name
+                      - type
+                      type: object
+                    type: array
+                type: object
               volumes:
                 description: Volumes contains volume Overrides of the BuildStrategy
                   volumes in case those are allowed to be overridden. Must only contain
diff --git a/docs/build.md b/docs/build.md
index 9639e210..f30bf8e0 100644
--- a/docs/build.md
+++ b/docs/build.md
@@ -17,6 +17,7 @@ SPDX-License-Identifier: Apache-2.0
   - [Defining the Output](#defining-the-output)
   - [Defining Retention Parameters](#defining-retention-parameters)
   - [Defining Volumes](#defining-volumes)
+  - [Defining Triggers](#defining-triggers)
 - [BuildRun deletion](#BuildRun-deletion)
 
 ## Overview
@@ -602,6 +603,109 @@ spec:
         name: test-config
 ```
 
+### Defining Triggers
+
+Using the triggers, you can submit `BuildRun` instances when certain events happen. The idea is to be able to trigger Shipwright builds in an event driven fashion, for that purpose you can watch certain types of events.
+
+**Note**: triggers rely on the [Shipwright Triggers](https://github.com/shipwright-io/triggers) project to be deployed and configured in the same Kubernetes cluster where you run Shipwright Build. If it is not set up, the triggers defined in a Build are ignored.
+
+The types of events under watch are defined on the `.spec.trigger` attribute, please consider the following example:
+
+```yaml
+apiVersion: shipwright.io/v1alpha1
+kind: Build
+spec:
+  source:
+    url: https://github.com/shipwright-io/sample-go
+    contextDir: docker-build
+    credentials:
+      name: webhook-secret
+  trigger:
+    when: []
+```
+
+Certain types of events will use attributes defined on `.spec.source` to complete the information needed in order to dispatch events.
+
+#### GitHub
+
+The GitHub type is meant to react upon events coming from GitHub WebHook interface, the events are compared against the existing `Build` resources, and therefore it can identify the `Build` objects based on `.spec.source.url` combined with the attributes on `.spec.trigger.when[].github`.
+
+To identify a given `Build` object, the first criteria is the repository URL, and then the branch name listed on the GitHub event payload must also match. Following the criteria:
+
+- First, the branch name is checked against the `.spec.trigger.when[].github.branches` entries
+- If the `.spec.trigger.when[].github.branches` is empty, the branch name is compared against `.spec.source.revision`
+- If `spec.source.revision` is empty, the default revision name is used ("main")
+
+The following snippet shows a configuration machting `Push` and `PullRequest` events on the `main` branch, for example:
+
+```yaml
+# [...]
+spec:
+  source:
+    url: https://github.com/shipwright-io/sample-go
+  trigger:
+    when:
+      - name: push and pull-request on the main branch
+        type: GitHub
+        github:
+          events:
+            - Push
+            - PullRequest
+          branches:
+            - main
+```
+
+#### Image
+
+In order to watch over images, in combination with the [Image](https://github.com/shipwright-io/image) controller, you can trigger new builds when those container image names change.
+
+For instance, lets imagine the image named `ghcr.io/some/base-image` is used as input for the Build process and every time it changes we would like to trigger a new build. Please consider the following snippet:
+
+```yaml
+# [...]
+spec:
+  trigger:
+    when:
+      - name: watching for the base-image changes
+        type: Image
+        image:
+          names:
+            - ghcr.io/some/base-image:latest
+```
+
+#### Tekton Pipeline
+
+Shipwright can also be used in combination with [Tekton Pipeline](https://github.com/tektoncd/pipeline), you can configure the Build to watch for `Pipeline` resources in Kubernetes reacting when the object reaches the desired status (`.objectRef.status`), and is identified either by its name (`.objectRef.name`) or a label selector (`.objectRef.selector`). The example below uses the label selector approach:
+
+```yaml
+# [...]
+spec:
+  trigger:
+    when:
+      - name: watching over for the Tekton Pipeline
+        type: Pipeline
+        objectRef:
+          status:
+            - Succeeded
+          selector:
+            label: value
+```
+
+While the next snippet uses the object name for identification:
+
+```yaml
+# [...]
+spec:
+  trigger:
+    when:
+      - name: watching over for the Tekton Pipeline
+        type: Pipeline
+        objectRef:
+          status:
+            - Succeeded
+          name: tekton-pipeline-name
+```
+
 ### Sources
 
 Sources represent remote artifacts, as in external entities added to the build context before the actual Build starts. Therefore, you may employ `.spec.sources` to download artifacts from external repositories.
diff --git a/docs/buildstrategies.md b/docs/buildstrategies.md
index 2c8e84a7..a5ce0c45 100644
--- a/docs/buildstrategies.md
+++ b/docs/buildstrategies.md
@@ -906,7 +906,7 @@ metadata:
 spec:
   buildSteps:
     - name: build
-      image: quay.io/containers/buildah:v1.26.0
+      image: quay.io/containers/buildah:v1.23.3
       workingDir: $(params.shp-source-root)
       command:
         - buildah
diff --git a/go.mod b/go.mod
index f97bcfb6..80879336 100644
--- a/go.mod
+++ b/go.mod
@@ -3,16 +3,16 @@ module github.com/shipwright-io/build
 go 1.18
 
 require (
-	github.com/docker/cli v20.10.17+incompatible
+	github.com/docker/cli v20.10.16+incompatible
 	github.com/go-git/go-git/v5 v5.4.2
 	github.com/go-logr/logr v1.2.3
-	github.com/golang-jwt/jwt/v4 v4.4.2
-	github.com/google/go-containerregistry v0.10.0
+	github.com/golang-jwt/jwt/v4 v4.4.1
+	github.com/google/go-containerregistry v0.9.0
 	github.com/onsi/ginkgo/v2 v2.1.4
 	github.com/onsi/gomega v1.19.0
 	github.com/prometheus/client_golang v1.12.2
 	github.com/prometheus/client_model v0.2.0
-	github.com/spf13/cobra v1.5.0
+	github.com/spf13/cobra v1.4.0
 	github.com/spf13/pflag v1.0.5
 	github.com/tektoncd/pipeline v0.35.1
 	go.uber.org/zap v1.21.0
@@ -99,8 +99,8 @@ require (
 	go.uber.org/multierr v1.8.0 // indirect
 	golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29 // indirect
 	golang.org/x/mod v0.6.0-dev.0.20220330205332-605edab4323b // indirect
-	golang.org/x/net v0.0.0-20220524220425-1d687d428aca // indirect
-	golang.org/x/oauth2 v0.0.0-20220524215830-622c5d57e401 // indirect
+	golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 // indirect
+	golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 // indirect
 	golang.org/x/sync v0.0.0-20220513210516-0976fa681c29 // indirect
 	golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect
 	golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
diff --git a/go.sum b/go.sum
index d2c059e3..1623b9c7 100644
--- a/go.sum
+++ b/go.sum
@@ -163,7 +163,7 @@ github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV
 github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
 github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
 github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
-github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
+github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 github.com/danieljoos/wincred v1.1.0/go.mod h1:XYlo+eRTsVA9aHGp7NGjFkPla4m+DCL7hqDjlFjiygg=
@@ -173,8 +173,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
 github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE=
 github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
 github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
-github.com/docker/cli v20.10.17+incompatible h1:eO2KS7ZFeov5UJeaDmIs1NFEDRf32PaqRpvoEkKBy5M=
-github.com/docker/cli v20.10.17+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
+github.com/docker/cli v20.10.16+incompatible h1:aLQ8XowgKpR3/IysPj8qZQJBVQ+Qws61icFuZl6iKYs=
+github.com/docker/cli v20.10.16+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
 github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
 github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68=
 github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
@@ -321,8 +321,8 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
 github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
 github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
 github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
-github.com/golang-jwt/jwt/v4 v4.4.2 h1:rcc4lwaZgFMCZ5jxF9ABolDcIHdBytAFgqFPbSJQAYs=
-github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
+github.com/golang-jwt/jwt/v4 v4.4.1 h1:pC5DB52sCeK48Wlb9oPcdhnjkz1TKt1D/P7WKJ0kUcQ=
+github.com/golang-jwt/jwt/v4 v4.4.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -377,8 +377,8 @@ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
 github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
 github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
 github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/google/go-containerregistry v0.10.0 h1:qd/fv2nQajGZJenaNcdaghlwSPjQ0NphN9hzArr2WWg=
-github.com/google/go-containerregistry v0.10.0/go.mod h1:C7uwbB1QUAtvnknyd3ethxJRd4gtEjU/9WLXzckfI1Y=
+github.com/google/go-containerregistry v0.9.0 h1:5Ths7RjxyFV0huKChQTgY6fLzvHhZMpLTFNja8U0/0w=
+github.com/google/go-containerregistry v0.9.0/go.mod h1:9eq4BnSufyT1kHNffX+vSXVonaJ7yaIOulrKZejMxnQ=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
@@ -659,8 +659,8 @@ github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU
 github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
 github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
 github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk=
-github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=
-github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM=
+github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q=
+github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g=
 github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
 github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
 github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
@@ -869,9 +869,8 @@ golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su
 golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
+golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 h1:NWy5+hlRbC7HK+PmcXVUmW1IMyFce7to56IUvhUFm7Y=
 golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.0.0-20220524220425-1d687d428aca h1:xTaFYiPROfpPhqrfTIDXj0ri1SpfueYT951s4bAuDO8=
-golang.org/x/net v0.0.0-20220524220425-1d687d428aca/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -891,9 +890,8 @@ golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ
 golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
 golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
+golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 h1:OSnWWcOd/CtWQC2cYSBgbTSJv3ciqd8r54ySIW2y3RE=
 golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
-golang.org/x/oauth2 v0.0.0-20220524215830-622c5d57e401 h1:zwrSfklXn0gxyLRX/aR+q6cgHbV/ItVyzbPlbA+dkAw=
-golang.org/x/oauth2 v0.0.0-20220524215830-622c5d57e401/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
diff --git a/pkg/apis/build/v1alpha1/build_types.go b/pkg/apis/build/v1alpha1/build_types.go
index 1bf4c814..0ecd43e6 100644
--- a/pkg/apis/build/v1alpha1/build_types.go
+++ b/pkg/apis/build/v1alpha1/build_types.go
@@ -60,6 +60,17 @@ const (
 	VolumeNotOverridable BuildReason = "VolumeNotOverridable"
 	// UndefinedVolume indicates that volume defined by build is not found in the strategy
 	UndefinedVolume BuildReason = "UndefinedVolume"
+	// TriggerNameCanNotBeBlank indicates the trigger condition does not have a name
+	TriggerNameCanNotBeBlank BuildReason = "TriggerNameCanNotBeBlank"
+	// TriggerInvalidType indicates the trigger type is invalid
+	TriggerInvalidType BuildReason = "TriggerInvalidType"
+	// TriggerInvalidGitHubWebHook indicates the trigger type GitHub is invalid
+	TriggerInvalidGitHubWebHook BuildReason = "TriggerInvalidGitHubWebHook"
+	// TriggerInvalidImage indicates the trigger type Image is invalid
+	TriggerInvalidImage BuildReason = "TriggerInvalidImage"
+	// TriggerInvalidPipeline indicates the trigger type Pipeline is invalid
+	TriggerInvalidPipeline BuildReason = "TriggerInvalidPipeline"
+
 	// AllValidationsSucceeded indicates a Build was successfully validated
 	AllValidationsSucceeded = "all validations succeeded"
 )
@@ -109,6 +120,11 @@ type BuildSpec struct {
 	// +optional
 	Sources []BuildSource `json:"sources,omitempty"`
 
+	// Trigger defines the scenarios where a new build should be triggered.
+	//
+	// +optional
+	Trigger *Trigger `json:"trigger,omitempty"`
+
 	// Strategy references the BuildStrategy to use to build the container
 	// image.
 	Strategy Strategy `json:"strategy"`
diff --git a/pkg/apis/build/v1alpha1/trigger.go b/pkg/apis/build/v1alpha1/trigger.go
new file mode 100644
index 00000000..c581a5bb
--- /dev/null
+++ b/pkg/apis/build/v1alpha1/trigger.go
@@ -0,0 +1,19 @@
+// Copyright The Shipwright Contributors
+//
+// SPDX-License-Identifier: Apache-2.0
+package v1alpha1
+
+import (
+	corev1 "k8s.io/api/core/v1"
+)
+
+// Trigger represents the webhook trigger configuration for a Build.
+type Trigger struct {
+	// When the list of scenarios when a new build should take place.
+	When []TriggerWhen `json:"when,omitempty"`
+
+	// SecretRef points to a local object carrying the secret token to validate webhook request.
+	//
+	// +optional
+	SecretRef *corev1.LocalObjectReference `json:"secretRef,omitempty"`
+}
diff --git a/pkg/apis/build/v1alpha1/trigger_types.go b/pkg/apis/build/v1alpha1/trigger_types.go
new file mode 100644
index 00000000..697033ca
--- /dev/null
+++ b/pkg/apis/build/v1alpha1/trigger_types.go
@@ -0,0 +1,66 @@
+// Copyright The Shipwright Contributors
+//
+// SPDX-License-Identifier: Apache-2.0
+package v1alpha1
+
+// TriggerType set of TriggerWhen valid names.
+type TriggerType string
+
+const (
+	// GitHubWebHookTrigger GitHubWebHookTrigger trigger type name.
+	GitHubWebHookTrigger TriggerType = "GitHub"
+
+	// ImageTrigger Image trigger type name.
+	ImageTrigger TriggerType = "Image"
+
+	// PipelineTrigger Tekton Pipeline trigger type name.
+	PipelineTrigger TriggerType = "Pipeline"
+)
+
+// GitHubEventName set of WhenGitHub valid event names.
+type GitHubEventName string
+
+const (
+	// GitHubPullRequestEvent github pull-request event name.
+	GitHubPullRequestEvent GitHubEventName = "PullRequest"
+
+	// GitHubPushEvent git push webhook event name.
+	GitHubPushEvent GitHubEventName = "Push"
+)
+
+// WhenImage attributes to match Image events.
+type WhenImage struct {
+	// Names fully qualified image names.
+	//
+	// +optional
+	Names []string `json:"names,omitempty"`
+}
+
+// WhenGitHub attributes to match GitHub events.
+type WhenGitHub struct {
+	// Events GitHub event names.
+	//
+	// +kubebuilder:validation:MinItems=1
+	Events []GitHubEventName `json:"events,omitempty"`
+
+	// Branches slice of branch names where the event applies.
+	//
+	// +optional
+	Branches []string `json:"branches,omitempty"`
+}
+
+// WhenObjectRef attributes to reference local Kubernetes objects.
+type WhenObjectRef struct {
+	// Name target object name.
+	//
+	// +optional
+	Name string `json:"name,omitempty"`
+
+	// Status object status.
+	Status []string `json:"status,omitempty"`
+
+	// Selector label selector.
+	//
+	// +optional
+	Selector map[string]string `json:"selector,omitempty"`
+}
diff --git a/pkg/apis/build/v1alpha1/trigger_when.go b/pkg/apis/build/v1alpha1/trigger_when.go
new file mode 100644
index 00000000..f127a0ad
--- /dev/null
+++ b/pkg/apis/build/v1alpha1/trigger_when.go
@@ -0,0 +1,41 @@
+// Copyright The Shipwright Contributors
+//
+// SPDX-License-Identifier: Apache-2.0
+package v1alpha1
+
+// TriggerWhen a given scenario where the webhook trigger is applicable.
+type TriggerWhen struct {
+	// Name name or the short description of the trigger condition.
+	Name string `json:"name"`
+
+	// Type the event type
+	Type TriggerType `json:"type"`
+
+	// GitHub describes how to trigger builds based on GitHub (SCM) events.
+	//
+	// +optional
+	GitHub *WhenGitHub `json:"github,omitempty"`
+
+	// Image slice of image names where the event applies.
+	//
+	// +optional
+	Image *WhenImage `json:"image,omitempty"`
+
+	// ObjectRef describes how to match a foreign resource, either using the name or the label
+	// selector, plus the current resource status.
+	//
+	// +optional
+	ObjectRef *WhenObjectRef `json:"objectRef,omitempty"`
+}
+
+// GetBranches return a slice of branch names based on the WhenTypeName informed.
+func (w *TriggerWhen) GetBranches(whenType TriggerType) []string {
+	switch whenType {
+	case GitHubWebHookTrigger:
+		if w.GitHub == nil {
+			return nil
+		}
+		return w.GitHub.Branches
+	}
+	return nil
+}
diff --git a/pkg/apis/build/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/build/v1alpha1/zz_generated.deepcopy.go
index 44852474..be577460 100644
--- a/pkg/apis/build/v1alpha1/zz_generated.deepcopy.go
+++ b/pkg/apis/build/v1alpha1/zz_generated.deepcopy.go
@@ -394,6 +394,11 @@ func (in *BuildSpec) DeepCopyInto(out *BuildSpec) {
 			(*in)[i].DeepCopyInto(&(*out)[i])
 		}
 	}
+	if in.Trigger != nil {
+		in, out := &in.Trigger, &out.Trigger
+		*out = new(Trigger)
+		(*in).DeepCopyInto(*out)
+	}
 	in.Strategy.DeepCopyInto(&out.Strategy)
 	if in.Builder != nil {
 		in, out := &in.Builder, &out.Builder
@@ -1125,3 +1130,137 @@ func (in *Strategy) DeepCopy() *Strategy {
 	in.DeepCopyInto(out)
 	return out
 }
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *Trigger) DeepCopyInto(out *Trigger) {
+	*out = *in
+	if in.When != nil {
+		in, out := &in.When, &out.When
+		*out = make([]TriggerWhen, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+	if in.SecretRef != nil {
+		in, out := &in.SecretRef, &out.SecretRef
+		*out = new(corev1.LocalObjectReference)
+		**out = **in
+	}
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Trigger.
+func (in *Trigger) DeepCopy() *Trigger {
+	if in == nil {
+		return nil
+	}
+	out := new(Trigger)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *TriggerWhen) DeepCopyInto(out *TriggerWhen) {
+	*out = *in
+	if in.GitHub != nil {
+		in, out := &in.GitHub, &out.GitHub
+		*out = new(WhenGitHub)
+		(*in).DeepCopyInto(*out)
+	}
+	if in.Image != nil {
+		in, out := &in.Image, &out.Image
+		*out = new(WhenImage)
+		(*in).DeepCopyInto(*out)
+	}
+	if in.ObjectRef != nil {
+		in, out := &in.ObjectRef, &out.ObjectRef
+		*out = new(WhenObjectRef)
+		(*in).DeepCopyInto(*out)
+	}
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TriggerWhen.
+func (in *TriggerWhen) DeepCopy() *TriggerWhen {
+	if in == nil {
+		return nil
+	}
+	out := new(TriggerWhen)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *WhenGitHub) DeepCopyInto(out *WhenGitHub) {
+	*out = *in
+	if in.Events != nil {
+		in, out := &in.Events, &out.Events
+		*out = make([]GitHubEventName, len(*in))
+		copy(*out, *in)
+	}
+	if in.Branches != nil {
+		in, out := &in.Branches, &out.Branches
+		*out = make([]string, len(*in))
+		copy(*out, *in)
+	}
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WhenGitHub.
+func (in *WhenGitHub) DeepCopy() *WhenGitHub {
+	if in == nil {
+		return nil
+	}
+	out := new(WhenGitHub)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *WhenImage) DeepCopyInto(out *WhenImage) {
+	*out = *in
+	if in.Names != nil {
+		in, out := &in.Names, &out.Names
+		*out = make([]string, len(*in))
+		copy(*out, *in)
+	}
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WhenImage.
+func (in *WhenImage) DeepCopy() *WhenImage {
+	if in == nil {
+		return nil
+	}
+	out := new(WhenImage)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *WhenObjectRef) DeepCopyInto(out *WhenObjectRef) {
+	*out = *in
+	if in.Status != nil {
+		in, out := &in.Status, &out.Status
+		*out = make([]string, len(*in))
+		copy(*out, *in)
+	}
+	if in.Selector != nil {
+		in, out := &in.Selector, &out.Selector
+		*out = make(map[string]string, len(*in))
+		for key, val := range *in {
+			(*out)[key] = val
+		}
+	}
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WhenObjectRef.
+func (in *WhenObjectRef) DeepCopy() *WhenObjectRef {
+	if in == nil {
+		return nil
+	}
+	out := new(WhenObjectRef)
+	in.DeepCopyInto(out)
+	return out
+}
diff --git a/pkg/reconciler/build/build.go b/pkg/reconciler/build/build.go
index 9d6f2d8b..ba9b2a9d 100644
--- a/pkg/reconciler/build/build.go
+++ b/pkg/reconciler/build/build.go
@@ -31,6 +31,7 @@ var validationTypes = [...]string{
 	validate.Sources,
 	validate.BuildName,
 	validate.Envs,
+	validate.Triggers,
 }
 
 // ReconcileBuild reconciles a Build object
diff --git a/pkg/validate/trigger.go b/pkg/validate/trigger.go
new file mode 100644
index 00000000..562aafea
--- /dev/null
+++ b/pkg/validate/trigger.go
@@ -0,0 +1,121 @@
+// Copyright The Shipwright Contributors
+//
+// SPDX-License-Identifier: Apache-2.0
+package validate
+
+import (
+	"context"
+	"fmt"
+
+	build "github.com/shipwright-io/build/pkg/apis/build/v1alpha1"
+	kerrors "k8s.io/apimachinery/pkg/util/errors"
+	"k8s.io/utils/pointer"
+)
+
+// Trigger implements the interface BuildPath with the objective of applying validations against the
+// `.spec.trigger` related attributes.
+type Trigger struct {
+	build *build.Build // build instance
+}
+
+// validate goes through the trigger "when" conditions to validate each entry.
+func (t *Trigger) validate(triggerWhen []build.TriggerWhen) []error {
+	var allErrs []error
+	for _, when := range triggerWhen {
+		if when.Name == "" {
+			t.build.Status.Reason = build.BuildReasonPtr(build.TriggerNameCanNotBeBlank)
+			t.build.Status.Message = pointer.String("name is not set on when trigger condition")
+			allErrs = append(allErrs, fmt.Errorf("%s", *t.build.Status.Message))
+		}
+
+		switch when.Type {
+		case build.GitHubWebHookTrigger:
+			if when.GitHub == nil {
+				t.build.Status.Reason = build.BuildReasonPtr(build.TriggerInvalidGitHubWebHook)
+				t.build.Status.Message = pointer.String(fmt.Sprintf(
+					"%q is missing required attribute `.github`", when.Name,
+				))
+				allErrs = append(allErrs, fmt.Errorf("%s", *t.build.Status.Message))
+			} else {
+				if len(when.GitHub.Events) == 0 {
+					t.build.Status.Reason = build.BuildReasonPtr(build.TriggerInvalidGitHubWebHook)
+					t.build.Status.Message = pointer.String(fmt.Sprintf(
+						"%q is missing required attribute `.github.events`", when.Name,
+					))
+					allErrs = append(allErrs, fmt.Errorf("%s", *t.build.Status.Message))
+				}
+			}
+		case build.ImageTrigger:
+			if when.Image == nil {
+				t.build.Status.Reason = build.BuildReasonPtr(build.TriggerInvalidImage)
+				t.build.Status.Message = pointer.String(fmt.Sprintf(
+					"%q is missing required attribute `.image`", when.Name,
+				))
+				allErrs = append(allErrs, fmt.Errorf("%s", *t.build.Status.Message))
+			} else {
+				if len(when.Image.Names) == 0 {
+					t.build.Status.Reason = build.BuildReasonPtr(build.TriggerInvalidImage)
+					t.build.Status.Message = pointer.String(fmt.Sprintf(
+						"%q is missing required attribute `.image.names`", when.Name,
+					))
+					allErrs = append(allErrs, fmt.Errorf("%s", *t.build.Status.Message))
+				}
+			}
+		case build.PipelineTrigger:
+			if when.ObjectRef == nil {
+				t.build.Status.Reason = build.BuildReasonPtr(build.TriggerInvalidPipeline)
+				t.build.Status.Message = pointer.String(fmt.Sprintf(
+					"%q is missing required attribute `.objectRef`", when.Name,
+				))
+				allErrs = append(allErrs, fmt.Errorf("%s", *t.build.Status.Message))
+			} else {
+				if len(when.ObjectRef.Status) == 0 {
+					t.build.Status.Reason = build.BuildReasonPtr(build.TriggerInvalidPipeline)
+					t.build.Status.Message = pointer.String(fmt.Sprintf(
+						"%q is missing required attribute `.objectRef.status`", when.Name,
+					))
+					allErrs = append(allErrs, fmt.Errorf("%s", *t.build.Status.Message))
+				}
+				if when.ObjectRef.Name == "" && len(when.ObjectRef.Selector) == 0 {
+					t.build.Status.Reason = build.BuildReasonPtr(build.TriggerInvalidPipeline)
+					t.build.Status.Message = pointer.String(fmt.Sprintf(
+						"%q is missing required attributes `.objectRef.name` or `.objectRef.selector`",
+						when.Name,
+					))
+					allErrs = append(allErrs, fmt.Errorf("%s", *t.build.Status.Message))
+				}
+				if when.ObjectRef.Name != "" && len(when.ObjectRef.Selector) > 0 {
+					t.build.Status.Reason = build.BuildReasonPtr(build.TriggerInvalidPipeline)
+					t.build.Status.Message = pointer.String(fmt.Sprintf(
+						"%q contains `.objectRef.name` and `.objectRef.selector`, must be only one",
+						when.Name,
+					))
+					allErrs = append(allErrs, fmt.Errorf("%s", *t.build.Status.Message))
+				}
+			}
+		default:
+			t.build.Status.Reason = build.BuildReasonPtr(build.TriggerInvalidType)
+			t.build.Status.Message = pointer.String(
+				fmt.Sprintf("%q contains an invalid type %q", when.Name, when.Type))
+			allErrs = append(allErrs, fmt.Errorf("%s", *t.build.Status.Message))
+		}
+	}
+	return allErrs
+}
+
+// ValidatePath validates the `.spec.trigger` path.
+func (t *Trigger) ValidatePath(_ context.Context) error {
+	if t.build.Spec.Trigger == nil || len(t.build.Spec.Trigger.When) == 0 {
+		return nil
+	}
+
+	if allErrs := t.validate(t.build.Spec.Trigger.When); len(allErrs) != 0 {
+		return fmt.Errorf("%s", kerrors.NewAggregate(allErrs).Error())
+	}
+	return nil
+}
+
+// NewTrigger instantiate Trigger validation helper.
+func NewTrigger(b *build.Build) *Trigger {
+	return &Trigger{build: b}
+}
diff --git a/pkg/validate/validate.go b/pkg/validate/validate.go
index 566c6c0c..408fee10 100644
--- a/pkg/validate/validate.go
+++ b/pkg/validate/validate.go
@@ -31,8 +31,13 @@ const (
 	// OwnerReferences for validating the ownerreferences between a Build
 	// and BuildRun objects
 	OwnerReferences = "ownerreferences"
-	namespace       = "namespace"
-	name            = "name"
+	// Triggers for validating the `.spec.triggers` entries
+	Triggers = "triggers"
+)
+
+const (
+	namespace = "namespace"
+	name      = "name"
 )
 
 // BuildPath is an interface that holds a ValidatePath() function
@@ -64,6 +69,8 @@ func NewValidation(
 		return &BuildNameRef{Build: build}, nil
 	case Envs:
 		return &Env{Build: build}, nil
+	case Triggers:
+		return &Trigger{build: build}, nil
 	default:
 		return nil, fmt.Errorf("unknown validation type")
 	}
@@ -113,6 +120,11 @@ func BuildRunFields(buildRun *build.BuildRun) (string, string) {
 			return resources.BuildRunBuildFieldOverrideForbidden,
 				"cannot use 'timeout' override and 'buildSpec' simultaneously"
 		}
+
+		if buildRun.Spec.BuildSpec.Trigger != nil {
+			return resources.BuildRunBuildFieldOverrideForbidden,
+				"cannot use 'triggers' override in the 'BuildRun', only allowed in the 'Build'"
+		}
 	}
 
 	return "", ""
diff --git a/samples/buildstrategy/buildah/buildstrategy_buildah_cr.yaml b/samples/buildstrategy/buildah/buildstrategy_buildah_cr.yaml
index d54b35c2..48088df3 100644
--- a/samples/buildstrategy/buildah/buildstrategy_buildah_cr.yaml
+++ b/samples/buildstrategy/buildah/buildstrategy_buildah_cr.yaml
@@ -6,7 +6,7 @@ metadata:
 spec:
   buildSteps:
     - name: build-and-push
-      image: quay.io/containers/buildah:v1.26.0
+      image: quay.io/containers/buildah:v1.23.3
       workingDir: $(params.shp-source-root)
       securityContext:
         privileged: true
diff --git a/samples/buildstrategy/source-to-image/buildstrategy_source-to-image-redhat_cr.yaml b/samples/buildstrategy/source-to-image/buildstrategy_source-to-image-redhat_cr.yaml
index 8a3d55a5..1699913f 100644
--- a/samples/buildstrategy/source-to-image/buildstrategy_source-to-image-redhat_cr.yaml
+++ b/samples/buildstrategy/source-to-image/buildstrategy_source-to-image-redhat_cr.yaml
@@ -21,7 +21,7 @@ spec:
         - name: s2i
           mountPath: /s2i
     - name: buildah
-      image: quay.io/containers/buildah:v1.26.0
+      image: quay.io/containers/buildah:v1.23.3
       workingDir: /s2i
       securityContext:
         privileged: true
diff --git a/vendor/github.com/golang-jwt/jwt/v4/README.md b/vendor/github.com/golang-jwt/jwt/v4/README.md
index f5d551ca..01b21646 100644
--- a/vendor/github.com/golang-jwt/jwt/v4/README.md
+++ b/vendor/github.com/golang-jwt/jwt/v4/README.md
@@ -36,23 +36,9 @@ The part in the middle is the interesting bit.  It's called the Claims and conta
 
 This library supports the parsing and verification as well as the generation and signing of JWTs.  Current supported signing algorithms are HMAC SHA, RSA, RSA-PSS, and ECDSA, though hooks are present for adding your own.
 
-## Installation Guidelines
-
-1. To install the jwt package, you first need to have [Go](https://go.dev/doc/install) installed, then you can use the command below to add `jwt-go` as a dependency in your Go program.
-
-```sh
-go get -u github.com/golang-jwt/jwt/v4
-```
-
-2. Import it in your code:
-
-```go
-import "github.com/golang-jwt/jwt/v4"
-```
-
 ## Examples
 
-See [the project documentation](https://pkg.go.dev/github.com/golang-jwt/jwt/v4) for examples of usage:
+See [the project documentation](https://pkg.go.dev/github.com/golang-jwt/jwt) for examples of usage:
 
 * [Simple example of parsing and validating a token](https://pkg.go.dev/github.com/golang-jwt/jwt#example-Parse-Hmac)
 * [Simple example of building and signing a token](https://pkg.go.dev/github.com/golang-jwt/jwt#example-New-Hmac)
@@ -60,15 +46,14 @@ See [the project documentation](https://pkg.go.dev/github.com/golang-jwt/jwt/v4)
 
 ## Extensions
 
-This library publishes all the necessary components for adding your own signing methods or key functions.  Simply implement the `SigningMethod` interface and register a factory method using `RegisterSigningMethod` or provide a `jwt.Keyfunc`.
+This library publishes all the necessary components for adding your own signing methods.  Simply implement the `SigningMethod` interface and register a factory method using `RegisterSigningMethod`. 
 
-A common use case would be integrating with different 3rd party signature providers, like key management services from various cloud providers or Hardware Security Modules (HSMs) or to implement additional standards.
+A common use case would be integrating with different 3rd party signature providers, like key management services from various cloud providers or Hardware Security Modules (HSMs).
 
-| Extension | Purpose                                                                                                  | Repo                                       |
-| --------- | -------------------------------------------------------------------------------------------------------- | ------------------------------------------ |
-| GCP       | Integrates with multiple Google Cloud Platform signing tools (AppEngine, IAM API, Cloud KMS)             | https://github.com/someone1/gcp-jwt-go     |
-| AWS       | Integrates with AWS Key Management Service, KMS                                                          | https://github.com/matelang/jwt-go-aws-kms |
-| JWKS      | Provides support for JWKS ([RFC 7517](https://datatracker.ietf.org/doc/html/rfc7517)) as a `jwt.Keyfunc` | https://github.com/MicahParks/keyfunc       |
+| Extension | Purpose                                                                                      | Repo                                       |
+|-----------|----------------------------------------------------------------------------------------------|--------------------------------------------|
+| GCP       | Integrates with multiple Google Cloud Platform signing tools (AppEngine, IAM API, Cloud KMS) | https://github.com/someone1/gcp-jwt-go     |
+| AWS       | Integrates with AWS Key Management Service, KMS                                              | https://github.com/matelang/jwt-go-aws-kms |
 
 *Disclaimer*: Unless otherwise specified, these integrations are maintained by third parties and should not be considered as a primary offer by any of the mentioned cloud providers
 
diff --git a/vendor/github.com/golang-jwt/jwt/v4/SECURITY.md b/vendor/github.com/golang-jwt/jwt/v4/SECURITY.md
deleted file mode 100644
index b08402c3..00000000
--- a/vendor/github.com/golang-jwt/jwt/v4/SECURITY.md
+++ /dev/null
@@ -1,19 +0,0 @@
-# Security Policy
-
-## Supported Versions
-
-As of February 2022 (and until this document is updated), the latest version `v4` is supported.
-
-## Reporting a Vulnerability
-
-If you think you found a vulnerability, and even if you are not sure, please report it to jwt-go-security@googlegroups.com or one of the other [golang-jwt maintainers](https://github.com/orgs/golang-jwt/people). Please try be explicit, describe steps to reproduce the security issue with code example(s).
-
-You will receive a response within a timely manner. If the issue is confirmed, we will do our best to release a patch as soon as possible given the complexity of the problem.
-
-## Public Discussions
-
-Please avoid publicly discussing a potential security vulnerability.
-
-Let's take this offline and find a solution first, this limits the potential impact as much as possible.
-
-We appreciate your help!
diff --git a/vendor/github.com/golang-jwt/jwt/v4/rsa_pss.go b/vendor/github.com/golang-jwt/jwt/v4/rsa_pss.go
index 4fd6f9e6..5a8502fe 100644
--- a/vendor/github.com/golang-jwt/jwt/v4/rsa_pss.go
+++ b/vendor/github.com/golang-jwt/jwt/v4/rsa_pss.go
@@ -1,4 +1,3 @@
-//go:build go1.4
 // +build go1.4
 
 package jwt
diff --git a/vendor/github.com/golang-jwt/jwt/v4/token.go b/vendor/github.com/golang-jwt/jwt/v4/token.go
index 3cb0f3f0..09b4cde5 100644
--- a/vendor/github.com/golang-jwt/jwt/v4/token.go
+++ b/vendor/github.com/golang-jwt/jwt/v4/token.go
@@ -7,6 +7,7 @@ import (
 	"time"
 )
 
+
 // DecodePaddingAllowed will switch the codec used for decoding JWTs respectively. Note that the JWS RFC7515
 // states that the tokens will utilize a Base64url encoding with no padding. Unfortunately, some implementations
 // of JWT are producing non-standard tokens, and thus require support for decoding. Note that this is a global
diff --git a/vendor/github.com/golang-jwt/jwt/v4/types.go b/vendor/github.com/golang-jwt/jwt/v4/types.go
index ac8e140e..2c647fd2 100644
--- a/vendor/github.com/golang-jwt/jwt/v4/types.go
+++ b/vendor/github.com/golang-jwt/jwt/v4/types.go
@@ -53,23 +53,9 @@ func (date NumericDate) MarshalJSON() (b []byte, err error) {
 	if TimePrecision < time.Second {
 		prec = int(math.Log10(float64(time.Second) / float64(TimePrecision)))
 	}
-	truncatedDate := date.Truncate(TimePrecision)
-
-	// For very large timestamps, UnixNano would overflow an int64, but this
-	// function requires nanosecond level precision, so we have to use the
-	// following technique to get round the issue:
-	// 1. Take the normal unix timestamp to form the whole number part of the
-	//    output,
-	// 2. Take the result of the Nanosecond function, which retuns the offset
-	//    within the second of the particular unix time instance, to form the
-	//    decimal part of the output
-	// 3. Concatenate them to produce the final result
-	seconds := strconv.FormatInt(truncatedDate.Unix(), 10)
-	nanosecondsOffset := strconv.FormatFloat(float64(truncatedDate.Nanosecond())/float64(time.Second), 'f', prec, 64)
-
-	output := append([]byte(seconds), []byte(nanosecondsOffset)[1:]...)
-
-	return output, nil
+	f := float64(date.Truncate(TimePrecision).UnixNano()) / float64(time.Second)
+
+	return []byte(strconv.FormatFloat(f, 'f', prec, 64)), nil
 }
 
 // UnmarshalJSON is an implementation of the json.RawMessage interface and deserializses a
diff --git a/vendor/github.com/google/go-containerregistry/pkg/crane/options.go b/vendor/github.com/google/go-containerregistry/pkg/crane/options.go
index 2f95e055..6e1177e4 100644
--- a/vendor/github.com/google/go-containerregistry/pkg/crane/options.go
+++ b/vendor/github.com/google/go-containerregistry/pkg/crane/options.go
@@ -108,14 +108,6 @@ func WithUserAgent(ua string) Option {
 	}
 }
 
-// WithNondistributable is an option that allows pushing non-distributable
-// layers.
-func WithNondistributable() Option {
-	return func(o *Options) {
-		o.Remote = append(o.Remote, remote.WithNondistributable)
-	}
-}
-
 // WithContext is a functional option for setting the context.
 func WithContext(ctx context.Context) Option {
 	return func(o *Options) {
diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/partial/with.go b/vendor/github.com/google/go-containerregistry/pkg/v1/partial/with.go
index b64e9881..3a5c6157 100644
--- a/vendor/github.com/google/go-containerregistry/pkg/v1/partial/with.go
+++ b/vendor/github.com/google/go-containerregistry/pkg/v1/partial/with.go
@@ -88,22 +88,9 @@ func (cl *configLayer) MediaType() (types.MediaType, error) {
 
 var _ v1.Layer = (*configLayer)(nil)
 
-// withConfigLayer allows partial image implementations to provide a layer
-// for their config file.
-type withConfigLayer interface {
-	ConfigLayer() (v1.Layer, error)
-}
-
 // ConfigLayer implements v1.Layer from the raw config bytes.
 // This is so that clients (e.g. remote) can access the config as a blob.
-//
-// Images that want to return a specific layer implementation can implement
-// withConfigLayer.
 func ConfigLayer(i WithRawConfigFile) (v1.Layer, error) {
-	if wcl, ok := unwrap(i).(withConfigLayer); ok {
-		return wcl.ConfigLayer()
-	}
-
 	h, err := ConfigName(i)
 	if err != nil {
 		return nil, err
diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/remote/check.go b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/check.go
index e714a330..25d86956 100644
--- a/vendor/github.com/google/go-containerregistry/pkg/v1/remote/check.go
+++ b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/check.go
@@ -39,7 +39,7 @@ func CheckPushPermission(ref name.Reference, kc authn.Keychain, t http.RoundTrip
 		client:  &http.Client{Transport: tr},
 		context: context.Background(),
 	}
-	loc, _, err := w.initiateUpload("", "", "")
+	loc, _, err := w.initiateUpload("", "")
 	if loc != "" {
 		// Since we're only initiating the upload to check whether we
 		// can, we should attempt to cancel it, in case initiating
diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/remote/mount.go b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/mount.go
index 36d08856..72899704 100644
--- a/vendor/github.com/google/go-containerregistry/pkg/v1/remote/mount.go
+++ b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/mount.go
@@ -93,16 +93,3 @@ func (mi *mountableImage) LayerByDiffID(d v1.Hash) (v1.Layer, error) {
 func (mi *mountableImage) Descriptor() (*v1.Descriptor, error) {
 	return partial.Descriptor(mi.Image)
 }
-
-// ConfigLayer retains the original reference so that it can be mounted.
-// See partial.ConfigLayer.
-func (mi *mountableImage) ConfigLayer() (v1.Layer, error) {
-	l, err := partial.ConfigLayer(mi.Image)
-	if err != nil {
-		return nil, err
-	}
-	return &MountableLayer{
-		Layer:     l,
-		Reference: mi.Reference,
-	}, nil
-}
diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/remote/write.go b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/write.go
index 7b9f31e1..d412f953 100644
--- a/vendor/github.com/google/go-containerregistry/pkg/v1/remote/write.go
+++ b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/write.go
@@ -267,16 +267,13 @@ func (w *writer) checkExistingManifest(h v1.Hash, mt types.MediaType) (bool, err
 // On success, the layer was either mounted (nothing more to do) or a blob
 // upload was initiated and the body of that blob should be sent to the returned
 // location.
-func (w *writer) initiateUpload(from, mount, origin string) (location string, mounted bool, err error) {
+func (w *writer) initiateUpload(from, mount string) (location string, mounted bool, err error) {
 	u := w.url(fmt.Sprintf("/v2/%s/blobs/uploads/", w.repo.RepositoryStr()))
 	uv := url.Values{}
 	if mount != "" && from != "" {
 		// Quay will fail if we specify a "mount" without a "from".
-		uv.Set("mount", mount)
-		uv.Set("from", from)
-		if origin != "" {
-			uv.Set("origin", origin)
-		}
+		uv["mount"] = []string{mount}
+		uv["from"] = []string{from}
 	}
 	u.RawQuery = uv.Encode()
 
@@ -337,29 +334,16 @@ func (r *progressReader) Close() error { return r.rc.Close() }
 // streamBlob streams the contents of the blob to the specified location.
 // On failure, this will return an error.  On success, this will return the location
 // header indicating how to commit the streamed blob.
-func (w *writer) streamBlob(ctx context.Context, layer v1.Layer, streamLocation string) (commitLocation string, rerr error) {
+func (w *writer) streamBlob(ctx context.Context, blob io.ReadCloser, streamLocation string) (commitLocation string, rerr error) {
 	reset := func() {}
 	defer func() {
 		if rerr != nil {
 			reset()
 		}
 	}()
-	blob, err := layer.Compressed()
-	if err != nil {
-		return "", err
-	}
-
-	getBody := layer.Compressed
 	if w.updates != nil {
 		var count int64
 		blob = &progressReader{rc: blob, updates: w.updates, lastUpdate: w.lastUpdate, count: &count}
-		getBody = func() (io.ReadCloser, error) {
-			blob, err := layer.Compressed()
-			if err != nil {
-				return nil, err
-			}
-			return &progressReader{rc: blob, updates: w.updates, lastUpdate: w.lastUpdate, count: &count}, nil
-		}
 		reset = func() {
 			atomic.AddInt64(&w.lastUpdate.Complete, -count)
 			w.updates <- *w.lastUpdate
@@ -370,10 +354,6 @@ func (w *writer) streamBlob(ctx context.Context, layer v1.Layer, streamLocation
 	if err != nil {
 		return "", err
 	}
-	if _, ok := layer.(*stream.Layer); !ok {
-		// We can't retry streaming layers.
-		req.GetBody = getBody
-	}
 	req.Header.Set("Content-Type", "application/octet-stream")
 
 	resp, err := w.client.Do(req.WithContext(ctx))
@@ -431,7 +411,7 @@ func (w *writer) incrProgress(written int64) {
 // uploadOne performs a complete upload of a single layer.
 func (w *writer) uploadOne(ctx context.Context, l v1.Layer) error {
 	tryUpload := func() error {
-		var from, mount, origin string
+		var from, mount string
 		if h, err := l.Digest(); err == nil {
 			// If we know the digest, this isn't a streaming layer. Do an existence
 			// check so we can skip uploading the layer if possible.
@@ -452,11 +432,12 @@ func (w *writer) uploadOne(ctx context.Context, l v1.Layer) error {
 			mount = h.String()
 		}
 		if ml, ok := l.(*MountableLayer); ok {
-			from = ml.Reference.Context().RepositoryStr()
-			origin = ml.Reference.Context().RegistryStr()
+			if w.repo.RegistryStr() == ml.Reference.Context().RegistryStr() {
+				from = ml.Reference.Context().RepositoryStr()
+			}
 		}
 
-		location, mounted, err := w.initiateUpload(from, mount, origin)
+		location, mounted, err := w.initiateUpload(from, mount)
 		if err != nil {
 			return err
 		} else if mounted {
@@ -484,7 +465,11 @@ func (w *writer) uploadOne(ctx context.Context, l v1.Layer) error {
 			ctx = redact.NewContext(ctx, "omitting binary blobs from logs")
 		}
 
-		location, err = w.streamBlob(ctx, l, location)
+		blob, err := l.Compressed()
+		if err != nil {
+			return err
+		}
+		location, err = w.streamBlob(ctx, blob, location)
 		if err != nil {
 			return err
 		}
diff --git a/vendor/github.com/spf13/cobra/CHANGELOG.md b/vendor/github.com/spf13/cobra/CHANGELOG.md
new file mode 100644
index 00000000..8a23b4f8
--- /dev/null
+++ b/vendor/github.com/spf13/cobra/CHANGELOG.md
@@ -0,0 +1,51 @@
+# Cobra Changelog
+
+## v1.1.3
+
+* **Fix:** release-branch.cobra1.1 only: Revert "Deprecate Go < 1.14" to maintain backward compatibility
+
+## v1.1.2
+
+### Notable Changes
+
+* Bump license year to 2021 in golden files (#1309) @Bowbaq
+* Enhance PowerShell completion with custom comp (#1208) @Luap99
+* Update gopkg.in/yaml.v2 to v2.4.0: The previous breaking change in yaml.v2 v2.3.0 has been reverted, see go-yaml/yaml#670
+* Documentation readability improvements (#1228 etc.) @zaataylor etc.
+* Use golangci-lint: Repair warnings and errors resulting from linting (#1044) @umarcor
+
+## v1.1.1
+
+* **Fix:** yaml.v2 2.3.0 contained a unintended breaking change. This release reverts to yaml.v2 v2.2.8 which has recent critical CVE fixes, but does not have the breaking changes. See https://github.com/spf13/cobra/pull/1259 for context.
+* **Fix:** correct internal formatting for go-md2man v2 (which caused man page generation to be broken). See https://github.com/spf13/cobra/issues/1049 for context.
+
+## v1.1.0
+
+### Notable Changes
+
+* Extend Go completions and revamp zsh comp (#1070)
+* Fix man page doc generation - no auto generated tag when `cmd.DisableAutoGenTag = true` (#1104) @jpmcb
+* Add completion for help command (#1136)
+* Complete subcommands when TraverseChildren is set (#1171)
+* Fix stderr printing functions (#894)
+* fix: fish output redirection (#1247)
+
+## v1.0.0
+
+Announcing v1.0.0 of Cobra. 🎉
+
+### Notable Changes
+* Fish completion (including support for Go custom completion) @marckhouzam
+* API (urgent): Rename BashCompDirectives to ShellCompDirectives @marckhouzam
+* Remove/replace SetOutput on Command - deprecated @jpmcb
+* add support for autolabel stale PR @xchapter7x
+* Add Labeler Actions @xchapter7x
+* Custom completions coded in Go (instead of Bash) @marckhouzam
+* Partial Revert of #922 @jharshman
+* Add Makefile to project @jharshman
+* Correct documentation for InOrStdin @desponda
+* Apply formatting to templates @jharshman
+* Revert change so help is printed on stdout again @marckhouzam
+* Update md2man to v2.0.0 @pdf
+* update viper to v1.4.0 @umarcor
+* Update cmd/root.go example in README.md @jharshman
diff --git a/vendor/github.com/spf13/cobra/README.md b/vendor/github.com/spf13/cobra/README.md
index 2bf15208..7adef143 100644
--- a/vendor/github.com/spf13/cobra/README.md
+++ b/vendor/github.com/spf13/cobra/README.md
@@ -2,7 +2,7 @@
 
 Cobra is a library for creating powerful modern CLI applications.
 
-Cobra is used in many Go projects such as [Kubernetes](https://kubernetes.io/),
+Cobra is used in many Go projects such as [Kubernetes](http://kubernetes.io/),
 [Hugo](https://gohugo.io), and [Github CLI](https://github.com/cli/cli) to
 name a few. [This list](./projects_using_cobra.md) contains a more extensive list of projects using Cobra.
 
@@ -28,7 +28,7 @@ Cobra provides:
 * Automatically generated man pages for your application
 * Command aliases so you can change things without breaking them
 * The flexibility to define your own help, usage, etc.
-* Optional seamless integration with [viper](https://github.com/spf13/viper) for 12-factor apps
+* Optional seamless integration with [viper](http://github.com/spf13/viper) for 12-factor apps
 
 # Concepts
 
@@ -102,7 +102,7 @@ It can be installed by running:
 go install github.com/spf13/cobra-cli@latest
 ```
 
-For complete details on using the Cobra-CLI generator, please read [The Cobra Generator README](https://github.com/spf13/cobra-cli/blob/main/README.md)
+For complete details on using the Cobra-CLI generator, please read [The Cobra Generator README](https://github.com/spf13/cobra-cli/blob/master/README.md)
 
 For complete details on using the Cobra library, please read the [The Cobra User Guide](user_guide.md).
 
diff --git a/vendor/github.com/spf13/cobra/active_help.go b/vendor/github.com/spf13/cobra/active_help.go
deleted file mode 100644
index 0c631913..00000000
--- a/vendor/github.com/spf13/cobra/active_help.go
+++ /dev/null
@@ -1,49 +0,0 @@
-package cobra
-
-import (
-	"fmt"
-	"os"
-	"strings"
-)
-
-const (
-	activeHelpMarker = "_activeHelp_ "
-	// The below values should not be changed: programs will be using them explicitly
-	// in their user documentation, and users will be using them explicitly.
-	activeHelpEnvVarSuffix  = "_ACTIVE_HELP"
-	activeHelpGlobalEnvVar  = "COBRA_ACTIVE_HELP"
-	activeHelpGlobalDisable = "0"
-)
-
-// AppendActiveHelp adds the specified string to the specified array to be used as ActiveHelp.
-// Such strings will be processed by the completion script and will be shown as ActiveHelp
-// to the user.
-// The array parameter should be the array that will contain the completions.
-// This function can be called multiple times before and/or after completions are added to
-// the array.  Each time this function is called with the same array, the new
-// ActiveHelp line will be shown below the previous ones when completion is triggered.
-func AppendActiveHelp(compArray []string, activeHelpStr string) []string {
-	return append(compArray, fmt.Sprintf("%s%s", activeHelpMarker, activeHelpStr))
-}
-
-// GetActiveHelpConfig returns the value of the ActiveHelp environment variable
-// <PROGRAM>_ACTIVE_HELP where <PROGRAM> is the name of the root command in upper
-// case, with all - replaced by _.
-// It will always return "0" if the global environment variable COBRA_ACTIVE_HELP
-// is set to "0".
-func GetActiveHelpConfig(cmd *Command) string {
-	activeHelpCfg := os.Getenv(activeHelpGlobalEnvVar)
-	if activeHelpCfg != activeHelpGlobalDisable {
-		activeHelpCfg = os.Getenv(activeHelpEnvVar(cmd.Root().Name()))
-	}
-	return activeHelpCfg
-}
-
-// activeHelpEnvVar returns the name of the program-specific ActiveHelp environment
-// variable.  It has the format <PROGRAM>_ACTIVE_HELP where <PROGRAM> is the name of the
-// root command in upper case, with all - replaced by _.
-func activeHelpEnvVar(name string) string {
-	// This format should not be changed: users will be using it explicitly.
-	activeHelpEnvVar := strings.ToUpper(fmt.Sprintf("%s%s", name, activeHelpEnvVarSuffix))
-	return strings.ReplaceAll(activeHelpEnvVar, "-", "_")
-}
diff --git a/vendor/github.com/spf13/cobra/active_help.md b/vendor/github.com/spf13/cobra/active_help.md
deleted file mode 100644
index 5e7f59af..00000000
--- a/vendor/github.com/spf13/cobra/active_help.md
+++ /dev/null
@@ -1,157 +0,0 @@
-# Active Help
-
-Active Help is a framework provided by Cobra which allows a program to define messages (hints, warnings, etc) that will be printed during program usage.  It aims to make it easier for your users to learn how to use your program.  If configured by the program, Active Help is printed when the user triggers shell completion.
-
-For example, 
-```
-bash-5.1$ helm repo add [tab]
-You must choose a name for the repo you are adding.
-
-bash-5.1$ bin/helm package [tab]
-Please specify the path to the chart to package
-
-bash-5.1$ bin/helm package [tab][tab]
-bin/    internal/    scripts/    pkg/     testdata/
-```
-
-**Hint**: A good place to use Active Help messages is when the normal completion system does not provide any suggestions. In such cases, Active Help nicely supplements the normal shell completions to guide the user in knowing what is expected by the program.
-## Supported shells
-
-Active Help is currently only supported for the following shells:
-- Bash (using [bash completion V2](shell_completions.md#bash-completion-v2) only). Note that bash 4.4 or higher is required for the prompt to appear when an Active Help message is printed.
-- Zsh
-
-## Adding Active Help messages
-
-As Active Help uses the shell completion system, the implementation of Active Help messages is done by enhancing custom dynamic completions.  If you are not familiar with dynamic completions, please refer to [Shell Completions](shell_completions.md).
-
-Adding Active Help is done through the use of the `cobra.AppendActiveHelp(...)` function, where the program repeatedly adds Active Help messages to the list of completions.  Keep reading for details.
-
-### Active Help for nouns
-
-Adding Active Help when completing a noun is done within the `ValidArgsFunction(...)` of a command.  Please notice the use of `cobra.AppendActiveHelp(...)` in the following example:
-
-```go
-cmd := &cobra.Command{
-	Use:   "add [NAME] [URL]",
-	Short: "add a chart repository",
-	Args:  require.ExactArgs(2),
-	RunE: func(cmd *cobra.Command, args []string) error {
-		return addRepo(args)
-	},
-	ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
-		var comps []string
-		if len(args) == 0 {
-			comps = cobra.AppendActiveHelp(comps, "You must choose a name for the repo you are adding")
-		} else if len(args) == 1 {
-			comps = cobra.AppendActiveHelp(comps, "You must specify the URL for the repo you are adding")
-		} else {
-			comps = cobra.AppendActiveHelp(comps, "This command does not take any more arguments")
-		}
-		return comps, cobra.ShellCompDirectiveNoFileComp
-	},
-}
-```
-The example above defines the completions (none, in this specific example) as well as the Active Help messages for the `helm repo add` command.  It yields the following behavior:
-```
-bash-5.1$ helm repo add [tab]
-You must choose a name for the repo you are adding
-
-bash-5.1$ helm repo add grafana [tab]
-You must specify the URL for the repo you are adding
-
-bash-5.1$ helm repo add grafana https://grafana.github.io/helm-charts [tab]
-This command does not take any more arguments
-```
-**Hint**: As can be seen in the above example, a good place to use Active Help messages is when the normal completion system does not provide any suggestions. In such cases, Active Help nicely supplements the normal shell completions.
-
-### Active Help for flags
-
-Providing Active Help for flags is done in the same fashion as for nouns, but using the completion function registered for the flag.  For example:
-```go
-_ = cmd.RegisterFlagCompletionFunc("version", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
-		if len(args) != 2 {
-			return cobra.AppendActiveHelp(nil, "You must first specify the chart to install before the --version flag can be completed"), cobra.ShellCompDirectiveNoFileComp
-		}
-		return compVersionFlag(args[1], toComplete)
-	})
-```
-The example above prints an Active Help message when not enough information was given by the user to complete the `--version` flag.
-```
-bash-5.1$ bin/helm install myrelease --version 2.0.[tab]
-You must first specify the chart to install before the --version flag can be completed
-
-bash-5.1$ bin/helm install myrelease bitnami/solr --version 2.0.[tab][tab]
-2.0.1  2.0.2  2.0.3
-```
-
-## User control of Active Help
-
-You may want to allow your users to disable Active Help or choose between different levels of Active Help.  It is entirely up to the program to define the type of configurability of Active Help that it wants to offer, if any.
-Allowing to configure Active Help is entirely optional; you can use Active Help in your program without doing anything about Active Help configuration.
-
-The way to configure Active Help is to use the program's Active Help environment
-variable.  That variable is named `<PROGRAM>_ACTIVE_HELP` where `<PROGRAM>` is the name of your 
-program in uppercase with any `-` replaced by an `_`.  The variable should be set by the user to whatever
-Active Help configuration values are supported by the program.
-
-For example, say `helm` has chosen to support three levels for Active Help: `on`, `off`, `local`.  Then a user
-would set the desired behavior to `local` by doing `export HELM_ACTIVE_HELP=local` in their shell.
-
-For simplicity, when in `cmd.ValidArgsFunction(...)` or a flag's completion function, the program should read the
-Active Help configuration using the `cobra.GetActiveHelpConfig(cmd)` function and select what Active Help messages
-should or should not be added (instead of reading the environment variable directly).
-
-For example:
-```go
-ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
-	activeHelpLevel := cobra.GetActiveHelpConfig(cmd)
-
-	var comps []string
-	if len(args) == 0 {
-		if activeHelpLevel != "off"  {
-			comps = cobra.AppendActiveHelp(comps, "You must choose a name for the repo you are adding")
-		}
-	} else if len(args) == 1 {
-		if activeHelpLevel != "off" {
-			comps = cobra.AppendActiveHelp(comps, "You must specify the URL for the repo you are adding")
-		}
-	} else {
-		if activeHelpLevel == "local" {
-			comps = cobra.AppendActiveHelp(comps, "This command does not take any more arguments")
-		}
-	}
-	return comps, cobra.ShellCompDirectiveNoFileComp
-},
-```
-**Note 1**: If the `<PROGRAM>_ACTIVE_HELP` environment variable is set to the string "0", Cobra will automatically disable all Active Help output (even if some output was specified by the program using the `cobra.AppendActiveHelp(...)` function).  Using "0" can simplify your code in situations where you want to blindly disable Active Help without having to call `cobra.GetActiveHelpConfig(cmd)` explicitly.
-
-**Note 2**: If a user wants to disable Active Help for every single program based on Cobra, she can set the environment variable `COBRA_ACTIVE_HELP` to "0".  In this case `cobra.GetActiveHelpConfig(cmd)` will return "0" no matter what the variable `<PROGRAM>_ACTIVE_HELP` is set to.
-
-**Note 3**: If the user does not set `<PROGRAM>_ACTIVE_HELP` or `COBRA_ACTIVE_HELP` (which will be a common case), the default value for the Active Help configuration returned by `cobra.GetActiveHelpConfig(cmd)` will be the empty string. 
-## Active Help with Cobra's default completion command
-
-Cobra provides a default `completion` command for programs that wish to use it.
-When using the default `completion` command, Active Help is configurable in the same
-fashion as described above using environment variables.  You may wish to document this in more
-details for your users.
-
-## Debugging Active Help
-
-Debugging your Active Help code is done in the same way as debugging your dynamic completion code, which is with Cobra's hidden `__complete` command.  Please refer to [debugging shell completion](shell_completions.md#debugging) for details.
-
-When debugging with the `__complete` command, if you want to specify different Active Help configurations, you should use the active help environment variable.  That variable is named `<PROGRAM>_ACTIVE_HELP` where any `-` is replaced by an `_`.  For example, we can test deactivating some Active Help as shown below:
-```
-$ HELM_ACTIVE_HELP=1 bin/helm __complete install wordpress bitnami/h<ENTER>
-bitnami/haproxy
-bitnami/harbor
-_activeHelp_ WARNING: cannot re-use a name that is still in use
-:0
-Completion ended with directive: ShellCompDirectiveDefault
-
-$ HELM_ACTIVE_HELP=0 bin/helm __complete install wordpress bitnami/h<ENTER>
-bitnami/haproxy
-bitnami/harbor
-:0
-Completion ended with directive: ShellCompDirectiveDefault
-```
diff --git a/vendor/github.com/spf13/cobra/bash_completions.go b/vendor/github.com/spf13/cobra/bash_completions.go
index cb7e1953..6c360c59 100644
--- a/vendor/github.com/spf13/cobra/bash_completions.go
+++ b/vendor/github.com/spf13/cobra/bash_completions.go
@@ -73,8 +73,7 @@ __%[1]s_handle_go_custom_completion()
     # Prepare the command to request completions for the program.
     # Calling ${words[0]} instead of directly %[1]s allows to handle aliases
     args=("${words[@]:1}")
-    # Disable ActiveHelp which is not supported for bash completion v1
-    requestComp="%[8]s=0 ${words[0]} %[2]s ${args[*]}"
+    requestComp="${words[0]} %[2]s ${args[*]}"
 
     lastParam=${words[$((${#words[@]}-1))]}
     lastChar=${lastParam:$((${#lastParam}-1)):1}
@@ -100,7 +99,7 @@ __%[1]s_handle_go_custom_completion()
         directive=0
     fi
     __%[1]s_debug "${FUNCNAME[0]}: the completion directive is: ${directive}"
-    __%[1]s_debug "${FUNCNAME[0]}: the completions are: ${out}"
+    __%[1]s_debug "${FUNCNAME[0]}: the completions are: ${out[*]}"
 
     if [ $((directive & shellCompDirectiveError)) -ne 0 ]; then
         # Error code.  No completion.
@@ -126,7 +125,7 @@ __%[1]s_handle_go_custom_completion()
         local fullFilter filter filteringCmd
         # Do not use quotes around the $out variable or else newline
         # characters will be kept.
-        for filter in ${out}; do
+        for filter in ${out[*]}; do
             fullFilter+="$filter|"
         done
 
@@ -137,7 +136,7 @@ __%[1]s_handle_go_custom_completion()
         # File completion for directories only
         local subdir
         # Use printf to strip any trailing newline
-        subdir=$(printf "%%s" "${out}")
+        subdir=$(printf "%%s" "${out[0]}")
         if [ -n "$subdir" ]; then
             __%[1]s_debug "Listing directories in $subdir"
             __%[1]s_handle_subdirs_in_dir_flag "$subdir"
@@ -148,7 +147,7 @@ __%[1]s_handle_go_custom_completion()
     else
         while IFS='' read -r comp; do
             COMPREPLY+=("$comp")
-        done < <(compgen -W "${out}" -- "$cur")
+        done < <(compgen -W "${out[*]}" -- "$cur")
     fi
 }
 
@@ -384,11 +383,11 @@ __%[1]s_handle_word()
 
 `, name, ShellCompNoDescRequestCmd,
 		ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp,
-		ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, activeHelpEnvVar(name)))
+		ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs))
 }
 
 func writePostscript(buf io.StringWriter, name string) {
-	name = strings.ReplaceAll(name, ":", "__")
+	name = strings.Replace(name, ":", "__", -1)
 	WriteStringAndCheck(buf, fmt.Sprintf("__start_%s()\n", name))
 	WriteStringAndCheck(buf, fmt.Sprintf(`{
     local cur prev words cword split
@@ -646,8 +645,8 @@ func gen(buf io.StringWriter, cmd *Command) {
 		gen(buf, c)
 	}
 	commandName := cmd.CommandPath()
-	commandName = strings.ReplaceAll(commandName, " ", "_")
-	commandName = strings.ReplaceAll(commandName, ":", "__")
+	commandName = strings.Replace(commandName, " ", "_", -1)
+	commandName = strings.Replace(commandName, ":", "__", -1)
 
 	if cmd.Root() == cmd {
 		WriteStringAndCheck(buf, fmt.Sprintf("_%s_root_command()\n{\n", commandName))
diff --git a/vendor/github.com/spf13/cobra/bash_completionsV2.go b/vendor/github.com/spf13/cobra/bash_completionsV2.go
index 767bf031..82d26c17 100644
--- a/vendor/github.com/spf13/cobra/bash_completionsV2.go
+++ b/vendor/github.com/spf13/cobra/bash_completionsV2.go
@@ -78,7 +78,7 @@ __%[1]s_get_completion_results() {
         directive=0
     fi
     __%[1]s_debug "The completion directive is: ${directive}"
-    __%[1]s_debug "The completions are: ${out}"
+    __%[1]s_debug "The completions are: ${out[*]}"
 }
 
 __%[1]s_process_completion_results() {
@@ -111,18 +111,13 @@ __%[1]s_process_completion_results() {
         fi
     fi
 
-    # Separate activeHelp from normal completions
-    local completions=()
-    local activeHelp=()
-    __%[1]s_extract_activeHelp
-
     if [ $((directive & shellCompDirectiveFilterFileExt)) -ne 0 ]; then
         # File extension filtering
         local fullFilter filter filteringCmd
 
-        # Do not use quotes around the $completions variable or else newline
+        # Do not use quotes around the $out variable or else newline
         # characters will be kept.
-        for filter in ${completions[*]}; do
+        for filter in ${out[*]}; do
             fullFilter+="$filter|"
         done
 
@@ -134,7 +129,7 @@ __%[1]s_process_completion_results() {
 
         # Use printf to strip any trailing newline
         local subdir
-        subdir=$(printf "%%s" "${completions[0]}")
+        subdir=$(printf "%%s" "${out[0]}")
         if [ -n "$subdir" ]; then
             __%[1]s_debug "Listing directories in $subdir"
             pushd "$subdir" >/dev/null 2>&1 && _filedir -d && popd >/dev/null 2>&1 || return
@@ -148,43 +143,6 @@ __%[1]s_process_completion_results() {
 
     __%[1]s_handle_special_char "$cur" :
     __%[1]s_handle_special_char "$cur" =
-
-    # Print the activeHelp statements before we finish
-    if [ ${#activeHelp} -ne 0 ]; then
-        printf "\n";
-        printf "%%s\n" "${activeHelp[@]}"
-        printf "\n"
-
-        # The prompt format is only available from bash 4.4.
-        # We test if it is available before using it.
-        if (x=${PS1@P}) 2> /dev/null; then
-            printf "%%s" "${PS1@P}${COMP_LINE[@]}"
-        else
-            # Can't print the prompt.  Just print the
-            # text the user had typed, it is workable enough.
-            printf "%%s" "${COMP_LINE[@]}"
-        fi
-    fi
-}
-
-# Separate activeHelp lines from real completions.
-# Fills the $activeHelp and $completions arrays.
-__%[1]s_extract_activeHelp() {
-    local activeHelpMarker="%[8]s"
-    local endIndex=${#activeHelpMarker}
-
-    while IFS='' read -r comp; do
-        if [ "${comp:0:endIndex}" = "$activeHelpMarker" ]; then
-            comp=${comp:endIndex}
-            __%[1]s_debug "ActiveHelp found: $comp"
-            if [ -n "$comp" ]; then
-                activeHelp+=("$comp")
-            fi
-        else
-            # Not an activeHelp line but a normal completion
-            completions+=("$comp")
-        fi
-    done < <(printf "%%s\n" "${out}")
 }
 
 __%[1]s_handle_completion_types() {
@@ -196,16 +154,17 @@ __%[1]s_handle_completion_types() {
         # If the user requested inserting one completion at a time, or all
         # completions at once on the command-line we must remove the descriptions.
         # https://github.com/spf13/cobra/issues/1508
-        local tab=$'\t' comp
+        local tab comp
+        tab=$(printf '\t')
         while IFS='' read -r comp; do
-            [[ -z $comp ]] && continue
             # Strip any description
             comp=${comp%%%%$tab*}
             # Only consider the completions that match
-            if [[ $comp == "$cur"* ]]; then
+            comp=$(compgen -W "$comp" -- "$cur")
+            if [ -n "$comp" ]; then
                 COMPREPLY+=("$comp")
             fi
-        done < <(printf "%%s\n" "${completions[@]}")
+        done < <(printf "%%s\n" "${out[@]}")
         ;;
 
     *)
@@ -216,37 +175,44 @@ __%[1]s_handle_completion_types() {
 }
 
 __%[1]s_handle_standard_completion_case() {
-    local tab=$'\t' comp
-
-    # Short circuit to optimize if we don't have descriptions
-    if [[ "${completions[*]}" != *$tab* ]]; then
-        IFS=$'\n' read -ra COMPREPLY -d '' < <(compgen -W "${completions[*]}" -- "$cur")
-        return 0
-    fi
+    local tab comp
+    tab=$(printf '\t')
 
     local longest=0
-    local compline
     # Look for the longest completion so that we can format things nicely
-    while IFS='' read -r compline; do
-        [[ -z $compline ]] && continue
+    while IFS='' read -r comp; do
         # Strip any description before checking the length
-        comp=${compline%%%%$tab*}
+        comp=${comp%%%%$tab*}
         # Only consider the completions that match
-        [[ $comp == "$cur"* ]] || continue
-        COMPREPLY+=("$compline")
+        comp=$(compgen -W "$comp" -- "$cur")
         if ((${#comp}>longest)); then
             longest=${#comp}
         fi
-    done < <(printf "%%s\n" "${completions[@]}")
+    done < <(printf "%%s\n" "${out[@]}")
+
+    local completions=()
+    while IFS='' read -r comp; do
+        if [ -z "$comp" ]; then
+            continue
+        fi
+
+        __%[1]s_debug "Original comp: $comp"
+        comp="$(__%[1]s_format_comp_descriptions "$comp" "$longest")"
+        __%[1]s_debug "Final comp: $comp"
+        completions+=("$comp")
+    done < <(printf "%%s\n" "${out[@]}")
+
+    while IFS='' read -r comp; do
+        COMPREPLY+=("$comp")
+    done < <(compgen -W "${completions[*]}" -- "$cur")
 
     # If there is a single completion left, remove the description text
     if [ ${#COMPREPLY[*]} -eq 1 ]; then
         __%[1]s_debug "COMPREPLY[0]: ${COMPREPLY[0]}"
-        comp="${COMPREPLY[0]%%%%$tab*}"
+        comp="${COMPREPLY[0]%%%% *}"
         __%[1]s_debug "Removed description from single completion, which is now: ${comp}"
-        COMPREPLY[0]=$comp
-    else # Format the descriptions
-        __%[1]s_format_comp_descriptions $longest
+        COMPREPLY=()
+        COMPREPLY+=("$comp")
     fi
 }
 
@@ -265,48 +231,45 @@ __%[1]s_handle_special_char()
 
 __%[1]s_format_comp_descriptions()
 {
-    local tab=$'\t'
-    local comp desc maxdesclength
-    local longest=$1
-
-    local i ci
-    for ci in ${!COMPREPLY[*]}; do
-        comp=${COMPREPLY[ci]}
-        # Properly format the description string which follows a tab character if there is one
-        if [[ "$comp" == *$tab* ]]; then
-            __%[1]s_debug "Original comp: $comp"
-            desc=${comp#*$tab}
-            comp=${comp%%%%$tab*}
-
-            # $COLUMNS stores the current shell width.
-            # Remove an extra 4 because we add 2 spaces and 2 parentheses.
-            maxdesclength=$(( COLUMNS - longest - 4 ))
-
-            # Make sure we can fit a description of at least 8 characters
-            # if we are to align the descriptions.
-            if [[ $maxdesclength -gt 8 ]]; then
-                # Add the proper number of spaces to align the descriptions
-                for ((i = ${#comp} ; i < longest ; i++)); do
-                    comp+=" "
-                done
-            else
-                # Don't pad the descriptions so we can fit more text after the completion
-                maxdesclength=$(( COLUMNS - ${#comp} - 4 ))
-            fi
+    local tab
+    tab=$(printf '\t')
+    local comp="$1"
+    local longest=$2
+
+    # Properly format the description string which follows a tab character if there is one
+    if [[ "$comp" == *$tab* ]]; then
+        desc=${comp#*$tab}
+        comp=${comp%%%%$tab*}
+
+        # $COLUMNS stores the current shell width.
+        # Remove an extra 4 because we add 2 spaces and 2 parentheses.
+        maxdesclength=$(( COLUMNS - longest - 4 ))
+
+        # Make sure we can fit a description of at least 8 characters
+        # if we are to align the descriptions.
+        if [[ $maxdesclength -gt 8 ]]; then
+            # Add the proper number of spaces to align the descriptions
+            for ((i = ${#comp} ; i < longest ; i++)); do
+                comp+=" "
+            done
+        else
+            # Don't pad the descriptions so we can fit more text after the completion
+            maxdesclength=$(( COLUMNS - ${#comp} - 4 ))
+        fi
 
-            # If there is enough space for any description text,
-            # truncate the descriptions that are too long for the shell width
-            if [ $maxdesclength -gt 0 ]; then
-                if [ ${#desc} -gt $maxdesclength ]; then
-                    desc=${desc:0:$(( maxdesclength - 1 ))}
-                    desc+="…"
-                fi
-                comp+="  ($desc)"
+        # If there is enough space for any description text,
+        # truncate the descriptions that are too long for the shell width
+        if [ $maxdesclength -gt 0 ]; then
+            if [ ${#desc} -gt $maxdesclength ]; then
+                desc=${desc:0:$(( maxdesclength - 1 ))}
+                desc+="…"
             fi
-            COMPREPLY[ci]=$comp
-            __%[1]s_debug "Final comp: $comp"
+            comp+="  ($desc)"
         fi
-    done
+    fi
+
+    # Must use printf to escape all special characters
+    printf "%%q" "${comp}"
 }
 
 __start_%[1]s()
@@ -347,8 +310,7 @@ fi
 # ex: ts=4 sw=4 et filetype=sh
 `, name, compCmd,
 		ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp,
-		ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs,
-		activeHelpMarker))
+		ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs))
 }
 
 // GenBashCompletionFileV2 generates Bash completion version 2.
diff --git a/vendor/github.com/spf13/cobra/command.go b/vendor/github.com/spf13/cobra/command.go
index 675bb134..2cc18891 100644
--- a/vendor/github.com/spf13/cobra/command.go
+++ b/vendor/github.com/spf13/cobra/command.go
@@ -18,7 +18,6 @@ package cobra
 import (
 	"bytes"
 	"context"
-	"errors"
 	"fmt"
 	"io"
 	"os"
@@ -167,7 +166,7 @@ type Command struct {
 	// errWriter is a writer defined by the user that replaces stderr
 	errWriter io.Writer
 
-	// FParseErrWhitelist flag parse errors to be ignored
+	//FParseErrWhitelist flag parse errors to be ignored
 	FParseErrWhitelist FParseErrWhitelist
 
 	// CompletionOptions is a set of options to control the handling of shell completion
@@ -225,23 +224,12 @@ type Command struct {
 	SuggestionsMinimumDistance int
 }
 
-// Context returns underlying command context. If command was executed
-// with ExecuteContext or the context was set with SetContext, the
-// previously set context will be returned. Otherwise, nil is returned.
-//
-// Notice that a call to Execute and ExecuteC will replace a nil context of
-// a command with a context.Background, so a background context will be
-// returned by Context after one of these functions has been called.
+// Context returns underlying command context. If command wasn't
+// executed with ExecuteContext Context returns Background context.
 func (c *Command) Context() context.Context {
 	return c.ctx
 }
 
-// SetContext sets context for the command. It is set to context.Background by default and will be overwritten by
-// Command.ExecuteContext or Command.ExecuteContextC
-func (c *Command) SetContext(ctx context.Context) {
-	c.ctx = ctx
-}
-
 // SetArgs sets arguments for the command. It is set to os.Args[1:] by default, if desired, can be overridden
 // particularly useful when testing.
 func (c *Command) SetArgs(a []string) {
@@ -864,10 +852,6 @@ func (c *Command) execute(a []string) (err error) {
 	if err := c.validateRequiredFlags(); err != nil {
 		return err
 	}
-	if err := c.validateFlagGroups(); err != nil {
-		return err
-	}
-
 	if c.RunE != nil {
 		if err := c.RunE(c, argWoFlags); err != nil {
 			return err
@@ -991,7 +975,7 @@ func (c *Command) ExecuteC() (cmd *Command, err error) {
 	if err != nil {
 		// Always show help if requested, even if SilenceErrors is in
 		// effect
-		if errors.Is(err, flag.ErrHelp) {
+		if err == flag.ErrHelp {
 			cmd.HelpFunc()(cmd, args)
 			return cmd, nil
 		}
@@ -1013,7 +997,7 @@ func (c *Command) ExecuteC() (cmd *Command, err error) {
 
 func (c *Command) ValidateArgs(args []string) error {
 	if c.Args == nil {
-		return ArbitraryArgs(c, args)
+		return nil
 	}
 	return c.Args(c, args)
 }
diff --git a/vendor/github.com/spf13/cobra/completions.go b/vendor/github.com/spf13/cobra/completions.go
index 2c248399..9ecd56a4 100644
--- a/vendor/github.com/spf13/cobra/completions.go
+++ b/vendor/github.com/spf13/cobra/completions.go
@@ -103,14 +103,6 @@ func NoFileCompletions(cmd *Command, args []string, toComplete string) ([]string
 	return nil, ShellCompDirectiveNoFileComp
 }
 
-// FixedCompletions can be used to create a completion function which always
-// returns the same results.
-func FixedCompletions(choices []string, directive ShellCompDirective) func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
-	return func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
-		return choices, directive
-	}
-}
-
 // RegisterFlagCompletionFunc should be called to register a function to provide completion for a flag.
 func (c *Command) RegisterFlagCompletionFunc(flagName string, f func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective)) error {
 	flag := c.Flag(flagName)
@@ -178,12 +170,6 @@ func (c *Command) initCompleteCmd(args []string) {
 
 			noDescriptions := (cmd.CalledAs() == ShellCompNoDescRequestCmd)
 			for _, comp := range completions {
-				if GetActiveHelpConfig(finalCmd) == activeHelpGlobalDisable {
-					// Remove all activeHelp entries in this case
-					if strings.HasPrefix(comp, activeHelpMarker) {
-						continue
-					}
-				}
 				if noDescriptions {
 					// Remove any description that may be included following a tab character.
 					comp = strings.Split(comp, "\t")[0]
@@ -325,11 +311,8 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi
 	var completions []string
 	var directive ShellCompDirective
 
-	// Enforce flag groups before doing flag completions
-	finalCmd.enforceFlagGroupsForCompletion()
-
 	// Note that we want to perform flagname completion even if finalCmd.DisableFlagParsing==true;
-	// doing this allows for completion of persistent flag names even for commands that disable flag parsing.
+	// doing this allows for completion of persistant flag names even for commands that disable flag parsing.
 	//
 	// When doing completion of a flag name, as soon as an argument starts with
 	// a '-' we know it is a flag.  We cannot use isFlagArg() here as it requires
@@ -661,7 +644,7 @@ To load completions for every new session, execute once:
 
 #### macOS:
 
-	%[1]s completion bash > $(brew --prefix)/etc/bash_completion.d/%[1]s
+	%[1]s completion bash > /usr/local/etc/bash_completion.d/%[1]s
 
 You will need to start a new shell for this setup to take effect.
 `, c.Root().Name()),
@@ -686,10 +669,6 @@ to enable it.  You can execute the following once:
 
 	echo "autoload -U compinit; compinit" >> ~/.zshrc
 
-To load completions in your current shell session:
-
-	source <(%[1]s completion zsh); compdef _%[1]s %[1]s
-
 To load completions for every new session, execute once:
 
 #### Linux:
@@ -698,7 +677,7 @@ To load completions for every new session, execute once:
 
 #### macOS:
 
-	%[1]s completion zsh > $(brew --prefix)/share/zsh/site-functions/_%[1]s
+	%[1]s completion zsh > /usr/local/share/zsh/site-functions/_%[1]s
 
 You will need to start a new shell for this setup to take effect.
 `, c.Root().Name()),
diff --git a/vendor/github.com/spf13/cobra/fish_completions.go b/vendor/github.com/spf13/cobra/fish_completions.go
index 005ee6be..bb57fd56 100644
--- a/vendor/github.com/spf13/cobra/fish_completions.go
+++ b/vendor/github.com/spf13/cobra/fish_completions.go
@@ -11,8 +11,8 @@ import (
 func genFishComp(buf io.StringWriter, name string, includeDesc bool) {
 	// Variables should not contain a '-' or ':' character
 	nameForVar := name
-	nameForVar = strings.ReplaceAll(nameForVar, "-", "_")
-	nameForVar = strings.ReplaceAll(nameForVar, ":", "_")
+	nameForVar = strings.Replace(nameForVar, "-", "_", -1)
+	nameForVar = strings.Replace(nameForVar, ":", "_", -1)
 
 	compCmd := ShellCompRequestCmd
 	if !includeDesc {
@@ -38,8 +38,7 @@ function __%[1]s_perform_completion
     __%[1]s_debug "args: $args"
     __%[1]s_debug "last arg: $lastArg"
 
-    # Disable ActiveHelp which is not supported for fish shell
-    set -l requestComp "%[9]s=0 $args[1] %[3]s $args[2..-1] $lastArg"
+    set -l requestComp "$args[1] %[3]s $args[2..-1] $lastArg"
 
     __%[1]s_debug "Calling $requestComp"
     set -l results (eval $requestComp 2> /dev/null)
@@ -197,7 +196,7 @@ complete -c %[2]s -n '__%[1]s_prepare_completions' -f -a '$__%[1]s_comp_results'
 
 `, nameForVar, name, compCmd,
 		ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp,
-		ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, activeHelpEnvVar(name)))
+		ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs))
 }
 
 // GenFishCompletion generates fish completion file and writes to the passed writer.
diff --git a/vendor/github.com/spf13/cobra/flag_groups.go b/vendor/github.com/spf13/cobra/flag_groups.go
deleted file mode 100644
index dc784311..00000000
--- a/vendor/github.com/spf13/cobra/flag_groups.go
+++ /dev/null
@@ -1,223 +0,0 @@
-// Copyright © 2022 Steve Francia <spf@spf13.com>.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// 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.
-
-package cobra
-
-import (
-	"fmt"
-	"sort"
-	"strings"
-
-	flag "github.com/spf13/pflag"
-)
-
-const (
-	requiredAsGroup   = "cobra_annotation_required_if_others_set"
-	mutuallyExclusive = "cobra_annotation_mutually_exclusive"
-)
-
-// MarkFlagsRequiredTogether marks the given flags with annotations so that Cobra errors
-// if the command is invoked with a subset (but not all) of the given flags.
-func (c *Command) MarkFlagsRequiredTogether(flagNames ...string) {
-	c.mergePersistentFlags()
-	for _, v := range flagNames {
-		f := c.Flags().Lookup(v)
-		if f == nil {
-			panic(fmt.Sprintf("Failed to find flag %q and mark it as being required in a flag group", v))
-		}
-		if err := c.Flags().SetAnnotation(v, requiredAsGroup, append(f.Annotations[requiredAsGroup], strings.Join(flagNames, " "))); err != nil {
-			// Only errs if the flag isn't found.
-			panic(err)
-		}
-	}
-}
-
-// MarkFlagsMutuallyExclusive marks the given flags with annotations so that Cobra errors
-// if the command is invoked with more than one flag from the given set of flags.
-func (c *Command) MarkFlagsMutuallyExclusive(flagNames ...string) {
-	c.mergePersistentFlags()
-	for _, v := range flagNames {
-		f := c.Flags().Lookup(v)
-		if f == nil {
-			panic(fmt.Sprintf("Failed to find flag %q and mark it as being in a mutually exclusive flag group", v))
-		}
-		// Each time this is called is a single new entry; this allows it to be a member of multiple groups if needed.
-		if err := c.Flags().SetAnnotation(v, mutuallyExclusive, append(f.Annotations[mutuallyExclusive], strings.Join(flagNames, " "))); err != nil {
-			panic(err)
-		}
-	}
-}
-
-// validateFlagGroups validates the mutuallyExclusive/requiredAsGroup logic and returns the
-// first error encountered.
-func (c *Command) validateFlagGroups() error {
-	if c.DisableFlagParsing {
-		return nil
-	}
-
-	flags := c.Flags()
-
-	// groupStatus format is the list of flags as a unique ID,
-	// then a map of each flag name and whether it is set or not.
-	groupStatus := map[string]map[string]bool{}
-	mutuallyExclusiveGroupStatus := map[string]map[string]bool{}
-	flags.VisitAll(func(pflag *flag.Flag) {
-		processFlagForGroupAnnotation(flags, pflag, requiredAsGroup, groupStatus)
-		processFlagForGroupAnnotation(flags, pflag, mutuallyExclusive, mutuallyExclusiveGroupStatus)
-	})
-
-	if err := validateRequiredFlagGroups(groupStatus); err != nil {
-		return err
-	}
-	if err := validateExclusiveFlagGroups(mutuallyExclusiveGroupStatus); err != nil {
-		return err
-	}
-	return nil
-}
-
-func hasAllFlags(fs *flag.FlagSet, flagnames ...string) bool {
-	for _, fname := range flagnames {
-		f := fs.Lookup(fname)
-		if f == nil {
-			return false
-		}
-	}
-	return true
-}
-
-func processFlagForGroupAnnotation(flags *flag.FlagSet, pflag *flag.Flag, annotation string, groupStatus map[string]map[string]bool) {
-	groupInfo, found := pflag.Annotations[annotation]
-	if found {
-		for _, group := range groupInfo {
-			if groupStatus[group] == nil {
-				flagnames := strings.Split(group, " ")
-
-				// Only consider this flag group at all if all the flags are defined.
-				if !hasAllFlags(flags, flagnames...) {
-					continue
-				}
-
-				groupStatus[group] = map[string]bool{}
-				for _, name := range flagnames {
-					groupStatus[group][name] = false
-				}
-			}
-
-			groupStatus[group][pflag.Name] = pflag.Changed
-		}
-	}
-}
-
-func validateRequiredFlagGroups(data map[string]map[string]bool) error {
-	keys := sortedKeys(data)
-	for _, flagList := range keys {
-		flagnameAndStatus := data[flagList]
-
-		unset := []string{}
-		for flagname, isSet := range flagnameAndStatus {
-			if !isSet {
-				unset = append(unset, flagname)
-			}
-		}
-		if len(unset) == len(flagnameAndStatus) || len(unset) == 0 {
-			continue
-		}
-
-		// Sort values, so they can be tested/scripted against consistently.
-		sort.Strings(unset)
-		return fmt.Errorf("if any flags in the group [%v] are set they must all be set; missing %v", flagList, unset)
-	}
-
-	return nil
-}
-
-func validateExclusiveFlagGroups(data map[string]map[string]bool) error {
-	keys := sortedKeys(data)
-	for _, flagList := range keys {
-		flagnameAndStatus := data[flagList]
-		var set []string
-		for flagname, isSet := range flagnameAndStatus {
-			if isSet {
-				set = append(set, flagname)
-			}
-		}
-		if len(set) == 0 || len(set) == 1 {
-			continue
-		}
-
-		// Sort values, so they can be tested/scripted against consistently.
-		sort.Strings(set)
-		return fmt.Errorf("if any flags in the group [%v] are set none of the others can be; %v were all set", flagList, set)
-	}
-	return nil
-}
-
-func sortedKeys(m map[string]map[string]bool) []string {
-	keys := make([]string, len(m))
-	i := 0
-	for k := range m {
-		keys[i] = k
-		i++
-	}
-	sort.Strings(keys)
-	return keys
-}
-
-// enforceFlagGroupsForCompletion will do the following:
-// - when a flag in a group is present, other flags in the group will be marked required
-// - when a flag in a mutually exclusive group is present, other flags in the group will be marked as hidden
-// This allows the standard completion logic to behave appropriately for flag groups
-func (c *Command) enforceFlagGroupsForCompletion() {
-	if c.DisableFlagParsing {
-		return
-	}
-
-	flags := c.Flags()
-	groupStatus := map[string]map[string]bool{}
-	mutuallyExclusiveGroupStatus := map[string]map[string]bool{}
-	c.Flags().VisitAll(func(pflag *flag.Flag) {
-		processFlagForGroupAnnotation(flags, pflag, requiredAsGroup, groupStatus)
-		processFlagForGroupAnnotation(flags, pflag, mutuallyExclusive, mutuallyExclusiveGroupStatus)
-	})
-
-	// If a flag that is part of a group is present, we make all the other flags
-	// of that group required so that the shell completion suggests them automatically
-	for flagList, flagnameAndStatus := range groupStatus {
-		for _, isSet := range flagnameAndStatus {
-			if isSet {
-				// One of the flags of the group is set, mark the other ones as required
-				for _, fName := range strings.Split(flagList, " ") {
-					_ = c.MarkFlagRequired(fName)
-				}
-			}
-		}
-	}
-
-	// If a flag that is mutually exclusive to others is present, we hide the other
-	// flags of that group so the shell completion does not suggest them
-	for flagList, flagnameAndStatus := range mutuallyExclusiveGroupStatus {
-		for flagName, isSet := range flagnameAndStatus {
-			if isSet {
-				// One of the flags of the mutually exclusive group is set, mark the other ones as hidden
-				// Don't mark the flag that is already set as hidden because it may be an
-				// array or slice flag and therefore must continue being suggested
-				for _, fName := range strings.Split(flagList, " ") {
-					if fName != flagName {
-						flag := c.Flags().Lookup(fName)
-						flag.Hidden = true
-					}
-				}
-			}
-		}
-	}
-}
diff --git a/vendor/github.com/spf13/cobra/powershell_completions.go b/vendor/github.com/spf13/cobra/powershell_completions.go
index 379e7c08..62d719f0 100644
--- a/vendor/github.com/spf13/cobra/powershell_completions.go
+++ b/vendor/github.com/spf13/cobra/powershell_completions.go
@@ -61,7 +61,6 @@ Register-ArgumentCompleter -CommandName '%[1]s' -ScriptBlock {
     # Prepare the command to request completions for the program.
     # Split the command at the first space to separate the program and arguments.
     $Program,$Arguments = $Command.Split(" ",2)
-
     $RequestComp="$Program %[2]s $Arguments"
     __%[1]s_debug "RequestComp: $RequestComp"
 
@@ -91,13 +90,11 @@ Register-ArgumentCompleter -CommandName '%[1]s' -ScriptBlock {
     }
 
     __%[1]s_debug "Calling $RequestComp"
-    # First disable ActiveHelp which is not supported for Powershell
-    $env:%[8]s=0
-
     #call the command store the output in $out and redirect stderr and stdout to null
     # $Out is an array contains each line per element
     Invoke-Expression -OutVariable out "$RequestComp" 2>&1 | Out-Null
 
+
     # get directive from last line
     [int]$Directive = $Out[-1].TrimStart(':')
     if ($Directive -eq "") {
@@ -245,7 +242,7 @@ Register-ArgumentCompleter -CommandName '%[1]s' -ScriptBlock {
 }
 `, name, compCmd,
 		ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp,
-		ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, activeHelpEnvVar(name)))
+		ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs))
 }
 
 func (c *Command) genPowerShellCompletion(w io.Writer, includeDesc bool) error {
diff --git a/vendor/github.com/spf13/cobra/projects_using_cobra.md b/vendor/github.com/spf13/cobra/projects_using_cobra.md
index ac680118..9674c348 100644
--- a/vendor/github.com/spf13/cobra/projects_using_cobra.md
+++ b/vendor/github.com/spf13/cobra/projects_using_cobra.md
@@ -1,8 +1,8 @@
 ## Projects using Cobra
 
 - [Arduino CLI](https://github.com/arduino/arduino-cli)
-- [Bleve](https://blevesearch.com/)
-- [CockroachDB](https://www.cockroachlabs.com/)
+- [Bleve](http://www.blevesearch.com/)
+- [CockroachDB](http://www.cockroachlabs.com/)
 - [Cosmos SDK](https://github.com/cosmos/cosmos-sdk)
 - [Datree](https://github.com/datreeio/datree)
 - [Delve](https://github.com/derekparker/delve)
@@ -14,15 +14,14 @@
 - [Github CLI](https://github.com/cli/cli)
 - [GitHub Labeler](https://github.com/erdaltsksn/gh-label)
 - [Golangci-lint](https://golangci-lint.run)
-- [GopherJS](https://github.com/gopherjs/gopherjs)
+- [GopherJS](http://www.gopherjs.org/)
 - [GoReleaser](https://goreleaser.com)
 - [Helm](https://helm.sh)
 - [Hugo](https://gohugo.io)
 - [Infracost](https://github.com/infracost/infracost)
 - [Istio](https://istio.io)
 - [Kool](https://github.com/kool-dev/kool)
-- [Kubernetes](https://kubernetes.io/)
-- [Kubescape](https://github.com/armosec/kubescape)
+- [Kubernetes](http://kubernetes.io/)
 - [Linkerd](https://linkerd.io/)
 - [Mattermost-server](https://github.com/mattermost/mattermost-server)
 - [Mercure](https://mercure.rocks/)
@@ -37,11 +36,9 @@
 - [Ory Hydra](https://github.com/ory/hydra)
 - [Ory Kratos](https://github.com/ory/kratos)
 - [Pixie](https://github.com/pixie-io/pixie)
-- [Polygon Edge](https://github.com/0xPolygon/polygon-edge)
 - [Pouch](https://github.com/alibaba/pouch)
-- [ProjectAtomic (enterprise)](https://www.projectatomic.io/)
+- [ProjectAtomic (enterprise)](http://www.projectatomic.io/)
 - [Prototool](https://github.com/uber/prototool)
-- [Pulumi](https://www.pulumi.com)
 - [QRcp](https://github.com/claudiodangelis/qrcp)
 - [Random](https://github.com/erdaltsksn/random)
 - [Rclone](https://rclone.org/)
diff --git a/vendor/github.com/spf13/cobra/shell_completions.md b/vendor/github.com/spf13/cobra/shell_completions.md
index 1e2058ed..33a4c65a 100644
--- a/vendor/github.com/spf13/cobra/shell_completions.md
+++ b/vendor/github.com/spf13/cobra/shell_completions.md
@@ -40,7 +40,7 @@ Bash:
   # Linux:
   $ %[1]s completion bash > /etc/bash_completion.d/%[1]s
   # macOS:
-  $ %[1]s completion bash > $(brew --prefix)/etc/bash_completion.d/%[1]s
+  $ %[1]s completion bash > /usr/local/etc/bash_completion.d/%[1]s
 
 Zsh:
 
@@ -122,7 +122,7 @@ For example, if you want `kubectl get [tab][tab]` to show a list of valid "nouns
 Some simplified code from `kubectl get` looks like:
 
 ```go
-validArgs = []string{ "pod", "node", "service", "replicationcontroller" }
+validArgs []string = { "pod", "node", "service", "replicationcontroller" }
 
 cmd := &cobra.Command{
 	Use:     "get [(-o|--output=)json|yaml|template|...] (RESOURCE [NAME] | RESOURCE/NAME ...)",
@@ -148,7 +148,7 @@ node   pod   replicationcontroller   service
 If your nouns have aliases, you can define them alongside `ValidArgs` using `ArgAliases`:
 
 ```go
-argAliases = []string { "pods", "nodes", "services", "svc", "replicationcontrollers", "rc" }
+argAliases []string = { "pods", "nodes", "services", "svc", "replicationcontrollers", "rc" }
 
 cmd := &cobra.Command{
     ...
diff --git a/vendor/github.com/spf13/cobra/user_guide.md b/vendor/github.com/spf13/cobra/user_guide.md
index 5a7acf88..4a3c2b0d 100644
--- a/vendor/github.com/spf13/cobra/user_guide.md
+++ b/vendor/github.com/spf13/cobra/user_guide.md
@@ -32,7 +32,7 @@ func main() {
 Cobra-CLI is its own program that will create your application and add any
 commands you want. It's the easiest way to incorporate Cobra into your application.
 
-For complete details on using the Cobra generator, please refer to [The Cobra-CLI Generator README](https://github.com/spf13/cobra-cli/blob/main/README.md)
+For complete details on using the Cobra generator, please refer to [The Cobra-CLI Generator README](https://github.com/spf13/cobra-cli/blob/master/README.md)
 
 ## Using the Cobra Library
 
@@ -51,7 +51,7 @@ var rootCmd = &cobra.Command{
   Short: "Hugo is a very fast static site generator",
   Long: `A Fast and Flexible Static Site Generator built with
                 love by spf13 and friends in Go.
-                Complete documentation is available at https://gohugo.io/documentation/`,
+                Complete documentation is available at http://hugo.spf13.com`,
   Run: func(cmd *cobra.Command, args []string) {
     // Do Stuff Here
   },
@@ -300,34 +300,10 @@ rootCmd.PersistentFlags().StringVarP(&Region, "region", "r", "", "AWS region (re
 rootCmd.MarkPersistentFlagRequired("region")
 ```
 
-### Flag Groups
-
-If you have different flags that must be provided together (e.g. if they provide the `--username` flag they MUST provide the `--password` flag as well) then 
-Cobra can enforce that requirement:
-```go
-rootCmd.Flags().StringVarP(&u, "username", "u", "", "Username (required if password is set)")
-rootCmd.Flags().StringVarP(&pw, "password", "p", "", "Password (required if username is set)")
-rootCmd.MarkFlagsRequiredTogether("username", "password")
-``` 
-
-You can also prevent different flags from being provided together if they represent mutually 
-exclusive options such as specifying an output format as either `--json` or `--yaml` but never both:
-```go
-rootCmd.Flags().BoolVar(&u, "json", false, "Output in JSON")
-rootCmd.Flags().BoolVar(&pw, "yaml", false, "Output in YAML")
-rootCmd.MarkFlagsMutuallyExclusive("json", "yaml")
-```
-
-In both of these cases:
-  - both local and persistent flags can be used
-    - **NOTE:** the group is only enforced on commands where every flag is defined
-  - a flag may appear in multiple groups
-  - a group may contain any number of flags
-
 ## Positional and Custom Arguments
 
-Validation of positional arguments can be specified using the `Args` field of `Command`.
-If `Args` is undefined or `nil`, it defaults to `ArbitraryArgs`.
+Validation of positional arguments can be specified using the `Args` field
+of `Command`.
 
 The following validators are built in:
 
@@ -429,7 +405,7 @@ a count and a string.`,
 }
 ```
 
-For a more complete example of a larger application, please checkout [Hugo](https://gohugo.io/).
+For a more complete example of a larger application, please checkout [Hugo](http://gohugo.io/).
 
 ## Help Command
 
@@ -627,7 +603,7 @@ Did you mean this?
 Run 'hugo --help' for usage.
 ```
 
-Suggestions are automatic based on every subcommand registered and use an implementation of [Levenshtein distance](https://en.wikipedia.org/wiki/Levenshtein_distance). Every registered command that matches a minimum distance of 2 (ignoring case) will be displayed as a suggestion.
+Suggestions are automatic based on every subcommand registered and use an implementation of [Levenshtein distance](http://en.wikipedia.org/wiki/Levenshtein_distance). Every registered command that matches a minimum distance of 2 (ignoring case) will be displayed as a suggestion.
 
 If you need to disable suggestions or tweak the string distance in your command, use:
 
@@ -660,7 +636,3 @@ Cobra can generate documentation based on subcommands, flags, etc. Read more abo
 ## Generating shell completions
 
 Cobra can generate a shell-completion file for the following shells: bash, zsh, fish, PowerShell. If you add more information to your commands, these completions can be amazingly powerful and flexible.  Read more about it in [Shell Completions](shell_completions.md).
-
-## Providing Active Help
-
-Cobra makes use of the shell-completion system to define a framework allowing you to provide Active Help to your users.  Active Help are messages (hints, warnings, etc) printed as the program is being used.  Read more about it in [Active Help](active_help.md).
diff --git a/vendor/github.com/spf13/cobra/zsh_completions.go b/vendor/github.com/spf13/cobra/zsh_completions.go
index 65cd94c6..624adab5 100644
--- a/vendor/github.com/spf13/cobra/zsh_completions.go
+++ b/vendor/github.com/spf13/cobra/zsh_completions.go
@@ -75,7 +75,7 @@ func genZshComp(buf io.StringWriter, name string, includeDesc bool) {
 	if !includeDesc {
 		compCmd = ShellCompNoDescRequestCmd
 	}
-	WriteStringAndCheck(buf, fmt.Sprintf(`#compdef %[1]s
+	WriteStringAndCheck(buf, fmt.Sprintf(`#compdef _%[1]s %[1]s
 
 # zsh completion for %-36[1]s -*- shell-script -*-
 
@@ -163,24 +163,7 @@ _%[1]s()
         return
     fi
 
-    local activeHelpMarker="%[8]s"
-    local endIndex=${#activeHelpMarker}
-    local startIndex=$((${#activeHelpMarker}+1))
-    local hasActiveHelp=0
     while IFS='\n' read -r comp; do
-        # Check if this is an activeHelp statement (i.e., prefixed with $activeHelpMarker)
-        if [ "${comp[1,$endIndex]}" = "$activeHelpMarker" ];then
-            __%[1]s_debug "ActiveHelp found: $comp"
-            comp="${comp[$startIndex,-1]}"
-            if [ -n "$comp" ]; then
-                compadd -x "${comp}"
-                __%[1]s_debug "ActiveHelp will need delimiter"
-                hasActiveHelp=1
-            fi
-
-            continue
-        fi
-
         if [ -n "$comp" ]; then
             # If requested, completions are returned with a description.
             # The description is preceded by a TAB character.
@@ -188,7 +171,7 @@ _%[1]s()
             # We first need to escape any : as part of the completion itself.
             comp=${comp//:/\\:}
 
-            local tab="$(printf '\t')"
+            local tab=$(printf '\t')
             comp=${comp//$tab/:}
 
             __%[1]s_debug "Adding completion: ${comp}"
@@ -197,17 +180,6 @@ _%[1]s()
         fi
     done < <(printf "%%s\n" "${out[@]}")
 
-    # Add a delimiter after the activeHelp statements, but only if:
-    # - there are completions following the activeHelp statements, or
-    # - file completion will be performed (so there will be choices after the activeHelp)
-    if [ $hasActiveHelp -eq 1 ]; then
-        if [ ${#completions} -ne 0 ] || [ $((directive & shellCompDirectiveNoFileComp)) -eq 0 ]; then
-            __%[1]s_debug "Adding activeHelp delimiter"
-            compadd -x "--"
-            hasActiveHelp=0
-        fi
-    fi
-
     if [ $((directive & shellCompDirectiveNoSpace)) -ne 0 ]; then
         __%[1]s_debug "Activating nospace."
         noSpace="-S ''"
@@ -282,6 +254,5 @@ if [ "$funcstack[1]" = "_%[1]s" ]; then
 fi
 `, name, compCmd,
 		ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp,
-		ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs,
-		activeHelpMarker))
+		ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs))
 }
diff --git a/vendor/golang.org/x/net/http2/server.go b/vendor/golang.org/x/net/http2/server.go
index 47524a61..2d859af8 100644
--- a/vendor/golang.org/x/net/http2/server.go
+++ b/vendor/golang.org/x/net/http2/server.go
@@ -315,20 +315,6 @@ type ServeConnOpts struct {
 	// requests. If nil, BaseConfig.Handler is used. If BaseConfig
 	// or BaseConfig.Handler is nil, http.DefaultServeMux is used.
 	Handler http.Handler
-
-	// UpgradeRequest is an initial request received on a connection
-	// undergoing an h2c upgrade. The request body must have been
-	// completely read from the connection before calling ServeConn,
-	// and the 101 Switching Protocols response written.
-	UpgradeRequest *http.Request
-
-	// Settings is the decoded contents of the HTTP2-Settings header
-	// in an h2c upgrade request.
-	Settings []byte
-
-	// SawClientPreface is set if the HTTP/2 connection preface
-	// has already been read from the connection.
-	SawClientPreface bool
 }
 
 func (o *ServeConnOpts) context() context.Context {
@@ -397,7 +383,6 @@ func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) {
 		headerTableSize:             initialHeaderTableSize,
 		serveG:                      newGoroutineLock(),
 		pushEnabled:                 true,
-		sawClientPreface:            opts.SawClientPreface,
 	}
 
 	s.state.registerConn(sc)
@@ -480,27 +465,9 @@ func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) {
 		}
 	}
 
-	if opts.Settings != nil {
-		fr := &SettingsFrame{
-			FrameHeader: FrameHeader{valid: true},
-			p:           opts.Settings,
-		}
-		if err := fr.ForeachSetting(sc.processSetting); err != nil {
-			sc.rejectConn(ErrCodeProtocol, "invalid settings")
-			return
-		}
-		opts.Settings = nil
-	}
-
 	if hook := testHookGetServerConn; hook != nil {
 		hook(sc)
 	}
-
-	if opts.UpgradeRequest != nil {
-		sc.upgradeRequest(opts.UpgradeRequest)
-		opts.UpgradeRequest = nil
-	}
-
 	sc.serve()
 }
 
@@ -545,7 +512,6 @@ type serverConn struct {
 	// Everything following is owned by the serve loop; use serveG.check():
 	serveG                      goroutineLock // used to verify funcs are on serve()
 	pushEnabled                 bool
-	sawClientPreface            bool // preface has already been read, used in h2c upgrade
 	sawFirstSettings            bool // got the initial SETTINGS frame after the preface
 	needToSendSettingsAck       bool
 	unackedSettings             int    // how many SETTINGS have we sent without ACKs?
@@ -1008,9 +974,6 @@ var errPrefaceTimeout = errors.New("timeout waiting for client preface")
 // returns errPrefaceTimeout on timeout, or an error if the greeting
 // is invalid.
 func (sc *serverConn) readPreface() error {
-	if sc.sawClientPreface {
-		return nil
-	}
 	errc := make(chan error, 1)
 	go func() {
 		// Read the client preface
@@ -1952,26 +1915,6 @@ func (sc *serverConn) processHeaders(f *MetaHeadersFrame) error {
 	return nil
 }
 
-func (sc *serverConn) upgradeRequest(req *http.Request) {
-	sc.serveG.check()
-	id := uint32(1)
-	sc.maxClientStreamID = id
-	st := sc.newStream(id, 0, stateHalfClosedRemote)
-	st.reqTrailer = req.Trailer
-	if st.reqTrailer != nil {
-		st.trailer = make(http.Header)
-	}
-	rw := sc.newResponseWriter(st, req)
-
-	// Disable any read deadline set by the net/http package
-	// prior to the upgrade.
-	if sc.hs.ReadTimeout != 0 {
-		sc.conn.SetReadDeadline(time.Time{})
-	}
-
-	go sc.runHandler(rw, req, sc.handler.ServeHTTP)
-}
-
 func (st *stream) processTrailerHeaders(f *MetaHeadersFrame) error {
 	sc := st.sc
 	sc.serveG.check()
@@ -2202,11 +2145,6 @@ func (sc *serverConn) newWriterAndRequestNoBody(st *stream, rp requestParam) (*r
 	}
 	req = req.WithContext(st.ctx)
 
-	rw := sc.newResponseWriter(st, req)
-	return rw, req, nil
-}
-
-func (sc *serverConn) newResponseWriter(st *stream, req *http.Request) *responseWriter {
 	rws := responseWriterStatePool.Get().(*responseWriterState)
 	bwSave := rws.bw
 	*rws = responseWriterState{} // zero all the fields
@@ -2215,7 +2153,10 @@ func (sc *serverConn) newResponseWriter(st *stream, req *http.Request) *response
 	rws.bw.Reset(chunkWriter{rws})
 	rws.stream = st
 	rws.req = req
-	return &responseWriter{rws: rws}
+	rws.body = body
+
+	rw := &responseWriter{rws: rws}
+	return rw, req, nil
 }
 
 // Run on its own goroutine.
@@ -2430,6 +2371,7 @@ type responseWriterState struct {
 	// immutable within a request:
 	stream *stream
 	req    *http.Request
+	body   *requestBody // to close at end of request, if DATA frames didn't
 	conn   *serverConn
 
 	// TODO: adjust buffer writing sizes based on server config, frame size updates from peer, etc
diff --git a/vendor/golang.org/x/oauth2/google/google.go b/vendor/golang.org/x/oauth2/google/google.go
index ceddd5dd..ccc23ee0 100644
--- a/vendor/golang.org/x/oauth2/google/google.go
+++ b/vendor/golang.org/x/oauth2/google/google.go
@@ -139,7 +139,6 @@ func (f *credentialsFile) jwtConfig(scopes []string, subject string) *jwt.Config
 		Scopes:       scopes,
 		TokenURL:     f.TokenURL,
 		Subject:      subject, // This is the user email to impersonate
-		Audience:     f.Audience,
 	}
 	if cfg.TokenURL == "" {
 		cfg.TokenURL = JWTTokenURL
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 4d086bd1..e66a1ca8 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -75,7 +75,7 @@ github.com/containerd/stargz-snapshotter/estargz/errorutil
 # github.com/davecgh/go-spew v1.1.1
 ## explicit
 github.com/davecgh/go-spew/spew
-# github.com/docker/cli v20.10.17+incompatible
+# github.com/docker/cli v20.10.16+incompatible
 ## explicit
 github.com/docker/cli/cli/config
 github.com/docker/cli/cli/config/configfile
@@ -197,7 +197,7 @@ github.com/go-openapi/swag
 ## explicit; go 1.15
 github.com/gogo/protobuf/proto
 github.com/gogo/protobuf/sortkeys
-# github.com/golang-jwt/jwt/v4 v4.4.2
+# github.com/golang-jwt/jwt/v4 v4.4.1
 ## explicit; go 1.16
 github.com/golang-jwt/jwt/v4
 # github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da
@@ -221,7 +221,7 @@ github.com/google/go-cmp/cmp/internal/diff
 github.com/google/go-cmp/cmp/internal/flags
 github.com/google/go-cmp/cmp/internal/function
 github.com/google/go-cmp/cmp/internal/value
-# github.com/google/go-containerregistry v0.10.0
+# github.com/google/go-containerregistry v0.9.0
 ## explicit; go 1.17
 github.com/google/go-containerregistry/internal/and
 github.com/google/go-containerregistry/internal/estargz
@@ -383,7 +383,7 @@ github.com/sergi/go-diff/diffmatchpatch
 # github.com/sirupsen/logrus v1.8.1
 ## explicit; go 1.13
 github.com/sirupsen/logrus
-# github.com/spf13/cobra v1.5.0
+# github.com/spf13/cobra v1.4.0
 ## explicit; go 1.15
 github.com/spf13/cobra
 # github.com/spf13/pflag v1.0.5
@@ -465,7 +465,7 @@ golang.org/x/crypto/ssh/knownhosts
 golang.org/x/mod/internal/lazyregexp
 golang.org/x/mod/module
 golang.org/x/mod/semver
-# golang.org/x/net v0.0.0-20220524220425-1d687d428aca
+# golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2
 ## explicit; go 1.17
 golang.org/x/net/context
 golang.org/x/net/context/ctxhttp
@@ -480,7 +480,7 @@ golang.org/x/net/internal/socks
 golang.org/x/net/internal/timeseries
 golang.org/x/net/proxy
 golang.org/x/net/trace
-# golang.org/x/oauth2 v0.0.0-20220524215830-622c5d57e401
+# golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5
 ## explicit; go 1.11
 golang.org/x/oauth2
 golang.org/x/oauth2/authhandler


Test patch:
diff --git a/pkg/validate/trigger_test.go b/pkg/validate/trigger_test.go
new file mode 100644
index 00000000..728e8e35
--- /dev/null
+++ b/pkg/validate/trigger_test.go
@@ -0,0 +1,272 @@
+// Copyright The Shipwright Contributors
+//
+// SPDX-License-Identifier: Apache-2.0
+package validate_test
+
+import (
+	"context"
+
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
+	build "github.com/shipwright-io/build/pkg/apis/build/v1alpha1"
+	"github.com/shipwright-io/build/pkg/validate"
+)
+
+var _ = Describe("ValidateBuildTriggers", func() {
+	Context("trigger name is not informed", func() {
+		b := &build.Build{
+			Spec: build.BuildSpec{
+				Trigger: &build.Trigger{
+					When: []build.TriggerWhen{{
+						Name: "",
+					}},
+				},
+			},
+		}
+
+		It("should error when name is not set", func() {
+			err := validate.NewTrigger(b).ValidatePath(context.TODO())
+			Expect(err.Error()).To(ContainSubstring("name is not set"))
+		})
+	})
+
+	Context("trigger type github", func() {
+		It("should error when github attribute is not set", func() {
+			b := &build.Build{
+				Spec: build.BuildSpec{
+					Trigger: &build.Trigger{
+						When: []build.TriggerWhen{{
+							Name: "github",
+							Type: build.GitHubWebHookTrigger,
+						}},
+					},
+				},
+			}
+
+			err := validate.NewTrigger(b).ValidatePath(context.TODO())
+			Expect(err.Error()).To(ContainSubstring("missing required attribute `.github`"))
+		})
+
+		It("should error when github events attribute is empty", func() {
+			b := &build.Build{
+				Spec: build.BuildSpec{
+					Trigger: &build.Trigger{
+						When: []build.TriggerWhen{{
+							Name: "github",
+							Type: build.GitHubWebHookTrigger,
+							GitHub: &build.WhenGitHub{
+								Events: []build.GitHubEventName{},
+							},
+						}},
+					},
+				},
+			}
+
+			err := validate.NewTrigger(b).ValidatePath(context.TODO())
+			Expect(err.Error()).To(ContainSubstring("missing required attribute `.github.events`"))
+		})
+
+		It("should pass when github type is complete", func() {
+			b := &build.Build{
+				Spec: build.BuildSpec{
+					Trigger: &build.Trigger{
+						When: []build.TriggerWhen{{
+							Name: "github",
+							Type: build.GitHubWebHookTrigger,
+							GitHub: &build.WhenGitHub{
+								Events: []build.GitHubEventName{
+									build.GitHubPushEvent,
+								},
+							},
+						}},
+					},
+				},
+			}
+
+			err := validate.NewTrigger(b).ValidatePath(context.TODO())
+			Expect(err).ToNot(HaveOccurred())
+		})
+	})
+
+	Context("trigger type image", func() {
+		It("should error when image attribute is not set", func() {
+			b := &build.Build{
+				Spec: build.BuildSpec{
+					Trigger: &build.Trigger{
+						When: []build.TriggerWhen{{
+							Name: "image",
+							Type: build.ImageTrigger,
+						}},
+					},
+				},
+			}
+
+			err := validate.NewTrigger(b).ValidatePath(context.TODO())
+			Expect(err.Error()).To(ContainSubstring("missing required attribute `.image`"))
+		})
+
+		It("should error when image names attribute is empty", func() {
+			b := &build.Build{
+				Spec: build.BuildSpec{
+					Trigger: &build.Trigger{
+						When: []build.TriggerWhen{{
+							Name: "image",
+							Type: build.ImageTrigger,
+							Image: &build.WhenImage{
+								Names: []string{},
+							},
+						}},
+					},
+				},
+			}
+
+			err := validate.NewTrigger(b).ValidatePath(context.TODO())
+			Expect(err.Error()).To(ContainSubstring("missing required attribute `.image.names`"))
+		})
+
+		It("should pass when github type is complete", func() {
+			b := &build.Build{
+				Spec: build.BuildSpec{
+					Trigger: &build.Trigger{
+						When: []build.TriggerWhen{{
+							Name: "image",
+							Type: build.ImageTrigger,
+							Image: &build.WhenImage{
+								Names: []string{
+									"ghcr.io/shipwright-io/build:latest",
+								},
+							},
+						}},
+					},
+				},
+			}
+
+			err := validate.NewTrigger(b).ValidatePath(context.TODO())
+			Expect(err).ToNot(HaveOccurred())
+		})
+	})
+
+	Context("trigger type pipeline", func() {
+		It("should error when objectRef attribute is not set", func() {
+			b := &build.Build{
+				Spec: build.BuildSpec{
+					Trigger: &build.Trigger{
+						When: []build.TriggerWhen{{
+							Name: "pipeline",
+							Type: build.PipelineTrigger,
+						}},
+					},
+				},
+			}
+
+			err := validate.NewTrigger(b).ValidatePath(context.TODO())
+			Expect(err.Error()).To(ContainSubstring("missing required attribute `.objectRef`"))
+		})
+
+		It("should error when status attribute is empty", func() {
+			b := &build.Build{
+				Spec: build.BuildSpec{
+					Trigger: &build.Trigger{
+						When: []build.TriggerWhen{{
+							Name: "pipeline",
+							Type: build.PipelineTrigger,
+							ObjectRef: &build.WhenObjectRef{
+								Status: []string{},
+							},
+						}},
+					},
+				},
+			}
+
+			err := validate.NewTrigger(b).ValidatePath(context.TODO())
+			Expect(err.Error()).To(ContainSubstring("missing required attribute `.objectRef.status`"))
+		})
+
+		It("should error when missing required attributes", func() {
+			b := &build.Build{
+				Spec: build.BuildSpec{
+					Trigger: &build.Trigger{
+						When: []build.TriggerWhen{{
+							Name: "pipeline",
+							Type: build.PipelineTrigger,
+							ObjectRef: &build.WhenObjectRef{
+								Status: []string{"Succeed"},
+							},
+						}},
+					},
+				},
+			}
+
+			err := validate.NewTrigger(b).ValidatePath(context.TODO())
+			Expect(err.Error()).To(ContainSubstring(
+				"is missing required attributes `.objectRef.name` or `.objectRef.selector`",
+			))
+		})
+
+		It("should error when declaring conflicting attributes", func() {
+			b := &build.Build{
+				Spec: build.BuildSpec{
+					Trigger: &build.Trigger{
+						When: []build.TriggerWhen{{
+							Name: "pipeline",
+							Type: build.PipelineTrigger,
+							ObjectRef: &build.WhenObjectRef{
+								Status: []string{"Succeed"},
+								Name:   "name",
+								Selector: map[string]string{
+									"k": "v",
+								},
+							},
+						}},
+					},
+				},
+			}
+
+			err := validate.NewTrigger(b).ValidatePath(context.TODO())
+			Expect(err.Error()).To(ContainSubstring(
+				"contains `.objectRef.name` and `.objectRef.selector`, must be only one",
+			))
+		})
+
+		It("should pass when objectRef type is complete", func() {
+			b := &build.Build{
+				Spec: build.BuildSpec{
+					Trigger: &build.Trigger{
+						When: []build.TriggerWhen{{
+							Name: "pipeline",
+							Type: build.PipelineTrigger,
+							ObjectRef: &build.WhenObjectRef{
+								Status: []string{"Succeed"},
+								Name:   "name",
+							},
+						}},
+					},
+				},
+			}
+
+			err := validate.NewTrigger(b).ValidatePath(context.TODO())
+			Expect(err).ToNot(HaveOccurred())
+		})
+	})
+
+	Context("invalid trigger type", func() {
+		It("should error when declaring a invalid trigger type", func() {
+			b := &build.Build{
+				Spec: build.BuildSpec{
+					Trigger: &build.Trigger{
+						When: []build.TriggerWhen{{
+							Name: "pipeline",
+							Type: build.TriggerType("invalid"),
+							ObjectRef: &build.WhenObjectRef{
+								Name: "name",
+							},
+						}},
+					},
+				},
+			}
+
+			err := validate.NewTrigger(b).ValidatePath(context.TODO())
+			Expect(err.Error()).To(ContainSubstring("contains an invalid type"))
+		})
+	})
+})