diff options
| -rw-r--r-- | internal/showcase/metadata.go | 18 | ||||
| -rw-r--r-- | internal/showcase/metadata_test.go | 19 | ||||
| -rw-r--r-- | internal/showcase/showcase.go | 4 | ||||
| -rw-r--r-- | internal/version/version.go | 2 |
4 files changed, 31 insertions, 12 deletions
diff --git a/internal/showcase/metadata.go b/internal/showcase/metadata.go index 3c51812..798b8fe 100644 --- a/internal/showcase/metadata.go +++ b/internal/showcase/metadata.go @@ -11,6 +11,8 @@ import ( "time" ) +const unreleasedScorePenalty = 0.75 + // LanguageStats holds statistics for a programming language type LanguageStats struct { Name string @@ -30,7 +32,7 @@ type RepoMetadata struct { 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, and tag count + 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 @@ -103,13 +105,14 @@ func extractRepoMetadata(repoPath string) (*RepoMetadata, error) { metadata.TagCount = tagCount // Calculate score with recent activity as the strongest signal, - // a smaller LOC contribution than before, and a modest tag bonus. - metadata.Score = calculateRepoScore(metadata.LinesOfCode, metadata.AvgCommitAge, metadata.TagCount) + // a smaller LOC contribution than before, a modest tag bonus, + // and a penalty for projects without a release yet. + metadata.Score = calculateRepoScore(metadata.LinesOfCode, metadata.AvgCommitAge, metadata.TagCount, metadata.HasReleases) return metadata, nil } -func calculateRepoScore(linesOfCode int, avgCommitAge float64, tagCount int) float64 { +func calculateRepoScore(linesOfCode int, avgCommitAge float64, tagCount int, hasReleases bool) float64 { sizeComponent := 0.0 if linesOfCode > 0 { sizeComponent = math.Sqrt(math.Log10(float64(linesOfCode)+1.0)) * 250.0 @@ -120,7 +123,12 @@ func calculateRepoScore(linesOfCode int, avgCommitAge float64, tagCount int) flo tagComponent = math.Log1p(float64(tagCount)) * 40.0 } - return (sizeComponent + tagComponent) / (avgCommitAge + 1.0) + score := (sizeComponent + tagComponent) / (avgCommitAge + 1.0) + if !hasReleases { + score *= unreleasedScorePenalty + } + + return score } // getCommitCount returns the total number of commits diff --git a/internal/showcase/metadata_test.go b/internal/showcase/metadata_test.go index 3aa49c1..e8777ce 100644 --- a/internal/showcase/metadata_test.go +++ b/internal/showcase/metadata_test.go @@ -10,8 +10,8 @@ import ( func TestCalculateRepoScore_IncreasesWithTagCount(t *testing.T) { t.Parallel() - withoutTags := calculateRepoScore(5000, 14, 0) - withTags := calculateRepoScore(5000, 14, 10) + withoutTags := calculateRepoScore(5000, 14, 0, true) + withTags := calculateRepoScore(5000, 14, 10, true) if withTags <= withoutTags { t.Fatalf("expected tags to increase score, got without=%f with=%f", withoutTags, withTags) @@ -21,14 +21,25 @@ func TestCalculateRepoScore_IncreasesWithTagCount(t *testing.T) { func TestCalculateRepoScore_DecreasesWithAge(t *testing.T) { t.Parallel() - recent := calculateRepoScore(5000, 7, 3) - old := calculateRepoScore(5000, 70, 3) + recent := calculateRepoScore(5000, 7, 3, true) + old := calculateRepoScore(5000, 70, 3, true) if recent <= old { t.Fatalf("expected newer activity to score higher, got recent=%f old=%f", recent, old) } } +func TestCalculateRepoScore_DecreasesWithoutRelease(t *testing.T) { + t.Parallel() + + released := calculateRepoScore(5000, 14, 3, true) + unreleased := calculateRepoScore(5000, 14, 3, false) + + if unreleased >= released { + t.Fatalf("expected unreleased repo to score lower, got released=%f unreleased=%f", released, unreleased) + } +} + func TestGetLatestTag_ReturnsTotalTagCount(t *testing.T) { t.Parallel() diff --git a/internal/showcase/showcase.go b/internal/showcase/showcase.go index 7248a77..dd5fc9c 100644 --- a/internal/showcase/showcase.go +++ b/internal/showcase/showcase.go @@ -787,7 +787,7 @@ func (g *Generator) formatGemtext(summaries []ProjectSummary) string { builder.WriteString(fmt.Sprintf("Generated on: %s\n\n", time.Now().Format("2006-01-02"))) // Introduction paragraph - builder.WriteString("This page showcases my side projects, providing an overview of what each project does, its technical implementation, and key metrics. Each project summary includes information about the programming languages used, development activity, releases, and licensing. The projects are ranked by score, which combines recent activity, project size, and tag history.\n\n") + builder.WriteString("This page showcases my side projects, providing an overview of what each project does, its technical implementation, and key metrics. Each project summary includes information about the programming languages used, development activity, releases, and licensing. The projects are ranked by score, which combines recent activity, project size, tag history, and whether the project has shipped a release.\n\n") // Template inline TOC builder.WriteString("<< template::inline::toc\n\n") @@ -909,7 +909,7 @@ func (g *Generator) formatGemtext(summaries []ProjectSummary) string { } builder.WriteString(fmt.Sprintf("* 🏷️ Tags: %d\n", summary.Metadata.TagCount)) builder.WriteString(fmt.Sprintf("* 📅 Development Period: %s to %s\n", summary.Metadata.FirstCommitDate, summary.Metadata.LastCommitDate)) - builder.WriteString(fmt.Sprintf("* 🏆 Score: %.1f (combines recent activity, code size, and tags)\n", summary.Metadata.Score)) + builder.WriteString(fmt.Sprintf("* 🏆 Score: %.1f (combines recent activity, code size, tags, and release status)\n", summary.Metadata.Score)) builder.WriteString(fmt.Sprintf("* ⚖️ License: %s\n", summary.Metadata.License)) // Add release information or experimental status diff --git a/internal/version/version.go b/internal/version/version.go index 7997fd7..4582164 100644 --- a/internal/version/version.go +++ b/internal/version/version.go @@ -7,7 +7,7 @@ import ( var ( // Version is the current version of gitsyncer - Version = "0.15.3" + Version = "0.15.4" // GitCommit is the git commit hash at build time GitCommit = "unknown" |
