2 Commits
v12 ... master

Author SHA1 Message Date
Fritz Elfert
4ca48c76b4 Fix 234 (#271)
* Fix envelope handling

* Updated documentation

* Update main.js

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update README.md

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Implemented review suggestions

* Implemented review suggestions

* Implemented review suggestions

* Use nodemailer's addressparser instead of regular expressions

* Updated README regarding address formats

* Updated README regarding address formats

* Update README.md

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Use addressparser regardless of envelopeX ist set or not.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-13 10:51:21 +01:00
Fritz Elfert
e9d44227e5 Upgrade to node24 (#273) 2026-03-11 09:04:18 +01:00
3 changed files with 103 additions and 40 deletions

View File

@@ -27,54 +27,86 @@ Some features:
# * smtp://user:password@server:port
# * smtp+starttls://user:password@server:port
connection_url: ${{secrets.MAIL_CONNECTION}}
# Required mail server address if not connection_url:
server_address: smtp.gmail.com
# Server port, default 25:
server_port: 465
# Optional whether this connection use TLS (default is true if server_port is 465)
secure: true
# Optional (recommended) mail server username:
username: ${{secrets.MAIL_USERNAME}}
# Optional (recommended) mail server password:
password: ${{secrets.MAIL_PASSWORD}}
# Required mail subject:
subject: Github Actions job result
# Optional recipients' addresses:
to: obiwan@example.com,yoda@example.com
# Required sender (Either: "Plain Simple Name <user@doma.in>" or just "user@doma.in" (without the <>))
# Important: '<' and '>' are special chars in yaml. Therefore this string should be quoted
# Optional recipients. Separate multiple addresses by a comma (possibly surrounded by whitespace):
to: obiwan@example.com, yoda@example.com
# Required sender (supported formats: see "Supported address formats" below)
from: 'Luke Skywalker <user@example.com>'
# Optional plain body:
body: Build job of ${{github.repository}} completed successfully!
# Optional HTML body read from file:
html_body: file://README.html
# Optional carbon copy recipients:
cc: kyloren@example.com,leia@example.com
# Optional blind carbon copy recipients:
bcc: r2d2@example.com,hansolo@example.com
# Optional carbon copy recipients. Separate multiple addresses by a comma (possibly surrounded by whitespace):
cc: 'kyloren@example.com, "Her Majesty, Princess Leia" <leia@example.com>'
# Optional blind carbon copy recipients. Separate multiple addresses by a comma (possibly surrounded by whitespace):
bcc: r2d2@example.com, hansolo@example.com
# Optional recipient of the email response:
reply_to: luke@example.com
# Optional Message ID this message is replying to:
in_reply_to: <random-luke@example.com>
in_reply_to: '<3cc627c8-6181-453b-d90b-04aae9e23b21@github.com>'
# Optional unsigned/invalid certificates allowance:
ignore_cert: true
# Optional converting Markdown to HTML (set content_type to text/html too):
convert_markdown: true
# Optional attachments:
attachments: attachments.zip,git.diff,./dist/static/*.js
# Optional priority: 'high', 'normal' (default) or 'low'
priority: low
# Optional custom headers:
headers: '{"X-Priority": "3 (Normal)", "X-My-Header": "MyValue"}'
# Optional nodemailerlog: true/false
nodemailerlog: false
# Optional nodemailerdebug: true/false if true lognodem will also be set true
# Optional nodemailerdebug: true/false if true nodemailerlog will also be set true
nodemailerdebug: false
# Optional custom SMTP MAIL FROM address (overrides username):
envelope_from: mailer@example.com
# Optional custom SMTP RCPT TO addresses (overrides to, cc, bcc):
envelope_to: mailer@example.com,admin@example.com
# Optional custom SMTP RCPT TO addresses (overrides to, cc, bcc). Separate multiple addresses by a comma (possibly surrounded by whitespace):
envelope_to: mailer@example.com, admin@example.com
```
### Remark for `envelope_from` and `envelope_to`
[nodemailer](https://nodemailer.com/) (the node module that does the actual sending) requires that if the optional custom envelope is used, **both** its attributes `from` and `to` must be set. To facilitate setting only one of `envelope_from` or `envelope_to`, this action sets the other one from the regular message fields in the following way:
* If only `envelope_from` is set, `envelope_to` will be set to the concatenation of `to`, `cc` and `bcc` (with duplicates removed).
* If only `envelope_to` is set, `envelope_from` will be set to the address specified in `from`.
### Supported address formats
This action now uses nodemailer's addressparser. The supported address formats are described [here](https://nodemailer.com/message/addresses).
Mail addresses can contain YAML special characters like '<' and '>'. To avoid YAML parsing issues, addresses that contain such characters should be enclosed in single quotes.
## Troubleshooting

View File

@@ -73,5 +73,5 @@ inputs:
description: Custom envelope recipient addresses for SMTP RCPT TO command (separated with comma)
required: false
runs:
using: node20
using: node24
main: main.js

57
main.js
View File

@@ -1,4 +1,5 @@
import nodemailer from "nodemailer";
import addressparser from "nodemailer/lib/addressparser/index.js";
import * as core from "@actions/core";
import * as glob from "@actions/glob";
import fs from "node:fs";
@@ -39,6 +40,39 @@ function sleep(ms) {
});
}
/**
* Prepare an envelope object for nodemailer.
*
* If only one of envelopeFrom or envelopeTo is set, make sure that both
* are set in the returned object. Furthermore, make sure that the attribute 'to'
* is an array of email addresses, not a comma-separated string.
*/
function setupEnvelope(envelopeFrom, envelopeTo, from, to, cc, bcc) {
if (envelopeFrom || envelopeTo) {
// Take address in from, if envelopeFrom is not set.
envelopeFrom = envelopeFrom ? addressparser(envelopeFrom) : addressparser(from);
if (envelopeFrom.length != 1 || envelopeFrom[0].address == '') {
throw new Error("'envelopeFrom' address is invalid");
}
if (envelopeTo) {
envelopeTo = addressparser(envelopeTo);
} else {
// Take addresses in to, cc and bcc. Deduplication is handled by nodemailer.
for (const src of [to, cc, bcc]) {
if (src) {
let parsed = addressparser(src);
envelopeTo = envelopeTo ? envelopeTo.concat(parsed) : parsed;
}
}
}
return {
from: envelopeFrom,
to: envelopeTo,
};
}
return undefined;
}
async function main() {
try {
let serverAddress = core.getInput("server_address");
@@ -111,7 +145,8 @@ async function main() {
// Basic check for an email sender address
// Either: "Plain Simple Name <user@doma.in>" or just "user@doma.in" (without the <>)
if (!(/^([^<>@\s]+\s+)+<[^@\s>]+@[^@\s>]+>$/.test(from) || /^[^<>@\s]+@[^@\s<>]+$/.test(from))) {
let parsed = addressparser(from);
if (parsed.length != 1 || parsed[0].address == '') {
throw new Error("'from' address is invalid");
}
@@ -148,10 +183,7 @@ async function main() {
proxy: process.env.HTTP_PROXY,
});
let i = 1;
while (true) {
try {
const info = await transport.sendMail({
const messageOptions = {
from: from,
to: to,
subject: getText(subject, false),
@@ -169,14 +201,13 @@ async function main() {
attachments: attachments
? await getAttachments(attachments)
: undefined,
envelope:
envelopeFrom || envelopeTo
? {
from: envelopeFrom ? envelopeFrom : undefined,
to: envelopeTo ? envelopeTo : undefined,
}
: undefined,
});
envelope: setupEnvelope(envelopeFrom, envelopeTo, from, to, cc, bcc),
};
let i = 1;
while (true) {
try {
const info = await transport.sendMail(messageOptions);
break;
} catch (error) {
if (!error.message.includes("Try again later,")) {