diff --git a/routers/web/repo/release.go b/routers/web/repo/release.go
index 38bb1305f..54e9aed20 100644
--- a/routers/web/repo/release.go
+++ b/routers/web/repo/release.go
@@ -11,6 +11,7 @@ import (
"strings"
"code.gitea.io/gitea/models"
+ "code.gitea.io/gitea/models/asymkey"
"code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git"
repo_model "code.gitea.io/gitea/models/repo"
@@ -18,6 +19,7 @@ import (
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/markup/markdown"
@@ -192,6 +194,7 @@ func Releases(ctx *context.Context) {
}
ctx.Data["Releases"] = releases
+ addVerifyTagToContext(ctx)
numReleases := ctx.Data["NumReleases"].(int64)
pager := context.NewPagination(int(numReleases), listOptions.PageSize, listOptions.Page, 5)
@@ -201,6 +204,44 @@ func Releases(ctx *context.Context) {
ctx.HTML(http.StatusOK, tplReleasesList)
}
+func verifyTagSignature(ctx *context.Context, r *repo_model.Release) (*asymkey.ObjectVerification, error) {
+ if err := r.LoadAttributes(ctx); err != nil {
+ return nil, err
+ }
+ gitRepo, err := gitrepo.OpenRepository(ctx, r.Repo)
+ if err != nil {
+ return nil, err
+ }
+ defer gitRepo.Close()
+
+ tag, err := gitRepo.GetTag(r.TagName)
+ if err != nil {
+ return nil, err
+ }
+ if tag.Signature == nil {
+ return nil, nil
+ }
+
+ verification := asymkey.ParseTagWithSignature(ctx, gitRepo, tag)
+ return verification, nil
+}
+
+func addVerifyTagToContext(ctx *context.Context) {
+ ctx.Data["VerifyTag"] = func(r *repo_model.Release) *asymkey.ObjectVerification {
+ v, err := verifyTagSignature(ctx, r)
+ if err != nil {
+ return nil
+ }
+ return v
+ }
+ ctx.Data["HasSignature"] = func(verification *asymkey.ObjectVerification) bool {
+ if verification == nil {
+ return false
+ }
+ return verification.Reason != "gpg.error.not_signed_commit"
+ }
+}
+
// TagsList render tags list page
func TagsList(ctx *context.Context) {
ctx.Data["PageIsTagList"] = true
@@ -240,6 +281,7 @@ func TagsList(ctx *context.Context) {
}
ctx.Data["Releases"] = releases
+ addVerifyTagToContext(ctx)
numTags := ctx.Data["NumTags"].(int64)
pager := context.NewPagination(int(numTags), opts.PageSize, opts.Page, 5)
@@ -304,6 +346,7 @@ func SingleRelease(ctx *context.Context) {
if release.IsTag && release.Title == "" {
release.Title = release.TagName
}
+ addVerifyTagToContext(ctx)
ctx.Data["PageIsSingleTag"] = release.IsTag
if release.IsTag {
diff --git a/templates/repo/release/list.tmpl b/templates/repo/release/list.tmpl
index 5aa4e51f3..f3b4bc844 100644
--- a/templates/repo/release/list.tmpl
+++ b/templates/repo/release/list.tmpl
@@ -60,6 +60,7 @@
{{$release.RenderedNote}}
+ {{template "repo/tag/verification_line" (dict "ctxData" $ "release" $release)}}
diff --git a/templates/repo/tag/list.tmpl b/templates/repo/tag/list.tmpl
index 5378a8a32..82f3dc04a 100644
--- a/templates/repo/tag/list.tmpl
+++ b/templates/repo/tag/list.tmpl
@@ -16,12 +16,13 @@
{{range $idx, $release := .Releases}}
-
+
{{if $canReadReleases}}
{{.TagName}}
{{else}}
{{.TagName}}
{{end}}
+ {{template "repo/tag/verification_box" (dict "ctxData" $ "release" $release)}}
{{if $.Permission.CanRead $.UnitTypeCode}}
diff --git a/templates/repo/tag/verification_box.tmpl b/templates/repo/tag/verification_box.tmpl
new file mode 100644
index 000000000..3cf88ac23
--- /dev/null
+++ b/templates/repo/tag/verification_box.tmpl
@@ -0,0 +1,27 @@
+{{$v := call .ctxData.VerifyTag .release}}
+{{if call .ctxData.HasSignature $v}}
+ {{$class := "isSigned"}}
+ {{$href := ""}}
+ {{if $v.Verified}}
+ {{$href = $v.SigningUser.HomeLink}}
+ {{$class = (print $class " isVerified")}}
+ {{else}}
+ {{$class = (print $class " isWarning")}}
+ {{end}}
+
+
+ {{if $v.Verified}}
+
+ {{if ne $v.SigningUser.ID 0}}
+ {{svg "gitea-lock"}}
+ {{ctx.AvatarUtils.Avatar $v.SigningUser 28 "signature"}}
+ {{else}}
+ {{svg "gitea-lock-cog"}}
+ {{ctx.AvatarUtils.AvatarByEmail $v.Verification.SigningEmail "" 28 "signature"}}
+ {{end}}
+
+ {{else}}
+ {{svg "gitea-unlock"}}
+ {{end}}
+
+{{end}}
diff --git a/templates/repo/tag/verification_line.tmpl b/templates/repo/tag/verification_line.tmpl
new file mode 100644
index 000000000..f83719de2
--- /dev/null
+++ b/templates/repo/tag/verification_line.tmpl
@@ -0,0 +1,80 @@
+{{$v := call .ctxData.VerifyTag .release}}
+{{if call .ctxData.HasSignature $v}}
+ {{$class := "isSigned"}}
+ {{$href := ""}}
+ {{if $v.Verified}}
+ {{$href = $v.SigningUser.HomeLink}}
+ {{$class = (print $class " isVerified")}}
+ {{else}}
+ {{$class = (print $class " isWarning")}}
+ {{end}}
+
+
+
+ {{if $v.Verified}}
+ {{if ne $v.SigningUser.ID 0}}
+ {{svg "gitea-lock" 16 "tw-mr-2"}}
+ {{ctx.Locale.Tr "repo.commits.signed_by"}}
+ {{ctx.AvatarUtils.Avatar $v.SigningUser 28 "tw-mr-2"}}
+ {{$v.SigningUser.GetDisplayName}}
+ {{else}}
+ {{svg "gitea-lock-cog" 16 "tw-mr-2"}}
+ {{ctx.Locale.Tr "repo.commits.signed_by"}}:
+ {{ctx.AvatarUtils.AvatarByEmail $v.SigningEmail "" 28 "tw-mr-2"}}
+ {{$v.SigningUser.GetDisplayName}}
+ {{end}}
+ {{else}}
+ {{svg "gitea-unlock" 16 "tw-mr-2"}}
+ {{ctx.Locale.Tr $v.Reason}}
+ {{end}}
+
+
+
+ {{if $v.Verified}}
+ {{if ne $v.SigningUser.ID 0}}
+ {{svg "octicon-verified" 16 "tw-mr-2"}}
+ {{if $v.SigningSSHKey}}
+ {{ctx.Locale.Tr "repo.commits.ssh_key_fingerprint"}}:
+ {{$v.SigningSSHKey.Fingerprint}}
+ {{else}}
+ {{ctx.Locale.Tr "repo.commits.gpg_key_id"}}:
+ {{$v.SigningKey.PaddedKeyID}}
+ {{end}}
+ {{else}}
+ {{svg "octicon-unverified" 16 "tw-mr-2"}}
+ {{if $v.SigningSSHKey}}
+ {{ctx.Locale.Tr "repo.commits.ssh_key_fingerprint"}}:
+ {{$v.SigningSSHKey.Fingerprint}}
+ {{else}}
+ {{ctx.Locale.Tr "repo.commits.gpg_key_id"}}:
+ {{$v.SigningKey.PaddedKeyID}}
+ {{end}}
+ {{end}}
+ {{else if $v.Warning}}
+ {{svg "octicon-unverified" 16 "tw-mr-2"}}
+ {{if $v.SigningSSHKey}}
+ {{ctx.Locale.Tr "repo.commits.ssh_key_fingerprint"}}:
+ {{$v.SigningSSHKey.Fingerprint}}
+ {{else}}
+ {{ctx.Locale.Tr "repo.commits.gpg_key_id"}}:
+ {{$v.SigningKey.PaddedKeyID}}
+ {{end}}
+ {{else}}
+ {{if $v.SigningKey}}
+ {{if ne $v.SigningKey.KeyID ""}}
+ {{svg "octicon-verified" 16 "tw-mr-2"}}
+ {{ctx.Locale.Tr "repo.commits.gpg_key_id"}}:
+ {{$v.SigningKey.PaddedKeyID}}
+ {{end}}
+ {{end}}
+ {{if $v.SigningSSHKey}}
+ {{if ne $v.SigningSSHKey.Fingerprint ""}}
+ {{svg "octicon-verified" 16 "tw-mr-2"}}
+ {{ctx.Locale.Tr "repo.commits.ssh_key_fingerprint"}}:
+ {{$v.SigningSSHKey.Fingerprint}}
+ {{end}}
+ {{end}}
+ {{end}}
+
+
+{{end}}
diff --git a/web_src/css/repo.css b/web_src/css/repo.css
index d28dc4b96..de18100d3 100644
--- a/web_src/css/repo.css
+++ b/web_src/css/repo.css
@@ -1874,6 +1874,31 @@
border-bottom: 1px solid var(--color-warning-border);
}
+.repository .release-tag-name .ui.label.isSigned,
+.repository .release-list-title .ui.label.isSigned {
+ padding: 0 0.5em;
+ box-shadow: none;
+}
+
+.repository .release-tag-name .ui.label.isSigned .avatar,
+.repository .release-list-title .ui.label.isSigned .avatar {
+ margin-left: .5rem;
+}
+
+.repository .release-tag-name .ui.label.isSigned.isVerified,
+.repository .release-list-title .ui.label.isSigned.isVerified {
+ border: 1px solid var(--color-success-border);
+ background-color: var(--color-success-bg);
+ color: var(--color-success-text);
+}
+
+.repository .release-tag-name .ui.label.isSigned.isWarning,
+.repository .release-list-title .ui.label.isSigned.isWarning {
+ border: 1px solid var(--color-warning-border);
+ background-color: var(--color-warning-bg);
+ color: var(--color-warning-text);
+}
+
.repository .segment.reactions.dropdown .menu,
.repository .select-reaction.dropdown .menu {
right: 0 !important;
@@ -2107,12 +2132,19 @@
padding-top: 15px;
}
-.commit-header-row {
+.commit-header-row,
+.tag-signature-row {
min-height: 50px !important;
padding-top: 0 !important;
padding-bottom: 0 !important;
}
+.tag-signature-row div {
+ margin-top: auto !important;
+ margin-bottom: auto !important;
+ display: inline-block !important;
+}
+
.settings.webhooks .list > .item:not(:first-child),
.settings.githooks .list > .item:not(:first-child),
.settings.actions .list > .item:not(:first-child) {
|