mirror of
https://github.com/dawidd6/action-send-mail.git
synced 2026-03-14 21:24:11 +07:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4ca48c76b4 | ||
|
|
e9d44227e5 |
56
README.md
56
README.md
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
85
main.js
85
main.js
@@ -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,35 +183,31 @@ async function main() {
|
||||
proxy: process.env.HTTP_PROXY,
|
||||
});
|
||||
|
||||
const messageOptions = {
|
||||
from: from,
|
||||
to: to,
|
||||
subject: getText(subject, false),
|
||||
cc: cc ? cc : undefined,
|
||||
bcc: bcc ? bcc : undefined,
|
||||
replyTo: replyTo ? replyTo : undefined,
|
||||
inReplyTo: inReplyTo ? inReplyTo : undefined,
|
||||
references: inReplyTo ? inReplyTo : undefined,
|
||||
text: body ? getText(body, false) : undefined,
|
||||
html: htmlBody
|
||||
? getText(htmlBody, convertMarkdown)
|
||||
: undefined,
|
||||
priority: priority ? priority : undefined,
|
||||
headers: headers ? JSON.parse(headers) : undefined,
|
||||
attachments: attachments
|
||||
? await getAttachments(attachments)
|
||||
: undefined,
|
||||
envelope: setupEnvelope(envelopeFrom, envelopeTo, from, to, cc, bcc),
|
||||
};
|
||||
|
||||
let i = 1;
|
||||
while (true) {
|
||||
try {
|
||||
const info = await transport.sendMail({
|
||||
from: from,
|
||||
to: to,
|
||||
subject: getText(subject, false),
|
||||
cc: cc ? cc : undefined,
|
||||
bcc: bcc ? bcc : undefined,
|
||||
replyTo: replyTo ? replyTo : undefined,
|
||||
inReplyTo: inReplyTo ? inReplyTo : undefined,
|
||||
references: inReplyTo ? inReplyTo : undefined,
|
||||
text: body ? getText(body, false) : undefined,
|
||||
html: htmlBody
|
||||
? getText(htmlBody, convertMarkdown)
|
||||
: undefined,
|
||||
priority: priority ? priority : undefined,
|
||||
headers: headers ? JSON.parse(headers) : undefined,
|
||||
attachments: attachments
|
||||
? await getAttachments(attachments)
|
||||
: undefined,
|
||||
envelope:
|
||||
envelopeFrom || envelopeTo
|
||||
? {
|
||||
from: envelopeFrom ? envelopeFrom : undefined,
|
||||
to: envelopeTo ? envelopeTo : undefined,
|
||||
}
|
||||
: undefined,
|
||||
});
|
||||
const info = await transport.sendMail(messageOptions);
|
||||
break;
|
||||
} catch (error) {
|
||||
if (!error.message.includes("Try again later,")) {
|
||||
|
||||
Reference in New Issue
Block a user