diff --git a/models/git/commit_status.go b/models/git/commit_status.go index c3cda7b73d..d12afc42c5 100644 --- a/models/git/commit_status.go +++ b/models/git/commit_status.go @@ -397,36 +397,16 @@ func GetLatestCommitStatusForRepoCommitIDs(ctx context.Context, repoID int64, co // FindRepoRecentCommitStatusContexts returns repository's recent commit status contexts func FindRepoRecentCommitStatusContexts(ctx context.Context, repoID int64, before time.Duration) ([]string, error) { - type result struct { - Index int64 - SHA string - } - getBase := func() *xorm.Session { - return db.GetEngine(ctx).Table(&CommitStatus{}).Where("repo_id = ?", repoID) - } - start := timeutil.TimeStampNow().AddDuration(-before) - results := make([]result, 0, 10) - sess := getBase().And("updated_unix >= ?", start). - Select("max( `index` ) as `index`, sha"). - GroupBy("context_hash, sha").OrderBy("max( `index` ) desc") - - err := sess.Find(&results) - if err != nil { + var contexts []string + if err := db.GetEngine(ctx).Table("commit_status"). + Where("repo_id = ?", repoID).And("updated_unix >= ?", start). + Cols("context").Distinct().Find(&contexts); err != nil { return nil, err } - contexts := make([]string, 0, len(results)) - if len(results) == 0 { - return contexts, nil - } - - conds := make([]builder.Cond, 0, len(results)) - for _, result := range results { - conds = append(conds, builder.Eq{"`index`": result.Index, "sha": result.SHA}) - } - return contexts, getBase().And(builder.Or(conds...)).Select("context").Find(&contexts) + return contexts, nil } // NewCommitStatusOptions holds options for creating a CommitStatus diff --git a/models/git/commit_status_test.go b/models/git/commit_status_test.go index 74ba4a1006..08eba6e293 100644 --- a/models/git/commit_status_test.go +++ b/models/git/commit_status_test.go @@ -5,11 +5,15 @@ package git_test import ( "testing" + "time" "code.gitea.io/gitea/models/db" git_model "code.gitea.io/gitea/models/git" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" + user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/structs" "github.com/stretchr/testify/assert" @@ -175,3 +179,55 @@ func Test_CalcCommitStatus(t *testing.T) { assert.Equal(t, kase.expected, git_model.CalcCommitStatus(kase.statuses)) } } + +func TestFindRepoRecentCommitStatusContexts(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + + repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}) + user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + gitRepo, err := gitrepo.OpenRepository(git.DefaultContext, repo2) + assert.NoError(t, err) + defer gitRepo.Close() + + commit, err := gitRepo.GetBranchCommit(repo2.DefaultBranch) + assert.NoError(t, err) + + defer func() { + _, err := db.DeleteByBean(db.DefaultContext, &git_model.CommitStatus{ + RepoID: repo2.ID, + CreatorID: user2.ID, + SHA: commit.ID.String(), + }) + assert.NoError(t, err) + }() + + err = git_model.NewCommitStatus(db.DefaultContext, git_model.NewCommitStatusOptions{ + Repo: repo2, + Creator: user2, + SHA: commit.ID, + CommitStatus: &git_model.CommitStatus{ + State: structs.CommitStatusFailure, + TargetURL: "https://example.com/tests/", + Context: "compliance/lint-backend", + }, + }) + assert.NoError(t, err) + + err = git_model.NewCommitStatus(db.DefaultContext, git_model.NewCommitStatusOptions{ + Repo: repo2, + Creator: user2, + SHA: commit.ID, + CommitStatus: &git_model.CommitStatus{ + State: structs.CommitStatusSuccess, + TargetURL: "https://example.com/tests/", + Context: "compliance/lint-backend", + }, + }) + assert.NoError(t, err) + + contexts, err := git_model.FindRepoRecentCommitStatusContexts(db.DefaultContext, repo2.ID, time.Hour) + assert.NoError(t, err) + if assert.Len(t, contexts, 1) { + assert.Equal(t, "compliance/lint-backend", contexts[0]) + } +} diff --git a/modules/templates/util_render.go b/modules/templates/util_render.go index 659422aee7..b15de6521d 100644 --- a/modules/templates/util_render.go +++ b/modules/templates/util_render.go @@ -121,29 +121,25 @@ func RenderIssueTitle(ctx context.Context, text string, metas map[string]string) // RenderLabel renders a label // locale is needed due to an import cycle with our context providing the `Tr` function func RenderLabel(ctx context.Context, locale translation.Locale, label *issues_model.Label) template.HTML { - var ( - archivedCSSClass string - textColor = util.ContrastColor(label.Color) - labelScope = label.ExclusiveScope() - ) - - description := emoji.ReplaceAliases(template.HTMLEscapeString(label.Description)) + var extraCSSClasses string + textColor := util.ContrastColor(label.Color) + labelScope := label.ExclusiveScope() + descriptionText := emoji.ReplaceAliases(label.Description) if label.IsArchived() { - archivedCSSClass = "archived-label" - description = fmt.Sprintf("(%s) %s", locale.TrString("archived"), description) + extraCSSClasses = "archived-label" + descriptionText = fmt.Sprintf("(%s) %s", locale.TrString("archived"), descriptionText) } if labelScope == "" { // Regular label - s := fmt.Sprintf("
%s
", - archivedCSSClass, textColor, label.Color, description, RenderEmoji(ctx, label.Name)) - return template.HTML(s) + return HTMLFormat(`
%s
`, + extraCSSClasses, textColor, label.Color, descriptionText, RenderEmoji(ctx, label.Name)) } // Scoped label - scopeText := RenderEmoji(ctx, labelScope) - itemText := RenderEmoji(ctx, label.Name[len(labelScope)+1:]) + scopeHTML := RenderEmoji(ctx, labelScope) + itemHTML := RenderEmoji(ctx, label.Name[len(labelScope)+1:]) // Make scope and item background colors slightly darker and lighter respectively. // More contrast needed with higher luminance, empirically tweaked. @@ -171,14 +167,13 @@ func RenderLabel(ctx context.Context, locale translation.Locale, label *issues_m itemColor := "#" + hex.EncodeToString(itemBytes) scopeColor := "#" + hex.EncodeToString(scopeBytes) - s := fmt.Sprintf(""+ - "
%s
"+ - "
%s
"+ - "
", - archivedCSSClass, description, - textColor, scopeColor, scopeText, - textColor, itemColor, itemText) - return template.HTML(s) + return HTMLFormat(``+ + `
%s
`+ + `
%s
`+ + `
`, + extraCSSClasses, descriptionText, + textColor, scopeColor, scopeHTML, + textColor, itemColor, itemHTML) } // RenderEmoji renders html text with emoji post processors diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index de6ef9e93b..0c8363a168 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -2177,7 +2177,10 @@ func GetIssueInfo(ctx *context.Context) { } } - ctx.JSON(http.StatusOK, convert.ToIssue(ctx, ctx.Doer, issue)) + ctx.JSON(http.StatusOK, map[string]any{ + "convertedIssue": convert.ToIssue(ctx, ctx.Doer, issue), + "renderedLabels": templates.RenderLabels(ctx, ctx.Locale, issue.Labels, ctx.Repo.RepoLink, issue), + }) } // UpdateIssueTitle change issue's title diff --git a/tests/integration/issue_test.go b/tests/integration/issue_test.go index 44d362d9c7..b7952b0879 100644 --- a/tests/integration/issue_test.go +++ b/tests/integration/issue_test.go @@ -6,6 +6,7 @@ package integration import ( "context" "fmt" + "html/template" "net/http" "net/url" "path" @@ -573,10 +574,14 @@ func TestGetIssueInfo(t *testing.T) { urlStr := fmt.Sprintf("/%s/%s/issues/%d/info", owner.Name, repo.Name, issue.Index) req := NewRequest(t, "GET", urlStr) resp := session.MakeRequest(t, req, http.StatusOK) - var apiIssue api.Issue - DecodeJSON(t, resp, &apiIssue) + var respStruct struct { + ConvertedIssue api.Issue + RenderedLabels template.HTML + } + DecodeJSON(t, resp, &respStruct) - assert.EqualValues(t, issue.ID, apiIssue.ID) + assert.EqualValues(t, issue.ID, respStruct.ConvertedIssue.ID) + assert.Contains(t, string(respStruct.RenderedLabels), `"labels-list"`) } func TestUpdateIssueDeadline(t *testing.T) { diff --git a/web_src/js/components/ContextPopup.vue b/web_src/js/components/ContextPopup.vue index 65a6089522..e4e8bce184 100644 --- a/web_src/js/components/ContextPopup.vue +++ b/web_src/js/components/ContextPopup.vue @@ -1,6 +1,5 @@