2828import com .cloudbees .jenkins .plugins .bitbucket .api .endpoint .BitbucketEndpoint ;
2929import com .cloudbees .jenkins .plugins .bitbucket .api .endpoint .BitbucketEndpointProvider ;
3030import com .cloudbees .jenkins .plugins .bitbucket .client .repository .BitbucketCloudHook ;
31+ import com .cloudbees .jenkins .plugins .bitbucket .endpoints .AbstractBitbucketEndpoint ;
3132import com .cloudbees .jenkins .plugins .bitbucket .endpoints .BitbucketServerEndpoint ;
3233import com .cloudbees .jenkins .plugins .bitbucket .impl .util .BitbucketApiUtils ;
3334import com .cloudbees .jenkins .plugins .bitbucket .server .client .repository .BitbucketPluginWebhook ;
4546import java .util .List ;
4647import java .util .Set ;
4748import java .util .TreeSet ;
49+ import java .util .logging .Logger ;
50+ import org .apache .commons .collections .CollectionUtils ;
4851import org .jenkinsci .plugins .plaincredentials .StringCredentials ;
4952
5053/**
5154 * Contains the webhook configuration
5255 */
5356public class WebhookConfiguration {
57+ private static final Logger logger = Logger .getLogger (WebhookConfiguration .class .getName ());
5458
5559 /**
5660 * The list of events available in Bitbucket Cloud.
@@ -81,6 +85,20 @@ public class WebhookConfiguration {
8185 HookEventType .SERVER_PULL_REQUEST_FROM_REF_UPDATED .getKey ()
8286 ));
8387
88+ // See https://help.moveworkforward.com/BPW/how-to-manage-configurations-using-post-webhooks-f#HowtomanageconfigurationsusingPostWebhooksforBitbucketAPIs?-Possibleeventtypes
89+ private static final List <String > PLUGIN_SERVER_EVENTS = Collections .unmodifiableList (Arrays .asList (
90+ "ABSTRACT_REPOSITORY_REFS_CHANGED" , // push event
91+ "BRANCH_CREATED" ,
92+ "BRANCH_DELETED" ,
93+ "PULL_REQUEST_DECLINED" ,
94+ "PULL_REQUEST_DELETED" ,
95+ "PULL_REQUEST_MERGED" ,
96+ "PULL_REQUEST_OPENED" ,
97+ "PULL_REQUEST_REOPENED" ,
98+ "PULL_REQUEST_UPDATED" ,
99+ "REPOSITORY_MIRROR_SYNCHRONIZED" , // not supported by the hookprocessor
100+ "TAG_CREATED" ));
101+
84102 /**
85103 * The list of events available in Bitbucket Server v6.5+.
86104 */
@@ -99,7 +117,7 @@ public class WebhookConfiguration {
99117 /**
100118 * The title of the webhook.
101119 */
102- private static final String description = "Jenkins hook" ;
120+ private static final String DESCRIPTION = "Jenkins hook" ;
103121
104122 /**
105123 * The comma separated list of committers to ignore.
@@ -121,43 +139,72 @@ public String getCommittersToIgnore() {
121139 boolean updateHook (BitbucketWebHook hook , BitbucketSCMSource owner ) {
122140 boolean updated = false ;
123141
142+ final String serverURL = owner .getServerUrl ();
143+ final String rootURL = getEndpointJenkinsRootURL (serverURL );
124144 final String signatureSecret = getSecret (owner .getServerUrl ());
125145
126146 if (hook instanceof BitbucketCloudHook cloudHook ) {
127- if (!hook .getEvents ().containsAll (CLOUD_EVENTS )) {
128- Set <String > events = new TreeSet <>(hook .getEvents ());
129- events .addAll (CLOUD_EVENTS );
130- cloudHook .setEvents (new ArrayList <>(events ));
147+ String url = getCloudWebhookURL (serverURL , rootURL );
148+ if (!Objects .equal (hook .getUrl (), url )) {
149+ cloudHook .setUrl (url );
131150 updated = true ;
132151 }
152+
153+ List <String > events = hook .getEvents ();
154+ if (!events .containsAll (CLOUD_EVENTS )) {
155+ Set <String > newEvents = new TreeSet <>(events );
156+ newEvents .addAll (CLOUD_EVENTS );
157+ cloudHook .setEvents (new ArrayList <>(newEvents ));
158+ logger .info (() -> "Update cloud webhook because the following events was missing: " + CollectionUtils .subtract (CLOUD_EVENTS , events ));
159+ updated = true ;
160+ }
161+
133162 if (!Objects .equal (hook .getSecret (), signatureSecret )) {
134163 cloudHook .setSecret (signatureSecret );
135164 updated = true ;
136165 }
137- } else if (hook instanceof BitbucketPluginWebhook serverHook ) {
138- String hookCommittersToIgnore = Util .fixEmptyAndTrim (serverHook .getCommittersToIgnore ());
166+ } else if (hook instanceof BitbucketPluginWebhook pluginHook ) {
167+ String hookCommittersToIgnore = Util .fixEmptyAndTrim (pluginHook .getCommittersToIgnore ());
139168 String thisCommittersToIgnore = Util .fixEmptyAndTrim (committersToIgnore );
140169 if (!Objects .equal (thisCommittersToIgnore , hookCommittersToIgnore )) {
141- serverHook .setCommittersToIgnore (thisCommittersToIgnore );
170+ pluginHook .setCommittersToIgnore (thisCommittersToIgnore );
171+ updated = true ;
172+ }
173+
174+ String url = getServerWebhookURL (serverURL , rootURL );
175+ if (!url .equals (pluginHook .getUrl ())) {
176+ pluginHook .setUrl (url );
177+ updated = true ;
178+ }
179+
180+ if (!pluginHook .isActive ()) {
181+ pluginHook .setActive (true );
142182 updated = true ;
143183 }
144- } else if (hook instanceof BitbucketServerWebhook serverHook ) {
145- String serverURL = owner .getServerUrl ();
146- String url = getServerWebhookURL (serverURL , owner .getEndpointJenkinsRootURL ());
147184
185+ List <String > supportedPluginEvents = getPluginServerEvents (serverURL );
186+ List <String > events = pluginHook .getEvents ();
187+ if (!events .containsAll (supportedPluginEvents )) {
188+ Set <String > newEvents = new TreeSet <>(events );
189+ newEvents .addAll (supportedPluginEvents );
190+ pluginHook .setEvents (new ArrayList <>(newEvents ));
191+ logger .info (() -> "Update plugin webhook because the following events was missing: " + CollectionUtils .subtract (supportedPluginEvents , events ));
192+ updated = true ;
193+ }
194+ } else if (hook instanceof BitbucketServerWebhook serverHook ) {
195+ String url = getServerWebhookURL (serverURL , rootURL );
148196 if (!url .equals (serverHook .getUrl ())) {
149197 serverHook .setUrl (url );
150198 updated = true ;
151199 }
152200
201+ List <String > supportedNativeEvents = getNativeServerEvents (serverURL );
153202 List <String > events = serverHook .getEvents ();
154- if (events == null ) {
155- serverHook .setEvents (getNativeServerEvents (serverURL ));
156- updated = true ;
157- } else if (!events .containsAll (getNativeServerEvents (serverURL ))) {
203+ if (!events .containsAll (supportedNativeEvents )) {
158204 Set <String > newEvents = new TreeSet <>(events );
159- newEvents .addAll (getNativeServerEvents ( serverURL ) );
205+ newEvents .addAll (supportedNativeEvents );
160206 serverHook .setEvents (new ArrayList <>(newEvents ));
207+ logger .info (() -> "Update native webhook because the following events was missing: " + CollectionUtils .subtract (supportedNativeEvents , events ));
161208 updated = true ;
162209 }
163210
@@ -170,28 +217,34 @@ boolean updateHook(BitbucketWebHook hook, BitbucketSCMSource owner) {
170217 return updated ;
171218 }
172219
220+ @ NonNull
221+ private String getEndpointJenkinsRootURL (@ NonNull String serverURL ) {
222+ return AbstractBitbucketEndpoint .getEndpointJenkinsRootUrl (serverURL );
223+ }
224+
225+ @ NonNull
173226 public BitbucketWebHook getHook (BitbucketSCMSource owner ) {
174- final String serverUrl = owner .getServerUrl ();
175- final String rootUrl = owner . getEndpointJenkinsRootURL ();
227+ final String serverURL = owner .getServerUrl ();
228+ final String rootURL = getEndpointJenkinsRootURL (serverURL );
176229 final String signatureSecret = getSecret (owner .getServerUrl ());
177230
178- if (BitbucketApiUtils .isCloud (serverUrl )) {
231+ if (BitbucketApiUtils .isCloud (serverURL )) {
179232 BitbucketCloudHook hook = new BitbucketCloudHook ();
180233 hook .setEvents (CLOUD_EVENTS );
181234 hook .setActive (true );
182- hook .setDescription (description );
183- hook .setUrl (rootUrl + BitbucketSCMSourcePushHookReceiver . FULL_PATH );
235+ hook .setDescription (DESCRIPTION );
236+ hook .setUrl (getCloudWebhookURL ( serverURL , rootURL ) );
184237 hook .setSecret (signatureSecret );
185238 return hook ;
186239 }
187240
188- switch (BitbucketServerEndpoint .findWebhookImplementation (serverUrl )) {
241+ switch (BitbucketServerEndpoint .findWebhookImplementation (serverURL )) {
189242 case NATIVE : {
190243 BitbucketServerWebhook hook = new BitbucketServerWebhook ();
191244 hook .setActive (true );
192- hook .setDescription (description );
193- hook .setEvents (getNativeServerEvents (serverUrl ));
194- hook .setUrl (getServerWebhookURL (serverUrl , rootUrl ));
245+ hook .setDescription (DESCRIPTION );
246+ hook .setEvents (getNativeServerEvents (serverURL ));
247+ hook .setUrl (getServerWebhookURL (serverURL , rootURL ));
195248 hook .setSecret (signatureSecret );
196249 return hook ;
197250 }
@@ -200,8 +253,8 @@ public BitbucketWebHook getHook(BitbucketSCMSource owner) {
200253 default : {
201254 BitbucketPluginWebhook hook = new BitbucketPluginWebhook ();
202255 hook .setActive (true );
203- hook .setDescription (description );
204- hook .setUrl (getServerWebhookURL (serverUrl , rootUrl ));
256+ hook .setDescription (DESCRIPTION );
257+ hook .setUrl (getServerWebhookURL (serverURL , rootURL ));
205258 hook .setCommittersToIgnore (committersToIgnore );
206259 return hook ;
207260 }
@@ -224,9 +277,13 @@ private String getSecret(@NonNull String serverURL) {
224277 return null ;
225278 }
226279
227- private static List <String > getNativeServerEvents (String serverUrl ) {
280+ private static List <String > getPluginServerEvents (String serverURL ) {
281+ return PLUGIN_SERVER_EVENTS ;
282+ }
283+
284+ private static List <String > getNativeServerEvents (String serverURL ) {
228285 BitbucketServerEndpoint endpoint = BitbucketEndpointProvider
229- .lookupEndpoint (serverUrl , BitbucketServerEndpoint .class )
286+ .lookupEndpoint (serverURL , BitbucketServerEndpoint .class )
230287 .orElse (null );
231288 if (endpoint != null ) {
232289 switch (endpoint .getServerVersion ()) {
@@ -256,6 +313,13 @@ private static List<String> getNativeServerEvents(String serverUrl) {
256313 return NATIVE_SERVER_EVENTS_v7 ;
257314 }
258315
316+ private static String getCloudWebhookURL (String serverURL , String rootURL ) {
317+ return UriTemplate .buildFromTemplate (rootURL )
318+ .template (BitbucketSCMSourcePushHookReceiver .FULL_PATH )
319+ .build ()
320+ .expand ();
321+ }
322+
259323 private static String getServerWebhookURL (String serverURL , String rootURL ) {
260324 return UriTemplate .buildFromTemplate (rootURL )
261325 .template (BitbucketSCMSourcePushHookReceiver .FULL_PATH )
0 commit comments