├── .gitreview ├── src ├── main │ ├── webapp │ │ └── icons │ │ │ ├── gerrit-logo-16x16.png │ │ │ ├── gerrit-logo-24x24.png │ │ │ ├── gerrit-logo-32x32.png │ │ │ └── gerrit-logo-48x48.png │ ├── resources │ │ ├── jenkins │ │ │ └── plugins │ │ │ │ └── gerrit │ │ │ │ ├── GerritSCMNavigator │ │ │ │ ├── config_en.properties │ │ │ │ ├── help-insecureHttps.html │ │ │ │ ├── help-serverUri.html │ │ │ │ ├── help-apiKey.html │ │ │ │ ├── help-credentialsId.html │ │ │ │ └── config.jelly │ │ │ │ ├── GerritSCMSource │ │ │ │ ├── config-detail_en.properties │ │ │ │ ├── help-insecureHttps.html │ │ │ │ ├── help-apiKey.html │ │ │ │ ├── help-credentialsId.html │ │ │ │ ├── help-remote.html │ │ │ │ └── config-detail.jelly │ │ │ │ ├── Messages.properties │ │ │ │ └── traits │ │ │ │ ├── FilterChecksTrait │ │ │ │ ├── help-queryString.html │ │ │ │ └── config.jelly │ │ │ │ ├── ChangeDiscoveryTrait │ │ │ │ ├── config.jelly │ │ │ │ └── help-queryString.html │ │ │ │ └── Messages.properties │ │ └── index.jelly │ └── java │ │ ├── jenkins │ │ └── plugins │ │ │ └── gerrit │ │ │ ├── RefUpdateProjectName.java │ │ │ ├── GerritProjectName.java │ │ │ ├── GerritWebHookCrumbExclusion.java │ │ │ ├── GerritSCMNavigatorContext.java │ │ │ ├── GerritSCMNavigatorRequest.java │ │ │ ├── PendingChecksFilter.java │ │ │ ├── NullPrintStream.java │ │ │ ├── GerritProjectEvent.java │ │ │ ├── FakeTaskListener.java │ │ │ ├── ChangeSCMRevision.java │ │ │ ├── GerritVersion.java │ │ │ ├── GerritChange.java │ │ │ ├── ProjectChanges.java │ │ │ ├── GerritSCMSourceBuilder.java │ │ │ ├── UsernamePasswordCredentialsProvider.java │ │ │ ├── GerritLogo.java │ │ │ ├── SSLNoVerifyCertificateManagerClientBuilderExtension.java │ │ │ ├── ChangeSCMHead.java │ │ │ ├── UserAgentClientBuilderExtension.java │ │ │ ├── traits │ │ │ ├── ChangeDiscoveryTrait.java │ │ │ └── FilterChecksTrait.java │ │ │ ├── PagedCodeProjectsRequest.java │ │ │ ├── GerritSCMSourceContext.java │ │ │ ├── GerritSCMSourceRequest.java │ │ │ ├── workflow │ │ │ ├── GerritCommentStep.java │ │ │ ├── GerritCheckStep.java │ │ │ └── GerritReviewStep.java │ │ │ ├── GerritURI.java │ │ │ └── GerritApiBuilder.java │ │ └── com │ │ └── google │ │ └── gerrit │ │ └── plugins │ │ └── checks │ │ ├── api │ │ ├── CheckablePatchSetInfo.java │ │ ├── BlockingCondition.java │ │ ├── CheckerInput.java │ │ ├── PendingChecksInfo.java │ │ ├── CheckerStatus.java │ │ ├── PendingCheckInfo.java │ │ ├── RerunInput.java │ │ ├── CheckerInfo.java │ │ ├── CheckState.java │ │ ├── CheckInput.java │ │ └── CheckInfo.java │ │ └── client │ │ ├── GerritChecksApi.java │ │ ├── UTCTimestampTypeAdapter.java │ │ ├── AbstractEndpoint.java │ │ ├── GerritChecksApiBuilder.java │ │ ├── PendingChecks.java │ │ ├── Checkers.java │ │ └── Checks.java └── test │ └── java │ ├── hudson │ └── util │ │ └── TestSecret.java │ └── jenkins │ └── plugins │ └── gerrit │ ├── AbstractGerritSCMSourceTest.java │ ├── ChangeSCMRevisionTest.java │ ├── GerritProjectEventTest.java │ ├── GerritChangeTest.java │ ├── GerritSCMNavigatorDescriptorTest.java │ ├── PagedCodeProjectsRequestTest.java │ ├── GerritApiBuilderTest.java │ ├── ChecksTest.java │ ├── GerritWebHookTest.java │ ├── GerritVersionTest.java │ ├── GerritMockServerRule.java │ ├── GerritURITest.java │ ├── PendingChecksFilterTests.java │ ├── workflow │ └── GerritCommentStepTest.java │ ├── GerritSCMNavigatorTest.java │ └── GerritEnvironmentContributorTest.java ├── BUILD.md ├── .gitignore └── Jenkinsfile /.gitreview: -------------------------------------------------------------------------------- 1 | [gerrit] 2 | host=review.gerrithub.io 3 | port=29418 4 | project=jenkinsci/gerrit-code-review-plugin.git 5 | -------------------------------------------------------------------------------- /src/main/webapp/icons/gerrit-logo-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jenkinsci/gerrit-code-review-plugin/HEAD/src/main/webapp/icons/gerrit-logo-16x16.png -------------------------------------------------------------------------------- /src/main/webapp/icons/gerrit-logo-24x24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jenkinsci/gerrit-code-review-plugin/HEAD/src/main/webapp/icons/gerrit-logo-24x24.png -------------------------------------------------------------------------------- /src/main/webapp/icons/gerrit-logo-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jenkinsci/gerrit-code-review-plugin/HEAD/src/main/webapp/icons/gerrit-logo-32x32.png -------------------------------------------------------------------------------- /src/main/webapp/icons/gerrit-logo-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jenkinsci/gerrit-code-review-plugin/HEAD/src/main/webapp/icons/gerrit-logo-48x48.png -------------------------------------------------------------------------------- /src/main/resources/jenkins/plugins/gerrit/GerritSCMNavigator/config_en.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jenkinsci/gerrit-code-review-plugin/HEAD/src/main/resources/jenkins/plugins/gerrit/GerritSCMNavigator/config_en.properties -------------------------------------------------------------------------------- /BUILD.md: -------------------------------------------------------------------------------- 1 | ## Building the Plugin 2 | 3 | ```bash 4 | $ java -version # Need Java 1.8, earlier versions are unsupported for build 5 | $ mvn -version # Need a modern maven version; maven 3.2.5 and 3.5.0 are known to work 6 | $ mvn clean install 7 | ``` 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | bin 3 | .classpath 4 | .settings 5 | .project 6 | *.iml 7 | *.ipr 8 | *.iws 9 | work 10 | nbactions.xml 11 | nb-configuration.xml 12 | release.properties 13 | pom.xml.releaseBackup 14 | .idea 15 | *.sublime-project 16 | *.sublime-workspace 17 | *.pem 18 | 19 | # vim 20 | *.swp 21 | Session.vim 22 | /nbproject/ 23 | .DS_Store 24 | -------------------------------------------------------------------------------- /src/main/java/jenkins/plugins/gerrit/RefUpdateProjectName.java: -------------------------------------------------------------------------------- 1 | package jenkins.plugins.gerrit; 2 | 3 | public class RefUpdateProjectName { 4 | String project; 5 | 6 | public RefUpdateProjectName(String project) { 7 | this.project = project; 8 | } 9 | 10 | @Override 11 | public String toString() { 12 | return project; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/resources/jenkins/plugins/gerrit/GerritSCMSource/config-detail_en.properties: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2018 GerritForge Ltd 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. 14 | 15 | Behaviours=Behaviours 16 | -------------------------------------------------------------------------------- /src/main/resources/jenkins/plugins/gerrit/GerritSCMSource/help-insecureHttps.html: -------------------------------------------------------------------------------- 1 | 16 | 17 |
18 | Perform insecure HTTPS negotiation. 19 |
20 | -------------------------------------------------------------------------------- /src/main/resources/jenkins/plugins/gerrit/GerritSCMNavigator/help-insecureHttps.html: -------------------------------------------------------------------------------- 1 | 16 | 17 |
18 | Perform insecure HTTPS negotiation. 19 |
20 | -------------------------------------------------------------------------------- /src/main/resources/jenkins/plugins/gerrit/GerritSCMNavigator/help-serverUri.html: -------------------------------------------------------------------------------- 1 | 16 | 17 |
18 | Specify the URL of this Gerrit server. 19 |
20 | -------------------------------------------------------------------------------- /src/main/resources/index.jelly: -------------------------------------------------------------------------------- 1 | 2 | 17 |
18 | This plugin integrates Gerrit Code Review with Jenkins. 19 |
20 | -------------------------------------------------------------------------------- /src/main/resources/jenkins/plugins/gerrit/GerritSCMNavigator/help-apiKey.html: -------------------------------------------------------------------------------- 1 | 16 | 17 |
18 | Gerrit web-hook apiKey used as additional security layer to prevent accidental SCM scans. 19 |
20 | -------------------------------------------------------------------------------- /src/main/resources/jenkins/plugins/gerrit/GerritSCMSource/help-apiKey.html: -------------------------------------------------------------------------------- 1 | 16 | 17 |
18 | Gerrit web-hook apiKey used as additional security layer to prevent accidental SCM scans. 19 |
20 | -------------------------------------------------------------------------------- /src/main/resources/jenkins/plugins/gerrit/GerritSCMSource/help-credentialsId.html: -------------------------------------------------------------------------------- 1 | 16 | 17 |
18 | Credentials used to scan branches and check out sources. 19 |
20 | -------------------------------------------------------------------------------- /src/main/resources/jenkins/plugins/gerrit/GerritSCMSource/help-remote.html: -------------------------------------------------------------------------------- 1 | 16 | 17 |
18 | Specify the URL of this remote repository. This uses the same syntax as your git clone command. 19 |
20 | -------------------------------------------------------------------------------- /src/main/resources/jenkins/plugins/gerrit/GerritSCMNavigator/help-credentialsId.html: -------------------------------------------------------------------------------- 1 | 16 | 17 |
18 | Credentials used to scan projects and branches then check out sources. 19 |
20 | -------------------------------------------------------------------------------- /src/main/resources/jenkins/plugins/gerrit/Messages.properties: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2018 GerritForge Ltd 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. 14 | 15 | GerritSCMSource.DisplayName=Gerrit 16 | GerritSCMSource.ChangeRequestCategory=Changes 17 | OriginPullRequestDiscoveryTrait.authorityDisplayName=Discover Changes 18 | ChangeSCMHead.Pronoun=Change 19 | GerritAvatar.IconDescription=Gerrit Repository -------------------------------------------------------------------------------- /src/main/resources/jenkins/plugins/gerrit/traits/FilterChecksTrait/help-queryString.html: -------------------------------------------------------------------------------- 1 | 16 | 17 |
18 | Provide a query string to search for pending checks. Depending on which mode was chosen, 19 | this either should be a checker-scheme or the UUID of a specific checker. 20 |
21 | -------------------------------------------------------------------------------- /src/test/java/hudson/util/TestSecret.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 GerritForge Ltd 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. 14 | 15 | package hudson.util; 16 | 17 | public class TestSecret { 18 | 19 | public static final String TEST_CLEARTEXT_SECRET = "secret-ef16dbe5fdb54"; 20 | 21 | public static Secret newTestSecret() { 22 | return new Secret(TEST_CLEARTEXT_SECRET); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/jenkins/plugins/gerrit/GerritProjectName.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 GerritForge Ltd 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. 14 | 15 | package jenkins.plugins.gerrit; 16 | 17 | public class GerritProjectName { 18 | public final String name; 19 | 20 | public GerritProjectName(String name) { 21 | this.name = name; 22 | } 23 | 24 | @Override 25 | public String toString() { 26 | return name; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- 1 | #!groovy 2 | 3 | def jenkinsVersions = [null, '2.60.1'] 4 | def jenkinsPlatforms = ['linux'] 5 | 6 | // Don't test plugin compatibility - exceeds 1 hour timeout 7 | // Allow failing tests to retry execution 8 | // buildPlugin(failFast: false) 9 | 10 | if (env.GERRIT_API_URL == null) { 11 | this.gerritComment = { dict -> } 12 | this.gerritReview = { dict -> } 13 | } 14 | 15 | // Test plugin compatbility to latest Jenkins LTS 16 | // Allow failing tests to retry execution 17 | try { 18 | gerritReview labels: [:], message: "Build started ${env.BUILD_URL}" 19 | buildPlugin(jenkinsVersions: jenkinsVersions, failFast: false, platforms: jenkinsPlatforms) 20 | if (currentBuild.result == 'UNSTABLE') { 21 | gerritReview labels: [Verified: 0], message: "Build is unstable, there are failed tests ${env.BUILD_URL}" 22 | } else { 23 | gerritReview labels: [Verified: +1], message: "Build succeeded ${env.BUILD_URL}" 24 | } 25 | } catch (e) { 26 | gerritReview labels: [Verified: -1], message: "Build failed ${env.BUILD_URL}" 27 | throw e 28 | } 29 | 30 | -------------------------------------------------------------------------------- /src/main/java/jenkins/plugins/gerrit/GerritWebHookCrumbExclusion.java: -------------------------------------------------------------------------------- 1 | package jenkins.plugins.gerrit; 2 | 3 | import hudson.Extension; 4 | import hudson.security.csrf.CrumbExclusion; 5 | import java.io.IOException; 6 | import javax.servlet.FilterChain; 7 | import javax.servlet.ServletException; 8 | import javax.servlet.http.HttpServletRequest; 9 | import javax.servlet.http.HttpServletResponse; 10 | 11 | @Extension 12 | public class GerritWebHookCrumbExclusion extends CrumbExclusion { 13 | 14 | @Override 15 | public boolean process(HttpServletRequest req, HttpServletResponse resp, FilterChain chain) 16 | throws IOException, ServletException { 17 | String pathInfo = req.getPathInfo(); 18 | if (pathInfo == null || pathInfo.isEmpty()) { 19 | return false; 20 | } 21 | pathInfo = pathInfo.endsWith("/") ? pathInfo : pathInfo + '/'; 22 | if (!pathInfo.equals(getExclusionPath())) { 23 | return false; 24 | } 25 | chain.doFilter(req, resp); 26 | return true; 27 | } 28 | 29 | public String getExclusionPath() { 30 | return "/" + GerritWebHook.URLNAME + "/"; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/resources/jenkins/plugins/gerrit/traits/ChangeDiscoveryTrait/config.jelly: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/main/resources/jenkins/plugins/gerrit/traits/Messages.properties: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2018 GerritForge Ltd 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. 14 | 15 | ChangeDiscoveryTrait.displayName=Discover open changes 16 | ChangeDiscoveryTrait.openChanges=Query for open changes 17 | ChangeDiscoveryTrait.pendingChecks=Query for pending checks 18 | FilterChecksTrait.displayName=Filter by Pending Checks 19 | FilterChecksTrait.checkerIdOperator=Query pending checks of a single checker by its ID 20 | FilterChecksTrait.schemeOperator=Query pending checks of multiple checkers having the same scheme 21 | -------------------------------------------------------------------------------- /src/main/resources/jenkins/plugins/gerrit/traits/FilterChecksTrait/config.jelly: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/main/java/com/google/gerrit/plugins/checks/api/CheckablePatchSetInfo.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 The Android Open Source Project 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. 14 | 15 | package com.google.gerrit.plugins.checks.api; 16 | 17 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 18 | 19 | /** REST API representation of a patch set for which checks are pending. */ 20 | @SuppressFBWarnings("UUF_UNUSED_PUBLIC_OR_PROTECTED_FIELD") 21 | public class CheckablePatchSetInfo { 22 | /** Repository name. */ 23 | public String repository; 24 | 25 | /** Change number. */ 26 | public int changeNumber; 27 | 28 | /** Patch set ID. */ 29 | public int patchSetId; 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/google/gerrit/plugins/checks/api/BlockingCondition.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 The Android Open Source Project 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. 14 | 15 | package com.google.gerrit.plugins.checks.api; 16 | 17 | import java.util.Set; 18 | 19 | /** 20 | * Conditions evaluated on a check in the context of a change that determine whether the check 21 | * blocks submission of a change. 22 | */ 23 | public enum BlockingCondition { 24 | /** Block submission unless all required checks on the change is passing. */ 25 | STATE_NOT_PASSING; 26 | 27 | public static Boolean isRequired(Set blocking) { 28 | return blocking.contains(STATE_NOT_PASSING); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/google/gerrit/plugins/checks/api/CheckerInput.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 The Android Open Source Project 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. 14 | 15 | package com.google.gerrit.plugins.checks.api; 16 | 17 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 18 | import java.util.Set; 19 | 20 | @SuppressFBWarnings( 21 | value = {"UUF_UNUSED_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD"}) 22 | public class CheckerInput { 23 | public String uuid; 24 | public String name; 25 | public String description; 26 | public String url; 27 | public String repository; 28 | public CheckerStatus status; 29 | public Set blocking; 30 | public String query; 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/google/gerrit/plugins/checks/api/PendingChecksInfo.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 The Android Open Source Project 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. 14 | 15 | package com.google.gerrit.plugins.checks.api; 16 | 17 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 18 | import java.util.Map; 19 | 20 | /** REST API representation of pending checks on patch set. */ 21 | @SuppressFBWarnings( 22 | value = {"UUF_UNUSED_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD"}) 23 | public class PendingChecksInfo { 24 | /** Patch set for which the checks are pending. */ 25 | public CheckablePatchSetInfo patchSet; 26 | 27 | /** Pending checks on the patch set by checker UUID. */ 28 | public Map pendingChecks; 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/jenkins/plugins/gerrit/GerritSCMNavigatorContext.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022 Réda Housni Alaoui 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. 14 | 15 | package jenkins.plugins.gerrit; 16 | 17 | import edu.umd.cs.findbugs.annotations.NonNull; 18 | import jenkins.scm.api.SCMNavigator; 19 | import jenkins.scm.api.SCMSourceObserver; 20 | import jenkins.scm.api.trait.SCMNavigatorContext; 21 | 22 | public class GerritSCMNavigatorContext 23 | extends SCMNavigatorContext { 24 | @NonNull 25 | @Override 26 | public GerritSCMNavigatorRequest newRequest( 27 | @NonNull SCMNavigator navigator, @NonNull SCMSourceObserver observer) { 28 | return new GerritSCMNavigatorRequest(navigator, this, observer); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/google/gerrit/plugins/checks/api/CheckerStatus.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 The Android Open Source Project 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. 14 | 15 | package com.google.gerrit.plugins.checks.api; 16 | 17 | /** 18 | * Status of a configured checker. 19 | * 20 | *

This status is a property of the checker's configuration; not to be confused with {@code 21 | * CheckState}, which is the state of an individual check performed by a checker against a specific 22 | * change. 23 | */ 24 | public enum CheckerStatus { 25 | /** The checker is enabled. */ 26 | ENABLED, 27 | 28 | /** 29 | * The checker is disabled, meaning its checks are not displayed alongside any changes, and the 30 | * results are not considered when determining submit requirements. 31 | */ 32 | DISABLED 33 | } 34 | -------------------------------------------------------------------------------- /src/main/resources/jenkins/plugins/gerrit/GerritSCMSource/config-detail.jelly: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/main/resources/jenkins/plugins/gerrit/GerritSCMNavigator/config.jelly: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/main/java/jenkins/plugins/gerrit/GerritSCMNavigatorRequest.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022 Réda Housni Alaoui 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. 14 | 15 | package jenkins.plugins.gerrit; 16 | 17 | import edu.umd.cs.findbugs.annotations.NonNull; 18 | import jenkins.scm.api.SCMNavigator; 19 | import jenkins.scm.api.SCMSourceObserver; 20 | import jenkins.scm.api.trait.SCMNavigatorContext; 21 | import jenkins.scm.api.trait.SCMNavigatorRequest; 22 | 23 | public class GerritSCMNavigatorRequest extends SCMNavigatorRequest { 24 | /** 25 | * Constructor. 26 | * 27 | * @param source the source. 28 | * @param context the context. 29 | * @param observer the observer. 30 | */ 31 | protected GerritSCMNavigatorRequest( 32 | @NonNull SCMNavigator source, 33 | @NonNull SCMNavigatorContext context, 34 | @NonNull SCMSourceObserver observer) { 35 | super(source, context, observer); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/jenkins/plugins/gerrit/PendingChecksFilter.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 SAP SE 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. 14 | 15 | package jenkins.plugins.gerrit; 16 | 17 | import java.io.IOException; 18 | import jenkins.scm.api.SCMHead; 19 | import jenkins.scm.api.trait.SCMHeadFilter; 20 | import jenkins.scm.api.trait.SCMSourceRequest; 21 | 22 | public class PendingChecksFilter extends SCMHeadFilter { 23 | 24 | @Override 25 | public boolean isExcluded(SCMSourceRequest request, SCMHead head) 26 | throws IOException, InterruptedException { 27 | if (head instanceof ChangeSCMHead) { 28 | return !((GerritSCMSourceRequest) request) 29 | .getPatchsetWithPendingChecks() 30 | .containsKey( 31 | String.format( 32 | "%d/%d", 33 | ((ChangeSCMHead) head).getChangeNumber(), 34 | ((ChangeSCMHead) head).getPatchSetNumber())); 35 | } 36 | return true; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/resources/jenkins/plugins/gerrit/traits/ChangeDiscoveryTrait/help-queryString.html: -------------------------------------------------------------------------------- 1 | 16 | 17 |

18 |

19 | Provide an additional query string to search for open changes. The status:open is 20 | implicitly added and does not need to be specified. 21 | See Gerrit 23 | search operators documentation 24 | for a detailed list of the supported search operators. 25 |

26 | 27 |

Examples:

28 |

29 |

    30 |
  • 31 |
    -is:wip
    32 | does not include work-in-progress changes 33 |
  • 34 |
  • 35 |
    is:private
    36 | includes private changes 37 |
  • 38 |
39 |

40 |
41 | -------------------------------------------------------------------------------- /src/main/java/jenkins/plugins/gerrit/NullPrintStream.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 GerritForge Ltd 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. 14 | 15 | package jenkins.plugins.gerrit; 16 | 17 | import java.io.IOException; 18 | import java.io.OutputStream; 19 | import java.io.PrintStream; 20 | import java.io.UnsupportedEncodingException; 21 | import java.nio.charset.StandardCharsets; 22 | 23 | public class NullPrintStream extends PrintStream { 24 | public static NullPrintStream INSTANCE = nullPrintStream(); 25 | 26 | public NullPrintStream() throws UnsupportedEncodingException { 27 | super( 28 | new OutputStream() { 29 | @Override 30 | public void write(int b) throws IOException { 31 | // Do nothing 32 | } 33 | }, 34 | true, 35 | StandardCharsets.UTF_8.toString()); 36 | } 37 | 38 | private static NullPrintStream nullPrintStream() { 39 | try { 40 | return new NullPrintStream(); 41 | } catch (UnsupportedEncodingException e) { 42 | // UTF_8 would always be supported 43 | return null; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/jenkins/plugins/gerrit/GerritProjectEvent.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 GerritForge Ltd 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. 14 | 15 | package jenkins.plugins.gerrit; 16 | 17 | public class GerritProjectEvent { 18 | public final GerritProjectName project; 19 | public final RefUpdateProjectName refUpdate; 20 | public final String type; 21 | 22 | public GerritProjectEvent( 23 | GerritProjectName project, RefUpdateProjectName refUpdate, String type) { 24 | this.project = project; 25 | this.refUpdate = refUpdate; 26 | this.type = type; 27 | } 28 | 29 | public String getProjectName() { 30 | if (project != null) { 31 | return project.name; 32 | } 33 | if (refUpdate != null) { 34 | return refUpdate.project; 35 | } 36 | return null; 37 | } 38 | 39 | @Override 40 | public String toString() { 41 | return "Gerrit event " 42 | + type 43 | + (getProjectName() != null ? (" on project " + getProjectName()) : ""); 44 | } 45 | 46 | public boolean matches(String remoteUrl) { 47 | return getProjectName() != null 48 | && remoteUrl.replaceFirst("\\.git$", "").endsWith(getProjectName()); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/jenkins/plugins/gerrit/FakeTaskListener.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 GerritForge Ltd 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. 14 | 15 | package jenkins.plugins.gerrit; 16 | 17 | import hudson.console.ConsoleNote; 18 | import hudson.model.TaskListener; 19 | import java.io.IOException; 20 | import java.io.PrintStream; 21 | import java.io.PrintWriter; 22 | 23 | public class FakeTaskListener implements TaskListener { 24 | 25 | public static final FakeTaskListener INSTANCE = new FakeTaskListener(); 26 | 27 | @Override 28 | public PrintStream getLogger() { 29 | return null; 30 | } 31 | 32 | @Override 33 | public void annotate(ConsoleNote consoleNote) throws IOException {} 34 | 35 | @Override 36 | public void hyperlink(String s, String s1) throws IOException {} 37 | 38 | @Override 39 | public PrintWriter error(String s) { 40 | return null; 41 | } 42 | 43 | @Override 44 | public PrintWriter error(String s, Object... objects) { 45 | return null; 46 | } 47 | 48 | @Override 49 | public PrintWriter fatalError(String s) { 50 | return null; 51 | } 52 | 53 | @Override 54 | public PrintWriter fatalError(String s, Object... objects) { 55 | return null; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/jenkins/plugins/gerrit/ChangeSCMRevision.java: -------------------------------------------------------------------------------- 1 | package jenkins.plugins.gerrit; 2 | 3 | import javax.annotation.Nonnull; 4 | import jenkins.plugins.git.AbstractGitSCMSource; 5 | import jenkins.scm.api.mixin.ChangeRequestSCMRevision; 6 | 7 | public class ChangeSCMRevision extends ChangeRequestSCMRevision { 8 | 9 | private static final long serialVersionUID = 1L; 10 | private final @Nonnull String patchsetHash; 11 | private final boolean isFilteredByPendingChecks; 12 | 13 | ChangeSCMRevision(@Nonnull ChangeSCMHead head, @Nonnull String patchsetHash) { 14 | super(head, new AbstractGitSCMSource.SCMRevisionImpl(head.getTarget(), patchsetHash)); 15 | this.patchsetHash = patchsetHash; 16 | this.isFilteredByPendingChecks = !head.getPendingCheckerUuids().isEmpty(); 17 | } 18 | 19 | /** 20 | * The commit hash of the head of the pull request branch. 21 | * 22 | * @return The commit hash of the head of the pull request branch 23 | */ 24 | @Nonnull 25 | public String getPatchsetHash() { 26 | return patchsetHash; 27 | } 28 | 29 | @Override 30 | public boolean equivalent(ChangeRequestSCMRevision o) { 31 | if (!(o instanceof ChangeSCMRevision)) { 32 | return false; 33 | } 34 | ChangeSCMRevision other = (ChangeSCMRevision) o; 35 | 36 | // Force a rebuild, if the job building this change already exists, but has a pending checks. 37 | // Only used, if the FilterChecksTrait is used. 38 | if (isFilteredByPendingChecks) { 39 | return false; 40 | } 41 | 42 | return getHead().equals(other.getHead()) && patchsetHash.equals(other.patchsetHash); 43 | } 44 | 45 | @Override 46 | public int _hashCode() { 47 | return patchsetHash.hashCode(); 48 | } 49 | 50 | @Override 51 | public String toString() { 52 | return patchsetHash; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/google/gerrit/plugins/checks/client/GerritChecksApi.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 The Android Open Source Project 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. 14 | 15 | package com.google.gerrit.plugins.checks.client; 16 | 17 | import java.io.IOException; 18 | import java.net.URISyntaxException; 19 | import org.apache.http.impl.client.CloseableHttpClient; 20 | import org.eclipse.jgit.transport.URIish; 21 | 22 | public class GerritChecksApi { 23 | 24 | private URIish gerritBaseUrl; 25 | private CloseableHttpClient client; 26 | private final boolean isAuthenticated; 27 | 28 | public GerritChecksApi( 29 | URIish gerritBaseUrl, CloseableHttpClient client, boolean isAuthenticated) { 30 | this.gerritBaseUrl = gerritBaseUrl; 31 | this.client = client; 32 | this.isAuthenticated = isAuthenticated; 33 | } 34 | 35 | public Checkers checkers() throws URISyntaxException { 36 | return new Checkers(gerritBaseUrl, client, isAuthenticated); 37 | } 38 | 39 | public Checks checks() throws URISyntaxException { 40 | return new Checks(gerritBaseUrl, client, isAuthenticated); 41 | } 42 | 43 | public PendingChecks pendingChecks() throws URISyntaxException { 44 | return new PendingChecks(gerritBaseUrl, client, isAuthenticated); 45 | } 46 | 47 | public void close() throws IOException { 48 | client.close(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/google/gerrit/plugins/checks/client/UTCTimestampTypeAdapter.java: -------------------------------------------------------------------------------- 1 | package com.google.gerrit.plugins.checks.client; 2 | 3 | import static java.time.ZoneOffset.UTC; 4 | 5 | import com.google.gson.JsonParseException; 6 | import com.google.gson.TypeAdapter; 7 | import com.google.gson.stream.JsonReader; 8 | import com.google.gson.stream.JsonToken; 9 | import com.google.gson.stream.JsonWriter; 10 | import java.io.IOException; 11 | import java.sql.Timestamp; 12 | import java.text.DateFormat; 13 | import java.text.ParseException; 14 | import java.text.SimpleDateFormat; 15 | import java.util.Calendar; 16 | import java.util.Locale; 17 | import java.util.TimeZone; 18 | 19 | /** 20 | * Format {@link java.sql.Timestamp} objects to JSON string representation compatible with the 21 | * Gerrit API. 22 | */ 23 | class UTCTimestampTypeAdapter extends TypeAdapter { 24 | private final DateFormat utcDateFormat; 25 | 26 | public UTCTimestampTypeAdapter() { 27 | super(); 28 | utcDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US); 29 | utcDateFormat.setTimeZone(TimeZone.getTimeZone(UTC)); 30 | } 31 | 32 | @Override 33 | public void write(JsonWriter out, Timestamp date) throws IOException { 34 | if (date == null) { 35 | out.nullValue(); 36 | } else { 37 | out.value(utcDateFormat.format(date)); 38 | } 39 | } 40 | 41 | @Override 42 | public Timestamp read(JsonReader in) throws IOException { 43 | if (in.peek() == JsonToken.NULL) { 44 | in.nextNull(); 45 | return null; 46 | } 47 | 48 | try { 49 | Calendar utcCalendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); 50 | utcCalendar.setTimeInMillis(utcDateFormat.parse(in.nextString()).getTime()); 51 | return Timestamp.from(utcCalendar.toInstant()); 52 | } catch (ParseException e) { 53 | throw new JsonParseException(e); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/google/gerrit/plugins/checks/api/PendingCheckInfo.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 The Android Open Source Project 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. 14 | 15 | package com.google.gerrit.plugins.checks.api; 16 | 17 | import com.google.common.base.MoreObjects; 18 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 19 | import java.util.Objects; 20 | 21 | /** 22 | * REST API representation of a pending check. 23 | * 24 | *

Checks are pending if they are in a non-final state and the external checker system intends to 25 | * post further updates on them. Which states these are depends on the external checker system, by 26 | * default we only consider checks in state {@link CheckState#NOT_STARTED} as pending. 27 | */ 28 | @SuppressFBWarnings("UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD") 29 | public class PendingCheckInfo { 30 | /** State of the check. */ 31 | public CheckState state; 32 | 33 | public PendingCheckInfo(CheckState state) { 34 | this.state = state; 35 | } 36 | 37 | @Override 38 | public int hashCode() { 39 | return Objects.hash(state); 40 | } 41 | 42 | @Override 43 | public boolean equals(Object obj) { 44 | if (!(obj instanceof PendingCheckInfo)) { 45 | return false; 46 | } 47 | PendingCheckInfo o = (PendingCheckInfo) obj; 48 | return Objects.equals(state, o.state); 49 | } 50 | 51 | @Override 52 | public String toString() { 53 | return MoreObjects.toStringHelper(this).add("state", state).toString(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/test/java/jenkins/plugins/gerrit/AbstractGerritSCMSourceTest.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2025 GerritForge Ltd 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. 14 | package jenkins.plugins.gerrit; 15 | 16 | import java.util.List; 17 | import jenkins.plugins.git.GitSCMBuilder; 18 | import jenkins.scm.api.SCMHead; 19 | import jenkins.scm.impl.mock.MockSCMRevision; 20 | import org.junit.Assert; 21 | import org.junit.Test; 22 | 23 | public class AbstractGerritSCMSourceTest { 24 | 25 | @Test 26 | public void providedRefSpecsAreNotRemoved() { 27 | 28 | SCMHead head = new SCMHead("52/47452/3"); 29 | GitSCMBuilder gitSCMBuilder = 30 | new GitSCMBuilder<>(head, new MockSCMRevision(head, "foo"), "origin", "secret"); 31 | gitSCMBuilder.withRefSpec("foo"); 32 | 33 | MyGerritSCMSource gerritSCMSource = new MyGerritSCMSource(); 34 | gerritSCMSource.decorate(gitSCMBuilder); 35 | 36 | List refSpecs = gitSCMBuilder.refSpecs(); 37 | Assert.assertEquals(2, refSpecs.size()); 38 | Assert.assertEquals("foo", refSpecs.get(0)); 39 | Assert.assertEquals("refs/changes/52/47452/3:refs/remotes/origin/52/47452/3", refSpecs.get(1)); 40 | } 41 | 42 | private static class MyGerritSCMSource extends AbstractGerritSCMSource { 43 | 44 | @Override 45 | public String getCredentialsId() { 46 | throw new UnsupportedOperationException(); 47 | } 48 | 49 | @Override 50 | public String getRemote() { 51 | throw new UnsupportedOperationException(); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/test/java/jenkins/plugins/gerrit/ChangeSCMRevisionTest.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 SAP SE 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. 14 | 15 | package jenkins.plugins.gerrit; 16 | 17 | import static org.junit.Assert.assertFalse; 18 | import static org.junit.Assert.assertTrue; 19 | 20 | import java.util.HashMap; 21 | import java.util.HashSet; 22 | import org.eclipse.jgit.lib.ObjectId; 23 | import org.junit.Before; 24 | import org.junit.Test; 25 | 26 | public class ChangeSCMRevisionTest { 27 | 28 | public HashMap refs; 29 | 30 | @Before 31 | public void setup() { 32 | refs = new HashMap(); 33 | refs.put("/refs/changes/11/11111/1", ObjectId.zeroId()); 34 | } 35 | 36 | @Test 37 | public void testEquivalentReturnsFalseIfPendingChecks() { 38 | HashSet checkerUuids = new HashSet(); 39 | checkerUuids.add("checker"); 40 | 41 | ChangeSCMRevision revision = 42 | new ChangeSCMRevision( 43 | new ChangeSCMHead(refs.entrySet().iterator().next(), "master", checkerUuids), "1234"); 44 | assertFalse(revision.equivalent(revision)); 45 | } 46 | 47 | @Test 48 | public void testEquivalentReturnsFalseIfNoPendingChecks() { 49 | HashSet checkerUuids = new HashSet(); 50 | 51 | ChangeSCMRevision revision = 52 | new ChangeSCMRevision( 53 | new ChangeSCMHead(refs.entrySet().iterator().next(), "master", checkerUuids), "1234"); 54 | assertTrue(revision.equivalent(revision)); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/jenkins/plugins/gerrit/GerritVersion.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 GerritForge Ltd 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. 14 | 15 | package jenkins.plugins.gerrit; 16 | 17 | import com.google.gerrit.extensions.api.GerritApi; 18 | import com.google.gerrit.extensions.restapi.RestApiException; 19 | import java.util.logging.Level; 20 | import java.util.logging.Logger; 21 | 22 | public class GerritVersion { 23 | private static final Logger LOGGER = Logger.getLogger(GerritVersion.class.getName()); 24 | 25 | public static boolean isVersionBelow215(GerritApi gerritApi) throws RestApiException { 26 | String version = gerritApi.config().server().getVersion(); 27 | 28 | if (version == null) { 29 | return false; 30 | } 31 | 32 | if (version.equals("<2.8")) { 33 | return true; 34 | } 35 | 36 | String[] versionSplit = version.split("\\."); 37 | if (versionSplit.length == 0) { 38 | return false; 39 | } 40 | try { 41 | int majorVersion = Integer.parseInt(versionSplit[0]); 42 | if (majorVersion < 2) { 43 | return true; 44 | } 45 | if (majorVersion > 2) { 46 | return false; 47 | } 48 | if (versionSplit.length < 2) { 49 | return true; 50 | } 51 | int minorVersion = Integer.parseInt(versionSplit[1]); 52 | if (minorVersion < 15) { 53 | return true; 54 | } 55 | return false; 56 | } catch (NumberFormatException e) { 57 | LOGGER.log(Level.SEVERE, "Unable to parse Gerrit version " + version, e); 58 | return false; 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/google/gerrit/plugins/checks/api/RerunInput.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 The Android Open Source Project 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. 14 | 15 | package com.google.gerrit.plugins.checks.api; 16 | 17 | import com.google.common.base.MoreObjects; 18 | import com.google.gerrit.common.Nullable; 19 | import com.google.gerrit.extensions.api.changes.NotifyHandling; 20 | import com.google.gerrit.extensions.api.changes.NotifyInfo; 21 | import com.google.gerrit.extensions.api.changes.RecipientType; 22 | import java.util.Map; 23 | import java.util.Objects; 24 | 25 | public class RerunInput { 26 | /** 27 | * Whom to send email notifications to when the combined check state changes due to rerunning this 28 | * check. 29 | */ 30 | @Nullable public NotifyHandling notify; 31 | /** Additional information about whom to notify regardless of the {@link #notify} setting. */ 32 | @Nullable public Map notifyDetails; 33 | 34 | @Override 35 | public boolean equals(Object o) { 36 | if (!(o instanceof RerunInput)) { 37 | return false; 38 | } 39 | RerunInput other = (RerunInput) o; 40 | return Objects.equals(other.notify, notify) 41 | && Objects.equals(other.notifyDetails, notifyDetails); 42 | } 43 | 44 | @Override 45 | public int hashCode() { 46 | return Objects.hash(notify, notifyDetails); 47 | } 48 | 49 | @Override 50 | public String toString() { 51 | return MoreObjects.toStringHelper(this) 52 | .add("notify", notify) 53 | .add("notifyDetails", notifyDetails) 54 | .toString(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/test/java/jenkins/plugins/gerrit/GerritProjectEventTest.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 GerritForge Ltd 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. 14 | 15 | package jenkins.plugins.gerrit; 16 | 17 | import static org.junit.Assert.*; 18 | 19 | import org.junit.Test; 20 | 21 | public class GerritProjectEventTest { 22 | 23 | private String eventType = "ref-updated"; 24 | private String projectName = "dotgit"; 25 | private String refUpdateProjectName = "dotgit"; 26 | private String remoteUrlWithoutDotGit = 27 | String.format("http://host/a/project/without/%s", projectName); 28 | private String remoteUrlWithDotGit = 29 | String.format("http://host/a/project/with/%s.git", projectName); 30 | 31 | @Test 32 | public void projectEventRemoteUrlMatchesEndsWithoutDotGit() { 33 | GerritProjectEvent e = 34 | new GerritProjectEvent( 35 | new GerritProjectName(projectName), 36 | new RefUpdateProjectName(refUpdateProjectName), 37 | eventType); 38 | assertTrue(e.matches(remoteUrlWithoutDotGit)); 39 | } 40 | 41 | @Test 42 | public void projectEventRemoteUrlMatchesEndsWithDotGit() { 43 | GerritProjectEvent e = 44 | new GerritProjectEvent( 45 | new GerritProjectName(projectName), 46 | new RefUpdateProjectName(refUpdateProjectName), 47 | eventType); 48 | assertTrue(e.matches(remoteUrlWithDotGit)); 49 | } 50 | 51 | @Test 52 | public void projectEventRemoteUrlNoMatchNullProjectName() { 53 | GerritProjectEvent e = new GerritProjectEvent(null, null, null); 54 | assertFalse(e.matches(remoteUrlWithoutDotGit)); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/test/java/jenkins/plugins/gerrit/GerritChangeTest.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 GerritForge Ltd 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. 14 | 15 | package jenkins.plugins.gerrit; 16 | 17 | import static org.junit.Assert.*; 18 | 19 | import java.util.Collections; 20 | import java.util.HashMap; 21 | import java.util.Map; 22 | import org.junit.Test; 23 | 24 | public class GerritChangeTest { 25 | 26 | @Test 27 | public void testNone() throws Exception { 28 | GerritChange change = new GerritChange(Collections.emptyMap(), System.out); 29 | assertFalse(change.valid()); 30 | } 31 | 32 | @Test 33 | public void testGerritCodeReviewPlugin() throws Exception { 34 | Integer changeId = 1234; 35 | Integer revision = 1; 36 | Map env = new HashMap<>(); 37 | env.put("BRANCH_NAME", String.format("%02d/%d/%d", changeId % 100, changeId, revision)); 38 | GerritChange change = new GerritChange(env, System.out); 39 | assertTrue(change.valid()); 40 | assertEquals(changeId, change.getChangeId()); 41 | assertEquals(revision, change.getRevision()); 42 | } 43 | 44 | @Test 45 | public void testGerritTriggerPlugin() throws Exception { 46 | Integer changeId = 1234; 47 | Integer revision = 1; 48 | Map env = new HashMap<>(); 49 | env.put("GERRIT_CHANGE_NUMBER", changeId.toString()); 50 | env.put("GERRIT_PATCHSET_NUMBER", revision.toString()); 51 | GerritChange change = new GerritChange(env, System.out); 52 | assertTrue(change.valid()); 53 | assertEquals(changeId, change.getChangeId()); 54 | assertEquals(revision, change.getRevision()); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/jenkins/plugins/gerrit/GerritChange.java: -------------------------------------------------------------------------------- 1 | package jenkins.plugins.gerrit; 2 | 3 | import hudson.EnvVars; 4 | import hudson.model.TaskListener; 5 | import java.io.IOException; 6 | import java.io.PrintStream; 7 | import java.util.Map; 8 | import java.util.regex.Matcher; 9 | import java.util.regex.Pattern; 10 | import org.apache.commons.lang.StringUtils; 11 | import org.jenkinsci.plugins.workflow.steps.StepContext; 12 | 13 | public class GerritChange { 14 | 15 | public static final Pattern BRANCH_PATTERN = 16 | Pattern.compile("[0-9][0-9]/(?[0-9]+)/(?[0-9]+)"); 17 | 18 | private Integer changeId = null; 19 | private Integer revision = null; 20 | 21 | private String project; 22 | 23 | public GerritChange(Map env, PrintStream logger) 24 | throws IOException, InterruptedException { 25 | 26 | project = env.get("GERRIT_PROJECT"); 27 | if (StringUtils.isNotEmpty(env.get("GERRIT_CHANGE_NUMBER"))) { 28 | changeId = Integer.parseInt(env.get("GERRIT_CHANGE_NUMBER")); 29 | revision = Integer.parseInt(env.get("GERRIT_PATCHSET_NUMBER")); 30 | } else { 31 | if (StringUtils.isNotEmpty(env.get("BRANCH_NAME"))) { 32 | Matcher matcher = BRANCH_PATTERN.matcher(env.get("BRANCH_NAME")); 33 | if (matcher.matches()) { 34 | changeId = Integer.parseInt(matcher.group("changeId")); 35 | revision = Integer.parseInt(matcher.group("revision")); 36 | } 37 | } 38 | } 39 | if (changeId == null) { 40 | if (logger != null) { 41 | logger.println( 42 | "Gerrit Review is disabled, invalid reference at BRANCH_NAME or GERRIT_CHANGE_NUMBER/GERRIT_PATCHSET_NUMBER"); 43 | } 44 | } 45 | } 46 | 47 | public GerritChange(StepContext context) throws IOException, InterruptedException { 48 | this(context.get(EnvVars.class), context.get(TaskListener.class).getLogger()); 49 | } 50 | 51 | public boolean valid() { 52 | return changeId != null; 53 | } 54 | 55 | public Integer getChangeId() { 56 | return changeId; 57 | } 58 | 59 | public Integer getRevision() { 60 | return revision; 61 | } 62 | 63 | public String getProject() { 64 | return project; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/jenkins/plugins/gerrit/ProjectChanges.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 GerritForge Ltd 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. 14 | 15 | package jenkins.plugins.gerrit; 16 | 17 | import com.google.gerrit.extensions.api.GerritApi; 18 | import com.google.gerrit.extensions.client.ListChangesOption; 19 | import com.google.gerrit.extensions.common.ChangeInfo; 20 | import com.google.gerrit.extensions.restapi.RestApiException; 21 | import java.util.EnumSet; 22 | import java.util.Optional; 23 | import java.util.logging.Logger; 24 | 25 | class ProjectChanges { 26 | private static final Logger LOGGER = Logger.getLogger(ProjectChanges.class.getName()); 27 | 28 | private final GerritApi gerritApi; 29 | 30 | ProjectChanges(GerritApi gerritApi) { 31 | this.gerritApi = gerritApi; 32 | } 33 | 34 | public Optional get(int changeNumber, String projectName) { 35 | try { 36 | EnumSet options = EnumSet.allOf(ListChangesOption.class); 37 | options.remove(ListChangesOption.CHECK); 38 | 39 | if (GerritVersion.isVersionBelow215(gerritApi)) { 40 | options.remove(ListChangesOption.TRACKING_IDS); 41 | options.remove(ListChangesOption.SKIP_MERGEABLE); 42 | 43 | return Optional.ofNullable(gerritApi.changes().id(changeNumber).get(options)); 44 | } 45 | return Optional.ofNullable(gerritApi.changes().id(projectName, changeNumber).get(options)); 46 | } catch (RestApiException e) { 47 | LOGGER.severe( 48 | String.format("Unable to retrieve change %d project %s", changeNumber, projectName)); 49 | LOGGER.throwing(ProjectChanges.class.getName(), "get", e); 50 | return Optional.empty(); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/jenkins/plugins/gerrit/GerritSCMSourceBuilder.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022 Réda Housni Alaoui 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. 14 | 15 | package jenkins.plugins.gerrit; 16 | 17 | import edu.umd.cs.findbugs.annotations.NonNull; 18 | import java.net.URISyntaxException; 19 | import javax.annotation.CheckForNull; 20 | import jenkins.scm.api.trait.SCMSourceBuilder; 21 | import org.eclipse.jgit.transport.URIish; 22 | 23 | public class GerritSCMSourceBuilder 24 | extends SCMSourceBuilder { 25 | 26 | private final String id; 27 | private final GerritURI gerritURI; 28 | private final boolean insecureHttps; 29 | @CheckForNull private final String credentialsId; 30 | 31 | public GerritSCMSourceBuilder( 32 | String id, 33 | @NonNull String projectName, 34 | GerritURI gerritURI, 35 | boolean insecureHttps, 36 | @CheckForNull String credentialsId) { 37 | super(GerritSCMSource.class, projectName); 38 | this.id = id; 39 | this.gerritURI = gerritURI; 40 | this.insecureHttps = insecureHttps; 41 | this.credentialsId = credentialsId; 42 | } 43 | 44 | @NonNull 45 | @Override 46 | public GerritSCMSource build() { 47 | URIish projectUri; 48 | try { 49 | projectUri = gerritURI.setProject(projectName()); 50 | } catch (URISyntaxException e) { 51 | throw new IllegalStateException(e); 52 | } 53 | 54 | GerritSCMSource scmSource = new GerritSCMSource(projectUri.toString()); 55 | scmSource.setId(id); 56 | scmSource.setCredentialsId(credentialsId); 57 | scmSource.setInsecureHttps(insecureHttps); 58 | scmSource.setTraits(traits()); 59 | return scmSource; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/jenkins/plugins/gerrit/UsernamePasswordCredentialsProvider.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 GerritForge Ltd 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. 14 | 15 | package jenkins.plugins.gerrit; 16 | 17 | import com.cloudbees.plugins.credentials.common.StandardUsernameCredentials; 18 | import org.eclipse.jgit.transport.CredentialItem; 19 | import org.eclipse.jgit.transport.URIish; 20 | import org.jenkinsci.plugins.gitclient.trilead.SmartCredentialsProvider; 21 | 22 | public class UsernamePasswordCredentialsProvider extends SmartCredentialsProvider { 23 | private final StandardUsernameCredentials credentials; 24 | 25 | public static class UsernamePassword { 26 | public final String username; 27 | public final String password; 28 | 29 | public UsernamePassword(String username, String password) { 30 | this.username = username; 31 | this.password = password; 32 | } 33 | } 34 | 35 | public UsernamePasswordCredentialsProvider(StandardUsernameCredentials credentials) { 36 | super(FakeTaskListener.INSTANCE); 37 | 38 | this.credentials = credentials; 39 | } 40 | 41 | public UsernamePassword getUsernamePassword(URIish uri) { 42 | addDefaultCredentials(credentials); 43 | 44 | String username = uri.getUser(); 45 | String password = uri.getPass(); 46 | 47 | CredentialItem.Username u = new CredentialItem.Username(); 48 | CredentialItem.Password p = new CredentialItem.Password(); 49 | 50 | if (supports(u, p) && get(uri, u, p)) { 51 | username = u.getValue(); 52 | char[] v = p.getValue(); 53 | password = (v == null) ? null : new String(p.getValue()); 54 | p.clear(); 55 | } 56 | 57 | return new UsernamePassword(username, password); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/test/java/jenkins/plugins/gerrit/GerritSCMNavigatorDescriptorTest.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022 Réda Housni Alaoui 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. 14 | 15 | package jenkins.plugins.gerrit; 16 | 17 | import java.util.Collections; 18 | import jenkins.branch.OrganizationFolder; 19 | import jenkins.plugins.git.AbstractGitSCMSource; 20 | import jenkins.plugins.git.traits.RefSpecsSCMSourceTrait; 21 | import org.junit.Rule; 22 | import org.junit.Test; 23 | import org.jvnet.hudson.test.JenkinsRule; 24 | 25 | public class GerritSCMNavigatorDescriptorTest { 26 | 27 | @Rule public JenkinsRule j = new JenkinsRule(); 28 | 29 | @Test 30 | public void testConfigRoundtrip() throws Exception { 31 | OrganizationFolder organizationFolder = j.createProject(OrganizationFolder.class); 32 | organizationFolder.getSCMNavigators().add(new GerritSCMNavigator()); 33 | organizationFolder = j.configRoundtrip(organizationFolder); 34 | 35 | j.assertEqualDataBoundBeans( 36 | new GerritSCMNavigator(), organizationFolder.getSCMNavigators().get(0)); 37 | } 38 | 39 | @Test 40 | public void testConfigRoundTripWithCustomValues() throws Exception { 41 | OrganizationFolder organizationFolder = j.createProject(OrganizationFolder.class); 42 | 43 | GerritSCMNavigator genuineNavigator = 44 | new GerritSCMNavigator( 45 | "https://gerrit.example.org", 46 | true, 47 | "my-credentials-id", 48 | Collections.singletonList( 49 | new RefSpecsSCMSourceTrait(AbstractGitSCMSource.REF_SPEC_DEFAULT))); 50 | 51 | organizationFolder.getSCMNavigators().add(genuineNavigator); 52 | organizationFolder = j.configRoundtrip(organizationFolder); 53 | 54 | j.assertEqualDataBoundBeans(genuineNavigator, organizationFolder.getSCMNavigators().get(0)); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/google/gerrit/plugins/checks/api/CheckerInfo.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 The Android Open Source Project 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. 14 | 15 | package com.google.gerrit.plugins.checks.api; 16 | 17 | import com.google.common.base.MoreObjects; 18 | import java.sql.Timestamp; 19 | import java.util.Objects; 20 | import java.util.Set; 21 | 22 | public class CheckerInfo { 23 | public String uuid; 24 | public String name; 25 | public String description; 26 | public String url; 27 | public String repository; 28 | public CheckerStatus status; 29 | public Set blocking; 30 | public String query; 31 | public Timestamp created; 32 | public Timestamp updated; 33 | 34 | @Override 35 | public int hashCode() { 36 | return Objects.hash( 37 | uuid, name, description, url, repository, status, blocking, query, created, updated); 38 | } 39 | 40 | @Override 41 | public boolean equals(Object obj) { 42 | if (!(obj instanceof CheckerInfo)) { 43 | return false; 44 | } 45 | CheckerInfo o = (CheckerInfo) obj; 46 | return Objects.equals(uuid, o.uuid) 47 | && Objects.equals(name, o.name) 48 | && Objects.equals(description, o.description) 49 | && Objects.equals(url, o.url) 50 | && Objects.equals(repository, o.repository) 51 | && Objects.equals(status, o.status) 52 | && Objects.equals(blocking, o.blocking) 53 | && Objects.equals(query, o.query) 54 | && Objects.equals(created, o.created) 55 | && Objects.equals(updated, o.updated); 56 | } 57 | 58 | @Override 59 | public String toString() { 60 | return MoreObjects.toStringHelper(this) 61 | .add("uuid", uuid) 62 | .add("name", name) 63 | .add("description", description) 64 | .add("repository", repository) 65 | .add("url", url) 66 | .add("status", status) 67 | .add("blockingConditions", blocking) 68 | .add("query", query) 69 | .add("created", created) 70 | .add("updated", updated) 71 | .toString(); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/com/google/gerrit/plugins/checks/api/CheckState.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 The Android Open Source Project 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. 14 | 15 | package com.google.gerrit.plugins.checks.api; 16 | 17 | /** 18 | * State of a single check on a change. 19 | * 20 | *

This state applies to a single check; for the aggregated state associated with a change, see 21 | * CombinedCheckState. 22 | * 23 | *

Ordering is not significant in this class, but for consistency's sake the ordering matches 24 | * {@code CombinedCheckState} where applicable. 25 | */ 26 | public enum CheckState { 27 | /** 28 | * The check terminated and failed. 29 | * 30 | *

Failure may include the following cases: 31 | * 32 | *

    33 | *
  • The check completed normally and found a problem with the code in the change. 34 | *
  • The check failed to start. 35 | *
  • The check started, but failed for some reason not directly related to the code, such as a 36 | * setup failure in the checker. 37 | *
38 | */ 39 | FAILED(false), 40 | 41 | /** The check is relevant to the change, but the checker has not started work. */ 42 | NOT_STARTED(true), 43 | 44 | /** 45 | * The checker has acknowledged that it has work to do on the change, and will start work in the 46 | * future. 47 | */ 48 | SCHEDULED(true), 49 | 50 | /** The checker is currently running the check. */ 51 | RUNNING(true), 52 | 53 | /** The check terminated and succeeded. */ 54 | SUCCESSFUL(false), 55 | 56 | /** The check is not relevant for the change. */ 57 | NOT_RELEVANT(false); 58 | 59 | private final boolean inProgress; 60 | 61 | CheckState(boolean inProgress) { 62 | this.inProgress = inProgress; 63 | } 64 | 65 | /** 66 | * Returns whether the state represents an in-progress state. 67 | * 68 | *

A check is in progress if the checker is relevant to the change, and the checker system has 69 | * not yet completed the check. 70 | * 71 | * @return whether the state represents an in-progress state. 72 | */ 73 | public boolean isInProgress() { 74 | return inProgress; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/jenkins/plugins/gerrit/GerritLogo.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 GerritForge Ltd 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. 14 | 15 | package jenkins.plugins.gerrit; 16 | 17 | import jenkins.scm.api.metadata.AvatarMetadataAction; 18 | import org.jenkins.ui.icon.Icon; 19 | import org.jenkins.ui.icon.IconSet; 20 | 21 | /** Gerrit Code Review Logo */ 22 | public class GerritLogo extends AvatarMetadataAction { 23 | public static final String ICONS_PREFIX = "plugin/gerrit-code-review/icons/"; 24 | public static final String ICON_CLASS_NAME = "icon-gerrit-logo"; 25 | 26 | static { 27 | IconSet.icons.addIcon( 28 | new Icon( 29 | "icon-gerrit-logo icon-sm", 30 | ICONS_PREFIX + "gerrit-logo-16x16.png", 31 | Icon.ICON_SMALL_STYLE)); 32 | IconSet.icons.addIcon( 33 | new Icon( 34 | "icon-gerrit-logo icon-md", 35 | ICONS_PREFIX + "gerrit-logo-24x24.png", 36 | Icon.ICON_MEDIUM_STYLE)); 37 | IconSet.icons.addIcon( 38 | new Icon( 39 | "icon-gerrit-logo icon-lg", 40 | ICONS_PREFIX + "/gerrit-logo-32x32.png", 41 | Icon.ICON_LARGE_STYLE)); 42 | IconSet.icons.addIcon( 43 | new Icon( 44 | "icon-gerrit-logo icon-xlg", 45 | ICONS_PREFIX + "/gerrit-logo-48x48.png", 46 | Icon.ICON_XLARGE_STYLE)); 47 | } 48 | 49 | /** {@inheritDoc} */ 50 | @Override 51 | public String getAvatarIconClassName() { 52 | return ICON_CLASS_NAME; 53 | } 54 | 55 | /** {@inheritDoc} */ 56 | @Override 57 | public String getAvatarDescription() { 58 | return Messages.GerritAvatar_IconDescription(); 59 | } 60 | 61 | /** {@inheritDoc} */ 62 | @Override 63 | public boolean equals(Object o) { 64 | if (this == o) { 65 | return true; 66 | } 67 | if (o == null || getClass() != o.getClass()) { 68 | return false; 69 | } 70 | return true; 71 | } 72 | 73 | /** {@inheritDoc} */ 74 | @Override 75 | public int hashCode() { 76 | return 0; 77 | } 78 | 79 | /** {@inheritDoc} */ 80 | @Override 81 | public String toString() { 82 | return "GerritLogo{}"; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/test/java/jenkins/plugins/gerrit/PagedCodeProjectsRequestTest.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022 Réda Housni Alaoui 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. 14 | 15 | package jenkins.plugins.gerrit; 16 | 17 | import static org.junit.Assert.assertEquals; 18 | 19 | import com.google.gerrit.extensions.common.ProjectInfo; 20 | import java.net.URISyntaxException; 21 | import java.util.List; 22 | import java.util.stream.Collectors; 23 | import java.util.stream.StreamSupport; 24 | import org.junit.Before; 25 | import org.junit.Rule; 26 | import org.junit.Test; 27 | 28 | public class PagedCodeProjectsRequestTest { 29 | 30 | private static final int CHUNK_MAX_SIZE = 4; 31 | 32 | @Rule public GerritMockServerRule g = new GerritMockServerRule(this); 33 | 34 | private PagedCodeProjectsRequest request; 35 | 36 | @Before 37 | public void beforeEach() throws URISyntaxException { 38 | request = 39 | new PagedCodeProjectsRequest( 40 | new GerritApiBuilder().gerritApiUrl(g.getUrl()).build(), CHUNK_MAX_SIZE); 41 | } 42 | 43 | @Test 44 | public void testSinglePartialChunk() { 45 | test(CHUNK_MAX_SIZE - 1); 46 | } 47 | 48 | @Test 49 | public void testSingleFullChunk() { 50 | test(CHUNK_MAX_SIZE); 51 | } 52 | 53 | @Test 54 | public void testAFullChunkAndAPartialOne() { 55 | test(CHUNK_MAX_SIZE + 1); 56 | } 57 | 58 | @Test 59 | public void test2FullChunksAndAPartialOne() { 60 | test(2 * CHUNK_MAX_SIZE + 1); 61 | } 62 | 63 | private void test(int numberOfProjects) { 64 | for (int i = 1; i <= numberOfProjects; i++) { 65 | g.addProject(createProject(String.valueOf(i))); 66 | } 67 | 68 | List collectedProjectIds = 69 | StreamSupport.stream(request.spliterator(), false) 70 | .map(projectInfo -> projectInfo.id) 71 | .collect(Collectors.toList()); 72 | List projectRepositoryIds = 73 | g.getProjectRepository() 74 | .stream() 75 | .map(projectInfo -> projectInfo.id) 76 | .collect(Collectors.toList()); 77 | 78 | assertEquals(projectRepositoryIds, collectedProjectIds); 79 | } 80 | 81 | private ProjectInfo createProject(String name) { 82 | ProjectInfo projectInfo = new ProjectInfo(); 83 | projectInfo.id = name; 84 | return projectInfo; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/com/google/gerrit/plugins/checks/client/AbstractEndpoint.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 The Android Open Source Project 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. 14 | 15 | package com.google.gerrit.plugins.checks.client; 16 | 17 | import com.google.gson.FieldNamingPolicy; 18 | import com.google.gson.Gson; 19 | import com.google.gson.GsonBuilder; 20 | import java.lang.reflect.Type; 21 | import java.net.URISyntaxException; 22 | import java.sql.Timestamp; 23 | import org.apache.http.client.utils.URIBuilder; 24 | import org.apache.http.impl.client.CloseableHttpClient; 25 | import org.eclipse.jgit.transport.URIish; 26 | 27 | abstract class AbstractEndpoint { 28 | 29 | private static final String AUTH_PREFIX = "/a/"; 30 | 31 | private final URIish gerritBaseUrl; 32 | private final boolean isAuthentcated; 33 | protected final CloseableHttpClient client; 34 | protected final URIBuilder uriBuilder; 35 | 36 | protected AbstractEndpoint( 37 | URIish gerritBaseUrl, CloseableHttpClient client, boolean isAuthenticated) 38 | throws URISyntaxException { 39 | this.isAuthentcated = isAuthenticated; 40 | this.gerritBaseUrl = gerritBaseUrl; 41 | this.client = client; 42 | this.uriBuilder = new URIBuilder(gerritBaseUrl.toASCIIString()); 43 | } 44 | 45 | protected String getPrefix() { 46 | return isAuthentcated ? AUTH_PREFIX : "/"; 47 | } 48 | 49 | protected URIish getGerritBaseUrl() { 50 | return gerritBaseUrl; 51 | } 52 | 53 | public static class JsonBodyParser { 54 | private static final String JSON_PREFIX = ")]}'"; 55 | 56 | public static T parseResponse(String json, Type type) { 57 | json = removeJsonPrefix(json); 58 | return newGson().fromJson(json, type); 59 | } 60 | 61 | public static String createRequestBody(Object object, Type type) { 62 | return newGson().toJson(object, type); 63 | } 64 | 65 | private static Gson newGson() { 66 | return new GsonBuilder() 67 | .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) 68 | .setDateFormat("yyyy-MM-dd HH:mm:ss.SSS") 69 | .registerTypeAdapter(Timestamp.class, new UTCTimestampTypeAdapter()) 70 | .create(); 71 | } 72 | 73 | private static String removeJsonPrefix(String json) { 74 | if (json.startsWith(JSON_PREFIX)) { 75 | return json.split("\n", 2)[1]; 76 | } 77 | return json; 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/jenkins/plugins/gerrit/SSLNoVerifyCertificateManagerClientBuilderExtension.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 GerritForge Ltd 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. 14 | 15 | package jenkins.plugins.gerrit; 16 | 17 | import com.urswolfer.gerrit.client.rest.GerritAuthData; 18 | import com.urswolfer.gerrit.client.rest.http.HttpClientBuilderExtension; 19 | import java.security.SecureRandom; 20 | import java.security.cert.CertificateException; 21 | import javax.net.ssl.*; 22 | import org.apache.http.impl.client.HttpClientBuilder; 23 | 24 | public class SSLNoVerifyCertificateManagerClientBuilderExtension 25 | extends HttpClientBuilderExtension { 26 | private static SSLContext trustAnyX509Certificate = null; 27 | private static HostnameVerifier acceptAnyX509Hostname = null; 28 | 29 | static { 30 | try { 31 | trustAnyX509Certificate = SSLContext.getInstance("SSL"); 32 | 33 | trustAnyX509Certificate.init( 34 | null, 35 | new TrustManager[] { 36 | new X509TrustManager() { 37 | @Override 38 | public void checkClientTrusted( 39 | java.security.cert.X509Certificate[] x509Certificates, String s) 40 | throws CertificateException {} 41 | 42 | @Override 43 | public void checkServerTrusted( 44 | java.security.cert.X509Certificate[] x509Certificates, String s) 45 | throws CertificateException {} 46 | 47 | @Override 48 | public java.security.cert.X509Certificate[] getAcceptedIssuers() { 49 | return new java.security.cert.X509Certificate[0]; 50 | } 51 | } 52 | }, 53 | new SecureRandom()); 54 | 55 | acceptAnyX509Hostname = 56 | new HostnameVerifier() { 57 | @Override 58 | public boolean verify(String s, SSLSession sslSession) { 59 | return true; 60 | } 61 | }; 62 | } catch (Exception e) { 63 | System.err.println("Unable to initialize SSLContext for accepting any X.509 certificate"); 64 | e.printStackTrace(); 65 | } 66 | } 67 | 68 | public static final SSLNoVerifyCertificateManagerClientBuilderExtension INSTANCE = 69 | new SSLNoVerifyCertificateManagerClientBuilderExtension(); 70 | 71 | @Override 72 | public HttpClientBuilder extend(HttpClientBuilder httpClientBuilder, GerritAuthData authData) { 73 | HttpClientBuilder builder = super.extend(httpClientBuilder, authData); 74 | builder.setSSLContext(trustAnyX509Certificate); 75 | builder.setSSLHostnameVerifier(acceptAnyX509Hostname); 76 | return builder; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/test/java/jenkins/plugins/gerrit/GerritApiBuilderTest.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 GerritForge Ltd 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. 14 | 15 | package jenkins.plugins.gerrit; 16 | 17 | import static org.junit.Assert.*; 18 | 19 | import com.google.gerrit.extensions.api.GerritApi; 20 | import com.google.gerrit.plugins.checks.client.GerritChecksApi; 21 | import java.net.URISyntaxException; 22 | import org.junit.*; 23 | import org.junit.Rule; 24 | import org.mockserver.junit.MockServerRule; 25 | 26 | public class GerritApiBuilderTest { 27 | 28 | @Rule public MockServerRule g = new MockServerRule(this); 29 | 30 | @Test 31 | public void testShouldReturnValidGerritApiWithoutCredentials() throws URISyntaxException { 32 | GerritApi restApi = getGerritApiBuilderWithUri().credentials(null, null).build(); 33 | assertNotNull(restApi); 34 | } 35 | 36 | @Test 37 | public void testShouldReturnValidGerritChecksApiWithoutCredentials() throws URISyntaxException { 38 | GerritChecksApi restApi = getGerritApiBuilderWithUri().credentials(null, null).buildChecksApi(); 39 | assertNotNull(restApi); 40 | } 41 | 42 | private GerritApiBuilder getGerritApiBuilderWithUri() throws URISyntaxException { 43 | return new GerritApiBuilder().gerritApiUrl("http://gerrit.mycompany.com/a/project"); 44 | } 45 | 46 | @Test 47 | public void testShouldReturnNullGerritApiWithoutCredentials() throws URISyntaxException { 48 | assertNull(getGerritApiBuilderWithUri().requireAuthentication().build()); 49 | } 50 | 51 | @Test 52 | public void testShouldReturnNullGerritChecksApiWithoutCredentials() throws URISyntaxException { 53 | assertNull(getGerritApiBuilderWithUri().requireAuthentication().buildChecksApi()); 54 | } 55 | 56 | @Test 57 | public void testShouldReturnNullGerritApiWithNullCredentials() throws URISyntaxException { 58 | assertNull( 59 | getGerritApiBuilderWithUri().credentials(null, null).requireAuthentication().build()); 60 | } 61 | 62 | @Test 63 | public void testShouldReturnNullGerritChecksApiWithNullCredentials() throws URISyntaxException { 64 | assertNull( 65 | getGerritApiBuilderWithUri() 66 | .credentials(null, null) 67 | .requireAuthentication() 68 | .buildChecksApi()); 69 | } 70 | 71 | @Test 72 | public void testShouldReturnNullGerritApiWithEmptyCredentials() throws URISyntaxException { 73 | assertNull(getGerritApiBuilderWithUri().credentials("", "").requireAuthentication().build()); 74 | } 75 | 76 | @Test 77 | public void testShouldReturnNullGerritChecksApiWithEmptyCredentials() throws URISyntaxException { 78 | assertNull( 79 | getGerritApiBuilderWithUri().credentials("", "").requireAuthentication().buildChecksApi()); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/test/java/jenkins/plugins/gerrit/ChecksTest.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 GerritForge Ltd 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. 14 | 15 | package jenkins.plugins.gerrit; 16 | 17 | import static org.junit.Assert.assertEquals; 18 | import static org.junit.Assert.assertNotNull; 19 | 20 | import com.google.gerrit.plugins.checks.api.CheckInfo; 21 | import com.google.gerrit.plugins.checks.api.CheckState; 22 | import com.google.gerrit.plugins.checks.client.Checks; 23 | import com.google.gson.reflect.TypeToken; 24 | import java.text.ParseException; 25 | import java.text.SimpleDateFormat; 26 | import java.util.Date; 27 | import java.util.TimeZone; 28 | import org.junit.Test; 29 | 30 | public class ChecksTest { 31 | 32 | @Test 33 | public void shouldDeserializeCheckInfo() throws ParseException { 34 | String testRepo = "test-repo"; 35 | int changeNumber = 1; 36 | int patchSetId = 2; 37 | String checkerUuid = "test:my-checker"; 38 | CheckState state = CheckState.NOT_STARTED; 39 | String url = "https://foo.corp.com/test-checker/results/123"; 40 | SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); 41 | dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); 42 | 43 | Date created = dateFormat.parse("2021-09-05 10:11:12.5678"); 44 | Date updated = dateFormat.parse("2021-09-06 11:12:13.6789"); 45 | 46 | String checksJsonResponse = 47 | String.format( 48 | "{\n" 49 | + " \"repository\": \"%s\",\n" 50 | + " \"change_number\": %d,\n" 51 | + " \"patch_set_id\": %d,\n" 52 | + " \"checker_uuid\": \"%s\",\n" 53 | + " \"state\": \"%s\",\n" 54 | + " \"url\": \"%s\",\n" 55 | + " \"created\": \"%s\",\n" 56 | + " \"updated\": \"%s\"\n" 57 | + " }", 58 | testRepo, 59 | changeNumber, 60 | patchSetId, 61 | checkerUuid, 62 | state, 63 | url, 64 | dateFormat.format(created), 65 | dateFormat.format(updated)); 66 | 67 | CheckInfo checkInfo = 68 | Checks.JsonBodyParser.parseResponse( 69 | checksJsonResponse, new TypeToken() {}.getType()); 70 | 71 | assertNotNull(checkInfo); 72 | assertEquals(testRepo, checkInfo.repository); 73 | assertEquals(changeNumber, checkInfo.changeNumber); 74 | assertEquals(patchSetId, checkInfo.patchSetId); 75 | assertEquals(checkerUuid, checkInfo.checkerUuid); 76 | assertEquals(state, checkInfo.state); 77 | assertEquals(url, checkInfo.url); 78 | assertEquals(created, checkInfo.created); 79 | assertEquals(updated, checkInfo.updated); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/com/google/gerrit/plugins/checks/api/CheckInput.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 The Android Open Source Project 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. 14 | 15 | package com.google.gerrit.plugins.checks.api; 16 | 17 | import com.google.common.base.MoreObjects; 18 | import com.google.gerrit.common.Nullable; 19 | import com.google.gerrit.extensions.api.changes.NotifyHandling; 20 | import com.google.gerrit.extensions.api.changes.NotifyInfo; 21 | import com.google.gerrit.extensions.api.changes.RecipientType; 22 | import java.sql.Timestamp; 23 | import java.util.Map; 24 | import java.util.Objects; 25 | 26 | /** Input to create or update a Check. */ 27 | public class CheckInput { 28 | /** UUID of the checker. */ 29 | @Nullable public String checkerUuid; 30 | /** State of the check. */ 31 | @Nullable public CheckState state; 32 | /** Short message explaining the check state. */ 33 | @Nullable public String message; 34 | /** Fully qualified URL to detailed result on the Checker's service. */ 35 | @Nullable public String url; 36 | /** Date/Time at which the checker started processing this check. */ 37 | @Nullable public Timestamp started; 38 | /** Date/Time at which the checker finished processing this check. */ 39 | @Nullable public Timestamp finished; 40 | /** 41 | * Whom to send email notifications to when the combined check state changes due to posting this 42 | * check. 43 | */ 44 | @Nullable public NotifyHandling notify; 45 | /** Additional information about whom to notify regardless of the {@link #notify} setting. */ 46 | @Nullable public Map notifyDetails; 47 | 48 | @Override 49 | public boolean equals(Object o) { 50 | if (!(o instanceof CheckInput)) { 51 | return false; 52 | } 53 | CheckInput other = (CheckInput) o; 54 | return Objects.equals(other.checkerUuid, checkerUuid) 55 | && Objects.equals(other.state, state) 56 | && Objects.equals(other.message, message) 57 | && Objects.equals(other.url, url) 58 | && Objects.equals(other.started, started) 59 | && Objects.equals(other.finished, finished) 60 | && Objects.equals(other.notify, notify) 61 | && Objects.equals(other.notifyDetails, notifyDetails); 62 | } 63 | 64 | @Override 65 | public int hashCode() { 66 | return Objects.hash(checkerUuid, state, message, url, started, finished, notify, notifyDetails); 67 | } 68 | 69 | @Override 70 | public String toString() { 71 | return MoreObjects.toStringHelper(this) 72 | .add("checkerUuid", checkerUuid) 73 | .add("state", state) 74 | .add("message", message) 75 | .add("url", url) 76 | .add("started", started) 77 | .add("finished", finished) 78 | .add("notify", notify) 79 | .add("notifyDetails", notifyDetails) 80 | .toString(); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/jenkins/plugins/gerrit/ChangeSCMHead.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 GerritForge Ltd 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. 14 | 15 | package jenkins.plugins.gerrit; 16 | 17 | import java.util.Map; 18 | import java.util.Set; 19 | import java.util.logging.Logger; 20 | import javax.annotation.Nonnull; 21 | import jenkins.scm.api.SCMHead; 22 | import jenkins.scm.api.mixin.ChangeRequestCheckoutStrategy; 23 | import jenkins.scm.api.mixin.ChangeRequestSCMHead2; 24 | import org.eclipse.jgit.lib.ObjectId; 25 | 26 | /** Head corresponding to a change. */ 27 | public class ChangeSCMHead extends SCMHead implements ChangeRequestSCMHead2 { 28 | 29 | private static final Logger LOGGER = Logger.getLogger(ChangeSCMHead.class.getName()); 30 | 31 | private static final long serialVersionUID = 1; 32 | 33 | private final int changeNumber; 34 | 35 | private final int patchset; 36 | 37 | private final String rev; 38 | 39 | private final Set pendingCheckerUuids; 40 | 41 | ChangeSCMHead( 42 | Map.Entry ref, String branchName, Set pendingCheckerUuids) { 43 | super(branchName); 44 | changeNumber = parseChangeNumber(ref); 45 | patchset = parsePatchset(ref); 46 | rev = ref.getValue().name(); 47 | this.pendingCheckerUuids = pendingCheckerUuids; 48 | } 49 | 50 | private static int parseChangeNumber(Map.Entry ref) { 51 | return parseIntPart(ref, 3); 52 | } 53 | 54 | private static int parsePatchset(Map.Entry ref) { 55 | return parseIntPart(ref, 4); 56 | } 57 | 58 | private static int parseIntPart(Map.Entry ref, int index) { 59 | String[] changeParts = ref.getKey().split("/"); 60 | return Integer.parseInt(changeParts[index]); 61 | } 62 | 63 | /** {@inheritDoc} */ 64 | @Override 65 | public String getPronoun() { 66 | return Messages.ChangeSCMHead_Pronoun(); 67 | } 68 | 69 | /** {@inheritDoc} */ 70 | @Nonnull 71 | @Override 72 | public ChangeRequestCheckoutStrategy getCheckoutStrategy() { 73 | return ChangeRequestCheckoutStrategy.HEAD; 74 | } 75 | 76 | @Nonnull 77 | @Override 78 | public String getOriginName() { 79 | return getName(); 80 | } 81 | 82 | /** {@inheritDoc} */ 83 | @Nonnull 84 | @Override 85 | public String getId() { 86 | return "C-" + changeNumber + "/" + patchset; 87 | } 88 | 89 | public int getChangeNumber() { 90 | return changeNumber; 91 | } 92 | 93 | public int getPatchSetNumber() { 94 | return patchset; 95 | } 96 | 97 | public String getRev() { 98 | return rev; 99 | } 100 | 101 | public Set getPendingCheckerUuids() { 102 | return pendingCheckerUuids; 103 | } 104 | 105 | @Nonnull 106 | @Override 107 | public SCMHead getTarget() { 108 | return new SCMHead(getName()); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/main/java/jenkins/plugins/gerrit/UserAgentClientBuilderExtension.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 GerritForge Ltd 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. 14 | 15 | package jenkins.plugins.gerrit; 16 | 17 | import com.urswolfer.gerrit.client.rest.GerritAuthData; 18 | import com.urswolfer.gerrit.client.rest.http.HttpClientBuilderExtension; 19 | import java.io.IOException; 20 | import java.io.InputStream; 21 | import java.net.URL; 22 | import java.util.Optional; 23 | import java.util.Properties; 24 | import java.util.logging.Level; 25 | import java.util.logging.Logger; 26 | import org.apache.http.*; 27 | import org.apache.http.impl.client.HttpClientBuilder; 28 | import org.apache.http.protocol.HttpContext; 29 | 30 | class UserAgentClientBuilderExtension extends HttpClientBuilderExtension { 31 | static final HttpClientBuilderExtension INSTANCE = new UserAgentClientBuilderExtension(); 32 | private static final Logger LOGGER = 33 | Logger.getLogger(UserAgentClientBuilderExtension.class.getName()); 34 | 35 | private static String pluginVersion; 36 | 37 | @Override 38 | public HttpClientBuilder extend(HttpClientBuilder httpClientBuilder, GerritAuthData authData) { 39 | HttpClientBuilder builder = super.extend(httpClientBuilder, authData); 40 | httpClientBuilder.addInterceptorLast(new UserAgentHttpRequestInterceptor()); 41 | return builder; 42 | } 43 | 44 | private static class UserAgentHttpRequestInterceptor implements HttpRequestInterceptor { 45 | public void process(final HttpRequest request, final HttpContext context) 46 | throws HttpException, IOException { 47 | Header existingUserAgent = request.getFirstHeader(HttpHeaders.USER_AGENT); 48 | String userAgent = String.format("gerrit-code-review-plugin/%s", getCurrentVersion()); 49 | userAgent += " using " + existingUserAgent.getValue(); 50 | request.setHeader(HttpHeaders.USER_AGENT, userAgent); 51 | } 52 | } 53 | 54 | private static String getCurrentVersion() { 55 | if (pluginVersion != null) { 56 | return pluginVersion; 57 | } 58 | 59 | ClassLoader pluginClassLoader = UserAgentClientBuilderExtension.class.getClassLoader(); 60 | Optional jarProperties = 61 | Optional.ofNullable( 62 | pluginClassLoader.getResource( 63 | "META-INF/maven/org.jenkins-ci.plugins/gerrit-code-review/pom.properties")) 64 | .map(UserAgentClientBuilderExtension::loadProperties); 65 | return jarProperties 66 | .flatMap(p -> Optional.ofNullable((String) p.get("version"))) 67 | .orElse("(dev)"); 68 | } 69 | 70 | private static Properties loadProperties(URL url) { 71 | Properties properties = new Properties(); 72 | try { 73 | try (InputStream is = url.openStream()) { 74 | properties.load(is); 75 | } 76 | return properties; 77 | } catch (IOException ioe) { 78 | LOGGER.log(Level.WARNING, "Unable to extract the current plugin version", ioe); 79 | return properties; 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/test/java/jenkins/plugins/gerrit/GerritWebHookTest.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 GerritForge Ltd 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. 14 | 15 | package jenkins.plugins.gerrit; 16 | 17 | import static org.junit.Assert.assertEquals; 18 | import static org.junit.Assert.assertFalse; 19 | import static org.junit.Assert.assertTrue; 20 | import static org.mockito.Mockito.mock; 21 | import static org.mockito.Mockito.when; 22 | 23 | import com.google.gson.JsonParseException; 24 | import java.io.ByteArrayInputStream; 25 | import java.io.IOException; 26 | import java.nio.charset.StandardCharsets; 27 | import java.util.Optional; 28 | import javax.servlet.ReadListener; 29 | import javax.servlet.ServletInputStream; 30 | import javax.servlet.http.HttpServletRequest; 31 | import org.junit.Test; 32 | 33 | public class GerritWebHookTest { 34 | String testRepoName = "testrepo"; 35 | GerritWebHook webHook = new GerritWebHook(); 36 | 37 | @Test 38 | public void shouldExtractHttpPostBodyWhenLengthIsUnknown() throws Exception { 39 | byte[] gerritEventBody = 40 | "{\"project\":{\"name\":\"testrepo\"}, \"type\":\"ref-updated\"}" 41 | .getBytes(StandardCharsets.UTF_8); 42 | Optional projectEvent = 43 | webHook.getBody(getInMemoryServletRequest(gerritEventBody)); 44 | 45 | assertTrue(projectEvent.isPresent()); 46 | assertEquals(testRepoName, projectEvent.get().project.name); 47 | } 48 | 49 | @Test 50 | public void shouldIngoreNotInterestingEvents() throws Exception { 51 | assertFalse( 52 | webHook 53 | .getBody(getInMemoryServletRequest("{\"type\": \"dont-care\"}".getBytes())) 54 | .isPresent()); 55 | } 56 | 57 | @Test(expected = JsonParseException.class) 58 | public void shouldThrowExceptionForInvalidJsonEvents() throws Exception { 59 | webHook.getBody(getInMemoryServletRequest("this-is-invalid-JSON".getBytes())); 60 | } 61 | 62 | private HttpServletRequest getInMemoryServletRequest(byte[] body) throws IOException { 63 | int gerritEventBodySize = body.length; 64 | ByteArrayInputStream bodyInputStream = new ByteArrayInputStream(body); 65 | HttpServletRequest mockedServletRequest = mock(HttpServletRequest.class); 66 | when(mockedServletRequest.getContentLength()).thenReturn(-1); 67 | when(mockedServletRequest.getInputStream()) 68 | .thenReturn( 69 | new ServletInputStream() { 70 | private int readIdx = 0; 71 | 72 | @Override 73 | public boolean isFinished() { 74 | return readIdx >= gerritEventBodySize; 75 | } 76 | 77 | @Override 78 | public boolean isReady() { 79 | return true; 80 | } 81 | 82 | @Override 83 | public void setReadListener(ReadListener readListener) {} 84 | 85 | public int read() throws IOException { 86 | return bodyInputStream.read(); 87 | } 88 | }); 89 | return mockedServletRequest; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/com/google/gerrit/plugins/checks/client/GerritChecksApiBuilder.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 The Android Open Source Project 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. 14 | 15 | package com.google.gerrit.plugins.checks.client; 16 | 17 | import java.security.KeyManagementException; 18 | import java.security.KeyStoreException; 19 | import java.security.NoSuchAlgorithmException; 20 | import java.security.cert.CertificateException; 21 | import java.security.cert.X509Certificate; 22 | import java.util.logging.Level; 23 | import java.util.logging.Logger; 24 | import javax.net.ssl.SSLContext; 25 | import org.apache.http.auth.AuthScope; 26 | import org.apache.http.auth.UsernamePasswordCredentials; 27 | import org.apache.http.client.CredentialsProvider; 28 | import org.apache.http.conn.ssl.NoopHostnameVerifier; 29 | import org.apache.http.conn.ssl.SSLConnectionSocketFactory; 30 | import org.apache.http.impl.client.BasicCredentialsProvider; 31 | import org.apache.http.impl.client.HttpClientBuilder; 32 | import org.apache.http.ssl.SSLContextBuilder; 33 | import org.apache.http.ssl.TrustStrategy; 34 | import org.eclipse.jgit.transport.URIish; 35 | 36 | public class GerritChecksApiBuilder { 37 | public static final Logger LOGGER = Logger.getLogger(GerritChecksApiBuilder.class.getName()); 38 | 39 | private URIish gerritBaseURL; 40 | private HttpClientBuilder clientBuilder; 41 | private boolean isAuthenticated = false; 42 | 43 | public GerritChecksApiBuilder(URIish gerritBaseURL) { 44 | this.gerritBaseURL = gerritBaseURL; 45 | clientBuilder = HttpClientBuilder.create(); 46 | } 47 | 48 | public GerritChecksApiBuilder allowInsecureHttps() { 49 | try { 50 | SSLContext sslContext = 51 | new SSLContextBuilder() 52 | .loadTrustMaterial( 53 | null, 54 | new TrustStrategy() { 55 | public boolean isTrusted(final X509Certificate[] chain, String authType) 56 | throws CertificateException { 57 | return true; 58 | } 59 | }) 60 | .build(); 61 | SSLConnectionSocketFactory sslsf = 62 | new SSLConnectionSocketFactory(sslContext, new NoopHostnameVerifier()); 63 | clientBuilder.setSSLSocketFactory(sslsf); 64 | } catch (KeyStoreException | KeyManagementException | NoSuchAlgorithmException e) { 65 | LOGGER.log(Level.WARNING, "Could not disable SSL verification.", e); 66 | } 67 | return this; 68 | } 69 | 70 | public GerritChecksApiBuilder setBasicAuthCredentials(String username, String password) { 71 | CredentialsProvider provider = new BasicCredentialsProvider(); 72 | UsernamePasswordCredentials auth = new UsernamePasswordCredentials(username, password); 73 | provider.setCredentials(AuthScope.ANY, auth); 74 | clientBuilder.setDefaultCredentialsProvider(provider); 75 | isAuthenticated = true; 76 | return this; 77 | } 78 | 79 | public GerritChecksApi build() { 80 | return new GerritChecksApi(gerritBaseURL, clientBuilder.build(), isAuthenticated); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/com/google/gerrit/plugins/checks/client/PendingChecks.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 The Android Open Source Project 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. 14 | 15 | package com.google.gerrit.plugins.checks.client; 16 | 17 | import com.google.gerrit.extensions.restapi.RestApiException; 18 | import com.google.gerrit.plugins.checks.api.CheckState; 19 | import com.google.gerrit.plugins.checks.api.PendingChecksInfo; 20 | import com.google.gson.reflect.TypeToken; 21 | import java.net.URI; 22 | import java.net.URISyntaxException; 23 | import java.util.HashMap; 24 | import java.util.List; 25 | import java.util.Map; 26 | import org.apache.http.HttpStatus; 27 | import org.apache.http.client.methods.CloseableHttpResponse; 28 | import org.apache.http.client.methods.HttpGet; 29 | import org.apache.http.impl.client.CloseableHttpClient; 30 | import org.apache.http.util.EntityUtils; 31 | import org.eclipse.jgit.transport.URIish; 32 | 33 | public class PendingChecks extends AbstractEndpoint { 34 | private static final String PENDING_CHECKS_PATH = "plugins/checks/checks.pending/"; 35 | 36 | private HashMap queries = new HashMap(); 37 | 38 | public PendingChecks(URIish gerritBaseUrl, CloseableHttpClient client, boolean isAuthenticated) 39 | throws URISyntaxException { 40 | super(gerritBaseUrl, client, isAuthenticated); 41 | } 42 | 43 | public PendingChecks checker(String uuid) { 44 | queries.put("checker", uuid); 45 | return this; 46 | } 47 | 48 | public PendingChecks scheme(String scheme) { 49 | queries.put("scheme", scheme); 50 | return this; 51 | } 52 | 53 | public PendingChecks state(CheckState state) { 54 | queries.put("state", state.name()); 55 | return this; 56 | } 57 | 58 | public List list() throws RestApiException { 59 | try { 60 | HttpGet request = new HttpGet(buildRequestUrl()); 61 | try (CloseableHttpResponse response = client.execute(request)) { 62 | if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { 63 | return JsonBodyParser.parseResponse( 64 | EntityUtils.toString(response.getEntity()), 65 | new TypeToken>() {}.getType()); 66 | } 67 | throw new RestApiException( 68 | String.format( 69 | "Request failed with status: %d", response.getStatusLine().getStatusCode())); 70 | } 71 | } catch (Exception e) { 72 | throw new RestApiException("Failed to list pending checks: ", e); 73 | } 74 | } 75 | 76 | private URI buildRequestUrl() throws URISyntaxException { 77 | if (!queries.isEmpty()) { 78 | StringBuffer queryString = new StringBuffer(); 79 | for (Map.Entry query : queries.entrySet()) { 80 | String connector = queryString.length() > 0 ? "+" : ""; 81 | queryString.append(String.format("%s%s:%s", connector, query.getKey(), query.getValue())); 82 | } 83 | uriBuilder.setParameter("query", queryString.toString()); 84 | } 85 | return uriBuilder.setPath(getPrefix() + PENDING_CHECKS_PATH).build(); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/test/java/jenkins/plugins/gerrit/GerritVersionTest.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 GerritForge Ltd 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. 14 | 15 | package jenkins.plugins.gerrit; 16 | 17 | import static org.junit.Assert.*; 18 | 19 | import com.google.gerrit.extensions.api.GerritApi; 20 | import org.junit.Rule; 21 | import org.junit.Test; 22 | import org.mockserver.junit.MockServerRule; 23 | import org.mockserver.model.HttpRequest; 24 | import org.mockserver.model.HttpResponse; 25 | 26 | public class GerritVersionTest { 27 | 28 | @Rule public MockServerRule g = new MockServerRule(this); 29 | 30 | @Test 31 | public void testisVersionBelow215() throws Exception { 32 | GerritApi gerritApi = 33 | new GerritApiBuilder() 34 | .gerritApiUrl("http://localhost:" + g.getPort()) 35 | .credentials(null, null) 36 | .build(); 37 | 38 | assertTrue(GerritVersion.isVersionBelow215(gerritApi)); 39 | setGerritServerVersion("1.0"); 40 | assertTrue(GerritVersion.isVersionBelow215(gerritApi)); 41 | setGerritServerVersion("1.1.1"); 42 | assertTrue(GerritVersion.isVersionBelow215(gerritApi)); 43 | setGerritServerVersion("2"); 44 | assertTrue(GerritVersion.isVersionBelow215(gerritApi)); 45 | setGerritServerVersion("2.0"); 46 | assertTrue(GerritVersion.isVersionBelow215(gerritApi)); 47 | setGerritServerVersion("2.0.19"); 48 | assertTrue(GerritVersion.isVersionBelow215(gerritApi)); 49 | setGerritServerVersion("2.14"); 50 | assertTrue(GerritVersion.isVersionBelow215(gerritApi)); 51 | setGerritServerVersion("2.14.99"); 52 | assertTrue(GerritVersion.isVersionBelow215(gerritApi)); 53 | 54 | setGerritServerVersion("2.15"); 55 | assertFalse(GerritVersion.isVersionBelow215(gerritApi)); 56 | setGerritServerVersion("2.15.0"); 57 | assertFalse(GerritVersion.isVersionBelow215(gerritApi)); 58 | setGerritServerVersion("2.15.99"); 59 | assertFalse(GerritVersion.isVersionBelow215(gerritApi)); 60 | setGerritServerVersion("2.16"); 61 | assertFalse(GerritVersion.isVersionBelow215(gerritApi)); 62 | setGerritServerVersion("3"); 63 | assertFalse(GerritVersion.isVersionBelow215(gerritApi)); 64 | setGerritServerVersion("3.0"); 65 | assertFalse(GerritVersion.isVersionBelow215(gerritApi)); 66 | setGerritServerVersion("3.0.0"); 67 | assertFalse(GerritVersion.isVersionBelow215(gerritApi)); 68 | setGerritServerVersion("3.1"); 69 | assertFalse(GerritVersion.isVersionBelow215(gerritApi)); 70 | setGerritServerVersion("null"); 71 | assertFalse(GerritVersion.isVersionBelow215(gerritApi)); 72 | setGerritServerVersion(""); 73 | assertFalse(GerritVersion.isVersionBelow215(gerritApi)); 74 | setGerritServerVersion(" "); 75 | assertFalse(GerritVersion.isVersionBelow215(gerritApi)); 76 | setGerritServerVersion("."); 77 | assertFalse(GerritVersion.isVersionBelow215(gerritApi)); 78 | setGerritServerVersion(".."); 79 | assertFalse(GerritVersion.isVersionBelow215(gerritApi)); 80 | setGerritServerVersion("Error"); 81 | assertFalse(GerritVersion.isVersionBelow215(gerritApi)); 82 | } 83 | 84 | private void setGerritServerVersion(String version) { 85 | g.getClient().reset(); 86 | g.getClient() 87 | .when(HttpRequest.request("/config/server/version").withMethod("GET")) 88 | .respond( 89 | HttpResponse.response().withStatusCode(200).withBody(")]}'\n" + "\"" + version + "\"")); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/jenkins/plugins/gerrit/traits/ChangeDiscoveryTrait.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 GerritForge Ltd 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. 14 | 15 | package jenkins.plugins.gerrit.traits; 16 | 17 | import hudson.Extension; 18 | import javax.annotation.Nonnull; 19 | import jenkins.plugins.gerrit.GerritSCMSource; 20 | import jenkins.plugins.gerrit.GerritSCMSourceContext; 21 | import jenkins.plugins.git.GitSCMBuilder; 22 | import jenkins.plugins.git.traits.Messages; 23 | import jenkins.scm.api.*; 24 | import jenkins.scm.api.trait.*; 25 | import jenkins.scm.impl.trait.Discovery; 26 | import org.kohsuke.stapler.DataBoundConstructor; 27 | 28 | /** A {@link Discovery} trait that would discover all the Gerrit Changes */ 29 | public class ChangeDiscoveryTrait extends SCMSourceTrait { 30 | private final String queryString; 31 | 32 | /** Constructor for stapler. */ 33 | @DataBoundConstructor 34 | public ChangeDiscoveryTrait(String queryString) { 35 | this.queryString = queryString; 36 | } 37 | 38 | /** 39 | * Returns the changes query string. 40 | * 41 | * @return the changes query string. 42 | */ 43 | public String getQueryString() { 44 | return queryString; 45 | } 46 | 47 | /** {@inheritDoc} */ 48 | @Override 49 | protected void decorateContext(SCMSourceContext context) { 50 | GerritSCMSourceContext ctx = (GerritSCMSourceContext) context; 51 | ctx.wantBranches(true); 52 | ctx.withAuthority(new BranchSCMHeadAuthority()); 53 | ctx.setChangeFilter(queryString); 54 | } 55 | 56 | /** {@inheritDoc} */ 57 | @Override 58 | public boolean includeCategory(@Nonnull SCMHeadCategory category) { 59 | return category.isUncategorized(); 60 | } 61 | 62 | /** Our descriptor. */ 63 | @Extension 64 | @Discovery 65 | public static class DescriptorImpl extends SCMSourceTraitDescriptor { 66 | 67 | /** {@inheritDoc} */ 68 | @Override 69 | public String getDisplayName() { 70 | return jenkins.plugins.gerrit.traits.Messages.ChangeDiscoveryTrait_displayName(); 71 | } 72 | 73 | /** {@inheritDoc} */ 74 | @Override 75 | public Class getBuilderClass() { 76 | return GitSCMBuilder.class; 77 | } 78 | 79 | /** {@inheritDoc} */ 80 | @Override 81 | public Class getContextClass() { 82 | return GerritSCMSourceContext.class; 83 | } 84 | 85 | /** {@inheritDoc} */ 86 | @Override 87 | public Class getSourceClass() { 88 | return GerritSCMSource.class; 89 | } 90 | } 91 | 92 | /** Trusts branches from the repository. */ 93 | public static class BranchSCMHeadAuthority 94 | extends SCMHeadAuthority { 95 | /** {@inheritDoc} */ 96 | @Override 97 | protected boolean checkTrusted(@Nonnull SCMSourceRequest request, @Nonnull SCMHead head) { 98 | return true; 99 | } 100 | 101 | /** Out descriptor. */ 102 | @Extension 103 | public static class DescriptorImpl extends SCMHeadAuthorityDescriptor { 104 | /** {@inheritDoc} */ 105 | @Override 106 | public String getDisplayName() { 107 | return Messages.BranchDiscoveryTrait_authorityDisplayName(); 108 | } 109 | 110 | /** {@inheritDoc} */ 111 | @Override 112 | public boolean isApplicableToOrigin(@Nonnull Class originClass) { 113 | return SCMHeadOrigin.Default.class.isAssignableFrom(originClass); 114 | } 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/main/java/jenkins/plugins/gerrit/traits/FilterChecksTrait.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 SAP SE 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. 14 | 15 | package jenkins.plugins.gerrit.traits; 16 | 17 | import hudson.Extension; 18 | import hudson.util.ListBoxModel; 19 | import javax.annotation.Nonnull; 20 | import jenkins.plugins.gerrit.GerritSCMSource; 21 | import jenkins.plugins.gerrit.GerritSCMSourceContext; 22 | import jenkins.plugins.gerrit.PendingChecksFilter; 23 | import jenkins.plugins.git.GitSCMBuilder; 24 | import jenkins.scm.api.SCMHeadCategory; 25 | import jenkins.scm.api.SCMSource; 26 | import jenkins.scm.api.trait.SCMBuilder; 27 | import jenkins.scm.api.trait.SCMSourceContext; 28 | import jenkins.scm.api.trait.SCMSourceTrait; 29 | import jenkins.scm.api.trait.SCMSourceTraitDescriptor; 30 | import jenkins.scm.impl.trait.Discovery; 31 | import org.kohsuke.stapler.DataBoundConstructor; 32 | 33 | public class FilterChecksTrait extends SCMSourceTrait { 34 | 35 | public enum ChecksQueryOperator { 36 | ID, 37 | SCHEME 38 | } 39 | 40 | private final ChecksQueryOperator queryOperator; 41 | private final String queryString; 42 | 43 | /** Constructor for stapler. */ 44 | @DataBoundConstructor 45 | public FilterChecksTrait(ChecksQueryOperator queryOperator, String queryString) { 46 | this.queryOperator = queryOperator; 47 | this.queryString = queryString; 48 | } 49 | 50 | /** 51 | * Returns the query operator. 52 | * 53 | * @return the query operator. 54 | */ 55 | public ChecksQueryOperator getQueryOperator() { 56 | return queryOperator; 57 | } 58 | 59 | /** 60 | * Returns the query string. 61 | * 62 | * @return the query string. 63 | */ 64 | public String getQueryString() { 65 | return queryString; 66 | } 67 | 68 | /** {@inheritDoc} */ 69 | @Override 70 | protected void decorateContext(SCMSourceContext context) { 71 | GerritSCMSourceContext ctx = (GerritSCMSourceContext) context; 72 | ctx.wantFilterForPendingChecks(true); 73 | ctx.withChecksQueryOperator(queryOperator); 74 | ctx.withChecksQueryString(queryString); 75 | ctx.withFilter(new PendingChecksFilter()); 76 | } 77 | 78 | /** {@inheritDoc} */ 79 | @Override 80 | public boolean includeCategory(@Nonnull SCMHeadCategory category) { 81 | return category.isUncategorized(); 82 | } 83 | 84 | /** Our descriptor. */ 85 | @Extension 86 | @Discovery 87 | public static class DescriptorImpl extends SCMSourceTraitDescriptor { 88 | 89 | /** {@inheritDoc} */ 90 | @Override 91 | public String getDisplayName() { 92 | return Messages.FilterChecksTrait_displayName(); 93 | } 94 | 95 | public ListBoxModel doFillQueryOperatorItems() { 96 | ListBoxModel items = new ListBoxModel(); 97 | 98 | items.add(Messages.FilterChecksTrait_checkerIdOperator(), ChecksQueryOperator.ID.name()); 99 | items.add(Messages.FilterChecksTrait_schemeOperator(), ChecksQueryOperator.SCHEME.name()); 100 | 101 | return items; 102 | } 103 | 104 | /** {@inheritDoc} */ 105 | @Override 106 | public Class getBuilderClass() { 107 | return GitSCMBuilder.class; 108 | } 109 | 110 | /** {@inheritDoc} */ 111 | @Override 112 | public Class getContextClass() { 113 | return GerritSCMSourceContext.class; 114 | } 115 | 116 | /** {@inheritDoc} */ 117 | @Override 118 | public Class getSourceClass() { 119 | return GerritSCMSource.class; 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/main/java/jenkins/plugins/gerrit/PagedCodeProjectsRequest.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022 Réda Housni Alaoui 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. 14 | 15 | package jenkins.plugins.gerrit; 16 | 17 | import com.google.gerrit.extensions.api.GerritApi; 18 | import com.google.gerrit.extensions.api.projects.Projects; 19 | import com.google.gerrit.extensions.common.ProjectInfo; 20 | import com.google.gerrit.extensions.restapi.RestApiException; 21 | import java.util.Collections; 22 | import java.util.Iterator; 23 | import java.util.List; 24 | 25 | /** 26 | * Allows to iterate on a Gerrit instance's projects in a computation-wise and memory-wise 27 | * performant manner. Computation-wise because it will try to fetch as few projects as possible 28 | * considering the consumer needs. Memory-wise because it will not hold more in-memory projects than 29 | * the configured chunk size. 30 | * 31 | *

Those optimizations should also allow to reduce the pressure on the Gerrit instance resources. 32 | */ 33 | public class PagedCodeProjectsRequest implements Iterable { 34 | 35 | private static final int CHUNK_DEFAULT_MAX_SIZE = 20; 36 | 37 | private final GerritApi gerritApi; 38 | private final int chunkMaxSize; 39 | 40 | public PagedCodeProjectsRequest(GerritApi gerritApi) { 41 | this(gerritApi, CHUNK_DEFAULT_MAX_SIZE); 42 | } 43 | 44 | public PagedCodeProjectsRequest(GerritApi gerritApi, int chunkMaxSize) { 45 | this.gerritApi = gerritApi; 46 | this.chunkMaxSize = chunkMaxSize; 47 | } 48 | 49 | @Override 50 | public Iterator iterator() { 51 | return new ProjectIterator(gerritApi, chunkMaxSize); 52 | } 53 | 54 | private static class ProjectIterator implements Iterator { 55 | 56 | private final GerritApi gerritApi; 57 | private final int chunkMaxSize; 58 | 59 | private Projects.ListRequest listRequest; 60 | private Iterator chunkIterator; 61 | private boolean moreChunkToFetch = true; 62 | 63 | public ProjectIterator(GerritApi gerritApi, int chunkMaxSize) { 64 | this.gerritApi = gerritApi; 65 | this.chunkMaxSize = chunkMaxSize; 66 | } 67 | 68 | @Override 69 | public boolean hasNext() { 70 | return getChunkIterator().hasNext(); 71 | } 72 | 73 | @Override 74 | public ProjectInfo next() { 75 | return getChunkIterator().next(); 76 | } 77 | 78 | private Iterator getChunkIterator() { 79 | if (chunkIterator != null && chunkIterator.hasNext()) { 80 | return chunkIterator; 81 | } 82 | if (!moreChunkToFetch) { 83 | return Collections.emptyIterator(); 84 | } 85 | 86 | List nextChunk; 87 | try { 88 | if (listRequest != null) { 89 | listRequest.withStart(listRequest.getStart() + chunkMaxSize); 90 | } else { 91 | listRequest = createListRequest(); 92 | } 93 | nextChunk = listRequest.get(); 94 | } catch (RestApiException e) { 95 | throw new IllegalStateException(e); 96 | } 97 | moreChunkToFetch = nextChunk.size() >= chunkMaxSize; 98 | chunkIterator = nextChunk.iterator(); 99 | return chunkIterator; 100 | } 101 | 102 | private Projects.ListRequest createListRequest() { 103 | return gerritApi 104 | .projects() 105 | .list() 106 | // Setting the type even if it is not used by the implementation ... 107 | .withType(Projects.ListRequest.FilterType.CODE) 108 | .withLimit(chunkMaxSize); 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/main/java/jenkins/plugins/gerrit/GerritSCMSourceContext.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 SAP SE 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. 14 | 15 | package jenkins.plugins.gerrit; 16 | 17 | import edu.umd.cs.findbugs.annotations.CheckForNull; 18 | import edu.umd.cs.findbugs.annotations.NonNull; 19 | import hudson.model.TaskListener; 20 | import jenkins.plugins.gerrit.traits.FilterChecksTrait.ChecksQueryOperator; 21 | import jenkins.plugins.git.GitSCMSourceContext; 22 | import jenkins.scm.api.SCMHeadObserver; 23 | import jenkins.scm.api.SCMSource; 24 | import jenkins.scm.api.SCMSourceCriteria; 25 | 26 | public class GerritSCMSourceContext 27 | extends GitSCMSourceContext { 28 | 29 | @NonNull private ChecksQueryOperator checksQueryOperator = ChecksQueryOperator.SCHEME; 30 | @NonNull private boolean filterForPendingChecks = false; 31 | @NonNull private String checksQueryString = ""; 32 | @NonNull private String changesQueryFilter = ""; 33 | 34 | public GerritSCMSourceContext(SCMSourceCriteria criteria, SCMHeadObserver observer) { 35 | super(criteria, observer); 36 | } 37 | 38 | /** 39 | * Defines whether changes should by filtered by pending checks. 40 | * 41 | * @param wantFilter whether the filter should be applied. 42 | * @return {@code this} for method chaining. 43 | */ 44 | @NonNull 45 | public GerritSCMSourceContext wantFilterForPendingChecks(boolean wantFilter) { 46 | filterForPendingChecks = wantFilter; 47 | return this; 48 | } 49 | 50 | /** 51 | * Returns true, if open changes should be filtered for pending checks 52 | * 53 | * @return boolean whether open changes should be filtered for pending checks. 54 | */ 55 | @NonNull 56 | public final boolean filterForPendingChecks() { 57 | return filterForPendingChecks; 58 | } 59 | 60 | /** 61 | * Defines the query operator to be used to query pending checks. 62 | * 63 | * @param queryOperator the query operator to be used. 64 | * @return {@code this} for method chaining. 65 | */ 66 | @NonNull 67 | public GerritSCMSourceContext withChecksQueryOperator(ChecksQueryOperator queryOperator) { 68 | checksQueryOperator = queryOperator; 69 | return this; 70 | } 71 | 72 | /** 73 | * Returns the {@link ChecksQueryOperator} to use to discover pending checks. 74 | * 75 | * @return the {@link ChecksQueryOperator} to use to discover pending checks. 76 | */ 77 | @NonNull 78 | public final ChecksQueryOperator checksQueryOperator() { 79 | return checksQueryOperator; 80 | } 81 | 82 | /** 83 | * Defines the query string to be used to query pending checks. 84 | * 85 | * @param queryString the query string to be used. 86 | * @return {@code this} for method chaining. 87 | */ 88 | @NonNull 89 | public GerritSCMSourceContext withChecksQueryString(String queryString) { 90 | checksQueryString = queryString; 91 | return this; 92 | } 93 | 94 | /** 95 | * Returns the ID or scheme to use to discover pending checks. 96 | * 97 | * @return the ID or scheme to use to discover pending checks. 98 | */ 99 | @NonNull 100 | public final String checksQueryString() { 101 | return checksQueryString; 102 | } 103 | 104 | @NonNull 105 | @Override 106 | public GerritSCMSourceRequest newRequest( 107 | @NonNull SCMSource source, @CheckForNull TaskListener listener) { 108 | return new GerritSCMSourceRequest((GerritSCMSource) source, this, listener); 109 | } 110 | 111 | public void setChangeFilter(String changesQueryFilter) { 112 | this.changesQueryFilter = changesQueryFilter; 113 | } 114 | 115 | public String changesQueryFilter() { 116 | return changesQueryFilter; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/main/java/jenkins/plugins/gerrit/GerritSCMSourceRequest.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 SAP SE 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. 14 | 15 | package jenkins.plugins.gerrit; 16 | 17 | import com.google.gerrit.extensions.restapi.RestApiException; 18 | import com.google.gerrit.plugins.checks.api.PendingChecksInfo; 19 | import com.google.gerrit.plugins.checks.client.GerritChecksApi; 20 | import hudson.model.TaskListener; 21 | import java.io.IOException; 22 | import java.net.URISyntaxException; 23 | import java.util.ArrayList; 24 | import java.util.HashMap; 25 | import java.util.HashSet; 26 | import java.util.List; 27 | import java.util.Map; 28 | import jenkins.plugins.git.GitSCMSourceRequest; 29 | import org.eclipse.jgit.transport.URIish; 30 | 31 | public class GerritSCMSourceRequest extends GitSCMSourceRequest { 32 | 33 | private final boolean filterForPendingChecks; 34 | 35 | private final Map> patchsetWithPendingChecks; 36 | 37 | public GerritSCMSourceRequest( 38 | GerritSCMSource source, GerritSCMSourceContext context, TaskListener listener) { 39 | super(source, context, listener); 40 | this.filterForPendingChecks = context.filterForPendingChecks(); 41 | this.patchsetWithPendingChecks = 42 | filterForPendingChecks 43 | ? getChangesWithPendingChecks(source, context, listener) 44 | : new HashMap>(); 45 | } 46 | 47 | public Map> getPatchsetWithPendingChecks() { 48 | return patchsetWithPendingChecks; 49 | } 50 | 51 | private GerritChecksApi getGerritChecksApi(GerritSCMSource source, TaskListener listener) 52 | throws IOException { 53 | try { 54 | return source.createGerritChecksApi(listener, new GerritURI(new URIish(source.getRemote()))); 55 | } catch (URISyntaxException | IOException e) { 56 | throw new IOException(e); 57 | } 58 | } 59 | 60 | private HashMap> getChangesWithPendingChecks( 61 | GerritSCMSource source, GerritSCMSourceContext context, TaskListener listener) { 62 | HashMap> patchsetWithPendingChecks = 63 | new HashMap>(); 64 | List pendingChecks = new ArrayList(); 65 | 66 | try { 67 | GerritChecksApi gerritChecksApi = getGerritChecksApi(source, listener); 68 | switch (context.checksQueryOperator()) { 69 | case ID: 70 | pendingChecks = 71 | gerritChecksApi.pendingChecks().checker(context.checksQueryString()).list(); 72 | break; 73 | case SCHEME: 74 | pendingChecks = 75 | gerritChecksApi.pendingChecks().scheme(context.checksQueryString()).list(); 76 | break; 77 | default: 78 | throw new IOException("Unknown query operator for querying pending checks."); 79 | } 80 | } catch (URISyntaxException | IOException | RestApiException e) { 81 | listener.getLogger().println("Unable to query for pending checks: " + e); 82 | } 83 | 84 | for (PendingChecksInfo check : pendingChecks) { 85 | if (check.patchSet == null) { 86 | continue; 87 | } 88 | String ref = String.format("%d/%d", check.patchSet.changeNumber, check.patchSet.patchSetId); 89 | HashSet checks = new HashSet(); 90 | if (patchsetWithPendingChecks.containsKey(ref)) { 91 | checks = patchsetWithPendingChecks.get(ref); 92 | checks.add(check); 93 | } else { 94 | checks.add(check); 95 | } 96 | patchsetWithPendingChecks.put(ref, checks); 97 | } 98 | 99 | return patchsetWithPendingChecks; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/jenkins/plugins/gerrit/workflow/GerritCommentStep.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 GerritForge Ltd 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. 14 | 15 | package jenkins.plugins.gerrit.workflow; 16 | 17 | import com.google.gerrit.extensions.api.GerritApi; 18 | import com.google.gerrit.extensions.api.changes.ChangeApi; 19 | import com.google.gerrit.extensions.api.changes.Changes; 20 | import com.google.gerrit.extensions.api.changes.DraftInput; 21 | import com.google.gerrit.extensions.restapi.RestApiException; 22 | import hudson.EnvVars; 23 | import hudson.Extension; 24 | import hudson.model.TaskListener; 25 | import java.io.IOException; 26 | import java.util.Collections; 27 | import java.util.Set; 28 | import javax.annotation.Nonnull; 29 | import jenkins.plugins.gerrit.GerritApiBuilder; 30 | import jenkins.plugins.gerrit.GerritChange; 31 | import jenkins.plugins.gerrit.GerritVersion; 32 | import org.jenkinsci.plugins.workflow.steps.*; 33 | import org.kohsuke.stapler.DataBoundConstructor; 34 | import org.kohsuke.stapler.DataBoundSetter; 35 | 36 | public class GerritCommentStep extends Step { 37 | 38 | private String path; 39 | private int line; 40 | private String message; 41 | 42 | @DataBoundConstructor 43 | public GerritCommentStep(String path, String message) { 44 | this.path = path; 45 | this.message = message; 46 | } 47 | 48 | public int getLine() { 49 | return line; 50 | } 51 | 52 | @DataBoundSetter 53 | public void setLine(int line) { 54 | this.line = line; 55 | } 56 | 57 | public class Execution extends SynchronousStepExecution { 58 | private final TaskListener listener; 59 | private final EnvVars envVars; 60 | 61 | protected Execution(@Nonnull StepContext context) throws IOException, InterruptedException { 62 | super(context); 63 | 64 | this.envVars = context.get(EnvVars.class); 65 | this.listener = getContext().get(TaskListener.class); 66 | } 67 | 68 | @Override 69 | protected Void run() throws Exception { 70 | GerritApi gerritApi = 71 | new GerritApiBuilder().stepContext(getContext()).requireAuthentication().build(); 72 | if (gerritApi == null) { 73 | return null; 74 | } 75 | 76 | GerritChange change = new GerritChange(getContext()); 77 | if (change.valid()) { 78 | listener 79 | .getLogger() 80 | .format( 81 | "Gerrit review change %d/%d %s=%d (%s)%n", 82 | change.getChangeId(), change.getRevision(), path, line, message); 83 | DraftInput draftInput = new DraftInput(); 84 | draftInput.path = path; 85 | draftInput.line = line; 86 | draftInput.message = message; 87 | getChangeApi(gerritApi, change).revision(change.getRevision()).createDraft(draftInput); 88 | } 89 | return null; 90 | } 91 | } 92 | 93 | private ChangeApi getChangeApi(GerritApi gerritApi, GerritChange change) throws RestApiException { 94 | Changes changesApi = gerritApi.changes(); 95 | return GerritVersion.isVersionBelow215(gerritApi) 96 | ? changesApi.id(change.getChangeId()) 97 | : changesApi.id(change.getProject(), change.getChangeId()); 98 | } 99 | 100 | @Override 101 | public StepExecution start(StepContext stepContext) throws Exception { 102 | return new GerritCommentStep.Execution(stepContext); 103 | } 104 | 105 | @Extension 106 | public static class DescriptorImpl extends StepDescriptor { 107 | 108 | @Override 109 | public Set> getRequiredContext() { 110 | return Collections.emptySet(); 111 | } 112 | 113 | @Override 114 | public String getFunctionName() { 115 | return "gerritComment"; 116 | } 117 | 118 | @Nonnull 119 | @Override 120 | public String getDisplayName() { 121 | return "Gerrit Review Comment"; 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/main/java/jenkins/plugins/gerrit/GerritURI.java: -------------------------------------------------------------------------------- 1 | package jenkins.plugins.gerrit; 2 | 3 | import java.net.URISyntaxException; 4 | import java.util.regex.Matcher; 5 | import java.util.regex.Pattern; 6 | import org.eclipse.jgit.transport.URIish; 7 | 8 | /** 9 | * A GerritURI encapsulates a Gerrit remote URI and is able to extract certain properties like the 10 | * project name and URI prefix. 11 | */ 12 | public class GerritURI { 13 | 14 | private static enum Scheme { 15 | HTTP, 16 | HTTPS, 17 | SSH 18 | } 19 | 20 | /** Pattern which matches the URI prefix and the project name of a Gerrit HTTP URI. */ 21 | private static final Pattern GERRIT_AUTH_HTTP_URI_PATTERN = 22 | Pattern.compile("(.*?)/a/(.*)(\\.git)?"); 23 | 24 | private static final Pattern GERRIT_ANON_HTTP_URI_PATTERN = 25 | Pattern.compile("(/)(.*(? 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. 14 | 15 | package jenkins.plugins.gerrit; 16 | 17 | import static java.util.Optional.ofNullable; 18 | 19 | import com.google.gerrit.extensions.common.ProjectInfo; 20 | import java.util.ArrayList; 21 | import java.util.Collections; 22 | import java.util.LinkedHashMap; 23 | import java.util.List; 24 | import java.util.Map; 25 | import java.util.function.Function; 26 | import java.util.stream.Collectors; 27 | import org.apache.commons.lang.StringUtils; 28 | import org.junit.rules.TestRule; 29 | import org.junit.runner.Description; 30 | import org.junit.runners.model.Statement; 31 | import org.mockserver.junit.MockServerRule; 32 | import org.mockserver.model.HttpRequest; 33 | import org.mockserver.model.HttpResponse; 34 | import org.mockserver.model.JsonBody; 35 | 36 | public class GerritMockServerRule implements TestRule { 37 | 38 | private final MockServerRule serverRule; 39 | private final Map projectRepository = new LinkedHashMap<>(); 40 | 41 | public GerritMockServerRule(Object target) { 42 | this.serverRule = new MockServerRule(target); 43 | } 44 | 45 | public String getUrl() { 46 | return "http://localhost:" + serverRule.getPort(); 47 | } 48 | 49 | public void addProject(ProjectInfo projectInfo) { 50 | projectRepository.put(projectInfo.id, projectInfo); 51 | } 52 | 53 | public List getProjectRepository() { 54 | return new ArrayList<>(projectRepository.values()); 55 | } 56 | 57 | @Override 58 | public Statement apply(Statement base, Description description) { 59 | return serverRule.apply( 60 | new Statement() { 61 | @Override 62 | public void evaluate() throws Throwable { 63 | projectRepository.clear(); 64 | setupExpectations(); 65 | base.evaluate(); 66 | } 67 | }, 68 | description); 69 | } 70 | 71 | private void setupExpectations() { 72 | serverRule 73 | .getClient() 74 | .when(HttpRequest.request("/projects/").withMethod("GET")) 75 | .callback( 76 | httpRequest -> { 77 | int start = 78 | ofNullable(httpRequest.getFirstQueryStringParameter("S")) 79 | .filter(StringUtils::isNotBlank) 80 | .map(Integer::parseInt) 81 | .orElse(0); 82 | int limit = 83 | ofNullable(httpRequest.getFirstQueryStringParameter("n")) 84 | .filter(StringUtils::isNotBlank) 85 | .map(Integer::parseInt) 86 | .orElse(projectRepository.size()); 87 | 88 | if (start >= projectRepository.size()) { 89 | return HttpResponse.response() 90 | .withStatusCode(200) 91 | .withBody(JsonBody.json(Collections.emptyMap())); 92 | } 93 | 94 | Map projectSlice = 95 | new ArrayList<>(projectRepository.values()) 96 | .subList(start, Math.min(start + limit, projectRepository.size())) 97 | .stream() 98 | .collect( 99 | Collectors.toMap( 100 | projectInfo -> projectInfo.id, 101 | Function.identity(), 102 | (u, v) -> { 103 | throw new IllegalStateException( 104 | String.format("Duplicate key %s", u)); 105 | }, 106 | LinkedHashMap::new)); 107 | return HttpResponse.response() 108 | .withStatusCode(200) 109 | .withBody(JsonBody.json(projectSlice)); 110 | }); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/main/java/com/google/gerrit/plugins/checks/api/CheckInfo.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 The Android Open Source Project 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. 14 | 15 | package com.google.gerrit.plugins.checks.api; 16 | 17 | import com.google.common.base.MoreObjects; 18 | import com.google.gerrit.common.Nullable; 19 | import java.sql.Timestamp; 20 | import java.util.Objects; 21 | import java.util.Set; 22 | 23 | /** REST API representation of a Check. */ 24 | public class CheckInfo { 25 | /** Repository name that this check applies to. */ 26 | public String repository; 27 | /** Change number that this check applies to. */ 28 | public int changeNumber; 29 | /** Patch set ID that this check applies to. */ 30 | public int patchSetId; 31 | /** UUID of the checker that posted this check. */ 32 | public String checkerUuid; 33 | 34 | /** State that this check exited. */ 35 | public CheckState state; 36 | /** Short message explaining the check state. */ 37 | @Nullable public String message; 38 | /** Fully qualified URL to detailed result on the Checker's service. */ 39 | @Nullable public String url; 40 | /** Timestamp of when this check was created. */ 41 | @Nullable public Timestamp started; 42 | /** Timestamp of when this check was last updated. */ 43 | @Nullable public Timestamp finished; 44 | 45 | /** Timestamp of when this check was created. */ 46 | public Timestamp created; 47 | /** Timestamp of when this check was last updated. */ 48 | public Timestamp updated; 49 | 50 | /** Name of the checker that produced this check. */ 51 | public String checkerName; 52 | 53 | /** Status of the checker that produced this check. */ 54 | public CheckerStatus checkerStatus; 55 | 56 | /** Blocking conditions that apply to this check. */ 57 | public Set blocking; 58 | 59 | /** Description of the checker that produced this check */ 60 | public String checkerDescription; 61 | 62 | @Override 63 | public boolean equals(Object o) { 64 | if (!(o instanceof CheckInfo)) { 65 | return false; 66 | } 67 | CheckInfo other = (CheckInfo) o; 68 | return Objects.equals(other.repository, repository) 69 | && Objects.equals(other.changeNumber, changeNumber) 70 | && Objects.equals(other.patchSetId, patchSetId) 71 | && Objects.equals(other.checkerUuid, checkerUuid) 72 | && Objects.equals(other.state, state) 73 | && Objects.equals(other.message, message) 74 | && Objects.equals(other.url, url) 75 | && Objects.equals(other.started, started) 76 | && Objects.equals(other.finished, finished) 77 | && Objects.equals(other.created, created) 78 | && Objects.equals(other.updated, updated) 79 | && Objects.equals(other.checkerName, checkerName) 80 | && Objects.equals(other.checkerStatus, checkerStatus) 81 | && Objects.equals(other.blocking, blocking) 82 | && Objects.equals(other.checkerDescription, checkerDescription); 83 | } 84 | 85 | @Override 86 | public int hashCode() { 87 | return Objects.hash( 88 | repository, 89 | changeNumber, 90 | patchSetId, 91 | checkerUuid, 92 | state, 93 | message, 94 | url, 95 | started, 96 | finished, 97 | created, 98 | updated, 99 | checkerName, 100 | checkerStatus, 101 | blocking, 102 | checkerDescription); 103 | } 104 | 105 | @Override 106 | public String toString() { 107 | return MoreObjects.toStringHelper(this) 108 | .add("repository", repository) 109 | .add("changeNumber", changeNumber) 110 | .add("patchSetId", patchSetId) 111 | .add("checkerUuid", checkerUuid) 112 | .add("state", state) 113 | .add("message", message) 114 | .add("url", url) 115 | .add("started", started) 116 | .add("finished", finished) 117 | .add("created", created) 118 | .add("updated", updated) 119 | .add("checkerName", checkerName) 120 | .add("checkerStatus", checkerStatus) 121 | .add("blocking", blocking) 122 | .add("checkerDescription", checkerDescription) 123 | .toString(); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/main/java/com/google/gerrit/plugins/checks/client/Checkers.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 The Android Open Source Project 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. 14 | 15 | package com.google.gerrit.plugins.checks.client; 16 | 17 | import com.google.gerrit.extensions.restapi.RestApiException; 18 | import com.google.gerrit.plugins.checks.api.CheckerInfo; 19 | import com.google.gerrit.plugins.checks.api.CheckerInput; 20 | import com.google.gson.reflect.TypeToken; 21 | import java.net.URI; 22 | import java.net.URISyntaxException; 23 | import org.apache.http.HttpStatus; 24 | import org.apache.http.client.methods.CloseableHttpResponse; 25 | import org.apache.http.client.methods.HttpGet; 26 | import org.apache.http.client.methods.HttpPost; 27 | import org.apache.http.entity.StringEntity; 28 | import org.apache.http.impl.client.CloseableHttpClient; 29 | import org.apache.http.util.EntityUtils; 30 | import org.eclipse.jgit.transport.URIish; 31 | 32 | public class Checkers extends AbstractEndpoint { 33 | 34 | protected Checkers(URIish gerritBaseUrl, CloseableHttpClient client, boolean isAuthenticated) 35 | throws URISyntaxException { 36 | super(gerritBaseUrl, client, isAuthenticated); 37 | } 38 | 39 | public CheckerInfo create(CheckerInput input) throws RestApiException { 40 | try { 41 | HttpPost request = new HttpPost(buildRequestUrl()); 42 | String inputString = 43 | JsonBodyParser.createRequestBody(input, new TypeToken() {}.getType()); 44 | request.setEntity(new StringEntity(inputString)); 45 | request.setHeader("Content-type", "application/json"); 46 | try (CloseableHttpResponse response = client.execute(request)) { 47 | if (response.getStatusLine().getStatusCode() == HttpStatus.SC_CREATED) { 48 | return JsonBodyParser.parseResponse( 49 | EntityUtils.toString(response.getEntity()), 50 | new TypeToken() {}.getType()); 51 | } 52 | throw new RestApiException( 53 | String.format("Request returned status %s", response.getStatusLine().getStatusCode())); 54 | } 55 | } catch (Exception e) { 56 | throw new RestApiException("Could not create checker", e); 57 | } 58 | } 59 | 60 | public CheckerInfo get(String checkerUuid) throws RestApiException { 61 | try { 62 | HttpGet request = new HttpGet(buildRequestUrl(checkerUuid)); 63 | try (CloseableHttpResponse response = client.execute(request)) { 64 | if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { 65 | return JsonBodyParser.parseResponse( 66 | EntityUtils.toString(response.getEntity()), 67 | new TypeToken() {}.getType()); 68 | } 69 | throw new RestApiException( 70 | String.format( 71 | "Request failed with status: %d", response.getStatusLine().getStatusCode())); 72 | } 73 | } catch (Exception e) { 74 | throw new RestApiException("Failed to get checker info: ", e); 75 | } 76 | } 77 | 78 | public CheckerInfo update(CheckerInput input) throws RestApiException { 79 | try { 80 | HttpPost request = new HttpPost(buildRequestUrl(input.uuid)); 81 | String inputString = 82 | JsonBodyParser.createRequestBody(input, new TypeToken() {}.getType()); 83 | request.setEntity(new StringEntity(inputString)); 84 | request.setHeader("Content-type", "application/json"); 85 | 86 | try (CloseableHttpResponse response = client.execute(request)) { 87 | if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { 88 | return JsonBodyParser.parseResponse( 89 | EntityUtils.toString(response.getEntity()), 90 | new TypeToken() {}.getType()); 91 | } 92 | throw new RestApiException( 93 | String.format("Request returned status %s", response.getStatusLine().getStatusCode())); 94 | } 95 | } catch (Exception e) { 96 | throw new RestApiException("Could not update checker", e); 97 | } 98 | } 99 | 100 | private URI buildRequestUrl() throws URISyntaxException { 101 | return buildRequestUrl(""); 102 | } 103 | 104 | private URI buildRequestUrl(String suffixPath) throws URISyntaxException { 105 | return uriBuilder 106 | .setPath(String.format("%splugins/checks/checkers/%s", getPrefix(), suffixPath)) 107 | .build(); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/test/java/jenkins/plugins/gerrit/GerritURITest.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 GerritForge Ltd 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. 14 | 15 | package jenkins.plugins.gerrit; 16 | 17 | import static org.junit.Assert.*; 18 | 19 | import java.net.URISyntaxException; 20 | import org.eclipse.jgit.transport.URIish; 21 | import org.junit.Test; 22 | 23 | public class GerritURITest { 24 | 25 | @Test 26 | public void projectNameWithSlashesIsExtractedFromHTTPURI() throws URISyntaxException { 27 | GerritURI gerritURI = new GerritURI(new URIish("http://host/a/project/with/slashes")); 28 | 29 | assertEquals("project/with/slashes", gerritURI.getProject()); 30 | } 31 | 32 | @Test 33 | public void anonymousAccessProjectNameIsExtractedFromHTTPURI() throws URISyntaxException { 34 | GerritURI gerritURI = new GerritURI(new URIish("http://host/organisation/project")); 35 | 36 | assertEquals("organisation/project", gerritURI.getProject()); 37 | } 38 | 39 | @Test 40 | public void projectNameEndingWithDotGitIsExtractedFromHTTPURI() throws URISyntaxException { 41 | GerritURI gerritURI = new GerritURI(new URIish("http://host/project.git")); 42 | 43 | assertEquals("project", gerritURI.getProject()); 44 | } 45 | 46 | @Test 47 | public void firstAInURIIsConsideredTheProjectMarker() throws URISyntaxException { 48 | GerritURI gerritURI = new GerritURI(new URIish("http://host/prefix/a/project/a/suffix")); 49 | 50 | assertEquals("/prefix", gerritURI.getPrefix()); 51 | assertEquals("project/a/suffix", gerritURI.getProject()); 52 | } 53 | 54 | @Test 55 | public void projectNameWithSlashesIsExtractedFromHTTPSURI() throws URISyntaxException { 56 | GerritURI gerritURI = new GerritURI(new URIish("https://host/a/project/with/slashes")); 57 | 58 | assertEquals("project/with/slashes", gerritURI.getProject()); 59 | } 60 | 61 | @Test 62 | public void projectNameWithSlashesIsExtractedFromSSHURI() throws URISyntaxException { 63 | GerritURI gerritURI = 64 | new GerritURI(new URIish("ssh://username@host:29418/project/with/slashes")); 65 | 66 | assertEquals("project/with/slashes", gerritURI.getProject()); 67 | } 68 | 69 | @Test 70 | public void prefixWithSlashesIsExtractedFromHTTPURI() throws URISyntaxException { 71 | GerritURI gerritURI = new GerritURI(new URIish("http://host/prefix/with/slashes/a/project")); 72 | 73 | assertEquals("/prefix/with/slashes", gerritURI.getPrefix()); 74 | } 75 | 76 | @Test 77 | public void prefixIsExtractedFromHTTPURIWithChangeWithProjectWithDots() 78 | throws URISyntaxException { 79 | GerritURI gerritURI = new GerritURI(new URIish("http://host/c/project/with/d.o.t.s/+/9916469")); 80 | 81 | assertEquals("/", gerritURI.getPrefix()); 82 | } 83 | 84 | @Test 85 | public void prefixIsExtractedFromHTTPURIWithChangeWithoutProject() throws URISyntaxException { 86 | GerritURI gerritURI = new GerritURI(new URIish("http://host/c/9916469")); 87 | 88 | assertEquals("/", gerritURI.getPrefix()); 89 | } 90 | 91 | @Test 92 | public void prefixIsExtractedFromHTTPURIForChangeWithoutC() throws URISyntaxException { 93 | GerritURI gerritURI = new GerritURI(new URIish("http://host/9916469")); 94 | 95 | assertEquals("/", gerritURI.getPrefix()); 96 | } 97 | 98 | @Test 99 | public void prefixWithSlashesIsExtractedFromHTTPSURI() throws URISyntaxException { 100 | GerritURI gerritURI = new GerritURI(new URIish("https://host/prefix/with/slashes/a/project")); 101 | 102 | assertEquals("/prefix/with/slashes", gerritURI.getPrefix()); 103 | } 104 | 105 | @Test 106 | public void prefixOfSSHURIIsAlwaysEmpty() throws URISyntaxException { 107 | GerritURI gerritURI = 108 | new GerritURI(new URIish("ssh://username@host:29418/project/with/slashes")); 109 | 110 | assertEquals("", gerritURI.getPrefix()); 111 | } 112 | 113 | @Test 114 | public void httpUriWithoutPrefixAreAccepted() throws URISyntaxException { 115 | GerritURI gerritURI = new GerritURI(new URIish("https://host")); 116 | 117 | assertEquals("/", gerritURI.getPrefix()); 118 | } 119 | 120 | @Test 121 | public void settingProjectToBaseUriReturnsTheProjectUri() throws URISyntaxException { 122 | GerritURI gerritURI = new GerritURI(new URIish("https://host")); 123 | 124 | assertEquals("https://host/foo", gerritURI.setProject("foo").toString()); 125 | } 126 | 127 | @Test 128 | public void settingProjectToProjectUriReturnsTheNewProjectUri() throws URISyntaxException { 129 | GerritURI gerritURI = new GerritURI(new URIish("https://host/foo")); 130 | 131 | assertEquals("https://host/bar", gerritURI.setProject("bar").toString()); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/main/java/jenkins/plugins/gerrit/GerritApiBuilder.java: -------------------------------------------------------------------------------- 1 | package jenkins.plugins.gerrit; 2 | 3 | import com.cloudbees.plugins.credentials.CredentialsProvider; 4 | import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials; 5 | import com.google.common.base.Strings; 6 | import com.google.gerrit.extensions.api.GerritApi; 7 | import com.google.gerrit.plugins.checks.client.GerritChecksApi; 8 | import com.google.gerrit.plugins.checks.client.GerritChecksApiBuilder; 9 | import com.urswolfer.gerrit.client.rest.GerritAuthData; 10 | import com.urswolfer.gerrit.client.rest.GerritRestApiFactory; 11 | import com.urswolfer.gerrit.client.rest.http.HttpClientBuilderExtension; 12 | import hudson.EnvVars; 13 | import hudson.model.Run; 14 | import hudson.model.TaskListener; 15 | import java.io.IOException; 16 | import java.io.PrintStream; 17 | import java.net.URISyntaxException; 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | import org.apache.commons.lang.StringUtils; 21 | import org.eclipse.jgit.transport.URIish; 22 | import org.jenkinsci.plugins.workflow.steps.StepContext; 23 | 24 | /** A wrapper on top of GerritApi. Enables common functionality. */ 25 | public class GerritApiBuilder { 26 | private PrintStream logger = System.out; 27 | private URIish gerritApiUrl; 28 | private Boolean insecureHttps; 29 | private boolean requireAuthentication; 30 | private String username; 31 | private String password; 32 | 33 | public GerritApiBuilder logger(PrintStream logger) { 34 | this.logger = logger; 35 | return this; 36 | } 37 | 38 | public GerritApiBuilder gerritApiUrl(URIish gerritApiUrl) { 39 | this.gerritApiUrl = gerritApiUrl; 40 | return this; 41 | } 42 | 43 | public GerritApiBuilder gerritApiUrl(String gerritApiUrl) throws URISyntaxException { 44 | if (gerritApiUrl == null) { 45 | this.gerritApiUrl = null; 46 | } else { 47 | gerritApiUrl(new URIish(gerritApiUrl)); 48 | } 49 | return this; 50 | } 51 | 52 | public GerritApiBuilder insecureHttps(Boolean insecureHttps) { 53 | this.insecureHttps = insecureHttps; 54 | return this; 55 | } 56 | 57 | public GerritApiBuilder requireAuthentication() { 58 | this.requireAuthentication = true; 59 | return this; 60 | } 61 | 62 | public GerritApiBuilder credentials(String username, String password) { 63 | this.username = username; 64 | this.password = password; 65 | return this; 66 | } 67 | 68 | public GerritApiBuilder credentials(StandardUsernamePasswordCredentials credentials) { 69 | if (credentials != null) { 70 | username = credentials.getUsername(); 71 | password = credentials.getPassword().getPlainText(); 72 | } 73 | return this; 74 | } 75 | 76 | public GerritApiBuilder stepContext(StepContext context) 77 | throws URISyntaxException, IOException, InterruptedException { 78 | EnvVars envVars = context.get(EnvVars.class); 79 | logger(context.get(TaskListener.class).getLogger()); 80 | if (StringUtils.isNotEmpty(envVars.get("GERRIT_API_URL"))) { 81 | gerritApiUrl(envVars.get("GERRIT_API_URL")); 82 | } else if (StringUtils.isNotEmpty(envVars.get("GERRIT_CHANGE_URL"))) { 83 | gerritApiUrl(new GerritURI(new URIish(envVars.get("GERRIT_CHANGE_URL"))).getApiURI()); 84 | } 85 | insecureHttps(Boolean.parseBoolean(envVars.get("GERRIT_API_INSECURE_HTTPS"))); 86 | String credentialsId = StringUtils.defaultIfEmpty(envVars.get("GERRIT_CREDENTIALS_ID"), null); 87 | if (credentialsId != null) { 88 | credentials( 89 | CredentialsProvider.findCredentialById( 90 | credentialsId, StandardUsernamePasswordCredentials.class, context.get(Run.class))); 91 | } 92 | return this; 93 | } 94 | 95 | public GerritApi build() { 96 | GerritApi gerritApi = null; 97 | if (verifyParameters()) { 98 | List extensions = new ArrayList<>(); 99 | extensions.add(UserAgentClientBuilderExtension.INSTANCE); 100 | if (Boolean.TRUE.equals(insecureHttps)) { 101 | extensions.add(SSLNoVerifyCertificateManagerClientBuilderExtension.INSTANCE); 102 | } 103 | gerritApi = 104 | new GerritRestApiFactory() 105 | .create( 106 | new GerritAuthData.Basic(gerritApiUrl.toString(), username, password, true), 107 | extensions.toArray(new HttpClientBuilderExtension[0])); 108 | } 109 | return gerritApi; 110 | } 111 | 112 | public GerritChecksApi buildChecksApi() { 113 | if (verifyParameters()) { 114 | GerritChecksApiBuilder gerritChecksApiBuilder = new GerritChecksApiBuilder(gerritApiUrl); 115 | if (username != null) { 116 | gerritChecksApiBuilder.setBasicAuthCredentials(username, password); 117 | } 118 | if (Boolean.TRUE.equals(insecureHttps)) { 119 | gerritChecksApiBuilder.allowInsecureHttps(); 120 | } 121 | return gerritChecksApiBuilder.build(); 122 | } 123 | return null; 124 | } 125 | 126 | @Override 127 | public String toString() { 128 | return gerritApiUrl == null ? "null" : gerritApiUrl.toString(); 129 | } 130 | 131 | private boolean verifyParameters() { 132 | if (gerritApiUrl == null) { 133 | logger.println("Gerrit Review is disabled no API URL"); 134 | return false; 135 | } else if (requireAuthentication && Strings.isNullOrEmpty(username)) { 136 | logger.println( 137 | "Gerrit Review requires authentication, however there are no credentials defined or are empty."); 138 | return false; 139 | } 140 | return true; 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/test/java/jenkins/plugins/gerrit/PendingChecksFilterTests.java: -------------------------------------------------------------------------------- 1 | package jenkins.plugins.gerrit; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import com.cloudbees.plugins.credentials.CredentialsProvider; 6 | import com.cloudbees.plugins.credentials.CredentialsScope; 7 | import com.cloudbees.plugins.credentials.domains.Domain; 8 | import com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl; 9 | import com.fasterxml.jackson.databind.PropertyNamingStrategy; 10 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 11 | import com.google.gerrit.plugins.checks.api.CheckState; 12 | import com.google.gerrit.plugins.checks.api.CheckablePatchSetInfo; 13 | import com.google.gerrit.plugins.checks.api.PendingCheckInfo; 14 | import com.google.gerrit.plugins.checks.api.PendingChecksInfo; 15 | import hudson.util.StreamTaskListener; 16 | import java.util.ArrayList; 17 | import java.util.Arrays; 18 | import java.util.HashMap; 19 | import java.util.HashSet; 20 | import java.util.List; 21 | import jenkins.plugins.gerrit.traits.FilterChecksTrait.ChecksQueryOperator; 22 | import jenkins.scm.api.SCMHeadObserver; 23 | import org.eclipse.jgit.lib.ObjectId; 24 | import org.junit.Before; 25 | import org.junit.BeforeClass; 26 | import org.junit.Rule; 27 | import org.junit.Test; 28 | import org.jvnet.hudson.test.JenkinsRule; 29 | import org.mockserver.junit.MockServerRule; 30 | import org.mockserver.model.HttpRequest; 31 | import org.mockserver.model.HttpResponse; 32 | import org.mockserver.model.JsonBody; 33 | 34 | @SuppressWarnings("deprecation") 35 | public class PendingChecksFilterTests { 36 | 37 | @Rule public MockServerRule g = new MockServerRule(this); 38 | @Rule public JenkinsRule j = new JenkinsRule(); 39 | 40 | private static final String checkerUuid = "test:checker"; 41 | 42 | private static GerritSCMSourceContext context; 43 | private static PendingChecksFilter filter; 44 | private static ArrayList pendingChecksInfos; 45 | private static HashMap> query; 46 | 47 | private GerritSCMSourceRequest request; 48 | 49 | @BeforeClass 50 | public static void setupClass() throws Exception { 51 | context = new GerritSCMSourceContext(null, SCMHeadObserver.none()); 52 | context.wantFilterForPendingChecks(true); 53 | context.withChecksQueryOperator(ChecksQueryOperator.ID); 54 | context.withChecksQueryString(checkerUuid); 55 | 56 | pendingChecksInfos = new ArrayList(); 57 | pendingChecksInfos.add(getPendingChecksInfo("test", 11111, 1, CheckState.NOT_STARTED)); 58 | 59 | query = new HashMap>(); 60 | query.put("query", Arrays.asList(new String[] {"checker:test:checker"})); 61 | 62 | filter = new PendingChecksFilter(); 63 | } 64 | 65 | @Before 66 | public void setup() throws Exception { 67 | UsernamePasswordCredentialsImpl c = 68 | new UsernamePasswordCredentialsImpl( 69 | CredentialsScope.GLOBAL, "cid", "cid", "USERNAME", "PASSWORD"); 70 | CredentialsProvider.lookupStores(j.jenkins) 71 | .iterator() 72 | .next() 73 | .addCredentials(Domain.global(), c); 74 | 75 | g.getClient() 76 | .when( 77 | HttpRequest.request("/a/plugins/checks/checks.pending/") 78 | .withQueryStringParameters(query) 79 | .withMethod("GET")) 80 | .respond( 81 | HttpResponse.response() 82 | .withStatusCode(200) 83 | .withBody(JsonBody.json(pendingChecksInfos))); 84 | 85 | GerritSCMSource source = 86 | new GerritSCMSource( 87 | String.format( 88 | "https://%s:%s/a/test", 89 | g.getClient().remoteAddress().getHostName(), 90 | g.getClient().remoteAddress().getPort())); 91 | source.setInsecureHttps(true); 92 | source.setCredentialsId("cid"); 93 | request = context.newRequest(source, new StreamTaskListener()); 94 | } 95 | 96 | @Test 97 | public void testPendingChecksFilterExcludesNonPending() throws Exception { 98 | HashMap refs = new HashMap(); 99 | refs.put("refs/changes/22/22222/2", ObjectId.zeroId()); 100 | 101 | ChangeSCMHead head = 102 | new ChangeSCMHead(refs.entrySet().iterator().next(), "master", new HashSet()); 103 | 104 | assertTrue(filter.isExcluded(request, head)); 105 | } 106 | 107 | @Test 108 | public void testPendingChecksFilterIncludesPending() throws Exception { 109 | HashMap refs = new HashMap(); 110 | refs.put("refs/changes/11/11111/1", ObjectId.zeroId()); 111 | 112 | ChangeSCMHead head = 113 | new ChangeSCMHead(refs.entrySet().iterator().next(), "master", new HashSet()); 114 | 115 | assertFalse(filter.isExcluded(request, head)); 116 | } 117 | 118 | private static PendingChecksInfo getPendingChecksInfo( 119 | String project, int changeNumber, int patchSetNumber, CheckState state) { 120 | CheckablePatchSetInfo checkablePatchSet = new CheckablePatchSetInfoMapper(); 121 | checkablePatchSet.repository = project; 122 | checkablePatchSet.changeNumber = changeNumber; 123 | checkablePatchSet.patchSetId = patchSetNumber; 124 | 125 | PendingChecksInfo pendingChecksInfo = new PendingChecksInfoMapper(); 126 | pendingChecksInfo.patchSet = checkablePatchSet; 127 | pendingChecksInfo.pendingChecks = new HashMap<>(); 128 | pendingChecksInfo.pendingChecks.put(checkerUuid, new PendingCheckInfo(state)); 129 | 130 | return pendingChecksInfo; 131 | } 132 | 133 | @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) 134 | private static class PendingChecksInfoMapper extends PendingChecksInfo {} 135 | 136 | @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) 137 | private static class CheckablePatchSetInfoMapper extends CheckablePatchSetInfo {} 138 | } 139 | -------------------------------------------------------------------------------- /src/test/java/jenkins/plugins/gerrit/workflow/GerritCommentStepTest.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 GerritForge Ltd 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. 14 | 15 | package jenkins.plugins.gerrit.workflow; 16 | 17 | import com.cloudbees.plugins.credentials.CredentialsProvider; 18 | import com.cloudbees.plugins.credentials.CredentialsScope; 19 | import com.cloudbees.plugins.credentials.domains.Domain; 20 | import com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl; 21 | import com.google.gerrit.extensions.api.changes.DraftInput; 22 | import java.io.IOException; 23 | import java.util.Collections; 24 | import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; 25 | import org.jenkinsci.plugins.workflow.job.WorkflowJob; 26 | import org.jenkinsci.plugins.workflow.job.WorkflowRun; 27 | import org.junit.Before; 28 | import org.junit.Rule; 29 | import org.junit.Test; 30 | import org.jvnet.hudson.test.JenkinsRule; 31 | import org.mockserver.junit.MockServerRule; 32 | import org.mockserver.model.HttpRequest; 33 | import org.mockserver.model.HttpResponse; 34 | import org.mockserver.model.JsonBody; 35 | import org.mockserver.verify.VerificationTimes; 36 | 37 | public class GerritCommentStepTest { 38 | 39 | @Rule public MockServerRule g = new MockServerRule(this); 40 | @Rule public JenkinsRule j = new JenkinsRule(); 41 | 42 | String projectName = "test-project"; 43 | int changeNumber = 4321; 44 | int revision = 1; 45 | String path = "/path/to/file"; 46 | int line = 1; 47 | String message = "Invalid spacing"; 48 | String branch = String.format("%02d/%d/%d", changeNumber % 100, changeNumber, revision); 49 | DraftInput draftInput; 50 | 51 | @Before 52 | public void setup() { 53 | draftInput = new DraftInput(); 54 | draftInput.path = path; 55 | draftInput.line = line; 56 | draftInput.message = message; 57 | } 58 | 59 | @Test 60 | public void gerritCommentStepInvokeTestForGerrit2_14() throws Exception { 61 | 62 | WorkflowJob p = createWorkflowJob(path, line, message, branch); 63 | 64 | setupServerVersion("2.14"); 65 | 66 | setupDrafts(String.valueOf(changeNumber)); 67 | 68 | WorkflowRun run = j.assertBuildStatusSuccess(p.scheduleBuild2(0)); 69 | String log = JenkinsRule.getLog(run); 70 | 71 | verifyDrafts(String.valueOf(changeNumber)); 72 | } 73 | 74 | @Test 75 | public void gerritCommentStepInvokeTestForGerrit2_16() throws Exception { 76 | WorkflowJob p = createWorkflowJob(path, line, message, branch); 77 | setupServerVersion("2.16"); 78 | 79 | String changeId = String.format("%s~%s", projectName, changeNumber); 80 | setupDrafts(changeId); 81 | 82 | WorkflowRun run = j.assertBuildStatusSuccess(p.scheduleBuild2(0)); 83 | String log = JenkinsRule.getLog(run); 84 | 85 | verifyDrafts(changeId); 86 | } 87 | 88 | private void verifyDrafts(String changeId) { 89 | g.getClient() 90 | .verify( 91 | HttpRequest.request( 92 | String.format("/a/changes/%s/revisions/%s/drafts", changeId, revision)), 93 | VerificationTimes.once()); 94 | } 95 | 96 | private void setupServerVersion(String version) { 97 | g.getClient() 98 | .when(HttpRequest.request("/a/config/server/version").withMethod("GET")) 99 | .respond(HttpResponse.response().withStatusCode(200).withBody(")]}'\n\"" + version + "\"")); 100 | } 101 | 102 | private void setupDrafts(String changeId) { 103 | g.getClient() 104 | .when( 105 | HttpRequest.request( 106 | String.format("/a/changes/%s/revisions/%s/drafts", changeId, revision)) 107 | .withMethod("PUT") 108 | .withBody(JsonBody.json(draftInput))) 109 | .respond( 110 | HttpResponse.response() 111 | .withStatusCode(200) 112 | .withBody(JsonBody.json(Collections.emptyMap()))); 113 | } 114 | 115 | private WorkflowJob createWorkflowJob(String path, int line, String message, String branch) 116 | throws IOException { 117 | UsernamePasswordCredentialsImpl c = 118 | new UsernamePasswordCredentialsImpl( 119 | CredentialsScope.GLOBAL, "cid", "cid", "USERNAME", "PASSWORD"); 120 | CredentialsProvider.lookupStores(j.jenkins) 121 | .iterator() 122 | .next() 123 | .addCredentials(Domain.global(), c); 124 | WorkflowJob p = j.jenkins.createProject(WorkflowJob.class, "p"); 125 | p.setDefinition( 126 | new CpsFlowDefinition( 127 | String.format( 128 | "" 129 | + "node {\n" 130 | + " withEnv([\n" 131 | + " 'GERRIT_API_URL=https://%s:%s/',\n" 132 | + " 'GERRIT_PROJECT=%s',\n" 133 | + " 'GERRIT_API_INSECURE_HTTPS=true',\n" 134 | + " 'GERRIT_CREDENTIALS_ID=cid',\n" 135 | + " 'BRANCH_NAME=%s',\n" 136 | + " ]) {\n" 137 | + " gerritComment path: '%s', line: %s, message: '%s'\n" 138 | + " }\n" 139 | + "}", 140 | g.getClient().remoteAddress().getHostString(), 141 | g.getClient().remoteAddress().getPort(), 142 | projectName, 143 | branch, 144 | path, 145 | line, 146 | message), 147 | true)); 148 | return p; 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /src/main/java/jenkins/plugins/gerrit/workflow/GerritCheckStep.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 SAP SE 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. 14 | 15 | package jenkins.plugins.gerrit.workflow; 16 | 17 | import com.google.gerrit.plugins.checks.api.CheckInput; 18 | import com.google.gerrit.plugins.checks.api.CheckState; 19 | import com.google.gerrit.plugins.checks.client.GerritChecksApi; 20 | import hudson.Extension; 21 | import hudson.model.Run; 22 | import hudson.model.TaskListener; 23 | import java.io.IOException; 24 | import java.net.URISyntaxException; 25 | import java.sql.Timestamp; 26 | import java.util.Collections; 27 | import java.util.Map; 28 | import java.util.Set; 29 | import javax.annotation.Nonnull; 30 | import jenkins.model.Jenkins; 31 | import jenkins.plugins.gerrit.GerritApiBuilder; 32 | import jenkins.plugins.gerrit.GerritChange; 33 | import org.jenkinsci.plugins.workflow.steps.Step; 34 | import org.jenkinsci.plugins.workflow.steps.StepContext; 35 | import org.jenkinsci.plugins.workflow.steps.StepDescriptor; 36 | import org.jenkinsci.plugins.workflow.steps.StepExecution; 37 | import org.jenkinsci.plugins.workflow.steps.SynchronousStepExecution; 38 | import org.kohsuke.stapler.DataBoundConstructor; 39 | import org.kohsuke.stapler.DataBoundSetter; 40 | 41 | public class GerritCheckStep extends Step { 42 | private Map checks; 43 | private String message = ""; 44 | private String url; 45 | 46 | @DataBoundConstructor 47 | public GerritCheckStep() {} 48 | 49 | public class Execution extends SynchronousStepExecution { 50 | private final TaskListener listener; 51 | private final String consoleLogUri; 52 | 53 | protected Execution(StepContext context) 54 | throws IOException, InterruptedException, URISyntaxException { 55 | super(context); 56 | this.listener = getContext().get(TaskListener.class); 57 | this.consoleLogUri = getConsoleLogUri(context); 58 | } 59 | 60 | @Override 61 | protected Void run() throws Exception { 62 | GerritChecksApi gerritChecksApi = 63 | new GerritApiBuilder().stepContext(getContext()).requireAuthentication().buildChecksApi(); 64 | if (gerritChecksApi == null) { 65 | return null; 66 | } 67 | GerritChange change = new GerritChange(getContext()); 68 | if (change.valid()) { 69 | listener 70 | .getLogger() 71 | .format( 72 | "Gerrit review change %d/%d checks %s (%s)%n", 73 | change.getChangeId(), change.getRevision(), checks, message); 74 | if (checks != null) { 75 | for (Map.Entry check : checks.entrySet()) { 76 | CheckInput input = new CheckInput(); 77 | input.checkerUuid = check.getKey(); 78 | input.state = CheckState.valueOf(check.getValue()); 79 | input.message = message; 80 | input.url = url != null ? url : consoleLogUri; 81 | input = setCheckTimestamps(input, input.state); 82 | gerritChecksApi 83 | .checks() 84 | .change(change.getChangeId()) 85 | .patchSet(change.getRevision()) 86 | .update(input); 87 | } 88 | } 89 | } 90 | return null; 91 | } 92 | } 93 | 94 | public Map getChecks() { 95 | return checks; 96 | } 97 | 98 | @DataBoundSetter 99 | public void setChecks(Map checks) { 100 | this.checks = checks; 101 | } 102 | 103 | public String getMessage() { 104 | return message; 105 | } 106 | 107 | @DataBoundSetter 108 | public void setMessage(String message) { 109 | this.message = message; 110 | } 111 | 112 | public String getUrl() { 113 | return url; 114 | } 115 | 116 | @DataBoundSetter 117 | public void setUrl(String url) { 118 | this.url = url; 119 | } 120 | 121 | @Override 122 | public StepExecution start(StepContext stepContext) throws Exception { 123 | return new Execution(stepContext); 124 | } 125 | 126 | private String getConsoleLogUri(StepContext stepContext) 127 | throws IOException, InterruptedException { 128 | String rootUrl = Jenkins.getInstance().getRootUrl(); 129 | if (rootUrl == null) { 130 | throw new NullPointerException("Jenkins URL has to be set in the Jenkins configuration."); 131 | } 132 | return rootUrl + stepContext.get(Run.class).getUrl() + "console"; 133 | } 134 | 135 | private CheckInput setCheckTimestamps(CheckInput input, CheckState state) { 136 | Timestamp now = new Timestamp(System.currentTimeMillis()); 137 | switch (state) { 138 | case RUNNING: 139 | input.started = now; 140 | break; 141 | case SUCCESSFUL: 142 | case FAILED: 143 | input.finished = now; 144 | break; 145 | case NOT_STARTED: 146 | case NOT_RELEVANT: 147 | case SCHEDULED: 148 | default: 149 | break; 150 | } 151 | 152 | return input; 153 | } 154 | 155 | @Extension 156 | public static class DescriptorImpl extends StepDescriptor { 157 | 158 | @Override 159 | public Set> getRequiredContext() { 160 | return Collections.emptySet(); 161 | } 162 | 163 | @Override 164 | public String getFunctionName() { 165 | return "gerritCheck"; 166 | } 167 | 168 | @Nonnull 169 | @Override 170 | public String getDisplayName() { 171 | return "Gerrit Review Check"; 172 | } 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /src/main/java/jenkins/plugins/gerrit/workflow/GerritReviewStep.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 GerritForge Ltd 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. 14 | 15 | package jenkins.plugins.gerrit.workflow; 16 | 17 | import com.google.gerrit.extensions.api.GerritApi; 18 | import com.google.gerrit.extensions.api.changes.ChangeApi; 19 | import com.google.gerrit.extensions.api.changes.Changes; 20 | import com.google.gerrit.extensions.api.changes.NotifyHandling; 21 | import com.google.gerrit.extensions.api.changes.ReviewInput; 22 | import com.google.gerrit.extensions.restapi.RestApiException; 23 | import hudson.EnvVars; 24 | import hudson.Extension; 25 | import hudson.model.TaskListener; 26 | import java.io.IOException; 27 | import java.util.Collections; 28 | import java.util.Map; 29 | import java.util.Optional; 30 | import java.util.Set; 31 | import javax.annotation.Nonnull; 32 | import jenkins.plugins.gerrit.GerritApiBuilder; 33 | import jenkins.plugins.gerrit.GerritChange; 34 | import jenkins.plugins.gerrit.GerritVersion; 35 | import org.jenkinsci.plugins.workflow.steps.*; 36 | import org.kohsuke.stapler.DataBoundConstructor; 37 | import org.kohsuke.stapler.DataBoundSetter; 38 | 39 | public class GerritReviewStep extends Step { 40 | @Deprecated private String label = "Verified"; 41 | @Deprecated private int score; 42 | private Map labels; 43 | private String message = ""; 44 | private Optional notify = Optional.empty(); 45 | 46 | @DataBoundConstructor 47 | public GerritReviewStep() {} 48 | 49 | public class Execution extends SynchronousStepExecution { 50 | private final TaskListener listener; 51 | private final EnvVars envVars; 52 | 53 | protected Execution(@Nonnull StepContext context) throws IOException, InterruptedException { 54 | super(context); 55 | this.envVars = context.get(EnvVars.class); 56 | this.listener = getContext().get(TaskListener.class); 57 | } 58 | 59 | @Override 60 | protected Void run() throws Exception { 61 | GerritApi gerritApi = 62 | new GerritApiBuilder().stepContext(getContext()).requireAuthentication().build(); 63 | if (gerritApi == null) { 64 | return null; 65 | } 66 | 67 | GerritChange change = new GerritChange(getContext()); 68 | if (change.valid()) { 69 | ReviewInput reviewInput = new ReviewInput().message(message); 70 | boolean negativeVote = false; 71 | if (labels == null && label != null) { 72 | labels = Collections.singletonMap(label, score); 73 | } 74 | listener 75 | .getLogger() 76 | .format( 77 | "Gerrit review change %d/%d labels %s (%s)%n", 78 | change.getChangeId(), change.getRevision(), labels, message); 79 | if (labels != null) { 80 | for (Map.Entry l : labels.entrySet()) { 81 | reviewInput.label(l.getKey(), l.getValue()); 82 | if (l.getValue() < 0) { 83 | negativeVote = true; 84 | } 85 | } 86 | } 87 | reviewInput.drafts = ReviewInput.DraftHandling.PUBLISH; 88 | reviewInput.tag = "autogenerated:jenkins"; 89 | if (notify.isPresent()) { 90 | reviewInput.notify = notify.get(); 91 | } else { 92 | String notifyProp = System.getProperty("gerrit.notify"); 93 | if (notifyProp != null) { 94 | reviewInput.notify = NotifyHandling.valueOf(notifyProp); 95 | } else if (negativeVote) { 96 | reviewInput.notify = NotifyHandling.OWNER; 97 | } 98 | } 99 | getChangeApi(gerritApi, change).revision(change.getRevision()).review(reviewInput); 100 | } 101 | return null; 102 | } 103 | } 104 | 105 | private ChangeApi getChangeApi(GerritApi gerritApi, GerritChange change) throws RestApiException { 106 | Changes changesApi = gerritApi.changes(); 107 | return GerritVersion.isVersionBelow215(gerritApi) 108 | ? changesApi.id(change.getChangeId()) 109 | : changesApi.id(change.getProject(), change.getChangeId()); 110 | } 111 | 112 | @Deprecated 113 | public int getScore() { 114 | return score; 115 | } 116 | 117 | @Deprecated 118 | @DataBoundSetter 119 | public void setScore(int score) { 120 | this.score = score; 121 | } 122 | 123 | public Map getLabels() { 124 | return labels; 125 | } 126 | 127 | @DataBoundSetter 128 | public void setLabels(Map labels) { 129 | this.labels = labels; 130 | } 131 | 132 | public String getMessage() { 133 | return message; 134 | } 135 | 136 | @DataBoundSetter 137 | public void setMessage(String message) { 138 | this.message = message; 139 | } 140 | 141 | @DataBoundSetter 142 | public void setNotify(NotifyHandling notify) { 143 | this.notify = Optional.ofNullable(notify); 144 | } 145 | 146 | public Optional getNotify() { 147 | return notify; 148 | } 149 | 150 | @Deprecated 151 | public String getLabel() { 152 | return label; 153 | } 154 | 155 | @Deprecated 156 | @DataBoundSetter 157 | public void setLabel(String label) { 158 | this.label = label; 159 | } 160 | 161 | @Override 162 | public StepExecution start(StepContext stepContext) throws Exception { 163 | return new Execution(stepContext); 164 | } 165 | 166 | @Extension 167 | public static class DescriptorImpl extends StepDescriptor { 168 | 169 | @Override 170 | public Set> getRequiredContext() { 171 | return Collections.emptySet(); 172 | } 173 | 174 | @Override 175 | public String getFunctionName() { 176 | return "gerritReview"; 177 | } 178 | 179 | @Nonnull 180 | @Override 181 | public String getDisplayName() { 182 | return "Gerrit Review Label"; 183 | } 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /src/test/java/jenkins/plugins/gerrit/GerritSCMNavigatorTest.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022 Réda Housni Alaoui 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. 14 | 15 | package jenkins.plugins.gerrit; 16 | 17 | import static org.junit.Assert.assertEquals; 18 | import static org.junit.Assert.assertTrue; 19 | import static org.mockito.Mockito.mock; 20 | 21 | import com.google.gerrit.extensions.common.ProjectInfo; 22 | import edu.umd.cs.findbugs.annotations.NonNull; 23 | import edu.umd.cs.findbugs.annotations.Nullable; 24 | import hudson.model.TaskListener; 25 | import hudson.util.LogTaskListener; 26 | import java.io.IOException; 27 | import java.util.ArrayList; 28 | import java.util.Collections; 29 | import java.util.HashMap; 30 | import java.util.List; 31 | import java.util.Map; 32 | import java.util.logging.Level; 33 | import java.util.logging.Logger; 34 | import jenkins.plugins.git.traits.RefSpecsSCMSourceTrait; 35 | import jenkins.scm.api.SCMSource; 36 | import jenkins.scm.api.SCMSourceObserver; 37 | import jenkins.scm.api.SCMSourceOwner; 38 | import jenkins.scm.api.trait.SCMSourceTrait; 39 | import org.apache.commons.lang3.StringUtils; 40 | import org.junit.Rule; 41 | import org.junit.Test; 42 | 43 | public class GerritSCMNavigatorTest { 44 | 45 | @Rule public GerritMockServerRule g = new GerritMockServerRule(this); 46 | 47 | @Test 48 | public void testId() { 49 | GerritSCMNavigator navigator = 50 | new GerritSCMNavigator(g.getUrl(), false, null, Collections.emptyList()); 51 | assertEquals( 52 | GerritSCMNavigator.class.getName() + "::server-url=" + g.getUrl() + "::credentials-id=null", 53 | navigator.getId()); 54 | } 55 | 56 | @Test 57 | public void visitSources() throws IOException, InterruptedException { 58 | g.addProject(createProject("foo")); 59 | g.addProject(createProject("bar")); 60 | 61 | GerritSCMNavigator navigator = 62 | new GerritSCMNavigator(g.getUrl(), false, null, Collections.emptyList()); 63 | RecordingSCMSourceObserver sourceObserver = new RecordingSCMSourceObserver(); 64 | navigator.visitSources(sourceObserver); 65 | 66 | assertEquals(2, sourceObserver.observedProjectNames.size()); 67 | assertTrue(sourceObserver.observedProjectNames.contains("foo")); 68 | assertTrue(sourceObserver.observedProjectNames.contains("bar")); 69 | 70 | assertEquals(2, sourceObserver.addedSourceByProjectName.size()); 71 | assertSCMSourceValidity( 72 | sourceObserver.addedSourceByProjectName.get("foo"), 73 | "foo", 74 | navigator, 75 | Collections.emptyList()); 76 | assertSCMSourceValidity( 77 | sourceObserver.addedSourceByProjectName.get("bar"), 78 | "bar", 79 | navigator, 80 | Collections.emptyList()); 81 | } 82 | 83 | @Test 84 | public void sourcesShareCommonPropertiesWithTheNavigator() 85 | throws IOException, InterruptedException { 86 | ProjectInfo project = createProject("foo"); 87 | g.addProject(project); 88 | RefSpecsSCMSourceTrait trait = new RefSpecsSCMSourceTrait(); 89 | GerritSCMNavigator navigator = 90 | new GerritSCMNavigator( 91 | g.getUrl(), true, "my-credentials", Collections.singletonList(trait)); 92 | 93 | RecordingSCMSourceObserver sourceObserver = new RecordingSCMSourceObserver(); 94 | navigator.visitSources(sourceObserver); 95 | 96 | assertEquals(1, sourceObserver.observedProjectNames.size()); 97 | assertEquals(project.id, sourceObserver.observedProjectNames.get(0)); 98 | 99 | assertEquals(1, sourceObserver.addedSourceByProjectName.size()); 100 | assertSCMSourceValidity( 101 | sourceObserver.addedSourceByProjectName.get(project.id), 102 | project.id, 103 | navigator, 104 | Collections.singletonList(trait)); 105 | } 106 | 107 | private void assertSCMSourceValidity( 108 | SCMSource scmSource, 109 | String projectName, 110 | GerritSCMNavigator navigator, 111 | List traits) { 112 | GerritSCMSource source = (GerritSCMSource) scmSource; 113 | assertEquals( 114 | StringUtils.appendIfMissing(navigator.getServerUrl(), "/") + projectName, 115 | source.getRemote()); 116 | assertEquals(navigator.isInsecureHttps(), source.getInsecureHttps()); 117 | assertEquals(navigator.getCredentialsId(), source.getCredentialsId()); 118 | assertEquals(traits.size(), source.getTraits().size()); 119 | traits.forEach(trait -> assertTrue(source.getTraits().contains(trait))); 120 | } 121 | 122 | private ProjectInfo createProject(String name) { 123 | ProjectInfo projectInfo = new ProjectInfo(); 124 | projectInfo.id = name; 125 | return projectInfo; 126 | } 127 | 128 | private static class RecordingSCMSourceObserver extends SCMSourceObserver { 129 | 130 | private final List observedProjectNames = new ArrayList<>(); 131 | private final Map addedSourceByProjectName = new HashMap<>(); 132 | 133 | @NonNull 134 | @Override 135 | public SCMSourceOwner getContext() { 136 | return mock(SCMSourceOwner.class); 137 | } 138 | 139 | @NonNull 140 | @Override 141 | public TaskListener getListener() { 142 | return new LogTaskListener(Logger.getAnonymousLogger(), Level.INFO); 143 | } 144 | 145 | @NonNull 146 | @Override 147 | public ProjectObserver observe(@NonNull String projectName) throws IllegalArgumentException { 148 | observedProjectNames.add(projectName); 149 | return new ProjectObserver() { 150 | 151 | @Override 152 | public void addSource(@NonNull SCMSource source) { 153 | if (addedSourceByProjectName.put(projectName, source) == null) { 154 | return; 155 | } 156 | throw new IllegalArgumentException("Duplicate source for project " + projectName); 157 | } 158 | 159 | @Override 160 | public void addAttribute(@NonNull String key, @Nullable Object value) 161 | throws IllegalArgumentException, ClassCastException {} 162 | 163 | @Override 164 | public void complete() throws IllegalStateException {} 165 | }; 166 | } 167 | 168 | @Override 169 | public void addAttribute(@NonNull String key, @Nullable Object value) 170 | throws IllegalArgumentException, ClassCastException {} 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /src/main/java/com/google/gerrit/plugins/checks/client/Checks.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 The Android Open Source Project 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. 14 | 15 | package com.google.gerrit.plugins.checks.client; 16 | 17 | import com.google.gerrit.extensions.restapi.RestApiException; 18 | import com.google.gerrit.plugins.checks.api.CheckInfo; 19 | import com.google.gerrit.plugins.checks.api.CheckInput; 20 | import com.google.gerrit.plugins.checks.api.RerunInput; 21 | import com.google.gson.reflect.TypeToken; 22 | import java.io.IOException; 23 | import java.net.URI; 24 | import java.net.URISyntaxException; 25 | import java.util.List; 26 | import org.apache.http.HttpStatus; 27 | import org.apache.http.ParseException; 28 | import org.apache.http.client.methods.CloseableHttpResponse; 29 | import org.apache.http.client.methods.HttpGet; 30 | import org.apache.http.client.methods.HttpPost; 31 | import org.apache.http.entity.StringEntity; 32 | import org.apache.http.impl.client.CloseableHttpClient; 33 | import org.apache.http.util.EntityUtils; 34 | import org.eclipse.jgit.transport.URIish; 35 | 36 | public class Checks extends AbstractEndpoint { 37 | 38 | private int changeNumber; 39 | private int patchSetNumber; 40 | 41 | public Checks(URIish gerritBaseUrl, CloseableHttpClient client, boolean isAuthenticated) 42 | throws URISyntaxException { 43 | super(gerritBaseUrl, client, isAuthenticated); 44 | } 45 | 46 | public Checks change(int changeNumber) { 47 | this.changeNumber = changeNumber; 48 | return this; 49 | } 50 | 51 | public Checks patchSet(int patchSetNumber) { 52 | this.patchSetNumber = patchSetNumber; 53 | return this; 54 | } 55 | 56 | public CheckInfo create(CheckInput input) throws RestApiException { 57 | try { 58 | return performCreateOrUpdate(input); 59 | } catch (Exception e) { 60 | throw new RestApiException("Could not create check", e); 61 | } 62 | } 63 | 64 | public CheckInfo get(String checkerUuid) throws RestApiException { 65 | try { 66 | HttpGet request = new HttpGet(buildRequestUrl(checkerUuid)); 67 | try (CloseableHttpResponse response = client.execute(request)) { 68 | if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { 69 | return JsonBodyParser.parseResponse( 70 | EntityUtils.toString(response.getEntity()), new TypeToken() {}.getType()); 71 | } 72 | throw new RestApiException( 73 | String.format( 74 | "Request failed with status: %d", response.getStatusLine().getStatusCode())); 75 | } 76 | } catch (Exception e) { 77 | throw new RestApiException("Failed to get check info: ", e); 78 | } 79 | } 80 | 81 | public List list() throws RestApiException { 82 | try { 83 | HttpGet request = new HttpGet(buildRequestUrl()); 84 | try (CloseableHttpResponse response = client.execute(request)) { 85 | if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { 86 | return JsonBodyParser.parseResponse( 87 | EntityUtils.toString(response.getEntity()), 88 | new TypeToken>() {}.getType()); 89 | } 90 | throw new RestApiException( 91 | String.format( 92 | "Request failed with status: %d", response.getStatusLine().getStatusCode())); 93 | } 94 | } catch (Exception e) { 95 | throw new RestApiException("Failed to list checks: ", e); 96 | } 97 | } 98 | 99 | public CheckInfo rerun(String checkerUuid) throws RestApiException { 100 | return rerun(checkerUuid, new RerunInput()); 101 | } 102 | 103 | public CheckInfo rerun(String checkerUuid, RerunInput input) throws RestApiException { 104 | try { 105 | HttpPost request = new HttpPost(buildRequestUrl(checkerUuid + "/rerun")); 106 | String inputString = 107 | JsonBodyParser.createRequestBody(input, new TypeToken() {}.getType()); 108 | request.setEntity(new StringEntity(inputString)); 109 | request.setHeader("Content-type", "application/json"); 110 | try (CloseableHttpResponse response = client.execute(request)) { 111 | if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { 112 | return JsonBodyParser.parseResponse( 113 | EntityUtils.toString(response.getEntity()), new TypeToken() {}.getType()); 114 | } 115 | throw new RestApiException( 116 | String.format( 117 | "Request failed with status: %d", response.getStatusLine().getStatusCode())); 118 | } 119 | } catch (Exception e) { 120 | throw new RestApiException("Could not rerun check", e); 121 | } 122 | } 123 | 124 | public CheckInfo update(CheckInput input) throws RestApiException { 125 | try { 126 | return performCreateOrUpdate(input); 127 | } catch (Exception e) { 128 | throw new RestApiException("Could not update check", e); 129 | } 130 | } 131 | 132 | private CheckInfo performCreateOrUpdate(CheckInput input) 133 | throws RestApiException, URISyntaxException, ParseException, IOException { 134 | HttpPost request = new HttpPost(buildRequestUrl()); 135 | String inputString = 136 | JsonBodyParser.createRequestBody(input, new TypeToken() {}.getType()); 137 | request.setEntity(new StringEntity(inputString)); 138 | request.setHeader("Content-type", "application/json"); 139 | 140 | try (CloseableHttpResponse response = client.execute(request)) { 141 | if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { 142 | return JsonBodyParser.parseResponse( 143 | EntityUtils.toString(response.getEntity()), new TypeToken() {}.getType()); 144 | } 145 | throw new RestApiException( 146 | String.format( 147 | "POST %s with body '%s' returned status %d (%s)", 148 | request.getURI(), 149 | inputString, 150 | response.getStatusLine().getStatusCode(), 151 | EntityUtils.toString(response.getEntity()))); 152 | } 153 | } 154 | 155 | private URI buildRequestUrl() throws URISyntaxException { 156 | return buildRequestUrl(""); 157 | } 158 | 159 | private URI buildRequestUrl(String suffixPath) throws URISyntaxException { 160 | return uriBuilder 161 | .setPath( 162 | String.format( 163 | "%schanges/%d/revisions/%d/checks/%s", 164 | getPrefix(), changeNumber, patchSetNumber, suffixPath)) 165 | .build(); 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /src/test/java/jenkins/plugins/gerrit/GerritEnvironmentContributorTest.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023 GerritForge Ltd 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. 14 | 15 | package jenkins.plugins.gerrit; 16 | 17 | import static com.google.common.truth.Truth.assertThat; 18 | 19 | import com.google.gerrit.extensions.client.ChangeKind; 20 | import com.google.gerrit.extensions.common.AccountInfo; 21 | import com.google.gerrit.extensions.common.ChangeInfo; 22 | import com.google.gerrit.extensions.common.RevisionInfo; 23 | import java.net.URISyntaxException; 24 | import java.util.HashMap; 25 | import java.util.Map; 26 | import java.util.Optional; 27 | import jenkins.plugins.gerrit.GerritEnvironmentContributor.ChangeInfoInvisibleAction; 28 | import org.eclipse.jgit.transport.URIish; 29 | import org.junit.Before; 30 | import org.junit.Test; 31 | 32 | public class GerritEnvironmentContributorTest { 33 | 34 | public static final String TEST_PROJECT_NAME = "myproject"; 35 | public static final AccountInfo TEST_ACCOUNT_INFO_JOHN_DOE = 36 | new AccountInfo("John Doe", "john.doe@mycompany.com"); 37 | public static final String TEST_BRANCH = "mybranch"; 38 | public static final String TEST_CHANGE_SUBJECT = "This is a test change"; 39 | public static final String TEST_CHANGE_TOPIC = "test-topic"; 40 | public static final String TEST_CHANGE_ID = "I2ff60b01ab0e2305fdf8739cd884038091f2b888"; 41 | public static final String TEST_CHANGE_ID_TRIPLET = 42 | TEST_PROJECT_NAME + "~" + TEST_BRANCH + "~" + TEST_CHANGE_ID; 43 | public static final int TEST_PATCHSET_NUMBER = 2; 44 | public static final int TEST_CHANGE_NUMBER = 1; 45 | public static final String TEST_CHANGE_KIND = ChangeKind.TRIVIAL_REBASE.name(); 46 | public static final int TEST_REVERTED_CHANGE_NUMBER = 3; 47 | public static final String TEST_CHANGE_REF_NAME = 48 | "refs/changes/" 49 | + String.format("%02d", TEST_CHANGE_NUMBER) 50 | + "/" 51 | + TEST_CHANGE_NUMBER 52 | + "/" 53 | + TEST_PATCHSET_NUMBER; 54 | public static final AccountInfo TEST_ACCOUNT_INFO_MATT_SMITH = 55 | new AccountInfo("Matt Smith", "matt.smith@mycompany.com"); 56 | public static final String TEST_GERRIT_URL = "http://gerrit.mycompany.com"; 57 | private ChangeInfo changeInfo; 58 | private GerritURI gerritURI; 59 | 60 | @Before 61 | public void setup() throws URISyntaxException { 62 | changeInfo = 63 | new ChangeInfo() { 64 | { 65 | this._number = TEST_CHANGE_NUMBER; 66 | this.project = TEST_PROJECT_NAME; 67 | this.owner = TEST_ACCOUNT_INFO_JOHN_DOE; 68 | this.branch = TEST_BRANCH; 69 | this.subject = TEST_CHANGE_SUBJECT; 70 | this.topic = TEST_CHANGE_TOPIC; 71 | this.changeId = TEST_CHANGE_ID; 72 | this.id = TEST_CHANGE_ID_TRIPLET; 73 | this.revisions = 74 | new HashMap() { 75 | { 76 | put( 77 | Integer.toString(TEST_PATCHSET_NUMBER), 78 | new RevisionInfo() { 79 | { 80 | this._number = TEST_PATCHSET_NUMBER; 81 | this.uploader = TEST_ACCOUNT_INFO_MATT_SMITH; 82 | this.ref = TEST_CHANGE_REF_NAME; 83 | this.kind = ChangeKind.TRIVIAL_REBASE; 84 | } 85 | }); 86 | } 87 | }; 88 | this.revertOf = TEST_REVERTED_CHANGE_NUMBER; 89 | } 90 | }; 91 | gerritURI = new GerritURI(new URIish(TEST_GERRIT_URL)); 92 | } 93 | 94 | @Test 95 | public void testBuildEnvironmentForChangeInfo() throws Exception { 96 | Map changeEnvs = 97 | new ChangeInfoInvisibleAction(Optional.of(changeInfo), TEST_PATCHSET_NUMBER, gerritURI) 98 | .getChangeEnvs(); 99 | Map expectedMap = 100 | new HashMap() { 101 | { 102 | put("GERRIT_BRANCH", TEST_BRANCH); 103 | put("GERRIT_PATCHSET_UPLOADER_NAME", TEST_ACCOUNT_INFO_MATT_SMITH.name); 104 | put( 105 | "GERRIT_CHANGE_OWNER", 106 | TEST_ACCOUNT_INFO_JOHN_DOE.name + " <" + TEST_ACCOUNT_INFO_JOHN_DOE.email + ">"); 107 | put("GERRIT_CHANGE_OWNER_NAME", TEST_ACCOUNT_INFO_JOHN_DOE.name); 108 | put("GERRIT_CHANGE_OWNER_EMAIL", TEST_ACCOUNT_INFO_JOHN_DOE.email); 109 | put( 110 | "GERRIT_PATCHSET_UPLOADER", 111 | TEST_ACCOUNT_INFO_MATT_SMITH.name 112 | + " <" 113 | + TEST_ACCOUNT_INFO_MATT_SMITH.email 114 | + ">"); 115 | put("GERRIT_PATCHSET_UPLOADER_NAME", TEST_ACCOUNT_INFO_MATT_SMITH.name); 116 | put("GERRIT_PATCHSET_UPLOADER_EMAIL", TEST_ACCOUNT_INFO_MATT_SMITH.email); 117 | put("GERRIT_CHANGE_SUBJECT", TEST_CHANGE_SUBJECT); 118 | put("GERRIT_TOPIC", TEST_CHANGE_TOPIC); 119 | put("GERRIT_REFNAME", TEST_CHANGE_REF_NAME); 120 | put("GERRIT_CHANGE_URL", TEST_GERRIT_URL + "/" + TEST_CHANGE_NUMBER); 121 | put("GERRIT_CHANGE_NUMBER", Integer.toString(TEST_CHANGE_NUMBER)); 122 | put("GERRIT_PATCHSET_KIND", TEST_CHANGE_KIND); 123 | put("GERRIT_PATCHSET_REVISION", Integer.toString(TEST_PATCHSET_NUMBER)); 124 | put("GERRIT_PATCHSET_NUMBER", Integer.toString(TEST_PATCHSET_NUMBER)); 125 | put("GERRIT_CHANGE_WIP_STATE", "false"); 126 | put("GERRIT_CHANGE_ID", TEST_CHANGE_ID_TRIPLET); 127 | put("GERRIT_CHANGE_PRIVATE_STATE", "false"); 128 | put("GERRIT_REFSPEC", TEST_CHANGE_REF_NAME); 129 | put("GERRIT_REVERTED_CHANGE_NUMBER", Integer.toString(TEST_REVERTED_CHANGE_NUMBER)); 130 | } 131 | }; 132 | assertThat(changeEnvs).containsExactlyEntriesIn(expectedMap); 133 | } 134 | 135 | @Test 136 | public void testBuildEnvironmentForPrivateChange() { 137 | changeInfo.isPrivate = true; 138 | assertThat( 139 | new ChangeInfoInvisibleAction(Optional.of(changeInfo), TEST_PATCHSET_NUMBER, gerritURI) 140 | .getChangeEnvs()) 141 | .containsEntry("GERRIT_CHANGE_PRIVATE_STATE", "true"); 142 | } 143 | 144 | @Test 145 | public void testBuildEnvironmentForWipChange() { 146 | changeInfo.workInProgress = true; 147 | assertThat( 148 | new ChangeInfoInvisibleAction(Optional.of(changeInfo), TEST_PATCHSET_NUMBER, gerritURI) 149 | .getChangeEnvs()) 150 | .containsEntry("GERRIT_CHANGE_WIP_STATE", "true"); 151 | } 152 | } 153 | --------------------------------------------------------------------------------