allow 'borg repo-list' for Borg 1.x repositories via --from-borg1

- Add --from-borg1 option to borg repo-list command.
- Add allow_v1 argument to @with_repository decorator.
- If allow_v1 is True and v1_legacy is requested, allow version 1 repositories and load Manifest with RepoObj1 object class.
- Support LegacyRemoteRepository in Manifest class.
- Add test_repo_list_from_borg1.
This commit is contained in:
Thomas Waldmann 2026-06-03 00:45:16 +02:00
parent ec331057a9
commit 8a13309539
No known key found for this signature in database
GPG key ID: 243ACFA951F78E01
4 changed files with 55 additions and 8 deletions

View file

@ -74,7 +74,14 @@ def compat_check(*, create, manifest, key, cache, compatibility, decorator_name)
def with_repository(
create=False, lock=True, exclusive=False, manifest=True, cache=False, secure=True, compatibility=None
create=False,
lock=True,
exclusive=False,
manifest=True,
cache=False,
secure=True,
compatibility=None,
allow_v1=False,
):
"""
Method decorator for subcommand-handling methods: do_XYZ(self, args, repository, )
@ -88,6 +95,7 @@ def with_repository(
:param secure: do assert_secure after loading manifest
:param compatibility: mandatory if not create and (manifest or cache), specifies mandatory
feature categories to check
:param allow_v1: (bool) allow legacy Borg 1.x repositories
"""
# Note: with_repository decorator does not have a "key" argument (yet?)
compatibility = compat_check(
@ -116,6 +124,8 @@ def with_repository(
assert isinstance(exclusive, bool)
lock = getattr(args, "lock", _lock)
v1_legacy = getattr(args, "v1_legacy", False) if allow_v1 else False
repository = get_repository(
location,
create=create,
@ -123,18 +133,25 @@ def with_repository(
lock_wait=self.lock_wait,
lock=lock,
args=args,
v1_legacy=False,
v1_legacy=v1_legacy,
)
with repository:
if repository.version not in (4,):
acceptable_versions = (1,) if v1_legacy else (4,)
if repository.version not in acceptable_versions:
raise Error(
f"This borg version only accepts version 4 repos for -r/--repo, "
f"but not version {repository.version}. "
f"This borg version only accepts version {' or '.join(str(v) for v in acceptable_versions)} "
f"repos for -r/--repo, but not version {repository.version}. "
f"You can use 'borg transfer' to copy archives from old to new repos."
)
if manifest or cache:
manifest_ = Manifest.load(repository, compatibility, other=False)
if repository.version > 1:
ro_cls = RepoObj
else:
from ..legacy.repoobj import RepoObj1
ro_cls = RepoObj1
manifest_ = Manifest.load(repository, compatibility, other=False, ro_cls=ro_cls)
kwargs["manifest"] = manifest_
if "compression" in args:
manifest_.repo_objs.compressor = args.compression.compressor

View file

@ -14,7 +14,7 @@ logger = create_logger()
class RepoListMixIn:
@with_repository(compatibility=(Manifest.Operation.READ,))
@with_repository(compatibility=(Manifest.Operation.READ,), allow_v1=True)
def do_repo_list(self, args, repository, manifest):
"""List the archives contained in a repository."""
if args.format is not None:
@ -91,6 +91,7 @@ class RepoListMixIn:
subparser.add_argument(
"--short", dest="short", action="store_true", help="only print the archive IDs, nothing else"
)
subparser.add_argument("--from-borg1", dest="v1_legacy", action="store_true", help="repository is Borg 1.x")
subparser.add_argument(
"--format",
metavar="FORMAT",

View file

@ -448,9 +448,10 @@ class Manifest:
def __init__(self, key, repository, item_keys=None, ro_cls=RepoObj):
from .legacy.repository import LegacyRepository
from .legacy.remote import LegacyRemoteRepository
from .legacy.archives import LegacyArchives
if isinstance(repository, LegacyRepository):
if isinstance(repository, (LegacyRepository, LegacyRemoteRepository)):
self.archives: ArchivesInterface = LegacyArchives(repository, self)
else:
self.archives: ArchivesInterface = Archives(repository, self)

View file

@ -1,6 +1,8 @@
import json
import os
import pytest
from ...constants import * # NOQA
from . import cmd, checkts, create_regular_file, generate_archiver_tests, RK_ENCRYPTION
from .prune_cmd_test import _create_archive_ts
@ -169,3 +171,29 @@ def test_repo_list_deleted(archivers, request, backup_files):
assert "normal2" not in output
assert "deleted1" in output
assert "deleted2" in output
def test_repo_list_from_borg1(archivers, request, monkeypatch):
archiver = request.getfixturevalue(archivers)
if archiver.get_kind() in ["remote", "binary"]:
pytest.skip("only works locally")
import tarfile
repo12_tar = os.path.join(os.path.dirname(__file__), "repo12.tar.gz")
original_location = archiver.repository_location
extract_dir = f"{original_location}1"
os.makedirs(extract_dir)
with tarfile.open(repo12_tar) as tf:
tf.extractall(extract_dir)
monkeypatch.setenv("BORG_PASSPHRASE", "waytooeasyonlyfortests")
monkeypatch.setenv("BORG_TESTONLY_WEAKEN_KDF", "0")
# Set repository location to the extracted Borg 1.x repository
archiver.repository_location = extract_dir
archiver.repository_path = extract_dir
output = cmd(archiver, "repo-list", "--from-borg1")
assert "archive1" in output
assert "archive2" in output