Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fixit fix exits with 0 even if there were errors #409

Open
samueljsb opened this issue Nov 30, 2023 · 2 comments
Open

fixit fix exits with 0 even if there were errors #409

samueljsb opened this issue Nov 30, 2023 · 2 comments
Labels
bug Something isn't working

Comments

@samueljsb
Copy link
Contributor

fixit fix will exit with an exit code of 0 if it finds errors but does not apply fixes. This makes using it in CI (or pre-commit hooks) awkward because it hides errors that can't be auto-fixed. The solution is to run the tool *twice: once to fix fixable errors, then once again to re-report them in a way that can cause a Ci job to fail. This is a frustrating duplication of work.

Given the outcome of #258, I think this is a bug rather than intentional design, but I'm happy to be corrected if there's another change I've missed 🙂

There are two cases where I would expect a non-zero exit code from fixit fix.

failure to run

When it fails to evaluate the rules at all because of an error:

$ cat t.py
this is not valid python

$ fixit fix -a t.py
t.py: EXCEPTION: Syntax Error @ 1:1.
parser error: error at 1:24: expected one of !=, %, &, (, *, **, +, ,, -, ., /, //, ;, <, <<, <=, ==, >, >=, >>, @, NEWLINE, [, ^, and, if, in, is, not, or, |

this is not valid python
^
Traceback (most recent call last):
  File "/private/tmp/tmpvenv-c329d/venv/lib/python3.11/site-packages/fixit/api.py", line 100, in fixit_bytes
    runner = LintRunner(path, content)
             ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/private/tmp/tmpvenv-c329d/venv/lib/python3.11/site-packages/fixit/engine.py", line 55, in __init__
    self.module: Module = parse_module(source)
                          ^^^^^^^^^^^^^^^^^^^^
  File "/private/tmp/tmpvenv-c329d/venv/lib/python3.11/site-packages/libcst/_parser/entrypoints.py", line 109, in parse_module
    result = _parse(
             ^^^^^^^
  File "/private/tmp/tmpvenv-c329d/venv/lib/python3.11/site-packages/libcst/_parser/entrypoints.py", line 55, in _parse
    return parse(source_str)
           ^^^^^^^^^^^^^^^^^
libcst._exceptions.ParserSyntaxError: Syntax Error @ 1:1.
parser error: error at 1:24: expected one of !=, %, &, (, *, **, +, ,, -, ., /, //, ;, <, <<, <=, ==, >, >=, >>, @, NEWLINE, [, ^, and, if, in, is, not, or, |

this is not valid python
^
🛠️  1 file checked, 1 file with errors 🛠️

$ echo $?
0

I would expect fixit to exit with a non-zero code in the above case.

errors found but not fixed

fixit fix also exits 0 if errors were found but no fixes were applied:

$ cat t.py
try:
    ...
except ValueError or KeyError:
    ...

$ fixit fix -a t.py
t.py@1:0 AvoidOrInExcept: Avoid using 'or' in an except block. For example:'except ValueError or TypeError' only catches 'ValueError'. Instead, use parentheses, 'except (ValueError, TypeError)'
🛠️  1 file checked, 1 file with errors 🛠️

$ echo $?
0

I would also expect it to exit non-zero in this case, although the suggestion in #258 for that to be opt-in seems reasonable if backwards-compatibility is a concern.


Fixit version 2.1.0
Python 3.11.5 (main, Aug 24 2023, 15:09:45) [Clang 14.0.3 (clang-1403.0.22.14.1)]

@amyreese amyreese added the bug Something isn't working label Jan 12, 2024
@amyreese
Copy link
Member

The first case seems like a clear bug — we should probably match the error code that fixit lint gives in cases of these errors.

I'm interested in the CI use case for fixes here. Is CI expecting to auto-amend the fixes in this case, but still fail if the repo is not "lint clean" after fixes? The suggested CI isage is more along the lines of fixit lint --diff, while fixit fix would be used interactively or in cases where outstanding lints wouldn't be reason for the auto-fixes to produce an error code (ie, the fixing was successful, even if the code has outstanding/unfixable errors).

Would a "strict" mode (either a --strict flag or config value) to enforce lint clean results from all commands be a reasonable option?

@samueljsb
Copy link
Contributor Author

I'm interested in the CI use case for fixes here. Is CI expecting to auto-amend the fixes in this case, but still fail if the repo is not "lint clean" after fixes?

There are two cases I'm thinking about here:

  1. In CI it's useful to use the fix command with --diff because the CI job failure then shows exactly what needs to be changed to fix the problem.
  2. With pre-commit, we want the fixes to be applied when they are available, so they can be committed. But we also need to fail if there are unfixable errors. The only way we have to work around this at the moment is to run fixit twice:
-   repo: https://github.com/Instagram/Fixit
    rev: v2.1.0
    hooks:
    -   id: fixit-fix  # fixes if available, but passes if there are unfixable errors
    -   id: fixit-lint  # fails if unfixable errors

Both of these have workarounds (use lint instead of fix in CI and forego the diff; run twice with pre-commit), but it took us a long time using fixit to figure out that our linting process was faulty. It is very surprising that a result that says "there are errors" is counted as a success.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants