diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index 72a04aa..4651a85 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -1,5 +1,5 @@ - id: gira name: gira - entry: gira-precommit - language: system + entry: gira + language: python stages: [prepare-commit-msg] diff --git a/README.md b/README.md index 5c64755..2bf43b9 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,27 @@ # Gira -Gira gathers JIRA tickets from commit messages of your updated dependencies when you -change them in one of: `pyproject.toml`, `poetry.lock`, `west.yaml`, and `pubspec.yaml`. -It is especially usefull in "prepare-commit-msg" stage of [pre-commit](https://pre-commit.com) +Gira reacts on changes in your dependency files and bring you JIRA tickets mentioned in commits between current and updated version of the dependencies. Supported files are: -//Disclaimer: works the best if your dependencies follow [semantic release](https://semantic-release.gitbook.io/semantic-release/) (have tags in `v{X.Y.*}` format)// +- pyproject.toml +- poetry.lock +- west.yaml +- pubspec.yaml + + It is especially usefull in [pre-commit](https://pre-commit.com). + +___Pssst__: works the best if your dependencies follow [semantic release](https://semantic-release.gitbook.io/semantic-release/) thus have tags in `vX.Y.*` format. ## Usage ```bash -gira [-r revision] [--format="commit|detail|markdown"] +gira [-r revision] [-c config] [--format="commit|detail|markdown"] ``` -Revision can be tag/branch or a commit. Gira will check dependency files for changes between -the current version and the revision version. +Revision can be tag, branch, or a commit. By default `gira` check for staged/unstaged changes and if no are found then it diffs to the previous commit. This is the way to tell `gira` to diff to a different tag (for example). -Format is useful if you generate into a commit message (only ticket names (e.g. JIRA-1234)) -or you want a user readable "detailed" print or the same but markdown formatted. +Format can be short or detailed. Detailed formats will try to reach to your JIRA server for the details. The short (default) version is useful as suffix to +commit messages. The markdown version is intended for changelogs. ```bash $ gira [--format=commit] @@ -25,6 +29,21 @@ internal-dependency1 => : JIRA-123, JIRA-567 other-followed-lib => : JIRA-876, JIRA-543 ``` +Config file is by default _.gira.yaml_ but can be pretty much any YAML or pyproject.toml. See section bellow. + +## Pre-commit + +Add to your .pre-commit-config.yaml + +```yaml + - repo: https://github.com/dronetag/gira + rev: v1.0.0 + hooks: + - id: gira + # if you use other config than .gira.yaml then + # args: ["-c", "your-config.yaml"] +``` + ## Configuration Gira is configured either by pyproject.toml or standalone .gira.yaml or actually any other @@ -32,14 +51,20 @@ YAML file that you specify with `-c` and has "gira.observe" and optionally "gira ### Observed Dependencies -Observed dependencies are in form of NAME=git-url where NAME must be the same as specified -in your dependency file (e.g. pyproject.toml or a YAML). +Observed dependencies are in form of NAME=git-url where NAME must be the same as specified in your dependency/lock file. ```toml [tool.gira.observe] internal-lib1 = "github.com/company/internal-lib1" other-dependency = "bitbucket.com/company/other-dependency" ``` +or +```yaml +gira: + observe: + internal-lib1: "github.com/company/internal-lib1" + other-dependency: "bitbucket.com/company/other-dependency" +``` ### Submodules diff --git a/gira/config.py b/gira/config.py index 2349f28..1ec1c6d 100644 --- a/gira/config.py +++ b/gira/config.py @@ -12,12 +12,7 @@ from . import logger from .jira import Jira -DEFAULT_CONFIG_PATHS = ( - ".gira.yaml", - "pyproject.toml", - "west.yml", - "west.yaml", -) +DEFAULT_CONFIG = Path(".gira.yaml") class Config: @@ -36,19 +31,17 @@ def from_file(path: Optional[Path]) -> Config: if path and path.exists(): return _parse_file(path) - for path_str in DEFAULT_CONFIG_PATHS: - if Path(path_str).exists(): - logger.debug(f"Loading configuration from {path_str}") - return _parse_file(Path(path_str)) + if DEFAULT_CONFIG.exists(): + return _parse_file(DEFAULT_CONFIG) raise FileNotFoundError("No configuration file found") def _parse_file(path: Path) -> Config: - if path.name == "pyproject.toml": - return _pytoml(path) if path.name == ".gira.yaml": return _conf(path) + if path.name == "pyproject.toml": + return _pytoml(path) if path.name.startswith("west"): return _west(path) if path.name.endswith(".yaml"): diff --git a/gira/formatter.py b/gira/formatter.py index 8aea970..a9c2872 100644 --- a/gira/formatter.py +++ b/gira/formatter.py @@ -5,7 +5,7 @@ def get_formatter(name: str, stream: TextIO): - if name == "commit": + if name in ("commit", "short"): return CommitFormatter(stream) if name in ("detail", "detailed"): return DetailFormatter(stream) diff --git a/gira/repo.py b/gira/repo.py index 6c477c6..eb413d1 100644 --- a/gira/repo.py +++ b/gira/repo.py @@ -12,12 +12,14 @@ class Repo: bare: bool repo: pygit2.Repository _submodules: Optional[dict[Path, str]] + _diff_cached: bool MESSAGE_LIMIT = 250 def __init__(self, path: Path, ref: Optional[str] = "", bare: bool = False): self.path = path self.repo = pygit2.Repository(str(path), pygit2.GIT_REPOSITORY_OPEN_BARE if bare else 0) self.bare = bare + self._diff_cached = False try: self.ref = self._check_ref(ref) except KeyError as e: @@ -52,7 +54,7 @@ def changed_files(self) -> list[Path]: def submodule_change(self, submodule_path: Path) -> tuple[str, str]: """Return list of lines added and removed in the diff""" try: - diffs = self.repo.diff(self.ref, context_lines=0) + diffs = self.repo.diff(self.ref, context_lines=0, cached=self._diff_cached) except KeyError: raise RuntimeError(f"Revision {self.ref} does not exist") @@ -95,8 +97,11 @@ def _check_ref(self, ref: Optional[str]): return "refs/tags/" + ref except KeyError: return "refs/heads/" + ref - if self.repo and len(self.repo.diff("HEAD")) == 0: - return "HEAD^" + if self.repo and len(self.repo.diff("HEAD")) > 0: + return "HEAD" + if self.repo and len(self.repo.diff("HEAD", cached=True)) > 0: + self._diff_cached = True + return "HEAD" return "HEAD" def messages(self, a: str, b: Optional[str] = None): diff --git a/tests/local-template/.gira.yaml b/tests/local-template/.gira.yaml new file mode 100644 index 0000000..d2c43ca --- /dev/null +++ b/tests/local-template/.gira.yaml @@ -0,0 +1,2 @@ +observed: + dep1: "file:///${GIRA_TEST_ROOT}/remote/dep1/.git" diff --git a/tests/local-template/poetry/poetry.lock b/tests/local-template/poetry/poetry.lock index 89c8ffc..c399ef1 100644 --- a/tests/local-template/poetry/poetry.lock +++ b/tests/local-template/poetry/poetry.lock @@ -15,7 +15,7 @@ files = [ colorama = {version = "*", markers = "platform_system == \"Windows\""} [[package]] -name = "dep1" +name = "dep1-poetry" version = "1.0.0" description = "The dummy dependency" optional = false diff --git a/tests/local-template/poetry/pyproject.toml b/tests/local-template/poetry/pyproject.toml index e294d9c..a0ce537 100644 --- a/tests/local-template/poetry/pyproject.toml +++ b/tests/local-template/poetry/pyproject.toml @@ -20,7 +20,7 @@ classifiers = [ [tool.poetry.dependencies] python = "^3.8" -dep1 = "1.0.0" +dep1-poetry = "1.0.0" wheel = "*" pytest = {version="*", optional=true} @@ -28,4 +28,4 @@ pytest = {version="*", optional=true} dev = ["wheel", "pytest"] [tool.gira.observe] -dep1 = "file://${GIRA_TEST_ROOT}/remote/dep1/.git" +dep1-poetry = "file://${GIRA_TEST_ROOT}/remote/dep1/.git" diff --git a/tests/local-template/pubspec.yaml b/tests/local-template/pubspec.yaml index e8a3d02..135c665 100644 --- a/tests/local-template/pubspec.yaml +++ b/tests/local-template/pubspec.yaml @@ -14,7 +14,7 @@ environment: dependencies: analyzer: ^5.13.0 - dep1: + dep1-pubspec: git: url: file:///${GIRA_TEST_ROOT}/remote/dep1/.git ref: v1.0.0 @@ -22,4 +22,4 @@ dependencies: gira: observe: - dep1: "file:///${GIRA_TEST_ROOT}/remote/dep1/.git" + dep1-pubspec: "file:///${GIRA_TEST_ROOT}/remote/dep1/.git" diff --git a/tests/local-template/pyproject.toml b/tests/local-template/pyproject.toml index 92ec5b0..c7500c8 100644 --- a/tests/local-template/pyproject.toml +++ b/tests/local-template/pyproject.toml @@ -16,8 +16,8 @@ dependencies = [ "tomli; python_version < '3.11'", "pyyaml", "jira", - "dep1==1.0.0", + "dep1-pytoml==1.0.0", ] [tool.gira.observe] -dep1 = "file://${GIRA_TEST_ROOT}/remote/dep1/.git" +dep1-pytoml = "file://${GIRA_TEST_ROOT}/remote/dep1/.git" diff --git a/tests/local-template/west.yaml b/tests/local-template/west.yaml index 3464488..23033b6 100644 --- a/tests/local-template/west.yaml +++ b/tests/local-template/west.yaml @@ -9,11 +9,11 @@ manifest: remote: local projects: - - name: dep1 + - name: dep1-west repo-path: libs/dep1 revision: v1.0.0 import: true gira: observe: - dep1: "file://${GIRA_TEST_ROOT}/remote/dep1/.git" + dep1-west: "file://${GIRA_TEST_ROOT}/remote/dep1/.git" diff --git a/tests/run.sh b/tests/run.sh index 25d4fa6..217c053 100644 --- a/tests/run.sh +++ b/tests/run.sh @@ -16,61 +16,79 @@ envsubst < local-template/poetry/poetry.lock > local/poetry/poetry.lock envsubst < local-template/pyproject.toml > local/pyproject.toml envsubst < local-template/pubspec.yaml > local/pubspec.yaml envsubst < local-template/west.yaml > local/west.yaml +envsubst < local-template/.gira.yaml > local/.gira.yaml pushd local -git init +git init 2> /dev/null git add . -git commit -m "Initial commit" +git commit -m "Initial commit" 2> /dev/null +# run on no changes in dependencies +echo "-- Test no changes" > README.md +gira +git reset README.md +rm README.md ############################################# ## run tests -echo "Test poetry/poetry.lock" +echo "-- Test poetry/poetry.lock" git reset --hard rm -rf .gira_cache output.txt sed -i 's/1.0.0/1.1.0/g' poetry/poetry.lock -gira > output.txt +gira -c poetry/pyproject.toml | tee output.txt +grep dep1-poetry output.txt +grep "1.0.0" output.txt +grep "1.1.0" output.txt grep OCD-1234 output.txt grep -v OCD-567 output.txt -echo "Test poetry/pyproject.toml" +echo "-- Test poetry/pyproject.toml" git reset --hard rm -rf .gira_cache output.txt sed -i 's/1.0.0/1.1.1/g' poetry/pyproject.toml gira -c poetry/pyproject.toml > output.txt +grep dep1-poetry output.txt +grep "1.0.0" output.txt +grep "1.1.1" output.txt grep OCD-1234 output.txt grep OCD-567 output.txt -echo "Test pyproject.toml" +echo "-- Test pyproject.toml" git reset --hard rm -rf .gira_cache output.txt sed -i 's/1.0.0/1.1.0/g' pyproject.toml -gira > output.txt +gira -c pyproject.toml > output.txt +grep dep1-pytoml output.txt +grep "1.0.0" output.txt +grep "1.1.0" output.txt grep OCD-1234 output.txt grep -v OCD-567 output.txt -echo "Test pubspec.yaml" +echo "-- Test pubspec.yaml" git reset --hard rm -rf .gira_cache output.txt sed -i 's/1.0.0/1.1.1/g' pubspec.yaml gira -c pubspec.yaml > output.txt +grep dep1-pubspec output.txt grep OCD-1234 output.txt grep OCD-567 output.txt -echo "Test pubspec.yaml" +echo "-- Test pubspec.yaml" git reset --hard rm -rf .gira_cache output.txt sed -i 's/1.0.0/1.1.0/g' west.yaml gira -c west.yaml > output.txt +grep dep1-west output.txt grep OCD-1234 output.txt grep -v OCD-567 output.txt -echo "Test moving from 1.0.0 to 1.1.0 and then to 1.1.1" + +echo "-- Test moving from 1.0.0 to 1.1.0 and then to 1.1.1" git reset --hard rm -rf .gira_cache output.txt sed -i 's/1.0.0/1.1.0/g' west.yaml @@ -84,7 +102,7 @@ grep OCD-1234 output.txt grep OCD-567 output.txt -echo "Test pre-commit" +echo "-- Test pre-commit" git reset --hard rm -rf .gira_cache output.txt sed -i 's/1.0.0/1.1.1/g' west.yaml