diff --git a/pom.xml b/pom.xml
index 205a9ac..a6cb0af 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,5 +1,7 @@
-
+
4.0.0
fr.cnes.sonar.plugins.scan
@@ -18,7 +20,7 @@
2.11.0
5.11.0-M2
0.8.12
- 17
+ 17
https://cnes.fr
CNES
@@ -48,7 +50,7 @@
Topin2001
- Topin
+ Topin
@@ -173,4 +175,4 @@
-
+
\ No newline at end of file
diff --git a/src/main/java/fr/cnes/sonar/plugins/scan/tasks/AbstractTask.java b/src/main/java/fr/cnes/sonar/plugins/scan/tasks/AbstractTask.java
index 20cb575..b469edd 100644
--- a/src/main/java/fr/cnes/sonar/plugins/scan/tasks/AbstractTask.java
+++ b/src/main/java/fr/cnes/sonar/plugins/scan/tasks/AbstractTask.java
@@ -49,8 +49,6 @@ public abstract class AbstractTask implements RequestHandler {
* @throws InterruptedException when a command is not finished
*/
protected String executeCommand(final String command) throws IOException, InterruptedException {
- // log the command to execute
- LOGGER.info(command);
// prepare a string builder for the output gathering
final StringBuilder output = new StringBuilder();
diff --git a/src/main/java/fr/cnes/sonar/plugins/scan/tasks/AnalysisTask.java b/src/main/java/fr/cnes/sonar/plugins/scan/tasks/AnalysisTask.java
index 7fa0417..ec8ff15 100644
--- a/src/main/java/fr/cnes/sonar/plugins/scan/tasks/AnalysisTask.java
+++ b/src/main/java/fr/cnes/sonar/plugins/scan/tasks/AnalysisTask.java
@@ -17,41 +17,48 @@
package fr.cnes.sonar.plugins.scan.tasks;
import fr.cnes.sonar.plugins.scan.utils.StringManager;
+
import org.sonar.api.config.Configuration;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import org.sonar.api.utils.text.JsonWriter;
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
+import java.lang.reflect.Type;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
+import java.util.ArrayList;
import java.util.Date;
+import java.util.List;
import java.util.concurrent.*;
/**
* Execute the scan of a project
+ *
* @author lequal
*/
public class AnalysisTask extends AbstractTask {
private final Configuration config;
- public AnalysisTask(Configuration config){
+ public AnalysisTask(Configuration config) {
this.config = config;
}
+
/**
* Logged message when a file can not be deleted
*/
- private static final String FILE_DELETION_ERROR =
- "The following file could not be deleted: %s.";
+ private static final String FILE_DELETION_ERROR = "The following file could not be deleted: %s.";
/**
* Logged message when a file can not be set as executable
*/
- private static final String FILE_PERMISSIONS_ERROR =
- "Permissions of the following file could not be changed: %s.";
+ private static final String FILE_PERMISSIONS_ERROR = "Permissions of the following file could not be changed: %s.";
/**
* Just a slash
*/
@@ -69,40 +76,48 @@ public AnalysisTask(Configuration config){
*/
private static final String NEW_LINE = "\n";
-
-
/**
* Execute the scan of a project
- * @param projectName name of the project to analyze
- * @param projectFolder url of the folder containing the project to analyze
+ *
+ * @param projectName name of the project to analyze
+ * @param projectFolder url of the folder containing the project to
+ * analyze
* @param sonarProjectProperties the sonar-project.properties as string
+ * @param qualityProfiles the quality profiles as string
* @return logs
- * @throws IOException when a file writing goes wrong
+ * @throws IOException when a file writing goes wrong
* @throws InterruptedException when a command is not finished
*/
- private String analyze(final String projectName, final String projectFolder, final String sonarProjectProperties)
+ private String analyze(final String projectName, final String projectFolder, final String sonarProjectProperties,
+ final String qualityProfiles)
throws IOException, InterruptedException {
// setting a timer based on user's timeout property configuration
- final Integer timeout = Integer.parseInt(config.get(StringManager.string(StringManager.TIMEOUT_PROP_DEF_KEY)).orElse(StringManager.string(StringManager.DEFAULT_STRING)));
+ final Integer timeout = Integer.parseInt(config.get(StringManager.string(StringManager.TIMEOUT_PROP_DEF_KEY))
+ .orElse(StringManager.string(StringManager.DEFAULT_STRING)));
final ExecutorService service = Executors.newSingleThreadExecutor();
try {
final Runnable task = () -> {
try {
// path where spp should be written
final String sppPath = String.format(StringManager.string(StringManager.CNES_SPP_PATH),
- config.get(StringManager.string(StringManager.WORKSPACE_PROP_DEF_KEY)).orElse(StringManager.string(StringManager.DEFAULT_STRING)), projectFolder);
+ config.get(StringManager.string(StringManager.WORKSPACE_PROP_DEF_KEY))
+ .orElse(StringManager.string(StringManager.DEFAULT_STRING)),
+ projectFolder);
// write sonar-project.properties in the project folder
writeTextFile(sppPath, sonarProjectProperties);
// build the scan command
final String analysisCommand = String.format(
StringManager.string(StringManager.CNES_COMMAND_SCAN),
- config.get(StringManager.string(StringManager.SCANNER_PROP_DEF_KEY)).orElse(StringManager.string(StringManager.DEFAULT_STRING)),
- config.get(StringManager.string(StringManager.WORKSPACE_PROP_DEF_KEY)).orElse(StringManager.string(StringManager.DEFAULT_STRING)),
+ config.get(StringManager.string(StringManager.SCANNER_PROP_DEF_KEY))
+ .orElse(StringManager.string(StringManager.DEFAULT_STRING)),
+ config.get(StringManager.string(StringManager.WORKSPACE_PROP_DEF_KEY))
+ .orElse(StringManager.string(StringManager.DEFAULT_STRING)),
projectFolder,
- config.get(StringManager.string(StringManager.WORKSPACE_PROP_DEF_KEY)).orElse(StringManager.string(StringManager.DEFAULT_STRING)),
+ config.get(StringManager.string(StringManager.WORKSPACE_PROP_DEF_KEY))
+ .orElse(StringManager.string(StringManager.DEFAULT_STRING)),
projectFolder);
- final File script = createScript(projectFolder, analysisCommand);
+ final File script = createScript(projectFolder, analysisCommand, qualityProfiles);
// string formatted date as string
final String date = new SimpleDateFormat(
@@ -110,14 +125,18 @@ private String analyze(final String projectName, final String projectFolder, fin
// export log file
final String logPath = String.format(StringManager.string(StringManager.CNES_LOG_PATH),
- config.get(StringManager.string(StringManager.WORKSPACE_PROP_DEF_KEY)).orElse(StringManager.string(StringManager.DEFAULT_STRING)), date, projectName);
+ config.get(StringManager.string(StringManager.WORKSPACE_PROP_DEF_KEY))
+ .orElse(StringManager.string(StringManager.DEFAULT_STRING)),
+ date, projectName);
// scan execution
- final String scriptCommand = config.get(StringManager.string(StringManager.WORKSPACE_PROP_DEF_KEY)).orElse(StringManager.string(StringManager.DEFAULT_STRING)) +
+ final String scriptCommand = config.get(StringManager.string(StringManager.WORKSPACE_PROP_DEF_KEY))
+ .orElse(StringManager.string(StringManager.DEFAULT_STRING)) +
SLASH + projectFolder + SLASH + CAT_SCAN_SCRIPT;
log(executeCommand(scriptCommand));
// log output file
- final String path = config.get(StringManager.string(StringManager.WORKSPACE_PROP_DEF_KEY)).orElse(StringManager.string(StringManager.DEFAULT_STRING)) +
+ final String path = config.get(StringManager.string(StringManager.WORKSPACE_PROP_DEF_KEY))
+ .orElse(StringManager.string(StringManager.DEFAULT_STRING)) +
SLASH + projectFolder + SLASH + CAT_LOG_FILE;
for (final String line : Files.readAllLines(Paths.get(path))) {
log(line + NEW_LINE);
@@ -137,7 +156,6 @@ private String analyze(final String projectName, final String projectFolder, fin
LOGGER.severe(e.getMessage());
}
-
};
final Future> execution = service.submit(task);
@@ -153,13 +171,13 @@ private String analyze(final String projectName, final String projectFolder, fin
service.shutdown();
}
-
// return the complete logs
return getLogs();
}
/**
* Export the sonar-project.properties in the corresponding folder
+ *
* @param path Output folder
* @param data Data to write
* @throws IOException when a file writing goes wrong
@@ -170,7 +188,7 @@ private void writeTextFile(final String path, final String data) throws IOExcept
// create the writer
// true to append; false to overwrite.
- try(FileWriter fileWriter = new FileWriter(spp, false)) {
+ try (FileWriter fileWriter = new FileWriter(spp, false)) {
// write the data
fileWriter.write(data);
}
@@ -178,9 +196,10 @@ private void writeTextFile(final String path, final String data) throws IOExcept
/**
* Use the user's request to launch an scan
- * @param request request coming from the user
+ *
+ * @param request request coming from the user
* @param response response to send to the user
- * @throws IOException when communicating with the client
+ * @throws IOException when communicating with the client
* @throws InterruptedException ...
*/
@Override
@@ -196,9 +215,11 @@ public void handle(final Request request, final Response response)
StringManager.string(StringManager.ANALYZE_FOLDER_NAME));
final String sonarProjectProperties = request.mandatoryParam(
StringManager.string(StringManager.ANALYZE_SPP_NAME));
+ final String qualityProfiles = request.mandatoryParam(
+ StringManager.string(StringManager.ANALYZE_QUALITY_PROFILES_NAME));
// concrete scan
- final String result = analyze(projectName, workspace, sonarProjectProperties);
+ final String result = analyze(projectName, workspace, sonarProjectProperties, qualityProfiles);
// write the json response
try (JsonWriter jsonWriter = response.newJsonWriter()) {
@@ -209,33 +230,83 @@ public void handle(final Request request, final Response response)
}
}
+ private String setupExternalTools(String qualityProfile) {
+ StringBuilder setupExternalTools = new StringBuilder();
+
+ Gson gson = new Gson();
+ Type outerListType = new TypeToken>() {
+ }.getType();
+ List outerList = gson.fromJson(qualityProfile, outerListType);
+ List> qualityProfiles = new ArrayList<>();
+ for (String innerJson : outerList) {
+ Type innerListType = new TypeToken>() {
+ }.getType();
+ List innerList = gson.fromJson(innerJson, innerListType);
+ qualityProfiles.add(innerList);
+ }
+
+ for (List qp : qualityProfiles) {
+ if (qp.get(0).equals("py")) {
+ LOGGER.info("Setup pylint");
+ // Detect and run correct pylintrc according to RNC
+ String pylintrc = "/opt/python/pylintrc_RNC2015_D";
+ switch (qp.get(1)) {
+ case "RNC A":
+ pylintrc = "/opt/python/pylintrc_RNC2015_A_B";
+ break;
+ case "RNC B":
+ pylintrc = "/opt/python/pylintrc_RNC2015_A_B";
+ break;
+ case "RNC C":
+ pylintrc = "/opt/python/pylintrc_RNC2015_C";
+ break;
+ default:
+ break;
+ }
+ setupExternalTools.append(
+ "\npylint --rcfile=" + pylintrc
+ + " --load-plugins=pylint_sonarjson --output-format=sonarjson --output=pylint-report.json *.py");
+ }
+ if (qp.contains("docker")) {
+ LOGGER.info("Setup hadolint");
+ setupExternalTools.append(
+ "\nhadolint -f sonarqube --no-fail --config=/opt/hadolint/hadolint_RNC_A_B_C_D.yaml Dockerfile > hadolint-report.json");
+ }
+ }
+ return setupExternalTools.toString();
+ }
+
/**
- * Create a temporary script containing dedicated command executing sonar-scanner
- * @param project repository containing the source code
+ * Create a temporary script containing dedicated command executing
+ * sonar-scanner
+ *
+ * @param project repository containing the source code
* @param commandLine command line to execute
* @return The created file
*/
- private File createScript(final String project, final String commandLine) {
+ private File createScript(final String project, final String commandLine, final String qualityProfiles) {
// path to the workspace
- final String workspace = config.get(StringManager.string(StringManager.WORKSPACE_PROP_DEF_KEY)).orElse(StringManager.string(StringManager.DEFAULT_STRING))+
- SLASH +project+ SLASH;
+ final String workspace = config.get(StringManager.string(StringManager.WORKSPACE_PROP_DEF_KEY))
+ .orElse(StringManager.string(StringManager.DEFAULT_STRING)) +
+ SLASH + project + SLASH;
// create script in a file located in the project's repository
final File scriptOutput = new File(workspace + CAT_SCAN_SCRIPT);
+ String setupExternalTools = setupExternalTools(qualityProfiles);
+
// Write all command lines in a single temporary script
try (
- FileWriter script = new FileWriter(scriptOutput)
- ){
- script.write("#!/bin/bash -e");
- script.write("\ncd "+workspace);
- script.write(StringManager.string(StringManager.CNES_LOG_SEPARATOR)+commandLine);
- LOGGER.info("commandLine : " + commandLine);
+ FileWriter script = new FileWriter(scriptOutput)) {
+ script.write("#!/bin/bash");
+ script.write("\ncd " + workspace);
+ script.write("\n" + setupExternalTools);
+ script.write(StringManager.string(StringManager.CNES_LOG_SEPARATOR) + commandLine);
} catch (IOException e) {
LOGGER.severe(e.getMessage());
}
// give execution rights on the script
- if(!scriptOutput.setExecutable(true)) {
+ if (!scriptOutput.setExecutable(true)) {
LOGGER.severe(String.format(FILE_PERMISSIONS_ERROR, scriptOutput.getName()));
}
diff --git a/src/main/java/fr/cnes/sonar/plugins/scan/utils/StringManager.java b/src/main/java/fr/cnes/sonar/plugins/scan/utils/StringManager.java
index 75d8e01..193c16a 100644
--- a/src/main/java/fr/cnes/sonar/plugins/scan/utils/StringManager.java
+++ b/src/main/java/fr/cnes/sonar/plugins/scan/utils/StringManager.java
@@ -62,6 +62,11 @@ public final class StringManager {
* Property for action 1 (scan) param 6 description
*/
public static final String ANALYZE_SPP_DESC = "cnes.action.analyze.param.spp.desc";
+ /**
+ * Property for action 1 (scan) param 7 description
+ */
+ public static final String ANALYZE_QUALITY_PROFILES_DESC =
+ "cnes.action.analyze.param.profiles.desc";
/**
* Property for quality profiles separator
*/
@@ -191,6 +196,10 @@ public final class StringManager {
* Define the name of the projects's sonar-project.properties parameter
*/
public static final String ANALYZE_SPP_NAME = "cnes.action.analyze.param.spp.name";
+ /**
+ * Define the name of the project's quality profiles parameter
+ */
+ public static final String ANALYZE_QUALITY_PROFILES_NAME = "cnes.action.analyze.param.profiles.name";
/**
* Define the name of the returned log filed
*/
diff --git a/src/main/java/fr/cnes/sonar/plugins/scan/ws/CnesWs.java b/src/main/java/fr/cnes/sonar/plugins/scan/ws/CnesWs.java
index 218c62b..17ed339 100644
--- a/src/main/java/fr/cnes/sonar/plugins/scan/ws/CnesWs.java
+++ b/src/main/java/fr/cnes/sonar/plugins/scan/ws/CnesWs.java
@@ -118,6 +118,10 @@ private void analyzeAction(final NewController controller) {
newParam = analysis.createParam(StringManager.string(StringManager.ANALYZE_SPP_NAME));
newParam.setDescription(StringManager.string(StringManager.ANALYZE_SPP_DESC));
newParam.setRequired(true);
+ // quality profiles parameter
+ newParam = analysis.createParam(StringManager.string(StringManager.ANALYZE_QUALITY_PROFILES_NAME));
+ newParam.setDescription(StringManager.string(StringManager.ANALYZE_QUALITY_PROFILES_DESC));
+ newParam.setRequired(true);
}
diff --git a/src/main/resources/static/analysis.js b/src/main/resources/static/analysis.js
index 34f21d0..6fdd0d8 100644
--- a/src/main/resources/static/analysis.js
+++ b/src/main/resources/static/analysis.js
@@ -233,33 +233,31 @@ function registerScan(options, token) {
// complete the spp with sources repository
spp = spp.concat("\nsonar.sources=" + sources);
- //This part is not usefull anymore, since we need to implement the pylint analysis
+ const externalReportSonar = "\nsonar.externalIssuesReportPaths=";
+ let toolsPath = ""
+
// if a python quality profile is set and there are no pylintrc set
- // for (const element of qualityprofiles) {
- // const qualityprofile = JSON.parse(element);
- // if (qualityprofile[0].toLowerCase() == "py" && spp.indexOf("sonar.python.pylint.reportPaths") === -1) {
- // // sonar pylint configuration property
- // let pylintrcSonar = "\nsonar.python.pylint.reportPaths=";
- // // name of the configuration file to use
- // let filename = "pylintrc_RNC2015_D";
- // // we append the appropriate one
- // // check if there is a rated A or B profile and add the corresponding file
- // if (qualityprofile[1] == "RNC A" || qualityprofile[1] == "RNC B") {
- // filename = "pylintrc_RNC2015_A_B";
- // spp = spp.concat(pylintrcSonar + pylintrcfolder + filename);
- // info("Use of configuration file " + filename + " for Pylint.");
- // // check if there is a rated C profile and add the corresponding file
- // } else if (qualityprofile[1] == "RNC C") {
- // filename = "pylintrc_RNC2015_C";
- // spp = spp.concat(pylintrcSonar + pylintrcfolder + filename);
- // info("Use of configuration file " + filename + " for Pylint.");
- // // otherwise it is a D configuration to use
- // } else {
- // spp = spp.concat(pylintrcSonar + pylintrcfolder + filename);
- // info("Use of configuration file " + filename + " for Pylint.");
- // }
- // }
- // }
+ for (const element of qualityprofiles) {
+ const qualityprofile = JSON.parse(element);
+ if (qualityprofile[0].toLowerCase() == "py") {
+ if (toolsPath == "") {
+ toolsPath += "./pylint-report.json"
+ } else {
+ toolsPath += ",./pylint-report.json"
+ }
+ }
+ if (qualityprofile[0].toLowerCase() == "docker") {
+ if (toolsPath == "") {
+ toolsPath += "./hadolint-report.json"
+ } else {
+ toolsPath += ",./hadolint-report.json"
+ }
+ }
+
+ }
+ if (toolsPath != "") {
+ spp = spp.concat(externalReportSonar + toolsPath);
+ }
return spp;
@@ -290,13 +288,14 @@ function registerScan(options, token) {
// log the finally used spp
info("Here comes the finally used sonar-project.properties:\n" + spp);
info("The analysis is running, please wait.");
+ //change json to string for qualityProfile
+ const qualityprofiles = JSON.stringify(qualityprofile);
// send post request to the cnes web service
window.SonarRequest.postJSON(
'/api/cnes/analyze',
- { key: key, name: name, folder: folder, sonarProjectProperties: spp }
+ { key: key, name: name, folder: folder, sonarProjectProperties: spp, qualityProfiles: qualityprofiles }
).then(function (response) {
- // on success
// log output
info("Project analysis response: \n" + response.logs);
// wait that sonarqube has finished to import the report to produce the report
@@ -432,7 +431,9 @@ function registerScan(options, token) {
// Check if there are no queued tasks, indicating readiness to report
if (response.queue.length === 0) {
// Produce the report
- callback(key, author, token);
+ console.log('SonarQube done importing the report');
+ setEnabled(true);
+ //callback(key, author, token);
} else {
// Retry after 2 seconds
setTimeout(() => waitSonarQube(key, author, token, callback), 2000);
@@ -529,26 +530,6 @@ function registerScan(options, token) {
});
};
- /**
- * Return a well formatted string for the profile argument of the web service
- * @param options
- */
- let optionsToString = function (options) {
- let result = "";
-
- // we concatenate all profiles' name in a string
- // separated by ';'
- for (let i = 0; i < options.length; ++i) {
- // add a separator when necessary
- if (i > 0) {
- result = result + ';';
- }
- result = result + options[i].value;
- }
-
- return result.replace(new RegExp('\\+', "g"), '%2B');
- };
-
// once the request is done, and the page is still displayed (not closed already)
if (isDisplayedAnalysis) {
diff --git a/src/main/resources/strings.properties b/src/main/resources/strings.properties
index 7d03c6e..a4c3395 100644
--- a/src/main/resources/strings.properties
+++ b/src/main/resources/strings.properties
@@ -79,6 +79,8 @@ cnes.action.analyze.param.name.name=name
cnes.action.analyze.param.folder.name=folder
# Define the name of the projects's sonar-project.properties parameter
cnes.action.analyze.param.spp.name=sonarProjectProperties
+# Define the name of the project's quality profiles parameter
+cnes.action.analyze.param.profiles.name=qualityProfiles
# Define the name of the returned log filed
cnes.action.analyze.response.log=logs
# Property for controller description
@@ -97,6 +99,8 @@ cnes.action.analyze.param.name.desc=Name of the project to analyze.
cnes.action.analyze.param.folder.desc=Name of the project folder.
# Property for action 1 (scan) param 6 description
cnes.action.analyze.param.spp.desc=The classical sonar-project.properties content.
+# Property for action 1 (scan) param 7 description
+cnes.action.analyze.param.profiles.desc=Quality profiles to use.
# Property for action 2 (reporting) param 1 name