git-graphable 0.5.0__tar.gz → 0.6.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.
- {git_graphable-0.5.0 → git_graphable-0.6.0}/.github/workflows/ci.yml +13 -0
- git_graphable-0.6.0/.github/workflows/pages.yml +65 -0
- {git_graphable-0.5.0 → git_graphable-0.6.0}/.gitignore +2 -0
- {git_graphable-0.5.0 → git_graphable-0.6.0}/CHANGELOG.md +37 -0
- git_graphable-0.6.0/LICENSE +21 -0
- {git_graphable-0.5.0 → git_graphable-0.6.0}/PKG-INFO +17 -14
- {git_graphable-0.5.0 → git_graphable-0.6.0}/README.md +13 -12
- {git_graphable-0.5.0 → git_graphable-0.6.0}/USAGE.md +6 -2
- {git_graphable-0.5.0 → git_graphable-0.6.0}/action.yml +13 -2
- {git_graphable-0.5.0 → git_graphable-0.6.0}/pyproject.toml +7 -1
- {git_graphable-0.5.0 → git_graphable-0.6.0}/src/git_graphable/bare_cli.py +20 -1
- {git_graphable-0.5.0 → git_graphable-0.6.0}/src/git_graphable/commands.py +5 -2
- {git_graphable-0.5.0 → git_graphable-0.6.0}/src/git_graphable/core.py +5 -0
- git_graphable-0.6.0/src/git_graphable/github.py +25 -0
- git_graphable-0.6.0/src/git_graphable/highlighter.py +7 -0
- git_graphable-0.6.0/src/git_graphable/highlights/core.py +59 -0
- git_graphable-0.6.0/src/git_graphable/highlights/external.py +259 -0
- git_graphable-0.6.0/src/git_graphable/highlights/hygiene.py +212 -0
- git_graphable-0.6.0/src/git_graphable/highlights/visual.py +119 -0
- git_graphable-0.6.0/src/git_graphable/issues/__init__.py +46 -0
- git_graphable-0.6.0/src/git_graphable/issues/base.py +35 -0
- git_graphable-0.6.0/src/git_graphable/issues/github.py +50 -0
- git_graphable-0.6.0/src/git_graphable/issues/gitlab.py +40 -0
- git_graphable-0.6.0/src/git_graphable/issues/jira.py +59 -0
- git_graphable-0.6.0/src/git_graphable/issues/script.py +68 -0
- {git_graphable-0.5.0 → git_graphable-0.6.0}/src/git_graphable/parser.py +1 -1
- git_graphable-0.6.0/src/git_graphable/prs/__init__.py +37 -0
- git_graphable-0.6.0/src/git_graphable/prs/base.py +40 -0
- git_graphable-0.6.0/src/git_graphable/prs/github.py +61 -0
- git_graphable-0.6.0/src/git_graphable/prs/gitlab.py +55 -0
- git_graphable-0.6.0/src/git_graphable/prs/script.py +56 -0
- {git_graphable-0.5.0 → git_graphable-0.6.0}/src/git_graphable/rich_cli.py +15 -1
- git_graphable-0.6.0/src/git_graphable/styler.py +79 -0
- git_graphable-0.6.0/src/git_graphable/styling/base.py +133 -0
- git_graphable-0.6.0/src/git_graphable/styling/generic.py +132 -0
- git_graphable-0.6.0/src/git_graphable/styling/html.py +340 -0
- git_graphable-0.6.0/src/git_graphable/styling/mermaid.py +87 -0
- git_graphable-0.6.0/tests/__init__.py +0 -0
- git_graphable-0.6.0/tests/conftest.py +33 -0
- git_graphable-0.6.0/tests/highlights/__init__.py +0 -0
- git_graphable-0.6.0/tests/highlights/test_external.py +328 -0
- git_graphable-0.6.0/tests/highlights/test_hygiene.py +202 -0
- git_graphable-0.6.0/tests/highlights/test_visual.py +59 -0
- git_graphable-0.6.0/tests/issues/__init__.py +0 -0
- git_graphable-0.6.0/tests/issues/test_github.py +14 -0
- git_graphable-0.6.0/tests/issues/test_gitlab.py +25 -0
- git_graphable-0.6.0/tests/issues/test_jira_engine.py +21 -0
- git_graphable-0.6.0/tests/issues/test_script.py +106 -0
- git_graphable-0.6.0/tests/prs/__init__.py +0 -0
- git_graphable-0.6.0/tests/prs/test_github.py +32 -0
- git_graphable-0.6.0/tests/prs/test_gitlab.py +38 -0
- git_graphable-0.6.0/tests/prs/test_script.py +72 -0
- git_graphable-0.6.0/tests/styling/__init__.py +0 -0
- git_graphable-0.6.0/tests/styling/test_generic.py +111 -0
- git_graphable-0.6.0/tests/styling/test_mermaid.py +58 -0
- {git_graphable-0.5.0 → git_graphable-0.6.0}/tests/test_bare_cli.py +54 -25
- git_graphable-0.6.0/tests/test_commands.py +60 -0
- git_graphable-0.6.0/tests/test_highlighter.py +9 -0
- git_graphable-0.6.0/tests/test_hygiene_scorer.py +91 -0
- {git_graphable-0.5.0 → git_graphable-0.6.0}/tests/test_parser.py +10 -0
- {git_graphable-0.5.0 → git_graphable-0.6.0}/tests/test_rich_cli.py +26 -0
- git_graphable-0.6.0/tests/test_styler.py +15 -0
- {git_graphable-0.5.0 → git_graphable-0.6.0}/uv.lock +1 -1
- git_graphable-0.5.0/.github/workflows/pages.yml +0 -46
- git_graphable-0.5.0/graph.html +0 -573
- git_graphable-0.5.0/report_output/final_report.json +0 -34
- git_graphable-0.5.0/report_output/plus_metadata.json +0 -8
- git_graphable-0.5.0/report_output/report.html +0 -700
- git_graphable-0.5.0/report_output/screenshots/test_interactivity_toggling_chromium__failure.png +0 -0
- git_graphable-0.5.0/src/git_graphable/github.py +0 -77
- git_graphable-0.5.0/src/git_graphable/highlighter.py +0 -644
- git_graphable-0.5.0/src/git_graphable/issues.py +0 -177
- git_graphable-0.5.0/src/git_graphable/styler.py +0 -709
- git_graphable-0.5.0/tests/test_highlighter.py +0 -198
- git_graphable-0.5.0/tests/test_hygiene.py +0 -540
- git_graphable-0.5.0/tests/test_issues.py +0 -42
- git_graphable-0.5.0/tests/test_styler.py +0 -64
- {git_graphable-0.5.0 → git_graphable-0.6.0}/.gemini/GEMINI.md +0 -0
- {git_graphable-0.5.0 → git_graphable-0.6.0}/.gemini/code-ordering.md +0 -0
- {git_graphable-0.5.0 → git_graphable-0.6.0}/.github/dependabot.yml +0 -0
- {git_graphable-0.5.0 → git_graphable-0.6.0}/.github/workflows/publish.yml +0 -0
- {git_graphable-0.5.0 → git_graphable-0.6.0}/.python-version +0 -0
- {git_graphable-0.5.0 → git_graphable-0.6.0}/Justfile +0 -0
- {git_graphable-0.5.0 → git_graphable-0.6.0}/SECURITY.md +0 -0
- {git_graphable-0.5.0 → git_graphable-0.6.0}/STYLING.md +0 -0
- {git_graphable-0.5.0 → git_graphable-0.6.0}/examples/EXAMPLES.md +0 -0
- {git_graphable-0.5.0 → git_graphable-0.6.0}/examples/generate_demos.py +0 -0
- {git_graphable-0.5.0 → git_graphable-0.6.0}/examples/index_template.html +0 -0
- {git_graphable-0.5.0 → git_graphable-0.6.0}/examples/publish_demos.py +0 -0
- {git_graphable-0.5.0 → git_graphable-0.6.0}/src/git_graphable/__init__.py +0 -0
- {git_graphable-0.5.0 → git_graphable-0.6.0}/src/git_graphable/cli.py +0 -0
- {git_graphable-0.5.0 → git_graphable-0.6.0}/src/git_graphable/cli_utils.py +0 -0
- {git_graphable-0.5.0 → git_graphable-0.6.0}/src/git_graphable/default_config.toml +0 -0
- {git_graphable-0.5.0 → git_graphable-0.6.0}/src/git_graphable/hygiene.py +0 -0
- {git_graphable-0.5.0 → git_graphable-0.6.0}/src/git_graphable/models.py +0 -0
- {git_graphable-0.5.0 → git_graphable-0.6.0}/src/git_graphable/templates.py +0 -0
- {git_graphable-0.5.0 → git_graphable-0.6.0}/tests/test_cli.py +0 -0
- {git_graphable-0.5.0 → git_graphable-0.6.0}/tests/test_cli_utils.py +0 -0
- {git_graphable-0.5.0 → git_graphable-0.6.0}/tests/test_config.py +0 -0
- {git_graphable-0.5.0 → git_graphable-0.6.0}/tests/test_core.py +0 -0
- {git_graphable-0.5.0 → git_graphable-0.6.0}/tests/test_examples_html.py +0 -0
- {git_graphable-0.5.0 → git_graphable-0.6.0}/tests/test_github.py +0 -0
- {git_graphable-0.5.0 → git_graphable-0.6.0}/tests/test_interactive_html.py +0 -0
- {git_graphable-0.5.0 → git_graphable-0.6.0}/tests/test_models.py +0 -0
- {git_graphable-0.5.0 → git_graphable-0.6.0}/tests/test_ui_interactive.py +0 -0
|
@@ -34,8 +34,21 @@ jobs:
|
|
|
34
34
|
- name: Run checks
|
|
35
35
|
run: just check
|
|
36
36
|
|
|
37
|
+
- name: Generate Coverage Badge
|
|
38
|
+
run: |
|
|
39
|
+
if [ -f "htmlcov/index.html" ]; then
|
|
40
|
+
COVERAGE=$(grep -oP 'pc_cov">\d+%' htmlcov/index.html | head -1 | grep -oP '\d+')
|
|
41
|
+
echo "{\"schemaVersion\": 1, \"label\": \"coverage\", \"message\": \"$COVERAGE%\", \"color\": \"brightgreen\"}" > coverage.json
|
|
42
|
+
fi
|
|
43
|
+
|
|
37
44
|
- name: Git Graph Reports
|
|
38
45
|
uses: ./
|
|
39
46
|
with:
|
|
40
47
|
production_branch: 'main'
|
|
41
48
|
output_dir: 'git-graph-reports'
|
|
49
|
+
|
|
50
|
+
- name: Copy Coverage to Reports
|
|
51
|
+
run: |
|
|
52
|
+
if [ -f "coverage.json" ]; then
|
|
53
|
+
cp coverage.json git-graph-reports/
|
|
54
|
+
fi
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
name: Deploy Demos to Pages
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ main ]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
pages: write
|
|
11
|
+
id-token: write
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
deploy:
|
|
15
|
+
environment:
|
|
16
|
+
name: github-pages
|
|
17
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
steps:
|
|
20
|
+
- name: Checkout
|
|
21
|
+
uses: actions/checkout@v4
|
|
22
|
+
with:
|
|
23
|
+
fetch-depth: 0
|
|
24
|
+
|
|
25
|
+
- name: Setup uv
|
|
26
|
+
uses: astral-sh/setup-uv@v5
|
|
27
|
+
with:
|
|
28
|
+
enable-cache: true
|
|
29
|
+
|
|
30
|
+
- name: Install dependencies
|
|
31
|
+
run: uv sync --all-extras
|
|
32
|
+
|
|
33
|
+
- name: Install just
|
|
34
|
+
uses: extractions/setup-just@v3
|
|
35
|
+
|
|
36
|
+
- name: Generate Demos
|
|
37
|
+
run: uv run python examples/publish_demos.py
|
|
38
|
+
|
|
39
|
+
- name: Generate Coverage Badge
|
|
40
|
+
run: |
|
|
41
|
+
just coverage
|
|
42
|
+
if [ -f "htmlcov/index.html" ]; then
|
|
43
|
+
COVERAGE=$(grep -oP 'pc_cov">\d+%' htmlcov/index.html | head -1 | grep -oP '\d+')
|
|
44
|
+
echo "{\"schemaVersion\": 1, \"label\": \"coverage\", \"message\": \"$COVERAGE%\", \"color\": \"brightgreen\"}" > demo-site/coverage.json
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
- name: Generate Hygiene Badge
|
|
48
|
+
run: |
|
|
49
|
+
uv run git-graphable analyze . --bare --hygiene-output hygiene.json --output -
|
|
50
|
+
SCORE=$(grep -oP '"score": \d+' hygiene.json | grep -oP '\d+')
|
|
51
|
+
GRADE=$(grep -oP '"grade": "[^"]+"' hygiene.json | cut -d'"' -f4)
|
|
52
|
+
COLOR=$(grep -oP '"color": "[^"]+"' hygiene.json | cut -d'"' -f4)
|
|
53
|
+
echo "{\"schemaVersion\": 1, \"label\": \"hygiene\", \"message\": \"$SCORE% ($GRADE)\", \"color\": \"$COLOR\"}" > demo-site/hygiene_badge.json
|
|
54
|
+
|
|
55
|
+
- name: Setup Pages
|
|
56
|
+
uses: actions/configure-pages@v4
|
|
57
|
+
|
|
58
|
+
- name: Upload artifact
|
|
59
|
+
uses: actions/upload-pages-artifact@v3
|
|
60
|
+
with:
|
|
61
|
+
path: 'demo-site'
|
|
62
|
+
|
|
63
|
+
- name: Deploy to GitHub Pages
|
|
64
|
+
id: deployment
|
|
65
|
+
uses: actions/deploy-pages@v4
|
|
@@ -39,6 +39,7 @@ pip-delete-this-directory.txt
|
|
|
39
39
|
# Unit test / coverage reports
|
|
40
40
|
htmlcov/
|
|
41
41
|
htmlrep/
|
|
42
|
+
report_output/
|
|
42
43
|
.pytest_worker_jsons/
|
|
43
44
|
.tox/
|
|
44
45
|
.nox/
|
|
@@ -134,3 +135,4 @@ cython_debug/
|
|
|
134
135
|
demo-site/
|
|
135
136
|
examples/repos/
|
|
136
137
|
examples/assets/
|
|
138
|
+
graph.html
|
|
@@ -2,6 +2,43 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.6.0] - 2026-03-06
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- **Native GitLab Support**: Added support for GitLab Merge Requests and Issues using the `glab` CLI.
|
|
9
|
+
- **Pull Request Provider Abstraction**: Introduced a new `PullRequestProvider` interface, decoupling PR status logic from GitHub.
|
|
10
|
+
- Added `GitHubPullRequestProvider` (default) using the `gh` CLI.
|
|
11
|
+
- Added `ScriptPullRequestProvider` for custom script-based PR status lookups.
|
|
12
|
+
- **Configuration Trust Mechanism**: Introduced a security layer for local repository configurations.
|
|
13
|
+
- Added `--trust` CLI flag to explicitly trust automatically loaded `.git-graphable.toml` or `pyproject.toml` files.
|
|
14
|
+
- Users are now warned when executing custom scripts from untrusted configurations.
|
|
15
|
+
- **Dynamic Project Badges**:
|
|
16
|
+
- Added automated generation of Shields.io-compatible JSON badges for **Git Hygiene** and **Code Coverage**.
|
|
17
|
+
- Updated `README.md` to display live status from GitHub Pages.
|
|
18
|
+
- Added `--hygiene-output <path>` to CLI for machine-readable (JSON) hygiene summaries.
|
|
19
|
+
- **Improved Automatic Visualization**:
|
|
20
|
+
- Restored the behavior of automatically opening a graph if no output path is provided.
|
|
21
|
+
- Changed default automatic visualization export format from SVG to **PNG** for broader compatibility.
|
|
22
|
+
- **Modular Architecture Refactor**: Substantially reorganized the codebase into specialized sub-packages for better maintainability:
|
|
23
|
+
- `src/git_graphable/prs/`: Modular PR providers.
|
|
24
|
+
- `src/git_graphable/issues/`: Modular issue tracker engines.
|
|
25
|
+
- `src/git_graphable/highlights/`: Specialized highlighting logic (visual, hygiene, external).
|
|
26
|
+
- `src/git_graphable/styling/`: Specialized styling logic per visualization engine.
|
|
27
|
+
- **Enhanced Issue Scripting**: `ScriptIssueEngine` now supports robust JSON output from scripts (with legacy CSV fallback).
|
|
28
|
+
- **Project Governance**: Added official **MIT License** to the repository.
|
|
29
|
+
- **Reorganized Test Suite**: Refactored tests into a modular structure mirroring the source code, including a global `conftest.py` for shared fixtures.
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
- **Comprehensive Security Hardening**:
|
|
33
|
+
- Protected against argument injection in `git clone`, `glab`, `gh`, and `git rev-list` commands using the `--` separator.
|
|
34
|
+
- Resolved potential path traversal and SSRF risks in Jira issue lookups by properly URL-encoding IDs.
|
|
35
|
+
- Corrected argument passing logic for `git log` limits.
|
|
36
|
+
- **Mermaid Parser Reliability**: Fixed parsing errors in Mermaid graphs by substituting sensitive characters (brackets and parentheses) with visually similar full-width Unicode characters.
|
|
37
|
+
- **CI Stability**: Fixed CI failures in GitHub Pages deployment by preventing automatic visualization triggers during badge generation.
|
|
38
|
+
- **Naming Collisions**: Resolved pytest module collision issues by adding `__init__.py` files to test directories.
|
|
39
|
+
- **Code Maintenance**: Updated `.gitignore` to prevent tracking of build artifacts and coverage data.
|
|
40
|
+
|
|
41
|
+
|
|
5
42
|
## [0.5.0] - 2026-03-06
|
|
6
43
|
|
|
7
44
|
### Added
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Richard West
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: git-graphable
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.0
|
|
4
4
|
Summary: A powerful Git history visualizer and hygiene linter with CI gating.
|
|
5
5
|
Project-URL: Homepage, https://github.com/TheTrueSCU/git-graphable
|
|
6
6
|
Project-URL: Issues, https://github.com/TheTrueSCU/git-graphable/issues
|
|
7
7
|
Project-URL: Repository, https://github.com/TheTrueSCU/git-graphable
|
|
8
8
|
Author-email: Richard West <dopplereffect.us@gmail.com>
|
|
9
|
-
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: analysis,automation,badges,ci,cytoscape,d2,devops,git,git-flow,github,github-actions,gitlab,graph,html,hygiene,interactive,jira,lint,mermaid,metrics,topology,visualization
|
|
10
12
|
Requires-Python: >=3.13
|
|
11
13
|
Requires-Dist: graphable>=0.6.0
|
|
12
14
|
Provides-Extra: cli
|
|
@@ -16,6 +18,12 @@ Description-Content-Type: text/markdown
|
|
|
16
18
|
|
|
17
19
|
# Git Graphable
|
|
18
20
|
|
|
21
|
+
[](https://github.com/TheTrueSCU/git-graphable/actions/workflows/ci.yml)
|
|
22
|
+
[](https://opensource.org/licenses/MIT)
|
|
23
|
+
[](https://thetruescu.github.io/git-graphable/)
|
|
24
|
+
[](https://thetruescu.github.io/git-graphable/)
|
|
25
|
+
|
|
26
|
+
|
|
19
27
|
A powerful Python tool to convert Git commit history into beautiful, interactive flowcharts using the `graphable` library. Supporting Mermaid, D2, Graphviz, and HTML.
|
|
20
28
|
|
|
21
29
|
## Git Plugin Support
|
|
@@ -31,18 +39,13 @@ Check out the tool in action with our **[Live Interactive Demos](https://thetrue
|
|
|
31
39
|
## Features
|
|
32
40
|
|
|
33
41
|
- **Multi-Engine Support**: Export to Mermaid (.mmd), D2 (.d2), Graphviz (.dot), or HTML (.html).
|
|
34
|
-
- **Automatic Visualization**: Generates and opens an image (
|
|
42
|
+
- **Automatic Visualization**: Generates and opens an image (PNG) automatically if no output is specified.
|
|
35
43
|
- **Advanced Highlighting**: Visualize author patterns, topological distance, and specific merge paths.
|
|
36
|
-
- **
|
|
44
|
+
- **VCS Integration**: Highlight commits based on pull request/merge request status using `gh` (GitHub) or `glab` (GitLab) CLIs.
|
|
37
45
|
- **Hygiene Analysis**: Automatically detect WIP commits, direct pushes to protected branches, squashed PRs, back-merges, and contributor silos.
|
|
38
|
-
- **Issue Tracker Integration**: Connect to Jira, GitHub Issues, or custom scripts to highlight status desyncs.
|
|
39
|
-
- **
|
|
40
|
-
- **
|
|
41
|
-
- **Visual Customization**: Fully customize colors, widths, and line styles for nodes and edges. See [STYLING.md](STYLING.md).
|
|
42
|
-
- **Configurable Penalties**: Fully customize the scoring logic by adjusting penalties and caps for each metric.
|
|
43
|
-
- **CI Gating**: Use the `--check` flag to return a non-zero exit code if the hygiene score falls below a threshold (configurable via `--min-score`).
|
|
44
|
-
- **Flexible Input**: Works with local repository paths or remote Git URLs.
|
|
45
|
-
- **Dual CLI**: Modern Rich/Typer interface with a robust argparse fallback for bare environments.
|
|
46
|
+
- **Issue Tracker Integration**: Connect to Jira, GitHub Issues, GitLab Issues, or custom scripts to highlight status desyncs.
|
|
47
|
+
- **Security First**: Configuration trust mechanism ensures custom scripts only run from trusted sources (use `--trust` to authorize local configs).
|
|
48
|
+
- **Dynamic Badges**: Host live Shields.io badges for Git Hygiene and Code Coverage on GitHub Pages.
|
|
46
49
|
|
|
47
50
|
## Installation
|
|
48
51
|
|
|
@@ -86,7 +89,7 @@ jobs:
|
|
|
86
89
|
fetch-depth: 0 # Required to see full history
|
|
87
90
|
|
|
88
91
|
- name: Generate Git Graph Reports
|
|
89
|
-
uses: TheTrueSCU/git-graphable@v0.
|
|
92
|
+
uses: TheTrueSCU/git-graphable@v0.6.0
|
|
90
93
|
with:
|
|
91
94
|
production_branch: 'main'
|
|
92
95
|
output_dir: 'reports'
|
|
@@ -125,7 +128,7 @@ Git Graphable provides several ways to highlight commits and relationships. Mult
|
|
|
125
128
|
| Option | Target | Effect | Conflicts With |
|
|
126
129
|
| :--- | :--- | :--- | :--- |
|
|
127
130
|
| `--highlight-authors` | **Fill** | Unique color per author | PR Status, Distance, Stale |
|
|
128
|
-
| `--highlight-pr-status` | **Fill/Stroke**| Color by PR state (Merged=Purple, Open=Green) | Authors, Distance, Stale |
|
|
131
|
+
| `--highlight-pr-status` | **Fill/Stroke**| Color by PR/MR state (Merged=Purple, Open=Green) | Authors, Distance, Stale |
|
|
129
132
|
| `--highlight-distance-from` | **Fill** | Blue gradient fading by distance | Authors, PR Status, Stale |
|
|
130
133
|
| `--highlight-stale` | **Fill** | Gradient white to red by age | Authors, PR Status, Distance |
|
|
131
134
|
| `--highlight-path` | **Edge** | Thick Orange edge connecting nodes | None |
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Git Graphable
|
|
2
2
|
|
|
3
|
+
[](https://github.com/TheTrueSCU/git-graphable/actions/workflows/ci.yml)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](https://thetruescu.github.io/git-graphable/)
|
|
6
|
+
[](https://thetruescu.github.io/git-graphable/)
|
|
7
|
+
|
|
8
|
+
|
|
3
9
|
A powerful Python tool to convert Git commit history into beautiful, interactive flowcharts using the `graphable` library. Supporting Mermaid, D2, Graphviz, and HTML.
|
|
4
10
|
|
|
5
11
|
## Git Plugin Support
|
|
@@ -15,18 +21,13 @@ Check out the tool in action with our **[Live Interactive Demos](https://thetrue
|
|
|
15
21
|
## Features
|
|
16
22
|
|
|
17
23
|
- **Multi-Engine Support**: Export to Mermaid (.mmd), D2 (.d2), Graphviz (.dot), or HTML (.html).
|
|
18
|
-
- **Automatic Visualization**: Generates and opens an image (
|
|
24
|
+
- **Automatic Visualization**: Generates and opens an image (PNG) automatically if no output is specified.
|
|
19
25
|
- **Advanced Highlighting**: Visualize author patterns, topological distance, and specific merge paths.
|
|
20
|
-
- **
|
|
26
|
+
- **VCS Integration**: Highlight commits based on pull request/merge request status using `gh` (GitHub) or `glab` (GitLab) CLIs.
|
|
21
27
|
- **Hygiene Analysis**: Automatically detect WIP commits, direct pushes to protected branches, squashed PRs, back-merges, and contributor silos.
|
|
22
|
-
- **Issue Tracker Integration**: Connect to Jira, GitHub Issues, or custom scripts to highlight status desyncs.
|
|
23
|
-
- **
|
|
24
|
-
- **
|
|
25
|
-
- **Visual Customization**: Fully customize colors, widths, and line styles for nodes and edges. See [STYLING.md](STYLING.md).
|
|
26
|
-
- **Configurable Penalties**: Fully customize the scoring logic by adjusting penalties and caps for each metric.
|
|
27
|
-
- **CI Gating**: Use the `--check` flag to return a non-zero exit code if the hygiene score falls below a threshold (configurable via `--min-score`).
|
|
28
|
-
- **Flexible Input**: Works with local repository paths or remote Git URLs.
|
|
29
|
-
- **Dual CLI**: Modern Rich/Typer interface with a robust argparse fallback for bare environments.
|
|
28
|
+
- **Issue Tracker Integration**: Connect to Jira, GitHub Issues, GitLab Issues, or custom scripts to highlight status desyncs.
|
|
29
|
+
- **Security First**: Configuration trust mechanism ensures custom scripts only run from trusted sources (use `--trust` to authorize local configs).
|
|
30
|
+
- **Dynamic Badges**: Host live Shields.io badges for Git Hygiene and Code Coverage on GitHub Pages.
|
|
30
31
|
|
|
31
32
|
## Installation
|
|
32
33
|
|
|
@@ -70,7 +71,7 @@ jobs:
|
|
|
70
71
|
fetch-depth: 0 # Required to see full history
|
|
71
72
|
|
|
72
73
|
- name: Generate Git Graph Reports
|
|
73
|
-
uses: TheTrueSCU/git-graphable@v0.
|
|
74
|
+
uses: TheTrueSCU/git-graphable@v0.6.0
|
|
74
75
|
with:
|
|
75
76
|
production_branch: 'main'
|
|
76
77
|
output_dir: 'reports'
|
|
@@ -109,7 +110,7 @@ Git Graphable provides several ways to highlight commits and relationships. Mult
|
|
|
109
110
|
| Option | Target | Effect | Conflicts With |
|
|
110
111
|
| :--- | :--- | :--- | :--- |
|
|
111
112
|
| `--highlight-authors` | **Fill** | Unique color per author | PR Status, Distance, Stale |
|
|
112
|
-
| `--highlight-pr-status` | **Fill/Stroke**| Color by PR state (Merged=Purple, Open=Green) | Authors, Distance, Stale |
|
|
113
|
+
| `--highlight-pr-status` | **Fill/Stroke**| Color by PR/MR state (Merged=Purple, Open=Green) | Authors, Distance, Stale |
|
|
113
114
|
| `--highlight-distance-from` | **Fill** | Blue gradient fading by distance | Authors, PR Status, Stale |
|
|
114
115
|
| `--highlight-stale` | **Fill** | Gradient white to red by age | Authors, PR Status, Distance |
|
|
115
116
|
| `--highlight-path` | **Edge** | Thick Orange edge connecting nodes | None |
|
|
@@ -50,7 +50,9 @@ Analyze git history and generate a graph. This is the default command; if no com
|
|
|
50
50
|
* `--highlight-long-running`: Highlight long-running branches
|
|
51
51
|
* `--long-running-days INTEGER`: Threshold in days for long-running branches
|
|
52
52
|
* `--long-running-base TEXT`: Base branch for long-running analysis
|
|
53
|
-
* `--highlight-pr-status`: Highlight commits based on GitHub
|
|
53
|
+
* `--highlight-pr-status`: Highlight commits based on PR status (GitHub or GitLab)
|
|
54
|
+
* `--pr-provider [github|gitlab|script]`: Provider to fetch PR statuses (default: github)
|
|
55
|
+
* `--pr-script TEXT`: Path to a script that returns PR data in JSON format
|
|
54
56
|
* `--highlight-wip`: Highlight WIP/TODO commits
|
|
55
57
|
* `--wip-keyword TEXT`: Additional keyword to trigger WIP highlighting
|
|
56
58
|
* `--highlight-direct-pushes`: Highlight non-merge commits on protected branches
|
|
@@ -61,7 +63,7 @@ Analyze git history and generate a graph. This is the default command; if no com
|
|
|
61
63
|
* `--silo-author-count INTEGER`: Author count threshold for silo detection
|
|
62
64
|
* `--highlight-issue-inconsistencies`: Highlight mismatches between Git and Issue Tracker
|
|
63
65
|
* `--issue-pattern TEXT`: Regex pattern to extract issue IDs
|
|
64
|
-
* `--issue-engine [github|jira|script]`: Engine to fetch issue statuses
|
|
66
|
+
* `--issue-engine [github|gitlab|jira|script]`: Engine to fetch issue statuses
|
|
65
67
|
* `--jira-url TEXT`: Base URL for Jira instance
|
|
66
68
|
* `--issue-script TEXT`: Shell command template for script engine
|
|
67
69
|
* `--highlight-release-inconsistencies`: Highlight issues marked Released but not tagged
|
|
@@ -74,6 +76,8 @@ Analyze git history and generate a graph. This is the default command; if no com
|
|
|
74
76
|
* `--check`: Exit with non-zero if hygiene score is below threshold
|
|
75
77
|
* `--min-score INTEGER`: Minimum hygiene score required for --check
|
|
76
78
|
* `--bare`: Force bare mode (no rich output)
|
|
79
|
+
* `--hygiene-output TEXT`: Path to save hygiene summary as JSON.
|
|
80
|
+
* `--trust`: Trust configuration files (.git-graphable.toml, pyproject.toml) found in the repository. Required to execute custom scripts from these sources.
|
|
77
81
|
* `--help`: Show this message and exit.
|
|
78
82
|
|
|
79
83
|
---
|
|
@@ -52,15 +52,26 @@ runs:
|
|
|
52
52
|
shell: bash
|
|
53
53
|
run: mkdir -p ${{ inputs.output_dir }}
|
|
54
54
|
|
|
55
|
-
- name: Generate Simplified Graph (Mermaid)
|
|
55
|
+
- name: Generate Simplified Graph (Mermaid) and Hygiene Stats
|
|
56
56
|
shell: bash
|
|
57
57
|
run: |
|
|
58
|
-
ARGS="--production-branch ${{ inputs.production_branch }} --simplify --engine mermaid -o ${{ inputs.output_dir }}/summary.mmd"
|
|
58
|
+
ARGS="--production-branch ${{ inputs.production_branch }} --simplify --engine mermaid -o ${{ inputs.output_dir }}/summary.mmd --hygiene-output ${{ inputs.output_dir }}/hygiene.json"
|
|
59
59
|
if [ -n "${{ inputs.issue_engine }}" ]; then ARGS="$ARGS --issue-engine ${{ inputs.issue_engine }}"; fi
|
|
60
60
|
uv tool run git-graphable --bare ${{ inputs.path }} $ARGS
|
|
61
61
|
env:
|
|
62
62
|
GITHUB_TOKEN: ${{ inputs.github_token }}
|
|
63
63
|
|
|
64
|
+
- name: Generate Hygiene Badge Data
|
|
65
|
+
shell: bash
|
|
66
|
+
run: |
|
|
67
|
+
if [ -f "${{ inputs.output_dir }}/hygiene.json" ]; then
|
|
68
|
+
SCORE=$(grep -oP '"score": \d+' ${{ inputs.output_dir }}/hygiene.json | grep -oP '\d+')
|
|
69
|
+
GRADE=$(grep -oP '"grade": "[^"]+"' ${{ inputs.output_dir }}/hygiene.json | cut -d'"' -f4)
|
|
70
|
+
COLOR=$(grep -oP '"color": "[^"]+"' ${{ inputs.output_dir }}/hygiene.json | cut -d'"' -f4)
|
|
71
|
+
# Shields.io JSON endpoint format
|
|
72
|
+
echo "{\"schemaVersion\": 1, \"label\": \"hygiene\", \"message\": \"$SCORE% ($GRADE)\", \"color\": \"$COLOR\"}" > ${{ inputs.output_dir }}/hygiene_badge.json
|
|
73
|
+
fi
|
|
74
|
+
|
|
64
75
|
- name: Generate Full Interactive Graph (HTML)
|
|
65
76
|
shell: bash
|
|
66
77
|
run: |
|
|
@@ -9,26 +9,32 @@ description = "A powerful Git history visualizer and hygiene linter with CI gati
|
|
|
9
9
|
keywords = [
|
|
10
10
|
"analysis",
|
|
11
11
|
"automation",
|
|
12
|
+
"badges",
|
|
12
13
|
"ci",
|
|
13
14
|
"cytoscape",
|
|
14
15
|
"d2",
|
|
16
|
+
"devops",
|
|
15
17
|
"git",
|
|
16
18
|
"git-flow",
|
|
17
19
|
"github",
|
|
18
20
|
"github-actions",
|
|
21
|
+
"gitlab",
|
|
19
22
|
"graph",
|
|
20
23
|
"html",
|
|
21
24
|
"hygiene",
|
|
22
25
|
"interactive",
|
|
26
|
+
"jira",
|
|
23
27
|
"lint",
|
|
24
28
|
"mermaid",
|
|
29
|
+
"metrics",
|
|
25
30
|
"topology",
|
|
26
31
|
"visualization",
|
|
27
32
|
]
|
|
33
|
+
license = "MIT"
|
|
28
34
|
name = "git-graphable"
|
|
29
35
|
readme = "README.md"
|
|
30
36
|
requires-python = ">=3.13"
|
|
31
|
-
version = "0.
|
|
37
|
+
version = "0.6.0"
|
|
32
38
|
|
|
33
39
|
[project.optional-dependencies]
|
|
34
40
|
cli = [
|
|
@@ -38,7 +38,7 @@ def run_bare_cli():
|
|
|
38
38
|
default="mermaid",
|
|
39
39
|
help="Visualization engine (mermaid, graphviz, d2, html)",
|
|
40
40
|
)
|
|
41
|
-
p.add_argument("-o", "--output", default=
|
|
41
|
+
p.add_argument("-o", "--output", default=None, help="Output file path")
|
|
42
42
|
p.add_argument(
|
|
43
43
|
"--image",
|
|
44
44
|
action="store_true",
|
|
@@ -156,6 +156,15 @@ def run_bare_cli():
|
|
|
156
156
|
help="Exit with non-zero if hygiene score is below threshold",
|
|
157
157
|
)
|
|
158
158
|
p.add_argument("--min-score", type=int, help="Minimum score for --check")
|
|
159
|
+
p.add_argument(
|
|
160
|
+
"--hygiene-output",
|
|
161
|
+
help="Path to save hygiene summary as JSON",
|
|
162
|
+
)
|
|
163
|
+
p.add_argument(
|
|
164
|
+
"--trust",
|
|
165
|
+
action="store_true",
|
|
166
|
+
help="Trust configuration files found in the repository",
|
|
167
|
+
)
|
|
159
168
|
p.add_argument(
|
|
160
169
|
"--penalty",
|
|
161
170
|
action="append",
|
|
@@ -287,8 +296,11 @@ def run_bare_cli():
|
|
|
287
296
|
else {},
|
|
288
297
|
"theme": parse_style_overrides(args.style) if args.style else {},
|
|
289
298
|
"min_hygiene_score": args.min_score,
|
|
299
|
+
"hygiene_output": args.hygiene_output,
|
|
300
|
+
"trust": args.trust,
|
|
290
301
|
}
|
|
291
302
|
|
|
303
|
+
|
|
292
304
|
try:
|
|
293
305
|
results = convert_command(
|
|
294
306
|
args.path,
|
|
@@ -303,7 +315,14 @@ def run_bare_cli():
|
|
|
303
315
|
print(results["content"])
|
|
304
316
|
|
|
305
317
|
if results.get("summary"):
|
|
318
|
+
import json
|
|
319
|
+
|
|
306
320
|
hygiene = results["summary"].get("Hygiene Score", {})
|
|
321
|
+
|
|
322
|
+
if args.hygiene_output:
|
|
323
|
+
with open(args.hygiene_output, "w") as f:
|
|
324
|
+
json.dump(hygiene, f, indent=2)
|
|
325
|
+
|
|
307
326
|
print("\n--- Git Hygiene Summary ---")
|
|
308
327
|
print(
|
|
309
328
|
f"Overall Score: {hygiene.get('score', 0)}% ({hygiene.get('grade', 'F')})"
|
|
@@ -11,7 +11,7 @@ from .styler import export_graph
|
|
|
11
11
|
def get_extension(engine: Engine, as_image: bool) -> str:
|
|
12
12
|
"""Get file extension for the given engine and export type."""
|
|
13
13
|
if as_image:
|
|
14
|
-
return ".
|
|
14
|
+
return ".png" # Default to PNG for images
|
|
15
15
|
|
|
16
16
|
extensions = {
|
|
17
17
|
Engine.MERMAID: ".mmd",
|
|
@@ -83,7 +83,9 @@ def load_config(
|
|
|
83
83
|
# 4. pyproject.toml in the repo
|
|
84
84
|
|
|
85
85
|
# Try to find a config file if not explicitly provided
|
|
86
|
+
is_trusted = True
|
|
86
87
|
if not config_path:
|
|
88
|
+
is_trusted = False
|
|
87
89
|
possible_paths = [
|
|
88
90
|
os.path.join(path, ".git-graphable.toml"),
|
|
89
91
|
os.path.join(path, "pyproject.toml"),
|
|
@@ -96,6 +98,7 @@ def load_config(
|
|
|
96
98
|
base_config = GitLogConfig()
|
|
97
99
|
if config_path and os.path.exists(config_path):
|
|
98
100
|
base_config = GitLogConfig.from_toml(config_path)
|
|
101
|
+
base_config.trusted = is_trusted
|
|
99
102
|
|
|
100
103
|
return base_config.merge(cli_overrides)
|
|
101
104
|
|
|
@@ -108,7 +111,7 @@ def ensure_local_repo(path: str) -> tuple[str, Optional[tempfile.TemporaryDirect
|
|
|
108
111
|
try:
|
|
109
112
|
# Clone bare repo for speed and less disk space
|
|
110
113
|
subprocess.run(
|
|
111
|
-
["git", "clone", "--bare", path, temp_dir.name],
|
|
114
|
+
["git", "clone", "--bare", "--", path, temp_dir.name],
|
|
112
115
|
check=True,
|
|
113
116
|
capture_output=True,
|
|
114
117
|
)
|
|
@@ -193,6 +193,8 @@ class GitLogConfig:
|
|
|
193
193
|
longevity_threshold_days: int = (
|
|
194
194
|
14 # Max diff between Issue created and first commit
|
|
195
195
|
)
|
|
196
|
+
trusted: bool = True # True if explicitly provided via CLI or from a trusted source
|
|
197
|
+
trust: bool = False # CLI override to force trust
|
|
196
198
|
hygiene_weights: HygieneWeights = field(default_factory=HygieneWeights)
|
|
197
199
|
theme: ThemeConfig = field(default_factory=ThemeConfig)
|
|
198
200
|
|
|
@@ -297,6 +299,9 @@ class GitLogConfig:
|
|
|
297
299
|
else:
|
|
298
300
|
setattr(new_config, key, value)
|
|
299
301
|
|
|
302
|
+
if new_config.trust:
|
|
303
|
+
new_config.trusted = True
|
|
304
|
+
|
|
300
305
|
return new_config
|
|
301
306
|
|
|
302
307
|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Backward compatibility shim for github-related PR logic.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Dict, List
|
|
6
|
+
|
|
7
|
+
from .prs.base import PullRequestInfo
|
|
8
|
+
from .prs.github import GitHubPullRequestProvider
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def get_repo_prs(repo_path: str) -> List[PullRequestInfo]:
|
|
12
|
+
"""Backward compatible helper."""
|
|
13
|
+
return GitHubPullRequestProvider().get_repo_prs(repo_path)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def map_prs_to_commits(prs: List[PullRequestInfo]) -> Dict[str, PullRequestInfo]:
|
|
17
|
+
"""Backward compatible helper."""
|
|
18
|
+
# We use an instance here to access the base method
|
|
19
|
+
from .prs.base import PullRequestProvider
|
|
20
|
+
|
|
21
|
+
class Shim(PullRequestProvider):
|
|
22
|
+
def get_repo_prs(self, repo_path: str):
|
|
23
|
+
return []
|
|
24
|
+
|
|
25
|
+
return Shim().map_prs_to_commits(prs)
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Core highlights application logic.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
from graphable import Graph
|
|
8
|
+
|
|
9
|
+
from ..core import Engine, GitCommit, GitLogConfig
|
|
10
|
+
from .external import (
|
|
11
|
+
_apply_issue_highlights,
|
|
12
|
+
_apply_pr_highlights,
|
|
13
|
+
_apply_release_highlights,
|
|
14
|
+
_apply_squash_highlights,
|
|
15
|
+
)
|
|
16
|
+
from .hygiene import (
|
|
17
|
+
_apply_back_merge_highlights,
|
|
18
|
+
_apply_direct_push_highlights,
|
|
19
|
+
_apply_divergence_highlights,
|
|
20
|
+
_apply_long_running_highlights,
|
|
21
|
+
_apply_orphan_highlights,
|
|
22
|
+
_apply_silo_highlights,
|
|
23
|
+
_apply_stale_highlights,
|
|
24
|
+
_apply_wip_highlights,
|
|
25
|
+
)
|
|
26
|
+
from .visual import (
|
|
27
|
+
_apply_author_highlights,
|
|
28
|
+
_apply_critical_highlights,
|
|
29
|
+
_apply_distance_highlights,
|
|
30
|
+
_apply_path_highlights,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def apply_highlights(
|
|
35
|
+
graph: Graph[GitCommit], config: GitLogConfig, repo_path: Optional[str] = None
|
|
36
|
+
):
|
|
37
|
+
"""Apply highlighting tags based on configuration."""
|
|
38
|
+
is_html = config.engine == Engine.HTML
|
|
39
|
+
|
|
40
|
+
# We now always apply hygiene tags so the summary/score is stable.
|
|
41
|
+
# The 'force=True' ensures tags are added to nodes.
|
|
42
|
+
# The CLI flags now only control the VISUAL highlighting (color: tags).
|
|
43
|
+
|
|
44
|
+
_apply_pr_highlights(graph, config, repo_path, force=True)
|
|
45
|
+
_apply_author_highlights(graph, config, force=is_html)
|
|
46
|
+
_apply_critical_highlights(graph, config, force=True)
|
|
47
|
+
_apply_distance_highlights(graph, config, force=is_html)
|
|
48
|
+
_apply_path_highlights(graph, config)
|
|
49
|
+
_apply_divergence_highlights(graph, config, force=True)
|
|
50
|
+
_apply_orphan_highlights(graph, config, force=True)
|
|
51
|
+
_apply_stale_highlights(graph, config, force=True)
|
|
52
|
+
_apply_long_running_highlights(graph, config, force=True)
|
|
53
|
+
_apply_wip_highlights(graph, config, force=True)
|
|
54
|
+
_apply_direct_push_highlights(graph, config, force=True)
|
|
55
|
+
_apply_squash_highlights(graph, config, repo_path, force=True)
|
|
56
|
+
_apply_back_merge_highlights(graph, config, force=True)
|
|
57
|
+
_apply_silo_highlights(graph, config, force=True)
|
|
58
|
+
_apply_issue_highlights(graph, config, repo_path, force=True)
|
|
59
|
+
_apply_release_highlights(graph, config, repo_path, force=True)
|