Skip to content

Commit f25764d

Browse files
MSONAR-205 Remove sonar.projectKey for submodules
Fix a bug where the scanner-engine would reject a multi-module project where the `sonar.projectKey` is specified in the root or in a non-leaf module. When the user specifies `sonar.projectKey` as a maven property in the root (or in any non-leaf project), the property is inherited by its sub modules. All (sub)modules would then share the same `sonar.projectKey` clashing with a legacy test in the scanner-engine that would ensure that each submodule has a different key (where the key is defined as whatever value is set for the property `sonar.projectKey` of that module). MSONAR-205
1 parent 9059ca0 commit f25764d

File tree

7 files changed

+180
-10
lines changed

7 files changed

+180
-10
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?xml version="1.0"?>
2+
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
4+
<modelVersion>4.0.0</modelVersion>
5+
<parent>
6+
<groupId>org.example</groupId>
7+
<artifactId>java-multi-module-project-key-in-pom</artifactId>
8+
<version>1.0-SNAPSHOT</version>
9+
</parent>
10+
11+
<artifactId>module1</artifactId>
12+
<version>1.0-SNAPSHOT</version>
13+
14+
<dependencies>
15+
<dependency>
16+
<groupId>junit</groupId>
17+
<artifactId>junit</artifactId>
18+
<version>RELEASE</version>
19+
<scope>test</scope>
20+
</dependency>
21+
</dependencies>
22+
</project>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package org.example;
2+
3+
/**
4+
* Hello world!
5+
*
6+
*/
7+
public class App
8+
{
9+
public static void main( String[] args )
10+
{
11+
System.out.println( "Hello World!" );
12+
}
13+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package org.example;
2+
3+
import junit.framework.Test;
4+
import junit.framework.TestCase;
5+
import junit.framework.TestSuite;
6+
7+
/**
8+
* Unit test for simple App.
9+
*/
10+
public class AppTest
11+
extends TestCase
12+
{
13+
/**
14+
* Create the test case
15+
*
16+
* @param testName name of the test case
17+
*/
18+
public AppTest( String testName )
19+
{
20+
super( testName );
21+
}
22+
23+
/**
24+
* @return the suite of tests being tested
25+
*/
26+
public static Test suite()
27+
{
28+
return new TestSuite( AppTest.class );
29+
}
30+
31+
/**
32+
* Rigourous Test :-)
33+
*/
34+
public void testApp()
35+
{
36+
assertTrue( true );
37+
}
38+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
6+
<groupId>org.example</groupId>
7+
<artifactId>java-multi-module-project-key-in-pom</artifactId>
8+
<packaging>pom</packaging>
9+
<version>1.0-SNAPSHOT</version>
10+
11+
<modules>
12+
<module>module1</module>
13+
</modules>
14+
15+
<properties>
16+
<sonar.projectKey>this-property-overrides-the-project-key</sonar.projectKey>
17+
</properties>
18+
</project>
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
Properties properties = new Properties()
2+
File propertiesFile = new File(basedir, 'out.properties')
3+
propertiesFile.withInputStream {
4+
properties.load(it)
5+
}
6+
7+
def projectKey = 'sonar.projectKey'
8+
def modules = 'sonar.modules'
9+
10+
// We test that the project key is the one in the pom properties of the project
11+
assert properties.'sonar.projectKey' == 'this-property-overrides-the-project-key'
12+
// We test that we have one submodule detected by the scanner
13+
assert properties.'sonar.modules' == 'org.example:module1'
14+
// We test that the submodule does not have a sonar.projectKey property
15+
assert !properties.hasProperty('org.example:module1.sonar.projectKey')

src/main/java/org/sonarsource/scanner/maven/bootstrap/MavenProjectConverter.java

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,12 @@ public class MavenProjectConverter {
127127

128128
private boolean sourceDirsIsOverridden = false;
129129

130+
/**
131+
* This field is introduced to keep track of the root project in multi-module projects and can be used to decide
132+
* whether to apply specific treatments to submodules as we recursively configure them.
133+
*/
134+
private MavenProject root;
135+
130136
public MavenProjectConverter(Log log, MavenCompilerResolver mavenCompilerResolver, Properties envProperties) {
131137
this.log = log;
132138
this.mavenCompilerResolver = mavenCompilerResolver;
@@ -151,6 +157,7 @@ Map<String, String> configure(List<MavenProject> mavenProjects, MavenProject roo
151157
Map<MavenProject, Map<String, String>> propsByModule = new LinkedHashMap<>();
152158

153159
try {
160+
this.root = root;
154161
configureModules(mavenProjects, propsByModule);
155162
Map<String, String> props = new HashMap<>();
156163
props.put(ScanProperties.PROJECT_KEY, getArtifactKey(root));
@@ -163,6 +170,8 @@ Map<String, String> configure(List<MavenProject> mavenProjects, MavenProject roo
163170
return props;
164171
} catch (IOException e) {
165172
throw new IllegalStateException("Cannot configure project", e);
173+
} finally {
174+
this.root = null;
166175
}
167176
}
168177

@@ -252,7 +261,7 @@ private static MavenProject findMavenProject(final File modulePath, Collection<M
252261

253262
private Map<String, String> computeSonarQubeProperties(MavenProject pom) throws MojoExecutionException {
254263
Map<String, String> props = new HashMap<>();
255-
defineModuleKey(pom, props, specifiedProjectKey);
264+
defineModuleKey(pom, props);
256265
props.put(ScanProperties.PROJECT_VERSION, pom.getVersion());
257266
props.put(ScanProperties.PROJECT_NAME, pom.getName());
258267
String description = pom.getDescription();
@@ -280,16 +289,24 @@ private static String specifiedProjectKey(Properties userProperties, MavenProjec
280289
return projectKey;
281290
}
282291

283-
private static void defineModuleKey(MavenProject pom, Map<String, String> props, @Nullable String specifiedProjectKey) {
292+
/**
293+
* Generates a unique module key for a (sub)module and adds it to the existing properties.
294+
* If the project is the root, we try to use the specified project key ({@link MavenProjectConverter#specifiedProjectKey}) if available.
295+
* Otherwise, we use the artifact key ({@link MavenProjectConverter#getArtifactKey(MavenProject)}.
296+
*
297+
* @param project The maven submodule for which a key must be generated
298+
* @param props The existing properties where the module key will be added
299+
* @return The generated module key
300+
*/
301+
private String defineModuleKey(MavenProject project, Map<String, String> props) {
284302
String key;
285-
if (pom.getModel().getProperties().containsKey(ScanProperties.PROJECT_KEY)) {
286-
key = pom.getModel().getProperties().getProperty(ScanProperties.PROJECT_KEY);
287-
} else if (specifiedProjectKey != null) {
288-
key = specifiedProjectKey + ":" + getArtifactKey(pom);
303+
if (project.equals(root) && this.specifiedProjectKey != null) {
304+
key = this.specifiedProjectKey;
289305
} else {
290-
key = getArtifactKey(pom);
306+
key = getArtifactKey(project);
291307
}
292308
props.put(MODULE_KEY, key);
309+
return key;
293310
}
294311

295312
private static String getArtifactKey(MavenProject pom) {
@@ -393,7 +410,9 @@ private void synchronizeFileSystemAndOtherProps(MavenProject pom, Map<String, St
393410
// IMPORTANT NOTE : reference on properties from POM model must not be saved,
394411
// instead they should be copied explicitly - see SONAR-2896
395412
for (String k : pom.getModel().getProperties().stringPropertyNames()) {
396-
props.put(k, pom.getModel().getProperties().getProperty(k));
413+
if (!ScanProperties.PROJECT_KEY.equals(k) || pom.equals(this.root)) {
414+
props.put(k, pom.getModel().getProperties().getProperty(k));
415+
}
397416
}
398417

399418
MavenUtils.putAll(envProperties, props);

src/test/java/org/sonarsource/scanner/maven/bootstrap/MavenProjectConverterTest.java

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,9 @@
2929
import java.util.Map;
3030
import java.util.Optional;
3131
import java.util.Properties;
32-
3332
import org.apache.maven.plugin.MojoExecutionException;
3433
import org.apache.maven.plugin.logging.Log;
3534
import org.apache.maven.project.MavenProject;
36-
3735
import org.junit.jupiter.api.BeforeEach;
3836
import org.junit.jupiter.api.Test;
3937
import org.junit.jupiter.api.io.TempDir;
@@ -638,6 +636,53 @@ void two_modules_in_same_folder() throws Exception {
638636
.containsEntry(module2Key + ".sonar.projectBaseDir", modulesBaseDir.getAbsolutePath());
639637
}
640638

639+
@Test
640+
void submodules_are_not_assigned_user_provided_project_key_from_parent() throws MojoExecutionException, IOException {
641+
Properties rootPomProperties = new Properties();
642+
rootPomProperties.put(ScanProperties.PROJECT_KEY, "the_greatest_project_key_there_ever_was");
643+
File baseDir = temp.toFile();
644+
baseDir.mkdirs();
645+
MavenProject root = createProject(rootPomProperties, "pom");
646+
root.setGroupId("org.example");
647+
root.setArtifactId("root");
648+
649+
File module1BaseDir = temp.resolve("module1").toFile();
650+
module1BaseDir.mkdirs();
651+
File module1Pom = new File(module1BaseDir, "pom.xml");
652+
MavenProject module1 = new MavenProject();
653+
File target = new File(module1Pom.getParentFile(), "target");
654+
File classes = new File(target, "classes");
655+
File testClasses = new File(target, "test-classes");
656+
classes.mkdirs();
657+
testClasses.mkdirs();
658+
659+
module1.getModel().setGroupId("org.example");
660+
module1.getModel().setArtifactId("module1");
661+
module1.getModel().setName("My Project");
662+
module1.getModel().setDescription("My sample module1");
663+
module1.getModel().setVersion("2.1");
664+
module1.getModel().setPackaging("jar");
665+
module1.getBuild().setOutputDirectory(classes.getAbsolutePath());
666+
module1.getBuild().setTestOutputDirectory(testClasses.getAbsolutePath());
667+
module1.getModel().setProperties(rootPomProperties);
668+
module1.getBuild().setDirectory(new File(module1Pom.getParentFile(), "target").getAbsolutePath());
669+
module1.setFile(module1Pom);
670+
module1.setParent(root);
671+
root.getModules().add("module1");
672+
673+
Map<String, String> properties = projectConverter.configure(
674+
Arrays.asList(module1, root),
675+
root,
676+
new Properties()
677+
);
678+
679+
assertThat(properties.get(ScanProperties.PROJECT_KEY))
680+
.isNotNull()
681+
.isEqualTo("the_greatest_project_key_there_ever_was");
682+
String keyPrefixForModule1 = module1.getGroupId() + ":" + module1.getArtifactId() + ".";
683+
assertThat(properties).doesNotContainKey(keyPrefixForModule1 + ScanProperties.PROJECT_KEY);
684+
}
685+
641686
private MavenProject createProject(Properties pomProps, String packaging) throws IOException {
642687
File pom = temp.resolve("pom.xml").toFile();
643688
pom.createNewFile();

0 commit comments

Comments
 (0)