def tearDown(self): with utils.work_in(config.DEFAULT_CONFIG['cookiecutters_dir']): if os.path.isdir('cookiecutter-pypackage'): shutil.rmtree('cookiecutter-pypackage', onerror=force_delete) if os.path.isdir('boilerplate'): shutil.rmtree('boilerplate', onerror=force_delete) super(TestExamplesRepoArg, self).tearDown()
def tearDown(self): with utils.work_in(config.DEFAULT_CONFIG['cookiecutters_dir']): if os.path.isdir('cookiecutter-pypackage'): utils.rmtree('cookiecutter-pypackage') if os.path.isdir('boilerplate'): utils.rmtree('boilerplate') super(TestGitBranch, self).tearDown()
def test_find_hooks(self): '''Getting the list of all defined hooks''' with utils.work_in(self.repo_path): self.assertEqual({ 'pre_gen_project': os.path.abspath('hooks/pre_gen_project.py'), 'post_gen_project': os.path.abspath(os.path.join('hooks', self.post_hook)), }, hooks.find_hooks())
def test_find_hook(self): """Finds the specified hook""" with utils.work_in(self.repo_path): self.assertEqual({ 'pre_gen_project': os.path.abspath('hooks/pre_gen_project.py'), 'post_gen_project': os.path.abspath(os.path.join('hooks', self.post_hook)), }, hooks.find_hooks())
def fin_remove_additional_dirs(): with utils.work_in(config.DEFAULT_CONFIG['cookiecutters_dir']): if os.path.isdir('cookiecutter-pypackage'): utils.rmtree('cookiecutter-pypackage') if os.path.isdir('boilerplate'): utils.rmtree('boilerplate') if os.path.isdir('python_boilerplate'): utils.rmtree('python_boilerplate')
def test_find_hooks(self): '''Getting the list of all defined hooks''' repo_path = 'tests/test-hooks/' with utils.work_in(repo_path): self.assertEqual({ 'pre_gen_project': os.path.abspath('hooks/pre_gen_project.py'), 'post_gen_project': os.path.abspath('hooks/post_gen_project.sh'), }, hooks.find_hooks())
def test_public_run_hook(self): '''Execute hook from specified template in specified output directory''' tests_dir = os.path.join(self.repo_path, 'input{{hooks}}') with utils.work_in(self.repo_path): hooks.run_hook('pre_gen_project', tests_dir) self.assertTrue(os.path.isfile(os.path.join(tests_dir, 'python_pre.txt'))) hooks.run_hook('post_gen_project', tests_dir) self.assertTrue(os.path.isfile(os.path.join(tests_dir, 'shell_post.txt')))
def test_find_hook(self): """Finds the specified hook.""" with utils.work_in(self.repo_path): expected_pre = os.path.abspath('hooks/pre_gen_project.py') actual_hook_path = hooks.find_hook('pre_gen_project') assert expected_pre == actual_hook_path expected_post = os.path.abspath('hooks/{}'.format(self.post_hook)) actual_hook_path = hooks.find_hook('post_gen_project') assert expected_post == actual_hook_path
def test_run_hook(self): """Execute hook from specified template in specified output directory. """ tests_dir = os.path.join(self.repo_path, 'input{{hooks}}') with utils.work_in(self.repo_path): hooks.run_hook('pre_gen_project', tests_dir, {}) assert os.path.isfile(os.path.join(tests_dir, 'python_pre.txt')) hooks.run_hook('post_gen_project', tests_dir, {}) assert os.path.isfile(os.path.join(tests_dir, 'shell_post.txt'))
def test_run_failing_hook(self): hook_path = os.path.join(self.hooks_path, "pre_gen_project.py") tests_dir = os.path.join(self.repo_path, "input{{hooks}}") with open(hook_path, "w") as f: f.write("#!/usr/bin/env python\n") f.write("import sys; sys.exit(1)\n") with utils.work_in(self.repo_path): with pytest.raises(exceptions.FailedHookException) as excinfo: hooks.run_hook("pre_gen_project", tests_dir, {}) assert "Hook script failed" in str(excinfo.value)
def test_run_failing_hook(self): hook_path = os.path.join(self.hooks_path, 'pre_gen_project.py') tests_dir = os.path.join(self.repo_path, 'input{{hooks}}') with open(hook_path, 'w') as f: f.write("#!/usr/bin/env python\n") f.write("import sys; sys.exit(1)\n") with utils.work_in(self.repo_path): with pytest.raises(exceptions.FailedHookException) as excinfo: hooks.run_hook('pre_gen_project', tests_dir, {}) assert 'Hook script failed' in str(excinfo.value)
def test_run_failing_hook(self): """Test correct exception raise if hook exit code is not zero.""" hook_path = os.path.join(self.hooks_path, 'pre_gen_project.py') tests_dir = os.path.join(self.repo_path, 'input{{hooks}}') with open(hook_path, 'w') as f: f.write("#!/usr/bin/env python\n") f.write("import sys; sys.exit(1)\n") with utils.work_in(self.repo_path): with pytest.raises(exceptions.FailedHookException) as excinfo: hooks.run_hook('pre_gen_project', tests_dir, {}) assert 'Hook script failed' in str(excinfo.value)
def test_work_in(tmp_path): """Verify returning to original folder after `utils.work_in` use.""" cwd = Path.cwd() ch_to = tmp_path assert ch_to != Path.cwd() # Under context manager we should work in tmp_path. with utils.work_in(ch_to): assert ch_to == Path.cwd() # Make sure we return to the correct folder assert cwd == Path.cwd()
def test_find_hook(self): """Finds the specified hook.""" with utils.work_in(self.repo_path): expected = { 'pre_gen_project': [os.path.abspath( 'hooks/pre_gen_project.py' )], 'post_gen_project': [os.path.abspath( os.path.join('hooks', self.post_hook) )], } assert expected == hooks.find_hooks()
def test_public_run_hook(self): '''Execute hook from specified template in specified output directory''' tests_dir = os.path.join(self.repo_path, 'input{{hooks}}') with utils.work_in(self.repo_path): hooks.run_hook('pre_gen_project', tests_dir) self.assertTrue(os.path.isfile(os.path.join(tests_dir, 'python_pre.txt'))) config_file = os.path.join(self.repo_path, "../test-evaluate/cookiecutter.json") hooks.run_hook('post_gen_project', tests_dir, config_file) self.assertTrue(os.path.isfile(os.path.join(tests_dir, 'shell_post.txt'))) self.assertTrue(os.path.isfile(config_file)) self.assertIn("cookiecutter.json", open(os.path.join(tests_dir, 'config_file.txt')).read()) self.assertIn("fat", open(os.path.join(tests_dir, 'yo_mama_file.txt')).read())
def test_git_clone_custom_dir(self): os.makedirs("tests/custom_dir1/custom_dir2/") repo_dir = vcs.clone( repo_url='https://github.com/audreyr/cookiecutter-pypackage.git', checkout=None, clone_to_dir="tests/custom_dir1/custom_dir2/" ) with utils.work_in("tests/custom_dir1/custom_dir2/"): self.assertEqual(repo_dir, 'tests/custom_dir1/custom_dir2/cookiecutter-pypackage') self.assertTrue(os.path.isfile('cookiecutter-pypackage/README.rst')) if os.path.isdir('cookiecutter-pypackage'): shutil.rmtree('cookiecutter-pypackage') if os.path.isdir('tests/custom_dir1'): shutil.rmtree('tests/custom_dir1')
def test_git_clone_custom_dir(): os.makedirs('tests/custom_dir1/custom_dir2/') repo_dir = vcs.clone( repo_url='https://github.com/audreyr/cookiecutter-pypackage.git', checkout=None, clone_to_dir='tests/custom_dir1/custom_dir2/') with utils.work_in('tests/custom_dir1/custom_dir2/'): test_dir = 'tests/custom_dir1/custom_dir2/cookiecutter-pypackage' assert repo_dir == test_dir.replace('/', os.sep) assert os.path.isfile('cookiecutter-pypackage/README.rst') if os.path.isdir('cookiecutter-pypackage'): utils.rmtree('cookiecutter-pypackage') if os.path.isdir('tests/custom_dir1'): utils.rmtree('tests/custom_dir1')
def test_git_clone_custom_dir(): os.makedirs('tests/custom_dir1/custom_dir2/') repo_dir = vcs.clone( repo_url='https://github.com/audreyr/cookiecutter-pypackage.git', checkout=None, clone_to_dir='tests/custom_dir1/custom_dir2/' ) with utils.work_in('tests/custom_dir1/custom_dir2/'): test_dir = 'tests/custom_dir1/custom_dir2/cookiecutter-pypackage' assert repo_dir == test_dir.replace('/', os.sep) assert os.path.isfile('cookiecutter-pypackage/README.rst') if os.path.isdir('cookiecutter-pypackage'): utils.rmtree('cookiecutter-pypackage') if os.path.isdir('tests/custom_dir1'): utils.rmtree('tests/custom_dir1')
def test_public_run_hook(self): '''Execute hook from specified template in specified output directory''' tests_dir = os.path.join(self.repo_path, 'input{{hooks}}') with utils.work_in(self.repo_path): hooks.run_hook('pre_gen_project', {'cookiecutter': {'test': 'test'}}, tests_dir) self.assertTrue(os.path.isfile(os.path.join(tests_dir, 'python_pre.txt'))) f = open(os.path.join(tests_dir, 'python_pre.txt')) self.assertEqual(f.readline(), 'test') hooks.run_hook('post_gen_project', {'cookiecutter': {'test': 'test'}}, tests_dir) self.assertTrue(os.path.isfile(os.path.join(tests_dir, 'shell_post.txt'))) f = open(os.path.join(tests_dir, 'shell_post.txt')) self.assertEqual(f.readline(), 'test\n')
def create_pull_request_for_code_content(self, input_data: dict) -> Response: code_content = self.load_request(input_data) self.check_code_is_valid(code_content) # async self.fork_repo() with tempfile.TemporaryDirectory() as dirname: dirname = str(pathlib.Path(dirname).resolve()) repo = self.clone_repo(dirname) with work_in(dirname): self.configure_repo(repo) feature_name, branch_name = self.create_new_branch(repo) changed_files, new_feature_path = self.start_new_feature(dirname, feature_name) self.write_code_content(new_feature_path, code_content) self.commit_changes(repo, changed_files) push_result = self.push_to_remote(repo, branch_name) # noqa F841 # TODO if push failed, likely because fork does not yet exist, then try again return self.create_pull_request(feature_name, branch_name)
def push_directory_to_repo(directory, github_repo): auth = FixedGittleAuth(pkey=open(settings.GITHUB_PRIVATE_KEY)) repo = Gittle.init(directory, auth=auth) files = [] with work_in(directory): for root, dirnames, filenames in os.walk("."): # remove the leading './' root = root[2:] # skip .git directories if ".git" in dirnames: dirnames.remove(".git") for f in filenames: path = os.path.join(root, f) files.append(str(path)) repo.commit(name="bakehouse", message="Hello world", files=files) repo.push(github_repo.ssh_url, branch_name="master")
def _run_hook_from_repo_dir(repo_dir, hook_name, project_dir, context, delete_project_on_failure): """Run hook from repo directory, clean project directory if hook fails. :param repo_dir: Project template input directory. :param hook_name: The hook to execute. :param project_dir: The directory to execute the script from. :param context: Cookiecutter project context. :param delete_project_on_failure: Delete the project directory on hook failure? """ with work_in(repo_dir): try: run_hook(hook_name, project_dir, context) except FailedHookException: if delete_project_on_failure: rmtree(project_dir) logger.error("Stopping generation because {} hook " "script didn't exit successfully".format(hook_name)) raise
def test_work_in(): with utils.work_in(ch_to): test_dir = os.path.join(cwd, ch_to).replace("/", os.sep) self.assertEqual(test_dir, os.getcwd()) raise TestException()
def test_no_hooks(self): """find_hooks should return None if the hook could not be found.""" with utils.work_in('tests/fake-repo'): assert {} == hooks.find_hooks()
def test_hook_not_found(self): """`find_hooks` should return None if the hook could not be found.""" with utils.work_in(self.repo_path): assert hooks.find_hook('unknown_hook') is None
def test_hook_not_found(self): with utils.work_in(self.repo_path): assert hooks.find_hook('unknown_hook') is None
def test_work_in(): with utils.work_in(ch_to): self.assertEqual(os.path.join(cwd, ch_to), os.getcwd()) raise TestException()
def test_unknown_hooks_dir(self): with utils.work_in(self.repo_path): assert hooks.find_hook("pre_gen_project", hooks_dir="hooks_dir") is None
def fin_remove_additional_dirs(): with utils.work_in(config.DEFAULT_CONFIG['cookiecutters_dir']): if os.path.isdir('cookiecutter-pypackage'): utils.rmtree('cookiecutter-pypackage') if os.path.isdir('boilerplate'): utils.rmtree('boilerplate')
def _run_ballet_update_template(d, project_slug, **kwargs): with work_in(safepath(d.joinpath(project_slug))): ballet.update.update_project_template(**kwargs)
def test_work_in(): with utils.work_in(ch_to): self.assertEqual(os.path.join(cwd, ch_to), os.getcwd()) raise TestException()
def generate_files( repo_dir, context=None, output_dir='.', overwrite_if_exists=False, skip_if_file_exists=False, skip_hooks=False, ): """Render the templates and saves them to files. :param repo_dir: Project template input directory. :param context: Dict for populating the template's variables. :param output_dir: Where to output the generated project dir into. :param overwrite_if_exists: Overwrite the contents of the output directory if it exists. """ template_dir = find_template(repo_dir) logger.debug('Generating project from %s...', template_dir) context = context or OrderedDict([]) unrendered_dir = os.path.split(template_dir)[1] ensure_dir_is_templated(unrendered_dir) env = StrictEnvironment(context=context, keep_trailing_newline=True) try: project_dir, output_directory_created = render_and_create_dir( unrendered_dir, context, output_dir, env, overwrite_if_exists) except UndefinedError as err: msg = "Unable to create project directory '{}'".format(unrendered_dir) raise UndefinedVariableInTemplate(msg, err, context) # We want the Jinja path and the OS paths to match. Consequently, we'll: # + CD to the template folder # + Set Jinja's path to '.' # # In order to build our files to the correct folder(s), we'll use an # absolute path for the target folder (project_dir) project_dir = os.path.abspath(project_dir) logger.debug('Project directory is %s', project_dir) # if we created the output directory, then it's ok to remove it # if rendering fails delete_project_on_failure = output_directory_created if not skip_hooks: _run_hook_from_repo_dir(repo_dir, 'pre_gen_project', project_dir, context, delete_project_on_failure) else: logging.info('Skipping pre-gen hooks') with work_in(template_dir): env.loader = FileSystemLoader('.') for root, dirs, files in os.walk('.'): # We must separate the two types of dirs into different lists. # The reason is that we don't want ``os.walk`` to go through the # unrendered directories, since they will just be copied. copy_dirs = [] render_dirs = [] for d in dirs: d_ = os.path.normpath(os.path.join(root, d)) # We check the full path, because that's how it can be # specified in the ``_copy_without_render`` setting, but # we store just the dir name if is_copy_only_path(d_, context): copy_dirs.append(d) else: render_dirs.append(d) for copy_dir in copy_dirs: indir = os.path.normpath(os.path.join(root, copy_dir)) outdir = os.path.normpath(os.path.join(project_dir, indir)) outdir = env.from_string(outdir).render(**context) logger.debug('Copying dir %s to %s without rendering', indir, outdir) shutil.copytree(indir, outdir) # We mutate ``dirs``, because we only want to go through these dirs # recursively dirs[:] = render_dirs for d in dirs: unrendered_dir = os.path.join(project_dir, root, d) try: render_and_create_dir(unrendered_dir, context, output_dir, env, overwrite_if_exists) except UndefinedError as err: if delete_project_on_failure: rmtree(project_dir) _dir = os.path.relpath(unrendered_dir, output_dir) msg = "Unable to create directory '{}'".format(_dir) raise UndefinedVariableInTemplate(msg, err, context) for f in files: infile = os.path.normpath(os.path.join(root, f)) if is_copy_only_path(infile, context): outfile_tmpl = env.from_string(infile) outfile_rendered = outfile_tmpl.render(**context) outfile = os.path.join(project_dir, outfile_rendered) logger.debug('Copying file %s to %s without rendering', infile, outfile) shutil.copyfile(infile, outfile) shutil.copymode(infile, outfile) continue try: generate_file(project_dir, infile, context, env, skip_if_file_exists) except UndefinedError as err: if delete_project_on_failure: rmtree(project_dir) msg = "Unable to create file '{}'".format(infile) raise UndefinedVariableInTemplate(msg, err, context) if not skip_hooks: _run_hook_from_repo_dir( repo_dir, 'post_gen_project', project_dir, context, delete_project_on_failure, ) else: logging.info('Skipping post-gen hooks') return project_dir
def test_no_hooks(self): """find_hooks should return None if the hook could not be found.""" with utils.work_in('tests/fake-repo'): assert None is hooks.find_hook('pre_gen_project')
def test_unknown_hooks_dir(self): with utils.work_in(self.repo_path): assert hooks.find_hook( 'pre_gen_project', hooks_dir='hooks_dir' ) is None
def test_no_hooks(self): '''find_hooks should return an empty dict if no hooks folder could be found. ''' with utils.work_in('tests/fake-repo'): self.assertEqual({}, hooks.find_hooks())
def create_pull_request_for_code_content(code_content): with stacklog(app.logger.info, 'Checking for valid code'): if not is_valid_python(code_content): return { 'result': False, 'url': None, 'message': 'Submitted code is not valid Python code', } with tempfile.TemporaryDirectory() as dirname: dirname = str(pathlib.Path(dirname).resolve()) # clone directory to dir with stacklog(app.logger.info, 'Cloning repo'): repo = git.Repo.clone_from(REPO_URL, to_path=dirname) with work_in(dirname): # configure repo with stacklog(app.logger.info, 'Configuring repo'): set_config_variables( repo, { 'user.name': 'Demo1', 'user.email': '*****@*****.**', }) repo.remote().set_url(REPO_URL) # create a new branch with stacklog(app.logger.info, 'Creating new branch and checking it out'): feature_name, branch_name = make_feature_and_branch_name() repo.create_head(branch_name) repo.heads[branch_name].checkout() # start new feature with stacklog(app.logger.info, 'Starting new feature'): extra_context = { 'username': USERNAME.replace('-', '_'), 'featurename': feature_name, } changes = ballet.templating.start_new_feature( no_input=True, extra_context=extra_context) changed_files = [ str(pathlib.Path(name).relative_to(dirname)) for (name, kind) in changes if kind == 'file' ] new_feature_path = get_new_feature_path(changes) # add code content to path with stacklog(app.logger.info, 'Adding code content'): with open(new_feature_path, 'w') as f: blackened_code_content = blacken_code(code_content) f.write(blackened_code_content) # commit new code with stacklog(app.logger.info, 'Committing new feature'): repo.index.add(changed_files) repo.index.commit('Add new feature') # push to branch with stacklog(app.logger.info, 'Pushing to remote'): refspec = f'refs/heads/{branch_name}:refs/heads/{branch_name}' if not is_debug(): repo.remote().push(refspec=refspec) # create pull request with stacklog(app.logger.info, 'Creating pull request'): github = Github(PASSWORD) grepo = github.get_repo(UPSTREAM_REPO_SPEC) title = 'Propose new feature' body = dedent(f'''\ Propose new feature: {feature_name} Submitted by user: {USERNAME} -- Pull request automatically created by ballet-submit-server ''') base = 'master' head = f'{USERNAME}:{branch_name}' maintainer_can_modify = True app.logger.debug( f'About to create pull: title={title}, body={body}, base={base}, head={head}' ) if not is_debug(): pr = grepo.create_pull( title=title, body=body, base=base, head=head, maintainer_can_modify=maintainer_can_modify) return { 'result': True, 'url': pr.html_url, 'message': None, } if is_debug(): return { 'result': True, 'url': 'http://some/testing/url', 'message': None, }
def test_no_hooks(self): """find_hooks should return None if the hook could not be found.""" with utils.work_in("tests/fake-repo"): assert None is hooks.find_hook("pre_gen_project")
def test_no_hooks(self): """`find_hooks` should return None if the hook could not be found.""" with utils.work_in('tests/fake-repo'): assert None is hooks.find_hook('pre_gen_project')
def test_hook_not_found(self): with utils.work_in(self.repo_path): assert hooks.find_hook("unknown_hook") is None
def test_unknown_hooks_dir(self): """`find_hooks` should return None if hook directory not found.""" with utils.work_in(self.repo_path): assert hooks.find_hook('pre_gen_project', hooks_dir='hooks_dir') is None
def test_no_hooks(self): """find_hooks should return None if the hook could not be found.""" with utils.work_in('tests/fake-repo'): assert {} == hooks.find_hooks()
def test_work_in(): with utils.work_in(ch_to): test_dir = os.path.join(cwd, ch_to).replace("/", os.sep) assert test_dir == os.getcwd() raise TestException()