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
|
1.6.2.dev0
|
||||||
* #536: Fix generate-borgmatic-config with "--source" flag to support more complex schema changes
|
* #536: Fix generate-borgmatic-config with "--source" flag to support more complex schema changes
|
||||||
like the new Healthchecks configuration options.
|
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
|
1.6.1
|
||||||
* #294: Add Healthchecks monitoring hook "ping_body_limit" option to configure how many bytes of
|
* #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))
|
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
|
Build a top-level parser and its subparsers and return them as a tuple.
|
||||||
them as a dict mapping from subparser name (or "global") to an argparse.Namespace instance.
|
|
||||||
'''
|
'''
|
||||||
config_paths = collect.get_default_config_paths(expand_home=True)
|
config_paths = collect.get_default_config_paths(expand_home=True)
|
||||||
unexpanded_config_paths = collect.get_default_config_paths(expand_home=False)
|
unexpanded_config_paths = collect.get_default_config_paths(expand_home=False)
|
||||||
|
@ -189,6 +188,12 @@ def parse_arguments(*unparsed_arguments):
|
||||||
action='extend',
|
action='extend',
|
||||||
help='One or more configuration file options to override with specified values',
|
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(
|
global_group.add_argument(
|
||||||
'--version',
|
'--version',
|
||||||
dest='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')
|
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(
|
arguments, remaining_arguments = parse_subparser_arguments(
|
||||||
unparsed_arguments, subparsers.choices
|
unparsed_arguments, subparsers.choices
|
||||||
)
|
)
|
||||||
|
|
|
@ -11,6 +11,7 @@ from subprocess import CalledProcessError
|
||||||
import colorama
|
import colorama
|
||||||
import pkg_resources
|
import pkg_resources
|
||||||
|
|
||||||
|
import borgmatic.commands.completion
|
||||||
from borgmatic.borg import borg as borg_borg
|
from borgmatic.borg import borg as borg_borg
|
||||||
from borgmatic.borg import check as borg_check
|
from borgmatic.borg import check as borg_check
|
||||||
from borgmatic.borg import compact as borg_compact
|
from borgmatic.borg import compact as borg_compact
|
||||||
|
@ -884,6 +885,9 @@ def main(): # pragma: no cover
|
||||||
if global_arguments.version:
|
if global_arguments.version:
|
||||||
print(pkg_resources.require('borgmatic')[0].version)
|
print(pkg_resources.require('borgmatic')[0].version)
|
||||||
sys.exit(0)
|
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))
|
config_filenames = tuple(collect.collect_config_filenames(global_arguments.config_paths))
|
||||||
configs, parse_logs = load_configurations(config_filenames, global_arguments.overrides)
|
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
|
[Hetzner](https://www.hetzner.com/storage/storage-box) have compatible storage
|
||||||
offerings, but do not currently fund borgmatic development or hosting.
|
offerings, but do not currently fund borgmatic development or hosting.
|
||||||
|
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
After you install borgmatic, generate a sample configuration file:
|
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).
|
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
|
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,
|
`--json` flag. Otherwise, you can disable it by passing the `--no-color` flag,
|
||||||
setting the environment variable `PY_COLORS=False`, or setting the `color`
|
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