Show if commit is signed in activity feed and unify sha box (#6933)
Old activities are shown like before, new commits are displayed like commits in e.g. the commits list. _(Second commit)_ | New signed commits | Old (signed) commits | |:--:|:--:| |  |  | Additionally the sha box was moved in an own component to unify the usage. _(First commit)_ Closes #1824 <!--start release-notes-assistant--> ## Release notes <!--URL:https://codeberg.org/forgejo/forgejo--> - User Interface features - [PR](https://codeberg.org/forgejo/forgejo/pulls/6933): <!--number 6933 --><!--line 0 --><!--description U2hvdyBpZiBjb21taXQgaXMgdmVyaWZpZWQgaW4gYWN0aXZpdHkgZmVlZCBvZiBhbiB1c2VyIG9yIGFuIG9yZ2FuaXphdGlvbiBmb3IgbmV3IGFjdGl2aXR5-->Show if commit is verified in activity feed of an user or an organization for new activity<!--description--> <!--end release-notes-assistant--> Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6933 Reviewed-by: Gusted <gusted@noreply.codeberg.org> Co-authored-by: Beowulf <beowulf@beocode.eu> Co-committed-by: Beowulf <beowulf@beocode.eu>
This commit is contained in:
		
					parent
					
						
							
								82477cb55c
							
						
					
				
			
			
				commit
				
					
						37d566bdb0
					
				
			
		
					 22 changed files with 344 additions and 89 deletions
				
			
		| 
						 | 
				
			
			@ -1,5 +1,6 @@
 | 
			
		|||
// Copyright 2014 The Gogs Authors. All rights reserved.
 | 
			
		||||
// Copyright 2019 The Gitea Authors. All rights reserved.
 | 
			
		||||
// Copyright 2025 The Forgejo Authors. All rights reserved.
 | 
			
		||||
// SPDX-License-Identifier: MIT
 | 
			
		||||
 | 
			
		||||
package activities
 | 
			
		||||
| 
						 | 
				
			
			@ -247,6 +248,12 @@ func (a *Action) GetActDisplayNameTitle(ctx context.Context) string {
 | 
			
		|||
	return a.GetActFullName(ctx)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetRepo returns the repository of the action.
 | 
			
		||||
func (a *Action) GetRepo(ctx context.Context) *repo_model.Repository {
 | 
			
		||||
	a.loadRepo(ctx)
 | 
			
		||||
	return a.Repo
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetRepoUserName returns the name of the action repository owner.
 | 
			
		||||
func (a *Action) GetRepoUserName(ctx context.Context) string {
 | 
			
		||||
	a.loadRepo(ctx)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -44,6 +44,8 @@ const (
 | 
			
		|||
	BadDefaultSignature = "gpg.error.probable_bad_default_signature"
 | 
			
		||||
	// NoKeyFound is used as the reason when no key can be found to verify the signature.
 | 
			
		||||
	NoKeyFound = "gpg.error.no_gpg_keys_found"
 | 
			
		||||
	// NotSigned is used as the reason when the commit is not signed.
 | 
			
		||||
	NotSigned = "gpg.error.not_signed_commit"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type GitObject struct {
 | 
			
		||||
| 
						 | 
				
			
			@ -101,8 +103,8 @@ func ParseObjectWithSignature(ctx context.Context, c *GitObject) *ObjectVerifica
 | 
			
		|||
	if c.Signature == nil {
 | 
			
		||||
		return &ObjectVerification{
 | 
			
		||||
			CommittingUser: committer,
 | 
			
		||||
			Verified:       false,                         // Default value
 | 
			
		||||
			Reason:         "gpg.error.not_signed_commit", // Default value
 | 
			
		||||
			Verified:       false,     // Default value
 | 
			
		||||
			Reason:         NotSigned, // Default value
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,5 @@
 | 
			
		|||
// Copyright 2019 The Gitea Authors. All rights reserved.
 | 
			
		||||
// Copyright 2025 The Forgejo Authors. All rights reserved.
 | 
			
		||||
// SPDX-License-Identifier: MIT
 | 
			
		||||
 | 
			
		||||
package repository
 | 
			
		||||
| 
						 | 
				
			
			@ -9,6 +10,7 @@ import (
 | 
			
		|||
	"net/url"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	asymkey_model "forgejo.org/models/asymkey"
 | 
			
		||||
	"forgejo.org/models/avatars"
 | 
			
		||||
	user_model "forgejo.org/models/user"
 | 
			
		||||
	"forgejo.org/modules/cache"
 | 
			
		||||
| 
						 | 
				
			
			@ -26,6 +28,8 @@ type PushCommit struct {
 | 
			
		|||
	AuthorName     string
 | 
			
		||||
	CommitterEmail string
 | 
			
		||||
	CommitterName  string
 | 
			
		||||
	Signature      *git.ObjectSignature
 | 
			
		||||
	Verification   *asymkey_model.ObjectVerification
 | 
			
		||||
	Timestamp      time.Time
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -145,6 +149,32 @@ func (pc *PushCommits) AvatarLink(ctx context.Context, email string) string {
 | 
			
		|||
	return v
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PushCommitToCommit transforms a PushCommit to a git.Commit type on a best effort basis.
 | 
			
		||||
//
 | 
			
		||||
// Attention: Converting a Commit to a PushCommit and converting back to a Commit will not result in an identical object!
 | 
			
		||||
func PushCommitToCommit(commit *PushCommit) (*git.Commit, error) {
 | 
			
		||||
	id, err := git.NewIDFromString(commit.Sha1)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &git.Commit{
 | 
			
		||||
		ID: id,
 | 
			
		||||
		Author: &git.Signature{
 | 
			
		||||
			Name:  commit.AuthorName,
 | 
			
		||||
			Email: commit.AuthorEmail,
 | 
			
		||||
			When:  commit.Timestamp,
 | 
			
		||||
		},
 | 
			
		||||
		Committer: &git.Signature{
 | 
			
		||||
			Name:  commit.CommitterName,
 | 
			
		||||
			Email: commit.CommitterEmail,
 | 
			
		||||
			When:  commit.Timestamp,
 | 
			
		||||
		},
 | 
			
		||||
		CommitMessage: commit.Message,
 | 
			
		||||
		Signature:     commit.Signature,
 | 
			
		||||
		Parents:       []git.ObjectID{},
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CommitToPushCommit transforms a git.Commit to PushCommit type.
 | 
			
		||||
func CommitToPushCommit(commit *git.Commit) *PushCommit {
 | 
			
		||||
	return &PushCommit{
 | 
			
		||||
| 
						 | 
				
			
			@ -154,6 +184,7 @@ func CommitToPushCommit(commit *git.Commit) *PushCommit {
 | 
			
		|||
		AuthorName:     commit.Author.Name,
 | 
			
		||||
		CommitterEmail: commit.Committer.Email,
 | 
			
		||||
		CommitterName:  commit.Committer.Name,
 | 
			
		||||
		Signature:      commit.Signature,
 | 
			
		||||
		Timestamp:      commit.Author.When,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,5 @@
 | 
			
		|||
// Copyright 2019 The Gitea Authors. All rights reserved.
 | 
			
		||||
// Copyright 2025 The Forgejo Authors. All rights reserved.
 | 
			
		||||
// SPDX-License-Identifier: MIT
 | 
			
		||||
 | 
			
		||||
package repository
 | 
			
		||||
| 
						 | 
				
			
			@ -133,6 +134,50 @@ func TestPushCommits_AvatarLink(t *testing.T) {
 | 
			
		|||
		pushCommits.AvatarLink(db.DefaultContext, "nonexistent@example.com"))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestPushCommitToCommit(t *testing.T) {
 | 
			
		||||
	now := time.Now()
 | 
			
		||||
	sig := &git.Signature{
 | 
			
		||||
		Email: "example@example.com",
 | 
			
		||||
		Name:  "John Doe",
 | 
			
		||||
		When:  now,
 | 
			
		||||
	}
 | 
			
		||||
	const hexString = "0123456789abcdef0123456789abcdef01234567"
 | 
			
		||||
	sha1, err := git.NewIDFromString(hexString)
 | 
			
		||||
	require.NoError(t, err)
 | 
			
		||||
	commit, err := PushCommitToCommit(&PushCommit{
 | 
			
		||||
		Sha1:           sha1.String(),
 | 
			
		||||
		Message:        "Commit Message",
 | 
			
		||||
		AuthorEmail:    "example@example.com",
 | 
			
		||||
		AuthorName:     "John Doe",
 | 
			
		||||
		CommitterEmail: "example@example.com",
 | 
			
		||||
		CommitterName:  "John Doe",
 | 
			
		||||
		Signature:      nil,
 | 
			
		||||
		Timestamp:      now,
 | 
			
		||||
	})
 | 
			
		||||
	require.NoError(t, err)
 | 
			
		||||
	assert.Equal(t, sha1, commit.ID)
 | 
			
		||||
	assert.Equal(t, "Commit Message", commit.CommitMessage)
 | 
			
		||||
	assert.Equal(t, sig, commit.Author)
 | 
			
		||||
	assert.Equal(t, sig, commit.Committer)
 | 
			
		||||
	assert.Nil(t, commit.Signature)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestPushCommitToCommitInvalidSha(t *testing.T) {
 | 
			
		||||
	now := time.Now()
 | 
			
		||||
	const hexString = "012"
 | 
			
		||||
	_, err := PushCommitToCommit(&PushCommit{
 | 
			
		||||
		Sha1:           hexString,
 | 
			
		||||
		Message:        "Commit Message",
 | 
			
		||||
		AuthorEmail:    "example@example.com",
 | 
			
		||||
		AuthorName:     "John Doe",
 | 
			
		||||
		CommitterEmail: "example@example.com",
 | 
			
		||||
		CommitterName:  "John Doe",
 | 
			
		||||
		Signature:      nil,
 | 
			
		||||
		Timestamp:      now,
 | 
			
		||||
	})
 | 
			
		||||
	require.Error(t, err)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestCommitToPushCommit(t *testing.T) {
 | 
			
		||||
	now := time.Now()
 | 
			
		||||
	sig := &git.Signature{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,5 @@
 | 
			
		|||
// Copyright 2023 The Gitea Authors. All rights reserved.
 | 
			
		||||
// Copyright 2025 The Forgejo Authors. All rights reserved.
 | 
			
		||||
// SPDX-License-Identifier: MIT
 | 
			
		||||
 | 
			
		||||
package templates
 | 
			
		||||
| 
						 | 
				
			
			@ -13,7 +14,9 @@ import (
 | 
			
		|||
	"time"
 | 
			
		||||
 | 
			
		||||
	activities_model "forgejo.org/models/activities"
 | 
			
		||||
	asymkey_model "forgejo.org/models/asymkey"
 | 
			
		||||
	repo_model "forgejo.org/models/repo"
 | 
			
		||||
	user_model "forgejo.org/models/user"
 | 
			
		||||
	"forgejo.org/modules/git"
 | 
			
		||||
	giturl "forgejo.org/modules/git/url"
 | 
			
		||||
	"forgejo.org/modules/json"
 | 
			
		||||
| 
						 | 
				
			
			@ -60,6 +63,7 @@ func IsMultilineCommitMessage(msg string) bool {
 | 
			
		|||
type Actioner interface {
 | 
			
		||||
	GetOpType() activities_model.ActionType
 | 
			
		||||
	GetActUserName(ctx context.Context) string
 | 
			
		||||
	GetRepo(ctx context.Context) *repo_model.Repository
 | 
			
		||||
	GetRepoUserName(ctx context.Context) string
 | 
			
		||||
	GetRepoName(ctx context.Context) string
 | 
			
		||||
	GetRepoPath(ctx context.Context) string
 | 
			
		||||
| 
						 | 
				
			
			@ -109,7 +113,7 @@ func ActionIcon(opType activities_model.ActionType) string {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// ActionContent2Commits converts action content to push commits
 | 
			
		||||
func ActionContent2Commits(act Actioner) *repository.PushCommits {
 | 
			
		||||
func ActionContent2Commits(ctx context.Context, act Actioner) *repository.PushCommits {
 | 
			
		||||
	push := repository.NewPushCommits()
 | 
			
		||||
 | 
			
		||||
	if act == nil || act.GetContent() == "" {
 | 
			
		||||
| 
						 | 
				
			
			@ -123,6 +127,23 @@ func ActionContent2Commits(act Actioner) *repository.PushCommits {
 | 
			
		|||
	if push.Len == 0 {
 | 
			
		||||
		push.Len = len(push.Commits)
 | 
			
		||||
	}
 | 
			
		||||
	repo := act.GetRepo(ctx)
 | 
			
		||||
	for _, commit := range push.Commits {
 | 
			
		||||
		gitCommit, err := repository.PushCommitToCommit(commit)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			// Only happens if the commit has an invalid sha
 | 
			
		||||
			commit.Verification = &asymkey_model.ObjectVerification{
 | 
			
		||||
				Verified: false,
 | 
			
		||||
				Reason:   "git.error.invalid_commit_id",
 | 
			
		||||
			}
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		verification := asymkey_model.ParseCommitWithSignature(ctx, gitCommit)
 | 
			
		||||
		_ = asymkey_model.CalculateTrustStatus(verification, repo.GetTrustModel(), func(user *user_model.User) (bool, error) {
 | 
			
		||||
			return repo_model.IsOwnerMemberCollaborator(ctx, repo, user.ID)
 | 
			
		||||
		}, nil)
 | 
			
		||||
		commit.Verification = verification
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return push
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										132
									
								
								modules/templates/util_misc_test.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								modules/templates/util_misc_test.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,132 @@
 | 
			
		|||
// Copyright 2025 The Forgejo Authors. All rights reserved.
 | 
			
		||||
// SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
 | 
			
		||||
package templates
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	activities_model "forgejo.org/models/activities"
 | 
			
		||||
	asymkey_model "forgejo.org/models/asymkey"
 | 
			
		||||
	repo_model "forgejo.org/models/repo"
 | 
			
		||||
	"forgejo.org/models/unittest"
 | 
			
		||||
	user_model "forgejo.org/models/user"
 | 
			
		||||
	"forgejo.org/modules/git"
 | 
			
		||||
	"forgejo.org/modules/json"
 | 
			
		||||
	"forgejo.org/modules/repository"
 | 
			
		||||
	"forgejo.org/modules/setting"
 | 
			
		||||
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
	"github.com/stretchr/testify/require"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func pushCommits() *repository.PushCommits {
 | 
			
		||||
	pushCommits := repository.NewPushCommits()
 | 
			
		||||
	pushCommits.Commits = []*repository.PushCommit{
 | 
			
		||||
		{
 | 
			
		||||
			Sha1:           "x",
 | 
			
		||||
			CommitterEmail: "user2@example.com",
 | 
			
		||||
			CommitterName:  "User2",
 | 
			
		||||
			AuthorEmail:    "user2@example.com",
 | 
			
		||||
			AuthorName:     "User2",
 | 
			
		||||
			Message:        "invalid sha1",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			Sha1:           "2c54faec6c45d31c1abfaecdab471eac6633738a",
 | 
			
		||||
			CommitterEmail: "user2@example.com",
 | 
			
		||||
			CommitterName:  "User2",
 | 
			
		||||
			AuthorEmail:    "user2@example.com",
 | 
			
		||||
			AuthorName:     "User2",
 | 
			
		||||
			Message:        "not signed commit",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			Sha1:           "2d491b2985a7ff848d5c02748e7ea9f9f7619f9f",
 | 
			
		||||
			CommitterEmail: "non-existent",
 | 
			
		||||
			CommitterName:  "user2",
 | 
			
		||||
			AuthorEmail:    "non-existent",
 | 
			
		||||
			AuthorName:     "user2",
 | 
			
		||||
			Message:        "Using email that isn't known to Forgejo",
 | 
			
		||||
			Signature: &git.ObjectSignature{
 | 
			
		||||
				Payload: `tree 2d491b2985a7ff848d5c02748e7ea9f9f7619f9f
 | 
			
		||||
parent 45b03601635a1f463b81963a4022c7f87ce96ef9
 | 
			
		||||
author user2 <non-existent> 1699710556 +0100
 | 
			
		||||
committer user2 <non-existent> 1699710556 +0100
 | 
			
		||||
 | 
			
		||||
Using email that isn't known to Forgejo
 | 
			
		||||
`,
 | 
			
		||||
				Signature: `-----BEGIN SSH SIGNATURE-----
 | 
			
		||||
U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgoGSe9Zy7Ez9bSJcaTNjh/Y7p95
 | 
			
		||||
f5DujjqkpzFRtw6CEAAAADZ2l0AAAAAAAAAAZzaGE1MTIAAABTAAAAC3NzaC1lZDI1NTE5
 | 
			
		||||
AAAAQIMufOuSjZeDUujrkVK4sl7ICa0WwEftas8UAYxx0Thdkiw2qWjR1U1PKfTLm16/w8
 | 
			
		||||
/bS1LX1lZNuzm2LR2qEgw=
 | 
			
		||||
-----END SSH SIGNATURE-----
 | 
			
		||||
`,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			Sha1:           "853694aae8816094a0d875fee7ea26278dbf5d0f",
 | 
			
		||||
			CommitterEmail: "user2@example.com",
 | 
			
		||||
			CommitterName:  "user2",
 | 
			
		||||
			AuthorEmail:    "user2@example.com",
 | 
			
		||||
			AuthorName:     "user2",
 | 
			
		||||
			Message:        "Add content",
 | 
			
		||||
			Signature: &git.ObjectSignature{
 | 
			
		||||
				Payload: `tree 853694aae8816094a0d875fee7ea26278dbf5d0f
 | 
			
		||||
parent c2780d5c313da2a947eae22efd7dacf4213f4e7f
 | 
			
		||||
author user2 <user2@example.com> 1699707877 +0100
 | 
			
		||||
committer user2 <user2@example.com> 1699707877 +0100
 | 
			
		||||
 | 
			
		||||
Add content
 | 
			
		||||
`,
 | 
			
		||||
				Signature: `-----BEGIN SSH SIGNATURE-----
 | 
			
		||||
U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgoGSe9Zy7Ez9bSJcaTNjh/Y7p95
 | 
			
		||||
f5DujjqkpzFRtw6CEAAAADZ2l0AAAAAAAAAAZzaGE1MTIAAABTAAAAC3NzaC1lZDI1NTE5
 | 
			
		||||
AAAAQBe2Fwk/FKY3SBCnG6jSYcO6ucyahp2SpQ/0P+otslzIHpWNW8cQ0fGLdhhaFynJXQ
 | 
			
		||||
fs9cMpZVM9BfIKNUSO8QY=
 | 
			
		||||
-----END SSH SIGNATURE-----
 | 
			
		||||
`,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	return pushCommits
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestActionContent2Commits_VerificationState(t *testing.T) {
 | 
			
		||||
	defer unittest.OverrideFixtures(
 | 
			
		||||
		unittest.FixturesOptions{
 | 
			
		||||
			Dir:  filepath.Join(setting.AppWorkPath, "models/fixtures/"),
 | 
			
		||||
			Base: setting.AppWorkPath,
 | 
			
		||||
			Dirs: []string{"models/fixtures/TestParseCommitWithSSHSignature/"},
 | 
			
		||||
		},
 | 
			
		||||
	)()
 | 
			
		||||
	require.NoError(t, unittest.PrepareTestDatabase())
 | 
			
		||||
	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
 | 
			
		||||
	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2, OwnerID: user2.ID})
 | 
			
		||||
	commits, err := json.Marshal(pushCommits())
 | 
			
		||||
	require.NoError(t, err)
 | 
			
		||||
 | 
			
		||||
	act := &activities_model.Action{
 | 
			
		||||
		OpType:  activities_model.ActionCommitRepo,
 | 
			
		||||
		Repo:    repo,
 | 
			
		||||
		Content: string(commits),
 | 
			
		||||
	}
 | 
			
		||||
	push := ActionContent2Commits(t.Context(), act)
 | 
			
		||||
 | 
			
		||||
	assert.Equal(t, 4, push.Len)
 | 
			
		||||
	assert.False(t, push.Commits[0].Verification.Verified)
 | 
			
		||||
	assert.Empty(t, push.Commits[0].Verification.TrustStatus)
 | 
			
		||||
	assert.Equal(t, "git.error.invalid_commit_id", push.Commits[0].Verification.Reason)
 | 
			
		||||
 | 
			
		||||
	assert.False(t, push.Commits[1].Verification.Verified)
 | 
			
		||||
	assert.Empty(t, push.Commits[1].Verification.TrustStatus)
 | 
			
		||||
	assert.Equal(t, asymkey_model.NotSigned, push.Commits[1].Verification.Reason)
 | 
			
		||||
 | 
			
		||||
	assert.False(t, push.Commits[2].Verification.Verified)
 | 
			
		||||
	assert.Empty(t, push.Commits[2].Verification.TrustStatus)
 | 
			
		||||
	assert.Equal(t, asymkey_model.NoKeyFound, push.Commits[2].Verification.Reason)
 | 
			
		||||
 | 
			
		||||
	assert.True(t, push.Commits[3].Verification.Verified)
 | 
			
		||||
	assert.Equal(t, "user2 / SHA256:TKfwbZMR7e9OnlV2l1prfah1TXH8CmqR0PvFEXVCXA4", push.Commits[3].Verification.Reason)
 | 
			
		||||
	assert.Equal(t, "trusted", push.Commits[3].Verification.TrustStatus)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1
									
								
								release-notes/6933.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								release-notes/6933.md
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
feat: Show if commit is verified in activity feed of an user or an organization for new activity
 | 
			
		||||
| 
						 | 
				
			
			@ -1,4 +1,5 @@
 | 
			
		|||
// Copyright 2021 The Gitea Authors. All rights reserved.
 | 
			
		||||
// Copyright 2025 The Forgejo Authors. All rights reserved.
 | 
			
		||||
// SPDX-License-Identifier: MIT
 | 
			
		||||
 | 
			
		||||
package feed
 | 
			
		||||
| 
						 | 
				
			
			@ -209,7 +210,7 @@ func feedActionsToFeedItems(ctx *context.Context, actions activities_model.Actio
 | 
			
		|||
		{
 | 
			
		||||
			switch act.OpType {
 | 
			
		||||
			case activities_model.ActionCommitRepo, activities_model.ActionMirrorSyncPush:
 | 
			
		||||
				push := templates.ActionContent2Commits(act)
 | 
			
		||||
				push := templates.ActionContent2Commits(ctx, act)
 | 
			
		||||
 | 
			
		||||
				for _, commit := range push.Commits {
 | 
			
		||||
					if len(desc) != 0 {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,6 @@
 | 
			
		|||
// Copyright 2014 The Gogs Authors. All rights reserved.
 | 
			
		||||
// Copyright 2018 The Gitea Authors. All rights reserved.
 | 
			
		||||
// Copyright 2025 The Forgejo Authors. All rights reserved.
 | 
			
		||||
// SPDX-License-Identifier: MIT
 | 
			
		||||
 | 
			
		||||
package repo
 | 
			
		||||
| 
						 | 
				
			
			@ -249,7 +250,7 @@ func addVerifyTagToContext(ctx *context.Context) {
 | 
			
		|||
		if verification == nil {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
		return verification.Reason != "gpg.error.not_signed_commit"
 | 
			
		||||
		return verification.Reason != asymkey.NotSigned
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,5 @@
 | 
			
		|||
// Copyright 2019 The Gitea Authors. All rights reserved.
 | 
			
		||||
// Copyright 2025 The Forgejo Authors. All rights reserved.
 | 
			
		||||
// SPDX-License-Identifier: MIT
 | 
			
		||||
 | 
			
		||||
package feed
 | 
			
		||||
| 
						 | 
				
			
			@ -102,7 +103,7 @@ func TestSyncPushCommits(t *testing.T) {
 | 
			
		|||
		NewNotifier().SyncPushCommits(db.DefaultContext, user, repo, &repository.PushUpdateOptions{RefFullName: git.RefNameFromBranch("master")}, pushCommits())
 | 
			
		||||
 | 
			
		||||
		newNotification := unittest.AssertExistsAndLoadBean(t, &activities_model.Action{ActUserID: user.ID, RefName: "refs/heads/master"}, unittest.Cond("id > ?", maxID))
 | 
			
		||||
		assert.JSONEq(t, `{"Commits":[{"Sha1":"69554a6","Message":"not signed commit","AuthorEmail":"user2@example.com","AuthorName":"User2","CommitterEmail":"user2@example.com","CommitterName":"User2","Timestamp":"0001-01-01T00:00:00Z"},{"Sha1":"27566bd","Message":"good signed commit (with not yet validated email)","AuthorEmail":"user2@example.com","AuthorName":"User2","CommitterEmail":"user2@example.com","CommitterName":"User2","Timestamp":"0001-01-01T00:00:00Z"},{"Sha1":"5099b81","Message":"good signed commit","AuthorEmail":"user2@example.com","AuthorName":"User2","CommitterEmail":"user2@example.com","CommitterName":"User2","Timestamp":"0001-01-01T00:00:00Z"}],"HeadCommit":{"Sha1":"69554a6","Message":"","AuthorEmail":"","AuthorName":"","CommitterEmail":"","CommitterName":"","Timestamp":"0001-01-01T00:00:00Z"},"CompareURL":"","Len":0}`, newNotification.Content)
 | 
			
		||||
		assert.JSONEq(t, `{"Commits":[{"Sha1":"69554a6","Message":"not signed commit","AuthorEmail":"user2@example.com","AuthorName":"User2","CommitterEmail":"user2@example.com","CommitterName":"User2","Signature":null,"Verification":null,"Timestamp":"0001-01-01T00:00:00Z"},{"Sha1":"27566bd","Message":"good signed commit (with not yet validated email)","AuthorEmail":"user2@example.com","AuthorName":"User2","CommitterEmail":"user2@example.com","CommitterName":"User2","Signature":null,"Verification":null,"Timestamp":"0001-01-01T00:00:00Z"},{"Sha1":"5099b81","Message":"good signed commit","AuthorEmail":"user2@example.com","AuthorName":"User2","CommitterEmail":"user2@example.com","CommitterName":"User2","Signature":null,"Verification":null,"Timestamp":"0001-01-01T00:00:00Z"}],"HeadCommit":{"Sha1":"69554a6","Message":"","AuthorEmail":"","AuthorName":"","CommitterEmail":"","CommitterName":"","Signature":null,"Verification":null,"Timestamp":"0001-01-01T00:00:00Z"},"CompareURL":"","Len":0}`, newNotification.Content)
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	t.Run("Only one commit", func(t *testing.T) {
 | 
			
		||||
| 
						 | 
				
			
			@ -112,7 +113,7 @@ func TestSyncPushCommits(t *testing.T) {
 | 
			
		|||
		NewNotifier().SyncPushCommits(db.DefaultContext, user, repo, &repository.PushUpdateOptions{RefFullName: git.RefNameFromBranch("main")}, pushCommits())
 | 
			
		||||
 | 
			
		||||
		newNotification := unittest.AssertExistsAndLoadBean(t, &activities_model.Action{ActUserID: user.ID, RefName: "refs/heads/main"}, unittest.Cond("id > ?", maxID))
 | 
			
		||||
		assert.JSONEq(t, `{"Commits":[{"Sha1":"69554a6","Message":"not signed commit","AuthorEmail":"user2@example.com","AuthorName":"User2","CommitterEmail":"user2@example.com","CommitterName":"User2","Timestamp":"0001-01-01T00:00:00Z"}],"HeadCommit":{"Sha1":"69554a6","Message":"","AuthorEmail":"","AuthorName":"","CommitterEmail":"","CommitterName":"","Timestamp":"0001-01-01T00:00:00Z"},"CompareURL":"","Len":0}`, newNotification.Content)
 | 
			
		||||
		assert.JSONEq(t, `{"Commits":[{"Sha1":"69554a6","Message":"not signed commit","AuthorEmail":"user2@example.com","AuthorName":"User2","CommitterEmail":"user2@example.com","CommitterName":"User2","Signature":null,"Verification":null,"Timestamp":"0001-01-01T00:00:00Z"}],"HeadCommit":{"Sha1":"69554a6","Message":"","AuthorEmail":"","AuthorName":"","CommitterEmail":"","CommitterName":"","Signature":null,"Verification":null,"Timestamp":"0001-01-01T00:00:00Z"},"CompareURL":"","Len":0}`, newNotification.Content)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -129,7 +130,7 @@ func TestPushCommits(t *testing.T) {
 | 
			
		|||
		NewNotifier().PushCommits(db.DefaultContext, user, repo, &repository.PushUpdateOptions{RefFullName: git.RefNameFromBranch("master")}, pushCommits())
 | 
			
		||||
 | 
			
		||||
		newNotification := unittest.AssertExistsAndLoadBean(t, &activities_model.Action{ActUserID: user.ID, RefName: "refs/heads/master"}, unittest.Cond("id > ?", maxID))
 | 
			
		||||
		assert.JSONEq(t, `{"Commits":[{"Sha1":"69554a6","Message":"not signed commit","AuthorEmail":"user2@example.com","AuthorName":"User2","CommitterEmail":"user2@example.com","CommitterName":"User2","Timestamp":"0001-01-01T00:00:00Z"},{"Sha1":"27566bd","Message":"good signed commit (with not yet validated email)","AuthorEmail":"user2@example.com","AuthorName":"User2","CommitterEmail":"user2@example.com","CommitterName":"User2","Timestamp":"0001-01-01T00:00:00Z"},{"Sha1":"5099b81","Message":"good signed commit","AuthorEmail":"user2@example.com","AuthorName":"User2","CommitterEmail":"user2@example.com","CommitterName":"User2","Timestamp":"0001-01-01T00:00:00Z"}],"HeadCommit":{"Sha1":"69554a6","Message":"","AuthorEmail":"","AuthorName":"","CommitterEmail":"","CommitterName":"","Timestamp":"0001-01-01T00:00:00Z"},"CompareURL":"","Len":0}`, newNotification.Content)
 | 
			
		||||
		assert.JSONEq(t, `{"Commits":[{"Sha1":"69554a6","Message":"not signed commit","AuthorEmail":"user2@example.com","AuthorName":"User2","CommitterEmail":"user2@example.com","CommitterName":"User2","Signature":null,"Verification":null,"Timestamp":"0001-01-01T00:00:00Z"},{"Sha1":"27566bd","Message":"good signed commit (with not yet validated email)","AuthorEmail":"user2@example.com","AuthorName":"User2","CommitterEmail":"user2@example.com","CommitterName":"User2","Signature":null,"Verification":null,"Timestamp":"0001-01-01T00:00:00Z"},{"Sha1":"5099b81","Message":"good signed commit","AuthorEmail":"user2@example.com","AuthorName":"User2","CommitterEmail":"user2@example.com","CommitterName":"User2","Signature":null,"Verification":null,"Timestamp":"0001-01-01T00:00:00Z"}],"HeadCommit":{"Sha1":"69554a6","Message":"","AuthorEmail":"","AuthorName":"","CommitterEmail":"","CommitterName":"","Signature":null,"Verification":null,"Timestamp":"0001-01-01T00:00:00Z"},"CompareURL":"","Len":0}`, newNotification.Content)
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	t.Run("Only one commit", func(t *testing.T) {
 | 
			
		||||
| 
						 | 
				
			
			@ -139,6 +140,6 @@ func TestPushCommits(t *testing.T) {
 | 
			
		|||
		NewNotifier().PushCommits(db.DefaultContext, user, repo, &repository.PushUpdateOptions{RefFullName: git.RefNameFromBranch("main")}, pushCommits())
 | 
			
		||||
 | 
			
		||||
		newNotification := unittest.AssertExistsAndLoadBean(t, &activities_model.Action{ActUserID: user.ID, RefName: "refs/heads/main"}, unittest.Cond("id > ?", maxID))
 | 
			
		||||
		assert.JSONEq(t, `{"Commits":[{"Sha1":"69554a6","Message":"not signed commit","AuthorEmail":"user2@example.com","AuthorName":"User2","CommitterEmail":"user2@example.com","CommitterName":"User2","Timestamp":"0001-01-01T00:00:00Z"}],"HeadCommit":{"Sha1":"69554a6","Message":"","AuthorEmail":"","AuthorName":"","CommitterEmail":"","CommitterName":"","Timestamp":"0001-01-01T00:00:00Z"},"CompareURL":"","Len":0}`, newNotification.Content)
 | 
			
		||||
		assert.JSONEq(t, `{"Commits":[{"Sha1":"69554a6","Message":"not signed commit","AuthorEmail":"user2@example.com","AuthorName":"User2","CommitterEmail":"user2@example.com","CommitterName":"User2","Signature":null,"Verification":null,"Timestamp":"0001-01-01T00:00:00Z"}],"HeadCommit":{"Sha1":"69554a6","Message":"","AuthorEmail":"","AuthorName":"","CommitterEmail":"","CommitterName":"","Signature":null,"Verification":null,"Timestamp":"0001-01-01T00:00:00Z"},"CompareURL":"","Len":0}`, newNotification.Content)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,5 @@
 | 
			
		|||
// Copyright 2019 The Gitea Authors. All rights reserved.
 | 
			
		||||
// Copyright 2025 The Forgejo Authors. All rights reserved.
 | 
			
		||||
// SPDX-License-Identifier: MIT
 | 
			
		||||
 | 
			
		||||
package files
 | 
			
		||||
| 
						 | 
				
			
			@ -38,7 +39,7 @@ func GetPayloadCommitVerification(ctx context.Context, commit *git.Commit) *stru
 | 
			
		|||
	verification.Verified = commitVerification.Verified
 | 
			
		||||
	verification.Reason = commitVerification.Reason
 | 
			
		||||
	if verification.Reason == "" && !verification.Verified {
 | 
			
		||||
		verification.Reason = "gpg.error.not_signed_commit"
 | 
			
		||||
		verification.Reason = asymkey_model.NotSigned
 | 
			
		||||
	}
 | 
			
		||||
	return verification
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -28,21 +28,6 @@
 | 
			
		|||
							</div>
 | 
			
		||||
						</td>
 | 
			
		||||
						<td class="sha">
 | 
			
		||||
							{{$class := "ui sha label"}}
 | 
			
		||||
							{{if .Signature}}
 | 
			
		||||
								{{$class = (print $class " isSigned")}}
 | 
			
		||||
								{{if .Verification.Verified}}
 | 
			
		||||
									{{if eq .Verification.TrustStatus "trusted"}}
 | 
			
		||||
										{{$class = (print $class " isVerified")}}
 | 
			
		||||
									{{else if eq .Verification.TrustStatus "untrusted"}}
 | 
			
		||||
										{{$class = (print $class " isVerifiedUntrusted")}}
 | 
			
		||||
									{{else}}
 | 
			
		||||
										{{$class = (print $class " isVerifiedUnmatched")}}
 | 
			
		||||
									{{end}}
 | 
			
		||||
								{{else if .Verification.Warning}}
 | 
			
		||||
									{{$class = (print $class " isWarning")}}
 | 
			
		||||
								{{end}}
 | 
			
		||||
							{{end}}
 | 
			
		||||
							{{$commitShaLink := ""}}
 | 
			
		||||
							{{if $.PageIsWiki}}
 | 
			
		||||
								{{$commitShaLink = (printf "%s/wiki/commit/%s" $commitRepoLink (PathEscape .ID.String))}}
 | 
			
		||||
| 
						 | 
				
			
			@ -51,10 +36,12 @@
 | 
			
		|||
							{{else if $.Reponame}}
 | 
			
		||||
								{{$commitShaLink = (printf "%s/commit/%s" $commitRepoLink (PathEscape .ID.String))}}
 | 
			
		||||
							{{end}}
 | 
			
		||||
							<a {{if $commitShaLink}}href="{{$commitShaLink}}"{{end}} class="{{$class}}">
 | 
			
		||||
								<span class="shortsha">{{ShortSha .ID.String}}</span>
 | 
			
		||||
								{{if .Signature}}{{template "repo/shabox_badge" dict "root" $ "verification" .Verification}}{{end}}
 | 
			
		||||
							</a>
 | 
			
		||||
							{{template "repo/shabox" (dict
 | 
			
		||||
								"sha1" .ID.String
 | 
			
		||||
								"commitLink" $commitShaLink
 | 
			
		||||
								"signature" .Signature
 | 
			
		||||
								"verification" .Verification
 | 
			
		||||
							)}}
 | 
			
		||||
						</td>
 | 
			
		||||
						<td class="message">
 | 
			
		||||
							<span class="message-wrapper">
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,29 +21,14 @@
 | 
			
		|||
			<button class="ui button ellipsis-button show-panel toggle" data-panel="[data-singular-commit-body-for='{{$tag}}']">...</button>
 | 
			
		||||
		{{end}}
 | 
			
		||||
 | 
			
		||||
		<span class="shabox tw-flex tw-items-center">
 | 
			
		||||
		<span class="shabox tw-flex tw-items-center tw-gap-2">
 | 
			
		||||
			{{template "repo/commit_statuses" dict "Status" .Status "Statuses" .Statuses}}
 | 
			
		||||
			{{$class := "ui sha label"}}
 | 
			
		||||
			{{if .Signature}}
 | 
			
		||||
				{{$class = (print $class " isSigned")}}
 | 
			
		||||
				{{if .Verification.Verified}}
 | 
			
		||||
					{{if eq .Verification.TrustStatus "trusted"}}
 | 
			
		||||
						{{$class = (print $class " isVerified")}}
 | 
			
		||||
					{{else if eq .Verification.TrustStatus "untrusted"}}
 | 
			
		||||
						{{$class = (print $class " isVerifiedUntrusted")}}
 | 
			
		||||
					{{else}}
 | 
			
		||||
						{{$class = (print $class " isVerifiedUnmatched")}}
 | 
			
		||||
					{{end}}
 | 
			
		||||
				{{else if .Verification.Warning}}
 | 
			
		||||
					{{$class = (print $class " isWarning")}}
 | 
			
		||||
				{{end}}
 | 
			
		||||
			{{end}}
 | 
			
		||||
			<a href="{{$commitLink}}" rel="nofollow" class="tw-ml-2 {{$class}}">
 | 
			
		||||
				<span class="shortsha">{{ShortSha .ID.String}}</span>
 | 
			
		||||
				{{if .Signature}}
 | 
			
		||||
					{{template "repo/shabox_badge" dict "root" $.root "verification" .Verification}}
 | 
			
		||||
				{{end}}
 | 
			
		||||
			</a>
 | 
			
		||||
			{{template "repo/shabox" (dict
 | 
			
		||||
				"sha1" .ID.String
 | 
			
		||||
				"commitLink" $commitLink
 | 
			
		||||
				"signature" .Signature
 | 
			
		||||
				"verification" .Verification
 | 
			
		||||
			)}}
 | 
			
		||||
		</span>
 | 
			
		||||
	</div>
 | 
			
		||||
	{{if IsMultilineCommitMessage .Message}}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,27 +6,12 @@
 | 
			
		|||
					<span></span>
 | 
			
		||||
				{{else}}
 | 
			
		||||
					<span class="sha" id="{{$commit.ShortRev}}">
 | 
			
		||||
						{{$class := "ui sha label"}}
 | 
			
		||||
						{{if $commit.Commit.Signature}}
 | 
			
		||||
							{{$class = (print $class " isSigned")}}
 | 
			
		||||
							{{if $commit.Verification.Verified}}
 | 
			
		||||
								{{if eq $commit.Verification.TrustStatus "trusted"}}
 | 
			
		||||
									{{$class = (print $class " isVerified")}}
 | 
			
		||||
								{{else if eq $commit.Verification.TrustStatus "untrusted"}}
 | 
			
		||||
									{{$class = (print $class " isVerifiedUntrusted")}}
 | 
			
		||||
								{{else}}
 | 
			
		||||
									{{$class = (print $class " isVerifiedUnmatched")}}
 | 
			
		||||
								{{end}}
 | 
			
		||||
							{{else if $commit.Verification.Warning}}
 | 
			
		||||
								{{$class = (print $class " isWarning")}}
 | 
			
		||||
							{{end}}
 | 
			
		||||
						{{end}}
 | 
			
		||||
						<a href="{{$.RepoLink}}/commit/{{$commit.Rev|PathEscape}}" rel="nofollow" class="{{$class}}">
 | 
			
		||||
							<span class="shortsha">{{ShortSha $commit.Commit.ID.String}}</span>
 | 
			
		||||
							{{- if $commit.Commit.Signature -}}
 | 
			
		||||
								{{template "repo/shabox_badge" dict "root" $ "verification" $commit.Verification}}
 | 
			
		||||
							{{- end -}}
 | 
			
		||||
						</a>
 | 
			
		||||
						{{template "repo/shabox" (dict
 | 
			
		||||
							"sha1" $commit.Commit.ID.String
 | 
			
		||||
							"commitLink" (printf "%s/commit/%s" $.RepoLink ($commit.Rev|PathEscape))
 | 
			
		||||
							"signature" $commit.Commit.Signature
 | 
			
		||||
							"verification" $commit.Verification
 | 
			
		||||
						)}}
 | 
			
		||||
					</span>
 | 
			
		||||
					<span class="message tw-inline-block gt-ellipsis tw-mr-2">
 | 
			
		||||
						<span>{{RenderCommitMessage $.Context $commit.Subject ($.Repository.ComposeMetas ctx)}}</span>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,12 +14,12 @@
 | 
			
		|||
			<span class="author-wrapper" title="{{.LatestCommit.Author.Name}}"><strong>{{.LatestCommit.Author.Name}}</strong></span>
 | 
			
		||||
		{{end}}
 | 
			
		||||
	{{end}}
 | 
			
		||||
	<a rel="nofollow" class="ui sha label {{if .LatestCommit.Signature}} isSigned {{if .LatestCommitVerification.Verified}} isVerified{{if eq .LatestCommitVerification.TrustStatus "trusted"}}{{else if eq .LatestCommitVerification.TrustStatus "untrusted"}}Untrusted{{else}}Unmatched{{end}}{{else if .LatestCommitVerification.Warning}} isWarning{{end}}{{end}}" href="{{.RepoLink}}/commit/{{PathEscape .LatestCommit.ID.String}}">
 | 
			
		||||
		<span class="shortsha">{{ShortSha .LatestCommit.ID.String}}</span>
 | 
			
		||||
		{{if .LatestCommit.Signature}}
 | 
			
		||||
			{{template "repo/shabox_badge" dict "root" $ "verification" .LatestCommitVerification}}
 | 
			
		||||
		{{end}}
 | 
			
		||||
	</a>
 | 
			
		||||
	{{template "repo/shabox" (dict
 | 
			
		||||
		"sha1" .LatestCommit.ID.String
 | 
			
		||||
		"commitLink" (printf "%s/commit/%s" .RepoLink (.LatestCommit.ID.String|PathEscape))
 | 
			
		||||
		"signature" .LatestCommit.Signature
 | 
			
		||||
		"verification" .LatestCommitVerification
 | 
			
		||||
	)}}
 | 
			
		||||
	{{template "repo/commit_statuses" dict "Status" .LatestCommitStatus "Statuses" .LatestCommitStatuses}}
 | 
			
		||||
	{{$commitLink:= printf "%s/commit/%s" .RepoLink (PathEscape .LatestCommit.ID.String)}}
 | 
			
		||||
	<span class="grey commit-summary" title="{{.LatestCommit.Summary}}"><span class="message-wrapper">{{RenderCommitMessageLinkSubject $.Context .LatestCommit.Message $commitLink ($.Repository.ComposeMetas ctx)}}</span>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										21
									
								
								templates/repo/shabox.tmpl
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								templates/repo/shabox.tmpl
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,21 @@
 | 
			
		|||
{{$class := "ui sha label"}}
 | 
			
		||||
{{if .signature}}
 | 
			
		||||
	{{$class = (print $class " isSigned")}}
 | 
			
		||||
	{{if .verification.Verified}}
 | 
			
		||||
		{{if eq .verification.TrustStatus "trusted"}}
 | 
			
		||||
			{{$class = (print $class " isVerified")}}
 | 
			
		||||
		{{else if eq .verification.TrustStatus "untrusted"}}
 | 
			
		||||
			{{$class = (print $class " isVerifiedUntrusted")}}
 | 
			
		||||
		{{else}}
 | 
			
		||||
			{{$class = (print $class " isVerifiedUnmatched")}}
 | 
			
		||||
		{{end}}
 | 
			
		||||
	{{else if .verification.Warning}}
 | 
			
		||||
		{{$class = (print $class " isWarning")}}
 | 
			
		||||
	{{end}}
 | 
			
		||||
{{end}}
 | 
			
		||||
<a {{if .commitLink}}href="{{.commitLink}}"{{end}} rel="nofollow" class="{{$class}}">
 | 
			
		||||
	<span class="shortsha">{{ShortSha .sha1}}</span>
 | 
			
		||||
	{{- if .signature -}}
 | 
			
		||||
		{{template "repo/shabox_badge" dict "verification" .verification "svgSize" .svgSize}}
 | 
			
		||||
	{{- end -}}
 | 
			
		||||
</a>
 | 
			
		||||
| 
						 | 
				
			
			@ -2,14 +2,14 @@
 | 
			
		|||
	{{if .verification.Verified}}
 | 
			
		||||
		<div title="{{if eq .verification.TrustStatus "trusted"}}{{else if eq .verification.TrustStatus "untrusted"}}{{ctx.Locale.Tr "repo.commits.signed_by_untrusted_user"}}: {{else}}{{ctx.Locale.Tr "repo.commits.signed_by_untrusted_user_unmatched"}}: {{end}}{{.verification.Reason}}">
 | 
			
		||||
		{{if ne .verification.SigningUser.ID 0}}
 | 
			
		||||
			{{svg "gitea-lock"}}
 | 
			
		||||
			{{svg "gitea-lock" .svgSize}}
 | 
			
		||||
			{{ctx.AvatarUtils.Avatar .verification.SigningUser 28 "signature"}}
 | 
			
		||||
		{{else}}
 | 
			
		||||
			<span title="{{ctx.Locale.Tr "gpg.default_key"}}">{{svg "gitea-lock-cog"}}</span>
 | 
			
		||||
			<span title="{{ctx.Locale.Tr "gpg.default_key"}}">{{svg "gitea-lock-cog" .svgSize}}</span>
 | 
			
		||||
			{{ctx.AvatarUtils.AvatarByEmail .verification.SigningEmail "" 28 "signature"}}
 | 
			
		||||
		{{end}}
 | 
			
		||||
		</div>
 | 
			
		||||
	{{else}}
 | 
			
		||||
		<span title="{{ctx.Locale.Tr .verification.Reason}}">{{svg "gitea-unlock"}}</span>
 | 
			
		||||
		<span title="{{ctx.Locale.Tr .verification.Reason}}">{{svg "gitea-unlock" .svgSize}}</span>
 | 
			
		||||
	{{end}}
 | 
			
		||||
</div>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -84,15 +84,20 @@
 | 
			
		|||
					{{DateUtils.TimeSince .GetCreate}}
 | 
			
		||||
				</div>
 | 
			
		||||
				{{if .GetOpType.InActions "commit_repo" "mirror_sync_push"}}
 | 
			
		||||
					{{$push := ActionContent2Commits .}}
 | 
			
		||||
					{{$push := ActionContent2Commits ctx .}}
 | 
			
		||||
					{{$repoLink := (.GetRepoLink ctx)}}
 | 
			
		||||
					{{$repo := .Repo}}
 | 
			
		||||
					<div class="tw-flex tw-flex-col tw-gap-1">
 | 
			
		||||
						{{range $push.Commits}}
 | 
			
		||||
							{{$commitLink := printf "%s/commit/%s" $repoLink .Sha1}}
 | 
			
		||||
							<div class="flex-text-block">
 | 
			
		||||
								<img class="ui avatar" src="{{$push.AvatarLink $.Context .AuthorEmail}}" alt="" title="{{.AuthorName}}" width="16" height="16">
 | 
			
		||||
								<a class="ui sha label" href="{{$commitLink}}">{{ShortSha .Sha1}}</a>
 | 
			
		||||
								{{template "repo/shabox" (dict
 | 
			
		||||
									"sha1" .Sha1
 | 
			
		||||
									"commitLink" (printf "%s/commit/%s" $repoLink .Sha1)
 | 
			
		||||
									"signature" .Signature
 | 
			
		||||
									"verification" .Verification
 | 
			
		||||
									"svgSize" 13
 | 
			
		||||
								)}}
 | 
			
		||||
								<span class="text truncate">
 | 
			
		||||
									{{RenderCommitMessage $.Context .Message ($repo.ComposeMetas ctx)}}
 | 
			
		||||
								</span>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,5 @@
 | 
			
		|||
// Copyright 2019 The Gitea Authors. All rights reserved.
 | 
			
		||||
// Copyright 2025 The Forgejo Authors. All rights reserved.
 | 
			
		||||
// SPDX-License-Identifier: MIT
 | 
			
		||||
 | 
			
		||||
package integration
 | 
			
		||||
| 
						 | 
				
			
			@ -12,6 +13,7 @@ import (
 | 
			
		|||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"forgejo.org/models/asymkey"
 | 
			
		||||
	auth_model "forgejo.org/models/auth"
 | 
			
		||||
	repo_model "forgejo.org/models/repo"
 | 
			
		||||
	"forgejo.org/models/unittest"
 | 
			
		||||
| 
						 | 
				
			
			@ -106,7 +108,7 @@ func getExpectedFileResponseForCreate(repoFullName, commitID, treePath, latestCo
 | 
			
		|||
		},
 | 
			
		||||
		Verification: &api.PayloadCommitVerification{
 | 
			
		||||
			Verified:  false,
 | 
			
		||||
			Reason:    "gpg.error.not_signed_commit",
 | 
			
		||||
			Reason:    asymkey.NotSigned,
 | 
			
		||||
			Signature: "",
 | 
			
		||||
			Payload:   "",
 | 
			
		||||
		},
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,5 @@
 | 
			
		|||
// Copyright 2019 The Gitea Authors. All rights reserved.
 | 
			
		||||
// Copyright 2025 The Forgejo Authors. All rights reserved.
 | 
			
		||||
// SPDX-License-Identifier: MIT
 | 
			
		||||
 | 
			
		||||
package integration
 | 
			
		||||
| 
						 | 
				
			
			@ -11,6 +12,7 @@ import (
 | 
			
		|||
	"path/filepath"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"forgejo.org/models/asymkey"
 | 
			
		||||
	auth_model "forgejo.org/models/auth"
 | 
			
		||||
	repo_model "forgejo.org/models/repo"
 | 
			
		||||
	"forgejo.org/models/unittest"
 | 
			
		||||
| 
						 | 
				
			
			@ -97,7 +99,7 @@ func getExpectedFileResponseForUpdate(commitID, treePath, lastCommitSHA string)
 | 
			
		|||
		},
 | 
			
		||||
		Verification: &api.PayloadCommitVerification{
 | 
			
		||||
			Verified:  false,
 | 
			
		||||
			Reason:    "gpg.error.not_signed_commit",
 | 
			
		||||
			Reason:    asymkey.NotSigned,
 | 
			
		||||
			Signature: "",
 | 
			
		||||
			Payload:   "",
 | 
			
		||||
		},
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,5 @@
 | 
			
		|||
// Copyright 2019 The Gitea Authors. All rights reserved.
 | 
			
		||||
// Copyright 2025 The Forgejo Authors. All rights reserved.
 | 
			
		||||
// SPDX-License-Identifier: MIT
 | 
			
		||||
 | 
			
		||||
package integration
 | 
			
		||||
| 
						 | 
				
			
			@ -10,6 +11,7 @@ import (
 | 
			
		|||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"forgejo.org/models/asymkey"
 | 
			
		||||
	repo_model "forgejo.org/models/repo"
 | 
			
		||||
	"forgejo.org/models/unittest"
 | 
			
		||||
	user_model "forgejo.org/models/user"
 | 
			
		||||
| 
						 | 
				
			
			@ -101,7 +103,7 @@ func getExpectedFileResponseForRepofilesDelete() *api.FileResponse {
 | 
			
		|||
		},
 | 
			
		||||
		Verification: &api.PayloadCommitVerification{
 | 
			
		||||
			Verified:  false,
 | 
			
		||||
			Reason:    "gpg.error.not_signed_commit",
 | 
			
		||||
			Reason:    asymkey.NotSigned,
 | 
			
		||||
			Signature: "",
 | 
			
		||||
			Payload:   "",
 | 
			
		||||
		},
 | 
			
		||||
| 
						 | 
				
			
			@ -171,7 +173,7 @@ func getExpectedFileResponseForRepofilesCreate(commitID, lastCommitSHA string, l
 | 
			
		|||
		},
 | 
			
		||||
		Verification: &api.PayloadCommitVerification{
 | 
			
		||||
			Verified:  false,
 | 
			
		||||
			Reason:    "gpg.error.not_signed_commit",
 | 
			
		||||
			Reason:    asymkey.NotSigned,
 | 
			
		||||
			Signature: "",
 | 
			
		||||
			Payload:   "",
 | 
			
		||||
		},
 | 
			
		||||
| 
						 | 
				
			
			@ -240,7 +242,7 @@ func getExpectedFileResponseForRepofilesUpdate(commitID, filename, lastCommitSHA
 | 
			
		|||
		},
 | 
			
		||||
		Verification: &api.PayloadCommitVerification{
 | 
			
		||||
			Verified:  false,
 | 
			
		||||
			Reason:    "gpg.error.not_signed_commit",
 | 
			
		||||
			Reason:    asymkey.NotSigned,
 | 
			
		||||
			Signature: "",
 | 
			
		||||
			Payload:   "",
 | 
			
		||||
		},
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1269,6 +1269,7 @@ td .commit-summary {
 | 
			
		|||
  background-color: var(--color-light) !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#activity-feed .sha.label,
 | 
			
		||||
.repository #commits-table td.sha .sha.label,
 | 
			
		||||
.repository #repo-files-table .sha.label,
 | 
			
		||||
.repository #repo-file-commit-box .sha.label,
 | 
			
		||||
| 
						 | 
				
			
			@ -1277,6 +1278,12 @@ td .commit-summary {
 | 
			
		|||
  border: 1px solid var(--color-light-border);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#activity-feed .sha.label .ui.signature.avatar {
 | 
			
		||||
  height: 13px;
 | 
			
		||||
  margin-bottom: 0;
 | 
			
		||||
  width: 13px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.repository #commits-table td.sha .sha.label .ui.signature.avatar,
 | 
			
		||||
.repository #repo-files-table .sha.label .ui.signature.avatar,
 | 
			
		||||
.repository #repo-file-commit-box .sha.label .ui.signature.avatar,
 | 
			
		||||
| 
						 | 
				
			
			@ -1287,6 +1294,7 @@ td .commit-summary {
 | 
			
		|||
  width: 16px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#activity-feed .sha.label .detail.icon,
 | 
			
		||||
.repository #commits-table td.sha .sha.label .detail.icon,
 | 
			
		||||
.repository #repo-files-table .sha.label .detail.icon,
 | 
			
		||||
.repository #repo-file-commit-box .sha.label .detail.icon,
 | 
			
		||||
| 
						 | 
				
			
			@ -1303,6 +1311,7 @@ td .commit-summary {
 | 
			
		|||
  border-bottom-left-radius: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#activity-feed .sha.label .detail.icon img,
 | 
			
		||||
.repository #commits-table td.sha .sha.label .detail.icon img,
 | 
			
		||||
.repository #repo-files-table .sha.label .detail.icon img,
 | 
			
		||||
.repository #repo-file-commit-box .sha.label .detail.icon img,
 | 
			
		||||
| 
						 | 
				
			
			@ -1311,6 +1320,7 @@ td .commit-summary {
 | 
			
		|||
  margin-right: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#activity-feed .sha.label .detail.icon .svg,
 | 
			
		||||
.repository #commits-table td.sha .sha.label .detail.icon .svg,
 | 
			
		||||
.repository #repo-files-table .sha.label .detail.icon .svg,
 | 
			
		||||
.repository #repo-file-commit-box .sha.label .detail.icon .svg,
 | 
			
		||||
| 
						 | 
				
			
			@ -1319,6 +1329,7 @@ td .commit-summary {
 | 
			
		|||
  margin: 0 0.25em 0 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#activity-feed .sha.label .detail.icon > div,
 | 
			
		||||
.repository #commits-table td.sha .sha.label .detail.icon > div,
 | 
			
		||||
.repository #repo-files-table .sha.label .detail.icon > div,
 | 
			
		||||
.repository #repo-file-commit-box .sha.label .detail.icon > div,
 | 
			
		||||
| 
						 | 
				
			
			@ -1328,6 +1339,7 @@ td .commit-summary {
 | 
			
		|||
  align-items: center;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#activity-feed .sha.label.isSigned.isWarning,
 | 
			
		||||
.repository #commits-table td.sha .sha.label.isSigned.isWarning,
 | 
			
		||||
.repository #repo-files-table .sha.label.isSigned.isWarning,
 | 
			
		||||
.repository #repo-file-commit-box .sha.label.isSigned.isWarning,
 | 
			
		||||
| 
						 | 
				
			
			@ -1337,6 +1349,7 @@ td .commit-summary {
 | 
			
		|||
  background: var(--color-red-badge-bg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#activity-feed .sha.label.isSigned.isWarning .detail.icon,
 | 
			
		||||
.repository #commits-table td.sha .sha.label.isSigned.isWarning .detail.icon,
 | 
			
		||||
.repository #repo-files-table .sha.label.isSigned.isWarning .detail.icon,
 | 
			
		||||
.repository #repo-file-commit-box .sha.label.isSigned.isWarning .detail.icon,
 | 
			
		||||
| 
						 | 
				
			
			@ -1346,6 +1359,7 @@ td .commit-summary {
 | 
			
		|||
  color: var(--color-red-badge);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#activity-feed .sha.label.isSigned.isWarning:hover,
 | 
			
		||||
.repository #commits-table td.sha .sha.label.isSigned.isWarning:hover,
 | 
			
		||||
.repository #repo-files-table .sha.label.isSigned.isWarning:hover,
 | 
			
		||||
.repository #repo-file-commit-box .sha.label.isSigned.isWarning:hover,
 | 
			
		||||
| 
						 | 
				
			
			@ -1354,6 +1368,7 @@ td .commit-summary {
 | 
			
		|||
  background: var(--color-red-badge-hover-bg) !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#activity-feed .sha.label.isSigned.isVerified,
 | 
			
		||||
.repository #commits-table td.sha .sha.label.isSigned.isVerified,
 | 
			
		||||
.repository #repo-files-table .sha.label.isSigned.isVerified,
 | 
			
		||||
.repository #repo-file-commit-box .sha.label.isSigned.isVerified,
 | 
			
		||||
| 
						 | 
				
			
			@ -1363,6 +1378,7 @@ td .commit-summary {
 | 
			
		|||
  background: var(--color-green-badge-bg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#activity-feed .sha.label.isSigned.isVerified .detail.icon,
 | 
			
		||||
.repository #commits-table td.sha .sha.label.isSigned.isVerified .detail.icon,
 | 
			
		||||
.repository #repo-files-table .sha.label.isSigned.isVerified .detail.icon,
 | 
			
		||||
.repository #repo-file-commit-box .sha.label.isSigned.isVerified .detail.icon,
 | 
			
		||||
| 
						 | 
				
			
			@ -1372,6 +1388,7 @@ td .commit-summary {
 | 
			
		|||
  color: var(--color-green-badge);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#activity-feed .sha.label.isSigned.isVerified:hover,
 | 
			
		||||
.repository #commits-table td.sha .sha.label.isSigned.isVerified:hover,
 | 
			
		||||
.repository #repo-files-table .sha.label.isSigned.isVerified:hover,
 | 
			
		||||
.repository #repo-file-commit-box .sha.label.isSigned.isVerified:hover,
 | 
			
		||||
| 
						 | 
				
			
			@ -1380,6 +1397,7 @@ td .commit-summary {
 | 
			
		|||
  background: var(--color-green-badge-hover-bg) !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#activity-feed .sha.label.isSigned.isVerifiedUntrusted,
 | 
			
		||||
.repository #commits-table td.sha .sha.label.isSigned.isVerifiedUntrusted,
 | 
			
		||||
.repository #repo-files-table .sha.label.isSigned.isVerifiedUntrusted,
 | 
			
		||||
.repository #repo-file-commit-box .sha.label.isSigned.isVerifiedUntrusted,
 | 
			
		||||
| 
						 | 
				
			
			@ -1389,6 +1407,7 @@ td .commit-summary {
 | 
			
		|||
  background: var(--color-yellow-badge-bg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#activity-feed .sha.label.isSigned.isVerifiedUntrusted .detail.icon,
 | 
			
		||||
.repository #commits-table td.sha .sha.label.isSigned.isVerifiedUntrusted .detail.icon,
 | 
			
		||||
.repository #repo-files-table .sha.label.isSigned.isVerifiedUntrusted .detail.icon,
 | 
			
		||||
.repository #repo-file-commit-box .sha.label.isSigned.isVerifiedUntrusted .detail.icon,
 | 
			
		||||
| 
						 | 
				
			
			@ -1398,6 +1417,7 @@ td .commit-summary {
 | 
			
		|||
  color: var(--color-yellow-badge);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#activity-feed .sha.label.isSigned.isVerifiedUntrusted:hover,
 | 
			
		||||
.repository #commits-table td.sha .sha.label.isSigned.isVerifiedUntrusted:hover,
 | 
			
		||||
.repository #repo-files-table .sha.label.isSigned.isVerifiedUntrusted:hover,
 | 
			
		||||
.repository #repo-file-commit-box .sha.label.isSigned.isVerifiedUntrusted:hover,
 | 
			
		||||
| 
						 | 
				
			
			@ -1406,6 +1426,7 @@ td .commit-summary {
 | 
			
		|||
  background: var(--color-yellow-badge-hover-bg) !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#activity-feed .sha.label.isSigned.isVerifiedUnmatched,
 | 
			
		||||
.repository #commits-table td.sha .sha.label.isSigned.isVerifiedUnmatched,
 | 
			
		||||
.repository #repo-files-table .sha.label.isSigned.isVerifiedUnmatched,
 | 
			
		||||
.repository #repo-file-commit-box .sha.label.isSigned.isVerifiedUnmatched,
 | 
			
		||||
| 
						 | 
				
			
			@ -1415,6 +1436,7 @@ td .commit-summary {
 | 
			
		|||
  background: var(--color-orange-badge-bg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#activity-feed .sha.label.isSigned.isVerifiedUnmatched .detail.icon,
 | 
			
		||||
.repository #commits-table td.sha .sha.label.isSigned.isVerifiedUnmatched .detail.icon,
 | 
			
		||||
.repository #repo-files-table .sha.label.isSigned.isVerifiedUnmatched .detail.icon,
 | 
			
		||||
.repository #repo-file-commit-box .sha.label.isSigned.isVerifiedUnmatched .detail.icon,
 | 
			
		||||
| 
						 | 
				
			
			@ -1424,6 +1446,7 @@ td .commit-summary {
 | 
			
		|||
  color: var(--color-orange-badge);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#activity-feed .sha.label.isSigned.isVerifiedUnmatched:hover,
 | 
			
		||||
.repository #commits-table td.sha .sha.label.isSigned.isVerifiedUnmatched:hover,
 | 
			
		||||
.repository #repo-files-table .sha.label.isSigned.isVerifiedUnmatched:hover,
 | 
			
		||||
.repository #repo-file-commit-box .sha.label.isSigned.isVerifiedUnmatched:hover,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue