Skip to content

Commit

Permalink
Add oitnb support (#242)
Browse files Browse the repository at this point in the history
  • Loading branch information
zacharyburnett authored May 27, 2021
1 parent f0a9dc8 commit ebd4f4f
Show file tree
Hide file tree
Showing 9 changed files with 186 additions and 2 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ jobs:
- name: Install Python dependencies
run: |
cd ./test/linters/projects/
pip install -r ./black/requirements.txt -r ./flake8/requirements.txt -r ./mypy/requirements.txt
pip install -r ./black/requirements.txt -r ./flake8/requirements.txt -r ./mypy/requirements.txt -r ./oitnb/requirements.txt
# Ruby

Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ _**Note:** The behavior of actions like this one is currently limited in the con
- [Black](https://black.readthedocs.io)
- [Flake8](http://flake8.pycqa.org)
- [Mypy](https://mypy.readthedocs.io/)
- [oitnb](https://pypi.org/project/oitnb/)
- **Ruby:**
- [RuboCop](https://rubocop.readthedocs.io)
- **Swift:**
Expand Down Expand Up @@ -206,7 +207,7 @@ jobs:

### Linter-specific options

`[linter]` can be one of `black`, `eslint`, `flake8`, `gofmt`, `golint`, `mypy`, `php_codesniffer`, `prettier`, `rubocop`, `stylelint`, `swift_format_official`, `swift_format_lockwood`, `swiftlint` and `xo`:
`[linter]` can be one of `black`, `eslint`, `flake8`, `gofmt`, `golint`, `mypy`, `oitnb`, `php_codesniffer`, `prettier`, `rubocop`, `stylelint`, `swift_format_official`, `swift_format_lockwood`, `swiftlint` and `xo`:

- **`[linter]`:** Enables the linter in your repository. Default: `false`
- **`[linter]_args`**: Additional arguments to pass to the linter. Example: `eslint_args: "--max-warnings 0"` if ESLint checks should fail even if there are no errors and only warnings. Default: `""`
Expand Down Expand Up @@ -248,6 +249,7 @@ Some options are not be available for specific linters:
| gofmt | ✅ | ❌ (go) |
| golint | ❌ | ❌ (go) |
| mypy | ✅ | ✅ |
| oitnb | ✅ | ✅ |
| php_codesniffer | ❌ | ✅ |
| prettier | ✅ | ✅ |
| rubocop | ✅ | ❌ (rb) |
Expand Down
20 changes: 20 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,26 @@ inputs:
required: false
default: ""

oitnb:
description: Enable or disable oitnb checks
required: false
default: "false"
oitnb_args:
description: Additional arguments to pass to the linter
required: false
default: ""
oitnb_dir:
description: Directory where the oitnb command should be run
required: false
oitnb_extensions:
description: Extensions of files to check with oitnb
required: false
default: "py"
oitnb_command_prefix:
description: Shell command to prepend to the linter command
required: false
default: ""

# Ruby

rubocop:
Expand Down
2 changes: 2 additions & 0 deletions src/linters/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const Flake8 = require("./flake8");
const Gofmt = require("./gofmt");
const Golint = require("./golint");
const Mypy = require("./mypy");
const Oitnb = require("./oitnb");
const PHPCodeSniffer = require("./php-codesniffer");
const Prettier = require("./prettier");
const RuboCop = require("./rubocop");
Expand All @@ -28,6 +29,7 @@ const linters = {
// Formatters (should be run after linters)
black: Black,
gofmt: Gofmt,
oitnb: Oitnb,
prettier: Prettier,
swift_format_lockwood: SwiftFormatLockwood,
swift_format_official: SwiftFormatOfficial,
Expand Down
66 changes: 66 additions & 0 deletions src/linters/oitnb.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
const { run } = require("../utils/action");
const commandExists = require("../utils/command-exists");
const { parseErrorsFromDiff } = require("../utils/diff");
const { initLintResult } = require("../utils/lint-result");

/**
* https://pypi.org/project/oitnb/
*/
class Oitnb {
static get name() {
return "oitnb";
}

/**
* 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 Python is installed (required to execute oitnb)
if (!(await commandExists("python"))) {
throw new Error("Python is not installed");
}

// Verify that oitnb is installed
try {
run(`${prefix} oitnb --version`, { dir });
} catch (err) {
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 = "") {
const files = `^.*\\.(${extensions.join("|")})$`;
const fixArg = fix ? "" : "--check --diff";
return run(`${prefix} oitnb ${fixArg} --include "${files}" ${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 {import('../utils/lint-result').LintResult} - Parsed lint result
*/
static parseOutput(dir, output) {
const lintResult = initLintResult();
lintResult.error = parseErrorsFromDiff(output.stdout);
lintResult.isSuccess = output.status === 0;
return lintResult;
}
}

module.exports = Oitnb;
65 changes: 65 additions & 0 deletions test/linters/params/oitnb.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
const Oitnb = require("../../../src/linters/oitnb");
const { TEST_DATE } = require("../../test-utils");

const testName = "oitnb";
const linter = Oitnb;
const commandPrefix = "";
const extensions = ["py"];

// Linting without auto-fixing
function getLintParams(dir) {
const stdoutFile1 = `--- file1.py ${TEST_DATE}\n+++ file1.py ${TEST_DATE}\n@@ -1,10 +1,10 @@\n var_1 = "hello"\n var_2 = "world"\n \n \n-def main (): # Whitespace error\n+def main(): # Whitespace error\n print("hello " + var_2)\n \n \n def add(num_1, num_2):\n return num_1 + num_2\n@@ -19,8 +19,9 @@\n \n \n def divide(num_1, num_2):\n return num_1 / num_2\n \n+\n # Blank lines error\n \n main()`;
const stdoutFile2 = `--- file2.py ${TEST_DATE}\n+++ file2.py ${TEST_DATE}\n@@ -1,2 +1,2 @@\n def add(num_1, num_2):\n- return num_1 + num_2 # Indentation error\n+ return num_1 + num_2 # Indentation error`;
return {
// Expected output of the linting function
cmdOutput: {
status: 1,
stdoutParts: [stdoutFile1, stdoutFile2],
stdout: `${stdoutFile1}\n \n${stdoutFile2}`,
},
// Expected output of the parsing function
lintResult: {
isSuccess: false,
warning: [],
error: [
{
path: "file1.py",
firstLine: 1,
lastLine: 11,
message: ` -var_1 = "hello"\n+var_1 = 'hello'\n -var_2 = "world"\n+-var_2 = 'world'\n \n \n-def main (): # Whitespace error\n+def main(): # Whitespace error\n -print("hello " + var_2)\n+print('hello ' + var_2)\n \n \n def add(num_1, num_2):\n return num_1 + num_2`,
},
{
path: "file1.py",
firstLine: 19,
lastLine: 27,
message: ` \n \n def divide(num_1, num_2):\n return num_1 / num_2\n \n+\n # Blank lines error\n \n main()\n `,
},
{
path: "file2.py",
firstLine: 1,
lastLine: 3,
message: ` def add(num_1, num_2):\n- return num_1 + num_2 # Indentation error\n+ return num_1 + num_2 # Indentation error`,
},
],
},
};
}

// Linting with auto-fixing
function getFixParams(dir) {
return {
// Expected output of the linting function
cmdOutput: {
status: 0,
stdout: "",
},
// Expected output of the parsing function
lintResult: {
isSuccess: true,
warning: [],
error: [],
},
};
}

module.exports = [testName, linter, commandPrefix, extensions, getLintParams, getFixParams];
26 changes: 26 additions & 0 deletions test/linters/projects/oitnb/file1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
var_1 = "hello"
var_2 = "world"


def main (): # Whitespace error
print("hello " + var_2)


def add(num_1, num_2):
return num_1 + num_2


def subtract(num_1, num_2):
return num_1 - num_2


def multiply(num_1, num_2):
return num_1 * num_2


def divide(num_1, num_2):
return num_1 / num_2

# Blank lines error

main()
2 changes: 2 additions & 0 deletions test/linters/projects/oitnb/file2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
def add(num_1, num_2):
return num_1 + num_2 # Indentation error
1 change: 1 addition & 0 deletions test/linters/projects/oitnb/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
oitnb>=0.2.2

0 comments on commit ebd4f4f

Please sign in to comment.