mirror of
https://github.com/go-gitea/gitea.git
synced 2025-07-22 03:32:08 +02:00
MAILER
This commit is contained in:
parent
c8b64c7686
commit
027c9fef2b
@ -16,6 +16,8 @@ import (
|
||||
"testing"
|
||||
texttmpl "text/template"
|
||||
|
||||
actions_model "code.gitea.io/gitea/models/actions"
|
||||
|
||||
activities_model "code.gitea.io/gitea/models/activities"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
issues_model "code.gitea.io/gitea/models/issues"
|
||||
@ -440,6 +442,16 @@ func TestGenerateMessageIDForRelease(t *testing.T) {
|
||||
assert.Equal(t, "<owner/repo/releases/1@localhost>", msgID)
|
||||
}
|
||||
|
||||
func TestGenerateMessageIDForActionsWorkflowRunStatusEmail(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
|
||||
run := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{ID: 795, RepoID: repo.ID})
|
||||
assert.NoError(t, run.LoadAttributes(db.DefaultContext))
|
||||
msgID := generateMessageIDForActionsWorkflowRunStatusEmail(repo, run)
|
||||
assert.Equal(t, "<user2/repo2/actions/runs/191@localhost>", msgID)
|
||||
}
|
||||
|
||||
func TestFromDisplayName(t *testing.T) {
|
||||
tmpl, err := texttmpl.New("mailFrom").Parse("{{ .DisplayName }}")
|
||||
assert.NoError(t, err)
|
||||
|
133
services/mailer/mail_workflow_run.go
Normal file
133
services/mailer/mail_workflow_run.go
Normal file
@ -0,0 +1,133 @@
|
||||
// Copyright 2025 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package mailer
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
actions_model "code.gitea.io/gitea/models/actions"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
sender_service "code.gitea.io/gitea/services/mailer/sender"
|
||||
)
|
||||
|
||||
const tplWorkflowRun = "notify/workflow_run"
|
||||
|
||||
func generateMessageIDForActionsWorkflowRunStatusEmail(repo *repo_model.Repository, run *actions_model.ActionRun) string {
|
||||
return fmt.Sprintf("<%s/actions/runs/%d@%s>", repo.FullName(), run.Index, setting.Domain)
|
||||
}
|
||||
|
||||
func sendActionsWorkflowRunStatusEmail(ctx context.Context, repo *repo_model.Repository, run *actions_model.ActionRun, sender *user_model.User, recipients []*user_model.User) {
|
||||
msgs := make([]*sender_service.Message, 0, len(recipients))
|
||||
|
||||
messageID := generateMessageIDForActionsWorkflowRunStatusEmail(repo, run)
|
||||
headers := generateMetadataHeaders(repo)
|
||||
|
||||
subject := "Run"
|
||||
if run.IsForkPullRequest {
|
||||
subject = "PR run"
|
||||
}
|
||||
switch run.Status {
|
||||
case actions_model.StatusFailure:
|
||||
subject = subject + " failed"
|
||||
case actions_model.StatusCancelled:
|
||||
subject = subject + " cancelled"
|
||||
case actions_model.StatusSuccess:
|
||||
subject = subject + " is successful"
|
||||
}
|
||||
subject = fmt.Sprintf("%s: %s (%s)", subject, run.WorkflowID, base.ShortSha(run.CommitSHA))
|
||||
|
||||
jobs, err := actions_model.GetRunJobsByRunID(ctx, run.ID)
|
||||
if err != nil {
|
||||
log.Error("GetRunJobsByRunID: %v", err)
|
||||
} else {
|
||||
sort.SliceStable(jobs, func(i, j int) bool {
|
||||
si := jobs[i].Status
|
||||
sj := jobs[j].Status
|
||||
if si.IsSuccess() {
|
||||
si = 99
|
||||
}
|
||||
if sj.IsSuccess() {
|
||||
sj = 99
|
||||
}
|
||||
return si < sj
|
||||
})
|
||||
}
|
||||
|
||||
var mailBody bytes.Buffer
|
||||
if err := bodyTemplates.ExecuteTemplate(&mailBody, tplWorkflowRun, map[string]any{
|
||||
"Subject": subject,
|
||||
"Repo": repo,
|
||||
"Run": run,
|
||||
"Jobs": jobs,
|
||||
}); err != nil {
|
||||
log.Error("ExecuteTemplate [%s]: %v", tplWorkflowRun, err)
|
||||
}
|
||||
|
||||
for _, recipient := range recipients {
|
||||
msg := sender_service.NewMessageFrom(
|
||||
recipient.Email,
|
||||
fromDisplayName(sender),
|
||||
setting.MailService.FromEmail,
|
||||
subject,
|
||||
mailBody.String(),
|
||||
)
|
||||
msg.Info = subject
|
||||
for k, v := range generateSenderRecipientHeaders(sender, recipient) {
|
||||
msg.SetHeader(k, v)
|
||||
}
|
||||
for k, v := range headers {
|
||||
msg.SetHeader(k, v)
|
||||
}
|
||||
msg.SetHeader("Message-ID", messageID)
|
||||
msgs = append(msgs, msg)
|
||||
}
|
||||
SendAsync(msgs...)
|
||||
}
|
||||
|
||||
func SendActionsWorkflowRunStatusEmail(ctx context.Context, sender *user_model.User, repo *repo_model.Repository, run *actions_model.ActionRun) {
|
||||
if setting.MailService == nil {
|
||||
return
|
||||
}
|
||||
if run.Status.IsSkipped() {
|
||||
return
|
||||
}
|
||||
|
||||
recipients := make([]*user_model.User, 0)
|
||||
|
||||
if !sender.IsGiteaActions() && !sender.IsGhost() && sender.IsMailable() {
|
||||
if run.Status.IsSuccess() {
|
||||
if sender.EmailNotificationsPreference == user_model.EmailNotificationsAndYourOwn {
|
||||
recipients = append(recipients, sender)
|
||||
}
|
||||
sendActionsWorkflowRunStatusEmail(ctx, repo, run, sender, recipients)
|
||||
return
|
||||
} else if sender.EmailNotificationsPreference != user_model.EmailNotificationsOnMention &&
|
||||
sender.EmailNotificationsPreference != user_model.EmailNotificationsDisabled {
|
||||
recipients = append(recipients, sender)
|
||||
}
|
||||
}
|
||||
|
||||
watchers, err := repo_model.GetRepoWatchers(ctx, repo.ID, db.ListOptionsAll)
|
||||
if err != nil {
|
||||
log.Error("GetWatchers: %v", err)
|
||||
}
|
||||
for _, watcher := range watchers {
|
||||
if watcher.ID == sender.ID {
|
||||
continue
|
||||
}
|
||||
if watcher.IsMailable() && watcher.EmailNotificationsPreference != user_model.EmailNotificationsOnMention &&
|
||||
watcher.EmailNotificationsPreference != user_model.EmailNotificationsDisabled {
|
||||
recipients = append(recipients, watcher)
|
||||
}
|
||||
}
|
||||
sendActionsWorkflowRunStatusEmail(ctx, repo, run, sender, recipients)
|
||||
}
|
@ -7,6 +7,8 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
actions_model "code.gitea.io/gitea/models/actions"
|
||||
|
||||
activities_model "code.gitea.io/gitea/models/activities"
|
||||
issues_model "code.gitea.io/gitea/models/issues"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
@ -205,3 +207,10 @@ func (m *mailNotifier) RepoPendingTransfer(ctx context.Context, doer, newOwner *
|
||||
log.Error("SendRepoTransferNotifyMail: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *mailNotifier) WorkflowRunStatusUpdate(ctx context.Context, repo *repo_model.Repository, sender *user_model.User, run *actions_model.ActionRun) {
|
||||
if !run.Status.IsDone() {
|
||||
return
|
||||
}
|
||||
SendActionsWorkflowRunStatusEmail(ctx, sender, repo, run)
|
||||
}
|
||||
|
25
templates/mail/notify/workflow_run.tmpl
Normal file
25
templates/mail/notify/workflow_run.tmpl
Normal file
@ -0,0 +1,25 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<meta name="format-detection" content="telephone=no,date=no,address=no,email=no,url=no">
|
||||
<title>{{.Subject}}</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>{{.Repo.FullName}} {{.Run.WorkflowID}}: {{.Run.Status}}</h1>
|
||||
<ul>
|
||||
{{range $index, $job := .Jobs}}
|
||||
<li>
|
||||
<a href="{{$.Run.Link}}/jobs/{{$index}}">
|
||||
{{$job.Status}}: {{$job.Name}}{{if $job.Attempt gt 1}}, Attempt #{{$job.Attempt}}{{end}}
|
||||
</a>
|
||||
</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
<p>
|
||||
---
|
||||
<br>
|
||||
<a href="{{.Run.Link}}">{{.locale.Tr "mail.view_it_on" AppName}}</a>.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
Loading…
x
Reference in New Issue
Block a user