Files
git-velocity/internal/config/schema.go
T
2025-12-10 21:09:25 +00:00

455 lines
14 KiB
Go

package config
import "time"
// Config represents the main configuration structure
type Config struct {
Version string `yaml:"version"`
Auth AuthConfig `yaml:"auth"`
Repositories []RepositoryConfig `yaml:"repositories"`
DateRange DateRangeConfig `yaml:"date_range"`
Granularity []string `yaml:"granularity"`
CustomPeriods []CustomPeriod `yaml:"custom_periods,omitempty"`
Teams []TeamConfig `yaml:"teams,omitempty"`
Scoring ScoringConfig `yaml:"scoring"`
Output OutputConfig `yaml:"output"`
Cache CacheConfig `yaml:"cache"`
Options OptionsConfig `yaml:"options"`
}
// AuthConfig holds authentication configuration
type AuthConfig struct {
// Token-based authentication
GithubToken string `yaml:"github_token,omitempty"`
// GitHub App authentication
GithubApp *GithubAppConfig `yaml:"github_app,omitempty"`
}
// GithubAppConfig holds GitHub App authentication details
type GithubAppConfig struct {
AppID int64 `yaml:"app_id"`
InstallationID int64 `yaml:"installation_id"`
PrivateKeyPath string `yaml:"private_key_path,omitempty"`
PrivateKey string `yaml:"private_key,omitempty"`
}
// RepositoryConfig defines a repository to analyze
type RepositoryConfig struct {
Owner string `yaml:"owner"`
Name string `yaml:"name,omitempty"`
Pattern string `yaml:"pattern,omitempty"` // For wildcard matching
}
// DateRangeConfig specifies the analysis time range
type DateRangeConfig struct {
Start string `yaml:"start,omitempty"` // ISO 8601 format
End string `yaml:"end,omitempty"` // ISO 8601 format
}
// CustomPeriod defines a custom time period for analysis
type CustomPeriod struct {
Name string `yaml:"name"`
Start string `yaml:"start"`
End string `yaml:"end"`
}
// TeamConfig defines a team and its members
type TeamConfig struct {
Name string `yaml:"name"`
Members []string `yaml:"members"`
Color string `yaml:"color,omitempty"`
}
// ScoringConfig holds gamification scoring configuration
type ScoringConfig struct {
Enabled bool `yaml:"enabled"`
Points PointsConfig `yaml:"points"`
Achievements []AchievementConfig `yaml:"achievements,omitempty"`
}
// PointsConfig defines point values for various activities
type PointsConfig struct {
Commit int `yaml:"commit"`
CommitWithTests int `yaml:"commit_with_tests"`
LinesAdded float64 `yaml:"lines_added"`
LinesDeleted float64 `yaml:"lines_deleted"`
PROpened int `yaml:"pr_opened"`
PRMerged int `yaml:"pr_merged"`
PRReviewed int `yaml:"pr_reviewed"`
ReviewComment int `yaml:"review_comment"` // PR review comments (not code comments)
IssueOpened int `yaml:"issue_opened"`
IssueClosed int `yaml:"issue_closed"`
FastReview1h int `yaml:"fast_review_1h"`
FastReview4h int `yaml:"fast_review_4h"`
FastReview24h int `yaml:"fast_review_24h"`
}
// AchievementConfig defines an achievement badge
type AchievementConfig struct {
ID string `yaml:"id"`
Name string `yaml:"name"`
Description string `yaml:"description"`
Icon string `yaml:"icon"`
Condition AchievementCondition `yaml:"condition"`
}
// AchievementCondition defines when an achievement is earned
type AchievementCondition struct {
Type string `yaml:"type"` // commit_count, pr_count, review_count, avg_review_time, etc.
Threshold float64 `yaml:"threshold"`
}
// TierFromThreshold returns the tier level (1-11) based on threshold value
// Tiers: 1=1, 2=10, 3=25, 4=50, 5=100, 6=250, 7=500, 8=1000, 9=5000, 10=10000, 11=25000+
func TierFromThreshold(threshold float64) int {
tiers := []float64{1, 10, 25, 50, 100, 250, 500, 1000, 5000, 10000, 25000}
for i := len(tiers) - 1; i >= 0; i-- {
if threshold >= tiers[i] {
return i + 1
}
}
return 1
}
// OutputConfig specifies output generation settings
type OutputConfig struct {
Directory string `yaml:"directory"`
Format []string `yaml:"format"` // html, json
Deploy DeployConfig `yaml:"deploy"`
}
// DeployConfig specifies deployment options
type DeployConfig struct {
GHPages bool `yaml:"gh_pages"`
Artifact bool `yaml:"artifact"`
}
// CacheConfig holds caching configuration
type CacheConfig struct {
Enabled bool `yaml:"enabled"`
Directory string `yaml:"directory"`
TTL string `yaml:"ttl"` // Duration string like "24h"
}
// OptionsConfig holds advanced options
type OptionsConfig struct {
ConcurrentRequests int `yaml:"concurrent_requests"`
IncludeBots bool `yaml:"include_bots"`
BotPatterns []string `yaml:"bot_patterns"`
CloneDirectory string `yaml:"clone_directory"` // Directory for local git clones
UseLocalGit bool `yaml:"use_local_git"` // Use local git for commits (faster)
UserAliases []UserAlias `yaml:"user_aliases,omitempty"` // Manual email/name to login mappings
}
// UserAlias maps git emails or names to a GitHub login
type UserAlias struct {
GithubLogin string `yaml:"github_login"` // The canonical GitHub username
Emails []string `yaml:"emails,omitempty"` // Git commit emails to map
Names []string `yaml:"names,omitempty"` // Git commit author names to map
}
// ParsedDateRange holds parsed date range values
type ParsedDateRange struct {
Start *time.Time
End *time.Time
}
// DefaultConfig returns a configuration with sensible defaults
func DefaultConfig() *Config {
return &Config{
Version: "1.0",
Granularity: []string{"daily", "weekly", "monthly"},
Scoring: ScoringConfig{
Enabled: true,
Points: PointsConfig{
Commit: 10,
CommitWithTests: 15,
LinesAdded: 0.1,
LinesDeleted: 0.05,
PROpened: 25,
PRMerged: 50,
PRReviewed: 30,
ReviewComment: 5,
IssueOpened: 15,
IssueClosed: 20,
FastReview1h: 50,
FastReview4h: 25,
FastReview24h: 10,
},
Achievements: defaultAchievements(),
},
Output: OutputConfig{
Directory: "./dist",
Format: []string{"html", "json"},
Deploy: DeployConfig{
GHPages: true,
Artifact: true,
},
},
Cache: CacheConfig{
Enabled: true,
Directory: "./.cache",
TTL: "24h",
},
Options: OptionsConfig{
ConcurrentRequests: 5,
IncludeBots: false,
BotPatterns: []string{
"*[bot]",
"dependabot*",
"renovate*",
"github-actions*",
},
CloneDirectory: "./.repos",
UseLocalGit: true, // Default to faster local git analysis
},
}
}
// defaultAchievements returns the default achievement badges
func defaultAchievements() []AchievementConfig {
return []AchievementConfig{
{
ID: "first-commit",
Name: "First Steps",
Description: "Made your first commit",
Icon: "fa-baby",
Condition: AchievementCondition{Type: "commit_count", Threshold: 1},
},
{
ID: "commit-10",
Name: "Getting Started",
Description: "Made 10 commits",
Icon: "fa-seedling",
Condition: AchievementCondition{Type: "commit_count", Threshold: 10},
},
{
ID: "commit-100",
Name: "Committed",
Description: "Made 100 commits",
Icon: "fa-fire",
Condition: AchievementCondition{Type: "commit_count", Threshold: 100},
},
{
ID: "commit-500",
Name: "Code Machine",
Description: "Made 500 commits",
Icon: "fa-robot",
Condition: AchievementCondition{Type: "commit_count", Threshold: 500},
},
{
ID: "commit-1000",
Name: "Code Warrior",
Description: "Made 1000 commits",
Icon: "fa-crown",
Condition: AchievementCondition{Type: "commit_count", Threshold: 1000},
},
{
ID: "pr-opener",
Name: "PR Pioneer",
Description: "Opened your first pull request",
Icon: "fa-code-pull-request",
Condition: AchievementCondition{Type: "pr_opened_count", Threshold: 1},
},
{
ID: "pr-10",
Name: "Pull Request Pro",
Description: "Opened 10 pull requests",
Icon: "fa-code-branch",
Condition: AchievementCondition{Type: "pr_opened_count", Threshold: 10},
},
{
ID: "pr-50",
Name: "Merge Master",
Description: "Opened 50 pull requests",
Icon: "fa-code-merge",
Condition: AchievementCondition{Type: "pr_opened_count", Threshold: 50},
},
{
ID: "reviewer",
Name: "Code Reviewer",
Description: "Reviewed your first pull request",
Icon: "fa-magnifying-glass-chart",
Condition: AchievementCondition{Type: "review_count", Threshold: 1},
},
{
ID: "reviewer-25",
Name: "Review Regular",
Description: "Reviewed 25 pull requests",
Icon: "fa-eye",
Condition: AchievementCondition{Type: "review_count", Threshold: 25},
},
{
ID: "reviewer-100",
Name: "Review Guru",
Description: "Reviewed 100 pull requests",
Icon: "fa-user-graduate",
Condition: AchievementCondition{Type: "review_count", Threshold: 100},
},
{
ID: "speed-demon",
Name: "Speed Demon",
Description: "Average review response under 1 hour",
Icon: "fa-bolt",
Condition: AchievementCondition{Type: "avg_review_time_hours", Threshold: 1},
},
{
ID: "quick-responder",
Name: "Quick Responder",
Description: "Average review response under 4 hours",
Icon: "fa-clock",
Condition: AchievementCondition{Type: "avg_review_time_hours", Threshold: 4},
},
{
ID: "commentator",
Name: "Commentator",
Description: "Left 50 PR review comments",
Icon: "fa-comments",
Condition: AchievementCondition{Type: "comment_count", Threshold: 50},
},
{
ID: "lines-1000",
Name: "Thousand Lines",
Description: "Added 1000 lines of code",
Icon: "fa-layer-group",
Condition: AchievementCondition{Type: "lines_added", Threshold: 1000},
},
{
ID: "lines-10000",
Name: "Ten Thousand",
Description: "Added 10000 lines of code",
Icon: "fa-mountain",
Condition: AchievementCondition{Type: "lines_added", Threshold: 10000},
},
{
ID: "cleaner",
Name: "Code Cleaner",
Description: "Deleted 1000 lines of code",
Icon: "fa-broom",
Condition: AchievementCondition{Type: "lines_deleted", Threshold: 1000},
},
{
ID: "refactorer",
Name: "Refactoring Champion",
Description: "Deleted 10000 lines of code",
Icon: "fa-recycle",
Condition: AchievementCondition{Type: "lines_deleted", Threshold: 10000},
},
{
ID: "multi-repo",
Name: "Multi-Repo Master",
Description: "Contributed to 5 repositories",
Icon: "fa-folder-tree",
Condition: AchievementCondition{Type: "repo_count", Threshold: 5},
},
{
ID: "team-player",
Name: "Team Player",
Description: "Reviewed PRs from 10 different contributors",
Icon: "fa-people-group",
Condition: AchievementCondition{Type: "unique_reviewees", Threshold: 10},
},
// PR Quality achievements
{
ID: "big-pr",
Name: "Heavy Lifter",
Description: "Merged a PR with 1000+ lines changed",
Icon: "fa-weight-hanging",
Condition: AchievementCondition{Type: "largest_pr_size", Threshold: 1000},
},
{
ID: "mega-pr",
Name: "Mega Merge",
Description: "Merged a PR with 5000+ lines changed",
Icon: "fa-dumbbell",
Condition: AchievementCondition{Type: "largest_pr_size", Threshold: 5000},
},
{
ID: "small-pr-10",
Name: "Small PR Advocate",
Description: "Merged 10 PRs under 100 lines",
Icon: "fa-compress",
Condition: AchievementCondition{Type: "small_pr_count", Threshold: 10},
},
{
ID: "small-pr-50",
Name: "Atomic Commits Hero",
Description: "Merged 50 PRs under 100 lines",
Icon: "fa-atom",
Condition: AchievementCondition{Type: "small_pr_count", Threshold: 50},
},
{
ID: "perfect-pr-5",
Name: "Clean Code",
Description: "5 PRs merged without changes requested",
Icon: "fa-check-double",
Condition: AchievementCondition{Type: "perfect_prs", Threshold: 5},
},
{
ID: "perfect-pr-25",
Name: "Flawless",
Description: "25 PRs merged without changes requested",
Icon: "fa-gem",
Condition: AchievementCondition{Type: "perfect_prs", Threshold: 25},
},
// Activity pattern achievements
{
ID: "streak-7",
Name: "Week Warrior",
Description: "7 day contribution streak",
Icon: "fa-calendar-week",
Condition: AchievementCondition{Type: "longest_streak", Threshold: 7},
},
{
ID: "streak-30",
Name: "Month Master",
Description: "30 day contribution streak",
Icon: "fa-calendar-check",
Condition: AchievementCondition{Type: "longest_streak", Threshold: 30},
},
{
ID: "early-bird",
Name: "Early Bird",
Description: "50 commits before 9am",
Icon: "fa-sun",
Condition: AchievementCondition{Type: "early_bird_count", Threshold: 50},
},
{
ID: "night-owl",
Name: "Night Owl",
Description: "50 commits after 9pm",
Icon: "fa-moon",
Condition: AchievementCondition{Type: "night_owl_count", Threshold: 50},
},
{
ID: "nosferatu",
Name: "Nosferatu",
Description: "25 commits between midnight and 4am",
Icon: "fa-skull",
Condition: AchievementCondition{Type: "midnight_count", Threshold: 25},
},
{
ID: "weekend-warrior",
Name: "Weekend Warrior",
Description: "25 weekend commits",
Icon: "fa-couch",
Condition: AchievementCondition{Type: "weekend_warrior", Threshold: 25},
},
{
ID: "active-30",
Name: "Consistent Contributor",
Description: "Active on 30 different days",
Icon: "fa-chart-line",
Condition: AchievementCondition{Type: "active_days", Threshold: 30},
},
{
ID: "active-100",
Name: "Dedicated Developer",
Description: "Active on 100 different days",
Icon: "fa-fire-flame-curved",
Condition: AchievementCondition{Type: "active_days", Threshold: 100},
},
}
}