1717package fr .cnes .sonar .plugins .scan .tasks ;
1818
1919import fr .cnes .sonar .plugins .scan .utils .StringManager ;
20+
2021import org .sonar .api .config .Configuration ;
2122import org .sonar .api .server .ws .Request ;
2223import org .sonar .api .server .ws .Response ;
2324import org .sonar .api .utils .text .JsonWriter ;
2425
26+ import com .google .gson .Gson ;
27+ import com .google .gson .reflect .TypeToken ;
28+
2529import java .io .File ;
2630import java .io .FileWriter ;
2731import java .io .IOException ;
32+ import java .lang .reflect .Type ;
2833import java .nio .file .Files ;
2934import java .nio .file .Paths ;
3035import java .text .SimpleDateFormat ;
36+ import java .util .ArrayList ;
3137import java .util .Date ;
38+ import java .util .List ;
3239import java .util .concurrent .*;
3340
3441/**
3542 * Execute the scan of a project
43+ *
3644 * @author lequal
3745 */
3846public 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+ "\n pylint --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+ "\n hadolint -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 ("\n cd " +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 ("\n cd " + 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
0 commit comments