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 test_run_task__failed_class_level_no_symboltable__spring20_managed(self): self._mock_apex_class_query(name="ns__Test_TEST", namespace="ns") self._mock_run_tests() self._mock_get_failed_test_classes_failure() self._mock_tests_complete() self._mock_get_test_results() self._mock_get_symboltable_failure() task_config = TaskConfig() task_config.config["options"] = { "junit_output": "results_junit.xml", "poll_interval": 1, "test_name_match": "%_TEST", "managed": True, "namespace": "ns", } task = RunApexTests(self.project_config, task_config, self.org_config) task._get_test_methods_for_class = Mock() task() task._get_test_methods_for_class.assert_not_called()
def task_doc(config): config_src = config.global_config click.echo("==========================================") click.echo("Tasks Reference") click.echo("==========================================") click.echo("") for name, options in list(config_src.tasks.items()): task_config = TaskConfig(options) doc = doc_task(name, task_config) click.echo(doc) click.echo("")
def _make_task(task_class, task_config): task_config = TaskConfig(task_config) universal_config = UniversalConfig() project_config = BaseProjectConfig( universal_config, config={"noyaml": True, "project": {"package": {"api_version": "46.0"}}}, ) keychain = BaseProjectKeychain(project_config, "") project_config.set_keychain(keychain) org_config = DummyOrgConfig( {"instance_url": "https://example.com", "access_token": "abc123"}, "test" ) return task_class(project_config, task_config, org_config)
def test_retry_on_exception(self): """ calling _retry() should call try until the task succeeds. """ task_config = TaskConfig({ "options": { "retries": 5, "retry_interval": 1, "retry_interval_add": 1 } }) task = BaseTask(self.project_config, task_config, self.org_config) task._try = mock.Mock(side_effect=[Exception, Exception, 1]) task._retry() self.assertEqual(task._try.call_count, 3)
def setUp(self): self.global_config = BaseGlobalConfig() self.project_config = BaseProjectConfig(self.global_config, config={"noyaml": True}) self.task_config = TaskConfig() self._task_log_handler.reset() self.task_log = self._task_log_handler.messages self.Popen = MockPopen() self.r = Replacer() self.r.replace("cumulusci.tasks.command.subprocess.Popen", self.Popen) self.addCleanup(self.r.restore)
def test_run_task(self, CommitDir): task_config = TaskConfig() task = CommitApexDocs(self.project_config, task_config) commit_dir = mock.Mock() CommitDir.return_value = commit_dir task() commit_dir.assert_called_once_with( os.path.join("docs", "ApexDocumentation"), "master", "docs", "Update Apex docs", False, )
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 setUp(self): self.global_config = BaseGlobalConfig() self.project_config = BaseProjectConfig(self.global_config, config={"noyaml": True}) self.task_config = TaskConfig({"options": {"command": "ls"}}) self.org_config = OrgConfig( { "access_token": "TOKEN", "instance_url": "https://na01.salesforce.com" }, "test", ) self.org_config.refresh_oauth_token = mock.Mock()
def test_tag_not_found(self, commit_dir, extract_github): responses.add( method=responses.GET, url=self.repo_api_url, json=self._get_expected_repo(owner=self.repo_owner, name=self.repo_name), ) responses.add( responses.GET, self.repo_api_url + "/releases/latest", json=self._get_expected_release("release/1.0"), ) responses.add( method=responses.GET, url=self.public_repo_url, json=self._get_expected_repo( owner=self.public_owner, name=self.public_name ), ) responses.add( method=responses.GET, url=self.repo_api_url + "/git/refs/tags/release/1.0", status=201, json=self._get_expected_tag_ref("release/1.0", "SHA"), ) responses.add( responses.GET, self.public_repo_url + "/releases/tags/release/1.0", json=self._get_expected_release("release/1.0"), ) responses.add( method=responses.GET, url=self.repo_api_url + "/git/tags/SHA", status=404 ) task_config = TaskConfig( { "options": { "branch": "master", "version": "latest", "repo_url": self.public_repo_url, "includes": ["tasks/foo.py", "unpackaged/pre/foo/package.xml"], } } ) extract_github.return_value.namelist.return_value = [ "tasks/foo.py", "unpackaged/pre/foo/package.xml", "force-app", ] task = PublishSubtree(self.project_config, task_config) with pytest.raises(GithubException) as exc: task() assert "Tag release/1.0 not found" == str(exc.value)
def test_run_task(self, removeXmlElement): with temporary_dir() as path: revert_path = os.path.join(os.path.dirname(path), os.path.basename(path) + "_revert") project_config = BaseProjectConfig(BaseGlobalConfig()) task_config = TaskConfig( {"options": { "path": path, "revert_path": revert_path }}) task = CreateUnmanagedEESrc(project_config, task_config) task() removeXmlElement.assert_called_once_with("availableFields", path, "*.object")
def test_renames_not_list_error(self): task_config = TaskConfig({ "options": { "branch": "master", "repo_url": self.public_repo_url, "includes": ["tasks/foo.py", "unpackaged/pre/foo/package.xml"], "renames": ["tasks/foo.py", "unpackaged/pre/foo/package.xml"], } }) with pytest.raises(TaskOptionsError) as exc: PublishSubtree(self.project_config, task_config) assert ( "Renamed paths must be a list of dicts with `local:` and `target:` keys." == str(exc.value))
def test_get_test_class_query__exclude(self): task_config = TaskConfig({ "options": { "test_name_match": "%_TEST", "test_name_exclude": "EXCL" } }) task = RunApexTests(self.project_config, task_config, self.org_config) query = task._get_test_class_query() self.assertEqual( "SELECT Id, Name FROM ApexClass WHERE NamespacePrefix = null " "AND (Name LIKE '%_TEST') AND (NOT Name LIKE 'EXCL')", query, )
def test_run_task__retry_tests_fails(self): self._mock_apex_class_query() self._mock_run_tests() self._mock_run_tests(body="JOBID_9999") self._mock_get_failed_test_classes() self._mock_get_failed_test_classes(job_id="JOBID_9999") self._mock_tests_complete() self._mock_tests_complete(job_id="JOBID_9999") self._mock_get_test_results("Fail", "UNABLE_TO_LOCK_ROW") self._mock_get_test_results("Fail", "DUPLICATES_DETECTED", job_id="JOBID_9999") task_config = TaskConfig() task_config.config["options"] = { "junit_output": "results_junit.xml", "poll_interval": 1, "test_name_match": "%_TEST", "retry_failures": ["UNABLE_TO_LOCK_ROW"], } task = RunApexTests(self.project_config, task_config, self.org_config) with self.assertRaises(ApexTestException): task()
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 test_run_task__status_not_found(self): self.init_github() repo_response = self._get_expected_repo("TestOwner", "TestRepo") responses.add(method=responses.GET, url=self.repo_api_url, json=repo_response) responses.add( method=responses.GET, url=f"{self.repo_api_url}/commits/abcdef", json=self._get_expected_commit("abcdef"), ) responses.add( method=responses.GET, url=f"{self.repo_api_url}/commits/abcdef/status", json={ "url": f"{self.repo_api_url}/abcdef/status", "commit_url": f"{self.repo_api_url}/abcdef", "repository": repo_response, "sha": "abcdef", "state": "success", "statuses": [], "total_count": 0, }, ) project_config = create_project_config(repo_commit="abcdef") project_config.keychain.set_service( "github", ServiceConfig({ "username": "******", "token": "TestPass", "email": "*****@*****.**", }), ) task_config = TaskConfig({"options": {"context": "2gp"}}) org_config = OrgConfig( { "instance_url": "https://salesforce", "access_token": "TOKEN" }, "test") task = GetPackageDataFromCommitStatus(project_config, task_config, org_config) task._init_task() with pytest.raises( DependencyLookupError, match= "Could not find package version id in '2gp' commit status", ): task._run_task()
def test_is_retriable_failure(self): task_config = TaskConfig() task_config.config["options"] = { "junit_output": "results_junit.xml", "poll_interval": 1, "test_name_match": "%_TEST", "retry_failures": [ "UNABLE_TO_LOCK_ROW", "unable to obtain exclusive access to this record", ], } task = RunApexTests(self.project_config, task_config, self.org_config) task._init_options(task_config.config["options"]) self.assertTrue( task._is_retriable_failure({ "Message": "UNABLE_TO_LOCK_ROW", "StackTrace": "test", "Outcome": "Fail", })) self.assertTrue( task._is_retriable_failure({ "Message": "TEST", "StackTrace": "unable to obtain exclusive access to this record", "Outcome": "Fail", })) self.assertFalse( task._is_retriable_failure({ "Message": "DUPLICATES_DETECTED", "StackTrace": "test", "Outcome": "Fail", }))
def test_init_devhub__from_service(self, project_config, org_config): project_config.keychain.services["devhub"] = ServiceConfig( {"username": "******"}) task = CreatePackageVersion( project_config, TaskConfig({ "options": { "package_type": "Unlocked", "package_name": "Test Package", } }), org_config, ) devhub_config = task._init_devhub() assert devhub_config.username == "*****@*****.**"
def test_FindReplace_env_replace_error(self, find_replace): with pytest.raises(TaskOptionsError): task_config = TaskConfig({ "options": { "find": "foo", "replace": "bar", "env_replace": True, "path": ".", "max": 1, } }) task = util.FindReplace(self.project_config, task_config, self.org_config) task() find_replace.assert_called_once()
def test_run_task(self): with temporary_dir() as revert_path: with open(os.path.join(revert_path, "file"), "w") as f: pass path = os.path.join(os.path.dirname(revert_path), os.path.basename(revert_path) + "_orig") project_config = BaseProjectConfig(BaseGlobalConfig()) task_config = TaskConfig( {"options": { "path": path, "revert_path": revert_path }}) task = RevertUnmanagedEESrc(project_config, task_config) task() self.assertTrue(os.path.exists(os.path.join(path, "file")))
def test_find_or_create_plan_template__not_found(self): responses.add( "GET", "https://metadeploy/plantemplates?product=abcdef&name=install", json={"data": []}, ) responses.add( "POST", "https://metadeploy/plantemplates", json={"url": "https://NEW_PLANTEMPLATE"}, ) responses.add("POST", "https://metadeploy/planslug", json={"url": "http://NEW_PLANSLUG"}) project_config = create_project_config() project_config.config["project"]["git"]["repo_url"] = "EXISTING_REPO" expected_plans = { "install": { "title": "Test Install", "slug": "install", "tier": "primary", "steps": { 1: { "flow": "install_prod" } }, } } project_config.config["plans"] = expected_plans project_config.keychain.set_service( "metadeploy", ServiceConfig({ "url": "https://metadeploy", "token": "TOKEN" })) task_config = TaskConfig({"options": {"tag": "release/1.0"}}) task = Publish(project_config, task_config) task._init_task() plantemplate = task._find_or_create_plan_template( { "url": "https://EXISTING_PRODUCT", "id": "abcdef" }, "install", {"slug": "install"}, ) self.assertEqual("https://NEW_PLANTEMPLATE", plantemplate["url"])
def test_run_string_only(self): task_config = TaskConfig( {"options": { "apex": 'System.debug("test");' }}) task = AnonymousApexTask(self.project_config, task_config, self.org_config) url = self.base_tooling_url + "executeAnonymous" responses.add(responses.GET, url, status=200, json={ "compiled": True, "success": True }) task()
def task(project_config, devhub_config, org_config): task = CreatePackageVersion( project_config, TaskConfig({ "options": { "package_type": "Unlocked", "org_dependent": False, "package_name": "Test Package", "static_resource_path": "static-resources", } }), org_config, ) task._init_devhub = mock.Mock(return_value=devhub_config) task._init_task() return task
def run_task_class(self, class_path, **options): """ Runs a CumulusCI task class with task options via kwargs. Use this keyword to run logic from CumulusCI tasks which have not been configured in the project's cumulusci.yml file. This is most useful in cases where a test needs to use task logic for logic unique to the test and thus not worth making into a named task for the project Examples: | =Keyword= | =task_class= | =task_options= | | Run Task Class | cumulusci.task.utils.DownloadZip | url=http://test.com/test.zip dir=test_zip | """ logger.console("\n") task_class, task_config = self._init_task(class_path, options, TaskConfig()) return self._run_task(task_class, task_config)
def test_freeze_steps__skip(self): project_config = create_project_config() project_config.keychain.set_service( "metadeploy", ServiceConfig({"url": "https://metadeploy", "token": "TOKEN"}) ) plan_config = { "title": "Test Install", "slug": "install", "tier": "primary", "steps": {1: {"task": "None"}}, } task_config = TaskConfig({"options": {"tag": "release/1.0"}}) task = Publish(project_config, task_config) task._init_task() steps = task._freeze_steps(project_config, plan_config) assert steps == []
def test_find_product__not_found(self): responses.add( "GET", "https://metadeploy/products?repo_url=EXISTING_REPO", json={"data": []}, ) project_config = create_project_config() project_config.config["project"]["git"]["repo_url"] = "EXISTING_REPO" project_config.keychain.set_service( "metadeploy", ServiceConfig({"url": "https://metadeploy", "token": "TOKEN"}) ) task_config = TaskConfig({"options": {"tag": "release/1.0"}}) task = Publish(project_config, task_config) task._init_task() with self.assertRaises(Exception): task._find_product()
def test_FindReplace_env_replace(self, find_replace): with mock.patch.dict(os.environ, {"bar": "bars"}): task_config = TaskConfig({ "options": { "find": "foo", "replace": "bar", "env_replace": True, "path": ".", "max": 1, } }) task = util.FindReplace(self.project_config, task_config, self.org_config) task() assert task.options["replace"] == "bars" find_replace.assert_called_once()
def _deploy_org_settings(*, cci, org_name, scratch_org_config, scratch_org): """Deploy org settings via Metadata API. Do a Metadata API deployment to configure org settings as specified in the scratch org definition file. """ org_config = refresh_access_token( scratch_org=scratch_org, config=scratch_org_config.config, org_name=org_name, keychain=cci.keychain, ) path = os.path.join(cci.project_config.repo_root, scratch_org_config.config_file) task_config = TaskConfig({"options": {"definition_file": path}}) task = DeployOrgSettings(cci.project_config, task_config, org_config) task() return org_config
def test_run_task(self, removeXmlElement): with temporary_dir() as path: revert_path = os.path.join(os.path.dirname(path), os.path.basename(path) + "_revert") project_config = BaseProjectConfig(BaseGlobalConfig(), config={"noyaml": True}) task_config = TaskConfig( {"options": { "path": path, "revert_path": revert_path }}) task = CreateUnmanagedEESrc(project_config, task_config) task() removeXmlElement.assert_has_calls([ mock.call("availableFields", path, "*.object"), mock.call("visibility[.='Protected']", path, "*.object"), ])
def _freeze_steps(self, project_config, plan_config): steps = plan_config["steps"] flow_config = FlowConfig(plan_config) flow_config.project_config = project_config flow = FlowCoordinator(project_config, flow_config) steps = [] for step in flow.steps: if step.skip: continue with cd(step.project_config.repo_root): task = step.task_class( step.project_config, TaskConfig(step.task_config), name=step.task_name, ) steps.extend(task.freeze(step)) return steps
def setUp(self): self.api_version = 36.0 self.global_config = BaseGlobalConfig( {'project': {'package': {'api_version': self.api_version}}}) self.project_config = BaseProjectConfig(self.global_config) self.project_config.config['project'] = { 'package': { 'api_version': self.api_version, } } self.task_config = TaskConfig() self.org_config = OrgConfig({ 'instance_url': 'example.com', 'access_token': 'abc123', }) self.base_tooling_url = 'https://{}/services/data/v{}/tooling/'.format( self.org_config.instance_url, self.api_version)
def test_run_task(self): responses.add( method=responses.GET, url=self.repo_api_url, json=self._get_expected_repo(owner=self.repo_owner, name=self.repo_name), ) responses.add( method=responses.GET, url=self.repo_api_url + "/releases?per_page=100", json=[], ) responses.add( method=responses.GET, url=self.repo_api_url + "/git/refs/tags/release/1.0", status=404, ) responses.add( method=responses.POST, url=self.repo_api_url + "/git/tags", json=self._get_expected_tag("release/1.0", "SHA"), status=201, ) responses.add( method=responses.POST, url=self.repo_api_url + "/git/refs", json={}, status=201, ) responses.add( method=responses.POST, url=self.repo_api_url + "/releases", json=self._get_expected_release("release"), status=201, ) task = CreateRelease(self.project_config, TaskConfig({"options": { "version": "1.0" }})) task() self.assertEqual({ "tag_name": "release/1.0", "name": "1.0" }, task.return_values)