Skip to content

Commit

Permalink
Add fix for uncleared spaces around variables and file uploads (#166)
Browse files Browse the repository at this point in the history
* Trim spaces around environment variables, and resolves #105
* Add ability to send files over slack using an app token, resolves #43 
* Prepare for release
  • Loading branch information
L0RD-ZER0 authored Mar 19, 2024
1 parent df21811 commit a7edf7e
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 41 deletions.
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"

This comment has been minimized.

Copy link
@codal-bhaumik

codal-bhaumik Mar 19, 2024

@L0RD-ZER0 receiving error invalid reference format: repository name must be lowercase , seems like repository name has been updated.
image

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

0 comments on commit a7edf7e

Please sign in to comment.