mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2025-08-19 17:01:12 +00:00
feat: add tag label to commit list view (#8759)
Mainly a port of https://github.com/go-gitea/gitea/pull/31082. closes #3573 ## Screenshots   --- ## Checklist ### Tests - I added test coverage for Go changes... - [x] in their respective `*_test.go` for unit tests. - [ ] in the `tests/integration` directory if it involves interactions with a live Forgejo server. - I added test coverage for JavaScript changes... - [ ] in `web_src/js/*.test.js` if it can be unit tested. - [ ] in `tests/e2e/*.test.e2e.js` if it requires interactions with a live Forgejo server (see also the [developer guide for JavaScript testing](https://codeberg.org/forgejo/forgejo/src/branch/forgejo/tests/e2e/README.md#end-to-end-tests)). ### Documentation - [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change. - [x] I did not document these changes and I do not expect someone else to do it. ### Release notes - [ ] I do not want this change to show in the release notes. - [x] I want the title to show in the release notes with a link to this pull request. - [ ] I want the content of the `release-notes/<pull request number>.md` to be be used for the release notes instead of the title. <!--start release-notes-assistant--> ## Release notes <!--URL:https://codeberg.org/forgejo/forgejo--> - User Interface features - [PR](https://codeberg.org/forgejo/forgejo/pulls/8759): <!--number 8759 --><!--line 0 --><!--description YWRkIHRhZyBsYWJlbCB0byBjb21taXQgbGlzdCB2aWV3-->add tag label to commit list view<!--description--> <!--end release-notes-assistant--> Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com> Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8759 Reviewed-by: Gusted <gusted@noreply.codeberg.org> Co-authored-by: pat-s <patrick.schratz@gmail.com> Co-committed-by: pat-s <patrick.schratz@gmail.com>
This commit is contained in:
parent
5294cff95f
commit
b6046c17a1
11 changed files with 201 additions and 4 deletions
|
@ -117,6 +117,8 @@ var migrations = []*Migration{
|
|||
NewMigration("Add `resolved_unix` column to `abuse_report` table", AddResolvedUnixToAbuseReport),
|
||||
// v38 -> v39
|
||||
NewMigration("Migrate `data` column of `secret` table to store keying material", MigrateActionSecretsToKeying),
|
||||
// v39 -> v40
|
||||
NewMigration("Add index for release sha1", AddIndexForReleaseSha1),
|
||||
}
|
||||
|
||||
// GetCurrentDBVersion returns the current Forgejo database version.
|
||||
|
|
13
models/forgejo_migrations/v40.go
Normal file
13
models/forgejo_migrations/v40.go
Normal file
|
@ -0,0 +1,13 @@
|
|||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package forgejo_migrations
|
||||
|
||||
import "xorm.io/xorm"
|
||||
|
||||
func AddIndexForReleaseSha1(x *xorm.Engine) error {
|
||||
type Release struct {
|
||||
Sha1 string `xorm:"INDEX VARCHAR(64)"`
|
||||
}
|
||||
return x.Sync(new(Release))
|
||||
}
|
|
@ -77,7 +77,7 @@ type Release struct {
|
|||
Target string
|
||||
TargetBehind string `xorm:"-"` // to handle non-existing or empty target
|
||||
Title string
|
||||
Sha1 string `xorm:"VARCHAR(64)"`
|
||||
Sha1 string `xorm:"INDEX VARCHAR(64)"`
|
||||
HideArchiveLinks bool `xorm:"NOT NULL DEFAULT false"`
|
||||
NumCommits int64
|
||||
NumCommitsBehind int64 `xorm:"-"`
|
||||
|
@ -618,3 +618,17 @@ func InsertReleases(ctx context.Context, rels ...*Release) error {
|
|||
|
||||
return committer.Commit()
|
||||
}
|
||||
|
||||
func FindTagsByCommitIDs(ctx context.Context, repoID int64, commitIDs ...string) (map[string][]*Release, error) {
|
||||
releases := make([]*Release, 0, len(commitIDs))
|
||||
if err := db.GetEngine(ctx).Where("repo_id=?", repoID).
|
||||
In("sha1", commitIDs).
|
||||
Find(&releases); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res := make(map[string][]*Release, len(releases))
|
||||
for _, r := range releases {
|
||||
res[r.Sha1] = append(res[r.Sha1], r)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
|
|
@ -49,3 +49,16 @@ func TestReleaseDisplayName(t *testing.T) {
|
|||
release.Title = "Title"
|
||||
assert.Equal(t, "Title", release.DisplayName())
|
||||
}
|
||||
|
||||
func Test_FindTagsByCommitIDs(t *testing.T) {
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
sha1Rels, err := FindTagsByCommitIDs(db.DefaultContext, 1, "65f1bf27bc3bf70f64657658635e66094edbcb4d")
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, sha1Rels, 1)
|
||||
rels := sha1Rels["65f1bf27bc3bf70f64657658635e66094edbcb4d"]
|
||||
assert.Len(t, rels, 3)
|
||||
assert.Equal(t, "v1.1", rels[0].TagName)
|
||||
assert.Equal(t, "delete-tag", rels[1].TagName)
|
||||
assert.Equal(t, "v1.0", rels[2].TagName)
|
||||
}
|
||||
|
|
|
@ -111,6 +111,7 @@
|
|||
"settings.visibility.description": "Profile visibility affects others' ability to access your non-private repositories. <a href=\"%s\" target=\"_blank\">Learn more</a>.",
|
||||
"avatar.constraints_hint": "Custom avatar may not exceed %[1]s in size or be larger than %[2]dx%[3]d pixels",
|
||||
"og.repo.summary_card.alt_description": "Summary card of repository %[1]s, described as: %[2]s",
|
||||
"repo.commit.load_tags_failed": "Load tags failed because of internal error",
|
||||
"compare.branches.title": "Compare branches",
|
||||
"meta.last_line": "Thank you for translating Forgejo! This line isn't seen by the users but it serves other purposes in the translation management. You can place a fun fact in the translation instead of translating it."
|
||||
}
|
||||
|
|
|
@ -85,6 +85,17 @@ func Commits(ctx *context.Context) {
|
|||
}
|
||||
ctx.Data["Commits"] = git_model.ParseCommitsWithStatus(ctx, commits, ctx.Repo.Repository)
|
||||
|
||||
commitIDs := make([]string, 0, len(commits))
|
||||
for _, c := range commits {
|
||||
commitIDs = append(commitIDs, c.ID.String())
|
||||
}
|
||||
commitTagsMap, err := repo_model.FindTagsByCommitIDs(ctx, ctx.Repo.Repository.ID, commitIDs...)
|
||||
if err != nil {
|
||||
log.Error("FindTagsByCommitIDs: %v", err)
|
||||
ctx.Flash.Error(ctx.Tr("repo.commit.load_tags_failed"))
|
||||
} else {
|
||||
ctx.Data["CommitTagsMap"] = commitTagsMap
|
||||
}
|
||||
ctx.Data["Username"] = ctx.Repo.Owner.Name
|
||||
ctx.Data["Reponame"] = ctx.Repo.Repository.Name
|
||||
ctx.Data["CommitCount"] = commitsCount
|
||||
|
|
|
@ -59,6 +59,14 @@
|
|||
<button class="ui button js-toggle-commit-body ellipsis-button" aria-expanded="false">...</button>
|
||||
{{end}}
|
||||
{{template "repo/commit_statuses" dict "Status" .Status "Statuses" .Statuses}}
|
||||
{{if $.CommitTagsMap}}
|
||||
{{$tags := index $.CommitTagsMap .ID.String}}
|
||||
{{if $tags}}
|
||||
{{range $tags}}
|
||||
{{- template "repo/tag/name" dict "RepoLink" $.Repository.Link "TagName" .TagName "IsRelease" (not .IsTag) -}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{if IsMultilineCommitMessage .Message}}
|
||||
<pre class="commit-body tw-hidden">{{RenderCommitBody $.Context .Message ($.Repository.ComposeMetas ctx)}}</pre>
|
||||
{{end}}
|
||||
|
|
|
@ -27,9 +27,7 @@
|
|||
</a>
|
||||
{{end}}
|
||||
{{else if eq $refGroup "tags"}}
|
||||
<a class="ui basic button" href="{{$.RepoLink}}/src/tag/{{.ShortName|PathEscape}}">
|
||||
{{svg "octicon-tag"}} {{.ShortName}}
|
||||
</a>
|
||||
{{- template "repo/tag/name" dict "RepoLink" $.Repository.Link "TagName" .ShortName -}}
|
||||
{{else if eq $refGroup "remotes"}}
|
||||
<a class="ui basic button" href="{{$.RepoLink}}/src/commit/{{$commit.Rev|PathEscape}}">
|
||||
{{svg "octicon-cross-reference"}} {{.ShortName}}
|
||||
|
|
3
templates/repo/tag/name.tmpl
Normal file
3
templates/repo/tag/name.tmpl
Normal file
|
@ -0,0 +1,3 @@
|
|||
<a class="ui label basic tw-p-1 primary{{if .IsRelease}} primary{{end}}" href="{{.RepoLink}}/src/tag/{{.TagName|PathEscape}}">
|
||||
{{svg "octicon-tag"}} {{.TagName}}
|
||||
</a>
|
73
tests/integration/repo_commits_tags_test.go
Normal file
73
tests/integration/repo_commits_tags_test.go
Normal file
|
@ -0,0 +1,73 @@
|
|||
// Copyright 2025 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package integration
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"forgejo.org/tests"
|
||||
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// TestRepoCommitsWithTags tests that tags are displayed inline with commit messages
|
||||
// in the commits list, and not in a separate column
|
||||
func TestRepoCommitsWithTags(t *testing.T) {
|
||||
defer tests.PrepareTestEnv(t)()
|
||||
|
||||
session := loginUser(t, "user2")
|
||||
req := NewRequest(t, "GET", "/user2/repo1/commits/branch/master")
|
||||
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||
|
||||
doc := NewHTMLParser(t, resp.Body)
|
||||
|
||||
// Find the commit with SHA 65f1bf27bc3bf70f64657658635e66094edbcb4d
|
||||
// This commit should have tags v1.1
|
||||
commitRow := doc.doc.Find(`#commits-table tbody tr`).FilterFunction(func(i int, s *goquery.Selection) bool {
|
||||
shaLink := s.Find("td.sha a")
|
||||
href, _ := shaLink.Attr("href")
|
||||
return strings.Contains(href, "65f1bf27bc3bf70f64657658635e66094edbcb4d")
|
||||
})
|
||||
|
||||
// 1. Check for tag labels within the message cell
|
||||
messageCell := commitRow.Find("td.message")
|
||||
tagLabels := messageCell.Find("a.ui.label.basic")
|
||||
assert.GreaterOrEqual(t, tagLabels.Length(), 1, "Should find tag label")
|
||||
|
||||
// 2. tag has proper HTML attr and links to the correct tag
|
||||
tagFound := false
|
||||
tagLabels.Each(func(i int, s *goquery.Selection) {
|
||||
if strings.Contains(s.Text(), "v1.1") {
|
||||
tagFound = true
|
||||
href, exists := s.Attr("href")
|
||||
assert.True(t, exists, "Tag should have href")
|
||||
assert.Contains(t, href, "/src/tag/v1.1", "Tag link should point to tag page")
|
||||
assert.Equal(t, 1, s.Find("svg.octicon-tag").Length(), "Tag should have octicon-tag icon")
|
||||
}
|
||||
})
|
||||
assert.True(t, tagFound, "Should find v1.1 tag")
|
||||
|
||||
// 3. tags appear after the commit messsage and status indicators
|
||||
messageHTML, _ := messageCell.Html()
|
||||
messageWrapperPos := strings.Index(messageHTML, "message-wrapper")
|
||||
ellipsisButtonPos := strings.Index(messageHTML, "ellipsis-button")
|
||||
commitStatusPos := strings.Index(messageHTML, "commit-status")
|
||||
tagLabelPos := strings.Index(messageHTML, "ui label basic")
|
||||
|
||||
// 4. Tags should appear after the message wrapper
|
||||
assert.Greater(t, tagLabelPos, messageWrapperPos, "Tags should appear after message wrapper")
|
||||
|
||||
// 5. If ellipsis button exists, tags should appear after that one
|
||||
if ellipsisButtonPos > 0 {
|
||||
assert.Greater(t, tagLabelPos, ellipsisButtonPos, "Tags should appear after ellipsis button")
|
||||
}
|
||||
|
||||
// 6. If commit status exists, tags should appear after that one
|
||||
if commitStatusPos > 0 {
|
||||
assert.Greater(t, tagLabelPos, commitStatusPos, "Tags should appear after commit status")
|
||||
}
|
||||
}
|
61
tests/integration/repo_commits_template_test.go
Normal file
61
tests/integration/repo_commits_template_test.go
Normal file
|
@ -0,0 +1,61 @@
|
|||
// Copyright 2025 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package integration
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"forgejo.org/tests"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// TestRepoCommitsTemplateVariables ensures that template variables in commits_list.tmpl are correctly referenced
|
||||
func TestRepoCommitsTemplateVariables(t *testing.T) {
|
||||
defer tests.PrepareTestEnv(t)()
|
||||
|
||||
session := loginUser(t, "user2")
|
||||
|
||||
// Test the main commits page
|
||||
req := NewRequest(t, "GET", "/user2/repo1/commits/branch/master")
|
||||
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||
assert.Equal(t, http.StatusOK, resp.Code, "Template should render without errors")
|
||||
|
||||
doc := NewHTMLParser(t, resp.Body)
|
||||
|
||||
// 1. Repository.Link is used in tag template
|
||||
tagLinks := doc.doc.Find("a.ui.label.basic[href*='/src/tag/']")
|
||||
if tagLinks.Length() > 0 {
|
||||
href, _ := tagLinks.First().Attr("href")
|
||||
assert.Contains(t, href, "/user2/repo1/src/tag/", "Repository link should be correctly rendered in tag URLs")
|
||||
}
|
||||
|
||||
// 2. Repository.ObjectFormatName is used in the SHA column header
|
||||
shaHeader := doc.doc.Find("#commits-table thead tr th.sha")
|
||||
assert.Equal(t, 1, shaHeader.Length(), "SHA column header should exist")
|
||||
headerText := strings.TrimSpace(shaHeader.Text())
|
||||
assert.NotEmpty(t, headerText, "SHA column header should have text (ObjectFormatName)")
|
||||
// Should be uppercase SHA1 or SHA256 depending on the repository format
|
||||
assert.True(t, headerText == "SHA1" || headerText == "SHA256", "ObjectFormatName should be rendered correctly, got: %s", headerText)
|
||||
|
||||
// 3. Repository.ComposeMetas is used for rendering commit messages
|
||||
commitMessages := doc.doc.Find("#commits-table tbody tr td.message .commit-summary")
|
||||
assert.Positive(t, commitMessages.Length(), "Should have commit messages rendered")
|
||||
|
||||
// 4. RepoLink variable is used throughout
|
||||
commitLinks := doc.doc.Find("#commits-table tbody tr td.sha a[href*='/commit/']")
|
||||
assert.Positive(t, commitLinks.Length(), "Should have commit links")
|
||||
firstCommitLink, _ := commitLinks.First().Attr("href")
|
||||
assert.Contains(t, firstCommitLink, "/user2/repo1/commit/", "RepoLink should be correctly used in commit URLs")
|
||||
|
||||
// 5. CommitTagsMap is used for tag rendering
|
||||
// If $.CommitTagsMap is mistyped, the template would fail with a 500 error
|
||||
// (for detailed tag rendering tests see repo_commits_tags_test.go)
|
||||
tagLabels := doc.doc.Find("#commits-table tbody tr td.message a.ui.label.basic")
|
||||
if tagLabels.Length() > 0 {
|
||||
assert.NotContains(t, tagLabels.First().Text(), "{{", "Tags should be properly rendered without template syntax")
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue