gitflow-analytics 1.0.0__py3-none-any.whl → 1.0.3__py3-none-any.whl
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.
- gitflow_analytics/__init__.py +11 -9
- gitflow_analytics/_version.py +2 -2
- gitflow_analytics/cli.py +691 -243
- gitflow_analytics/cli_rich.py +353 -0
- gitflow_analytics/config.py +389 -96
- gitflow_analytics/core/analyzer.py +175 -78
- gitflow_analytics/core/branch_mapper.py +132 -132
- gitflow_analytics/core/cache.py +242 -173
- gitflow_analytics/core/identity.py +214 -178
- gitflow_analytics/extractors/base.py +13 -11
- gitflow_analytics/extractors/story_points.py +70 -59
- gitflow_analytics/extractors/tickets.py +111 -88
- gitflow_analytics/integrations/github_integration.py +91 -77
- gitflow_analytics/integrations/jira_integration.py +284 -0
- gitflow_analytics/integrations/orchestrator.py +99 -72
- gitflow_analytics/metrics/dora.py +183 -179
- gitflow_analytics/models/database.py +191 -54
- gitflow_analytics/qualitative/__init__.py +30 -0
- gitflow_analytics/qualitative/classifiers/__init__.py +13 -0
- gitflow_analytics/qualitative/classifiers/change_type.py +468 -0
- gitflow_analytics/qualitative/classifiers/domain_classifier.py +399 -0
- gitflow_analytics/qualitative/classifiers/intent_analyzer.py +436 -0
- gitflow_analytics/qualitative/classifiers/risk_analyzer.py +412 -0
- gitflow_analytics/qualitative/core/__init__.py +13 -0
- gitflow_analytics/qualitative/core/llm_fallback.py +653 -0
- gitflow_analytics/qualitative/core/nlp_engine.py +373 -0
- gitflow_analytics/qualitative/core/pattern_cache.py +457 -0
- gitflow_analytics/qualitative/core/processor.py +540 -0
- gitflow_analytics/qualitative/models/__init__.py +25 -0
- gitflow_analytics/qualitative/models/schemas.py +272 -0
- gitflow_analytics/qualitative/utils/__init__.py +13 -0
- gitflow_analytics/qualitative/utils/batch_processor.py +326 -0
- gitflow_analytics/qualitative/utils/cost_tracker.py +343 -0
- gitflow_analytics/qualitative/utils/metrics.py +347 -0
- gitflow_analytics/qualitative/utils/text_processing.py +243 -0
- gitflow_analytics/reports/analytics_writer.py +25 -8
- gitflow_analytics/reports/csv_writer.py +60 -32
- gitflow_analytics/reports/narrative_writer.py +21 -15
- gitflow_analytics/tui/__init__.py +5 -0
- gitflow_analytics/tui/app.py +721 -0
- gitflow_analytics/tui/screens/__init__.py +8 -0
- gitflow_analytics/tui/screens/analysis_progress_screen.py +487 -0
- gitflow_analytics/tui/screens/configuration_screen.py +547 -0
- gitflow_analytics/tui/screens/loading_screen.py +358 -0
- gitflow_analytics/tui/screens/main_screen.py +304 -0
- gitflow_analytics/tui/screens/results_screen.py +698 -0
- gitflow_analytics/tui/widgets/__init__.py +7 -0
- gitflow_analytics/tui/widgets/data_table.py +257 -0
- gitflow_analytics/tui/widgets/export_modal.py +301 -0
- gitflow_analytics/tui/widgets/progress_widget.py +192 -0
- gitflow_analytics-1.0.3.dist-info/METADATA +490 -0
- gitflow_analytics-1.0.3.dist-info/RECORD +62 -0
- gitflow_analytics-1.0.0.dist-info/METADATA +0 -201
- gitflow_analytics-1.0.0.dist-info/RECORD +0 -30
- {gitflow_analytics-1.0.0.dist-info → gitflow_analytics-1.0.3.dist-info}/WHEEL +0 -0
- {gitflow_analytics-1.0.0.dist-info → gitflow_analytics-1.0.3.dist-info}/entry_points.txt +0 -0
- {gitflow_analytics-1.0.0.dist-info → gitflow_analytics-1.0.3.dist-info}/licenses/LICENSE +0 -0
- {gitflow_analytics-1.0.0.dist-info → gitflow_analytics-1.0.3.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
"""Progress widget with ETA for GitFlow Analytics TUI."""
|
|
2
|
+
|
|
3
|
+
import time
|
|
4
|
+
from typing import Optional
|
|
5
|
+
from datetime import datetime, timedelta
|
|
6
|
+
|
|
7
|
+
from textual.widgets import ProgressBar, Label
|
|
8
|
+
from textual.containers import Container, Vertical
|
|
9
|
+
from textual.reactive import reactive
|
|
10
|
+
from rich.text import Text
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class AnalysisProgressWidget(Container):
|
|
14
|
+
"""
|
|
15
|
+
Custom progress widget that shows progress with ETA calculation.
|
|
16
|
+
|
|
17
|
+
WHY: Standard progress bars don't provide time estimates which are crucial
|
|
18
|
+
for long-running analysis operations. This widget combines progress tracking
|
|
19
|
+
with ETA calculations to give users better feedback.
|
|
20
|
+
|
|
21
|
+
DESIGN DECISION: Uses reactive attributes for real-time updates and
|
|
22
|
+
calculates ETA based on average processing speed rather than simple linear
|
|
23
|
+
extrapolation for more accurate estimates.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
DEFAULT_CSS = """
|
|
27
|
+
AnalysisProgressWidget {
|
|
28
|
+
height: auto;
|
|
29
|
+
border: solid $primary;
|
|
30
|
+
margin: 1;
|
|
31
|
+
padding: 1;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.progress-title {
|
|
35
|
+
text-style: bold;
|
|
36
|
+
color: $primary;
|
|
37
|
+
margin-bottom: 1;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.progress-status {
|
|
41
|
+
color: $text;
|
|
42
|
+
margin-top: 1;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.progress-eta {
|
|
46
|
+
color: $accent;
|
|
47
|
+
text-style: italic;
|
|
48
|
+
}
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
progress = reactive(0.0)
|
|
52
|
+
total = reactive(100.0)
|
|
53
|
+
status_text = reactive("Initializing...")
|
|
54
|
+
|
|
55
|
+
def __init__(
|
|
56
|
+
self,
|
|
57
|
+
title: str,
|
|
58
|
+
total: float = 100.0,
|
|
59
|
+
*,
|
|
60
|
+
name: Optional[str] = None,
|
|
61
|
+
id: Optional[str] = None,
|
|
62
|
+
classes: Optional[str] = None
|
|
63
|
+
) -> None:
|
|
64
|
+
super().__init__(name=name, id=id, classes=classes)
|
|
65
|
+
self.title = title
|
|
66
|
+
self.total = total
|
|
67
|
+
self.start_time = time.time()
|
|
68
|
+
self.last_update_time = time.time()
|
|
69
|
+
self.progress_history = []
|
|
70
|
+
|
|
71
|
+
def compose(self):
|
|
72
|
+
"""Compose the progress widget."""
|
|
73
|
+
yield Label(self.title, classes="progress-title")
|
|
74
|
+
yield ProgressBar(total=self.total, id="progress-bar")
|
|
75
|
+
yield Label(self.status_text, classes="progress-status", id="status-label")
|
|
76
|
+
yield Label("", classes="progress-eta", id="eta-label")
|
|
77
|
+
|
|
78
|
+
def update_progress(self, value: float, status: str = "") -> None:
|
|
79
|
+
"""
|
|
80
|
+
Update progress and status with ETA calculation.
|
|
81
|
+
|
|
82
|
+
WHY: Provides comprehensive progress updates including time estimates
|
|
83
|
+
which are essential for user experience during long operations.
|
|
84
|
+
|
|
85
|
+
@param value: Current progress value
|
|
86
|
+
@param status: Status message to display
|
|
87
|
+
"""
|
|
88
|
+
current_time = time.time()
|
|
89
|
+
|
|
90
|
+
# Update reactive values
|
|
91
|
+
self.progress = value
|
|
92
|
+
if status:
|
|
93
|
+
self.status_text = status
|
|
94
|
+
|
|
95
|
+
# Update progress bar
|
|
96
|
+
progress_bar = self.query_one("#progress-bar", ProgressBar)
|
|
97
|
+
progress_bar.update(progress=value)
|
|
98
|
+
|
|
99
|
+
# Update status label
|
|
100
|
+
status_label = self.query_one("#status-label", Label)
|
|
101
|
+
status_label.update(status)
|
|
102
|
+
|
|
103
|
+
# Calculate and update ETA
|
|
104
|
+
eta_text = self._calculate_eta(value, current_time)
|
|
105
|
+
eta_label = self.query_one("#eta-label", Label)
|
|
106
|
+
eta_label.update(eta_text)
|
|
107
|
+
|
|
108
|
+
# Store progress history for better ETA calculation
|
|
109
|
+
self.progress_history.append({
|
|
110
|
+
'time': current_time,
|
|
111
|
+
'progress': value
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
# Keep only recent history (last 10 updates)
|
|
115
|
+
if len(self.progress_history) > 10:
|
|
116
|
+
self.progress_history = self.progress_history[-10:]
|
|
117
|
+
|
|
118
|
+
def _calculate_eta(self, current_progress: float, current_time: float) -> str:
|
|
119
|
+
"""
|
|
120
|
+
Calculate estimated time of arrival based on progress history.
|
|
121
|
+
|
|
122
|
+
WHY: Uses historical data points to calculate a more accurate ETA
|
|
123
|
+
than simple linear extrapolation, accounting for variations in
|
|
124
|
+
processing speed.
|
|
125
|
+
"""
|
|
126
|
+
if current_progress <= 0 or current_progress >= self.total:
|
|
127
|
+
return ""
|
|
128
|
+
|
|
129
|
+
# Need at least 2 data points for calculation
|
|
130
|
+
if len(self.progress_history) < 2:
|
|
131
|
+
return "Calculating ETA..."
|
|
132
|
+
|
|
133
|
+
# Calculate average rate from recent history
|
|
134
|
+
recent_history = self.progress_history[-5:] # Last 5 updates
|
|
135
|
+
if len(recent_history) < 2:
|
|
136
|
+
return "Calculating ETA..."
|
|
137
|
+
|
|
138
|
+
time_span = recent_history[-1]['time'] - recent_history[0]['time']
|
|
139
|
+
progress_span = recent_history[-1]['progress'] - recent_history[0]['progress']
|
|
140
|
+
|
|
141
|
+
if time_span <= 0 or progress_span <= 0:
|
|
142
|
+
return "Calculating ETA..."
|
|
143
|
+
|
|
144
|
+
# Calculate rate (progress per second)
|
|
145
|
+
rate = progress_span / time_span
|
|
146
|
+
|
|
147
|
+
# Calculate remaining work and time
|
|
148
|
+
remaining_progress = self.total - current_progress
|
|
149
|
+
estimated_seconds = remaining_progress / rate
|
|
150
|
+
|
|
151
|
+
# Format ETA
|
|
152
|
+
if estimated_seconds < 60:
|
|
153
|
+
return f"ETA: {estimated_seconds:.0f}s"
|
|
154
|
+
elif estimated_seconds < 3600:
|
|
155
|
+
minutes = estimated_seconds / 60
|
|
156
|
+
return f"ETA: {minutes:.1f}m"
|
|
157
|
+
else:
|
|
158
|
+
hours = estimated_seconds / 3600
|
|
159
|
+
return f"ETA: {hours:.1f}h"
|
|
160
|
+
|
|
161
|
+
def reset(self) -> None:
|
|
162
|
+
"""Reset the progress widget to initial state."""
|
|
163
|
+
self.progress = 0.0
|
|
164
|
+
self.status_text = "Initializing..."
|
|
165
|
+
self.start_time = time.time()
|
|
166
|
+
self.progress_history = []
|
|
167
|
+
|
|
168
|
+
# Reset UI elements
|
|
169
|
+
progress_bar = self.query_one("#progress-bar", ProgressBar)
|
|
170
|
+
progress_bar.update(progress=0)
|
|
171
|
+
|
|
172
|
+
status_label = self.query_one("#status-label", Label)
|
|
173
|
+
status_label.update("Initializing...")
|
|
174
|
+
|
|
175
|
+
eta_label = self.query_one("#eta-label", Label)
|
|
176
|
+
eta_label.update("")
|
|
177
|
+
|
|
178
|
+
def complete(self, final_message: str = "Complete!") -> None:
|
|
179
|
+
"""Mark progress as complete."""
|
|
180
|
+
self.update_progress(self.total, final_message)
|
|
181
|
+
|
|
182
|
+
# Calculate total elapsed time
|
|
183
|
+
total_time = time.time() - self.start_time
|
|
184
|
+
if total_time < 60:
|
|
185
|
+
time_str = f"{total_time:.1f}s"
|
|
186
|
+
elif total_time < 3600:
|
|
187
|
+
time_str = f"{total_time/60:.1f}m"
|
|
188
|
+
else:
|
|
189
|
+
time_str = f"{total_time/3600:.1f}h"
|
|
190
|
+
|
|
191
|
+
eta_label = self.query_one("#eta-label", Label)
|
|
192
|
+
eta_label.update(f"Completed in {time_str}")
|
|
@@ -0,0 +1,490 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: gitflow-analytics
|
|
3
|
+
Version: 1.0.3
|
|
4
|
+
Summary: Analyze Git repositories for developer productivity insights
|
|
5
|
+
Author-email: Bob Matyas <bobmatnyc@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/bobmatnyc/gitflow-analytics
|
|
8
|
+
Project-URL: Documentation, https://github.com/bobmatnyc/gitflow-analytics/blob/main/README.md
|
|
9
|
+
Project-URL: Repository, https://github.com/bobmatnyc/gitflow-analytics
|
|
10
|
+
Project-URL: Issues, https://github.com/bobmatnyc/gitflow-analytics/issues
|
|
11
|
+
Keywords: git,analytics,productivity,metrics,development
|
|
12
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Software Development :: Version Control :: Git
|
|
20
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
21
|
+
Requires-Python: >=3.9
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Requires-Dist: click>=8.1
|
|
25
|
+
Requires-Dist: gitpython>=3.1
|
|
26
|
+
Requires-Dist: tqdm>=4.65
|
|
27
|
+
Requires-Dist: sqlalchemy>=2.0
|
|
28
|
+
Requires-Dist: pandas>=2.0
|
|
29
|
+
Requires-Dist: pyyaml>=6.0
|
|
30
|
+
Requires-Dist: python-dateutil>=2.8
|
|
31
|
+
Requires-Dist: python-dotenv>=1.0
|
|
32
|
+
Requires-Dist: rich>=13.0.0
|
|
33
|
+
Requires-Dist: spacy>=3.7.0
|
|
34
|
+
Requires-Dist: scikit-learn>=1.3.0
|
|
35
|
+
Requires-Dist: openai>=1.30.0
|
|
36
|
+
Requires-Dist: tiktoken>=0.7.0
|
|
37
|
+
Requires-Dist: numpy>=1.24.0
|
|
38
|
+
Provides-Extra: dev
|
|
39
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
40
|
+
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
41
|
+
Requires-Dist: pytest-mock>=3.0; extra == "dev"
|
|
42
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
43
|
+
Requires-Dist: mypy>=1.0; extra == "dev"
|
|
44
|
+
Requires-Dist: black>=23.0; extra == "dev"
|
|
45
|
+
Requires-Dist: isort>=5.0; extra == "dev"
|
|
46
|
+
Requires-Dist: bandit[toml]>=1.7; extra == "dev"
|
|
47
|
+
Requires-Dist: safety>=2.0; extra == "dev"
|
|
48
|
+
Requires-Dist: python-semantic-release>=8.0.0; extra == "dev"
|
|
49
|
+
Requires-Dist: types-PyYAML>=6.0; extra == "dev"
|
|
50
|
+
Requires-Dist: types-requests>=2.28; extra == "dev"
|
|
51
|
+
Provides-Extra: github
|
|
52
|
+
Requires-Dist: pygithub>=1.58; extra == "github"
|
|
53
|
+
Provides-Extra: tui
|
|
54
|
+
Requires-Dist: textual>=0.41.0; extra == "tui"
|
|
55
|
+
Provides-Extra: all
|
|
56
|
+
Requires-Dist: gitflow-analytics[github,tui]; extra == "all"
|
|
57
|
+
Dynamic: license-file
|
|
58
|
+
|
|
59
|
+
# GitFlow Analytics
|
|
60
|
+
|
|
61
|
+
[](https://badge.fury.io/py/gitflow-analytics)
|
|
62
|
+
[](https://pypi.org/project/gitflow-analytics/)
|
|
63
|
+
[](https://opensource.org/licenses/MIT)
|
|
64
|
+
|
|
65
|
+
A Python package for analyzing Git repositories to generate comprehensive developer productivity reports. It extracts data directly from Git history and GitHub APIs, providing weekly summaries, productivity insights, and gap analysis.
|
|
66
|
+
|
|
67
|
+
## Features
|
|
68
|
+
|
|
69
|
+
- 🚀 **Multi-repository analysis** with project grouping
|
|
70
|
+
- 🏢 **Organization-based repository discovery** from GitHub
|
|
71
|
+
- 👥 **Developer identity resolution** and normalization
|
|
72
|
+
- 📊 **Work volume analysis** (absolute vs relative effort)
|
|
73
|
+
- 🎯 **Story point extraction** from commit messages and PR descriptions
|
|
74
|
+
- 🎫 **Multi-platform ticket tracking** (JIRA, GitHub Issues, ClickUp, Linear)
|
|
75
|
+
- 📈 **Weekly CSV reports** with productivity metrics
|
|
76
|
+
- 🔒 **Data anonymization** for external sharing
|
|
77
|
+
- ⚡ **Smart caching** for fast repeated analyses
|
|
78
|
+
- 🔄 **Batch processing** for large repositories
|
|
79
|
+
|
|
80
|
+
## Quick Start
|
|
81
|
+
|
|
82
|
+
### Installation
|
|
83
|
+
|
|
84
|
+
**From PyPI (Recommended):**
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
pip install gitflow-analytics
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**From Source (Development):**
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
git clone https://github.com/bobmatnyc/gitflow-analytics.git
|
|
94
|
+
cd gitflow-analytics
|
|
95
|
+
pip install -e ".[dev]"
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Basic Usage
|
|
99
|
+
|
|
100
|
+
1. Create a configuration file (`config.yaml`):
|
|
101
|
+
|
|
102
|
+
**Option A: Organization-based (Automatic Repository Discovery)**
|
|
103
|
+
```yaml
|
|
104
|
+
version: "1.0"
|
|
105
|
+
|
|
106
|
+
github:
|
|
107
|
+
token: "${GITHUB_TOKEN}"
|
|
108
|
+
organization: "myorg" # Automatically discovers all repositories
|
|
109
|
+
|
|
110
|
+
analysis:
|
|
111
|
+
story_point_patterns:
|
|
112
|
+
- "(?:story\\s*points?|sp|pts?)\\s*[:=]\\s*(\\d+)"
|
|
113
|
+
- "\\[(\\d+)\\s*(?:sp|pts?)\\]"
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
**Option B: Repository-based (Manual Configuration)**
|
|
117
|
+
```yaml
|
|
118
|
+
version: "1.0"
|
|
119
|
+
|
|
120
|
+
github:
|
|
121
|
+
token: "${GITHUB_TOKEN}"
|
|
122
|
+
owner: "${GITHUB_OWNER}"
|
|
123
|
+
|
|
124
|
+
repositories:
|
|
125
|
+
- name: "frontend"
|
|
126
|
+
path: "~/repos/frontend"
|
|
127
|
+
github_repo: "myorg/frontend"
|
|
128
|
+
project_key: "FRONTEND"
|
|
129
|
+
|
|
130
|
+
- name: "backend"
|
|
131
|
+
path: "~/repos/backend"
|
|
132
|
+
github_repo: "myorg/backend"
|
|
133
|
+
project_key: "BACKEND"
|
|
134
|
+
|
|
135
|
+
analysis:
|
|
136
|
+
story_point_patterns:
|
|
137
|
+
- "(?:story\\s*points?|sp|pts?)\\s*[:=]\\s*(\\d+)"
|
|
138
|
+
- "\\[(\\d+)\\s*(?:sp|pts?)\\]"
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
2. Create a `.env` file in the same directory as your `config.yaml`:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
# .env
|
|
145
|
+
GITHUB_TOKEN=ghp_your_github_token_here
|
|
146
|
+
GITHUB_OWNER=your_github_org # Only for repository-based setup
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
3. Run the analysis:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
gitflow-analytics analyze -c config.yaml
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Configuration Options
|
|
156
|
+
|
|
157
|
+
### Environment Variables and Credentials
|
|
158
|
+
|
|
159
|
+
GitFlow Analytics automatically loads environment variables from a `.env` file in the same directory as your configuration YAML. This is the recommended approach for managing credentials securely.
|
|
160
|
+
|
|
161
|
+
#### Step 1: Create a `.env` file
|
|
162
|
+
|
|
163
|
+
Create a `.env` file next to your configuration YAML:
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
# .env file (same directory as your config.yaml)
|
|
167
|
+
# GitHub credentials (required)
|
|
168
|
+
GITHUB_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxx
|
|
169
|
+
GITHUB_OWNER=myorg # Optional: default owner for repositories
|
|
170
|
+
|
|
171
|
+
# JIRA credentials (optional - only if using JIRA integration)
|
|
172
|
+
JIRA_ACCESS_USER=your.email@company.com
|
|
173
|
+
JIRA_ACCESS_TOKEN=xxxxxxxxxxxxxxxxxxxx
|
|
174
|
+
|
|
175
|
+
# Other optional tokens
|
|
176
|
+
CLICKUP_TOKEN=pk_xxxxxxxxxxxx
|
|
177
|
+
LINEAR_TOKEN=lin_api_xxxxxxxxxxxx
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
#### Step 2: Reference in YAML configuration
|
|
181
|
+
|
|
182
|
+
Use `${VARIABLE_NAME}` syntax in your YAML to reference environment variables:
|
|
183
|
+
|
|
184
|
+
```yaml
|
|
185
|
+
# config.yaml
|
|
186
|
+
version: "1.0"
|
|
187
|
+
|
|
188
|
+
github:
|
|
189
|
+
token: "${GITHUB_TOKEN}" # Required
|
|
190
|
+
owner: "${GITHUB_OWNER}" # Optional
|
|
191
|
+
organization: "${GITHUB_ORG}" # Optional (for org-based discovery)
|
|
192
|
+
|
|
193
|
+
# Optional: JIRA integration
|
|
194
|
+
jira:
|
|
195
|
+
access_user: "${JIRA_ACCESS_USER}"
|
|
196
|
+
access_token: "${JIRA_ACCESS_TOKEN}"
|
|
197
|
+
base_url: "https://yourcompany.atlassian.net"
|
|
198
|
+
|
|
199
|
+
# Optional: Configure which JIRA fields contain story points
|
|
200
|
+
jira_integration:
|
|
201
|
+
story_point_fields:
|
|
202
|
+
- "Story Points"
|
|
203
|
+
- "customfield_10016" # Your custom field ID
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
#### Important Notes:
|
|
207
|
+
|
|
208
|
+
- **Never commit `.env` files** to version control (add to `.gitignore`)
|
|
209
|
+
- If credentials are not found in the `.env` file, the tool will exit with an informative error
|
|
210
|
+
- The `.env` file must be in the same directory as your YAML configuration
|
|
211
|
+
- All configured services must have corresponding environment variables set
|
|
212
|
+
|
|
213
|
+
### Organization vs Repository-based Setup
|
|
214
|
+
|
|
215
|
+
GitFlow Analytics supports two main configuration approaches:
|
|
216
|
+
|
|
217
|
+
#### Organization-based Configuration (Recommended)
|
|
218
|
+
|
|
219
|
+
Automatically discovers all non-archived repositories from a GitHub organization:
|
|
220
|
+
|
|
221
|
+
```yaml
|
|
222
|
+
version: "1.0"
|
|
223
|
+
|
|
224
|
+
github:
|
|
225
|
+
token: "${GITHUB_TOKEN}"
|
|
226
|
+
organization: "myorg" # Your GitHub organization name
|
|
227
|
+
|
|
228
|
+
# Optional: Customize analysis settings
|
|
229
|
+
analysis:
|
|
230
|
+
story_point_patterns:
|
|
231
|
+
- "(?:story\\s*points?|sp|pts?)\\s*[:=]\\s*(\\d+)"
|
|
232
|
+
|
|
233
|
+
exclude:
|
|
234
|
+
authors:
|
|
235
|
+
- "dependabot[bot]"
|
|
236
|
+
- "github-actions[bot]"
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
**Benefits:**
|
|
240
|
+
- Automatically discovers new repositories as they're added to the organization
|
|
241
|
+
- No need to manually configure each repository
|
|
242
|
+
- Simplified configuration management
|
|
243
|
+
- Perfect for teams with many repositories
|
|
244
|
+
|
|
245
|
+
**Requirements:**
|
|
246
|
+
- Your GitHub token must have organization read access
|
|
247
|
+
- Repositories will be automatically cloned to local directories if they don't exist
|
|
248
|
+
|
|
249
|
+
#### Repository-based Configuration
|
|
250
|
+
|
|
251
|
+
Manually specify each repository to analyze:
|
|
252
|
+
|
|
253
|
+
```yaml
|
|
254
|
+
version: "1.0"
|
|
255
|
+
|
|
256
|
+
github:
|
|
257
|
+
token: "${GITHUB_TOKEN}"
|
|
258
|
+
owner: "${GITHUB_OWNER}" # Default owner for repositories
|
|
259
|
+
|
|
260
|
+
repositories:
|
|
261
|
+
- name: "frontend"
|
|
262
|
+
path: "~/repos/frontend"
|
|
263
|
+
github_repo: "myorg/frontend"
|
|
264
|
+
project_key: "FRONTEND"
|
|
265
|
+
|
|
266
|
+
- name: "backend"
|
|
267
|
+
path: "~/repos/backend"
|
|
268
|
+
github_repo: "myorg/backend"
|
|
269
|
+
project_key: "BACKEND"
|
|
270
|
+
|
|
271
|
+
analysis:
|
|
272
|
+
story_point_patterns:
|
|
273
|
+
- "(?:story\\s*points?|sp|pts?)\\s*[:=]\\s*(\\d+)"
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
**Benefits:**
|
|
277
|
+
- Fine-grained control over which repositories to analyze
|
|
278
|
+
- Custom project keys and local paths
|
|
279
|
+
- Works with mixed-ownership repositories
|
|
280
|
+
- Compatible with existing configurations
|
|
281
|
+
|
|
282
|
+
### Directory Defaults
|
|
283
|
+
|
|
284
|
+
GitFlow Analytics now defaults cache and report directories to be relative to the configuration file location:
|
|
285
|
+
|
|
286
|
+
- **Reports**: Default to same directory as config file (unless overridden with `--output`)
|
|
287
|
+
- **Cache**: Default to `.gitflow-cache/` in config file directory
|
|
288
|
+
- **Backward compatibility**: Absolute paths in configuration continue to work as before
|
|
289
|
+
|
|
290
|
+
Example directory structure:
|
|
291
|
+
```
|
|
292
|
+
/project/
|
|
293
|
+
├── config.yaml # Configuration file
|
|
294
|
+
├── weekly_metrics.csv # Reports generated here by default
|
|
295
|
+
├── summary.csv
|
|
296
|
+
└── .gitflow-cache/ # Cache directory
|
|
297
|
+
├── gitflow_cache.db
|
|
298
|
+
└── identities.db
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
## Command Line Interface
|
|
302
|
+
|
|
303
|
+
### Main Commands
|
|
304
|
+
|
|
305
|
+
```bash
|
|
306
|
+
# Analyze repositories
|
|
307
|
+
gitflow-analytics analyze -c config.yaml --weeks 12 --output ./reports
|
|
308
|
+
|
|
309
|
+
# Show cache statistics
|
|
310
|
+
gitflow-analytics cache-stats -c config.yaml
|
|
311
|
+
|
|
312
|
+
# List known developers
|
|
313
|
+
gitflow-analytics list-developers -c config.yaml
|
|
314
|
+
|
|
315
|
+
# Merge developer identities
|
|
316
|
+
gitflow-analytics merge-identity -c config.yaml dev1_id dev2_id
|
|
317
|
+
|
|
318
|
+
# Discover JIRA story point fields
|
|
319
|
+
gitflow-analytics discover-jira-fields -c config.yaml
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
### Options
|
|
323
|
+
|
|
324
|
+
- `--weeks, -w`: Number of weeks to analyze (default: 12)
|
|
325
|
+
- `--output, -o`: Output directory for reports (default: ./reports)
|
|
326
|
+
- `--anonymize`: Anonymize developer information
|
|
327
|
+
- `--no-cache`: Disable caching for fresh analysis
|
|
328
|
+
- `--clear-cache`: Clear cache before analysis
|
|
329
|
+
- `--validate-only`: Validate configuration without running
|
|
330
|
+
|
|
331
|
+
## Complete Configuration Example
|
|
332
|
+
|
|
333
|
+
Here's a complete example showing `.env` file and corresponding YAML configuration:
|
|
334
|
+
|
|
335
|
+
### `.env` file
|
|
336
|
+
```bash
|
|
337
|
+
# GitHub Configuration
|
|
338
|
+
GITHUB_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxx
|
|
339
|
+
GITHUB_ORG=EWTN-Global
|
|
340
|
+
|
|
341
|
+
# JIRA Configuration
|
|
342
|
+
JIRA_ACCESS_USER=developer@ewtn.com
|
|
343
|
+
JIRA_ACCESS_TOKEN=ATATT3xxxxxxxxxxx
|
|
344
|
+
|
|
345
|
+
# Optional: Other integrations
|
|
346
|
+
# CLICKUP_TOKEN=pk_xxxxxxxxxxxx
|
|
347
|
+
# LINEAR_TOKEN=lin_api_xxxxxxxxxxxx
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
### `config.yaml` file
|
|
351
|
+
```yaml
|
|
352
|
+
version: "1.0"
|
|
353
|
+
|
|
354
|
+
# GitHub configuration with organization discovery
|
|
355
|
+
github:
|
|
356
|
+
token: "${GITHUB_TOKEN}"
|
|
357
|
+
organization: "${GITHUB_ORG}"
|
|
358
|
+
|
|
359
|
+
# JIRA integration for story points
|
|
360
|
+
jira:
|
|
361
|
+
access_user: "${JIRA_ACCESS_USER}"
|
|
362
|
+
access_token: "${JIRA_ACCESS_TOKEN}"
|
|
363
|
+
base_url: "https://ewtn.atlassian.net"
|
|
364
|
+
|
|
365
|
+
jira_integration:
|
|
366
|
+
enabled: true
|
|
367
|
+
fetch_story_points: true
|
|
368
|
+
story_point_fields:
|
|
369
|
+
- "Story point estimate" # Your field name
|
|
370
|
+
- "customfield_10016" # Fallback field ID
|
|
371
|
+
|
|
372
|
+
# Analysis configuration
|
|
373
|
+
analysis:
|
|
374
|
+
# Only track JIRA tickets (ignore GitHub issues, etc.)
|
|
375
|
+
ticket_platforms:
|
|
376
|
+
- jira
|
|
377
|
+
|
|
378
|
+
# Exclude bot commits and boilerplate files
|
|
379
|
+
exclude:
|
|
380
|
+
authors:
|
|
381
|
+
- "dependabot[bot]"
|
|
382
|
+
- "renovate[bot]"
|
|
383
|
+
paths:
|
|
384
|
+
- "**/node_modules/**"
|
|
385
|
+
- "**/*.min.js"
|
|
386
|
+
- "**/package-lock.json"
|
|
387
|
+
|
|
388
|
+
# Developer identity consolidation
|
|
389
|
+
identity:
|
|
390
|
+
similarity_threshold: 0.85
|
|
391
|
+
manual_mappings:
|
|
392
|
+
- primary_email: "john.doe@company.com"
|
|
393
|
+
aliases:
|
|
394
|
+
- "jdoe@oldcompany.com"
|
|
395
|
+
- "john@personal.com"
|
|
396
|
+
|
|
397
|
+
# Output configuration
|
|
398
|
+
output:
|
|
399
|
+
directory: "./reports"
|
|
400
|
+
formats:
|
|
401
|
+
- csv
|
|
402
|
+
- markdown
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
## Output Reports
|
|
406
|
+
|
|
407
|
+
The tool generates three CSV reports:
|
|
408
|
+
|
|
409
|
+
1. **Weekly Metrics** (`weekly_metrics_YYYYMMDD.csv`)
|
|
410
|
+
- Week-by-week developer productivity
|
|
411
|
+
- Story points, commits, lines changed
|
|
412
|
+
- Ticket coverage percentages
|
|
413
|
+
- Per-project breakdown
|
|
414
|
+
|
|
415
|
+
2. **Summary Statistics** (`summary_YYYYMMDD.csv`)
|
|
416
|
+
- Overall project statistics
|
|
417
|
+
- Platform-specific ticket counts
|
|
418
|
+
- Top contributors
|
|
419
|
+
|
|
420
|
+
3. **Developer Report** (`developers_YYYYMMDD.csv`)
|
|
421
|
+
- Complete developer profiles
|
|
422
|
+
- Total contributions
|
|
423
|
+
- Identity aliases
|
|
424
|
+
|
|
425
|
+
## Story Point Patterns
|
|
426
|
+
|
|
427
|
+
Configure custom regex patterns to match your team's story point format:
|
|
428
|
+
|
|
429
|
+
```yaml
|
|
430
|
+
story_point_patterns:
|
|
431
|
+
- "SP: (\\d+)" # SP: 5
|
|
432
|
+
- "\\[([0-9]+) pts\\]" # [3 pts]
|
|
433
|
+
- "estimate: (\\d+)" # estimate: 8
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
## Ticket Platform Support
|
|
437
|
+
|
|
438
|
+
Automatically detects and tracks tickets from:
|
|
439
|
+
- **JIRA**: `PROJ-123`
|
|
440
|
+
- **GitHub**: `#123`, `GH-123`
|
|
441
|
+
- **ClickUp**: `CU-abc123`
|
|
442
|
+
- **Linear**: `ENG-123`
|
|
443
|
+
|
|
444
|
+
### JIRA Integration
|
|
445
|
+
|
|
446
|
+
GitFlow Analytics can fetch story points directly from JIRA tickets. Configure your JIRA instance:
|
|
447
|
+
|
|
448
|
+
```yaml
|
|
449
|
+
jira:
|
|
450
|
+
access_user: "${JIRA_ACCESS_USER}"
|
|
451
|
+
access_token: "${JIRA_ACCESS_TOKEN}"
|
|
452
|
+
base_url: "https://your-company.atlassian.net"
|
|
453
|
+
|
|
454
|
+
jira_integration:
|
|
455
|
+
enabled: true
|
|
456
|
+
story_point_fields:
|
|
457
|
+
- "Story point estimate" # Your custom field name
|
|
458
|
+
- "customfield_10016" # Or use field ID
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
To discover your JIRA story point fields:
|
|
462
|
+
```bash
|
|
463
|
+
gitflow-analytics discover-jira-fields -c config.yaml
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
## Caching
|
|
467
|
+
|
|
468
|
+
The tool uses SQLite for intelligent caching:
|
|
469
|
+
- Commit analysis results
|
|
470
|
+
- Developer identity mappings
|
|
471
|
+
- Pull request data
|
|
472
|
+
|
|
473
|
+
Cache is automatically managed with configurable TTL.
|
|
474
|
+
|
|
475
|
+
## Developer Identity Resolution
|
|
476
|
+
|
|
477
|
+
Intelligently merges developer identities across:
|
|
478
|
+
- Different email addresses
|
|
479
|
+
- Name variations
|
|
480
|
+
- GitHub usernames
|
|
481
|
+
|
|
482
|
+
Manual overrides supported in configuration.
|
|
483
|
+
|
|
484
|
+
## Contributing
|
|
485
|
+
|
|
486
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
487
|
+
|
|
488
|
+
## License
|
|
489
|
+
|
|
490
|
+
This project is licensed under the MIT License - see the LICENSE file for details.
|