From 06aaa5622f0b2171c74fa6b770d6ed8ba5361e49 Mon Sep 17 00:00:00 2001 From: L0RD-ZER0 <68327382+L0RD-ZER0@users.noreply.github.com> Date: Thu, 14 Mar 2024 18:32:27 +0530 Subject: [PATCH 1/3] Trim spaces around environment variables, and resolves #105 --- action.yml | 4 +-- main.go | 80 ++++++++++++++++++++++++++++-------------------------- 2 files changed, 44 insertions(+), 40 deletions(-) diff --git a/action.yml b/action.yml index f3cb06f..b3758f5 100644 --- a/action.yml +++ b/action.yml @@ -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/l0rd-zer0/action-slack-notify:master" - name: "Slack Notification (Unformatted)" if: env.SLACKIFY_MARKDOWN != 'true' - uses: "docker://ghcr.io/rtcamp/action-slack-notify:v2.2.1" + uses: "docker://ghcr.io/l0rd-zer0/action-slack-notify:master" env: GITHUB_RUN: "${{ github.event.repository.html_url }}/actions/runs/${{ github.run_id }}" branding: diff --git a/main.go b/main.go index 6772bbd..f3b37d6 100644 --- a/main.go +++ b/main.go @@ -63,14 +63,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") @@ -83,12 +83,12 @@ 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) @@ -96,11 +96,11 @@ func main() { } } - 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 @@ -118,12 +118,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, }, @@ -133,7 +133,7 @@ func main() { requiredFields := strings.Split(minimal, ",") mainFields := []Field{ { - Title: os.Getenv(EnvSlackTitle), + Title: getEnv(EnvSlackTitle), Value: text, Short: false, }, @@ -144,7 +144,7 @@ func main() { field := []Field{ { Title: "Ref", - Value: os.Getenv("GITHUB_REF"), + Value: getEnv("GITHUB_REF"), Short: true, }, } @@ -153,7 +153,7 @@ func main() { field := []Field{ { Title: "Event", - Value: os.Getenv("GITHUB_EVENT_NAME"), + Value: getEnv("GITHUB_EVENT_NAME"), Short: true, }, } @@ -162,7 +162,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, }, } @@ -171,7 +171,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, }, } @@ -183,25 +183,25 @@ 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, }, @@ -209,17 +209,17 @@ func main() { 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, }, } @@ -227,20 +227,20 @@ func main() { } 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, " | <"+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, " | <"+getEnv(EnvGithubRun)+"|Triggered on this workflow run>"), Fields: fields, }, }, @@ -254,9 +254,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 } @@ -275,7 +279,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": @@ -284,11 +288,11 @@ 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) } From 9c212669bc3dededd5433822737c1fbfb76f2adc Mon Sep 17 00:00:00 2001 From: L0RD-ZER0 <68327382+L0RD-ZER0@users.noreply.github.com> Date: Mon, 18 Mar 2024 18:51:30 +0530 Subject: [PATCH 2/3] Add ability to send files over slack using an app token. --- entrypoint.sh | 11 ++++++++ main.go | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/entrypoint.sh b/entrypoint.sh index db190bc..b55ffd1 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -27,6 +27,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" diff --git a/main.go b/main.go index f3b37d6..36e25d7 100644 --- a/main.go +++ b/main.go @@ -4,6 +4,8 @@ import ( "bytes" "encoding/json" "fmt" + "io" + "mime/multipart" "net/http" "os" "strings" @@ -30,6 +32,7 @@ const ( EnvMinimal = "MSG_MINIMAL" EnvSlackLinkNames = "SLACK_LINK_NAMES" EnvThreadTs = "SLACK_THREAD_TS" + EnvSlackUpload = "SLACK_FILE_UPLOAD" EnvMessageMode = "MSG_MODE" ) @@ -300,6 +303,74 @@ func send_raw(endpoint string, payload []byte) error { 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) } From bbaed78ff69556a5b84c569decc6b3e71b63f2aa Mon Sep 17 00:00:00 2001 From: L0RD-ZER0 <68327382+L0RD-ZER0@users.noreply.github.com> Date: Tue, 19 Mar 2024 11:53:38 +0530 Subject: [PATCH 3/3] Prepare for release --- README.md | 3 ++- action.yml | 4 ++-- entrypoint.sh | 1 + 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9fa6aa8..559a302 100644 --- a/README.md +++ b/README.md @@ -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. @@ -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/). diff --git a/action.yml b/action.yml index b3758f5..15e944c 100644 --- a/action.yml +++ b/action.yml @@ -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/l0rd-zer0/action-slack-notify:master" + uses: "docker://ghcr.io/rtCamp/action-slack-notify:v2.2.1" - name: "Slack Notification (Unformatted)" if: env.SLACKIFY_MARKDOWN != 'true' - uses: "docker://ghcr.io/l0rd-zer0/action-slack-notify:master" + 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: diff --git a/entrypoint.sh b/entrypoint.sh index b55ffd1..97e0ae6 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -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"