- {{$class := ""}}
- {{if .Commit.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}}
-
+
-
- {{$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 := ""}}
+ {{$commitBaseLink := ""}}
{{if $.PageIsWiki}}
- {{$commitShaLink = (printf "%s/wiki/commit/%s" $commitRepoLink (PathEscape .ID.String))}}
+ {{$commitBaseLink = printf "%s/wiki/commit" $commitRepoLink}}
{{else if $.PageIsPullCommits}}
- {{$commitShaLink = (printf "%s/pulls/%d/commits/%s" $commitRepoLink $.Issue.Index (PathEscape .ID.String))}}
+ {{$commitBaseLink = printf "%s/pulls/%d/commits" $commitRepoLink $.Issue.Index}}
{{else if $.Reponame}}
- {{$commitShaLink = (printf "%s/commit/%s" $commitRepoLink (PathEscape .ID.String))}}
+ {{$commitBaseLink = printf "%s/commit" $commitRepoLink}}
{{end}}
-
- {{ShortSha .ID.String}}
- {{if .Signature}}{{template "repo/shabox_badge" dict "root" $ "verification" .Verification}}{{end}}
-
+ {{template "repo/commit_sign_badge" dict "Commit" . "CommitBaseLink" $commitBaseLink "CommitSignVerification" .Verification}}
diff --git a/templates/repo/commits_list_small.tmpl b/templates/repo/commits_list_small.tmpl
index 0657eaba1d..2acf7c58b8 100644
--- a/templates/repo/commits_list_small.tmpl
+++ b/templates/repo/commits_list_small.tmpl
@@ -3,7 +3,7 @@
{{range .comment.Commits}}
{{$tag := printf "%s-%d" $.comment.HashTag $index}}
{{$index = Eval $index "+" 1}}
-
{{ctx.RenderUtils.RenderCommitMessage .Commit.Message ($.Repository.ComposeMetas ctx)}}{{template "repo/commit_statuses" dict "Status" .CommitStatus "Statuses" .CommitStatuses}}
{{if not $.PageIsWiki}} @@ -142,125 +128,59 @@ {{end}} {{template "repo/commit_load_branches_and_tags" .}}
-
- {{if .Author}}
- {{ctx.AvatarUtils.Avatar .Author 28 "tw-mr-2"}}
- {{if .Author.FullName}}
- {{.Author.FullName}}
- {{else}}
- {{.Commit.Author.Name}}
- {{end}}
+
+
diff --git a/templates/repo/commit_sign_badge.tmpl b/templates/repo/commit_sign_badge.tmpl
new file mode 100644
index 0000000000..aa68e9dd23
--- /dev/null
+++ b/templates/repo/commit_sign_badge.tmpl
@@ -0,0 +1,78 @@
+{{/* Template attributes:
+* Commit
+* CommitBaseLink
+* CommitSignVerification
+If you'd like to modify this template, you could test it on the devtest page.
+ATTENTION: this template could be re-rendered many times (on the graph and commit list page),
+so this template should be kept as small as possbile, DO NOT put large components like modal/dialog into it.
+*/}}
+{{- $commit := $.Commit -}}
+{{- $commitBaseLink := $.CommitBaseLink -}}
+{{- $verification := $.CommitSignVerification -}}
+
+{{- $extraClass := "" -}}
+{{- $verified := false -}}
+{{- $signingUser := NIL -}}
+{{- $signingEmail := "" -}}
+{{- $msgReasonPrefix := "" -}}
+{{- $msgReason := "" -}}
+{{- $msgSigningKey := "" -}}
+
+{{- if $verification -}}
+ {{- $signingUser = $verification.SigningUser -}}
+ {{- $signingEmail = $verification.SigningEmail -}}
+ {{- $extraClass = print $extraClass " commit-is-signed" -}}
+ {{- if $verification.Verified -}}
+ {{- /* reason is "{name} / {key-id}" */ -}}
+ {{- $msgReason = $verification.Reason -}}
+ {{- $verified = true -}}
+ {{- if eq $verification.TrustStatus "trusted" -}}
+ {{- $extraClass = print $extraClass " sign-trusted" -}}
+ {{- else if eq $verification.TrustStatus "untrusted" -}}
+ {{- $extraClass = print $extraClass " sign-untrusted" -}}
+ {{- $msgReasonPrefix = ctx.Locale.Tr "repo.commits.signed_by_untrusted_user" -}}
+ {{- else -}}
+ {{- $extraClass = print $extraClass " sign-unmatched" -}}
+ {{- $msgReasonPrefix = ctx.Locale.Tr "repo.commits.signed_by_untrusted_user_unmatched" -}}
+ {{- end -}}
+ {{- else -}}
+ {{- if $verification.Warning -}}
+ {{- $extraClass = print $extraClass " sign-warning" -}}
+ {{- end -}}
+ {{- $msgReason = ctx.Locale.Tr $verification.Reason -}}{{- /* dirty part: it is the translation key ..... */ -}}
+ {{- end -}}
+
+ {{- if $msgReasonPrefix -}}
+ {{- $msgReason = print $msgReasonPrefix ": " $msgReason -}}
+ {{- end -}}
+
+ {{- if $verification.SigningSSHKey -}}
+ {{- $msgSigningKey = print (ctx.Locale.Tr "repo.commits.ssh_key_fingerprint") ": " $verification.SigningSSHKey.Fingerprint -}}
+ {{- else if $verification.SigningKey -}}
+ {{- $msgSigningKey = print (ctx.Locale.Tr "repo.commits.gpg_key_id") ": " $verification.SigningKey.PaddedKeyID -}}
+ {{- end -}}
+{{- end -}}
+
+{{- if $commit -}}
+
+ {{- ShortSha $commit.ID.String -}}
+{{- end -}}
+
+ {{- if $verified -}}
+ {{- if and $signingUser $signingUser.ID -}}
+ {{svg "gitea-lock"}}
+ {{ctx.AvatarUtils.Avatar $signingUser 16}}
+ {{- else -}}
+ {{svg "gitea-lock-cog"}}
+ {{ctx.AvatarUtils.AvatarByEmail $signingEmail "" 16}}
+ {{- end -}}
+ {{- else -}}
+ {{svg "gitea-unlock"}}
+ {{- end -}}
+
+
+{{- if $commit -}}
+
+{{- end -}}
+
+{{- /* This template should be kept as small as possbile, DO NOT put large components like modal/dialog into it. */ -}}
diff --git a/templates/repo/commits_list.tmpl b/templates/repo/commits_list.tmpl
index 50b754cc23..329dc45149 100644
--- a/templates/repo/commits_list.tmpl
+++ b/templates/repo/commits_list.tmpl
@@ -28,33 +28,15 @@
+
{{end}}
+
{{template "repo/diff/box" .}}
+ {{if .Author}}
+ {{ctx.AvatarUtils.Avatar .Author 20}}
+ {{if .Author.FullName}}
+ {{.Author.FullName}}
{{else}}
- {{ctx.AvatarUtils.AvatarByEmail .Commit.Author.Email .Commit.Author.Email 28 "tw-mr-2"}}
- {{.Commit.Author.Name}}
+ {{.Commit.Author.Name}}
{{end}}
- {{DateUtils.TimeSince .Commit.Author.When}}
- {{if or (ne .Commit.Committer.Name .Commit.Author.Name) (ne .Commit.Committer.Email .Commit.Author.Email)}}
- {{ctx.Locale.Tr "repo.diff.committed_by"}}
- {{if ne .Verification.CommittingUser.ID 0}}
- {{ctx.AvatarUtils.Avatar .Verification.CommittingUser 28 "tw-mx-2"}}
- {{.Commit.Committer.Name}}
- {{else}}
- {{ctx.AvatarUtils.AvatarByEmail .Commit.Committer.Email .Commit.Committer.Name 28 "tw-mr-2"}}
- {{.Commit.Committer.Name}}
+ {{else}}
+ {{ctx.AvatarUtils.AvatarByEmail .Commit.Author.Email .Commit.Author.Email 20}}
+ {{.Commit.Author.Name}}
+ {{end}}
+
+
+ {{DateUtils.TimeSince .Commit.Author.When}}
+
+
+ {{if or (ne .Commit.Committer.Name .Commit.Author.Name) (ne .Commit.Committer.Email .Commit.Author.Email)}}
+ {{ctx.Locale.Tr "repo.diff.committed_by"}}
+ {{if ne .Verification.CommittingUser.ID 0}}
+ {{ctx.AvatarUtils.Avatar .Verification.CommittingUser 20}}
+ {{.Commit.Committer.Name}}
+ {{else}}
+ {{ctx.AvatarUtils.AvatarByEmail .Commit.Committer.Email .Commit.Committer.Name 20}}
+ {{.Commit.Committer.Name}}
+ {{end}}
+ {{end}}
+
+
+ {{if .Verification}}
+ {{template "repo/commit_sign_badge" dict "CommitSignVerification" .Verification}}
+ {{end}}
+
+
+
+
+ {{if .Parents}}
+
- {{if .Commit.Signature}}
-
+ {{ctx.Locale.Tr "repo.diff.parent"}}
+ {{range .Parents}}
+ {{ShortSha .}}
{{end}}
- {{end}}
-
-
- {{if .Parents}}
-
-
- {{ctx.Locale.Tr "repo.diff.parent"}}
- {{range .Parents}}
- {{if $.PageIsWiki}}
- {{ShortSha .}}
- {{else}}
- {{ShortSha .}}
- {{end}}
- {{end}}
-
- {{end}}
-
- {{ctx.Locale.Tr "repo.diff.commit"}}
- {{ShortSha .CommitID}}
-
-
+
{{if .NoteRendered}}
- {{if .Verification.Verified}}
- {{if ne .Verification.SigningUser.ID 0}}
- {{svg "gitea-lock" 16 "tw-mr-2"}}
- {{if eq .Verification.TrustStatus "trusted"}}
- {{ctx.Locale.Tr "repo.commits.signed_by"}}:
- {{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}}
- {{ctx.AvatarUtils.Avatar .Verification.SigningUser 28 "tw-mr-2"}}
- {{.Verification.SigningUser.GetDisplayName}}
- {{else}}
- {{svg "gitea-lock-cog" 16 "tw-mr-2"}}
- {{ctx.Locale.Tr "repo.commits.signed_by"}}:
- {{ctx.AvatarUtils.AvatarByEmail .Verification.SigningEmail "" 28 "tw-mr-2"}}
- {{.Verification.SigningUser.GetDisplayName}}
- {{end}}
- {{else}}
- {{svg "gitea-unlock" 16 "tw-mr-2"}}
- {{ctx.Locale.Tr .Verification.Reason}}
- {{end}}
-
-
- {{if .Verification.Verified}}
- {{if ne .Verification.SigningUser.ID 0}}
- {{svg "octicon-verified" 16 "tw-mr-2"}}
- {{if .Verification.SigningSSHKey}}
- {{ctx.Locale.Tr "repo.commits.ssh_key_fingerprint"}}:
- {{.Verification.SigningSSHKey.Fingerprint}}
- {{else}}
- {{ctx.Locale.Tr "repo.commits.gpg_key_id"}}:
- {{.Verification.SigningKey.PaddedKeyID}}
- {{end}}
- {{else}}
- {{svg "octicon-unverified" 16 "tw-mr-2"}}
- {{if .Verification.SigningSSHKey}}
- {{ctx.Locale.Tr "repo.commits.ssh_key_fingerprint"}}:
- {{.Verification.SigningSSHKey.Fingerprint}}
- {{else}}
- {{ctx.Locale.Tr "repo.commits.gpg_key_id"}}:
- {{.Verification.SigningKey.PaddedKeyID}}
- {{end}}
- {{end}}
- {{else if .Verification.Warning}}
- {{svg "octicon-unverified" 16 "tw-mr-2"}}
- {{if .Verification.SigningSSHKey}}
- {{ctx.Locale.Tr "repo.commits.ssh_key_fingerprint"}}:
- {{.Verification.SigningSSHKey.Fingerprint}}
- {{else}}
- {{ctx.Locale.Tr "repo.commits.gpg_key_id"}}:
- {{.Verification.SigningKey.PaddedKeyID}}
- {{end}}
- {{else}}
- {{if .Verification.SigningKey}}
- {{if ne .Verification.SigningKey.KeyID ""}}
- {{svg "octicon-verified" 16 "tw-mr-2"}}
- {{ctx.Locale.Tr "repo.commits.gpg_key_id"}}:
- {{.Verification.SigningKey.PaddedKeyID}}
- {{end}}
- {{end}}
- {{if .Verification.SigningSSHKey}}
- {{if ne .Verification.SigningSSHKey.Fingerprint ""}}
- {{svg "octicon-verified" 16 "tw-mr-2"}}
- {{ctx.Locale.Tr "repo.commits.ssh_key_fingerprint"}}:
- {{.Verification.SigningSSHKey.Fingerprint}}
- {{end}}
- {{end}}
- {{end}}
+ {{end}}
+
- {{end}}
+
+ {{ctx.Locale.Tr "repo.diff.commit"}}
+ {{ShortSha .CommitID}}
{{svg "octicon-note" 16 "tw-mr-2"}}
@@ -276,12 +196,13 @@
{{else}}
{{.NoteCommit.Author.Name}}
{{end}}
- {{DateUtils.TimeSince .NoteCommit.Author.When}}
+ {{DateUtils.TimeSince .NoteCommit.Author.When}}
{{.NoteRendered | SanitizeHTML}}
+
diff --git a/templates/repo/graph/commits.tmpl b/templates/repo/graph/commits.tmpl
index f1d0e62330..6af0ba1f0f 100644
--- a/templates/repo/graph/commits.tmpl
+++ b/templates/repo/graph/commits.tmpl
@@ -5,33 +5,13 @@
{{if $commit.OnlyRelation}}
{{else}}
-
- {{$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}}
-
- {{ShortSha $commit.Commit.ID.String}}
- {{- if $commit.Commit.Signature -}}
- {{template "repo/shabox_badge" dict "root" $ "verification" $commit.Verification}}
- {{- end -}}
-
-
-
+ {{template "repo/commit_sign_badge" dict "Commit" $commit.Commit "CommitBaseLink" (print $.RepoLink "/commit") "CommitSignVerification" $commit.Verification}}
+
+
{{ctx.RenderUtils.RenderCommitMessage $commit.Subject ($.Repository.ComposeMetas ctx)}}
-
+
+
{{range $commit.Refs}}
{{$refGroup := .RefGroup}}
{{if eq $refGroup "pull"}}
@@ -56,7 +36,8 @@
{{end}}
{{end}}
-
+
+
{{$userName := $commit.Commit.Author.Name}}
{{if $commit.User}}
{{if and $commit.User.FullName DefaultShowFullName}}
@@ -69,7 +50,8 @@
{{$userName}}
{{end}}
- {{DateUtils.FullTime $commit.Date}}
+
+ {{DateUtils.FullTime $commit.Date}}
{{end}}
{{end}}
diff --git a/templates/repo/latest_commit.tmpl b/templates/repo/latest_commit.tmpl
index 34a5df8f77..b176b4190c 100644
--- a/templates/repo/latest_commit.tmpl
+++ b/templates/repo/latest_commit.tmpl
@@ -1,3 +1,4 @@
+
{{/*singular-commit*/}}
{{svg "octicon-git-commit"}}
{{if .User}}
{{ctx.AvatarUtils.Avatar .User 20}}
@@ -11,7 +11,8 @@
{{ctx.AvatarUtils.AvatarByEmail .Author.Email .Author.Name 20}}
{{end}}
- {{$commitLink:= printf "%s/commit/%s" $.comment.Issue.PullRequest.BaseRepo.Link (PathEscape .ID.String)}}
+ {{$commitBaseLink := printf "%s/commit" $.comment.Issue.PullRequest.BaseRepo.Link}}
+ {{$commitLink:= printf "%s/%s" $commitBaseLink (PathEscape .ID.String)}}
{{- ctx.RenderUtils.RenderCommitMessageLinkSubject .Message $commitLink ($.comment.Issue.PullRequest.BaseRepo.ComposeMetas ctx) -}}
@@ -21,29 +22,9 @@
{{end}}
-
+
{{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}}
-
- {{ShortSha .ID.String}}
- {{if .Signature}}
- {{template "repo/shabox_badge" dict "root" $.root "verification" .Verification}}
- {{end}}
-
+ {{template "repo/commit_sign_badge" dict "Commit" . "CommitBaseLink" $commitBaseLink "CommitSignVerification" .Verification}}
{{if IsMultilineCommitMessage .Message}}
diff --git a/templates/repo/diff/section_unified.tmpl b/templates/repo/diff/section_unified.tmpl
index a06cd2ddd1..cb612bc27c 100644
--- a/templates/repo/diff/section_unified.tmpl
+++ b/templates/repo/diff/section_unified.tmpl
@@ -1,5 +1,7 @@
{{$file := .file}}
-{{$blobExcerptLink := print (or ctx.RootData.CommitRepoLink ctx.RootData.RepoLink) (Iif $.root.PageIsWiki "/wiki" "") "/blob_excerpt/" (PathEscape $.root.AfterCommitID) "?"}}
+{{$repoLink := or ctx.RootData.CommitRepoLink ctx.RootData.RepoLink}}
+{{$afterCommitID := or $.root.AfterCommitID "no-after-commit-id"}}{{/* this tmpl is also used by the PR Conversation page, so the "AfterCommitID" may not exist */}}
+{{$blobExcerptLink := print $repoLink (Iif $.root.PageIsWiki "/wiki" "") "/blob_excerpt/" (PathEscape $afterCommitID) "?"}}
{{if not .LatestCommit}}
…
{{else}}
@@ -14,13 +15,11 @@
{{.LatestCommit.Author.Name}}
{{end}}
{{end}}
-
- {{ShortSha .LatestCommit.ID.String}}
- {{if .LatestCommit.Signature}}
- {{template "repo/shabox_badge" dict "root" $ "verification" .LatestCommitVerification}}
- {{end}}
-
+
+ {{template "repo/commit_sign_badge" dict "Commit" .LatestCommit "CommitBaseLink" (print .RepoLink "/commit") "CommitSignVerification" .LatestCommitVerification}}
+
{{template "repo/commit_statuses" dict "Status" .LatestCommitStatus "Statuses" .LatestCommitStatuses}}
+
{{$commitLink:= printf "%s/commit/%s" .RepoLink (PathEscape .LatestCommit.ID.String)}}
{{ctx.RenderUtils.RenderCommitMessageLinkSubject .LatestCommit.Message $commitLink ($.Repository.ComposeMetas ctx)}}
{{if IsMultilineCommitMessage .LatestCommit.Message}}
@@ -29,3 +28,4 @@
{{end}}
{{end}}
+
diff --git a/templates/repo/shabox_badge.tmpl b/templates/repo/shabox_badge.tmpl
deleted file mode 100644
index 36fc9e04b1..0000000000
--- a/templates/repo/shabox_badge.tmpl
+++ /dev/null
@@ -1,15 +0,0 @@
-
- {{if .verification.Verified}}
-
diff --git a/templates/repo/view_file.tmpl b/templates/repo/view_file.tmpl
index 86366ae053..0a43e3db54 100644
--- a/templates/repo/view_file.tmpl
+++ b/templates/repo/view_file.tmpl
@@ -12,9 +12,7 @@
{{if not .ReadmeInList}}
- {{if ne .verification.SigningUser.ID 0}}
- {{svg "gitea-lock"}}
- {{ctx.AvatarUtils.Avatar .verification.SigningUser 16 "signature"}}
- {{else}}
- {{svg "gitea-lock-cog"}}
- {{ctx.AvatarUtils.AvatarByEmail .verification.SigningEmail "" 16 "signature"}}
- {{end}}
-
- {{else}}
- {{svg "gitea-unlock"}}
- {{end}}
-
-
- {{template "repo/latest_commit" .}}
-
+ {{template "repo/latest_commit" .}}
{{if .LatestCommit}}
{{if .LatestCommit.Committer}}
diff --git a/templates/repo/view_list.tmpl b/templates/repo/view_list.tmpl
index 2d555e4c2e..01bb70e06f 100644
--- a/templates/repo/view_list.tmpl
+++ b/templates/repo/view_list.tmpl
@@ -1,7 +1,7 @@
{{/* use grid layout, still use the old ID because there are many other CSS styles depending on this ID */}}
{{svg "octicon-kebab-horizontal" 28 "icon tw-p-1"}}
+ {{end}}
{{end}}
diff --git a/templates/user/profile.tmpl b/templates/user/profile.tmpl
index cf61bb906a..2c83ce97cd 100644
--- a/templates/user/profile.tmpl
+++ b/templates/user/profile.tmpl
@@ -27,6 +27,8 @@
{{template "repo/user_cards" .}}
{{else if eq .TabName "overview"}}
-
{{if .HasParentPath}}
diff --git a/templates/shared/user/profile_big_avatar.tmpl b/templates/shared/user/profile_big_avatar.tmpl
index f04f1ef6c4..91f04e0b53 100644
--- a/templates/shared/user/profile_big_avatar.tmpl
+++ b/templates/shared/user/profile_big_avatar.tmpl
@@ -92,6 +92,9 @@
{{end}}
{{end}}
+ {{if .ShowMoreOrgs}}
+ {{template "repo/latest_commit" .}}
+ {{template "repo/latest_commit" .}}
{{if and .LatestCommit .LatestCommit.Committer}}{{DateUtils.TimeSince .LatestCommit.Committer.When}}{{end}}
{{.ProfileReadme}}
+ {{else if eq .TabName "organizations"}}
+ {{template "repo/user_cards" .}}
{{else}}
{{template "shared/repo_search" .}}
{{template "explore/repo_list" .}}
diff --git a/tests/integration/actions_job_test.go b/tests/integration/actions_job_test.go
new file mode 100644
index 0000000000..e13277678d
--- /dev/null
+++ b/tests/integration/actions_job_test.go
@@ -0,0 +1,410 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package integration
+
+import (
+ "encoding/base64"
+ "fmt"
+ "net/http"
+ "net/url"
+ "testing"
+ "time"
+
+ actions_model "code.gitea.io/gitea/models/actions"
+ auth_model "code.gitea.io/gitea/models/auth"
+ "code.gitea.io/gitea/models/unittest"
+ user_model "code.gitea.io/gitea/models/user"
+ api "code.gitea.io/gitea/modules/structs"
+
+ runnerv1 "code.gitea.io/actions-proto-go/runner/v1"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestJobWithNeeds(t *testing.T) {
+ testCases := []struct {
+ treePath string
+ fileContent string
+ outcomes map[string]*mockTaskOutcome
+ expectedStatuses map[string]string
+ }{
+ {
+ treePath: ".gitea/workflows/job-with-needs.yml",
+ fileContent: `name: job-with-needs
+on:
+ push:
+ paths:
+ - '.gitea/workflows/job-with-needs.yml'
+jobs:
+ job1:
+ runs-on: ubuntu-latest
+ steps:
+ - run: echo job1
+ job2:
+ runs-on: ubuntu-latest
+ needs: [job1]
+ steps:
+ - run: echo job2
+`,
+ outcomes: map[string]*mockTaskOutcome{
+ "job1": {
+ result: runnerv1.Result_RESULT_SUCCESS,
+ },
+ "job2": {
+ result: runnerv1.Result_RESULT_SUCCESS,
+ },
+ },
+ expectedStatuses: map[string]string{
+ "job1": actions_model.StatusSuccess.String(),
+ "job2": actions_model.StatusSuccess.String(),
+ },
+ },
+ {
+ treePath: ".gitea/workflows/job-with-needs-fail.yml",
+ fileContent: `name: job-with-needs-fail
+on:
+ push:
+ paths:
+ - '.gitea/workflows/job-with-needs-fail.yml'
+jobs:
+ job1:
+ runs-on: ubuntu-latest
+ steps:
+ - run: echo job1
+ job2:
+ runs-on: ubuntu-latest
+ needs: [job1]
+ steps:
+ - run: echo job2
+`,
+ outcomes: map[string]*mockTaskOutcome{
+ "job1": {
+ result: runnerv1.Result_RESULT_FAILURE,
+ },
+ },
+ expectedStatuses: map[string]string{
+ "job1": actions_model.StatusFailure.String(),
+ "job2": actions_model.StatusSkipped.String(),
+ },
+ },
+ {
+ treePath: ".gitea/workflows/job-with-needs-fail-if.yml",
+ fileContent: `name: job-with-needs-fail-if
+on:
+ push:
+ paths:
+ - '.gitea/workflows/job-with-needs-fail-if.yml'
+jobs:
+ job1:
+ runs-on: ubuntu-latest
+ steps:
+ - run: echo job1
+ job2:
+ runs-on: ubuntu-latest
+ if: ${{ always() }}
+ needs: [job1]
+ steps:
+ - run: echo job2
+`,
+ outcomes: map[string]*mockTaskOutcome{
+ "job1": {
+ result: runnerv1.Result_RESULT_FAILURE,
+ },
+ "job2": {
+ result: runnerv1.Result_RESULT_SUCCESS,
+ },
+ },
+ expectedStatuses: map[string]string{
+ "job1": actions_model.StatusFailure.String(),
+ "job2": actions_model.StatusSuccess.String(),
+ },
+ },
+ }
+ onGiteaRun(t, func(t *testing.T, u *url.URL) {
+ user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ session := loginUser(t, user2.Name)
+ token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
+
+ apiRepo := createActionsTestRepo(t, token, "actions-jobs-with-needs", false)
+ runner := newMockRunner()
+ runner.registerAsRepoRunner(t, user2.Name, apiRepo.Name, "mock-runner", []string{"ubuntu-latest"})
+
+ for _, tc := range testCases {
+ t.Run(fmt.Sprintf("test %s", tc.treePath), func(t *testing.T) {
+ // create the workflow file
+ opts := getWorkflowCreateFileOptions(user2, apiRepo.DefaultBranch, fmt.Sprintf("create %s", tc.treePath), tc.fileContent)
+ fileResp := createWorkflowFile(t, token, user2.Name, apiRepo.Name, tc.treePath, opts)
+
+ // fetch and execute task
+ for i := 0; i < len(tc.outcomes); i++ {
+ task := runner.fetchTask(t)
+ jobName := getTaskJobNameByTaskID(t, token, user2.Name, apiRepo.Name, task.Id)
+ outcome := tc.outcomes[jobName]
+ assert.NotNil(t, outcome)
+ runner.execTask(t, task, outcome)
+ }
+
+ // check result
+ req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/%s/actions/tasks", user2.Name, apiRepo.Name)).
+ AddTokenAuth(token)
+ resp := MakeRequest(t, req, http.StatusOK)
+ var actionTaskRespAfter api.ActionTaskResponse
+ DecodeJSON(t, resp, &actionTaskRespAfter)
+ for _, apiTask := range actionTaskRespAfter.Entries {
+ if apiTask.HeadSHA != fileResp.Commit.SHA {
+ continue
+ }
+ status := apiTask.Status
+ assert.Equal(t, status, tc.expectedStatuses[apiTask.Name])
+ }
+ })
+ }
+
+ httpContext := NewAPITestContext(t, user2.Name, apiRepo.Name, auth_model.AccessTokenScopeWriteRepository)
+ doAPIDeleteRepository(httpContext)(t)
+ })
+}
+
+func TestJobNeedsMatrix(t *testing.T) {
+ testCases := []struct {
+ treePath string
+ fileContent string
+ outcomes map[string]*mockTaskOutcome
+ expectedTaskNeeds map[string]*runnerv1.TaskNeed // jobID => TaskNeed
+ }{
+ {
+ treePath: ".gitea/workflows/jobs-outputs-with-matrix.yml",
+ fileContent: `name: jobs-outputs-with-matrix
+on:
+ push:
+ paths:
+ - '.gitea/workflows/jobs-outputs-with-matrix.yml'
+jobs:
+ job1:
+ runs-on: ubuntu-latest
+ outputs:
+ output_1: ${{ steps.gen_output.outputs.output_1 }}
+ output_2: ${{ steps.gen_output.outputs.output_2 }}
+ output_3: ${{ steps.gen_output.outputs.output_3 }}
+ strategy:
+ matrix:
+ version: [1, 2, 3]
+ steps:
+ - name: Generate output
+ id: gen_output
+ run: |
+ version="${{ matrix.version }}"
+ echo "output_${version}=${version}" >> "$GITHUB_OUTPUT"
+ job2:
+ runs-on: ubuntu-latest
+ needs: [job1]
+ steps:
+ - run: echo '${{ toJSON(needs.job1.outputs) }}'
+`,
+ outcomes: map[string]*mockTaskOutcome{
+ "job1 (1)": {
+ result: runnerv1.Result_RESULT_SUCCESS,
+ outputs: map[string]string{
+ "output_1": "1",
+ "output_2": "",
+ "output_3": "",
+ },
+ },
+ "job1 (2)": {
+ result: runnerv1.Result_RESULT_SUCCESS,
+ outputs: map[string]string{
+ "output_1": "",
+ "output_2": "2",
+ "output_3": "",
+ },
+ },
+ "job1 (3)": {
+ result: runnerv1.Result_RESULT_SUCCESS,
+ outputs: map[string]string{
+ "output_1": "",
+ "output_2": "",
+ "output_3": "3",
+ },
+ },
+ },
+ expectedTaskNeeds: map[string]*runnerv1.TaskNeed{
+ "job1": {
+ Result: runnerv1.Result_RESULT_SUCCESS,
+ Outputs: map[string]string{
+ "output_1": "1",
+ "output_2": "2",
+ "output_3": "3",
+ },
+ },
+ },
+ },
+ {
+ treePath: ".gitea/workflows/jobs-outputs-with-matrix-failure.yml",
+ fileContent: `name: jobs-outputs-with-matrix-failure
+on:
+ push:
+ paths:
+ - '.gitea/workflows/jobs-outputs-with-matrix-failure.yml'
+jobs:
+ job1:
+ runs-on: ubuntu-latest
+ outputs:
+ output_1: ${{ steps.gen_output.outputs.output_1 }}
+ output_2: ${{ steps.gen_output.outputs.output_2 }}
+ output_3: ${{ steps.gen_output.outputs.output_3 }}
+ strategy:
+ matrix:
+ version: [1, 2, 3]
+ steps:
+ - name: Generate output
+ id: gen_output
+ run: |
+ version="${{ matrix.version }}"
+ echo "output_${version}=${version}" >> "$GITHUB_OUTPUT"
+ job2:
+ runs-on: ubuntu-latest
+ if: ${{ always() }}
+ needs: [job1]
+ steps:
+ - run: echo '${{ toJSON(needs.job1.outputs) }}'
+`,
+ outcomes: map[string]*mockTaskOutcome{
+ "job1 (1)": {
+ result: runnerv1.Result_RESULT_SUCCESS,
+ outputs: map[string]string{
+ "output_1": "1",
+ "output_2": "",
+ "output_3": "",
+ },
+ },
+ "job1 (2)": {
+ result: runnerv1.Result_RESULT_FAILURE,
+ outputs: map[string]string{
+ "output_1": "",
+ "output_2": "",
+ "output_3": "",
+ },
+ },
+ "job1 (3)": {
+ result: runnerv1.Result_RESULT_SUCCESS,
+ outputs: map[string]string{
+ "output_1": "",
+ "output_2": "",
+ "output_3": "3",
+ },
+ },
+ },
+ expectedTaskNeeds: map[string]*runnerv1.TaskNeed{
+ "job1": {
+ Result: runnerv1.Result_RESULT_FAILURE,
+ Outputs: map[string]string{
+ "output_1": "1",
+ "output_2": "",
+ "output_3": "3",
+ },
+ },
+ },
+ },
+ }
+ onGiteaRun(t, func(t *testing.T, u *url.URL) {
+ user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ session := loginUser(t, user2.Name)
+ token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
+
+ apiRepo := createActionsTestRepo(t, token, "actions-jobs-outputs-with-matrix", false)
+ runner := newMockRunner()
+ runner.registerAsRepoRunner(t, user2.Name, apiRepo.Name, "mock-runner", []string{"ubuntu-latest"})
+
+ for _, tc := range testCases {
+ t.Run(fmt.Sprintf("test %s", tc.treePath), func(t *testing.T) {
+ opts := getWorkflowCreateFileOptions(user2, apiRepo.DefaultBranch, fmt.Sprintf("create %s", tc.treePath), tc.fileContent)
+ createWorkflowFile(t, token, user2.Name, apiRepo.Name, tc.treePath, opts)
+
+ for i := 0; i < len(tc.outcomes); i++ {
+ task := runner.fetchTask(t)
+ jobName := getTaskJobNameByTaskID(t, token, user2.Name, apiRepo.Name, task.Id)
+ outcome := tc.outcomes[jobName]
+ assert.NotNil(t, outcome)
+ runner.execTask(t, task, outcome)
+ }
+
+ task := runner.fetchTask(t)
+ actualTaskNeeds := task.Needs
+ assert.Len(t, actualTaskNeeds, len(tc.expectedTaskNeeds))
+ for jobID, tn := range tc.expectedTaskNeeds {
+ actualNeed := actualTaskNeeds[jobID]
+ assert.Equal(t, tn.Result, actualNeed.Result)
+ assert.Len(t, actualNeed.Outputs, len(tn.Outputs))
+ for outputKey, outputValue := range tn.Outputs {
+ assert.Equal(t, outputValue, actualNeed.Outputs[outputKey])
+ }
+ }
+ })
+ }
+
+ httpContext := NewAPITestContext(t, user2.Name, apiRepo.Name, auth_model.AccessTokenScopeWriteRepository)
+ doAPIDeleteRepository(httpContext)(t)
+ })
+}
+
+func createActionsTestRepo(t *testing.T, authToken, repoName string, isPrivate bool) *api.Repository {
+ req := NewRequestWithJSON(t, "POST", "/api/v1/user/repos", &api.CreateRepoOption{
+ Name: repoName,
+ Private: isPrivate,
+ Readme: "Default",
+ AutoInit: true,
+ DefaultBranch: "main",
+ }).AddTokenAuth(authToken)
+ resp := MakeRequest(t, req, http.StatusCreated)
+ var apiRepo api.Repository
+ DecodeJSON(t, resp, &apiRepo)
+ return &apiRepo
+}
+
+func getWorkflowCreateFileOptions(u *user_model.User, branch, msg, content string) *api.CreateFileOptions {
+ return &api.CreateFileOptions{
+ FileOptions: api.FileOptions{
+ BranchName: branch,
+ Message: msg,
+ Author: api.Identity{
+ Name: u.Name,
+ Email: u.Email,
+ },
+ Committer: api.Identity{
+ Name: u.Name,
+ Email: u.Email,
+ },
+ Dates: api.CommitDateOptions{
+ Author: time.Now(),
+ Committer: time.Now(),
+ },
+ },
+ ContentBase64: base64.StdEncoding.EncodeToString([]byte(content)),
+ }
+}
+
+func createWorkflowFile(t *testing.T, authToken, ownerName, repoName, treePath string, opts *api.CreateFileOptions) *api.FileResponse {
+ req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s", ownerName, repoName, treePath), opts).
+ AddTokenAuth(authToken)
+ resp := MakeRequest(t, req, http.StatusCreated)
+ var fileResponse api.FileResponse
+ DecodeJSON(t, resp, &fileResponse)
+ return &fileResponse
+}
+
+// getTaskJobNameByTaskID get the job name of the task by task ID
+// there is currently not an API for querying a task by ID so we have to list all the tasks
+func getTaskJobNameByTaskID(t *testing.T, authToken, ownerName, repoName string, taskID int64) string {
+ // FIXME: we may need to query several pages
+ req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/%s/actions/tasks", ownerName, repoName)).
+ AddTokenAuth(authToken)
+ resp := MakeRequest(t, req, http.StatusOK)
+ var taskRespBefore api.ActionTaskResponse
+ DecodeJSON(t, resp, &taskRespBefore)
+ for _, apiTask := range taskRespBefore.Entries {
+ if apiTask.ID == taskID {
+ return apiTask.Name
+ }
+ }
+ return ""
+}
diff --git a/tests/integration/actions_log_test.go b/tests/integration/actions_log_test.go
new file mode 100644
index 0000000000..fd055fc4c4
--- /dev/null
+++ b/tests/integration/actions_log_test.go
@@ -0,0 +1,159 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package integration
+
+import (
+ "fmt"
+ "net/http"
+ "net/url"
+ "strings"
+ "testing"
+ "time"
+
+ auth_model "code.gitea.io/gitea/models/auth"
+ repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/models/unittest"
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/storage"
+ "code.gitea.io/gitea/modules/test"
+
+ runnerv1 "code.gitea.io/actions-proto-go/runner/v1"
+ "github.com/stretchr/testify/assert"
+ "google.golang.org/protobuf/types/known/timestamppb"
+)
+
+func TestDownloadTaskLogs(t *testing.T) {
+ now := time.Now()
+ testCases := []struct {
+ treePath string
+ fileContent string
+ outcome *mockTaskOutcome
+ zstdEnabled bool
+ }{
+ {
+ treePath: ".gitea/workflows/download-task-logs-zstd.yml",
+ fileContent: `name: download-task-logs-zstd
+on:
+ push:
+ paths:
+ - '.gitea/workflows/download-task-logs-zstd.yml'
+jobs:
+ job1:
+ runs-on: ubuntu-latest
+ steps:
+ - run: echo job1 with zstd enabled
+`,
+ outcome: &mockTaskOutcome{
+ result: runnerv1.Result_RESULT_SUCCESS,
+ logRows: []*runnerv1.LogRow{
+ {
+ Time: timestamppb.New(now.Add(1 * time.Second)),
+ Content: " \U0001F433 docker create image",
+ },
+ {
+ Time: timestamppb.New(now.Add(2 * time.Second)),
+ Content: "job1 zstd enabled",
+ },
+ {
+ Time: timestamppb.New(now.Add(3 * time.Second)),
+ Content: "\U0001F3C1 Job succeeded",
+ },
+ },
+ },
+ zstdEnabled: true,
+ },
+ {
+ treePath: ".gitea/workflows/download-task-logs-no-zstd.yml",
+ fileContent: `name: download-task-logs-no-zstd
+on:
+ push:
+ paths:
+ - '.gitea/workflows/download-task-logs-no-zstd.yml'
+jobs:
+ job1:
+ runs-on: ubuntu-latest
+ steps:
+ - run: echo job1 with zstd disabled
+`,
+ outcome: &mockTaskOutcome{
+ result: runnerv1.Result_RESULT_SUCCESS,
+ logRows: []*runnerv1.LogRow{
+ {
+ Time: timestamppb.New(now.Add(4 * time.Second)),
+ Content: " \U0001F433 docker create image",
+ },
+ {
+ Time: timestamppb.New(now.Add(5 * time.Second)),
+ Content: "job1 zstd disabled",
+ },
+ {
+ Time: timestamppb.New(now.Add(6 * time.Second)),
+ Content: "\U0001F3C1 Job succeeded",
+ },
+ },
+ },
+ zstdEnabled: false,
+ },
+ }
+ onGiteaRun(t, func(t *testing.T, u *url.URL) {
+ user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ session := loginUser(t, user2.Name)
+ token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
+
+ apiRepo := createActionsTestRepo(t, token, "actions-download-task-logs", false)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: apiRepo.ID})
+ runner := newMockRunner()
+ runner.registerAsRepoRunner(t, user2.Name, repo.Name, "mock-runner", []string{"ubuntu-latest"})
+
+ for _, tc := range testCases {
+ t.Run(fmt.Sprintf("test %s", tc.treePath), func(t *testing.T) {
+ var resetFunc func()
+ if tc.zstdEnabled {
+ resetFunc = test.MockVariableValue(&setting.Actions.LogCompression, "zstd")
+ assert.True(t, setting.Actions.LogCompression.IsZstd())
+ } else {
+ resetFunc = test.MockVariableValue(&setting.Actions.LogCompression, "none")
+ assert.False(t, setting.Actions.LogCompression.IsZstd())
+ }
+
+ // create the workflow file
+ opts := getWorkflowCreateFileOptions(user2, repo.DefaultBranch, fmt.Sprintf("create %s", tc.treePath), tc.fileContent)
+ createWorkflowFile(t, token, user2.Name, repo.Name, tc.treePath, opts)
+
+ // fetch and execute task
+ task := runner.fetchTask(t)
+ runner.execTask(t, task, tc.outcome)
+
+ // check whether the log file exists
+ logFileName := fmt.Sprintf("%s/%02x/%d.log", repo.FullName(), task.Id%256, task.Id)
+ if setting.Actions.LogCompression.IsZstd() {
+ logFileName += ".zst"
+ }
+ _, err := storage.Actions.Stat(logFileName)
+ assert.NoError(t, err)
+
+ // download task logs and check content
+ runIndex := task.Context.GetFields()["run_number"].GetStringValue()
+ req := NewRequest(t, "GET", fmt.Sprintf("/%s/%s/actions/runs/%s/jobs/0/logs", user2.Name, repo.Name, runIndex)).
+ AddTokenAuth(token)
+ resp := MakeRequest(t, req, http.StatusOK)
+ logTextLines := strings.Split(strings.TrimSpace(resp.Body.String()), "\n")
+ assert.Len(t, logTextLines, len(tc.outcome.logRows))
+ for idx, lr := range tc.outcome.logRows {
+ assert.Equal(
+ t,
+ fmt.Sprintf("%s %s", lr.Time.AsTime().Format("2006-01-02T15:04:05.0000000Z07:00"), lr.Content),
+ logTextLines[idx],
+ )
+ }
+
+ resetFunc()
+ })
+ }
+
+ httpContext := NewAPITestContext(t, user2.Name, repo.Name, auth_model.AccessTokenScopeWriteRepository)
+ doAPIDeleteRepository(httpContext)(t)
+ })
+}
diff --git a/tests/integration/actions_runner_test.go b/tests/integration/actions_runner_test.go
new file mode 100644
index 0000000000..355ea1705e
--- /dev/null
+++ b/tests/integration/actions_runner_test.go
@@ -0,0 +1,157 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package integration
+
+import (
+ "context"
+ "fmt"
+ "net/http"
+ "testing"
+ "time"
+
+ auth_model "code.gitea.io/gitea/models/auth"
+ "code.gitea.io/gitea/modules/setting"
+
+ pingv1 "code.gitea.io/actions-proto-go/ping/v1"
+ "code.gitea.io/actions-proto-go/ping/v1/pingv1connect"
+ runnerv1 "code.gitea.io/actions-proto-go/runner/v1"
+ "code.gitea.io/actions-proto-go/runner/v1/runnerv1connect"
+ "connectrpc.com/connect"
+ "github.com/stretchr/testify/assert"
+ "google.golang.org/protobuf/types/known/timestamppb"
+)
+
+type mockRunner struct {
+ client *mockRunnerClient
+}
+
+type mockRunnerClient struct {
+ pingServiceClient pingv1connect.PingServiceClient
+ runnerServiceClient runnerv1connect.RunnerServiceClient
+}
+
+func newMockRunner() *mockRunner {
+ client := newMockRunnerClient("", "")
+ return &mockRunner{client: client}
+}
+
+func newMockRunnerClient(uuid, token string) *mockRunnerClient {
+ baseURL := fmt.Sprintf("%sapi/actions", setting.AppURL)
+
+ opt := connect.WithInterceptors(connect.UnaryInterceptorFunc(func(next connect.UnaryFunc) connect.UnaryFunc {
+ return func(ctx context.Context, req connect.AnyRequest) (connect.AnyResponse, error) {
+ if uuid != "" {
+ req.Header().Set("x-runner-uuid", uuid)
+ }
+ if token != "" {
+ req.Header().Set("x-runner-token", token)
+ }
+ return next(ctx, req)
+ }
+ }))
+
+ client := &mockRunnerClient{
+ pingServiceClient: pingv1connect.NewPingServiceClient(http.DefaultClient, baseURL, opt),
+ runnerServiceClient: runnerv1connect.NewRunnerServiceClient(http.DefaultClient, baseURL, opt),
+ }
+
+ return client
+}
+
+func (r *mockRunner) doPing(t *testing.T) {
+ resp, err := r.client.pingServiceClient.Ping(context.Background(), connect.NewRequest(&pingv1.PingRequest{
+ Data: "mock-runner",
+ }))
+ assert.NoError(t, err)
+ assert.Equal(t, "Hello, mock-runner!", resp.Msg.Data)
+}
+
+func (r *mockRunner) doRegister(t *testing.T, name, token string, labels []string) {
+ r.doPing(t)
+ resp, err := r.client.runnerServiceClient.Register(context.Background(), connect.NewRequest(&runnerv1.RegisterRequest{
+ Name: name,
+ Token: token,
+ Version: "mock-runner-version",
+ Labels: labels,
+ }))
+ assert.NoError(t, err)
+ r.client = newMockRunnerClient(resp.Msg.Runner.Uuid, resp.Msg.Runner.Token)
+}
+
+func (r *mockRunner) registerAsRepoRunner(t *testing.T, ownerName, repoName, runnerName string, labels []string) {
+ session := loginUser(t, ownerName)
+ token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
+ req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/%s/actions/runners/registration-token", ownerName, repoName)).AddTokenAuth(token)
+ resp := MakeRequest(t, req, http.StatusOK)
+ var registrationToken struct {
+ Token string `json:"token"`
+ }
+ DecodeJSON(t, resp, ®istrationToken)
+ r.doRegister(t, runnerName, registrationToken.Token, labels)
+}
+
+func (r *mockRunner) fetchTask(t *testing.T, timeout ...time.Duration) *runnerv1.Task {
+ fetchTimeout := 10 * time.Second
+ if len(timeout) > 0 {
+ fetchTimeout = timeout[0]
+ }
+ ddl := time.Now().Add(fetchTimeout)
+ var task *runnerv1.Task
+ for time.Now().Before(ddl) {
+ resp, err := r.client.runnerServiceClient.FetchTask(context.Background(), connect.NewRequest(&runnerv1.FetchTaskRequest{
+ TasksVersion: 0,
+ }))
+ assert.NoError(t, err)
+ if resp.Msg.Task != nil {
+ task = resp.Msg.Task
+ break
+ }
+ time.Sleep(time.Second)
+ }
+ assert.NotNil(t, task, "failed to fetch a task")
+ return task
+}
+
+type mockTaskOutcome struct {
+ result runnerv1.Result
+ outputs map[string]string
+ logRows []*runnerv1.LogRow
+ execTime time.Duration
+}
+
+func (r *mockRunner) execTask(t *testing.T, task *runnerv1.Task, outcome *mockTaskOutcome) {
+ for idx, lr := range outcome.logRows {
+ resp, err := r.client.runnerServiceClient.UpdateLog(context.Background(), connect.NewRequest(&runnerv1.UpdateLogRequest{
+ TaskId: task.Id,
+ Index: int64(idx),
+ Rows: []*runnerv1.LogRow{lr},
+ NoMore: idx == len(outcome.logRows)-1,
+ }))
+ assert.NoError(t, err)
+ assert.EqualValues(t, idx+1, resp.Msg.AckIndex)
+ }
+ sentOutputKeys := make([]string, 0, len(outcome.outputs))
+ for outputKey, outputValue := range outcome.outputs {
+ resp, err := r.client.runnerServiceClient.UpdateTask(context.Background(), connect.NewRequest(&runnerv1.UpdateTaskRequest{
+ State: &runnerv1.TaskState{
+ Id: task.Id,
+ Result: runnerv1.Result_RESULT_UNSPECIFIED,
+ },
+ Outputs: map[string]string{outputKey: outputValue},
+ }))
+ assert.NoError(t, err)
+ sentOutputKeys = append(sentOutputKeys, outputKey)
+ assert.ElementsMatch(t, sentOutputKeys, resp.Msg.SentOutputs)
+ }
+ time.Sleep(outcome.execTime)
+ resp, err := r.client.runnerServiceClient.UpdateTask(context.Background(), connect.NewRequest(&runnerv1.UpdateTaskRequest{
+ State: &runnerv1.TaskState{
+ Id: task.Id,
+ Result: outcome.result,
+ StoppedAt: timestamppb.Now(),
+ },
+ }))
+ assert.NoError(t, err)
+ assert.Equal(t, outcome.result, resp.Msg.State.Result)
+}
diff --git a/tests/integration/api_issue_label_test.go b/tests/integration/api_issue_label_test.go
index 0e4cd8243b..c9cdd46b9a 100644
--- a/tests/integration/api_issue_label_test.go
+++ b/tests/integration/api_issue_label_test.go
@@ -117,27 +117,33 @@ func TestAPIAddIssueLabels(t *testing.T) {
func TestAPIAddIssueLabelsWithLabelNames(t *testing.T) {
assert.NoError(t, unittest.LoadFixtures())
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
- issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{RepoID: repo.ID})
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3})
+ issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 6, RepoID: repo.ID})
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
+ repoLabel := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 10, RepoID: repo.ID})
+ orgLabel := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 4, OrgID: owner.ID})
- session := loginUser(t, owner.Name)
- token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue)
- urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/labels",
- repo.OwnerName, repo.Name, issue.Index)
+ user1Session := loginUser(t, "user1")
+ token := getTokenForLoggedInUser(t, user1Session, auth_model.AccessTokenScopeWriteIssue)
+
+ // add the org label and the repo label to the issue
+ urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/labels", owner.Name, repo.Name, issue.Index)
req := NewRequestWithJSON(t, "POST", urlStr, &api.IssueLabelsOption{
- Labels: []any{"label1", "label2"},
+ Labels: []any{repoLabel.Name, orgLabel.Name},
}).AddTokenAuth(token)
resp := MakeRequest(t, req, http.StatusOK)
var apiLabels []*api.Label
DecodeJSON(t, resp, &apiLabels)
assert.Len(t, apiLabels, unittest.GetCount(t, &issues_model.IssueLabel{IssueID: issue.ID}))
-
var apiLabelNames []string
for _, label := range apiLabels {
apiLabelNames = append(apiLabelNames, label.Name)
}
- assert.ElementsMatch(t, apiLabelNames, []string{"label1", "label2"})
+ assert.ElementsMatch(t, apiLabelNames, []string{repoLabel.Name, orgLabel.Name})
+
+ // delete labels
+ req = NewRequest(t, "DELETE", urlStr).AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusNoContent)
}
func TestAPIReplaceIssueLabels(t *testing.T) {
diff --git a/tests/integration/org_team_invite_test.go b/tests/integration/org_team_invite_test.go
index 274fde4085..4c1053702e 100644
--- a/tests/integration/org_team_invite_test.go
+++ b/tests/integration/org_team_invite_test.go
@@ -274,7 +274,8 @@ func TestOrgTeamEmailInviteRedirectsNewUserWithActivation(t *testing.T) {
user, err := user_model.GetUserByName(db.DefaultContext, "doesnotexist")
assert.NoError(t, err)
- activateURL := fmt.Sprintf("/user/activate?code=%s", user.GenerateEmailActivateCode("doesnotexist@example.com"))
+ activationCode := user_model.GenerateUserTimeLimitCode(&user_model.TimeLimitCodeOptions{Purpose: user_model.TimeLimitCodeActivateAccount}, user)
+ activateURL := fmt.Sprintf("/user/activate?code=%s", activationCode)
req = NewRequestWithValues(t, "POST", activateURL, map[string]string{
"password": "examplePassword!1",
})
diff --git a/tests/integration/pull_create_test.go b/tests/integration/pull_create_test.go
index 9812d2073d..4bc2a1da9a 100644
--- a/tests/integration/pull_create_test.go
+++ b/tests/integration/pull_create_test.go
@@ -12,6 +12,7 @@ import (
"strings"
"testing"
+ "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/tests"
@@ -226,3 +227,21 @@ func TestPullCreatePrFromBaseToFork(t *testing.T) {
assert.Regexp(t, "^/user1/repo1/pulls/[0-9]*$", url)
})
}
+
+func TestCreateAgitPullWithReadPermission(t *testing.T) {
+ onGiteaRun(t, func(t *testing.T, u *url.URL) {
+ dstPath := t.TempDir()
+
+ u.Path = "user2/repo1.git"
+ u.User = url.UserPassword("user4", userPassword)
+
+ t.Run("Clone", doGitClone(dstPath, u))
+
+ t.Run("add commit", doGitAddSomeCommits(dstPath, "master"))
+
+ t.Run("do agit pull create", func(t *testing.T) {
+ err := git.NewCommand(git.DefaultContext, "push", "origin", "HEAD:refs/for/master", "-o").AddDynamicArguments("topic=" + "test-topic").Run(&git.RunOpts{Dir: dstPath})
+ assert.NoError(t, err)
+ })
+ })
+}
diff --git a/tests/integration/repo_commits_test.go b/tests/integration/repo_commits_test.go
index bb65d9e04a..fc066e06d3 100644
--- a/tests/integration/repo_commits_test.go
+++ b/tests/integration/repo_commits_test.go
@@ -30,7 +30,7 @@ func TestRepoCommits(t *testing.T) {
resp := session.MakeRequest(t, req, http.StatusOK)
doc := NewHTMLParser(t, resp.Body)
- commitURL, exists := doc.doc.Find("#commits-table tbody tr td.sha a").Attr("href")
+ commitURL, exists := doc.doc.Find("#commits-table .commit-id-short").Attr("href")
assert.True(t, exists)
assert.NotEmpty(t, commitURL)
}
@@ -46,7 +46,7 @@ func doTestRepoCommitWithStatus(t *testing.T, state string, classes ...string) {
doc := NewHTMLParser(t, resp.Body)
// Get first commit URL
- commitURL, exists := doc.doc.Find("#commits-table tbody tr td.sha a").Attr("href")
+ commitURL, exists := doc.doc.Find("#commits-table .commit-id-short").Attr("href")
assert.True(t, exists)
assert.NotEmpty(t, commitURL)
@@ -64,7 +64,7 @@ func doTestRepoCommitWithStatus(t *testing.T, state string, classes ...string) {
doc = NewHTMLParser(t, resp.Body)
// Check if commit status is displayed in message column (.tippy-target to ignore the tippy trigger)
- sel := doc.doc.Find("#commits-table tbody tr td.message .tippy-target .commit-status")
+ sel := doc.doc.Find("#commits-table .message .tippy-target .commit-status")
assert.Equal(t, 1, sel.Length())
for _, class := range classes {
assert.True(t, sel.HasClass(class))
@@ -140,7 +140,7 @@ func TestRepoCommitsStatusParallel(t *testing.T) {
doc := NewHTMLParser(t, resp.Body)
// Get first commit URL
- commitURL, exists := doc.doc.Find("#commits-table tbody tr td.sha a").Attr("href")
+ commitURL, exists := doc.doc.Find("#commits-table .commit-id-short").Attr("href")
assert.True(t, exists)
assert.NotEmpty(t, commitURL)
@@ -175,7 +175,7 @@ func TestRepoCommitsStatusMultiple(t *testing.T) {
doc := NewHTMLParser(t, resp.Body)
// Get first commit URL
- commitURL, exists := doc.doc.Find("#commits-table tbody tr td.sha a").Attr("href")
+ commitURL, exists := doc.doc.Find("#commits-table .commit-id-short").Attr("href")
assert.True(t, exists)
assert.NotEmpty(t, commitURL)
@@ -200,6 +200,6 @@ func TestRepoCommitsStatusMultiple(t *testing.T) {
doc = NewHTMLParser(t, resp.Body)
// Check that the data-tippy="commit-statuses" (for trigger) and commit-status (svg) are present
- sel := doc.doc.Find("#commits-table tbody tr td.message [data-tippy=\"commit-statuses\"] .commit-status")
+ sel := doc.doc.Find("#commits-table .message [data-tippy=\"commit-statuses\"] .commit-status")
assert.Equal(t, 1, sel.Length())
}
diff --git a/tests/integration/signup_test.go b/tests/integration/signup_test.go
index e9a05201ee..e86851352e 100644
--- a/tests/integration/signup_test.go
+++ b/tests/integration/signup_test.go
@@ -9,6 +9,7 @@ import (
"strings"
"testing"
+ "code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/setting"
@@ -99,34 +100,39 @@ func TestSignupEmailActive(t *testing.T) {
// try to sign up and send the activation email
req := NewRequestWithValues(t, "POST", "/user/sign_up", map[string]string{
- "user_name": "test-user-1",
- "email": "email-1@example.com",
+ "user_name": "Test-User-1",
+ "email": "EmAiL-1@example.com",
"password": "password1",
"retype": "password1",
})
resp := MakeRequest(t, req, http.StatusOK)
- assert.Contains(t, resp.Body.String(), `A new confirmation email has been sent to email-1@example.com.`)
+ assert.Contains(t, resp.Body.String(), `A new confirmation email has been sent to EmAiL-1@example.com.`)
// access "user/activate" means trying to re-send the activation email
session := loginUserWithPassword(t, "test-user-1", "password1")
resp = session.MakeRequest(t, NewRequest(t, "GET", "/user/activate"), http.StatusOK)
assert.Contains(t, resp.Body.String(), "You have already requested an activation email recently")
- // access anywhere else will see a "Activate Your Account" prompt, and there is a chance to change email
+ // access anywhere else will see an "Activate Your Account" prompt, and there is a chance to change email
resp = session.MakeRequest(t, NewRequest(t, "GET", "/user/issues"), http.StatusOK)
assert.Contains(t, resp.Body.String(), ` .commit-status-link {
float: right;
margin-right: 8px;
@@ -936,14 +919,6 @@ td .commit-summary {
width: 200px;
}
-.repository #commits-table thead .shatd {
- text-align: center;
-}
-
-.repository #commits-table td.sha .sha.label {
- margin: 0;
-}
-
.repository #commits-table.ui.basic.striped.table tbody tr:nth-child(2n) {
background-color: var(--color-light) !important;
}
@@ -1440,12 +1415,6 @@ td .commit-summary {
padding-top: 15px;
}
-.commit-header-row {
- min-height: 50px !important;
- padding-top: 0 !important;
- padding-bottom: 0 !important;
-}
-
.commit-header-buttons {
display: flex;
gap: 4px;
@@ -1622,7 +1591,7 @@ td .commit-summary {
align-items: center;
}
-.labels-list .label {
+.labels-list .label, .scope-parent > .label {
padding: 0 6px;
min-height: 20px;
line-height: 1.3; /* there is a `font-size: 1.25em` for inside emoji, so here the line-height needs to be larger slightly */
@@ -2128,18 +2097,6 @@ tbody.commit-list {
.repository.view.issue .comment-list .timeline .comment-header-right .role-label {
display: none;
}
- .commit-header-row .ui.horizontal.list {
- width: 100%;
- overflow-x: auto;
- margin-top: 2px;
- }
- .commit-header-row .ui.horizontal.list .item {
- align-items: center;
- display: flex;
- }
- .commit-header-row .author {
- padding: 3px 0;
- }
.commit-header h3 {
flex-basis: auto !important;
margin-bottom: 0.5rem !important;
diff --git a/web_src/css/repo/commit-sign.css b/web_src/css/repo/commit-sign.css
index e757030419..834fdd95d1 100644
--- a/web_src/css/repo/commit-sign.css
+++ b/web_src/css/repo/commit-sign.css
@@ -1,272 +1,60 @@
-
-.repository .ui.attached.isSigned.isWarning {
- border-left: 1px solid var(--color-error-border);
- border-right: 1px solid var(--color-error-border);
-}
-
-.repository .ui.attached.isSigned.isWarning.top,
-.repository .ui.attached.isSigned.isWarning.message {
- border-top: 1px solid var(--color-error-border);
-}
-
-.repository .ui.attached.isSigned.isWarning.message {
- box-shadow: none;
- background-color: var(--color-error-bg);
- color: var(--color-error-text);
-}
-
-.repository .ui.attached.isSigned.isWarning.message .ui.text {
- color: var(--color-error-text);
-}
-
-.repository .ui.attached.isSigned.isWarning:last-child,
-.repository .ui.attached.isSigned.isWarning.bottom {
- border-bottom: 1px solid var(--color-error-border);
-}
-
-.repository .ui.attached.isSigned.isVerified {
- border-left: 1px solid var(--color-success-border);
- border-right: 1px solid var(--color-success-border);
-}
-
-.repository .ui.attached.isSigned.isVerified.top,
-.repository .ui.attached.isSigned.isVerified.message {
- border-top: 1px solid var(--color-success-border);
-}
-
-.repository .ui.attached.isSigned.isVerified.message {
- box-shadow: none;
- background-color: var(--color-success-bg);
- color: var(--color-success-text);
-}
-
-.repository .ui.attached.isSigned.isVerified.message .pull-right {
- color: var(--color-text);
-}
-
-.repository .ui.attached.isSigned.isVerified.message .ui.text {
- color: var(--color-success-text);
-}
-
-.repository .ui.attached.isSigned.isVerified:last-child,
-.repository .ui.attached.isSigned.isVerified.bottom {
- border-bottom: 1px solid var(--color-success-border);
-}
-
-.repository .ui.attached.isSigned.isVerifiedUntrusted,
-.repository .ui.attached.isSigned.isVerifiedUnmatched {
- border-left: 1px solid var(--color-warning-border);
- border-right: 1px solid var(--color-warning-border);
-}
-
-.repository .ui.attached.isSigned.isVerifiedUntrusted.top,
-.repository .ui.attached.isSigned.isVerifiedUnmatched.top,
-.repository .ui.attached.isSigned.isVerifiedUntrusted.message,
-.repository .ui.attached.isSigned.isVerifiedUnmatched.message {
- border-top: 1px solid var(--color-warning-border);
-}
-
-.repository .ui.attached.isSigned.isVerifiedUntrusted.message,
-.repository .ui.attached.isSigned.isVerifiedUnmatched.message {
- box-shadow: none;
- background-color: var(--color-warning-bg);
- color: var(--color-warning-text);
-}
-
-.repository .ui.attached.isSigned.isVerifiedUntrusted.message .ui.text,
-.repository .ui.attached.isSigned.isVerifiedUnmatched.message .ui.text {
- color: var(--color-warning-text);
-}
-
-.repository .ui.attached.isSigned.isVerifiedUntrusted:last-child,
-.repository .ui.attached.isSigned.isVerifiedUnmatched:last-child,
-.repository .ui.attached.isSigned.isVerifiedUntrusted.bottom,
-.repository .ui.attached.isSigned.isVerifiedUnmatched.bottom {
- border-bottom: 1px solid var(--color-warning-border);
-}
-
-.repository #commits-table td.sha .sha.label,
-.repository #repo-files-table .sha.label,
-.repository #repo-file-commit-box .sha.label,
-.repository #rev-list .sha.label,
-.repository .timeline-item.commits-list .singular-commit .sha.label {
+.ui.label.commit-id-short,
+.ui.label.commit-sign-badge {
border: 1px solid var(--color-light-border);
+ font-size: 13px;
+ font-weight: var(--font-weight-normal);
+ padding: 3px 5px;
+ flex-shrink: 0;
}
-.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,
-.repository #rev-list .sha.label .detail.icon,
-.repository .timeline-item.commits-list .singular-commit .sha.label .detail.icon {
- background: var(--color-light);
- margin: -6px -10px -4px 0;
- padding: 5px 4px 5px 6px;
- border-left: 1px solid var(--color-light-border);
- border-top: 0;
- border-right: 0;
- border-bottom: 0;
- border-top-left-radius: 0;
- border-bottom-left-radius: 0;
+.ui.label.commit-id-short {
+ font-family: var(--fonts-monospace);
}
-.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,
-.repository #rev-list .sha.label .detail.icon .svg,
-.repository .timeline-item.commits-list .singular-commit .sha.label .detail.icon .svg {
- margin: 0 0.25em 0 0;
-}
-
-.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,
-.repository #rev-list .sha.label .detail.icon > div,
-.repository .timeline-item.commits-list .singular-commit .sha.label .detail.icon > div {
- display: flex;
- align-items: center;
-}
-
-.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,
-.repository #rev-list .sha.label.isSigned.isWarning,
-.repository .timeline-item.commits-list .singular-commit .sha.label.isSigned.isWarning {
- border: 1px solid var(--color-red-badge);
- background: var(--color-red-badge-bg);
-}
-
-.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,
-.repository #rev-list .sha.label.isSigned.isWarning .detail.icon,
-.repository .timeline-item.commits-list .singular-commit .sha.label.isSigned.isWarning .detail.icon {
- border-left: 1px solid var(--color-red-badge);
- color: var(--color-red-badge);
-}
-
-.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,
-.repository #rev-list .sha.label.isSigned.isWarning:hover,
-.repository .timeline-item.commits-list .singular-commit .sha.label.isSigned.isWarning:hover {
- background: var(--color-red-badge-hover-bg) !important;
-}
-
-.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,
-.repository #rev-list .sha.label.isSigned.isVerified,
-.repository .timeline-item.commits-list .singular-commit .sha.label.isSigned.isVerified {
- border: 1px solid var(--color-green-badge);
- background: var(--color-green-badge-bg);
-}
-
-.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,
-.repository #rev-list .sha.label.isSigned.isVerified .detail.icon,
-.repository .timeline-item.commits-list .singular-commit .sha.label.isSigned.isVerified .detail.icon {
- border-left: 1px solid var(--color-green-badge);
- color: var(--color-green-badge);
-}
-
-.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,
-.repository #rev-list .sha.label.isSigned.isVerified:hover,
-.repository .timeline-item.commits-list .singular-commit .sha.label.isSigned.isVerified:hover {
- background: var(--color-green-badge-hover-bg) !important;
-}
-
-.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,
-.repository #rev-list .sha.label.isSigned.isVerifiedUntrusted,
-.repository .timeline-item.commits-list .singular-commit .sha.label.isSigned.isVerifiedUntrusted {
- border: 1px solid var(--color-yellow-badge);
- background: var(--color-yellow-badge-bg);
-}
-
-.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,
-.repository #rev-list .sha.label.isSigned.isVerifiedUntrusted .detail.icon,
-.repository .timeline-item.commits-list .singular-commit .sha.label.isSigned.isVerifiedUntrusted .detail.icon {
- border-left: 1px solid var(--color-yellow-badge);
- color: var(--color-yellow-badge);
-}
-
-.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,
-.repository #rev-list .sha.label.isSigned.isVerifiedUntrusted:hover,
-.repository .timeline-item.commits-list .singular-commit .sha.label.isSigned.isVerifiedUntrusted:hover {
- background: var(--color-yellow-badge-hover-bg) !important;
-}
-
-.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,
-.repository #rev-list .sha.label.isSigned.isVerifiedUnmatched,
-.repository .timeline-item.commits-list .singular-commit .sha.label.isSigned.isVerifiedUnmatched {
- border: 1px solid var(--color-orange-badge);
- background: var(--color-orange-badge-bg);
-}
-
-.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,
-.repository #rev-list .sha.label.isSigned.isVerifiedUnmatched .detail.icon,
-.repository .timeline-item.commits-list .singular-commit .sha.label.isSigned.isVerifiedUnmatched .detail.icon {
- border-left: 1px solid var(--color-orange-badge);
- color: var(--color-orange-badge);
-}
-
-.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,
-.repository #rev-list .sha.label.isSigned.isVerifiedUnmatched:hover,
-.repository .timeline-item.commits-list .singular-commit .sha.label.isSigned.isVerifiedUnmatched:hover {
- background: var(--color-orange-badge-hover-bg) !important;
-}
-
-.singular-commit .shabox .sha.label {
+.ui.label.commit-id-short > .commit-sign-badge {
margin: 0;
- border: 1px solid var(--color-light-border);
+ padding: 0;
+ border: 0 !important;
+ border-radius: 0;
+ background: transparent;
}
-.singular-commit .shabox .sha.label.isSigned.isWarning {
- border: 1px solid var(--color-red-badge);
- background: var(--color-red-badge-bg);
+.ui.label.commit-id-short > .commit-sign-badge:hover {
+ background: transparent !important;
}
-.singular-commit .shabox .sha.label.isSigned.isWarning:hover {
- background: var(--color-red-badge-hover-bg) !important;
+.commit-is-signed.sign-trusted {
+ border: 1px solid var(--color-green-badge) !important;
+ background: var(--color-green-badge-bg) !important;
}
-.singular-commit .shabox .sha.label.isSigned.isVerified {
- border: 1px solid var(--color-green-badge);
- background: var(--color-green-badge-bg);
-}
-
-.singular-commit .shabox .sha.label.isSigned.isVerified:hover {
+.commit-is-signed.sign-trusted:hover {
background: var(--color-green-badge-hover-bg) !important;
}
-.singular-commit .shabox .sha.label.isSigned.isVerifiedUntrusted {
- border: 1px solid var(--color-yellow-badge);
- background: var(--color-yellow-badge-bg);
+.commit-is-signed.sign-untrusted {
+ border: 1px solid var(--color-yellow-badge) !important;
+ background: var(--color-yellow-badge-bg) !important;
}
-.singular-commit .shabox .sha.label.isSigned.isVerifiedUntrusted:hover {
+.commit-is-signed.sign-untrusted:hover {
background: var(--color-yellow-badge-hover-bg) !important;
}
-.singular-commit .shabox .sha.label.isSigned.isVerifiedUnmatched {
- border: 1px solid var(--color-orange-badge);
- background: var(--color-orange-badge-bg);
+.commit-is-signed.sign-unmatched {
+ border: 1px solid var(--color-orange-badge) !important;
+ background: var(--color-orange-badge-bg) !important;
}
-.singular-commit .shabox .sha.label.isSigned.isVerifiedUnmatched:hover {
+.commit-is-signed.sign-unmatched:hover {
background: var(--color-orange-badge-hover-bg) !important;
}
+
+.commit-is-signed.sign-warning {
+ border: 1px solid var(--color-red-badge) !important;
+ background: var(--color-red-badge-bg) !important;
+}
+
+.commit-is-signed.sign-warning:hover {
+ background: var(--color-red-badge-hover-bg) !important;
+}