Skip to content

Commit

Permalink
green-code-initiative#92 refactor(rule/python): moves Python rules in…
Browse files Browse the repository at this point in the history
…to `ecocode-rules-specifications` module
  • Loading branch information
jycr committed Apr 14, 2023
1 parent 9e664a7 commit 0551227
Show file tree
Hide file tree
Showing 13 changed files with 152 additions and 104 deletions.
19 changes: 19 additions & 0 deletions ecocode-rules-specifications/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,13 @@
</fileset>
<flattenmapper/>
</copy>
<copy todir="${project.build.directory}/python">
<fileset dir="${project.build.directory}/rules">
<include name="*/python/EC*.html"/>
<include name="*/EC*.json"/>
</fileset>
<flattenmapper/>
</copy>
</target>
</configuration>
<goals>
Expand Down Expand Up @@ -118,6 +125,18 @@
</descriptors>
</configuration>
</execution>
<execution>
<id>assembly-python</id>
<phase>prepare-package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptors>
<descriptor>${project.basedir}/src/main/assembly/python.xml</descriptor>
</descriptors>
</configuration>
</execution>
</executions>
<configuration>
<appendAssemblyId>true</appendAssemblyId>
Expand Down
22 changes: 22 additions & 0 deletions ecocode-rules-specifications/src/main/assembly/python.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.1.1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.1.1 https://maven.apache.org/xsd/assembly-2.1.1.xsd">
<id>python</id>
<formats>
<format>jar</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<fileSets>
<fileSet>
<directory>${project.build.outputDirectory}</directory>
<includes>
<include>io/ecocode/rules/python/*.class</include>
</includes>
<outputDirectory/>
</fileSet>
<fileSet>
<directory>${project.build.directory}/python</directory>
<outputDirectory>io/ecocode/rules/python/specifications</outputDirectory>
</fileSet>
</fileSets>
</assembly>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package io.ecocode.rules.python;

public class PythonRulesRepository {
public static final String REPOSITORY_KEY = "ecocode-python";
public static final String NAME = "ecoCode";
public static final String LANGUAGE = "py";
public static final String RESOURCE_BASE_PATH = PythonRulesRepository.class.getPackageName().replace('.', '/') + "/specifications";
}
15 changes: 15 additions & 0 deletions ecocode-rules-specifications/src/main/rules/EC7/EC7.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"title": "Avoid creating getter and setter methods in classes",
"type": "CODE_SMELL",
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"eco-design",
"performance",
"ecocode"
],
"defaultSeverity": "Minor"
}
74 changes: 39 additions & 35 deletions python-plugin/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,36 @@
<url>https://github.com/green-code-initiative/ecoCode/tree/main/python-plugin</url>

<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>ecocode-rules-specifications</artifactId>
<version>${project.version}</version>
<classifier>python</classifier>
</dependency>

<dependency>
<groupId>org.sonarsource.python</groupId>
<artifactId>sonar-python-plugin</artifactId>
<type>sonar-plugin</type>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>org.sonarsource.sonarqube</groupId>
<artifactId>sonar-plugin-api</artifactId>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>org.sonarsource.analyzer-commons</groupId>
<artifactId>sonar-analyzer-commons</artifactId>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>org.sonarsource.sonarqube</groupId>
<artifactId>sonar-plugin-api-impl</artifactId>
<scope>test</scope>
</dependency>

<!-- for security on regex patterns : https://github.com/google/re2j -->
Expand Down Expand Up @@ -65,44 +81,32 @@
<sonarLintSupported>true</sonarLintSupported>
<requirePlugins>python:${sonar.python.version}</requirePlugins>
<sonarQubeMinVersion>${sonarqube.version}</sonarQubeMinVersion>
<basePlugin>python</basePlugin>
<jreMinVersion>${java.version}</jreMinVersion>
</configuration>
</plugin>
<!-- <plugin>-->
<!-- <groupId>org.apache.maven.plugins</groupId>-->
<!-- <artifactId>maven-shade-plugin</artifactId>-->
<!-- <executions>-->
<!-- <execution>-->
<!-- <phase>package</phase>-->
<!-- <goals>-->
<!-- <goal>shade</goal>-->
<!-- </goals>-->
<!-- <configuration>-->
<!-- <filters>-->
<!-- <filter>-->
<!-- <artifact>commons-*:*</artifact>-->
<!-- <excludes>-->
<!-- <exclude>META-INF/**</exclude>-->
<!-- </excludes>-->
<!-- </filter>-->
<!-- <filter>-->
<!-- <artifact>org.*:*</artifact>-->
<!-- <excludes>-->
<!-- <exclude>META-INF/**</exclude>-->
<!-- <exclude>org/sonar/api/batch/sensor/**</exclude>-->
<!-- </excludes>-->
<!-- </filter>-->
<!-- <filter>-->
<!-- <artifact>com.*:*</artifact>-->
<!-- <excludes>-->
<!-- <exclude>META-INF/**</exclude>-->
<!-- </excludes>-->
<!-- </filter>-->
<!-- </filters>-->
<!-- </configuration>-->
<!-- </execution>-->
<!-- </executions>-->
<!-- </plugin>-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<filters>
<filter>
<artifact>*:ecocode-rules-specifications</artifact>
<excludes>
<exclude>META-INF/**</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,63 +19,66 @@
*/
package fr.greencodeinitiative.python;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import fr.greencodeinitiative.python.checks.AvoidFullSQLRequest;
import fr.greencodeinitiative.python.checks.AvoidGettersAndSetters;
import fr.greencodeinitiative.python.checks.AvoidGlobalVariableInFunctionCheck;
import fr.greencodeinitiative.python.checks.AvoidSQLRequestInLoop;
import fr.greencodeinitiative.python.checks.AvoidTryCatchFinallyCheck;
import fr.greencodeinitiative.python.checks.NoFunctionCallWhenDeclaringForLoop;
import org.apache.commons.lang.StringUtils;
import org.sonar.api.SonarEdition;
import org.sonar.api.SonarProduct;
import org.sonar.api.SonarQubeSide;
import org.sonar.api.SonarRuntime;
import org.sonar.api.server.rule.RulesDefinition;
import org.sonar.api.server.rule.RulesDefinitionAnnotationLoader;
import org.sonar.api.utils.Version;
import org.sonar.plugins.python.api.PythonCustomRuleRepository;
import org.sonarsource.analyzer.commons.RuleMetadataLoader;

public class PythonRuleRepository implements RulesDefinition, PythonCustomRuleRepository {
import java.util.ArrayList;
import java.util.List;

public static final String LANGUAGE = "py";
public static final String NAME = "ecoCode";
public static final String RESOURCE_BASE_PATH = "/fr/greencodeinitiative/l10n/python/rules/python/";
public static final String REPOSITORY_KEY = "ecocode-python";
import static io.ecocode.rules.python.PythonRulesRepository.LANGUAGE;
import static io.ecocode.rules.python.PythonRulesRepository.NAME;
import static io.ecocode.rules.python.PythonRulesRepository.REPOSITORY_KEY;
import static io.ecocode.rules.python.PythonRulesRepository.RESOURCE_BASE_PATH;

@Override
public void define(Context context) {
NewRepository repository = context.createRepository(repositoryKey(), LANGUAGE).setName(NAME);

new RulesDefinitionAnnotationLoader().load(repository, checkClasses().toArray(new Class[] {}));
public class PythonRuleRepository implements RulesDefinition, PythonCustomRuleRepository {
private static final List<Class<?>> ANNOTATED_RULE_CLASSES = List.of(
AvoidGettersAndSetters.class,
AvoidGlobalVariableInFunctionCheck.class,
AvoidSQLRequestInLoop.class,
AvoidTryCatchFinallyCheck.class,
NoFunctionCallWhenDeclaringForLoop.class,
AvoidFullSQLRequest.class
);
private static final Version SONARQUBE_RUNTIME_VERSION = Version.create(9, 8);
private static final SonarRuntime SONARQUBE_RUNTIME = new SonarRuntime() {
@Override
public Version getApiVersion() {
return SONARQUBE_RUNTIME_VERSION;
}

// technical debt
Map<String, String> remediationCosts = new HashMap<>();
remediationCosts.put(AvoidSQLRequestInLoop.RULE_KEY, "10min");
remediationCosts.put(AvoidFullSQLRequest.RULE_KEY, "20min");
repository.rules().forEach(rule -> {
String debt = remediationCosts.get(rule.key());
@Override
public SonarProduct getProduct() {
return SonarProduct.SONARQUBE;
}

// TODO DDC : create support to use org.apache.commons.lang.StringUtils
// if (StringUtils.isBlank(debt)) {
if (debt == null || debt.trim().equals("")) {
// default debt to 5min for issue correction
rule.setDebtRemediationFunction(
rule.debtRemediationFunctions().constantPerIssue("5min"));
} else {
rule.setDebtRemediationFunction(
rule.debtRemediationFunctions().constantPerIssue(debt));
}
});
@Override
public SonarQubeSide getSonarQubeSide() {
return SonarQubeSide.SCANNER;
}

// HTML description
repository.rules().forEach(rule ->
rule.setHtmlDescription(loadResource(RESOURCE_BASE_PATH + rule.key() + ".html")));
@Override
public SonarEdition getEdition() {
return SonarEdition.COMMUNITY;
}
};

@Override
public void define(Context context) {
NewRepository repository = context.createRepository(REPOSITORY_KEY, LANGUAGE).setName(NAME);
RuleMetadataLoader ruleMetadataLoader = new RuleMetadataLoader(RESOURCE_BASE_PATH, SONARQUBE_RUNTIME);
ruleMetadataLoader.addRulesByAnnotatedClass(repository, ANNOTATED_RULE_CLASSES);
repository.done();
}

Expand All @@ -86,30 +89,6 @@ public String repositoryKey() {

@Override
public List<Class> checkClasses() {
return Arrays.asList(
AvoidGettersAndSetters.class,
AvoidGlobalVariableInFunctionCheck.class,
AvoidSQLRequestInLoop.class,
AvoidTryCatchFinallyCheck.class,
NoFunctionCallWhenDeclaringForLoop.class,
AvoidFullSQLRequest.class
);
}

private String loadResource(String path) {
URL resource = getClass().getResource(path);
if (resource == null) {
throw new IllegalStateException("Resource not found: " + path);
}
ByteArrayOutputStream result = new ByteArrayOutputStream();
try (InputStream in = resource.openStream()) {
byte[] buffer = new byte[1024];
for (int len = in.read(buffer); len != -1; len = in.read(buffer)) {
result.write(buffer, 0, len);
}
return new String(result.toByteArray(), StandardCharsets.UTF_8);
} catch (IOException e) {
throw new IllegalStateException("Failed to read resource: " + path, e);
}
return new ArrayList<>(ANNOTATED_RULE_CLASSES);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

import static org.assertj.core.api.Assertions.assertThat;

import io.ecocode.rules.python.PythonRulesRepository;
import org.assertj.core.api.SoftAssertions;
import org.junit.Before;
import org.junit.Test;
Expand All @@ -37,12 +38,12 @@ public void init() {
pythonRuleRepository = new PythonRuleRepository();
context = new RulesDefinition.Context();
pythonRuleRepository.define(context);
repository = context.repository(PythonRuleRepository.REPOSITORY_KEY);
repository = context.repository(PythonRulesRepository.REPOSITORY_KEY);
}

@Test
public void test() {
assertThat(pythonRuleRepository.repositoryKey()).isEqualTo(PythonRuleRepository.REPOSITORY_KEY);
assertThat(pythonRuleRepository.repositoryKey()).isEqualTo(PythonRulesRepository.REPOSITORY_KEY);
assertThat(context.repositories()).hasSize(1).extracting("key").containsExactly(pythonRuleRepository.repositoryKey());
assertThat(context.repositories().get(0).rules()).hasSize(6);
assertThat(pythonRuleRepository.checkClasses()).hasSize(6);
Expand Down

0 comments on commit 0551227

Please sign in to comment.