├── action.yml ├── entrypoint.sh ├── LICENSE ├── Dockerfile ├── main.sh ├── main.go └── README.md /action.yml: -------------------------------------------------------------------------------- 1 | name: 'Slack Notify' 2 | description: 'This action will send a notification to Slack' 3 | author: 'rtCamp' 4 | runs: 5 | using: 'docker' 6 | image: 'docker://rtcamp/action-slack-notify:v2.0.3' 7 | branding: 8 | icon: 'bell' 9 | color: 'yellow' 10 | -------------------------------------------------------------------------------- /entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Check required env variables 4 | flag=0 5 | if [[ -z "$SLACK_WEBHOOK" ]]; then 6 | flag=1 7 | missing_secret="SLACK_WEBHOOK" 8 | if [[ -n "$VAULT_ADDR" ]] && [[ -n "$VAULT_TOKEN" ]]; then 9 | flag=0 10 | fi 11 | if [[ -n "$VAULT_ADDR" ]] || [[ -n "$VAULT_TOKEN" ]]; then 12 | missing_secret="VAULT_ADDR and/or VAULT_TOKEN" 13 | fi 14 | fi 15 | 16 | if [[ "$flag" -eq 1 ]]; then 17 | printf "[\e[0;31mERROR\e[0m] Secret \`$missing_secret\` is missing. Please add it to this action for proper execution.\nRefer https://github.com/rtCamp/action-slack-notify for more information.\n" 18 | exit 1 19 | fi 20 | 21 | # custom path for files to override default files 22 | custom_path="$GITHUB_WORKSPACE/.github/slack" 23 | main_script="/main.sh" 24 | 25 | if [[ -d "$custom_path" ]]; then 26 | rsync -av "$custom_path/" / 27 | chmod +x /*.sh 28 | fi 29 | 30 | bash "$main_script" 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 rtCamp 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.14-alpine3.11@sha256:6578dc0c1bde86ccef90e23da3cdaa77fe9208d23c1bb31d942c8b663a519fa5 AS builder 2 | 3 | LABEL "com.github.actions.icon"="bell" 4 | LABEL "com.github.actions.color"="yellow" 5 | LABEL "com.github.actions.name"="Slack Notify" 6 | LABEL "com.github.actions.description"="This action will send notification to Slack" 7 | 8 | 9 | WORKDIR ${GOPATH}/src/github.com/rtcamp/action-slack-notify 10 | COPY main.go ${GOPATH}/src/github.com/rtcamp/action-slack-notify 11 | 12 | ENV CGO_ENABLED 0 13 | ENV GOOS linux 14 | 15 | RUN go get -v ./... 16 | RUN go build -a -installsuffix cgo -ldflags '-w -extldflags "-static"' -o /go/bin/slack-notify . 17 | 18 | # alpine:latest at 2020-01-18T01:19:37.187497623Z 19 | FROM alpine@sha256:ab00606a42621fb68f2ed6ad3c88be54397f981a7b70a79db3d1172b11c4367d 20 | 21 | COPY --from=builder /go/bin/slack-notify /usr/bin/slack-notify 22 | 23 | ENV VAULT_VERSION 1.0.2 24 | 25 | RUN apk update \ 26 | && apk upgrade \ 27 | && apk add \ 28 | bash \ 29 | jq \ 30 | ca-certificates \ 31 | python \ 32 | py2-pip \ 33 | rsync && \ 34 | pip install shyaml && \ 35 | rm -rf /var/cache/apk/* 36 | 37 | # Setup Vault 38 | RUN wget https://releases.hashicorp.com/vault/${VAULT_VERSION}/vault_${VAULT_VERSION}_linux_amd64.zip && \ 39 | unzip vault_${VAULT_VERSION}_linux_amd64.zip && \ 40 | rm vault_${VAULT_VERSION}_linux_amd64.zip && \ 41 | mv vault /usr/local/bin/vault 42 | 43 | # fix the missing dependency - https://stackoverflow.com/a/35613430 44 | RUN mkdir /lib64 && ln -s /lib/libc.musl-x86_64.so.1 /lib64/ld-linux-x86-64.so.2 45 | 46 | COPY *.sh / 47 | 48 | RUN chmod +x /*.sh 49 | 50 | ENTRYPOINT ["/entrypoint.sh"] 51 | -------------------------------------------------------------------------------- /main.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | export GITHUB_BRANCH=${GITHUB_REF##*heads/} 4 | export SLACK_ICON=${SLACK_ICON:-"https://avatars0.githubusercontent.com/u/43742164"} 5 | export SLACK_USERNAME=${SLACK_USERNAME:-"rtBot"} 6 | export CI_SCRIPT_OPTIONS="ci_script_options" 7 | export SLACK_TITLE=${SLACK_TITLE:-"Message"} 8 | export COMMIT_MESSAGE=$(cat "$GITHUB_EVENT_PATH" | jq .commits | jq '.[0].message' -r) 9 | 10 | hosts_file="$GITHUB_WORKSPACE/.github/hosts.yml" 11 | 12 | if [[ -z "$SLACK_CHANNEL" ]]; then 13 | if [[ -f "$hosts_file" ]]; then 14 | user_slack_channel=$(cat "$hosts_file" | shyaml get-value "$CI_SCRIPT_OPTIONS.slack-channel" | tr '[:upper:]' '[:lower:]') 15 | fi 16 | fi 17 | 18 | if [[ -n "$user_slack_channel" ]]; then 19 | export SLACK_CHANNEL="$user_slack_channel" 20 | fi 21 | 22 | # Login to vault using GH Token 23 | if [[ -n "$VAULT_GITHUB_TOKEN" ]]; then 24 | unset VAULT_TOKEN 25 | vault login -method=github token="$VAULT_GITHUB_TOKEN" > /dev/null 26 | fi 27 | 28 | if [[ -n "$VAULT_GITHUB_TOKEN" ]] || [[ -n "$VAULT_TOKEN" ]]; then 29 | export SLACK_WEBHOOK=$(vault read -field=webhook secret/slack) 30 | fi 31 | 32 | if [[ -f "$hosts_file" ]]; then 33 | hostname=$(cat "$hosts_file" | shyaml get-value "$GITHUB_BRANCH.hostname") 34 | user=$(cat "$hosts_file" | shyaml get-value "$GITHUB_BRANCH.user") 35 | export HOST_NAME="\`$user@$hostname\`" 36 | export DEPLOY_PATH=$(cat "$hosts_file" | shyaml get-value "$GITHUB_BRANCH.deploy_path") 37 | 38 | temp_url=${DEPLOY_PATH%%/app*} 39 | export SITE_NAME="${temp_url##*sites/}" 40 | export HOST_TITLE="SSH Host" 41 | fi 42 | 43 | PR_SHA=$(cat $GITHUB_EVENT_PATH | jq -r .pull_request.head.sha) 44 | [[ 'null' != $PR_SHA ]] && export GITHUB_SHA="$PR_SHA" 45 | 46 | if [[ -n "$SITE_NAME" ]]; then 47 | export SITE_TITLE="Site" 48 | fi 49 | 50 | 51 | if [[ -z "$SLACK_MESSAGE" ]]; then 52 | export SLACK_MESSAGE="$COMMIT_MESSAGE" 53 | fi 54 | 55 | slack-notify "$@" 56 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "net/http" 8 | "os" 9 | ) 10 | 11 | const ( 12 | EnvSlackWebhook = "SLACK_WEBHOOK" 13 | EnvSlackIcon = "SLACK_ICON" 14 | EnvSlackIconEmoji = "SLACK_ICON_EMOJI" 15 | EnvSlackChannel = "SLACK_CHANNEL" 16 | EnvSlackTitle = "SLACK_TITLE" 17 | EnvSlackMessage = "SLACK_MESSAGE" 18 | EnvSlackColor = "SLACK_COLOR" 19 | EnvSlackUserName = "SLACK_USERNAME" 20 | EnvGithubActor = "GITHUB_ACTOR" 21 | EnvSiteName = "SITE_NAME" 22 | EnvHostName = "HOST_NAME" 23 | EnvDepolyPath = "DEPLOY_PATH" 24 | EnvMinimal = "MSG_MINIMAL" 25 | ) 26 | 27 | type Webhook struct { 28 | Text string `json:"text,omitempty"` 29 | UserName string `json:"username,omitempty"` 30 | IconURL string `json:"icon_url,omitempty"` 31 | IconEmoji string `json:"icon_emoji,omitempty"` 32 | Channel string `json:"channel,omitempty"` 33 | UnfurlLinks bool `json:"unfurl_links"` 34 | Attachments []Attachment `json:"attachments,omitmepty"` 35 | } 36 | 37 | type Attachment struct { 38 | Fallback string `json:"fallback"` 39 | Pretext string `json:"pretext,omitempty"` 40 | Color string `json:"color,omitempty"` 41 | AuthorName string `json:"author_name,omitempty"` 42 | AuthorLink string `json:"author_link,omitempty"` 43 | AuthorIcon string `json:"author_icon,omitempty"` 44 | Footer string `json:"footer,omitempty"` 45 | Fields []Field `json:"fields,omitempty"` 46 | 47 | } 48 | 49 | type Field struct { 50 | Title string `json:"title,omitempty"` 51 | Value string `json:"value,omitempty"` 52 | Short bool `json:"short,omitempty"` 53 | } 54 | 55 | func main() { 56 | endpoint := os.Getenv(EnvSlackWebhook) 57 | if endpoint == "" { 58 | fmt.Fprintln(os.Stderr, "URL is required") 59 | os.Exit(1) 60 | } 61 | text := os.Getenv(EnvSlackMessage) 62 | if text == "" { 63 | fmt.Fprintln(os.Stderr, "Message is required") 64 | os.Exit(1) 65 | } 66 | 67 | minimal := os.Getenv(EnvMinimal) 68 | fields := []Field{} 69 | if minimal == "true" { 70 | mainFields:= []Field{ 71 | { 72 | Title: os.Getenv(EnvSlackTitle), 73 | Value: envOr(EnvSlackMessage, "EOM"), 74 | Short: false, 75 | }, 76 | } 77 | fields = append(mainFields, fields...) 78 | } else { 79 | mainFields:= []Field{ 80 | { 81 | Title: "Ref", 82 | Value: os.Getenv("GITHUB_REF"), 83 | Short: true, 84 | }, { 85 | Title: "Event", 86 | Value: os.Getenv("GITHUB_EVENT_NAME"), 87 | Short: true, 88 | }, 89 | { 90 | Title: "Actions URL", 91 | Value: "https://github.com/" + os.Getenv("GITHUB_REPOSITORY") + "/commit/" + os.Getenv("GITHUB_SHA") + "/checks", 92 | Short: false, 93 | }, 94 | { 95 | Title: os.Getenv(EnvSlackTitle), 96 | Value: envOr(EnvSlackMessage, "EOM"), 97 | Short: false, 98 | }, 99 | } 100 | fields = append(mainFields, fields...) 101 | } 102 | 103 | hostName := os.Getenv(EnvHostName) 104 | if hostName != "" { 105 | newfields:= []Field{ 106 | { 107 | Title: os.Getenv("SITE_TITLE"), 108 | Value: os.Getenv(EnvSiteName), 109 | Short: true, 110 | }, 111 | { 112 | Title: os.Getenv("HOST_TITLE"), 113 | Value: os.Getenv(EnvHostName), 114 | Short: true, 115 | }, 116 | } 117 | fields = append(newfields, fields...) 118 | } 119 | 120 | msg := Webhook{ 121 | UserName: os.Getenv(EnvSlackUserName), 122 | IconURL: os.Getenv(EnvSlackIcon), 123 | IconEmoji: os.Getenv(EnvSlackIconEmoji), 124 | Channel: os.Getenv(EnvSlackChannel), 125 | Attachments: []Attachment{ 126 | { 127 | 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")), 128 | Color: envOr(EnvSlackColor, "good"), 129 | AuthorName: envOr(EnvGithubActor, ""), 130 | AuthorLink: "http://github.com/" + os.Getenv(EnvGithubActor), 131 | AuthorIcon: "http://github.com/" + os.Getenv(EnvGithubActor) + ".png?size=32", 132 | Footer: "", 133 | Fields: fields, 134 | }, 135 | }, 136 | } 137 | 138 | if err := send(endpoint, msg); err != nil { 139 | fmt.Fprintf(os.Stderr, "Error sending message: %s\n", err) 140 | os.Exit(2) 141 | } 142 | } 143 | 144 | func envOr(name, def string) string { 145 | if d, ok := os.LookupEnv(name); ok { 146 | return d 147 | } 148 | return def 149 | } 150 | 151 | func send(endpoint string, msg Webhook) error { 152 | enc, err := json.Marshal(msg) 153 | if err != nil { 154 | return err 155 | } 156 | b := bytes.NewBuffer(enc) 157 | res, err := http.Post(endpoint, "application/json", b) 158 | if err != nil { 159 | return err 160 | } 161 | 162 | if res.StatusCode >= 299 { 163 | return fmt.Errorf("Error on message: %s\n", res.Status) 164 | } 165 | fmt.Println(res.Status) 166 | return nil 167 | } 168 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This action is a part of [GitHub Actions Library](https://github.com/rtCamp/github-actions-library/) created by [rtCamp](https://github.com/rtCamp/). 2 | 3 | # Slack Notify - GitHub Action 4 | [![Project Status: Active – The project has reached a stable, usable state and is being actively developed.](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active) 5 | 6 | 7 | A [GitHub Action](https://github.com/features/actions) to send a message to a Slack channel. 8 | 9 | **Screenshot** 10 | 11 | action-slack-notify-rtcamp 12 | 13 | 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). 14 | 15 | ## Usage 16 | 17 | You can use this action after any other action. Here is an example setup of this action: 18 | 19 | 1. Create a `.github/workflows/slack-notify.yml` file in your GitHub repo. 20 | 2. Add the following code to the `slack-notify.yml` file. 21 | 22 | ```yml 23 | on: push 24 | name: Slack Notification Demo 25 | jobs: 26 | slackNotification: 27 | name: Slack Notification 28 | runs-on: ubuntu-latest 29 | steps: 30 | - uses: actions/checkout@v2 31 | - name: Slack Notification 32 | uses: rtCamp/action-slack-notify@v2.0.2 33 | env: 34 | SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} 35 | ``` 36 | 37 | 3. Create `SLACK_WEBHOOK` secret using [GitHub Action's Secret](https://help.github.com/en/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets#creating-encrypted-secrets-for-a-repository). You can [generate a Slack incoming webhook token from here](https://slack.com/apps/A0F7XDUAZ-incoming-webhooks). 38 | 39 | 40 | ## Environment Variables 41 | 42 | By default, action is designed to run with minimal configuration but you can alter Slack notification using following environment variables: 43 | 44 | Variable | Default | Purpose 45 | ------------------|-------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------- 46 | SLACK_CHANNEL | Set during Slack webhook creation | Specify Slack channel in which message needs to be sent 47 | SLACK_USERNAME | `rtBot` | The name of the sender of the message. Does not need to be a "real" username 48 | SLACK_ICON | ![rtBot Avatar](https://github.com/rtBot.png?size=32) | User/Bot icon shown with Slack message. It uses the URL supplied to this env variable to display the icon in slack message. 49 | SLACK_ICON_EMOJI | - | User/Bot icon shown with Slack message, in case you do not wish to add a URL for slack icon as above, you can set slack emoji in this env variable. Example value: `:bell:` or any other valid slack emoji. 50 | SLACK_COLOR | `good` (green) | You can pass an RGB value like `#efefef` which would change color on left side vertical line of Slack message. 51 | SLACK_MESSAGE | Generated from git commit message. | The main Slack message in attachment. It is advised not to override this. 52 | SLACK_TITLE | Message | Title to use before main Slack message. 53 | MSG_MINIMAL | - | If set to true, removes: `Ref`, `Event` and `Actions URL` from the message. 54 | 55 | 56 | You can see the action block with all variables as below: 57 | 58 | ```yml 59 | - name: Slack Notification 60 | uses: rtCamp/action-slack-notify@v2.0.2 61 | env: 62 | SLACK_CHANNEL: general 63 | SLACK_COLOR: '#3278BD' 64 | SLACK_ICON: https://github.com/rtCamp.png?size=48 65 | SLACK_MESSAGE: 'Post Content :rocket:' 66 | SLACK_TITLE: Post Title 67 | SLACK_USERNAME: rtCamp 68 | SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} 69 | ``` 70 | 71 | Below screenshot help you visualize message part controlled by different variables: 72 | 73 | Screenshot_2019-03-26_at_5_56_05_PM 74 | 75 | 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). 76 | 77 | ## Hashicorp Vault (Optional) 78 | 79 | This GitHub action supports [Hashicorp Vault](https://www.vaultproject.io/). 80 | 81 | To enable Hashicorp Vault support, please define following GitHub secrets: 82 | 83 | Variable | Purpose | Example Vaule 84 | --------------|-------------------------------------------------------------------------------|------------- 85 | `VAULT_ADDR` | [Vault server address](https://www.vaultproject.io/docs/commands/#vault_addr) | `https://example.com:8200` 86 | `VAULT_TOKEN` | [Vault token](https://www.vaultproject.io/docs/concepts/tokens.html) | `s.gIX5MKov9TUp7iiIqhrP1HgN` 87 | 88 | You will need to change `secrets` line in `slack-notify.yml` file to look like below. 89 | 90 | ```yml 91 | on: push 92 | name: Slack Notification Demo 93 | jobs: 94 | slackNotification: 95 | name: Slack Notification 96 | runs-on: ubuntu-latest 97 | steps: 98 | - uses: actions/checkout@v2 99 | - name: Slack Notification 100 | uses: rtCamp/action-slack-notify@v2.0.2 101 | env: 102 | VAULT_ADDR: ${{ secrets.VAULT_ADDR }} 103 | VAULT_TOKEN: ${{ secrets.VAULT_TOKEN }} 104 | ``` 105 | 106 | GitHub action uses `VAULT_TOKEN` to connect to `VAULT_ADDR` to retrieve slack webhook from Vault. 107 | 108 | In the Vault, the Slack webhook should be setup as field `webhook` on path `secret/slack`. 109 | 110 | ## License 111 | 112 | [MIT](LICENSE) © 2019 rtCamp 113 | 114 | ## Does this interest you? 115 | 116 | Join us at rtCamp, we specialize in providing high performance enterprise WordPress solutions 117 | --------------------------------------------------------------------------------