def cli(ctx, wfile, skip, recursive, colors): """ Creates a graph in the .dot format representing the workflow. """ def add_to_graph(dot_str, wf, parent, children, node_attrs, stage_edges): """Recursively goes over the children ("next" attribute) of the given parent, adding an edge from parent to children """ for n in children: edge = ' "{}" -> "{}";\n'.format(parent, n) if edge in stage_edges: continue dot_str += edge + ' "{}" [{}];\n'.format(n, node_attrs) stage_edges.add(edge) for M in wf.get_action(n).get('next', []): dot_str = add_to_graph(dot_str, wf, n, [M], node_attrs, stage_edges) return dot_str wfile_list = list() if recursive: wfile_list = pu.find_recursive_wfile() else: wfile_list.append(pu.find_default_wfile(wfile)) for wfile in wfile_list: wf = Workflow(wfile) wf.parse() wf = Workflow.skip_actions(wf, skip) wf.check_for_unreachable_actions() node_attrs = ('shape=box, style="filled{}", fillcolor=transparent{}') wf_attr = node_attrs.format(',rounded', ',color=red' if colors else '') act_attr = node_attrs.format('', ',color=cyan' if colors else '') dot_str = add_to_graph("", wf, wf.name, wf.root, act_attr, set()) dot_str += ' "{}" [{}];\n'.format(wf.name, wf_attr) log.info("digraph G { graph [bgcolor=transparent];\n" + dot_str + "}\n")
def test_check_for_unreachable_actions(self): self.create_workflow_file(""" workflow "example" { resolves = "end" } action "a" { uses = "sh" args = "ls" } action "b" { uses = "sh" args = "ls" } action "c" { uses = "sh" args = "ls" } action "d" { needs = ["c"] uses = "sh" args = "ls" } action "e" { needs = ["d", "b", "a"] uses = "sh" args = "ls" } action "end" { needs = "e" uses = "sh" args = "ls" } """) wf = Workflow('/tmp/test_folder/a.workflow') wf.parse() changed_wf = Workflow.skip_actions(wf, ['d', 'a', 'b']) self.assertDictEqual( changed_wf.action, { 'a': { 'uses': 'sh', 'args': ['ls'], 'name': 'a', 'next': set() }, 'b': { 'uses': 'sh', 'args': ['ls'], 'name': 'b', 'next': set() }, 'c': { 'uses': 'sh', 'args': ['ls'], 'name': 'c', 'next': set() }, 'd': { 'needs': [], 'uses': 'sh', 'args': ['ls'], 'name': 'd', 'next': set() }, 'e': { 'needs': [], 'uses': 'sh', 'args': ['ls'], 'name': 'e', 'next': {'end'} }, 'end': { 'needs': ['e'], 'uses': 'sh', 'args': ['ls'], 'name': 'end' } }) self.assertRaises(SystemExit, changed_wf.check_for_unreachable_actions, True) self.create_workflow_file(""" workflow "sample" { resolves = ["reachable"] } action "reachable" { uses = "popperized/bin/sh@master" args = "ls" } action "unreachable" { uses = "popperized/bin/sh@master" args = ["ls -ltr"] } """) wf = Workflow('/tmp/test_folder/a.workflow') wf.parse() wf.check_for_unreachable_actions()