From 3be0ab52d83ef3c9eb3b53f62d8c4850eaaf2fc1 Mon Sep 17 00:00:00 2001 From: "Greg Mat." <65409906+itsvyle@users.noreply.github.com> Date: Thu, 8 Feb 2024 13:48:33 +0000 Subject: [PATCH 1/9] Should have added basic support for statictest, with barebone tests --- action.yml | 23 +++++++ src/linters/staticcheck.js | 77 ++++++++++++++++++++++ test/linters/projects/staticcheck/file1.go | 37 +++++++++++ test/linters/projects/staticcheck/file2.go | 34 ++++++++++ test/linters/projects/staticcheck/go.mod | 3 + 5 files changed, 174 insertions(+) create mode 100644 src/linters/staticcheck.js create mode 100644 test/linters/projects/staticcheck/file1.go create mode 100644 test/linters/projects/staticcheck/file2.go create mode 100644 test/linters/projects/staticcheck/go.mod diff --git a/action.yml b/action.yml index 0bc82a37..0fc9372b 100644 --- a/action.yml +++ b/action.yml @@ -120,6 +120,29 @@ inputs: required: false default: "false" + + staticcheck: + description: Enable or disable staticcheck checks + required: false + default: "false" + staticcheck_dir: + description: Directory where the staticcheck command should be run + required: false + staticcheck_args: + description: Additional arguments to pass to the linter + required: false + default: "" + staticcheck_extensions: + description: Extensions of files to check with staticcheck + required: false + default: "go" + staticcheck_command_prefix: + description: Shell command to prepend to the linter command + required: false + default: "" + + + # JavaScript eslint: diff --git a/src/linters/staticcheck.js b/src/linters/staticcheck.js new file mode 100644 index 00000000..d883e0f9 --- /dev/null +++ b/src/linters/staticcheck.js @@ -0,0 +1,77 @@ +const { run } = require("../utils/action"); +const commandExists = require("../utils/command-exists"); +const { initLintResult } = require("../utils/lint-result"); +const { capitalizeFirstLetter } = require("../utils/string"); + +const PARSE_REGEX = /^(.+):([0-9]+):[0-9]+: (.+)$/gm; + +/** @typedef {import('../utils/lint-result').LintResult} LintResult */ + +/** + * https://github.com/golang/lint + */ +class StaticCheck { + static get name() { + return "staticcheck"; + } + + /** + * Verifies that all required programs are installed. Throws an error if programs are missing + * @param {string} dir - Directory to run the linting program in + * @param {string} prefix - Prefix to the lint command + */ + static async verifySetup(dir, prefix = "") { + // Verify that golint is installed + if (!(await commandExists("staticcheck"))) { + throw new Error(`${this.name} is not installed`); + } + } + + /** + * Runs the linting program and returns the command output + * @param {string} dir - Directory to run the linter in + * @param {string[]} extensions - File extensions which should be linted + * @param {string} args - Additional arguments to pass to the linter + * @param {boolean} fix - Whether the linter should attempt to fix code style issues automatically + * @param {string} prefix - Prefix to the lint command + * @returns {{status: number, stdout: string, stderr: string}} - Output of the lint command + */ + static lint(dir, extensions, args = "", fix = false, prefix = "") { + if (extensions.length !== 1 || extensions[0] !== "go") { + throw new Error(`${this.name} error: File extensions are not configurable`); + } + + return run(`${prefix} staticcheck -f text ${args} "./..."`, { + dir, + ignoreErrors: true, + }); + } + + /** + * Parses the output of the lint command. Determines the success of the lint process and the + * severity of the identified code style violations + * @param {string} dir - Directory in which the linter has been run + * @param {{status: number, stdout: string, stderr: string}} output - Output of the lint command + * @returns {LintResult} - Parsed lint result + */ + static parseOutput(dir, output) { + const lintResult = initLintResult(); + lintResult.isSuccess = output.status === 0; + + const matches = output.stdout.matchAll(PARSE_REGEX); + for (const match of matches) { + const [_, path, line, text] = match; + const lineNr = parseInt(line, 10); + lintResult.error.push({ + path, + firstLine: lineNr, + lastLine: lineNr, + message: capitalizeFirstLetter(text), + }); + } + + return lintResult; + } +} + +module.exports = Golint; diff --git a/test/linters/projects/staticcheck/file1.go b/test/linters/projects/staticcheck/file1.go new file mode 100644 index 00000000..a6fa6132 --- /dev/null +++ b/test/linters/projects/staticcheck/file1.go @@ -0,0 +1,37 @@ +package main + +import ( + "errors" + "fmt" + "log" +) + +type Result struct { + Entries []string +} + +func Query() (Result, error) { + return Result{ + Entries: []string{}, + }, nil +} + +func ResultEntries() (Result, error) { + err := errors.New("no entries found") + result, err := Query() + if err != nil { + return Result{}, err + } + if len(result.Entries) == 0 { + return Result{}, err + } + return result, nil +} + +func main1() { + result, err := ResultEntries() + if err != nil { + log.Fatal(err) + } + fmt.Printf("result=%v, err=%v", result, err) +} diff --git a/test/linters/projects/staticcheck/file2.go b/test/linters/projects/staticcheck/file2.go new file mode 100644 index 00000000..66a5bbf9 --- /dev/null +++ b/test/linters/projects/staticcheck/file2.go @@ -0,0 +1,34 @@ +package main + +import ( + "fmt" + "log" + "regexp" +) + +func ValidateEmails(addrs []string) (bool, error) { + for _, email := range addrs { + //lint:ignore SA1000 we love invalid regular expressions! + matched, err := regexp.MatchString("^[a-zA-Z0-9.]+@[a-zA-Z0-9]+\\.[a-zA-Z0-9]*$", email) + if err != nil { + return false, err + } + + if !matched { + return false, nil + } + } + + return true, nil +} + +func main2() { + emails := []string{"testuser@gmail.com", "anotheruser@yahoo.com", "onemoreuser@hotmail.com"} + + matched, err := ValidateEmails(emails) + if err != nil { + log.Fatal(err) + } + + fmt.Println(matched) +} \ No newline at end of file diff --git a/test/linters/projects/staticcheck/go.mod b/test/linters/projects/staticcheck/go.mod new file mode 100644 index 00000000..57178014 --- /dev/null +++ b/test/linters/projects/staticcheck/go.mod @@ -0,0 +1,3 @@ +module linting-test + +go 1.21.6 From a95d1d2cb7ff28ad53df96fa1bb58d9b4618c64d Mon Sep 17 00:00:00 2001 From: "Greg Mat." <65409906+itsvyle@users.noreply.github.com> Date: Thu, 8 Feb 2024 15:17:23 +0000 Subject: [PATCH 2/9] Added tests --- src/linters/staticcheck.js | 4 +- test/linters/linters.test.js | 2 + test/linters/params/staticcheck.js | 71 ++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 test/linters/params/staticcheck.js diff --git a/src/linters/staticcheck.js b/src/linters/staticcheck.js index d883e0f9..4e2290a3 100644 --- a/src/linters/staticcheck.js +++ b/src/linters/staticcheck.js @@ -10,7 +10,7 @@ const PARSE_REGEX = /^(.+):([0-9]+):[0-9]+: (.+)$/gm; /** * https://github.com/golang/lint */ -class StaticCheck { +class Staticcheck { static get name() { return "staticcheck"; } @@ -74,4 +74,4 @@ class StaticCheck { } } -module.exports = Golint; +module.exports = Staticcheck; diff --git a/test/linters/linters.test.js b/test/linters/linters.test.js index 6c7366b9..3897e068 100644 --- a/test/linters/linters.test.js +++ b/test/linters/linters.test.js @@ -20,6 +20,7 @@ const prettierParams = require("./params/prettier"); const pylintParams = require("./params/pylint"); const ruboCopParams = require("./params/rubocop"); const rustfmtParams = require("./params/rustfmt"); +const staticcheckParams = require("./params/staticcheck"); const stylelintParams = require("./params/stylelint"); const swiftFormatLockwood = require("./params/swift-format-lockwood"); // const swiftFormatOfficial = require("./params/swift-format-official"); @@ -39,6 +40,7 @@ const linterParams = [ flake8Params, gofmtParams, golintParams, + staticcheckParams, mypyParams, phpCodeSnifferParams, prettierParams, diff --git a/test/linters/params/staticcheck.js b/test/linters/params/staticcheck.js new file mode 100644 index 00000000..9035e15d --- /dev/null +++ b/test/linters/params/staticcheck.js @@ -0,0 +1,71 @@ +const Staticcheck = require("../../../src/linters/staticcheck"); + +const testName = "staticcheck"; +const linter = Staticcheck; +const commandPrefix = ""; +const args = ""; +const extensions = ["go"]; + +// Linting without auto-fixing +function getLintParams(dir) { + const stdoutFile1 = + "file1.go:20:2: this value of err is never used (SA4006)\nfile1.go:20:9: New doesn't have side effects and its return value is ignored (SA4017)\nfile1.go:31:6: func main1 is unused (U1000)"; + const stdoutFile2 = + `file2.go:11:3: this linter directive didn't match anything; should it be removed? (staticcheck)\nfile2.go:12:19: calling regexp.MatchString in a loop has poor performance, consider using regexp.Compile (SA6000)\nfile2.go:25:6: func main2 is unused (U1000)`; + return { + // Expected output of the linting function + cmdOutput: { + status: 1, + stdoutParts: [stdoutFile1, stdoutFile2], + stdout: `${stdoutFile1}\n${stdoutFile2}`, + }, + // Expected output of the parsing function + lintResult: { + isSuccess: false, + warning: [], + error: [ + { + path: "file1.go", + firstLine: 20, + lastLine: 20, + message: "This value of err is never used (SA4006)", + }, + { + path: "file1.go", + firstLine: 20, + lastLine: 20, + message: "New doesn't have side effects and its return value is ignored (SA4017)", + }, + { + path: "file1.go", + firstLine: 31, + lastLine: 31, + message: "Func main1 is unused (U1000)", + }, + { + path: "file2.go", + firstLine: 11, + lastLine: 11, + message: "This linter directive didn't match anything; should it be removed? (staticcheck)", + }, + { + path: "file2.go", + firstLine: 12, + lastLine: 12, + message: "Calling regexp.MatchString in a loop has poor performance, consider using regexp.Compile (SA6000)", + }, + { + path: "file2.go", + firstLine: 25, + lastLine: 25, + message: "Func main2 is unused (U1000)", + }, + ], + }, + }; +} + +// Linting with auto-fixing +const getFixParams = getLintParams; // Does not support auto-fixing -> option has no effect + +module.exports = [testName, linter, commandPrefix, extensions, args, getLintParams, getFixParams]; From 9688e55e7c701944180875befcb3c7259e888fbf Mon Sep 17 00:00:00 2001 From: "Greg Mat." <65409906+itsvyle@users.noreply.github.com> Date: Thu, 8 Feb 2024 15:30:09 +0000 Subject: [PATCH 3/9] Ran prettier --- action.yml | 3 --- test/linters/params/staticcheck.js | 9 +++++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/action.yml b/action.yml index 0fc9372b..c63001c7 100644 --- a/action.yml +++ b/action.yml @@ -120,7 +120,6 @@ inputs: required: false default: "false" - staticcheck: description: Enable or disable staticcheck checks required: false @@ -141,8 +140,6 @@ inputs: required: false default: "" - - # JavaScript eslint: diff --git a/test/linters/params/staticcheck.js b/test/linters/params/staticcheck.js index 9035e15d..3c1a29aa 100644 --- a/test/linters/params/staticcheck.js +++ b/test/linters/params/staticcheck.js @@ -10,8 +10,7 @@ const extensions = ["go"]; function getLintParams(dir) { const stdoutFile1 = "file1.go:20:2: this value of err is never used (SA4006)\nfile1.go:20:9: New doesn't have side effects and its return value is ignored (SA4017)\nfile1.go:31:6: func main1 is unused (U1000)"; - const stdoutFile2 = - `file2.go:11:3: this linter directive didn't match anything; should it be removed? (staticcheck)\nfile2.go:12:19: calling regexp.MatchString in a loop has poor performance, consider using regexp.Compile (SA6000)\nfile2.go:25:6: func main2 is unused (U1000)`; + const stdoutFile2 = `file2.go:11:3: this linter directive didn't match anything; should it be removed? (staticcheck)\nfile2.go:12:19: calling regexp.MatchString in a loop has poor performance, consider using regexp.Compile (SA6000)\nfile2.go:25:6: func main2 is unused (U1000)`; return { // Expected output of the linting function cmdOutput: { @@ -46,13 +45,15 @@ function getLintParams(dir) { path: "file2.go", firstLine: 11, lastLine: 11, - message: "This linter directive didn't match anything; should it be removed? (staticcheck)", + message: + "This linter directive didn't match anything; should it be removed? (staticcheck)", }, { path: "file2.go", firstLine: 12, lastLine: 12, - message: "Calling regexp.MatchString in a loop has poor performance, consider using regexp.Compile (SA6000)", + message: + "Calling regexp.MatchString in a loop has poor performance, consider using regexp.Compile (SA6000)", }, { path: "file2.go", From fe4027f9622d5cd3d740c61efc6b739fce973e65 Mon Sep 17 00:00:00 2001 From: "Greg Mat." <65409906+itsvyle@users.noreply.github.com> Date: Thu, 8 Feb 2024 15:55:34 +0000 Subject: [PATCH 4/9] Added the import of staticcheck to the tests --- .github/workflows/test.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0849f0b9..28a7a121 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -52,6 +52,8 @@ jobs: run: | cd ./test/linters/projects/golint go install golang.org/x/lint/golint@latest + cd ./test/linters/projects/staticcheck + go install honnef.co/go/tools/cmd/staticcheck@latest # Node.js From 79b4abe30e2fc2ef3a540656b0e6fe8cd6ad4cce Mon Sep 17 00:00:00 2001 From: "Greg Mat." <65409906+itsvyle@users.noreply.github.com> Date: Thu, 8 Feb 2024 15:55:50 +0000 Subject: [PATCH 5/9] Added staticcheck to readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index e4a08da4..2eece3c8 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ _**Note:** The behavior of actions like this one is currently limited in the con - **Go:** - [gofmt](https://golang.org/cmd/gofmt) - [golint](https://github.com/golang/lint) + - [staticcheck](https://staticcheck.dev/) - **JavaScript:** - [ESLint](https://eslint.org) - [Prettier](https://prettier.io) @@ -437,6 +438,7 @@ Some options are not available for specific linters: | flake8 | ❌ | ✅ | | gofmt | ✅ | ❌ (go) | | golint | ❌ | ❌ (go) | +| staticcheck | ❌ | ❌ (go) | | mypy | ❌ | ❌ (py) | | oitnb | ✅ | ✅ | | php_codesniffer | ❌ | ✅ | From de12027d197bfa7e29cc8a7eebf5e4e0bf43cf07 Mon Sep 17 00:00:00 2001 From: "Greg Mat." <65409906+itsvyle@users.noreply.github.com> Date: Thu, 8 Feb 2024 15:59:40 +0000 Subject: [PATCH 6/9] Fixed invalid pathing to tests --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 28a7a121..d78eb118 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -52,7 +52,7 @@ jobs: run: | cd ./test/linters/projects/golint go install golang.org/x/lint/golint@latest - cd ./test/linters/projects/staticcheck + cd $GITHUB_WORKSPACE/test/linters/projects/staticcheck go install honnef.co/go/tools/cmd/staticcheck@latest # Node.js From c55872bcef485959f4df42c5a9cedfe5beda1bf6 Mon Sep 17 00:00:00 2001 From: Greg Mat <65409906+itsvyle@users.noreply.github.com> Date: Thu, 8 Feb 2024 16:13:26 +0000 Subject: [PATCH 7/9] Update test.yml --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d78eb118..4b996b23 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -50,9 +50,9 @@ jobs: - name: Install Go dependencies run: | - cd ./test/linters/projects/golint + cd ${{ github.workspace }}/test/linters/projects/golint go install golang.org/x/lint/golint@latest - cd $GITHUB_WORKSPACE/test/linters/projects/staticcheck + cd ${{ github.workspace }}/test/linters/projects/staticcheck go install honnef.co/go/tools/cmd/staticcheck@latest # Node.js From ecf39e591145c6a57b94de189ce7069ebe9f1fbe Mon Sep 17 00:00:00 2001 From: "Greg Mat." <65409906+itsvyle@users.noreply.github.com> Date: Thu, 8 Feb 2024 17:19:00 +0000 Subject: [PATCH 8/9] Updated import method for staticcheck --- .github/workflows/test.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4b996b23..de1dd3c0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -50,9 +50,12 @@ jobs: - name: Install Go dependencies run: | - cd ${{ github.workspace }}/test/linters/projects/golint + cd ./test/linters/projects/golint/ go install golang.org/x/lint/golint@latest - cd ${{ github.workspace }}/test/linters/projects/staticcheck + + - name: Install Go staticcheck + run: | + cd ./test/linters/projects/staticcheck/ go install honnef.co/go/tools/cmd/staticcheck@latest # Node.js From b717fa05a2379844cfbd282e545dfbe62e7261ab Mon Sep 17 00:00:00 2001 From: "Greg Mat." <65409906+itsvyle@users.noreply.github.com> Date: Thu, 8 Feb 2024 17:54:34 +0000 Subject: [PATCH 9/9] Trying new method of matching --- test/linters/params/staticcheck.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/linters/params/staticcheck.js b/test/linters/params/staticcheck.js index 3c1a29aa..1aac8c8d 100644 --- a/test/linters/params/staticcheck.js +++ b/test/linters/params/staticcheck.js @@ -15,7 +15,7 @@ function getLintParams(dir) { // Expected output of the linting function cmdOutput: { status: 1, - stdoutParts: [stdoutFile1, stdoutFile2], + // stdoutParts: [stdoutFile1, stdoutFile2], stdout: `${stdoutFile1}\n${stdoutFile2}`, }, // Expected output of the parsing function