ELC/pycracks

View on GitHub
pycracks/pycracks.py

Summary

Maintainability
A
0 mins
Test Coverage
from __future__ import annotations

import shlex
import subprocess
from pathlib import Path
from typing import Iterable

import git.exc
from git import Repo
from packaging.version import Version, parse

from .logger import logger


def run(test_command: str, paths: Iterable[Path], target_version: Version) -> bool:
    repo = get_repo()
    fetch_tags(repo)

    literal_latest_version = get_latest_version(repo)
    latest_version = parse(literal_latest_version)

    if target_version <= latest_version:
        logger.error("Target version should be greater than last version")
        return False

    chekcout_paths_from_reference(repo, literal_latest_version, paths)

    test_succeeded = run_test_command(test_command)

    discard_changes(repo)

    return is_breaking_change_expected(target_version, latest_version, test_succeeded)


def is_breaking_change_expected(
    target_version: Version, latest_version: Version, test_succeeded: bool
) -> bool:
    major_increased = target_version.major > latest_version.major
    return major_increased or test_succeeded


def get_repo(path: Path = Path()) -> Repo:
    try:
        return Repo(path)
    except git.exc.InvalidGitRepositoryError as exception:
        error_message = "repo not found"
        raise ValueError(error_message) from exception


def run_test_command(test_command: str) -> bool:
    command = shlex.split(test_command)
    logger.info("Running command %s", command)
    completed_proccess = subprocess.run(command)
    return completed_proccess.returncode == 0


def fetch_tags(repo: Repo) -> None:
    logger.info("Fetching Tags")
    for remote in repo.remotes:
        fetch_info = remote.fetch(tags=True)
        for info in fetch_info:
            logger.info("Successfully fetched %s at %s", info.ref, info.commit)


def get_latest_version(repo: Repo) -> str:
    logger.info("Get latest Tag")
    latest_tag: str = repo.git.describe(abbrev=0, tags=True)
    return latest_tag


def chekcout_paths_from_reference(
    repo: Repo, reference: str, paths: Iterable[Path]
) -> None:
    """Using checkout in sparse mode to bring only one path from reference"""
    for path in paths:
        logger.info("Get %s from %s", path, reference)
        repo.git.checkout(reference, "--", path)


def discard_changes(repo: Repo) -> None:
    logger.info("Discard Changes")
    repo.git.reset("HEAD")
    repo.git.checkout(".")