Skip to content

Commit cbad5fe

Browse files
authored
Merge pull request #23 from cnescatlab/feature/add_external_tool_analysis
Add external tool analysis
2 parents 4510233 + 6b4bf48 commit cbad5fe

File tree

7 files changed

+162
-93
lines changed

7 files changed

+162
-93
lines changed

pom.xml

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
35
<modelVersion>4.0.0</modelVersion>
46

57
<groupId>fr.cnes.sonar.plugins.scan</groupId>
@@ -18,7 +20,7 @@
1820
<gson.version>2.11.0</gson.version>
1921
<junit.jupiter.api.version>5.11.0-M2</junit.jupiter.api.version>
2022
<jacoco-maven-plugin.version>0.8.12</jacoco-maven-plugin.version>
21-
<jdk.version>17</jdk.version>
23+
<jdk.version>17</jdk.version>
2224
<pluginUrl>https://cnes.fr</pluginUrl>
2325
<pluginOrganizationName>CNES</pluginOrganizationName>
2426
</properties>
@@ -48,7 +50,7 @@
4850
</developer>
4951
<developer>
5052
<id>Topin2001</id>
51-
<name>Topin</name>
53+
<name>Topin</name>
5254
</developer>
5355
</developers>
5456

@@ -173,4 +175,4 @@
173175
</plugins>
174176
</build>
175177

176-
</project>
178+
</project>

src/main/java/fr/cnes/sonar/plugins/scan/tasks/AbstractTask.java

-2
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,6 @@ public abstract class AbstractTask implements RequestHandler {
4949
* @throws InterruptedException when a command is not finished
5050
*/
5151
protected String executeCommand(final String command) throws IOException, InterruptedException {
52-
// log the command to execute
53-
LOGGER.info(command);
5452

5553
// prepare a string builder for the output gathering
5654
final StringBuilder output = new StringBuilder();

src/main/java/fr/cnes/sonar/plugins/scan/tasks/AnalysisTask.java

+109-38
Original file line numberDiff line numberDiff line change
@@ -17,41 +17,48 @@
1717
package fr.cnes.sonar.plugins.scan.tasks;
1818

1919
import fr.cnes.sonar.plugins.scan.utils.StringManager;
20+
2021
import org.sonar.api.config.Configuration;
2122
import org.sonar.api.server.ws.Request;
2223
import org.sonar.api.server.ws.Response;
2324
import org.sonar.api.utils.text.JsonWriter;
2425

26+
import com.google.gson.Gson;
27+
import com.google.gson.reflect.TypeToken;
28+
2529
import java.io.File;
2630
import java.io.FileWriter;
2731
import java.io.IOException;
32+
import java.lang.reflect.Type;
2833
import java.nio.file.Files;
2934
import java.nio.file.Paths;
3035
import java.text.SimpleDateFormat;
36+
import java.util.ArrayList;
3137
import java.util.Date;
38+
import java.util.List;
3239
import java.util.concurrent.*;
3340

3441
/**
3542
* Execute the scan of a project
43+
*
3644
* @author lequal
3745
*/
3846
public class AnalysisTask extends AbstractTask {
3947

4048
private final Configuration config;
4149

42-
public AnalysisTask(Configuration config){
50+
public AnalysisTask(Configuration config) {
4351
this.config = config;
4452
}
53+
4554
/**
4655
* Logged message when a file can not be deleted
4756
*/
48-
private static final String FILE_DELETION_ERROR =
49-
"The following file could not be deleted: %s.";
57+
private static final String FILE_DELETION_ERROR = "The following file could not be deleted: %s.";
5058
/**
5159
* Logged message when a file can not be set as executable
5260
*/
53-
private static final String FILE_PERMISSIONS_ERROR =
54-
"Permissions of the following file could not be changed: %s.";
61+
private static final String FILE_PERMISSIONS_ERROR = "Permissions of the following file could not be changed: %s.";
5562
/**
5663
* Just a slash
5764
*/
@@ -69,55 +76,67 @@ public AnalysisTask(Configuration config){
6976
*/
7077
private static final String NEW_LINE = "\n";
7178

72-
73-
7479
/**
7580
* Execute the scan of a project
76-
* @param projectName name of the project to analyze
77-
* @param projectFolder url of the folder containing the project to analyze
81+
*
82+
* @param projectName name of the project to analyze
83+
* @param projectFolder url of the folder containing the project to
84+
* analyze
7885
* @param sonarProjectProperties the sonar-project.properties as string
86+
* @param qualityProfiles the quality profiles as string
7987
* @return logs
80-
* @throws IOException when a file writing goes wrong
88+
* @throws IOException when a file writing goes wrong
8189
* @throws InterruptedException when a command is not finished
8290
*/
83-
private String analyze(final String projectName, final String projectFolder, final String sonarProjectProperties)
91+
private String analyze(final String projectName, final String projectFolder, final String sonarProjectProperties,
92+
final String qualityProfiles)
8493
throws IOException, InterruptedException {
8594
// setting a timer based on user's timeout property configuration
86-
final Integer timeout = Integer.parseInt(config.get(StringManager.string(StringManager.TIMEOUT_PROP_DEF_KEY)).orElse(StringManager.string(StringManager.DEFAULT_STRING)));
95+
final Integer timeout = Integer.parseInt(config.get(StringManager.string(StringManager.TIMEOUT_PROP_DEF_KEY))
96+
.orElse(StringManager.string(StringManager.DEFAULT_STRING)));
8797
final ExecutorService service = Executors.newSingleThreadExecutor();
8898
try {
8999
final Runnable task = () -> {
90100
try {
91101
// path where spp should be written
92102
final String sppPath = String.format(StringManager.string(StringManager.CNES_SPP_PATH),
93-
config.get(StringManager.string(StringManager.WORKSPACE_PROP_DEF_KEY)).orElse(StringManager.string(StringManager.DEFAULT_STRING)), projectFolder);
103+
config.get(StringManager.string(StringManager.WORKSPACE_PROP_DEF_KEY))
104+
.orElse(StringManager.string(StringManager.DEFAULT_STRING)),
105+
projectFolder);
94106

95107
// write sonar-project.properties in the project folder
96108
writeTextFile(sppPath, sonarProjectProperties);
97109
// build the scan command
98110
final String analysisCommand = String.format(
99111
StringManager.string(StringManager.CNES_COMMAND_SCAN),
100-
config.get(StringManager.string(StringManager.SCANNER_PROP_DEF_KEY)).orElse(StringManager.string(StringManager.DEFAULT_STRING)),
101-
config.get(StringManager.string(StringManager.WORKSPACE_PROP_DEF_KEY)).orElse(StringManager.string(StringManager.DEFAULT_STRING)),
112+
config.get(StringManager.string(StringManager.SCANNER_PROP_DEF_KEY))
113+
.orElse(StringManager.string(StringManager.DEFAULT_STRING)),
114+
config.get(StringManager.string(StringManager.WORKSPACE_PROP_DEF_KEY))
115+
.orElse(StringManager.string(StringManager.DEFAULT_STRING)),
102116
projectFolder,
103-
config.get(StringManager.string(StringManager.WORKSPACE_PROP_DEF_KEY)).orElse(StringManager.string(StringManager.DEFAULT_STRING)),
117+
config.get(StringManager.string(StringManager.WORKSPACE_PROP_DEF_KEY))
118+
.orElse(StringManager.string(StringManager.DEFAULT_STRING)),
104119
projectFolder);
105-
final File script = createScript(projectFolder, analysisCommand);
120+
final File script = createScript(projectFolder, analysisCommand, qualityProfiles);
106121

107122
// string formatted date as string
108123
final String date = new SimpleDateFormat(
109124
StringManager.string(StringManager.DATE_PATTERN)).format(new Date());
110125

111126
// export log file
112127
final String logPath = String.format(StringManager.string(StringManager.CNES_LOG_PATH),
113-
config.get(StringManager.string(StringManager.WORKSPACE_PROP_DEF_KEY)).orElse(StringManager.string(StringManager.DEFAULT_STRING)), date, projectName);
128+
config.get(StringManager.string(StringManager.WORKSPACE_PROP_DEF_KEY))
129+
.orElse(StringManager.string(StringManager.DEFAULT_STRING)),
130+
date, projectName);
114131

115132
// scan execution
116-
final String scriptCommand = config.get(StringManager.string(StringManager.WORKSPACE_PROP_DEF_KEY)).orElse(StringManager.string(StringManager.DEFAULT_STRING)) +
133+
final String scriptCommand = config.get(StringManager.string(StringManager.WORKSPACE_PROP_DEF_KEY))
134+
.orElse(StringManager.string(StringManager.DEFAULT_STRING)) +
117135
SLASH + projectFolder + SLASH + CAT_SCAN_SCRIPT;
118136
log(executeCommand(scriptCommand));
119137
// log output file
120-
final String path = config.get(StringManager.string(StringManager.WORKSPACE_PROP_DEF_KEY)).orElse(StringManager.string(StringManager.DEFAULT_STRING)) +
138+
final String path = config.get(StringManager.string(StringManager.WORKSPACE_PROP_DEF_KEY))
139+
.orElse(StringManager.string(StringManager.DEFAULT_STRING)) +
121140
SLASH + projectFolder + SLASH + CAT_LOG_FILE;
122141
for (final String line : Files.readAllLines(Paths.get(path))) {
123142
log(line + NEW_LINE);
@@ -137,7 +156,6 @@ private String analyze(final String projectName, final String projectFolder, fin
137156
LOGGER.severe(e.getMessage());
138157
}
139158

140-
141159
};
142160
final Future<?> execution = service.submit(task);
143161

@@ -153,13 +171,13 @@ private String analyze(final String projectName, final String projectFolder, fin
153171
service.shutdown();
154172
}
155173

156-
157174
// return the complete logs
158175
return getLogs();
159176
}
160177

161178
/**
162179
* Export the sonar-project.properties in the corresponding folder
180+
*
163181
* @param path Output folder
164182
* @param data Data to write
165183
* @throws IOException when a file writing goes wrong
@@ -170,17 +188,18 @@ private void writeTextFile(final String path, final String data) throws IOExcept
170188

171189
// create the writer
172190
// true to append; false to overwrite.
173-
try(FileWriter fileWriter = new FileWriter(spp, false)) {
191+
try (FileWriter fileWriter = new FileWriter(spp, false)) {
174192
// write the data
175193
fileWriter.write(data);
176194
}
177195
}
178196

179197
/**
180198
* Use the user's request to launch an scan
181-
* @param request request coming from the user
199+
*
200+
* @param request request coming from the user
182201
* @param response response to send to the user
183-
* @throws IOException when communicating with the client
202+
* @throws IOException when communicating with the client
184203
* @throws InterruptedException ...
185204
*/
186205
@Override
@@ -196,9 +215,11 @@ public void handle(final Request request, final Response response)
196215
StringManager.string(StringManager.ANALYZE_FOLDER_NAME));
197216
final String sonarProjectProperties = request.mandatoryParam(
198217
StringManager.string(StringManager.ANALYZE_SPP_NAME));
218+
final String qualityProfiles = request.mandatoryParam(
219+
StringManager.string(StringManager.ANALYZE_QUALITY_PROFILES_NAME));
199220

200221
// concrete scan
201-
final String result = analyze(projectName, workspace, sonarProjectProperties);
222+
final String result = analyze(projectName, workspace, sonarProjectProperties, qualityProfiles);
202223

203224
// write the json response
204225
try (JsonWriter jsonWriter = response.newJsonWriter()) {
@@ -209,33 +230,83 @@ public void handle(final Request request, final Response response)
209230
}
210231
}
211232

233+
private String setupExternalTools(String qualityProfile) {
234+
StringBuilder setupExternalTools = new StringBuilder();
235+
236+
Gson gson = new Gson();
237+
Type outerListType = new TypeToken<List<String>>() {
238+
}.getType();
239+
List<String> outerList = gson.fromJson(qualityProfile, outerListType);
240+
List<List<String>> qualityProfiles = new ArrayList<>();
241+
for (String innerJson : outerList) {
242+
Type innerListType = new TypeToken<List<String>>() {
243+
}.getType();
244+
List<String> innerList = gson.fromJson(innerJson, innerListType);
245+
qualityProfiles.add(innerList);
246+
}
247+
248+
for (List<String> qp : qualityProfiles) {
249+
if (qp.get(0).equals("py")) {
250+
LOGGER.info("Setup pylint");
251+
// Detect and run correct pylintrc according to RNC
252+
String pylintrc = "/opt/python/pylintrc_RNC2015_D";
253+
switch (qp.get(1)) {
254+
case "RNC A":
255+
pylintrc = "/opt/python/pylintrc_RNC2015_A_B";
256+
break;
257+
case "RNC B":
258+
pylintrc = "/opt/python/pylintrc_RNC2015_A_B";
259+
break;
260+
case "RNC C":
261+
pylintrc = "/opt/python/pylintrc_RNC2015_C";
262+
break;
263+
default:
264+
break;
265+
}
266+
setupExternalTools.append(
267+
"\npylint --rcfile=" + pylintrc
268+
+ " --load-plugins=pylint_sonarjson --output-format=sonarjson --output=pylint-report.json *.py");
269+
}
270+
if (qp.contains("docker")) {
271+
LOGGER.info("Setup hadolint");
272+
setupExternalTools.append(
273+
"\nhadolint -f sonarqube --no-fail --config=/opt/hadolint/hadolint_RNC_A_B_C_D.yaml Dockerfile > hadolint-report.json");
274+
}
275+
}
276+
return setupExternalTools.toString();
277+
}
278+
212279
/**
213-
* Create a temporary script containing dedicated command executing sonar-scanner
214-
* @param project repository containing the source code
280+
* Create a temporary script containing dedicated command executing
281+
* sonar-scanner
282+
*
283+
* @param project repository containing the source code
215284
* @param commandLine command line to execute
216285
* @return The created file
217286
*/
218-
private File createScript(final String project, final String commandLine) {
287+
private File createScript(final String project, final String commandLine, final String qualityProfiles) {
219288
// path to the workspace
220-
final String workspace = config.get(StringManager.string(StringManager.WORKSPACE_PROP_DEF_KEY)).orElse(StringManager.string(StringManager.DEFAULT_STRING))+
221-
SLASH +project+ SLASH;
289+
final String workspace = config.get(StringManager.string(StringManager.WORKSPACE_PROP_DEF_KEY))
290+
.orElse(StringManager.string(StringManager.DEFAULT_STRING)) +
291+
SLASH + project + SLASH;
222292
// create script in a file located in the project's repository
223293
final File scriptOutput = new File(workspace + CAT_SCAN_SCRIPT);
224294

295+
String setupExternalTools = setupExternalTools(qualityProfiles);
296+
225297
// Write all command lines in a single temporary script
226298
try (
227-
FileWriter script = new FileWriter(scriptOutput)
228-
){
229-
script.write("#!/bin/bash -e");
230-
script.write("\ncd "+workspace);
231-
script.write(StringManager.string(StringManager.CNES_LOG_SEPARATOR)+commandLine);
232-
LOGGER.info("commandLine : " + commandLine);
299+
FileWriter script = new FileWriter(scriptOutput)) {
300+
script.write("#!/bin/bash");
301+
script.write("\ncd " + workspace);
302+
script.write("\n" + setupExternalTools);
303+
script.write(StringManager.string(StringManager.CNES_LOG_SEPARATOR) + commandLine);
233304
} catch (IOException e) {
234305
LOGGER.severe(e.getMessage());
235306
}
236307

237308
// give execution rights on the script
238-
if(!scriptOutput.setExecutable(true)) {
309+
if (!scriptOutput.setExecutable(true)) {
239310
LOGGER.severe(String.format(FILE_PERMISSIONS_ERROR, scriptOutput.getName()));
240311
}
241312

src/main/java/fr/cnes/sonar/plugins/scan/utils/StringManager.java

+9
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,11 @@ public final class StringManager {
6262
* Property for action 1 (scan) param 6 description
6363
*/
6464
public static final String ANALYZE_SPP_DESC = "cnes.action.analyze.param.spp.desc";
65+
/**
66+
* Property for action 1 (scan) param 7 description
67+
*/
68+
public static final String ANALYZE_QUALITY_PROFILES_DESC =
69+
"cnes.action.analyze.param.profiles.desc";
6570
/**
6671
* Property for quality profiles separator
6772
*/
@@ -191,6 +196,10 @@ public final class StringManager {
191196
* Define the name of the projects's sonar-project.properties parameter
192197
*/
193198
public static final String ANALYZE_SPP_NAME = "cnes.action.analyze.param.spp.name";
199+
/**
200+
* Define the name of the project's quality profiles parameter
201+
*/
202+
public static final String ANALYZE_QUALITY_PROFILES_NAME = "cnes.action.analyze.param.profiles.name";
194203
/**
195204
* Define the name of the returned log filed
196205
*/

src/main/java/fr/cnes/sonar/plugins/scan/ws/CnesWs.java

+4
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,10 @@ private void analyzeAction(final NewController controller) {
118118
newParam = analysis.createParam(StringManager.string(StringManager.ANALYZE_SPP_NAME));
119119
newParam.setDescription(StringManager.string(StringManager.ANALYZE_SPP_DESC));
120120
newParam.setRequired(true);
121+
// quality profiles parameter
122+
newParam = analysis.createParam(StringManager.string(StringManager.ANALYZE_QUALITY_PROFILES_NAME));
123+
newParam.setDescription(StringManager.string(StringManager.ANALYZE_QUALITY_PROFILES_DESC));
124+
newParam.setRequired(true);
121125
}
122126

123127

0 commit comments

Comments
 (0)