diff --git a/README.md b/README.md index 29d73ff1..9d24ce2d 100644 --- a/README.md +++ b/README.md @@ -160,6 +160,7 @@ using the matched rule and runs it. Rules enabled by default are as follows: * `django_south_merge` – adds `--merge` to inconsistent django south migration; * `docker_not_command` – fixes wrong docker commands like `docker tags`; * `dry` – fixes repetitions like `git git push`; +* `fab_command_not_found` – fix misspelled fabric commands; * `fix_alt_space` – replaces Alt+Space with Space character; * `fix_file` – opens a file with an error in your `$EDITOR`; * `git_add` – fixes *"pathspec 'foo' did not match any file(s) known to git."*; diff --git a/tests/rules/test_fab_command_not_found.py b/tests/rules/test_fab_command_not_found.py new file mode 100644 index 00000000..361c3742 --- /dev/null +++ b/tests/rules/test_fab_command_not_found.py @@ -0,0 +1,49 @@ +import pytest +from thefuck.rules.fab_command_not_found import match, get_new_command +from tests.utils import Command + +stderr = ''' +Warning: Command(s) not found: + extenson + deloyp +''' +stdout = ''' +Available commands: + + update_config + prepare_extension + Template A string class for supporting $-substitutions. + deploy + glob Return a list of paths matching a pathname pattern. + install_web + set_version +''' + + +@pytest.mark.parametrize('command', [ + Command('fab extenson', stderr=stderr), + Command('fab deloyp', stderr=stderr), + Command('fab extenson deloyp', stderr=stderr)]) +def test_match(command): + assert match(command) + + +@pytest.mark.parametrize('command', [ + Command('gulp extenson', stderr=stderr), + Command('fab deloyp')]) +def test_not_match(command): + assert not match(command) + + +@pytest.mark.parametrize('script, result', [ + ('fab extenson', 'fab prepare_extension'), + ('fab extenson:version=2016', + 'fab prepare_extension:version=2016'), + ('fab extenson:version=2016 install_web set_version:val=0.5.0', + 'fab prepare_extension:version=2016 install_web set_version:val=0.5.0'), + ('fab extenson:version=2016 deloyp:beta=true -H the.fuck', + 'fab prepare_extension:version=2016 deploy:beta=true -H the.fuck'), +]) +def test_get_new_command(script, result): + command = Command(script, stdout,stderr) + assert get_new_command(command) == result diff --git a/thefuck/rules/fab_command_not_found.py b/thefuck/rules/fab_command_not_found.py new file mode 100644 index 00000000..8d610d4a --- /dev/null +++ b/thefuck/rules/fab_command_not_found.py @@ -0,0 +1,37 @@ +from thefuck.utils import eager, get_closest + + +def match(command): + return (command.script_parts[0] == 'fab' + and 'Warning: Command(s) not found:' in command.stderr) + + +# We need different behavior then in get_all_matched_commands. +@eager +def _get_between(content, start, end=None): + should_yield = False + for line in content.split('\n'): + if start in line: + should_yield = True + continue + + if end and end in line: + return + + if should_yield and line: + yield line.strip().split(' ')[0] + + +def get_new_command(command): + not_found_commands = _get_between( + command.stderr, 'Warning: Command(s) not found:', 'Available commands:') + possible_commands = _get_between( + command.stdout, 'Available commands:') + + script = command.script + for not_found in not_found_commands: + fix = get_closest(not_found, possible_commands) + script = script.replace(' {}'.format(not_found), + ' {}'.format(fix)) + + return script