feat: always publish the link to the commit status (#8177)

See https://codeberg.org/forgejo/forgejo/pulls/4801#issuecomment-5094525 and #8152 for more context.

The current implementation is limited to self-hosted actions and buggy as soon as multiple repos are involved, like for the homepage (because each permission must be fetched individually).

Ideally this feature should work for all kind of status (with some setting indicating which collaborator can access with status). Probably inside the `git_model.ParseCommitsWithStatus` function.

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8177
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
Co-authored-by: oliverpool <git@olivier.pfad.fr>
Co-committed-by: oliverpool <git@olivier.pfad.fr>
This commit is contained in:
oliverpool 2025-06-13 12:41:34 +02:00 committed by Earl Warren
parent 402a85a9b6
commit 09699c1506
13 changed files with 21 additions and 153 deletions

View file

@ -179,25 +179,6 @@ func (status *CommitStatus) LocaleString(lang translation.Locale) string {
return lang.TrString("repo.commitstatus." + status.State.String()) return lang.TrString("repo.commitstatus." + status.State.String())
} }
// HideActionsURL set `TargetURL` to an empty string if the status comes from Gitea Actions
func (status *CommitStatus) HideActionsURL(ctx context.Context) {
if status.RepoID == 0 {
return
}
if status.Repo == nil {
if err := status.loadRepository(ctx); err != nil {
log.Error("loadRepository: %v", err)
return
}
}
prefix := fmt.Sprintf("%s/actions", status.Repo.Link())
if strings.HasPrefix(status.TargetURL, prefix) {
status.TargetURL = ""
}
}
// CalcCommitStatus returns commit status state via some status, the commit statues should order by id desc // CalcCommitStatus returns commit status state via some status, the commit statues should order by id desc
func CalcCommitStatus(statuses []*CommitStatus) *CommitStatus { func CalcCommitStatus(statuses []*CommitStatus) *CommitStatus {
if len(statuses) == 0 { if len(statuses) == 0 {
@ -453,11 +434,19 @@ type SignCommitWithStatuses struct {
*asymkey_model.SignCommit *asymkey_model.SignCommit
} }
// ParseCommitsWithStatus checks commits latest statuses and calculates its worst status state // ParseCommitsWithStatus converts git commits into SignCommitWithStatuses (checks signature and calculates its worst status state)
func ParseCommitsWithStatus(ctx context.Context, oldCommits []*asymkey_model.SignCommit, repo *repo_model.Repository) []*SignCommitWithStatuses { func ParseCommitsWithStatus(ctx context.Context, commits []*git.Commit, repo *repo_model.Repository) []*SignCommitWithStatuses {
newCommits := make([]*SignCommitWithStatuses, 0, len(oldCommits)) commitsWithSignature := asymkey_model.ParseCommitsWithSignature(
ctx,
user_model.ValidateCommitsWithEmails(ctx, commits),
repo.GetTrustModel(),
func(user *user_model.User) (bool, error) {
return repo_model.IsOwnerMemberCollaborator(ctx, repo, user.ID)
},
)
for _, c := range oldCommits { commitsWithStatus := make([]*SignCommitWithStatuses, 0, len(commitsWithSignature))
for _, c := range commitsWithSignature {
commit := &SignCommitWithStatuses{ commit := &SignCommitWithStatuses{
SignCommit: c, SignCommit: c,
} }
@ -469,43 +458,12 @@ func ParseCommitsWithStatus(ctx context.Context, oldCommits []*asymkey_model.Sig
commit.Status = CalcCommitStatus(statuses) commit.Status = CalcCommitStatus(statuses)
} }
newCommits = append(newCommits, commit) commitsWithStatus = append(commitsWithStatus, commit)
} }
return newCommits return commitsWithStatus
} }
// hashCommitStatusContext hash context // hashCommitStatusContext hash context
func hashCommitStatusContext(context string) string { func hashCommitStatusContext(context string) string {
return fmt.Sprintf("%x", sha1.Sum([]byte(context))) return fmt.Sprintf("%x", sha1.Sum([]byte(context)))
} }
// ConvertFromGitCommit converts git commits into SignCommitWithStatuses
func ConvertFromGitCommit(ctx context.Context, commits []*git.Commit, repo *repo_model.Repository) []*SignCommitWithStatuses {
return ParseCommitsWithStatus(ctx,
asymkey_model.ParseCommitsWithSignature(
ctx,
user_model.ValidateCommitsWithEmails(ctx, commits),
repo.GetTrustModel(),
func(user *user_model.User) (bool, error) {
return repo_model.IsOwnerMemberCollaborator(ctx, repo, user.ID)
},
),
repo,
)
}
// CommitStatusesHideActionsURL hide Gitea Actions urls
func CommitStatusesHideActionsURL(ctx context.Context, statuses []*CommitStatus) {
idToRepos := make(map[int64]*repo_model.Repository)
for _, status := range statuses {
if status == nil {
continue
}
if status.Repo == nil {
status.Repo = idToRepos[status.RepoID]
}
status.HideActionsURL(ctx)
idToRepos[status.RepoID] = status.Repo
}
}

View file

@ -4,11 +4,9 @@
package git_test package git_test
import ( import (
"fmt"
"testing" "testing"
"time" "time"
actions_model "forgejo.org/models/actions"
"forgejo.org/models/db" "forgejo.org/models/db"
git_model "forgejo.org/models/git" git_model "forgejo.org/models/git"
repo_model "forgejo.org/models/repo" repo_model "forgejo.org/models/repo"
@ -246,26 +244,3 @@ func TestFindRepoRecentCommitStatusContexts(t *testing.T) {
assert.Equal(t, "compliance/lint-backend", contexts[0]) assert.Equal(t, "compliance/lint-backend", contexts[0])
} }
} }
func TestCommitStatusesHideActionsURL(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
run := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{ID: 791, RepoID: repo.ID})
require.NoError(t, run.LoadAttributes(db.DefaultContext))
statuses := []*git_model.CommitStatus{
{
RepoID: repo.ID,
TargetURL: fmt.Sprintf("%s/jobs/%d", run.Link(), run.Index),
},
{
RepoID: repo.ID,
TargetURL: "https://mycicd.org/1",
},
}
git_model.CommitStatusesHideActionsURL(db.DefaultContext, statuses)
assert.Empty(t, statuses[0].TargetURL)
assert.Equal(t, "https://mycicd.org/1", statuses[1].TargetURL)
}

View file

@ -802,7 +802,7 @@ func (c *Comment) LoadPushCommits(ctx context.Context) (err error) {
} }
defer closer.Close() defer closer.Close()
c.Commits = git_model.ConvertFromGitCommit(ctx, gitRepo.GetCommitsFromIDs(data.CommitIDs), c.Issue.Repo) c.Commits = git_model.ParseCommitsWithStatus(ctx, gitRepo.GetCommitsFromIDs(data.CommitIDs), c.Issue.Repo)
c.CommitsNum = int64(len(c.Commits)) c.CommitsNum = int64(len(c.Commits))
} }

View file

@ -70,11 +70,6 @@ func Branches(ctx *context.Context) {
ctx.ServerError("LoadBranches", err) ctx.ServerError("LoadBranches", err)
return return
} }
if !ctx.Repo.CanRead(unit.TypeActions) {
for key := range commitStatuses {
git_model.CommitStatusesHideActionsURL(ctx, commitStatuses[key])
}
}
commitStatus := make(map[string]*git_model.CommitStatus) commitStatus := make(map[string]*git_model.CommitStatus)
for commitID, cs := range commitStatuses { for commitID, cs := range commitStatuses {

View file

@ -16,7 +16,6 @@ import (
"forgejo.org/models/db" "forgejo.org/models/db"
git_model "forgejo.org/models/git" git_model "forgejo.org/models/git"
repo_model "forgejo.org/models/repo" repo_model "forgejo.org/models/repo"
unit_model "forgejo.org/models/unit"
user_model "forgejo.org/models/user" user_model "forgejo.org/models/user"
"forgejo.org/modules/base" "forgejo.org/modules/base"
"forgejo.org/modules/charset" "forgejo.org/modules/charset"
@ -84,7 +83,7 @@ func Commits(ctx *context.Context) {
ctx.ServerError("CommitsByRange", err) ctx.ServerError("CommitsByRange", err)
return return
} }
ctx.Data["Commits"] = processGitCommits(ctx, commits) ctx.Data["Commits"] = git_model.ParseCommitsWithStatus(ctx, commits, ctx.Repo.Repository)
ctx.Data["Username"] = ctx.Repo.Owner.Name ctx.Data["Username"] = ctx.Repo.Owner.Name
ctx.Data["Reponame"] = ctx.Repo.Repository.Name ctx.Data["Reponame"] = ctx.Repo.Repository.Name
@ -202,7 +201,7 @@ func SearchCommits(ctx *context.Context) {
return return
} }
ctx.Data["CommitCount"] = len(commits) ctx.Data["CommitCount"] = len(commits)
ctx.Data["Commits"] = processGitCommits(ctx, commits) ctx.Data["Commits"] = git_model.ParseCommitsWithStatus(ctx, commits, ctx.Repo.Repository)
ctx.Data["Keyword"] = query ctx.Data["Keyword"] = query
if all { if all {
@ -267,7 +266,7 @@ func FileHistory(ctx *context.Context) {
} }
} }
ctx.Data["Commits"] = processGitCommits(ctx, commits) ctx.Data["Commits"] = git_model.ParseCommitsWithStatus(ctx, commits, ctx.Repo.Repository)
ctx.Data["Username"] = ctx.Repo.Owner.Name ctx.Data["Username"] = ctx.Repo.Owner.Name
ctx.Data["Reponame"] = ctx.Repo.Repository.Name ctx.Data["Reponame"] = ctx.Repo.Repository.Name
@ -375,9 +374,6 @@ func Diff(ctx *context.Context) {
if err != nil { if err != nil {
log.Error("GetLatestCommitStatus: %v", err) log.Error("GetLatestCommitStatus: %v", err)
} }
if !ctx.Repo.CanRead(unit_model.TypeActions) {
git_model.CommitStatusesHideActionsURL(ctx, statuses)
}
ctx.Data["CommitStatus"] = git_model.CalcCommitStatus(statuses) ctx.Data["CommitStatus"] = git_model.CalcCommitStatus(statuses)
ctx.Data["CommitStatuses"] = statuses ctx.Data["CommitStatuses"] = statuses
@ -456,20 +452,6 @@ func RawDiff(ctx *context.Context) {
} }
} }
func processGitCommits(ctx *context.Context, gitCommits []*git.Commit) []*git_model.SignCommitWithStatuses {
commits := git_model.ConvertFromGitCommit(ctx, gitCommits, ctx.Repo.Repository)
if !ctx.Repo.CanRead(unit_model.TypeActions) {
for _, commit := range commits {
if commit.Status == nil {
continue
}
commit.Status.HideActionsURL(ctx)
git_model.CommitStatusesHideActionsURL(ctx, commit.Statuses)
}
}
return commits
}
func SetCommitNotes(ctx *context.Context) { func SetCommitNotes(ctx *context.Context) {
form := web.GetForm(ctx).(*forms.CommitNotesForm) form := web.GetForm(ctx).(*forms.CommitNotesForm)

View file

@ -654,7 +654,7 @@ func PrepareCompareDiff(
return false return false
} }
commits := processGitCommits(ctx, ci.CompareInfo.Commits) commits := git_model.ParseCommitsWithStatus(ctx, ci.CompareInfo.Commits, ctx.Repo.Repository)
ctx.Data["Commits"] = commits ctx.Data["Commits"] = commits
ctx.Data["CommitCount"] = len(commits) ctx.Data["CommitCount"] = len(commits)

View file

@ -348,11 +348,6 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt
ctx.ServerError("GetIssuesAllCommitStatus", err) ctx.ServerError("GetIssuesAllCommitStatus", err)
return return
} }
if !ctx.Repo.CanRead(unit.TypeActions) {
for key := range commitStatuses {
git_model.CommitStatusesHideActionsURL(ctx, commitStatuses[key])
}
}
if err := issues.LoadAttributes(ctx); err != nil { if err := issues.LoadAttributes(ctx); err != nil {
ctx.ServerError("issues.LoadAttributes", err) ctx.ServerError("issues.LoadAttributes", err)
@ -1799,15 +1794,6 @@ func ViewIssue(ctx *context.Context) {
ctx.ServerError("LoadPushCommits", err) ctx.ServerError("LoadPushCommits", err)
return return
} }
if !ctx.Repo.CanRead(unit.TypeActions) {
for _, commit := range comment.Commits {
if commit.Status == nil {
continue
}
commit.Status.HideActionsURL(ctx)
git_model.CommitStatusesHideActionsURL(ctx, commit.Statuses)
}
}
} else if comment.Type == issues_model.CommentTypeAddTimeManual || } else if comment.Type == issues_model.CommentTypeAddTimeManual ||
comment.Type == issues_model.CommentTypeStopTracking || comment.Type == issues_model.CommentTypeStopTracking ||
comment.Type == issues_model.CommentTypeDeleteTimeManual { comment.Type == issues_model.CommentTypeDeleteTimeManual {

View file

@ -515,9 +515,6 @@ func PrepareMergedViewPullInfo(ctx *context.Context, issue *issues_model.Issue)
ctx.ServerError("GetLatestCommitStatus", err) ctx.ServerError("GetLatestCommitStatus", err)
return nil return nil
} }
if !ctx.Repo.CanRead(unit.TypeActions) {
git_model.CommitStatusesHideActionsURL(ctx, commitStatuses)
}
if len(commitStatuses) != 0 { if len(commitStatuses) != 0 {
ctx.Data["LatestCommitStatuses"] = commitStatuses ctx.Data["LatestCommitStatuses"] = commitStatuses
@ -581,9 +578,6 @@ func PrepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C
ctx.ServerError("GetLatestCommitStatus", err) ctx.ServerError("GetLatestCommitStatus", err)
return nil return nil
} }
if !ctx.Repo.CanRead(unit.TypeActions) {
git_model.CommitStatusesHideActionsURL(ctx, commitStatuses)
}
if len(commitStatuses) > 0 { if len(commitStatuses) > 0 {
ctx.Data["LatestCommitStatuses"] = commitStatuses ctx.Data["LatestCommitStatuses"] = commitStatuses
@ -677,9 +671,6 @@ func PrepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C
ctx.ServerError("GetLatestCommitStatus", err) ctx.ServerError("GetLatestCommitStatus", err)
return nil return nil
} }
if !ctx.Repo.CanRead(unit.TypeActions) {
git_model.CommitStatusesHideActionsURL(ctx, commitStatuses)
}
if len(commitStatuses) > 0 { if len(commitStatuses) > 0 {
ctx.Data["LatestCommitStatuses"] = commitStatuses ctx.Data["LatestCommitStatuses"] = commitStatuses
@ -847,7 +838,7 @@ func ViewPullCommits(ctx *context.Context) {
ctx.Data["Username"] = ctx.Repo.Owner.Name ctx.Data["Username"] = ctx.Repo.Owner.Name
ctx.Data["Reponame"] = ctx.Repo.Repository.Name ctx.Data["Reponame"] = ctx.Repo.Repository.Name
commits := processGitCommits(ctx, prInfo.Commits) commits := git_model.ParseCommitsWithStatus(ctx, prInfo.Commits, ctx.Repo.Repository)
ctx.Data["Commits"] = commits ctx.Data["Commits"] = commits
ctx.Data["CommitCount"] = len(commits) ctx.Data["CommitCount"] = len(commits)

View file

@ -693,9 +693,6 @@ func SearchRepo(ctx *context.Context) {
ctx.JSON(http.StatusInternalServerError, nil) ctx.JSON(http.StatusInternalServerError, nil)
return return
} }
if !ctx.Repo.CanRead(unit.TypeActions) {
git_model.CommitStatusesHideActionsURL(ctx, latestCommitStatuses)
}
results := make([]*repo_service.WebSearchRepository, len(repos)) results := make([]*repo_service.WebSearchRepository, len(repos))
for i, repo := range repos { for i, repo := range repos {

View file

@ -369,9 +369,6 @@ func loadLatestCommitData(ctx *context.Context, latestCommit *git.Commit) bool {
if err != nil { if err != nil {
log.Error("GetLatestCommitStatus: %v", err) log.Error("GetLatestCommitStatus: %v", err)
} }
if !ctx.Repo.CanRead(unit_model.TypeActions) {
git_model.CommitStatusesHideActionsURL(ctx, statuses)
}
ctx.Data["LatestCommitStatus"] = git_model.CalcCommitStatus(statuses) ctx.Data["LatestCommitStatus"] = git_model.CalcCommitStatus(statuses)
ctx.Data["LatestCommitStatuses"] = statuses ctx.Data["LatestCommitStatuses"] = statuses

View file

@ -393,7 +393,7 @@ func renderRevisionPage(ctx *context.Context) (*git.Repository, *git.TreeEntry)
ctx.ServerError("CommitsByFileAndRange", err) ctx.ServerError("CommitsByFileAndRange", err)
return nil, nil return nil, nil
} }
ctx.Data["Commits"] = git_model.ConvertFromGitCommit(ctx, commitsHistory, ctx.Repo.Repository) ctx.Data["Commits"] = git_model.ParseCommitsWithStatus(ctx, commitsHistory, ctx.Repo.Repository)
pager := context.NewPagination(int(commitsCount), setting.Git.CommitsRangeSize, page, 5) pager := context.NewPagination(int(commitsCount), setting.Git.CommitsRangeSize, page, 5)
pager.SetDefaultParams(ctx) pager.SetDefaultParams(ctx)

View file

@ -16,7 +16,6 @@ import (
activities_model "forgejo.org/models/activities" activities_model "forgejo.org/models/activities"
asymkey_model "forgejo.org/models/asymkey" asymkey_model "forgejo.org/models/asymkey"
"forgejo.org/models/db" "forgejo.org/models/db"
git_model "forgejo.org/models/git"
issues_model "forgejo.org/models/issues" issues_model "forgejo.org/models/issues"
"forgejo.org/models/organization" "forgejo.org/models/organization"
repo_model "forgejo.org/models/repo" repo_model "forgejo.org/models/repo"
@ -611,11 +610,6 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
ctx.ServerError("GetIssuesLastCommitStatus", err) ctx.ServerError("GetIssuesLastCommitStatus", err)
return return
} }
if !ctx.Repo.CanRead(unit.TypeActions) {
for key := range commitStatuses {
git_model.CommitStatusesHideActionsURL(ctx, commitStatuses[key])
}
}
// ------------------------------- // -------------------------------
// Fill stats to post to ctx.Data. // Fill stats to post to ctx.Data.

View file

@ -13,10 +13,8 @@ import (
activities_model "forgejo.org/models/activities" activities_model "forgejo.org/models/activities"
"forgejo.org/models/db" "forgejo.org/models/db"
git_model "forgejo.org/models/git"
issues_model "forgejo.org/models/issues" issues_model "forgejo.org/models/issues"
repo_model "forgejo.org/models/repo" repo_model "forgejo.org/models/repo"
"forgejo.org/models/unit"
"forgejo.org/modules/base" "forgejo.org/modules/base"
"forgejo.org/modules/log" "forgejo.org/modules/log"
"forgejo.org/modules/optional" "forgejo.org/modules/optional"
@ -311,11 +309,6 @@ func NotificationSubscriptions(ctx *context.Context) {
ctx.ServerError("GetIssuesAllCommitStatus", err) ctx.ServerError("GetIssuesAllCommitStatus", err)
return return
} }
if !ctx.Repo.CanRead(unit.TypeActions) {
for key := range commitStatuses {
git_model.CommitStatusesHideActionsURL(ctx, commitStatuses[key])
}
}
ctx.Data["CommitLastStatus"] = lastStatus ctx.Data["CommitLastStatus"] = lastStatus
ctx.Data["CommitStatuses"] = commitStatuses ctx.Data["CommitStatuses"] = commitStatuses
ctx.Data["Issues"] = issues ctx.Data["Issues"] = issues