gc: add --skip-failed

This commit is contained in:
dberenbaum
2023-12-20 00:21:23 +02:00
committed by Ruslan Kuprieiev
parent 7821e126fe
commit 827f9a7f03
6 changed files with 59 additions and 14 deletions
+7
View File
@@ -86,6 +86,7 @@ class CmdGC(CmdBase):
num=self.args.num, num=self.args.num,
not_in_remote=self.args.not_in_remote, not_in_remote=self.args.not_in_remote,
dry=self.args.dry, dry=self.args.dry,
skip_failed=self.args.skip_failed,
) )
return 0 return 0
@@ -188,6 +189,12 @@ def add_parser(subparsers, parent_parser):
help="Remote storage to collect garbage in", help="Remote storage to collect garbage in",
metavar="<name>", metavar="<name>",
) )
gc_parser.add_argument(
"--skip-failed",
action="store_true",
default=False,
help="Skip revisions that fail when collected.",
)
gc_parser.add_argument( gc_parser.add_argument(
"-f", "-f",
"--force", "--force",
+12
View File
@@ -357,3 +357,15 @@ class ArtifactNotFoundError(DvcException):
desc = f" @ {stage or version}" if (stage or version) else "" desc = f" @ {stage or version}" if (stage or version) else ""
super().__init__(f"Unable to find artifact '{name}{desc}'") super().__init__(f"Unable to find artifact '{name}{desc}'")
class RevCollectionError(DvcException):
"""Thrown if a revision failed to be collected.
Args:
rev (str): revision that failed (or "workspace").
"""
def __init__(self, rev):
self.rev = rev
super().__init__(f"Failed to collect '{rev}'")
+25 -13
View File
@@ -12,7 +12,12 @@ from typing import (
Union, Union,
) )
from dvc.exceptions import NotDvcRepoError, OutputNotFoundError from dvc.exceptions import (
DvcException,
NotDvcRepoError,
OutputNotFoundError,
RevCollectionError,
)
from dvc.ignore import DvcIgnoreFilter from dvc.ignore import DvcIgnoreFilter
from dvc.log import logger from dvc.log import logger
from dvc.utils.objects import cached_property from dvc.utils.objects import cached_property
@@ -492,6 +497,7 @@ class Repo:
revs=None, revs=None,
num=1, num=1,
push: bool = False, push: bool = False,
skip_failed: bool = False,
): ):
"""Get the stages related to the given target and collect """Get the stages related to the given target and collect
the `info` of its outputs. the `info` of its outputs.
@@ -510,7 +516,7 @@ class Repo:
""" """
used = defaultdict(set) used = defaultdict(set)
for _ in self.brancher( for rev in self.brancher(
revs=revs, revs=revs,
all_branches=all_branches, all_branches=all_branches,
all_tags=all_tags, all_tags=all_tags,
@@ -519,17 +525,23 @@ class Repo:
commit_date=commit_date, commit_date=commit_date,
num=num, num=num,
): ):
for odb, objs in self.index.used_objs( try:
targets, for odb, objs in self.index.used_objs(
remote=remote, targets,
force=force, remote=remote,
jobs=jobs, force=force,
recursive=recursive, jobs=jobs,
with_deps=with_deps, recursive=recursive,
push=push, with_deps=with_deps,
).items(): push=push,
used[odb].update(objs) ).items():
used[odb].update(objs)
except DvcException as exc:
rev = rev or "workspace"
if skip_failed:
logger.warning("Failed to collect '%s', skipping", rev)
else:
raise RevCollectionError(rev) from exc
if used_run_cache: if used_run_cache:
for odb, objs in self.stage_cache.get_used_objs( for odb, objs in self.stage_cache.get_used_objs(
used_run_cache, remote=remote, force=force, jobs=jobs used_run_cache, remote=remote, force=force, jobs=jobs
+2
View File
@@ -68,6 +68,7 @@ def gc( # noqa: PLR0913, C901
num: Optional[int] = None, num: Optional[int] = None,
not_in_remote: bool = False, not_in_remote: bool = False,
dry: bool = False, dry: bool = False,
skip_failed: bool = False,
): ):
# require `workspace` to be true to come into effect. # require `workspace` to be true to come into effect.
# assume `workspace` to be enabled if any of `all_tags`, `all_commits`, # assume `workspace` to be enabled if any of `all_tags`, `all_commits`,
@@ -113,6 +114,7 @@ def gc( # noqa: PLR0913, C901
jobs=jobs, jobs=jobs,
revs=[rev] if rev else None, revs=[rev] if rev else None,
num=num or 1, num=num or 1,
skip_failed=skip_failed,
).items(): ).items():
if odb not in odb_to_obj_ids: if odb not in odb_to_obj_ids:
odb_to_obj_ids[odb] = set() odb_to_obj_ids[odb] = set()
+11 -1
View File
@@ -7,7 +7,7 @@ import textwrap
import pytest import pytest
from dvc.cli import main from dvc.cli import main
from dvc.exceptions import CollectCacheError, InvalidArgumentError from dvc.exceptions import CollectCacheError, InvalidArgumentError, RevCollectionError
from dvc.fs import LocalFileSystem from dvc.fs import LocalFileSystem
from dvc.utils.fs import remove from dvc.utils.fs import remove
from dvc_data.hashfile.db.local import LocalHashFileDB from dvc_data.hashfile.db.local import LocalHashFileDB
@@ -439,3 +439,13 @@ def test_gc_logging(caplog, dvc, good_and_bad_cache):
assert "Removed 3 objects from repo cache." in caplog.text assert "Removed 3 objects from repo cache." in caplog.text
assert "No unused 'local' cache to remove." in caplog.text assert "No unused 'local' cache to remove." in caplog.text
assert "No unused 'legacy' cache to remove." in caplog.text assert "No unused 'legacy' cache to remove." in caplog.text
def test_gc_skip_failed(tmp_dir, dvc):
with open("dvc.yaml", mode="w") as f:
f.write("\ninvalid")
with pytest.raises(RevCollectionError):
dvc.gc(force=True, workspace=True)
dvc.gc(force=True, workspace=True, skip_failed=True)
+2
View File
@@ -26,6 +26,7 @@ def test_(dvc, scm, mocker):
"--projects", "--projects",
"project1", "project1",
"project2", "project2",
"--skip-failed",
] ]
) )
assert cli_args.func == CmdGC assert cli_args.func == CmdGC
@@ -51,6 +52,7 @@ def test_(dvc, scm, mocker):
num=None, num=None,
not_in_remote=False, not_in_remote=False,
dry=True, dry=True,
skip_failed=True,
) )
cli_args = parse_args(["gc"]) cli_args = parse_args(["gc"])