Skip to content

Instantly share code, notes, and snippets.

@methylDragon
Last active April 18, 2022 17:58
Show Gist options
  • Select an option

  • Save methylDragon/357080fda22ad73824f42a5288841131 to your computer and use it in GitHub Desktop.

Select an option

Save methylDragon/357080fda22ad73824f42a5288841131 to your computer and use it in GitHub Desktop.
Get topologically sorted level-groups for packages and their dependents on gazebodistro (Place script in gazebodistro root and run)
import yaml
import os
import re
from collections import defaultdict
# PARAMS ===========================================================================================
PATH = "."
BUMP_TARGETS = ["ign-cmake", "ign-tools", "ign-plugin", "ign-utils"]
# PARSE YAMLS ======================================================================================
os.chdir(PATH)
files = [f for f in os.listdir() if os.path.isfile(f)]
# Get library names and corresponding versions from file-list
lib_re = re.compile("(\D*)(\d+).yaml")
lib_tuples = [
(f_re.group(1), int(f_re.group(2)))
for f in files
if (f_re := lib_re.match(f))
]
# Abuse sorting to get max lib versions
lib_tuples.sort()
max_libs_dict = {lib: "".join((os.path.curdir, "/", lib, str(ver), ".yaml"))
for lib, ver in lib_tuples}
# OBTAIN DEPENDANTS ================================================================================
dependant_dict = {target: [] for target in BUMP_TARGETS}
for target in BUMP_TARGETS:
for lib, filename in max_libs_dict.items():
with open(filename, "r") as f:
try:
conf = yaml.safe_load(f)
except yaml.YAMLError as exc:
print(exc)
continue
if target in conf['repositories'].keys():
dependant_dict[target].append(lib)
dependants = set() # Union of all dependants of all target libraries
for dependant_list in dependant_dict.values():
dependants.update(dependant_list)
# TOPO SORT UTILS ==================================================================================
class Graph:
def __init__(self, vertex_count):
self.graph = defaultdict(list) # Adjacency List
self.V_num = vertex_count
def add_edge(self,u,v):
self.graph[u].append(v)
def assignlevel(graph, v, level):
"""
Get topological level of vertex.
Source: https://stackoverflow.com/questions/3420685/how-to-assign-levels-to-vertices-of-an-acyclic-directed-graph
Uses the heuristic of longest directed path length.
The highest level is first in topological order!
"""
if v not in level:
if v not in graph or not graph[v]:
level[v] = 1
else:
level[v] = max(assignlevel(graph, w, level) + 1 for w in graph[v])
return level[v]
# EXECUTE TOPO SORT ================================================================================
g = Graph(len(dependants))
# This dict stores the DOWNSTREAM dependants of each library!
extended_dependant_dict = {parent_dep: [] for parent_dep in dependants} # Deps of deps and targets
for dep in dependants:
for lib, filename in max_libs_dict.items():
with open(filename, "r") as f:
try:
conf = yaml.safe_load(f)
except yaml.YAMLError as exc:
print(exc)
continue
if dep in conf['repositories'].keys():
if lib == dep:
continue
extended_dependant_dict[dep].append(lib)
for lib, dependant_list in extended_dependant_dict.items():
for dependant in dependant_list:
if lib == dependant:
continue
g.add_edge(lib, dependant)
l = {} # Vertex level dict
for v in g.graph:
assignlevel(g.graph, v, l)
topo_groups = [(level, lib) for lib, level in l.items()]
topo_groups.sort()
topo_groups.reverse()
topo_groups
# OUT ==============================================================================================
# The level number of a vertex is the max length of path in the dependency tree starting from it
# Merging PRs from highest level downwards should fix most if not all dependency issues
#
# Vertices on the same level can be merged together
#
# Note: This ordering might not be the most efficient, but it will be safe
# E.g. Merging top level vertices from separate branches would work, but this strategy
# doesn't account for that
topo_group_dict = defaultdict(list)
for level, lib in topo_groups:
topo_group_dict[level].append(lib)
for level, libs in topo_group_dict.items():
print(level, libs)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment