gitlab: Prune untouched specs less aggressively (#33669)

Untouched spec pruning was added to reduce the number of specs
developers see getting rebuilt in their PR pipelines that they
don't understand.  Because the state of the develop mirror lags
quite far behind the tip of the develop branch, PRs often find
they need to rebuild things untouched by their PR.

Untouched spec pruning was previously implemented by finding all
specs in the environment with names of packages touched by the PR,
traversing in both directions the DAGS of those specs, and adding
all dependencies as well as dependents to a list of concrete specs
that should not be considered for pruning.

We found that this heuristic results in too many pruned specs, and
that dependents of touched specs must have all their dependencies
added to the list of specs that should not be considered for pruning.
This commit is contained in:
Scott Wittenburg 2022-11-03 13:33:52 -06:00 committed by GitHub
parent 08bee718a2
commit b55509ffa8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 31 additions and 26 deletions

View File

@ -515,38 +515,36 @@ def compute_affected_packages(rev1="HEAD^", rev2="HEAD"):
return spack.repo.get_all_package_diffs("ARC", rev1=rev1, rev2=rev2)
def get_spec_filter_list(env, affected_pkgs, dependencies=True, dependents=True):
"""Given a list of package names, and assuming an active and
concretized environment, return a set of concrete specs from
the environment corresponding to any of the affected pkgs (or
optionally to any of their dependencies/dependents).
def get_spec_filter_list(env, affected_pkgs):
"""Given a list of package names and an active/concretized
environment, return the set of all concrete specs from the
environment that could have been affected by changing the
list of packages.
Arguments:
env (spack.environment.Environment): Active concrete environment
affected_pkgs (List[str]): Affected package names
dependencies (bool): Include dependencies of affected packages
dependents (bool): Include dependents of affected pacakges
Returns:
A list of concrete specs from the active environment including
those associated with affected packages, and possible their
dependencies and dependents as well.
A set of concrete specs from the active environment including
those associated with affected packages, their dependencies and
dependents, as well as their dependents dependencies.
"""
affected_specs = set()
all_concrete_specs = env.all_specs()
tty.debug("All concrete environment specs:")
for s in all_concrete_specs:
tty.debug(" {0}/{1}".format(s.name, s.dag_hash()[:7]))
for pkg in affected_pkgs:
env_matches = [s for s in all_concrete_specs if s.name == pkg]
for match in env_matches:
affected_specs.add(match)
if dependencies:
affected_specs.update(match.traverse(direction="children", root=False))
if dependents:
affected_specs.update(match.traverse(direction="parents", root=False))
env_matches = [s for s in all_concrete_specs if s.name in frozenset(affected_pkgs)]
visited = set()
dag_hash = lambda s: s.dag_hash()
for match in env_matches:
for parent in match.traverse(direction="parents", key=dag_hash):
affected_specs.update(
parent.traverse(direction="children", visited=visited, key=dag_hash)
)
return affected_specs
@ -625,7 +623,7 @@ def generate_gitlab_ci_yaml(
affected_specs = get_spec_filter_list(env, affected_pkgs)
tty.debug("all affected specs:")
for s in affected_specs:
tty.debug(" {0}".format(s.name))
tty.debug(" {0}/{1}".format(s.name, s.dag_hash()[:7]))
# Allow overriding --prune-dag cli opt with environment variable
prune_dag_override = os.environ.get("SPACK_PRUNE_UP_TO_DATE", None)
@ -848,7 +846,11 @@ def generate_gitlab_ci_yaml(
if prune_untouched_packages:
if release_spec not in affected_specs:
tty.debug("Pruning {0}, untouched by change.".format(release_spec.name))
tty.debug(
"Pruning {0}/{1}, untouched by change.".format(
release_spec.name, release_spec.dag_hash()[:7]
)
)
spec_record["needs_rebuild"] = False
continue

View File

@ -442,13 +442,16 @@ def test_get_spec_filter_list(mutable_mock_env_path, config, mutable_mock_repo):
touched = ["libdwarf"]
# traversing both directions from libdwarf in the graphs depicted
# above results in the following possibly affected env specs:
# mpileaks, callpath, dyninst, libdwarf, and libelf. Unaffected
# specs are mpich, plus hypre and it's dependencies.
# above (and additionally including dependencies of dependents of
# libdwarf) results in the following possibly affected env specs:
# mpileaks, callpath, dyninst, libdwarf, libelf, and mpich.
# Unaffected specs are hypre and it's dependencies.
affected_specs = ci.get_spec_filter_list(e1, touched)
affected_pkg_names = set([s.name for s in affected_specs])
expected_affected_pkg_names = set(["mpileaks", "callpath", "dyninst", "libdwarf", "libelf"])
expected_affected_pkg_names = set(
["mpileaks", "mpich", "callpath", "dyninst", "libdwarf", "libelf"]
)
assert affected_pkg_names == expected_affected_pkg_names

View File

@ -1837,8 +1837,8 @@ def fake_stack_changed(env_path, rev1="HEAD^", rev2="HEAD"):
yaml_contents = syaml.load(contents)
for ci_key in yaml_contents.keys():
if "archive-files" in ci_key or "mpich" in ci_key:
print("Error: archive-files and mpich should have been pruned")
if "archive-files" in ci_key:
print("Error: archive-files should have been pruned")
assert False