diff --git a/.github/workflows/slack.yml b/.github/workflows/slack.yml new file mode 100644 index 0000000000..f148faf228 --- /dev/null +++ b/.github/workflows/slack.yml @@ -0,0 +1,144 @@ +# Post a slack message something like the following for issue and PR actions: +# <$url|$title> +# | $repo#$num · issue opened by $user +# +# Configuration: +# 1. Set `SLACK_CHANNEL`. +# 2. Add a `SLACK_BOT_TOKEN` secret to your repo. This is the "Bot User OAuth +# Token" from the "OAuth & Permissions" section of your Slack App +# (https://api.slack.com/apps). The token must have the `chat:write` +# permission. +# 3. Optionally tweak the `if:` and `on:` sections below to control which issue +# and PR events are skipped. + +name: slack + +env: + SLACK_CHANNEL: "#apm-agent-node" + +on: + issues: + types: [opened, reopened, closed] + pull_request: + types: [opened, ready_for_review, reopened, closed] + +jobs: + slack: + # Skip notification if: + # - dependabot PRs, too noisy + # - draft PRs + if: ${{ !(github.event.action == 'opened' && github.event.pull_request.draft) || + github.event.pull_request.user.login != 'dependabot[bot]' || + github.event.pull_request.user.login != 'elastic-renovate-prod[bot]' }} + runs-on: ubuntu-latest + steps: + - name: Prepare Slack message + id: prepare + shell: python + env: + GITHUB_CONTEXT: ${{ toJson(github) }} + run: | + import os + from pprint import pprint + import json + + CLOSED_RED = '#cb2431' + GITHUB_BLACK = '#24292f' + MERGED_PURPLE = '#6f42c1' + OPEN_GREEN = '#36a64f' + DRAFT_GRAY = '#6a737d' + + ctx = json.loads(os.environ["GITHUB_CONTEXT"]) + # pprint(ctx) # for dev/debugging + event = ctx["event"] + action = event["action"] + if "issue" in event: + title = event["issue"]["title"] + url = event["issue"]["html_url"] + num = event["issue"]["number"] + action_str = f"issue {action}" + color = { + "opened": OPEN_GREEN, + "reopened": OPEN_GREEN, + "closed": CLOSED_RED, + }.get(action, "#ffffff") + elif "pull_request" in event: + title = event["pull_request"]["title"] + url = event["pull_request"]["html_url"] + num = event["pull_request"]["number"] + if action == "closed": + if event["pull_request"]["merged"]: + action_str = "PR merged" + color = MERGED_PURPLE + else: + action_str = "PR closed" + color = CLOSED_RED + elif event["pull_request"]["draft"]: + action_str = "PR in draft" + color = DRAFT_GRAY + elif action == "ready_for_review": + action_str = "PR ready for review" + color = OPEN_GREEN + else: + action_str = "PR opened" + color = OPEN_GREEN + else: + pprint(ctx) + raise ValueError('unexpected event: not an issue or PR event') + + def gfm2slack(text): + """Limited conversion of the subset of GitHub Flavor Markdown (gfm) + supported in GitHub issue/PR titles to a safe string for a Slack + link. https://api.slack.com/reference/surfaces/formatting""" + # Escape angle brackets to allow usage in Slack link. + text = text.replace('<', '<') + text = text.replace('>', '>') + # TODO: How to escape asterisk bolding and underscore italicizing? + return text + + # Use https://app.slack.com/block-kit-builder to play with styling. + payload = { + "channel": os.environ["SLACK_CHANNEL"], + # Note: Omitting the "text" field is intentional, so that it is not + # rendered by default. Guidelines on accessibility in: + # https://api.slack.com/methods/chat.postMessage#text-blocks-attachments + # are unclear for "attachments" usage. This competes with: + # https://api.slack.com/reference/messaging/attachments#guidelines__message-attachments-as-objects + # guidelines to group all object data inside the attachment. + # The downside is that the `chatMessage` below results in warnings + # from the Slack API about not including the top-level "text". + #"text": f"<{url}|{gfm2slack(title)}>", + "attachments": [ + { + "color": color, + "blocks": [ + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": f"<{url}|{gfm2slack(title)}>" + } + }, + { + "type": "context", + "elements": [ + { + "type": "mrkdwn", + "text": f"{ctx['repository']}#{num} · *{action_str}* by {event['sender']['login']}" + } + ] + } + ] + } + ] + } + + with open(os.environ.get("GITHUB_OUTPUT"), "a") as f: + f.write("payload={}".format(json.dumps(payload))) + + - name: Post Slack message + uses: slackapi/slack-github-action@v2.0.0 + with: + method: chat.postMessage + token: ${{ secrets.SLACK_BOT_TOKEN }} + payload: ${{ steps.prepare.outputs.payload }}