create: add --tags, fixes #9401

- add `--tags TAG [TAG ...]` option to `borg create` to tag newly created archives.
- validate the tags exactly like `borg tag` does, including checking that any special tags starting with `@` are known `SPECIAL_TAGS`.
- add `test_create_tags` and `test_create_invalid_tags` to ensure proper behavior.
This commit is contained in:
Thomas Waldmann 2026-03-11 18:18:15 +01:00
parent 90f84e7219
commit 5f4e921b9a
No known key found for this signature in database
GPG key ID: 243ACFA951F78E01
2 changed files with 41 additions and 1 deletions

View file

@ -211,6 +211,14 @@ class CreateMixIn:
# do not save the archive if the user ctrl-c-ed.
raise Error("Got Ctrl-C / SIGINT.")
else:
# deal with tags
if args.tags is not None:
tags = {tag for tag in args.tags if tag}
special = {tag for tag in tags if tag.startswith("@")}
if not special.issubset(SPECIAL_TAGS):
raise Error("Unknown special tags given.")
archive.tags = tags
archive.save(comment=args.comment, timestamp=args.timestamp)
args.stats |= args.json
if args.stats:
@ -595,6 +603,8 @@ class CreateMixIn:
The archive will consume almost no disk space for files or parts of files that
have already been stored in other archives.
The ``--tags`` option can be used to add a list of tags to the new archive.
The archive name does not need to be unique; you can and should use the same
name for a series of archives. The unique archive identifier is its ID (hash),
and you can abbreviate the ID as long as it is unique.
@ -995,6 +1005,14 @@ class CreateMixIn:
action=Highlander,
help="explicitly set username for the archive",
)
archive_group.add_argument(
"--tags",
metavar="TAG",
dest="tags",
type=helpers.tag_validator,
nargs="+",
help="add tags to archive (comma-separated or multiple arguments)",
)
subparser.add_argument("name", metavar="NAME", type=archivename_validator, help="specify the archive name")
subparser.add_argument(

View file

@ -15,7 +15,7 @@ from ...constants import zeros
from ...manifest import Manifest
from ...platform import is_win32
from ...repository import Repository
from ...helpers import CommandError, BackupPermissionError
from ...helpers import CommandError, BackupPermissionError, Error
from .. import has_lchflags, has_mknod
from .. import changedir
from .. import (
@ -682,6 +682,28 @@ def test_file_status(archivers, request):
assert "A input/file2" in output
def test_create_tags(archivers, request):
archiver = request.getfixturevalue(archivers)
create_test_files(archiver.input_path)
cmd(archiver, "repo-create", RK_ENCRYPTION)
cmd(archiver, "create", "--tags", "foo", "bar", "baz", "--", "test", "input")
info = cmd(archiver, "info", "--json", "test")
info = json.loads(info)
assert sorted(info["archives"][0]["tags"]) == ["bar", "baz", "foo"]
def test_create_invalid_tags(archivers, request):
archiver = request.getfixturevalue(archivers)
create_test_files(archiver.input_path)
cmd(archiver, "repo-create", RK_ENCRYPTION)
if archiver.FORK_DEFAULT:
output = cmd(archiver, "create", "--tags", "@INVALID", "--", "test", "input", exit_code=EXIT_ERROR)
assert "Unknown special tags given" in output
else:
with pytest.raises(Error):
cmd(archiver, "create", "--tags", "@INVALID", "--", "test", "input")
@pytest.mark.skipif(
is_win32, reason="ctime attribute is file creation time on Windows"
) # see https://docs.python.org/3/library/os.html#os.stat_result.st_ctime