mirror of
https://github.com/nvbn/thefuck.git
synced 2025-11-01 07:32:09 +00:00
Compare commits
37 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d3a05426de | ||
|
|
88db57b4b1 | ||
|
|
444908ce1c | ||
|
|
2ced7a7f33 | ||
|
|
b28ece0f34 | ||
|
|
eb60900330 | ||
|
|
ed8aaa7f26 | ||
|
|
77992029b6 | ||
|
|
25c858c13e | ||
|
|
60073bea78 | ||
|
|
d10fc80fa5 | ||
|
|
fdea32b47d | ||
|
|
9381cfefa5 | ||
|
|
793510ad48 | ||
|
|
d85099b8da | ||
|
|
ecee70f774 | ||
|
|
70b414aca2 | ||
|
|
80cfd6991d | ||
|
|
0ccb34bde8 | ||
|
|
581a292797 | ||
|
|
7a9d87f502 | ||
|
|
4f165bf6df | ||
|
|
6789701e23 | ||
|
|
64dd018c1a | ||
|
|
3bbd0e9463 | ||
|
|
c53676e42f | ||
|
|
84c16fb69a | ||
|
|
1683f45e94 | ||
|
|
d88454a638 | ||
|
|
8ef9634492 | ||
|
|
335ae40675 | ||
|
|
3bbe391391 | ||
|
|
01a5ba99d0 | ||
|
|
4c3a559124 | ||
|
|
e047c1eb40 | ||
|
|
48e1e4217f | ||
|
|
59dc6cbf90 |
11
.editorconfig
Normal file
11
.editorconfig
Normal file
@@ -0,0 +1,11 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[*.py]
|
||||
max_line_length = 119
|
||||
45
.travis.yml
45
.travis.yml
@@ -1,34 +1,18 @@
|
||||
language: python
|
||||
sudo: false
|
||||
os: linux
|
||||
dist: xenial
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
dist: xenial
|
||||
python: "nightly"
|
||||
- os: linux
|
||||
dist: xenial
|
||||
python: "3.8-dev"
|
||||
- os: linux
|
||||
dist: xenial
|
||||
python: "3.7-dev"
|
||||
- os: linux
|
||||
dist: xenial
|
||||
python: "3.7"
|
||||
- os: linux
|
||||
dist: trusty
|
||||
python: "3.6-dev"
|
||||
- os: linux
|
||||
dist: trusty
|
||||
python: "3.6"
|
||||
- os: linux
|
||||
dist: trusty
|
||||
python: "3.5"
|
||||
- os: linux
|
||||
dist: trusty
|
||||
python: "3.4"
|
||||
- os: linux
|
||||
dist: trusty
|
||||
python: "2.7"
|
||||
- python: "nightly"
|
||||
- python: "3.8-dev"
|
||||
- python: "3.8"
|
||||
- python: "3.7-dev"
|
||||
- python: "3.7"
|
||||
- python: "3.6-dev"
|
||||
- python: "3.6"
|
||||
- python: "3.5"
|
||||
- python: "2.7"
|
||||
- os: osx
|
||||
language: generic
|
||||
allow_failures:
|
||||
@@ -46,6 +30,7 @@ addons:
|
||||
before_install:
|
||||
- if [[ $TRAVIS_OS_NAME == "osx" ]]; then rm -rf /usr/local/include/c++; fi
|
||||
- if [[ $TRAVIS_OS_NAME == "osx" ]]; then brew update; fi
|
||||
- if [[ $TRAVIS_OS_NAME == "osx" ]]; then brew unlink python@2; fi
|
||||
- if [[ $TRAVIS_OS_NAME == "osx" ]]; then brew upgrade python; fi
|
||||
- if [[ $TRAVIS_OS_NAME == "osx" ]]; then pip3 install virtualenv; fi
|
||||
- if [[ $TRAVIS_OS_NAME == "osx" ]]; then virtualenv venv -p python3; fi
|
||||
@@ -60,7 +45,7 @@ script:
|
||||
- flake8
|
||||
- export COVERAGE_PYTHON_VERSION=python-${TRAVIS_PYTHON_VERSION:0:1}
|
||||
- export RUN_TESTS="coverage run --source=thefuck,tests -m py.test -v --capture=sys tests"
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == 3.7 && $TRAVIS_OS_NAME != "osx" ]]; then $RUN_TESTS --enable-functional; fi
|
||||
- if [[ $TRAVIS_PYTHON_VERSION != 3.7 || $TRAVIS_OS_NAME == "osx" ]]; then $RUN_TESTS; fi
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == 3.8 && $TRAVIS_OS_NAME != "osx" ]]; then $RUN_TESTS --enable-functional; fi
|
||||
- if [[ $TRAVIS_PYTHON_VERSION != 3.8 || $TRAVIS_OS_NAME == "osx" ]]; then $RUN_TESTS; fi
|
||||
after_success:
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == 3.7 && $TRAVIS_OS_NAME != "osx" ]]; then coveralls; fi
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == 3.8 && $TRAVIS_OS_NAME != "osx" ]]; then coveralls; fi
|
||||
|
||||
@@ -26,6 +26,8 @@ fixes, etc.
|
||||
|
||||
# Developing
|
||||
|
||||
[Create and activate a Python 3 virtual environment.](https://docs.python.org/3/tutorial/venv.html)
|
||||
|
||||
Install `The Fuck` for development:
|
||||
|
||||
```bash
|
||||
|
||||
23
README.md
23
README.md
@@ -145,7 +145,7 @@ eval $(thefuck --alias FUCK)
|
||||
Changes are only available in a new shell session. To make changes immediately
|
||||
available, run `source ~/.bashrc` (or your shell config file like `.zshrc`).
|
||||
|
||||
To run fixed commands without confirmation, use the `--yeah` option (or just `-y` for short):
|
||||
To run fixed commands without confirmation, use the `--yeah` option (or just `-y` for short, or `--hard` if you're especially frustrated):
|
||||
|
||||
```bash
|
||||
fuck --yeah
|
||||
@@ -182,7 +182,9 @@ following rules are enabled by default:
|
||||
* `cd_mkdir` – creates directories before cd'ing into them;
|
||||
* `cd_parent` – changes `cd..` to `cd ..`;
|
||||
* `chmod_x` – add execution bit;
|
||||
* `choco_install` – append common suffixes for chocolatey packages;
|
||||
* `composer_not_command` – fixes composer command name;
|
||||
* `cp_create_destination` – creates a new directory when you attempt to `cp` or `mv` to a non existent one
|
||||
* `cp_omitting_directory` – adds `-a` when you `cp` directory;
|
||||
* `cpp11` – adds missing `-std=c++11` to `g++` or `clang++`;
|
||||
* `dirty_untar` – fixes `tar x` command that untarred in the current directory;
|
||||
@@ -191,6 +193,7 @@ following rules are enabled by default:
|
||||
* `django_south_merge` – adds `--merge` to inconsistent django south migration;
|
||||
* `docker_login` – executes a `docker login` and repeats the previous command;
|
||||
* `docker_not_command` – fixes wrong docker commands like `docker tags`;
|
||||
* `docker_image_being_used_by_container` ‐ removes the container that is using the image before removing the image;
|
||||
* `dry` – fixes repetitions like `git git push`;
|
||||
* `fab_command_not_found` – fix misspelled fabric commands;
|
||||
* `fix_alt_space` – replaces Alt+Space with Space character;
|
||||
@@ -200,6 +203,7 @@ following rules are enabled by default:
|
||||
* `git_add_force` – adds `--force` to `git add <pathspec>...` when paths are .gitignore'd;
|
||||
* `git_bisect_usage` – fixes `git bisect strt`, `git bisect goood`, `git bisect rset`, etc. when bisecting;
|
||||
* `git_branch_delete` – changes `git branch -d` to `git branch -D`;
|
||||
* `git_branch_delete_checked_out` – changes `git branch -d` to `git checkout master && git branch -D` when trying to delete a checked out branch;
|
||||
* `git_branch_exists` – offers `git branch -d foo`, `git branch -D foo` or `git checkout foo` when creating a branch that already exists;
|
||||
* `git_branch_list` – catches `git branch list` in place of `git branch` and removes created branch;
|
||||
* `git_checkout` – fixes branch name or creates new branch;
|
||||
@@ -226,12 +230,13 @@ following rules are enabled by default:
|
||||
* `git_rm_recursive` – adds `-r` when you try to `rm` a directory;
|
||||
* `git_rm_staged` – adds `-f` or `--cached` when you try to `rm` a file with staged changes
|
||||
* `git_rebase_merge_dir` – offers `git rebase (--continue | --abort | --skip)` or removing the `.git/rebase-merge` dir when a rebase is in progress;
|
||||
* `git_remote_seturl_add` – runs `git remote add` when `git remote set_url` on nonexistant remote;
|
||||
* `git_remote_seturl_add` – runs `git remote add` when `git remote set_url` on nonexistent remote;
|
||||
* `git_stash` – stashes your local modifications before rebasing or switching branch;
|
||||
* `git_stash_pop` – adds your local modifications before popping stash, then resets;
|
||||
* `git_tag_force` – adds `--force` to `git tag <tagname>` when the tag already exists;
|
||||
* `git_two_dashes` – adds a missing dash to commands like `git commit -amend` or `git rebase -continue`;
|
||||
* `go_run` – appends `.go` extension when compiling/running Go programs;
|
||||
* `go_unknown_command` – fixes wrong `go` commands, for example `go bulid`;
|
||||
* `gradle_no_task` – fixes not found or ambiguous `gradle` task;
|
||||
* `gradle_wrapper` – replaces `gradle` with `./gradlew`;
|
||||
* `grep_arguments_order` – fixes `grep` arguments order for situations like `grep -lir . test`;
|
||||
@@ -258,7 +263,7 @@ following rules are enabled by default:
|
||||
* `missing_space_before_subcommand` – fixes command with missing space like `npminstall`;
|
||||
* `mkdir_p` – adds `-p` when you try to create a directory without parent;
|
||||
* `mvn_no_command` – adds `clean package` to `mvn`;
|
||||
* `mvn_unknown_lifecycle_phase` – fixes misspelled lifecycle phases with `mvn`;
|
||||
* `mvn_unknown_lifecycle_phase` – fixes misspelled life cycle phases with `mvn`;
|
||||
* `npm_missing_script` – fixes `npm` custom script name in `npm run-script <script>`;
|
||||
* `npm_run_script` – adds missing `run-script` for custom `npm` scripts;
|
||||
* `npm_wrong_command` – fixes wrong npm commands like `npm urgrade`;
|
||||
@@ -276,7 +281,8 @@ following rules are enabled by default:
|
||||
* `quotation_marks` – fixes uneven usage of `'` and `"` when containing args';
|
||||
* `path_from_history` – replaces not found path with similar absolute path from history;
|
||||
* `react_native_command_unrecognized` – fixes unrecognized `react-native` commands;
|
||||
* `remove_trailing_cedilla` – remove trailling cedillas `ç`, a common typo for european keyboard layouts;
|
||||
* `remove_shell_prompt_literal` – remove leading shell prompt symbol `$`, common when copying commands from documentations;
|
||||
* `remove_trailing_cedilla` – remove trailing cedillas `ç`, a common typo for european keyboard layouts;
|
||||
* `rm_dir` – adds `-rf` when you try to remove a directory;
|
||||
* `scm_correction` – corrects wrong scm like `hg log` to `git log`;
|
||||
* `sed_unterminated_s` – adds missing '/' to `sed`'s `s` commands;
|
||||
@@ -286,6 +292,7 @@ following rules are enabled by default:
|
||||
* `sudo_command_from_user_path` – runs commands from users `$PATH` with `sudo`;
|
||||
* `switch_lang` – switches command from your local layout to en;
|
||||
* `systemctl` – correctly orders parameters of confusing `systemctl`;
|
||||
* `terraform_init.py` – run `terraform init` before plan or apply;
|
||||
* `test.py` – runs `py.test` instead of `test.py`;
|
||||
* `touch` – creates missing directories before "touching";
|
||||
* `tsuru_login` – runs `tsuru login` if not authenticated or session expired;
|
||||
@@ -316,8 +323,10 @@ The following rules are enabled by default on specific platforms only:
|
||||
* `brew_unknown_command` – fixes wrong brew commands, for example `brew docto/brew doctor`;
|
||||
* `brew_update_formula` – turns `brew update <formula>` into `brew upgrade <formula>`;
|
||||
* `dnf_no_such_command` – fixes mistyped DNF commands;
|
||||
* `nixos_cmd_not_found` – installs apps on NixOS;
|
||||
* `pacman` – installs app with `pacman` if it is not installed (uses `yay` or `yaourt` if available);
|
||||
* `pacman_not_found` – fixes package name with `pacman`, `yay` or `yaourt`.
|
||||
* `yum_invalid_operation` – fixes invalid `yum` calls, like `yum isntall vim`;
|
||||
|
||||
The following commands are bundled with *The Fuck*, but are not enabled by
|
||||
default:
|
||||
@@ -348,8 +357,8 @@ Your rule should not change `Command`.
|
||||
|
||||
**Rules api changed in 3.0:** To access a rule's settings, import it with
|
||||
`from thefuck.conf import settings`
|
||||
|
||||
`settings` is a special object assembled from `~/.config/thefuck/settings.py`,
|
||||
|
||||
`settings` is a special object assembled from `~/.config/thefuck/settings.py`,
|
||||
and values from env ([see more below](#settings)).
|
||||
|
||||
A simple example rule for running a script with `sudo`:
|
||||
@@ -383,7 +392,7 @@ requires_output = True
|
||||
Several *The Fuck* parameters can be changed in the file `$XDG_CONFIG_HOME/thefuck/settings.py`
|
||||
(`$XDG_CONFIG_HOME` defaults to `~/.config`):
|
||||
|
||||
* `rules` – list of enabled rules, by default `thefuck.conf.DEFAULT_RULES`;
|
||||
* `rules` – list of enabled rules, by default `thefuck.const.DEFAULT_RULES`;
|
||||
* `exclude_rules` – list of disabled rules, by default `[]`;
|
||||
* `require_confirmation` – requires confirmation before running new command, by default `True`;
|
||||
* `wait_command` – max amount of time in seconds for getting previous command output;
|
||||
|
||||
@@ -3,7 +3,6 @@ build: false
|
||||
environment:
|
||||
matrix:
|
||||
- PYTHON: "C:/Python27"
|
||||
- PYTHON: "C:/Python34"
|
||||
- PYTHON: "C:/Python35"
|
||||
- PYTHON: "C:/Python36"
|
||||
- PYTHON: "C:/Python37"
|
||||
|
||||
@@ -32,6 +32,6 @@ call('git push --tags', shell=True)
|
||||
|
||||
env = os.environ
|
||||
env['CONVERT_README'] = 'true'
|
||||
call('rm -rf dist/*')
|
||||
call('rm -rf dist/*', shell=True, env=env)
|
||||
call('python setup.py sdist bdist_wheel', shell=True, env=env)
|
||||
call('twine upload dist/*', shell=True, env=env)
|
||||
|
||||
6
setup.py
6
setup.py
@@ -26,12 +26,12 @@ if version < (2, 7):
|
||||
print('thefuck requires Python version 2.7 or later' +
|
||||
' ({}.{} detected).'.format(*version))
|
||||
sys.exit(-1)
|
||||
elif (3, 0) < version < (3, 4):
|
||||
print('thefuck requires Python version 3.4 or later' +
|
||||
elif (3, 0) < version < (3, 5):
|
||||
print('thefuck requires Python version 3.5 or later' +
|
||||
' ({}.{} detected).'.format(*version))
|
||||
sys.exit(-1)
|
||||
|
||||
VERSION = '3.29'
|
||||
VERSION = '3.30'
|
||||
|
||||
install_requires = ['psutil', 'colorama', 'six', 'decorator', 'pyte']
|
||||
extras_require = {':python_version<"3.4"': ['pathlib2'],
|
||||
|
||||
@@ -76,6 +76,45 @@ apt_get_operations = ['update', 'upgrade', 'install', 'remove', 'autoremove',
|
||||
'dselect-upgrade', 'clean', 'autoclean', 'check',
|
||||
'changelog', 'download']
|
||||
|
||||
new_apt_get_help = b'''apt 1.6.12 (amd64)
|
||||
Usage: apt-get [options] command
|
||||
apt-get [options] install|remove pkg1 [pkg2 ...]
|
||||
apt-get [options] source pkg1 [pkg2 ...]
|
||||
|
||||
apt-get is a command line interface for retrieval of packages
|
||||
and information about them from authenticated sources and
|
||||
for installation, upgrade and removal of packages together
|
||||
with their dependencies.
|
||||
|
||||
Most used commands:
|
||||
update - Retrieve new lists of packages
|
||||
upgrade - Perform an upgrade
|
||||
install - Install new packages (pkg is libc6 not libc6.deb)
|
||||
remove - Remove packages
|
||||
purge - Remove packages and config files
|
||||
autoremove - Remove automatically all unused packages
|
||||
dist-upgrade - Distribution upgrade, see apt-get(8)
|
||||
dselect-upgrade - Follow dselect selections
|
||||
build-dep - Configure build-dependencies for source packages
|
||||
clean - Erase downloaded archive files
|
||||
autoclean - Erase old downloaded archive files
|
||||
check - Verify that there are no broken dependencies
|
||||
source - Download source archives
|
||||
download - Download the binary package into the current directory
|
||||
changelog - Download and display the changelog for the given package
|
||||
|
||||
See apt-get(8) for more information about the available commands.
|
||||
Configuration options and syntax is detailed in apt.conf(5).
|
||||
Information about how to configure sources can be found in sources.list(5).
|
||||
Package and version choices can be expressed via apt_preferences(5).
|
||||
Security details are available in apt-secure(8).
|
||||
This APT has Super Cow Powers.
|
||||
'''
|
||||
new_apt_get_operations = ['update', 'upgrade', 'install', 'remove', 'purge',
|
||||
'autoremove', 'dist-upgrade', 'dselect-upgrade',
|
||||
'build-dep', 'clean', 'autoclean', 'check',
|
||||
'source', 'download', 'changelog']
|
||||
|
||||
|
||||
@pytest.mark.parametrize('script, output', [
|
||||
('apt', invalid_operation('saerch')),
|
||||
@@ -104,7 +143,8 @@ def set_help(mocker):
|
||||
|
||||
@pytest.mark.parametrize('app, help_text, operations', [
|
||||
('apt', apt_help, apt_operations),
|
||||
('apt-get', apt_get_help, apt_get_operations)
|
||||
('apt-get', apt_get_help, apt_get_operations),
|
||||
('apt-get', new_apt_get_help, new_apt_get_operations)
|
||||
])
|
||||
def test_get_operations(set_help, app, help_text, operations):
|
||||
set_help(help_text)
|
||||
@@ -116,6 +156,8 @@ def test_get_operations(set_help, app, help_text, operations):
|
||||
apt_get_help, 'apt-get install vim'),
|
||||
('apt saerch vim', invalid_operation('saerch'),
|
||||
apt_help, 'apt search vim'),
|
||||
('apt uninstall vim', invalid_operation('uninstall'),
|
||||
apt_help, 'apt remove vim'),
|
||||
])
|
||||
def test_get_new_command(set_help, output, script, help_text, result):
|
||||
set_help(help_text)
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import pytest
|
||||
from thefuck.rules.apt_list_upgradable import get_new_command, match
|
||||
from thefuck.types import Command
|
||||
|
||||
match_output = '''
|
||||
full_english_output = '''
|
||||
Hit:1 http://us.archive.ubuntu.com/ubuntu zesty InRelease
|
||||
Hit:2 http://us.archive.ubuntu.com/ubuntu zesty-updates InRelease
|
||||
Get:3 http://us.archive.ubuntu.com/ubuntu zesty-backports InRelease [89.2 kB]
|
||||
@@ -17,6 +19,11 @@ Reading state information... Done
|
||||
8 packages can be upgraded. Run 'apt list --upgradable' to see them.
|
||||
'''
|
||||
|
||||
match_output = [
|
||||
full_english_output,
|
||||
'Führen Sie »apt list --upgradable« aus, um sie anzuzeigen.' # German
|
||||
]
|
||||
|
||||
no_match_output = '''
|
||||
Hit:1 http://us.archive.ubuntu.com/ubuntu zesty InRelease
|
||||
Get:2 http://us.archive.ubuntu.com/ubuntu zesty-updates InRelease [89.2 kB]
|
||||
@@ -48,8 +55,9 @@ All packages are up to date.
|
||||
'''
|
||||
|
||||
|
||||
def test_match():
|
||||
assert match(Command('sudo apt update', match_output))
|
||||
@pytest.mark.parametrize('output', match_output)
|
||||
def test_match(output):
|
||||
assert match(Command('sudo apt update', output))
|
||||
|
||||
|
||||
@pytest.mark.parametrize('command', [
|
||||
@@ -67,9 +75,10 @@ def test_not_match(command):
|
||||
assert not match(command)
|
||||
|
||||
|
||||
def test_get_new_command():
|
||||
new_command = get_new_command(Command('sudo apt update', match_output))
|
||||
@pytest.mark.parametrize('output', match_output)
|
||||
def test_get_new_command(output):
|
||||
new_command = get_new_command(Command('sudo apt update', output))
|
||||
assert new_command == 'sudo apt list --upgradable'
|
||||
|
||||
new_command = get_new_command(Command('apt update', match_output))
|
||||
new_command = get_new_command(Command('apt update', output))
|
||||
assert new_command == 'apt list --upgradable'
|
||||
|
||||
86
tests/rules/test_choco_install.py
Normal file
86
tests/rules/test_choco_install.py
Normal file
@@ -0,0 +1,86 @@
|
||||
import pytest
|
||||
from thefuck.rules.choco_install import match, get_new_command
|
||||
from thefuck.types import Command
|
||||
|
||||
|
||||
package_not_found_error = (
|
||||
'Chocolatey v0.10.15\n'
|
||||
'Installing the following packages:\n'
|
||||
'logstitcher\n'
|
||||
'By installing you accept licenses for the packages.\n'
|
||||
'logstitcher not installed. The package was not found with the source(s) listed.\n'
|
||||
' Source(s): \'https://chocolatey.org/api/v2/\'\n'
|
||||
' NOTE: When you specify explicit sources, it overrides default sources.\n'
|
||||
'If the package version is a prerelease and you didn\'t specify `--pre`,\n'
|
||||
' the package may not be found.\n'
|
||||
'Please see https://chocolatey.org/docs/troubleshooting for more\n'
|
||||
' assistance.\n'
|
||||
'\n'
|
||||
'Chocolatey installed 0/1 packages. 1 packages failed.\n'
|
||||
' See the log for details (C:\\ProgramData\\chocolatey\\logs\\chocolatey.log).\n'
|
||||
'\n'
|
||||
'Failures\n'
|
||||
' - logstitcher - logstitcher not installed. The package was not found with the source(s) listed.\n'
|
||||
' Source(s): \'https://chocolatey.org/api/v2/\'\n'
|
||||
' NOTE: When you specify explicit sources, it overrides default sources.\n'
|
||||
'If the package version is a prerelease and you didn\'t specify `--pre`,\n'
|
||||
' the package may not be found.\n'
|
||||
'Please see https://chocolatey.org/docs/troubleshooting for more\n'
|
||||
' assistance.\n'
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('command', [
|
||||
Command('choco install logstitcher', package_not_found_error),
|
||||
Command('cinst logstitcher', package_not_found_error),
|
||||
Command('choco install logstitcher -y', package_not_found_error),
|
||||
Command('cinst logstitcher -y', package_not_found_error),
|
||||
Command('choco install logstitcher -y -n=test', package_not_found_error),
|
||||
Command('cinst logstitcher -y -n=test', package_not_found_error),
|
||||
Command('choco install logstitcher -y -n=test /env', package_not_found_error),
|
||||
Command('cinst logstitcher -y -n=test /env', package_not_found_error),
|
||||
Command('choco install chocolatey -y', package_not_found_error),
|
||||
Command('cinst chocolatey -y', package_not_found_error)])
|
||||
def test_match(command):
|
||||
assert match(command)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('command', [
|
||||
Command('choco /?', ''),
|
||||
Command('choco upgrade logstitcher', ''),
|
||||
Command('cup logstitcher', ''),
|
||||
Command('choco upgrade logstitcher -y', ''),
|
||||
Command('cup logstitcher -y', ''),
|
||||
Command('choco upgrade logstitcher -y -n=test', ''),
|
||||
Command('cup logstitcher -y -n=test', ''),
|
||||
Command('choco upgrade logstitcher -y -n=test /env', ''),
|
||||
Command('cup logstitcher -y -n=test /env', ''),
|
||||
Command('choco upgrade chocolatey -y', ''),
|
||||
Command('cup chocolatey -y', ''),
|
||||
Command('choco uninstall logstitcher', ''),
|
||||
Command('cuninst logstitcher', ''),
|
||||
Command('choco uninstall logstitcher -y', ''),
|
||||
Command('cuninst logstitcher -y', ''),
|
||||
Command('choco uninstall logstitcher -y -n=test', ''),
|
||||
Command('cuninst logstitcher -y -n=test', ''),
|
||||
Command('choco uninstall logstitcher -y -n=test /env', ''),
|
||||
Command('cuninst logstitcher -y -n=test /env', ''),
|
||||
Command('choco uninstall chocolatey -y', ''),
|
||||
Command('cuninst chocolatey -y', '')])
|
||||
def not_test_match(command):
|
||||
assert not match(command)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('before, after', [
|
||||
('choco install logstitcher', 'choco install logstitcher.install'),
|
||||
('cinst logstitcher', 'cinst logstitcher.install'),
|
||||
('choco install logstitcher -y', 'choco install logstitcher.install -y'),
|
||||
('cinst logstitcher -y', 'cinst logstitcher.install -y'),
|
||||
('choco install logstitcher -y -n=test', 'choco install logstitcher.install -y -n=test'),
|
||||
('cinst logstitcher -y -n=test', 'cinst logstitcher.install -y -n=test'),
|
||||
('choco install logstitcher -y -n=test /env', 'choco install logstitcher.install -y -n=test /env'),
|
||||
('cinst logstitcher -y -n=test /env', 'cinst logstitcher.install -y -n=test /env'),
|
||||
('choco install chocolatey -y', 'choco install chocolatey.install -y'),
|
||||
('cinst chocolatey -y', 'cinst chocolatey.install -y'), ])
|
||||
def test_get_new_command(before, after):
|
||||
assert (get_new_command(Command(before, '')) == after)
|
||||
30
tests/rules/test_cp_create_destination.py
Normal file
30
tests/rules/test_cp_create_destination.py
Normal file
@@ -0,0 +1,30 @@
|
||||
import pytest
|
||||
from thefuck.rules.cp_create_destination import match, get_new_command
|
||||
from thefuck.types import Command
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"script, output",
|
||||
[("cp", "cp: directory foo does not exist\n"), ("mv", "No such file or directory")],
|
||||
)
|
||||
def test_match(script, output):
|
||||
assert match(Command(script, output))
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"script, output", [("cp", ""), ("mv", ""), ("ls", "No such file or directory")]
|
||||
)
|
||||
def test_not_match(script, output):
|
||||
assert not match(Command(script, output))
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"script, output, new_command",
|
||||
[
|
||||
("cp foo bar/", "cp: directory foo does not exist\n", "mkdir -p bar/ && cp foo bar/"),
|
||||
("mv foo bar/", "No such file or directory", "mkdir -p bar/ && mv foo bar/"),
|
||||
("cp foo bar/baz/", "cp: directory foo does not exist\n", "mkdir -p bar/baz/ && cp foo bar/baz/"),
|
||||
],
|
||||
)
|
||||
def test_get_new_command(script, output, new_command):
|
||||
assert get_new_command(Command(script, output)) == new_command
|
||||
27
tests/rules/test_docker_image_being_used_by_container.py
Normal file
27
tests/rules/test_docker_image_being_used_by_container.py
Normal file
@@ -0,0 +1,27 @@
|
||||
from thefuck.rules.docker_image_being_used_by_container import match, get_new_command
|
||||
from thefuck.types import Command
|
||||
|
||||
|
||||
def test_match():
|
||||
err_response = """Error response from daemon: conflict: unable to delete cd809b04b6ff (cannot be forced) - image is being used by running container e5e2591040d1"""
|
||||
assert match(Command('docker image rm -f cd809b04b6ff', err_response))
|
||||
|
||||
|
||||
def test_not_match():
|
||||
err_response = 'bash: docker: command not found'
|
||||
assert not match(Command('docker image rm -f cd809b04b6ff', err_response))
|
||||
|
||||
|
||||
def test_not_docker_command():
|
||||
err_response = """Error response from daemon: conflict: unable to delete cd809b04b6ff (cannot be forced) - image is being used by running container e5e2591040d1"""
|
||||
assert not match(Command('git image rm -f cd809b04b6ff', err_response))
|
||||
|
||||
|
||||
def test_get_new_command():
|
||||
err_response = """
|
||||
Error response from daemon: conflict: unable to delete cd809b04b6ff (cannot be forced) - image
|
||||
is being used by running container e5e2591040d1
|
||||
"""
|
||||
result = get_new_command(Command('docker image rm -f cd809b04b6ff', err_response))
|
||||
expected = 'docker container rm -f e5e2591040d1 && docker image rm -f cd809b04b6ff'
|
||||
assert result == expected
|
||||
@@ -4,6 +4,46 @@ from thefuck.types import Command
|
||||
from thefuck.rules.docker_not_command import get_new_command, match
|
||||
|
||||
|
||||
_DOCKER_SWARM_OUTPUT = '''
|
||||
Usage: docker swarm COMMAND
|
||||
|
||||
Manage Swarm
|
||||
|
||||
Commands:
|
||||
ca Display and rotate the root CA
|
||||
init Initialize a swarm
|
||||
join Join a swarm as a node and/or manager
|
||||
join-token Manage join tokens
|
||||
leave Leave the swarm
|
||||
unlock Unlock swarm
|
||||
unlock-key Manage the unlock key
|
||||
update Update the swarm
|
||||
|
||||
Run 'docker swarm COMMAND --help' for more information on a command.
|
||||
'''
|
||||
_DOCKER_IMAGE_OUTPUT = '''
|
||||
Usage: docker image COMMAND
|
||||
|
||||
Manage images
|
||||
|
||||
Commands:
|
||||
build Build an image from a Dockerfile
|
||||
history Show the history of an image
|
||||
import Import the contents from a tarball to create a filesystem image
|
||||
inspect Display detailed information on one or more images
|
||||
load Load an image from a tar archive or STDIN
|
||||
ls List images
|
||||
prune Remove unused images
|
||||
pull Pull an image or a repository from a registry
|
||||
push Push an image or a repository to a registry
|
||||
rm Remove one or more images
|
||||
save Save one or more images to a tar archive (streamed to STDOUT by default)
|
||||
tag Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE
|
||||
|
||||
Run 'docker image COMMAND --help' for more information on a command.
|
||||
'''
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def docker_help(mocker):
|
||||
help = b'''Usage: docker [OPTIONS] COMMAND [arg...]
|
||||
@@ -104,6 +144,94 @@ Run 'docker COMMAND --help' for more information on a command.
|
||||
return mock
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def docker_help_new(mocker):
|
||||
helptext_new = b'''
|
||||
Usage: docker [OPTIONS] COMMAND
|
||||
|
||||
A self-sufficient runtime for containers
|
||||
|
||||
Options:
|
||||
--config string Location of client config files (default "/Users/ik1ne/.docker")
|
||||
-c, --context string Name of the context to use to connect to the daemon (overrides DOCKER_HOST env var
|
||||
and default context set with "docker context use")
|
||||
-D, --debug Enable debug mode
|
||||
-H, --host list Daemon socket(s) to connect to
|
||||
-l, --log-level string Set the logging level ("debug"|"info"|"warn"|"error"|"fatal") (default "info")
|
||||
--tls Use TLS; implied by --tlsverify
|
||||
--tlscacert string Trust certs signed only by this CA (default "/Users/ik1ne/.docker/ca.pem")
|
||||
--tlscert string Path to TLS certificate file (default "/Users/ik1ne/.docker/cert.pem")
|
||||
--tlskey string Path to TLS key file (default "/Users/ik1ne/.docker/key.pem")
|
||||
--tlsverify Use TLS and verify the remote
|
||||
-v, --version Print version information and quit
|
||||
|
||||
Management Commands:
|
||||
builder Manage builds
|
||||
config Manage Docker configs
|
||||
container Manage containers
|
||||
context Manage contexts
|
||||
image Manage images
|
||||
network Manage networks
|
||||
node Manage Swarm nodes
|
||||
plugin Manage plugins
|
||||
secret Manage Docker secrets
|
||||
service Manage services
|
||||
stack Manage Docker stacks
|
||||
swarm Manage Swarm
|
||||
system Manage Docker
|
||||
trust Manage trust on Docker images
|
||||
volume Manage volumes
|
||||
|
||||
Commands:
|
||||
attach Attach local standard input, output, and error streams to a running container
|
||||
build Build an image from a Dockerfile
|
||||
commit Create a new image from a container's changes
|
||||
cp Copy files/folders between a container and the local filesystem
|
||||
create Create a new container
|
||||
diff Inspect changes to files or directories on a container's filesystem
|
||||
events Get real time events from the server
|
||||
exec Run a command in a running container
|
||||
export Export a container's filesystem as a tar archive
|
||||
history Show the history of an image
|
||||
images List images
|
||||
import Import the contents from a tarball to create a filesystem image
|
||||
info Display system-wide information
|
||||
inspect Return low-level information on Docker objects
|
||||
kill Kill one or more running containers
|
||||
load Load an image from a tar archive or STDIN
|
||||
login Log in to a Docker registry
|
||||
logout Log out from a Docker registry
|
||||
logs Fetch the logs of a container
|
||||
pause Pause all processes within one or more containers
|
||||
port List port mappings or a specific mapping for the container
|
||||
ps List containers
|
||||
pull Pull an image or a repository from a registry
|
||||
push Push an image or a repository to a registry
|
||||
rename Rename a container
|
||||
restart Restart one or more containers
|
||||
rm Remove one or more containers
|
||||
rmi Remove one or more images
|
||||
run Run a command in a new container
|
||||
save Save one or more images to a tar archive (streamed to STDOUT by default)
|
||||
search Search the Docker Hub for images
|
||||
start Start one or more stopped containers
|
||||
stats Display a live stream of container(s) resource usage statistics
|
||||
stop Stop one or more running containers
|
||||
tag Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE
|
||||
top Display the running processes of a container
|
||||
unpause Unpause all processes within one or more containers
|
||||
update Update configuration of one or more containers
|
||||
version Show the Docker version information
|
||||
wait Block until one or more containers stop, then print their exit codes
|
||||
|
||||
Run 'docker COMMAND --help' for more information on a command.
|
||||
'''
|
||||
mock = mocker.patch('subprocess.Popen')
|
||||
mock.return_value.stdout = BytesIO(b'')
|
||||
mock.return_value.stderr = BytesIO(helptext_new)
|
||||
return mock
|
||||
|
||||
|
||||
def output(cmd):
|
||||
return "docker: '{}' is not a docker command.\n" \
|
||||
"See 'docker --help'.".format(cmd)
|
||||
@@ -113,6 +241,24 @@ def test_match():
|
||||
assert match(Command('docker pes', output('pes')))
|
||||
|
||||
|
||||
# tests docker (management command)
|
||||
@pytest.mark.usefixtures('no_memoize')
|
||||
@pytest.mark.parametrize('script, output', [
|
||||
('docker swarn', output('swarn')),
|
||||
('docker imge', output('imge'))])
|
||||
def test_match_management_cmd(script, output):
|
||||
assert match(Command(script, output))
|
||||
|
||||
|
||||
# tests docker (management cmd) (management subcmd)
|
||||
@pytest.mark.usefixtures('no_memoize')
|
||||
@pytest.mark.parametrize('script, output', [
|
||||
('docker swarm int', _DOCKER_SWARM_OUTPUT),
|
||||
('docker image la', _DOCKER_IMAGE_OUTPUT)])
|
||||
def test_match_management_subcmd(script, output):
|
||||
assert match(Command(script, output))
|
||||
|
||||
|
||||
@pytest.mark.parametrize('script, output', [
|
||||
('docker ps', ''),
|
||||
('cat pes', output('pes'))])
|
||||
@@ -120,10 +266,28 @@ def test_not_match(script, output):
|
||||
assert not match(Command(script, output))
|
||||
|
||||
|
||||
@pytest.mark.usefixtures('docker_help')
|
||||
@pytest.mark.usefixtures('no_memoize', 'docker_help')
|
||||
@pytest.mark.parametrize('wrong, fixed', [
|
||||
('pes', ['ps', 'push', 'pause']),
|
||||
('tags', ['tag', 'stats', 'images'])])
|
||||
def test_get_new_command(wrong, fixed):
|
||||
command = Command('docker {}'.format(wrong), output(wrong))
|
||||
assert get_new_command(command) == ['docker {}'.format(x) for x in fixed]
|
||||
|
||||
|
||||
@pytest.mark.usefixtures('no_memoize', 'docker_help_new')
|
||||
@pytest.mark.parametrize('wrong, fixed', [
|
||||
('swarn', ['swarm', 'start', 'search']),
|
||||
('inage', ['image', 'images', 'rename'])])
|
||||
def test_get_new_management_command(wrong, fixed):
|
||||
command = Command('docker {}'.format(wrong), output(wrong))
|
||||
assert get_new_command(command) == ['docker {}'.format(x) for x in fixed]
|
||||
|
||||
|
||||
@pytest.mark.usefixtures('no_memoize', 'docker_help_new')
|
||||
@pytest.mark.parametrize('wrong, fixed, output', [
|
||||
('swarm int', ['swarm init', 'swarm join', 'swarm join-token'], _DOCKER_SWARM_OUTPUT),
|
||||
('image la', ['image load', 'image ls', 'image tag'], _DOCKER_IMAGE_OUTPUT)])
|
||||
def test_get_new_management_command_subcommand(wrong, fixed, output):
|
||||
command = Command('docker {}'.format(wrong), output)
|
||||
assert get_new_command(command) == ['docker {}'.format(x) for x in fixed]
|
||||
|
||||
29
tests/rules/test_git_branch_delete_checked_out.py
Normal file
29
tests/rules/test_git_branch_delete_checked_out.py
Normal file
@@ -0,0 +1,29 @@
|
||||
import pytest
|
||||
from thefuck.rules.git_branch_delete_checked_out import match, get_new_command
|
||||
from thefuck.types import Command
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def output():
|
||||
return "error: Cannot delete branch 'foo' checked out at '/bar/foo'"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("script", ["git branch -d foo", "git branch -D foo"])
|
||||
def test_match(script, output):
|
||||
assert match(Command(script, output))
|
||||
|
||||
|
||||
@pytest.mark.parametrize("script", ["git branch -d foo", "git branch -D foo"])
|
||||
def test_not_match(script):
|
||||
assert not match(Command(script, "Deleted branch foo (was a1b2c3d)."))
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"script, new_command",
|
||||
[
|
||||
("git branch -d foo", "git checkout master && git branch -D foo"),
|
||||
("git branch -D foo", "git checkout master && git branch -D foo"),
|
||||
],
|
||||
)
|
||||
def test_get_new_command(script, new_command, output):
|
||||
assert get_new_command(Command(script, output)) == new_command
|
||||
@@ -39,6 +39,11 @@ def test_not_match(command):
|
||||
(b'', []),
|
||||
(b'* master', ['master']),
|
||||
(b' remotes/origin/master', ['master']),
|
||||
(b' remotes/origin/test/1', ['test/1']),
|
||||
(b' remotes/origin/test/1/2/3', ['test/1/2/3']),
|
||||
(b' test/1', ['test/1']),
|
||||
(b' test/1/2/3', ['test/1/2/3']),
|
||||
(b' remotes/origin/HEAD -> origin/master', []),
|
||||
(b' just-another-branch', ['just-another-branch']),
|
||||
(b'* master\n just-another-branch', ['master', 'just-another-branch']),
|
||||
(b'* master\n remotes/origin/master\n just-another-branch',
|
||||
@@ -51,18 +56,18 @@ def test_get_branches(branches, branch_list, git_branch):
|
||||
@pytest.mark.parametrize('branches, command, new_command', [
|
||||
(b'',
|
||||
Command('git checkout unknown', did_not_match('unknown')),
|
||||
'git checkout -b unknown'),
|
||||
['git checkout -b unknown']),
|
||||
(b'',
|
||||
Command('git commit unknown', did_not_match('unknown')),
|
||||
'git branch unknown && git commit unknown'),
|
||||
['git branch unknown && git commit unknown']),
|
||||
(b' test-random-branch-123',
|
||||
Command('git checkout tst-rdm-brnch-123',
|
||||
did_not_match('tst-rdm-brnch-123')),
|
||||
'git checkout test-random-branch-123'),
|
||||
['git checkout test-random-branch-123', 'git checkout -b tst-rdm-brnch-123']),
|
||||
(b' test-random-branch-123',
|
||||
Command('git commit tst-rdm-brnch-123',
|
||||
did_not_match('tst-rdm-brnch-123')),
|
||||
'git commit test-random-branch-123')])
|
||||
['git commit test-random-branch-123'])])
|
||||
def test_get_new_command(branches, command, new_command, git_branch):
|
||||
git_branch(branches)
|
||||
assert get_new_command(command) == new_command
|
||||
|
||||
21
tests/rules/test_go_unknown_command.py
Normal file
21
tests/rules/test_go_unknown_command.py
Normal file
@@ -0,0 +1,21 @@
|
||||
import pytest
|
||||
from thefuck.rules.go_unknown_command import match, get_new_command
|
||||
from thefuck.types import Command
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def build_misspelled_output():
|
||||
return '''go bulid: unknown command
|
||||
Run 'go help' for usage.'''
|
||||
|
||||
|
||||
def test_match(build_misspelled_output):
|
||||
assert match(Command('go bulid', build_misspelled_output))
|
||||
|
||||
|
||||
def test_not_match():
|
||||
assert not match(Command('go run', 'go run: no go files listed'))
|
||||
|
||||
|
||||
def test_get_new_command(build_misspelled_output):
|
||||
assert get_new_command(Command('go bulid', build_misspelled_output)) == 'go build'
|
||||
25
tests/rules/test_nixos_cmd_not_found.py
Normal file
25
tests/rules/test_nixos_cmd_not_found.py
Normal file
@@ -0,0 +1,25 @@
|
||||
import pytest
|
||||
from thefuck.rules.nixos_cmd_not_found import match, get_new_command
|
||||
from thefuck.types import Command
|
||||
|
||||
|
||||
@pytest.mark.parametrize('command', [
|
||||
Command('vim', 'nix-env -iA nixos.vim')])
|
||||
def test_match(mocker, command):
|
||||
mocker.patch('thefuck.rules.nixos_cmd_not_found', return_value=None)
|
||||
assert match(command)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('command', [
|
||||
Command('vim', ''),
|
||||
Command('', '')])
|
||||
def test_not_match(mocker, command):
|
||||
mocker.patch('thefuck.rules.nixos_cmd_not_found', return_value=None)
|
||||
assert not match(command)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('command, new_command', [
|
||||
(Command('vim', 'nix-env -iA nixos.vim'), 'nix-env -iA nixos.vim && vim'),
|
||||
(Command('pacman', 'nix-env -iA nixos.pacman'), 'nix-env -iA nixos.pacman && pacman')])
|
||||
def test_get_new_command(mocker, command, new_command):
|
||||
assert get_new_command(command) == new_command
|
||||
@@ -4,13 +4,23 @@ from thefuck.types import Command
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def pip_unknown_cmd():
|
||||
return '''ERROR: unknown command "instatl" - maybe you meant "install"'''
|
||||
def pip_unknown_cmd_without_recommend():
|
||||
return '''ERROR: unknown command "i"'''
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def pip_unknown_cmd_without_recommend():
|
||||
return '''ERROR: unknown command "i"'''
|
||||
def broken():
|
||||
return 'instatl'
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def suggested():
|
||||
return 'install'
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def pip_unknown_cmd(broken, suggested):
|
||||
return 'ERROR: unknown command "{}" - maybe you meant "{}"'.format(broken, suggested)
|
||||
|
||||
|
||||
def test_match(pip_unknown_cmd, pip_unknown_cmd_without_recommend):
|
||||
@@ -19,6 +29,9 @@ def test_match(pip_unknown_cmd, pip_unknown_cmd_without_recommend):
|
||||
pip_unknown_cmd_without_recommend))
|
||||
|
||||
|
||||
def test_get_new_command(pip_unknown_cmd):
|
||||
assert get_new_command(Command('pip instatl',
|
||||
pip_unknown_cmd)) == 'pip install'
|
||||
@pytest.mark.parametrize('script, broken, suggested, new_cmd', [
|
||||
('pip un+install thefuck', 'un+install', 'uninstall', 'pip uninstall thefuck'),
|
||||
('pip instatl', 'instatl', 'install', 'pip install')])
|
||||
def test_get_new_command(script, new_cmd, pip_unknown_cmd):
|
||||
assert get_new_command(Command(script,
|
||||
pip_unknown_cmd)) == new_cmd
|
||||
|
||||
38
tests/rules/test_remove_shell_prompt_literal.py
Normal file
38
tests/rules/test_remove_shell_prompt_literal.py
Normal file
@@ -0,0 +1,38 @@
|
||||
import pytest
|
||||
from thefuck.rules.remove_shell_prompt_literal import match, get_new_command
|
||||
from thefuck.types import Command
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def output():
|
||||
return "$: command not found"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("script", ["$ cd newdir", " $ cd newdir"])
|
||||
def test_match(script, output):
|
||||
assert match(Command(script, output))
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"command",
|
||||
[
|
||||
Command("$", "$: command not found"),
|
||||
Command(" $", "$: command not found"),
|
||||
Command("$?", "127: command not found"),
|
||||
Command(" $?", "127: command not found"),
|
||||
Command("", ""),
|
||||
],
|
||||
)
|
||||
def test_not_match(command):
|
||||
assert not match(command)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"script, new_command",
|
||||
[
|
||||
("$ cd newdir", "cd newdir"),
|
||||
("$ python3 -m virtualenv env", "python3 -m virtualenv env"),
|
||||
],
|
||||
)
|
||||
def test_get_new_command(script, new_command, output):
|
||||
assert get_new_command(Command(script, output)) == new_command
|
||||
@@ -1,6 +1,7 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
|
||||
import pytest
|
||||
|
||||
from thefuck.rules import switch_lang
|
||||
from thefuck.types import Command
|
||||
|
||||
@@ -9,7 +10,8 @@ from thefuck.types import Command
|
||||
Command(u'фзе-пуе', 'command not found: фзе-пуе'),
|
||||
Command(u'λσ', 'command not found: λσ'),
|
||||
Command(u'שפא-עקא', 'command not found: שפא-עקא'),
|
||||
Command(u'ךד', 'command not found: ךד')])
|
||||
Command(u'ךד', 'command not found: ךד'),
|
||||
Command(u'녀애 ㅣㄴ', 'command not found: 녀애 ㅣㄴ')])
|
||||
def test_match(command):
|
||||
assert switch_lang.match(command)
|
||||
|
||||
@@ -19,7 +21,8 @@ def test_match(command):
|
||||
Command(u'ls', 'command not found: ls'),
|
||||
Command(u'агсл', 'command not found: агсл'),
|
||||
Command(u'фзе-пуе', 'some info'),
|
||||
Command(u'שפא-עקא', 'some info')])
|
||||
Command(u'שפא-עקא', 'some info'),
|
||||
Command(u'녀애 ㅣㄴ', 'some info')])
|
||||
def test_not_match(command):
|
||||
assert not switch_lang.match(command)
|
||||
|
||||
@@ -28,6 +31,9 @@ def test_not_match(command):
|
||||
(Command(u'фзе-пуе штыефдд мшь', ''), 'apt-get install vim'),
|
||||
(Command(u'λσ -λα', ''), 'ls -la'),
|
||||
(Command(u'שפא-עקא ןמדאשךך הןצ', ''), 'apt-get install vim'),
|
||||
(Command(u'ךד -ךש', ''), 'ls -la')])
|
||||
(Command(u'ךד -ךש', ''), 'ls -la'),
|
||||
(Command(u'멧-ㅎㄷㅅ ㅑㅜㄴㅅ미ㅣ 퍄ㅡ', ''), 'apt-get install vim'),
|
||||
(Command(u'ㅣㄴ -ㅣㅁ', ''), 'ls -la'),
|
||||
(Command(u'ㅔㅁㅅ촤', ''), 'patchk'), ])
|
||||
def test_get_new_command(command, new_command):
|
||||
assert switch_lang.get_new_command(command) == new_command
|
||||
|
||||
33
tests/rules/test_terraform_init.py
Normal file
33
tests/rules/test_terraform_init.py
Normal file
@@ -0,0 +1,33 @@
|
||||
import pytest
|
||||
from thefuck.rules.terraform_init import match, get_new_command
|
||||
from thefuck.types import Command
|
||||
|
||||
|
||||
@pytest.mark.parametrize('script, output', [
|
||||
('terraform plan', 'Error: Initialization required. '
|
||||
'Please see the error message above.'),
|
||||
('terraform plan', 'This module is not yet installed. Run "terraform init" '
|
||||
'to install all modules required by this configuration.'),
|
||||
('terraform apply', 'Error: Initialization required. '
|
||||
'Please see the error message above.'),
|
||||
('terraform apply', 'This module is not yet installed. Run "terraform init" '
|
||||
'to install all modules required by this configuration.')])
|
||||
def test_match(script, output):
|
||||
assert match(Command(script, output))
|
||||
|
||||
|
||||
@pytest.mark.parametrize('script, output', [
|
||||
('terraform --version', 'Terraform v0.12.2'),
|
||||
('terraform plan', 'No changes. Infrastructure is up-to-date.'),
|
||||
('terraform apply', 'Apply complete! Resources: 0 added, 0 changed, 0 destroyed.'),
|
||||
])
|
||||
def test_not_match(script, output):
|
||||
assert not match(Command(script, output=output))
|
||||
|
||||
|
||||
@pytest.mark.parametrize('command, new_command', [
|
||||
(Command('terraform plan', ''), 'terraform init && terraform plan'),
|
||||
(Command('terraform apply', ''), 'terraform init && terraform apply'),
|
||||
])
|
||||
def test_get_new_command(command, new_command):
|
||||
assert get_new_command(command) == new_command
|
||||
173
tests/rules/test_yum_invalid_operation.py
Normal file
173
tests/rules/test_yum_invalid_operation.py
Normal file
@@ -0,0 +1,173 @@
|
||||
from io import BytesIO
|
||||
|
||||
import pytest
|
||||
|
||||
from thefuck.rules.yum_invalid_operation import match, get_new_command, _get_operations
|
||||
from thefuck.types import Command
|
||||
|
||||
yum_help_text = '''Loaded plugins: extras_suggestions, langpacks, priorities, update-motd
|
||||
Usage: yum [options] COMMAND
|
||||
|
||||
List of Commands:
|
||||
|
||||
check Check for problems in the rpmdb
|
||||
check-update Check for available package updates
|
||||
clean Remove cached data
|
||||
deplist List a package's dependencies
|
||||
distribution-synchronization Synchronize installed packages to the latest available versions
|
||||
downgrade downgrade a package
|
||||
erase Remove a package or packages from your system
|
||||
fs Acts on the filesystem data of the host, mainly for removing docs/lanuages for minimal hosts.
|
||||
fssnapshot Creates filesystem snapshots, or lists/deletes current snapshots.
|
||||
groups Display, or use, the groups information
|
||||
help Display a helpful usage message
|
||||
history Display, or use, the transaction history
|
||||
info Display details about a package or group of packages
|
||||
install Install a package or packages on your system
|
||||
langavailable Check available languages
|
||||
langinfo List languages information
|
||||
langinstall Install appropriate language packs for a language
|
||||
langlist List installed languages
|
||||
langremove Remove installed language packs for a language
|
||||
list List a package or groups of packages
|
||||
load-transaction load a saved transaction from filename
|
||||
makecache Generate the metadata cache
|
||||
provides Find what package provides the given value
|
||||
reinstall reinstall a package
|
||||
repo-pkgs Treat a repo. as a group of packages, so we can install/remove all of them
|
||||
repolist Display the configured software repositories
|
||||
search Search package details for the given string
|
||||
shell Run an interactive yum shell
|
||||
swap Simple way to swap packages, instead of using shell
|
||||
update Update a package or packages on your system
|
||||
update-minimal Works like upgrade, but goes to the 'newest' package match which fixes a problem that affects your system
|
||||
updateinfo Acts on repository update information
|
||||
upgrade Update packages taking obsoletes into account
|
||||
version Display a version for the machine and/or available repos.
|
||||
|
||||
|
||||
Options:
|
||||
-h, --help show this help message and exit
|
||||
-t, --tolerant be tolerant of errors
|
||||
-C, --cacheonly run entirely from system cache, don't update cache
|
||||
-c [config file], --config=[config file]
|
||||
config file location
|
||||
-R [minutes], --randomwait=[minutes]
|
||||
maximum command wait time
|
||||
-d [debug level], --debuglevel=[debug level]
|
||||
debugging output level
|
||||
--showduplicates show duplicates, in repos, in list/search commands
|
||||
-e [error level], --errorlevel=[error level]
|
||||
error output level
|
||||
--rpmverbosity=[debug level name]
|
||||
debugging output level for rpm
|
||||
-q, --quiet quiet operation
|
||||
-v, --verbose verbose operation
|
||||
-y, --assumeyes answer yes for all questions
|
||||
--assumeno answer no for all questions
|
||||
--version show Yum version and exit
|
||||
--installroot=[path] set install root
|
||||
--enablerepo=[repo] enable one or more repositories (wildcards allowed)
|
||||
--disablerepo=[repo] disable one or more repositories (wildcards allowed)
|
||||
-x [package], --exclude=[package]
|
||||
exclude package(s) by name or glob
|
||||
--disableexcludes=[repo]
|
||||
disable exclude from main, for a repo or for
|
||||
everything
|
||||
--disableincludes=[repo]
|
||||
disable includepkgs for a repo or for everything
|
||||
--obsoletes enable obsoletes processing during updates
|
||||
--noplugins disable Yum plugins
|
||||
--nogpgcheck disable gpg signature checking
|
||||
--disableplugin=[plugin]
|
||||
disable plugins by name
|
||||
--enableplugin=[plugin]
|
||||
enable plugins by name
|
||||
--skip-broken skip packages with depsolving problems
|
||||
--color=COLOR control whether color is used
|
||||
--releasever=RELEASEVER
|
||||
set value of $releasever in yum config and repo files
|
||||
--downloadonly don't update, just download
|
||||
--downloaddir=DLDIR specifies an alternate directory to store packages
|
||||
--setopt=SETOPTS set arbitrary config and repo options
|
||||
--bugfix Include bugfix relevant packages, in updates
|
||||
--security Include security relevant packages, in updates
|
||||
--advisory=ADVS, --advisories=ADVS
|
||||
Include packages needed to fix the given advisory, in
|
||||
updates
|
||||
--bzs=BZS Include packages needed to fix the given BZ, in
|
||||
updates
|
||||
--cves=CVES Include packages needed to fix the given CVE, in
|
||||
updates
|
||||
--sec-severity=SEVS, --secseverity=SEVS
|
||||
Include security relevant packages matching the
|
||||
severity, in updates
|
||||
|
||||
Plugin Options:
|
||||
--samearch-priorities
|
||||
Priority-exclude packages based on name + arch
|
||||
'''
|
||||
yum_unsuccessful_search_text = '''Warning: No matches found for: {}
|
||||
No matches found
|
||||
'''
|
||||
yum_successful_vim_search_text = '''================================================== N/S matched: vim ===================================================
|
||||
protobuf-vim.x86_64 : Vim syntax highlighting for Google Protocol Buffers descriptions
|
||||
vim-X11.x86_64 : The VIM version of the vi editor for the X Window System - GVim
|
||||
vim-common.x86_64 : The common files needed by any version of the VIM editor
|
||||
vim-enhanced.x86_64 : A version of the VIM editor which includes recent enhancements
|
||||
vim-filesystem.x86_64 : VIM filesystem layout
|
||||
vim-filesystem.noarch : VIM filesystem layout
|
||||
vim-minimal.x86_64 : A minimal version of the VIM editor
|
||||
|
||||
Name and summary matches only, use "search all" for everything.
|
||||
'''
|
||||
|
||||
yum_invalid_op_text = '''Loaded plugins: extras_suggestions, langpacks, priorities, update-motd
|
||||
No such command: {}. Please use /usr/bin/yum --help
|
||||
'''
|
||||
|
||||
yum_operations = [
|
||||
'check', 'check-update', 'clean', 'deplist', 'distribution-synchronization', 'downgrade', 'erase', 'fs',
|
||||
'fssnapshot', 'groups', 'help', 'history', 'info', 'install', 'langavailable', 'langinfo', 'langinstall',
|
||||
'langlist', 'langremove', 'list', 'load-transaction', 'makecache', 'provides', 'reinstall', 'repo-pkgs', 'repolist',
|
||||
'search', 'shell', 'swap', 'update', 'update-minimal', 'updateinfo', 'upgrade', 'version', ]
|
||||
|
||||
|
||||
@pytest.mark.parametrize('command', [
|
||||
'saerch', 'uninstall',
|
||||
])
|
||||
def test_match(command):
|
||||
assert match(Command('yum {}'.format(command), yum_invalid_op_text.format(command)))
|
||||
|
||||
|
||||
@pytest.mark.parametrize('command, output', [
|
||||
('vim', ''),
|
||||
('yum', yum_help_text,),
|
||||
('yum help', yum_help_text,),
|
||||
('yum search asdf', yum_unsuccessful_search_text.format('asdf'),),
|
||||
('yum search vim', yum_successful_vim_search_text)
|
||||
])
|
||||
def test_not_match(command, output):
|
||||
assert not match(Command(command, output))
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def yum_help(mocker):
|
||||
mock = mocker.patch('subprocess.Popen')
|
||||
mock.return_value.stdout = BytesIO(bytes(yum_help_text.encode('utf-8')))
|
||||
return mock
|
||||
|
||||
|
||||
@pytest.mark.usefixtures('no_memoize', 'yum_help')
|
||||
def test_get_operations():
|
||||
assert _get_operations() == yum_operations
|
||||
|
||||
|
||||
@pytest.mark.usefixtures('no_memoize', 'yum_help')
|
||||
@pytest.mark.parametrize('script, output, result', [
|
||||
('yum uninstall', yum_invalid_op_text.format('uninstall'), 'yum remove',),
|
||||
('yum saerch asdf', yum_invalid_op_text.format('saerch'), 'yum search asdf',),
|
||||
('yum hlep', yum_invalid_op_text.format('hlep'), 'yum help',),
|
||||
])
|
||||
def test_get_new_command(script, output, result):
|
||||
assert get_new_command(Command(script, output))[0] == result
|
||||
@@ -225,9 +225,12 @@ class TestGetValidHistoryWithoutCurrent(object):
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def history(self, mocker):
|
||||
return mocker.patch('thefuck.shells.shell.get_history',
|
||||
return_value=['le cat', 'fuck', 'ls cat',
|
||||
'diff x', 'nocommand x', u'café ô'])
|
||||
mock = mocker.patch('thefuck.shells.shell.get_history')
|
||||
# Passing as an argument causes `UnicodeDecodeError`
|
||||
# with newer py.test and python 2.7
|
||||
mock.return_value = ['le cat', 'fuck', 'ls cat',
|
||||
'diff x', 'nocommand x', u'café ô']
|
||||
return mock
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def alias(self, mocker):
|
||||
|
||||
@@ -55,7 +55,7 @@ class Parser(object):
|
||||
"""It's too dangerous to use `-y` and `-r` together."""
|
||||
group = self._parser.add_mutually_exclusive_group()
|
||||
group.add_argument(
|
||||
'-y', '--yes', '--yeah',
|
||||
'-y', '--yes', '--yeah', '--hard',
|
||||
action='store_true',
|
||||
help='execute fixed command without confirmation')
|
||||
group.add_argument(
|
||||
|
||||
@@ -22,10 +22,13 @@ def main():
|
||||
elif known_args.version:
|
||||
logs.version(get_installation_info().version,
|
||||
sys.version.split()[0], shell.info())
|
||||
elif known_args.command or 'TF_HISTORY' in os.environ:
|
||||
fix_command(known_args)
|
||||
# It's important to check if an alias is being requested before checking if
|
||||
# `TF_HISTORY` is in `os.environ`, otherwise it might mess with subshells.
|
||||
# Check https://github.com/nvbn/thefuck/issues/921 for reference
|
||||
elif known_args.alias:
|
||||
print_alias(known_args)
|
||||
elif known_args.command or 'TF_HISTORY' in os.environ:
|
||||
fix_command(known_args)
|
||||
elif known_args.shell_logger:
|
||||
try:
|
||||
from .shell_logger import shell_logger # noqa: E402
|
||||
|
||||
@@ -106,7 +106,7 @@ def how_to_configure_alias(configuration_details):
|
||||
|
||||
if configuration_details.can_configure_automatically:
|
||||
print(
|
||||
u"Or run {bold}fuck{reset} second time for configuring"
|
||||
u"Or run {bold}fuck{reset} a second time to configure"
|
||||
u" it automatically.".format(
|
||||
bold=color(colorama.Style.BRIGHT),
|
||||
reset=color(colorama.Style.RESET_ALL)))
|
||||
|
||||
@@ -53,8 +53,9 @@ def get_output(script, expanded):
|
||||
env = dict(os.environ)
|
||||
env.update(settings.env)
|
||||
|
||||
is_slow = shlex.split(expanded) in settings.slow_commands
|
||||
with logs.debug_time(u'Call: {}; with env: {}; is slow: '.format(
|
||||
split_expand = shlex.split(expanded)
|
||||
is_slow = split_expand[0] in settings.slow_commands if split_expand else False
|
||||
with logs.debug_time(u'Call: {}; with env: {}; is slow: {}'.format(
|
||||
script, env, is_slow)):
|
||||
result = Popen(expanded, shell=True, stdin=PIPE,
|
||||
stdout=PIPE, stderr=STDOUT, env=env)
|
||||
|
||||
@@ -34,7 +34,8 @@ def _parse_apt_get_and_cache_operations(help_text_lines):
|
||||
return
|
||||
|
||||
yield line.split()[0]
|
||||
elif line.startswith('Commands:'):
|
||||
elif line.startswith('Commands:') \
|
||||
or line.startswith('Most used commands:'):
|
||||
is_commands_list = True
|
||||
|
||||
|
||||
@@ -53,5 +54,10 @@ def _get_operations(app):
|
||||
@sudo_support
|
||||
def get_new_command(command):
|
||||
invalid_operation = command.output.split()[-1]
|
||||
operations = _get_operations(command.script_parts[0])
|
||||
return replace_command(command, invalid_operation, operations)
|
||||
|
||||
if invalid_operation == 'uninstall':
|
||||
return [command.script.replace('uninstall', 'remove')]
|
||||
|
||||
else:
|
||||
operations = _get_operations(command.script_parts[0])
|
||||
return replace_command(command, invalid_operation, operations)
|
||||
|
||||
@@ -8,7 +8,7 @@ enabled_by_default = apt_available
|
||||
@sudo_support
|
||||
@for_app('apt')
|
||||
def match(command):
|
||||
return "Run 'apt list --upgradable' to see them." in command.output
|
||||
return 'apt list --upgradable' in command.output
|
||||
|
||||
|
||||
@sudo_support
|
||||
|
||||
25
thefuck/rules/choco_install.py
Normal file
25
thefuck/rules/choco_install.py
Normal file
@@ -0,0 +1,25 @@
|
||||
from thefuck.utils import for_app, which
|
||||
|
||||
|
||||
@for_app("choco", "cinst")
|
||||
def match(command):
|
||||
return ((command.script.startswith('choco install') or 'cinst' in command.script_parts)
|
||||
and 'Installing the following packages' in command.output)
|
||||
|
||||
|
||||
def get_new_command(command):
|
||||
# Find the argument that is the package name
|
||||
for script_part in command.script_parts:
|
||||
if (
|
||||
script_part not in ["choco", "cinst", "install"]
|
||||
# Need exact match (bc chocolatey is a package)
|
||||
and not script_part.startswith('-')
|
||||
# Leading hyphens are parameters; some packages contain them though
|
||||
and '=' not in script_part and '/' not in script_part
|
||||
# These are certainly parameters
|
||||
):
|
||||
return command.script.replace(script_part, script_part + ".install")
|
||||
return []
|
||||
|
||||
|
||||
enabled_by_default = bool(which("choco")) or bool(which("cinst"))
|
||||
15
thefuck/rules/cp_create_destination.py
Normal file
15
thefuck/rules/cp_create_destination.py
Normal file
@@ -0,0 +1,15 @@
|
||||
from thefuck.shells import shell
|
||||
from thefuck.utils import for_app
|
||||
|
||||
|
||||
@for_app("cp", "mv")
|
||||
def match(command):
|
||||
return (
|
||||
"No such file or directory" in command.output
|
||||
or command.output.startswith("cp: directory")
|
||||
and command.output.rstrip().endswith("does not exist")
|
||||
)
|
||||
|
||||
|
||||
def get_new_command(command):
|
||||
return shell.and_(u"mkdir -p {}".format(command.script_parts[-1]), command.script)
|
||||
20
thefuck/rules/docker_image_being_used_by_container.py
Normal file
20
thefuck/rules/docker_image_being_used_by_container.py
Normal file
@@ -0,0 +1,20 @@
|
||||
from thefuck.utils import for_app
|
||||
from thefuck.shells import shell
|
||||
|
||||
|
||||
@for_app('docker')
|
||||
def match(command):
|
||||
'''
|
||||
Matches a command's output with docker's output
|
||||
warning you that you need to remove a container before removing an image.
|
||||
'''
|
||||
return 'image is being used by running container' in command.output
|
||||
|
||||
|
||||
def get_new_command(command):
|
||||
'''
|
||||
Prepends docker container rm -f {container ID} to
|
||||
the previous docker image rm {image ID} command
|
||||
'''
|
||||
container_id = command.output.strip().split(' ')
|
||||
return shell.and_('docker container rm -f {}', '{}').format(container_id[-1], command.script)
|
||||
@@ -8,16 +8,30 @@ from thefuck.specific.sudo import sudo_support
|
||||
@sudo_support
|
||||
@for_app('docker')
|
||||
def match(command):
|
||||
return 'is not a docker command' in command.output
|
||||
return 'is not a docker command' in command.output or 'Usage: docker' in command.output
|
||||
|
||||
|
||||
def _parse_commands(lines, starts_with):
|
||||
lines = dropwhile(lambda line: not line.startswith(starts_with), lines)
|
||||
lines = islice(lines, 1, None)
|
||||
lines = list(takewhile(lambda line: line.strip(), lines))
|
||||
return [line.strip().split(' ')[0] for line in lines]
|
||||
|
||||
|
||||
def get_docker_commands():
|
||||
proc = subprocess.Popen('docker', stdout=subprocess.PIPE)
|
||||
lines = [line.decode('utf-8') for line in proc.stdout.readlines()]
|
||||
lines = dropwhile(lambda line: not line.startswith('Commands:'), lines)
|
||||
lines = islice(lines, 1, None)
|
||||
lines = list(takewhile(lambda line: line != '\n', lines))
|
||||
return [line.strip().split(' ')[0] for line in lines]
|
||||
proc = subprocess.Popen('docker', stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
|
||||
# Old version docker returns its output to stdout, while newer version returns to stderr.
|
||||
lines = proc.stdout.readlines() or proc.stderr.readlines()
|
||||
lines = [line.decode('utf-8') for line in lines]
|
||||
|
||||
# Only newer versions of docker have management commands in the help text.
|
||||
if 'Management Commands:\n' in lines:
|
||||
management_commands = _parse_commands(lines, 'Management Commands:')
|
||||
else:
|
||||
management_commands = []
|
||||
regular_commands = _parse_commands(lines, 'Commands:')
|
||||
return management_commands + regular_commands
|
||||
|
||||
|
||||
if which('docker'):
|
||||
@@ -26,6 +40,10 @@ if which('docker'):
|
||||
|
||||
@sudo_support
|
||||
def get_new_command(command):
|
||||
if 'Usage:' in command.output and len(command.script_parts) > 1:
|
||||
management_subcommands = _parse_commands(command.output.split('\n'), 'Commands:')
|
||||
return replace_command(command, command.script_parts[2], management_subcommands)
|
||||
|
||||
wrong_command = re.findall(
|
||||
r"docker: '(\w+)' is not a docker command.", command.output)[0]
|
||||
return replace_command(command, wrong_command, get_docker_commands())
|
||||
|
||||
19
thefuck/rules/git_branch_delete_checked_out.py
Normal file
19
thefuck/rules/git_branch_delete_checked_out.py
Normal file
@@ -0,0 +1,19 @@
|
||||
from thefuck.shells import shell
|
||||
from thefuck.specific.git import git_support
|
||||
from thefuck.utils import replace_argument
|
||||
|
||||
|
||||
@git_support
|
||||
def match(command):
|
||||
return (
|
||||
("branch -d" in command.script or "branch -D" in command.script)
|
||||
and "error: Cannot delete branch '" in command.output
|
||||
and "' checked out at '" in command.output
|
||||
)
|
||||
|
||||
|
||||
@git_support
|
||||
def get_new_command(command):
|
||||
return shell.and_("git checkout master", "{}").format(
|
||||
replace_argument(command.script, "-d", "-D")
|
||||
)
|
||||
@@ -8,7 +8,7 @@ from thefuck.shells import shell
|
||||
|
||||
@git_support
|
||||
def match(command):
|
||||
return ('did not match any file(s) known to git.' in command.output
|
||||
return ('did not match any file(s) known to git' in command.output
|
||||
and "Did you forget to 'git add'?" not in command.output)
|
||||
|
||||
|
||||
@@ -18,10 +18,12 @@ def get_branches():
|
||||
stdout=subprocess.PIPE)
|
||||
for line in proc.stdout.readlines():
|
||||
line = line.decode('utf-8')
|
||||
if '->' in line: # Remote HEAD like b' remotes/origin/HEAD -> origin/master'
|
||||
continue
|
||||
if line.startswith('*'):
|
||||
line = line.split(' ')[1]
|
||||
if '/' in line:
|
||||
line = line.split('/')[-1]
|
||||
if line.strip().startswith('remotes/'):
|
||||
line = '/'.join(line.split('/')[2:])
|
||||
yield line.strip()
|
||||
|
||||
|
||||
@@ -29,13 +31,19 @@ def get_branches():
|
||||
def get_new_command(command):
|
||||
missing_file = re.findall(
|
||||
r"error: pathspec '([^']*)' "
|
||||
r"did not match any file\(s\) known to git.", command.output)[0]
|
||||
r"did not match any file\(s\) known to git", command.output)[0]
|
||||
closest_branch = utils.get_closest(missing_file, get_branches(),
|
||||
fallback_to_first=False)
|
||||
|
||||
new_commands = []
|
||||
|
||||
if closest_branch:
|
||||
return replace_argument(command.script, missing_file, closest_branch)
|
||||
elif command.script_parts[1] == 'checkout':
|
||||
return replace_argument(command.script, 'checkout', 'checkout -b')
|
||||
else:
|
||||
return shell.and_('git branch {}', '{}').format(
|
||||
missing_file, command.script)
|
||||
new_commands.append(replace_argument(command.script, missing_file, closest_branch))
|
||||
if command.script_parts[1] == 'checkout':
|
||||
new_commands.append(replace_argument(command.script, 'checkout', 'checkout -b'))
|
||||
|
||||
if not new_commands:
|
||||
new_commands.append(shell.and_('git branch {}', '{}').format(
|
||||
missing_file, command.script))
|
||||
|
||||
return new_commands
|
||||
|
||||
28
thefuck/rules/go_unknown_command.py
Normal file
28
thefuck/rules/go_unknown_command.py
Normal file
@@ -0,0 +1,28 @@
|
||||
from itertools import dropwhile, islice, takewhile
|
||||
import subprocess
|
||||
|
||||
from thefuck.utils import get_closest, replace_argument, for_app, which, cache
|
||||
|
||||
|
||||
def get_golang_commands():
|
||||
proc = subprocess.Popen('go', stderr=subprocess.PIPE)
|
||||
lines = [line.decode('utf-8').strip() for line in proc.stderr.readlines()]
|
||||
lines = dropwhile(lambda line: line != 'The commands are:', lines)
|
||||
lines = islice(lines, 2, None)
|
||||
lines = takewhile(lambda line: line, lines)
|
||||
return [line.split(' ')[0] for line in lines]
|
||||
|
||||
|
||||
if which('go'):
|
||||
get_docker_commands = cache(which('go'))(get_golang_commands)
|
||||
|
||||
|
||||
@for_app('go')
|
||||
def match(command):
|
||||
return 'unknown command' in command.output
|
||||
|
||||
|
||||
def get_new_command(command):
|
||||
closest_subcommand = get_closest(command.script_parts[1], get_golang_commands())
|
||||
return replace_argument(command.script, command.script_parts[1],
|
||||
closest_subcommand)
|
||||
15
thefuck/rules/nixos_cmd_not_found.py
Normal file
15
thefuck/rules/nixos_cmd_not_found.py
Normal file
@@ -0,0 +1,15 @@
|
||||
import re
|
||||
from thefuck.specific.nix import nix_available
|
||||
from thefuck.shells import shell
|
||||
|
||||
regex = re.compile(r'nix-env -iA ([^\s]*)')
|
||||
enabled_by_default = nix_available
|
||||
|
||||
|
||||
def match(command):
|
||||
return regex.findall(command.output)
|
||||
|
||||
|
||||
def get_new_command(command):
|
||||
name = regex.findall(command.output)[0]
|
||||
return shell.and_('nix-env -iA {}'.format(name), command.script)
|
||||
@@ -12,8 +12,8 @@ def match(command):
|
||||
|
||||
|
||||
def get_new_command(command):
|
||||
broken_cmd = re.findall(r'ERROR: unknown command \"([a-z]+)\"',
|
||||
broken_cmd = re.findall(r'ERROR: unknown command "([^"]+)"',
|
||||
command.output)[0]
|
||||
new_cmd = re.findall(r'maybe you meant \"([a-z]+)\"', command.output)[0]
|
||||
new_cmd = re.findall(r'maybe you meant "([^"]+)"', command.output)[0]
|
||||
|
||||
return replace_argument(command.script, broken_cmd, new_cmd)
|
||||
|
||||
22
thefuck/rules/remove_shell_prompt_literal.py
Normal file
22
thefuck/rules/remove_shell_prompt_literal.py
Normal file
@@ -0,0 +1,22 @@
|
||||
"""Fixes error for commands containing the shell prompt symbol '$'.
|
||||
|
||||
This usually happens when commands are copied from documentations
|
||||
including them in their code blocks.
|
||||
|
||||
Example:
|
||||
> $ git clone https://github.com/nvbn/thefuck.git
|
||||
bash: $: command not found...
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
|
||||
def match(command):
|
||||
return (
|
||||
"$: command not found" in command.output
|
||||
and re.search(r"^[\s]*\$ [\S]+", command.script) is not None
|
||||
)
|
||||
|
||||
|
||||
def get_new_command(command):
|
||||
return command.script.replace("$", "", 1).strip()
|
||||
@@ -21,7 +21,7 @@ patterns = ['permission denied',
|
||||
'edspermissionerror',
|
||||
'you don\'t have write permissions',
|
||||
'use `sudo`',
|
||||
'SudoRequiredError',
|
||||
'sudorequirederror',
|
||||
'error: insufficient privileges']
|
||||
|
||||
|
||||
|
||||
@@ -5,13 +5,14 @@ target_layout = '''qwertyuiop[]asdfghjkl;'zxcvbnm,./QWERTYUIOP{}ASDFGHJKL:"ZXCVB
|
||||
# any new keyboard layout must be appended
|
||||
|
||||
greek = u''';ςερτυθιοπ[]ασδφγηξκλ΄ζχψωβνμ,./:΅ΕΡΤΥΘΙΟΠ{}ΑΣΔΦΓΗΞΚΛ¨"ΖΧΨΩΒΝΜ<>?'''
|
||||
korean = u'''ㅂㅈㄷㄱㅅㅛㅕㅑㅐㅔ[]ㅁㄴㅇㄹㅎㅗㅓㅏㅣ;'ㅋㅌㅊㅍㅠㅜㅡ,./ㅃㅉㄸㄲㅆㅛㅕㅑㅒㅖ{}ㅁㄴㅇㄹㅎㅗㅓㅏㅣ:"ㅋㅌㅊㅍㅠㅜㅡ<>?'''
|
||||
|
||||
source_layouts = [u'''йцукенгшщзхъфывапролджэячсмитьбю.ЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭЯЧСМИТЬБЮ,''',
|
||||
u'''йцукенгшщзхїфівапролджєячсмитьбю.ЙЦУКЕНГШЩЗХЇФІВАПРОЛДЖЄЯЧСМИТЬБЮ,''',
|
||||
u'''ضصثقفغعهخحجچشسیبلاتنمکگظطزرذدپو./ًٌٍَُِّْ][}{ؤئيإأآة»«:؛كٓژٰٔء><؟''',
|
||||
u'''/'קראטוןםפ][שדגכעיחלךף,זסבהנמצתץ.QWERTYUIOP{}ASDFGHJKL:"ZXCVBNM<>?''',
|
||||
greek]
|
||||
|
||||
greek,
|
||||
korean]
|
||||
|
||||
source_to_target = {
|
||||
greek: {u';': "q", u'ς': "w", u'ε': "e", u'ρ': "r", u'τ': "t", u'υ': "y",
|
||||
@@ -30,6 +31,19 @@ source_to_target = {
|
||||
u'Ώ': "V"},
|
||||
}
|
||||
|
||||
'''Lists used for decomposing korean letters.'''
|
||||
HEAD_LIST = [u'ㄱ', u'ㄲ', u'ㄴ', u'ㄷ', u'ㄸ', u'ㄹ', u'ㅁ', u'ㅂ', u'ㅃ', u'ㅅ', u'ㅆ',
|
||||
u'ㅇ', u'ㅈ', u'ㅉ', u'ㅊ', u'ㅋ', u'ㅌ', u'ㅍ', u'ㅎ']
|
||||
BODY_LIST = [u'ㅏ', u'ㅐ', u'ㅑ', u'ㅒ', u'ㅓ', u'ㅔ', u'ㅕ', u'ㅖ', u'ㅗ', u'ㅘ', u'ㅙ',
|
||||
u'ㅚ', u'ㅛ', u'ㅜ', u'ㅝ', u'ㅞ', u'ㅟ', u'ㅠ', u'ㅡ', u'ㅢ', u'ㅣ']
|
||||
TAIL_LIST = [u' ', u'ㄱ', u'ㄲ', u'ㄳ', u'ㄴ', u'ㄵ', u'ㄶ', u'ㄷ', u'ㄹ', u'ㄺ', u'ㄻ',
|
||||
u'ㄼ', u'ㄽ', u'ㄾ', u'ㄿ', u'ㅀ', u'ㅁ', u'ㅂ', u'ㅄ', u'ㅅ', u'ㅆ', u'ㅇ', u'ㅈ',
|
||||
u'ㅊ', u'ㅋ', u'ㅌ', u'ㅍ', u'ㅎ']
|
||||
DOUBLE_LIST = [u'ㅘ', u'ㅙ', u'ㅚ', u'ㅝ', u'ㅞ', u'ㅟ', u'ㅢ', u'ㄳ', u'ㄵ', u'ㄶ', u'ㄺ',
|
||||
u'ㄻ', u'ㄼ', u'ㄽ', u'ㄾ', u'ㅀ', u'ㅄ']
|
||||
DOUBLE_MOD_LIST = [u'ㅗㅏ', u'ㅗㅐ', u'ㅗㅣ', u'ㅜㅓ', u'ㅜㅔ', u'ㅜㅣ', u'ㅡㅣ', u'ㄱㅅ',
|
||||
u'ㄴㅈ', u'ㄴㅎ', u'ㄹㄱ', u'ㄹㅁ', u'ㄹㅂ', u'ㄹㅅ', u'ㄹㅌ', u'ㄹㅎ', u'ㅂㅅ']
|
||||
|
||||
|
||||
@memoize
|
||||
def _get_matched_layout(command):
|
||||
@@ -50,8 +64,7 @@ def _get_matched_layout(command):
|
||||
def _switch(ch, layout):
|
||||
if ch in layout:
|
||||
return target_layout[layout.index(ch)]
|
||||
else:
|
||||
return ch
|
||||
return ch
|
||||
|
||||
|
||||
def _switch_command(command, layout):
|
||||
@@ -63,9 +76,33 @@ def _switch_command(command, layout):
|
||||
return ''.join(_switch(ch, layout) for ch in command.script)
|
||||
|
||||
|
||||
def _decompose_korean(command):
|
||||
def _change_double(ch):
|
||||
if ch in DOUBLE_LIST:
|
||||
return DOUBLE_MOD_LIST[DOUBLE_LIST.index(ch)]
|
||||
return ch
|
||||
|
||||
hg_str = u''
|
||||
for ch in command.script:
|
||||
if u'가' <= ch <= u'힣':
|
||||
ord_ch = ord(ch) - ord(u'가')
|
||||
hd = ord_ch // 588
|
||||
bd = (ord_ch - 588 * hd) // 28
|
||||
tl = ord_ch - 588 * hd - 28 * bd
|
||||
for ch in [HEAD_LIST[hd], BODY_LIST[bd], TAIL_LIST[tl]]:
|
||||
if ch != ' ':
|
||||
hg_str += _change_double(ch)
|
||||
else:
|
||||
hg_str += _change_double(ch)
|
||||
return hg_str
|
||||
|
||||
|
||||
def match(command):
|
||||
if 'not found' not in command.output:
|
||||
return False
|
||||
if any(u'ㄱ' <= ch <= u'ㅎ' or u'ㅏ' <= ch <= u'ㅣ' or u'가' <= ch <= u'힣'
|
||||
for ch in command.script):
|
||||
return True
|
||||
|
||||
matched_layout = _get_matched_layout(command)
|
||||
return (matched_layout and
|
||||
@@ -73,5 +110,8 @@ def match(command):
|
||||
|
||||
|
||||
def get_new_command(command):
|
||||
if any(u'ㄱ' <= ch <= u'ㅎ' or u'ㅏ' <= ch <= u'ㅣ' or u'가' <= ch <= u'힣'
|
||||
for ch in command.script):
|
||||
command.script = _decompose_korean(command)
|
||||
matched_layout = _get_matched_layout(command)
|
||||
return _switch_command(command, matched_layout)
|
||||
|
||||
13
thefuck/rules/terraform_init.py
Normal file
13
thefuck/rules/terraform_init.py
Normal file
@@ -0,0 +1,13 @@
|
||||
from thefuck.shells import shell
|
||||
from thefuck.utils import for_app
|
||||
|
||||
|
||||
@for_app('terraform')
|
||||
def match(command):
|
||||
return ('this module is not yet installed' in command.output.lower() or
|
||||
'initialization required' in command.output.lower()
|
||||
)
|
||||
|
||||
|
||||
def get_new_command(command):
|
||||
return shell.and_('terraform init', command.script)
|
||||
@@ -9,6 +9,6 @@ def match(command):
|
||||
|
||||
|
||||
def get_new_command(command):
|
||||
path = path = re.findall(
|
||||
path = re.findall(
|
||||
r"touch: (?:cannot touch ')?(.+)/.+'?:", command.output)[0]
|
||||
return shell.and_(u'mkdir -p {}'.format(path), command.script)
|
||||
|
||||
39
thefuck/rules/yum_invalid_operation.py
Normal file
39
thefuck/rules/yum_invalid_operation.py
Normal file
@@ -0,0 +1,39 @@
|
||||
import subprocess
|
||||
from itertools import dropwhile, islice, takewhile
|
||||
|
||||
from thefuck.specific.sudo import sudo_support
|
||||
from thefuck.specific.yum import yum_available
|
||||
from thefuck.utils import for_app, replace_command, which, cache
|
||||
|
||||
enabled_by_default = yum_available
|
||||
|
||||
|
||||
@sudo_support
|
||||
@for_app('yum')
|
||||
def match(command):
|
||||
return 'No such command: ' in command.output
|
||||
|
||||
|
||||
def _get_operations():
|
||||
proc = subprocess.Popen('yum', stdout=subprocess.PIPE)
|
||||
|
||||
lines = proc.stdout.readlines()
|
||||
lines = [line.decode('utf-8') for line in lines]
|
||||
lines = dropwhile(lambda line: not line.startswith("List of Commands:"), lines)
|
||||
lines = islice(lines, 2, None)
|
||||
lines = list(takewhile(lambda line: line.strip(), lines))
|
||||
return [line.strip().split(' ')[0] for line in lines]
|
||||
|
||||
|
||||
if which('yum'):
|
||||
_get_operations = cache(which('yum'))(_get_operations)
|
||||
|
||||
|
||||
@sudo_support
|
||||
def get_new_command(command):
|
||||
invalid_operation = command.script_parts[1]
|
||||
|
||||
if invalid_operation == 'uninstall':
|
||||
return [command.script.replace('uninstall', 'remove')]
|
||||
|
||||
return replace_command(command, invalid_operation, _get_operations())
|
||||
@@ -24,9 +24,9 @@ class Powershell(Generic):
|
||||
|
||||
def how_to_configure(self):
|
||||
return ShellConfiguration(
|
||||
content=u'iex "thefuck --alias"',
|
||||
content=u'iex "$(thefuck --alias)"',
|
||||
path='$profile',
|
||||
reload='& $profile',
|
||||
reload='. $profile',
|
||||
can_configure_automatically=False)
|
||||
|
||||
def _get_version(self):
|
||||
|
||||
3
thefuck/specific/nix.py
Normal file
3
thefuck/specific/nix.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from thefuck.utils import which
|
||||
|
||||
nix_available = bool(which('nix'))
|
||||
3
thefuck/specific/yum.py
Normal file
3
thefuck/specific/yum.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from thefuck.utils import which
|
||||
|
||||
yum_available = bool(which('yum'))
|
||||
@@ -98,7 +98,7 @@ def get_closest(word, possibilities, cutoff=0.6, fallback_to_first=True):
|
||||
|
||||
|
||||
def get_close_matches(word, possibilities, n=None, cutoff=0.6):
|
||||
"""Overrides `difflib.get_close_match` to controle argument `n`."""
|
||||
"""Overrides `difflib.get_close_match` to control argument `n`."""
|
||||
if n is None:
|
||||
n = settings.num_close_matches
|
||||
return difflib_get_close_matches(word, possibilities, n, cutoff)
|
||||
|
||||
Reference in New Issue
Block a user