Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add fix for uncleared spaces around variables and file uploads #166

Merged
merged 5 commits into from
Mar 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ SLACK_ON_SUCCESS | - | I
SLACK_ON_FAILURE | - | If set, will send the provided message instead of the default message when the passed status (through ``SLACK_COLOR``) is `failure`.
SLACK_ON_CANCEL | - | If set, will send the provided message instead of the default message when the passed status (through ``SLACK_COLOR``) is `cancelled`.
SLACK_CUSTOM_PAYLOAD | - | If you want to send a custom payload to slack, you can pass it as a string to this variable. This will override all other variables and send the custom payload to slack. Example: `SLACK_CUSTOM_PAYLOAD: '{"text": "Hello, World!"}'`, Note: This payload should be in JSON format, and is not validated by the action.
SLACK_FILE_UPLOAD | - | If you want to upload a file to slack, you can pass the file path to this variable. Example: `SLACK_FILE_UPLOAD: /path/to/file.txt`. Note: This file should be present in the repository, or github workspace. Otherwise, should be accessable in the container the action is running in.
ENABLE_ESCAPES | - | If set to `true`, will enable backslash escape sequences such as `\n`, `\t`, etc. in the message. Note: This only works for custom messages and not for the default message generated by the action.


Expand All @@ -85,7 +86,7 @@ Below screenshot help you visualize message part controlled by different variabl

The `Site` and `SSH Host` details are only available if this action is run after [Deploy WordPress GitHub action](https://github.com/rtCamp/action-deploy-wordpress).

## Hashicorp Vault (Optional)
## Hashicorp Vault (Optional) (Deprecated)

This GitHub action supports [Hashicorp Vault](https://www.vaultproject.io/).

Expand Down
4 changes: 2 additions & 2 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ runs:
SLACK_MESSAGE: "${{ steps.slackify.outputs.text }}"
GITHUB_RUN: "${{ github.event.repository.html_url }}/actions/runs/${{ github.run_id }}"
ENABLE_ESCAPES: "true"
uses: "docker://ghcr.io/rtcamp/action-slack-notify:v2.2.1"
uses: "docker://ghcr.io/rtCamp/action-slack-notify:v2.2.1"

- name: "Slack Notification (Unformatted)"
if: env.SLACKIFY_MARKDOWN != 'true'
uses: "docker://ghcr.io/rtcamp/action-slack-notify:v2.2.1"
uses: "docker://ghcr.io/rtCamp/action-slack-notify:v2.2.1"
env:
GITHUB_RUN: "${{ github.event.repository.html_url }}/actions/runs/${{ github.run_id }}"
branding:
Expand Down
12 changes: 12 additions & 0 deletions entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ if [[ -z "$SLACK_WEBHOOK" ]]; then
missing_secret="SLACK_WEBHOOK"
if [[ -n "$VAULT_ADDR" ]] && [[ -n "$VAULT_TOKEN" ]]; then
flag=0
echo -e "[\e[0;33mWARNING\e[0m] Both \`VAULT_ADDR\` and \`VAULT_TOKEN\` are provided. Using Vault for secrets. This feature is deprecated and will be removed in future versions. Please provide the credentials directly.\n"
fi
if [[ -n "$VAULT_ADDR" ]] || [[ -n "$VAULT_TOKEN" ]]; then
missing_secret="VAULT_ADDR and/or VAULT_TOKEN"
Expand All @@ -27,6 +28,17 @@ fi

export MSG_MODE="$mode"

if [[ -n "$SLACK_FILE_UPLOAD" ]]; then
if [[ -z "$SLACK_TOKEN" ]]; then
echo -e "[\e[0;31mERROR\e[0m] Secret \`SLACK_TOKEN\` is missing and a file upload is specified. File Uploads require an application token to be present.\n"
exit 1
fi
if [[ -z "$SLACK_CHANNEL" ]]; then
echo -e "[\e[0;31mERROR\e[0m] Secret \`SLACK_CHANNEL\` is missing and a file upload is specified. File Uploads require a channel to be specified.\n"
exit 1
fi
fi

# custom path for files to override default files
custom_path="$GITHUB_WORKSPACE/.github/slack"
main_script="/main.sh"
Expand Down
151 changes: 113 additions & 38 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"bytes"
"encoding/json"
"fmt"
"io"
"mime/multipart"
"net/http"
"os"
"strings"
Expand All @@ -30,6 +32,7 @@ const (
EnvMinimal = "MSG_MINIMAL"
EnvSlackLinkNames = "SLACK_LINK_NAMES"
EnvThreadTs = "SLACK_THREAD_TS"
EnvSlackUpload = "SLACK_FILE_UPLOAD"
EnvMessageMode = "MSG_MODE"
)

Expand Down Expand Up @@ -63,14 +66,14 @@ type Field struct {
}

func main() {
endpoint := os.Getenv(EnvSlackWebhook)
endpoint := getEnv(EnvSlackWebhook)
custom_payload := envOr(EnvSlackCustom, "")
if endpoint == "" {
if os.Getenv(EnvSlackChannel) == "" {
if getEnv(EnvSlackChannel) == "" {
fmt.Fprintln(os.Stderr, "Channel is required for sending message using a token")
os.Exit(1)
}
if os.Getenv(EnvMessageMode) == "TOKEN" {
if getEnv(EnvMessageMode) == "TOKEN" {
endpoint = "https://slack.com/api/chat.postMessage"
} else {
fmt.Fprintln(os.Stderr, "URL is required")
Expand All @@ -83,24 +86,24 @@ func main() {
os.Exit(2)
}
} else {
text := os.Getenv(EnvSlackMessage)
text := getEnv(EnvSlackMessage)
if text == "" {
fmt.Fprintln(os.Stderr, "Message is required")
os.Exit(3)
}
if strings.HasPrefix(os.Getenv("GITHUB_WORKFLOW"), ".github") {
if strings.HasPrefix(getEnv("GITHUB_WORKFLOW"), ".github") {
err := os.Setenv("GITHUB_WORKFLOW", "Link to action run.yaml")
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to update the workflow's variables: %s\n\n", err)
os.Exit(4)
}
}

long_sha := os.Getenv("GITHUB_SHA")
long_sha := getEnv("GITHUB_SHA")
commit_sha := long_sha[0:6]

color := ""
switch strings.ToLower(os.Getenv(EnvSlackColor)) {
switch strings.ToLower(getEnv(EnvSlackColor)) {
case "success":
color = "good"
text = envOr(EnvSlackOnSuccess, text) // If exists, override with on success
Expand All @@ -118,12 +121,12 @@ func main() {
text = "EOM"
}

minimal := os.Getenv(EnvMinimal)
minimal := getEnv(EnvMinimal)
fields := []Field{}
if minimal == "true" {
mainFields := []Field{
{
Title: os.Getenv(EnvSlackTitle),
Title: getEnv(EnvSlackTitle),
Value: text,
Short: false,
},
Expand All @@ -133,7 +136,7 @@ func main() {
requiredFields := strings.Split(minimal, ",")
mainFields := []Field{
{
Title: os.Getenv(EnvSlackTitle),
Title: getEnv(EnvSlackTitle),
Value: text,
Short: false,
},
Expand All @@ -144,7 +147,7 @@ func main() {
field := []Field{
{
Title: "Ref",
Value: os.Getenv("GITHUB_REF"),
Value: getEnv("GITHUB_REF"),
Short: true,
},
}
Expand All @@ -153,7 +156,7 @@ func main() {
field := []Field{
{
Title: "Event",
Value: os.Getenv("GITHUB_EVENT_NAME"),
Value: getEnv("GITHUB_EVENT_NAME"),
Short: true,
},
}
Expand All @@ -162,7 +165,7 @@ func main() {
field := []Field{
{
Title: "Actions URL",
Value: "<" + os.Getenv("GITHUB_SERVER_URL") + "/" + os.Getenv("GITHUB_REPOSITORY") + "/commit/" + os.Getenv("GITHUB_SHA") + "/checks|" + os.Getenv("GITHUB_WORKFLOW") + ">",
Value: "<" + getEnv("GITHUB_SERVER_URL") + "/" + getEnv("GITHUB_REPOSITORY") + "/commit/" + getEnv("GITHUB_SHA") + "/checks|" + getEnv("GITHUB_WORKFLOW") + ">",
Short: true,
},
}
Expand All @@ -171,7 +174,7 @@ func main() {
field := []Field{
{
Title: "Commit",
Value: "<" + os.Getenv("GITHUB_SERVER_URL") + "/" + os.Getenv("GITHUB_REPOSITORY") + "/commit/" + os.Getenv("GITHUB_SHA") + "|" + commit_sha + ">",
Value: "<" + getEnv("GITHUB_SERVER_URL") + "/" + getEnv("GITHUB_REPOSITORY") + "/commit/" + getEnv("GITHUB_SHA") + "|" + commit_sha + ">",
Short: true,
},
}
Expand All @@ -183,64 +186,64 @@ func main() {
mainFields := []Field{
{
Title: "Ref",
Value: os.Getenv("GITHUB_REF"),
Value: getEnv("GITHUB_REF"),
Short: true,
}, {
Title: "Event",
Value: os.Getenv("GITHUB_EVENT_NAME"),
Value: getEnv("GITHUB_EVENT_NAME"),
Short: true,
},
{
Title: "Actions URL",
Value: "<" + os.Getenv("GITHUB_SERVER_URL") + "/" + os.Getenv("GITHUB_REPOSITORY") + "/commit/" + os.Getenv("GITHUB_SHA") + "/checks|" + os.Getenv("GITHUB_WORKFLOW") + ">",
Value: "<" + getEnv("GITHUB_SERVER_URL") + "/" + getEnv("GITHUB_REPOSITORY") + "/commit/" + getEnv("GITHUB_SHA") + "/checks|" + getEnv("GITHUB_WORKFLOW") + ">",
Short: true,
},
{
Title: "Commit",
Value: "<" + os.Getenv("GITHUB_SERVER_URL") + "/" + os.Getenv("GITHUB_REPOSITORY") + "/commit/" + os.Getenv("GITHUB_SHA") + "|" + commit_sha + ">",
Value: "<" + getEnv("GITHUB_SERVER_URL") + "/" + getEnv("GITHUB_REPOSITORY") + "/commit/" + getEnv("GITHUB_SHA") + "|" + commit_sha + ">",
Short: true,
},
{
Title: os.Getenv(EnvSlackTitle),
Title: getEnv(EnvSlackTitle),
Value: text,
Short: false,
},
}
fields = append(mainFields, fields...)
}

hostName := os.Getenv(EnvHostName)
hostName := getEnv(EnvHostName)
if hostName != "" {
newfields := []Field{
{
Title: os.Getenv("SITE_TITLE"),
Value: os.Getenv(EnvSiteName),
Title: getEnv("SITE_TITLE"),
Value: getEnv(EnvSiteName),
Short: true,
},
{
Title: os.Getenv("HOST_TITLE"),
Value: os.Getenv(EnvHostName),
Title: getEnv("HOST_TITLE"),
Value: getEnv(EnvHostName),
Short: true,
},
}
fields = append(newfields, fields...)
}

msg := Webhook{
UserName: os.Getenv(EnvSlackUserName),
IconURL: os.Getenv(EnvSlackIcon),
IconEmoji: os.Getenv(EnvSlackIconEmoji),
Channel: os.Getenv(EnvSlackChannel),
LinkNames: os.Getenv(EnvSlackLinkNames),
ThreadTs: os.Getenv(EnvThreadTs),
UserName: getEnv(EnvSlackUserName),
IconURL: getEnv(EnvSlackIcon),
IconEmoji: getEnv(EnvSlackIconEmoji),
Channel: getEnv(EnvSlackChannel),
LinkNames: getEnv(EnvSlackLinkNames),
ThreadTs: getEnv(EnvThreadTs),
Attachments: []Attachment{
{
Fallback: envOr(EnvSlackMessage, "GITHUB_ACTION="+os.Getenv("GITHUB_ACTION")+" \n GITHUB_ACTOR="+os.Getenv("GITHUB_ACTOR")+" \n GITHUB_EVENT_NAME="+os.Getenv("GITHUB_EVENT_NAME")+" \n GITHUB_REF="+os.Getenv("GITHUB_REF")+" \n GITHUB_REPOSITORY="+os.Getenv("GITHUB_REPOSITORY")+" \n GITHUB_WORKFLOW="+os.Getenv("GITHUB_WORKFLOW")),
Fallback: envOr(EnvSlackMessage, "GITHUB_ACTION="+getEnv("GITHUB_ACTION")+" \n GITHUB_ACTOR="+getEnv("GITHUB_ACTOR")+" \n GITHUB_EVENT_NAME="+getEnv("GITHUB_EVENT_NAME")+" \n GITHUB_REF="+getEnv("GITHUB_REF")+" \n GITHUB_REPOSITORY="+getEnv("GITHUB_REPOSITORY")+" \n GITHUB_WORKFLOW="+getEnv("GITHUB_WORKFLOW")),
Color: color,
AuthorName: envOr(EnvGithubActor, ""),
AuthorLink: os.Getenv("GITHUB_SERVER_URL") + "/" + os.Getenv(EnvGithubActor),
AuthorIcon: os.Getenv("GITHUB_SERVER_URL") + "/" + os.Getenv(EnvGithubActor) + ".png?size=32",
Footer: envOr(EnvSlackFooter, "<https://github.com/rtCamp/github-actions-library|Powered By rtCamp's GitHub Actions Library> | <"+os.Getenv(EnvGithubRun)+"|Triggered on this workflow run>"),
AuthorLink: getEnv("GITHUB_SERVER_URL") + "/" + getEnv(EnvGithubActor),
AuthorIcon: getEnv("GITHUB_SERVER_URL") + "/" + getEnv(EnvGithubActor) + ".png?size=32",
Footer: envOr(EnvSlackFooter, "<https://github.com/rtCamp/github-actions-library|Powered By rtCamp's GitHub Actions Library> | <"+getEnv(EnvGithubRun)+"|Triggered on this workflow run>"),
Fields: fields,
},
},
Expand All @@ -254,9 +257,13 @@ func main() {
fmt.Fprintf(os.Stdout, "Successfully sent the message!")
}

func getEnv(name string) string {
return strings.TrimSpace(os.Getenv(name))
}

func envOr(name, def string) string {
if d, ok := os.LookupEnv(name); ok {
return d
return strings.TrimSpace(d)
}
return def
}
Expand All @@ -275,7 +282,7 @@ func send_raw(endpoint string, payload []byte) error {
var res *http.Response
var err error

switch os.Getenv(EnvMessageMode) {
switch getEnv(EnvMessageMode) {
case "WEBHOOK":
res, err = http.Post(endpoint, "application/json", b)
case "TOKEN":
Expand All @@ -284,18 +291,86 @@ func send_raw(endpoint string, payload []byte) error {
return fmt.Errorf("Error creating request: %s\n", err)
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+os.Getenv("SLACK_TOKEN"))
req.Header.Set("Authorization", "Bearer "+getEnv("SLACK_TOKEN"))
client := &http.Client{}
res, err = client.Do(req)
default:
fmt.Fprintf(os.Stderr, "Invalid message mode: %s\n", os.Getenv(EnvMessageMode))
fmt.Fprintf(os.Stderr, "Invalid message mode: %s\n", getEnv(EnvMessageMode))
os.Exit(6)
}

if err != nil {
return err
}

if res.StatusCode >= 299 {
return fmt.Errorf("Error on message: %s\n", res.Status)
}

if os.Getenv(EnvSlackUpload) != "" {
err = sendFile(os.Getenv(EnvSlackUpload), "", os.Getenv(EnvSlackChannel), os.Getenv(EnvThreadTs))
if err != nil {
return err
}
}

return nil
}

func sendFile(filename string, message string, channel string, thread_ts string) error {
file, err := os.Open(filename)
if err != nil {
return err
}
defer file.Close()

fileData := &bytes.Buffer{}
writer := multipart.NewWriter(fileData)

part, err := writer.CreateFormFile("file", filename)
if err != nil {
return err
}

_, err = io.Copy(part, file)

err = writer.WriteField("initial_comment", message)
if err != nil {
return err
}

err = writer.WriteField("channels", channel)
if err != nil {
return err
}

if thread_ts != "" {
err = writer.WriteField("thread_ts", thread_ts)
if err != nil {
return err
}
}

err = writer.Close()
if err != nil {
return err
}

req, err := http.NewRequest("POST", "https://slack.com/api/files.upload", fileData)

if err != nil {
return err
}

req.Header.Set("Content-Type", writer.FormDataContentType())
req.Header.Set("Authorization", "Bearer "+os.Getenv("SLACK_TOKEN"))

client := &http.Client{}
res, err := client.Do(req)
if err != nil {
return err
}

if res.StatusCode >= 299 {
return fmt.Errorf("Error on message: %s\n", res.Status)
}
Expand Down