diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3d11d8e..21b0599 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,11 +32,14 @@ jobs: - name: Build with Maven run: ./mvnw -e --no-transfer-progress clean install + continue-on-error: false - name: Check for changes shell: bash run: | - git diff --exit-code || ( + if ! git diff --exit-code; then echo "::error::Code changes detected - run './mvnw clean verify' locally" + echo "::error::Modified files:" + git diff --name-only exit 1 - ) + fi diff --git a/rewrite.yml b/rewrite.yml index fe77ec7..f24f1a2 100644 --- a/rewrite.yml +++ b/rewrite.yml @@ -1,4 +1,6 @@ -# mvn rewrite:run -Drewrite.activeRecipes=org.checkstyle.recipes.OpenRewriteRecipeBestPractices +# OpenRewrite Configuration for Checkstyle Auto-Fix +# Usage: mvn rewrite:run -Drewrite.activeRecipes=org.checkstyle.recipes.OpenRewriteRecipeBestPractices +# This file defines recipes for automatically fixing Checkstyle violations and applying best practices. --- type: specs.openrewrite.org/v1beta/recipe name: org.checkstyle.recipes.OpenRewriteRecipeBestPractices @@ -101,6 +103,11 @@ tags: - code-quality recipeList: - org.checkstyle.autofix.CheckstyleAutoFix: + # Path to Checkstyle violation report (XML or SARIF format) + # Ensure this file exists before running the recipe violationReportPath: "target/checkstyle/checkstyle-report.xml" + # Path or URL to Checkstyle configuration file + # Must be accessible and valid XML format configurationPath: "https://raw.githubusercontent.com/checkstyle/checkstyle/checkstyle-${checkstyle.version}/config/checkstyle-checks.xml" + # Optional: Path to properties file for Checkstyle configuration propertiesPath: "config/checkstyle.properties" diff --git a/src/main/java/org/checkstyle/autofix/CheckstyleAutoFix.java b/src/main/java/org/checkstyle/autofix/CheckstyleAutoFix.java index 76b76f3..44cc555 100644 --- a/src/main/java/org/checkstyle/autofix/CheckstyleAutoFix.java +++ b/src/main/java/org/checkstyle/autofix/CheckstyleAutoFix.java @@ -85,6 +85,7 @@ public String getPropertiesPath() { @Override public List getRecipeList() { + validateInputs(); final ReportParser reportParser = createReportParser(getViolationReportPath()); final List violations = reportParser .parse(Path.of(getViolationReportPath())); @@ -94,6 +95,17 @@ public List getRecipeList() { return CheckstyleRecipeRegistry.getRecipes(violations, configuration); } + private void validateInputs() { + if (violationReportPath == null || violationReportPath.trim().isEmpty()) { + throw new IllegalArgumentException( + "Violation report path cannot be null or empty"); + } + if (configurationPath == null || configurationPath.trim().isEmpty()) { + throw new IllegalArgumentException( + "Configuration path cannot be null or empty"); + } + } + private ReportParser createReportParser(String path) { final ReportParser result; if (path.endsWith(".xml")) { diff --git a/src/main/java/org/checkstyle/autofix/parser/ConfigurationLoader.java b/src/main/java/org/checkstyle/autofix/parser/ConfigurationLoader.java index 1f1247a..2e8cbea 100644 --- a/src/main/java/org/checkstyle/autofix/parser/ConfigurationLoader.java +++ b/src/main/java/org/checkstyle/autofix/parser/ConfigurationLoader.java @@ -71,16 +71,25 @@ private static Map getProperties(Configuration config) { public static Map loadConfiguration( String checkstyleConfigurationPath, String propFile) { + if (checkstyleConfigurationPath == null || checkstyleConfigurationPath.trim().isEmpty()) { + throw new IllegalArgumentException( + "Checkstyle configuration path cannot be null or empty"); + } + Properties props = new Properties(); if (propFile == null) { props = System.getProperties(); } else { + if (propFile.trim().isEmpty()) { + throw new IllegalArgumentException("Properties file path cannot be empty"); + } try (FileInputStream input = new FileInputStream(propFile)) { props.load(input); } catch (IOException exception) { - throw new IllegalStateException("Failed to read: " + propFile, exception); + throw new IllegalStateException("Failed to read properties file: " + + propFile, exception); } } diff --git a/src/main/java/org/checkstyle/autofix/parser/SarifReportParser.java b/src/main/java/org/checkstyle/autofix/parser/SarifReportParser.java index 5035b32..b41f2e0 100644 --- a/src/main/java/org/checkstyle/autofix/parser/SarifReportParser.java +++ b/src/main/java/org/checkstyle/autofix/parser/SarifReportParser.java @@ -46,6 +46,13 @@ public SarifReportParser() { @Override public List parse(Path reportPath) { + if (reportPath == null) { + throw new IllegalArgumentException("Report path cannot be null"); + } + if (!reportPath.toFile().exists()) { + throw new IllegalArgumentException("Report file does not exist: " + reportPath); + } + final SarifSchema210 report; try { report = parser.fromFile(reportPath); @@ -53,6 +60,9 @@ public List parse(Path reportPath) { catch (IOException exception) { throw new IllegalArgumentException("Failed to parse report: " + reportPath, exception); } + if (report == null || report.getRuns() == null || report.getRuns().isEmpty()) { + throw new IllegalArgumentException("SARIF report is empty or invalid: " + reportPath); + } final List result = new ArrayList<>(); for (final Run run: report.getRuns()) { if (run.getResults() != null) { @@ -68,11 +78,24 @@ public List parse(Path reportPath) { } private CheckstyleViolation createViolation(CheckstyleCheck check, Result result) { + if (result.getLevel() == null) { + throw new IllegalStateException("Result level is missing"); + } + if (result.getMessage() == null || result.getMessage().getText() == null) { + throw new IllegalStateException("Result message is missing"); + } + if (result.getLocations() == null || result.getLocations().isEmpty()) { + throw new IllegalStateException("Result locations are missing"); + } + final String severity = result.getLevel().name(); final String message = result.getMessage().getText(); final PhysicalLocation location = result.getLocations().get(0).getPhysicalLocation(); final Path filePath = getFilePath(location); final Region region = location.getRegion(); + if (region == null || region.getStartLine() == null) { + throw new IllegalStateException("Region or start line is missing"); + } final int line = region.getStartLine(); final Optional columnMaybe = Optional.ofNullable(region.getStartColumn()); return columnMaybe.map(column -> { diff --git a/src/main/java/org/checkstyle/autofix/parser/XmlReportParser.java b/src/main/java/org/checkstyle/autofix/parser/XmlReportParser.java index a2c1f4d..d3ed7fc 100644 --- a/src/main/java/org/checkstyle/autofix/parser/XmlReportParser.java +++ b/src/main/java/org/checkstyle/autofix/parser/XmlReportParser.java @@ -56,6 +56,12 @@ public class XmlReportParser implements ReportParser { @Override public List parse(Path xmlPath) { + if (xmlPath == null) { + throw new IllegalArgumentException("XML path cannot be null"); + } + if (!xmlPath.toFile().exists()) { + throw new IllegalArgumentException("XML file does not exist: " + xmlPath); + } final List result = new ArrayList<>(); @@ -108,6 +114,9 @@ private String parseFileTag(StartElement startElement) { break; } } + if (fileName == null || fileName.trim().isEmpty()) { + throw new IllegalStateException("File name attribute is missing or empty"); + } return fileName; }