Add Bash completion script so you can tab-complete the borgmatic command-line.
This commit is contained in:
parent
691ec96909
commit
77b84f8a48
7 changed files with 123 additions and 5 deletions
3
NEWS
3
NEWS
|
@ -1,6 +1,9 @@
|
|||
1.6.2.dev0
|
||||
* #536: Fix generate-borgmatic-config with "--source" flag to support more complex schema changes
|
||||
like the new Healthchecks configuration options.
|
||||
* Add Bash completion script so you can tab-complete the borgmatic command-line. See the
|
||||
documentation for more information:
|
||||
https://torsion.org/borgmatic/docs/how-to/set-up-backups/#shell-completion
|
||||
|
||||
1.6.1
|
||||
* #294: Add Healthchecks monitoring hook "ping_body_limit" option to configure how many bytes of
|
||||
|
|
|
@ -109,10 +109,9 @@ class Extend_action(Action):
|
|||
setattr(namespace, self.dest, list(values))
|
||||
|
||||
|
||||
def parse_arguments(*unparsed_arguments):
|
||||
def make_parsers():
|
||||
'''
|
||||
Given command-line arguments with which this script was invoked, parse the arguments and return
|
||||
them as a dict mapping from subparser name (or "global") to an argparse.Namespace instance.
|
||||
Build a top-level parser and its subparsers and return them as a tuple.
|
||||
'''
|
||||
config_paths = collect.get_default_config_paths(expand_home=True)
|
||||
unexpanded_config_paths = collect.get_default_config_paths(expand_home=False)
|
||||
|
@ -189,6 +188,12 @@ def parse_arguments(*unparsed_arguments):
|
|||
action='extend',
|
||||
help='One or more configuration file options to override with specified values',
|
||||
)
|
||||
global_group.add_argument(
|
||||
'--bash-completion',
|
||||
default=False,
|
||||
action='store_true',
|
||||
help='Show bash completion script and exit',
|
||||
)
|
||||
global_group.add_argument(
|
||||
'--version',
|
||||
dest='version',
|
||||
|
@ -647,6 +652,16 @@ def parse_arguments(*unparsed_arguments):
|
|||
)
|
||||
borg_group.add_argument('-h', '--help', action='help', help='Show this help message and exit')
|
||||
|
||||
return top_level_parser, subparsers
|
||||
|
||||
|
||||
def parse_arguments(*unparsed_arguments):
|
||||
'''
|
||||
Given command-line arguments with which this script was invoked, parse the arguments and return
|
||||
them as a dict mapping from subparser name (or "global") to an argparse.Namespace instance.
|
||||
'''
|
||||
top_level_parser, subparsers = make_parsers()
|
||||
|
||||
arguments, remaining_arguments = parse_subparser_arguments(
|
||||
unparsed_arguments, subparsers.choices
|
||||
)
|
||||
|
|
|
@ -11,6 +11,7 @@ from subprocess import CalledProcessError
|
|||
import colorama
|
||||
import pkg_resources
|
||||
|
||||
import borgmatic.commands.completion
|
||||
from borgmatic.borg import borg as borg_borg
|
||||
from borgmatic.borg import check as borg_check
|
||||
from borgmatic.borg import compact as borg_compact
|
||||
|
@ -884,6 +885,9 @@ def main(): # pragma: no cover
|
|||
if global_arguments.version:
|
||||
print(pkg_resources.require('borgmatic')[0].version)
|
||||
sys.exit(0)
|
||||
if global_arguments.bash_completion:
|
||||
print(borgmatic.commands.completion.bash_completion())
|
||||
sys.exit(0)
|
||||
|
||||
config_filenames = tuple(collect.collect_config_filenames(global_arguments.config_paths))
|
||||
configs, parse_logs = load_configurations(config_filenames, global_arguments.overrides)
|
||||
|
|
60
borgmatic/commands/completion.py
Normal file
60
borgmatic/commands/completion.py
Normal file
|
@ -0,0 +1,60 @@
|
|||
import pkg_resources
|
||||
|
||||
from borgmatic.commands import arguments
|
||||
|
||||
UPGRADE_MESSAGE = '''
|
||||
Your bash completions script is from a different version of borgmatic than is
|
||||
currently installed. Please upgrade your script so your completions match the
|
||||
command-line flags in your installed borgmatic! Try this to upgrade:
|
||||
|
||||
sudo sh -c "borgmatic --bash-completions > $BASH_SOURCE"
|
||||
source $BASH_SOURCE
|
||||
'''
|
||||
|
||||
|
||||
def parser_flags(parser):
|
||||
'''
|
||||
Given an argparse.ArgumentParser instance, return its argument flags in a space-separated
|
||||
string.
|
||||
'''
|
||||
return ' '.join(option for action in parser._actions for option in action.option_strings)
|
||||
|
||||
|
||||
def bash_completion():
|
||||
'''
|
||||
Return a bash completion script for the borgmatic command. Produce this by introspecting
|
||||
borgmatic's command-line argument parsers.
|
||||
'''
|
||||
top_level_parser, subparsers = arguments.make_parsers()
|
||||
global_flags = parser_flags(top_level_parser)
|
||||
actions = ' '.join(subparsers.choices.keys())
|
||||
borgmatic_version = pkg_resources.require('borgmatic')[0].version
|
||||
|
||||
# Avert your eyes.
|
||||
return '\n'.join(
|
||||
(
|
||||
'check_version() {',
|
||||
' local installed_version="$(borgmatic --version 2> /dev/null)"',
|
||||
' if [ "$installed_version" != "%s" ] && [ "$installed_version" != "" ];'
|
||||
% borgmatic_version,
|
||||
' then cat << EOF\n%s\nEOF' % UPGRADE_MESSAGE,
|
||||
' fi',
|
||||
'}',
|
||||
'complete_borgmatic() {',
|
||||
)
|
||||
+ tuple(
|
||||
''' if [[ " ${COMP_WORDS[*]} " =~ " %s " ]]; then
|
||||
COMPREPLY=($(compgen -W "%s %s %s" -- "${COMP_WORDS[COMP_CWORD]}"))
|
||||
return 0
|
||||
fi'''
|
||||
% (action, parser_flags(subparser), actions, global_flags)
|
||||
for action, subparser in subparsers.choices.items()
|
||||
)
|
||||
+ (
|
||||
' COMPREPLY=($(compgen -W "%s %s" -- "${COMP_WORDS[COMP_CWORD]}"))'
|
||||
% (actions, global_flags),
|
||||
' (check_version &)',
|
||||
'}',
|
||||
'\ncomplete -F complete_borgmatic borgmatic',
|
||||
)
|
||||
)
|
|
@ -111,6 +111,7 @@ Additionally, [rsync.net](https://www.rsync.net/products/borg.html) and
|
|||
[Hetzner](https://www.hetzner.com/storage/storage-box) have compatible storage
|
||||
offerings, but do not currently fund borgmatic development or hosting.
|
||||
|
||||
|
||||
## Configuration
|
||||
|
||||
After you install borgmatic, generate a sample configuration file:
|
||||
|
@ -302,9 +303,34 @@ interested in an [unofficial work-around for Full Disk
|
|||
Access](https://projects.torsion.org/borgmatic-collective/borgmatic/issues/293).
|
||||
|
||||
|
||||
## Colored output
|
||||
## Niceties
|
||||
|
||||
Borgmatic produces colored terminal output by default. It is disabled when a
|
||||
|
||||
### Shell completion
|
||||
|
||||
borgmatic includes a shell completion script (currently only for Bash) to
|
||||
support tab-completing borgmatic command-line actions and flags. Depending on
|
||||
how you installed borgmatic, this may be enabled by default. But if it's not,
|
||||
you can install the shell completion script globally:
|
||||
|
||||
```bash
|
||||
sudo su -c "borgmatic --bash-completion > /usr/share/bash-completion/completions/borgmatic"
|
||||
```
|
||||
|
||||
Alternatively, if you'd like to install the script for just the current user:
|
||||
|
||||
```bash
|
||||
mkdir --parents ~/.local/share/bash-completion/completions
|
||||
borgmatic --bash-completion > ~/.local/share/bash-completion/completions/borgmatic
|
||||
```
|
||||
|
||||
In either case, you may also need to install the `bash-completion` Linux
|
||||
package and restart your shell (`exit` and open a new shell).
|
||||
|
||||
|
||||
### Colored output
|
||||
|
||||
borgmatic produces colored terminal output by default. It is disabled when a
|
||||
non-interactive terminal is detected (like a cron job), or when you use the
|
||||
`--json` flag. Otherwise, you can disable it by passing the `--no-color` flag,
|
||||
setting the environment variable `PY_COLORS=False`, or setting the `color`
|
||||
|
|
5
tests/end-to-end/test_completion.py
Normal file
5
tests/end-to-end/test_completion.py
Normal file
|
@ -0,0 +1,5 @@
|
|||
import subprocess
|
||||
|
||||
|
||||
def test_bash_completion_runs_without_error():
|
||||
subprocess.check_call('eval "$(borgmatic --bash-completion)"', shell=True)
|
5
tests/integration/commands/test_completion.py
Normal file
5
tests/integration/commands/test_completion.py
Normal file
|
@ -0,0 +1,5 @@
|
|||
from borgmatic.commands import completion as module
|
||||
|
||||
|
||||
def test_bash_completion_does_not_raise():
|
||||
assert module.bash_completion()
|
Loading…
Reference in a new issue