esys-gitlog 0.1.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.
Files changed (43) hide show
  1. esys_gitlog-0.1.0/LICENSE.txt +18 -0
  2. esys_gitlog-0.1.0/PKG-INFO +77 -0
  3. esys_gitlog-0.1.0/README.md +55 -0
  4. esys_gitlog-0.1.0/pyproject.toml +64 -0
  5. esys_gitlog-0.1.0/setup.cfg +4 -0
  6. esys_gitlog-0.1.0/src/esys_gitlog/__init__.py +3 -0
  7. esys_gitlog-0.1.0/src/esys_gitlog/__main__.py +6 -0
  8. esys_gitlog-0.1.0/src/esys_gitlog/app.py +215 -0
  9. esys_gitlog-0.1.0/src/esys_gitlog/cli.py +323 -0
  10. esys_gitlog-0.1.0/src/esys_gitlog/config.py +202 -0
  11. esys_gitlog-0.1.0/src/esys_gitlog/detection.py +145 -0
  12. esys_gitlog-0.1.0/src/esys_gitlog/display.py +82 -0
  13. esys_gitlog-0.1.0/src/esys_gitlog/git_adapter.py +370 -0
  14. esys_gitlog-0.1.0/src/esys_gitlog/identifiers.py +46 -0
  15. esys_gitlog-0.1.0/src/esys_gitlog/interpret.py +308 -0
  16. esys_gitlog-0.1.0/src/esys_gitlog/links.py +87 -0
  17. esys_gitlog-0.1.0/src/esys_gitlog/model.py +138 -0
  18. esys_gitlog-0.1.0/src/esys_gitlog/pager.py +106 -0
  19. esys_gitlog-0.1.0/src/esys_gitlog/pipeline.py +44 -0
  20. esys_gitlog-0.1.0/src/esys_gitlog/preferences.py +380 -0
  21. esys_gitlog-0.1.0/src/esys_gitlog/qa.py +196 -0
  22. esys_gitlog-0.1.0/src/esys_gitlog/render.py +372 -0
  23. esys_gitlog-0.1.0/src/esys_gitlog/report.py +117 -0
  24. esys_gitlog-0.1.0/src/esys_gitlog/tool_paths.py +62 -0
  25. esys_gitlog-0.1.0/src/esys_gitlog.egg-info/PKG-INFO +77 -0
  26. esys_gitlog-0.1.0/src/esys_gitlog.egg-info/SOURCES.txt +41 -0
  27. esys_gitlog-0.1.0/src/esys_gitlog.egg-info/dependency_links.txt +1 -0
  28. esys_gitlog-0.1.0/src/esys_gitlog.egg-info/entry_points.txt +2 -0
  29. esys_gitlog-0.1.0/src/esys_gitlog.egg-info/requires.txt +13 -0
  30. esys_gitlog-0.1.0/src/esys_gitlog.egg-info/top_level.txt +1 -0
  31. esys_gitlog-0.1.0/tests/test_browser_display.py +57 -0
  32. esys_gitlog-0.1.0/tests/test_cli.py +189 -0
  33. esys_gitlog-0.1.0/tests/test_coverage_gaps.py +463 -0
  34. esys_gitlog-0.1.0/tests/test_detection.py +77 -0
  35. esys_gitlog-0.1.0/tests/test_git_adapter.py +163 -0
  36. esys_gitlog-0.1.0/tests/test_installed_launcher.py +128 -0
  37. esys_gitlog-0.1.0/tests/test_pager.py +30 -0
  38. esys_gitlog-0.1.0/tests/test_preferences.py +291 -0
  39. esys_gitlog-0.1.0/tests/test_render.py +232 -0
  40. esys_gitlog-0.1.0/tests/test_semantic_default.py +110 -0
  41. esys_gitlog-0.1.0/tests/test_semantic_p4.py +104 -0
  42. esys_gitlog-0.1.0/tests/test_smoke.py +8 -0
  43. esys_gitlog-0.1.0/tests/test_tool_paths.py +58 -0
@@ -0,0 +1,18 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 esys-gitlog-dev
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
6
+ associated documentation files (the "Software"), to deal in the Software without restriction, including
7
+ without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
9
+ following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all copies or substantial
12
+ portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
15
+ LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
16
+ EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
18
+ USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,77 @@
1
+ Metadata-Version: 2.4
2
+ Name: esys-gitlog
3
+ Version: 0.1.0
4
+ Summary: Python implementation of git elog
5
+ Author: Project maintainers
6
+ License-Expression: MIT
7
+ Requires-Python: >=3.11
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE.txt
10
+ Requires-Dist: typer>=0.12
11
+ Requires-Dist: rich>=13.0
12
+ Provides-Extra: dev
13
+ Requires-Dist: pytest>=8.0; extra == "dev"
14
+ Requires-Dist: pytest-cov>=5.0; extra == "dev"
15
+ Requires-Dist: ruff>=0.8; extra == "dev"
16
+ Requires-Dist: build>=1.0; extra == "dev"
17
+ Requires-Dist: twine>=5.0; extra == "dev"
18
+ Requires-Dist: pre-commit>=3.0; extra == "dev"
19
+ Provides-Extra: standalone
20
+ Requires-Dist: pyinstaller>=6.0; extra == "standalone"
21
+ Dynamic: license-file
22
+
23
+ # esys-gitlog
24
+
25
+ Python implementation of `git elog`. The distribution name is `esys-gitlog`. The import package is `esys_gitlog`. The installed command is `git-elog`, so Git can run `git elog`.
26
+
27
+ The shared contract lives in `esys-gitlog-spec`. This repository carries that contract in Python. It does not define a second one.
28
+
29
+ ## Smoke
30
+
31
+ ```bash
32
+ pip install .
33
+ git elog --help
34
+ git elog --json
35
+ ```
36
+
37
+ `--json` writes one QA report document to stdout. The first skeleton returns an empty `commits` array.
38
+
39
+ ## Release / publish
40
+
41
+ Tag-driven CI publishes to GitLab Package Registry, TestPyPI, then PyPI (Deckicorn-style ADR-0009 flow). See [docs/release-pipeline.md](docs/release-pipeline.md).
42
+
43
+ ```bash
44
+ python scripts/publish_release.py --dry-run --bump patch
45
+ python scripts/build_package.py
46
+ python scripts/smoke_install_package.py --dist dist --expected-version 0.1.0
47
+ ```
48
+
49
+ ## Preferences and profiles
50
+
51
+ Layered tool preferences (user → `.tools/esys/esys-gitlog/` → `.tools_local/…`)
52
+ and named profiles follow root SADR-0009 / SADR-0010 and contract CADR-0012.
53
+
54
+ ```bash
55
+ git-elog --preferences path.json # preference overlay
56
+ git-elog --profile acme # built-in or profiles/acme.json
57
+ git-elog --config path/profile.json # one-off profile file
58
+ git-elog preferences detect # probe jira/gh/glab/git remote; write missing user keys
59
+ ```
60
+
61
+ ## Standalone binary (optional)
62
+
63
+ The primary install remains the wheel + `git-elog` console script. For a
64
+ frozen one-file binary (for example Cursor `esysGitlog.executablePath` without
65
+ a Python env on PATH):
66
+
67
+ ```bash
68
+ python scripts/build_standalone.py
69
+ ```
70
+
71
+ Output: `dist/git-elog.exe` (Windows) or `dist/git-elog` (Linux/macOS). Build on
72
+ the OS you need — this does not cross-compile.
73
+
74
+ - [Architecture](docs/architecture/overview.md)
75
+ - [ADRs](docs/adr/INDEX.md)
76
+ - [ASRs](docs/asr/INDEX.md)
77
+ - [Implementation plan](plans/implementation.md)
@@ -0,0 +1,55 @@
1
+ # esys-gitlog
2
+
3
+ Python implementation of `git elog`. The distribution name is `esys-gitlog`. The import package is `esys_gitlog`. The installed command is `git-elog`, so Git can run `git elog`.
4
+
5
+ The shared contract lives in `esys-gitlog-spec`. This repository carries that contract in Python. It does not define a second one.
6
+
7
+ ## Smoke
8
+
9
+ ```bash
10
+ pip install .
11
+ git elog --help
12
+ git elog --json
13
+ ```
14
+
15
+ `--json` writes one QA report document to stdout. The first skeleton returns an empty `commits` array.
16
+
17
+ ## Release / publish
18
+
19
+ Tag-driven CI publishes to GitLab Package Registry, TestPyPI, then PyPI (Deckicorn-style ADR-0009 flow). See [docs/release-pipeline.md](docs/release-pipeline.md).
20
+
21
+ ```bash
22
+ python scripts/publish_release.py --dry-run --bump patch
23
+ python scripts/build_package.py
24
+ python scripts/smoke_install_package.py --dist dist --expected-version 0.1.0
25
+ ```
26
+
27
+ ## Preferences and profiles
28
+
29
+ Layered tool preferences (user → `.tools/esys/esys-gitlog/` → `.tools_local/…`)
30
+ and named profiles follow root SADR-0009 / SADR-0010 and contract CADR-0012.
31
+
32
+ ```bash
33
+ git-elog --preferences path.json # preference overlay
34
+ git-elog --profile acme # built-in or profiles/acme.json
35
+ git-elog --config path/profile.json # one-off profile file
36
+ git-elog preferences detect # probe jira/gh/glab/git remote; write missing user keys
37
+ ```
38
+
39
+ ## Standalone binary (optional)
40
+
41
+ The primary install remains the wheel + `git-elog` console script. For a
42
+ frozen one-file binary (for example Cursor `esysGitlog.executablePath` without
43
+ a Python env on PATH):
44
+
45
+ ```bash
46
+ python scripts/build_standalone.py
47
+ ```
48
+
49
+ Output: `dist/git-elog.exe` (Windows) or `dist/git-elog` (Linux/macOS). Build on
50
+ the OS you need — this does not cross-compile.
51
+
52
+ - [Architecture](docs/architecture/overview.md)
53
+ - [ADRs](docs/adr/INDEX.md)
54
+ - [ASRs](docs/asr/INDEX.md)
55
+ - [Implementation plan](plans/implementation.md)
@@ -0,0 +1,64 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "esys-gitlog"
7
+ version = "0.1.0"
8
+ description = "Python implementation of git elog"
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = "MIT"
12
+ authors = [{ name = "Project maintainers" }]
13
+ dependencies = [
14
+ "typer>=0.12",
15
+ "rich>=13.0",
16
+ ]
17
+
18
+ [project.optional-dependencies]
19
+ dev = [
20
+ "pytest>=8.0",
21
+ "pytest-cov>=5.0",
22
+ "ruff>=0.8",
23
+ "build>=1.0",
24
+ "twine>=5.0",
25
+ "pre-commit>=3.0",
26
+ ]
27
+ standalone = [
28
+ "pyinstaller>=6.0",
29
+ ]
30
+
31
+ [project.scripts]
32
+ git-elog = "esys_gitlog.cli:main"
33
+
34
+ [tool.setuptools]
35
+ package-dir = { "" = "src" }
36
+
37
+ [tool.setuptools.packages.find]
38
+ where = ["src"]
39
+
40
+ [tool.pytest.ini_options]
41
+ testpaths = ["tests"]
42
+ pythonpath = [".", "src"]
43
+ addopts = [
44
+ "--cov=esys_gitlog",
45
+ "--cov-report=term-missing",
46
+ "--cov-fail-under=95.01",
47
+ ]
48
+
49
+ [tool.coverage.run]
50
+ source = ["esys_gitlog"]
51
+ branch = false
52
+ omit = ["*/esys_gitlog/__main__.py"]
53
+
54
+ [tool.coverage.report]
55
+ show_missing = true
56
+ skip_empty = true
57
+
58
+ [tool.ruff]
59
+ line-length = 100
60
+ target-version = "py311"
61
+ src = ["src", "tests"]
62
+
63
+ [tool.ruff.lint]
64
+ select = ["E", "F", "I", "UP"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,3 @@
1
+ """esys_gitlog — installable package."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,6 @@
1
+ """Allow ``python -m esys_gitlog`` and PyInstaller entry."""
2
+
3
+ from esys_gitlog.cli import main
4
+
5
+ if __name__ == "__main__":
6
+ main()
@@ -0,0 +1,215 @@
1
+ """Application service. Callable without Typer."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Sequence
6
+ from pathlib import Path
7
+ from typing import TextIO
8
+
9
+ from rich.console import Console
10
+
11
+ from esys_gitlog.config import AnalysisConfig, DisplayMode, QaMode, resolve_config
12
+ from esys_gitlog.git_adapter import GitError, iter_commits, list_commits
13
+ from esys_gitlog.links import resolve_commit_url
14
+ from esys_gitlog.model import CommitResult
15
+ from esys_gitlog.pipeline import analyze_commit
16
+ from esys_gitlog.render import render_history
17
+ from esys_gitlog.report import (
18
+ build_report,
19
+ report_exit_code,
20
+ stream_commit_event,
21
+ stream_end_event,
22
+ stream_exit_code,
23
+ stream_start_event,
24
+ write_report,
25
+ write_stream_event,
26
+ )
27
+
28
+
29
+ def analyze_history(
30
+ cwd: str | Path = ".",
31
+ *,
32
+ profile: str | None = None,
33
+ qa: QaMode | None = None,
34
+ display: DisplayMode | None = None,
35
+ config_path: Path | None = None,
36
+ preferences_path: Path | None = None,
37
+ git_log_args: Sequence[str] | None = None,
38
+ ) -> tuple[list[tuple[str, CommitResult, str]], AnalysisConfig]:
39
+ """Load Git history and run the semantic pipeline.
40
+
41
+ Returns ``(rows, config)`` where each row is
42
+ ``(commit_id, result, subject)`` in ``git log`` order.
43
+ """
44
+ root = Path(cwd)
45
+ resolved = resolve_config(
46
+ profile=profile,
47
+ qa=qa,
48
+ display=display,
49
+ config_path=config_path,
50
+ preferences_path=preferences_path,
51
+ cwd=root,
52
+ )
53
+ analyzed: list[tuple[str, CommitResult, str]] = []
54
+ for raw in list_commits(root, git_log_args=git_log_args):
55
+ result = analyze_commit(raw.subject, raw.body, config=resolved)
56
+ result.message = raw.message or "\n\n".join(
57
+ part for part in (raw.subject, raw.body) if part
58
+ )
59
+ result.author_name = raw.author_name or None
60
+ result.author_email = raw.author_email or None
61
+ result.authored_date = raw.authored_date or None
62
+ result.commit_url = resolve_commit_url(
63
+ raw.id,
64
+ resolved.profile,
65
+ cwd=root,
66
+ )
67
+ analyzed.append((raw.id, result, raw.subject))
68
+ return analyzed, resolved
69
+
70
+
71
+ def run_json(
72
+ stdout: TextIO[str],
73
+ *,
74
+ cwd: str | Path = ".",
75
+ profile: str | None = None,
76
+ qa: QaMode | None = None,
77
+ display: DisplayMode | None = None,
78
+ config_path: Path | None = None,
79
+ preferences_path: Path | None = None,
80
+ git_log_args: Sequence[str] | None = None,
81
+ ) -> int:
82
+ """Write the QA report to ``stdout`` only. Return the process exit code."""
83
+ analyzed, _config = analyze_history(
84
+ cwd,
85
+ profile=profile,
86
+ qa=qa,
87
+ display=display,
88
+ config_path=config_path,
89
+ preferences_path=preferences_path,
90
+ git_log_args=git_log_args,
91
+ )
92
+ pairs = [(commit_id, result) for commit_id, result, _subject in analyzed]
93
+ document = build_report(pairs)
94
+ write_report(stdout, document)
95
+ return report_exit_code(document, walk_finished=True)
96
+
97
+
98
+ def run_json_stream(
99
+ stdout: TextIO[str],
100
+ *,
101
+ cwd: str | Path = ".",
102
+ profile: str | None = None,
103
+ qa: QaMode | None = None,
104
+ display: DisplayMode | None = None,
105
+ config_path: Path | None = None,
106
+ preferences_path: Path | None = None,
107
+ git_log_args: Sequence[str] | None = None,
108
+ ) -> int:
109
+ """Write NDJSON stream events to ``stdout`` as each commit is analyzed."""
110
+ root = Path(cwd)
111
+ resolved = resolve_config(
112
+ profile=profile,
113
+ qa=qa,
114
+ display=display,
115
+ config_path=config_path,
116
+ preferences_path=preferences_path,
117
+ cwd=root,
118
+ )
119
+ write_stream_event(stdout, stream_start_event())
120
+ commits = 0
121
+ warnings = 0
122
+ errors = 0
123
+ walk_finished = True
124
+ try:
125
+ for raw in iter_commits(root, git_log_args=git_log_args):
126
+ result = analyze_commit(raw.subject, raw.body, config=resolved)
127
+ result.message = raw.message or "\n\n".join(
128
+ part for part in (raw.subject, raw.body) if part
129
+ )
130
+ result.author_name = raw.author_name or None
131
+ result.author_email = raw.author_email or None
132
+ result.authored_date = raw.authored_date or None
133
+ result.commit_url = resolve_commit_url(
134
+ raw.id,
135
+ resolved.profile,
136
+ cwd=root,
137
+ )
138
+ for diagnostic in result.diagnostics:
139
+ if diagnostic.severity == "warning":
140
+ warnings += 1
141
+ elif diagnostic.severity == "error":
142
+ errors += 1
143
+ commits += 1
144
+ write_stream_event(stdout, stream_commit_event(raw.id, result))
145
+ except BrokenPipeError:
146
+ walk_finished = False
147
+ return stream_exit_code(errors=errors, walk_finished=False)
148
+ write_stream_event(
149
+ stdout,
150
+ stream_end_event(
151
+ commits=commits,
152
+ warnings=warnings,
153
+ errors=errors,
154
+ walk_finished=walk_finished,
155
+ ),
156
+ )
157
+ return stream_exit_code(errors=errors, walk_finished=walk_finished)
158
+
159
+
160
+ def run_human(
161
+ *,
162
+ cwd: str | Path = ".",
163
+ profile: str | None = None,
164
+ qa: QaMode | None = None,
165
+ display: DisplayMode | None = None,
166
+ config_path: Path | None = None,
167
+ preferences_path: Path | None = None,
168
+ console: Console | None = None,
169
+ show_author: bool | None = None,
170
+ show_date: bool | None = None,
171
+ show_body: bool | None = None,
172
+ show_id: bool | None = None,
173
+ show_conventional: bool | None = None,
174
+ use_pager: bool | None = None,
175
+ git_log_args: Sequence[str] | None = None,
176
+ ) -> int:
177
+ """Render history for a person. Return the process exit code.
178
+
179
+ Explicit ``show_*`` CLI values override preference defaults; ``None`` keeps
180
+ the resolved preference. ``use_pager`` defaults to on when stdout is a TTY.
181
+ """
182
+ analyzed, config = analyze_history(
183
+ cwd,
184
+ profile=profile,
185
+ qa=qa,
186
+ display=display,
187
+ config_path=config_path,
188
+ preferences_path=preferences_path,
189
+ git_log_args=git_log_args,
190
+ )
191
+ render_history(
192
+ analyzed,
193
+ console=console,
194
+ ref_id_width=config.ref_id_width,
195
+ type_width=config.type_width,
196
+ show_author=(config.show_author if show_author is None else show_author),
197
+ show_date=config.show_date if show_date is None else show_date,
198
+ show_body=config.show_body if show_body is None else show_body,
199
+ show_id=config.show_id if show_id is None else show_id,
200
+ show_conventional=(
201
+ config.show_conventional if show_conventional is None else show_conventional
202
+ ),
203
+ use_pager=use_pager,
204
+ )
205
+ pairs = [(commit_id, result) for commit_id, result, _subject in analyzed]
206
+ return report_exit_code(build_report(pairs), walk_finished=True)
207
+
208
+
209
+ __all__ = [
210
+ "GitError",
211
+ "analyze_history",
212
+ "run_human",
213
+ "run_json",
214
+ "run_json_stream",
215
+ ]