Esempio n. 1
0
def get_cruft_file(project_dir_path: Path, exists: bool = True) -> Path:
    cruft_file = project_dir_path / ".cruft.json"
    if not exists and cruft_file.is_file():
        raise CruftAlreadyPresent(cruft_file)
    if exists and not cruft_file.is_file():
        raise NoCruftFound(project_dir_path.resolve())
    return cruft_file
Esempio n. 2
0
def check(expanded_dir: str = ".") -> bool:
    """Checks to see if there have been any updates to the Cookiecutter template used
    to generate this project.
    """
    expanded_dir_path = Path(expanded_dir)
    cruft_file = expanded_dir_path / ".cruft.json"
    if not cruft_file.is_file():
        raise NoCruftFound(expanded_dir_path.resolve())

    cruft_state = json.loads(cruft_file.read_text())
    with TemporaryDirectory() as cookiecutter_template_dir:
        repo = Repo.clone_from(cruft_state["template"],
                               cookiecutter_template_dir)
        last_commit = repo.head.object.hexsha
        if last_commit == cruft_state["commit"] or not repo.index.diff(
                cruft_state["commit"]):
            return True

    return False
Esempio n. 3
0
def check(expanded_dir: str = ".") -> bool:
    """Checks to see if there have been any updates to the Cookiecutter template used
    to generate this project.
    """
    cruft_file = os.path.join(expanded_dir, ".cruft.json")
    if not os.path.isfile(cruft_file):
        raise NoCruftFound(os.path.abspath(expanded_dir))

    with open(cruft_file) as cruft_open_file:
        cruft_state = json.load(cruft_open_file)
        with TemporaryDirectory() as cookiecutter_template_dir:
            repo = Repo.clone_from(cruft_state["template"],
                                   cookiecutter_template_dir)
            last_commit = repo.head.object.hexsha
            if last_commit == cruft_state["commit"] or not repo.index.diff(
                    cruft_state["commit"]):
                return True

    return False
Esempio n. 4
0
def update(
    expanded_dir: str = ".",
    cookiecutter_input: bool = False,
    skip_apply_ask: bool = False,
    skip_update: bool = False,
) -> bool:
    """Update specified project's cruft to the latest and greatest release."""
    expanded_dir_path = Path(expanded_dir)
    pyproject_file = expanded_dir_path / "pyproject.toml"
    cruft_file = expanded_dir_path / ".cruft.json"
    if not cruft_file.is_file():
        raise NoCruftFound(cruft_file)

    cruft_state = json.loads(cruft_file.read_text())

    skip_cruft = cruft_state.get("skip", [])
    if toml and pyproject_file.is_file():
        pyproject_cruft = toml.loads(pyproject_file.read_text()).get(
            "tool", {}).get("cruft", {})
        skip_cruft.extend(pyproject_cruft.get("skip", []))

    with TemporaryDirectory() as compare_directory_str:
        compare_directory = Path(compare_directory_str)
        template_dir = compare_directory / "template"

        try:
            repo = Repo.clone_from(cruft_state["template"], template_dir)
            last_commit = repo.head.object.hexsha
        except Exception as e:  # pragma: no cover
            raise InvalidCookiecutterRepository(e)

        if last_commit == cruft_state["commit"] or not repo.index.diff(
                cruft_state["commit"]):
            return False

        directory = cruft_state.get("directory", "")

        template_dir = template_dir / directory

        context_file = template_dir / "cookiecutter.json"

        new_output_dir = compare_directory / "new_output"

        new_context = _generate_output(
            context_file=str(context_file),
            cruft_state=cruft_state,
            cookiecutter_input=cookiecutter_input,
            template_dir=str(template_dir),
            output_dir=str(new_output_dir),
        )

        old_output_dir = compare_directory / "old_output"
        repo.head.reset(commit=cruft_state["commit"], working_tree=True)
        _generate_output(
            context_file=str(context_file),
            cruft_state=cruft_state,
            cookiecutter_input=cookiecutter_input,
            template_dir=str(template_dir),
            output_dir=str(old_output_dir),
        )

        for dir_item in old_output_dir.glob("*"):
            if dir_item.is_dir():
                old_main_directory = dir_item

        new_main_directory = new_output_dir / old_main_directory.name

        for skip_file in skip_cruft:
            file_path_old = old_main_directory / skip_file
            file_path_new = new_main_directory / skip_file
            for file_path in (file_path_old, file_path_new):
                if file_path.is_dir():
                    rmtree(file_path)
                elif file_path.is_file():
                    file_path.unlink()

        diff = run(
            [
                "git", "diff", "--no-index",
                str(old_main_directory),
                str(new_main_directory)
            ],
            stdout=PIPE,
            stderr=PIPE,
        ).stdout.decode("utf8")
        diff = (diff.replace("\\\\",
                             "\\").replace(str(old_main_directory),
                                           "").replace(str(new_main_directory),
                                                       ""))

        print("The following diff would be applied:\n")
        print(diff)
        print("")

        if not skip_apply_ask and not skip_update:  # pragma: no cover
            update_str: str = ""
            while update_str not in ("y", "n", "s"):
                print(
                    'Respond with "s" to intentionally skip the update while marking '
                    "your project as up-to-date.")
                update_str = input(
                    "Apply diff and update [y/n/s]? ").lower()  # nosec

            if update_str == "n":
                sys.exit("User cancelled Cookiecutter template update.")
            elif update_str == "s":
                skip_update = True

        current_directory = Path.cwd()
        try:
            os.chdir(expanded_dir_path)
            if not skip_update:
                run(["patch", "-p1", "--merge"], input=diff.encode("utf8"))

            cruft_state["commit"] = last_commit
            cruft_state["context"] = new_context
            cruft_state["directory"] = directory
            cruft_file.write_text(json_dumps(cruft_state))
        finally:
            os.chdir(current_directory)

        return True
Esempio n. 5
0
def update(expanded_dir: str = ".",
           cookiecutter_input: bool = False,
           skip_apply_ask: bool = False) -> bool:
    """Update specified project's cruft to the latest and greatest release."""
    cruft_file = os.path.join(expanded_dir, ".cruft.json")
    if not os.path.isfile(cruft_file):
        raise NoCruftFound(os.path.abspath(expanded_dir))

    with open(cruft_file) as cruft_open_file:
        cruft_state = json.load(cruft_open_file)
        with TemporaryDirectory() as compare_directory:
            template_dir = os.path.join(compare_directory, "template")

            try:
                repo = Repo.clone_from(cruft_state["template"], template_dir)
                last_commit = repo.head.object.hexsha
            except Exception as e:
                raise InvalidCookiecutterRepository(e)

            if last_commit == cruft_state["commit"] or not repo.index.diff(
                    cruft_state["commit"]):
                return False

            context_file = os.path.join(template_dir, "cookiecutter.json")

            new_output_dir = os.path.join(compare_directory, "new_output")

            new_context = _generate_output(
                context_file=context_file,
                cruft_state=cruft_state,
                cookiecutter_input=cookiecutter_input,
                template_dir=template_dir,
                output_dir=new_output_dir,
            )

            old_output_dir = os.path.join(compare_directory, "old_output")
            repo.head.reset(commit=cruft_state["commit"], working_tree=True)
            _generate_output(
                context_file=context_file,
                cruft_state=cruft_state,
                cookiecutter_input=cookiecutter_input,
                template_dir=template_dir,
                output_dir=old_output_dir,
            )

            main_directory: str = ""
            for file_name in os.listdir(old_output_dir):
                file_path = os.path.join(old_output_dir, file_name)
                if os.path.isdir(file_path):
                    main_directory = file_name

            new_main_directory = os.path.join(new_output_dir, main_directory)
            old_main_directory = os.path.join(old_output_dir, main_directory)

            diff = run(["git", "diff", old_main_directory, new_main_directory],
                       capture_output=True).stdout.decode("utf8")
            diff = diff.replace(old_main_directory,
                                "").replace(new_main_directory, "")

            print("The following diff would be applied:\n")
            print(diff)
            print("")

            if not skip_apply_ask:
                update = ""
                while update.lower() not in ("y", "n"):
                    update = input("Apply diff and update [y/n]? ")  # nosec

                if update.lower() == "n":
                    sys.exit("User cancelled Cookiecutter template update.")

            current_directory = os.getcwd()
            try:
                os.chdir(expanded_dir)
                run(["git", "apply"], input=diff.encode("utf8"))

                cruft_state["commit"] = last_commit
                cruft_state["context"] = new_context
                with open(cruft_file, "w") as cruft_output:
                    json_dump(cruft_state, cruft_output)
            finally:
                os.chdir(current_directory)

            return True