├── .github ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── EULA.md ├── ISSUE_TEMPLATE │ ├── 1-Bug_report.md │ └── 2-Question.md ├── PRIVACY_POLICY.md ├── PULL_REQUEST_TEMPLATE.md └── dependabot.yml ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── checkstyle.xml ├── docs ├── dist │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── oauth2-redirect.html │ ├── swagger-ui-bundle.js │ ├── swagger-ui-bundle.js.map │ ├── swagger-ui-standalone-preset.js │ ├── swagger-ui-standalone-preset.js.map │ ├── swagger-ui.css │ ├── swagger-ui.css.map │ ├── swagger-ui.js │ └── swagger-ui.js.map ├── index.html └── spec.yml ├── parameterized-client ├── package-lock.json ├── package.json ├── src │ ├── common │ │ ├── aui.js │ │ └── rest.js │ ├── hook │ │ ├── job.js │ │ ├── state-reducers.js │ │ └── view.js │ ├── jenkins_settings │ │ ├── server.js │ │ ├── state-reducers.js │ │ └── view.js │ └── user_settings │ │ ├── server.js │ │ ├── state-reducers.js │ │ └── user_view.js └── webpack.config.js ├── pom.xml ├── readme └── img │ ├── build1.png │ ├── build2.png │ ├── build3.png │ ├── build_dialog.png │ ├── jenkins_admin.png │ ├── jenkins_hook.png │ ├── jenkins_hook2.png │ ├── jenkins_user.png │ └── triggers.png └── src ├── main ├── java │ └── com │ │ └── kylenicholls │ │ └── stash │ │ └── parameterizedbuilds │ │ ├── ParameterizedBuildHook.java │ │ ├── PullRequestHook.java │ │ ├── ciserver │ │ ├── AccountServer.java │ │ ├── CIServer.java │ │ ├── CIServerFactory.java │ │ ├── CIServlet.java │ │ ├── GlobalServer.java │ │ ├── Jenkins.java │ │ ├── JenkinsConnection.java │ │ └── ProjectServer.java │ │ ├── conditions │ │ ├── BaseCondition.java │ │ ├── BuildPermissionsCondition.java │ │ ├── HookIsEnabledCondition.java │ │ └── ManualButtonCondition.java │ │ ├── eventHandlers │ │ ├── BaseHandler.java │ │ ├── PRApprovedHandler.java │ │ ├── PRAutoMergedHandler.java │ │ ├── PRDeclinedHandler.java │ │ ├── PRDeletedHandler.java │ │ ├── PRDestRescopedHandler.java │ │ ├── PRHandler.java │ │ ├── PRMergedHandler.java │ │ ├── PROpenedHandler.java │ │ ├── PRReopenedHandler.java │ │ ├── PRSourceRescopedHandler.java │ │ ├── PushHandler.java │ │ ├── RefCreatedHandler.java │ │ ├── RefDeletedHandler.java │ │ └── RefHandler.java │ │ ├── helper │ │ ├── ScopeProjectVisitor.java │ │ └── SettingsService.java │ │ ├── item │ │ ├── BitbucketVariable.java │ │ ├── BitbucketVariables.java │ │ ├── JenkinsResponse.java │ │ ├── Job.java │ │ ├── Server.java │ │ └── UserToken.java │ │ └── rest │ │ ├── BuildResource.java │ │ ├── GlobalResource.java │ │ ├── ProjectResource.java │ │ └── ServerService.java └── resources │ ├── atlassian-plugin.xml │ ├── images │ └── icon.png │ ├── less │ └── hooks.less │ ├── parameterized-builds.properties │ ├── scripts │ └── jenkins │ │ ├── feature │ │ ├── build-dialog.js │ │ ├── build-dialog.less │ │ └── build-dialog.soy │ │ ├── pb-blayout-trigger.js │ │ ├── pb-blist-trigger.js │ │ └── pb-pr-trigger.js │ └── templates │ ├── jenkins-admin-settings.soy │ └── jenkins-user-settings.soy └── test └── java └── com └── kylenicholls └── stash └── parameterizedbuilds ├── ParameterizedBuildHookTest.java ├── PullRequestHookTest.java ├── ciserver ├── CIServletTest.java ├── JenkinsConnectionTest.java └── JenkinsTest.java ├── conditions ├── BuildPermissionsConditionTest.java ├── HookIsEnabledConditionTest.java └── ManualButtonConditionTest.java ├── eventHandlers ├── PRApprovedHandlerTest.java ├── PRAutoMergedHandlerTest.java ├── PRDeclinedHandlerTest.java ├── PRDeletedHandlerTest.java ├── PRDestRescopedHandlerTest.java ├── PRHandlerTest.java ├── PRMergedHandlerTest.java ├── PROpenedHandlerTest.java ├── PRReopenedHandlerTest.java ├── PRSourceRescopedHandlerTest.java ├── PRTestBase.java ├── PushHandlerTest.java ├── RefCreatedHandlerTest.java ├── RefDeletedHandlerTest.java ├── RefHandlerTest.java └── TestEventFactory.java ├── helper └── SettingsServiceTest.java ├── item ├── BitbucketVariableTest.java ├── BitbucketVariablesTest.java ├── JenkinsResponseTest.java ├── JobTest.java ├── ServerTest.java └── UserTokenTest.java └── rest ├── BuildResourceTest.java ├── GlobalResourceTest.java └── ProjectResourceTest.java /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at explosivepyrounicorns@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | There are a variety of different ways to contribute. We ask that you follow the [code of conduct](CODE_OF_CONDUCT.md) and follow these guidelines when creating a contribution. 4 | 5 | ## Getting Started 6 | 7 | A good first step to learn more about this project is to check out our docs located in the wiki section of Github. The 8 | docs are intended to provide helpful information to both users and contributors. Our docs could always be better so if 9 | you think they are lacking somewhere, check out the [documentation](#documentation) section to help up improve. 10 | 11 | Another great way to get started is to join our [Gitter](https://gitter.im/parameterized-builds/Lobby)! 12 | Introduce yourself and we'll do our best to assist you towards your first or latest contribution. 13 | 14 | ## Code Style and Best Practices 15 | 16 | This project uses [checkstyle](https://checkstyle.sourceforge.io/) as a Java code linter. The 17 | linting rules are as follows: 18 | 19 | - Max line length is 100 characters 20 | - Indentation is done with 4 spaces 21 | - Star imports are discouraged 22 | 23 | In addition, any code written should be accompanied by unit tests. 24 | 25 | ## Contribution Types 26 | 27 | ### Bug Reports 28 | 29 | Bug reports should be submitted through a Github issue. Follow the [issue template](ISSUE_TEMPLATE.md) to provide an 30 | appropriate amount of information in your report. 31 | 32 | ### Bug Fixes 33 | 34 | Before fixing a bug, make sure there is a bug report open. If not, please open one so that the issue can be tracked. 35 | Once an issue is open, mention in the issue that you would like to work on it so we can see where work is being done. 36 | 37 | ### Documentation 38 | 39 | Minor documentation updates such as typo fixes do not need approval to make. More significant updates like adding a 40 | new page or a new section to a page should first be approved by a code owner. A request can be made through Gitter and a 41 | core member will then work with you to make the update. 42 | -------------------------------------------------------------------------------- /.github/EULA.md: -------------------------------------------------------------------------------- 1 | # End User License Agreement 2 | 3 | Version 1.0, March 28 2019 4 | 5 | This End-User License Agreement ("EULA") is a legal agreement between you 6 | and Kyle Nicholls ("Publisher"), the publisher of Parameterized Builds for Jenkins 7 | (the "Marketplace Product"). 8 | 9 | By installing, copying, or otherwise using the App, you agrees to be bound by 10 | the terms and conditions set forth in this EULA. If you do not want to comply with 11 | this EULA, you must not use this Marketplace Product. 12 | 13 | The Marketplace Product is licensed under the Apache License Version 2.0. In case 14 | of any conflict between this EULA and the Apache License Version 2.0, the Apache 15 | License Version 2.0 will control. 16 | 17 | 1. **Grant of License** 18 | **A) Scope of License.** Subject to the terms of this EULA, Publisher grants you 19 | an unlimited, worldwide, non-exclusive, non-transferable, and non-sublicensable 20 | license license of the Marketplace Product. 21 | 22 | **B) Installation and Use.** You may download and use the Marketplace Product only 23 | on hardware systems owned, leased or controlled by you. You may install an unlimited 24 | number of copies of the Marketplace Product and make multiple back-up copies for 25 | business or personal use. 26 | 27 | **C) Reproduction and Distribution.** Reproduction and Distribution of the Marketplace 28 | Product is controlled by the Apache License Version 2.0. 29 | 30 | 2. **Rights and Limitation** 31 | **A) Limitation of Liability.** Under the Apach License Version 2.0 Section 8, 32 | 33 | 34 | In no event and under no legal theory, whether in tort (including negligence), 35 | contract, or otherwise, unless required by applicable law (such as deliberate 36 | and grossly negligent acts) or agreed to in writing, shall any Contributor be 37 | liable to You for damages, including any direct, indirect, special, incidental, 38 | or consequential damages of any character arising as a result of this License 39 | or out of the use or inability to use the Work (including but not limited to 40 | damages for loss of goodwill, work stoppage, computer failure or malfunction, 41 | or any and all other commercial damages or losses), even if such Contributor 42 | has been advised of the possibility of such damages. 43 | 44 | **B) Updates and Maintenance.** Publisher shall provide update and maintenance on an 45 | as need basis. 46 | 47 | **C) No Support.** Publisher has no obligation to provide support services for the 48 | Marketplace Product. 49 | 50 | 3. **Duration.** This EULA is perpetual or until: 51 | 52 | **A)** Automatically terminated or suspended if you fail to comply with any of the 53 | terms and conditions set forth in this EULA; or 54 | 55 | **B)** Terminated or suspended by Publisher, with or without cause. 56 | 57 | 4. **Disclaimer of Warranty** Under the Apach License Version 2.0 Section 7, 58 | 59 | 60 | Unless required by applicable law or agreed to in writing, Licensor provides 61 | the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, 62 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, 63 | without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, 64 | MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for 65 | determining the appropriateness of using or redistributing the Work and assume any 66 | risks associated with Your exercise of permissions under this License. -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/1-Bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Report technical issues 4 | labels: 'bug' 5 | --- 6 | 7 | 8 | 10 | 11 | ## Version Info 12 | 13 | 14 | 15 | - _Parameterized Builds for Jenkins_: 16 | - _Bitbucket Server_: 17 | - _Jenkins_: 18 | 19 | ## Expected Behavior 20 | 21 | 22 | 23 | ## Current Behavior 24 | 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/2-Question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Question 3 | about: Question about the plugin 4 | labels: 'question' 5 | --- 6 | 7 | -------------------------------------------------------------------------------- /.github/PRIVACY_POLICY.md: -------------------------------------------------------------------------------- 1 | #Privacy Policy 2 | 3 | This page informs you of our policies regarding the collection, use, and disclosure of Personal Information. 4 | By using the application, you agree to the collection and use of information in accordance with this policy. 5 | 6 | ## Information Collected 7 | 8 | We do not collect any personal data from within the application. 9 | 10 | ## Changes to this Policy 11 | 12 | This policy is effective as of 1/31/2019. This policy may change at any time and you should occasionally check 13 | for updates. Continued use of the application after any modifications will constitute acknowledgement and 14 | consent to the changes posted. If changes are made, best efforts will be made to inform all users. -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | - Pull requests should always have a corresponding issue. Please link the issue in the description. This can be done 2 | by prefixing the issue number with a '#' (`#`). 3 | - Write unit tests for any changes you make. 4 | - A passing [Travis CI](https://travis-ci.org/KyleLNicholls/parameterized-builds) build is required. 5 | - Describe the changes made to solve the linked issue and any additional testing done to validate this change. 6 | - Be open to constructive criticism. For criticism that is inappropriate or excessive harsh, consult the 7 | [code of conduct](CODE_OF_CONDUCT.md) for steps to take. -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: "/parameterized-client" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | versioning-strategy: increase 9 | ignore: 10 | - dependency-name: "@babel/core" 11 | versions: 12 | - 7.12.10 13 | - 7.12.13 14 | - 7.12.16 15 | - 7.12.17 16 | - 7.13.1 17 | - 7.13.10 18 | - 7.13.13 19 | - 7.13.14 20 | - 7.13.15 21 | - 7.13.8 22 | - dependency-name: y18n 23 | versions: 24 | - 4.0.1 25 | - 4.0.2 26 | - dependency-name: "@babel/preset-env" 27 | versions: 28 | - 7.12.11 29 | - 7.12.13 30 | - 7.12.16 31 | - 7.12.17 32 | - 7.13.0 33 | - 7.13.10 34 | - 7.13.12 35 | - 7.13.5 36 | - 7.13.8 37 | - 7.13.9 38 | - dependency-name: react-redux 39 | versions: 40 | - 7.2.2 41 | - 7.2.3 42 | - dependency-name: "@babel/preset-react" 43 | versions: 44 | - 7.12.10 45 | - 7.12.13 46 | - dependency-name: webpack-cli 47 | versions: 48 | - 4.4.0 49 | - 4.5.0 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .classpath 3 | .project 4 | /.settings 5 | /bin/ 6 | *.iml 7 | /.idea 8 | *.pack.js 9 | parameterized-client/node_modules 10 | .vscode 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - openjdk8 4 | cache: 5 | directories: 6 | - $HOME/.m2 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2016 Kyle Nicholls 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /checkstyle.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /docs/dist/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ParameterizedBuilds/parameterized-builds/2a27ca579ac233bcc68ca886c41bd8578e25a7d3/docs/dist/favicon-16x16.png -------------------------------------------------------------------------------- /docs/dist/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ParameterizedBuilds/parameterized-builds/2a27ca579ac233bcc68ca886c41bd8578e25a7d3/docs/dist/favicon-32x32.png -------------------------------------------------------------------------------- /docs/dist/oauth2-redirect.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 68 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Swagger UI 7 | 8 | 9 | 10 | 31 | 32 | 33 | 34 |
35 | 36 | 37 | 38 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /parameterized-client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "parameterized-builds", 3 | "version": "4.0.10", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "clean": "rm -rf node_modules/ npm-debug.log", 8 | "test": "echo \"Error: no test specified\" && exit 1", 9 | "prod-build": "./node_modules/.bin/webpack --config webpack.config.js" 10 | }, 11 | "author": "Kyle Nicholls/Austin Hacker", 12 | "private": true, 13 | "license": "Apache-2.0", 14 | "devDependencies": { 15 | "@babel/core": "^7.16.7", 16 | "@babel/preset-env": "^7.16.7", 17 | "@babel/preset-react": "^7.16.7", 18 | "babel-loader": "^8.2.3", 19 | "webpack": "^4.46.0", 20 | "webpack-cli": "^4.7.2" 21 | }, 22 | "dependencies": { 23 | "axios": "^0.24.0", 24 | "react": "^16.9.0", 25 | "react-dom": "^16.9.0", 26 | "react-redux": "^5.1.2", 27 | "redux": "^4.1.2" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /parameterized-client/src/common/aui.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export const TextInput = (inputOptions) => 4 | 5 | export const PasswordInput = (inputOptions) => 6 | 7 | const Input = ({ 8 | labelText, 9 | id, 10 | value, 11 | required = false, 12 | onChange = () => {}, 13 | description = "", 14 | errorMessage = "", 15 | type = "text" 16 | }) => ( 17 |
18 | 22 | 24 | {description.length > 0 && 25 |
{description}
} 26 | {errorMessage.length > 0 && 27 |
{errorMessage}
} 28 |
29 | ); 30 | 31 | export const Checkbox = ({ 32 | id, 33 | labelText, 34 | checked = false, 35 | onChange = () => {}, 36 | description = "", 37 | errorMessage = "" 38 | }) => ( 39 |
40 |
41 | 43 | 44 |
45 | {description.length > 0 && 46 |
{description}
} 47 | {errorMessage.length > 0 && 48 |
{errorMessage}
} 49 |
50 | ) 51 | 52 | export const Button = ({ 53 | id, 54 | name, 55 | buttonText, 56 | onClick= () => {}, 57 | type = "submit", 58 | disabled = false, 59 | extraClasses = [], 60 | }) => ( 61 | 63 | ) 64 | 65 | export const ButtonGroup = ({ children }) => ( 66 |
67 | {children} 68 |
69 | ) 70 | 71 | export const Message = ({ 72 | messageClass, 73 | closeable = false, 74 | children 75 | }) => ( 76 |
77 | {children} 78 |
79 | ) 80 | 81 | export const Form = ({ children }) => ( 82 |
83 | {children} 84 |
85 | ) 86 | 87 | export const Modal = ({ 88 | id, 89 | hidden=false, 90 | extraClasses = [], 91 | toggleFunction, 92 | title="", 93 | footerButtons=[], 94 | children, }) => ( 95 | 112 | ) -------------------------------------------------------------------------------- /parameterized-client/src/common/rest.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | const getRestUrl = (context) => { 4 | return `${context}/rest/parameterized-builds/latest` 5 | } 6 | 7 | export const getJenkinsServers = (context, projectKey) => { 8 | const baseUrl = getRestUrl(context); 9 | let fullUrl; 10 | if (projectKey === undefined || projectKey === ""){ 11 | fullUrl = `${baseUrl}/global/servers` 12 | } else { 13 | fullUrl = `${baseUrl}/projects/${projectKey}/servers` 14 | } 15 | 16 | return axios.get(fullUrl, { 17 | timeout: 1000 * 60 18 | }); 19 | }; 20 | 21 | export const saveJenkinsServer = (context, projectKey, serverData) => { 22 | const baseUrl = getRestUrl(context); 23 | if (serverData.alias.includes("/")) { 24 | throw "Server nickname cannot include \"/\"" 25 | } 26 | const alias = serverData.old_alias !== "" ? serverData.old_alias : serverData.alias; 27 | let fullUrl = projectKey === "" ? 28 | `${baseUrl}/global/servers/${alias}` : 29 | `${baseUrl}/projects/${projectKey}/servers/${alias}`; 30 | 31 | const data = { 32 | baseUrl: serverData.url, 33 | alias: serverData.alias, 34 | user: serverData.default_user, 35 | token: serverData.default_token, 36 | altUrl: serverData.root_token_enabled, 37 | csrfEnabled: serverData.csrf_enabled 38 | } 39 | 40 | return axios.put(fullUrl, data, { 41 | timeout: 1000 * 60, 42 | headers: { 43 | 'content-type': 'application/json' 44 | } 45 | }) 46 | } 47 | 48 | export const deleteJenkinsServer = (context, projectKey, serverName) => { 49 | const baseUrl = getRestUrl(context); 50 | let fullUrl = projectKey === "" ? 51 | `${baseUrl}/global/servers/${serverName}` : 52 | `${baseUrl}/projects/${projectKey}/servers/${serverName}`; 53 | 54 | return axios.delete(fullUrl, { 55 | timeout: 1000 * 60, 56 | }) 57 | } 58 | 59 | 60 | export const testJenkinsServer = (context, projectKey, serverData) => { 61 | const baseUrl = getRestUrl(context); 62 | let fullUrl = projectKey === "" ? 63 | `${baseUrl}/global/servers/validate` : 64 | `${baseUrl}/projects/${projectKey}/servers/validate`; 65 | 66 | const data = { 67 | baseUrl: serverData.url, 68 | alias: serverData.alias, 69 | user: serverData.default_user, 70 | token: serverData.default_token, 71 | altUrl: serverData.root_token_enabled, 72 | csrfEnabled: serverData.csrf_enabled 73 | } 74 | 75 | return axios.post(fullUrl, data, { 76 | timeout: 1000 * 60, 77 | headers: { 78 | 'content-type': 'application/json' 79 | } 80 | }) 81 | } 82 | 83 | 84 | export const updateUserToken = (context, projectKey, serverData) => { 85 | const baseUrl = getRestUrl(context); 86 | let alias = serverData.alias === "" ? "alias" : serverData.alias; 87 | let fullUrl = projectKey === "" ? 88 | `${baseUrl}/global/servers/${alias}/userToken` : 89 | `${baseUrl}/projects/${projectKey}/servers/${alias}/userToken`; 90 | 91 | const data = { 92 | token: serverData.default_token 93 | } 94 | 95 | return axios.put(fullUrl, data, { 96 | timeout: 1000 * 60, 97 | headers: { 98 | 'content-type': 'application/json' 99 | } 100 | }) 101 | } 102 | 103 | export const removeUserToken = (context, projectKey, alias) => { 104 | const baseUrl = getRestUrl(context); 105 | let name = alias === "" ? "alias" : alias; 106 | let fullUrl = projectKey === "" ? 107 | `${baseUrl}/global/servers/${name}/userToken` : 108 | `${baseUrl}/projects/${projectKey}/servers/${name}/userToken`; 109 | 110 | return axios.delete(fullUrl, { 111 | timeout: 1000 * 60 112 | }) 113 | } -------------------------------------------------------------------------------- /parameterized-client/src/hook/state-reducers.js: -------------------------------------------------------------------------------- 1 | const getInitialState = () => { 2 | return { 3 | id: null, 4 | active: true, 5 | jobName: "", 6 | jenkinsServer: null, 7 | isTag: false, 8 | isPipeline: false, 9 | triggers: "", 10 | token: "", 11 | buildParameters: "", 12 | branchRegex: "", 13 | pathRegex: "", 14 | requirePermission: "REPO_READ", 15 | prDestinationRegex: "", 16 | ignoreComitters: "", 17 | ignoreCommitMsg: "", 18 | } 19 | }; 20 | 21 | const jobState = (state=getInitialState(), action) => { 22 | switch (action.type) { 23 | case 'ADD_JOB': 24 | return { 25 | ...getInitialState(), 26 | id: action.id, 27 | }; 28 | case 'TOGGLE_JOB': 29 | return { 30 | ...state, 31 | active: action.id === state.id ? !state.active : state.active 32 | }; 33 | case 'DECREMENT_ID': 34 | return { 35 | ...state, 36 | id: state.id - 1 37 | }; 38 | case 'UPDATE_TEXT_FIELD': 39 | if (action.id !== state.id){ 40 | return state; 41 | } 42 | let newState = { 43 | ...state, 44 | }; 45 | newState[action.field] = action.value; 46 | return newState; 47 | case 'UPDATE_TRIGGER_FIELD': 48 | if (action.id !== state.id){ 49 | return state; 50 | } 51 | if (state.triggers.includes(action.value)){ 52 | return { 53 | ...state, 54 | triggers: state.triggers.replace(action.value, "") 55 | } 56 | } else { 57 | return { 58 | ...state, 59 | triggers: state.triggers + action.value 60 | } 61 | } 62 | default: 63 | return state 64 | } 65 | }; 66 | 67 | const createInitialState = (config) => { 68 | let i = 0; 69 | let initialState = []; 70 | while (typeof config["jobName-" + i] !== 'undefined'){ 71 | let newJob = { 72 | id: i, 73 | active: false, 74 | jobName: config["jobName-" + i], 75 | jenkinsServer: config["jenkinsServer-" + i], 76 | isTag: config["isTag-" + i] == 'true', 77 | isPipeline: config["isPipeline-" + i], 78 | triggers: config["triggers-" + i], 79 | token: config["token-" + i], 80 | buildParameters: config["buildParameters-" + i], 81 | branchRegex: config["branchRegex-" + i], 82 | pathRegex: config["pathRegex-" + i], 83 | requirePermission: config["requirePermission-" + i], 84 | prDestinationRegex: config["prDestinationRegex-" + i], 85 | ignoreComitters: config["ignoreComitters-" + i], 86 | ignoreCommitMsg: config["ignoreCommitMsg-" + i], 87 | }; 88 | if(newJob.triggers !== null) { 89 | newJob.triggers = newJob.triggers.replace('pullrequest;', 'propened;prreopened;prsourcerescoped;'); 90 | } 91 | initialState.push(newJob); 92 | i++; 93 | } 94 | return initialState; 95 | }; 96 | 97 | const jobs = (state = [], action) => { 98 | switch (action.type) { 99 | case 'INITIALIZE': 100 | return createInitialState(action.baseConfig); 101 | case 'ADD_JOB': 102 | return [ 103 | ...state, 104 | jobState(undefined, {...action, id: state.length}) 105 | ]; 106 | case 'TOGGLE_JOB': 107 | return state.map(t => jobState(t, action)); 108 | case 'DELETE_JOB': 109 | return [ 110 | ...state.slice(0, action.id), 111 | ...state.slice(action.id + 1).map(t => 112 | jobState(t, { 113 | ...action, 114 | type: 'DECREMENT_ID' 115 | })) 116 | ]; 117 | case 'UPDATE_TEXT_FIELD': 118 | return state.map(t => jobState(t, action)); 119 | case 'UPDATE_TRIGGER_FIELD': 120 | return state.map(t => jobState(t, action)); 121 | default: 122 | return state 123 | } 124 | }; 125 | 126 | export const jobDefinitions = (state = {jobs: [], jenkinsServers: []}, action) => { 127 | switch (action.type){ 128 | case 'UPDATE_JENKINS_SERVERS': 129 | return { 130 | ...state, 131 | jenkinsServers: action.servers 132 | } 133 | default: 134 | return { 135 | ...state, 136 | jobs: jobs(state.jobs, action) 137 | } 138 | } 139 | }; 140 | 141 | export const addJob = () => { 142 | return { 143 | type: 'ADD_JOB', 144 | } 145 | }; -------------------------------------------------------------------------------- /parameterized-client/src/hook/view.js: -------------------------------------------------------------------------------- 1 | import { Provider, connect } from 'react-redux'; 2 | import { createStore } from 'redux'; 3 | import React from 'react'; 4 | import { jobDefinitions, addJob } from "./state-reducers"; 5 | import { Job } from "./job"; 6 | import axios from 'axios'; 7 | 8 | window.parameterizedbuilds = window.parameterizedbuilds || {}; 9 | 10 | const getJenkinsServers = () => { 11 | const urlRegex = /(.+?)(\/projects\/.+?\/repos\/.+?\/)settings.*/ 12 | let urlParts = window.location.href.match(urlRegex); 13 | let restUrl = '' 14 | if (urlParts !== null) { 15 | restUrl = urlParts[1] + "/rest/parameterized-builds/latest" + urlParts[2] + "getJenkinsServers"; 16 | } else { 17 | const urlRegex = /(.+?)(\/projects\/.+?\/)settings.*/ 18 | urlParts = window.location.href.match(urlRegex); 19 | restUrl = urlParts[1] + "/rest/parameterized-builds/latest" + urlParts[2] + "servers"; 20 | } 21 | return axios.get(restUrl, { 22 | timeout: 1000 * 60 23 | }); 24 | } 25 | 26 | let AddJob = ({ dispatch }) => { 27 | return ( 28 | 29 | { 30 | dispatch(addJob()); 31 | }} /> 32 | 33 | ) 34 | }; 35 | AddJob = connect()(AddJob); 36 | 37 | 38 | let JobList = ({jobs, errors}) => { 39 | return ( 40 |
41 | { 42 | jobs.map(job => 43 | 44 | ) 45 | } 46 |
47 | ) 48 | }; 49 | 50 | const jobListStateInjector = (state, ownProps) => { 51 | return { 52 | jobs: state.jobs, 53 | errors: ownProps.errors 54 | } 55 | }; 56 | JobList = connect(jobListStateInjector)(JobList); 57 | 58 | const App = ({errors}) => { 59 | return ( 60 |
61 | {typeof errors["jenkins-admin-error"] !== 'undefined' && 62 |
63 |
64 |

{errors['jenkins-admin-error']}

65 |
66 |
67 | } 68 | 69 | 70 |
71 | ) 72 | }; 73 | 74 | parameterizedbuilds.view = function({config, errors}) { 75 | const baseStore = createStore(jobDefinitions); 76 | 77 | baseStore.dispatch({ 78 | type: "INITIALIZE", 79 | baseConfig: config, 80 | }); 81 | 82 | getJenkinsServers().then(response => { 83 | baseStore.dispatch({ 84 | type: "UPDATE_JENKINS_SERVERS", 85 | servers: response.data, 86 | }); 87 | }); 88 | 89 | return ( 90 | 91 | 92 | 93 | ) 94 | 95 | }; 96 | -------------------------------------------------------------------------------- /parameterized-client/src/jenkins_settings/state-reducers.js: -------------------------------------------------------------------------------- 1 | const createServer = (initialState = {}) => { 2 | return { 3 | id: null, 4 | url: "", 5 | alias: "", 6 | old_alias: initialState.alias || "", 7 | default_user: "", 8 | default_token: null, 9 | root_token_enabled: false, 10 | csrf_enabled: false, 11 | action_message: "", 12 | action_state: null, 13 | show_clear_modal: false, 14 | ...initialState 15 | } 16 | }; 17 | 18 | 19 | const server = (state=createServer(), action) => { 20 | switch(action.type) { 21 | case 'ADD_SERVER': 22 | return { 23 | ...createServer(action.serverConfig), 24 | id: action.id 25 | }; 26 | case 'DECREMENT_ID': 27 | return { 28 | ...state, 29 | id: state.id - 1 30 | }; 31 | case 'UPDATE_FIELD': 32 | let newState = { 33 | ...state, 34 | }; 35 | newState[action.field] = action.value; 36 | return newState; 37 | default: 38 | return state; 39 | } 40 | } 41 | 42 | 43 | const servers = (state = [], action) => { 44 | switch (action.type) { 45 | case 'INITIALIZE': 46 | return action.baseConfig.map((serverConfig, index) => 47 | server(state, { 48 | ...action, 49 | id: index, 50 | serverConfig: serverConfig, 51 | type: 'ADD_SERVER' 52 | })) 53 | case 'ADD_SERVER': 54 | return [ 55 | ...state, 56 | server(undefined, {...action, id: state.length}) 57 | ]; 58 | case 'DELETE_SERVER': 59 | let newState = [ 60 | ...state.slice(0, action.id), 61 | ...state.slice(action.id + 1).map(t => 62 | server(t, { 63 | ...action, 64 | type: 'DECREMENT_ID' 65 | })) 66 | ]; 67 | // always render an empty form 68 | if (newState.length == 0){ 69 | return [ 70 | server(undefined, {type: 'ADD_SERVER', id: 0}) 71 | ] 72 | } else { 73 | return newState 74 | } 75 | case 'UPDATE_FIELD': 76 | return [ 77 | ...state.slice(0, action.id), 78 | server(state[action.id], action), 79 | ...state.slice(action.id + 1) 80 | ]; 81 | default: 82 | return state 83 | } 84 | }; 85 | 86 | export const serverDefinitions = (state = {servers: [], project: null}, action) => { 87 | switch (action.type){ 88 | case 'INITIALIZE': 89 | return { 90 | ...state, 91 | project: action.project, 92 | context: action.context, 93 | servers: servers(state.servers, action) 94 | } 95 | default: 96 | return { 97 | ...state, 98 | servers: servers(state.servers, action) 99 | } 100 | } 101 | }; -------------------------------------------------------------------------------- /parameterized-client/src/jenkins_settings/view.js: -------------------------------------------------------------------------------- 1 | import { getJenkinsServers } from '../common/rest' 2 | import React from 'react'; 3 | import { render } from 'react-dom'; 4 | import { Provider, connect } from 'react-redux'; 5 | import { createStore } from 'redux'; 6 | import { Server } from './server' 7 | import { serverDefinitions } from "./state-reducers"; 8 | import { Form } from "../common/aui"; 9 | 10 | 11 | let ServerList = ({ servers }) => { 12 | return ( 13 |
14 | { 15 | servers.map((server, i) => 16 | 17 | ) 18 | } 19 | 20 | ) 21 | }; 22 | const serverListStateInjector = (state, ownProps) => { 23 | return { 24 | servers: state.servers 25 | } 26 | } 27 | ServerList = connect(serverListStateInjector)(ServerList); 28 | 29 | document.addEventListener("DOMContentLoaded", () => { 30 | const baseStore = createStore(serverDefinitions); 31 | 32 | const projectKey = document.getElementById('project-key').innerHTML; 33 | const bitbucketContext = document.getElementById('bitbucket-context').innerHTML; 34 | getJenkinsServers(bitbucketContext, projectKey).then(response => { 35 | const servers = response.data; 36 | 37 | baseStore.dispatch({ 38 | type: "INITIALIZE", 39 | baseConfig: servers, 40 | project: projectKey, 41 | context: bitbucketContext 42 | }); 43 | 44 | if (servers.length == 0){ 45 | baseStore.dispatch({ 46 | type: "ADD_SERVER" 47 | }); 48 | } 49 | }).catch(response => { 50 | console.error(response); 51 | }); 52 | 53 | const app = ( 54 | 55 | 56 | 57 | ) 58 | 59 | render(app, document.getElementById('jenkins-settings')) 60 | }); 61 | -------------------------------------------------------------------------------- /parameterized-client/src/user_settings/server.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { connect } from 'react-redux'; 3 | import { updateUserToken, removeUserToken, testJenkinsServer } from '../common/rest' 4 | import { PasswordInput, ButtonGroup, Button, Message } from '../common/aui' 5 | 6 | const ActionDialog = ({ 7 | message, 8 | state 9 | }) => { 10 | switch(state){ 11 | case 'LOADING': 12 | return ( 13 | 14 | {message} 15 | 16 | ); 17 | case 'ERROR': 18 | return ( 19 | 20 | {message} 21 | 22 | ); 23 | case 'SUCCESS': 24 | return ( 25 | 26 | {message} 27 | 28 | ) 29 | } 30 | } 31 | 32 | const ServerContainer = ({ 33 | serverData, 34 | context, 35 | updateServer 36 | }) => { 37 | 38 | const updateToken = e => { 39 | e.preventDefault(); 40 | updateServer(serverData.id, "action_message", "Saving settings..."); 41 | updateServer(serverData.id, "action_state", "LOADING"); 42 | updateUserToken(context, serverData.project_key, serverData).then(response => { 43 | updateServer(serverData.id, "action_message", "Token saved!") 44 | updateServer(serverData.id, "action_state", "SUCCESS") 45 | }).catch(error => { 46 | const message = `Failed to save token:\n${error.response.data.message}` 47 | updateServer(serverData.id, "action_message", message) 48 | updateServer(serverData.id, "action_state", "ERROR") 49 | }); 50 | } 51 | 52 | const removeToken = e => { 53 | e.preventDefault(); 54 | const alias = serverData.alias; 55 | updateServer(serverData.id, "show_clear_modal", false) 56 | updateServer(serverData.id, "action_message", "Removing settings...") 57 | updateServer(serverData.id, "action_state", "LOADING") 58 | removeUserToken(context, serverData.project_key, alias).then(response => { 59 | updateServer(serverData.id, "default_token", "") 60 | updateServer(serverData.id, "action_message", "Token removed!") 61 | updateServer(serverData.id, "action_state", "SUCCESS") 62 | }).catch(error => { 63 | updateServer(serverData.id, "action_message", "Could not remove token") 64 | updateServer(serverData.id, "action_state", "ERROR") 65 | }); 66 | } 67 | 68 | const testServer = e => { 69 | e.preventDefault(); 70 | updateServer(serverData.id, "action_message", "Testing settings...") 71 | updateServer(serverData.id, "action_state", "LOADING") 72 | testJenkinsServer(context, serverData.project_key, serverData).then(response => { 73 | updateServer(serverData.id, "action_message", response.data) 74 | updateServer(serverData.id, "action_state", "SUCCESS") 75 | }).catch(error => { 76 | updateServer(serverData.id, "action_message", error.response.data) 77 | updateServer(serverData.id, "action_state", "ERROR") 78 | }); 79 | } 80 | 81 | const openAPITokenWindow = e => { 82 | e.preventDefault(); 83 | const userSlug = document.getElementsByName("userSlug")[0].content; 84 | window.open(`${serverData.url}/user/${userSlug}/configure`, '_blank') 85 | } 86 | 87 | return ( 88 |
89 | updateServer(serverData.id, "default_token", e.target.value)}/> 92 | 93 |
106 | ); 107 | } 108 | 109 | const mapStateToServerContainerProps = (state, ownProps) => { 110 | return { 111 | serverData: state.servers[ownProps.id], 112 | context: state.context, 113 | }; 114 | }; 115 | 116 | const mapDispatchToServerContainerProps = dispatch => { 117 | return { 118 | updateServer: (id, fieldName, fieldValue) => { 119 | dispatch({ 120 | type: 'UPDATE_FIELD', 121 | id: id, 122 | field: fieldName, 123 | value: fieldValue 124 | }); 125 | } 126 | } 127 | } 128 | 129 | export const Server = connect( 130 | mapStateToServerContainerProps, 131 | mapDispatchToServerContainerProps)(ServerContainer); -------------------------------------------------------------------------------- /parameterized-client/src/user_settings/state-reducers.js: -------------------------------------------------------------------------------- 1 | const createServer = (initialState = {}) => { 2 | return { 3 | id: null, 4 | project_name: "", 5 | url: "", 6 | alias: "", 7 | default_user: "", 8 | default_token: null, 9 | action_message: "", 10 | action_state: null, 11 | show_clear_modal: false, 12 | ...initialState 13 | } 14 | }; 15 | 16 | 17 | const server = (state={}, action) => { 18 | switch(action.type) { 19 | case 'ADD_SERVER': 20 | return { 21 | ...createServer(action.serverConfig), 22 | id: action.id 23 | }; 24 | case 'DECREMENT_ID': 25 | return { 26 | ...state, 27 | id: state.id - 1 28 | }; 29 | case 'UPDATE_FIELD': 30 | let newState = { 31 | ...state, 32 | }; 33 | newState[action.field] = action.value; 34 | return newState; 35 | default: 36 | return state; 37 | } 38 | } 39 | 40 | 41 | const servers = (state = [], action) => { 42 | switch (action.type) { 43 | case 'INITIALIZE': 44 | return action.baseConfig.map((serverConfig, index) => 45 | server(state, { 46 | ...action, 47 | id: index, 48 | serverConfig: serverConfig, 49 | type: 'ADD_SERVER' 50 | })) 51 | case 'DELETE_SERVER': 52 | let newState = [ 53 | ...state.slice(0, action.id), 54 | ...state.slice(action.id + 1).map(t => 55 | server(t, { 56 | ...action, 57 | type: 'DECREMENT_ID' 58 | })) 59 | ]; 60 | // always render an empty form 61 | if (newState.length == 0){ 62 | return [ 63 | server(undefined, {type: 'ADD_SERVER', id: 0}) 64 | ] 65 | } else { 66 | return newState 67 | } 68 | case 'UPDATE_FIELD': 69 | return [ 70 | ...state.slice(0, action.id), 71 | server(state[action.id], action), 72 | ...state.slice(action.id + 1) 73 | ]; 74 | default: 75 | return state 76 | } 77 | }; 78 | 79 | export const serverDefinitions = (state = {servers: []}, action) => { 80 | switch (action.type){ 81 | case 'INITIALIZE': 82 | return { 83 | ...state, 84 | context: action.context, 85 | servers: servers(state.servers, action) 86 | } 87 | default: 88 | return { 89 | ...state, 90 | servers: servers(state.servers, action) 91 | } 92 | } 93 | }; -------------------------------------------------------------------------------- /parameterized-client/src/user_settings/user_view.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from 'react-dom'; 3 | import { Provider, connect } from 'react-redux'; 4 | import { createStore } from 'redux'; 5 | import { Server } from './server' 6 | import { serverDefinitions } from "./state-reducers"; 7 | import { Form } from "../common/aui"; 8 | 9 | let ServerList = ({ servers }) => { 10 | 11 | if (servers.length === 0){ 12 | return
13 | No Jenkins servers were found. Contact your Bitbucket Server administrator to add a Jenkins server. 14 |
15 | } 16 | 17 | const serversByProject = servers.reduce(function(rv, x) { 18 | (rv[x["project_name"]] = rv[x["project_name"]] || []).push(x); 19 | return rv; 20 | }, {}); 21 | 22 | return ( 23 |
24 | {Object.keys(serversByProject).map(projectName => 25 |
26 |

{projectName} Servers

27 | {serversByProject[projectName].map(server => 28 |
29 | 30 | 31 | )} 32 |
33 |
34 | )} 35 |
36 | ) 37 | }; 38 | const serverListStateInjector = (state, ownProps) => { 39 | return { 40 | servers: state.servers 41 | } 42 | } 43 | ServerList = connect(serverListStateInjector)(ServerList); 44 | 45 | document.addEventListener("DOMContentLoaded", () => { 46 | const baseStore = createStore(serverDefinitions); 47 | 48 | const servers = JSON.parse(document.getElementById('project-tokens').innerHTML); 49 | const bitbucketContext = document.getElementById('bitbucket-context').innerHTML; 50 | 51 | baseStore.dispatch({ 52 | type: "INITIALIZE", 53 | baseConfig: servers, 54 | project: null, 55 | context: bitbucketContext 56 | }); 57 | 58 | const app = ( 59 | 60 | 61 | 62 | ) 63 | 64 | render(app, document.getElementById('jenkins-settings')) 65 | }); -------------------------------------------------------------------------------- /parameterized-client/webpack.config.js: -------------------------------------------------------------------------------- 1 | require('webpack'); 2 | const path = require('path'); 3 | 4 | module.exports = { 5 | mode: 'production', 6 | entry: { 7 | 'hook/parameterized_build_hook': ['./src/hook/view.js'], 8 | 'jenkins_settings/server_form': ['./src/jenkins_settings/view.js'], 9 | 'jenkins_settings/user_server_form': ['./src/user_settings/user_view.js'] 10 | }, 11 | output: { 12 | path: path.join(__dirname, '../src/main/resources/'), 13 | filename: '[name].pack.js' 14 | }, 15 | module: { 16 | rules: [ 17 | { 18 | test: /\.js$/, 19 | exclude: /node_modules/, 20 | use: { 21 | loader: 'babel-loader', 22 | options: { 23 | presets: ['@babel/preset-env', '@babel/preset-react'], 24 | }, 25 | }, 26 | }, 27 | ], 28 | }, 29 | plugins: [ 30 | ], 31 | externals: [ 32 | ], 33 | }; -------------------------------------------------------------------------------- /readme/img/build1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ParameterizedBuilds/parameterized-builds/2a27ca579ac233bcc68ca886c41bd8578e25a7d3/readme/img/build1.png -------------------------------------------------------------------------------- /readme/img/build2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ParameterizedBuilds/parameterized-builds/2a27ca579ac233bcc68ca886c41bd8578e25a7d3/readme/img/build2.png -------------------------------------------------------------------------------- /readme/img/build3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ParameterizedBuilds/parameterized-builds/2a27ca579ac233bcc68ca886c41bd8578e25a7d3/readme/img/build3.png -------------------------------------------------------------------------------- /readme/img/build_dialog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ParameterizedBuilds/parameterized-builds/2a27ca579ac233bcc68ca886c41bd8578e25a7d3/readme/img/build_dialog.png -------------------------------------------------------------------------------- /readme/img/jenkins_admin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ParameterizedBuilds/parameterized-builds/2a27ca579ac233bcc68ca886c41bd8578e25a7d3/readme/img/jenkins_admin.png -------------------------------------------------------------------------------- /readme/img/jenkins_hook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ParameterizedBuilds/parameterized-builds/2a27ca579ac233bcc68ca886c41bd8578e25a7d3/readme/img/jenkins_hook.png -------------------------------------------------------------------------------- /readme/img/jenkins_hook2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ParameterizedBuilds/parameterized-builds/2a27ca579ac233bcc68ca886c41bd8578e25a7d3/readme/img/jenkins_hook2.png -------------------------------------------------------------------------------- /readme/img/jenkins_user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ParameterizedBuilds/parameterized-builds/2a27ca579ac233bcc68ca886c41bd8578e25a7d3/readme/img/jenkins_user.png -------------------------------------------------------------------------------- /readme/img/triggers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ParameterizedBuilds/parameterized-builds/2a27ca579ac233bcc68ca886c41bd8578e25a7d3/readme/img/triggers.png -------------------------------------------------------------------------------- /src/main/java/com/kylenicholls/stash/parameterizedbuilds/ParameterizedBuildHook.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds; 2 | 3 | import java.util.Collection; 4 | import java.util.List; 5 | import java.util.concurrent.ExecutorService; 6 | import java.util.regex.Pattern; 7 | import java.util.regex.PatternSyntaxException; 8 | 9 | import com.atlassian.bitbucket.auth.AuthenticationContext; 10 | import com.atlassian.bitbucket.commit.CommitService; 11 | import com.atlassian.bitbucket.hook.repository.PostRepositoryHook; 12 | import com.atlassian.bitbucket.hook.repository.PostRepositoryHookContext; 13 | import com.atlassian.bitbucket.hook.repository.RepositoryHookRequest; 14 | import com.atlassian.bitbucket.repository.RefChange; 15 | import com.atlassian.bitbucket.repository.Repository; 16 | import com.atlassian.bitbucket.scope.Scope; 17 | import com.atlassian.bitbucket.server.ApplicationPropertiesService; 18 | import com.atlassian.bitbucket.setting.SettingsValidator; 19 | import com.atlassian.bitbucket.setting.Settings; 20 | import com.atlassian.bitbucket.setting.SettingsValidationErrors; 21 | import com.atlassian.bitbucket.user.ApplicationUser; 22 | import com.atlassian.plugin.spring.scanner.annotation.imports.ComponentImport; 23 | import com.kylenicholls.stash.parameterizedbuilds.ciserver.Jenkins; 24 | import com.kylenicholls.stash.parameterizedbuilds.eventHandlers.PushHandler; 25 | import com.kylenicholls.stash.parameterizedbuilds.eventHandlers.RefCreatedHandler; 26 | import com.kylenicholls.stash.parameterizedbuilds.eventHandlers.RefDeletedHandler; 27 | import com.kylenicholls.stash.parameterizedbuilds.eventHandlers.RefHandler; 28 | import com.kylenicholls.stash.parameterizedbuilds.helper.ScopeProjectVisitor; 29 | import com.kylenicholls.stash.parameterizedbuilds.helper.SettingsService; 30 | import com.kylenicholls.stash.parameterizedbuilds.item.Job; 31 | import com.kylenicholls.stash.parameterizedbuilds.item.Job.Trigger; 32 | import com.kylenicholls.stash.parameterizedbuilds.item.Server; 33 | 34 | public class ParameterizedBuildHook 35 | implements PostRepositoryHook, SettingsValidator { 36 | 37 | private final SettingsService settingsService; 38 | private final CommitService commitService; 39 | private final Jenkins jenkins; 40 | private String url; 41 | private ApplicationUser user; 42 | private ExecutorService executorService; 43 | 44 | public ParameterizedBuildHook( 45 | SettingsService settingsService, 46 | CommitService commitService, 47 | Jenkins jenkins, 48 | ApplicationPropertiesService applicationPropertiesService, 49 | AuthenticationContext actx, 50 | @ComponentImport 51 | ExecutorService executorService) { 52 | 53 | this.settingsService = settingsService; 54 | this.commitService = commitService; 55 | this.jenkins = jenkins; 56 | this.url = applicationPropertiesService.getBaseUrl().toString(); 57 | this.user = actx.getCurrentUser(); 58 | this.executorService = executorService; 59 | } 60 | 61 | @Override 62 | public void postUpdate(PostRepositoryHookContext context, RepositoryHookRequest request) { 63 | Collection refChanges = request.getRefChanges(); 64 | Repository repository = request.getRepository(); 65 | 66 | for (RefChange refChange : refChanges) { 67 | this.executorService.submit(() -> { 68 | RefHandler refHandler = createHandler(refChange, repository); 69 | refHandler.run(); 70 | }); 71 | } 72 | } 73 | 74 | RefHandler createHandler(RefChange refChange, Repository repository){ 75 | switch (refChange.getType()) { 76 | case ADD: return new RefCreatedHandler(settingsService, jenkins, commitService, 77 | repository, refChange, url, user); 78 | case DELETE: return new RefDeletedHandler(settingsService, jenkins, commitService, 79 | repository, refChange, url, user); 80 | case UPDATE: return new PushHandler(settingsService, jenkins, commitService, repository, 81 | refChange, url, user); 82 | default: return new RefHandler(settingsService, jenkins, commitService, repository, 83 | refChange, url, user, Trigger.NULL); 84 | } 85 | } 86 | 87 | @Override 88 | public void validate(Settings settings, SettingsValidationErrors errors, Scope scope) { 89 | String projectKey = scope.accept(new ScopeProjectVisitor()).getKey(); 90 | List servers = jenkins.getJenkinsServers(projectKey); 91 | servers.addAll(jenkins.getJenkinsServers(null)); 92 | boolean serverExists = servers.stream() 93 | .anyMatch(server -> !server.getBaseUrl().isEmpty()); 94 | 95 | if (!serverExists) { 96 | errors.addFieldError("jenkins-admin-error", "Jenkins is not setup in Bitbucket Server"); 97 | return; 98 | } 99 | List jobList = settingsService.getJobs(settings.asMap()); 100 | for (int i = 0; i < jobList.size(); i++) { 101 | Job job = jobList.get(i); 102 | if (job.getJobName().isEmpty()) { 103 | errors.addFieldError(SettingsService.JOB_PREFIX + i, "Field is required"); 104 | } 105 | 106 | if (job.getJenkinsServer().isEmpty()) { 107 | errors.addFieldError(SettingsService.SERVER_PREFIX + i, 108 | "You must choose a jenkins server"); 109 | } 110 | 111 | if (job.getTriggers().contains(Trigger.NULL)) { 112 | errors.addFieldError(SettingsService.TRIGGER_PREFIX 113 | + i, "You must choose at least one trigger"); 114 | } 115 | 116 | PatternSyntaxException branchException = null; 117 | try { 118 | Pattern.compile(job.getBranchRegex()); 119 | } catch (PatternSyntaxException e) { 120 | branchException = e; 121 | } 122 | if (branchException != null) { 123 | errors.addFieldError(SettingsService.BRANCH_PREFIX + i, branchException 124 | .getDescription()); 125 | } 126 | 127 | PatternSyntaxException pathException = null; 128 | try { 129 | Pattern.compile(job.getPathRegex()); 130 | } catch (PatternSyntaxException e) { 131 | pathException = e; 132 | } 133 | if (pathException != null) { 134 | errors.addFieldError(SettingsService.PATH_PREFIX + i, pathException 135 | .getDescription()); 136 | } 137 | 138 | PatternSyntaxException ignoreCommitMsgException = null; 139 | try{ 140 | Pattern.compile(job.getIgnoreCommitMsg()); 141 | } catch (PatternSyntaxException e){ 142 | ignoreCommitMsgException = e; 143 | } 144 | if(ignoreCommitMsgException != null) { 145 | errors.addFieldError(SettingsService.IGNORE_COMMITTERS_PREFIX + i, 146 | ignoreCommitMsgException.getDescription()); 147 | } 148 | } 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /src/main/java/com/kylenicholls/stash/parameterizedbuilds/PullRequestHook.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds; 2 | 3 | import java.io.IOException; 4 | import java.util.concurrent.ExecutorService; 5 | 6 | import com.atlassian.bitbucket.pull.PullRequest; 7 | import com.atlassian.bitbucket.pull.PullRequestService; 8 | import com.atlassian.bitbucket.repository.Branch; 9 | import com.atlassian.bitbucket.server.ApplicationPropertiesService; 10 | import com.atlassian.event.api.EventListener; 11 | import com.atlassian.plugin.spring.scanner.annotation.imports.ComponentImport; 12 | import com.kylenicholls.stash.parameterizedbuilds.ciserver.Jenkins; 13 | import com.kylenicholls.stash.parameterizedbuilds.eventHandlers.BaseHandler; 14 | import com.kylenicholls.stash.parameterizedbuilds.eventHandlers.PRApprovedHandler; 15 | import com.kylenicholls.stash.parameterizedbuilds.eventHandlers.PRAutoMergedHandler; 16 | import com.kylenicholls.stash.parameterizedbuilds.eventHandlers.PRDeclinedHandler; 17 | import com.kylenicholls.stash.parameterizedbuilds.eventHandlers.PRDeletedHandler; 18 | import com.kylenicholls.stash.parameterizedbuilds.eventHandlers.PRDestRescopedHandler; 19 | import com.kylenicholls.stash.parameterizedbuilds.eventHandlers.PRMergedHandler; 20 | import com.kylenicholls.stash.parameterizedbuilds.eventHandlers.PROpenedHandler; 21 | import com.kylenicholls.stash.parameterizedbuilds.eventHandlers.PRReopenedHandler; 22 | import com.kylenicholls.stash.parameterizedbuilds.eventHandlers.PRSourceRescopedHandler; 23 | import com.kylenicholls.stash.parameterizedbuilds.helper.SettingsService; 24 | import com.atlassian.bitbucket.branch.automerge.AutomaticMergeEvent; 25 | import com.atlassian.bitbucket.event.pull.PullRequestDeclinedEvent; 26 | import com.atlassian.bitbucket.event.pull.PullRequestDeletedEvent; 27 | import com.atlassian.bitbucket.event.pull.PullRequestMergedEvent; 28 | import com.atlassian.bitbucket.event.pull.PullRequestOpenedEvent; 29 | import com.atlassian.bitbucket.event.pull.PullRequestParticipantApprovedEvent; 30 | import com.atlassian.bitbucket.event.pull.PullRequestReopenedEvent; 31 | import com.atlassian.bitbucket.event.pull.PullRequestRescopedEvent; 32 | 33 | public class PullRequestHook { 34 | private final SettingsService settingsService; 35 | private final PullRequestService pullRequestService; 36 | private final Jenkins jenkins; 37 | private final String url; 38 | private final ExecutorService executorService; 39 | 40 | public PullRequestHook( 41 | SettingsService settingsService, 42 | PullRequestService pullRequestService, 43 | Jenkins jenkins, 44 | ApplicationPropertiesService applicationPropertiesService, 45 | @ComponentImport 46 | ExecutorService executorService) { 47 | 48 | this.settingsService = settingsService; 49 | this.pullRequestService = pullRequestService; 50 | this.jenkins = jenkins; 51 | this.url = applicationPropertiesService.getBaseUrl().toString(); 52 | this.executorService = executorService; 53 | } 54 | 55 | @EventListener 56 | public void onPullRequestOpened(PullRequestOpenedEvent event) throws IOException { 57 | runHandler(new PROpenedHandler(settingsService, pullRequestService, jenkins, event, url)); 58 | } 59 | 60 | @EventListener 61 | public void onPullRequestReOpened(PullRequestReopenedEvent event) throws IOException { 62 | runHandler(new PRReopenedHandler(settingsService, pullRequestService, jenkins, event, url)); 63 | } 64 | 65 | @EventListener 66 | public void onPullRequestRescoped(PullRequestRescopedEvent event) throws IOException { 67 | PullRequest pullRequest = event.getPullRequest(); 68 | // Rescoped event is triggered if the source OR destination branch is 69 | // updated. If last and current hash on source branch is equal, we assume destination branch 70 | // has changed 71 | if (event.getPreviousFromHash().equals(pullRequest.getFromRef().getLatestCommit())) { 72 | runHandler(new PRDestRescopedHandler(settingsService, pullRequestService, jenkins, 73 | event, url)); 74 | } else { 75 | runHandler(new PRSourceRescopedHandler(settingsService, pullRequestService, jenkins, 76 | event, url)); 77 | } 78 | } 79 | 80 | @EventListener 81 | public void onPullRequestMerged(PullRequestMergedEvent event) throws IOException { 82 | runHandler(new PRMergedHandler(settingsService, pullRequestService, jenkins, event, url)); 83 | } 84 | 85 | @EventListener 86 | public void onPullRequestAutomaticMerged(AutomaticMergeEvent event) throws IOException { 87 | Iterable branches = event.getMergePath(); 88 | for (Branch branch : branches){ 89 | runHandler(new PRAutoMergedHandler(settingsService, jenkins, event, url, branch)); 90 | } 91 | } 92 | 93 | @EventListener 94 | public void onPullRequestDeclined(PullRequestDeclinedEvent event) throws IOException { 95 | runHandler(new PRDeclinedHandler(settingsService, pullRequestService, jenkins, event, url)); 96 | } 97 | 98 | @EventListener 99 | public void onPullRequestDeleted(PullRequestDeletedEvent event) throws IOException { 100 | runHandler(new PRDeletedHandler(settingsService, pullRequestService, jenkins, event, url)); 101 | } 102 | 103 | @EventListener 104 | public void onPullRequestApproved(PullRequestParticipantApprovedEvent event) 105 | throws IOException { 106 | runHandler(new PRApprovedHandler(settingsService, pullRequestService, jenkins, event, url)); 107 | } 108 | 109 | protected void runHandler(BaseHandler handler) { 110 | this.executorService.submit(() -> handler.run()); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/main/java/com/kylenicholls/stash/parameterizedbuilds/ciserver/AccountServer.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.ciserver; 2 | 3 | import com.atlassian.bitbucket.project.ProjectService; 4 | import com.atlassian.bitbucket.user.ApplicationUser; 5 | import com.google.common.collect.ImmutableMap; 6 | import com.kylenicholls.stash.parameterizedbuilds.item.UserToken; 7 | 8 | import java.util.HashMap; 9 | import java.util.List; 10 | import java.util.Map; 11 | 12 | import com.google.gson.JsonArray; 13 | 14 | public class AccountServer extends CIServer { 15 | 16 | private static final String PROJECT_TOKENS_KEY = "projectTokens"; 17 | private static final String USER_KEY = "user"; 18 | 19 | private final transient ProjectService projectService; 20 | private ApplicationUser user; 21 | private Jenkins jenkins; 22 | 23 | public AccountServer(Jenkins jenkins, ApplicationUser user, ProjectService projectService){ 24 | this.jenkins = jenkins; 25 | this.user = user; 26 | this.projectService = projectService; 27 | this.JENKINS_SETTINGS = "jenkins.user.settings"; 28 | this.ADDITIONAL_JS = "jenkins-user-settings-form"; 29 | } 30 | 31 | public ImmutableMap renderMap(Map renderOptions){ 32 | List projectTokens = jenkins 33 | .getAllUserTokens(user, projectService.findAllKeys(), projectService); 34 | 35 | JsonArray tokenArray = new JsonArray(); 36 | projectTokens.stream() 37 | .map(UserToken::toJson) 38 | .forEach(tokenArray::add); 39 | 40 | @SuppressWarnings("serial") 41 | Map baseMap = new HashMap() {{ 42 | put(USER_KEY, user); 43 | put(PROJECT_TOKENS_KEY, tokenArray.toString()); 44 | putAll(renderOptions); 45 | }}; 46 | return ImmutableMap.copyOf(baseMap); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/kylenicholls/stash/parameterizedbuilds/ciserver/CIServer.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.ciserver; 2 | 3 | import com.google.common.collect.ImmutableMap; 4 | 5 | import java.util.Map; 6 | 7 | public abstract class CIServer { 8 | 9 | String JENKINS_SETTINGS; 10 | String ADDITIONAL_JS; 11 | 12 | public ImmutableMap renderMap(){ 13 | return renderMap(ImmutableMap.of()); 14 | } 15 | 16 | public abstract ImmutableMap renderMap(Map renderOptions); 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/kylenicholls/stash/parameterizedbuilds/ciserver/CIServerFactory.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.ciserver; 2 | 3 | import com.atlassian.bitbucket.project.ProjectService; 4 | import com.atlassian.bitbucket.user.ApplicationUser; 5 | 6 | public class CIServerFactory { 7 | 8 | public static CIServer getServer(String pathInfo, Jenkins jenkins, 9 | ApplicationUser user, ProjectService projectService){ 10 | if (pathInfo.contains("/jenkins/account")){ 11 | return new AccountServer(jenkins, user, projectService); 12 | } else if (pathInfo.contains("/jenkins/project/")) { 13 | String projectKey = pathInfo.replaceAll(".*/jenkins/project/", "") 14 | .split("/")[0]; 15 | return new ProjectServer(projectKey); 16 | } else { 17 | return new GlobalServer(); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/kylenicholls/stash/parameterizedbuilds/ciserver/CIServlet.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.ciserver; 2 | 3 | import java.io.IOException; 4 | import java.util.Map; 5 | 6 | import javax.servlet.ServletException; 7 | import javax.servlet.http.HttpServlet; 8 | import javax.servlet.http.HttpServletRequest; 9 | import javax.servlet.http.HttpServletResponse; 10 | 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | 14 | import com.atlassian.bitbucket.auth.AuthenticationContext; 15 | import com.atlassian.bitbucket.nav.NavBuilder; 16 | import com.atlassian.bitbucket.project.ProjectService; 17 | import com.atlassian.soy.renderer.SoyException; 18 | import com.atlassian.soy.renderer.SoyTemplateRenderer; 19 | import com.atlassian.webresource.api.assembler.PageBuilderService; 20 | import com.google.common.collect.ImmutableMap; 21 | 22 | @SuppressWarnings("serial") 23 | public class CIServlet extends HttpServlet { 24 | private static final Logger logger = LoggerFactory.getLogger(CIServlet.class); 25 | private final transient SoyTemplateRenderer soyTemplateRenderer; 26 | private final transient AuthenticationContext authContext; 27 | private final transient NavBuilder navBuilder; 28 | private final transient Jenkins jenkins; 29 | private final transient ProjectService projectService; 30 | private final transient PageBuilderService pageBuilderService; 31 | 32 | public CIServlet(SoyTemplateRenderer soyTemplateRenderer, AuthenticationContext authContext, 33 | NavBuilder navBuilder, Jenkins jenkins, ProjectService projectService, 34 | PageBuilderService pageBuilderService) { 35 | this.soyTemplateRenderer = soyTemplateRenderer; 36 | this.authContext = authContext; 37 | this.navBuilder = navBuilder; 38 | this.jenkins = jenkins; 39 | this.projectService = projectService; 40 | this.pageBuilderService = pageBuilderService; 41 | } 42 | 43 | @Override 44 | protected void doGet(HttpServletRequest req, HttpServletResponse res) 45 | throws ServletException, IOException { 46 | try { 47 | String pathInfo = req.getPathInfo(); 48 | if (authContext.isAuthenticated()) { 49 | CIServer ciServer = CIServerFactory.getServer(pathInfo, jenkins, 50 | authContext.getCurrentUser(), projectService); 51 | render(res, ciServer.JENKINS_SETTINGS, ciServer.ADDITIONAL_JS, 52 | ciServer.renderMap()); 53 | } else { 54 | res.sendRedirect(navBuilder.login().next(req.getServletPath() + pathInfo) 55 | .buildAbsolute()); 56 | } 57 | } catch (Exception e) { 58 | logger.error("Exception in CIServlet.doGet: " + e.getMessage(), e); 59 | } 60 | } 61 | 62 | private void render(HttpServletResponse resp, String templateName, String addedJs, 63 | Map data) 64 | throws IOException, ServletException { 65 | 66 | pageBuilderService.assembler().resources() 67 | .requireWebResource( 68 | "com.kylenicholls.stash.parameterized-builds:" + addedJs); 69 | 70 | String baseUrl = navBuilder.buildRelative(); 71 | 72 | Map renderData = ImmutableMap.builder() 73 | .putAll(data) 74 | .put("bitbucketContext", baseUrl) 75 | .build(); 76 | 77 | resp.setContentType("text/html;charset=UTF-8"); 78 | try { 79 | soyTemplateRenderer.render(resp 80 | .getWriter(), "com.kylenicholls.stash.parameterized-builds:jenkins-admin-soy", 81 | templateName, renderData); 82 | } catch (SoyException e) { 83 | Throwable cause = e.getCause(); 84 | if (cause instanceof IOException) { 85 | throw (IOException) cause; 86 | } 87 | throw new ServletException(e); 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /src/main/java/com/kylenicholls/stash/parameterizedbuilds/ciserver/GlobalServer.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.ciserver; 2 | 3 | import com.google.common.collect.ImmutableMap; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | public class GlobalServer extends CIServer{ 9 | 10 | public GlobalServer(){ 11 | this.JENKINS_SETTINGS = "jenkins.admin.settings"; 12 | this.ADDITIONAL_JS = "jenkins-settings-form"; 13 | } 14 | 15 | public ImmutableMap renderMap(Map renderOptions){ 16 | @SuppressWarnings("serial") 17 | Map baseMap = new HashMap() {{ 18 | putAll(renderOptions); 19 | }}; 20 | return ImmutableMap.copyOf(baseMap); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/kylenicholls/stash/parameterizedbuilds/ciserver/ProjectServer.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.ciserver; 2 | 3 | import com.google.common.collect.ImmutableMap; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | public class ProjectServer extends CIServer{ 9 | 10 | static final String PROJECT_KEY = "projectKey"; 11 | 12 | private String projectKey; 13 | 14 | public ProjectServer(String projectKey){ 15 | this.projectKey = projectKey; 16 | this.JENKINS_SETTINGS = "jenkins.admin.settingsProjectAdmin"; 17 | this.ADDITIONAL_JS = "jenkins-settings-form"; 18 | } 19 | 20 | public ImmutableMap renderMap(Map renderOptions){ 21 | @SuppressWarnings("serial") 22 | Map baseMap = new HashMap() {{ 23 | put(PROJECT_KEY, projectKey); 24 | putAll(renderOptions); 25 | }}; 26 | return ImmutableMap.copyOf(baseMap); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/kylenicholls/stash/parameterizedbuilds/conditions/BaseCondition.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.conditions; 2 | 3 | import java.util.Map; 4 | import java.util.regex.Matcher; 5 | import java.util.regex.Pattern; 6 | 7 | import com.atlassian.bitbucket.repository.Repository; 8 | import com.atlassian.bitbucket.repository.RepositoryService; 9 | import com.atlassian.plugin.web.Condition; 10 | import com.kylenicholls.stash.parameterizedbuilds.helper.SettingsService; 11 | 12 | import javax.servlet.http.HttpServletRequest; 13 | 14 | public abstract class BaseCondition implements Condition { 15 | protected static final String REPOSITORY = "repository"; 16 | private static final String REQUEST = "request"; 17 | private static final Pattern REPOREGEX = Pattern.compile(".*?/projects/(.*?)/repos/(.*?)/.*"); 18 | 19 | protected SettingsService settingsService; 20 | private RepositoryService repositoryService; 21 | 22 | public BaseCondition(RepositoryService repositoryService, SettingsService settingsService) { 23 | this.settingsService = settingsService; 24 | this.repositoryService = repositoryService; 25 | } 26 | 27 | @Override 28 | public void init(Map params) { 29 | // Nothing to do here 30 | } 31 | 32 | private Repository getRepository(HttpServletRequest request){ 33 | String path = request.getRequestURI(); 34 | Matcher matcher = REPOREGEX.matcher(path); 35 | if(matcher.matches()){ 36 | String projectKey = matcher.group(1); 37 | String repoSlug = matcher.group(2); 38 | return repositoryService.getBySlug(projectKey, repoSlug); 39 | } 40 | return null; 41 | } 42 | 43 | protected Repository getRepository(Map context) { 44 | final Object obj = context.get(REPOSITORY); 45 | if (!(obj instanceof Repository)) { 46 | Object request = context.get(REQUEST); 47 | if (!(request instanceof HttpServletRequest)) { 48 | return null; 49 | } 50 | return getRepository((HttpServletRequest) request); 51 | } 52 | return (Repository) obj; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/kylenicholls/stash/parameterizedbuilds/conditions/BuildPermissionsCondition.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.conditions; 2 | 3 | import com.atlassian.bitbucket.auth.AuthenticationContext; 4 | import com.atlassian.bitbucket.permission.Permission; 5 | import com.atlassian.bitbucket.permission.PermissionService; 6 | import com.atlassian.bitbucket.repository.Repository; 7 | import com.atlassian.bitbucket.repository.RepositoryService; 8 | import com.atlassian.bitbucket.setting.Settings; 9 | import com.atlassian.bitbucket.user.ApplicationUser; 10 | import com.atlassian.plugin.spring.scanner.annotation.imports.ComponentImport; 11 | import com.kylenicholls.stash.parameterizedbuilds.helper.SettingsService; 12 | import com.kylenicholls.stash.parameterizedbuilds.item.Job; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | 15 | import java.util.Map; 16 | 17 | public class BuildPermissionsCondition extends BaseCondition{ 18 | 19 | private final PermissionService permissionService; 20 | private final AuthenticationContext authContext; 21 | 22 | @Autowired 23 | public BuildPermissionsCondition(@ComponentImport RepositoryService repositoryService, 24 | @ComponentImport PermissionService permissionService, 25 | SettingsService service, AuthenticationContext authContext) { 26 | super(repositoryService, service); 27 | this.permissionService = permissionService; 28 | this.authContext = authContext; 29 | } 30 | 31 | public boolean checkPermissions(Job job, Repository repository, ApplicationUser user){ 32 | Permission permissionRequired = Permission.valueOf(job.getPermissions()); 33 | return this.permissionService.hasRepositoryPermission(user, repository, permissionRequired); 34 | } 35 | 36 | @Override 37 | public boolean shouldDisplay(Map context) { 38 | final Repository repository = getRepository(context); 39 | if (repository == null) { 40 | return false; 41 | } 42 | Settings settings = settingsService.getSettings(repository); 43 | ApplicationUser user = authContext.getCurrentUser(); 44 | 45 | for (Job job : settingsService.getJobs(settings.asMap())) { 46 | if (checkPermissions(job, repository, user)) { 47 | return true; 48 | } 49 | } 50 | return false; 51 | } 52 | } 53 | 54 | -------------------------------------------------------------------------------- /src/main/java/com/kylenicholls/stash/parameterizedbuilds/conditions/HookIsEnabledCondition.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.conditions; 2 | 3 | import java.util.Map; 4 | 5 | import com.atlassian.bitbucket.hook.repository.RepositoryHook; 6 | import com.atlassian.bitbucket.repository.Repository; 7 | import com.atlassian.bitbucket.repository.RepositoryService; 8 | import com.atlassian.plugin.spring.scanner.annotation.imports.ComponentImport; 9 | import com.kylenicholls.stash.parameterizedbuilds.helper.SettingsService; 10 | 11 | public class HookIsEnabledCondition extends BaseCondition { 12 | 13 | public HookIsEnabledCondition(@ComponentImport RepositoryService repositoryService, 14 | SettingsService settingsService) { 15 | super(repositoryService, settingsService); 16 | } 17 | 18 | @Override 19 | public void init(Map context) { 20 | // Nothing to do here 21 | } 22 | 23 | @Override 24 | public boolean shouldDisplay(Map context) { 25 | final Repository repository = getRepository(context); 26 | if (repository == null) { 27 | return false; 28 | } 29 | RepositoryHook hook = settingsService.getHook(repository); 30 | 31 | return hook.isEnabled(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/kylenicholls/stash/parameterizedbuilds/conditions/ManualButtonCondition.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.conditions; 2 | 3 | import java.util.Map; 4 | 5 | import com.atlassian.bitbucket.repository.Repository; 6 | import com.atlassian.bitbucket.repository.RepositoryService; 7 | import com.atlassian.bitbucket.setting.Settings; 8 | import com.atlassian.plugin.spring.scanner.annotation.imports.ComponentImport; 9 | import com.kylenicholls.stash.parameterizedbuilds.helper.SettingsService; 10 | import com.kylenicholls.stash.parameterizedbuilds.item.Job; 11 | import com.kylenicholls.stash.parameterizedbuilds.item.Job.Trigger; 12 | 13 | public class ManualButtonCondition extends BaseCondition { 14 | 15 | public ManualButtonCondition(@ComponentImport RepositoryService repositoryService, 16 | SettingsService settingsService) { 17 | super(repositoryService, settingsService); 18 | } 19 | 20 | @Override 21 | public void init(Map context) { 22 | // Nothing to do here 23 | } 24 | 25 | @Override 26 | public boolean shouldDisplay(Map context) { 27 | final Repository repository = getRepository(context); 28 | if (repository == null) { 29 | return false; 30 | } 31 | Settings settings = settingsService.getSettings(repository); 32 | 33 | for (Job job : settingsService.getJobs(settings.asMap())) { 34 | if (job.getTriggers().contains(Trigger.MANUAL)) { 35 | return true; 36 | } 37 | } 38 | return false; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/kylenicholls/stash/parameterizedbuilds/eventHandlers/BaseHandler.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.eventHandlers; 2 | 3 | import com.atlassian.bitbucket.repository.Repository; 4 | import com.atlassian.bitbucket.setting.Settings; 5 | import com.atlassian.bitbucket.user.ApplicationUser; 6 | import com.kylenicholls.stash.parameterizedbuilds.ciserver.Jenkins; 7 | import com.kylenicholls.stash.parameterizedbuilds.ciserver.JenkinsConnection; 8 | import com.kylenicholls.stash.parameterizedbuilds.helper.SettingsService; 9 | import com.kylenicholls.stash.parameterizedbuilds.item.BitbucketVariables; 10 | import com.kylenicholls.stash.parameterizedbuilds.item.Job; 11 | 12 | public abstract class BaseHandler { 13 | 14 | final SettingsService settingsService; 15 | final Jenkins jenkins; 16 | final JenkinsConnection jenkinsConn; 17 | 18 | //these variables are set in the subclassed handlers 19 | Repository repository; 20 | String projectKey; 21 | ApplicationUser user; 22 | 23 | public BaseHandler(SettingsService settingsService, Jenkins jenkins){ 24 | this.settingsService = settingsService; 25 | this.jenkins = jenkins; 26 | this.jenkinsConn = new JenkinsConnection(jenkins); 27 | } 28 | 29 | public void run(){ 30 | BitbucketVariables bitbucketVariables = createBitbucketVariables(); 31 | Settings settings = settingsService.getSettings(repository); 32 | if (settings == null) { 33 | return; 34 | } 35 | 36 | for (final Job job : settingsService.getJobs(settings.asMap())) { 37 | if (validateJob(job, bitbucketVariables)) { 38 | triggerJenkins(job, bitbucketVariables); 39 | } 40 | } 41 | } 42 | 43 | void triggerJenkins(Job job, BitbucketVariables bitbucketVariables){ 44 | jenkinsConn.triggerJob(projectKey, user, job, bitbucketVariables); 45 | } 46 | 47 | abstract BitbucketVariables createBitbucketVariables(); 48 | 49 | abstract boolean validateJob(Job job, BitbucketVariables bitbucketVariables); 50 | 51 | boolean validateTrigger(Job job, Job.Trigger trigger){ 52 | return job.getTriggers().contains(trigger); 53 | } 54 | 55 | boolean validateTag(Job job, boolean isTag){ 56 | return job.getIsTag() == isTag; 57 | } 58 | 59 | boolean validateBranch(Job job, String branch){ 60 | String branchRegex = job.getBranchRegex(); 61 | return branchRegex.isEmpty() || branch.toLowerCase().matches(branchRegex.toLowerCase()); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/com/kylenicholls/stash/parameterizedbuilds/eventHandlers/PRApprovedHandler.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.eventHandlers; 2 | 3 | import com.atlassian.bitbucket.event.pull.PullRequestParticipantApprovedEvent; 4 | import com.atlassian.bitbucket.pull.PullRequestService; 5 | import com.kylenicholls.stash.parameterizedbuilds.ciserver.Jenkins; 6 | import com.kylenicholls.stash.parameterizedbuilds.helper.SettingsService; 7 | import com.kylenicholls.stash.parameterizedbuilds.item.Job; 8 | 9 | public class PRApprovedHandler extends PRHandler { 10 | 11 | public PRApprovedHandler(SettingsService settingsService, PullRequestService pullRequestService, 12 | Jenkins jenkins, PullRequestParticipantApprovedEvent event, 13 | String url){ 14 | super(settingsService, pullRequestService, jenkins, event, url, Job.Trigger.PRAPPROVED); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/kylenicholls/stash/parameterizedbuilds/eventHandlers/PRAutoMergedHandler.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.eventHandlers; 2 | 3 | import com.atlassian.bitbucket.branch.automerge.AutomaticMergeEvent; 4 | import com.atlassian.bitbucket.repository.Branch; 5 | import com.kylenicholls.stash.parameterizedbuilds.ciserver.Jenkins; 6 | import com.kylenicholls.stash.parameterizedbuilds.helper.SettingsService; 7 | import com.kylenicholls.stash.parameterizedbuilds.item.BitbucketVariables; 8 | import com.kylenicholls.stash.parameterizedbuilds.item.Job.Trigger; 9 | 10 | public class PRAutoMergedHandler extends PRHandler { 11 | 12 | private Branch branch; 13 | 14 | public PRAutoMergedHandler(SettingsService settingsService, Jenkins jenkins, 15 | AutomaticMergeEvent event, String url, Branch branch){ 16 | super(settingsService, jenkins, event, url, Trigger.PRAUTOMERGED); 17 | this.branch = branch; 18 | } 19 | 20 | BitbucketVariables createBitbucketVariables(){ 21 | return new BitbucketVariables.Builder() 22 | .populateFromBranch(branch, repository, projectKey, trigger, url) 23 | .build(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/kylenicholls/stash/parameterizedbuilds/eventHandlers/PRDeclinedHandler.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.eventHandlers; 2 | 3 | import com.atlassian.bitbucket.event.pull.PullRequestDeclinedEvent; 4 | import com.atlassian.bitbucket.pull.PullRequestService; 5 | import com.kylenicholls.stash.parameterizedbuilds.ciserver.Jenkins; 6 | import com.kylenicholls.stash.parameterizedbuilds.helper.SettingsService; 7 | import com.kylenicholls.stash.parameterizedbuilds.item.Job.Trigger; 8 | 9 | public class PRDeclinedHandler extends PRHandler{ 10 | 11 | public PRDeclinedHandler(SettingsService settingsService, PullRequestService pullRequestService, 12 | Jenkins jenkins, PullRequestDeclinedEvent event, String url){ 13 | super(settingsService, pullRequestService, jenkins, event, url, Trigger.PRDECLINED); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/kylenicholls/stash/parameterizedbuilds/eventHandlers/PRDeletedHandler.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.eventHandlers; 2 | 3 | import com.atlassian.bitbucket.event.pull.PullRequestEvent; 4 | import com.atlassian.bitbucket.pull.PullRequestService; 5 | import com.kylenicholls.stash.parameterizedbuilds.ciserver.Jenkins; 6 | import com.kylenicholls.stash.parameterizedbuilds.helper.SettingsService; 7 | import com.kylenicholls.stash.parameterizedbuilds.item.Job.Trigger; 8 | 9 | public class PRDeletedHandler extends PRHandler{ 10 | 11 | public PRDeletedHandler(SettingsService settingsService, PullRequestService pullRequestService, 12 | Jenkins jenkins, PullRequestEvent event, String url){ 13 | super(settingsService, pullRequestService, jenkins, event, url, Trigger.PRDELETED); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/kylenicholls/stash/parameterizedbuilds/eventHandlers/PRDestRescopedHandler.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.eventHandlers; 2 | 3 | import com.atlassian.bitbucket.event.pull.PullRequestRescopedEvent; 4 | import com.atlassian.bitbucket.pull.PullRequestService; 5 | import com.kylenicholls.stash.parameterizedbuilds.ciserver.Jenkins; 6 | import com.kylenicholls.stash.parameterizedbuilds.helper.SettingsService; 7 | import com.kylenicholls.stash.parameterizedbuilds.item.Job; 8 | 9 | public class PRDestRescopedHandler extends PRHandler { 10 | public PRDestRescopedHandler(SettingsService settingsService, 11 | PullRequestService pullRequestService, Jenkins jenkins, 12 | PullRequestRescopedEvent event, String url){ 13 | super(settingsService, pullRequestService, jenkins, event, url, Job.Trigger.PRDESTRESCOPED); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/kylenicholls/stash/parameterizedbuilds/eventHandlers/PRHandler.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.eventHandlers; 2 | 3 | import com.atlassian.bitbucket.branch.automerge.AutomaticMergeEvent; 4 | import com.atlassian.bitbucket.content.AbstractChangeCallback; 5 | import com.atlassian.bitbucket.content.Change; 6 | import com.atlassian.bitbucket.content.ChangeContext; 7 | import com.atlassian.bitbucket.content.ChangeSummary; 8 | import com.atlassian.bitbucket.event.pull.PullRequestEvent; 9 | import com.atlassian.bitbucket.pull.PullRequest; 10 | import com.atlassian.bitbucket.pull.PullRequestChangesRequest; 11 | import com.atlassian.bitbucket.pull.PullRequestService; 12 | import com.kylenicholls.stash.parameterizedbuilds.ciserver.Jenkins; 13 | import com.kylenicholls.stash.parameterizedbuilds.helper.SettingsService; 14 | import com.kylenicholls.stash.parameterizedbuilds.item.BitbucketVariables; 15 | import com.kylenicholls.stash.parameterizedbuilds.item.Job; 16 | import com.kylenicholls.stash.parameterizedbuilds.item.Job.Trigger; 17 | 18 | import java.io.IOException; 19 | 20 | public class PRHandler extends BaseHandler{ 21 | 22 | private PullRequestService pullRequestService; 23 | PullRequest pullRequest; 24 | String url; 25 | final Trigger trigger; 26 | 27 | public PRHandler(SettingsService settingsService, PullRequestService pullRequestService, 28 | Jenkins jenkins, PullRequestEvent event, String url, Trigger trigger) { 29 | super(settingsService, jenkins); 30 | this.pullRequestService = pullRequestService; 31 | this.pullRequest = event.getPullRequest(); 32 | this.user = pullRequest.getAuthor().getUser(); 33 | this.repository = pullRequest.getToRef().getRepository(); 34 | this.projectKey = repository.getProject().getKey(); 35 | this.url = url; 36 | this.trigger = trigger; 37 | } 38 | 39 | public PRHandler(SettingsService settingsService, Jenkins jenkins, 40 | AutomaticMergeEvent event, String url, Trigger trigger) { 41 | super(settingsService, jenkins); 42 | this.repository = event.getRepository(); 43 | this.projectKey = repository.getProject().getKey(); 44 | this.url = url; 45 | this.trigger = trigger; 46 | } 47 | 48 | @Override 49 | public void run(){ 50 | if (!settingsService.getHook(repository).isEnabled()) { 51 | return; 52 | } 53 | super.run(); 54 | } 55 | 56 | @Override 57 | BitbucketVariables createBitbucketVariables(){ 58 | return new BitbucketVariables.Builder() 59 | .populateFromPR(pullRequest, repository, projectKey, trigger, url) 60 | .build(); 61 | } 62 | 63 | @Override 64 | boolean validateJob(Job job, BitbucketVariables bitbucketVariables){ 65 | String prDest = pullRequest != null ? pullRequest.getToRef().getDisplayId() : ""; 66 | return validatePrDest(job, prDest) && validateTrigger(job, trigger) && 67 | validatePath(job, bitbucketVariables); 68 | } 69 | 70 | boolean validatePrDest(Job job,String prDest){ 71 | String prDestRegex = job.getPrDestRegex(); 72 | return prDestRegex.isEmpty() || prDest.toLowerCase().matches(prDestRegex.toLowerCase()); 73 | } 74 | 75 | boolean validatePath(Job job, BitbucketVariables bitbucketVariables) { 76 | String pathRegex = job.getPathRegex(); 77 | if (pathRegex.isEmpty()) { 78 | return true; 79 | } else if (pullRequest != null) { 80 | pullRequestService.streamChanges(new PullRequestChangesRequest.Builder(pullRequest) 81 | .build(), new AbstractChangeCallback() { 82 | 83 | @Override 84 | public boolean onChange(Change change) throws IOException { 85 | return triggerJob(change); 86 | } 87 | 88 | private boolean triggerJob(Change change) { 89 | if (change.getPath().toString().matches(pathRegex)) { 90 | jenkinsConn.triggerJob(projectKey, user, job, bitbucketVariables); 91 | return false; 92 | } 93 | return true; 94 | } 95 | 96 | @Override 97 | public void onStart(ChangeContext context) throws IOException { 98 | // noop 99 | } 100 | 101 | @Override 102 | public void onEnd(ChangeSummary summary) throws IOException { 103 | // noop 104 | } 105 | }); 106 | return false; 107 | } 108 | return true; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/main/java/com/kylenicholls/stash/parameterizedbuilds/eventHandlers/PRMergedHandler.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.eventHandlers; 2 | 3 | import com.atlassian.bitbucket.commit.MinimalCommit; 4 | import com.atlassian.bitbucket.event.pull.PullRequestMergedEvent; 5 | import com.atlassian.bitbucket.pull.PullRequestService; 6 | import com.kylenicholls.stash.parameterizedbuilds.ciserver.Jenkins; 7 | import com.kylenicholls.stash.parameterizedbuilds.helper.SettingsService; 8 | import com.kylenicholls.stash.parameterizedbuilds.item.BitbucketVariables; 9 | import com.kylenicholls.stash.parameterizedbuilds.item.Job; 10 | 11 | import java.util.Optional; 12 | 13 | public class PRMergedHandler extends PRHandler{ 14 | 15 | private String mergeCommit; 16 | 17 | public PRMergedHandler(SettingsService settingsService, PullRequestService pullRequestService, 18 | Jenkins jenkins, PullRequestMergedEvent event, String url){ 19 | super(settingsService, pullRequestService, jenkins, event, url, Job.Trigger.PRMERGED); 20 | this.mergeCommit = Optional.ofNullable(event.getCommit()) 21 | .map(MinimalCommit::getId) 22 | .orElse(""); 23 | } 24 | 25 | @Override 26 | BitbucketVariables createBitbucketVariables(){ 27 | return new BitbucketVariables.Builder() 28 | .populateFromPR(pullRequest, repository, projectKey, trigger, url) 29 | .add("$MERGECOMMIT", () -> mergeCommit) 30 | .build(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/kylenicholls/stash/parameterizedbuilds/eventHandlers/PROpenedHandler.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.eventHandlers; 2 | 3 | import com.atlassian.bitbucket.event.pull.PullRequestOpenedEvent; 4 | import com.atlassian.bitbucket.pull.PullRequestService; 5 | import com.kylenicholls.stash.parameterizedbuilds.ciserver.Jenkins; 6 | import com.kylenicholls.stash.parameterizedbuilds.helper.SettingsService; 7 | import com.kylenicholls.stash.parameterizedbuilds.item.Job.Trigger; 8 | 9 | public class PROpenedHandler extends PRHandler{ 10 | 11 | public PROpenedHandler(SettingsService settingsService, PullRequestService pullRequestService, 12 | Jenkins jenkins, PullRequestOpenedEvent event, String url){ 13 | super(settingsService, pullRequestService, jenkins, event, url, Trigger.PROPENED); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/kylenicholls/stash/parameterizedbuilds/eventHandlers/PRReopenedHandler.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.eventHandlers; 2 | 3 | import com.atlassian.bitbucket.event.pull.PullRequestReopenedEvent; 4 | import com.atlassian.bitbucket.pull.PullRequestService; 5 | import com.kylenicholls.stash.parameterizedbuilds.ciserver.Jenkins; 6 | import com.kylenicholls.stash.parameterizedbuilds.helper.SettingsService; 7 | import com.kylenicholls.stash.parameterizedbuilds.item.Job; 8 | 9 | public class PRReopenedHandler extends PRHandler { 10 | public PRReopenedHandler(SettingsService settingsService, PullRequestService pullRequestService, 11 | Jenkins jenkins, PullRequestReopenedEvent event, String url){ 12 | super(settingsService, pullRequestService, jenkins, event, url, Job.Trigger.PRREOPENED); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/kylenicholls/stash/parameterizedbuilds/eventHandlers/PRSourceRescopedHandler.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.eventHandlers; 2 | 3 | import com.atlassian.bitbucket.event.pull.PullRequestRescopedEvent; 4 | import com.atlassian.bitbucket.pull.PullRequestService; 5 | import com.kylenicholls.stash.parameterizedbuilds.ciserver.Jenkins; 6 | import com.kylenicholls.stash.parameterizedbuilds.helper.SettingsService; 7 | import com.kylenicholls.stash.parameterizedbuilds.item.Job; 8 | 9 | public class PRSourceRescopedHandler extends PRHandler { 10 | public PRSourceRescopedHandler(SettingsService settingsService, 11 | PullRequestService pullRequestService, Jenkins jenkins, 12 | PullRequestRescopedEvent event, String url){ 13 | super(settingsService, pullRequestService, jenkins, event, url, 14 | Job.Trigger.PRSOURCERESCOPED); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/kylenicholls/stash/parameterizedbuilds/eventHandlers/PushHandler.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.eventHandlers; 2 | 3 | import com.atlassian.bitbucket.commit.CommitRequest; 4 | import com.atlassian.bitbucket.commit.CommitService; 5 | import com.atlassian.bitbucket.content.AbstractChangeCallback; 6 | import com.atlassian.bitbucket.content.Change; 7 | import com.atlassian.bitbucket.content.ChangeContext; 8 | import com.atlassian.bitbucket.content.ChangeSummary; 9 | import com.atlassian.bitbucket.content.ChangesRequest; 10 | import com.atlassian.bitbucket.repository.RefChange; 11 | import com.atlassian.bitbucket.repository.Repository; 12 | import com.atlassian.bitbucket.user.ApplicationUser; 13 | import com.kylenicholls.stash.parameterizedbuilds.ciserver.Jenkins; 14 | import com.kylenicholls.stash.parameterizedbuilds.helper.SettingsService; 15 | import com.kylenicholls.stash.parameterizedbuilds.item.BitbucketVariables; 16 | import com.kylenicholls.stash.parameterizedbuilds.item.Job; 17 | import com.kylenicholls.stash.parameterizedbuilds.item.Job.Trigger; 18 | import java.io.IOException; 19 | import java.util.Arrays; 20 | 21 | public class PushHandler extends RefHandler { 22 | 23 | public PushHandler(SettingsService settingsService, Jenkins jenkins, 24 | CommitService commitService, Repository repository, RefChange refChange, 25 | String url, ApplicationUser user) { 26 | super(settingsService, jenkins, commitService, repository, refChange, url, user, 27 | Trigger.PUSH); 28 | } 29 | 30 | @Override 31 | boolean validateJob(Job job, BitbucketVariables bitbucketVariables) { 32 | return super.validateJob(job, bitbucketVariables) && validatePath(job, bitbucketVariables) 33 | && validateCommitMsg(job) && validateComitter(job); 34 | } 35 | 36 | boolean validatePath(Job job, BitbucketVariables bitbucketVariables) { 37 | String pathRegex = job.getPathRegex(); 38 | if (pathRegex.isEmpty()) { 39 | return true; 40 | } else { 41 | ChangesRequest request = new ChangesRequest.Builder(repository, 42 | refChange.getToHash()).sinceId(refChange.getFromHash()).build(); 43 | commitService.streamChanges(request, new AbstractChangeCallback() { 44 | 45 | @Override 46 | public boolean onChange(Change change) throws IOException { 47 | return triggerJob(change); 48 | } 49 | 50 | private boolean triggerJob(Change change) { 51 | if (change.getPath().toString().matches(pathRegex)) { 52 | jenkinsConn.triggerJob(projectKey, user, job, bitbucketVariables); 53 | return false; 54 | } 55 | return true; 56 | } 57 | 58 | @Override 59 | public void onStart(ChangeContext context) throws IOException { 60 | // noop 61 | } 62 | 63 | @Override 64 | public void onEnd(ChangeSummary summary) throws IOException { 65 | // noop 66 | } 67 | }); 68 | return false; 69 | } 70 | } 71 | 72 | boolean validateCommitMsg(Job job) { 73 | String ignoreCommitMsg = job.getIgnoreCommitMsg(); 74 | if(ignoreCommitMsg.isEmpty()){ 75 | return true; 76 | } 77 | else{ 78 | CommitRequest commitRequest = new CommitRequest.Builder( 79 | repository, refChange.getToHash()).build(); 80 | String actualCommitMsg = commitService.getCommit(commitRequest).getMessage(); 81 | boolean hasIgnoreMsg = actualCommitMsg != null && 82 | actualCommitMsg.matches(ignoreCommitMsg); 83 | return !hasIgnoreMsg; 84 | } 85 | } 86 | 87 | boolean validateComitter(Job job){ 88 | String ignoreComitters = job.getIgnoreComitters(); 89 | if(ignoreComitters.isEmpty()){ 90 | return true; 91 | } 92 | else{ 93 | String[] ignoreComitterList = job.getIgnoreComitters().toLowerCase().split("\\r?\\n"); 94 | CommitRequest commitRequest = new CommitRequest.Builder( 95 | repository, refChange.getToHash()).build(); 96 | String author = commitService.getCommit(commitRequest).getAuthor().getName(); 97 | boolean authorIgnored = Arrays.stream(ignoreComitterList).anyMatch(t -> 98 | t.equals(author.toLowerCase())); 99 | return !authorIgnored; 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/main/java/com/kylenicholls/stash/parameterizedbuilds/eventHandlers/RefCreatedHandler.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.eventHandlers; 2 | 3 | import com.atlassian.bitbucket.commit.CommitService; 4 | import com.atlassian.bitbucket.repository.RefChange; 5 | import com.atlassian.bitbucket.repository.Repository; 6 | import com.atlassian.bitbucket.user.ApplicationUser; 7 | import com.kylenicholls.stash.parameterizedbuilds.ciserver.Jenkins; 8 | import com.kylenicholls.stash.parameterizedbuilds.helper.SettingsService; 9 | import com.kylenicholls.stash.parameterizedbuilds.item.Job.Trigger; 10 | 11 | public class RefCreatedHandler extends RefHandler{ 12 | 13 | public RefCreatedHandler(SettingsService settingsService, Jenkins jenkins, 14 | CommitService commitService, Repository repository, 15 | RefChange refChange, String url, ApplicationUser user) { 16 | super(settingsService, jenkins, commitService, repository, refChange, url, user, 17 | Trigger.ADD); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/kylenicholls/stash/parameterizedbuilds/eventHandlers/RefDeletedHandler.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.eventHandlers; 2 | 3 | import com.atlassian.bitbucket.commit.CommitService; 4 | import com.atlassian.bitbucket.repository.RefChange; 5 | import com.atlassian.bitbucket.repository.Repository; 6 | import com.atlassian.bitbucket.user.ApplicationUser; 7 | import com.kylenicholls.stash.parameterizedbuilds.ciserver.Jenkins; 8 | import com.kylenicholls.stash.parameterizedbuilds.helper.SettingsService; 9 | import com.kylenicholls.stash.parameterizedbuilds.item.Job.Trigger; 10 | 11 | public class RefDeletedHandler extends RefHandler{ 12 | 13 | public RefDeletedHandler(SettingsService settingsService, Jenkins jenkins, 14 | CommitService commitService, Repository repository, 15 | RefChange refChange, String url, ApplicationUser user) { 16 | super(settingsService, jenkins, commitService, repository, refChange, url, user, 17 | Trigger.DELETE); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/kylenicholls/stash/parameterizedbuilds/eventHandlers/RefHandler.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.eventHandlers; 2 | 3 | import com.atlassian.bitbucket.commit.CommitService; 4 | import com.atlassian.bitbucket.repository.RefChange; 5 | import com.atlassian.bitbucket.repository.Repository; 6 | import com.atlassian.bitbucket.user.ApplicationUser; 7 | import com.kylenicholls.stash.parameterizedbuilds.ciserver.Jenkins; 8 | import com.kylenicholls.stash.parameterizedbuilds.helper.SettingsService; 9 | import com.kylenicholls.stash.parameterizedbuilds.item.BitbucketVariables; 10 | import com.kylenicholls.stash.parameterizedbuilds.item.Job; 11 | import com.kylenicholls.stash.parameterizedbuilds.item.Job.Trigger; 12 | 13 | public class RefHandler extends BaseHandler{ 14 | 15 | private static final String REFS_HEADS = "refs/heads/"; 16 | private static final String REFS_TAGS = "refs/tags/"; 17 | 18 | final CommitService commitService; 19 | 20 | RefChange refChange; 21 | String branch; 22 | boolean isTag; 23 | Trigger trigger; 24 | String url; 25 | 26 | public RefHandler(SettingsService settingsService, Jenkins jenkins, CommitService commitService, 27 | Repository repository, RefChange refChange, String url, ApplicationUser user, 28 | Trigger trigger) { 29 | super(settingsService, jenkins); 30 | this.commitService = commitService; 31 | this.refChange = refChange; 32 | 33 | branch = refChange.getRef().getId().replace(REFS_HEADS, ""); 34 | isTag = false; 35 | if (refChange.getRef().getId().startsWith(REFS_TAGS)) { 36 | branch = refChange.getRef().getId().replace(REFS_TAGS, ""); 37 | isTag = true; 38 | } 39 | 40 | this.repository = repository; 41 | this.projectKey = repository.getProject().getKey(); 42 | this.url = url; 43 | this.trigger = trigger; 44 | this.user = user; 45 | } 46 | 47 | @Override 48 | BitbucketVariables createBitbucketVariables(){ 49 | return new BitbucketVariables.Builder() 50 | .populateFromRef(branch, refChange, repository, projectKey, trigger, url) 51 | .build(); 52 | } 53 | 54 | @Override 55 | boolean validateJob(Job job, BitbucketVariables bitbucketVariables){ 56 | return validateTag(job, isTag) && validateBranch(job, branch) && 57 | validateTrigger(job, trigger); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/kylenicholls/stash/parameterizedbuilds/helper/ScopeProjectVisitor.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.helper; 2 | 3 | import com.atlassian.bitbucket.project.Project; 4 | import com.atlassian.bitbucket.scope.GlobalScope; 5 | import com.atlassian.bitbucket.scope.ProjectScope; 6 | import com.atlassian.bitbucket.scope.RepositoryScope; 7 | import com.atlassian.bitbucket.scope.ScopeVisitor; 8 | 9 | public class ScopeProjectVisitor implements ScopeVisitor { 10 | 11 | public Project visit(RepositoryScope scope){ 12 | return scope.getProject(); 13 | } 14 | 15 | public Project visit(ProjectScope scope){ 16 | return scope.getProject(); 17 | } 18 | 19 | public Project visit(GlobalScope scope){ 20 | return null; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/kylenicholls/stash/parameterizedbuilds/item/BitbucketVariable.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.item; 2 | 3 | import java.util.function.Supplier; 4 | 5 | public final class BitbucketVariable { 6 | 7 | private T value; 8 | private final Supplier supplier; 9 | 10 | public BitbucketVariable(Supplier supplier){ 11 | this.supplier = supplier; 12 | } 13 | 14 | public T getOrCompute() { 15 | final T result = value; 16 | return result == null ? compute() : result; 17 | } 18 | 19 | private T compute() { 20 | try { 21 | value = supplier.get(); 22 | } catch (NullPointerException e) { 23 | // this is probably due to a nested get returning null so refuse to resolve the variable 24 | return null; 25 | } 26 | return value; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/kylenicholls/stash/parameterizedbuilds/item/BitbucketVariables.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.item; 2 | 3 | import java.util.Arrays; 4 | import java.util.HashMap; 5 | import java.util.HashSet; 6 | import java.util.Map; 7 | import java.util.Set; 8 | 9 | import com.atlassian.bitbucket.pull.PullRequest; 10 | import com.atlassian.bitbucket.repository.Branch; 11 | import com.atlassian.bitbucket.repository.RefChange; 12 | import com.atlassian.bitbucket.repository.Repository; 13 | import com.google.common.base.Preconditions; 14 | import java.util.function.Supplier; 15 | 16 | import com.kylenicholls.stash.parameterizedbuilds.item.Job.Trigger; 17 | 18 | public class BitbucketVariables { 19 | private Map> variables; 20 | private final String [] SET_VALUES = { 21 | "$BRANCH", "$COMMIT", "$URL", "$REPOSITORY", "$PROJECT", "$PRID", 22 | "$PRAUTHOR", "$PREMAIL", "$PRTITLE", "$PRDESCRIPTION", "$PRDESTINATION", 23 | "$PRURL", "$TRIGGER", "$MERGECOMMIT","$PRSOURCEPROJECT", "$PRSOURCEREPOSITORY", 24 | "$PRUSERNAME",}; 25 | private final Set allowedVariables = new HashSet<>(Arrays.asList(SET_VALUES)); 26 | 27 | private BitbucketVariables(Builder builder){ 28 | assert allowedVariables.containsAll(builder.variables.keySet()); 29 | this.variables = builder.variables; 30 | } 31 | 32 | public Map> getVariables() { 33 | return variables; 34 | } 35 | 36 | public String fetch(String key) { 37 | return variables.get(key).getOrCompute(); 38 | } 39 | 40 | public static class Builder { 41 | private Map> variables; 42 | 43 | public Builder() { 44 | this.variables = new HashMap<>(); 45 | } 46 | 47 | public Builder add(String key, Supplier supplier) { 48 | Preconditions.checkNotNull(key); 49 | if (variables.containsKey(key)) { 50 | return this; 51 | } 52 | BitbucketVariable variable = new BitbucketVariable<>(supplier); 53 | variables.put(key, variable); 54 | return this; 55 | } 56 | 57 | public Builder populateFromPR(PullRequest pullRequest, Repository repository, 58 | String projectKey, Trigger trigger, String url){ 59 | String prId = Long.toString(pullRequest.getId()); 60 | return add("$BRANCH", () -> pullRequest.getFromRef().getDisplayId()) 61 | .add("$COMMIT", () -> pullRequest.getFromRef().getLatestCommit()) 62 | .add("$URL", () -> url) 63 | .add("$REPOSITORY", repository::getSlug) 64 | .add("$PROJECT", () -> projectKey) 65 | .add("$PRID", () -> prId) 66 | .add("$PRAUTHOR", () -> pullRequest.getAuthor().getUser().getDisplayName()) 67 | .add("$PREMAIL", () -> pullRequest.getAuthor().getUser().getEmailAddress()) 68 | .add("$PRUSERNAME", () -> pullRequest.getAuthor().getUser().getName()) 69 | .add("$PRTITLE", pullRequest::getTitle) 70 | .add("$PRDESCRIPTION", pullRequest::getDescription) 71 | .add("$PRDESTINATION", () -> pullRequest.getToRef().getDisplayId()) 72 | .add("$PRURL", () -> url + "/projects/" + projectKey + "/repos/" + 73 | repository.getSlug() + "/pull-requests/" + prId) 74 | .add("$PRSOURCEPROJECT", () -> pullRequest.getFromRef().getRepository() 75 | .getProject().getKey()) 76 | .add("$PRSOURCEREPOSITORY", () -> pullRequest.getFromRef().getRepository() 77 | .getSlug()) 78 | .add("$TRIGGER", trigger::toString); 79 | } 80 | 81 | public Builder populateFromRef(String branch, RefChange refChange, Repository repository, 82 | String projectKey, Trigger trigger, String url){ 83 | return add("$BRANCH", () -> branch) 84 | .add("$COMMIT", refChange::getToHash) 85 | .add("$URL", () -> url) 86 | .add("$REPOSITORY", repository::getSlug) 87 | .add("$PROJECT", () -> projectKey) 88 | .add("$TRIGGER", trigger::toString); 89 | } 90 | 91 | public Builder populateFromBranch(Branch branch, Repository repository, String projectKey, 92 | Trigger trigger, String url){ 93 | return add("$BRANCH", branch::getDisplayId) 94 | .add("$COMMIT", branch::getLatestCommit) 95 | .add("$URL", () -> url) 96 | .add("$REPOSITORY", repository::getSlug) 97 | .add("$PROJECT", () -> projectKey) 98 | .add("$TRIGGER", trigger::toString); 99 | } 100 | 101 | public Builder populateFromStrings(String branch, String commit, Repository repository, 102 | String projectKey, Trigger trigger, String url){ 103 | return add("$BRANCH", () -> branch) 104 | .add("$COMMIT", () -> commit) 105 | .add("$URL", () -> url) 106 | .add("$REPOSITORY", repository::getSlug) 107 | .add("$PROJECT", () -> projectKey) 108 | .add("$TRIGGER", trigger::toString); 109 | } 110 | 111 | public BitbucketVariables build() { 112 | return new BitbucketVariables(this); 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/main/java/com/kylenicholls/stash/parameterizedbuilds/item/JenkinsResponse.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.item; 2 | 3 | import java.util.LinkedHashMap; 4 | import java.util.Map; 5 | 6 | public class JenkinsResponse { 7 | private boolean error; 8 | private boolean prompt; 9 | private String messageText; 10 | 11 | private JenkinsResponse(JenkinsMessage message) { 12 | this.error = message.error; 13 | this.prompt = message.prompt; 14 | this.messageText = message.messageText; 15 | } 16 | 17 | public boolean getError() { 18 | return error; 19 | } 20 | 21 | public boolean getPrompt() { 22 | return prompt; 23 | } 24 | 25 | public String getMessageText() { 26 | return messageText; 27 | } 28 | 29 | public Map getMessage() { 30 | Map data = new LinkedHashMap<>(); 31 | data.put("error", this.error); 32 | data.put("prompt", this.prompt); 33 | data.put("messageText", this.messageText); 34 | return data; 35 | } 36 | 37 | public static class JenkinsMessage { 38 | private boolean error = false; 39 | private boolean prompt = false; 40 | private String messageText = ""; 41 | 42 | public JenkinsMessage error(boolean error) { 43 | this.error = error; 44 | return this; 45 | } 46 | 47 | public JenkinsMessage prompt(boolean prompt) { 48 | this.prompt = prompt; 49 | return this; 50 | } 51 | 52 | public JenkinsMessage messageText(String messageText) { 53 | this.messageText = messageText; 54 | return this; 55 | } 56 | 57 | public JenkinsResponse build() { 58 | return new JenkinsResponse(this); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/kylenicholls/stash/parameterizedbuilds/item/Server.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.item; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import java.util.Optional; 6 | 7 | public class Server { 8 | private String baseUrl; 9 | private String alias; 10 | private String user; 11 | private String token; 12 | private boolean altUrl; 13 | private boolean csrfEnabled; 14 | 15 | public Server(){ 16 | } 17 | 18 | public Server(String baseUrl, String alias, String user, String token, boolean altUrl, 19 | boolean csrfEnabled) { 20 | this.baseUrl = baseUrl; 21 | this.alias = alias; 22 | this.user = user; 23 | this.token = token; 24 | this.altUrl = altUrl; 25 | this.csrfEnabled = csrfEnabled; 26 | } 27 | 28 | public Server(Map map) { 29 | this.baseUrl = (String) map.get("baseUrl"); 30 | this.alias = (String) map.getOrDefault("alias", ""); 31 | this.user = (String) map.get("user"); 32 | this.token = (String) map.get("token"); 33 | this.altUrl = Boolean.parseBoolean(map.get("altUrl").toString()); 34 | this.csrfEnabled = Boolean.parseBoolean(map.getOrDefault("csrfEnabled", "true").toString()); 35 | } 36 | 37 | public String getBaseUrl() { 38 | if (baseUrl.endsWith("/")) { 39 | return baseUrl.substring(0, baseUrl.length() - 1); 40 | } 41 | return baseUrl; 42 | } 43 | 44 | public void setBaseUrl(String baseUrl) { 45 | this.baseUrl = baseUrl; 46 | } 47 | 48 | public String getAlias() { 49 | return alias; 50 | } 51 | 52 | public void setAlias(String alias) { 53 | this.alias = alias; 54 | } 55 | 56 | public String getUser() { 57 | return user; 58 | } 59 | 60 | public void setUser(String user) { 61 | this.user = user; 62 | } 63 | 64 | public String getToken() { 65 | return token; 66 | } 67 | 68 | public void setToken(String token) { 69 | this.token = token; 70 | } 71 | 72 | public boolean getCsrfEnabled() { 73 | return csrfEnabled; 74 | } 75 | 76 | public void setCsrfEnabled(Boolean csrfEnabled) { 77 | this.csrfEnabled = csrfEnabled; 78 | } 79 | 80 | public boolean getAltUrl() { 81 | return altUrl; 82 | } 83 | 84 | public void setAltUrl(Boolean altUrl) { 85 | this.altUrl = altUrl; 86 | } 87 | 88 | public Map asMap() { 89 | Map map = new HashMap<>(); 90 | map.put("baseUrl", baseUrl); 91 | map.put("alias", alias); 92 | map.put("user", user); 93 | map.put("token", token); 94 | map.put("altUrl", altUrl); 95 | map.put("csrfEnabled", csrfEnabled); 96 | return map; 97 | } 98 | 99 | public String getJoinedToken() { 100 | return Optional.ofNullable(user) 101 | .filter(u -> !u.isEmpty()) 102 | .map(u -> u + ":" + token) 103 | .orElse(null); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/main/java/com/kylenicholls/stash/parameterizedbuilds/item/UserToken.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.item; 2 | 3 | import com.google.gson.JsonObject; 4 | 5 | public class UserToken { 6 | private String baseUrl; 7 | private String alias; 8 | private String projectKey; 9 | private String projectName; 10 | private String userSlug; 11 | private String token; 12 | 13 | public UserToken(String baseUrl, String alias, String projectKey, String projectName, 14 | String userSlug, String token) { 15 | this.baseUrl = baseUrl; 16 | this.alias = alias; 17 | this.projectKey = projectKey; 18 | this.projectName = projectName; 19 | this.userSlug = userSlug; 20 | this.token = token; 21 | } 22 | 23 | public String getBaseUrl() { 24 | return baseUrl; 25 | } 26 | 27 | public String getAlias() { 28 | return alias; 29 | } 30 | 31 | public String getProjectKey() { 32 | return projectKey; 33 | } 34 | 35 | public String getProjectName() { 36 | return projectName; 37 | } 38 | 39 | public String getUserSlug() { 40 | return userSlug; 41 | } 42 | 43 | public String getToken() { 44 | return token; 45 | } 46 | 47 | public JsonObject toJson(){ 48 | JsonObject jsonObject = new JsonObject(); 49 | jsonObject.addProperty("url", this.baseUrl); 50 | jsonObject.addProperty("alias", this.alias); 51 | jsonObject.addProperty("project_key", this.projectKey); 52 | jsonObject.addProperty("project_name", this.projectName); 53 | jsonObject.addProperty("default_user", this.userSlug); 54 | jsonObject.addProperty("default_token", this.token); 55 | return jsonObject; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/com/kylenicholls/stash/parameterizedbuilds/rest/GlobalResource.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.rest; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | import java.util.stream.Collectors; 6 | 7 | import javax.ws.rs.Consumes; 8 | import javax.ws.rs.DELETE; 9 | import javax.ws.rs.GET; 10 | import javax.ws.rs.POST; 11 | import javax.ws.rs.PUT; 12 | import javax.ws.rs.Path; 13 | import javax.ws.rs.PathParam; 14 | import javax.ws.rs.Produces; 15 | import javax.ws.rs.core.Context; 16 | import javax.ws.rs.core.MediaType; 17 | import javax.ws.rs.core.Response; 18 | import javax.ws.rs.core.UriInfo; 19 | 20 | import com.atlassian.bitbucket.auth.AuthenticationContext; 21 | import com.atlassian.bitbucket.i18n.I18nService; 22 | import com.atlassian.bitbucket.rest.RestResource; 23 | import com.atlassian.bitbucket.rest.util.RestUtils; 24 | import com.google.gson.JsonArray; 25 | import com.google.gson.JsonObject; 26 | import com.google.gson.JsonPrimitive; 27 | import com.kylenicholls.stash.parameterizedbuilds.ciserver.Jenkins; 28 | import com.kylenicholls.stash.parameterizedbuilds.ciserver.JenkinsConnection; 29 | import com.kylenicholls.stash.parameterizedbuilds.item.Server; 30 | import com.sun.jersey.spi.resource.Singleton; 31 | 32 | 33 | @Path("/global") 34 | @Singleton 35 | public class GlobalResource extends RestResource implements ServerService{ 36 | 37 | private Jenkins jenkins; 38 | private final AuthenticationContext authContext; 39 | 40 | public GlobalResource(I18nService i18nService, Jenkins jenkins, 41 | AuthenticationContext authContext) { 42 | super(i18nService); 43 | this.jenkins = jenkins; 44 | this.authContext = authContext; 45 | } 46 | 47 | @Override 48 | @GET 49 | @Path("/servers") 50 | @Consumes({ MediaType.APPLICATION_JSON }) 51 | @Produces({ RestUtils.APPLICATION_JSON_UTF8 }) 52 | public Response getServers(@Context UriInfo ui){ 53 | if (authContext.isAuthenticated()) { 54 | List> servers = jenkins.getJenkinsServers(null).stream() 55 | .map(x -> createServerMap(x, null)) 56 | .collect(Collectors.toList()); 57 | 58 | return Response.ok(servers).build(); 59 | } else { 60 | return Response.status(Response.Status.FORBIDDEN).build(); 61 | } 62 | } 63 | 64 | @POST 65 | @Path("/servers/validate") 66 | @Consumes({ MediaType.APPLICATION_JSON }) 67 | @Produces({ RestUtils.APPLICATION_JSON_UTF8 }) 68 | public Response validate(@Context UriInfo ui, Server server){ 69 | if (authContext.isAuthenticated()) { 70 | Server oldServer = jenkins.getJenkinsServer(null, server.getAlias()); 71 | server.setToken(getCurrentDefaultToken(oldServer, server)); 72 | 73 | JenkinsConnection jenkinsConn = new JenkinsConnection(jenkins); 74 | String message = jenkinsConn.testConnection(server); 75 | 76 | if(message.equals("Connection successful")){ 77 | return Response.ok(message).build(); 78 | } 79 | 80 | return Response.status(400).entity(message).build(); 81 | } else { 82 | return Response.status(Response.Status.FORBIDDEN).build(); 83 | } 84 | } 85 | 86 | @PUT 87 | @Path("/servers/{serverAlias}") 88 | @Consumes({ MediaType.APPLICATION_JSON }) 89 | @Produces({ RestUtils.APPLICATION_JSON_UTF8 }) 90 | public Response addServer(@Context UriInfo ui, Server server, 91 | @PathParam("id") String serverAlias){ 92 | if (authContext.isAuthenticated()){ 93 | List errors = sanitizeServerInput(server); 94 | if (!errors.isEmpty()) { 95 | JsonArray errorMessages = new JsonArray(); 96 | errors.forEach(error -> errorMessages.add(new JsonPrimitive(error))); 97 | JsonObject response = new JsonObject(); 98 | response.add("errors", errorMessages); 99 | 100 | return Response.status(422).entity(response.toString()).build(); 101 | } 102 | 103 | Server oldServer = jenkins.getJenkinsServer(null, serverAlias); 104 | server.setToken(getCurrentDefaultToken(oldServer, server)); 105 | 106 | int returnStatus = oldServer == null ? 201 : 200; 107 | jenkins.saveJenkinsServer(server, null); 108 | return Response.status(returnStatus).build(); 109 | } else { 110 | return Response.status(Response.Status.FORBIDDEN).build(); 111 | } 112 | } 113 | 114 | @DELETE 115 | @Path("/servers/{serverAlias}") 116 | public Response removeServer(@Context UriInfo ui){ 117 | if (authContext.isAuthenticated()) { 118 | jenkins.saveJenkinsServer(null, null); 119 | return Response.status(Response.Status.NO_CONTENT).build(); 120 | } else { 121 | return Response.status(Response.Status.FORBIDDEN).build(); 122 | } 123 | } 124 | 125 | @PUT 126 | @Path("/servers/{serverAlias}/userToken") 127 | @Consumes({ MediaType.APPLICATION_JSON }) 128 | @Produces({ RestUtils.APPLICATION_JSON_UTF8 }) 129 | public Response addUserToken(@Context UriInfo ui, ServerService.Token token){ 130 | if (authContext.isAuthenticated()) { 131 | String user = authContext.getCurrentUser().getSlug(); 132 | jenkins.saveUserToken(user, "", token.getToken()); 133 | return Response.status(Response.Status.NO_CONTENT).build(); 134 | } else { 135 | return Response.status(Response.Status.FORBIDDEN).build(); 136 | } 137 | } 138 | 139 | @DELETE 140 | @Path("/servers/{serverAlias}/userToken") 141 | public Response removeUserToken(@Context UriInfo ui){ 142 | if (authContext.isAuthenticated()) { 143 | String user = authContext.getCurrentUser().getSlug(); 144 | jenkins.saveUserToken(user, "", ""); 145 | return Response.status(Response.Status.NO_CONTENT).build(); 146 | } else { 147 | return Response.status(Response.Status.FORBIDDEN).build(); 148 | } 149 | } 150 | } -------------------------------------------------------------------------------- /src/main/java/com/kylenicholls/stash/parameterizedbuilds/rest/ProjectResource.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.rest; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | import java.util.stream.Collectors; 6 | 7 | import javax.ws.rs.Consumes; 8 | import javax.ws.rs.DELETE; 9 | import javax.ws.rs.GET; 10 | import javax.ws.rs.POST; 11 | import javax.ws.rs.PUT; 12 | import javax.ws.rs.Path; 13 | import javax.ws.rs.PathParam; 14 | import javax.ws.rs.Produces; 15 | import javax.ws.rs.core.Context; 16 | import javax.ws.rs.core.MediaType; 17 | import javax.ws.rs.core.Response; 18 | import javax.ws.rs.core.UriInfo; 19 | 20 | import com.atlassian.bitbucket.auth.AuthenticationContext; 21 | import com.atlassian.bitbucket.i18n.I18nService; 22 | import com.atlassian.bitbucket.rest.RestResource; 23 | import com.atlassian.bitbucket.rest.util.RestUtils; 24 | import com.google.gson.JsonArray; 25 | import com.google.gson.JsonObject; 26 | import com.google.gson.JsonPrimitive; 27 | import com.kylenicholls.stash.parameterizedbuilds.ciserver.Jenkins; 28 | import com.kylenicholls.stash.parameterizedbuilds.ciserver.JenkinsConnection; 29 | import com.kylenicholls.stash.parameterizedbuilds.item.Server; 30 | import com.sun.jersey.spi.resource.Singleton; 31 | 32 | @Path("/projects/{projectKey}") 33 | @Singleton 34 | public class ProjectResource extends RestResource implements ServerService { 35 | private Jenkins jenkins; 36 | private final AuthenticationContext authContext; 37 | 38 | public ProjectResource(I18nService i18nService, Jenkins jenkins, 39 | AuthenticationContext authContext) { 40 | super(i18nService); 41 | this.jenkins = jenkins; 42 | this.authContext = authContext; 43 | } 44 | 45 | @GET 46 | @Path("/servers") 47 | @Consumes({ MediaType.APPLICATION_JSON }) 48 | @Produces({ RestUtils.APPLICATION_JSON_UTF8 }) 49 | public Response getServers(@Context UriInfo ui){ 50 | if (authContext.isAuthenticated()) { 51 | String projectKey = ui.getPathParameters().getFirst("projectKey"); 52 | 53 | List> servers = jenkins.getJenkinsServers(projectKey).stream() 54 | .map(x -> createServerMap(x, projectKey)) 55 | .collect(Collectors.toList()); 56 | 57 | return Response.ok(servers).build(); 58 | } else { 59 | return Response.status(Response.Status.FORBIDDEN).build(); 60 | } 61 | } 62 | 63 | @POST 64 | @Path("/servers/validate") 65 | @Consumes({ MediaType.APPLICATION_JSON }) 66 | @Produces({ RestUtils.APPLICATION_JSON_UTF8 }) 67 | public Response validate(@Context UriInfo ui, Server server){ 68 | if (authContext.isAuthenticated()) { 69 | String projectKey = ui.getPathParameters().getFirst("projectKey"); 70 | Server oldServer = jenkins.getJenkinsServer(projectKey, server.getAlias()); 71 | server.setToken(getCurrentDefaultToken(oldServer, server)); 72 | 73 | JenkinsConnection jenkinsConn = new JenkinsConnection(jenkins); 74 | String message = jenkinsConn.testConnection(server); 75 | 76 | if(message.equals("Connection successful")){ 77 | return Response.ok(message).build(); 78 | } 79 | 80 | return Response.status(400).entity(message).build(); 81 | } else { 82 | return Response.status(Response.Status.FORBIDDEN).build(); 83 | } 84 | } 85 | 86 | @PUT 87 | @Path("/servers/{serverAlias}") 88 | @Consumes({ MediaType.APPLICATION_JSON }) 89 | @Produces({ RestUtils.APPLICATION_JSON_UTF8 }) 90 | public Response addServer(@Context UriInfo ui, Server server, 91 | @PathParam("id") String serverAlias){ 92 | if (authContext.isAuthenticated()){ 93 | List errors = sanitizeServerInput(server); 94 | if (!errors.isEmpty()) { 95 | JsonArray errorMessages = new JsonArray(); 96 | errors.forEach(error -> errorMessages.add(new JsonPrimitive(error))); 97 | JsonObject response = new JsonObject(); 98 | response.add("errors", errorMessages); 99 | 100 | return Response.status(422).entity(response.toString()).build(); 101 | } 102 | 103 | String projectKey = ui.getPathParameters().getFirst("projectKey"); 104 | Server oldServer = jenkins.getJenkinsServer(projectKey, serverAlias); 105 | server.setToken(getCurrentDefaultToken(oldServer, server)); 106 | int returnStatus = oldServer == null ? 201 : 200; 107 | jenkins.saveJenkinsServer(server, projectKey); 108 | return Response.status(returnStatus).build(); 109 | } else { 110 | return Response.status(Response.Status.FORBIDDEN).build(); 111 | } 112 | } 113 | 114 | @DELETE 115 | @Path("/servers/{serverAlias}") 116 | public Response removeServer(@Context UriInfo ui){ 117 | if (authContext.isAuthenticated()) { 118 | String projectKey = ui.getPathParameters().getFirst("projectKey"); 119 | jenkins.saveJenkinsServer(null, projectKey); 120 | return Response.status(Response.Status.NO_CONTENT).build(); 121 | } else { 122 | return Response.status(Response.Status.FORBIDDEN).build(); 123 | } 124 | } 125 | 126 | @PUT 127 | @Path("/servers/{serverAlias}/userToken") 128 | @Consumes({ MediaType.APPLICATION_JSON }) 129 | @Produces({ RestUtils.APPLICATION_JSON_UTF8 }) 130 | public Response addUserToken(@Context UriInfo ui, ServerService.Token token){ 131 | if (authContext.isAuthenticated()) { 132 | String projectKey = ui.getPathParameters().getFirst("projectKey"); 133 | String user = authContext.getCurrentUser().getSlug(); 134 | jenkins.saveUserToken(user, projectKey, token.getToken()); 135 | return Response.status(Response.Status.NO_CONTENT).build(); 136 | } else { 137 | return Response.status(Response.Status.FORBIDDEN).build(); 138 | } 139 | } 140 | 141 | @DELETE 142 | @Path("/servers/{serverAlias}/userToken") 143 | public Response removeUserToken(@Context UriInfo ui){ 144 | if (authContext.isAuthenticated()) { 145 | String projectKey = ui.getPathParameters().getFirst("projectKey"); 146 | String user = authContext.getCurrentUser().getSlug(); 147 | jenkins.saveUserToken(user, projectKey, ""); 148 | return Response.status(Response.Status.NO_CONTENT).build(); 149 | } else { 150 | return Response.status(Response.Status.FORBIDDEN).build(); 151 | } 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /src/main/java/com/kylenicholls/stash/parameterizedbuilds/rest/ServerService.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.rest; 2 | 3 | import java.net.URISyntaxException; 4 | import java.util.ArrayList; 5 | import java.util.HashMap; 6 | import java.util.List; 7 | import java.util.Map; 8 | 9 | import javax.ws.rs.Consumes; 10 | import javax.ws.rs.DELETE; 11 | import javax.ws.rs.GET; 12 | import javax.ws.rs.POST; 13 | import javax.ws.rs.PUT; 14 | import javax.ws.rs.Path; 15 | import javax.ws.rs.PathParam; 16 | import javax.ws.rs.Produces; 17 | import javax.ws.rs.core.Context; 18 | import javax.ws.rs.core.MediaType; 19 | import javax.ws.rs.core.Response; 20 | import javax.ws.rs.core.UriInfo; 21 | 22 | import com.atlassian.bitbucket.rest.util.RestUtils; 23 | import com.kylenicholls.stash.parameterizedbuilds.item.Server; 24 | 25 | import org.apache.http.client.utils.URIBuilder; 26 | 27 | public interface ServerService { 28 | 29 | public class Token { 30 | private String token; 31 | 32 | public Token(){} 33 | 34 | public void setToken(String token){ 35 | this.token = token; 36 | } 37 | 38 | public String getToken(){ 39 | return token; 40 | } 41 | } 42 | 43 | @GET 44 | @Path("/servers") 45 | @Consumes({ MediaType.APPLICATION_JSON }) 46 | @Produces({ RestUtils.APPLICATION_JSON_UTF8 }) 47 | public Response getServers(@Context UriInfo ui); 48 | 49 | @POST 50 | @Path("/servers/validate") 51 | @Consumes({ MediaType.APPLICATION_JSON }) 52 | @Produces({ RestUtils.APPLICATION_JSON_UTF8 }) 53 | public Response validate(@Context UriInfo ui, Server server); 54 | 55 | @PUT 56 | @Path("/servers/{serverAlias}") 57 | @Consumes({ MediaType.APPLICATION_JSON }) 58 | @Produces({ RestUtils.APPLICATION_JSON_UTF8 }) 59 | public Response addServer(@Context UriInfo ui, Server server, 60 | @PathParam("id") String serverAlias); 61 | 62 | @DELETE 63 | @Path("/servers/{serverAlias}") 64 | public Response removeServer(@Context UriInfo ui); 65 | 66 | @PUT 67 | @Path("/servers/{serverAlias}/userToken") 68 | @Consumes({ MediaType.APPLICATION_JSON }) 69 | @Produces({ RestUtils.APPLICATION_JSON_UTF8 }) 70 | public Response addUserToken(@Context UriInfo ui, Token token); 71 | 72 | @DELETE 73 | @Path("/servers/{serverAlias}/userToken") 74 | public Response removeUserToken(@Context UriInfo ui); 75 | 76 | default Map createServerMap(Server server, String projectKey){ 77 | Map serverMap = new HashMap<>(); 78 | serverMap.put("url", server.getBaseUrl()); 79 | serverMap.put("alias", server.getAlias()); 80 | serverMap.put("scope", projectKey == null ? "global": "project"); 81 | serverMap.put("project", projectKey); 82 | serverMap.put("default_user", server.getUser()); 83 | serverMap.put("root_token_enabled", server.getAltUrl()); 84 | serverMap.put("csrf_enabled", server.getCsrfEnabled()); 85 | return serverMap; 86 | } 87 | 88 | default List sanitizeServerInput(Server server){ 89 | List errors = new ArrayList<>(2); 90 | if (server.getBaseUrl() == null || server.getBaseUrl().isEmpty()){ 91 | errors.add("Base Url required."); 92 | } else { 93 | URIBuilder builder; 94 | try { 95 | builder = new URIBuilder(server.getBaseUrl()); 96 | if (builder.getHost() == null){ 97 | errors.add("Invalide Base Url."); 98 | } 99 | } catch (URISyntaxException e) { 100 | errors.add("Invalide Base Url."); 101 | } 102 | } 103 | 104 | if (server.getAlias() == null || server.getAlias().isEmpty()){ 105 | errors.add("Alias required."); 106 | } else if (server.getAlias().contains("/")) { 107 | errors.add("Alias cannot include \"/\""); 108 | } 109 | 110 | return errors; 111 | } 112 | 113 | default Server mapToServer(Map serverMap){ 114 | return new Server(serverMap); 115 | } 116 | 117 | default String getCurrentDefaultToken(Server oldServer, Server newServer){ 118 | // if the new server didn't edit the token attribute and the server 119 | // credentials should be the same, save the old token 120 | if (shouldUseOldToken(oldServer, newServer)) { 121 | return oldServer.getToken(); 122 | } else if (newServer.getToken() == null) { 123 | return ""; 124 | } else { 125 | return newServer.getToken(); 126 | } 127 | } 128 | 129 | default boolean shouldUseOldToken(Server oldServer, Server newServer){ 130 | return 131 | oldServer != null && 132 | newServer.getToken() == null && 133 | oldServer.getBaseUrl().equals(newServer.getBaseUrl()) && 134 | oldServer.getUser().equals(newServer.getUser()); 135 | } 136 | 137 | } -------------------------------------------------------------------------------- /src/main/resources/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ParameterizedBuilds/parameterized-builds/2a27ca579ac233bcc68ca886c41bd8578e25a7d3/src/main/resources/images/icon.png -------------------------------------------------------------------------------- /src/main/resources/less/hooks.less: -------------------------------------------------------------------------------- 1 | @import "webstatic:/static/global.less"; 2 | 3 | .parameterized-builds { 4 | min-height: 430px; 5 | } 6 | 7 | .hidden { 8 | display:none; 9 | } 10 | 11 | .inline-button { 12 | display:inline; 13 | } 14 | 15 | .hide-trigger { 16 | display:none; 17 | } 18 | 19 | .hide-branches { 20 | display:none; 21 | } 22 | 23 | .hide-paths { 24 | display:none; 25 | } 26 | 27 | .hide-permissions { 28 | display:none; 29 | } 30 | 31 | .hide-pr-dest { 32 | display:none; 33 | } 34 | -------------------------------------------------------------------------------- /src/main/resources/parameterized-builds.properties: -------------------------------------------------------------------------------- 1 | #put any key/value pairs here 2 | parameterized-builds.name=Parameterized Builds for Jenkins 3 | parameterized-builds.description=Adds a build hook with customizable parameters for Jenkins 4 | parameterized-builds.buttonText=Build in Jenkins 5 | ci-servlet.name=CI Servlet 6 | ci-servlet.description=The CI Servlet Plugin 7 | -------------------------------------------------------------------------------- /src/main/resources/scripts/jenkins/feature/build-dialog.less: -------------------------------------------------------------------------------- 1 | @import "webstatic:/static/global.less"; 2 | 3 | section.aui-dialog2 .aui-dialog2-content { 4 | min-height: 0; 5 | } -------------------------------------------------------------------------------- /src/main/resources/scripts/jenkins/feature/build-dialog.soy: -------------------------------------------------------------------------------- 1 | {namespace com.kylenicholls.stash.parameterizedbuilds.jenkins.branchBuild} 2 | 3 | /** 4 | * @param jobs 5 | * @param title 6 | */ 7 | {template .fullDialog} 8 | {call aui.dialog.dialog2} 9 | {param content} 10 | {call .buildDialog} 11 | {param jobs : $jobs/} 12 | {/call} 13 | {/param} 14 | {param titleText: $title /} 15 | {param removeOnHide: true /} 16 | {param footerActionContent} 17 | {call .buildButton /} 18 | {/param} 19 | {/call} 20 | {/template} 21 | 22 | /** 23 | * @param jobs 24 | */ 25 | {template .buildDialog} 26 | {call aui.form.form} 27 | {param action: '' /} 28 | {param content} 29 | 34 | {call aui.form.form} 35 | {param content} 36 |
37 | {/param} 38 | {param action: ''/} 39 | {/call} 40 | {/param} 41 | {/call} 42 | {/template} 43 | 44 | /** 45 | * @param count 46 | * @param key 47 | * @param value 48 | */ 49 | {template .addStringParameter} 50 |
51 | {call aui.form.label} 52 | {param forField : 'build-param-value-' + $count /} 53 | {param content : $key /} 54 | {/call} 55 | 60 |
61 | {/template} 62 | 63 | /** 64 | * @param count 65 | * @param key 66 | * @param value 67 | */ 68 | {template .addBranchParameter} 69 |
70 | {call bitbucket.component.branchSelector.field} 71 | {param id: 'build-param-value-' + $count /} 72 | {param labelText: $key /} 73 | {param initialValue: $value /} 74 | {/call} 75 |
76 | {/template} 77 | 78 | /** 79 | * @param count 80 | * @param key 81 | * @param value 82 | */ 83 | {template .addBooleanParameter} 84 |
85 |
86 | 88 | 89 |
90 |
91 | {/template} 92 | 93 | /** 94 | * @param count 95 | * @param key 96 | * @param value 97 | */ 98 | {template .addArrayParameter} 99 |
100 | {call aui.form.label} 101 | {param forField : 'build-param-value-' + $count /} 102 | {param content : $key /} 103 | {/call} 104 | 109 |
110 | {/template} 111 | 112 | /** 113 | * Start build button 114 | */ 115 | {template .buildButton} 116 | {call widget.aui.form.button} 117 | {param id: 'start-build' /} 118 | {param label: 'Start Build' /} 119 | {param autofocus: true /} 120 | {/call} 121 | {/template} -------------------------------------------------------------------------------- /src/main/resources/scripts/jenkins/pb-blayout-trigger.js: -------------------------------------------------------------------------------- 1 | define('jenkins/parameterized-build-layout', [ 2 | 'bitbucket/util/state', 3 | 'trigger/build-dialog', 4 | 'exports' 5 | ], function( 6 | pageState, 7 | branchBuild, 8 | exports 9 | ) { 10 | exports.onReady = function () { 11 | branchBuild.bindToDropdownLink('.parameterized-build-layout', '#branch-actions-menu', function () { 12 | return [pageState.getRef().id, pageState.getRef().latestCommit]; 13 | }); 14 | }; 15 | }); 16 | 17 | $(document).ready(function () { 18 | require('jenkins/parameterized-build-layout').onReady(); 19 | }); -------------------------------------------------------------------------------- /src/main/resources/scripts/jenkins/pb-blist-trigger.js: -------------------------------------------------------------------------------- 1 | define('jenkins/parameterized-build-branchlist', [ 2 | 'jquery', 3 | 'trigger/build-dialog', 4 | 'exports' 5 | ], function( 6 | $, 7 | branchBuild, 8 | exports 9 | ) { 10 | exports.onReady = function () { 11 | branchBuild.bindToDropdownLink('.parameterized-build-branchlist', '.branch-list-action-dropdown', function (element) { 12 | return [$(element).closest('[data-id]').attr('data-id'), $(element).closest('[data-latest-commit]').attr('data-latest-commit')]; 13 | }); 14 | }; 15 | }); 16 | 17 | $(document).ready(function () { 18 | require('jenkins/parameterized-build-branchlist').onReady(); 19 | }); -------------------------------------------------------------------------------- /src/main/resources/templates/jenkins-admin-settings.soy: -------------------------------------------------------------------------------- 1 | {namespace jenkins.admin} 2 | 3 | /** 4 | * @param bitbucketContext 5 | */ 6 | {template .settings} 7 | 8 | 9 | 10 | 11 | Jenkins Settings 12 | 13 | 14 |

Jenkins Settings

15 |
16 | 17 | 18 |
19 | 20 | 21 | {/template} 22 | 23 | /** 24 | * @param projectKey 25 | * @param bitbucketContext 26 | */ 27 | {template .settingsProjectAdmin} 28 | 29 | 30 | 31 | 32 | 33 | Jenkins Settings 34 | 35 | 36 |

Jenkins Settings

37 |
38 | 39 | 40 |
41 | 42 | 43 | {/template} -------------------------------------------------------------------------------- /src/main/resources/templates/jenkins-user-settings.soy: -------------------------------------------------------------------------------- 1 | {namespace jenkins.user} 2 | 3 | /** 4 | * @param user 5 | * @param projectTokens 6 | * @param bitbucketContext 7 | */ 8 | {template .settings} 9 | 10 | 11 | 12 | 13 | 14 | Jenkins Settings for {$user.displayName} 15 | 16 | 17 |

Jenkins User Settings

18 |
19 | 20 | 21 |
22 | 23 | 24 | {/template} -------------------------------------------------------------------------------- /src/test/java/com/kylenicholls/stash/parameterizedbuilds/PullRequestHookTest.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds; 2 | 3 | import static org.mockito.ArgumentMatchers.any; 4 | import static org.mockito.Mockito.doAnswer; 5 | import static org.mockito.Mockito.mock; 6 | import static org.mockito.Mockito.times; 7 | import static org.mockito.Mockito.verify; 8 | import static org.mockito.Mockito.when; 9 | 10 | import java.io.IOException; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | import com.atlassian.bitbucket.event.pull.PullRequestRescopedEvent; 15 | import com.atlassian.bitbucket.hook.repository.RepositoryHook; 16 | import com.kylenicholls.stash.parameterizedbuilds.eventHandlers.BaseHandler; 17 | import com.kylenicholls.stash.parameterizedbuilds.eventHandlers.TestEventFactory; 18 | import org.junit.Before; 19 | import org.junit.Test; 20 | import org.junit.runner.RunWith; 21 | import org.powermock.api.mockito.PowerMockito; 22 | import org.powermock.core.classloader.annotations.PrepareForTest; 23 | import org.powermock.modules.junit4.PowerMockRunner; 24 | 25 | import com.atlassian.bitbucket.project.Project; 26 | import com.atlassian.bitbucket.pull.PullRequestService; 27 | import com.atlassian.bitbucket.repository.Repository; 28 | import com.atlassian.bitbucket.server.ApplicationPropertiesService; 29 | import com.atlassian.bitbucket.setting.Settings; 30 | import com.google.common.collect.Lists; 31 | import com.kylenicholls.stash.parameterizedbuilds.ciserver.Jenkins; 32 | import com.kylenicholls.stash.parameterizedbuilds.ciserver.JenkinsConnection; 33 | import com.kylenicholls.stash.parameterizedbuilds.helper.SettingsService; 34 | import com.kylenicholls.stash.parameterizedbuilds.item.Job; 35 | import com.kylenicholls.stash.parameterizedbuilds.item.Job.JobBuilder; 36 | import com.kylenicholls.stash.parameterizedbuilds.item.Server; 37 | 38 | import java.net.URI; 39 | import java.util.concurrent.ExecutorService; 40 | 41 | @RunWith(PowerMockRunner.class) 42 | @PrepareForTest({PullRequestHook.class, BaseHandler.class}) 43 | public class PullRequestHookTest { 44 | private final String COMMIT = "commithash"; 45 | private final String PR_URI = "http://pruri"; 46 | private final Server globalServer = new Server("globalurl", null, "globaluser", "globaltoken", 47 | false, false); 48 | private SettingsService settingsService; 49 | private Jenkins jenkins; 50 | private JenkinsConnection jenkinsConn; 51 | private ApplicationPropertiesService propertiesService; 52 | private PullRequestHook hook; 53 | private Repository repository; 54 | private JobBuilder jobBuilder; 55 | private List jobs; 56 | private RepositoryHook repoHook; 57 | private TestEventFactory eventFactory; 58 | private ExecutorService executorService; 59 | 60 | @Before 61 | public void setup() throws Exception { 62 | settingsService = mock(SettingsService.class); 63 | PullRequestService pullRequestService = mock(PullRequestService.class); 64 | jenkins = mock(Jenkins.class); 65 | propertiesService = mock(ApplicationPropertiesService.class); 66 | executorService = mock(ExecutorService.class); 67 | 68 | // executor simply invokes run on argument 69 | doAnswer(invocationOnMock -> { 70 | Object[] args = invocationOnMock.getArguments(); 71 | Runnable runnable = (Runnable) args[0]; 72 | runnable.run(); 73 | return null; 74 | }).when(executorService).submit(any(Runnable.class)); 75 | 76 | when(propertiesService.getBaseUrl()).thenReturn(new URI(PR_URI)); 77 | hook = new PullRequestHook(settingsService, pullRequestService, jenkins, propertiesService, 78 | executorService); 79 | eventFactory = new TestEventFactory(); 80 | 81 | Project project = mock(Project.class); 82 | Settings settings = mock(Settings.class); 83 | repository = mock(Repository.class); 84 | repoHook = mock(RepositoryHook.class); 85 | List testServers = Lists.newArrayList(globalServer); 86 | 87 | when(repository.getProject()).thenReturn(project); 88 | when(settingsService.getSettings(repository)).thenReturn(settings); 89 | when(jenkins.getJenkinsServers(null)).thenReturn(testServers); 90 | when(settingsService.getHook(any())).thenReturn(repoHook); 91 | when(repoHook.isEnabled()).thenReturn(true); 92 | 93 | jobBuilder = new Job.JobBuilder(1).jobName("").buildParameters("").branchRegex("") 94 | .pathRegex("").prDestRegex(""); 95 | jobs = new ArrayList<>(); 96 | when(settingsService.getJobs(any())).thenReturn(jobs); 97 | 98 | jenkinsConn = mock(JenkinsConnection.class); 99 | PowerMockito.whenNew(JenkinsConnection.class) 100 | .withArguments(jenkins) 101 | .thenReturn(jenkinsConn); 102 | } 103 | 104 | @Test 105 | public void testPRSourceRescopedTriggersBuild() throws IOException { 106 | Job job = jobBuilder.triggers(new String[] { "PRSOURCERESCOPED" }).build(); 107 | jobs.add(job); 108 | PullRequestRescopedEvent rescopedEvent = eventFactory.getMockedRescopedEvent(repository); 109 | when(rescopedEvent.getPreviousFromHash()).thenReturn("newhash"); 110 | hook.onPullRequestRescoped(rescopedEvent); 111 | 112 | verify(jenkinsConn, times(1)).triggerJob(any(), any(), any(), any()); 113 | } 114 | 115 | @Test 116 | public void testPRDestRescopedDoesntTriggerBuild() throws IOException { 117 | Job job = jobBuilder.triggers(new String[] { "PRDESTRESCOPED" }).build(); 118 | jobs.add(job); 119 | PullRequestRescopedEvent rescopedEvent = eventFactory.getMockedRescopedEvent(repository); 120 | when(rescopedEvent.getPreviousFromHash()).thenReturn(COMMIT); 121 | hook.onPullRequestRescoped(rescopedEvent); 122 | 123 | verify(jenkinsConn, times(1)).triggerJob(any(), any(), any(), any()); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/test/java/com/kylenicholls/stash/parameterizedbuilds/conditions/BuildPermissionsConditionTest.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.conditions; 2 | 3 | import com.atlassian.bitbucket.auth.AuthenticationContext; 4 | import com.atlassian.bitbucket.permission.Permission; 5 | import com.atlassian.bitbucket.permission.PermissionService; 6 | import com.atlassian.bitbucket.repository.Repository; 7 | import com.atlassian.bitbucket.repository.RepositoryService; 8 | import com.atlassian.bitbucket.setting.Settings; 9 | import com.kylenicholls.stash.parameterizedbuilds.helper.SettingsService; 10 | import com.kylenicholls.stash.parameterizedbuilds.item.Job; 11 | import org.junit.Before; 12 | import org.junit.Test; 13 | 14 | import javax.servlet.http.HttpServletRequest; 15 | import java.util.ArrayList; 16 | import java.util.HashMap; 17 | import java.util.List; 18 | import java.util.Map; 19 | 20 | import static org.junit.Assert.assertFalse; 21 | import static org.junit.Assert.assertTrue; 22 | import static org.mockito.ArgumentMatchers.any; 23 | import static org.mockito.ArgumentMatchers.eq; 24 | import static org.mockito.Mockito.mock; 25 | import static org.mockito.Mockito.when; 26 | 27 | public class BuildPermissionsConditionTest { 28 | 29 | private BuildPermissionsCondition condition; 30 | private Repository repository; 31 | private Map context; 32 | private SettingsService settingsService; 33 | private RepositoryService repositoryService; 34 | private Settings settings; 35 | 36 | @Before 37 | public void setup() { 38 | repository = mock(Repository.class); 39 | settingsService = mock(SettingsService.class); 40 | repositoryService = mock(RepositoryService.class); 41 | settings = mock(Settings.class); 42 | 43 | context = new HashMap<>(); 44 | context.put("repository", repository); 45 | 46 | PermissionService permissionService = mock(PermissionService.class); 47 | AuthenticationContext authContext = mock(AuthenticationContext.class); 48 | condition = new BuildPermissionsCondition(repositoryService, permissionService, 49 | settingsService, authContext); 50 | 51 | when(permissionService.hasRepositoryPermission(any(), any(), eq(Permission.REPO_WRITE))) 52 | .thenReturn(true); 53 | when(permissionService.hasRepositoryPermission(any(), any(), eq(Permission.REPO_READ))) 54 | .thenReturn(true); 55 | } 56 | 57 | @Test 58 | public void testShouldNotDisplayIfRepositoryNull() { 59 | context.put("repository", null); 60 | assertFalse(condition.shouldDisplay(context)); 61 | } 62 | 63 | @Test 64 | public void testShouldNotDisplayIfNotRepository() { 65 | context.put("repository", "notARepository"); 66 | assertFalse(condition.shouldDisplay(context)); 67 | } 68 | 69 | @Test 70 | public void testGetRepoFromRequest() { 71 | context.put("repository", null); 72 | HttpServletRequest mockRequest = mock(HttpServletRequest.class); 73 | when(mockRequest.getRequestURI()).thenReturn("/projects/PROJ1/repos/REP1/"); 74 | when(repositoryService.getBySlug("PROJ1", "REP1")).thenReturn(repository); 75 | when(settingsService.getSettings(repository)).thenReturn(settings); 76 | Job job = new Job.JobBuilder(1).permissions("REPO_WRITE").build(); 77 | List jobs = new ArrayList<>(); 78 | jobs.add(job); 79 | when(settingsService.getJobs(any())).thenReturn(jobs); 80 | context.put("request", mockRequest); 81 | assertTrue(condition.shouldDisplay(context)); 82 | } 83 | 84 | @Test 85 | public void testShouldNotDisplayIfInsufficientPermissions() { 86 | when(settingsService.getSettings(repository)).thenReturn(settings); 87 | Job job = new Job.JobBuilder(1).permissions("REPO_ADMIN").build(); 88 | List jobs = new ArrayList<>(); 89 | jobs.add(job); 90 | when(settingsService.getJobs(any())).thenReturn(jobs); 91 | assertFalse(condition.shouldDisplay(context)); 92 | } 93 | 94 | @Test 95 | public void testShouldDisplayExplicitPermissionPresent() { 96 | when(settingsService.getSettings(repository)).thenReturn(settings); 97 | Job job = new Job.JobBuilder(1).permissions("REPO_WRITE").build(); 98 | List jobs = new ArrayList<>(); 99 | jobs.add(job); 100 | when(settingsService.getJobs(any())).thenReturn(jobs); 101 | assertTrue(condition.shouldDisplay(context)); 102 | } 103 | 104 | @Test 105 | public void testShouldDisplayImplicitPermissionPresent() { 106 | when(settingsService.getSettings(repository)).thenReturn(settings); 107 | Job job = new Job.JobBuilder(1).permissions("REPO_READ").build(); 108 | List jobs = new ArrayList<>(); 109 | jobs.add(job); 110 | when(settingsService.getJobs(any())).thenReturn(jobs); 111 | assertTrue(condition.shouldDisplay(context)); 112 | } 113 | 114 | } -------------------------------------------------------------------------------- /src/test/java/com/kylenicholls/stash/parameterizedbuilds/conditions/HookIsEnabledConditionTest.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.conditions; 2 | 3 | import static org.junit.Assert.assertFalse; 4 | import static org.junit.Assert.assertTrue; 5 | import static org.mockito.Mockito.mock; 6 | import static org.mockito.Mockito.when; 7 | 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | 11 | import com.atlassian.bitbucket.repository.RepositoryService; 12 | import org.junit.Before; 13 | import org.junit.Test; 14 | 15 | import com.atlassian.bitbucket.hook.repository.RepositoryHook; 16 | import com.atlassian.bitbucket.repository.Repository; 17 | import com.kylenicholls.stash.parameterizedbuilds.helper.SettingsService; 18 | 19 | import javax.servlet.http.HttpServletRequest; 20 | 21 | public class HookIsEnabledConditionTest { 22 | private RepositoryHook repoHook; 23 | private Map context; 24 | private SettingsService settingsService; 25 | private RepositoryService repositoryService; 26 | private HookIsEnabledCondition condition; 27 | private Repository repository; 28 | 29 | @Before 30 | public void setup() { 31 | settingsService = mock(SettingsService.class); 32 | repositoryService = mock(RepositoryService.class); 33 | repository = mock(Repository.class); 34 | repoHook = mock(RepositoryHook.class); 35 | 36 | context = new HashMap<>(); 37 | context.put("repository", repository); 38 | 39 | condition = new HookIsEnabledCondition(repositoryService, settingsService); 40 | } 41 | 42 | @Test 43 | public void testShouldNotDisplayIfRepositoryNull() { 44 | context.put("repository", null); 45 | assertFalse(condition.shouldDisplay(context)); 46 | } 47 | 48 | @Test 49 | public void testShouldNotDisplayIfNotRepository() { 50 | context.put("repository", "notARepository"); 51 | assertFalse(condition.shouldDisplay(context)); 52 | } 53 | 54 | @Test 55 | public void testGetRepoFromRequest() { 56 | context.put("repository", null); 57 | HttpServletRequest mockRequest = mock(HttpServletRequest.class); 58 | when(mockRequest.getRequestURI()).thenReturn("/projects/PROJ1/repos/REP1/"); 59 | when(repositoryService.getBySlug("PROJ1", "REP1")).thenReturn(repository); 60 | when(settingsService.getHook(repository)).thenReturn(repoHook); 61 | when(repoHook.isEnabled()).thenReturn(true); 62 | context.put("request", mockRequest); 63 | assertTrue(condition.shouldDisplay(context)); 64 | } 65 | 66 | @Test 67 | public void testShouldDisplayHookEnabled() { 68 | when(settingsService.getHook(repository)).thenReturn(repoHook); 69 | when(repoHook.isEnabled()).thenReturn(true); 70 | assertTrue(condition.shouldDisplay(context)); 71 | } 72 | 73 | @Test 74 | public void testShouldNotDisplayWhenHookDisable() { 75 | when(settingsService.getHook(repository)).thenReturn(repoHook); 76 | when(repoHook.isEnabled()).thenReturn(false); 77 | assertFalse(condition.shouldDisplay(context)); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/test/java/com/kylenicholls/stash/parameterizedbuilds/conditions/ManualButtonConditionTest.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.conditions; 2 | 3 | import static org.junit.Assert.assertFalse; 4 | import static org.junit.Assert.assertTrue; 5 | import static org.mockito.ArgumentMatchers.any; 6 | import static org.mockito.Mockito.mock; 7 | import static org.mockito.Mockito.when; 8 | 9 | import java.util.ArrayList; 10 | import java.util.HashMap; 11 | import java.util.List; 12 | import java.util.Map; 13 | 14 | import com.atlassian.bitbucket.repository.RepositoryService; 15 | import org.junit.Before; 16 | import org.junit.Test; 17 | 18 | import com.atlassian.bitbucket.repository.Repository; 19 | import com.atlassian.bitbucket.setting.Settings; 20 | import com.kylenicholls.stash.parameterizedbuilds.helper.SettingsService; 21 | import com.kylenicholls.stash.parameterizedbuilds.item.Job; 22 | 23 | import javax.servlet.http.HttpServletRequest; 24 | 25 | public class ManualButtonConditionTest { 26 | private Map context; 27 | private SettingsService settingsService; 28 | private RepositoryService repositoryService; 29 | private ManualButtonCondition condition; 30 | private Repository repository; 31 | private Settings settings; 32 | 33 | @Before 34 | public void setup() { 35 | settingsService = mock(SettingsService.class); 36 | repositoryService = mock(RepositoryService.class); 37 | repository = mock(Repository.class); 38 | settings = mock(Settings.class); 39 | 40 | context = new HashMap<>(); 41 | context.put("repository", repository); 42 | 43 | condition = new ManualButtonCondition(repositoryService, settingsService); 44 | } 45 | 46 | @Test 47 | public void testShouldNotDisplayIfRepositoryNull() { 48 | context.put("repository", null); 49 | assertFalse(condition.shouldDisplay(context)); 50 | } 51 | 52 | @Test 53 | public void testShouldNotDisplayIfNotRepository() { 54 | context.put("repository", "notARepository"); 55 | assertFalse(condition.shouldDisplay(context)); 56 | } 57 | 58 | @Test 59 | public void testGetRepoFromRequest() { 60 | context.put("repository", null); 61 | HttpServletRequest mockRequest = mock(HttpServletRequest.class); 62 | when(mockRequest.getRequestURI()).thenReturn("/projects/PROJ1/repos/REP1/"); 63 | when(repositoryService.getBySlug("PROJ1", "REP1")).thenReturn(repository); 64 | when(settingsService.getSettings(repository)).thenReturn(settings); 65 | Job job = new Job.JobBuilder(1).jobName("").triggers("manual".split(";")) 66 | .buildParameters("").branchRegex("").pathRegex("").build(); 67 | List jobs = new ArrayList<>(); 68 | jobs.add(job); 69 | when(settingsService.getJobs(any())).thenReturn(jobs); 70 | context.put("request", mockRequest); 71 | assertTrue(condition.shouldDisplay(context)); 72 | } 73 | 74 | @Test 75 | public void testShouldDisplayWhenManualTrigger() { 76 | when(settingsService.getSettings(repository)).thenReturn(settings); 77 | Job job = new Job.JobBuilder(1).jobName("").triggers("manual".split(";")) 78 | .buildParameters("").branchRegex("").pathRegex("").build(); 79 | List jobs = new ArrayList<>(); 80 | jobs.add(job); 81 | when(settingsService.getJobs(any())).thenReturn(jobs); 82 | assertTrue(condition.shouldDisplay(context)); 83 | } 84 | 85 | @Test 86 | public void testShouldNotDisplayWhenNotManualTrigger() { 87 | when(settingsService.getSettings(repository)).thenReturn(settings); 88 | Job job = new Job.JobBuilder(1).jobName("").triggers("add".split(";")).buildParameters("") 89 | .branchRegex("").pathRegex("").build(); 90 | List jobs = new ArrayList<>(); 91 | jobs.add(job); 92 | when(settingsService.getJobs(any())).thenReturn(jobs); 93 | assertFalse(condition.shouldDisplay(context)); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/test/java/com/kylenicholls/stash/parameterizedbuilds/eventHandlers/PRApprovedHandlerTest.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.eventHandlers; 2 | 3 | import com.atlassian.bitbucket.event.pull.PullRequestParticipantApprovedEvent; 4 | import com.kylenicholls.stash.parameterizedbuilds.item.Job; 5 | import org.junit.Test; 6 | 7 | import java.io.IOException; 8 | 9 | import static org.mockito.ArgumentMatchers.any; 10 | import static org.mockito.ArgumentMatchers.eq; 11 | import static org.mockito.Mockito.doNothing; 12 | import static org.mockito.Mockito.spy; 13 | import static org.mockito.Mockito.times; 14 | import static org.mockito.Mockito.verify; 15 | 16 | public class PRApprovedHandlerTest extends PRTestBase { 17 | 18 | @Test 19 | public void testPRDeclinedAndTriggerIsPRDECLINED() throws IOException{ 20 | Job job = jobBuilder.triggers(new String[] { "PRAPPROVED" }).build(); 21 | jobs.add(job); 22 | PullRequestParticipantApprovedEvent approvedEvent = 23 | eventFactory.getMockedApprovedEvent(repository); 24 | PRApprovedHandler handler = new PRApprovedHandler(settingsService, pullRequestService, 25 | jenkins, approvedEvent, PR_URL); 26 | PRApprovedHandler spyHandler = spy(handler); 27 | doNothing().when(spyHandler).triggerJenkins(any(), any()); 28 | spyHandler.run(); 29 | 30 | verify(spyHandler, times(1)).triggerJenkins(eq(job), any()); 31 | } 32 | } -------------------------------------------------------------------------------- /src/test/java/com/kylenicholls/stash/parameterizedbuilds/eventHandlers/PRAutoMergedHandlerTest.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.eventHandlers; 2 | 3 | import com.atlassian.bitbucket.branch.automerge.AutomaticMergeEvent; 4 | import com.atlassian.bitbucket.repository.Branch; 5 | import com.kylenicholls.stash.parameterizedbuilds.item.Job; 6 | import org.junit.Test; 7 | 8 | import java.io.IOException; 9 | 10 | import static org.mockito.ArgumentMatchers.any; 11 | import static org.mockito.ArgumentMatchers.eq; 12 | import static org.mockito.Mockito.doNothing; 13 | import static org.mockito.Mockito.mock; 14 | import static org.mockito.Mockito.spy; 15 | import static org.mockito.Mockito.times; 16 | import static org.mockito.Mockito.verify; 17 | 18 | public class PRAutoMergedHandlerTest extends PRTestBase { 19 | 20 | @Test 21 | public void testPRAutoMergedAndTriggerIsPRAUTOMERGED() throws IOException{ 22 | Job job = jobBuilder.triggers(new String[] { "PRAUTOMERGED" }).build(); 23 | jobs.add(job); 24 | AutomaticMergeEvent automaticMergeEvent = eventFactory.getMockedAutoMergeEvent(repository); 25 | Branch branch = mock(Branch.class); 26 | PRAutoMergedHandler handler = new PRAutoMergedHandler(settingsService, jenkins, 27 | automaticMergeEvent, PR_URL, branch); 28 | PRAutoMergedHandler spyHandler = spy(handler); 29 | doNothing().when(spyHandler).triggerJenkins(any(), any()); 30 | spyHandler.run(); 31 | 32 | verify(spyHandler, times(1)).triggerJenkins(eq(job), any()); 33 | } 34 | } -------------------------------------------------------------------------------- /src/test/java/com/kylenicholls/stash/parameterizedbuilds/eventHandlers/PRDeclinedHandlerTest.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.eventHandlers; 2 | 3 | import com.atlassian.bitbucket.event.pull.PullRequestDeclinedEvent; 4 | import com.kylenicholls.stash.parameterizedbuilds.item.Job; 5 | import org.junit.Test; 6 | 7 | import java.io.IOException; 8 | 9 | import static org.mockito.ArgumentMatchers.any; 10 | import static org.mockito.ArgumentMatchers.eq; 11 | import static org.mockito.Mockito.doNothing; 12 | import static org.mockito.Mockito.spy; 13 | import static org.mockito.Mockito.times; 14 | import static org.mockito.Mockito.verify; 15 | public class PRDeclinedHandlerTest extends PRTestBase { 16 | 17 | @Test 18 | public void testPRDeclinedAndTriggerIsPRDECLINED() throws IOException{ 19 | Job job = jobBuilder.triggers(new String[] { "PRDECLINED" }).build(); 20 | jobs.add(job); 21 | PullRequestDeclinedEvent declinedEvent = eventFactory.getMockedDeclinedEvent(repository); 22 | PRDeclinedHandler handler = new PRDeclinedHandler(settingsService, pullRequestService, 23 | jenkins, declinedEvent, PR_URL); 24 | PRDeclinedHandler spyHandler = spy(handler); 25 | doNothing().when(spyHandler).triggerJenkins(any(), any()); 26 | spyHandler.run(); 27 | 28 | verify(spyHandler, times(1)).triggerJenkins(eq(job), any()); 29 | } 30 | } -------------------------------------------------------------------------------- /src/test/java/com/kylenicholls/stash/parameterizedbuilds/eventHandlers/PRDeletedHandlerTest.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.eventHandlers; 2 | 3 | import com.atlassian.bitbucket.event.pull.PullRequestDeletedEvent; 4 | import com.kylenicholls.stash.parameterizedbuilds.item.Job; 5 | import org.junit.Test; 6 | 7 | import java.io.IOException; 8 | 9 | import static org.mockito.ArgumentMatchers.any; 10 | import static org.mockito.ArgumentMatchers.eq; 11 | import static org.mockito.Mockito.doNothing; 12 | import static org.mockito.Mockito.spy; 13 | import static org.mockito.Mockito.times; 14 | import static org.mockito.Mockito.verify; 15 | 16 | public class PRDeletedHandlerTest extends PRTestBase { 17 | 18 | @Test 19 | public void testPRDeletedAndTriggerIsPRDELETED() throws IOException{ 20 | Job job = jobBuilder.triggers(new String[] { "PRDELETED" }).build(); 21 | jobs.add(job); 22 | PullRequestDeletedEvent deletedEvent = eventFactory.getMockedDeletedEvent(repository); 23 | PRDeletedHandler handler = new PRDeletedHandler(settingsService, pullRequestService, 24 | jenkins, deletedEvent, PR_URL); 25 | PRDeletedHandler spyHandler = spy(handler); 26 | doNothing().when(spyHandler).triggerJenkins(any(), any()); 27 | spyHandler.run(); 28 | 29 | verify(spyHandler, times(1)).triggerJenkins(eq(job), any()); 30 | } 31 | } -------------------------------------------------------------------------------- /src/test/java/com/kylenicholls/stash/parameterizedbuilds/eventHandlers/PRDestRescopedHandlerTest.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.eventHandlers; 2 | 3 | import com.atlassian.bitbucket.event.pull.PullRequestRescopedEvent; 4 | import com.kylenicholls.stash.parameterizedbuilds.item.Job; 5 | import org.junit.Test; 6 | 7 | import java.io.IOException; 8 | 9 | import static org.mockito.ArgumentMatchers.any; 10 | import static org.mockito.ArgumentMatchers.eq; 11 | import static org.mockito.Mockito.doNothing; 12 | import static org.mockito.Mockito.spy; 13 | import static org.mockito.Mockito.times; 14 | import static org.mockito.Mockito.verify; 15 | 16 | public class PRDestRescopedHandlerTest extends PRTestBase { 17 | 18 | @Test 19 | public void testPRTargetRescopedAndTriggerIsPULLREQUEST() throws IOException{ 20 | Job job = jobBuilder.triggers(new String[] { "PRDESTRESCOPED" }).build(); 21 | jobs.add(job); 22 | PullRequestRescopedEvent rescopedEvent = eventFactory.getMockedRescopedEvent(repository); 23 | PRDestRescopedHandler handler = new PRDestRescopedHandler(settingsService, 24 | pullRequestService, jenkins, rescopedEvent, PR_URL); 25 | PRDestRescopedHandler spyHandler = spy(handler); 26 | doNothing().when(spyHandler).triggerJenkins(any(), any()); 27 | spyHandler.run(); 28 | 29 | verify(spyHandler, times(1)).triggerJenkins(eq(job), any()); 30 | } 31 | } -------------------------------------------------------------------------------- /src/test/java/com/kylenicholls/stash/parameterizedbuilds/eventHandlers/PRHandlerTest.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.eventHandlers; 2 | 3 | import com.atlassian.bitbucket.event.pull.PullRequestDeclinedEvent; 4 | import com.atlassian.bitbucket.event.pull.PullRequestOpenedEvent; 5 | import com.atlassian.bitbucket.hook.repository.RepositoryHook; 6 | import com.atlassian.bitbucket.project.Project; 7 | import com.atlassian.bitbucket.pull.PullRequestService; 8 | import com.atlassian.bitbucket.repository.Repository; 9 | import com.atlassian.bitbucket.setting.Settings; 10 | import com.kylenicholls.stash.parameterizedbuilds.ciserver.Jenkins; 11 | import com.kylenicholls.stash.parameterizedbuilds.helper.SettingsService; 12 | import com.kylenicholls.stash.parameterizedbuilds.item.Job; 13 | import com.kylenicholls.stash.parameterizedbuilds.item.Server; 14 | import org.junit.Before; 15 | import org.junit.Test; 16 | 17 | import java.io.IOException; 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | 21 | import static org.mockito.ArgumentMatchers.any; 22 | import static org.mockito.ArgumentMatchers.eq; 23 | import static org.mockito.ArgumentMatchers.isNull; 24 | import static org.mockito.Mockito.doNothing; 25 | import static org.mockito.Mockito.mock; 26 | import static org.mockito.Mockito.spy; 27 | import static org.mockito.Mockito.times; 28 | import static org.mockito.Mockito.verify; 29 | import static org.mockito.Mockito.when; 30 | 31 | 32 | public class PRHandlerTest { 33 | 34 | 35 | private final String PROJECT_KEY = "projectkey"; 36 | private final String PROJECT_NAME = "projectname"; 37 | private final String PR_URL = "http://pruri"; 38 | private final Server globalServer = new Server("globalurl", null, "globaluser", "globaltoken", 39 | false, false); 40 | private SettingsService settingsService; 41 | private Jenkins jenkins; 42 | private Repository repository; 43 | private Job.JobBuilder jobBuilder; 44 | private List jobs; 45 | private RepositoryHook repoHook; 46 | private PullRequestService pullRequestService; 47 | private TestEventFactory eventFactory; 48 | 49 | @Before 50 | public void setup() { 51 | settingsService = mock(SettingsService.class); 52 | pullRequestService = mock(PullRequestService.class); 53 | jenkins = mock(Jenkins.class); 54 | eventFactory = new TestEventFactory(); 55 | 56 | Project project = mock(Project.class); 57 | Settings settings = mock(Settings.class); 58 | repository = mock(Repository.class); 59 | repoHook = mock(RepositoryHook.class); 60 | 61 | when(repository.getProject()).thenReturn(project); 62 | when(project.getName()).thenReturn(PROJECT_NAME); 63 | when(project.getKey()).thenReturn(PROJECT_KEY); 64 | when(settingsService.getSettings(repository)).thenReturn(settings); 65 | when(jenkins.getJenkinsServer(isNull(), any())).thenReturn(globalServer); 66 | when(settingsService.getHook(any())).thenReturn(repoHook); 67 | when(repoHook.isEnabled()).thenReturn(true); 68 | 69 | jobBuilder = new Job.JobBuilder(1).jobName("").buildParameters("").branchRegex("") 70 | .pathRegex("").prDestRegex(""); 71 | jobs = new ArrayList<>(); 72 | when(settingsService.getJobs(any())).thenReturn(jobs); 73 | } 74 | 75 | @Test 76 | public void testNoSettings() throws IOException { 77 | Job job = jobBuilder.triggers(new String[] { "PRDECLINED" }).prDestRegex("test_branch") 78 | .build(); 79 | jobs.add(job); 80 | when(settingsService.getSettings(repository)).thenReturn(null); 81 | PullRequestDeclinedEvent declinedEvent = eventFactory.getMockedDeclinedEvent(repository); 82 | PRHandler handler = new PRHandler(settingsService, pullRequestService, jenkins, 83 | declinedEvent, PR_URL, Job.Trigger.PRDECLINED); 84 | PRHandler spyHandler = spy(handler); 85 | spyHandler.run(); 86 | 87 | verify(spyHandler, times(0)).triggerJenkins(any(), any()); 88 | } 89 | 90 | @Test 91 | public void testPrDestRegexMatches() throws IOException { 92 | Job job = jobBuilder.triggers(new String[] { "PRDECLINED" }).prDestRegex("test_branch") 93 | .build(); 94 | jobs.add(job); 95 | PullRequestDeclinedEvent declinedEvent = eventFactory.getMockedDeclinedEvent(repository); 96 | when(declinedEvent.getPullRequest().getToRef().getDisplayId()).thenReturn("test_branch"); 97 | PRHandler handler = new PRHandler(settingsService, pullRequestService, jenkins, 98 | declinedEvent, PR_URL, Job.Trigger.PRDECLINED); 99 | PRHandler spyHandler = spy(handler); 100 | doNothing().when(spyHandler).triggerJenkins(any(), any()); 101 | spyHandler.run(); 102 | 103 | verify(spyHandler, times(1)).triggerJenkins(eq(job), any()); 104 | } 105 | 106 | @Test 107 | public void testPrDestRegexDoesNotMatch() throws IOException { 108 | Job job = jobBuilder.triggers(new String[]{"PRDECLINED"}).prDestRegex("test_branch") 109 | .build(); 110 | jobs.add(job); 111 | PullRequestDeclinedEvent declinedEvent = eventFactory.getMockedDeclinedEvent(repository); 112 | when(declinedEvent.getPullRequest().getToRef().getDisplayId()) 113 | .thenReturn("not_desired_branch"); 114 | PRHandler handler = new PRHandler(settingsService, pullRequestService, jenkins, 115 | declinedEvent, PR_URL, Job.Trigger.PRDECLINED); 116 | PRHandler spyHandler = spy(handler); 117 | spyHandler.run(); 118 | 119 | verify(spyHandler, times(0)).triggerJenkins(any(), any()); 120 | } 121 | 122 | @Test 123 | public void testHookIsDisabled() throws IOException { 124 | Job job = jobBuilder.triggers(new String[] { "PULLREQUEST" }).build(); 125 | jobs.add(job); 126 | when(repoHook.isEnabled()).thenReturn(false); 127 | PullRequestDeclinedEvent declinedEvent = eventFactory.getMockedDeclinedEvent(repository); 128 | PRHandler handler = new PRHandler(settingsService, pullRequestService, jenkins, 129 | declinedEvent, PR_URL, Job.Trigger.PRDECLINED); 130 | PRHandler spyHandler = spy(handler); 131 | spyHandler.run(); 132 | 133 | verify(spyHandler, times(0)).triggerJenkins(any(), any()); 134 | } 135 | 136 | @Test 137 | public void testMismatchingTriggers() throws IOException { 138 | Job job = jobBuilder.triggers(new String[] { "PRDECLINED" }).build(); 139 | jobs.add(job); 140 | PullRequestOpenedEvent openedEvent = eventFactory.getMockedOpenedEvent(repository); 141 | PRHandler handler = new PRHandler(settingsService, pullRequestService, jenkins, openedEvent, 142 | PR_URL, Job.Trigger.PROPENED); 143 | PRHandler spyHandler = spy(handler); 144 | spyHandler.run(); 145 | 146 | verify(spyHandler, times(0)).triggerJenkins(any(), any()); 147 | } 148 | } -------------------------------------------------------------------------------- /src/test/java/com/kylenicholls/stash/parameterizedbuilds/eventHandlers/PRMergedHandlerTest.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.eventHandlers; 2 | 3 | import com.atlassian.bitbucket.event.pull.PullRequestMergedEvent; 4 | import com.kylenicholls.stash.parameterizedbuilds.item.Job; 5 | import org.junit.Test; 6 | 7 | import java.io.IOException; 8 | 9 | import static org.mockito.ArgumentMatchers.any; 10 | import static org.mockito.ArgumentMatchers.eq; 11 | import static org.mockito.Mockito.doNothing; 12 | import static org.mockito.Mockito.spy; 13 | import static org.mockito.Mockito.times; 14 | import static org.mockito.Mockito.verify; 15 | 16 | public class PRMergedHandlerTest extends PRTestBase { 17 | 18 | @Test 19 | public void testPRMergedAndTriggerIsPRMERGED() throws IOException { 20 | Job job = jobBuilder.triggers(new String[] { "PRMERGED" }).build(); 21 | jobs.add(job); 22 | PullRequestMergedEvent mergedEvent = eventFactory.getMockedMergeEvent(repository); 23 | PRMergedHandler handler = new PRMergedHandler(settingsService, pullRequestService, jenkins, 24 | mergedEvent, PR_URL); 25 | PRMergedHandler spyHandler = spy(handler); 26 | doNothing().when(spyHandler).triggerJenkins(any(), any()); 27 | spyHandler.run(); 28 | 29 | verify(spyHandler, times(1)).triggerJenkins(eq(job), any()); 30 | } 31 | 32 | @Test 33 | public void testMergeCommitAdded() throws IOException { 34 | Job job = jobBuilder.triggers(new String[] { "PRMERGED" }).build(); 35 | jobs.add(job); 36 | PullRequestMergedEvent mergedEvent = eventFactory.getMockedMergeEvent(repository); 37 | PRMergedHandler handler = new PRMergedHandler(settingsService, pullRequestService, jenkins, 38 | mergedEvent, PR_URL); 39 | PRMergedHandler spyHandler = spy(handler); 40 | doNothing().when(spyHandler).triggerJenkins(any(), any()); 41 | spyHandler.run(); 42 | 43 | verify(spyHandler, times(1)).triggerJenkins(eq(job), any()); 44 | } 45 | 46 | } -------------------------------------------------------------------------------- /src/test/java/com/kylenicholls/stash/parameterizedbuilds/eventHandlers/PROpenedHandlerTest.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.eventHandlers; 2 | 3 | import com.atlassian.bitbucket.event.pull.PullRequestOpenedEvent; 4 | import com.kylenicholls.stash.parameterizedbuilds.item.Job; 5 | import org.junit.Test; 6 | 7 | import java.io.IOException; 8 | 9 | import static org.mockito.ArgumentMatchers.any; 10 | import static org.mockito.ArgumentMatchers.eq; 11 | import static org.mockito.Mockito.doNothing; 12 | import static org.mockito.Mockito.spy; 13 | import static org.mockito.Mockito.times; 14 | import static org.mockito.Mockito.verify; 15 | 16 | public class PROpenedHandlerTest extends PRTestBase { 17 | 18 | @Test 19 | public void testPROpenedAndTriggerIsPULLREQUEST() throws IOException{ 20 | Job job = jobBuilder.triggers(new String[] { "PROPENED" }).build(); 21 | jobs.add(job); 22 | PullRequestOpenedEvent openedEvent = eventFactory.getMockedOpenedEvent(repository); 23 | PROpenedHandler handler = new PROpenedHandler(settingsService, pullRequestService, jenkins, 24 | openedEvent, PR_URL); 25 | PROpenedHandler spyHandler = spy(handler); 26 | doNothing().when(spyHandler).triggerJenkins(any(), any()); 27 | spyHandler.run(); 28 | 29 | verify(spyHandler, times(1)).triggerJenkins(eq(job), any()); 30 | } 31 | } -------------------------------------------------------------------------------- /src/test/java/com/kylenicholls/stash/parameterizedbuilds/eventHandlers/PRReopenedHandlerTest.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.eventHandlers; 2 | 3 | import com.atlassian.bitbucket.event.pull.PullRequestReopenedEvent; 4 | import com.kylenicholls.stash.parameterizedbuilds.item.Job; 5 | import org.junit.Test; 6 | 7 | import java.io.IOException; 8 | 9 | import static org.mockito.ArgumentMatchers.any; 10 | import static org.mockito.ArgumentMatchers.eq; 11 | import static org.mockito.Mockito.doNothing; 12 | import static org.mockito.Mockito.spy; 13 | import static org.mockito.Mockito.times; 14 | import static org.mockito.Mockito.verify; 15 | 16 | public class PRReopenedHandlerTest extends PRTestBase { 17 | 18 | @Test 19 | public void testPRReOpenedAndTriggerIsPULLREQUEST() throws IOException{ 20 | Job job = jobBuilder.triggers(new String[] { "PRREOPENED" }).build(); 21 | jobs.add(job); 22 | PullRequestReopenedEvent reopenedEvent = eventFactory.getMockedReopenedEvent(repository); 23 | PRReopenedHandler handler = new PRReopenedHandler(settingsService, pullRequestService, 24 | jenkins, reopenedEvent, PR_URL); 25 | PRReopenedHandler spyHandler = spy(handler); 26 | doNothing().when(spyHandler).triggerJenkins(any(), any()); 27 | spyHandler.run(); 28 | 29 | verify(spyHandler, times(1)).triggerJenkins(eq(job), any()); 30 | } 31 | } -------------------------------------------------------------------------------- /src/test/java/com/kylenicholls/stash/parameterizedbuilds/eventHandlers/PRSourceRescopedHandlerTest.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.eventHandlers; 2 | 3 | import com.atlassian.bitbucket.event.pull.PullRequestRescopedEvent; 4 | import com.kylenicholls.stash.parameterizedbuilds.item.Job; 5 | import org.junit.Test; 6 | 7 | import java.io.IOException; 8 | 9 | import static org.mockito.ArgumentMatchers.any; 10 | import static org.mockito.ArgumentMatchers.eq; 11 | import static org.mockito.Mockito.doNothing; 12 | import static org.mockito.Mockito.spy; 13 | import static org.mockito.Mockito.times; 14 | import static org.mockito.Mockito.verify; 15 | 16 | public class PRSourceRescopedHandlerTest extends PRTestBase { 17 | 18 | @Test 19 | public void testPRSourceRescopedAndTriggerIsPULLREQUEST() throws IOException{ 20 | Job job = jobBuilder.triggers(new String[] { "PRSOURCERESCOPED" }).build(); 21 | jobs.add(job); 22 | PullRequestRescopedEvent rescopedEvent = eventFactory.getMockedRescopedEvent(repository); 23 | PRSourceRescopedHandler handler = new PRSourceRescopedHandler(settingsService, 24 | pullRequestService, jenkins, rescopedEvent, PR_URL); 25 | PRSourceRescopedHandler spyHandler = spy(handler); 26 | doNothing().when(spyHandler).triggerJenkins(any(), any()); 27 | spyHandler.run(); 28 | 29 | verify(spyHandler, times(1)).triggerJenkins(eq(job), any()); 30 | } 31 | 32 | } -------------------------------------------------------------------------------- /src/test/java/com/kylenicholls/stash/parameterizedbuilds/eventHandlers/PRTestBase.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.eventHandlers; 2 | 3 | import com.atlassian.bitbucket.hook.repository.RepositoryHook; 4 | import com.atlassian.bitbucket.project.Project; 5 | import com.atlassian.bitbucket.pull.PullRequestService; 6 | import com.atlassian.bitbucket.repository.Repository; 7 | import com.atlassian.bitbucket.setting.Settings; 8 | import com.kylenicholls.stash.parameterizedbuilds.ciserver.Jenkins; 9 | import com.kylenicholls.stash.parameterizedbuilds.helper.SettingsService; 10 | import com.kylenicholls.stash.parameterizedbuilds.item.Job; 11 | import com.kylenicholls.stash.parameterizedbuilds.item.Server; 12 | import org.junit.Before; 13 | 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | 17 | import static org.mockito.ArgumentMatchers.any; 18 | import static org.mockito.ArgumentMatchers.isNull; 19 | import static org.mockito.Mockito.mock; 20 | import static org.mockito.Mockito.when; 21 | 22 | public class PRTestBase { 23 | 24 | final String PR_URL = "http://pruri"; 25 | final Server globalServer = new Server("globalurl", null, "globaluser", "globaltoken", false, 26 | false); 27 | SettingsService settingsService; 28 | Jenkins jenkins; 29 | Repository repository; 30 | Job.JobBuilder jobBuilder; 31 | List jobs; 32 | RepositoryHook repoHook; 33 | PullRequestService pullRequestService; 34 | TestEventFactory eventFactory; 35 | 36 | @Before 37 | public void setup() { 38 | settingsService = mock(SettingsService.class); 39 | pullRequestService = mock(PullRequestService.class); 40 | jenkins = mock(Jenkins.class); 41 | eventFactory = new TestEventFactory(); 42 | 43 | Project project = mock(Project.class); 44 | Settings settings = mock(Settings.class); 45 | repository = mock(Repository.class); 46 | repoHook = mock(RepositoryHook.class); 47 | 48 | when(repository.getProject()).thenReturn(project); 49 | when(settingsService.getSettings(repository)).thenReturn(settings); 50 | when(jenkins.getJenkinsServer(isNull(), any())).thenReturn(globalServer); 51 | when(settingsService.getHook(any())).thenReturn(repoHook); 52 | when(repoHook.isEnabled()).thenReturn(true); 53 | 54 | jobBuilder = new Job.JobBuilder(1).jobName("").buildParameters("").branchRegex("") 55 | .pathRegex("").prDestRegex(""); 56 | jobs = new ArrayList<>(); 57 | when(settingsService.getJobs(any())).thenReturn(jobs); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/test/java/com/kylenicholls/stash/parameterizedbuilds/eventHandlers/PushHandlerTest.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.eventHandlers; 2 | 3 | import com.atlassian.bitbucket.commit.Commit; 4 | import com.atlassian.bitbucket.commit.CommitService; 5 | import com.atlassian.bitbucket.project.Project; 6 | import com.atlassian.bitbucket.repository.MinimalRef; 7 | import com.atlassian.bitbucket.repository.RefChange; 8 | import com.atlassian.bitbucket.repository.RefChangeType; 9 | import com.atlassian.bitbucket.repository.Repository; 10 | import com.atlassian.bitbucket.setting.Settings; 11 | import com.atlassian.bitbucket.user.ApplicationUser; 12 | import com.atlassian.bitbucket.user.Person; 13 | import com.kylenicholls.stash.parameterizedbuilds.ciserver.Jenkins; 14 | import com.kylenicholls.stash.parameterizedbuilds.helper.SettingsService; 15 | import com.kylenicholls.stash.parameterizedbuilds.item.Job; 16 | import com.kylenicholls.stash.parameterizedbuilds.item.Server; 17 | import org.junit.Before; 18 | import org.junit.Test; 19 | 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | 23 | import static org.mockito.ArgumentMatchers.any; 24 | import static org.mockito.ArgumentMatchers.eq; 25 | import static org.mockito.Mockito.doNothing; 26 | import static org.mockito.Mockito.mock; 27 | import static org.mockito.Mockito.spy; 28 | import static org.mockito.Mockito.times; 29 | import static org.mockito.Mockito.verify; 30 | import static org.mockito.Mockito.when; 31 | 32 | public class PushHandlerTest { 33 | 34 | private final String BRANCH_REF = "refs/heads/branch"; 35 | private final String PROJECT_KEY = "projectkey"; 36 | private final String COMMIT = "commithash"; 37 | private final String url = "http://url"; 38 | private final String commitMsg = "changes to modify conf skipCI"; 39 | private final String committer = "admin"; 40 | private final Server projectServer = new Server("projecturl", null, "projectuser", 41 | "projecttoken", false, false); 42 | private Settings settings; 43 | private RefChange refChange; 44 | private MinimalRef minimalRef; 45 | private SettingsService settingsService; 46 | private Jenkins jenkins; 47 | private CommitService commitService; 48 | private Repository repository; 49 | private Project project; 50 | private ApplicationUser user; 51 | private Job.JobBuilder jobBuilder; 52 | List jobs; 53 | 54 | @Before 55 | public void setup() { 56 | settingsService = mock(SettingsService.class); 57 | commitService = mock(CommitService.class); 58 | Commit commit = mock(Commit.class); 59 | jenkins = mock(Jenkins.class); 60 | Person person = mock(Person.class); 61 | settings = mock(Settings.class); 62 | refChange = mock(RefChange.class); 63 | minimalRef = mock(MinimalRef.class); 64 | repository = mock(Repository.class); 65 | project = mock(Project.class); 66 | user = mock(ApplicationUser.class); 67 | 68 | when(refChange.getRef()).thenReturn(minimalRef); 69 | when(refChange.getToHash()).thenReturn(COMMIT); 70 | when(settingsService.getSettings(any())).thenReturn(settings); 71 | when(repository.getProject()).thenReturn(project); 72 | when(project.getKey()).thenReturn(PROJECT_KEY); 73 | when(jenkins.getJenkinsServer(eq(project.getKey()), any())).thenReturn(projectServer); 74 | when(refChange.getType()).thenReturn(RefChangeType.UPDATE); 75 | 76 | when(minimalRef.getId()).thenReturn(BRANCH_REF); 77 | jobBuilder = new Job.JobBuilder(1).jobName("").buildParameters("").branchRegex("") 78 | .pathRegex("").ignoreCommitMsg("").ignoreComitters(""); 79 | jobs = new ArrayList<>(); 80 | when(settingsService.getJobs(any())).thenReturn(jobs); 81 | when(commitService.getCommit(any())).thenReturn(commit); 82 | when(commit.getMessage()).thenReturn(commitMsg); 83 | when(commit.getAuthor()).thenReturn(person); 84 | when(person.getName()).thenReturn(committer); 85 | 86 | } 87 | 88 | @Test 89 | public void testBranchUpdatedAndTriggerIsPush() { 90 | Job job = jobBuilder.triggers(new String[] { "push" }).build(); 91 | jobs.add(job); 92 | PushHandler handler = new PushHandler(settingsService, jenkins, commitService, repository, 93 | refChange, url, user); 94 | PushHandler spyHandler = spy(handler); 95 | doNothing().when(spyHandler).triggerJenkins(any(), any()); 96 | spyHandler.run(); 97 | 98 | verify(spyHandler, times(1)).triggerJenkins(eq(job), any()); 99 | } 100 | 101 | @Test 102 | public void testIgnoreCommitMsgAndJobTriggerIsSkipped (){ 103 | jobBuilder = new Job.JobBuilder(2).jobName("").buildParameters("").branchRegex("") 104 | .pathRegex("").ignoreCommitMsg(".*skipCI.*").ignoreComitters(""); 105 | Job job = jobBuilder.triggers(new String[] { "push" }).build(); 106 | jobs.add(job); 107 | PushHandler handler = new PushHandler(settingsService, jenkins, commitService, repository, 108 | refChange, url, user); 109 | PushHandler spyHandler = spy(handler); 110 | spyHandler.run(); 111 | verify(spyHandler, times(0)).triggerJenkins(eq(job), any()); 112 | } 113 | 114 | @Test 115 | public void testIgnoreCommitMsgAndJobIsTriggered (){ 116 | jobBuilder = new Job.JobBuilder(2).jobName("").buildParameters("").branchRegex("") 117 | .pathRegex("").ignoreCommitMsg(".*kuku.*").ignoreComitters(""); 118 | Job job = jobBuilder.triggers(new String[] { "push" }).build(); 119 | jobs.add(job); 120 | PushHandler handler = new PushHandler(settingsService, jenkins, commitService, repository, 121 | refChange, url, user); 122 | PushHandler spyHandler = spy(handler); 123 | doNothing().when(spyHandler).triggerJenkins(any(), any()); 124 | spyHandler.run(); 125 | verify(spyHandler, times(1)).triggerJenkins(eq(job), any()); 126 | } 127 | 128 | @Test 129 | public void testIgnoreCommittersAndJobIsTriggered (){ 130 | jobBuilder = new Job.JobBuilder(2).jobName("").buildParameters("").branchRegex("") 131 | .pathRegex("").ignoreCommitMsg("").ignoreComitters("ci_user\ntest_user"); 132 | Job job = jobBuilder.triggers(new String[] { "push" }).build(); 133 | jobs.add(job); 134 | PushHandler handler = new PushHandler(settingsService, jenkins, commitService, repository, 135 | refChange, url, user); 136 | PushHandler spyHandler = spy(handler); 137 | doNothing().when(spyHandler).triggerJenkins(any(), any()); 138 | spyHandler.run(); 139 | verify(spyHandler, times(1)).triggerJenkins(eq(job), any()); 140 | } 141 | 142 | @Test 143 | public void testIgnoreCommittersAndJobTriggerIsSkipped (){ 144 | jobBuilder = new Job.JobBuilder(2).jobName("").buildParameters("").branchRegex("") 145 | .pathRegex("").ignoreCommitMsg("").ignoreComitters("ci_user\nadmin"); 146 | Job job = jobBuilder.triggers(new String[] { "push" }).build(); 147 | jobs.add(job); 148 | PushHandler handler = new PushHandler(settingsService, jenkins, commitService, repository, 149 | refChange, url, user); 150 | PushHandler spyHandler = spy(handler); 151 | doNothing().when(spyHandler).triggerJenkins(any(), any()); 152 | spyHandler.run(); 153 | verify(spyHandler, times(0)).triggerJenkins(eq(job), any()); 154 | } 155 | 156 | } -------------------------------------------------------------------------------- /src/test/java/com/kylenicholls/stash/parameterizedbuilds/eventHandlers/RefCreatedHandlerTest.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.eventHandlers; 2 | 3 | import com.atlassian.bitbucket.commit.CommitService; 4 | import com.atlassian.bitbucket.project.Project; 5 | import com.atlassian.bitbucket.repository.MinimalRef; 6 | import com.atlassian.bitbucket.repository.RefChange; 7 | import com.atlassian.bitbucket.repository.RefChangeType; 8 | import com.atlassian.bitbucket.repository.Repository; 9 | import com.atlassian.bitbucket.setting.Settings; 10 | import com.atlassian.bitbucket.user.ApplicationUser; 11 | import com.kylenicholls.stash.parameterizedbuilds.ciserver.Jenkins; 12 | import com.kylenicholls.stash.parameterizedbuilds.helper.SettingsService; 13 | import com.kylenicholls.stash.parameterizedbuilds.item.Job; 14 | import com.kylenicholls.stash.parameterizedbuilds.item.Server; 15 | import org.junit.Before; 16 | import org.junit.Test; 17 | 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | 21 | import static org.mockito.ArgumentMatchers.any; 22 | import static org.mockito.ArgumentMatchers.eq; 23 | import static org.mockito.Mockito.doNothing; 24 | import static org.mockito.Mockito.mock; 25 | import static org.mockito.Mockito.spy; 26 | import static org.mockito.Mockito.times; 27 | import static org.mockito.Mockito.verify; 28 | import static org.mockito.Mockito.when; 29 | 30 | public class RefCreatedHandlerTest { 31 | 32 | private final String BRANCH_REF = "refs/heads/branch"; 33 | private final String PROJECT_KEY = "projectkey"; 34 | private final String COMMIT = "commithash"; 35 | private final String url = "http://url"; 36 | private final Server projectServer = new Server("projecturl", null, "projectuser", 37 | "projecttoken", false, false); 38 | private Settings settings; 39 | private RefChange refChange; 40 | private MinimalRef minimalRef; 41 | private SettingsService settingsService; 42 | private Jenkins jenkins; 43 | private CommitService commitService; 44 | private Repository repository; 45 | private Project project; 46 | private ApplicationUser user; 47 | private Job.JobBuilder jobBuilder; 48 | List jobs; 49 | 50 | @Before 51 | public void setup() { 52 | settingsService = mock(SettingsService.class); 53 | commitService = mock(CommitService.class); 54 | jenkins = mock(Jenkins.class); 55 | 56 | settings = mock(Settings.class); 57 | refChange = mock(RefChange.class); 58 | minimalRef = mock(MinimalRef.class); 59 | repository = mock(Repository.class); 60 | project = mock(Project.class); 61 | user = mock(ApplicationUser.class); 62 | 63 | when(refChange.getRef()).thenReturn(minimalRef); 64 | when(refChange.getToHash()).thenReturn(COMMIT); 65 | when(settingsService.getSettings(any())).thenReturn(settings); 66 | when(repository.getProject()).thenReturn(project); 67 | when(project.getKey()).thenReturn(PROJECT_KEY); 68 | when(jenkins.getJenkinsServer(eq(project.getKey()), any())).thenReturn(projectServer); 69 | when(refChange.getType()).thenReturn(RefChangeType.ADD); 70 | 71 | when(minimalRef.getId()).thenReturn(BRANCH_REF); 72 | jobBuilder = new Job.JobBuilder(1).jobName("").buildParameters("").branchRegex("") 73 | .pathRegex(""); 74 | jobs = new ArrayList<>(); 75 | when(settingsService.getJobs(any())).thenReturn(jobs); 76 | } 77 | 78 | @Test 79 | public void testBranchAddedAndTriggerIsAdd() { 80 | Job job = jobBuilder.triggers(new String[] { "add" }).build(); 81 | jobs.add(job); 82 | RefCreatedHandler handler = new RefCreatedHandler(settingsService, jenkins, commitService, 83 | repository, refChange, url, user); 84 | RefCreatedHandler spyHandler = spy(handler); 85 | doNothing().when(spyHandler).triggerJenkins(any(), any()); 86 | spyHandler.run(); 87 | 88 | verify(spyHandler, times(1)).triggerJenkins(eq(job), any()); 89 | } 90 | 91 | } -------------------------------------------------------------------------------- /src/test/java/com/kylenicholls/stash/parameterizedbuilds/eventHandlers/RefDeletedHandlerTest.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.eventHandlers; 2 | 3 | import com.atlassian.bitbucket.commit.CommitService; 4 | import com.atlassian.bitbucket.project.Project; 5 | import com.atlassian.bitbucket.repository.MinimalRef; 6 | import com.atlassian.bitbucket.repository.RefChange; 7 | import com.atlassian.bitbucket.repository.RefChangeType; 8 | import com.atlassian.bitbucket.repository.Repository; 9 | import com.atlassian.bitbucket.setting.Settings; 10 | import com.atlassian.bitbucket.user.ApplicationUser; 11 | import com.kylenicholls.stash.parameterizedbuilds.ciserver.Jenkins; 12 | import com.kylenicholls.stash.parameterizedbuilds.helper.SettingsService; 13 | import com.kylenicholls.stash.parameterizedbuilds.item.Job; 14 | import com.kylenicholls.stash.parameterizedbuilds.item.Server; 15 | import org.junit.Before; 16 | import org.junit.Test; 17 | 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | 21 | import static org.mockito.ArgumentMatchers.any; 22 | import static org.mockito.ArgumentMatchers.eq; 23 | import static org.mockito.Mockito.doNothing; 24 | import static org.mockito.Mockito.mock; 25 | import static org.mockito.Mockito.spy; 26 | import static org.mockito.Mockito.times; 27 | import static org.mockito.Mockito.verify; 28 | import static org.mockito.Mockito.when; 29 | 30 | public class RefDeletedHandlerTest { 31 | 32 | private final String BRANCH_REF = "refs/heads/branch"; 33 | private final String PROJECT_KEY = "projectkey"; 34 | private final String COMMIT = "commithash"; 35 | private final String url = "http://url"; 36 | private final Server projectServer = new Server("projecturl", null, "projectuser", 37 | "projecttoken", false, false); 38 | private Settings settings; 39 | private RefChange refChange; 40 | private MinimalRef minimalRef; 41 | private SettingsService settingsService; 42 | private Jenkins jenkins; 43 | private CommitService commitService; 44 | private Repository repository; 45 | private Project project; 46 | private ApplicationUser user; 47 | private Job.JobBuilder jobBuilder; 48 | List jobs; 49 | 50 | @Before 51 | public void setup() { 52 | settingsService = mock(SettingsService.class); 53 | commitService = mock(CommitService.class); 54 | jenkins = mock(Jenkins.class); 55 | 56 | settings = mock(Settings.class); 57 | refChange = mock(RefChange.class); 58 | minimalRef = mock(MinimalRef.class); 59 | repository = mock(Repository.class); 60 | project = mock(Project.class); 61 | user = mock(ApplicationUser.class); 62 | 63 | when(refChange.getRef()).thenReturn(minimalRef); 64 | when(refChange.getToHash()).thenReturn(COMMIT); 65 | when(settingsService.getSettings(any())).thenReturn(settings); 66 | when(repository.getProject()).thenReturn(project); 67 | when(project.getKey()).thenReturn(PROJECT_KEY); 68 | when(jenkins.getJenkinsServer(eq(project.getKey()), any())).thenReturn(projectServer); 69 | when(refChange.getType()).thenReturn(RefChangeType.DELETE); 70 | 71 | when(minimalRef.getId()).thenReturn(BRANCH_REF); 72 | jobBuilder = new Job.JobBuilder(1).jobName("").buildParameters("").branchRegex("") 73 | .pathRegex(""); 74 | jobs = new ArrayList<>(); 75 | when(settingsService.getJobs(any())).thenReturn(jobs); 76 | } 77 | 78 | @Test 79 | public void testBranchDeletedAndTriggerIsDelete() { 80 | Job job = jobBuilder.triggers(new String[] { "delete" }).build(); 81 | jobs.add(job); 82 | RefDeletedHandler handler = new RefDeletedHandler(settingsService, jenkins, commitService, 83 | repository, refChange, url, user); 84 | RefDeletedHandler spyHandler = spy(handler); 85 | doNothing().when(spyHandler).triggerJenkins(any(), any()); 86 | spyHandler.run(); 87 | 88 | verify(spyHandler, times(1)).triggerJenkins(eq(job), any()); 89 | } 90 | 91 | } -------------------------------------------------------------------------------- /src/test/java/com/kylenicholls/stash/parameterizedbuilds/eventHandlers/TestEventFactory.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.eventHandlers; 2 | 3 | import com.atlassian.bitbucket.branch.automerge.AutomaticMergeEvent; 4 | import com.atlassian.bitbucket.commit.MinimalCommit; 5 | import com.atlassian.bitbucket.event.pull.PullRequestDeclinedEvent; 6 | import com.atlassian.bitbucket.event.pull.PullRequestDeletedEvent; 7 | import com.atlassian.bitbucket.event.pull.PullRequestMergedEvent; 8 | import com.atlassian.bitbucket.event.pull.PullRequestOpenedEvent; 9 | import com.atlassian.bitbucket.event.pull.PullRequestParticipantApprovedEvent; 10 | import com.atlassian.bitbucket.event.pull.PullRequestReopenedEvent; 11 | import com.atlassian.bitbucket.event.pull.PullRequestRescopedEvent; 12 | import com.atlassian.bitbucket.pull.PullRequest; 13 | import com.atlassian.bitbucket.pull.PullRequestParticipant; 14 | import com.atlassian.bitbucket.pull.PullRequestRef; 15 | import com.atlassian.bitbucket.repository.Repository; 16 | 17 | import static org.mockito.Mockito.mock; 18 | import static org.mockito.Mockito.when; 19 | 20 | public class TestEventFactory { 21 | 22 | private PullRequestOpenedEvent openedEvent; 23 | private PullRequestReopenedEvent reopenedEvent; 24 | private PullRequestRescopedEvent rescopedEvent; 25 | private PullRequestMergedEvent mergedEvent; 26 | private PullRequestDeclinedEvent declinedEvent; 27 | private PullRequestDeletedEvent deletedEvent; 28 | private AutomaticMergeEvent autoMergeEvent; 29 | private PullRequestParticipantApprovedEvent approvedEvent; 30 | 31 | public void setup(Repository repository){ 32 | PullRequest pullRequest = mock(PullRequest.class); 33 | PullRequestRef prFromRef = mock(PullRequestRef.class); 34 | PullRequestRef prToRef = mock(PullRequestRef.class); 35 | PullRequestParticipant author = mock(PullRequestParticipant.class); 36 | MinimalCommit mergeCommit = mock(MinimalCommit.class); 37 | 38 | openedEvent = mock(PullRequestOpenedEvent.class); 39 | reopenedEvent = mock(PullRequestReopenedEvent.class); 40 | rescopedEvent = mock(PullRequestRescopedEvent.class); 41 | mergedEvent = mock(PullRequestMergedEvent.class); 42 | autoMergeEvent = mock(AutomaticMergeEvent.class); 43 | declinedEvent = mock(PullRequestDeclinedEvent.class); 44 | deletedEvent = mock(PullRequestDeletedEvent.class); 45 | approvedEvent = mock(PullRequestParticipantApprovedEvent.class); 46 | 47 | String SOURCE_BRANCH = "sourcebranch"; 48 | String DEST_BRANCH = "destbranch"; 49 | String COMMIT = "commithash"; 50 | String newCommit = "newcommithash"; 51 | String PR_TITLE = "prtitle"; 52 | Long PR_ID = 15L; 53 | 54 | when(openedEvent.getPullRequest()).thenReturn(pullRequest); 55 | when(reopenedEvent.getPullRequest()).thenReturn(pullRequest); 56 | when(rescopedEvent.getPullRequest()).thenReturn(pullRequest); 57 | when(mergedEvent.getPullRequest()).thenReturn(pullRequest); 58 | when(mergedEvent.getCommit()).thenReturn(mergeCommit); 59 | when(autoMergeEvent.getRepository()).thenReturn(repository); 60 | when(declinedEvent.getPullRequest()).thenReturn(pullRequest); 61 | when(deletedEvent.getPullRequest()).thenReturn(pullRequest); 62 | when(approvedEvent.getPullRequest()).thenReturn(pullRequest); 63 | when(pullRequest.getFromRef()).thenReturn(prFromRef); 64 | when(pullRequest.getToRef()).thenReturn(prToRef); 65 | when(pullRequest.getAuthor()).thenReturn(author); 66 | when(pullRequest.getDescription()).thenReturn(null); 67 | when(pullRequest.getTitle()).thenReturn(PR_TITLE); 68 | when(pullRequest.getId()).thenReturn(PR_ID); 69 | when(prFromRef.getRepository()).thenReturn(repository); 70 | when(prFromRef.getDisplayId()).thenReturn(SOURCE_BRANCH); 71 | when(prFromRef.getLatestCommit()).thenReturn(COMMIT); 72 | when(prToRef.getDisplayId()).thenReturn(DEST_BRANCH); 73 | when(prToRef.getRepository()).thenReturn(repository); 74 | when(mergeCommit.getId()).thenReturn(newCommit); 75 | } 76 | 77 | public PullRequestMergedEvent getMockedMergeEvent(Repository repository){ 78 | setup(repository); 79 | return mergedEvent; 80 | } 81 | 82 | public PullRequestOpenedEvent getMockedOpenedEvent(Repository repository){ 83 | setup(repository); 84 | return openedEvent; 85 | } 86 | 87 | public PullRequestReopenedEvent getMockedReopenedEvent(Repository repository){ 88 | setup(repository); 89 | return reopenedEvent; 90 | } 91 | 92 | public PullRequestRescopedEvent getMockedRescopedEvent(Repository repository){ 93 | setup(repository); 94 | return rescopedEvent; 95 | } 96 | 97 | public PullRequestDeclinedEvent getMockedDeclinedEvent(Repository repository){ 98 | setup(repository); 99 | return declinedEvent; 100 | } 101 | 102 | public PullRequestDeletedEvent getMockedDeletedEvent(Repository repository){ 103 | setup(repository); 104 | return deletedEvent; 105 | } 106 | 107 | public PullRequestParticipantApprovedEvent getMockedApprovedEvent(Repository repository){ 108 | setup(repository); 109 | return approvedEvent; 110 | } 111 | 112 | public AutomaticMergeEvent getMockedAutoMergeEvent(Repository repository){ 113 | setup(repository); 114 | return autoMergeEvent; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/test/java/com/kylenicholls/stash/parameterizedbuilds/item/BitbucketVariableTest.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.item; 2 | 3 | import static org.mockito.Mockito.mock; 4 | import static org.mockito.Mockito.when; 5 | 6 | import org.junit.Test; 7 | 8 | public class BitbucketVariableTest { 9 | 10 | @Test 11 | public void testGet() { 12 | String expected = "test_val"; 13 | BitbucketVariable variable = new BitbucketVariable<>(() -> expected); 14 | assert variable.getOrCompute() == expected; 15 | } 16 | 17 | @Test 18 | public void testOnlyComputeOnce() { 19 | 20 | Object mock_val = mock(Object.class); 21 | when(mock_val.toString()).thenReturn("first_val").thenReturn("second_val"); 22 | BitbucketVariable variable = new BitbucketVariable<>(() -> mock_val.toString()); 23 | assert variable.getOrCompute() == "first_val"; 24 | assert variable.getOrCompute() == "first_val"; 25 | } 26 | 27 | } -------------------------------------------------------------------------------- /src/test/java/com/kylenicholls/stash/parameterizedbuilds/item/JenkinsResponseTest.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.item; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertFalse; 5 | import static org.junit.Assert.assertTrue; 6 | 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | import org.junit.Test; 11 | 12 | public class JenkinsResponseTest { 13 | @Test 14 | public void testCreateDefaultResponse() { 15 | JenkinsResponse actual = new JenkinsResponse.JenkinsMessage().build(); 16 | 17 | assertFalse(actual.getError()); 18 | assertFalse(actual.getPrompt()); 19 | assertEquals("", actual.getMessageText()); 20 | Map expected = new HashMap<>(); 21 | expected.put("error", false); 22 | expected.put("prompt", false); 23 | expected.put("messageText", ""); 24 | assertEquals(expected, actual.getMessage()); 25 | } 26 | 27 | @Test 28 | public void testCreateResponse() { 29 | JenkinsResponse actual = new JenkinsResponse.JenkinsMessage().error(true).prompt(true) 30 | .messageText("message").build(); 31 | 32 | assertTrue(actual.getError()); 33 | assertTrue(actual.getPrompt()); 34 | assertEquals("message", actual.getMessageText()); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/test/java/com/kylenicholls/stash/parameterizedbuilds/item/ServerTest.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.item; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | import org.junit.Test; 9 | 10 | public class ServerTest { 11 | @Test 12 | public void testCreateNewServer() { 13 | String baseUrl = "url"; 14 | String alias = "alias"; 15 | String user = "user"; 16 | String token = "token"; 17 | boolean altUrl = false; 18 | boolean csrfEnabled = false; 19 | Server actual = new Server(baseUrl, alias, user, token, altUrl, csrfEnabled); 20 | 21 | assertEquals(baseUrl, actual.getBaseUrl()); 22 | assertEquals(alias, actual.getAlias()); 23 | assertEquals(user, actual.getUser()); 24 | assertEquals(token, actual.getToken()); 25 | assertEquals(altUrl, actual.getAltUrl()); 26 | assertEquals(csrfEnabled, actual.getCsrfEnabled()); 27 | assertEquals(user + ":" + token, actual.getJoinedToken()); 28 | } 29 | 30 | @Test 31 | public void testCreateNewServerWithSlash() { 32 | String baseUrl = "url"; 33 | Server actual = new Server(baseUrl + "/", null, "", "", false, false); 34 | 35 | assertEquals(baseUrl, actual.getBaseUrl()); 36 | } 37 | 38 | @Test 39 | public void testCreateNewServerFromMap() { 40 | Map expected = new HashMap<>(); 41 | expected.put("baseUrl", "url"); 42 | expected.put("alias", "alias"); 43 | expected.put("user", "user"); 44 | expected.put("token", "token"); 45 | expected.put("altUrl", false); 46 | expected.put("csrfEnabled", false); 47 | Map actual = new Server(expected).asMap(); 48 | 49 | assertEquals(expected, actual); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/test/java/com/kylenicholls/stash/parameterizedbuilds/item/UserTokenTest.java: -------------------------------------------------------------------------------- 1 | package com.kylenicholls.stash.parameterizedbuilds.item; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import org.junit.Test; 6 | 7 | public class UserTokenTest { 8 | @Test 9 | public void testCreateNewUserToken() { 10 | String baseUrl = "url"; 11 | String alias = "alias"; 12 | String projectKey = "key"; 13 | String projectName = "name"; 14 | String userSlug = "user"; 15 | String token = "token"; 16 | UserToken actual = new UserToken(baseUrl, alias, projectKey, projectName, userSlug, token); 17 | 18 | assertEquals(baseUrl, actual.getBaseUrl()); 19 | assertEquals(alias, actual.getAlias()); 20 | assertEquals(projectKey, actual.getProjectKey()); 21 | assertEquals(projectName, actual.getProjectName()); 22 | assertEquals(userSlug, actual.getUserSlug()); 23 | assertEquals(token, actual.getToken()); 24 | } 25 | } 26 | --------------------------------------------------------------------------------