Add tab completion, update README, version bump 0.2.0
Some checks failed
Lint & Security / precommit-and-security (pull_request) Failing after 1m37s

This commit is contained in:
2025-12-21 09:29:21 +00:00
parent a5551e7047
commit e0ec2ce60a
3 changed files with 108 additions and 22 deletions

View File

@@ -198,6 +198,16 @@ Snapshots are stored in a local SQLite database containing:
Usernames and permission strings are resolved only for display.
### TAB completion
Add this to your `.bashrc`
```
eval "$(register-python-argcomplete chguard)"
```
And then
```
source ~/.bashrc
```
## pre-commit
This project uses [**pre-commit**](https://pre-commit.com/) to run automatic formatting and security checks before each commit (Black, Bandit, and various safety checks).

View File

@@ -1,6 +1,8 @@
from __future__ import annotations
import argparse
import argcomplete
import importlib.metadata
import os
import sys
import stat
@@ -27,6 +29,13 @@ from chguard.restore import plan_restore, apply_restore
from chguard.util import normalize_root
def get_version():
try:
return importlib.metadata.version("chguard")
except importlib.metadata.PackageNotFoundError:
return "unknown"
def _uid_to_name(uid: int) -> str:
"""Return username for uid, or uid as string if unknown."""
try:
@@ -83,6 +92,19 @@ def _is_root() -> bool:
return os.geteuid() == 0
def complete_state_names(prefix, parsed_args, **kwargs):
try:
conn = connect(
Path(parsed_args.db).expanduser().resolve()
if parsed_args.db
else None
)
rows = conn.execute("SELECT name FROM states").fetchall()
return [name for (name,) in rows if name.startswith(prefix)]
except Exception:
return []
def main() -> None:
"""
Entry point for the CLI.
@@ -100,45 +122,99 @@ def main() -> None:
)
actions = parser.add_mutually_exclusive_group(required=True)
actions.add_argument("--save", metavar="PATH", help="Save state for PATH")
parser.add_argument(
"--version",
action="version",
version=f"chguard {get_version()}",
)
actions.add_argument(
"--restore", action="store_true", help="Restore a saved state"
)
"--save",
metavar="PATH",
help="Save state for PATH",
).completer = argcomplete.FilesCompleter()
actions.add_argument(
"--list", action="store_true", help="List saved states"
"--restore",
action="store_true",
help="Restore a saved state",
)
actions.add_argument(
"--delete", metavar="STATE", help="Delete a saved state"
"--list",
action="store_true",
help="List saved states",
)
actions.add_argument(
"--delete",
metavar="STATE",
help="Delete a saved state",
).completer = complete_state_names
# positional STATE
parser.add_argument(
"state",
nargs="?",
help="State name (required with --restore)",
).completer = complete_state_names
parser.add_argument(
"--name",
help="State name (required with --save)",
)
parser.add_argument(
"state", nargs="?", help="State name (required with --restore)"
)
parser.add_argument("--name", help="State name (required with --save)")
parser.add_argument(
"--overwrite", action="store_true", help="Overwrite existing state"
"--overwrite",
action="store_true",
help="Overwrite existing state",
)
parser.add_argument(
"--permissions", action="store_true", help="Restore MODE only"
)
parser.add_argument(
"--owner", action="store_true", help="Restore OWNER only"
"--permissions",
action="store_true",
help="Restore MODE only",
)
parser.add_argument(
"--dry-run", action="store_true", help="Preview only; do not apply"
)
parser.add_argument(
"--yes", action="store_true", help="Apply without confirmation"
"--owner",
action="store_true",
help="Restore OWNER only",
)
parser.add_argument("--root", metavar="PATH", help="Override restore root")
parser.add_argument(
"--exclude", action="append", default=[], help="Exclude path prefix"
"--dry-run",
action="store_true",
help="Preview only; do not apply",
)
parser.add_argument("--db", metavar="PATH", help="Override database path")
parser.add_argument(
"--yes",
action="store_true",
help="Apply without confirmation",
)
parser.add_argument(
"--root",
metavar="PATH",
help="Override restore root",
).completer = argcomplete.FilesCompleter()
parser.add_argument(
"--exclude",
action="append",
default=[],
help="Exclude path prefix",
).completer = argcomplete.FilesCompleter()
parser.add_argument(
"--db",
metavar="PATH",
help="Override database path",
).completer = argcomplete.FilesCompleter()
argcomplete.autocomplete(parser)
args = parser.parse_args()
console = Console()

View File

@@ -1,6 +1,6 @@
[tool.poetry]
name = "chguard"
version = "0.1.0"
version = "0.2.0"
description = "Safety-first tool to snapshot and restore filesystem ownership and permissions."
authors = ["Marco D'Aleo <marco@marcodaleo.com>"]
license = "GPL-3.0-or-later"