Skip to content

Commit a65f6b6

Browse files
committed
Merge branch 'release/V3.3.0'
2 parents b2d1aa9 + d1dbd59 commit a65f6b6

File tree

40 files changed

+1156
-267
lines changed

40 files changed

+1156
-267
lines changed

Jenkinsfile

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@ pipeline {
88
}
99

1010
stages {
11-
stage ('Build OpenEdge plugin') {
11+
stage ('👷 Build') {
1212
environment {
13-
MAVEN_GPG_PASSPHRASE=credentials('GPG_KEY')
13+
OP_CLI_PATH = '/usr/local/bin/'
14+
MAVEN_GPG_PASSPHRASE = 'op://Jenkins/GPG/password'
1415
}
1516
steps {
1617
checkout([$class: 'GitSCM', branches: scm.branches, extensions: scm.extensions + [[$class: 'CleanCheckout']], userRemoteConfigs: scm.userRemoteConfigs])
@@ -19,7 +20,11 @@ pipeline {
1920
script {
2021
withEnv(["MVN_HOME=${tool name: 'Maven 3', type: 'maven'}", "JAVA_HOME=${tool name: 'JDK17', type: 'jdk'}"]) {
2122
if ("main" == env.BRANCH_NAME) {
22-
sh "$MVN_HOME/bin/mvn -P release clean deploy -Dgit.commit=\$(git rev-parse --short HEAD)"
23+
withSecrets() {
24+
configFileProvider([configFile(fileId: 'MvnSettingsRSSW', variable: 'MAVEN_SETTINGS')]) {
25+
sh '$MVN_HOME/bin/mvn -s ${MAVEN_SETTINGS} -P release clean deploy -Dgit.commit=\$(git rev-parse --short HEAD)'
26+
}
27+
}
2328
mail body: "https://central.sonatype.com/publishing/deployments", to: "[email protected]", subject: "sonar-openedge - Publish artifact on Central"
2429
} else if ("develop" == env.BRANCH_NAME) {
2530
sh "$MVN_HOME/bin/mvn clean javadoc:javadoc deploy -Dmaven.test.failure.ignore=true -Dgit.commit=\$(git rev-parse --short HEAD)"
@@ -35,7 +40,7 @@ pipeline {
3540
}
3641
}
3742

38-
stage ('SonarQube analysis') {
43+
stage ('🔍 SonarQube analysis') {
3944
steps {
4045
script {
4146
withEnv(["MVN_HOME=${tool name: 'Maven 3', type: 'maven'}", "JAVA_HOME=${tool name: 'JDK17', type: 'jdk'}"]) {

coverage-report/pom.xml

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<parent>
66
<groupId>eu.rssw</groupId>
77
<artifactId>sonar-openedge</artifactId>
8-
<version>3.2.0</version>
8+
<version>3.3.0</version>
99
</parent>
1010
<groupId>eu.rssw.openedge.parsers</groupId>
1111
<artifactId>coverage-report</artifactId>
@@ -21,37 +21,37 @@
2121
<dependency>
2222
<groupId>eu.rssw.openedge.rcode</groupId>
2323
<artifactId>rcode-reader</artifactId>
24-
<version>3.2.0</version>
24+
<version>3.3.0</version>
2525
</dependency>
2626
<dependency>
2727
<groupId>eu.rssw.sonar.openedge</groupId>
2828
<artifactId>sonar-openedge-plugin</artifactId>
29-
<version>3.2.0</version>
29+
<version>3.3.0</version>
3030
</dependency>
3131
<dependency>
3232
<groupId>eu.rssw.openedge.parsers</groupId>
3333
<artifactId>proparse</artifactId>
34-
<version>3.2.0</version>
34+
<version>3.3.0</version>
3535
</dependency>
3636
<dependency>
3737
<groupId>eu.rssw.openedge.parsers</groupId>
3838
<artifactId>profiler-parser</artifactId>
39-
<version>3.2.0</version>
39+
<version>3.3.0</version>
4040
</dependency>
4141
<dependency>
4242
<groupId>eu.rssw.openedge.parsers</groupId>
4343
<artifactId>database-parser</artifactId>
44-
<version>3.2.0</version>
44+
<version>3.3.0</version>
4545
</dependency>
4646
<dependency>
4747
<groupId>eu.rssw.openedge.parsers</groupId>
4848
<artifactId>listing-parser</artifactId>
49-
<version>3.2.0</version>
49+
<version>3.3.0</version>
5050
</dependency>
5151
<dependency>
5252
<groupId>eu.rssw.openedge.checks</groupId>
5353
<artifactId>openedge-checks</artifactId>
54-
<version>3.2.0</version>
54+
<version>3.3.0</version>
5555
</dependency>
5656
</dependencies>
5757

database-parser/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<parent>
66
<groupId>eu.rssw</groupId>
77
<artifactId>sonar-openedge</artifactId>
8-
<version>3.2.0</version>
8+
<version>3.3.0</version>
99
</parent>
1010
<groupId>eu.rssw.openedge.parsers</groupId>
1111
<artifactId>database-parser</artifactId>

listing-parser/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<parent>
66
<groupId>eu.rssw</groupId>
77
<artifactId>sonar-openedge</artifactId>
8-
<version>3.2.0</version>
8+
<version>3.3.0</version>
99
</parent>
1010
<groupId>eu.rssw.openedge.parsers</groupId>
1111
<artifactId>listing-parser</artifactId>

openedge-checks/pom.xml

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<parent>
66
<groupId>eu.rssw</groupId>
77
<artifactId>sonar-openedge</artifactId>
8-
<version>3.2.0</version>
8+
<version>3.3.0</version>
99
</parent>
1010
<groupId>eu.rssw.openedge.checks</groupId>
1111
<artifactId>openedge-checks</artifactId>
@@ -23,12 +23,18 @@
2323
<dependency>
2424
<groupId>eu.rssw.openedge.parsers</groupId>
2525
<artifactId>database-parser</artifactId>
26-
<version>3.2.0</version>
26+
<version>3.3.0</version>
2727
</dependency>
2828
<dependency>
2929
<groupId>eu.rssw.openedge.parsers</groupId>
3030
<artifactId>proparse</artifactId>
31-
<version>3.2.0</version>
31+
<version>3.3.0</version>
32+
</dependency>
33+
<dependency>
34+
<groupId>org.sonarsource.sonarqube</groupId>
35+
<artifactId>sonar-plugin-api-impl</artifactId>
36+
<version>10.7.0.96327</version>
37+
<scope>test</scope>
3238
</dependency>
3339
<dependency>
3440
<groupId>org.testng</groupId>

openedge-checks/src/main/java/org/sonar/plugins/openedge/api/AnnotationBasedRulesDefinition.java

Lines changed: 112 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,24 @@
1919
*/
2020
package org.sonar.plugins.openedge.api;
2121

22+
import java.io.IOException;
2223
import java.lang.annotation.Annotation;
24+
import java.nio.charset.StandardCharsets;
2325
import java.util.List;
26+
import java.util.Locale;
2427
import java.util.Set;
28+
import java.util.regex.Matcher;
29+
import java.util.regex.Pattern;
30+
31+
import javax.annotation.Nullable;
2532

2633
import org.slf4j.Logger;
2734
import org.slf4j.LoggerFactory;
2835
import org.sonar.api.SonarRuntime;
2936
import org.sonar.api.rules.RuleType;
37+
import org.sonar.api.server.rule.Context;
38+
import org.sonar.api.server.rule.RuleDescriptionSection;
39+
import org.sonar.api.server.rule.RuleDescriptionSectionBuilder;
3040
import org.sonar.api.server.rule.RulesDefinition;
3141
import org.sonar.api.server.rule.RulesDefinition.NewRepository;
3242
import org.sonar.api.server.rule.RulesDefinition.NewRule;
@@ -55,6 +65,11 @@
5565
import com.google.common.collect.Lists;
5666
import com.google.common.collect.Sets;
5767

68+
import static org.sonar.api.server.rule.RuleDescriptionSection.RuleDescriptionSectionKeys.HOW_TO_FIX_SECTION_KEY;
69+
import static org.sonar.api.server.rule.RuleDescriptionSection.RuleDescriptionSectionKeys.INTRODUCTION_SECTION_KEY;
70+
import static org.sonar.api.server.rule.RuleDescriptionSection.RuleDescriptionSectionKeys.RESOURCES_SECTION_KEY;
71+
import static org.sonar.api.server.rule.RuleDescriptionSection.RuleDescriptionSectionKeys.ROOT_CAUSE_SECTION_KEY;
72+
5873
/**
5974
* Utility class which helps setting up an implementation of {@link RulesDefinition} with a list of rule classes
6075
* annotated with {@link Rule}, {@link RuleProperty} and one of:
@@ -74,14 +89,21 @@
7489
public class AnnotationBasedRulesDefinition {
7590
private static final Logger LOGGER = LoggerFactory.getLogger(AnnotationBasedRulesDefinition.class);
7691

92+
private static final String CODE_EXAMPLES_HEADER = "<h3>Code examples</h3>";
93+
private static final String WHY_SECTION_HEADER = "<h2>Why is this an issue\\?</h2>";
94+
private static final String HOW_TO_FIX_SECTION_HEADER = "<h2>How to fix it</h2>";
95+
private static final String RESOURCES_SECTION_HEADER = "<h2>Resources</h2>";
96+
private static final String HOW_TO_FIX_FRAMEWORK_SECTION_REGEX = "<h2>How to fix it in (?:(?:an|a|the)\\s)?(?<displayName>.*)</h2>";
97+
private static final Pattern HOW_TO_FIX_SECTION_PATTERN = Pattern.compile(HOW_TO_FIX_SECTION_HEADER);
98+
private static final Pattern HOW_TO_FIX_FRAMEWORK_SECTION_PATTERN = Pattern.compile(HOW_TO_FIX_FRAMEWORK_SECTION_REGEX);
99+
77100
private final NewRepository repository;
78-
private final ExternalDescriptionLoader externalDescriptionLoader;
101+
private final String basePath;
79102
private final SonarRuntime runtime;
80103

81104
public AnnotationBasedRulesDefinition(NewRepository repository, String languageKey, SonarRuntime runtime) {
82105
this.repository = repository;
83-
String externalDescriptionBasePath = String.format("/rules/%s/%s/", languageKey, repository.key());
84-
this.externalDescriptionLoader = new ExternalDescriptionLoader(externalDescriptionBasePath);
106+
this.basePath = String.format("/rules/%s/%s/", languageKey, repository.key());
85107
this.runtime = runtime;
86108
}
87109

@@ -97,7 +119,7 @@ public void addRuleClasses(boolean failIfNoExplicitKey, Iterable<Class> ruleClas
97119
for (Class<?> ruleClass : ruleClasses) {
98120
NewRule rule = newRule(ruleClass, failIfNoExplicitKey);
99121
rule.setTemplate(AnnotationUtils.getAnnotation(ruleClass, RuleTemplate.class) != null);
100-
externalDescriptionLoader.addHtmlDescription(rule, ruleClass);
122+
setupDocumentation(rule, ruleClass);
101123
setupSecurityModel(rule, ruleClass);
102124
if (runtime.getApiVersion().isGreaterThanOrEqual(Version.create(10, 1)))
103125
setupCleanCode(rule, ruleClass);
@@ -135,8 +157,6 @@ private void setupSecurityModel(NewRule rule, Class<?> ruleClass) {
135157
var hotspotAnnotation = AnnotationUtils.getAnnotation(ruleClass, SecurityHotspot.class);
136158
if (hotspotAnnotation != null) {
137159
rule.setType(RuleType.SECURITY_HOTSPOT);
138-
setOwasp(rule, OwaspTop10Version.Y2017, hotspotAnnotation.owasp());
139-
setCwe(rule, hotspotAnnotation.cwe());
140160
}
141161
var cweAnnotation = AnnotationUtils.getAnnotation(ruleClass, CWE.class);
142162
if (cweAnnotation != null)
@@ -208,23 +228,96 @@ private void setupSqaleModel(NewRule rule, Class<?> ruleClass) {
208228
}
209229
}
210230

211-
private class ExternalDescriptionLoader {
212-
private final String resourceBasePath;
231+
private void setupDocumentation(NewRule rule, Class<?> clz) {
232+
var url01 = clz.getResource(basePath + rule.key().replace('.', '/') + ".html");
233+
var url02 = clz.getResource(basePath + rule.key().replace('.', '/') + ".sections.html");
234+
if (url02 != null) {
235+
try (var in = url02.openStream()) {
236+
var desc = new String(in.readAllBytes(), StandardCharsets.UTF_8);
237+
rule.setHtmlDescription(desc); // Compatibility with old versions of SonarQube
238+
setupEducationDocumentation(rule, desc);
239+
} catch (IOException caught) {
240+
rule.setHtmlDescription("<p>Invalid description</p>");
241+
}
242+
} else if (url01 != null) {
243+
rule.setHtmlDescription(url01);
244+
} else {
245+
rule.setHtmlDescription("<p>No description</p>");
246+
LOGGER.warn("No HTML description found for rule {}", rule.key());
247+
}
248+
}
249+
250+
// Adapted from org.sonarsource.analyzer.commons.EducationRuleLoader
251+
private void setupEducationDocumentation(NewRule rule, String description) {
252+
// The "Why is this an issue?" section is expected.
253+
var split = description.split(WHY_SECTION_HEADER);
254+
255+
// Adding the introduction section if not empty.
256+
addSection(rule, INTRODUCTION_SECTION_KEY, split[0]);
257+
split = split[1].split(RESOURCES_SECTION_HEADER);
213258

214-
public ExternalDescriptionLoader(String resourceBasePath) {
215-
this.resourceBasePath = resourceBasePath;
259+
// Filtering out the "<h3>Code examples</h3>" title.
260+
var rootCauseAndHowToFixItSections = split[0].replace(CODE_EXAMPLES_HEADER, "");
261+
262+
// Either the generic "How to fix it" section or at least one framework specific "How to fix it in <framework_name>"
263+
// section is expected.
264+
var frameworkSpecificHowToFixItSectionMatcher = HOW_TO_FIX_FRAMEWORK_SECTION_PATTERN.matcher(
265+
rootCauseAndHowToFixItSections);
266+
var hasFrameworkSpecificHowToFixItSection = frameworkSpecificHowToFixItSectionMatcher.find();
267+
var hasGenericHowToFixItSection = HOW_TO_FIX_SECTION_PATTERN.matcher(rootCauseAndHowToFixItSections).find();
268+
if (hasGenericHowToFixItSection && hasFrameworkSpecificHowToFixItSection) {
269+
throw new IllegalStateException(String.format(
270+
"Invalid education rule format for '%s', rule description has both generic and framework-specific 'How to fix it' sections",
271+
rule.key()));
272+
} else if (hasFrameworkSpecificHowToFixItSection) {
273+
// Splitting by the "How to fix in <displayName>" will return an array where each element after the first is the
274+
// content related to a given framework.
275+
var innerSplit = rootCauseAndHowToFixItSections.split(HOW_TO_FIX_FRAMEWORK_SECTION_REGEX);
276+
addSection(rule, ROOT_CAUSE_SECTION_KEY, innerSplit[0]);
277+
addContextSpecificHowToFixItSection(rule, innerSplit, frameworkSpecificHowToFixItSectionMatcher);
278+
} else if (hasGenericHowToFixItSection) {
279+
// Rule has the generic "How to fix it" section.
280+
var innerSplit = rootCauseAndHowToFixItSections.split(HOW_TO_FIX_SECTION_HEADER);
281+
addSection(rule, ROOT_CAUSE_SECTION_KEY, innerSplit[0]);
282+
addSection(rule, HOW_TO_FIX_SECTION_KEY, innerSplit[1]);
283+
} else {
284+
// No "How to fix it" section for the rule, the only section present is "Why is it an issue".
285+
addSection(rule, ROOT_CAUSE_SECTION_KEY, rootCauseAndHowToFixItSections);
216286
}
217287

218-
public void addHtmlDescription(NewRule rule, Class<?> clz) {
219-
var path = resourceBasePath + rule.key().replace('.', '/') + ".html";
220-
var url = clz.getResource(path);
221-
if (url != null) {
222-
rule.setHtmlDescription(url);
223-
} else {
224-
rule.setHtmlDescription("<p>No description</p>");
225-
LOGGER.warn("No HTML description found in path {} for rule {}", path, rule.key());
226-
}
288+
// "Resources" section is optional.
289+
if (split.length > 1) {
290+
addSection(rule, RESOURCES_SECTION_KEY, split[1]);
227291
}
228292
}
229293

294+
private static void addContextSpecificHowToFixItSection(NewRule rule, String[] split, Matcher m) {
295+
var match = true;
296+
var splitIndex = 1;
297+
while (match) {
298+
var displayName = m.group("displayName").trim();
299+
var contextSpecificContent = split[splitIndex];
300+
var key = displayName.toLowerCase(Locale.ROOT).replaceAll("[^a-z0-9]", "_");
301+
addSection(rule, HOW_TO_FIX_SECTION_KEY, contextSpecificContent, new Context(key, displayName));
302+
match = m.find();
303+
splitIndex++;
304+
}
305+
}
306+
307+
private static void addSection(NewRule rule, String sectionKey, String content) {
308+
addSection(rule, sectionKey, content, null);
309+
}
310+
311+
private static void addSection(NewRule rule, String sectionKey, String content, @Nullable Context context) {
312+
if (content.isBlank())
313+
return;
314+
315+
RuleDescriptionSectionBuilder sectionBuilder = RuleDescriptionSection.builder() //
316+
.sectionKey(sectionKey) //
317+
.htmlContent(content.trim()) //
318+
.context(context);
319+
320+
rule.addDescriptionSection(sectionBuilder.build());
321+
}
322+
230323
}

openedge-checks/src/main/java/org/sonar/plugins/openedge/api/Constants.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,12 @@ public class Constants {
3939
public static final String SKIP_COGNITIVE_COMPLEXITY = "sonar.oe.skipCognitiveComplexity";
4040
public static final String USE_SIMPLE_CPD = "sonar.oe.simplecpd";
4141
public static final String PROPARSE_DEBUG = "sonar.oe.proparse.debug";
42+
public static final String PROPARSE_DEBUG_INCLUDES = "sonar.oe.proparse.debug.includes";
4243
public static final String PROPARSE_ERROR_STACKTRACE = "sonar.oe.proparse.error.stacktrace";
4344
public static final String BINARIES = "sonar.oe.binaries";
4445
public static final String DOTPCT = "sonar.oe.dotpct";
4546
public static final String SLINT_XREF = "sonar.oe.lint.xref";
47+
public static final String SLINT_XREF_DIRS = "sonar.oe.lint.xrefdirs";
4648
public static final String DLC = "sonar.oe.dlc";
4749
public static final String PROPATH = "sonar.oe.propath";
4850
public static final String PROPATH_DLC = "sonar.oe.propath.dlc";

0 commit comments

Comments
 (0)