├── .gitignore ├── Jenkinsfile ├── LICENSE ├── README.md ├── images └── configure-token.png ├── pom.xml └── src ├── main ├── java │ └── com │ │ └── igalg │ │ └── jenkins │ │ └── plugins │ │ └── mswt │ │ ├── ComputedFolderWebHookRequestReceiver.java │ │ ├── locator │ │ ├── ComputedFolderLocator.java │ │ ├── ComputedFolderWebHookTriggerLocator.java │ │ ├── JenkinsInstanceComputedFolderLocator.java │ │ └── LocatedComputedFolder.java │ │ └── trigger │ │ ├── ComputedFolderWebHookTrigger.java │ │ └── ComputedFolderWebHookTriggerResult.java └── resources │ ├── com │ └── igalg │ │ └── jenkins │ │ └── plugins │ │ └── mswt │ │ └── trigger │ │ └── ComputedFolderWebHookTrigger │ │ ├── config.jelly │ │ ├── help-token.html │ │ └── help.html │ └── index.jelly └── test └── java └── com └── igalg └── jenkins └── plugins └── mswt ├── ComputedFolderWebHookRequestReceiverTest.java └── locator └── ComputedFolderWebHookTriggerLocatorTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | *.versionsBackup 2 | .factorypath 3 | .classpath 4 | .project 5 | .settings 6 | .okhttpcache 7 | target 8 | work 9 | .idea 10 | *iml 11 | bin 12 | -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- 1 | #!groovy 2 | 3 | buildPlugin() 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 igalg 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Multibranch Scan Webhook Trigger 2 | 3 | [![Build Status](https://ci.jenkins.io/job/Plugins/job/multibranch-scan-webhook-trigger-plugin/job/master/badge/icon)](https://ci.jenkins.io/job/Plugins/job/multibranch-scan-webhook-trigger-plugin) 4 | 5 | All multibranch projects comes with build in periodically scan trigger that polls scm and check wich branches has changed and than build those branches. 6 | 7 | This is a Jenkins plugin that add functionality to do this scan on webhook: 8 | 9 | 1. Receive any HTTP request, `JENKINS_URL/multibranch-webhook-trigger/invoke?token=TOKENHERE` 10 | 2. Trigger a multibranch jobs scan that matches the token 11 | 12 | 13 | 14 | ### Configure the Token parameter 15 | 16 | There is a special `token` parameter. When supplied, the invocation will only trigger jobs with that exact token. The token also allows invocations without any other authentication credentials. 17 | 18 | ![Parameter](https://github.com/jenkinsci/multibranch-scan-webhook-trigger/blob/master/images/configure-token.png) 19 | 20 | The token can be supplied as a: 21 | 22 | * Request parameter: `curl -POST http://localhost:8080/jenkins/multibranch-webhook-trigger/invoke?token=my-token` 23 | * Token header: `curl -POST -H "token: my-token" http://localhost:8080/jenkins/multibranch-webhook-trigger/invoke` 24 | 25 | -------------------------------------------------------------------------------- /images/configure-token.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jenkinsci/multibranch-scan-webhook-trigger-plugin/68feb4b910c20607685389bf7d0b4b2afcc20acf/images/configure-token.png -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | org.jenkins-ci.plugins 5 | plugin 6 | 4.18 7 | 8 | 9 | 10 | 8 11 | 12 | false 13 | 14 | 2.263.1 15 | 2.6.4 16 | 4.5.2 17 | true 18 | 1.18 19 | 20 | 21 | igalg.jenkins.plugins 22 | multibranch-scan-webhook-trigger 23 | 1.0.12-SNAPSHOT 24 | Multibranch Scan Webhook Trigger 25 | Trigger that can receive any HTTP request and trigger a multibranch scan. 26 | 27 | https://github.com/jenkinsci/multibranch-scan-webhook-trigger-plugin 28 | 29 | hpi 30 | 31 | 32 | 33 | MIT License 34 | http://opensource.org/licenses/MIT 35 | 36 | 37 | 38 | 39 | igalg 40 | Igal Gluh 41 | igal.gluh@gmail.com 42 | 43 | 44 | 45 | 46 | GitHub 47 | https://github.com/jenkinsci/multibranch-scan-webhook-trigger 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | scm:git:ssh://git@github.com/jenkinsci/multibranch-scan-webhook-trigger.git 56 | scm:git:ssh://git@github.com/jenkinsci/multibranch-scan-webhook-trigger.git 57 | https://github.com/jenkinsci/multibranch-scan-webhook-trigger.git 58 | HEAD 59 | 60 | 61 | 62 | 63 | repo.jenkins-ci.org 64 | https://repo.jenkins-ci.org/public/ 65 | 66 | 67 | 68 | 69 | 70 | repo.jenkins-ci.org 71 | https://repo.jenkins-ci.org/public/ 72 | 73 | 74 | 75 | 76 | 77 | 78 | maven.jenkins-ci.org 79 | jenkinsci-releases 80 | https://repo.jenkins-ci.org/releases 81 | 82 | 83 | maven.jenkins-ci.org 84 | jenkinsci-snapshots 85 | https://repo.jenkins-ci.org/snapshots 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | org.apache.maven.plugins 95 | maven-surefire-plugin 96 | 97 | 98 | org.apache.maven.plugins 99 | maven-eclipse-plugin 100 | 101 | true 102 | true 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | src/main/resources 111 | true 112 | 113 | 114 | 115 | 116 | src/test/resources 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | org.kohsuke 125 | access-modifier-annotation 126 | 127 | 128 | 129 | org.jenkins-ci 130 | annotation-indexer 131 | 132 | 133 | 138 | 139 | 140 | 141 | 142 | org.jenkins-ci.plugins 143 | scm-api 144 | ${scm-api.version} 145 | 146 | 147 | org.jenkins-ci.plugins 148 | github-api 149 | 1.116 150 | 151 | 152 | org.jenkins-ci.plugins 153 | git 154 | ${git-plugin.version} 155 | 156 | 157 | org.jenkins-ci.plugins 158 | branch-api 159 | 2.5.9 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | com.jayway.jsonpath 173 | json-path 174 | 2.4.0 175 | 176 | 177 | com.google.code.gson 178 | gson 179 | 2.8.2 180 | 181 | 182 | org.jenkins-ci.plugins 183 | structs 184 | 1.22 185 | 186 | 187 | org.jenkins-ci.plugins 188 | credentials 189 | 2.3.19 190 | 191 | 192 | org.jenkins-ci.plugins 193 | structs 194 | 195 | 196 | 197 | 198 | 199 | 200 | junit 201 | junit 202 | test 203 | 204 | 205 | org.assertj 206 | assertj-core 207 | 2.1.0 208 | test 209 | 210 | 211 | org.powermock 212 | powermock-module-junit4 213 | 214 | test 215 | 216 | 217 | org.powermock 218 | powermock-api-mockito2 219 | 220 | test 221 | 222 | 223 | 224 | 225 | 226 | org.jenkins-ci.plugins.workflow 227 | workflow-job 228 | 2.41 229 | test 230 | 231 | 232 | org.jenkins-ci.plugins.workflow 233 | workflow-cps 234 | 2.92 235 | test 236 | 237 | 238 | 239 | 240 | 241 | org.jenkins-ci.plugins.workflow 242 | workflow-step-api 243 | 2.23 244 | test 245 | 246 | 247 | org.jenkins-ci.plugins 248 | token-macro 249 | 2.13 250 | test 251 | 252 | 253 | org.jenkins-ci.plugins 254 | plain-credentials 255 | 1.4 256 | test 257 | 258 | 259 | org.jenkins-ci.plugins 260 | script-security 261 | 1.77 262 | test 263 | 264 | 265 | 266 | org.jenkins-ci.plugins.workflow 267 | workflow-scm-step 268 | 2.13 269 | test 270 | 271 | 272 | org.jenkins-ci.plugins.workflow 273 | workflow-aggregator 274 | 2.6 275 | test 276 | 277 | 278 | org.jenkins-ci.plugins 279 | github-branch-source 280 | 2.9.0 281 | test 282 | 283 | 284 | org.jenkins-ci.plugins 285 | matrix-project 286 | 1.18 287 | test 288 | 289 | 290 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | -------------------------------------------------------------------------------- /src/main/java/com/igalg/jenkins/plugins/mswt/ComputedFolderWebHookRequestReceiver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2019 igalg 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package com.igalg.jenkins.plugins.mswt; 25 | 26 | import static hudson.util.HttpResponses.okJSON; 27 | import static java.util.logging.Level.INFO; 28 | import static java.util.logging.Level.SEVERE; 29 | 30 | import java.io.IOException; 31 | import java.util.Collections; 32 | import java.util.Enumeration; 33 | import java.util.HashMap; 34 | import java.util.List; 35 | import java.util.Map; 36 | import java.util.logging.Logger; 37 | 38 | import javax.servlet.FilterChain; 39 | import javax.servlet.ServletException; 40 | import javax.servlet.http.HttpServletRequest; 41 | import javax.servlet.http.HttpServletResponse; 42 | 43 | import org.kohsuke.stapler.HttpResponse; 44 | import org.kohsuke.stapler.StaplerRequest; 45 | 46 | import com.google.common.annotations.VisibleForTesting; 47 | import com.igalg.jenkins.plugins.mswt.locator.ComputedFolderLocator; 48 | import com.igalg.jenkins.plugins.mswt.locator.ComputedFolderWebHookTriggerLocator; 49 | import com.igalg.jenkins.plugins.mswt.locator.JenkinsInstanceComputedFolderLocator; 50 | import com.igalg.jenkins.plugins.mswt.locator.LocatedComputedFolder; 51 | import com.igalg.jenkins.plugins.mswt.trigger.ComputedFolderWebHookTrigger; 52 | import com.igalg.jenkins.plugins.mswt.trigger.ComputedFolderWebHookTriggerResult; 53 | 54 | import hudson.Extension; 55 | import hudson.model.UnprotectedRootAction; 56 | import hudson.security.csrf.CrumbExclusion; 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | @Extension 65 | public class ComputedFolderWebHookRequestReceiver extends CrumbExclusion implements UnprotectedRootAction { 66 | 67 | private static final String NO_JOBS_MSG = "Did not find any jobs with multibranch scan webhook trigger configured." 68 | + "you need to pass token like ...multibranch-webhook-trigger/invoke?token=TOKENHERE. "; 69 | 70 | private static final String URL_NAME = "multibranch-webhook-trigger"; 71 | 72 | private static final String TOKEN_PARAM = "token"; 73 | 74 | private static final Logger LOGGER = Logger.getLogger(ComputedFolderWebHookRequestReceiver.class.getName()); 75 | 76 | 77 | 78 | 79 | 80 | @Override 81 | public boolean process(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain)throws IOException, ServletException { 82 | final String pathInfo = request.getPathInfo(); 83 | 84 | if (pathInfo != null && pathInfo.startsWith("/" + URL_NAME + "/")) { 85 | chain.doFilter(request, response); 86 | return true; 87 | } 88 | return false; 89 | } 90 | 91 | public HttpResponse doInvoke(final StaplerRequest request) { 92 | Map parameterMap = null; 93 | Map> headers = null; 94 | 95 | headers = getHeaders(request); 96 | parameterMap = request.getParameterMap(); 97 | 98 | final String givenToken = getGivenToken(headers, parameterMap); 99 | return doInvoke(givenToken); 100 | } 101 | 102 | 103 | 104 | @VisibleForTesting 105 | String getGivenToken(final Map> headers, final Map parameterMap) { 106 | if (parameterMap.containsKey(TOKEN_PARAM)) { 107 | return parameterMap.get(TOKEN_PARAM)[0]; 108 | } 109 | if (headers.containsKey(TOKEN_PARAM)) { 110 | return headers.get(TOKEN_PARAM).get(0); 111 | } 112 | return null; 113 | } 114 | 115 | @VisibleForTesting 116 | Map> getHeaders(final StaplerRequest request) { 117 | Map> headers = new HashMap<>(); 118 | Enumeration headersEnumeration = request.getHeaderNames(); 119 | while (headersEnumeration.hasMoreElements()) { 120 | String headerName = headersEnumeration.nextElement(); 121 | headers.put(headerName.toLowerCase(), Collections.list(request.getHeaders(headerName))); 122 | } 123 | return headers; 124 | } 125 | 126 | @VisibleForTesting 127 | HttpResponse doInvoke(final String givenToken) { 128 | ComputedFolderLocator computedFolderLocator = new JenkinsInstanceComputedFolderLocator(); 129 | List locatedList = ComputedFolderWebHookTriggerLocator.locateJobsWithTrigger(computedFolderLocator,givenToken); 130 | Map triggerResultsMap = new HashMap(); 131 | 132 | if (locatedList.isEmpty()) { 133 | LOGGER.log(INFO, NO_JOBS_MSG); 134 | triggerResultsMap.put("ANY", NO_JOBS_MSG); 135 | } 136 | 137 | for (LocatedComputedFolder locatedComputedFolder : locatedList) { 138 | try { 139 | LOGGER.log(INFO, "Triggering " + locatedComputedFolder.getFullName()); 140 | ComputedFolderWebHookTrigger trigger = locatedComputedFolder.getTrigger(); 141 | ComputedFolderWebHookTriggerResult triggerResults = trigger.trigger(); 142 | triggerResultsMap.put(locatedComputedFolder.getFullName(), triggerResults); 143 | } catch (Throwable t) { 144 | LOGGER.log(SEVERE, locatedComputedFolder.getFullName(), t); 145 | final String msg = "Exception occurred, full stack trace in Jenkins server log. Thrown in: " + t.getStackTrace()[0].getClassName() + ":" + t.getStackTrace()[0].getLineNumber(); 146 | triggerResultsMap.put(locatedComputedFolder.getFullName(), msg); 147 | } 148 | } 149 | 150 | Map response = new HashMap<>(); 151 | response.put("triggerResults", triggerResultsMap); 152 | return okJSON(response); 153 | } 154 | 155 | @Override 156 | public String getIconFileName() { 157 | return null; 158 | } 159 | 160 | @Override 161 | public String getDisplayName() { 162 | return null; 163 | } 164 | 165 | @Override 166 | public String getUrlName() { 167 | return URL_NAME; 168 | } 169 | 170 | 171 | } 172 | -------------------------------------------------------------------------------- /src/main/java/com/igalg/jenkins/plugins/mswt/locator/ComputedFolderLocator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2019 igalg 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package com.igalg.jenkins.plugins.mswt.locator; 25 | 26 | import java.util.List; 27 | 28 | import com.cloudbees.hudson.plugins.folder.computed.ComputedFolder; 29 | 30 | public interface ComputedFolderLocator { 31 | @SuppressWarnings("rawtypes") 32 | public List getAllComputedFolders() ; 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/igalg/jenkins/plugins/mswt/locator/ComputedFolderWebHookTriggerLocator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2019 igalg 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package com.igalg.jenkins.plugins.mswt.locator; 25 | 26 | import static java.nio.charset.StandardCharsets.UTF_8; 27 | 28 | import java.security.MessageDigest; 29 | import java.util.ArrayList; 30 | import java.util.List; 31 | import java.util.Map; 32 | 33 | import com.cloudbees.hudson.plugins.folder.computed.ComputedFolder; 34 | import com.igalg.jenkins.plugins.mswt.trigger.ComputedFolderWebHookTrigger; 35 | 36 | import hudson.triggers.Trigger; 37 | import hudson.triggers.TriggerDescriptor; 38 | 39 | public class ComputedFolderWebHookTriggerLocator { 40 | public static List locateJobsWithTrigger(ComputedFolderLocator jobLocator , String givenToken) { 41 | List locatedList = new ArrayList(); 42 | @SuppressWarnings("rawtypes") 43 | List candidateProjects = jobLocator.getAllComputedFolders(); 44 | for (ComputedFolder candidateJob : candidateProjects) { 45 | ComputedFolderWebHookTrigger computedFolderWebHookTrigger = findComputedFolderWebHookTrigger(candidateJob.getTriggers()); 46 | if (computedFolderWebHookTrigger != null && 47 | MessageDigest.isEqual(computedFolderWebHookTrigger.getToken().getBytes(UTF_8), givenToken.getBytes(UTF_8))) { 48 | locatedList.add(new LocatedComputedFolder(candidateJob.getFullName(), computedFolderWebHookTrigger)); 49 | } 50 | 51 | } 52 | return locatedList; 53 | } 54 | 55 | private static ComputedFolderWebHookTrigger findComputedFolderWebHookTrigger(Map> triggers) { 56 | if (triggers == null) { 57 | return null; 58 | } 59 | for ( Trigger candidate : triggers.values()) { 60 | if (candidate instanceof ComputedFolderWebHookTrigger) { 61 | return (ComputedFolderWebHookTrigger) candidate; 62 | } 63 | } 64 | return null; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/com/igalg/jenkins/plugins/mswt/locator/JenkinsInstanceComputedFolderLocator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2019 igalg 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package com.igalg.jenkins.plugins.mswt.locator; 25 | 26 | import java.util.List; 27 | 28 | import org.acegisecurity.context.SecurityContext; 29 | import org.acegisecurity.context.SecurityContextHolder; 30 | 31 | import com.cloudbees.hudson.plugins.folder.computed.ComputedFolder; 32 | 33 | import hudson.security.ACL; 34 | import jenkins.model.Jenkins; 35 | 36 | public class JenkinsInstanceComputedFolderLocator implements ComputedFolderLocator { 37 | @SuppressWarnings({ "rawtypes", "deprecation" }) 38 | public List getAllComputedFolders() { 39 | 40 | SecurityContext orig = null; 41 | try { 42 | orig = ACL.impersonate(ACL.SYSTEM); 43 | return Jenkins.getInstance().getAllItems(ComputedFolder.class); 44 | }finally { 45 | SecurityContextHolder.setContext(orig); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/igalg/jenkins/plugins/mswt/locator/LocatedComputedFolder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2019 igalg 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package com.igalg.jenkins.plugins.mswt.locator; 25 | 26 | import com.igalg.jenkins.plugins.mswt.trigger.ComputedFolderWebHookTrigger; 27 | 28 | public class LocatedComputedFolder { 29 | 30 | private final String fullName; 31 | private final ComputedFolderWebHookTrigger trigger; 32 | 33 | public LocatedComputedFolder(String fullName, ComputedFolderWebHookTrigger trigger) { 34 | this.fullName = fullName; 35 | this.trigger = trigger; 36 | } 37 | 38 | public ComputedFolderWebHookTrigger getTrigger() { 39 | return trigger; 40 | } 41 | 42 | public String getFullName() { 43 | return fullName; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/igalg/jenkins/plugins/mswt/trigger/ComputedFolderWebHookTrigger.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2019 igalg 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package com.igalg.jenkins.plugins.mswt.trigger; 25 | 26 | import java.util.logging.Logger; 27 | 28 | import org.kohsuke.stapler.DataBoundConstructor; 29 | 30 | import com.cloudbees.hudson.plugins.folder.computed.ComputedFolder; 31 | 32 | import hudson.Extension; 33 | import hudson.model.Item; 34 | import hudson.model.Queue; 35 | import hudson.triggers.Trigger; 36 | import hudson.triggers.TriggerDescriptor; 37 | 38 | 39 | public class ComputedFolderWebHookTrigger extends Trigger> { 40 | 41 | private static final Logger LOGGER = Logger.getLogger(ComputedFolderWebHookTrigger.class.getName()); 42 | 43 | 44 | 45 | 46 | private final String token; 47 | 48 | 49 | public String getToken() { 50 | return token; 51 | } 52 | 53 | 54 | 55 | @DataBoundConstructor 56 | public ComputedFolderWebHookTrigger(String token) { 57 | this.token = token; 58 | LOGGER.info("setting token:"+token); 59 | 60 | } 61 | 62 | 63 | public ComputedFolderWebHookTriggerResult trigger() { 64 | if(job == null) 65 | return null; 66 | Queue.Item queueItem = job.scheduleBuild2(0); 67 | return new ComputedFolderWebHookTriggerResult(queueItem); 68 | } 69 | 70 | 71 | 72 | 73 | 74 | /** 75 | * Our {@link hudson.model.Descriptor} 76 | */ 77 | @Extension 78 | @SuppressWarnings("unused") // instantiated by Jenkins 79 | public static class DescriptorImpl extends TriggerDescriptor { 80 | /** 81 | * {@inheritDoc} 82 | */ 83 | public boolean isApplicable(Item item) { 84 | return item instanceof ComputedFolder; 85 | } 86 | 87 | /** 88 | * {@inheritDoc} 89 | */ 90 | public String getDisplayName() { 91 | return "Scan by webhook"; 92 | } 93 | } 94 | 95 | 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/com/igalg/jenkins/plugins/mswt/trigger/ComputedFolderWebHookTriggerResult.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2019 igalg 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package com.igalg.jenkins.plugins.mswt.trigger; 25 | 26 | import hudson.model.Queue.Item; 27 | 28 | public class ComputedFolderWebHookTriggerResult { 29 | 30 | private final String url; 31 | private final long id; 32 | private final boolean triggered; 33 | 34 | public ComputedFolderWebHookTriggerResult(Item queueItem) { 35 | if (queueItem != null) { 36 | this.url = queueItem.getUrl(); 37 | this.id = queueItem.getId(); 38 | this.triggered = true; 39 | } else { 40 | this.url = null; 41 | this.id = 0; 42 | this.triggered = false; 43 | } 44 | } 45 | 46 | public boolean isTriggered() { 47 | return triggered; 48 | } 49 | 50 | public long getId() { 51 | return id; 52 | } 53 | 54 | public String getUrl() { 55 | return url; 56 | } 57 | 58 | } -------------------------------------------------------------------------------- /src/main/resources/com/igalg/jenkins/plugins/mswt/trigger/ComputedFolderWebHookTrigger/config.jelly: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/main/resources/com/igalg/jenkins/plugins/mswt/trigger/ComputedFolderWebHookTrigger/help-token.html: -------------------------------------------------------------------------------- 1 | 24 |
25 | The token to match with webhook token. 26 | Receive any HTTP request, JENKINS_URL/multibranch-webhook-trigger/invoke?token=[Trigger token] 27 | If a token match, than a multibranch scan will bi triggered. 28 |
-------------------------------------------------------------------------------- /src/main/resources/com/igalg/jenkins/plugins/mswt/trigger/ComputedFolderWebHookTrigger/help.html: -------------------------------------------------------------------------------- 1 | 24 |
25 |

26 | Allows Multibranch Scan Webhook Trigger to trigger scan of this multibranch job. 27 |

28 |
-------------------------------------------------------------------------------- /src/main/resources/index.jelly: -------------------------------------------------------------------------------- 1 | 24 | 25 |
26 | Trigger that can receive any HTTP request and trigger a multibranch job scan when token matched. 27 |
28 | -------------------------------------------------------------------------------- /src/test/java/com/igalg/jenkins/plugins/mswt/ComputedFolderWebHookRequestReceiverTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2019 igalg 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package com.igalg.jenkins.plugins.mswt; 25 | 26 | import static com.google.common.collect.ImmutableMap.of; 27 | import static com.google.common.collect.Lists.newArrayList; 28 | import static com.google.common.collect.Maps.newHashMap; 29 | import static org.assertj.core.api.Assertions.assertThat; 30 | 31 | import java.util.List; 32 | import java.util.Map; 33 | 34 | import org.junit.Before; 35 | import org.junit.Test; 36 | 37 | public class ComputedFolderWebHookRequestReceiverTest { 38 | 39 | ComputedFolderWebHookRequestReceiver computedFolderWebHookRequestReceiver; 40 | 41 | @Before 42 | public void init() { 43 | computedFolderWebHookRequestReceiver = new ComputedFolderWebHookRequestReceiver(); 44 | } 45 | 46 | @Test 47 | public void testThatNoTokenReturnsNull() { 48 | 49 | Map> headers = newHashMap(); 50 | Map parameterMap = newHashMap(); 51 | String token = computedFolderWebHookRequestReceiver.getGivenToken(headers, parameterMap); 52 | assertThat(token).isNull(); 53 | } 54 | 55 | @Test 56 | public void testThatParameterTokenReturnsThatToken() { 57 | Map> headers = newHashMap(); 58 | Map parameterMap = of("token", new String[] {"token-parameter"}); 59 | final String token = computedFolderWebHookRequestReceiver.getGivenToken(headers, parameterMap); 60 | assertThat(token).isEqualTo("token-parameter"); 61 | } 62 | 63 | @Test 64 | public void testThatHeaderTokenReturnsThatToken() { 65 | Map> headers = of("token", (List) newArrayList("token-header")); 66 | Map parameterMap = newHashMap(); 67 | String token = computedFolderWebHookRequestReceiver.getGivenToken(headers, parameterMap); 68 | assertThat(token).isEqualTo("token-header"); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/test/java/com/igalg/jenkins/plugins/mswt/locator/ComputedFolderWebHookTriggerLocatorTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2019 igalg 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package com.igalg.jenkins.plugins.mswt.locator; 25 | 26 | import static org.assertj.core.api.Assertions.assertThat; 27 | import static org.mockito.Mockito.mock; 28 | import static org.mockito.Mockito.when; 29 | 30 | import java.util.ArrayList; 31 | import java.util.HashMap; 32 | import java.util.List; 33 | import java.util.Map; 34 | 35 | import org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject; 36 | import org.junit.Before; 37 | import org.junit.Test; 38 | import org.junit.runner.RunWith; 39 | import org.powermock.api.mockito.PowerMockito; 40 | import org.powermock.core.classloader.annotations.PrepareForTest; 41 | import org.powermock.modules.junit4.PowerMockRunner; 42 | 43 | import com.cloudbees.hudson.plugins.folder.computed.ComputedFolder; 44 | import com.igalg.jenkins.plugins.mswt.trigger.ComputedFolderWebHookTrigger; 45 | 46 | import hudson.triggers.Trigger; 47 | import hudson.triggers.TriggerDescriptor; 48 | 49 | @SuppressWarnings("rawtypes") 50 | @RunWith(PowerMockRunner.class) 51 | @PrepareForTest({WorkflowMultiBranchProject.class}) 52 | public class ComputedFolderWebHookTriggerLocatorTest { 53 | 54 | private List computedFolderList; 55 | private final WorkflowMultiBranchProject multibranchJobWithNoToken = createJobWorkflowMultiBranchProject("abc"); 56 | private final WorkflowMultiBranchProject multibranchJobWithToken = createJobWorkflowMultiBranchProject("token"); 57 | private final WorkflowMultiBranchProject multibranchJobWithNoTrigger = createJobWorkflowMultiBranchProjectWithouTrigger(); 58 | private ComputedFolderLocator computedFolderLocator; 59 | 60 | @Before 61 | public void before() { 62 | computedFolderList = new ArrayList<>(); 63 | computedFolderList.add(multibranchJobWithNoToken); 64 | computedFolderList.add(multibranchJobWithToken); 65 | computedFolderList.add(multibranchJobWithNoTrigger); 66 | computedFolderLocator = new ComputedFolderLocator() { 67 | 68 | @Override 69 | public List getAllComputedFolders(){ 70 | return computedFolderList; 71 | } 72 | }; 73 | } 74 | 75 | @Test 76 | public void testThatJobsWithMatchedTokenFoundedAndNotMatchNotFounded() { 77 | final String givenToken = "token"; 78 | 79 | List jobsToTrigger = getJobNameList(ComputedFolderWebHookTriggerLocator.locateJobsWithTrigger(computedFolderLocator, givenToken)); 80 | 81 | assertThat(jobsToTrigger.contains(multibranchJobWithToken.getFullName())).isTrue(); 82 | assertThat(jobsToTrigger.contains(multibranchJobWithNoToken.getFullName())).isFalse(); 83 | } 84 | 85 | @Test 86 | public void testThatJobsWithoutTriggerAreNotLocated() { 87 | final String givenToken = "token"; 88 | 89 | List jobsToTrigger = getJobNameList(ComputedFolderWebHookTriggerLocator.locateJobsWithTrigger(computedFolderLocator, givenToken)); 90 | 91 | 92 | assertThat(jobsToTrigger.contains(multibranchJobWithNoTrigger.getFullName())).isFalse(); 93 | 94 | } 95 | 96 | 97 | private WorkflowMultiBranchProject createJobWorkflowMultiBranchProjectWithouTrigger() { 98 | WorkflowMultiBranchProject mock = PowerMockito.mock(WorkflowMultiBranchProject.class); 99 | PowerMockito.when(mock.getFullName()) 100 | .thenReturn("WorkflowMultiBranchProject_noTrigger"); 101 | return mock; 102 | } 103 | private WorkflowMultiBranchProject createJobWorkflowMultiBranchProject(String token) { 104 | WorkflowMultiBranchProject mock = PowerMockito.mock(WorkflowMultiBranchProject.class); 105 | PowerMockito.when(mock.getFullName()).thenReturn("WorkflowMultiBranchProject_" + token); 106 | Map> triggers = new HashMap<>(); 107 | TriggerDescriptor typeDescr = mock(TriggerDescriptor.class); 108 | ComputedFolderWebHookTrigger trigger = new ComputedFolderWebHookTrigger(token); 109 | triggers.put(typeDescr, trigger); 110 | when(mock.getTriggers()).thenReturn(triggers); 111 | return mock; 112 | } 113 | 114 | 115 | 116 | private List getJobNameList(List jobsToTrigger) { 117 | List names = new ArrayList<>(); 118 | for ( LocatedComputedFolder locatedComputedFolder : jobsToTrigger) 119 | names.add(locatedComputedFolder.getFullName()); 120 | return names; 121 | } 122 | 123 | 124 | 125 | } 126 | --------------------------------------------------------------------------------