def task_info(config, task_name): check_project_config(config) task_config = getattr(config.project_config, 'tasks__{}'.format(task_name)) class_path = task_config.get('class_path') task_class = import_class(class_path) # General task info click.echo('Description: {}'.format(task_config.get('description'))) click.echo('Class: {}'.format(task_config.get('class_path'))) # Default options default_options = task_config.get('options', {}) if default_options: click.echo('') click.echo('Default Option Values') for key, value in default_options.items(): click.echo(' {}: {}'.format(key, value)) # Task options task_options = getattr(task_class, 'task_options', {}) if task_options: click.echo('') data = [] headers = ['Option', 'Required', 'Description'] for key, option in task_options.items(): if option.get('required'): data.append((key, '*', option.get('description'))) else: data.append((key, '', option.get('description'))) table = Table(data, headers) click.echo(table)
def doc_task(task_name, task_config, project_config=None, org_config=None): """ Document a (project specific) task configuration in RST format. """ from cumulusci.core.utils import import_class doc = [] doc.append("{}\n==========================================\n".format(task_name)) doc.append("**Description:** {}\n".format(task_config.description)) doc.append("**Class::** {}\n".format(task_config.class_path)) task_class = import_class(task_config.class_path) task_docs = textwrap.dedent(task_class.task_docs.strip("\n")) if task_docs: doc.append(task_docs + "\n") if task_class.task_options: doc.append("Options:\n------------------------------------------\n") defaults = task_config.options or {} for name, option in list(task_class.task_options.items()): default = defaults.get(name) if default: default = " **Default: {}**".format(default) else: default = "" if option.get("required"): doc.append( "* **{}** *(required)*: {}{}".format( name, option.get("description"), default ) ) else: doc.append( "* **{}**: {}{}".format(name, option.get("description"), default) ) return "\n".join(doc)
def doc_task(task_name, task_config, project_config=None, org_config=None): from cumulusci.core.utils import import_class doc = [] doc.append('{}\n==========================================\n'.format(task_name)) doc.append('**Description:** {}\n'.format(task_config.description)) doc.append('**Class::** {}\n'.format(task_config.class_path)) task_class = import_class(task_config.class_path) if task_class.task_options: doc.append('Options:\n------------------------------------------\n') defaults = task_config.options if not defaults: defaults = {} for name, option in task_class.task_options.items(): default = defaults.get('name') if default: default = ' **Default: {}**'.format(default) else: default = '' if option.get('required'): doc.append('* **{}** *(required)*: {}{}'.format(name, option.get('description'), default)) else: doc.append('* **{}**: {}{}'.format(name, option.get('description'), default)) return '\n'.join(doc)
def flow_run(config, flow_name, org): # Check environment check_keychain(config) # Get necessary configs if org: org_config = config.project_config.get_org(org) else: org_config = config.project_config.keychain.get_default_org() flow_config = FlowConfig( getattr(config.project_config, 'flows__{}'.format(flow_name)) ) if not flow_config.config: raise click.UsageError('No configuration fould for flow {}'.format(flow_name)) # Get the class to look up options class_path = flow_config.config.get('class_path', 'cumulusci.core.flows.BaseFlow') flow_class = import_class(class_path) # Create the flow and handle initialization exceptions try: flow = flow_class(config.project_config, flow_config, org_config) except TaskRequiresSalesforceOrg as e: raise click.UsageError('This flow requires a salesforce org. Use org default <name> to set a default org or pass the org name with the --org option') except TaskOptionsError as e: raise click.UsageError(e.message) # Run the flow and handle exceptions try: flow() except TaskOptionsError as e: raise click.UsageError(e.message) except Exception as e: raise click.ClickException('{}: {}'.format(e.__class__.__name__, e.message))
def get_keychain_class(self): default_keychain_class = (self.project_config.cumulusci__keychain if not self.is_global_keychain else self.global_config.cumulusci__keychain) keychain_class = os.environ.get("CUMULUSCI_KEYCHAIN_CLASS", default_keychain_class) return import_class(keychain_class)
def doc_task(task_name, task_config, project_config=None, org_config=None): """ Document a (project specific) task configuration in RST format. """ from cumulusci.core.utils import import_class doc = [] doc.append( '{}\n==========================================\n'.format(task_name)) doc.append('**Description:** {}\n'.format(task_config.description)) doc.append('**Class::** {}\n'.format(task_config.class_path)) task_class = import_class(task_config.class_path) if task_class.task_options: doc.append('Options:\n------------------------------------------\n') defaults = task_config.options if not defaults: defaults = {} for name, option in list(task_class.task_options.items()): default = defaults.get(name) if default: default = ' **Default: {}**'.format(default) else: default = '' if option.get('required'): doc.append('* **{}** *(required)*: {}{}'.format( name, option.get('description'), default)) else: doc.append('* **{}**: {}{}'.format(name, option.get('description'), default)) return '\n'.join(doc)
def task_run(config, task_name, org, o, debug, debug_before, debug_after, no_prompt): # Get necessary configs org, org_config = config.get_org(org, fail_if_missing=False) task_config = getattr(config.project_config, "tasks__{}".format(task_name)) if not task_config: raise TaskNotFoundError("Task not found: {}".format(task_name)) # Get the class to look up options class_path = task_config.get("class_path") task_class = import_class(class_path) # Parse command line options and add to task config if o: if "options" not in task_config: task_config["options"] = {} for name, value in o: # Validate the option if name not in task_class.task_options: raise click.UsageError( 'Option "{}" is not available for task {}'.format( name, task_name)) # Override the option in the task config task_config["options"][name] = value task_config = TaskConfig(task_config) # Create and run the task try: task = task_class(config.project_config, task_config, org_config=org_config) if debug_before: import pdb pdb.set_trace() task() if debug_after: import pdb pdb.set_trace() except CumulusCIUsageError as e: # Usage error; report with usage line and no traceback exception = click.UsageError(str(e)) handle_exception_debug(config, debug, throw_exception=exception) except (CumulusCIFailure, ScratchOrgException) as e: # Expected failure; report without traceback exception = click.ClickException(str(e) or e.__class__.__name__) handle_exception_debug(config, debug, throw_exception=exception) except Exception: # Unexpected exception; log to sentry and raise handle_exception_debug(config, debug, no_prompt=no_prompt) config.alert("Task complete: {}".format(task_name))
def run_flow(self, project_config, org_config): # Add the repo root to syspath to allow for custom tasks and flows in # the repo sys.path.append(project_config.repo_root) flow = getattr(project_config, 'flows__{}'.format(self.flow)) if not flow: raise FlowNotFoundError('Flow not found: {}'.format(self.flow)) flow_config = FlowConfig(flow) if settings.METACI_FLOW_SUBCLASS_ENABLED: class_path = 'metaci.build.flows.MetaCIFlow' else: # Get the class to look up options class_path = flow_config.config.get( 'class_path', 'cumulusci.core.flows.BaseFlow') flow_class = import_class(class_path) # Create the flow and handle initialization exceptions self.flow_instance = flow_class(project_config, flow_config, org_config, name=self.flow) if settings.METACI_FLOW_SUBCLASS_ENABLED: self.flow_instance.buildflow_id = self.pk # Run the flow res = self.flow_instance() return res
def _load_keychain(self): self.keychain_key = os.environ.get('CUMULUSCI_KEY') if self.project_config: keychain_class = os.environ.get( 'CUMULUSCI_KEYCHAIN_CLASS', self.project_config.cumulusci__keychain, ) self.keychain_class = import_class(keychain_class) self.keychain = self.keychain_class(self.project_config, self.keychain_key) self.project_config.set_keychain(self.keychain)
def _load_keychain(self): self.keychain_key = os.environ.get("CUMULUSCI_KEY") if self.project_config: keychain_class = os.environ.get( "CUMULUSCI_KEYCHAIN_CLASS", self.project_config.cumulusci__keychain) self.keychain_class = import_class(keychain_class) self.keychain = self.keychain_class(self.project_config, self.keychain_key) self.project_config.set_keychain(self.keychain)
def _run_task(self, flow_task_config): task_config = copy.deepcopy(flow_task_config['task_config'].config) task_config = TaskConfig(task_config) task_name = flow_task_config['flow_config']['task'] if 'options' not in task_config.config: task_config.config['options'] = {} task_config.config['options'].update( flow_task_config['flow_config'].get('options', {})) # If there were task option overrides passed in, merge them if task_name in self.task_options: task_config.config['options'].update(self.task_options[task_name]) # Skip the task if skip was requested if task_name in self.skip_tasks: self.logger.info('') self.logger.info('Skipping task {}'.format(task_name)) return # Handle dynamic value lookups in the format ^^task_name.attr1.attr2 for option, value in task_config.options.items(): if unicode(value).startswith('^^'): value_parts = value[2:].split('.') parent = self._find_task_by_name(value_parts[0]) for attr in value_parts[1:]: parent = parent.return_values.get(attr) task_config.config['options'][option] = parent task_class = import_class(task_config.class_path) self.logger.info('') self.logger.info('Running task: %s', task_name) task = task_class(self.project_config, task_config, org_config=self.org_config, flow=self) self.tasks.append(task) for line in self._render_task_config(task): self.logger.info(line) try: task() self.logger.info('Task complete: %s', task_name) self.task_results.append(task.result) self.task_return_values.append(task.return_values) except Exception as e: self.logger.error('Task failed: %s', task_name) if not flow_task_config['flow_config'].get('ignore_failure'): self.logger.error('Failing flow due to exception in task') traceback.print_exc() raise e self.logger.info('Continuing flow')
def to_spec(self, skip: bool = False): task_class = import_class(self.task_class) assert issubclass(task_class, BaseTask) return StepSpec( step_num=self.step_num, task_name=self. path, # skip from_flow path construction in StepSpec ctr task_config=self.task_config or {"options": {}}, task_class=task_class, skip=skip, )
def task_run(config, task_name, org, o): # Check environment check_keychain(config) # Get necessary configs if org: org_config = config.project_config.get_org(org) else: org_config = config.project_config.keychain.get_default_org() task_config = getattr(config.project_config, 'tasks__{}'.format(task_name)) # Get the class to look up options class_path = task_config.get('class_path') task_class = import_class(class_path) # Parse command line options and add to task config if o: if 'options' not in task_config: task_config['options'] = {} for option in o: name = option[0] value = option[1] # Validate the option if name not in task_class.task_options: raise click.UsageError( 'Option "{}" is not available for task {}'.format( name, task_name, ), ) # Override the option in the task config task_config['options'][name] = value task_config = TaskConfig(task_config) # Create and run the task try: task = task_class(config.project_config, task_config, org_config = org_config) except TaskRequiresSalesforceOrg as e: raise click.UsageError('This task requires a salesforce org. Use org default <name> to set a default org or pass the org name with the --org option') except TaskOptionsError as e: raise click.UsageError(e.message) except Exception as e: raise click.ClickException('{}: {}'.format(e.__class__.__name__, e.message)) try: task() except TaskOptionsError as e: raise click.UsageError(e.message) except Exception as e: raise click.ClickException('{}: {}'.format(e.__class__.__name__, unicode(e)))
def _run_task(self, flow_task_config): task_config = self.project_config.get_task(flow_task_config['task']) task_config = copy.deepcopy(task_config.config) task_config = TaskConfig(task_config) if flow_task_config: if 'options' not in task_config.config: task_config.config['options'] = {} task_config.config['options'].update(flow_task_config.get('options', {})) # Handle dynamic value lookups in the format ^^task_name.attr1.attr2 for option, value in task_config.options.items(): if unicode(value).startswith('^^'): value_parts = value[2:].split('.') task_name = value_parts[0] parent = self._find_task_by_name(task_name) for attr in value_parts[1:]: parent = getattr(parent, attr) task_config.config['options'][option] = parent task_class = import_class(task_config.class_path) self.logger.info('') self.logger.info( 'Running task: {}'.format( flow_task_config['task'], ) ) task = task_class( self.project_config, task_config, org_config = self.org_config, ) self.tasks.append(task) for line in self._render_task_config(task): self.logger.info(line) try: response = task() self.logger.info('Task complete: {}'.format(flow_task_config['task'])) self.responses.append(response) return response except: self.logger.error('Task failed: {}'.format(flow_task_config['task'])) if not flow_task_config.get('ignore_failure'): self.logger.info('Aborting flow') raise self.logger.info('Continuing flow')
def to_spec(self, project_config, skip: bool = False): if self.source: project_config = project_config.include_source(self.source) task_class = import_class(self.task_class) assert issubclass(task_class, BaseTask) return StepSpec( step_num=self.step_num, task_name=self.path, # skip from_flow path construction in StepSpec ctr task_config=self.task_config or {"options": {}}, task_class=task_class, skip=skip, project_config=project_config, )
def _load_keychain(self): self.keychain_key = os.environ.get("CUMULUSCI_KEY") if self.project_config: keychain_class = os.environ.get( "CUMULUSCI_KEYCHAIN_CLASS", self.project_config.cumulusci__keychain ) self.keychain_class = import_class(keychain_class) try: self.keychain = self.keychain_class( self.project_config, self.keychain_key ) except (KeychainKeyNotFound, ConfigError) as e: raise click.UsageError("Keychain Error: {}".format(str(e))) self.project_config.set_keychain(self.keychain)
def run_flow(self, project_config, org_config): flow = getattr(project_config, 'flows__{}'.format(self.flow)) if not flow: raise FlowNotFoundError('Flow not found: {}'.format(flow_name)) flow_config = FlowConfig(flow) # Get the class to look up options class_path = flow_config.config.get('class_path', 'cumulusci.core.flows.BaseFlow') flow_class = import_class(class_path) # Create the flow and handle initialization exceptions self.flow_instance = flow_class(project_config, flow_config, org_config) res = self.flow_instance()
def _run_task(self, flow_task_config): task_config = copy.deepcopy(flow_task_config['task_config'].config) task_config = TaskConfig(task_config) task_name = flow_task_config['flow_config']['task'] if 'options' not in task_config.config: task_config.config['options'] = {} task_config.config['options'].update(flow_task_config['flow_config'].get('options', {})) # Handle dynamic value lookups in the format ^^task_name.attr1.attr2 for option, value in task_config.options.items(): if unicode(value).startswith('^^'): value_parts = value[2:].split('.') parent = self._find_task_by_name(value_parts[0]) for attr in value_parts[1:]: parent = getattr(parent, attr) task_config.config['options'][option] = parent task_class = import_class(task_config.class_path) self.logger.info('') self.logger.info( 'Running task: {}'.format(task_name) ) task = task_class( self.project_config, task_config, org_config = self.org_config, ) self.tasks.append(task) for line in self._render_task_config(task): self.logger.info(line) try: response = task() self.logger.info('Task complete: {}'.format(task_name)) self.responses.append(response) return response except Exception as e: self.logger.error('Task failed: {}'.format(task_name)) if not flow_task_config['flow_config'].get('ignore_failure'): self.logger.error('Failing flow due to exception in task') raise self.logger.info('Continuing flow')
def _run_flow(self, stepnum, flow_task_config): class_path = flow_task_config['task_config'].config.get( 'class_path', 'cumulusci.core.flows.BaseFlow', ) flow_class = import_class(class_path) flow = flow_class( self.project_config, flow_task_config['task_config'], self.org_config, options=self.options, skip=self.skip, nested=True, ) flow() self.tasks.append(flow) self.task_return_values.append(flow.task_return_values)
def _get_task(self, stepnum, step_config): task_config = copy.deepcopy(step_config["task_config"].config) task_config = TaskConfig(task_config) task_name = step_config["step_config"]["task"] if "options" not in task_config.config: task_config.config["options"] = {} task_config.config["options"].update(step_config["step_config"].get( "options", {})) # If there were task option overrides passed in, merge them if task_name in self.task_options: task_config.config["options"].update(self.task_options[task_name]) # Handle dynamic value lookups in the format ^^task_name.attr1.attr2 for option, value in list(task_config.options.items()): if str(value).startswith("^^"): value_parts = value[2:].split(".") parent = self._find_step_by_name(value_parts[0]) n = 0 while isinstance(parent, BaseFlow): n += 1 parent = parent._find_step_by_name(value_parts[n]) for attr in value_parts[(n + 1):]: if getattr(parent, "nested", None): parent = parent._find_step_by_name() else: parent = parent.return_values.get(attr) task_config.config["options"][option] = parent task_class = import_class(task_config.class_path) task = task_class( self.project_config, task_config, org_config=self.org_config, name=task_name, stepnum=stepnum, flow=self, ) return task
def _run_flow(self, stepnum, step_config): class_path = step_config['flow_config'].config.get( 'class_path', 'cumulusci.core.flows.BaseFlow', ) flow_options = step_config['step_config'].get( 'options', {}, ) if flow_options: # Collapse down flow options into task__option format to pass options = {} for task, task_options in flow_options.items(): for option, value in task_options.items(): options['{}__{}'.format(task, option)] = value flow_options = options flow_class = import_class(class_path) flow = flow_class(self.project_config, step_config['flow_config'], self.org_config, options=flow_options, skip=self.skip, nested=True, parent=self, name=step_config['step_config']['flow'], stepnum=stepnum) self._pre_subflow(flow) if self._skip_next: self._skip_next = False return flow() self._post_subflow(flow) self.steps.append(flow) self.step_return_values.append(flow.step_return_values)
def task_info(config, task_name): check_project_config(config) task_config = getattr(config.project_config, 'tasks__{}'.format(task_name)) if not task_config: raise TaskNotFoundError('Task not found: {}'.format(task_name)) task_config = TaskConfig(task_config) click.echo(rst2ansi(doc_task(task_name, task_config))) return class_path = task_config.get('class_path') task_class = import_class(class_path) # General task info click.echo('Description: {}'.format(task_config.get('description'))) click.echo('Class: {}'.format(task_config.get('class_path'))) # Default options default_options = task_config.get('options', {}) if default_options: click.echo('') click.echo('Default Option Values') for key, value in default_options.items(): click.echo(' {}: {}'.format(key, value)) # Task options task_options = getattr(task_class, 'task_options', {}) if task_options: click.echo('') data = [] headers = ['Option', 'Required', 'Description'] for key, option in task_options.items(): if option.get('required'): data.append((key, '*', option.get('description'))) else: data.append((key, '', option.get('description'))) table = Table(data, headers) click.echo(table)
def _init_parsers(self): for cfg in self.parser_config: parser_class = import_class(cfg["class_path"]) self.parsers.append(parser_class(self, cfg["title"]))
def _visit_step( self, number, step_config, visited_steps=None, parent_options=None, from_flow=None, ): """ for each step (as defined in the flow YAML), _visit_step is called with only the first two parameters. this takes care of validating the step, collating the option overrides, and if it is a task, creating a StepSpec for it. If it is a flow, we recursively call _visit_step with the rest of the parameters of context. :param number: LooseVersion representation of the current step number :param step_config: the current step's config (dict from YAML) :param visited_steps: used when called recursively for nested steps, becomes the return value :param parent_options: used when called recursively for nested steps, options from parent flow :param from_flow: used when called recursively for nested steps, name of parent flow :return: List[StepSpec] a list of all resolved steps including/under the one passed in """ number = LooseVersion(str(number)) if visited_steps is None: visited_steps = [] if parent_options is None: parent_options = {} # Step Validation # - A step is either a task OR a flow. if all(k in step_config for k in ("flow", "task")): raise FlowConfigError( "Step {} is configured as both a flow AND a task. \n\t{}.". format(number, step_config)) # Skips # - either in YAML (with the None string) # - or by providing a skip list to the FlowRunner at initialization. if (("flow" in step_config and step_config["flow"] == "None") or ("task" in step_config and step_config["task"] == "None") or ("task" in step_config and step_config["task"] in self.skip)): visited_steps.append( StepSpec( number, step_config.get("task", step_config.get("flow")), step_config.get("options", {}), None, from_flow=from_flow, skip= True, # someday we could use different vals for why skipped )) return visited_steps if "task" in step_config: name = step_config["task"] # get the base task_config from the project config, as a dict for easier manipulation. # will raise if the task doesn't exist / is invalid task_config = copy.deepcopy( self.project_config.get_task(name).config) if "options" not in task_config: task_config["options"] = {} # merge the options together, from task_config all the way down through parent_options step_overrides = copy.deepcopy(parent_options.get(name, {})) step_overrides.update(step_config.get("options", {})) task_config["options"].update(step_overrides) # merge runtime options if name in self.runtime_options: task_config["options"].update(self.runtime_options[name]) # get implementation class. raise/fail if it doesn't exist, because why continue try: task_class = import_class(task_config["class_path"]) except (ImportError, AttributeError): # TODO: clean this up and raise a taskimporterror or something else correcter. raise FlowConfigError("Task named {} has bad classpath") visited_steps.append( StepSpec( number, name, task_config, task_class, step_config.get("ignore_failure", False), from_flow=from_flow, )) return visited_steps if "flow" in step_config: name = step_config["flow"] if from_flow: path = ".".join([from_flow, name]) else: path = name step_options = step_config.get("options", {}) flow_config = self.project_config.get_flow(name) for sub_number, sub_stepconf in flow_config.steps.items(): # append the flow number to the child number, since its a LooseVersion. # e.g. if we're in step 2.3 which references a flow with steps 1-5, it # simply ends up as five steps: 2.3.1, 2.3.2, 2.3.3, 2.3.4, 2.3.5 # TODO: how does this work with nested flowveride? what does defining step 2.3.2 later do? num = "{}.{}".format(number, sub_number) self._visit_step( num, sub_stepconf, visited_steps, parent_options=step_options, from_flow=path, ) return visited_steps
def get_keychain_class(self): keychain_class = os.environ.get( "CUMULUSCI_KEYCHAIN_CLASS", self.project_config.cumulusci__keychain) return import_class(keychain_class)
def flow_run(config, flow_name, org, delete_org, debug, o, skip, no_prompt): # Check environment check_keychain(config) # Get necessary configs if org: org_config = config.project_config.get_org(org) else: org, org_config = config.project_config.keychain.get_default_org() if not org_config: raise click.UsageError( '`cci flow run` requires an org.' ' No org was specified and default org is not set.') org_config = check_org_expired(config, org, org_config) if delete_org and not org_config.scratch: raise click.UsageError( '--delete-org can only be used with a scratch org') flow = getattr(config.project_config, 'flows__{}'.format(flow_name)) if not flow: raise FlowNotFoundError('Flow not found: {}'.format(flow_name)) flow_config = FlowConfig(flow) if not flow_config.config: raise click.UsageError( 'No configuration found for flow {}'.format(flow_name)) # Get the class to look up options class_path = flow_config.config.get('class_path', 'cumulusci.core.flows.BaseFlow') flow_class = import_class(class_path) exception = None # Parse command line options and add to task config options = {} if o: for option in o: options[option[0]] = option[1] # Create the flow and handle initialization exceptions try: flow = flow_class(config.project_config, flow_config, org_config, options, skip, name=flow_name) except TaskRequiresSalesforceOrg as e: exception = click.UsageError( 'This flow requires a salesforce org. Use org default <name> to set a default org or pass the org name with the --org option' ) except TaskOptionsError as e: exception = click.UsageError(e.message) handle_exception_debug(config, debug, e, throw_exception=exception) except Exception as e: handle_exception_debug(config, debug, e, no_prompt=no_prompt) if not exception: # Run the flow and handle exceptions try: flow() except TaskOptionsError as e: exception = click.UsageError(e.message) handle_exception_debug(config, debug, e, throw_exception=exception) except ApexTestException as e: exception = click.ClickException('Failed: ApexTestException') handle_exception_debug(config, debug, e, throw_exception=exception) except BrowserTestFailure as e: exception = click.ClickException('Failed: BrowserTestFailure') handle_exception_debug(config, debug, e, throw_exception=exception) except MetadataComponentFailure as e: exception = click.ClickException( 'Failed: MetadataComponentFailure') handle_exception_debug(config, debug, e, throw_exception=exception) except MetadataApiError as e: exception = click.ClickException('Failed: MetadataApiError') handle_exception_debug(config, debug, e, throw_exception=exception) except ScratchOrgException as e: exception = click.ClickException('ScratchOrgException: {}'.format( e.message)) handle_exception_debug(config, debug, e, throw_exception=exception) except Exception as e: handle_exception_debug(config, debug, e, no_prompt=no_prompt) # Delete the scratch org if --delete-org was set if delete_org: try: org_config.delete_org() except Exception as e: click.echo( 'Scratch org deletion failed. Ignoring the error below to complete the flow:' ) click.echo(e.message) if exception: handle_sentry_event(config, no_prompt) raise exception
def task_run(config, task_name, org, o, debug, debug_before, debug_after, no_prompt): # Check environment check_keychain(config) # Get necessary configs if org: org_config = config.project_config.get_org(org) else: org, org_config = config.project_config.keychain.get_default_org() if org_config: org_config = check_org_expired(config, org, org_config) task_config = getattr(config.project_config, 'tasks__{}'.format(task_name)) if not task_config: raise TaskNotFoundError('Task not found: {}'.format(task_name)) # Get the class to look up options class_path = task_config.get('class_path') task_class = import_class(class_path) # Parse command line options and add to task config if o: if 'options' not in task_config: task_config['options'] = {} for option in o: name = option[0] value = option[1] # Validate the option if name not in task_class.task_options: raise click.UsageError( 'Option "{}" is not available for task {}'.format( name, task_name, ), ) # Override the option in the task config task_config['options'][name] = value task_config = TaskConfig(task_config) exception = None # Create and run the task try: task = task_class(config.project_config, task_config, org_config=org_config) except TaskRequiresSalesforceOrg as e: exception = click.UsageError( 'This task requires a salesforce org. Use org default <name> to set a default org or pass the org name with the --org option' ) except TaskOptionsError as e: exception = click.UsageError(e.message) handle_exception_debug(config, debug, e, throw_exception=exception) except Exception as e: handle_exception_debug(config, debug, e, no_prompt=no_prompt) if debug_before: import pdb pdb.set_trace() if not exception: try: task() except TaskOptionsError as e: exception = click.UsageError(e.message) handle_exception_debug(config, debug, e, throw_exception=exception) except ApexTestException as e: exception = click.ClickException('Failed: ApexTestFailure') handle_exception_debug(config, debug, e, throw_exception=exception) except BrowserTestFailure as e: exception = click.ClickException('Failed: BrowserTestFailure') handle_exception_debug(config, debug, e, throw_exception=exception) except MetadataComponentFailure as e: exception = click.ClickException( 'Failed: MetadataComponentFailure') handle_exception_debug(config, debug, e, throw_exception=exception) except MetadataApiError as e: exception = click.ClickException('Failed: MetadataApiError') handle_exception_debug(config, debug, e, throw_exception=exception) except ScratchOrgException as e: exception = click.ClickException('ScratchOrgException: {}'.format( e.message)) handle_exception_debug(config, debug, e, throw_exception=exception) except Exception as e: handle_exception_debug(config, debug, e, no_prompt=no_prompt) if debug_after: import pdb pdb.set_trace() if exception: handle_sentry_event(config, no_prompt) raise exception
def flow_run(config, flow_name, org, delete_org, debug): # Check environment check_keychain(config) # Get necessary configs if org: org_config = config.project_config.get_org(org) else: org, org_config = config.project_config.keychain.get_default_org() if delete_org and not org_config.scratch: raise click.UsageError( '--delete-org can only be used with a scratch org') flow = getattr(config.project_config, 'flows__{}'.format(flow_name)) if not flow: raise FlowNotFoundError('Flow not found: {}'.format(flow_name)) flow_config = FlowConfig(flow) if not flow_config.config: raise click.UsageError( 'No configuration found for flow {}'.format(flow_name)) # Get the class to look up options class_path = flow_config.config.get('class_path', 'cumulusci.core.flows.BaseFlow') flow_class = import_class(class_path) exception = None # Create the flow and handle initialization exceptions try: flow = flow_class(config.project_config, flow_config, org_config) except TaskRequiresSalesforceOrg as e: exception = click.UsageError( 'This flow requires a salesforce org. Use org default <name> to set a default org or pass the org name with the --org option' ) except TaskOptionsError as e: exception = click.UsageError(e.message) except Exception as e: if debug: import pdb import traceback traceback.print_exc() pdb.post_mortem() else: raise if not exception: # Run the flow and handle exceptions try: flow() except TaskOptionsError as e: exception = click.UsageError(e.message) except ApexTestException as e: exception = click.ClickException('Failed: ApexTestException') except MetadataComponentFailure as e: exception = click.ClickException( 'Failed: MetadataComponentFailure') except MetadataApiError as e: exception = click.ClickException('Failed: MetadataApiError') except Exception as e: if debug: import pdb import traceback traceback.print_exc() pdb.post_mortem() else: raise # Delete the scratch org if --delete-org was set if delete_org: try: org_config.delete_org() except Exception as e: click.echo( 'Scratch org deletion failed. Ignoring the error below to complete the flow:' ) click.echo(e.message) # Save the org config in case it was modified in a task if org and org_config: config.keychain.set_org(org, org_config) if exception: raise exception
def flow_run(config, flow_name, org, delete_org, debug): # Check environment check_keychain(config) # Get necessary configs if org: org_config = config.project_config.get_org(org) else: org, org_config = config.project_config.keychain.get_default_org() if delete_org and not org_config.scratch: raise click.UsageError('--delete-org can only be used with a scratch org') flow = getattr(config.project_config, 'flows__{}'.format(flow_name)) if not flow: raise FlowNotFoundError('Flow not found: {}'.format(flow_name)) flow_config = FlowConfig(flow) if not flow_config.config: raise click.UsageError('No configuration found for flow {}'.format(flow_name)) # Get the class to look up options class_path = flow_config.config.get('class_path', 'cumulusci.core.flows.BaseFlow') flow_class = import_class(class_path) exception = None # Create the flow and handle initialization exceptions try: flow = flow_class(config.project_config, flow_config, org_config) except TaskRequiresSalesforceOrg as e: exception = click.UsageError('This flow requires a salesforce org. Use org default <name> to set a default org or pass the org name with the --org option') except TaskOptionsError as e: exception = click.UsageError(e.message) except Exception as e: if debug: import pdb import traceback traceback.print_exc() pdb.post_mortem() else: raise if not exception: # Run the flow and handle exceptions try: flow() except TaskOptionsError as e: exception = click.UsageError(e.message) except ApexTestException as e: exception = click.ClickException('Failed: ApexTestException') except MetadataApiError as e: exception = click.ClickException('Failed: MetadataApiError') except Exception as e: if debug: import pdb import traceback traceback.print_exc() pdb.post_mortem() else: raise # Delete the scratch org if --delete-org was set if delete_org: try: org_config.delete_org() except Exception as e: click.echo('Scratch org deletion failed. Ignoring the error below to complete the flow:') click.echo(e.message) # Save the org config in case it was modified in a task if org and org_config: config.keychain.set_org(org, org_config) if exception: raise exception
def task_run(config, task_name, org, o, debug, debug_before, debug_after, no_prompt): # Check environment config.check_keychain() # Get necessary configs org, org_config = config.get_org(org, fail_if_missing=False) task_config = getattr(config.project_config, "tasks__{}".format(task_name)) if not task_config: raise TaskNotFoundError("Task not found: {}".format(task_name)) # Get the class to look up options class_path = task_config.get("class_path") task_class = import_class(class_path) # Parse command line options and add to task config if o: if "options" not in task_config: task_config["options"] = {} for name, value in o: # Validate the option if name not in task_class.task_options: raise click.UsageError( 'Option "{}" is not available for task {}'.format( name, task_name)) # Override the option in the task config task_config["options"][name] = value task_config = TaskConfig(task_config) # Create and run the task try: task = task_class(config.project_config, task_config, org_config=org_config) if debug_before: import pdb pdb.set_trace() task() if debug_after: import pdb pdb.set_trace() except (TaskRequiresSalesforceOrg, TaskOptionsError) as e: # Usage error; report with usage line and no traceback exception = click.UsageError(e.message) handle_exception_debug(config, debug, throw_exception=exception) except ( ApexTestException, BrowserTestFailure, MetadataComponentFailure, MetadataApiError, ScratchOrgException, ) as e: # Expected failure; report without traceback exception = click.ClickException("Failed: {}".format( e.__class__.__name__)) handle_exception_debug(config, debug, throw_exception=exception) except Exception: # Unexpected exception; log to sentry and raise handle_exception_debug(config, debug, no_prompt=no_prompt)
def task_run(config, task_name, org, o, debug): # Check environment check_keychain(config) # Get necessary configs if org: org_config = config.project_config.get_org(org) else: org, org_config = config.project_config.keychain.get_default_org() task_config = getattr(config.project_config, 'tasks__{}'.format(task_name)) if not task_config: raise TaskNotFoundError('Task not found: {}'.format(task_name)) # Get the class to look up options class_path = task_config.get('class_path') task_class = import_class(class_path) # Parse command line options and add to task config if o: if 'options' not in task_config: task_config['options'] = {} for option in o: name = option[0] value = option[1] # Validate the option if name not in task_class.task_options: raise click.UsageError( 'Option "{}" is not available for task {}'.format( name, task_name, ), ) # Override the option in the task config task_config['options'][name] = value task_config = TaskConfig(task_config) exception = None # Create and run the task try: task = task_class(config.project_config, task_config, org_config = org_config) except TaskRequiresSalesforceOrg as e: exception = click.UsageError('This task requires a salesforce org. Use org default <name> to set a default org or pass the org name with the --org option') except TaskOptionsError as e: exception = click.UsageError(e.message) except Exception as e: if debug: import pdb import traceback traceback.print_exc() pdb.post_mortem() else: raise if not exception: try: task() except TaskOptionsError as e: exception = click.UsageError(e.message) except ApexTestException as e: exception = click.ClickException('Failed: ApexTestFailure') except MetadataApiError as e: exception = click.ClickException('Failed: MetadataApiError') except Exception as e: if debug: import pdb import traceback traceback.print_exc() pdb.post_mortem() else: raise # Save the org config in case it was modified in the task if org and org_config: config.keychain.set_org(org, org_config) if exception: raise exception
def _init_task(self, class_path, options, task_config): task_class = import_class(class_path) task_config = self._parse_task_options(options, task_class, task_config) return task_class, task_config
def task_run(config, task_name, org, o, debug): # Check environment check_keychain(config) # Get necessary configs if org: org_config = config.project_config.get_org(org) else: org, org_config = config.project_config.keychain.get_default_org() task_config = getattr(config.project_config, 'tasks__{}'.format(task_name)) if not task_config: raise TaskNotFoundError('Task not found: {}'.format(task_name)) # Get the class to look up options class_path = task_config.get('class_path') task_class = import_class(class_path) # Parse command line options and add to task config if o: if 'options' not in task_config: task_config['options'] = {} for option in o: name = option[0] value = option[1] # Validate the option if name not in task_class.task_options: raise click.UsageError( 'Option "{}" is not available for task {}'.format( name, task_name, ), ) # Override the option in the task config task_config['options'][name] = value task_config = TaskConfig(task_config) exception = None # Create and run the task try: task = task_class(config.project_config, task_config, org_config=org_config) except TaskRequiresSalesforceOrg as e: exception = click.UsageError( 'This task requires a salesforce org. Use org default <name> to set a default org or pass the org name with the --org option' ) except TaskOptionsError as e: exception = click.UsageError(e.message) except Exception as e: if debug: import pdb import traceback traceback.print_exc() pdb.post_mortem() else: raise if not exception: try: task() except TaskOptionsError as e: exception = click.UsageError(e.message) except ApexTestException as e: exception = click.ClickException('Failed: ApexTestFailure') except MetadataComponentFailure as e: exception = click.ClickException( 'Failed: MetadataComponentFailure') except MetadataApiError as e: exception = click.ClickException('Failed: MetadataApiError') except Exception as e: if debug: import pdb import traceback traceback.print_exc() pdb.post_mortem() else: raise # Save the org config in case it was modified in the task if org and org_config: config.keychain.set_org(org, org_config) if exception: raise exception