Skip to content

Commit fefe3ba

Browse files
Aliaksandr Kavalevichterma
authored andcommitted
Add sonar login/password and Token authentication. (#18)
* Add sonar token for authentication. * Issue-17: Add support for Jenkins pipelines. * Issue-17: Add login/password and Oath token possibility.
1 parent 22d21af commit fefe3ba

16 files changed

Lines changed: 167 additions & 56 deletions

File tree

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ Parse code coverage report generated by build and post code coverage status comm
4949
* step([$class: 'MasterCoverageAction'])
5050
* Trigger CompareCoverageAction to compare coverage and publish results
5151
* step([$class: 'CompareCoverageAction'])
52-
52+
* Optionally use can specify here sonar login and sonar password like this:
53+
step([$class: 'CompareCoverageAction', sonarLogin: "login", sonarPassword: "password"])
5354
## Master Coverage from Sonar
5455

5556
You have the option to get the master coverage (base coverage) from your SonarQube instance. Otherwise the plugin will keep track of the master coverage in the project configuration file.
@@ -58,11 +59,10 @@ You have the option to get the master coverage (base coverage) from your SonarQu
5859
* Find section ```Coverage status for GitHub Pull Requests```
5960
* Fill ```Sonar URL``` field - e.g. `http://sonar.mycompany.com`
6061
* Turn On ```Use Sonar for master coverage```
61-
* The next pullrequest build will use SonarQube as the the source for master coverage
62-
63-
The plugin will try to find the project in SonarQube based on the repository name. If more than one projects match, a waring is logged, and the first one will be used to get the coverage data.
62+
* Optionally use can specify your personal ```Sonar access token```.
63+
* The next pull request build will use SonarQube as the the source for master coverage
6464

65-
This feature is still very basic. e.g. authorization against SonarQube is not yet implemented. Feel free to contribute.
65+
The plugin will try to find the project in SonarQube based on the repository name. If more than one projects match, a warning is logged, and the first one will be used to get the coverage data.
6666

6767
## Troubleshooting
6868

src/main/java/com/github/terma/jenkins/githubprcoveragestatus/CompareCoverageAction.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.io.PrintStream;
2222
import java.io.PrintWriter;
2323

24+
import org.kohsuke.github.GHRepository;
2425
import org.kohsuke.stapler.DataBoundConstructor;
2526

2627
import hudson.Extension;
@@ -35,16 +36,29 @@
3536
import hudson.tasks.Publisher;
3637
import hudson.tasks.Recorder;
3738
import jenkins.tasks.SimpleBuildStep;
39+
import org.kohsuke.stapler.DataBoundSetter;
3840

3941
@SuppressWarnings({"unused", "WeakerAccess"})
4042
public class CompareCoverageAction extends Recorder implements SimpleBuildStep {
4143

4244
private static final long serialVersionUID = 1L;
45+
private String sonarLogin;
46+
private String sonarPassword;
4347

4448
@DataBoundConstructor
4549
public CompareCoverageAction() {
4650
}
4751

52+
@DataBoundSetter
53+
public void setSonarLogin(String sonarLogin) {
54+
this.sonarLogin = sonarLogin;
55+
}
56+
57+
@DataBoundSetter
58+
public void setSonarPassword(String sonarPassword) {
59+
this.sonarPassword = sonarPassword;
60+
}
61+
4862
// todo show message that addition comment in progress as it could take a while
4963
@SuppressWarnings("NullableProblems")
5064
@Override
@@ -64,8 +78,9 @@ public void perform(
6478
"https://wiki.jenkins-ci.org/display/JENKINS/GitHub+pull+request+builder+plugin " +
6579
"to trigger build!");
6680
}
81+
final GHRepository gitHubRepository = ServiceRegistry.getPullRequestRepository().getGitHubRepository(gitUrl);
6782

68-
final float masterCoverage = ServiceRegistry.getMasterCoverageRepository(buildLog).get(gitUrl);
83+
final float masterCoverage = ServiceRegistry.getMasterCoverageRepository(buildLog, sonarLogin, sonarPassword).get(gitHubRepository.getName());
6984
final float coverage = ServiceRegistry.getCoverageRepository().get(workspace);
7085

7186
final Message message = new Message(coverage, masterCoverage);
@@ -85,7 +100,7 @@ public void perform(
85100
settingsRepository.getYellowThreshold(),
86101
settingsRepository.getGreenThreshold(),
87102
settingsRepository.isPrivateJenkinsPublicGitHub());
88-
ServiceRegistry.getPullRequestRepository().comment(gitUrl, prId, comment);
103+
ServiceRegistry.getPullRequestRepository().comment(gitHubRepository, prId, comment);
89104
} catch (Exception ex) {
90105
PrintWriter pw = listener.error("Couldn't add comment to pull request #" + prId + "!");
91106
ex.printStackTrace(pw);

src/main/java/com/github/terma/jenkins/githubprcoveragestatus/Configuration.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,18 @@ public static String getSonarUrl() {
6161
return DESCRIPTOR.getSonarUrl();
6262
}
6363

64+
public static String getSonarToken() {
65+
return DESCRIPTOR.getSonarToken();
66+
}
67+
68+
public static String getSonarLogin() {
69+
return DESCRIPTOR.getSonarLogin();
70+
}
71+
72+
public static String getSonarPassword() {
73+
return DESCRIPTOR.getSonarPassword();
74+
}
75+
6476
public static Boolean isUseSonarForMasterCoverage() {
6577
return DESCRIPTOR.isUseSonarForMasterCoverage();
6678
}
@@ -89,6 +101,9 @@ public static final class ConfigurationDescriptor extends Descriptor<Configurati
89101
private boolean privateJenkinsPublicGitHub;
90102
private boolean useSonarForMasterCoverage;
91103
private String sonarUrl;
104+
private String sonarToken;
105+
private String sonarLogin;
106+
private String sonarPassword;
92107

93108
private int yellowThreshold = DEFAULT_YELLOW_THRESHOLD;
94109
private int greenThreshold = DEFAULT_GREEN_THRESHOLD;
@@ -152,11 +167,24 @@ public String getSonarUrl() {
152167
return sonarUrl;
153168
}
154169

170+
@Override
171+
public String getSonarToken() {
172+
return sonarToken;
173+
}
174+
155175
@Override
156176
public String getJenkinsUrl() {
157177
return jenkinsUrl;
158178
}
159179

180+
public String getSonarLogin() {
181+
return sonarLogin;
182+
}
183+
184+
public String getSonarPassword() {
185+
return sonarPassword;
186+
}
187+
160188
@Override
161189
public boolean configure(StaplerRequest req, JSONObject formData) throws FormException {
162190
gitHubApiUrl = StringUtils.trimToNull(formData.getString("gitHubApiUrl"));
@@ -167,6 +195,9 @@ public boolean configure(StaplerRequest req, JSONObject formData) throws FormExc
167195
privateJenkinsPublicGitHub = BooleanUtils.toBoolean(formData.getString("privateJenkinsPublicGitHub"));
168196
useSonarForMasterCoverage = BooleanUtils.toBoolean(formData.getString("useSonarForMasterCoverage"));
169197
sonarUrl = StringUtils.trimToNull(formData.getString("sonarUrl"));
198+
sonarToken = StringUtils.trimToNull(formData.getString("sonarToken"));
199+
sonarLogin = StringUtils.trimToNull(formData.getString("sonarLogin"));
200+
sonarPassword = StringUtils.trimToNull(formData.getString("sonarPassword"));
170201
save();
171202
return super.configure(req, formData);
172203
}

src/main/java/com/github/terma/jenkins/githubprcoveragestatus/GitHubPullRequestRepository.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88

99
public class GitHubPullRequestRepository implements PullRequestRepository {
1010

11-
private static GHRepository getGitHubRepository(final String gitHubUrl) throws IOException {
11+
@Override
12+
public GHRepository getGitHubRepository(final String gitHubUrl) throws IOException {
1213
GitHub gitHub = getGitHub();
1314

1415
try {
@@ -52,8 +53,8 @@ private static GitHub getGitHub() throws IOException {
5253
}
5354

5455
@Override
55-
public void comment(String gitUrl, int prId, String message) throws IOException {
56-
getGitHubRepository(gitUrl).getPullRequest(prId).comment(message);
56+
public void comment(final GHRepository ghRepository, final int prId, final String message) throws IOException {
57+
ghRepository.getPullRequest(prId).comment(message);
5758
}
5859

5960
}

src/main/java/com/github/terma/jenkins/githubprcoveragestatus/MasterCoverageRepository.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22

33
interface MasterCoverageRepository {
44

5-
float get(String gitUrl);
5+
float get(final String repoName);
66

77
}

src/main/java/com/github/terma/jenkins/githubprcoveragestatus/PullRequestRepository.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,12 @@
22

33
import java.io.IOException;
44

5+
import org.kohsuke.github.GHRepository;
6+
57
interface PullRequestRepository {
68

7-
void comment(String gitUrl, int prId, String message) throws IOException;
9+
GHRepository getGitHubRepository(final String gitHubUrl) throws IOException;
10+
11+
void comment(GHRepository ghRepository, int prId, String message) throws IOException;
812

913
}

src/main/java/com/github/terma/jenkins/githubprcoveragestatus/ServiceRegistry.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,19 @@ public class ServiceRegistry {
99
private static SettingsRepository settingsRepository;
1010
private static PullRequestRepository pullRequestRepository;
1111

12-
public static MasterCoverageRepository getMasterCoverageRepository(PrintStream buildLog) {
12+
public static MasterCoverageRepository getMasterCoverageRepository(PrintStream buildLog, final String login, final String password) {
1313
if (masterCoverageRepository != null) {
1414
return masterCoverageRepository;
1515
} else {
1616
if (Configuration.isUseSonarForMasterCoverage()) {
17-
return new SonarMasterCoverageRepository(Configuration.getSonarUrl(), buildLog);
17+
final String sonarUrl = Configuration.getSonarUrl();
18+
if (login != null && password != null) {
19+
return new SonarMasterCoverageRepository(sonarUrl, login, password, buildLog);
20+
}
21+
if (Configuration.getSonarToken() != null) {
22+
return new SonarMasterCoverageRepository(sonarUrl, Configuration.getSonarToken(), "", buildLog);
23+
}
24+
return new SonarMasterCoverageRepository(sonarUrl, Configuration.getSonarLogin(), Configuration.getSonarPassword(), buildLog);
1825
} else {
1926
return Configuration.DESCRIPTOR;
2027
}

src/main/java/com/github/terma/jenkins/githubprcoveragestatus/SettingsRepository.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,6 @@ interface SettingsRepository {
1717
boolean isUseSonarForMasterCoverage();
1818

1919
String getSonarUrl();
20+
21+
String getSonarToken();
2022
}

src/main/java/com/github/terma/jenkins/githubprcoveragestatus/SonarMasterCoverageRepository.java

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
import com.fasterxml.jackson.core.type.TypeReference;
55
import com.fasterxml.jackson.databind.ObjectMapper;
66
import org.apache.commons.httpclient.HttpClient;
7+
import org.apache.commons.httpclient.auth.BasicScheme;
78
import org.apache.commons.httpclient.methods.GetMethod;
8-
import org.apache.commons.lang.StringUtils;
9+
import org.apache.commons.httpclient.UsernamePasswordCredentials;
10+
import org.apache.commons.httpclient.auth.AuthScope;
911

1012
import java.io.IOException;
1113
import java.io.PrintStream;
@@ -20,27 +22,32 @@ public class SonarMasterCoverageRepository implements MasterCoverageRepository {
2022

2123
private static final String SONAR_SEARCH_PROJECTS_API_PATH = "/api/projects/index";
2224
private static final String SONAR_COMPONENT_MEASURE_API_PATH = "/api/measures/component";
23-
private static final String SONAR_OVERALL_LINE_COVERAGE_METRIC_NAME = "overall_line_coverage";
25+
public static final String SONAR_OVERALL_LINE_COVERAGE_METRIC_NAME = "coverage";
2426

2527
private final String sonarUrl;
28+
private final String login;
2629
private final HttpClient httpClient;
2730
private final ObjectMapper objectMapper = new ObjectMapper().disable(FAIL_ON_UNKNOWN_PROPERTIES);
2831
private PrintStream buildLog;
2932

30-
public SonarMasterCoverageRepository(String sonarUrl, PrintStream buildLog) {
33+
public SonarMasterCoverageRepository(String sonarUrl, String login, String password, PrintStream buildLog) {
3134
this.sonarUrl = sonarUrl;
35+
this.login = login;
3236
this.buildLog = buildLog;
3337
httpClient = new HttpClient();
38+
if (login != null) {
39+
httpClient.getState().setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(login, password));
40+
}
3441
}
3542

3643
@Override
37-
public float get(String gitUrl) {
38-
log("Getting coverage for %s", gitUrl);
44+
public float get(final String repoName) {
45+
log("Getting coverage for %s", repoName);
3946
try {
40-
final SonarProject sonarProject = getSonarProject(gitUrl);
47+
final SonarProject sonarProject = getSonarProject(repoName);
4148
return getCoverageMeasure(sonarProject);
4249
} catch (Exception e) {
43-
log("Failed to get master coverage for %s", gitUrl);
50+
log("Failed to get master coverage for %s", repoName);
4451
log("Exception message '%s'", e);
4552
e.printStackTrace(buildLog);
4653
return 0;
@@ -53,13 +60,11 @@ public float get(String gitUrl) {
5360
* @return the sonar project found in case multiple are found, the first one is returned
5461
* @throws SonarProjectRetrievalException if no project could be found or an error occurred during retrieval
5562
*/
56-
private SonarProject getSonarProject(String gitUrl) throws SonarProjectRetrievalException {
57-
String repoName = StringUtils.substringAfterLast(StringUtils.removeEnd(gitUrl, ".git"), "/");
58-
59-
final String searchUri = sonarUrl + SONAR_SEARCH_PROJECTS_API_PATH + "?search=" + repoName;
63+
private SonarProject getSonarProject(final String repoName) throws SonarProjectRetrievalException {
6064
try {
65+
final String searchUri = sonarUrl + SONAR_SEARCH_PROJECTS_API_PATH + "?search=" + repoName;
6166
final GetMethod method = executeGetRequest(searchUri);
62-
List<SonarProject> sonarProjects = objectMapper.readValue(method.getResponseBodyAsStream(), new TypeReference<List<SonarProject>>() {
67+
final List<SonarProject> sonarProjects = objectMapper.readValue(method.getResponseBodyAsStream(), new TypeReference<List<SonarProject>>() {
6368
});
6469

6570
if (sonarProjects.isEmpty()) {
@@ -71,8 +76,8 @@ private SonarProject getSonarProject(String gitUrl) throws SonarProjectRetrieval
7176
log("Found multiple projects for repo name %s - found %s - returning first result", repoName, sonarProjects);
7277
return sonarProjects.get(0);
7378
}
74-
} catch (Exception e) {
75-
throw new SonarProjectRetrievalException(String.format("failed to search for sonar project %s - %s", searchUri, e.getMessage()), e);
79+
} catch (final Exception e) {
80+
throw new SonarProjectRetrievalException(String.format("failed to search for sonar project %s - %s", repoName, e.getMessage()), e);
7681
}
7782
}
7883

@@ -95,6 +100,9 @@ private float getCoverageMeasure(SonarProject project) throws SonarCoverageMeasu
95100

96101
private GetMethod executeGetRequest(String uri) throws IOException, HttpClientException {
97102
final GetMethod method = new GetMethod(uri);
103+
if (login != null) {
104+
method.getHostAuthState().setAuthScheme(new BasicScheme());
105+
}
98106
int status = httpClient.executeMethod(method);
99107
if (status >= SC_BAD_REQUEST) {
100108
throw new HttpClientException(uri, status, method.getResponseBodyAsString());

src/main/resources/com/github/terma/jenkins/githubprcoveragestatus/Configuration/global.groovy

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,16 @@ f.section(title: descriptor.displayName) {
3636
f.textbox()
3737
}
3838

39+
f.entry(field: "sonarToken", title: _("Sonar access token")) {
40+
f.password()
41+
}
42+
43+
f.entry(field: "sonarLogin", title: _("Sonar login")) {
44+
f.textbox()
45+
}
46+
47+
f.entry(field: "sonarPassword", title: _("Sonar password")) {
48+
f.password()
49+
}
50+
3951
}

0 commit comments

Comments
 (0)