esys-gitlog 0.1.0__tar.gz → 0.3.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.3.0/PKG-INFO +119 -0
- esys_gitlog-0.3.0/README.md +97 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.3.0}/pyproject.toml +7 -1
- {esys_gitlog-0.1.0 → esys_gitlog-0.3.0}/src/esys_gitlog/__init__.py +1 -1
- {esys_gitlog-0.1.0 → esys_gitlog-0.3.0}/src/esys_gitlog/cli.py +101 -7
- {esys_gitlog-0.1.0 → esys_gitlog-0.3.0}/src/esys_gitlog/config.py +12 -5
- esys_gitlog-0.3.0/src/esys_gitlog/detection.py +377 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.3.0}/src/esys_gitlog/display.py +21 -11
- esys_gitlog-0.3.0/src/esys_gitlog/identifiers.py +86 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.3.0}/src/esys_gitlog/interpret.py +32 -4
- {esys_gitlog-0.1.0 → esys_gitlog-0.3.0}/src/esys_gitlog/links.py +21 -5
- {esys_gitlog-0.1.0 → esys_gitlog-0.3.0}/src/esys_gitlog/preferences.py +129 -2
- esys_gitlog-0.3.0/src/esys_gitlog.egg-info/PKG-INFO +119 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.3.0}/src/esys_gitlog.egg-info/SOURCES.txt +2 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.3.0}/src/esys_gitlog.egg-info/entry_points.txt +1 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.3.0}/tests/test_browser_display.py +23 -1
- esys_gitlog-0.3.0/tests/test_coverage_release_030.py +347 -0
- esys_gitlog-0.3.0/tests/test_detection.py +238 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.3.0}/tests/test_installed_launcher.py +31 -12
- {esys_gitlog-0.1.0 → esys_gitlog-0.3.0}/tests/test_preferences.py +49 -2
- esys_gitlog-0.3.0/tests/test_seed_jira_live_fixture.py +29 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.3.0}/tests/test_semantic_default.py +9 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.3.0}/tests/test_semantic_p4.py +26 -0
- esys_gitlog-0.1.0/PKG-INFO +0 -77
- esys_gitlog-0.1.0/README.md +0 -55
- esys_gitlog-0.1.0/src/esys_gitlog/detection.py +0 -145
- esys_gitlog-0.1.0/src/esys_gitlog/identifiers.py +0 -46
- esys_gitlog-0.1.0/src/esys_gitlog.egg-info/PKG-INFO +0 -77
- esys_gitlog-0.1.0/tests/test_detection.py +0 -77
- {esys_gitlog-0.1.0 → esys_gitlog-0.3.0}/LICENSE.txt +0 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.3.0}/setup.cfg +0 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.3.0}/src/esys_gitlog/__main__.py +0 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.3.0}/src/esys_gitlog/app.py +0 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.3.0}/src/esys_gitlog/git_adapter.py +0 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.3.0}/src/esys_gitlog/model.py +0 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.3.0}/src/esys_gitlog/pager.py +0 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.3.0}/src/esys_gitlog/pipeline.py +0 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.3.0}/src/esys_gitlog/qa.py +0 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.3.0}/src/esys_gitlog/render.py +0 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.3.0}/src/esys_gitlog/report.py +0 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.3.0}/src/esys_gitlog/tool_paths.py +0 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.3.0}/src/esys_gitlog.egg-info/dependency_links.txt +0 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.3.0}/src/esys_gitlog.egg-info/requires.txt +0 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.3.0}/src/esys_gitlog.egg-info/top_level.txt +0 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.3.0}/tests/test_cli.py +0 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.3.0}/tests/test_coverage_gaps.py +0 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.3.0}/tests/test_git_adapter.py +0 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.3.0}/tests/test_pager.py +0 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.3.0}/tests/test_render.py +0 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.3.0}/tests/test_smoke.py +0 -0
- {esys_gitlog-0.1.0 → esys_gitlog-0.3.0}/tests/test_tool_paths.py +0 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: esys-gitlog
|
|
3
|
+
Version: 0.3.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`. 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
|
+
|
|
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
|
+
elog --help
|
|
34
|
+
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
|
+
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-cli/acli + git remote; write missing user keys
|
|
59
|
+
elog preferences detect --json --no-write # machine-readable candidates (extension setup)
|
|
60
|
+
elog preferences set-link jira-id 'https://host/browse/{id}'
|
|
61
|
+
elog preferences clear-link jira-id
|
|
62
|
+
elog preferences set-tool jira 'C:\tools\jira.exe' # optional absolute path
|
|
63
|
+
elog preferences set-tool acli /usr/local/bin/acli
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Jira browse bases come from local **jira-cli** config and/or **Atlassian acli**
|
|
67
|
+
(`acli jira config get site`), never from per-commit HTTP (SADR-0010). Detection
|
|
68
|
+
only fills unset user keys unless you use `set-link`. Absolute paths live under
|
|
69
|
+
`detection.jiraExecutable` / `detection.acliExecutable` when the tools are not
|
|
70
|
+
on `PATH`.
|
|
71
|
+
|
|
72
|
+
### Live Jira fixture (SCRUM)
|
|
73
|
+
|
|
74
|
+
The free site [libesys.atlassian.net](https://libesys.atlassian.net/browse/SCRUM-1)
|
|
75
|
+
uses project key **`SCRUM`** only (do not invent other project keys).
|
|
76
|
+
|
|
77
|
+
1. Configure acli or jira-cli against that site (optional absolute paths via
|
|
78
|
+
`elog preferences set-tool`).
|
|
79
|
+
2. Seed once (creates 10 new issues, writes the pin — not part of every CI run):
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
python scripts/seed_jira_live_fixture.py --project SCRUM --count 10
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
3. Commit `tests/integration/jira_live.pin.json`.
|
|
86
|
+
4. Materialize a durable Git repo for the VS Code extension / manual checks:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
python scripts/materialize_jira_live_repo.py
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Output: `tests/integration/jira_live_repo/` (gitignored except a placeholder
|
|
93
|
+
README). Open that folder, store the browse template, refresh the commit browser.
|
|
94
|
+
5. Run the live test locally or enable GitLab variable `ESYS_GITLOG_JIRA_LIVE=1`
|
|
95
|
+
for job `test:jira-live`:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
ESYS_GITLOG_JIRA_LIVE=1 pytest -m jira_live -o addopts= --no-cov
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Default `pytest` excludes `@pytest.mark.jira_live` so coverage stays offline.
|
|
102
|
+
|
|
103
|
+
## Standalone binary (optional)
|
|
104
|
+
|
|
105
|
+
The primary install remains the wheel + `git-elog` / `elog` console scripts. For a
|
|
106
|
+
frozen one-file binary (for example Cursor `esysGitlog.executablePath` without
|
|
107
|
+
a Python env on PATH):
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
python scripts/build_standalone.py
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Output: `dist/git-elog.exe` (Windows) or `dist/git-elog` (Linux/macOS). Build on
|
|
114
|
+
the OS you need — this does not cross-compile.
|
|
115
|
+
|
|
116
|
+
- [Architecture](docs/architecture/overview.md)
|
|
117
|
+
- [ADRs](docs/adr/INDEX.md)
|
|
118
|
+
- [ASRs](docs/asr/INDEX.md)
|
|
119
|
+
- [Implementation plan](plans/implementation.md)
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# esys-gitlog
|
|
2
|
+
|
|
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
|
+
|
|
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
|
+
elog --help
|
|
12
|
+
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
|
+
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-cli/acli + git remote; write missing user keys
|
|
37
|
+
elog preferences detect --json --no-write # machine-readable candidates (extension setup)
|
|
38
|
+
elog preferences set-link jira-id 'https://host/browse/{id}'
|
|
39
|
+
elog preferences clear-link jira-id
|
|
40
|
+
elog preferences set-tool jira 'C:\tools\jira.exe' # optional absolute path
|
|
41
|
+
elog preferences set-tool acli /usr/local/bin/acli
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Jira browse bases come from local **jira-cli** config and/or **Atlassian acli**
|
|
45
|
+
(`acli jira config get site`), never from per-commit HTTP (SADR-0010). Detection
|
|
46
|
+
only fills unset user keys unless you use `set-link`. Absolute paths live under
|
|
47
|
+
`detection.jiraExecutable` / `detection.acliExecutable` when the tools are not
|
|
48
|
+
on `PATH`.
|
|
49
|
+
|
|
50
|
+
### Live Jira fixture (SCRUM)
|
|
51
|
+
|
|
52
|
+
The free site [libesys.atlassian.net](https://libesys.atlassian.net/browse/SCRUM-1)
|
|
53
|
+
uses project key **`SCRUM`** only (do not invent other project keys).
|
|
54
|
+
|
|
55
|
+
1. Configure acli or jira-cli against that site (optional absolute paths via
|
|
56
|
+
`elog preferences set-tool`).
|
|
57
|
+
2. Seed once (creates 10 new issues, writes the pin — not part of every CI run):
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
python scripts/seed_jira_live_fixture.py --project SCRUM --count 10
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
3. Commit `tests/integration/jira_live.pin.json`.
|
|
64
|
+
4. Materialize a durable Git repo for the VS Code extension / manual checks:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
python scripts/materialize_jira_live_repo.py
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Output: `tests/integration/jira_live_repo/` (gitignored except a placeholder
|
|
71
|
+
README). Open that folder, store the browse template, refresh the commit browser.
|
|
72
|
+
5. Run the live test locally or enable GitLab variable `ESYS_GITLOG_JIRA_LIVE=1`
|
|
73
|
+
for job `test:jira-live`:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
ESYS_GITLOG_JIRA_LIVE=1 pytest -m jira_live -o addopts= --no-cov
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Default `pytest` excludes `@pytest.mark.jira_live` so coverage stays offline.
|
|
80
|
+
|
|
81
|
+
## Standalone binary (optional)
|
|
82
|
+
|
|
83
|
+
The primary install remains the wheel + `git-elog` / `elog` console scripts. For a
|
|
84
|
+
frozen one-file binary (for example Cursor `esysGitlog.executablePath` without
|
|
85
|
+
a Python env on PATH):
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
python scripts/build_standalone.py
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Output: `dist/git-elog.exe` (Windows) or `dist/git-elog` (Linux/macOS). Build on
|
|
92
|
+
the OS you need — this does not cross-compile.
|
|
93
|
+
|
|
94
|
+
- [Architecture](docs/architecture/overview.md)
|
|
95
|
+
- [ADRs](docs/adr/INDEX.md)
|
|
96
|
+
- [ASRs](docs/asr/INDEX.md)
|
|
97
|
+
- [Implementation plan](plans/implementation.md)
|
|
@@ -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.3.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" }
|
|
@@ -40,7 +41,12 @@ where = ["src"]
|
|
|
40
41
|
[tool.pytest.ini_options]
|
|
41
42
|
testpaths = ["tests"]
|
|
42
43
|
pythonpath = [".", "src"]
|
|
44
|
+
markers = [
|
|
45
|
+
"jira_live: live Jira fixture (ESYS_GITLOG_JIRA_LIVE=1 + seeded pin)",
|
|
46
|
+
]
|
|
43
47
|
addopts = [
|
|
48
|
+
"-m",
|
|
49
|
+
"not jira_live",
|
|
44
50
|
"--cov=esys_gitlog",
|
|
45
51
|
"--cov-report=term-missing",
|
|
46
52
|
"--cov-fail-under=95.01",
|
|
@@ -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
|
|
|
@@ -11,13 +11,17 @@ from typer.core import TyperGroup
|
|
|
11
11
|
|
|
12
12
|
from esys_gitlog import app as application
|
|
13
13
|
from esys_gitlog.config import DisplayMode, QaMode
|
|
14
|
-
from esys_gitlog.detection import
|
|
14
|
+
from esys_gitlog.detection import detect_report, dump_detection_debug
|
|
15
15
|
from esys_gitlog.git_adapter import GitError, filter_git_log_args
|
|
16
16
|
from esys_gitlog.preferences import (
|
|
17
|
+
LinkPreferences,
|
|
18
|
+
clear_user_ref_link,
|
|
17
19
|
fill_link_gaps,
|
|
18
20
|
load_layered_preferences,
|
|
19
21
|
merge_missing_links_into_user_file,
|
|
20
22
|
preferences_to_object,
|
|
23
|
+
set_user_detection_executables,
|
|
24
|
+
set_user_ref_link,
|
|
21
25
|
)
|
|
22
26
|
from esys_gitlog.tool_paths import user_preferences_path
|
|
23
27
|
|
|
@@ -299,13 +303,40 @@ def preferences_detect(
|
|
|
299
303
|
"--debug",
|
|
300
304
|
help="Print which vendor CLIs are on PATH.",
|
|
301
305
|
),
|
|
306
|
+
as_json: bool = typer.Option(
|
|
307
|
+
False,
|
|
308
|
+
"--json",
|
|
309
|
+
help="Print a machine-readable detection report (candidates, sources, tools).",
|
|
310
|
+
),
|
|
311
|
+
probe_auth: bool = typer.Option(
|
|
312
|
+
False,
|
|
313
|
+
"--probe-auth",
|
|
314
|
+
help="Optionally probe jira-cli/acli auth (setup only; may touch the network).",
|
|
315
|
+
),
|
|
302
316
|
) -> None:
|
|
303
|
-
"""Probe local Jira/GitHub/GitLab CLIs and git remote; optionally persist."""
|
|
317
|
+
"""Probe local Jira (jira-cli/acli), GitHub/GitLab CLIs and git remote; optionally persist."""
|
|
304
318
|
cwd = Path.cwd()
|
|
305
319
|
prefs = load_layered_preferences(cwd)
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
320
|
+
report = detect_report(
|
|
321
|
+
cwd,
|
|
322
|
+
detection=prefs.detection,
|
|
323
|
+
probe_auth=probe_auth,
|
|
324
|
+
)
|
|
325
|
+
discovered = LinkPreferences(
|
|
326
|
+
ref_links=dict(report.get("refLinks") or {}),
|
|
327
|
+
commit_link=report.get("commitLink"),
|
|
328
|
+
)
|
|
329
|
+
if show_debug and not as_json:
|
|
330
|
+
typer.echo(dump_detection_debug(prefs.detection))
|
|
331
|
+
if as_json:
|
|
332
|
+
if write:
|
|
333
|
+
path = user_preferences_path()
|
|
334
|
+
merge_missing_links_into_user_file(discovered=discovered, user_path=path)
|
|
335
|
+
report["wrote"] = str(path)
|
|
336
|
+
else:
|
|
337
|
+
report["wrote"] = None
|
|
338
|
+
typer.echo(json.dumps(report, indent=2))
|
|
339
|
+
return
|
|
309
340
|
merged = fill_link_gaps(prefs, discovered)
|
|
310
341
|
typer.echo(json.dumps(preferences_to_object(merged), indent=2))
|
|
311
342
|
if write:
|
|
@@ -314,9 +345,72 @@ def preferences_detect(
|
|
|
314
345
|
typer.echo(f"wrote missing keys to {path}", err=True)
|
|
315
346
|
|
|
316
347
|
|
|
348
|
+
@preferences_app.command("set-link")
|
|
349
|
+
def preferences_set_link(
|
|
350
|
+
family: str = typer.Argument(help="Identifier family, e.g. jira-id"),
|
|
351
|
+
template: str = typer.Argument(
|
|
352
|
+
help="URL template with {id}, e.g. https://host/browse/{id}",
|
|
353
|
+
),
|
|
354
|
+
) -> None:
|
|
355
|
+
"""Create or overwrite a user-layer ref link template (explicit settings edit)."""
|
|
356
|
+
path = user_preferences_path()
|
|
357
|
+
set_user_ref_link(family, template, user_path=path)
|
|
358
|
+
typer.echo(json.dumps({"family": family, "template": template, "path": str(path)}, indent=2))
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
@preferences_app.command("clear-link")
|
|
362
|
+
def preferences_clear_link(
|
|
363
|
+
family: str = typer.Argument(help="Identifier family, e.g. jira-id"),
|
|
364
|
+
) -> None:
|
|
365
|
+
"""Remove a user-layer ref link template."""
|
|
366
|
+
path = user_preferences_path()
|
|
367
|
+
clear_user_ref_link(family, user_path=path)
|
|
368
|
+
typer.echo(json.dumps({"family": family, "cleared": True, "path": str(path)}, indent=2))
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
@preferences_app.command("set-tool")
|
|
372
|
+
def preferences_set_tool(
|
|
373
|
+
tool: str = typer.Argument(help="Tool name: jira or acli"),
|
|
374
|
+
executable: str = typer.Argument(help="Absolute path to the executable"),
|
|
375
|
+
) -> None:
|
|
376
|
+
"""Store an absolute path for jira-cli or acli (when not on PATH)."""
|
|
377
|
+
name = tool.strip().lower()
|
|
378
|
+
path = user_preferences_path()
|
|
379
|
+
if name in ("jira", "jira-cli"):
|
|
380
|
+
set_user_detection_executables(jira_executable=executable, user_path=path)
|
|
381
|
+
elif name == "acli":
|
|
382
|
+
set_user_detection_executables(acli_executable=executable, user_path=path)
|
|
383
|
+
else:
|
|
384
|
+
typer.echo("tool must be 'jira' or 'acli'", err=True)
|
|
385
|
+
raise typer.Exit(code=2)
|
|
386
|
+
typer.echo(json.dumps({"tool": name, "executable": executable, "path": str(path)}, indent=2))
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
@preferences_app.command("clear-tool")
|
|
390
|
+
def preferences_clear_tool(
|
|
391
|
+
tool: str = typer.Argument(help="Tool name: jira or acli"),
|
|
392
|
+
) -> None:
|
|
393
|
+
"""Remove a configured jira-cli or acli path (fall back to PATH)."""
|
|
394
|
+
name = tool.strip().lower()
|
|
395
|
+
path = user_preferences_path()
|
|
396
|
+
if name in ("jira", "jira-cli"):
|
|
397
|
+
set_user_detection_executables(clear_jira=True, user_path=path)
|
|
398
|
+
elif name == "acli":
|
|
399
|
+
set_user_detection_executables(clear_acli=True, user_path=path)
|
|
400
|
+
else:
|
|
401
|
+
typer.echo("tool must be 'jira' or 'acli'", err=True)
|
|
402
|
+
raise typer.Exit(code=2)
|
|
403
|
+
typer.echo(json.dumps({"tool": name, "cleared": True, "path": str(path)}, indent=2))
|
|
404
|
+
|
|
405
|
+
|
|
317
406
|
def main() -> None:
|
|
318
407
|
"""Console script entry point."""
|
|
319
|
-
|
|
408
|
+
# Normalize Windows separators so ``Path.name`` works when run under POSIX.
|
|
409
|
+
invoked = Path(sys.argv[0].replace("\\", "/")).name
|
|
410
|
+
if invoked.lower().endswith(".exe"):
|
|
411
|
+
invoked = invoked[:-4]
|
|
412
|
+
prog_name = invoked or "git-elog"
|
|
413
|
+
app(args=prepare_argv(sys.argv[1:]), prog_name=prog_name)
|
|
320
414
|
|
|
321
415
|
|
|
322
416
|
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.
|