@@ -23,10 +23,11 @@ var logger = ctrl.Log.WithName("webhookReceiver")
2323
2424// Provider type constants
2525const (
26- ProviderGitHub = "github"
27- ProviderGitLab = "gitlab"
28- ProviderForgejo = "forgejo"
29- ProviderUnknown = ""
26+ ProviderGitHub = "github"
27+ ProviderGitLab = "gitlab"
28+ ProviderForgejo = "forgejo"
29+ ProviderBitbucket = "bitbucket"
30+ ProviderUnknown = ""
3031)
3132
3233// WebhookReceiver is a server that listens for webhooks and triggers reconciles of ChangeTransferPolicies.
@@ -75,7 +76,7 @@ func (wr *WebhookReceiver) Start(ctx context.Context, addr string) error {
7576}
7677
7778// DetectProvider determines the SCM provider based on webhook headers.
78- // Returns ProviderGitHub, ProviderGitLab, ProviderForgejo, or ProviderUnknown.
79+ // Returns ProviderGitHub, ProviderGitLab, ProviderForgejo, ProviderBitbucket, or ProviderUnknown.
7980func (wr * WebhookReceiver ) DetectProvider (r * http.Request ) string {
8081 // Check for GitHub webhook headers
8182 if r .Header .Get ("X-Github-Event" ) != "" || r .Header .Get ("X-Github-Delivery" ) != "" {
@@ -92,6 +93,11 @@ func (wr *WebhookReceiver) DetectProvider(r *http.Request) string {
9293 return ProviderForgejo
9394 }
9495
96+ // Check for Bitbucket Cloud webhook headers
97+ if r .Header .Get ("X-Hook-UUID" ) != "" {
98+ return ProviderBitbucket
99+ }
100+
95101 return ProviderUnknown
96102}
97103
@@ -193,6 +199,20 @@ func (wr *WebhookReceiver) findChangeTransferPolicy(ctx context.Context, provide
193199 beforeSha = gjson .GetBytes (jsonBytes , "before" ).String ()
194200 ref = gjson .GetBytes (jsonBytes , "ref" ).String ()
195201 }
202+ case ProviderBitbucket :
203+ // Bitbucket Cloud webhook format
204+ if gjson .GetBytes (jsonBytes , "push.changes" ).Exists () && gjson .GetBytes (jsonBytes , "actor" ).Exists () {
205+ changes := gjson .GetBytes (jsonBytes , "push.changes" )
206+ if changes .IsArray () && len (changes .Array ()) > 0 {
207+ firstChange := changes .Array ()[0 ]
208+ beforeSha = firstChange .Get ("old.target.hash" ).String ()
209+ if newName := firstChange .Get ("new.name" ); newName .Exists () {
210+ ref = "refs/heads/" + newName .String ()
211+ } else if oldName := firstChange .Get ("old.name" ); oldName .Exists () {
212+ ref = "refs/heads/" + oldName .String ()
213+ }
214+ }
215+ }
196216 default :
197217 logger .V (4 ).Info ("unsupported provider" , "provider" , provider )
198218 return nil , nil
@@ -255,5 +275,15 @@ func (wr *WebhookReceiver) extractDeliveryID(r *http.Request) string {
255275 if id := r .Header .Get ("X-Gitea-Delivery" ); id != "" {
256276 return id
257277 }
278+ // Bitbucket Cloud
279+ // X-Request-UUID: Unique identifier for the webhook request
280+ // X-Hook-UUID: Unique identifier for the webhook itself (also used for provider detection)
281+ // Note: Go's http.Header.Get is case-insensitive, so this will match X-Request-UUID correctly
282+ if id := r .Header .Get ("X-Request-Uuid" ); id != "" {
283+ return id
284+ }
285+ if id := r .Header .Get ("X-Hook-Uuid" ); id != "" {
286+ return id
287+ }
258288 return ""
259289}
0 commit comments