diff options
Diffstat (limited to 'internal/showcase')
| -rw-r--r-- | internal/showcase/metadata.go | 60 | ||||
| -rw-r--r-- | internal/showcase/rank_history_svg.go | 24 |
2 files changed, 62 insertions, 22 deletions
diff --git a/internal/showcase/metadata.go b/internal/showcase/metadata.go index 038dc08..c6c756b 100644 --- a/internal/showcase/metadata.go +++ b/internal/showcase/metadata.go @@ -22,20 +22,21 @@ type LanguageStats struct { // RepoMetadata holds metadata about a repository type RepoMetadata struct { - Languages []LanguageStats // Programming languages with usage statistics - Documentation []LanguageStats // Documentation/text files with usage statistics - CommitCount int - LinesOfCode int // Lines of code (excluding documentation) - LinesOfDocs int // Lines of documentation - FirstCommitDate string - LastCommitDate string - License string - AvgCommitAge float64 // Average age of last 42 commits in days - TagCount int // Total number of git tags in the repository - Score float64 // Project score combining recent activity, reduced LOC weight, tag count, and release status - LatestTag string // Latest version tag (empty if no tags) - LatestTagDate string // Date of the latest tag (empty if no tags) - HasReleases bool // Whether the project has any releases/tags + Languages []LanguageStats // Programming languages with usage statistics + Documentation []LanguageStats // Documentation/text files with usage statistics + CommitCount int + LinesOfCode int // Lines of code (excluding documentation) + LinesOfDocs int // Lines of documentation + FirstCommitDate string + LastCommitDate string + LastActivityDate string // Most recent commit on any local branch (--all); used for activity checks + License string + AvgCommitAge float64 // Average age of last 42 commits in days (HEAD only; used for score) + TagCount int // Total number of git tags in the repository + Score float64 // Project score combining recent activity, reduced LOC weight, tag count, and release status + LatestTag string // Latest version tag (empty if no tags) + LatestTagDate string // Date of the latest tag (empty if no tags) + HasReleases bool // Whether the project has any releases/tags } // extractRepoMetadata extracts metadata from a repository @@ -83,6 +84,18 @@ func extractRepoMetadata(repoPath string) (*RepoMetadata, error) { } metadata.LastCommitDate = lastDate + // LastActivityDate is the most recent commit across ALL local branches so + // that projects with active development on non-default branches (e.g. a + // "develop" branch while HEAD is an old "master") are not falsely marked + // inactive. Code stats (LOC, AvgCommitAge, score) remain HEAD-only per + // the configured branch rules. + activityDate, err := getLastActivityDate(repoPath) + if err != nil { + fmt.Printf("Warning: Failed to get last activity date: %v\n", err) + activityDate = lastDate // fall back to HEAD-based date + } + metadata.LastActivityDate = activityDate + // Check for license file license := detectLicense(repoPath) metadata.License = license @@ -214,6 +227,25 @@ func getLastCommitDate(repoPath string) (string, error) { return "", fmt.Errorf("no commits found") } +// getLastActivityDate returns the date of the most recent commit across all +// local branches (--all). This is intentionally local-only: no network +// fetch is performed, so it reflects the state of the last sync. It is used +// solely for the inactivity check (greying in the rank-history SVG) so that +// projects with active work on non-default branches are not falsely flagged. +// Code stats (LOC, AvgCommitAge, score) remain HEAD-only as per config. +func getLastActivityDate(repoPath string) (string, error) { + cmd := exec.Command("git", "-C", repoPath, "log", "--all", "-1", "--pretty=format:%ai") + output, err := cmd.Output() + if err != nil { + return "", err + } + parts := strings.Fields(string(output)) + if len(parts) > 0 { + return parts[0], nil + } + return "", fmt.Errorf("no commits found across all branches") +} + // detectLicense checks for common license files func detectLicense(repoPath string) string { licenseFiles := []string{ diff --git a/internal/showcase/rank_history_svg.go b/internal/showcase/rank_history_svg.go index 7a0386d..5022cd3 100644 --- a/internal/showcase/rank_history_svg.go +++ b/internal/showcase/rank_history_svg.go @@ -207,15 +207,23 @@ func GenerateRankHistorySVG(summaries []ProjectSummary) string { continue // skip projects that have never appeared in any snapshot } - // Mirror the inactivity check from formatGemtext: avg commit age > 730 days - // AND the most recent commit was also over a year ago. Inactive projects - // are still drawn but as grey lines so they do not compete visually with - // active ones; they turn coloured only on legend-entry hover. + // A project is inactive when its average commit age (HEAD) exceeds 730 + // days AND no commit on ANY local branch is younger than 365 days. + // Using LastActivityDate (all-branches) avoids false positives for + // projects whose default branch is old but development continues on + // another branch (e.g. a "develop" or "master" branch). + // Code stats (AvgCommitAge, score) remain HEAD-only per config rules. inactive := false - if s.Metadata != nil && s.Metadata.AvgCommitAge > 730 && s.Metadata.LastCommitDate != "" { - if last, err := time.Parse("2006-01-02", s.Metadata.LastCommitDate); err == nil { - if time.Since(last).Hours()/24 > 365 { - inactive = true + if s.Metadata != nil && s.Metadata.AvgCommitAge > 730 { + activityDate := s.Metadata.LastActivityDate + if activityDate == "" { + activityDate = s.Metadata.LastCommitDate // fallback if field absent + } + if activityDate != "" { + if last, err := time.Parse("2006-01-02", activityDate); err == nil { + if time.Since(last).Hours()/24 > 365 { + inactive = true + } } } } |
