def cli(ctx, wfile, recursive): """ Creates a graph in the .dot format representing the workflow """ 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: pipeline = Workflow(wfile, False, False, False, False) graph = list() wf = pipeline.wf workflow_name = list(wf['workflow'].keys())[0] action = wf['resolves'][0] last_action = get_first_action(wf) for act in last_action: graph.append("\t{} -> {};\n".format( workflow_name.replace(' ', '_').replace('-', '_'), act.replace(' ', '_').replace('-', '_'))) parent_action = cur_action = action graph = add(parent_action, cur_action, wf['action'], graph) graph = ''.join(list(set(graph))) graph = "digraph G {\n" + graph + "}\n" pu.info(graph)
def __init__(self, wfile, workspace, quiet, debug, dry_run): wfile = pu.find_default_wfile(wfile) with open(wfile, 'r') as fp: self.wf = hcl.load(fp) self.workspace = workspace self.debug = debug if debug: self.quiet = False else: self.quiet = quiet self.dry_run = dry_run self.actions_cache_path = os.path.join('/', 'tmp', 'actions') self.validate_syntax() self.check_secrets() self.normalize() self.complete_graph() self.env = { 'GITHUB_WORKSPACE': self.workspace, 'GITHUB_WORKFLOW': self.wf['name'], 'GITHUB_ACTOR': 'popper', 'GITHUB_REPOSITORY': '{}/{}'.format(scm.get_user(), scm.get_name()), 'GITHUB_EVENT_NAME': self.wf['on'], 'GITHUB_EVENT_PATH': '/{}/{}'.format(self.workspace, 'workflow/event.json'), 'GITHUB_SHA': scm.get_sha(), 'GITHUB_REF': scm.get_ref() } for e in dict(self.env): self.env.update({e.replace('GITHUB_', 'POPPER_'): self.env[e]})
def cli(ctx, wfile, recursive): """ Creates a graph in the .dot format representing the workflow. """ def add_to_graph(graph_str, wf, parent, children): """Recursively goes through "next" and adds corresponding actions """ _parent = parent.replace(' ', '_').replace('-', '_') for n in children: _n = n.replace(' ', '_').replace('-', '_') graph_str += " {} -> {};\n".format(_parent, _n) for M in wf.get_action(n).get('next', []): graph_str = add_to_graph(graph_str, wf, n, [M]) return graph_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: pipeline = WorkflowRunner(wfile, False, False, False, False, True) wf = pipeline.wf workflow_name = wf.name.replace(' ', '_').replace('-', '_') graph_str = add_to_graph("", wf, workflow_name, wf.root) log.info("digraph G {\n" + graph_str + "}\n")
def run_workflow(**kwargs): kwargs['wfile'] = pu.find_default_wfile(kwargs['wfile']) log.info('Found and running workflow at ' + kwargs['wfile']) # Initialize a Worklow. During initialization all the validation # takes place automatically. wf = Workflow(kwargs['wfile']) wf_runner = WorkflowRunner(wf) # Check for injected actions pre_wfile = os.environ.get('POPPER_PRE_WORKFLOW_PATH') post_wfile = os.environ.get('POPPER_POST_WORKFLOW_PATH') # Saving workflow instance for signal handling popper.cli.interrupt_params['parallel'] = kwargs['parallel'] if kwargs['parallel']: if sys.version_info[0] < 3: log.fail('--parallel is only supported on Python3') log.warning("Using --parallel may result in interleaved output. " "You may use --quiet flag to avoid confusion.") if kwargs['with_dependencies'] and (not kwargs['action']): log.fail('`--with-dependencies` can be used only with ' 'action argument.') if kwargs['skip'] and kwargs['action']: log.fail('`--skip` can\'t be used when action argument ' 'is passed.') on_failure = kwargs.pop('on_failure') wfile = kwargs.pop('wfile') try: if pre_wfile: pre_wf = Workflow(pre_wfile) pre_wf_runner = WorkflowRunner(pre_wf) pre_wf_runner.run(**kwargs) wf_runner.run(**kwargs) if post_wfile: post_wf = Workflow(post_wfile) pre_wf_runner = WorkflowRunner(post_wf) pre_wf_runner.run(**kwargs) except SystemExit as e: if (e.code != 0) and on_failure: kwargs['skip'] = list() kwargs['action'] = on_failure wf_runner.run(**kwargs) else: raise if kwargs['action']: log.info('Action "{}" finished successfully.'.format(kwargs['action'])) else: log.info('Workflow "{}" finished successfully.'.format(wfile))
def test_find_default_wfile(self): os.makedirs('/tmp/test_folder/.github') os.chdir('/tmp/test_folder') self.assertRaises(SystemExit, pu.find_default_wfile, None) self.assertRaises(SystemExit, pu.find_default_wfile, 'a.workflow') self.touch_file('/tmp/test_folder/.github/main.workflow') wfile = pu.find_default_wfile() self.assertEqual(wfile, '.github/main.workflow') shutil.rmtree('/tmp/test_folder')
def __init__(self, wfile, workspace, dry_run, reuse, parallel, skip_secrets_prompt=False): wfile = pu.find_default_wfile(wfile) self.workspace = workspace self.dry_run = dry_run self.reuse = reuse self.parallel = parallel self.skip_secrets_prompt = skip_secrets_prompt self.actions_cache_path = os.path.join('/', 'tmp', 'actions') # Initialize a Worklow. During initialization all the validation # takes place automatically. self.wf = Workflow(wfile) self.check_secrets() log.debug('workflow:\n{}'.format( yaml.dump(self.wf, default_flow_style=False, default_style='')))
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 cli(ctx, action, wfile, skip_clone, skip_pull, skip, workspace, reuse, recursive, quiet, debug, dry_run, parallel, log_file, with_dependencies, on_failure): """Executes one or more pipelines and reports on their status. """ popper.scm.get_git_root_folder() level = 'ACTION_INFO' if quiet: level = 'INFO' if debug: level = 'DEBUG' log.setLevel(level) if log_file: logging.add_log(log, log_file) if os.environ.get('CI') == 'true': log.info("Running in CI environment.") if recursive: log.warning('When CI variable is set, --recursive is ignored.') wfile_list = pu.find_recursive_wfile() wfile_list = workflows_from_commit_message(wfile_list) else: if recursive: if action: log.fail( "An 'action' argument and the --recursive flag cannot be " "both given.") wfile_list = pu.find_recursive_wfile() else: wfile_list = [wfile] if not wfile_list: log.fail("No workflow to execute.") for wfile in wfile_list: wfile = pu.find_default_wfile(wfile) log.info("Found and running workflow at " + wfile) run_pipeline(action, wfile, skip_clone, skip_pull, skip, workspace, reuse, dry_run, parallel, with_dependencies, on_failure)