From a638e63336800f82be31f0e7c43968b6074dcaa5 Mon Sep 17 00:00:00 2001 From: Kendrick Curtis Date: Tue, 1 Jul 2025 15:25:31 +0100 Subject: [PATCH 1/7] added basic support for both pyrefly and solid-grader --- cli-v2.go | 2 + cmd/analyze.go | 29 +++++++ domain/tool.go | 2 + plugins/tools/pyrefly/plugin.yaml | 15 ++++ plugins/tools/solid_grader/plugin.yaml | 13 +++ tools/language_config.go | 5 ++ tools/pyreflyRunner.go | 78 ++++++++++++++++++ tools/solidGraderRunner.go | 47 +++++++++++ utils/sarif.go | 106 +++++++++++++++++++++++++ 9 files changed, 297 insertions(+) create mode 100644 plugins/tools/pyrefly/plugin.yaml create mode 100644 plugins/tools/solid_grader/plugin.yaml create mode 100644 tools/pyreflyRunner.go create mode 100644 tools/solidGraderRunner.go diff --git a/cli-v2.go b/cli-v2.go index e990590f..11824b47 100644 --- a/cli-v2.go +++ b/cli-v2.go @@ -14,6 +14,8 @@ import ( ) func main() { + + fmt.Printf("TESTING CLI BUILD") // Initialize config global object config.Init() diff --git a/cmd/analyze.go b/cmd/analyze.go index 100c693a..8b71e28e 100644 --- a/cmd/analyze.go +++ b/cmd/analyze.go @@ -412,6 +412,31 @@ func runEnigmaAnalysis(workDirectory string, pathsToCheck []string, outputFile s return tools.RunEnigma(workDirectory, enigma.InstallDir, enigma.Binaries["codacy-enigma-cli"], pathsToCheck, outputFile, outputFormat) } +func runSolidGraderAnalysis(workDirectory string, pathsToCheck []string, outputFile string, outputFormat string) error { + solidGrader := config.Config.Tools()["solid_grader"] + if solidGrader == nil { + log.Fatal("solid_grader tool configuration not found") + } + solidGraderBinary := solidGrader.Binaries["solid_grader"] + return tools.RunSolidGrader( + workDirectory, + solidGrader.InstallDir, + solidGraderBinary, + pathsToCheck, + outputFile, + outputFormat, + ) +} + +func runPyreflyAnalysis(workDirectory string, pathsToCheck []string, outputFile string, outputFormat string) error { + pyrefly := config.Config.Tools()["pyrefly"] + if pyrefly == nil { + log.Fatal("Pyrefly tool configuration not found") + } + pyreflyBinary := pyrefly.Binaries["pyrefly"] + return tools.RunPyrefly(workDirectory, pyreflyBinary, pathsToCheck, outputFile, outputFormat) +} + var analyzeCmd = &cobra.Command{ Use: "analyze", Short: "Runs all configured linters.", @@ -522,6 +547,10 @@ func runTool(workDirectory string, toolName string, args []string, outputFile st return runLizardAnalysis(workDirectory, args, outputFile, outputFormat) case "codacy-enigma-cli": return runEnigmaAnalysis(workDirectory, args, outputFile, outputFormat) + case "solid_grader": + return runSolidGraderAnalysis(workDirectory, args, outputFile, outputFormat) + case "pyrefly": + return runPyreflyAnalysis(workDirectory, args, outputFile, outputFormat) default: return fmt.Errorf("unsupported tool: %s", toolName) } diff --git a/domain/tool.go b/domain/tool.go index 60bfb7f9..97cecfe3 100644 --- a/domain/tool.go +++ b/domain/tool.go @@ -27,6 +27,7 @@ const ( DartAnalyzer string = "d203d615-6cf1-41f9-be5f-e2f660f7850f" Semgrep string = "6792c561-236d-41b7-ba5e-9d6bee0d548b" Lizard string = "76348462-84b3-409a-90d3-955e90abfb87" + SolidGrader string = "716FCE5C-F5E8-4B9D-A7D2-86CDFAE79D45" ) type ToolInfo struct { @@ -45,4 +46,5 @@ var SupportedToolsMetadata = map[string]ToolInfo{ DartAnalyzer: {Name: "dartanalyzer", Priority: 0}, Lizard: {Name: "lizard", Priority: 0}, Semgrep: {Name: "semgrep", Priority: 0}, + SolidGrader: {Name: "solid_grader", Priority: 0}, } diff --git a/plugins/tools/pyrefly/plugin.yaml b/plugins/tools/pyrefly/plugin.yaml new file mode 100644 index 00000000..6ce4eb70 --- /dev/null +++ b/plugins/tools/pyrefly/plugin.yaml @@ -0,0 +1,15 @@ +name: pyrefly +# Pyrefly: Fast, modern Python type checker (https://pyrefly.org/en/docs/installation/) +description: Pyrefly is a fast, modern static type checker for Python, designed for developer productivity and CI integration. +default_version: 0.22.0 +runtime: python +runtime_binaries: + package_manager: python3 + execution: python3 +binaries: + - name: pyrefly + path: "venv/bin/pyrefly" +output_options: + file_flag: "--output" +analysis_options: + default_path: "." \ No newline at end of file diff --git a/plugins/tools/solid_grader/plugin.yaml b/plugins/tools/solid_grader/plugin.yaml new file mode 100644 index 00000000..d0177878 --- /dev/null +++ b/plugins/tools/solid_grader/plugin.yaml @@ -0,0 +1,13 @@ +name: solid_grader +# TODO: Update description with accurate details +description: Solid Grader scans your code for SOLID principle violations. +default_version: 0.1.0 # TODO: Update with actual version if needed +binaries: + - name: solid_grader + path: "/Users/kendrickcurtis/Documents/GitHub/solid-grader2/solid-grader2" +formatters: + - name: text + flag: "" + - name: sarif + flag: "-f sarif" +# TODO: Add download section if binary is to be downloaded automatically \ No newline at end of file diff --git a/tools/language_config.go b/tools/language_config.go index 61360902..69b27f4e 100644 --- a/tools/language_config.go +++ b/tools/language_config.go @@ -58,6 +58,11 @@ func CreateLanguagesConfigFile(apiTools []domain.Tool, toolsConfigDir string, to Languages: []string{"C", "CPP", "C#", "Generic", "Go", "Java", "JavaScript", "JSON", "Kotlin", "Python", "TypeScript", "Ruby", "Rust", "JSX", "PHP", "Scala", "Swift", "Terraform"}, Extensions: []string{".c", ".cpp", ".h", ".hpp", ".cs", ".go", ".java", ".js", ".json", ".kt", ".py", ".ts", ".rb", ".rs", ".jsx", ".php", ".scala", ".swift", ".tf", ".tfvars"}, }, + "pyrefly": { + Name: "pyrefly", + Languages: []string{"Python"}, + Extensions: []string{".py"}, + }, } // Build a list of tool language info for enabled tools diff --git a/tools/pyreflyRunner.go b/tools/pyreflyRunner.go new file mode 100644 index 00000000..a94304ac --- /dev/null +++ b/tools/pyreflyRunner.go @@ -0,0 +1,78 @@ +package tools + +import ( + "codacy/cli-v2/utils" + "fmt" + "io/ioutil" + "os" + "os/exec" + "path/filepath" +) + +// RunPyrefly executes Pyrefly type checking on the specified directory or files +func RunPyrefly(workDirectory string, binary string, files []string, outputFile string, outputFormat string) error { + args := []string{"check"} + + // Always use JSON output for SARIF conversion + var tempFile string + if outputFormat == "sarif" { + tmp, err := ioutil.TempFile("", "pyrefly-*.json") + if err != nil { + return fmt.Errorf("failed to create temporary file: %w", err) + } + tempFile = tmp.Name() + tmp.Close() + defer os.Remove(tempFile) + args = append(args, "--output", tempFile, "--output-format", "json") + } else if outputFile != "" { + args = append(args, "--output", outputFile) + } + if outputFormat == "json" && outputFile == "" { + args = append(args, "--output-format", "json") + } + + // Detect config file (pyrefly.toml or pyproject.toml) + configFiles := []string{"pyrefly.toml", "pyproject.toml"} + for _, configFile := range configFiles { + if _, err := os.Stat(filepath.Join(workDirectory, configFile)); err == nil { + // Pyrefly auto-detects config, so no need to add a flag + break + } + } + + // Add files to check, or "." for current directory + if len(files) > 0 { + args = append(args, files...) + } else { + args = append(args, ".") + } + + cmd := exec.Command(binary, args...) + cmd.Dir = workDirectory + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + err := cmd.Run() + if err != nil { + if _, ok := err.(*exec.ExitError); !ok { + return fmt.Errorf("failed to run Pyrefly: %w", err) + } + } + + if outputFormat == "sarif" { + jsonOutput, err := os.ReadFile(tempFile) + if err != nil { + return fmt.Errorf("failed to read Pyrefly output: %w", err) + } + sarifOutput := utils.ConvertPyreflyToSarif(jsonOutput) + if outputFile != "" { + err = os.WriteFile(outputFile, sarifOutput, 0644) + if err != nil { + return fmt.Errorf("failed to write SARIF output: %w", err) + } + } else { + fmt.Println(string(sarifOutput)) + } + } + return nil +} diff --git a/tools/solidGraderRunner.go b/tools/solidGraderRunner.go new file mode 100644 index 00000000..34b8b281 --- /dev/null +++ b/tools/solidGraderRunner.go @@ -0,0 +1,47 @@ +package tools + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" +) + +func RunSolidGrader(workDirectory string, installationDirectory string, binary string, files []string, outputFile string, outputFormat string) error { + args := []string{} + + if outputFormat == "sarif" { + args = append(args, "-f", "sarif") + } + + if len(files) > 0 { + args = append(args, files...) + } else { + args = append(args, ".") + } + + /*if configExists != "" { + log.Println("Config file found, using it") + args = append(args, "--config", configExists) + } else { + log.Println("No config file found, using tool defaults") + }*/ + + cmd := exec.Command(binary, args...) + cmd.Dir = workDirectory + cmd.Stderr = os.Stderr + if outputFile != "" { + outputWriter, err := os.Create(filepath.Clean(outputFile)) + if err != nil { + return fmt.Errorf("failed to create output file: %w", err) + } + defer outputWriter.Close() + cmd.Stdout = outputWriter + } else { + cmd.Stdout = os.Stdout + } + if err := cmd.Run(); err != nil { + return fmt.Errorf("failed to run solid_grader: %w", err) + } + return nil +} diff --git a/utils/sarif.go b/utils/sarif.go index 1cdef47a..bca8319b 100644 --- a/utils/sarif.go +++ b/utils/sarif.go @@ -259,3 +259,109 @@ func FilterRulesFromSarif(sarifData []byte) ([]byte, error) { return filteredData, nil } + +// PyreflyIssue represents a single issue in Pyrefly's JSON output +// Example fields: line, column, stop_line, stop_column, path, code, name, description, concise_description +// See: https://pyrefly.org/en/docs/usage/#output-formats + +type PyreflyIssue struct { + Line int `json:"line"` + Column int `json:"column"` + StopLine int `json:"stop_line"` + StopColumn int `json:"stop_column"` + Path string `json:"path"` + Code int `json:"code"` + Name string `json:"name"` + Description string `json:"description"` + ConciseDescription string `json:"concise_description"` +} + +// ConvertPyreflyToSarif converts Pyrefly JSON output to SARIF format +func ConvertPyreflyToSarif(pyreflyOutput []byte) []byte { + // Pyrefly outputs: { "errors": [ ... ] } + type pyreflyRoot struct { + Errors []PyreflyIssue `json:"errors"` + } + var root pyreflyRoot + var sarifReport SarifReport + if err := json.Unmarshal(pyreflyOutput, &root); err != nil { + // If parsing fails, return empty SARIF report with Pyrefly metadata + sarifReport = SarifReport{ + Version: "2.1.0", + Schema: "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json", + Runs: []Run{ + { + Tool: Tool{ + Driver: Driver{ + Name: "Pyrefly", + Version: "0.22.0", + InformationURI: "https://pyrefly.org", + }, + }, + Results: []Result{}, + }, + }, + } + } else { + sarifReport = SarifReport{ + Version: "2.1.0", + Schema: "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json", + Runs: []Run{ + { + Tool: Tool{ + Driver: Driver{ + Name: "Pyrefly", + Version: "0.22.0", + InformationURI: "https://pyrefly.org", + }, + }, + Results: make([]Result, 0, len(root.Errors)), + }, + }, + } + for _, issue := range root.Errors { + result := Result{ + RuleID: issue.Name, + Level: "error", // Pyrefly only reports errors + Message: MessageText{ + Text: issue.Description, + }, + Locations: []Location{ + { + PhysicalLocation: PhysicalLocation{ + ArtifactLocation: ArtifactLocation{ + URI: issue.Path, + }, + Region: Region{ + StartLine: issue.Line, + StartColumn: issue.Column, + }, + }, + }, + }, + } + sarifReport.Runs[0].Results = append(sarifReport.Runs[0].Results, result) + } + } + sarifData, err := json.MarshalIndent(sarifReport, "", " ") + if err != nil { + // If marshaling fails, return a minimal SARIF report with Pyrefly metadata + return []byte(`{ + "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "Pyrefly", + "version": "0.22.0", + "informationUri": "https://pyrefly.org" + } + }, + "results": [] + } + ] +}`) + } + return sarifData +} From ea3f7316be8fef27a01dfb07e131f0de17a3b57c Mon Sep 17 00:00:00 2001 From: Kendrick Curtis Date: Wed, 2 Jul 2025 10:50:22 +0100 Subject: [PATCH 2/7] removed solid grader - will file separate PR --- cmd/analyze.go | 18 ---------- domain/tool.go | 2 -- plugins/tools/solid_grader/plugin.yaml | 13 ------- tools/solidGraderRunner.go | 47 -------------------------- 4 files changed, 80 deletions(-) delete mode 100644 plugins/tools/solid_grader/plugin.yaml delete mode 100644 tools/solidGraderRunner.go diff --git a/cmd/analyze.go b/cmd/analyze.go index 8b71e28e..e061168a 100644 --- a/cmd/analyze.go +++ b/cmd/analyze.go @@ -412,22 +412,6 @@ func runEnigmaAnalysis(workDirectory string, pathsToCheck []string, outputFile s return tools.RunEnigma(workDirectory, enigma.InstallDir, enigma.Binaries["codacy-enigma-cli"], pathsToCheck, outputFile, outputFormat) } -func runSolidGraderAnalysis(workDirectory string, pathsToCheck []string, outputFile string, outputFormat string) error { - solidGrader := config.Config.Tools()["solid_grader"] - if solidGrader == nil { - log.Fatal("solid_grader tool configuration not found") - } - solidGraderBinary := solidGrader.Binaries["solid_grader"] - return tools.RunSolidGrader( - workDirectory, - solidGrader.InstallDir, - solidGraderBinary, - pathsToCheck, - outputFile, - outputFormat, - ) -} - func runPyreflyAnalysis(workDirectory string, pathsToCheck []string, outputFile string, outputFormat string) error { pyrefly := config.Config.Tools()["pyrefly"] if pyrefly == nil { @@ -547,8 +531,6 @@ func runTool(workDirectory string, toolName string, args []string, outputFile st return runLizardAnalysis(workDirectory, args, outputFile, outputFormat) case "codacy-enigma-cli": return runEnigmaAnalysis(workDirectory, args, outputFile, outputFormat) - case "solid_grader": - return runSolidGraderAnalysis(workDirectory, args, outputFile, outputFormat) case "pyrefly": return runPyreflyAnalysis(workDirectory, args, outputFile, outputFormat) default: diff --git a/domain/tool.go b/domain/tool.go index 97cecfe3..60bfb7f9 100644 --- a/domain/tool.go +++ b/domain/tool.go @@ -27,7 +27,6 @@ const ( DartAnalyzer string = "d203d615-6cf1-41f9-be5f-e2f660f7850f" Semgrep string = "6792c561-236d-41b7-ba5e-9d6bee0d548b" Lizard string = "76348462-84b3-409a-90d3-955e90abfb87" - SolidGrader string = "716FCE5C-F5E8-4B9D-A7D2-86CDFAE79D45" ) type ToolInfo struct { @@ -46,5 +45,4 @@ var SupportedToolsMetadata = map[string]ToolInfo{ DartAnalyzer: {Name: "dartanalyzer", Priority: 0}, Lizard: {Name: "lizard", Priority: 0}, Semgrep: {Name: "semgrep", Priority: 0}, - SolidGrader: {Name: "solid_grader", Priority: 0}, } diff --git a/plugins/tools/solid_grader/plugin.yaml b/plugins/tools/solid_grader/plugin.yaml deleted file mode 100644 index d0177878..00000000 --- a/plugins/tools/solid_grader/plugin.yaml +++ /dev/null @@ -1,13 +0,0 @@ -name: solid_grader -# TODO: Update description with accurate details -description: Solid Grader scans your code for SOLID principle violations. -default_version: 0.1.0 # TODO: Update with actual version if needed -binaries: - - name: solid_grader - path: "/Users/kendrickcurtis/Documents/GitHub/solid-grader2/solid-grader2" -formatters: - - name: text - flag: "" - - name: sarif - flag: "-f sarif" -# TODO: Add download section if binary is to be downloaded automatically \ No newline at end of file diff --git a/tools/solidGraderRunner.go b/tools/solidGraderRunner.go deleted file mode 100644 index 34b8b281..00000000 --- a/tools/solidGraderRunner.go +++ /dev/null @@ -1,47 +0,0 @@ -package tools - -import ( - "fmt" - "os" - "os/exec" - "path/filepath" -) - -func RunSolidGrader(workDirectory string, installationDirectory string, binary string, files []string, outputFile string, outputFormat string) error { - args := []string{} - - if outputFormat == "sarif" { - args = append(args, "-f", "sarif") - } - - if len(files) > 0 { - args = append(args, files...) - } else { - args = append(args, ".") - } - - /*if configExists != "" { - log.Println("Config file found, using it") - args = append(args, "--config", configExists) - } else { - log.Println("No config file found, using tool defaults") - }*/ - - cmd := exec.Command(binary, args...) - cmd.Dir = workDirectory - cmd.Stderr = os.Stderr - if outputFile != "" { - outputWriter, err := os.Create(filepath.Clean(outputFile)) - if err != nil { - return fmt.Errorf("failed to create output file: %w", err) - } - defer outputWriter.Close() - cmd.Stdout = outputWriter - } else { - cmd.Stdout = os.Stdout - } - if err := cmd.Run(); err != nil { - return fmt.Errorf("failed to run solid_grader: %w", err) - } - return nil -} From 11311ee27faf00cfe54ac074e75d687b539343a5 Mon Sep 17 00:00:00 2001 From: Kendrick Curtis Date: Wed, 2 Jul 2025 11:01:15 +0100 Subject: [PATCH 3/7] removing debug code --- cli-v2.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/cli-v2.go b/cli-v2.go index 11824b47..e990590f 100644 --- a/cli-v2.go +++ b/cli-v2.go @@ -14,8 +14,6 @@ import ( ) func main() { - - fmt.Printf("TESTING CLI BUILD") // Initialize config global object config.Init() From 740762834dd6b9fb68bf51256bffb367f078b28e Mon Sep 17 00:00:00 2001 From: Kendrick Curtis Date: Tue, 8 Jul 2025 14:56:45 +0100 Subject: [PATCH 4/7] added auto test --- plugins/tools/pyrefly/test/expected.sarif | 78 +++++++++++++++++++++ plugins/tools/pyrefly/test/src/test_file.py | 25 +++++++ 2 files changed, 103 insertions(+) create mode 100644 plugins/tools/pyrefly/test/expected.sarif create mode 100644 plugins/tools/pyrefly/test/src/test_file.py diff --git a/plugins/tools/pyrefly/test/expected.sarif b/plugins/tools/pyrefly/test/expected.sarif new file mode 100644 index 00000000..6b9ca8d9 --- /dev/null +++ b/plugins/tools/pyrefly/test/expected.sarif @@ -0,0 +1,78 @@ +{ + "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "results": [ + { + "level": "error", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/test_file.py" + }, + "region": { + "startLine": 12, + "startColumn": 24 + } + } + } + ], + "message": { + "text": "Argument `Literal['one']` is not assignable to parameter `a` with type `int` in function `add_numbers`" + }, + "ruleId": "bad-argument-type" + }, + { + "level": "error", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/test_file.py" + }, + "region": { + "startLine": 14, + "startColumn": 25 + } + } + } + ], + "message": { + "text": "Function declared to return `int` but is missing an explicit `return`" + }, + "ruleId": "bad-return" + }, + { + "level": "error", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/test_file.py" + }, + "region": { + "startLine": 20, + "startColumn": 12 + } + } + } + ], + "message": { + "text": "Returned type `Literal[123]` is not assignable to declared return type `str`" + }, + "ruleId": "bad-return" + } + ], + "tool": { + "driver": { + "informationUri": "https://pyrefly.org", + "name": "Pyrefly", + "rules": null, + "version": "0.22.0" + } + } + } + ] +} \ No newline at end of file diff --git a/plugins/tools/pyrefly/test/src/test_file.py b/plugins/tools/pyrefly/test/src/test_file.py new file mode 100644 index 00000000..bfd484ce --- /dev/null +++ b/plugins/tools/pyrefly/test/src/test_file.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Test file for pyrefly analysis +""" + +def add_numbers(a: int, b: int) -> int: + return a + b + +def call_with_wrong_type(): + # This should trigger a type error in pyrefly + return add_numbers("one", 2) + +def missing_return() -> int: + # This function is missing a return statement + pass + +def wrong_return_type() -> str: + # This function returns an int instead of a str + return 123 + +if __name__ == "__main__": + call_with_wrong_type() + missing_return() + wrong_return_type() \ No newline at end of file From 477f28f844dd3742d7c883e70a84f0f836ad4cc2 Mon Sep 17 00:00:00 2001 From: Kendrick Curtis Date: Wed, 9 Jul 2025 11:06:44 +0100 Subject: [PATCH 5/7] fixed codacy issue --- utils/sarif.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/utils/sarif.go b/utils/sarif.go index 6c42cb78..06a5e1e6 100644 --- a/utils/sarif.go +++ b/utils/sarif.go @@ -277,10 +277,9 @@ func FilterRulesFromSarif(sarifData []byte) ([]byte, error) { return filteredData, nil } -// PyreflyIssue represents a single issue in Pyrefly's JSON output +// PyreflyIssue represents a single issue in Pyrefly's JSON output. // Example fields: line, column, stop_line, stop_column, path, code, name, description, concise_description // See: https://pyrefly.org/en/docs/usage/#output-formats - type PyreflyIssue struct { Line int `json:"line"` Column int `json:"column"` From 85cf9837c3f615ddf11f74ce6a504b72730aace3 Mon Sep 17 00:00:00 2001 From: Kendrick Curtis Date: Mon, 14 Jul 2025 16:39:46 +0100 Subject: [PATCH 6/7] getting build to pass --- .../tools/pyrefly/test/.codacy/codacy.yaml | 16 ++++ plugins/tools/pyrefly/test/actual.sarif | 78 +++++++++++++++++++ plugins/tools/pyrefly/test/expected.sarif | 18 ++--- utils/sarif.go | 14 +++- 4 files changed, 113 insertions(+), 13 deletions(-) create mode 100644 plugins/tools/pyrefly/test/.codacy/codacy.yaml create mode 100644 plugins/tools/pyrefly/test/actual.sarif diff --git a/plugins/tools/pyrefly/test/.codacy/codacy.yaml b/plugins/tools/pyrefly/test/.codacy/codacy.yaml new file mode 100644 index 00000000..5e7392c1 --- /dev/null +++ b/plugins/tools/pyrefly/test/.codacy/codacy.yaml @@ -0,0 +1,16 @@ +runtimes: + - dart@3.7.2 + - go@1.22.3 + - java@17.0.10 + - node@22.2.0 + - python@3.11.11 +tools: + - dartanalyzer@3.7.2 + - eslint@8.57.0 + - lizard@1.17.19 + - pmd@7.11.0 + - pylint@3.3.6 + - pyrefly@0.22.0 + - revive@1.7.0 + - semgrep@1.78.0 + - trivy@0.59.1 diff --git a/plugins/tools/pyrefly/test/actual.sarif b/plugins/tools/pyrefly/test/actual.sarif new file mode 100644 index 00000000..7c99af8b --- /dev/null +++ b/plugins/tools/pyrefly/test/actual.sarif @@ -0,0 +1,78 @@ +{ + "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json", + "runs": [ + { + "results": [ + { + "level": "error", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/test_file.py" + }, + "region": { + "startColumn": 24, + "startLine": 12 + } + } + } + ], + "message": { + "text": "Argument `Literal['one']` is not assignable to parameter `a` with type `int` in function `add_numbers`" + }, + "ruleId": "bad-argument-type" + }, + { + "level": "error", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "test_file.py" + }, + "region": { + "startColumn": 25, + "startLine": 14 + } + } + } + ], + "message": { + "text": "Function declared to return `int` but is missing an explicit `return`" + }, + "ruleId": "bad-return" + }, + { + "level": "error", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/test_file.py" + }, + "region": { + "startColumn": 12, + "startLine": 20 + } + } + } + ], + "message": { + "text": "Returned type `Literal[123]` is not assignable to declared return type `str`" + }, + "ruleId": "bad-return" + } + ], + "tool": { + "driver": { + "informationUri": "https://pyrefly.org", + "name": "Pyrefly", + "rules": null, + "version": "0.22.0" + } + } + } + ], + "version": "2.1.0" +} \ No newline at end of file diff --git a/plugins/tools/pyrefly/test/expected.sarif b/plugins/tools/pyrefly/test/expected.sarif index 6b9ca8d9..41dc18dd 100644 --- a/plugins/tools/pyrefly/test/expected.sarif +++ b/plugins/tools/pyrefly/test/expected.sarif @@ -10,11 +10,11 @@ { "physicalLocation": { "artifactLocation": { - "uri": "src/test_file.py" + "uri": "test_file.py" }, "region": { - "startLine": 12, - "startColumn": 24 + "startColumn": 24, + "startLine": 12 } } } @@ -30,11 +30,11 @@ { "physicalLocation": { "artifactLocation": { - "uri": "src/test_file.py" + "uri": "test_file.py" }, "region": { - "startLine": 14, - "startColumn": 25 + "startColumn": 25, + "startLine": 14 } } } @@ -50,11 +50,11 @@ { "physicalLocation": { "artifactLocation": { - "uri": "src/test_file.py" + "uri": "test_file.py" }, "region": { - "startLine": 20, - "startColumn": 12 + "startColumn": 12, + "startLine": 20 } } } diff --git a/utils/sarif.go b/utils/sarif.go index 06a5e1e6..12daad61 100644 --- a/utils/sarif.go +++ b/utils/sarif.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "os" + "path/filepath" ) // PylintIssue represents a single issue in Pylint's JSON output @@ -21,8 +22,8 @@ type PylintIssue struct { // SarifReport represents the SARIF report structure type SarifReport struct { - Version string `json:"version"` Schema string `json:"$schema"` + Version string `json:"version"` Runs []Run `json:"runs"` } @@ -300,11 +301,12 @@ func ConvertPyreflyToSarif(pyreflyOutput []byte) []byte { } var root pyreflyRoot var sarifReport SarifReport + cwd, _ := os.Getwd() if err := json.Unmarshal(pyreflyOutput, &root); err != nil { // If parsing fails, return empty SARIF report with Pyrefly metadata sarifReport = SarifReport{ - Version: "2.1.0", Schema: "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json", + Version: "2.1.0", Runs: []Run{ { Tool: Tool{ @@ -320,8 +322,8 @@ func ConvertPyreflyToSarif(pyreflyOutput []byte) []byte { } } else { sarifReport = SarifReport{ - Version: "2.1.0", Schema: "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json", + Version: "2.1.0", Runs: []Run{ { Tool: Tool{ @@ -336,6 +338,10 @@ func ConvertPyreflyToSarif(pyreflyOutput []byte) []byte { }, } for _, issue := range root.Errors { + relPath := issue.Path + if rel, err := filepath.Rel(cwd, issue.Path); err == nil { + relPath = rel + } result := Result{ RuleID: issue.Name, Level: "error", // Pyrefly only reports errors @@ -346,7 +352,7 @@ func ConvertPyreflyToSarif(pyreflyOutput []byte) []byte { { PhysicalLocation: PhysicalLocation{ ArtifactLocation: ArtifactLocation{ - URI: issue.Path, + URI: relPath, }, Region: Region{ StartLine: issue.Line, From 6423a6fe2a20f53593944eeb1d97168290c3f193 Mon Sep 17 00:00:00 2001 From: Kendrick Curtis Date: Mon, 14 Jul 2025 16:49:53 +0100 Subject: [PATCH 7/7] added pyrefly to codacy yaml --- .codacy/codacy.yaml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.codacy/codacy.yaml b/.codacy/codacy.yaml index ec91a308..b6a29d57 100644 --- a/.codacy/codacy.yaml +++ b/.codacy/codacy.yaml @@ -1,16 +1,14 @@ runtimes: - - dart@3.7.2 - go@1.22.3 - java@17.0.10 - node@22.2.0 - python@3.11.11 tools: - - codacy-enigma-cli@0.0.1-main.8.49310c3 - - dartanalyzer@3.7.2 - - eslint@8.57.0 + - eslint@9.26.0 - lizard@1.17.19 - pmd@6.55.0 - - pylint@3.3.6 + - pylint@3.3.7 - revive@1.7.0 - semgrep@1.78.0 - trivy@0.59.1 + - pyrefly@0.22.0