mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-27 00:23:41 +09:00 
			
		
		
		
	Fix git commit committer parsing and add some tests (#35007)
* Fix #34991 * Fix #34882 --------- Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
		| @@ -39,7 +39,6 @@ RUN chmod 755 /tmp/local/usr/bin/entrypoint \ | |||||||
|               /tmp/local/etc/s6/.s6-svscan/* \ |               /tmp/local/etc/s6/.s6-svscan/* \ | ||||||
|               /go/src/code.gitea.io/gitea/gitea \ |               /go/src/code.gitea.io/gitea/gitea \ | ||||||
|               /go/src/code.gitea.io/gitea/environment-to-ini |               /go/src/code.gitea.io/gitea/environment-to-ini | ||||||
| RUN chmod 644 /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete |  | ||||||
|  |  | ||||||
| FROM docker.io/library/alpine:3.22 | FROM docker.io/library/alpine:3.22 | ||||||
| LABEL maintainer="maintainers@gitea.io" | LABEL maintainer="maintainers@gitea.io" | ||||||
| @@ -83,4 +82,3 @@ CMD ["/usr/bin/s6-svscan", "/etc/s6"] | |||||||
| COPY --from=build-env /tmp/local / | COPY --from=build-env /tmp/local / | ||||||
| COPY --from=build-env /go/src/code.gitea.io/gitea/gitea /app/gitea/gitea | COPY --from=build-env /go/src/code.gitea.io/gitea/gitea /app/gitea/gitea | ||||||
| COPY --from=build-env /go/src/code.gitea.io/gitea/environment-to-ini /usr/local/bin/environment-to-ini | COPY --from=build-env /go/src/code.gitea.io/gitea/environment-to-ini /usr/local/bin/environment-to-ini | ||||||
| COPY --from=build-env /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete /etc/profile.d/gitea_bash_autocomplete.sh |  | ||||||
|   | |||||||
| @@ -37,7 +37,6 @@ RUN chmod 755 /tmp/local/usr/local/bin/docker-entrypoint.sh \ | |||||||
|               /tmp/local/usr/local/bin/gitea \ |               /tmp/local/usr/local/bin/gitea \ | ||||||
|               /go/src/code.gitea.io/gitea/gitea \ |               /go/src/code.gitea.io/gitea/gitea \ | ||||||
|               /go/src/code.gitea.io/gitea/environment-to-ini |               /go/src/code.gitea.io/gitea/environment-to-ini | ||||||
| RUN chmod 644 /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete |  | ||||||
|  |  | ||||||
| FROM docker.io/library/alpine:3.22 | FROM docker.io/library/alpine:3.22 | ||||||
| LABEL maintainer="maintainers@gitea.io" | LABEL maintainer="maintainers@gitea.io" | ||||||
| @@ -72,7 +71,6 @@ RUN chown git:git /var/lib/gitea /etc/gitea | |||||||
| COPY --from=build-env /tmp/local / | COPY --from=build-env /tmp/local / | ||||||
| COPY --from=build-env --chown=root:root /go/src/code.gitea.io/gitea/gitea /app/gitea/gitea | COPY --from=build-env --chown=root:root /go/src/code.gitea.io/gitea/gitea /app/gitea/gitea | ||||||
| COPY --from=build-env --chown=root:root /go/src/code.gitea.io/gitea/environment-to-ini /usr/local/bin/environment-to-ini | COPY --from=build-env --chown=root:root /go/src/code.gitea.io/gitea/environment-to-ini /usr/local/bin/environment-to-ini | ||||||
| COPY --from=build-env /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete /etc/profile.d/gitea_bash_autocomplete.sh |  | ||||||
|  |  | ||||||
| # git:git | # git:git | ||||||
| USER 1000:1000 | USER 1000:1000 | ||||||
|   | |||||||
| @@ -15,25 +15,6 @@ import ( | |||||||
| 	"github.com/ProtonMail/go-crypto/openpgp/packet" | 	"github.com/ProtonMail/go-crypto/openpgp/packet" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| //   __________________  ________   ____  __. |  | ||||||
| //  /  _____/\______   \/  _____/  |    |/ _|____ ___.__. |  | ||||||
| // /   \  ___ |     ___/   \  ___  |      <_/ __ <   |  | |  | ||||||
| // \    \_\  \|    |   \    \_\  \ |    |  \  ___/\___  | |  | ||||||
| //  \______  /|____|    \______  / |____|__ \___  > ____| |  | ||||||
| //         \/                  \/          \/   \/\/ |  | ||||||
| // _________                        .__  __ |  | ||||||
| // \_   ___ \  ____   _____   _____ |__|/  |_ |  | ||||||
| // /    \  \/ /  _ \ /     \ /     \|  \   __\ |  | ||||||
| // \     \___(  <_> )  Y Y  \  Y Y  \  ||  | |  | ||||||
| //  \______  /\____/|__|_|  /__|_|  /__||__| |  | ||||||
| //         \/             \/      \/ |  | ||||||
| // ____   ____           .__  _____.__               __  .__ |  | ||||||
| // \   \ /   /___________|__|/ ____\__| ____ _____ _/  |_|__| ____   ____ |  | ||||||
| //  \   Y   // __ \_  __ \  \   __\|  |/ ___\\__  \\   __\  |/  _ \ /    \ |  | ||||||
| //   \     /\  ___/|  | \/  ||  |  |  \  \___ / __ \|  | |  (  <_> )   |  \ |  | ||||||
| //    \___/  \___  >__|  |__||__|  |__|\___  >____  /__| |__|\____/|___|  / |  | ||||||
| //               \/                        \/     \/                    \/ |  | ||||||
|  |  | ||||||
| // This file provides functions relating commit verification | // This file provides functions relating commit verification | ||||||
|  |  | ||||||
| // CommitVerification represents a commit validation of signature | // CommitVerification represents a commit validation of signature | ||||||
| @@ -41,8 +22,8 @@ type CommitVerification struct { | |||||||
| 	Verified       bool | 	Verified       bool | ||||||
| 	Warning        bool | 	Warning        bool | ||||||
| 	Reason         string | 	Reason         string | ||||||
| 	SigningUser    *user_model.User | 	SigningUser    *user_model.User // if Verified, then SigningUser is non-nil | ||||||
| 	CommittingUser *user_model.User | 	CommittingUser *user_model.User // if Verified, then CommittingUser is non-nil | ||||||
| 	SigningEmail   string | 	SigningEmail   string | ||||||
| 	SigningKey     *GPGKey | 	SigningKey     *GPGKey | ||||||
| 	SigningSSHKey  *PublicKey | 	SigningSSHKey  *PublicKey | ||||||
|   | |||||||
| @@ -1166,12 +1166,6 @@ func ValidateCommitsWithEmails(ctx context.Context, oldCommits []*git.Commit) ([ | |||||||
|  |  | ||||||
| 	for _, c := range oldCommits { | 	for _, c := range oldCommits { | ||||||
| 		user := emailUserMap.GetByEmail(c.Author.Email) // FIXME: why ValidateCommitsWithEmails uses "Author", but ParseCommitsWithSignature uses "Committer"? | 		user := emailUserMap.GetByEmail(c.Author.Email) // FIXME: why ValidateCommitsWithEmails uses "Author", but ParseCommitsWithSignature uses "Committer"? | ||||||
| 		if user == nil { |  | ||||||
| 			user = &User{ |  | ||||||
| 				Name:  c.Author.Name, |  | ||||||
| 				Email: c.Author.Email, |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		newCommits = append(newCommits, &UserCommit{ | 		newCommits = append(newCommits, &UserCommit{ | ||||||
| 			User:   user, | 			User:   user, | ||||||
| 			Commit: c, | 			Commit: c, | ||||||
| @@ -1195,12 +1189,14 @@ func GetUsersByEmails(ctx context.Context, emails []string) (*EmailUserMap, erro | |||||||
|  |  | ||||||
| 	needCheckEmails := make(container.Set[string]) | 	needCheckEmails := make(container.Set[string]) | ||||||
| 	needCheckUserNames := make(container.Set[string]) | 	needCheckUserNames := make(container.Set[string]) | ||||||
|  | 	noReplyAddressSuffix := "@" + strings.ToLower(setting.Service.NoReplyAddress) | ||||||
| 	for _, email := range emails { | 	for _, email := range emails { | ||||||
| 		if strings.HasSuffix(email, "@"+setting.Service.NoReplyAddress) { | 		emailLower := strings.ToLower(email) | ||||||
| 			username := strings.TrimSuffix(email, "@"+setting.Service.NoReplyAddress) | 		if noReplyUserNameLower, ok := strings.CutSuffix(emailLower, noReplyAddressSuffix); ok { | ||||||
| 			needCheckUserNames.Add(strings.ToLower(username)) | 			needCheckUserNames.Add(noReplyUserNameLower) | ||||||
|  | 			needCheckEmails.Add(emailLower) | ||||||
| 		} else { | 		} else { | ||||||
| 			needCheckEmails.Add(strings.ToLower(email)) | 			needCheckEmails.Add(emailLower) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -85,6 +85,11 @@ func TestUserEmails(t *testing.T) { | |||||||
| 				testGetUserByEmail(t, c.Email, c.UID) | 				testGetUserByEmail(t, c.Email, c.UID) | ||||||
| 			}) | 			}) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | 		t.Run("NoReplyConflict", func(t *testing.T) { | ||||||
|  | 			setting.Service.NoReplyAddress = "example.com" | ||||||
|  | 			testGetUserByEmail(t, "user1-2@example.COM", 1) | ||||||
|  | 		}) | ||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -22,9 +22,9 @@ import ( | |||||||
| type Commit struct { | type Commit struct { | ||||||
| 	Tree // FIXME: bad design, this field can be nil if the commit is from "last commit cache" | 	Tree // FIXME: bad design, this field can be nil if the commit is from "last commit cache" | ||||||
|  |  | ||||||
| 	ID            ObjectID // The ID of this commit object | 	ID            ObjectID | ||||||
| 	Author        *Signature | 	Author        *Signature // never nil | ||||||
| 	Committer     *Signature | 	Committer     *Signature // never nil | ||||||
| 	CommitMessage string | 	CommitMessage string | ||||||
| 	Signature     *CommitSignature | 	Signature     *CommitSignature | ||||||
|  |  | ||||||
|   | |||||||
| @@ -24,47 +24,43 @@ import ( | |||||||
|  |  | ||||||
| // ParseCommitWithSignature check if signature is good against keystore. | // ParseCommitWithSignature check if signature is good against keystore. | ||||||
| func ParseCommitWithSignature(ctx context.Context, c *git.Commit) *asymkey_model.CommitVerification { | func ParseCommitWithSignature(ctx context.Context, c *git.Commit) *asymkey_model.CommitVerification { | ||||||
| 	var committer *user_model.User | 	committer, err := user_model.GetUserByEmail(ctx, c.Committer.Email) | ||||||
| 	if c.Committer != nil { | 	if err != nil && !user_model.IsErrUserNotExist(err) { | ||||||
| 		var err error | 		log.Error("GetUserByEmail: %v", err) | ||||||
| 		// Find Committer account | 		return &asymkey_model.CommitVerification{ | ||||||
| 		committer, err = user_model.GetUserByEmail(ctx, c.Committer.Email) // This finds the user by primary email or activated email so commit will not be valid if email is not | 			Verified: false, | ||||||
| 		if err != nil {                                                    // Skipping not user for committer | 			Reason:   "gpg.error.no_committer_account", // this error is not right, but such error should seldom happen | ||||||
| 			committer = &user_model.User{ |  | ||||||
| 				Name:  c.Committer.Name, |  | ||||||
| 				Email: c.Committer.Email, |  | ||||||
| 			} |  | ||||||
| 			// We can expect this to often be an ErrUserNotExist. in the case |  | ||||||
| 			// it is not, however, it is important to log it. |  | ||||||
| 			if !user_model.IsErrUserNotExist(err) { |  | ||||||
| 				log.Error("GetUserByEmail: %v", err) |  | ||||||
| 				return &asymkey_model.CommitVerification{ |  | ||||||
| 					CommittingUser: committer, |  | ||||||
| 					Verified:       false, |  | ||||||
| 					Reason:         "gpg.error.no_committer_account", |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return ParseCommitWithSignatureCommitter(ctx, c, committer) | 	return ParseCommitWithSignatureCommitter(ctx, c, committer) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // ParseCommitWithSignatureCommitter parses a commit's GPG or SSH signature. | ||||||
|  | // If the commit is singed by an instance key, then committer can be nil. | ||||||
|  | // If the signature exists, even if committer is nil, the returned CommittingUser will be a non-nil fake user. | ||||||
| func ParseCommitWithSignatureCommitter(ctx context.Context, c *git.Commit, committer *user_model.User) *asymkey_model.CommitVerification { | func ParseCommitWithSignatureCommitter(ctx context.Context, c *git.Commit, committer *user_model.User) *asymkey_model.CommitVerification { | ||||||
| 	// If no signature just report the committer | 	// If no signature, just report the committer | ||||||
| 	if c.Signature == nil { | 	if c.Signature == nil { | ||||||
| 		return &asymkey_model.CommitVerification{ | 		return &asymkey_model.CommitVerification{ | ||||||
| 			CommittingUser: committer, | 			CommittingUser: committer, | ||||||
| 			Verified:       false,                         // Default value | 			Verified:       false, | ||||||
| 			Reason:         "gpg.error.not_signed_commit", // Default value | 			Reason:         "gpg.error.not_signed_commit", | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  | 	// to support instance key, we need a fake committer user (not really needed, but legacy code accesses the committer without nil-check) | ||||||
| 	// If this a SSH signature handle it differently | 	if committer == nil { | ||||||
| 	if strings.HasPrefix(c.Signature.Signature, "-----BEGIN SSH SIGNATURE-----") { | 		committer = &user_model.User{ | ||||||
| 		return ParseCommitWithSSHSignature(ctx, c, committer) | 			Name:  c.Committer.Name, | ||||||
|  | 			Email: c.Committer.Email, | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
|  | 	if strings.HasPrefix(c.Signature.Signature, "-----BEGIN SSH SIGNATURE-----") { | ||||||
|  | 		return parseCommitWithSSHSignature(ctx, c, committer) | ||||||
|  | 	} | ||||||
|  | 	return parseCommitWithGPGSignature(ctx, c, committer) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func parseCommitWithGPGSignature(ctx context.Context, c *git.Commit, committer *user_model.User) *asymkey_model.CommitVerification { | ||||||
| 	// Parsing signature | 	// Parsing signature | ||||||
| 	sig, err := asymkey_model.ExtractSignature(c.Signature.Signature) | 	sig, err := asymkey_model.ExtractSignature(c.Signature.Signature) | ||||||
| 	if err != nil { // Skipping failed to extract sign | 	if err != nil { // Skipping failed to extract sign | ||||||
| @@ -165,7 +161,7 @@ func ParseCommitWithSignatureCommitter(ctx context.Context, c *git.Commit, commi | |||||||
| 		} | 		} | ||||||
| 		if err := gpgSettings.LoadPublicKeyContent(); err != nil { | 		if err := gpgSettings.LoadPublicKeyContent(); err != nil { | ||||||
| 			log.Error("Error getting default signing key: %s %v", gpgSettings.KeyID, err) | 			log.Error("Error getting default signing key: %s %v", gpgSettings.KeyID, err) | ||||||
| 		} else if commitVerification := VerifyWithGPGSettings(ctx, &gpgSettings, sig, c.Signature.Payload, committer, keyID); commitVerification != nil { | 		} else if commitVerification := verifyWithGPGSettings(ctx, &gpgSettings, sig, c.Signature.Payload, committer, keyID); commitVerification != nil { | ||||||
| 			if commitVerification.Reason == asymkey_model.BadSignature { | 			if commitVerification.Reason == asymkey_model.BadSignature { | ||||||
| 				defaultReason = asymkey_model.BadSignature | 				defaultReason = asymkey_model.BadSignature | ||||||
| 			} else { | 			} else { | ||||||
| @@ -180,7 +176,7 @@ func ParseCommitWithSignatureCommitter(ctx context.Context, c *git.Commit, commi | |||||||
| 	} else if defaultGPGSettings == nil { | 	} else if defaultGPGSettings == nil { | ||||||
| 		log.Warn("Unable to get defaultGPGSettings for unattached commit: %s", c.ID.String()) | 		log.Warn("Unable to get defaultGPGSettings for unattached commit: %s", c.ID.String()) | ||||||
| 	} else if defaultGPGSettings.Sign { | 	} else if defaultGPGSettings.Sign { | ||||||
| 		if commitVerification := VerifyWithGPGSettings(ctx, defaultGPGSettings, sig, c.Signature.Payload, committer, keyID); commitVerification != nil { | 		if commitVerification := verifyWithGPGSettings(ctx, defaultGPGSettings, sig, c.Signature.Payload, committer, keyID); commitVerification != nil { | ||||||
| 			if commitVerification.Reason == asymkey_model.BadSignature { | 			if commitVerification.Reason == asymkey_model.BadSignature { | ||||||
| 				defaultReason = asymkey_model.BadSignature | 				defaultReason = asymkey_model.BadSignature | ||||||
| 			} else { | 			} else { | ||||||
| @@ -295,7 +291,7 @@ func HashAndVerifyForKeyID(ctx context.Context, sig *packet.Signature, payload s | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func VerifyWithGPGSettings(ctx context.Context, gpgSettings *git.GPGSettings, sig *packet.Signature, payload string, committer *user_model.User, keyID string) *asymkey_model.CommitVerification { | func verifyWithGPGSettings(ctx context.Context, gpgSettings *git.GPGSettings, sig *packet.Signature, payload string, committer *user_model.User, keyID string) *asymkey_model.CommitVerification { | ||||||
| 	// First try to find the key in the db | 	// First try to find the key in the db | ||||||
| 	if commitVerification := HashAndVerifyForKeyID(ctx, sig, payload, committer, gpgSettings.KeyID, gpgSettings.Name, gpgSettings.Email); commitVerification != nil { | 	if commitVerification := HashAndVerifyForKeyID(ctx, sig, payload, committer, gpgSettings.KeyID, gpgSettings.Name, gpgSettings.Email); commitVerification != nil { | ||||||
| 		return commitVerification | 		return commitVerification | ||||||
| @@ -375,8 +371,8 @@ func verifySSHCommitVerificationByInstanceKey(c *git.Commit, committerUser, sign | |||||||
| 	return verifySSHCommitVerification(c.Signature.Signature, c.Signature.Payload, sshPubKey, committerUser, signerUser, committerGitEmail) | 	return verifySSHCommitVerification(c.Signature.Signature, c.Signature.Payload, sshPubKey, committerUser, signerUser, committerGitEmail) | ||||||
| } | } | ||||||
|  |  | ||||||
| // ParseCommitWithSSHSignature check if signature is good against keystore. | // parseCommitWithSSHSignature check if signature is good against keystore. | ||||||
| func ParseCommitWithSSHSignature(ctx context.Context, c *git.Commit, committerUser *user_model.User) *asymkey_model.CommitVerification { | func parseCommitWithSSHSignature(ctx context.Context, c *git.Commit, committerUser *user_model.User) *asymkey_model.CommitVerification { | ||||||
| 	// Now try to associate the signature with the committer, if present | 	// Now try to associate the signature with the committer, if present | ||||||
| 	if committerUser.ID != 0 { | 	if committerUser.ID != 0 { | ||||||
| 		keys, err := db.Find[asymkey_model.PublicKey](ctx, asymkey_model.FindPublicKeyOptions{ | 		keys, err := db.Find[asymkey_model.PublicKey](ctx, asymkey_model.FindPublicKeyOptions{ | ||||||
|   | |||||||
| @@ -41,7 +41,7 @@ Initial commit with signed file | |||||||
| 			Name:  "User Two", | 			Name:  "User Two", | ||||||
| 			Email: "user2@example.com", | 			Email: "user2@example.com", | ||||||
| 		} | 		} | ||||||
| 		ret := ParseCommitWithSSHSignature(t.Context(), commit, committingUser) | 		ret := parseCommitWithSSHSignature(t.Context(), commit, committingUser) | ||||||
| 		require.NotNil(t, ret) | 		require.NotNil(t, ret) | ||||||
| 		assert.True(t, ret.Verified) | 		assert.True(t, ret.Verified) | ||||||
| 		assert.False(t, ret.Warning) | 		assert.False(t, ret.Warning) | ||||||
|   | |||||||
| @@ -35,13 +35,6 @@ func ParseCommitsWithSignature(ctx context.Context, repo *repo_model.Repository, | |||||||
|  |  | ||||||
| 	for _, c := range oldCommits { | 	for _, c := range oldCommits { | ||||||
| 		committerUser := emailUsers.GetByEmail(c.Committer.Email) // FIXME: why ValidateCommitsWithEmails uses "Author", but ParseCommitsWithSignature uses "Committer"? | 		committerUser := emailUsers.GetByEmail(c.Committer.Email) // FIXME: why ValidateCommitsWithEmails uses "Author", but ParseCommitsWithSignature uses "Committer"? | ||||||
| 		if committerUser == nil { |  | ||||||
| 			committerUser = &user_model.User{ |  | ||||||
| 				Name:  c.Committer.Name, |  | ||||||
| 				Email: c.Committer.Email, |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		signCommit := &asymkey_model.SignCommit{ | 		signCommit := &asymkey_model.SignCommit{ | ||||||
| 			UserCommit:   c, | 			UserCommit:   c, | ||||||
| 			Verification: asymkey_service.ParseCommitWithSignatureCommitter(ctx, c.Commit, committerUser), | 			Verification: asymkey_service.ParseCommitWithSignatureCommitter(ctx, c.Commit, committerUser), | ||||||
|   | |||||||
| @@ -147,7 +147,7 @@ | |||||||
| 			<div class="flex-text-inline"> | 			<div class="flex-text-inline"> | ||||||
| 				{{if or (ne .Commit.Committer.Name .Commit.Author.Name) (ne .Commit.Committer.Email .Commit.Author.Email)}} | 				{{if or (ne .Commit.Committer.Name .Commit.Author.Name) (ne .Commit.Committer.Email .Commit.Author.Email)}} | ||||||
| 					<span class="text grey">{{ctx.Locale.Tr "repo.diff.committed_by"}}</span> | 					<span class="text grey">{{ctx.Locale.Tr "repo.diff.committed_by"}}</span> | ||||||
| 					{{if ne .Verification.CommittingUser.ID 0}} | 					{{if and .Verification.CommittingUser .Verification.CommittingUser.ID}} | ||||||
| 						{{ctx.AvatarUtils.Avatar .Verification.CommittingUser 20}} | 						{{ctx.AvatarUtils.Avatar .Verification.CommittingUser 20}} | ||||||
| 						<a href="{{.Verification.CommittingUser.HomeLink}}"><strong>{{.Commit.Committer.Name}}</strong></a> | 						<a href="{{.Verification.CommittingUser.HomeLink}}"><strong>{{.Commit.Committer.Name}}</strong></a> | ||||||
| 					{{else}} | 					{{else}} | ||||||
|   | |||||||
| @@ -16,7 +16,7 @@ | |||||||
| 					<td class="author"> | 					<td class="author"> | ||||||
| 						<div class="tw-flex"> | 						<div class="tw-flex"> | ||||||
| 							{{$userName := .Author.Name}} | 							{{$userName := .Author.Name}} | ||||||
| 							{{if and .User (gt .User.ID 0)}} /* User with id == 0 is a fake user from git author */ | 							{{if .User}} | ||||||
| 								{{if and .User.FullName DefaultShowFullName}} | 								{{if and .User.FullName DefaultShowFullName}} | ||||||
| 									{{$userName = .User.FullName}} | 									{{$userName = .User.FullName}} | ||||||
| 								{{end}} | 								{{end}} | ||||||
|   | |||||||
| @@ -24,39 +24,59 @@ import ( | |||||||
|  |  | ||||||
| func TestRepoCommits(t *testing.T) { | func TestRepoCommits(t *testing.T) { | ||||||
| 	defer tests.PrepareTestEnv(t)() | 	defer tests.PrepareTestEnv(t)() | ||||||
|  |  | ||||||
| 	session := loginUser(t, "user2") | 	session := loginUser(t, "user2") | ||||||
|  |  | ||||||
| 	// Request repository commits page | 	t.Run("CommitList", func(t *testing.T) { | ||||||
| 	req := NewRequest(t, "GET", "/user2/repo1/commits/branch/master") | 		req := NewRequest(t, "GET", "/user2/repo16/commits/branch/master") | ||||||
| 	resp := session.MakeRequest(t, req, http.StatusOK) | 		resp := session.MakeRequest(t, req, http.StatusOK) | ||||||
|  |  | ||||||
| 	doc := NewHTMLParser(t, resp.Body) | 		var commits, userHrefs []string | ||||||
| 	commitURL, exists := doc.doc.Find("#commits-table .commit-id-short").Attr("href") | 		doc := NewHTMLParser(t, resp.Body) | ||||||
| 	assert.True(t, exists) | 		doc.doc.Find("#commits-table .commit-id-short").Each(func(i int, s *goquery.Selection) { | ||||||
| 	assert.NotEmpty(t, commitURL) | 			commits = append(commits, path.Base(s.AttrOr("href", ""))) | ||||||
| } | 		}) | ||||||
|  | 		doc.doc.Find("#commits-table .author-wrapper").Each(func(i int, s *goquery.Selection) { | ||||||
| func Test_ReposGitCommitListNotMaster(t *testing.T) { | 			userHrefs = append(userHrefs, s.AttrOr("href", "")) | ||||||
| 	defer tests.PrepareTestEnv(t)() | 		}) | ||||||
| 	session := loginUser(t, "user2") | 		assert.Equal(t, []string{"69554a64c1e6030f051e5c3f94bfbd773cd6a324", "27566bd5738fc8b4e3fef3c5e72cce608537bd95", "5099b81332712fe655e34e8dd63574f503f61811"}, commits) | ||||||
| 	req := NewRequest(t, "GET", "/user2/repo16/commits/branch/master") | 		assert.Equal(t, []string{"/user2", "/user21", "/user2"}, userHrefs) | ||||||
| 	resp := session.MakeRequest(t, req, http.StatusOK) |  | ||||||
|  |  | ||||||
| 	doc := NewHTMLParser(t, resp.Body) |  | ||||||
| 	var commits []string |  | ||||||
| 	doc.doc.Find("#commits-table .commit-id-short").Each(func(i int, s *goquery.Selection) { |  | ||||||
| 		commitURL, _ := s.Attr("href") |  | ||||||
| 		commits = append(commits, path.Base(commitURL)) |  | ||||||
| 	}) | 	}) | ||||||
| 	assert.Equal(t, []string{"69554a64c1e6030f051e5c3f94bfbd773cd6a324", "27566bd5738fc8b4e3fef3c5e72cce608537bd95", "5099b81332712fe655e34e8dd63574f503f61811"}, commits) |  | ||||||
|  |  | ||||||
| 	var userHrefs []string | 	t.Run("LastCommit", func(t *testing.T) { | ||||||
| 	doc.doc.Find("#commits-table .author-wrapper").Each(func(i int, s *goquery.Selection) { | 		req := NewRequest(t, "GET", "/user2/repo16") | ||||||
| 		userHref, _ := s.Attr("href") | 		resp := session.MakeRequest(t, req, http.StatusOK) | ||||||
| 		userHrefs = append(userHrefs, userHref) | 		doc := NewHTMLParser(t, resp.Body) | ||||||
|  | 		commitHref := doc.doc.Find(".latest-commit .commit-id-short").AttrOr("href", "") | ||||||
|  | 		authorHref := doc.doc.Find(".latest-commit .author-wrapper").AttrOr("href", "") | ||||||
|  | 		assert.Equal(t, "/user2/repo16/commit/69554a64c1e6030f051e5c3f94bfbd773cd6a324", commitHref) | ||||||
|  | 		assert.Equal(t, "/user2", authorHref) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	t.Run("CommitListNonExistingCommiter", func(t *testing.T) { | ||||||
|  | 		// check the commit list for a repository with no gitea user | ||||||
|  | 		// * commit 985f0301dba5e7b34be866819cd15ad3d8f508ee (branch2) | ||||||
|  | 		// * Author: 6543 <6543@obermui.de> | ||||||
|  | 		req := NewRequest(t, "GET", "/user2/repo1/commits/branch/branch2") | ||||||
|  | 		resp := session.MakeRequest(t, req, http.StatusOK) | ||||||
|  |  | ||||||
|  | 		doc := NewHTMLParser(t, resp.Body) | ||||||
|  | 		commitHref := doc.doc.Find("#commits-table tr:first-child .commit-id-short").AttrOr("href", "") | ||||||
|  | 		assert.Equal(t, "/user2/repo1/commit/985f0301dba5e7b34be866819cd15ad3d8f508ee", commitHref) | ||||||
|  | 		authorElem := doc.doc.Find("#commits-table tr:first-child .author-wrapper") | ||||||
|  | 		assert.Equal(t, "6543", authorElem.Text()) | ||||||
|  | 		assert.Equal(t, "span", authorElem.Nodes[0].Data) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	t.Run("LastCommitNonExistingCommiter", func(t *testing.T) { | ||||||
|  | 		req := NewRequest(t, "GET", "/user2/repo1/src/branch/branch2") | ||||||
|  | 		resp := session.MakeRequest(t, req, http.StatusOK) | ||||||
|  | 		doc := NewHTMLParser(t, resp.Body) | ||||||
|  | 		commitHref := doc.doc.Find(".latest-commit .commit-id-short").AttrOr("href", "") | ||||||
|  | 		assert.Equal(t, "/user2/repo1/commit/985f0301dba5e7b34be866819cd15ad3d8f508ee", commitHref) | ||||||
|  | 		authorElem := doc.doc.Find(".latest-commit .author-wrapper") | ||||||
|  | 		assert.Equal(t, "6543", authorElem.Text()) | ||||||
|  | 		assert.Equal(t, "span", authorElem.Nodes[0].Data) | ||||||
| 	}) | 	}) | ||||||
| 	assert.Equal(t, []string{"/user2", "/user21", "/user2"}, userHrefs) |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func doTestRepoCommitWithStatus(t *testing.T, state string, classes ...string) { | func doTestRepoCommitWithStatus(t *testing.T, state string, classes ...string) { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user