Skip to content

Commit

Permalink
#674: Add support for Sonarqube 9.7
Browse files Browse the repository at this point in the history
Sonarqube has removed the use of the same external component key and
differing database IDs for different branches on the same projects and
now use different IDs in all cases. The pull request web service
endpoints have also been removed from community edition. To allow the
plugin to work with the new version of Sonarqube, the component key
generation for branches has been modified to save branch DTOs whenever a
new branch is created, and to remove the conditions around re-using the
same branch if the target branch details matched an existing branch. The
Pull Request endpoint actions have been copied from the old community
edition sources, and tidied up to use a cleaner abstraction model. As
the front-end only shows branch features if the implementation of
`BranchFeatureExtension` returns the name 'branch-support', the
`CommunityBranchFeatureExtension` has been altered to follow this
requirement, and an additional `MonoRepoFeature` has been implemented to
allow the mono-repo switches to be shown against the front-end.

Includes the migration of any altered unit tests to JUnit 5.
  • Loading branch information
mc1arke committed Dec 11, 2022
1 parent 874b9f2 commit a1f28e5
Show file tree
Hide file tree
Showing 43 changed files with 1,390 additions and 572 deletions.
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2020-2021 Michael Clarke
* Copyright (C) 2020-2022 Michael Clarke
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
Expand Down Expand Up @@ -40,7 +40,7 @@ repositories {
}
}

def sonarqubeVersion = '9.1.0.47736'
def sonarqubeVersion = '9.7.0.61563'
def sonarqubeLibDir = "${projectDir}/sonarqube-lib"
def sonarLibraries = "${sonarqubeLibDir}/sonarqube-${sonarqubeVersion}/lib"

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2021 Michael Clarke
* Copyright (C) 2021-2022 Michael Clarke
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
Expand Down Expand Up @@ -52,8 +52,9 @@ public static void premain(String args, Instrumentation instrumentation) throws

if (component == Component.CE) {
redefineEdition(instrumentation, "org.sonar.core.platform.PlatformEditionProvider", redefineOptionalEditionGetMethod());
redefineEdition(instrumentation, "org.sonar.server.almsettings.MultipleAlmFeature", redefineIsEnabledFlag());
} else if (component == Component.WEB) {
redefineEdition(instrumentation, "org.sonar.server.almsettings.MultipleAlmFeatureProvider", redefineConstructorEditionProviderField(EditionProvider.Edition.ENTERPRISE));
redefineEdition(instrumentation, "org.sonar.server.almsettings.MultipleAlmFeature", redefineIsEnabledFlag());
redefineEdition(instrumentation, "org.sonar.server.newcodeperiod.ws.SetAction", redefineConstructorEditionProviderField(EditionProvider.Edition.DEVELOPER));
redefineEdition(instrumentation, "org.sonar.server.newcodeperiod.ws.UnsetAction", redefineConstructorEditionProviderField(EditionProvider.Edition.DEVELOPER));
}
Expand Down Expand Up @@ -101,6 +102,13 @@ private static Redefiner redefineOptionalEditionGetMethod() {
};
}

private static Redefiner redefineIsEnabledFlag() {
return ctClass -> {
CtMethod ctMethod = ctClass.getDeclaredMethod("isEnabled");
ctMethod.setBody("return true;");
};
}

private static Redefiner redefineConstructorEditionProviderField(EditionProvider.Edition edition) {
return ctClass -> {
CtConstructor ctConstructor = ctClass.getDeclaredConstructors()[0];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,17 +43,22 @@
import com.github.mc1arke.sonarqube.plugin.scanner.autoconfiguration.JenkinsAutoConfigurer;
import com.github.mc1arke.sonarqube.plugin.server.CommunityBranchFeatureExtension;
import com.github.mc1arke.sonarqube.plugin.server.CommunityBranchSupportDelegate;
import com.github.mc1arke.sonarqube.plugin.server.MonoRepoFeature;
import com.github.mc1arke.sonarqube.plugin.server.pullrequest.validator.AzureDevopsValidator;
import com.github.mc1arke.sonarqube.plugin.server.pullrequest.validator.BitbucketValidator;
import com.github.mc1arke.sonarqube.plugin.server.pullrequest.validator.GithubValidator;
import com.github.mc1arke.sonarqube.plugin.server.pullrequest.validator.GitlabValidator;
import com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.action.DeleteBindingAction;
import com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.action.SetAzureBindingAction;
import com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.action.SetBitbucketBindingAction;
import com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.action.SetBitbucketCloudBindingAction;
import com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.action.SetGithubBindingAction;
import com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.action.SetGitlabBindingAction;
import com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.action.ValidateBindingAction;
import com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.binding.action.DeleteBindingAction;
import com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.binding.action.SetAzureBindingAction;
import com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.binding.action.SetBitbucketBindingAction;
import com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.binding.action.SetBitbucketCloudBindingAction;
import com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.binding.action.SetGithubBindingAction;
import com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.binding.action.SetGitlabBindingAction;
import com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.binding.action.ValidateBindingAction;
import com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.pullrequest.PullRequestWs;
import com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.pullrequest.action.DeleteAction;
import com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.pullrequest.action.ListAction;

import org.sonar.api.CoreProperties;
import org.sonar.api.Plugin;
import org.sonar.api.PropertyType;
Expand Down Expand Up @@ -88,6 +93,9 @@ public void load(CoreExtension.Context context) {
SetBitbucketCloudBindingAction.class,
SetGitlabBindingAction.class,
ValidateBindingAction.class,
DeleteAction.class,
ListAction.class,
PullRequestWs.class,

GithubValidator.class,
DefaultGraphqlProvider.class,
Expand Down Expand Up @@ -147,7 +155,8 @@ public void load(CoreExtension.Context context) {
.name("Images base URL")
.description("Base URL used to load the images for the PR comments (please use this only if images are not displayed properly).")
.type(PropertyType.STRING)
.build());
.build(),
MonoRepoFeature.class);

}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2020 Michael Clarke
* Copyright (C) 2020-2022 Michael Clarke
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
Expand All @@ -18,11 +18,8 @@
*/
package com.github.mc1arke.sonarqube.plugin.ce;

import org.apache.commons.lang.StringUtils;
import org.sonar.ce.task.projectanalysis.analysis.Branch;
import org.sonar.core.component.ComponentKeys;
import org.sonar.db.component.BranchType;
import org.sonar.db.component.ComponentDto;

/**
* @author Michael Clarke
Expand Down Expand Up @@ -80,24 +77,6 @@ public String getPullRequestKey() {
return pullRequestKey;
}

@Override
public String generateKey(String projectKey, String fileOrDirPath) {
String effectiveKey;
if (null == fileOrDirPath) {
effectiveKey = projectKey;
} else {
effectiveKey = ComponentKeys.createEffectiveKey(projectKey, StringUtils.trimToNull(fileOrDirPath));
}

if (main) {
return effectiveKey;
} else if (BranchType.PULL_REQUEST == branchType) {
return ComponentDto.generatePullRequestKey(effectiveKey, pullRequestKey);
} else {
return ComponentDto.generateBranchKey(effectiveKey, name);
}
}

@Override
public String getTargetBranchName() {
return targetBranchName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,16 +71,16 @@ public DecorationResult decorateQualityGateStatus(AnalysisDetails analysis, AlmS
U user = getCurrentUser(client);
List<PostAnalysisIssueVisitor.ComponentIssue> openSonarqubeIssues = analysis.getScmReportableIssues();

List<Triple<D, N, Optional<ProjectIssueIdentifier>>> currentProjectSonarqueComments = findOpenSonarqubeComments(client,
List<Triple<D, N, Optional<ProjectIssueIdentifier>>> currentProjectSonarqubeComments = findOpenSonarqubeComments(client,
pullRequest,
user)
.stream()
.filter(comment -> isCommentFromCurrentProject(comment, analysis.getAnalysisProjectKey()))
.filter(comment -> !projectAlmSettingDto.getMonorepo() || isCommentFromCurrentProject(comment, analysis.getAnalysisProjectKey()))
.collect(Collectors.toList());

List<String> commentKeysForOpenComments = closeOldDiscussionsAndExtractRemainingKeys(client,
user,
currentProjectSonarqueComments,
currentProjectSonarqubeComments,
openSonarqubeIssues,
pullRequest);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2019 Michael Clarke
* Copyright (C) 2019-2022 Michael Clarke
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
Expand Down Expand Up @@ -27,6 +27,11 @@
*/
public class CommunityBranchFeatureExtension implements BranchFeatureExtension {

@Override
public String getName() {
return "branch-support";
}

@Override
public boolean isEnabled() {
return true;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2020-2021 Michael Clarke
* Copyright (C) 2020-2022 Michael Clarke
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
Expand All @@ -18,7 +18,15 @@
*/
package com.github.mc1arke.sonarqube.plugin.server;

import java.time.Clock;
import java.util.Arrays;
import java.util.Date;
import java.util.Map;
import java.util.regex.Pattern;

import org.apache.commons.lang.StringUtils;
import org.sonar.api.config.Configuration;
import org.sonar.core.config.PurgeConstants;
import org.sonar.core.util.UuidFactory;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
Expand All @@ -28,11 +36,7 @@
import org.sonar.db.component.ComponentDto;
import org.sonar.server.ce.queue.BranchSupport;
import org.sonar.server.ce.queue.BranchSupportDelegate;

import java.time.Clock;
import java.util.Date;
import java.util.Map;
import java.util.Optional;
import org.sonar.server.setting.ProjectConfigurationLoader;

/**
* @author Michael Clarke
Expand All @@ -42,12 +46,15 @@ public class CommunityBranchSupportDelegate implements BranchSupportDelegate {
private final UuidFactory uuidFactory;
private final DbClient dbClient;
private final Clock clock;
private final ProjectConfigurationLoader projectConfigurationLoader;

public CommunityBranchSupportDelegate(UuidFactory uuidFactory, DbClient dbClient, Clock clock) {
public CommunityBranchSupportDelegate(UuidFactory uuidFactory, DbClient dbClient, Clock clock,
ProjectConfigurationLoader projectConfigurationLoader) {
super();
this.uuidFactory = uuidFactory;
this.dbClient = dbClient;
this.clock = clock;
this.projectConfigurationLoader = projectConfigurationLoader;
}

@Override
Expand All @@ -61,17 +68,15 @@ public CommunityComponentKey createComponentKey(String projectKey, Map<String, S
CeTaskCharacteristicDto.BRANCH_TYPE_KEY,
CeTaskCharacteristicDto.PULL_REQUEST));
} else {
return new CommunityComponentKey(projectKey,
ComponentDto.generatePullRequestKey(projectKey, pullRequest), null,
pullRequest);
return new CommunityComponentKey(projectKey, null, pullRequest);
}
}

String branch = StringUtils.trimToNull(characteristics.get(CeTaskCharacteristicDto.BRANCH_KEY));

try {
BranchType.valueOf(branchTypeParam);
return new CommunityComponentKey(projectKey, ComponentDto.generateBranchKey(projectKey, branch), branch, null);
return new CommunityComponentKey(projectKey, branch, null);
} catch (IllegalArgumentException ex) {
throw new IllegalArgumentException(String.format("Unsupported branch type '%s'", branchTypeParam), ex);
}
Expand All @@ -85,25 +90,37 @@ public ComponentDto createBranchComponent(DbSession dbSession, BranchSupport.Com
throw new IllegalStateException("Component Key and Main Component Key do not match");
}

Optional<String> branchOptional = componentKey.getBranchName();
if (branchOptional.isPresent() && branchOptional.get().equals(mainComponentBranchDto.getKey())) {
return mainComponentDto;
}

String branchUuid = uuidFactory.create();

// borrowed from https://github.com/SonarSource/sonarqube/blob/e80c0f3d1e5cd459f88b7e0c41a2d9a7519e260f/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/BranchPersisterImpl.java
ComponentDto branchDto = mainComponentDto.copy();
branchDto.setUuid(branchUuid);
branchDto.setProjectUuid(branchUuid);
branchDto.setRootUuid(branchUuid);
branchDto.setUuidPath(ComponentDto.UUID_PATH_OF_ROOT);
branchDto.setModuleUuidPath(ComponentDto.UUID_PATH_SEPARATOR + branchUuid + ComponentDto.UUID_PATH_SEPARATOR);
branchDto.setMainBranchProjectUuid(mainComponentDto.uuid());
branchDto.setDbKey(componentKey.getDbKey());
branchDto.setCreatedAt(new Date(clock.millis()));
dbClient.componentDao().insert(dbSession, branchDto);
return branchDto;
ComponentDto componentDto = mainComponentDto.copy()
.setUuid(branchUuid)
.setRootUuid(branchUuid)
.setBranchUuid(branchUuid)
.setUuidPath(ComponentDto.UUID_PATH_OF_ROOT)
.setModuleUuidPath(ComponentDto.UUID_PATH_SEPARATOR + branchUuid + ComponentDto.UUID_PATH_SEPARATOR)
.setMainBranchProjectUuid(mainComponentDto.uuid())
.setCreatedAt(new Date(clock.millis()));
dbClient.componentDao().insert(dbSession, componentDto);

BranchDto branchDto = new BranchDto()
.setProjectUuid(mainComponentDto.uuid())
.setUuid(branchUuid);
componentKey.getPullRequestKey().ifPresent(pullRequestKey -> branchDto.setBranchType(BranchType.PULL_REQUEST)
.setExcludeFromPurge(false)
.setKey(pullRequestKey));
componentKey.getBranchName().ifPresent(branchName -> branchDto.setBranchType(BranchType.BRANCH)
.setExcludeFromPurge(isBranchExcludedFromPurge(projectConfigurationLoader.loadProjectConfiguration(dbSession, mainComponentDto), branchName))
.setKey(branchName));
dbClient.branchDao().insert(dbSession, branchDto);

return componentDto;
}

private static boolean isBranchExcludedFromPurge(Configuration projectConfiguration, String branchName) {
return Arrays.stream(projectConfiguration.getStringArray(PurgeConstants.BRANCHES_TO_KEEP_WHEN_INACTIVE))
.map(Pattern::compile)
.map(Pattern::asMatchPredicate)
.anyMatch(p -> p.test(branchName));
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2020 Michael Clarke
* Copyright (C) 2020-2022 Michael Clarke
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
Expand Down Expand Up @@ -28,13 +28,11 @@
/*package*/ class CommunityComponentKey extends BranchSupport.ComponentKey {

private final String key;
private final String dbKey;
private final String branchName;
private final String pullRequestKey;

/*package*/ CommunityComponentKey(String key, String dbKey, String branchName, String pullRequestKey) {
/*package*/ CommunityComponentKey(String key, String branchName, String pullRequestKey) {
this.key = key;
this.dbKey = dbKey;
this.branchName = branchName;
this.pullRequestKey = pullRequestKey;
}
Expand All @@ -44,25 +42,14 @@ public String getKey() {
return key;
}

@Override
public String getDbKey() {
return dbKey;
}

@Override
public Optional<String> getBranchName() {
return Optional.ofNullable(branchName);
}

@Override
public Optional<String> getPullRequestKey() {
return Optional.ofNullable(pullRequestKey);
}

@Override
public CommunityComponentKey getMainBranchComponentKey() {
if (key.equals(dbKey)) {
return this;
}
return new CommunityComponentKey(key, key, null, null);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright (C) 2022 Michael Clarke
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
package com.github.mc1arke.sonarqube.plugin.server;

import org.sonar.api.ce.ComputeEngineSide;
import org.sonar.api.server.ServerSide;
import org.sonar.server.feature.SonarQubeFeature;

@ServerSide
@ComputeEngineSide
public class MonoRepoFeature implements SonarQubeFeature {

@Override
public String getName() {
return "monorepo";
}

@Override
public boolean isEnabled() {
return true;
}
}
Loading

0 comments on commit a1f28e5

Please sign in to comment.