git-graphable 0.4.0__tar.gz → 0.5.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 (59) hide show
  1. {git_graphable-0.4.0 → git_graphable-0.5.0}/.github/workflows/ci.yml +1 -1
  2. git_graphable-0.5.0/.github/workflows/pages.yml +46 -0
  3. {git_graphable-0.4.0 → git_graphable-0.5.0}/.gitignore +1 -3
  4. {git_graphable-0.4.0 → git_graphable-0.5.0}/CHANGELOG.md +9 -0
  5. {git_graphable-0.4.0 → git_graphable-0.5.0}/PKG-INFO +32 -4
  6. {git_graphable-0.4.0 → git_graphable-0.5.0}/README.md +31 -3
  7. {git_graphable-0.4.0/.github/actions/git-graphable → git_graphable-0.5.0}/action.yml +5 -4
  8. git_graphable-0.5.0/examples/index_template.html +24 -0
  9. git_graphable-0.5.0/examples/publish_demos.py +110 -0
  10. {git_graphable-0.4.0 → git_graphable-0.5.0}/pyproject.toml +1 -1
  11. {git_graphable-0.4.0 → git_graphable-0.5.0}/src/git_graphable/commands.py +22 -8
  12. {git_graphable-0.4.0 → git_graphable-0.5.0}/src/git_graphable/highlighter.py +6 -2
  13. {git_graphable-0.4.0 → git_graphable-0.5.0}/uv.lock +1 -1
  14. {git_graphable-0.4.0 → git_graphable-0.5.0}/.gemini/GEMINI.md +0 -0
  15. {git_graphable-0.4.0 → git_graphable-0.5.0}/.gemini/code-ordering.md +0 -0
  16. {git_graphable-0.4.0 → git_graphable-0.5.0}/.github/dependabot.yml +0 -0
  17. {git_graphable-0.4.0 → git_graphable-0.5.0}/.github/workflows/publish.yml +0 -0
  18. {git_graphable-0.4.0 → git_graphable-0.5.0}/.python-version +0 -0
  19. {git_graphable-0.4.0 → git_graphable-0.5.0}/Justfile +0 -0
  20. {git_graphable-0.4.0 → git_graphable-0.5.0}/SECURITY.md +0 -0
  21. {git_graphable-0.4.0 → git_graphable-0.5.0}/STYLING.md +0 -0
  22. {git_graphable-0.4.0 → git_graphable-0.5.0}/USAGE.md +0 -0
  23. {git_graphable-0.4.0 → git_graphable-0.5.0}/examples/EXAMPLES.md +0 -0
  24. {git_graphable-0.4.0 → git_graphable-0.5.0}/examples/generate_demos.py +0 -0
  25. {git_graphable-0.4.0 → git_graphable-0.5.0}/graph.html +0 -0
  26. {git_graphable-0.4.0 → git_graphable-0.5.0}/report_output/final_report.json +0 -0
  27. {git_graphable-0.4.0 → git_graphable-0.5.0}/report_output/plus_metadata.json +0 -0
  28. {git_graphable-0.4.0 → git_graphable-0.5.0}/report_output/report.html +0 -0
  29. {git_graphable-0.4.0 → git_graphable-0.5.0}/report_output/screenshots/test_interactivity_toggling_chromium__failure.png +0 -0
  30. {git_graphable-0.4.0 → git_graphable-0.5.0}/src/git_graphable/__init__.py +0 -0
  31. {git_graphable-0.4.0 → git_graphable-0.5.0}/src/git_graphable/bare_cli.py +0 -0
  32. {git_graphable-0.4.0 → git_graphable-0.5.0}/src/git_graphable/cli.py +0 -0
  33. {git_graphable-0.4.0 → git_graphable-0.5.0}/src/git_graphable/cli_utils.py +0 -0
  34. {git_graphable-0.4.0 → git_graphable-0.5.0}/src/git_graphable/core.py +0 -0
  35. {git_graphable-0.4.0 → git_graphable-0.5.0}/src/git_graphable/default_config.toml +0 -0
  36. {git_graphable-0.4.0 → git_graphable-0.5.0}/src/git_graphable/github.py +0 -0
  37. {git_graphable-0.4.0 → git_graphable-0.5.0}/src/git_graphable/hygiene.py +0 -0
  38. {git_graphable-0.4.0 → git_graphable-0.5.0}/src/git_graphable/issues.py +0 -0
  39. {git_graphable-0.4.0 → git_graphable-0.5.0}/src/git_graphable/models.py +0 -0
  40. {git_graphable-0.4.0 → git_graphable-0.5.0}/src/git_graphable/parser.py +0 -0
  41. {git_graphable-0.4.0 → git_graphable-0.5.0}/src/git_graphable/rich_cli.py +0 -0
  42. {git_graphable-0.4.0 → git_graphable-0.5.0}/src/git_graphable/styler.py +0 -0
  43. {git_graphable-0.4.0 → git_graphable-0.5.0}/src/git_graphable/templates.py +0 -0
  44. {git_graphable-0.4.0 → git_graphable-0.5.0}/tests/test_bare_cli.py +0 -0
  45. {git_graphable-0.4.0 → git_graphable-0.5.0}/tests/test_cli.py +0 -0
  46. {git_graphable-0.4.0 → git_graphable-0.5.0}/tests/test_cli_utils.py +0 -0
  47. {git_graphable-0.4.0 → git_graphable-0.5.0}/tests/test_config.py +0 -0
  48. {git_graphable-0.4.0 → git_graphable-0.5.0}/tests/test_core.py +0 -0
  49. {git_graphable-0.4.0 → git_graphable-0.5.0}/tests/test_examples_html.py +0 -0
  50. {git_graphable-0.4.0 → git_graphable-0.5.0}/tests/test_github.py +0 -0
  51. {git_graphable-0.4.0 → git_graphable-0.5.0}/tests/test_highlighter.py +0 -0
  52. {git_graphable-0.4.0 → git_graphable-0.5.0}/tests/test_hygiene.py +0 -0
  53. {git_graphable-0.4.0 → git_graphable-0.5.0}/tests/test_interactive_html.py +0 -0
  54. {git_graphable-0.4.0 → git_graphable-0.5.0}/tests/test_issues.py +0 -0
  55. {git_graphable-0.4.0 → git_graphable-0.5.0}/tests/test_models.py +0 -0
  56. {git_graphable-0.4.0 → git_graphable-0.5.0}/tests/test_parser.py +0 -0
  57. {git_graphable-0.4.0 → git_graphable-0.5.0}/tests/test_rich_cli.py +0 -0
  58. {git_graphable-0.4.0 → git_graphable-0.5.0}/tests/test_styler.py +0 -0
  59. {git_graphable-0.4.0 → git_graphable-0.5.0}/tests/test_ui_interactive.py +0 -0
@@ -35,7 +35,7 @@ jobs:
35
35
  run: just check
36
36
 
37
37
  - name: Git Graph Reports
38
- uses: ./.github/actions/git-graphable
38
+ uses: ./
39
39
  with:
40
40
  production_branch: 'main'
41
41
  output_dir: 'git-graph-reports'
@@ -0,0 +1,46 @@
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: Generate Demos
34
+ run: uv run python examples/publish_demos.py
35
+
36
+ - name: Setup Pages
37
+ uses: actions/configure-pages@v4
38
+
39
+ - name: Upload artifact
40
+ uses: actions/upload-pages-artifact@v3
41
+ with:
42
+ path: 'demo-site'
43
+
44
+ - name: Deploy to GitHub Pages
45
+ id: deployment
46
+ uses: actions/deploy-pages@v4
@@ -131,8 +131,6 @@ cython_debug/
131
131
  .DS_Store
132
132
 
133
133
  # Examples
134
+ demo-site/
134
135
  examples/repos/
135
136
  examples/assets/
136
-
137
- # Temporary test assets
138
- test_pr.mmd
@@ -2,6 +2,15 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [0.5.0] - 2026-03-06
6
+
7
+ ### Added
8
+ - **Interactive Demos**: Implemented `examples/publish_demos.py` to generate and host live HTML demos via GitHub Pages.
9
+ - **Marketplace Preparation**: Moved `action.yml` to the root directory for GitHub Marketplace publishing and updated related documentation and CI workflows.
10
+
11
+ ### Fixed
12
+ - **Squash Merge Detection**: Resolved a `KeyError` and `CycleError` in the logical merge visualization for squashed pull requests when local branch tips were present.
13
+
5
14
  ## [0.4.0] - 2026-03-05
6
15
 
7
16
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: git-graphable
3
- Version: 0.4.0
3
+ Version: 0.5.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
@@ -21,9 +21,13 @@ A powerful Python tool to convert Git commit history into beautiful, interactive
21
21
  ## Git Plugin Support
22
22
  When installed in your PATH, you can use this as a native Git plugin:
23
23
  ```bash
24
- git graphable .
24
+ git graphable analyze .
25
25
  ```
26
26
 
27
+ ## 🚀 Live Interactive Demo
28
+ Check out the tool in action with our **[Live Interactive Demos](https://thetruescu.github.io/git-graphable/)**. Explore different hygiene scenarios and toggle overlays in real-time.
29
+
30
+
27
31
  ## Features
28
32
 
29
33
  - **Multi-Engine Support**: Export to Mermaid (.mmd), D2 (.d2), Graphviz (.dot), or HTML (.html).
@@ -66,6 +70,7 @@ uv run git-graphable init
66
70
 
67
71
  # Simplify the graph (only show branches/tags)
68
72
  uv run git-graphable analyze . --simplify
73
+ ```
69
74
 
70
75
  ## GitHub Action
71
76
 
@@ -81,14 +86,37 @@ jobs:
81
86
  fetch-depth: 0 # Required to see full history
82
87
 
83
88
  - name: Generate Git Graph Reports
84
- uses: TheTrueSCU/git-graphable/.github/actions/git-graphable@v0.4.0
89
+ uses: TheTrueSCU/git-graphable@v0.5.0
85
90
  with:
86
91
  production_branch: 'main'
87
92
  output_dir: 'reports'
88
93
  ```
89
94
 
95
+ ### Inputs
96
+
97
+ The following inputs are available for the `git-graphable` action:
98
+
99
+ * **`path`**
100
+ * Description: Path to the git repository
101
+ * Required: `false`
102
+ * Default: `'.'`
103
+ * **`production_branch`**
104
+ * Description: The main production branch (e.g. main, master)
105
+ * Required: `false`
106
+ * Default: `'main'`
107
+ * **`issue_engine`**
108
+ * Description: Issue tracker engine (github or jira)
109
+ * Required: `false`
110
+ * **`github_token`**
111
+ * Description: GitHub token for issue integration
112
+ * Required: `false`
113
+ * Default: `${{ github.token }}`
114
+ * **`output_dir`**
115
+ * Description: Directory to save the generated reports
116
+ * Required: `false`
117
+ * Default: `'git-graph-reports'`
118
+
90
119
  The action generates a **simplified Mermaid summary** (for quick review) and a **full interactive HTML graph** (for deep-dive auditing), uploading them as workflow artifacts.
91
- ```
92
120
 
93
121
  ## Highlighting Options
94
122
 
@@ -5,9 +5,13 @@ A powerful Python tool to convert Git commit history into beautiful, interactive
5
5
  ## Git Plugin Support
6
6
  When installed in your PATH, you can use this as a native Git plugin:
7
7
  ```bash
8
- git graphable .
8
+ git graphable analyze .
9
9
  ```
10
10
 
11
+ ## 🚀 Live Interactive Demo
12
+ Check out the tool in action with our **[Live Interactive Demos](https://thetruescu.github.io/git-graphable/)**. Explore different hygiene scenarios and toggle overlays in real-time.
13
+
14
+
11
15
  ## Features
12
16
 
13
17
  - **Multi-Engine Support**: Export to Mermaid (.mmd), D2 (.d2), Graphviz (.dot), or HTML (.html).
@@ -50,6 +54,7 @@ uv run git-graphable init
50
54
 
51
55
  # Simplify the graph (only show branches/tags)
52
56
  uv run git-graphable analyze . --simplify
57
+ ```
53
58
 
54
59
  ## GitHub Action
55
60
 
@@ -65,14 +70,37 @@ jobs:
65
70
  fetch-depth: 0 # Required to see full history
66
71
 
67
72
  - name: Generate Git Graph Reports
68
- uses: TheTrueSCU/git-graphable/.github/actions/git-graphable@v0.4.0
73
+ uses: TheTrueSCU/git-graphable@v0.5.0
69
74
  with:
70
75
  production_branch: 'main'
71
76
  output_dir: 'reports'
72
77
  ```
73
78
 
79
+ ### Inputs
80
+
81
+ The following inputs are available for the `git-graphable` action:
82
+
83
+ * **`path`**
84
+ * Description: Path to the git repository
85
+ * Required: `false`
86
+ * Default: `'.'`
87
+ * **`production_branch`**
88
+ * Description: The main production branch (e.g. main, master)
89
+ * Required: `false`
90
+ * Default: `'main'`
91
+ * **`issue_engine`**
92
+ * Description: Issue tracker engine (github or jira)
93
+ * Required: `false`
94
+ * **`github_token`**
95
+ * Description: GitHub token for issue integration
96
+ * Required: `false`
97
+ * Default: `${{ github.token }}`
98
+ * **`output_dir`**
99
+ * Description: Directory to save the generated reports
100
+ * Required: `false`
101
+ * Default: `'git-graph-reports'`
102
+
74
103
  The action generates a **simplified Mermaid summary** (for quick review) and a **full interactive HTML graph** (for deep-dive auditing), uploading them as workflow artifacts.
75
- ```
76
104
 
77
105
  ## Highlighting Options
78
106
 
@@ -1,9 +1,9 @@
1
1
  name: 'Git Graphable'
2
- description: 'Generate interactive Git hygiene graphs and reports.'
2
+ description: 'Analyze and visualize Git history hygiene with interactive graphs and reports.'
3
3
  author: 'TheTrueSCU'
4
4
  branding:
5
- icon: 'activity'
6
- color: 'blue'
5
+ icon: 'bar-chart'
6
+ color: 'green'
7
7
 
8
8
  inputs:
9
9
  path:
@@ -38,7 +38,8 @@ runs:
38
38
  shell: bash
39
39
  run: |
40
40
  # Prefer installing local source if we are in the git-graphable repo
41
- ROOT_DIR="${{ github.action_path }}/../../.."
41
+ # github.action_path points to the root in this case
42
+ ROOT_DIR="${{ github.action_path }}"
42
43
  if [ -f "$ROOT_DIR/pyproject.toml" ] && grep -q "name = \"git-graphable\"" "$ROOT_DIR/pyproject.toml"; then
43
44
  echo "Installing git-graphable from local source: $ROOT_DIR"
44
45
  uv tool install "$ROOT_DIR"
@@ -0,0 +1,24 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Git Graphable Demos</title>
5
+ <style>
6
+ body { font-family: sans-serif; line-height: 1.6; max-width: 800px; margin: 40px auto; padding: 0 20px; }
7
+ h1 { border-bottom: 2px solid #eee; padding-bottom: 10px; }
8
+ ul { list-style: none; padding: 0; }
9
+ li { margin-bottom: 10px; background: #f9f9f9; padding: 15px; border-radius: 8px; border: 1px solid #ddd; }
10
+ a { text-decoration: none; color: #007bff; font-weight: bold; font-size: 1.2em; }
11
+ a:hover { text-decoration: underline; }
12
+ .badge { display: inline-block; padding: 2px 8px; background: #eee; border-radius: 4px; font-size: 0.8em; margin-left: 10px; color: #666; }
13
+ </style>
14
+ </head>
15
+ <body>
16
+ <h1>Git Graphable Interactive Demos</h1>
17
+ <p>Select a scenario below to explore the interactive Git hygiene visualization.</p>
18
+ <ul>
19
+ $html_links
20
+ </ul>
21
+ <hr>
22
+ <p><a href="https://github.com/TheTrueSCU/git-graphable" style="font-size: 1em;">Back to GitHub Repository</a></p>
23
+ </body>
24
+ </html>
@@ -0,0 +1,110 @@
1
+ import shutil
2
+ import subprocess
3
+ from pathlib import Path
4
+ from string import Template
5
+
6
+ EXAMPLES_DIR = Path(__file__).parent
7
+ REPOS_DIR = EXAMPLES_DIR / "repos"
8
+ OUTPUT_DIR = Path("demo-site")
9
+
10
+
11
+ def run_command(cmd):
12
+ print(f"Running: {' '.join(cmd)}")
13
+ subprocess.run(cmd, check=True)
14
+
15
+
16
+ def main():
17
+ # 1. Generate the repo data
18
+ import generate_demos
19
+
20
+ generate_demos.main()
21
+
22
+ # 2. Prepare output directory
23
+ if OUTPUT_DIR.exists():
24
+ shutil.rmtree(OUTPUT_DIR)
25
+ OUTPUT_DIR.mkdir()
26
+
27
+ # 3. Analyze each repo and generate HTML
28
+ repos = [d for d in REPOS_DIR.iterdir() if d.is_dir()]
29
+
30
+ html_links = []
31
+
32
+ for repo in sorted(repos):
33
+ repo_name = repo.name
34
+ output_file = f"{repo_name}.html"
35
+ print(f"Analyzing {repo_name}...")
36
+
37
+ # Build command with relevant flags for the specific demo
38
+ cmd = [
39
+ "uv",
40
+ "run",
41
+ "git-graphable",
42
+ "analyze",
43
+ "--bare",
44
+ str(repo),
45
+ "--engine",
46
+ "html",
47
+ "-o",
48
+ str(OUTPUT_DIR / output_file),
49
+ ]
50
+
51
+ # Add flags based on the demo type
52
+ if repo_name == "repo-pristine":
53
+ cmd.extend(["--highlight-authors", "--highlight-critical"])
54
+ elif repo_name == "repo-messy":
55
+ cmd.extend(["--highlight-direct-pushes", "--highlight-stale"])
56
+ elif repo_name == "repo-risk-silo":
57
+ cmd.extend(["--highlight-silos", "--silo-threshold", "20"])
58
+ elif repo_name == "repo-complex-hygiene":
59
+ cmd.extend(["--highlight-back-merges", "--highlight-squashed"])
60
+ elif repo_name == "repo-features":
61
+ cmd.extend(["--highlight-orphans", "--highlight-diverging-from", "main"])
62
+ elif repo_name == "repo-issue-desync":
63
+ cmd.extend(
64
+ [
65
+ "--highlight-issue-inconsistencies",
66
+ "--issue-engine",
67
+ "script",
68
+ "--issue-script",
69
+ "echo CLOSED",
70
+ ]
71
+ )
72
+ elif repo_name == "repo-release-desync":
73
+ cmd.extend(
74
+ [
75
+ "--highlight-release-inconsistencies",
76
+ "--issue-engine",
77
+ "script",
78
+ "--issue-script",
79
+ "echo CLOSED",
80
+ ]
81
+ )
82
+ elif repo_name == "repo-collab-gap":
83
+ cmd.extend(
84
+ [
85
+ "--highlight-collaboration-gaps",
86
+ "--issue-engine",
87
+ "script",
88
+ "--issue-script",
89
+ "echo OPEN,Bob",
90
+ ]
91
+ )
92
+
93
+ run_command(cmd)
94
+ label = repo_name.replace("repo-", "").replace("-", " ").title()
95
+ html_links.append(f'<li><a href="{output_file}">{label}</a></li>')
96
+
97
+ # 4. Create index.html from template
98
+ template_path = EXAMPLES_DIR / "index_template.html"
99
+ if template_path.exists():
100
+ template_str = template_path.read_text()
101
+ t = Template(template_str)
102
+ index_content = t.substitute(html_links="\n ".join(html_links))
103
+ (OUTPUT_DIR / "index.html").write_text(index_content)
104
+ print(f"\nDone! Demo site generated in {OUTPUT_DIR}/")
105
+ else:
106
+ print(f"Error: Template not found at {template_path}")
107
+
108
+
109
+ if __name__ == "__main__":
110
+ main()
@@ -28,7 +28,7 @@ keywords = [
28
28
  name = "git-graphable"
29
29
  readme = "README.md"
30
30
  requires-python = ">=3.13"
31
- version = "0.4.0"
31
+ version = "0.5.0"
32
32
 
33
33
  [project.optional-dependencies]
34
34
  cli = [
@@ -31,14 +31,28 @@ def handle_output(
31
31
  ) -> Optional[str]:
32
32
  """Handles exporting and optionally opening the graph. Returns content if output is '-'."""
33
33
  if output == "-":
34
- # Capture to string
35
- import io
36
- from contextlib import redirect_stdout
37
-
38
- f = io.StringIO()
39
- with redirect_stdout(f):
40
- export_graph(graph, "-", config, engine, as_image=False)
41
- return f.getvalue()
34
+ # If output is '-', create a temporary file, export to it,
35
+ # read its content, and return the content.
36
+ # This assumes export_graph expects a file path and handles the content.
37
+ # The 'as_image' flag from the handle_output call is now respected.
38
+ ext = get_extension(engine, as_image=as_image)
39
+ with tempfile.NamedTemporaryFile(suffix=ext, delete=False) as tf:
40
+ temp_path = tf.name
41
+
42
+ # Export to the temporary file, respecting the as_image flag
43
+ export_graph(graph, temp_path, config, engine, as_image=as_image)
44
+
45
+ # Read the content of the temporary file
46
+ mode = "rb" if as_image else "r"
47
+ encoding = None if as_image else "utf-8"
48
+
49
+ with open(temp_path, mode, encoding=encoding) as f:
50
+ content = f.read()
51
+
52
+ # Clean up the temporary file
53
+ os.remove(temp_path)
54
+
55
+ return content
42
56
 
43
57
  if output:
44
58
  # If output path is provided, we use the specified as_image flag or infer from extension
@@ -331,8 +331,12 @@ def _apply_squash_highlights(
331
331
  if is_tip:
332
332
  tips.append(c)
333
333
  for tip in tips:
334
- squash_commit.set_edge_attribute(
335
- tip, Tag.EDGE_LOGICAL_MERGE.value, True
334
+ # For squash merges, the 'logical' edge doesn't exist in Git,
335
+ # so we must create it in the graph.
336
+ # In this graph's convention, child nodes add_dependency(parent).
337
+ # The squash commit is the 'new' parent of the old tip.
338
+ tip.add_dependency(
339
+ squash_commit, **{Tag.EDGE_LOGICAL_MERGE.value: True}
336
340
  )
337
341
  tip.add_tag(Tag.SQUASHED.value)
338
342
 
@@ -275,7 +275,7 @@ wheels = [
275
275
 
276
276
  [[package]]
277
277
  name = "git-graphable"
278
- version = "0.4.0"
278
+ version = "0.5.0"
279
279
  source = { editable = "." }
280
280
  dependencies = [
281
281
  { name = "graphable" },
File without changes
File without changes
File without changes
File without changes
File without changes