Skip to content

Commit a7edf7e

Browse files
authored
Add fix for uncleared spaces around variables and file uploads (#166)
* Trim spaces around environment variables, and resolves #105 * Add ability to send files over slack using an app token, resolves #43 * Prepare for release
1 parent df21811 commit a7edf7e

File tree

4 files changed

+129
-41
lines changed

4 files changed

+129
-41
lines changed

README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ SLACK_ON_SUCCESS | - | I
6161
SLACK_ON_FAILURE | - | If set, will send the provided message instead of the default message when the passed status (through ``SLACK_COLOR``) is `failure`.
6262
SLACK_ON_CANCEL | - | If set, will send the provided message instead of the default message when the passed status (through ``SLACK_COLOR``) is `cancelled`.
6363
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.
64+
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.
6465
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.
6566

6667

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

8687
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).
8788

88-
## Hashicorp Vault (Optional)
89+
## Hashicorp Vault (Optional) (Deprecated)
8990

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

action.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@ runs:
1717
SLACK_MESSAGE: "${{ steps.slackify.outputs.text }}"
1818
GITHUB_RUN: "${{ github.event.repository.html_url }}/actions/runs/${{ github.run_id }}"
1919
ENABLE_ESCAPES: "true"
20-
uses: "docker://ghcr.io/rtcamp/action-slack-notify:v2.2.1"
20+
uses: "docker://ghcr.io/rtCamp/action-slack-notify:v2.2.1"
2121

2222
- name: "Slack Notification (Unformatted)"
2323
if: env.SLACKIFY_MARKDOWN != 'true'
24-
uses: "docker://ghcr.io/rtcamp/action-slack-notify:v2.2.1"
24+
uses: "docker://ghcr.io/rtCamp/action-slack-notify:v2.2.1"
2525
env:
2626
GITHUB_RUN: "${{ github.event.repository.html_url }}/actions/runs/${{ github.run_id }}"
2727
branding:

entrypoint.sh

+12
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ if [[ -z "$SLACK_WEBHOOK" ]]; then
88
missing_secret="SLACK_WEBHOOK"
99
if [[ -n "$VAULT_ADDR" ]] && [[ -n "$VAULT_TOKEN" ]]; then
1010
flag=0
11+
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"
1112
fi
1213
if [[ -n "$VAULT_ADDR" ]] || [[ -n "$VAULT_TOKEN" ]]; then
1314
missing_secret="VAULT_ADDR and/or VAULT_TOKEN"
@@ -27,6 +28,17 @@ fi
2728

2829
export MSG_MODE="$mode"
2930

31+
if [[ -n "$SLACK_FILE_UPLOAD" ]]; then
32+
if [[ -z "$SLACK_TOKEN" ]]; then
33+
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"
34+
exit 1
35+
fi
36+
if [[ -z "$SLACK_CHANNEL" ]]; then
37+
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"
38+
exit 1
39+
fi
40+
fi
41+
3042
# custom path for files to override default files
3143
custom_path="$GITHUB_WORKSPACE/.github/slack"
3244
main_script="/main.sh"

main.go

+113-38
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import (
44
"bytes"
55
"encoding/json"
66
"fmt"
7+
"io"
8+
"mime/multipart"
79
"net/http"
810
"os"
911
"strings"
@@ -30,6 +32,7 @@ const (
3032
EnvMinimal = "MSG_MINIMAL"
3133
EnvSlackLinkNames = "SLACK_LINK_NAMES"
3234
EnvThreadTs = "SLACK_THREAD_TS"
35+
EnvSlackUpload = "SLACK_FILE_UPLOAD"
3336
EnvMessageMode = "MSG_MODE"
3437
)
3538

@@ -63,14 +66,14 @@ type Field struct {
6366
}
6467

6568
func main() {
66-
endpoint := os.Getenv(EnvSlackWebhook)
69+
endpoint := getEnv(EnvSlackWebhook)
6770
custom_payload := envOr(EnvSlackCustom, "")
6871
if endpoint == "" {
69-
if os.Getenv(EnvSlackChannel) == "" {
72+
if getEnv(EnvSlackChannel) == "" {
7073
fmt.Fprintln(os.Stderr, "Channel is required for sending message using a token")
7174
os.Exit(1)
7275
}
73-
if os.Getenv(EnvMessageMode) == "TOKEN" {
76+
if getEnv(EnvMessageMode) == "TOKEN" {
7477
endpoint = "https://slack.com/api/chat.postMessage"
7578
} else {
7679
fmt.Fprintln(os.Stderr, "URL is required")
@@ -83,24 +86,24 @@ func main() {
8386
os.Exit(2)
8487
}
8588
} else {
86-
text := os.Getenv(EnvSlackMessage)
89+
text := getEnv(EnvSlackMessage)
8790
if text == "" {
8891
fmt.Fprintln(os.Stderr, "Message is required")
8992
os.Exit(3)
9093
}
91-
if strings.HasPrefix(os.Getenv("GITHUB_WORKFLOW"), ".github") {
94+
if strings.HasPrefix(getEnv("GITHUB_WORKFLOW"), ".github") {
9295
err := os.Setenv("GITHUB_WORKFLOW", "Link to action run.yaml")
9396
if err != nil {
9497
fmt.Fprintf(os.Stderr, "Unable to update the workflow's variables: %s\n\n", err)
9598
os.Exit(4)
9699
}
97100
}
98101

99-
long_sha := os.Getenv("GITHUB_SHA")
102+
long_sha := getEnv("GITHUB_SHA")
100103
commit_sha := long_sha[0:6]
101104

102105
color := ""
103-
switch strings.ToLower(os.Getenv(EnvSlackColor)) {
106+
switch strings.ToLower(getEnv(EnvSlackColor)) {
104107
case "success":
105108
color = "good"
106109
text = envOr(EnvSlackOnSuccess, text) // If exists, override with on success
@@ -118,12 +121,12 @@ func main() {
118121
text = "EOM"
119122
}
120123

121-
minimal := os.Getenv(EnvMinimal)
124+
minimal := getEnv(EnvMinimal)
122125
fields := []Field{}
123126
if minimal == "true" {
124127
mainFields := []Field{
125128
{
126-
Title: os.Getenv(EnvSlackTitle),
129+
Title: getEnv(EnvSlackTitle),
127130
Value: text,
128131
Short: false,
129132
},
@@ -133,7 +136,7 @@ func main() {
133136
requiredFields := strings.Split(minimal, ",")
134137
mainFields := []Field{
135138
{
136-
Title: os.Getenv(EnvSlackTitle),
139+
Title: getEnv(EnvSlackTitle),
137140
Value: text,
138141
Short: false,
139142
},
@@ -144,7 +147,7 @@ func main() {
144147
field := []Field{
145148
{
146149
Title: "Ref",
147-
Value: os.Getenv("GITHUB_REF"),
150+
Value: getEnv("GITHUB_REF"),
148151
Short: true,
149152
},
150153
}
@@ -153,7 +156,7 @@ func main() {
153156
field := []Field{
154157
{
155158
Title: "Event",
156-
Value: os.Getenv("GITHUB_EVENT_NAME"),
159+
Value: getEnv("GITHUB_EVENT_NAME"),
157160
Short: true,
158161
},
159162
}
@@ -162,7 +165,7 @@ func main() {
162165
field := []Field{
163166
{
164167
Title: "Actions URL",
165-
Value: "<" + os.Getenv("GITHUB_SERVER_URL") + "/" + os.Getenv("GITHUB_REPOSITORY") + "/commit/" + os.Getenv("GITHUB_SHA") + "/checks|" + os.Getenv("GITHUB_WORKFLOW") + ">",
168+
Value: "<" + getEnv("GITHUB_SERVER_URL") + "/" + getEnv("GITHUB_REPOSITORY") + "/commit/" + getEnv("GITHUB_SHA") + "/checks|" + getEnv("GITHUB_WORKFLOW") + ">",
166169
Short: true,
167170
},
168171
}
@@ -171,7 +174,7 @@ func main() {
171174
field := []Field{
172175
{
173176
Title: "Commit",
174-
Value: "<" + os.Getenv("GITHUB_SERVER_URL") + "/" + os.Getenv("GITHUB_REPOSITORY") + "/commit/" + os.Getenv("GITHUB_SHA") + "|" + commit_sha + ">",
177+
Value: "<" + getEnv("GITHUB_SERVER_URL") + "/" + getEnv("GITHUB_REPOSITORY") + "/commit/" + getEnv("GITHUB_SHA") + "|" + commit_sha + ">",
175178
Short: true,
176179
},
177180
}
@@ -183,64 +186,64 @@ func main() {
183186
mainFields := []Field{
184187
{
185188
Title: "Ref",
186-
Value: os.Getenv("GITHUB_REF"),
189+
Value: getEnv("GITHUB_REF"),
187190
Short: true,
188191
}, {
189192
Title: "Event",
190-
Value: os.Getenv("GITHUB_EVENT_NAME"),
193+
Value: getEnv("GITHUB_EVENT_NAME"),
191194
Short: true,
192195
},
193196
{
194197
Title: "Actions URL",
195-
Value: "<" + os.Getenv("GITHUB_SERVER_URL") + "/" + os.Getenv("GITHUB_REPOSITORY") + "/commit/" + os.Getenv("GITHUB_SHA") + "/checks|" + os.Getenv("GITHUB_WORKFLOW") + ">",
198+
Value: "<" + getEnv("GITHUB_SERVER_URL") + "/" + getEnv("GITHUB_REPOSITORY") + "/commit/" + getEnv("GITHUB_SHA") + "/checks|" + getEnv("GITHUB_WORKFLOW") + ">",
196199
Short: true,
197200
},
198201
{
199202
Title: "Commit",
200-
Value: "<" + os.Getenv("GITHUB_SERVER_URL") + "/" + os.Getenv("GITHUB_REPOSITORY") + "/commit/" + os.Getenv("GITHUB_SHA") + "|" + commit_sha + ">",
203+
Value: "<" + getEnv("GITHUB_SERVER_URL") + "/" + getEnv("GITHUB_REPOSITORY") + "/commit/" + getEnv("GITHUB_SHA") + "|" + commit_sha + ">",
201204
Short: true,
202205
},
203206
{
204-
Title: os.Getenv(EnvSlackTitle),
207+
Title: getEnv(EnvSlackTitle),
205208
Value: text,
206209
Short: false,
207210
},
208211
}
209212
fields = append(mainFields, fields...)
210213
}
211214

212-
hostName := os.Getenv(EnvHostName)
215+
hostName := getEnv(EnvHostName)
213216
if hostName != "" {
214217
newfields := []Field{
215218
{
216-
Title: os.Getenv("SITE_TITLE"),
217-
Value: os.Getenv(EnvSiteName),
219+
Title: getEnv("SITE_TITLE"),
220+
Value: getEnv(EnvSiteName),
218221
Short: true,
219222
},
220223
{
221-
Title: os.Getenv("HOST_TITLE"),
222-
Value: os.Getenv(EnvHostName),
224+
Title: getEnv("HOST_TITLE"),
225+
Value: getEnv(EnvHostName),
223226
Short: true,
224227
},
225228
}
226229
fields = append(newfields, fields...)
227230
}
228231

229232
msg := Webhook{
230-
UserName: os.Getenv(EnvSlackUserName),
231-
IconURL: os.Getenv(EnvSlackIcon),
232-
IconEmoji: os.Getenv(EnvSlackIconEmoji),
233-
Channel: os.Getenv(EnvSlackChannel),
234-
LinkNames: os.Getenv(EnvSlackLinkNames),
235-
ThreadTs: os.Getenv(EnvThreadTs),
233+
UserName: getEnv(EnvSlackUserName),
234+
IconURL: getEnv(EnvSlackIcon),
235+
IconEmoji: getEnv(EnvSlackIconEmoji),
236+
Channel: getEnv(EnvSlackChannel),
237+
LinkNames: getEnv(EnvSlackLinkNames),
238+
ThreadTs: getEnv(EnvThreadTs),
236239
Attachments: []Attachment{
237240
{
238-
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")),
241+
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")),
239242
Color: color,
240243
AuthorName: envOr(EnvGithubActor, ""),
241-
AuthorLink: os.Getenv("GITHUB_SERVER_URL") + "/" + os.Getenv(EnvGithubActor),
242-
AuthorIcon: os.Getenv("GITHUB_SERVER_URL") + "/" + os.Getenv(EnvGithubActor) + ".png?size=32",
243-
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>"),
244+
AuthorLink: getEnv("GITHUB_SERVER_URL") + "/" + getEnv(EnvGithubActor),
245+
AuthorIcon: getEnv("GITHUB_SERVER_URL") + "/" + getEnv(EnvGithubActor) + ".png?size=32",
246+
Footer: envOr(EnvSlackFooter, "<https://github.com/rtCamp/github-actions-library|Powered By rtCamp's GitHub Actions Library> | <"+getEnv(EnvGithubRun)+"|Triggered on this workflow run>"),
244247
Fields: fields,
245248
},
246249
},
@@ -254,9 +257,13 @@ func main() {
254257
fmt.Fprintf(os.Stdout, "Successfully sent the message!")
255258
}
256259

260+
func getEnv(name string) string {
261+
return strings.TrimSpace(os.Getenv(name))
262+
}
263+
257264
func envOr(name, def string) string {
258265
if d, ok := os.LookupEnv(name); ok {
259-
return d
266+
return strings.TrimSpace(d)
260267
}
261268
return def
262269
}
@@ -275,7 +282,7 @@ func send_raw(endpoint string, payload []byte) error {
275282
var res *http.Response
276283
var err error
277284

278-
switch os.Getenv(EnvMessageMode) {
285+
switch getEnv(EnvMessageMode) {
279286
case "WEBHOOK":
280287
res, err = http.Post(endpoint, "application/json", b)
281288
case "TOKEN":
@@ -284,18 +291,86 @@ func send_raw(endpoint string, payload []byte) error {
284291
return fmt.Errorf("Error creating request: %s\n", err)
285292
}
286293
req.Header.Set("Content-Type", "application/json")
287-
req.Header.Set("Authorization", "Bearer "+os.Getenv("SLACK_TOKEN"))
294+
req.Header.Set("Authorization", "Bearer "+getEnv("SLACK_TOKEN"))
288295
client := &http.Client{}
289296
res, err = client.Do(req)
290297
default:
291-
fmt.Fprintf(os.Stderr, "Invalid message mode: %s\n", os.Getenv(EnvMessageMode))
298+
fmt.Fprintf(os.Stderr, "Invalid message mode: %s\n", getEnv(EnvMessageMode))
292299
os.Exit(6)
293300
}
294301

295302
if err != nil {
296303
return err
297304
}
298305

306+
if res.StatusCode >= 299 {
307+
return fmt.Errorf("Error on message: %s\n", res.Status)
308+
}
309+
310+
if os.Getenv(EnvSlackUpload) != "" {
311+
err = sendFile(os.Getenv(EnvSlackUpload), "", os.Getenv(EnvSlackChannel), os.Getenv(EnvThreadTs))
312+
if err != nil {
313+
return err
314+
}
315+
}
316+
317+
return nil
318+
}
319+
320+
func sendFile(filename string, message string, channel string, thread_ts string) error {
321+
file, err := os.Open(filename)
322+
if err != nil {
323+
return err
324+
}
325+
defer file.Close()
326+
327+
fileData := &bytes.Buffer{}
328+
writer := multipart.NewWriter(fileData)
329+
330+
part, err := writer.CreateFormFile("file", filename)
331+
if err != nil {
332+
return err
333+
}
334+
335+
_, err = io.Copy(part, file)
336+
337+
err = writer.WriteField("initial_comment", message)
338+
if err != nil {
339+
return err
340+
}
341+
342+
err = writer.WriteField("channels", channel)
343+
if err != nil {
344+
return err
345+
}
346+
347+
if thread_ts != "" {
348+
err = writer.WriteField("thread_ts", thread_ts)
349+
if err != nil {
350+
return err
351+
}
352+
}
353+
354+
err = writer.Close()
355+
if err != nil {
356+
return err
357+
}
358+
359+
req, err := http.NewRequest("POST", "https://slack.com/api/files.upload", fileData)
360+
361+
if err != nil {
362+
return err
363+
}
364+
365+
req.Header.Set("Content-Type", writer.FormDataContentType())
366+
req.Header.Set("Authorization", "Bearer "+os.Getenv("SLACK_TOKEN"))
367+
368+
client := &http.Client{}
369+
res, err := client.Do(req)
370+
if err != nil {
371+
return err
372+
}
373+
299374
if res.StatusCode >= 299 {
300375
return fmt.Errorf("Error on message: %s\n", res.Status)
301376
}

0 commit comments

Comments
 (0)