fix: load repo language for converting to api struct (#12737)

Load the primary language of the repository when it's converted to a API struct. This is simpler than adding `LoadAttributes` to a lot of places.

Resolves forgejo/forgejo#12729

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/12737
Reviewed-by: Mathieu Fenniak <mfenniak@noreply.codeberg.org>
(cherry picked from commit 6c85dffb78)
This commit is contained in:
Gusted 2026-05-28 17:00:40 +02:00 committed by forgejo-backport-action
parent 34166e156e
commit 0ea34d7633
6 changed files with 58 additions and 15 deletions

View file

@ -0,0 +1,17 @@
-
id: 1
repo_id: 1
commit_id: 65f1bf27bc3bf70f64657658635e66094edbcb4d
is_primary: true
language: 'Go'
size: 1023
created_unix: 1779733547
-
id: 2
repo_id: 1
commit_id: 65f1bf27bc3bf70f64657658635e66094edbcb4d
is_primary: false
language: 'PHP'
size: 1
created_unix: 1779733547

View file

@ -33,13 +33,18 @@ func init() {
db.RegisterModel(new(LanguageStat))
}
// LoadAttributes loads attributes
func (stat *LanguageStat) LoadAttributes() {
stat.Color = enry.GetColor(stat.Language)
}
// LanguageStatList defines a list of language statistics
type LanguageStatList []*LanguageStat
// LoadAttributes loads attributes
func (stats LanguageStatList) LoadAttributes() {
for i := range stats {
stats[i].Color = enry.GetColor(stats[i].Language)
stats[i].LoadAttributes()
}
}

View file

@ -293,6 +293,28 @@ func (repo *Repository) AfterLoad() {
repo.NumOpenProjects = repo.NumProjects - repo.NumClosedProjects
}
// LoadLanguage loads the primary language of the repository, if one exists.
// If one doesn't exists `nil` is still returned.
func (repo *Repository) LoadLanguage(ctx context.Context) error {
if repo.PrimaryLanguage != nil {
return nil
}
var stat LanguageStat
has, err := db.GetEngine(ctx).
Where("`repo_id` = ? AND `is_primary` = ? AND `language` != ?", repo.ID, true, "other").
Get(&stat)
if err != nil {
return fmt.Errorf("unable to find the primary languages: %w", err)
}
if has {
stat.LoadAttributes()
repo.PrimaryLanguage = &stat
}
return nil
}
// LoadAttributes loads attributes of the repository.
func (repo *Repository) LoadAttributes(ctx context.Context) error {
// Load owner
@ -300,20 +322,11 @@ func (repo *Repository) LoadAttributes(ctx context.Context) error {
return fmt.Errorf("load owner: %w", err)
}
// Load primary language
stats := make(LanguageStatList, 0, 1)
if err := db.GetEngine(ctx).
Where("`repo_id` = ? AND `is_primary` = ? AND `language` != ?", repo.ID, true, "other").
Find(&stats); err != nil {
return fmt.Errorf("find primary languages: %w", err)
}
stats.LoadAttributes()
for _, st := range stats {
if st.RepoID == repo.ID {
repo.PrimaryLanguage = st
break
}
// Load the primary language.
if err := repo.LoadLanguage(ctx); err != nil {
return fmt.Errorf("load language: %w", err)
}
return nil
}

View file

@ -43,5 +43,7 @@ func TestRepoStatsIndex(t *testing.T) {
assert.Equal(t, "65f1bf27bc3bf70f64657658635e66094edbcb4d", status.CommitSha)
langs, err := repo_model.GetTopLanguageStats(db.DefaultContext, repo, 5)
require.NoError(t, err)
assert.Empty(t, langs)
if assert.Len(t, langs, 1) {
assert.Equal(t, &repo_model.LanguageStat{ID: 1, RepoID: 1, CommitID: "65f1bf27bc3bf70f64657658635e66094edbcb4d", IsPrimary: true, Language: "Go", Percentage: 99.9, Size: 1023, Color: "#00ADD8", CreatedUnix: 1779733547}, langs[0])
}
}

View file

@ -173,6 +173,10 @@ func innerToRepo(ctx stdCtx.Context, repo *repo_model.Repository, permissionInRe
}
}
if err := repo.LoadLanguage(ctx); err != nil {
log.Warn("Unable to load language for repo[id=%d]: %w", repo.ID, err)
}
var language string
if repo.PrimaryLanguage != nil {
language = repo.PrimaryLanguage.Language

View file

@ -63,6 +63,7 @@ func TestAPIStar(t *testing.T) {
DecodeJSON(t, resp, &repos)
assert.Len(t, repos, 1)
assert.Equal(t, repo, repos[0].FullName)
assert.Equal(t, "Go", repos[0].Language)
t.Run("disabled stars", func(t *testing.T) {
assertDisabledStarsNotFound(t, req)
@ -82,6 +83,7 @@ func TestAPIStar(t *testing.T) {
DecodeJSON(t, resp, &repos)
assert.Len(t, repos, 1)
assert.Equal(t, repo, repos[0].FullName)
assert.Equal(t, "Go", repos[0].Language)
t.Run("disabled stars", func(t *testing.T) {
assertDisabledStarsNotFound(t, req)