Compare commits
5 Commits
0.2.0
...
aafad81bb6
| Author | SHA1 | Date | |
|---|---|---|---|
| aafad81bb6 | |||
|
9658f534ea
|
|||
|
5af28d21ca
|
|||
| b0395a432f | |||
|
603e2ac0c6
|
2
.gitignore
vendored
2
.gitignore
vendored
@@ -85,7 +85,7 @@ ipython_config.py
|
|||||||
# pyenv
|
# pyenv
|
||||||
# For a library or package, you might want to ignore these files since the code is
|
# For a library or package, you might want to ignore these files since the code is
|
||||||
# intended to run in multiple environments; otherwise, check them in:
|
# intended to run in multiple environments; otherwise, check them in:
|
||||||
# .python-version
|
.python-version
|
||||||
|
|
||||||
# pipenv
|
# pipenv
|
||||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
# chguard
|
# chguard
|
||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<img src="chguard.png" alt="chguard logo" width="256" />
|
<img src="https://git.sysmd.uk/guardutils/chguard/src/branch/main/chguard.png" alt="chguard logo" width="256" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -248,14 +248,19 @@ def main() -> None:
|
|||||||
|
|
||||||
root = normalize_root(args.save)
|
root = normalize_root(args.save)
|
||||||
|
|
||||||
|
try:
|
||||||
|
with conn: # start transaction
|
||||||
if state_exists(conn, args.name):
|
if state_exists(conn, args.name):
|
||||||
if not args.overwrite:
|
if not args.overwrite:
|
||||||
raise SystemExit(
|
raise SystemExit(
|
||||||
f"State '{args.name}' already exists (use --overwrite)"
|
f"State '{args.name}' already exists (use --overwrite)"
|
||||||
)
|
)
|
||||||
delete_state(conn, args.name)
|
# if the new save fails, this delete_state step will also roll back
|
||||||
|
delete_state(conn, args.name, commit=False)
|
||||||
|
|
||||||
state_id = create_state(conn, args.name, str(root), os.getuid())
|
state_id = create_state(
|
||||||
|
conn, args.name, str(root), os.getuid(), commit=False
|
||||||
|
)
|
||||||
|
|
||||||
# Abort early if root-owned files exist and user is not root.
|
# Abort early if root-owned files exist and user is not root.
|
||||||
# This prevents creating snapshots that cannot be meaningfully restored.
|
# This prevents creating snapshots that cannot be meaningfully restored.
|
||||||
@@ -281,10 +286,12 @@ def main() -> None:
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
conn.commit()
|
|
||||||
console.print(f"Saved state '{args.name}' for {root}")
|
console.print(f"Saved state '{args.name}' for {root}")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
except SystemExit:
|
||||||
|
raise
|
||||||
|
|
||||||
if args.restore:
|
if args.restore:
|
||||||
if not args.state:
|
if not args.state:
|
||||||
parser.error("STATE is required with --restore")
|
parser.error("STATE is required with --restore")
|
||||||
|
|||||||
@@ -61,18 +61,27 @@ def state_exists(conn: sqlite3.Connection, name: str) -> bool:
|
|||||||
|
|
||||||
|
|
||||||
def create_state(
|
def create_state(
|
||||||
conn: sqlite3.Connection, name: str, root_path: str, created_by_uid: int
|
conn: sqlite3.Connection,
|
||||||
|
name: str,
|
||||||
|
root_path: str,
|
||||||
|
created_by_uid: int,
|
||||||
|
*,
|
||||||
|
commit: bool = True,
|
||||||
) -> int:
|
) -> int:
|
||||||
cur = conn.execute(
|
cur = conn.execute(
|
||||||
"INSERT INTO states (name, root_path, created_at, created_by_uid) VALUES (?, ?, ?, ?)",
|
"INSERT INTO states (name, root_path, created_at, created_by_uid) VALUES (?, ?, ?, ?)",
|
||||||
(name, root_path, utc_now_iso(), created_by_uid),
|
(name, root_path, utc_now_iso(), created_by_uid),
|
||||||
)
|
)
|
||||||
|
if commit:
|
||||||
conn.commit()
|
conn.commit()
|
||||||
return int(cur.lastrowid)
|
return int(cur.lastrowid)
|
||||||
|
|
||||||
|
|
||||||
def delete_state(conn: sqlite3.Connection, name: str) -> int:
|
def delete_state(
|
||||||
|
conn: sqlite3.Connection, name: str, commit: bool = True
|
||||||
|
) -> int:
|
||||||
cur = conn.execute("DELETE FROM states WHERE name = ?", (name,))
|
cur = conn.execute("DELETE FROM states WHERE name = ?", (name,))
|
||||||
|
if commit:
|
||||||
conn.commit()
|
conn.commit()
|
||||||
return cur.rowcount
|
return cur.rowcount
|
||||||
|
|
||||||
|
|||||||
2
poetry.lock
generated
2
poetry.lock
generated
@@ -289,4 +289,4 @@ test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess
|
|||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.0"
|
lock-version = "2.0"
|
||||||
python-versions = ">=3.10,<4.0"
|
python-versions = ">=3.10,<4.0"
|
||||||
content-hash = "aa4ded468b14fc02b90fdb2a0b1bd446195d02affc359c045b6bbb93858aa747"
|
content-hash = "4a5c993fcc16fe3739c43eb00bed750ce0803d45e37c7a786aa0b83bb4930267"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "chguard"
|
name = "chguard"
|
||||||
version = "0.2.0"
|
version = "0.2.2"
|
||||||
description = "Safety-first tool to snapshot and restore filesystem ownership and permissions."
|
description = "Safety-first tool to snapshot and restore filesystem ownership and permissions."
|
||||||
authors = ["Marco D'Aleo <marco@marcodaleo.com>"]
|
authors = ["Marco D'Aleo <marco@marcodaleo.com>"]
|
||||||
license = "GPL-3.0-or-later"
|
license = "GPL-3.0-or-later"
|
||||||
@@ -12,8 +12,8 @@ repository = "https://git.sysmd.uk/guardutils/chguard"
|
|||||||
python = ">=3.10,<4.0"
|
python = ">=3.10,<4.0"
|
||||||
rich = ">=12"
|
rich = ">=12"
|
||||||
argcomplete = ">=2"
|
argcomplete = ">=2"
|
||||||
platformdirs = "^4.5.1"
|
platformdirs = ">=4.5.1"
|
||||||
filelock = "^3.20.1"
|
filelock = ">=3.20.1"
|
||||||
|
|
||||||
[tool.poetry.scripts]
|
[tool.poetry.scripts]
|
||||||
chguard = "chguard.cli:main"
|
chguard = "chguard.cli:main"
|
||||||
|
|||||||
Reference in New Issue
Block a user