Skip to content

Conversation

@krancour
Copy link
Member

@krancour krancour commented Jun 16, 2025

Fixes #4486

Example usage using expr-lang:

apiVersion: kargo.akuity.io/v1alpha1
kind: ProjectConfig
metadata:
  name: webhooks-demo
  namespace: webhooks-demo
spec:
  webhookReceivers:
  - name: my-receiver
    generic:
      refresh:
        predicate: ${{ request.header('X-Kargo-Event') == 'push' }}
        selectors:
          gitRepoURL: ${{ request.body.repository.repo_name }}
      secretRef:
        name: my-receiver-secret
---
apiVersion: v1
kind: Secret
type: Opaque
metadata:
  name: my-receiver-secret
  namespace: webhooks-demo
data:
  secret: c295bGVudCBncmVlbiBpcyBwZW9wbGUK

Example usage using exact matches:

apiVersion: kargo.akuity.io/v1alpha1
kind: ProjectConfig
metadata:
  name: webhooks-demo
  namespace: webhooks-demo
spec:
  webhookReceivers:
  - name: my-receiver
    generic:
      refresh:
        selectors:
          gitRepoURL: https://github.com/example/repository.git
      secretRef:
        name: my-receiver-secret
---
apiVersion: v1
kind: Secret
type: Opaque
metadata:
  name: my-receiver-secret
  namespace: webhooks-demo
data:
  secret: c295bGVudCBncmVlbiBpcyBwZW9wbGUK

Note the API was designed with future expansion in mind. For instance, if handling of events representing the merge of a PR were added in the future, the above could be updated without any interference to what it's already doing:

# ...
spec:
  webhookReceivers:
  - name: my-receiver
    generic:
      refresh:
        predicate: ${{ request.header('X-Kargo-Event') == 'push' }}
        selectors:
          gitRepoURL: ${{ request.body.repository.repo_name }}
      prMerge:
        predicate: ${{ request.header('X-Kargo-Event') == 'merge' }}
        selectors:
          prNumber: ${{ request.body.pr.id }}
      secretRef:
        name: my-receiver-secret

@krancour krancour requested a review from a team as a code owner June 16, 2025 14:58
@netlify
Copy link

netlify bot commented Jun 16, 2025

Deploy Preview for docs-kargo-io ready!

Name Link
🔨 Latest commit d80e020
🔍 Latest deploy log https://app.netlify.com/projects/docs-kargo-io/deploys/68556238717bf200085de602
😎 Deploy Preview https://deploy-preview-4408.docs.kargo.io
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@codecov
Copy link

codecov bot commented Jun 16, 2025

Codecov Report

❌ Patch coverage is 85.27607% with 24 lines in your changes missing coverage. Please review.
✅ Project coverage is 53.14%. Comparing base (56e4936) to head (d80e020).
⚠️ Report is 369 commits behind head on main.

Files with missing lines Patch % Lines
internal/webhook/external/generic.go 81.81% 22 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4408      +/-   ##
==========================================
+ Coverage   53.00%   53.14%   +0.13%     
==========================================
  Files         356      357       +1     
  Lines       31605    31750     +145     
==========================================
+ Hits        16751    16872     +121     
- Misses      13990    14012      +22     
- Partials      864      866       +2     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@krancour krancour marked this pull request as draft June 16, 2025 15:48
@krancour krancour force-pushed the krancour/generic-receiver branch from 392eb0c to 2ff4eaa Compare June 16, 2025 18:04
Comment on lines 203 to 226
// GitRepoURL is a user-defined expression that should return a Git repository
// URL for which all subscribed Warehouses should be refreshed. If an empty
// string is returned, no Warehouses will be refreshed. If a non-empty string
// is returned, Git repo URL normalization will be applied before the URL is
// used to find Warehouses to refresh.
//
// +optional
GitRepoURL string `json:"gitRepoURL,omitempty" protobuf:"bytes,2,opt,name=gitRepoURL"`
// ImageRepoURL is a user-defined expression that should return a container
// image repository URL for which all subscribed Warehouses should be
// refreshed. If an empty string is returned, no Warehouses will be refreshed.
// If a non-empty string is returned, container image repo URL normalization
// will be applied before the URL is used to find Warehouses to refresh.
//
// +optional
ImageRepoURL string `json:"imageRepoURL,omitempty" protobuf:"bytes,3,opt,name=imageRepoURL"`
// ChartRepoURL is a user-defined expression that should return a Helm
// chart repository URL for which all subscribed Warehouses should be
// refreshed. If an empty string is returned, no Warehouses will be refreshed.
// If a non-empty string is returned, Helm chart repo URL normalization
// will be applied before the URL is used to find Warehouses to refresh.
//
// +optional
ChartRepoURL string `json:"chartRepoURL,omitempty" protobuf:"bytes,4,opt,name=chartRepoURL"`
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having separate fields for each of the supported repo types does two things:

  1. It allows specifying separate repo URL extraction logic for each kind of repo, making it possible, for instance, for one generic receiver to process push events for multiple types of repos.

  2. It allows us to know what kind of normalization process to apply to the URL, as the logic for normalizing each of Git, image, and chart URLs is different.

}
env["request"].(map[string]any)["body"] = parsedBody // nolint: forcetypeassert

program, err := expr.Compile(g.cfg.ArtifactPush.Predicate)
Copy link
Contributor

@hiddeco hiddeco Jun 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I am understanding this correctly, a predicate is always required and it's not sufficient to declare e.g. artifactPush without one?

Copy link
Member Author

@krancour krancour Jun 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As things stand currently, yes.

I had thought about blindly jumping into seeing if the other expressions evaluate to anything, but that seems to invite in the possibility that one or more of those expressions do coincidentally evaluate to something, but the actual event we're being notified of is not one of interest.

By way of example, ignore for a moment that we have a dedicated receiver for GitHub, and imagine we're using a generic receiver to handle incoming webhooks from there. Nearly all event types include repository.url in the payload. The predicate request.header('X-GitHub-Event') == 'push' would prevent us from blindly reading repository.url from an event that turned out to be, for instance, someone adding a comment to an issue.

Please note that I think this works well, but I'm not entirely convinced that there isn't a more ergonomic option I am overlooking, so if you have ideas, I'm totally open to them.

@krancour krancour self-assigned this Jun 17, 2025
@krancour krancour added area/external-webhooks Affects the server that responds to webhooks originating from external systems priority/high Needs to be addressed sooner rather than later kind/enhancement An entirely new feature labels Jun 17, 2025
@krancour krancour added this to the v1.6.0 milestone Jun 17, 2025
krancour and others added 6 commits June 20, 2025 14:11
Signed-off-by: Kent Rancourt <kent.rancourt@gmail.com>
Signed-off-by: Kent Rancourt <kent.rancourt@gmail.com>
Signed-off-by: Kent Rancourt <kent.rancourt@gmail.com>
Signed-off-by: Kent Rancourt <kent.rancourt@gmail.com>
Signed-off-by: Hidde Beydals <hidde@hhh.computer>
Signed-off-by: Hidde Beydals <hidde@hhh.computer>
@hiddeco hiddeco force-pushed the krancour/generic-receiver branch from 5669816 to f7bf66c Compare June 20, 2025 13:02
Signed-off-by: Hidde Beydals <hidde@hhh.computer>
@krancour krancour modified the milestones: v1.6.0, v1.7.0 Jun 23, 2025
@krancour krancour added the needs discussion A maintainer explicitly requests no action be taken without further discussion label Jun 24, 2025
@krancour
Copy link
Member Author

There's too much discussion to be had about this for this to reasonably make it into 1.7.0. Deferring to v1.8.0.

@krancour krancour modified the milestones: v1.7.0, v1.8.0 Jul 25, 2025
@krancour krancour modified the milestones: v1.8.0, v1.9.0 Aug 28, 2025
@krancour
Copy link
Member Author

@fuskovic you've worked extensively on the webhook receivers and have worked quite a bit with expressions recently. You're sitting right at the intersection of the things involved in this...

I'm going to turn this over to to you. Feel free to either build on what I've done or scrap it and start fresh if that's what ends up making more sense, but either way, don't start any work on this until we have 1.8 buttoned up and until you, @hiddeco, and I can meet to hammer out what a reasonable UX looks like here... previous conversation between @hiddeco and myself was offline, but revealed that there are a lot small concerns that aren't addressed by this PR.

@krancour
Copy link
Member Author

Closing in favor of #5305

@krancour krancour closed this Oct 29, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/external-webhooks Affects the server that responds to webhooks originating from external systems kind/enhancement An entirely new feature needs discussion A maintainer explicitly requests no action be taken without further discussion priority/high Needs to be addressed sooner rather than later

Projects

None yet

Development

Successfully merging this pull request may close these issues.

generic webhook receiver

3 participants