diff --git a/cookie_composer/cli.py b/cookie_composer/cli.py index 19ae92e..c23bb8b 100644 --- a/cookie_composer/cli.py +++ b/cookie_composer/cli.py @@ -9,6 +9,7 @@ from cookie_composer.commands.add import add_cmd from cookie_composer.commands.create import create_cmd +from cookie_composer.commands.update import update_cmd from cookie_composer.exceptions import GitError logger = logging.getLogger(__name__) @@ -74,7 +75,7 @@ def create( path_or_url: str, output_dir: Optional[Path], ): - """Create a project from a template or configuration.""" + """Create a project from the template or configuration PATH_OR_URL in OUTPUT_DIR.""" create_cmd( path_or_url, output_dir, @@ -137,20 +138,40 @@ def add( path_or_url: str, destination: Optional[Path], ): - """Add a template or configuration to an existing project.""" - destination = destination or Path(".") + """Add the template or configuration PATH_OR_URL to an existing project at DESTINATION.""" + destination = destination or Path.cwd() try: - add_cmd(path_or_url, destination, no_input=no_input) + add_cmd( + path_or_url, + destination, + no_input=no_input, + checkout=checkout, + directory=directory, + overwrite_if_exists=overwrite_if_exists, + skip_if_file_exists=skip_if_file_exists, + default_config=default_config, + ) except GitError as e: raise click.UsageError(str(e)) from e @cli.command() -def update(): - """ - Update the project to the latest version of each template. - """ - pass +@click.option( + "--no-input", + is_flag=True, + help="Do not prompt for parameters and only use cookiecutter.json file content. " + "Defaults to deleting any cached resources and re-downloading them.", +) +@click.argument( + "destination", required=False, type=click.Path(exists=True, file_okay=False, writable=True, path_type=Path) +) +def update(no_input: bool, destination: Optional[Path] = None): + """Update the project to the latest version of each template.""" + destination = destination or Path.cwd() + try: + update_cmd(destination, no_input=no_input) + except GitError as e: + raise click.UsageError(str(e)) from e @cli.command() diff --git a/cookie_composer/commands/update.py b/cookie_composer/commands/update.py index 22b165c..126f8bb 100644 --- a/cookie_composer/commands/update.py +++ b/cookie_composer/commands/update.py @@ -39,6 +39,8 @@ def update_cmd(destination_dir: Optional[Path] = None, no_input: bool = False): """ destination_dir = Path(destination_dir).resolve() or Path().cwd().resolve() output_dir = destination_dir.parent + repo = get_repo(destination_dir) + previously_untracked_files = set(repo.untracked_files) # Read the project composition file proj_composition_path = destination_dir / ".composition.yaml" @@ -61,7 +63,6 @@ def update_cmd(destination_dir: Optional[Path] = None, no_input: bool = False): echo("Done.") return - repo = get_repo(destination_dir) branch_name = "update_composition" if branch_exists(repo, branch_name) or remote_branch_exists(repo, branch_name): checkout_branch(repo, branch_name) @@ -73,6 +74,17 @@ def update_cmd(destination_dir: Optional[Path] = None, no_input: bool = False): ) new_composition = update_rendered_composition_layers(proj_composition, rendered_layers) write_rendered_composition(new_composition) + + # Commit changed files and newly created files + changed_files = [item.a_path for item in repo.index.diff(None)] + untracked_files = set(repo.untracked_files) + new_untracked_files = untracked_files - previously_untracked_files + changed_files.extend(list(new_untracked_files)) + + if changed_files: + repo.index.add(changed_files) + repo.index.commit(message="Updating composition layers") + echo("Done.") diff --git a/tests/test_commands/test_update.py b/tests/test_commands/test_update.py index 01a08d2..6f253cd 100644 --- a/tests/test_commands/test_update.py +++ b/tests/test_commands/test_update.py @@ -8,7 +8,7 @@ from git import Actor, Repo from cookie_composer.commands import update -from cookie_composer.git_commands import get_repo +from cookie_composer.git_commands import checkout_branch, get_repo @pytest.fixture @@ -19,7 +19,7 @@ def git_template(fixtures_path, tmp_path) -> dict: shutil.copytree(template_path, dest_path) template_repo = Repo.init(dest_path) - template_repo.index.add(".") + template_repo.index.add(template_repo.untracked_files) template_repo.index.commit( message="new: first commit", committer=Actor("Bob", "bob@example.com"), commit_date="2022-01-01 10:00:00" ) @@ -73,12 +73,6 @@ def git_project(fixtures_path, tmp_path, git_template: dict) -> dict: yaml.dump(rendered_comp, f) repo = Repo.init(dest_path) - repo.index.add(".") - repo.index.commit( - message="new: first commit", committer=Actor("Bob", "bob@example.com"), commit_date="2022-01-01 10:00:00" - ) - result = {"project_path": dest_path} - result.update(git_template) rendered_items = {item.name for item in os.scandir(dest_path)} assert rendered_items == { ".composition.yaml", @@ -89,6 +83,22 @@ def git_project(fixtures_path, tmp_path, git_template: dict) -> dict: "requirements.txt", } + repo.index.add( + [ + ".composition.yaml", + "README.md", + "dontmerge.json", + "merge.yaml", + "requirements.txt", + ] + ) + repo.index.commit( + message="new: first commit", committer=Actor("Bob", "bob@example.com"), commit_date="2022-01-01 10:00:00" + ) + result = {"project_path": dest_path} + result.update(git_template) + + assert not repo.untracked_files return result @@ -106,9 +116,22 @@ def test_update_command(git_project: dict): "requirements.txt", } repo = get_repo(git_project["project_path"]) + + # we should be on the update_composition branch assert repo.active_branch.name == "update_composition" + + # All the files should have been committed in the update_cmd + assert not repo.untracked_files + + # Make sure the composition was updated to the latest commit comp_text = Path(git_project["project_path"] / ".composition.yaml").read_text() assert re.search(f"commit: {git_project['second_commit']}", comp_text) + previous_sha = repo.active_branch.commit.hexsha + + # Check for idempotence + checkout_branch(repo, "master") + update.update_cmd(git_project["project_path"], no_input=True) + assert repo.active_branch.commit.hexsha == previous_sha def test_update_command_no_update_required(git_project: dict, capsys):