diff --git a/docs/USER_GUIDE.adoc b/docs/USER_GUIDE.adoc index f40a7c4a6..ece1265a2 100644 --- a/docs/USER_GUIDE.adoc +++ b/docs/USER_GUIDE.adoc @@ -119,7 +119,7 @@ For Bitbucket Data Center only it is possible chose which webhooks implementatio - Native implementation will configure the webhooks provided by default with the Server, so it will always be available. -- Plugin implementation relies on the configuration available via specific APIs provided by the https://marketplace.atlassian.com/apps/1215474/post-webhooks-for-bitbucket?tab=overview&hosting=datacenter[Post Webhooks for Bitbucket] plugin itself. To get it worked plugin must be already pre-installed on the server instance. This provider allows custom settings managed by the _ignore committers_ trait. _Note: This specific implementation will be moved to an individual repository as soon as https://issues.jenkins.io/browse/JENKINS-74913[JENKINS-74913] is implemented._ +- Plugin implementation (*deprecated*) relies on the configuration available via specific APIs provided by the https://marketplace.atlassian.com/apps/1215474/post-webhooks-for-bitbucket?tab=overview&hosting=datacenter[Post Webhooks for Bitbucket] plugin itself. To get it worked plugin must be already pre-installed on the server instance. This provider allows custom settings managed by the _ignore committers_ trait. _Note: This specific implementation will be moved to an individual repository as soon as https://issues.jenkins.io/browse/JENKINS-74913[JENKINS-74913] is implemented._ image::images/screenshot-14.png[] @@ -131,7 +131,7 @@ image::images/screenshot-18.png[] IMPORTANT: In order to have the auto-registering process working fine the Jenkins base URL must be properly configured in _Manage Jenkins_ » _System_ -=== Webhooks signature +=== Signature verification for incoming webhooks Once Jenkins is configured to receive payloads, it will listen for any delivery that's sent to the endpoint you configured. For security reasons, you should only process deliveries from Bitbucket. To ensure your self-hosted server only processes deliveries from Bitbucket, you need to: diff --git a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/endpoints/BitbucketCloudEndpoint.java b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/endpoints/BitbucketCloudEndpoint.java index 8c4d4d5cc..fdf087729 100644 --- a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/endpoints/BitbucketCloudEndpoint.java +++ b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/endpoints/BitbucketCloudEndpoint.java @@ -95,7 +95,7 @@ public BitbucketCloudEndpoint(boolean enableCache, int teamCacheDuration, int re * credentials to use for auto-management of hooks. * @param enableHookSignature {@code true} hooks that comes Bitbucket Data * Center are signed. - * @param credentialsId The {@link StringCredentials#getId()} of the + * @param hookSignatureCredentialsId The {@link StringCredentials#getId()} of the * credentials to use for verify the signature of payload. */ @DataBoundConstructor diff --git a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/endpoints/BitbucketServerEndpoint.java b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/endpoints/BitbucketServerEndpoint.java index 941dff42b..4133f2936 100644 --- a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/endpoints/BitbucketServerEndpoint.java +++ b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/endpoints/BitbucketServerEndpoint.java @@ -124,7 +124,7 @@ public BitbucketServerEndpoint(@CheckForNull String displayName, @NonNull String * credentials to use for auto-management of hooks. * @param enableHookSignature {@code true} hooks that comes Bitbucket Data * Center are signed. - * @param credentialsId The {@link StringCredentials#getId()} of the + * @param hookSignatureCredentialsId The {@link StringCredentials#getId()} of the * credentials to use for verify the signature of payload. */ @DataBoundConstructor diff --git a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/hooks/BitbucketSCMSourcePushHookReceiver.java b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/hooks/BitbucketSCMSourcePushHookReceiver.java index 08cb6e051..37be59c3c 100644 --- a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/hooks/BitbucketSCMSourcePushHookReceiver.java +++ b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/hooks/BitbucketSCMSourcePushHookReceiver.java @@ -107,16 +107,20 @@ public HttpResponse doNotify(StaplerRequest2 req) throws IOException { return HttpResponses.error(HttpServletResponse.SC_BAD_REQUEST, "X-Event-Key HTTP header invalid: " + eventKey); } - String bitbucketKey = req.getHeader("X-Bitbucket-Type"); + String bitbucketKey = req.getHeader("X-Bitbucket-Type"); // specific header from Plugin implementation String serverURL = req.getParameter("server_url"); BitbucketType instanceType = null; if (bitbucketKey != null) { instanceType = BitbucketType.fromString(bitbucketKey); } - if (instanceType == null && serverURL != null) { - LOGGER.log(Level.FINE, "server_url request parameter found. Bitbucket Native Server webhook incoming."); - instanceType = BitbucketType.SERVER; + if (serverURL != null) { + if (instanceType == null) { + LOGGER.log(Level.FINE, "server_url request parameter found. Bitbucket Native Server webhook incoming."); + instanceType = BitbucketType.SERVER; + } else { + LOGGER.log(Level.FINE, "X-Bitbucket-Type header / server_url request parameter found. Bitbucket Plugin Server webhook incoming."); + } } else { LOGGER.log(Level.FINE, "X-Bitbucket-Type header / server_url request parameter not found. Bitbucket Cloud webhook incoming."); instanceType = BitbucketType.CLOUD; @@ -179,7 +183,7 @@ private HttpResponseException checkSignature(@NonNull StaplerRequest2 req, @NonN String requestId = ObjectUtils.firstNonNull(req.getHeader("X-Request-UUID"), req.getHeader("X-Request-Id")); String hookSignatureCredentialsId = endpoint.getHookSignatureCredentialsId(); LOGGER.log(Level.WARNING, "No credentials {0} found to verify the signature of incoming webhook {1} request {2}", new Object[] { hookSignatureCredentialsId, hookId, requestId }); - return HttpResponses.error(HttpServletResponse.SC_FORBIDDEN, "No credentials " + hookSignatureCredentialsId + " found to verify the signature"); + return HttpResponses.error(HttpServletResponse.SC_FORBIDDEN, "No credentials " + hookSignatureCredentialsId + " found in Jenkins to verify the signature"); } return null; } diff --git a/src/test/java/com/cloudbees/jenkins/plugins/bitbucket/hooks/BitbucketSCMSourcePushHookReceiverTest.java b/src/test/java/com/cloudbees/jenkins/plugins/bitbucket/hooks/BitbucketSCMSourcePushHookReceiverTest.java index 5692d0be4..f67196bb5 100644 --- a/src/test/java/com/cloudbees/jenkins/plugins/bitbucket/hooks/BitbucketSCMSourcePushHookReceiverTest.java +++ b/src/test/java/com/cloudbees/jenkins/plugins/bitbucket/hooks/BitbucketSCMSourcePushHookReceiverTest.java @@ -66,8 +66,20 @@ static void init(JenkinsRule rule) { @BeforeEach void setup() throws Exception { req = mock(StaplerRequest2.class); + + hookProcessor = mock(HookProcessor.class); + sut = new BitbucketSCMSourcePushHookReceiver() { + @Override + HookProcessor getHookProcessor(HookEventType type) { + return hookProcessor; + } + }; + + credentialsId = BitbucketTestUtil.registerHookCredentials("Gkvl$k$wyNpQAF42", j).getId(); + } + + private void mockCloudRequest() { when(req.getRemoteHost()).thenReturn("https://bitbucket.org"); - when(req.getParameter("server_url")).thenReturn("https://bitbucket.org"); when(req.getRemoteAddr()).thenReturn("185.166.143.48"); when(req.getScheme()).thenReturn("https"); when(req.getServerName()).thenReturn("jenkins.example.com"); @@ -78,27 +90,44 @@ void setup() throws Exception { when(req.getHeader("Content-Type")).thenReturn("application/json"); when(req.getHeader("X-Hook-UUID")).thenReturn(UUID.randomUUID().toString()); when(req.getHeader("X-Request-UUID")).thenReturn(UUID.randomUUID().toString()); + when(req.getHeader("traceparent")).thenReturn(UUID.randomUUID().toString()); + when(req.getHeader("User-Agent")).thenReturn("Bitbucket-Webhooks/2.0"); + } - hookProcessor = mock(HookProcessor.class); - sut = new BitbucketSCMSourcePushHookReceiver() { - @Override - HookProcessor getHookProcessor(HookEventType type) { - return hookProcessor; - } - }; + private void mockServerRequest(String serverURL) { + when(req.getRemoteHost()).thenReturn("http://localhost:7990"); + when(req.getParameter("server_url")).thenReturn(serverURL); + when(req.getRemoteAddr()).thenReturn("127.0.0.1"); + when(req.getScheme()).thenReturn("https"); + when(req.getServerName()).thenReturn("jenkins.example.com"); + when(req.getLocalPort()).thenReturn(80); + when(req.getRequestURI()).thenReturn("/bitbucket-scmsource-hook/notify"); + when(req.getHeader("Content-Type")).thenReturn("application/json; charset=utf-8"); + when(req.getHeader("X-Request-Id")).thenReturn(UUID.randomUUID().toString()); + when(req.getHeader("User-Agent")).thenReturn("Atlassian HttpClient 4.2.0 / Bitbucket-9.5.2 (9005002) / Default"); + } - credentialsId = BitbucketTestUtil.registerHookCredentials("Gkvl$k$wyNpQAF42", j).getId(); + private void mockPluginRequest(String serverURL) { + when(req.getRemoteHost()).thenReturn("http://localhost:7990"); + when(req.getParameter("server_url")).thenReturn(serverURL); + when(req.getRemoteAddr()).thenReturn("127.0.0.1"); + when(req.getScheme()).thenReturn("https"); + when(req.getServerName()).thenReturn("jenkins.example.com"); + when(req.getLocalPort()).thenReturn(80); + when(req.getRequestURI()).thenReturn("/bitbucket-scmsource-hook/notify"); + when(req.getHeader("Content-Type")).thenReturn("application/json; charset=utf-8"); + when(req.getHeader("X-Bitbucket-Type")).thenReturn("server"); + when(req.getHeader("User-Agent")).thenReturn("Bitbucket version: 8.18.0, Post webhook plugin version: 7.13.41-SNAPSHOT"); } @Test - void test_signature_is_missing_from_cloud_payload() throws Exception { + void test_cloud_signature_is_missing() throws Exception { BitbucketCloudEndpoint endpoint = new BitbucketCloudEndpoint(false, 0, 0, false, null , true, credentialsId); endpoint.setBitbucketJenkinsRootUrl("http://jenkins.acme.com:8080/jenkins"); BitbucketEndpointConfiguration.get().updateEndpoint(endpoint); try { when(req.getHeader("X-Event-Key")).thenReturn("repo:push"); - when(req.getHeader("X-Bitbucket-Type")).thenReturn("cloud"); when(req.getInputStream()).thenReturn(loadResource("cloud/signed_payload.json")); /*HttpResponse response = */sut.doNotify(req); @@ -110,14 +139,14 @@ void test_signature_is_missing_from_cloud_payload() throws Exception { } @Test - void test_signature_from_cloud() throws Exception { + void test_cloud_signature() throws Exception { + mockCloudRequest(); BitbucketCloudEndpoint endpoint = new BitbucketCloudEndpoint(false, 0, 0, false, null , true, credentialsId); endpoint.setBitbucketJenkinsRootUrl("http://jenkins.acme.com:8080/jenkins"); BitbucketEndpointConfiguration.get().updateEndpoint(endpoint); try { when(req.getHeader("X-Event-Key")).thenReturn("repo:push"); - when(req.getHeader("X-Bitbucket-Type")).thenReturn("cloud"); when(req.getHeader("X-Hub-Signature")).thenReturn("sha256=f205c729821c6954aff2afe72b965c34015b4baf96ea8ddc2cc44999c014a035"); when(req.getInputStream()).thenReturn(loadResource("cloud/signed_payload.json")); @@ -134,14 +163,13 @@ void test_signature_from_cloud() throws Exception { } @Test - void test_bad_signature_from_cloud() throws Exception { + void test_cloud_bad_signature() throws Exception { BitbucketCloudEndpoint endpoint = new BitbucketCloudEndpoint(false, 0, 0, false, null , true, credentialsId); endpoint.setBitbucketJenkinsRootUrl("http://jenkins.acme.com:8080/jenkins"); BitbucketEndpointConfiguration.get().updateEndpoint(endpoint); try { when(req.getHeader("X-Event-Key")).thenReturn("repo:push"); - when(req.getHeader("X-Bitbucket-Type")).thenReturn("cloud"); when(req.getHeader("X-Hub-Signature")).thenReturn("sha256=f205c729821c6954aff2afe72b965c34015b4baf96ea8ddc2cc44999c014a036"); when(req.getInputStream()).thenReturn(loadResource("cloud/signed_payload.json")); @@ -154,16 +182,14 @@ void test_bad_signature_from_cloud() throws Exception { } @Test - void test_signature_from_native_server() throws Exception { + void test_native_signature() throws Exception { BitbucketServerEndpoint endpoint = new BitbucketServerEndpoint("datacenter", "http://localhost:7990/bitbucket", false, null, true, credentialsId); endpoint.setBitbucketJenkinsRootUrl("https://jenkins.example.com"); BitbucketEndpointConfiguration.get().updateEndpoint(endpoint); try { - when(req.getRemoteHost()).thenReturn("http://localhost:7990"); - when(req.getParameter("server_url")).thenReturn(endpoint.getServerUrl()); + mockServerRequest(endpoint.getServerUrl()); when(req.getHeader("X-Event-Key")).thenReturn("repo:refs_changed"); - when(req.getHeader("X-Request-Id")).thenReturn("2b15f131-4d3a-436e-bc63-caa9ae92580d"); when(req.getHeader("X-Hub-Signature")).thenReturn("sha256=4ffba9e7b58ea3d7e1a230446e8c92baea0aeec89b73f598932387254f0de13e"); when(req.getInputStream()).thenReturn(loadResource("native/signed_payload.json")); @@ -173,7 +199,7 @@ void test_signature_from_native_server() throws Exception { eq(HookEventType.SERVER_REFS_CHANGED), anyString(), eq(BitbucketType.SERVER), - eq("http://localhost:7990/185.166.143.48 ⇒ https://jenkins.example.com:80/bitbucket-scmsource-hook/notify"), + eq("http://localhost:7990/127.0.0.1 ⇒ https://jenkins.example.com:80/bitbucket-scmsource-hook/notify"), eq(endpoint.getServerUrl())); // verify bad signature @@ -189,16 +215,132 @@ void test_signature_from_native_server() throws Exception { } @Test - void test_bad_signature_from_native_server() throws Exception { + void test_native_ping() throws Exception { + BitbucketServerEndpoint endpoint = new BitbucketServerEndpoint("datacenter", "http://localhost:7990/bitbucket", false, null, false, null); + endpoint.setBitbucketJenkinsRootUrl("https://jenkins.example.com"); + BitbucketEndpointConfiguration.get().updateEndpoint(endpoint); + + try { + mockServerRequest(endpoint.getServerUrl()); + when(req.getHeader("X-Event-Key")).thenReturn("diagnostics:ping"); + when(req.getInputStream()).thenReturn(loadResource("native/ping_payload.json")); + + sut.doNotify(req); + verify(hookProcessor).process( + eq(HookEventType.SERVER_PING), + anyString(), + eq(BitbucketType.SERVER), + eq("http://localhost:7990/127.0.0.1 ⇒ https://jenkins.example.com:80/bitbucket-scmsource-hook/notify"), + eq(endpoint.getServerUrl())); + } finally { + BitbucketEndpointConfiguration.get().removeEndpoint(endpoint.getServerUrl()); + } + } + + @Test + void test_plugin_pullrequest_created() throws Exception { + BitbucketServerEndpoint endpoint = new BitbucketServerEndpoint("datacenter", "http://localhost:7990/bitbucket", false, null, false, null); + endpoint.setBitbucketJenkinsRootUrl("https://jenkins.example.com"); + BitbucketEndpointConfiguration.get().updateEndpoint(endpoint); + + try { + mockPluginRequest(endpoint.getServerUrl()); + when(req.getHeader("X-Event-Key")).thenReturn("pullrequest:created"); + when(req.getInputStream()).thenReturn(loadResource("plugin/pullrequest_created.json")); + + sut.doNotify(req); + verify(hookProcessor).process( + eq(HookEventType.PULL_REQUEST_CREATED), + anyString(), + eq(BitbucketType.SERVER), + eq("http://localhost:7990/127.0.0.1 ⇒ https://jenkins.example.com:80/bitbucket-scmsource-hook/notify"), + eq(endpoint.getServerUrl())); + } finally { + BitbucketEndpointConfiguration.get().removeEndpoint(endpoint.getServerUrl()); + } + } + + @Test + void test_plugin_pullrequest_updated() throws Exception { + BitbucketServerEndpoint endpoint = new BitbucketServerEndpoint("datacenter", "http://localhost:7990/bitbucket", false, null, false, null); + endpoint.setBitbucketJenkinsRootUrl("https://jenkins.example.com"); + BitbucketEndpointConfiguration.get().updateEndpoint(endpoint); + + try { + mockPluginRequest(endpoint.getServerUrl()); + when(req.getHeader("X-Event-Key")).thenReturn("pullrequest:updated"); + when(req.getInputStream()).thenReturn(loadResource("plugin/pullrequest_updated.json")); + + sut.doNotify(req); + verify(hookProcessor).process( + eq(HookEventType.PULL_REQUEST_UPDATED), + anyString(), + eq(BitbucketType.SERVER), + eq("http://localhost:7990/127.0.0.1 ⇒ https://jenkins.example.com:80/bitbucket-scmsource-hook/notify"), + eq(endpoint.getServerUrl())); + } finally { + BitbucketEndpointConfiguration.get().removeEndpoint(endpoint.getServerUrl()); + } + } + + @Test + void test_plugin_pullrequest_merged() throws Exception { + BitbucketServerEndpoint endpoint = new BitbucketServerEndpoint("datacenter", "http://localhost:7990/bitbucket", false, null, false, null); + endpoint.setBitbucketJenkinsRootUrl("https://jenkins.example.com"); + BitbucketEndpointConfiguration.get().updateEndpoint(endpoint); + + try { + mockPluginRequest(endpoint.getServerUrl()); + when(req.getHeader("X-Event-Key")).thenReturn("pullrequest:fulfilled"); + when(req.getInputStream()).thenReturn(loadResource("plugin/pullrequest_merged.json")); + + sut.doNotify(req); + verify(hookProcessor).process( + eq(HookEventType.PULL_REQUEST_MERGED), + anyString(), + eq(BitbucketType.SERVER), + eq("http://localhost:7990/127.0.0.1 ⇒ https://jenkins.example.com:80/bitbucket-scmsource-hook/notify"), + eq(endpoint.getServerUrl())); + } finally { + BitbucketEndpointConfiguration.get().removeEndpoint(endpoint.getServerUrl()); + } + } + + @Test + void test_plugin_create_branch() throws Exception { + BitbucketServerEndpoint endpoint = new BitbucketServerEndpoint("datacenter", "http://localhost:7990/bitbucket", false, null, false, null); + endpoint.setBitbucketJenkinsRootUrl("https://jenkins.example.com"); + BitbucketEndpointConfiguration.get().updateEndpoint(endpoint); + + try { + mockPluginRequest(endpoint.getServerUrl()); + when(req.getHeader("X-Event-Key")).thenReturn("repo:push"); + when(req.getInputStream()).thenReturn(loadResource("plugin/branch_created.json")); + // when(req.getInputStream()).thenReturn(loadResource("plugin/branch_deleted.json")); + // when(req.getInputStream()).thenReturn(loadResource("plugin/commit_update.json")); + // when(req.getInputStream()).thenReturn(loadResource("plugin/commit_update2.json")); + + sut.doNotify(req); + verify(hookProcessor).process( + eq(HookEventType.PUSH), + anyString(), + eq(BitbucketType.SERVER), + eq("http://localhost:7990/127.0.0.1 ⇒ https://jenkins.example.com:80/bitbucket-scmsource-hook/notify"), + eq(endpoint.getServerUrl())); + } finally { + BitbucketEndpointConfiguration.get().removeEndpoint(endpoint.getServerUrl()); + } + } + + @Test + void test_native_bad_signature() throws Exception { BitbucketServerEndpoint endpoint = new BitbucketServerEndpoint("datacenter", "http://localhost:7990/bitbucket", false, null, true, credentialsId); endpoint.setBitbucketJenkinsRootUrl("https://jenkins.example.com"); BitbucketEndpointConfiguration.get().updateEndpoint(endpoint); try { - when(req.getRemoteHost()).thenReturn("http://localhost:7990"); - when(req.getParameter("server_url")).thenReturn(endpoint.getServerUrl()); + mockServerRequest(endpoint.getServerUrl()); when(req.getHeader("X-Event-Key")).thenReturn("repo:refs_changed"); - when(req.getHeader("X-Request-Id")).thenReturn("2b15f131-4d3a-436e-bc63-caa9ae92580d"); when(req.getHeader("X-Hub-Signature")).thenReturn("sha256=4ffba9e7b58ea3d7e1a230446e8c92baea0aeec89b73f598932387254f0de13f"); when(req.getInputStream()).thenReturn(loadResource("native/signed_payload.json")); /*HttpResponse response = */ sut.doNotify(req); @@ -210,9 +352,9 @@ void test_bad_signature_from_native_server() throws Exception { } @Test - void test_pullrequest_created() throws Exception { + void test_cloud_pullrequest_created() throws Exception { + mockCloudRequest(); when(req.getHeader("X-Event-Key")).thenReturn("pullrequest:created"); - when(req.getHeader("X-Bitbucket-Type")).thenReturn("cloud"); when(req.getInputStream()).thenReturn(loadResource("cloud/pullrequest_created.json")); sut.doNotify(req); @@ -226,9 +368,9 @@ void test_pullrequest_created() throws Exception { } @Test - void test_pullrequest_declined() throws Exception { + void test_cloud_pullrequest_declined() throws Exception { + mockCloudRequest(); when(req.getHeader("X-Event-Key")).thenReturn("pullrequest:rejected"); - when(req.getHeader("X-Bitbucket-Type")).thenReturn("cloud"); when(req.getInputStream()).thenReturn(loadResource("cloud/pullrequest_rejected.json")); sut.doNotify(req); diff --git a/src/test/resources/com/cloudbees/jenkins/plugins/bitbucket/hooks/native/ping_payload.json b/src/test/resources/com/cloudbees/jenkins/plugins/bitbucket/hooks/native/ping_payload.json new file mode 100644 index 000000000..69fbf10ab --- /dev/null +++ b/src/test/resources/com/cloudbees/jenkins/plugins/bitbucket/hooks/native/ping_payload.json @@ -0,0 +1,3 @@ +{ + "test": true +} \ No newline at end of file diff --git a/src/test/resources/com/cloudbees/jenkins/plugins/bitbucket/hooks/plugin/branch_created.json b/src/test/resources/com/cloudbees/jenkins/plugins/bitbucket/hooks/plugin/branch_created.json new file mode 100644 index 000000000..74e2df820 --- /dev/null +++ b/src/test/resources/com/cloudbees/jenkins/plugins/bitbucket/hooks/plugin/branch_created.json @@ -0,0 +1,48 @@ +{ + "actor": { + "username": "admin", + "displayName": "Administrator", + "emailAddress": "admin@example.com" + }, + "repository": { + "scmId": "git", + "project": { + "key": "PROJECT_1", + "name": "Project 1" + }, + "slug": "rep_1", + "links": { + "self": [ + { + "href": "http://localhost:7990/bitbucket/projects/PROJECT_1/repos/rep_1/browse" + } + ] + }, + "public": false, + "ownerName": "PROJECT_1", + "fullName": "PROJECT_1/rep_1", + "owner": { + "username": "PROJECT_1", + "displayName": "PROJECT_1", + "emailAddress": null + } + }, + "push": { + "changes": [ + { + "created": true, + "closed": false, + "new": { + "type": "branch", + "name": "test-webhook", + "target": { + "type": "commit", + "hash": "417b2f673581ee6000e260a5fa65e62b56c7a3cd", + "commitMessage": "Test webhooks" + } + }, + "old": null + } + ] + } +} \ No newline at end of file diff --git a/src/test/resources/com/cloudbees/jenkins/plugins/bitbucket/hooks/plugin/branch_deleted.json b/src/test/resources/com/cloudbees/jenkins/plugins/bitbucket/hooks/plugin/branch_deleted.json new file mode 100644 index 000000000..ce6298c18 --- /dev/null +++ b/src/test/resources/com/cloudbees/jenkins/plugins/bitbucket/hooks/plugin/branch_deleted.json @@ -0,0 +1,48 @@ +{ + "actor": { + "username": "admin", + "displayName": "Administrator", + "emailAddress": "admin@example.com" + }, + "repository": { + "scmId": "git", + "project": { + "key": "PROJECT_1", + "name": "Project 1" + }, + "slug": "rep_1", + "links": { + "self": [ + { + "href": "http://localhost:7990/bitbucket/projects/PROJECT_1/repos/rep_1/browse" + } + ] + }, + "public": false, + "ownerName": "PROJECT_1", + "fullName": "PROJECT_1/rep_1", + "owner": { + "username": "PROJECT_1", + "displayName": "PROJECT_1", + "emailAddress": null + } + }, + "push": { + "changes": [ + { + "created": false, + "closed": true, + "new": null, + "old": { + "type": "branch", + "name": "test-webhook", + "target": { + "type": "commit", + "hash": "c0158b3e6c8cecf3bddc39d20957a98660cd23fd", + "commitMessage": "Test webhook 2" + } + } + } + ] + } +} \ No newline at end of file diff --git a/src/test/resources/com/cloudbees/jenkins/plugins/bitbucket/hooks/plugin/commit_update.json b/src/test/resources/com/cloudbees/jenkins/plugins/bitbucket/hooks/plugin/commit_update.json new file mode 100644 index 000000000..14041a233 --- /dev/null +++ b/src/test/resources/com/cloudbees/jenkins/plugins/bitbucket/hooks/plugin/commit_update.json @@ -0,0 +1,56 @@ +{ + "actor": { + "username": "admin", + "displayName": "Administrator", + "emailAddress": "admin@example.com" + }, + "repository": { + "scmId": "git", + "project": { + "key": "PROJECT_1", + "name": "Project 1" + }, + "slug": "rep_1", + "links": { + "self": [ + { + "href": "http://localhost:7990/bitbucket/projects/PROJECT_1/repos/rep_1/browse" + } + ] + }, + "public": false, + "ownerName": "PROJECT_1", + "fullName": "PROJECT_1/rep_1", + "owner": { + "username": "PROJECT_1", + "displayName": "PROJECT_1", + "emailAddress": null + } + }, + "push": { + "changes": [ + { + "created": false, + "closed": false, + "new": { + "type": "branch", + "name": "test-webhook", + "target": { + "type": "commit", + "hash": "c0158b3e6c8cecf3bddc39d20957a98660cd23fd", + "commitMessage": "Test webhook 2" + } + }, + "old": { + "type": "branch", + "name": "test-webhook", + "target": { + "type": "commit", + "hash": "417b2f673581ee6000e260a5fa65e62b56c7a3cd", + "commitMessage": "Test webhooks" + } + } + } + ] + } +} \ No newline at end of file diff --git a/src/test/resources/com/cloudbees/jenkins/plugins/bitbucket/hooks/plugin/commit_update2.json b/src/test/resources/com/cloudbees/jenkins/plugins/bitbucket/hooks/plugin/commit_update2.json new file mode 100644 index 000000000..1d6ff5a91 --- /dev/null +++ b/src/test/resources/com/cloudbees/jenkins/plugins/bitbucket/hooks/plugin/commit_update2.json @@ -0,0 +1,56 @@ +{ + "actor": { + "username": "admin", + "displayName": "Administrator", + "emailAddress": "admin@example.com" + }, + "repository": { + "scmId": "git", + "project": { + "key": "PROJECT_1", + "name": "Project 1" + }, + "slug": "rep_1", + "links": { + "self": [ + { + "href": "http://localhost:7990/bitbucket/projects/PROJECT_1/repos/rep_1/browse" + } + ] + }, + "public": false, + "ownerName": "PROJECT_1", + "fullName": "PROJECT_1/rep_1", + "owner": { + "username": "PROJECT_1", + "displayName": "PROJECT_1", + "emailAddress": null + } + }, + "push": { + "changes": [ + { + "created": false, + "closed": false, + "new": { + "type": "branch", + "name": "master", + "target": { + "type": "commit", + "hash": "500cf91e7b4b7d9f995cdb6e81cb5538216ac02e", + "commitMessage": "Pull request #1: Test webhooks\n\nMerge in PROJECT_1/rep_1 from test-webhook to master\n\n* commit 'c0158b3e6c8cecf3bddc39d20957a98660cd23fd':\n Test webhook 2\n Test webhooks" + } + }, + "old": { + "type": "branch", + "name": "master", + "target": { + "type": "commit", + "hash": "0a943a29376f2336b78312d99e65da17048951db", + "commitMessage": "Copy C.zip as D.zip" + } + } + } + ] + } +} \ No newline at end of file diff --git a/src/test/resources/com/cloudbees/jenkins/plugins/bitbucket/hooks/plugin/pullrequest_created.json b/src/test/resources/com/cloudbees/jenkins/plugins/bitbucket/hooks/plugin/pullrequest_created.json new file mode 100644 index 000000000..765d0258e --- /dev/null +++ b/src/test/resources/com/cloudbees/jenkins/plugins/bitbucket/hooks/plugin/pullrequest_created.json @@ -0,0 +1,106 @@ +{ + "actor": { + "username": "admin", + "displayName": "Administrator", + "emailAddress": "admin@example.com" + }, + "pullrequest": { + "id": "1", + "title": "Test webhooks", + "link": "http://localhost:7990/bitbucket/projects/PROJECT_1/repos/rep_1/pull-requests/1", + "authorLogin": "Administrator", + "fromRef": { + "repository": { + "scmId": "git", + "project": { + "key": "PROJECT_1", + "name": "Project 1" + }, + "slug": "rep_1", + "links": { + "self": [ + { + "href": "http://localhost:7990/bitbucket/projects/PROJECT_1/repos/rep_1/browse" + } + ] + }, + "public": false, + "ownerName": "PROJECT_1", + "fullName": "PROJECT_1/rep_1", + "owner": { + "username": "PROJECT_1", + "displayName": "PROJECT_1", + "emailAddress": null + } + }, + "branch": { + "rawNode": "417b2f673581ee6000e260a5fa65e62b56c7a3cd", + "name": "test-webhook" + }, + "commit": { + "message": null, + "date": "6/4/25 11:12 PM", + "hash": "417b2f673581ee6000e260a5fa65e62b56c7a3cd", + "authorTimestamp": 1749093143379 + } + }, + "toRef": { + "repository": { + "scmId": "git", + "project": { + "key": "PROJECT_1", + "name": "Project 1" + }, + "slug": "rep_1", + "links": { + "self": [ + { + "href": "http://localhost:7990/bitbucket/projects/PROJECT_1/repos/rep_1/browse" + } + ] + }, + "public": false, + "ownerName": "PROJECT_1", + "fullName": "PROJECT_1/rep_1", + "owner": { + "username": "PROJECT_1", + "displayName": "PROJECT_1", + "emailAddress": null + } + }, + "branch": { + "rawNode": "0a943a29376f2336b78312d99e65da17048951db", + "name": "master" + }, + "commit": { + "message": null, + "date": "6/4/25 11:12 PM", + "hash": "0a943a29376f2336b78312d99e65da17048951db", + "authorTimestamp": 1749093143385 + } + } + }, + "repository": { + "scmId": "git", + "project": { + "key": "PROJECT_1", + "name": "Project 1" + }, + "slug": "rep_1", + "links": { + "self": [ + { + "href": "http://localhost:7990/bitbucket/projects/PROJECT_1/repos/rep_1/browse" + } + ] + }, + "public": false, + "ownerName": "PROJECT_1", + "fullName": "PROJECT_1/rep_1", + "owner": { + "username": "PROJECT_1", + "displayName": "PROJECT_1", + "emailAddress": null + } + } +} \ No newline at end of file diff --git a/src/test/resources/com/cloudbees/jenkins/plugins/bitbucket/hooks/plugin/pullrequest_merged.json b/src/test/resources/com/cloudbees/jenkins/plugins/bitbucket/hooks/plugin/pullrequest_merged.json new file mode 100644 index 000000000..bb25f177d --- /dev/null +++ b/src/test/resources/com/cloudbees/jenkins/plugins/bitbucket/hooks/plugin/pullrequest_merged.json @@ -0,0 +1,106 @@ +{ + "actor": { + "username": "admin", + "displayName": "Administrator", + "emailAddress": "admin@example.com" + }, + "pullrequest": { + "id": "1", + "title": "Test webhooks", + "link": "http://localhost:7990/bitbucket/projects/PROJECT_1/repos/rep_1/pull-requests/1", + "authorLogin": "Administrator", + "fromRef": { + "repository": { + "scmId": "git", + "project": { + "key": "PROJECT_1", + "name": "Project 1" + }, + "slug": "rep_1", + "links": { + "self": [ + { + "href": "http://localhost:7990/bitbucket/projects/PROJECT_1/repos/rep_1/browse" + } + ] + }, + "public": false, + "ownerName": "PROJECT_1", + "fullName": "PROJECT_1/rep_1", + "owner": { + "username": "PROJECT_1", + "displayName": "PROJECT_1", + "emailAddress": null + } + }, + "branch": { + "rawNode": "c0158b3e6c8cecf3bddc39d20957a98660cd23fd", + "name": "test-webhook" + }, + "commit": { + "message": null, + "date": "6/4/25 11:15 PM", + "hash": "c0158b3e6c8cecf3bddc39d20957a98660cd23fd", + "authorTimestamp": 1749093354789 + } + }, + "toRef": { + "repository": { + "scmId": "git", + "project": { + "key": "PROJECT_1", + "name": "Project 1" + }, + "slug": "rep_1", + "links": { + "self": [ + { + "href": "http://localhost:7990/bitbucket/projects/PROJECT_1/repos/rep_1/browse" + } + ] + }, + "public": false, + "ownerName": "PROJECT_1", + "fullName": "PROJECT_1/rep_1", + "owner": { + "username": "PROJECT_1", + "displayName": "PROJECT_1", + "emailAddress": null + } + }, + "branch": { + "rawNode": "500cf91e7b4b7d9f995cdb6e81cb5538216ac02e", + "name": "master" + }, + "commit": { + "message": null, + "date": "6/4/25 11:15 PM", + "hash": "500cf91e7b4b7d9f995cdb6e81cb5538216ac02e", + "authorTimestamp": 1749093354790 + } + } + }, + "repository": { + "scmId": "git", + "project": { + "key": "PROJECT_1", + "name": "Project 1" + }, + "slug": "rep_1", + "links": { + "self": [ + { + "href": "http://localhost:7990/bitbucket/projects/PROJECT_1/repos/rep_1/browse" + } + ] + }, + "public": false, + "ownerName": "PROJECT_1", + "fullName": "PROJECT_1/rep_1", + "owner": { + "username": "PROJECT_1", + "displayName": "PROJECT_1", + "emailAddress": null + } + } +} \ No newline at end of file diff --git a/src/test/resources/com/cloudbees/jenkins/plugins/bitbucket/hooks/plugin/pullrequest_updated.json b/src/test/resources/com/cloudbees/jenkins/plugins/bitbucket/hooks/plugin/pullrequest_updated.json new file mode 100644 index 000000000..c862c9976 --- /dev/null +++ b/src/test/resources/com/cloudbees/jenkins/plugins/bitbucket/hooks/plugin/pullrequest_updated.json @@ -0,0 +1,106 @@ +{ + "actor": { + "username": "admin", + "displayName": "Administrator", + "emailAddress": "admin@example.com" + }, + "pullrequest": { + "id": "1", + "title": "Test webhooks", + "link": "http://localhost:7990/bitbucket/projects/PROJECT_1/repos/rep_1/pull-requests/1", + "authorLogin": "Administrator", + "fromRef": { + "repository": { + "scmId": "git", + "project": { + "key": "PROJECT_1", + "name": "Project 1" + }, + "slug": "rep_1", + "links": { + "self": [ + { + "href": "http://localhost:7990/bitbucket/projects/PROJECT_1/repos/rep_1/browse" + } + ] + }, + "public": false, + "ownerName": "PROJECT_1", + "fullName": "PROJECT_1/rep_1", + "owner": { + "username": "PROJECT_1", + "displayName": "PROJECT_1", + "emailAddress": null + } + }, + "branch": { + "rawNode": "c0158b3e6c8cecf3bddc39d20957a98660cd23fd", + "name": "test-webhook" + }, + "commit": { + "message": null, + "date": "6/4/25 11:12 PM", + "hash": "c0158b3e6c8cecf3bddc39d20957a98660cd23fd", + "authorTimestamp": 1749093171992 + } + }, + "toRef": { + "repository": { + "scmId": "git", + "project": { + "key": "PROJECT_1", + "name": "Project 1" + }, + "slug": "rep_1", + "links": { + "self": [ + { + "href": "http://localhost:7990/bitbucket/projects/PROJECT_1/repos/rep_1/browse" + } + ] + }, + "public": false, + "ownerName": "PROJECT_1", + "fullName": "PROJECT_1/rep_1", + "owner": { + "username": "PROJECT_1", + "displayName": "PROJECT_1", + "emailAddress": null + } + }, + "branch": { + "rawNode": "0a943a29376f2336b78312d99e65da17048951db", + "name": "master" + }, + "commit": { + "message": null, + "date": "6/4/25 11:12 PM", + "hash": "0a943a29376f2336b78312d99e65da17048951db", + "authorTimestamp": 1749093171993 + } + } + }, + "repository": { + "scmId": "git", + "project": { + "key": "PROJECT_1", + "name": "Project 1" + }, + "slug": "rep_1", + "links": { + "self": [ + { + "href": "http://localhost:7990/bitbucket/projects/PROJECT_1/repos/rep_1/browse" + } + ] + }, + "public": false, + "ownerName": "PROJECT_1", + "fullName": "PROJECT_1/rep_1", + "owner": { + "username": "PROJECT_1", + "displayName": "PROJECT_1", + "emailAddress": null + } + } +} \ No newline at end of file