mirror of
https://github.com/lukaszraczylo/git-velocity.git
synced 2026-06-05 22:43:56 +00:00
Fix filtering out the bot activity.
This commit is contained in:
@@ -50,9 +50,11 @@ $ git-velocity serve --port 8080
|
|||||||
|
|
||||||
### 🎮 Gamification Engine
|
### 🎮 Gamification Engine
|
||||||
- **Scoring System**: Earn points for every contribution
|
- **Scoring System**: Earn points for every contribution
|
||||||
- **34 Achievements**: From "First Steps" to "Code Warrior"
|
- **93 Achievements**: Tiered progression from "First Steps" to "Code Warrior"
|
||||||
- **Leaderboards**: Compete with your team
|
- **Leaderboards**: Compete with your team
|
||||||
- **Tier Progression**: Bronze → Silver → Gold → Diamond
|
- **Tier Progression**: Multiple tiers per achievement category
|
||||||
|
- **Activity Patterns**: Track early bird, night owl, weekend, and out-of-hours commits
|
||||||
|
- **Streak Tracking**: Daily streaks and work-week streaks (weekends don't break it!)
|
||||||
- **General velocity chart**: Visualize your velocity over time
|
- **General velocity chart**: Visualize your velocity over time
|
||||||
|
|
||||||
### 👥 Team Analytics
|
### 👥 Team Analytics
|
||||||
@@ -64,7 +66,7 @@ $ git-velocity serve --port 8080
|
|||||||
- **Local Git Analysis**: Clone repos locally for 10x faster commit analysis
|
- **Local Git Analysis**: Clone repos locally for 10x faster commit analysis
|
||||||
- **Smart Caching**: File-based caching with configurable TTL
|
- **Smart Caching**: File-based caching with configurable TTL
|
||||||
- **Concurrent Requests**: Parallel API calls for faster data fetching
|
- **Concurrent Requests**: Parallel API calls for faster data fetching
|
||||||
- **Bot Filtering**: Automatically excludes Dependabot, Renovate, and other bots
|
- **Bot Filtering**: Hardcoded patterns automatically exclude common bots (Dependabot, Renovate, GitHub Actions, etc.) with optional custom patterns
|
||||||
|
|
||||||
### 🎨 Beautiful Dashboard
|
### 🎨 Beautiful Dashboard
|
||||||
- Modern Vue.js SPA with dark/light mode
|
- Modern Vue.js SPA with dark/light mode
|
||||||
@@ -220,54 +222,44 @@ jobs:
|
|||||||
|
|
||||||
## 🏆 Achievements
|
## 🏆 Achievements
|
||||||
|
|
||||||
Git Velocity includes 34 unlockable achievements:
|
Git Velocity includes **93 hardcoded achievements** across 18 categories with multiple progression tiers. Achievements cannot be modified via configuration to prevent manipulation.
|
||||||
|
|
||||||
### Commit Achievements
|
### Achievement Categories
|
||||||
| Achievement | Description | Threshold |
|
|
||||||
|-------------|-------------|-----------|
|
|
||||||
| 🍼 First Steps | Made your first commit | 1 commit |
|
|
||||||
| 🌱 Getting Started | Made 10 commits | 10 commits |
|
|
||||||
| 🔥 Committed | Made 100 commits | 100 commits |
|
|
||||||
| 🤖 Code Machine | Made 500 commits | 500 commits |
|
|
||||||
| 👑 Code Warrior | Made 1000 commits | 1000 commits |
|
|
||||||
|
|
||||||
### Pull Request Achievements
|
| Category | Tiers | Description |
|
||||||
| Achievement | Description | Threshold |
|
|----------|-------|-------------|
|
||||||
|-------------|-------------|-----------|
|
| **Commits** | 1, 10, 50, 100, 500, 1000 | Track total commits made |
|
||||||
| 🔀 PR Pioneer | Opened your first PR | 1 PR |
|
| **PRs Opened** | 1, 10, 25, 50, 100, 250 | Track pull requests created |
|
||||||
| 🌿 Pull Request Pro | Opened 10 PRs | 10 PRs |
|
| **Reviews** | 1, 10, 25, 50, 100, 250 | Track code reviews performed |
|
||||||
| 🔀 Merge Master | Opened 50 PRs | 50 PRs |
|
| **Comments** | 10, 50, 100, 250, 500 | Track PR review comments |
|
||||||
|
| **Lines Added** | 100, 1K, 5K, 10K, 50K | Track code additions |
|
||||||
|
| **Lines Deleted** | 100, 500, 1K, 5K, 10K | Track code cleanup |
|
||||||
|
| **Review Time** | 24h, 4h, 1h | Fast review response times |
|
||||||
|
| **Multi-Repo** | 2, 5, 10 | Contribution across repositories |
|
||||||
|
| **Unique Reviewees** | 3, 10, 25 | Reviewing different contributors |
|
||||||
|
| **Large PRs** | 500, 1K, 5K lines | Big changes merged |
|
||||||
|
| **Small PRs** | 5, 10, 25, 50 | Atomic commits under 100 lines |
|
||||||
|
| **Perfect PRs** | 1, 5, 10, 25 | Merged without changes requested |
|
||||||
|
| **Active Days** | 7, 30, 60, 100 | Unique days with activity |
|
||||||
|
| **Streaks** | 3, 7, 14, 30 days | Consecutive day contributions |
|
||||||
|
| **Work Week Streak** | 3, 5, 10, 20 days | Weekday streaks (weekends don't break it!) |
|
||||||
|
| **Early Bird** | 10, 25, 50, 100 | Commits before 9am |
|
||||||
|
| **Night Owl** | 10, 25, 50, 100 | Commits after 9pm |
|
||||||
|
| **Midnight** | 5, 10, 25, 50 | Commits between midnight-4am |
|
||||||
|
| **Weekend** | 5, 10, 25, 50 | Weekend commits |
|
||||||
|
| **Out of Hours** | 10, 25, 50, 100 | Commits outside 9am-5pm |
|
||||||
|
|
||||||
### Review Achievements
|
### Example Achievements
|
||||||
| Achievement | Description | Threshold |
|
|
||||||
|-------------|-------------|-----------|
|
|
||||||
| 🔍 Code Reviewer | Reviewed your first PR | 1 review |
|
|
||||||
| 👁️ Review Regular | Reviewed 25 PRs | 25 reviews |
|
|
||||||
| 🎓 Review Guru | Reviewed 100 PRs | 100 reviews |
|
|
||||||
|
|
||||||
### Speed Achievements
|
| Achievement | Description |
|
||||||
| Achievement | Description | Threshold |
|
|-------------|-------------|
|
||||||
|-------------|-------------|-----------|
|
| 🍼 First Steps | Made your first commit |
|
||||||
| ⚡ Speed Demon | Avg review response < 1 hour | < 1h |
|
| 👑 Code Warrior | Made 1000 commits |
|
||||||
| ⏰ Quick Responder | Avg review response < 4 hours | < 4h |
|
| ⚡ Speed Demon | Average review response under 1 hour |
|
||||||
|
| 💎 Flawless | 25 PRs merged without changes requested |
|
||||||
### Activity Pattern Achievements
|
| 🏢 Full Work Week | 5 consecutive weekday streak |
|
||||||
| Achievement | Description | Threshold |
|
| 🌙 Night Owl | 50 commits after 9pm |
|
||||||
|-------------|-------------|-----------|
|
| ♾️ Time Bender | 100 commits outside 9am-5pm |
|
||||||
| 📅 Week Warrior | 7 day contribution streak | 7 days |
|
|
||||||
| 📆 Month Master | 30 day contribution streak | 30 days |
|
|
||||||
| 🌅 Early Bird | 50 commits before 9am | 50 commits |
|
|
||||||
| 🌙 Night Owl | 50 commits after 9pm | 50 commits |
|
|
||||||
| 💀 Nosferatu | 25 commits between midnight-4am | 25 commits |
|
|
||||||
| 🛋️ Weekend Warrior | 25 weekend commits | 25 commits |
|
|
||||||
|
|
||||||
### Code Quality Achievements
|
|
||||||
| Achievement | Description | Threshold |
|
|
||||||
|-------------|-------------|-----------|
|
|
||||||
| 🗜️ Small PR Advocate | 10 PRs under 100 lines | 10 PRs |
|
|
||||||
| ⚛️ Atomic Commits Hero | 50 PRs under 100 lines | 50 PRs |
|
|
||||||
| ✅ Clean Code | 5 PRs merged without changes requested | 5 PRs |
|
|
||||||
| 💎 Flawless | 25 PRs merged without changes requested | 25 PRs |
|
|
||||||
|
|
||||||
## ⚙️ Configuration
|
## ⚙️ Configuration
|
||||||
|
|
||||||
@@ -322,6 +314,7 @@ scoring:
|
|||||||
fast_review_1h: 50
|
fast_review_1h: 50
|
||||||
fast_review_4h: 25
|
fast_review_4h: 25
|
||||||
fast_review_24h: 10
|
fast_review_24h: 10
|
||||||
|
out_of_hours: 2 # Bonus per commit outside 9am-5pm
|
||||||
|
|
||||||
output:
|
output:
|
||||||
directory: "./dist"
|
directory: "./dist"
|
||||||
@@ -338,10 +331,10 @@ cache:
|
|||||||
options:
|
options:
|
||||||
concurrent_requests: 5
|
concurrent_requests: 5
|
||||||
include_bots: false
|
include_bots: false
|
||||||
bot_patterns:
|
# Add custom bot patterns (hardcoded defaults always apply)
|
||||||
- "*[bot]"
|
additional_bot_patterns:
|
||||||
- "dependabot*"
|
- "my-org-bot"
|
||||||
- "renovate*"
|
- "jenkins*"
|
||||||
use_local_git: true
|
use_local_git: true
|
||||||
clone_directory: "./.repos"
|
clone_directory: "./.repos"
|
||||||
user_aliases:
|
user_aliases:
|
||||||
@@ -366,6 +359,33 @@ options:
|
|||||||
- "JD"
|
- "JD"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Bot Filtering
|
||||||
|
|
||||||
|
Bot filtering uses **hardcoded default patterns** that always apply when `include_bots: false`. These cannot be disabled to ensure consistent filtering:
|
||||||
|
|
||||||
|
**Default Bot Patterns (always applied):**
|
||||||
|
- `*[bot]` - GitHub App bots (dependabot[bot], renovate[bot], etc.)
|
||||||
|
- `dependabot*` - Dependabot variants
|
||||||
|
- `renovate*` - Renovate bot variants
|
||||||
|
- `github-actions*` - GitHub Actions
|
||||||
|
- `codecov*` - Codecov bot
|
||||||
|
- `snyk*` - Snyk security bot
|
||||||
|
- `greenkeeper*` - Greenkeeper (legacy)
|
||||||
|
- `imgbot*` - Image optimization bot
|
||||||
|
- `allcontributors*` - All Contributors bot
|
||||||
|
- `semantic-release*` - Semantic release bot
|
||||||
|
|
||||||
|
**Add custom patterns** for your organization's bots:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
options:
|
||||||
|
include_bots: false # When false, hardcoded + additional patterns apply
|
||||||
|
additional_bot_patterns:
|
||||||
|
- "my-org-bot" # Exact match
|
||||||
|
- "jenkins*" # Prefix match
|
||||||
|
- "*-ci" # Suffix match
|
||||||
|
```
|
||||||
|
|
||||||
### Environment Variables
|
### Environment Variables
|
||||||
|
|
||||||
All configuration values support environment variable expansion:
|
All configuration values support environment variable expansion:
|
||||||
|
|||||||
+13
-14
@@ -98,16 +98,10 @@ scoring:
|
|||||||
fast_review_1h: 50 # Review response under 1 hour
|
fast_review_1h: 50 # Review response under 1 hour
|
||||||
fast_review_4h: 25 # Review response under 4 hours
|
fast_review_4h: 25 # Review response under 4 hours
|
||||||
fast_review_24h: 10 # Review response under 24 hours
|
fast_review_24h: 10 # Review response under 24 hours
|
||||||
|
out_of_hours: 2 # Bonus per commit outside 9am-5pm
|
||||||
|
|
||||||
# Achievement badges (optional, uses defaults if not specified)
|
# Note: Achievements are hardcoded (93 achievements across 18 categories)
|
||||||
# achievements:
|
# They cannot be configured to prevent manipulation
|
||||||
# - id: "custom-achievement"
|
|
||||||
# name: "Custom Badge"
|
|
||||||
# description: "Earned for custom condition"
|
|
||||||
# icon: "fa-star"
|
|
||||||
# condition:
|
|
||||||
# type: "commit_count" # commit_count, pr_opened_count, review_count, etc.
|
|
||||||
# threshold: 100
|
|
||||||
|
|
||||||
# Output configuration
|
# Output configuration
|
||||||
output:
|
output:
|
||||||
@@ -129,8 +123,13 @@ cache:
|
|||||||
options:
|
options:
|
||||||
concurrent_requests: 5 # Max parallel API requests (1-20)
|
concurrent_requests: 5 # Max parallel API requests (1-20)
|
||||||
include_bots: false # Include bot accounts in metrics
|
include_bots: false # Include bot accounts in metrics
|
||||||
bot_patterns: # Patterns to identify bot accounts
|
|
||||||
- "*[bot]"
|
# Bot filtering uses hardcoded default patterns that always apply:
|
||||||
- "dependabot*"
|
# *[bot], dependabot*, renovate*, github-actions*, codecov*,
|
||||||
- "renovate*"
|
# snyk*, greenkeeper*, imgbot*, allcontributors*, semantic-release*
|
||||||
- "github-actions*"
|
#
|
||||||
|
# Add your own custom patterns here (in addition to defaults):
|
||||||
|
additional_bot_patterns: []
|
||||||
|
# - "my-org-bot" # Exact match
|
||||||
|
# - "jenkins*" # Prefix match
|
||||||
|
# - "*-ci" # Suffix match
|
||||||
|
|||||||
@@ -192,19 +192,30 @@ func (c *Config) GetTeamForUser(username string) *TeamConfig {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsBot checks if a username matches bot patterns
|
// IsBot checks if a username matches bot patterns (hardcoded defaults + user-defined)
|
||||||
func (c *Config) IsBot(username string) bool {
|
func (c *Config) IsBot(username string) bool {
|
||||||
if c.Options.IncludeBots {
|
if c.Options.IncludeBots {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
lower := strings.ToLower(username)
|
lower := strings.ToLower(username)
|
||||||
for _, pattern := range c.Options.BotPatterns {
|
|
||||||
|
// Check hardcoded default patterns first
|
||||||
|
for _, pattern := range DefaultBotPatterns() {
|
||||||
pattern = strings.ToLower(pattern)
|
pattern = strings.ToLower(pattern)
|
||||||
if matchPattern(lower, pattern) {
|
if matchPattern(lower, pattern) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check user-defined additional patterns
|
||||||
|
for _, pattern := range c.Options.AdditionalBotPatterns {
|
||||||
|
pattern = strings.ToLower(pattern)
|
||||||
|
if matchPattern(lower, pattern) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -615,15 +615,10 @@ func TestConfig_GetTeamForUser(t *testing.T) {
|
|||||||
func TestConfig_IsBot(t *testing.T) {
|
func TestConfig_IsBot(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
// Bot patterns are now hardcoded, so we just need IncludeBots: false
|
||||||
cfg := &Config{
|
cfg := &Config{
|
||||||
Options: OptionsConfig{
|
Options: OptionsConfig{
|
||||||
IncludeBots: false,
|
IncludeBots: false,
|
||||||
BotPatterns: []string{
|
|
||||||
"*[bot]",
|
|
||||||
"dependabot*",
|
|
||||||
"renovate*",
|
|
||||||
"github-actions*",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -652,6 +647,16 @@ func TestConfig_IsBot(t *testing.T) {
|
|||||||
username: "github-actions[bot]",
|
username: "github-actions[bot]",
|
||||||
expected: true,
|
expected: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "codecov bot (hardcoded)",
|
||||||
|
username: "codecov[bot]",
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "snyk bot (hardcoded)",
|
||||||
|
username: "snyk-bot",
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "regular user",
|
name: "regular user",
|
||||||
username: "alice",
|
username: "alice",
|
||||||
@@ -674,19 +679,43 @@ func TestConfig_IsBot(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestConfig_IsBot_AdditionalPatterns(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
cfg := &Config{
|
||||||
|
Options: OptionsConfig{
|
||||||
|
IncludeBots: false,
|
||||||
|
AdditionalBotPatterns: []string{"my-custom-bot", "ci-*"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Custom patterns should work
|
||||||
|
assert.True(t, cfg.IsBot("my-custom-bot"))
|
||||||
|
assert.True(t, cfg.IsBot("ci-runner"))
|
||||||
|
assert.True(t, cfg.IsBot("ci-bot"))
|
||||||
|
|
||||||
|
// Hardcoded patterns should still work
|
||||||
|
assert.True(t, cfg.IsBot("dependabot[bot]"))
|
||||||
|
assert.True(t, cfg.IsBot("renovate[bot]"))
|
||||||
|
|
||||||
|
// Regular users should not match
|
||||||
|
assert.False(t, cfg.IsBot("alice"))
|
||||||
|
}
|
||||||
|
|
||||||
func TestConfig_IsBot_IncludeBots(t *testing.T) {
|
func TestConfig_IsBot_IncludeBots(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
cfg := &Config{
|
cfg := &Config{
|
||||||
Options: OptionsConfig{
|
Options: OptionsConfig{
|
||||||
IncludeBots: true,
|
IncludeBots: true,
|
||||||
BotPatterns: []string{"*[bot]"},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// When IncludeBots is true, nothing should be considered a bot
|
// When IncludeBots is true, nothing should be considered a bot
|
||||||
|
// (even hardcoded patterns are bypassed)
|
||||||
assert.False(t, cfg.IsBot("my-app[bot]"))
|
assert.False(t, cfg.IsBot("my-app[bot]"))
|
||||||
assert.False(t, cfg.IsBot("dependabot"))
|
assert.False(t, cfg.IsBot("dependabot"))
|
||||||
|
assert.False(t, cfg.IsBot("renovate[bot]"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMatchPattern(t *testing.T) {
|
func TestMatchPattern(t *testing.T) {
|
||||||
|
|||||||
+28
-16
@@ -139,12 +139,29 @@ type CacheConfig struct {
|
|||||||
|
|
||||||
// OptionsConfig holds advanced options
|
// OptionsConfig holds advanced options
|
||||||
type OptionsConfig struct {
|
type OptionsConfig struct {
|
||||||
ConcurrentRequests int `yaml:"concurrent_requests"`
|
ConcurrentRequests int `yaml:"concurrent_requests"`
|
||||||
IncludeBots bool `yaml:"include_bots"`
|
IncludeBots bool `yaml:"include_bots"`
|
||||||
BotPatterns []string `yaml:"bot_patterns"`
|
AdditionalBotPatterns []string `yaml:"additional_bot_patterns"` // User-defined patterns (added to hardcoded defaults)
|
||||||
CloneDirectory string `yaml:"clone_directory"` // Directory for local git clones
|
CloneDirectory string `yaml:"clone_directory"` // Directory for local git clones
|
||||||
UseLocalGit bool `yaml:"use_local_git"` // Use local git for commits (faster)
|
UseLocalGit bool `yaml:"use_local_git"` // Use local git for commits (faster)
|
||||||
UserAliases []UserAlias `yaml:"user_aliases,omitempty"` // Manual email/name to login mappings
|
UserAliases []UserAlias `yaml:"user_aliases,omitempty"` // Manual email/name to login mappings
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultBotPatterns returns the hardcoded bot patterns that are always applied
|
||||||
|
// These cannot be overridden by users to ensure consistent bot filtering
|
||||||
|
func DefaultBotPatterns() []string {
|
||||||
|
return []string{
|
||||||
|
"*[bot]", // GitHub App bots: dependabot[bot], renovate[bot], etc.
|
||||||
|
"dependabot*", // Dependabot variants
|
||||||
|
"renovate*", // Renovate bot variants
|
||||||
|
"github-actions*", // GitHub Actions
|
||||||
|
"codecov*", // Codecov bot
|
||||||
|
"snyk*", // Snyk security bot
|
||||||
|
"greenkeeper*", // Greenkeeper (legacy)
|
||||||
|
"imgbot*", // Image optimization bot
|
||||||
|
"allcontributors*", // All Contributors bot
|
||||||
|
"semantic-release*", // Semantic release bot
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// UserAlias maps git emails or names to a GitHub login
|
// UserAlias maps git emails or names to a GitHub login
|
||||||
@@ -198,16 +215,11 @@ func DefaultConfig() *Config {
|
|||||||
TTL: "24h",
|
TTL: "24h",
|
||||||
},
|
},
|
||||||
Options: OptionsConfig{
|
Options: OptionsConfig{
|
||||||
ConcurrentRequests: 5,
|
ConcurrentRequests: 5,
|
||||||
IncludeBots: false,
|
IncludeBots: false,
|
||||||
BotPatterns: []string{
|
AdditionalBotPatterns: []string{}, // Users can add custom patterns here
|
||||||
"*[bot]",
|
CloneDirectory: "./.repos",
|
||||||
"dependabot*",
|
UseLocalGit: true, // Default to faster local git analysis
|
||||||
"renovate*",
|
|
||||||
"github-actions*",
|
|
||||||
},
|
|
||||||
CloneDirectory: "./.repos",
|
|
||||||
UseLocalGit: true, // Default to faster local git analysis
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user