The package carried no ruff configuration at all, so it ran on the defaults (E4+E7+E9+F) while infinito-nexus-core, its only consumer, holds itself to a far wider selection. Measured against that selection this tree had 224 findings. It now has none. The selector list is core's verbatim so both repositories answer to one bar. target-version stays py39 rather than core's py311, because requires-python still declares >=3.9 and pyupgrade would otherwise propose syntax the declared minimum cannot run. Every ignore carries its reason: S603/S607 in particular, since running docker and dump binaries from PATH in list form is this tool's whole job and is already injection-safe. Two conversions are judgement rather than mechanics. os.path.join(dir, '') was the rsync idiom for a trailing separator, which Path drops, so it becomes an explicit os.sep. os.path.abspath stays where Path.resolve() would follow symlinks and let a symlinked volume test as inside the snapshot subject. BREAKING CHANGE: VersionMismatch is renamed VersionMismatchError. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
65 lines
2.3 KiB
Python
65 lines
2.3 KiB
Python
"""Restore a volume's file tree by writing into its mountpoint.
|
|
|
|
That shortcut only holds for a plain local volume, where the mountpoint *is*
|
|
the storage. A volume with driver options - NFS, a bind device, tmpfs - keeps
|
|
the same ``/var/lib/docker/volumes/<name>/_data`` path, but docker mounts the
|
|
real backing store over it on demand and unmounts it again when the last
|
|
consumer stops. Writing there while nothing has it mounted lands in the empty
|
|
directory underneath, is hidden by the next mount, and rsync reports success.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
from .run import docker_volume_exists, run, stdout_of
|
|
|
|
INSPECT_FORMAT = (
|
|
"{{ .Mountpoint }}|{{ .Driver }}|{{ if .Options }}opts{{ else }}plain{{ end }}"
|
|
)
|
|
|
|
|
|
def restore_volume_files(volume_name: str, backup_files_dir: str) -> int:
|
|
if not Path(backup_files_dir).is_dir():
|
|
print(f"ERROR: backup files dir not found: {backup_files_dir}", file=sys.stderr)
|
|
return 2
|
|
|
|
if not docker_volume_exists(volume_name):
|
|
print(f"Volume {volume_name} does not exist. Creating...")
|
|
run(["docker", "volume", "create", volume_name])
|
|
else:
|
|
print(f"Volume {volume_name} already exists.")
|
|
|
|
cp = run(
|
|
["docker", "volume", "inspect", "--format", INSPECT_FORMAT, volume_name],
|
|
capture=True,
|
|
)
|
|
fields = stdout_of(cp).split("|")
|
|
mountpoint = fields[0] if fields else ""
|
|
if not mountpoint:
|
|
print(
|
|
f"ERROR: could not resolve mountpoint for volume {volume_name}",
|
|
file=sys.stderr,
|
|
)
|
|
return 2
|
|
|
|
driver, options = ([*fields, "local", "plain"])[1:3]
|
|
if (driver != "local" or options == "opts") and not os.path.ismount(mountpoint):
|
|
print(
|
|
f"ERROR: volume {volume_name} has a backing store of its own "
|
|
f"(driver {driver}) but nothing has it mounted; writing to "
|
|
f"{mountpoint} now would land under the mount and be lost. "
|
|
"Start a container that mounts the volume, then restore again.",
|
|
file=sys.stderr,
|
|
)
|
|
return 2
|
|
|
|
# rsync reads "dir/" as its contents and "dir" as the directory itself.
|
|
src = f"{Path(backup_files_dir)}{os.sep}"
|
|
dest = f"{Path(mountpoint)}{os.sep}"
|
|
run(["rsync", "-avv", "--delete", src, dest])
|
|
print("File restore complete.")
|
|
return 0
|