esys-gitlog 0.1.0__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {esys_gitlog-0.1.0/src/esys_gitlog.egg-info → esys_gitlog-0.2.0}/PKG-INFO +9 -9
- {esys_gitlog-0.1.0 → esys_gitlog-0.2.0}/README.md +8 -8
- {esys_gitlog-0.1.0 → esys_gitlog-0.2.0}/pyproject.toml +2 -1
- {esys_gitlog-0.1.0 → esys_gitlog-0.2.0}/src/esys_gitlog/__init__.py +1 -1
- {esys_gitlog-0.1.0 → esys_gitlog-0.2.0}/src/esys_gitlog/cli.py +6 -2
- {esys_gitlog-0.1.0 → esys_gitlog-0.2.0}/src/esys_gitlog/config.py +12 -5
- {esys_gitlog-0.1.0 → esys_gitlog-0.2.0}/src/esys_gitlog/display.py +21 -11
- {esys_gitlog-0.1.0 → esys_gitlog-0.2.0}/src/esys_gitlog/identifiers.py +30 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.2.0}/src/esys_gitlog/interpret.py +28 -4
- {esys_gitlog-0.1.0 → esys_gitlog-0.2.0}/src/esys_gitlog/preferences.py +1 -1
- {esys_gitlog-0.1.0 → esys_gitlog-0.2.0/src/esys_gitlog.egg-info}/PKG-INFO +9 -9
- {esys_gitlog-0.1.0 → esys_gitlog-0.2.0}/src/esys_gitlog.egg-info/entry_points.txt +1 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.2.0}/tests/test_browser_display.py +2 -1
- {esys_gitlog-0.1.0 → esys_gitlog-0.2.0}/tests/test_installed_launcher.py +31 -12
- {esys_gitlog-0.1.0 → esys_gitlog-0.2.0}/tests/test_semantic_p4.py +26 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.2.0}/LICENSE.txt +0 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.2.0}/setup.cfg +0 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.2.0}/src/esys_gitlog/__main__.py +0 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.2.0}/src/esys_gitlog/app.py +0 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.2.0}/src/esys_gitlog/detection.py +0 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.2.0}/src/esys_gitlog/git_adapter.py +0 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.2.0}/src/esys_gitlog/links.py +0 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.2.0}/src/esys_gitlog/model.py +0 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.2.0}/src/esys_gitlog/pager.py +0 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.2.0}/src/esys_gitlog/pipeline.py +0 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.2.0}/src/esys_gitlog/qa.py +0 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.2.0}/src/esys_gitlog/render.py +0 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.2.0}/src/esys_gitlog/report.py +0 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.2.0}/src/esys_gitlog/tool_paths.py +0 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.2.0}/src/esys_gitlog.egg-info/SOURCES.txt +0 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.2.0}/src/esys_gitlog.egg-info/dependency_links.txt +0 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.2.0}/src/esys_gitlog.egg-info/requires.txt +0 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.2.0}/src/esys_gitlog.egg-info/top_level.txt +0 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.2.0}/tests/test_cli.py +0 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.2.0}/tests/test_coverage_gaps.py +0 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.2.0}/tests/test_detection.py +0 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.2.0}/tests/test_git_adapter.py +0 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.2.0}/tests/test_pager.py +0 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.2.0}/tests/test_preferences.py +0 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.2.0}/tests/test_render.py +0 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.2.0}/tests/test_semantic_default.py +0 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.2.0}/tests/test_smoke.py +0 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.2.0}/tests/test_tool_paths.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: esys-gitlog
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Python implementation of git elog
|
|
5
5
|
Author: Project maintainers
|
|
6
6
|
License-Expression: MIT
|
|
@@ -22,7 +22,7 @@ Dynamic: license-file
|
|
|
22
22
|
|
|
23
23
|
# esys-gitlog
|
|
24
24
|
|
|
25
|
-
Python implementation of `git elog`. The distribution name is `esys-gitlog`. The import package is `esys_gitlog`.
|
|
25
|
+
Python implementation of `git elog`. The distribution name is `esys-gitlog`. The import package is `esys_gitlog`. Installed commands: `git-elog` (so Git can run `git elog`) and the shorter alias `elog`. Prefer `elog --help` — `git elog --help` is handled by Git's own help path, not the Typer CLI.
|
|
26
26
|
|
|
27
27
|
The shared contract lives in `esys-gitlog-spec`. This repository carries that contract in Python. It does not define a second one.
|
|
28
28
|
|
|
@@ -30,8 +30,8 @@ The shared contract lives in `esys-gitlog-spec`. This repository carries that co
|
|
|
30
30
|
|
|
31
31
|
```bash
|
|
32
32
|
pip install .
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
elog --help
|
|
34
|
+
elog --json
|
|
35
35
|
```
|
|
36
36
|
|
|
37
37
|
`--json` writes one QA report document to stdout. The first skeleton returns an empty `commits` array.
|
|
@@ -52,15 +52,15 @@ Layered tool preferences (user → `.tools/esys/esys-gitlog/` → `.tools_local/
|
|
|
52
52
|
and named profiles follow root SADR-0009 / SADR-0010 and contract CADR-0012.
|
|
53
53
|
|
|
54
54
|
```bash
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
55
|
+
elog --preferences path.json # preference overlay
|
|
56
|
+
elog --profile acme # built-in or profiles/acme.json
|
|
57
|
+
elog --config path/profile.json # one-off profile file
|
|
58
|
+
elog preferences detect # probe jira/gh/glab/git remote; write missing user keys
|
|
59
59
|
```
|
|
60
60
|
|
|
61
61
|
## Standalone binary (optional)
|
|
62
62
|
|
|
63
|
-
The primary install remains the wheel + `git-elog` console
|
|
63
|
+
The primary install remains the wheel + `git-elog` / `elog` console scripts. For a
|
|
64
64
|
frozen one-file binary (for example Cursor `esysGitlog.executablePath` without
|
|
65
65
|
a Python env on PATH):
|
|
66
66
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# esys-gitlog
|
|
2
2
|
|
|
3
|
-
Python implementation of `git elog`. The distribution name is `esys-gitlog`. The import package is `esys_gitlog`.
|
|
3
|
+
Python implementation of `git elog`. The distribution name is `esys-gitlog`. The import package is `esys_gitlog`. Installed commands: `git-elog` (so Git can run `git elog`) and the shorter alias `elog`. Prefer `elog --help` — `git elog --help` is handled by Git's own help path, not the Typer CLI.
|
|
4
4
|
|
|
5
5
|
The shared contract lives in `esys-gitlog-spec`. This repository carries that contract in Python. It does not define a second one.
|
|
6
6
|
|
|
@@ -8,8 +8,8 @@ The shared contract lives in `esys-gitlog-spec`. This repository carries that co
|
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
10
|
pip install .
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
elog --help
|
|
12
|
+
elog --json
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
`--json` writes one QA report document to stdout. The first skeleton returns an empty `commits` array.
|
|
@@ -30,15 +30,15 @@ Layered tool preferences (user → `.tools/esys/esys-gitlog/` → `.tools_local/
|
|
|
30
30
|
and named profiles follow root SADR-0009 / SADR-0010 and contract CADR-0012.
|
|
31
31
|
|
|
32
32
|
```bash
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
elog --preferences path.json # preference overlay
|
|
34
|
+
elog --profile acme # built-in or profiles/acme.json
|
|
35
|
+
elog --config path/profile.json # one-off profile file
|
|
36
|
+
elog preferences detect # probe jira/gh/glab/git remote; write missing user keys
|
|
37
37
|
```
|
|
38
38
|
|
|
39
39
|
## Standalone binary (optional)
|
|
40
40
|
|
|
41
|
-
The primary install remains the wheel + `git-elog` console
|
|
41
|
+
The primary install remains the wheel + `git-elog` / `elog` console scripts. For a
|
|
42
42
|
frozen one-file binary (for example Cursor `esysGitlog.executablePath` without
|
|
43
43
|
a Python env on PATH):
|
|
44
44
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "esys-gitlog"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.2.0"
|
|
8
8
|
description = "Python implementation of git elog"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -30,6 +30,7 @@ standalone = [
|
|
|
30
30
|
|
|
31
31
|
[project.scripts]
|
|
32
32
|
git-elog = "esys_gitlog.cli:main"
|
|
33
|
+
elog = "esys_gitlog.cli:main"
|
|
33
34
|
|
|
34
35
|
[tool.setuptools]
|
|
35
36
|
package-dir = { "" = "src" }
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Thin Typer command boundary for ``git-elog`` / ``git elog``."""
|
|
1
|
+
"""Thin Typer command boundary for ``git-elog`` / ``elog`` / ``git elog``."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
@@ -316,7 +316,11 @@ def preferences_detect(
|
|
|
316
316
|
|
|
317
317
|
def main() -> None:
|
|
318
318
|
"""Console script entry point."""
|
|
319
|
-
|
|
319
|
+
invoked = Path(sys.argv[0]).name
|
|
320
|
+
if invoked.lower().endswith(".exe"):
|
|
321
|
+
invoked = invoked[:-4]
|
|
322
|
+
prog_name = invoked or "git-elog"
|
|
323
|
+
app(args=prepare_argv(sys.argv[1:]), prog_name=prog_name)
|
|
320
324
|
|
|
321
325
|
|
|
322
326
|
if __name__ == "__main__":
|
|
@@ -49,9 +49,9 @@ class ProfileRules:
|
|
|
49
49
|
|
|
50
50
|
name: str
|
|
51
51
|
conventional: bool = True
|
|
52
|
-
conventional_locations: tuple[str, ...] = ("title",)
|
|
52
|
+
conventional_locations: tuple[str, ...] = ("title", "trailer")
|
|
53
53
|
references: bool = True
|
|
54
|
-
reference_locations: tuple[str, ...] = ("refs"
|
|
54
|
+
reference_locations: tuple[str, ...] = ("prefix", "suffix", "refs")
|
|
55
55
|
identifier_family: str = "jira-id"
|
|
56
56
|
signed_off_by: bool = True
|
|
57
57
|
types: tuple[str, ...] = DEFAULT_TYPES
|
|
@@ -70,7 +70,7 @@ class AnalysisConfig:
|
|
|
70
70
|
display: DisplayMode = "subject-precedence"
|
|
71
71
|
preferences: Preferences = field(default_factory=empty_preferences)
|
|
72
72
|
# Human history: fixed-width refs / minimum Type(Scope) columns.
|
|
73
|
-
ref_id_width: int =
|
|
73
|
+
ref_id_width: int = 14
|
|
74
74
|
type_width: int = 10
|
|
75
75
|
show_author: bool = False
|
|
76
76
|
show_date: bool = False
|
|
@@ -87,6 +87,7 @@ def _trailer_profile() -> ProfileRules:
|
|
|
87
87
|
return ProfileRules(
|
|
88
88
|
name="trailer",
|
|
89
89
|
conventional_locations=("trailer",),
|
|
90
|
+
reference_locations=("refs",),
|
|
90
91
|
)
|
|
91
92
|
|
|
92
93
|
|
|
@@ -97,8 +98,14 @@ KNOWN_PROFILES: dict[str, ProfileRules] = {
|
|
|
97
98
|
|
|
98
99
|
|
|
99
100
|
def _profile_from_object(data: dict[str, Any], *, name: str = "custom") -> ProfileRules:
|
|
100
|
-
conventional_locations = data.get(
|
|
101
|
-
|
|
101
|
+
conventional_locations = data.get(
|
|
102
|
+
"conventionalLocations",
|
|
103
|
+
["title", "trailer"],
|
|
104
|
+
)
|
|
105
|
+
reference_locations = data.get(
|
|
106
|
+
"referenceLocations",
|
|
107
|
+
["prefix", "suffix", "refs"],
|
|
108
|
+
)
|
|
102
109
|
types = data.get("types", list(DEFAULT_TYPES))
|
|
103
110
|
reference_keys = data.get("referenceKeys", ["Refs"])
|
|
104
111
|
# Legacy profile link keys are ignored (CADR-0012); prefer preference files.
|
|
@@ -2,17 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
import re
|
|
6
|
-
|
|
7
5
|
from esys_gitlog.config import AnalysisConfig
|
|
8
|
-
from esys_gitlog.identifiers import
|
|
6
|
+
from esys_gitlog.identifiers import (
|
|
7
|
+
matches_family,
|
|
8
|
+
ref_identity_key,
|
|
9
|
+
strip_subject_edge_refs,
|
|
10
|
+
)
|
|
9
11
|
from esys_gitlog.interpret import title_pattern
|
|
10
12
|
from esys_gitlog.links import resolve_ref_url
|
|
11
13
|
from esys_gitlog.model import CommitResult, DisplayRef, DisplaySelection
|
|
12
14
|
|
|
13
|
-
_SUFFIX_REF = re.compile(r"(?:\s+|\[)(?P<token>[A-Za-z][A-Za-z0-9._-]*-\d+)\]?\s*$")
|
|
14
|
-
_PREFIX_REF = re.compile(r"^(?:\[)?(?P<token>[A-Za-z][A-Za-z0-9._-]*-\d+)(?:\])?(?::|\s+)\s*")
|
|
15
|
-
|
|
16
15
|
|
|
17
16
|
def _pick_single(
|
|
18
17
|
result: CommitResult,
|
|
@@ -38,14 +37,25 @@ def _pick_single(
|
|
|
38
37
|
def _cleaned_title(subject: str, config: AnalysisConfig) -> str:
|
|
39
38
|
line = subject.splitlines()[0] if subject else ""
|
|
40
39
|
rules = config.profile
|
|
40
|
+
strip_prefix = rules.references and "prefix" in rules.reference_locations
|
|
41
|
+
strip_suffix = rules.references and "suffix" in rules.reference_locations
|
|
41
42
|
if rules.conventional and "title" in rules.conventional_locations:
|
|
42
43
|
match = title_pattern(rules.types).fullmatch(line)
|
|
43
44
|
if match is not None:
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
description = match.group("description")
|
|
46
|
+
cleaned = strip_subject_edge_refs(
|
|
47
|
+
description,
|
|
48
|
+
strip_prefix=strip_prefix,
|
|
49
|
+
strip_suffix=strip_suffix,
|
|
50
|
+
)
|
|
51
|
+
return cleaned or description
|
|
52
|
+
if rules.references and (strip_prefix or strip_suffix):
|
|
53
|
+
stripped = strip_subject_edge_refs(
|
|
54
|
+
line,
|
|
55
|
+
strip_prefix=strip_prefix,
|
|
56
|
+
strip_suffix=strip_suffix,
|
|
57
|
+
)
|
|
58
|
+
return stripped or line
|
|
49
59
|
return line
|
|
50
60
|
|
|
51
61
|
|
|
@@ -7,6 +7,11 @@ import re
|
|
|
7
7
|
# Jira key and the same shape used by YouTrack / Linear.
|
|
8
8
|
_JIRA_KEY = re.compile(r"^[A-Za-z][A-Za-z0-9_]*-\d+$")
|
|
9
9
|
|
|
10
|
+
# Subject-edge reference tokens (CADR-0005): bare id or optional square brackets.
|
|
11
|
+
# Prefix may use a trailing colon instead of whitespace.
|
|
12
|
+
_SUBJECT_PREFIX_REF = re.compile(r"^(?:\[)?(?P<token>[A-Za-z][A-Za-z0-9_]*-\d+)(?:\])?(?::|\s+)\s*")
|
|
13
|
+
_SUBJECT_SUFFIX_REF = re.compile(r"(?:\s+|\[)(?P<token>[A-Za-z][A-Za-z0-9_]*-\d+)\]?\s*$")
|
|
14
|
+
|
|
10
15
|
# Shapes from references.md that any-id accepts beyond the Jira key.
|
|
11
16
|
_ANY_EXTRA = (
|
|
12
17
|
re.compile(r"^#\d+$"),
|
|
@@ -44,3 +49,28 @@ def matches_family(token: str, family: str) -> bool:
|
|
|
44
49
|
def ref_identity_key(token: str) -> str:
|
|
45
50
|
"""Letter case does not make a different id."""
|
|
46
51
|
return token.casefold()
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def match_subject_prefix_ref(subject_line: str) -> re.Match[str] | None:
|
|
55
|
+
"""Match a leading subject-edge reference, if any."""
|
|
56
|
+
return _SUBJECT_PREFIX_REF.match(subject_line)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def match_subject_suffix_ref(subject_line: str) -> re.Match[str] | None:
|
|
60
|
+
"""Match a trailing subject-edge reference, if any."""
|
|
61
|
+
return _SUBJECT_SUFFIX_REF.search(subject_line)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def strip_subject_edge_refs(
|
|
65
|
+
subject_line: str,
|
|
66
|
+
*,
|
|
67
|
+
strip_prefix: bool,
|
|
68
|
+
strip_suffix: bool,
|
|
69
|
+
) -> str:
|
|
70
|
+
"""Remove enabled edge references from a subject (or description) line."""
|
|
71
|
+
text = subject_line
|
|
72
|
+
if strip_prefix:
|
|
73
|
+
text = _SUBJECT_PREFIX_REF.sub("", text, count=1)
|
|
74
|
+
if strip_suffix:
|
|
75
|
+
text = _SUBJECT_SUFFIX_REF.sub("", text)
|
|
76
|
+
return text.strip()
|
|
@@ -5,11 +5,14 @@ from __future__ import annotations
|
|
|
5
5
|
import re
|
|
6
6
|
|
|
7
7
|
from esys_gitlog.config import AnalysisConfig, ProfileRules
|
|
8
|
-
from esys_gitlog.identifiers import
|
|
8
|
+
from esys_gitlog.identifiers import (
|
|
9
|
+
match_subject_prefix_ref,
|
|
10
|
+
match_subject_suffix_ref,
|
|
11
|
+
matches_family,
|
|
12
|
+
)
|
|
9
13
|
from esys_gitlog.model import Claim, CommitResult
|
|
10
14
|
|
|
11
15
|
_TRAILER_LINE = re.compile(r"^(?P<key>[A-Za-z0-9][A-Za-z0-9 -]*):\s?(?P<value>.*)$")
|
|
12
|
-
_PREFIX_REF = re.compile(r"^(?P<token>[A-Za-z][A-Za-z0-9._-]*-\d+)\b")
|
|
13
16
|
_BREAKING_KEYS = frozenset({"breaking change", "breaking"})
|
|
14
17
|
|
|
15
18
|
|
|
@@ -117,6 +120,8 @@ def interpret_commit(subject: str, body: str, config: AnalysisConfig) -> CommitR
|
|
|
117
120
|
|
|
118
121
|
if rules.references and "prefix" in rules.reference_locations:
|
|
119
122
|
_interpret_prefix_refs(subject_line, rules, result)
|
|
123
|
+
if rules.references and "suffix" in rules.reference_locations:
|
|
124
|
+
_interpret_suffix_refs(subject_line, rules, result)
|
|
120
125
|
|
|
121
126
|
trailer_type_ok = False
|
|
122
127
|
for key, value, line, column, empty in _parse_trailers(body_lines):
|
|
@@ -265,7 +270,7 @@ def _interpret_title(subject_line: str, rules: ProfileRules, result: CommitResul
|
|
|
265
270
|
|
|
266
271
|
|
|
267
272
|
def _interpret_prefix_refs(subject_line: str, rules: ProfileRules, result: CommitResult) -> None:
|
|
268
|
-
match =
|
|
273
|
+
match = match_subject_prefix_ref(subject_line)
|
|
269
274
|
if match is None:
|
|
270
275
|
return
|
|
271
276
|
token = match.group("token")
|
|
@@ -275,7 +280,7 @@ def _interpret_prefix_refs(subject_line: str, rules: ProfileRules, result: Commi
|
|
|
275
280
|
value=token,
|
|
276
281
|
source="subject",
|
|
277
282
|
line=1,
|
|
278
|
-
column=1,
|
|
283
|
+
column=match.start("token") + 1,
|
|
279
284
|
key="prefix",
|
|
280
285
|
)
|
|
281
286
|
)
|
|
@@ -283,6 +288,25 @@ def _interpret_prefix_refs(subject_line: str, rules: ProfileRules, result: Commi
|
|
|
283
288
|
result.references_matched = True
|
|
284
289
|
|
|
285
290
|
|
|
291
|
+
def _interpret_suffix_refs(subject_line: str, rules: ProfileRules, result: CommitResult) -> None:
|
|
292
|
+
match = match_subject_suffix_ref(subject_line)
|
|
293
|
+
if match is None:
|
|
294
|
+
return
|
|
295
|
+
token = match.group("token")
|
|
296
|
+
result.claims.append(
|
|
297
|
+
Claim(
|
|
298
|
+
field="refs",
|
|
299
|
+
value=token,
|
|
300
|
+
source="subject",
|
|
301
|
+
line=1,
|
|
302
|
+
column=match.start("token") + 1,
|
|
303
|
+
key="suffix",
|
|
304
|
+
)
|
|
305
|
+
)
|
|
306
|
+
if matches_family(token, rules.identifier_family):
|
|
307
|
+
result.references_matched = True
|
|
308
|
+
|
|
309
|
+
|
|
286
310
|
def _interpret_refs(
|
|
287
311
|
key: str,
|
|
288
312
|
value: str,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: esys-gitlog
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Python implementation of git elog
|
|
5
5
|
Author: Project maintainers
|
|
6
6
|
License-Expression: MIT
|
|
@@ -22,7 +22,7 @@ Dynamic: license-file
|
|
|
22
22
|
|
|
23
23
|
# esys-gitlog
|
|
24
24
|
|
|
25
|
-
Python implementation of `git elog`. The distribution name is `esys-gitlog`. The import package is `esys_gitlog`.
|
|
25
|
+
Python implementation of `git elog`. The distribution name is `esys-gitlog`. The import package is `esys_gitlog`. Installed commands: `git-elog` (so Git can run `git elog`) and the shorter alias `elog`. Prefer `elog --help` — `git elog --help` is handled by Git's own help path, not the Typer CLI.
|
|
26
26
|
|
|
27
27
|
The shared contract lives in `esys-gitlog-spec`. This repository carries that contract in Python. It does not define a second one.
|
|
28
28
|
|
|
@@ -30,8 +30,8 @@ The shared contract lives in `esys-gitlog-spec`. This repository carries that co
|
|
|
30
30
|
|
|
31
31
|
```bash
|
|
32
32
|
pip install .
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
elog --help
|
|
34
|
+
elog --json
|
|
35
35
|
```
|
|
36
36
|
|
|
37
37
|
`--json` writes one QA report document to stdout. The first skeleton returns an empty `commits` array.
|
|
@@ -52,15 +52,15 @@ Layered tool preferences (user → `.tools/esys/esys-gitlog/` → `.tools_local/
|
|
|
52
52
|
and named profiles follow root SADR-0009 / SADR-0010 and contract CADR-0012.
|
|
53
53
|
|
|
54
54
|
```bash
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
55
|
+
elog --preferences path.json # preference overlay
|
|
56
|
+
elog --profile acme # built-in or profiles/acme.json
|
|
57
|
+
elog --config path/profile.json # one-off profile file
|
|
58
|
+
elog preferences detect # probe jira/gh/glab/git remote; write missing user keys
|
|
59
59
|
```
|
|
60
60
|
|
|
61
61
|
## Standalone binary (optional)
|
|
62
62
|
|
|
63
|
-
The primary install remains the wheel + `git-elog` console
|
|
63
|
+
The primary install remains the wheel + `git-elog` / `elog` console scripts. For a
|
|
64
64
|
frozen one-file binary (for example Cursor `esysGitlog.executablePath` without
|
|
65
65
|
a Python env on PATH):
|
|
66
66
|
|
|
@@ -13,7 +13,8 @@ def test_cleaned_title_strips_conventional_prefix() -> None:
|
|
|
13
13
|
assert result.display.type == "feat"
|
|
14
14
|
assert result.display.scope == "api"
|
|
15
15
|
assert result.display.breaking is True
|
|
16
|
-
assert result.display.title == "add login
|
|
16
|
+
assert result.display.title == "add login"
|
|
17
|
+
assert [ref.id for ref in result.display.refs] == ["PROJ-1"]
|
|
17
18
|
|
|
18
19
|
|
|
19
20
|
def test_display_refs_from_trailer() -> None:
|
|
@@ -19,10 +19,10 @@ def _venv_paths(venv: Path) -> tuple[Path, Path]:
|
|
|
19
19
|
return venv / "bin" / "python", venv / "bin"
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
def
|
|
22
|
+
def _console_script(scripts: Path, name: str) -> Path:
|
|
23
23
|
if sys.platform == "win32":
|
|
24
|
-
return scripts / "
|
|
25
|
-
return scripts /
|
|
24
|
+
return scripts / f"{name}.exe"
|
|
25
|
+
return scripts / name
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
@pytest.fixture(scope="module")
|
|
@@ -45,8 +45,10 @@ def installed_env(
|
|
|
45
45
|
check=True,
|
|
46
46
|
cwd=elsewhere,
|
|
47
47
|
)
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
git_elog = _console_script(scripts, "git-elog")
|
|
49
|
+
elog = _console_script(scripts, "elog")
|
|
50
|
+
assert git_elog.is_file(), f"missing installed launcher: {git_elog}"
|
|
51
|
+
assert elog.is_file(), f"missing installed alias: {elog}"
|
|
50
52
|
# Empty repo so --json is a finished walk with no commits (exit 0).
|
|
51
53
|
subprocess.run(["git", "init"], cwd=elsewhere, check=True, capture_output=True)
|
|
52
54
|
subprocess.run(
|
|
@@ -63,13 +65,13 @@ def installed_env(
|
|
|
63
65
|
)
|
|
64
66
|
env = os.environ.copy()
|
|
65
67
|
env["PATH"] = str(scripts) + os.pathsep + env.get("PATH", "")
|
|
66
|
-
return elsewhere,
|
|
68
|
+
return elsewhere, git_elog, elog, env
|
|
67
69
|
|
|
68
70
|
|
|
69
71
|
def test_git_elog_help_from_outside_source(
|
|
70
|
-
installed_env: tuple[Path, Path, dict[str, str]],
|
|
72
|
+
installed_env: tuple[Path, Path, Path, dict[str, str]],
|
|
71
73
|
) -> None:
|
|
72
|
-
cwd, launcher, env = installed_env
|
|
74
|
+
cwd, launcher, _elog, env = installed_env
|
|
73
75
|
completed = subprocess.run(
|
|
74
76
|
[str(launcher), "--help"],
|
|
75
77
|
cwd=cwd,
|
|
@@ -82,10 +84,27 @@ def test_git_elog_help_from_outside_source(
|
|
|
82
84
|
assert "--json" in completed.stdout
|
|
83
85
|
|
|
84
86
|
|
|
87
|
+
def test_elog_alias_help_from_outside_source(
|
|
88
|
+
installed_env: tuple[Path, Path, Path, dict[str, str]],
|
|
89
|
+
) -> None:
|
|
90
|
+
cwd, _git_elog, elog, env = installed_env
|
|
91
|
+
completed = subprocess.run(
|
|
92
|
+
[str(elog), "--help"],
|
|
93
|
+
cwd=cwd,
|
|
94
|
+
env=env,
|
|
95
|
+
capture_output=True,
|
|
96
|
+
text=True,
|
|
97
|
+
check=False,
|
|
98
|
+
)
|
|
99
|
+
assert completed.returncode == 0, completed.stderr
|
|
100
|
+
assert "--json" in completed.stdout
|
|
101
|
+
assert "Usage: elog" in completed.stdout
|
|
102
|
+
|
|
103
|
+
|
|
85
104
|
def test_git_elog_json_from_outside_source(
|
|
86
|
-
installed_env: tuple[Path, Path, dict[str, str]],
|
|
105
|
+
installed_env: tuple[Path, Path, Path, dict[str, str]],
|
|
87
106
|
) -> None:
|
|
88
|
-
cwd, launcher, env = installed_env
|
|
107
|
+
cwd, launcher, _elog, env = installed_env
|
|
89
108
|
completed = subprocess.run(
|
|
90
109
|
[str(launcher), "--json"],
|
|
91
110
|
cwd=cwd,
|
|
@@ -102,9 +121,9 @@ def test_git_elog_json_from_outside_source(
|
|
|
102
121
|
|
|
103
122
|
|
|
104
123
|
def test_git_elog_subcommand_when_git_available(
|
|
105
|
-
installed_env: tuple[Path, Path, dict[str, str]],
|
|
124
|
+
installed_env: tuple[Path, Path, Path, dict[str, str]],
|
|
106
125
|
) -> None:
|
|
107
|
-
cwd, _launcher, env = installed_env
|
|
126
|
+
cwd, _launcher, _elog, env = installed_env
|
|
108
127
|
git = subprocess.run(
|
|
109
128
|
["git", "--version"],
|
|
110
129
|
cwd=cwd,
|
|
@@ -99,6 +99,32 @@ def test_custom_config_prefix_refs(tmp_path: Path) -> None:
|
|
|
99
99
|
assert "metadata.profile.partial" in _codes(result)
|
|
100
100
|
|
|
101
101
|
|
|
102
|
+
def test_custom_config_suffix_bracket_refs(tmp_path: Path) -> None:
|
|
103
|
+
path = tmp_path / "profile.json"
|
|
104
|
+
path.write_text(
|
|
105
|
+
json.dumps(
|
|
106
|
+
{
|
|
107
|
+
"conventional": True,
|
|
108
|
+
"conventionalLocations": ["title"],
|
|
109
|
+
"references": True,
|
|
110
|
+
"referenceLocations": ["suffix", "refs"],
|
|
111
|
+
"identifierFamily": "jira-id",
|
|
112
|
+
}
|
|
113
|
+
),
|
|
114
|
+
encoding="utf-8",
|
|
115
|
+
)
|
|
116
|
+
result = analyze_commit(
|
|
117
|
+
"feat: Add a narrated video of the UI panel [PD10020-1284]",
|
|
118
|
+
"",
|
|
119
|
+
config_path=path,
|
|
120
|
+
)
|
|
121
|
+
assert result.references_matched
|
|
122
|
+
assert [claim.value for claim in result.claims_for("refs")] == ["PD10020-1284"]
|
|
123
|
+
assert result.display is not None
|
|
124
|
+
assert result.display.title == "Add a narrated video of the UI panel"
|
|
125
|
+
assert [ref.id for ref in result.display.refs] == ["PD10020-1284"]
|
|
126
|
+
|
|
127
|
+
|
|
102
128
|
def test_resolve_trailer_default_display() -> None:
|
|
103
129
|
config = resolve_config(profile="trailer")
|
|
104
130
|
assert config.display == "trailer-precedence"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|