feat: email on action run status change

This commit is contained in:
christopher-besch 2025-04-09 15:55:13 +02:00
parent 9e148bce3e
commit 21fb2f8fe0
4 changed files with 132 additions and 0 deletions

View file

@ -576,6 +576,12 @@ team_invite.text_1 = %[1]s has invited you to join team %[2]s in organization %[
team_invite.text_2 = Please click the following link to join the team: team_invite.text_2 = Please click the following link to join the team:
team_invite.text_3 = Note: This invitation was intended for %[1]s. If you were not expecting this invitation, you can ignore this email. team_invite.text_3 = Note: This invitation was intended for %[1]s. If you were not expecting this invitation, you can ignore this email.
actions.successful_run_after_failure_subject = Workflow %[1]s recovered in repository %[2]s
actions.not_successful_run_subject = Workflow %[1]s failed in repository %[2]s
actions.successful_run_after_failure = Workflow %[1]s recovered in repository %[2]s
actions.not_successful_run = Workflow %[1]s failed in repository %[2]s
actions.run_info = This Run's Status: %[1]s (just updated from %[2]s)<br />Previous Run's Status: %[3]s<br />Branch: %[4]s<br />Commit: %[5]s<br />Triggered because: %[6]s<br />Triggered by: %[7]s
[modal] [modal]
yes = Yes yes = Yes
no = No no = No

View file

@ -0,0 +1,84 @@
// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: GPL-3.0-or-later
package mailer
import (
"bytes"
actions_model "forgejo.org/models/actions"
user_model "forgejo.org/models/user"
"forgejo.org/modules/base"
"forgejo.org/modules/setting"
"forgejo.org/modules/translation"
)
const (
tplActionNowDone base.TplName = "actions/now_done"
)
// requires !run.Status.IsSuccess() or !lastRun.Status.IsSuccess()
func MailActionRun(run *actions_model.ActionRun, priorStatus actions_model.Status, lastRun *actions_model.ActionRun) error {
if setting.MailService == nil {
// No mail service configured
return nil
}
if run.TriggerUser.Email != "" && run.TriggerUser.EmailNotificationsPreference != user_model.EmailNotificationsDisabled {
if err := sendMailActionRun(run.TriggerUser, run, priorStatus, lastRun); err != nil {
return err
}
}
if run.Repo.Owner.Email != "" && run.Repo.Owner.Email != run.TriggerUser.Email && run.Repo.Owner.EmailNotificationsPreference != user_model.EmailNotificationsDisabled {
if err := sendMailActionRun(run.Repo.Owner, run, priorStatus, lastRun); err != nil {
return err
}
}
return nil
}
func sendMailActionRun(to *user_model.User, run *actions_model.ActionRun, priorStatus actions_model.Status, lastRun *actions_model.ActionRun) error {
var (
locale = translation.NewLocale(to.Language)
content bytes.Buffer
)
var subject string
if run.Status.IsSuccess() {
subject = locale.TrString("mail.actions.successful_run_after_failure_subject", run.Title, run.Repo.FullName())
} else {
subject = locale.TrString("mail.actions.not_successful_run", run.Title, run.Repo.FullName())
}
commitSHA := run.CommitSHA
if len(commitSHA) > 7 {
commitSHA = commitSHA[:7]
}
branch := run.PrettyRef()
data := map[string]any{
"locale": locale,
"Link": run.HTMLURL(),
"Subject": subject,
"Language": locale.Language(),
"RepoFullName": run.Repo.FullName(),
"Run": run,
"TriggerUserLink": run.TriggerUser.HTMLURL(),
"LastRun": lastRun,
"PriorStatus": priorStatus,
"CommitSHA": commitSHA,
"Branch": branch,
"IsSuccess": run.Status.IsSuccess(),
}
if err := bodyTemplates.ExecuteTemplate(&content, string(tplActionNowDone), data); err != nil {
return err
}
msg := NewMessage(to.EmailTo(), subject, content.String())
msg.Info = subject
SendAsync(msg)
return nil
}

View file

@ -7,6 +7,7 @@ import (
"context" "context"
"fmt" "fmt"
actions_model "forgejo.org/models/actions"
activities_model "forgejo.org/models/activities" activities_model "forgejo.org/models/activities"
issues_model "forgejo.org/models/issues" issues_model "forgejo.org/models/issues"
repo_model "forgejo.org/models/repo" repo_model "forgejo.org/models/repo"
@ -208,3 +209,12 @@ func (m *mailNotifier) RepoPendingTransfer(ctx context.Context, doer, newOwner *
func (m *mailNotifier) NewUserSignUp(ctx context.Context, newUser *user_model.User) { func (m *mailNotifier) NewUserSignUp(ctx context.Context, newUser *user_model.User) {
MailNewUser(ctx, newUser) MailNewUser(ctx, newUser)
} }
func (m *mailNotifier) ActionRunNowDone(ctx context.Context, run *actions_model.ActionRun, priorStatus actions_model.Status, lastRun *actions_model.ActionRun) {
if run.Status.IsSuccess() && lastRun.Status.IsSuccess() {
return
}
if err := MailActionRun(run, priorStatus, lastRun); err != nil {
log.Error("MailActionRunNowDone: %v", err)
}
}

View file

@ -0,0 +1,32 @@
<!DOCTYPE html>
<html>
<head>
<style>
.footer { font-size:small; color:#666;}
</style>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
{{$repo_link := HTMLFormat "<a href='%s'>%s</a>" .Run.Repo.HTMLURL .RepoFullName}}
{{$action_run_link := HTMLFormat "<a href='%s'>%s</a>" .Link .Run.Title}}
{{$trigger_user_link := HTMLFormat "<a href='%s'>@%s</a>" .Run.TriggerUser.HTMLURL .Run.TriggerUser.Name}}
<body>
<p>
{{if .IsSuccess}}
{{.locale.Tr "mail.actions.successful_run_after_failure" $action_run_link $repo_link}}
{{else}}
{{.locale.Tr "mail.actions.not_successful_run" $action_run_link $repo_link}}
{{end}}
<br />
{{.locale.Tr "mail.actions.run_info" .Run.Status .PriorStatus .LastRun.Status .Branch .CommitSHA .Run.TriggerEvent $trigger_user_link}}
</p>
<div class="footer">
<p>
---
<br>
<a href="{{.Link}}">{{.locale.Tr "mail.view_it_on" AppName}}</a>.
</p>
</div>
</body>
</html>