From 031d012d0d62579526c4fdb8e86cd3b408ddbc6e Mon Sep 17 00:00:00 2001 From: GrzegorzKowalik Date: Mon, 11 Oct 2021 10:51:53 +0200 Subject: [PATCH] Add slack notifications after deployments (#1485) * Add slack notifications after deployments * Fix copy paste mistakes * Add deployment workflows to CODEOWNERS --- .github/CODEOWNERS | 6 +- .github/workflows/deploy-cloud.yaml | 9 ++ .github/workflows/deploy-demo.yaml | 9 ++ .github/workflows/deploy-latest-staging.yaml | 10 ++ .github/workflows/deploy-master-staging.yaml | 10 ++ .github/workflows/deploy-stable-staging.yaml | 10 ++ .github/workflows/notify/notify-slack.py | 103 +++++++++++++++++++ 7 files changed, 155 insertions(+), 2 deletions(-) create mode 100755 .github/workflows/notify/notify-slack.py diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index dc4bed141..e5ffe1bcb 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,2 +1,4 @@ -# Restrict Test Environment Deployment Workflows to be Approved by Cloud Team -.github/workflows/test-env* @saleor/cloud \ No newline at end of file +# Restrict Deployment Workflows to be Approved by Cloud Team +.github/workflows/test-env* @saleor/cloud +.github/workflows/deploy-* @saleor/cloud +.github/workflows/notify/* @saleor/cloud \ No newline at end of file diff --git a/.github/workflows/deploy-cloud.yaml b/.github/workflows/deploy-cloud.yaml index 11871fc52..116307fb3 100644 --- a/.github/workflows/deploy-cloud.yaml +++ b/.github/workflows/deploy-cloud.yaml @@ -55,3 +55,12 @@ jobs: if [[ -n "$CF_2_ID" ]]; then aws cloudfront create-invalidation --distribution-id ${CF_2_ID} --paths "/dashboard*" fi + - name: Notify Slack + if: ${{ always() }} + env: + JOB_DEPLOYMENT_KIND: production + JOB_STATUS: ${{ job.status }} + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_CLOUD_DEPLOYMENTS_WEBHOOK_URL }} + JOB_TITLE: "Dashboard deployment to {{ env.ENVIRONMENT }}" + run: | + python3 ./.github/workflows/notify/notify-slack.py diff --git a/.github/workflows/deploy-demo.yaml b/.github/workflows/deploy-demo.yaml index e310af945..8a03e0f61 100644 --- a/.github/workflows/deploy-demo.yaml +++ b/.github/workflows/deploy-demo.yaml @@ -39,3 +39,12 @@ jobs: aws s3 sync build/dashboard s3://${{ secrets.AWS_DEMO_DEPLOYMENT_BUCKET }}/dashboard/static/ aws s3 cp build/dashboard/index.html s3://${{ secrets.AWS_DEMO_DEPLOYMENT_BUCKET }}/dashboard/ aws cloudfront create-invalidation --distribution-id ${{ secrets.AWS_DEMO_CF_DIST_ID }} --paths "/dashboard*" + - name: Notify Slack + if: ${{ always() }} + env: + JOB_DEPLOYMENT_KIND: production + JOB_STATUS: ${{ job.status }} + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_CLOUD_DEPLOYMENTS_WEBHOOK_URL }} + JOB_TITLE: "Dashboard deployment to {{ env.ENVIRONMENT }}" + run: | + python3 ./.github/workflows/notify/notify-slack.py \ No newline at end of file diff --git a/.github/workflows/deploy-latest-staging.yaml b/.github/workflows/deploy-latest-staging.yaml index 6b55c4829..b82c87be2 100644 --- a/.github/workflows/deploy-latest-staging.yaml +++ b/.github/workflows/deploy-latest-staging.yaml @@ -49,3 +49,13 @@ jobs: aws s3 sync build/dashboard s3://${{ secrets.AWS_STAGING_DEPLOYMENT_BUCKET }}/${ENVIRONMENT}/static/ aws s3 cp build/dashboard/index.html s3://${{ secrets.AWS_STAGING_DEPLOYMENT_BUCKET }}/${ENVIRONMENT}/ aws cloudfront create-invalidation --distribution-id ${{ secrets.AWS_STAGING_CF_DIST_ID }} --paths "/dashboard*" + + - name: Notify Slack + if: ${{ always() }} + env: + JOB_DEPLOYMENT_KIND: staging + JOB_STATUS: ${{ job.status }} + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_CLOUD_DEPLOYMENTS_WEBHOOK_URL }} + JOB_TITLE: "Dashboard deployment to {{ env.ENVIRONMENT }}" + run: | + python3 ./.github/workflows/notify/notify-slack.py \ No newline at end of file diff --git a/.github/workflows/deploy-master-staging.yaml b/.github/workflows/deploy-master-staging.yaml index 14636651f..8f0dc196e 100644 --- a/.github/workflows/deploy-master-staging.yaml +++ b/.github/workflows/deploy-master-staging.yaml @@ -43,3 +43,13 @@ jobs: aws s3 sync build/dashboard s3://${{ secrets.AWS_STAGING_DEPLOYMENT_BUCKET }}/${ENVIRONMENT}/static/ aws s3 cp build/dashboard/index.html s3://${{ secrets.AWS_STAGING_DEPLOYMENT_BUCKET }}/${ENVIRONMENT}/ aws cloudfront create-invalidation --distribution-id ${{ secrets.AWS_STAGING_CF_DIST_ID }} --paths "/dashboard*" + + - name: Notify Slack + if: ${{ always() }} + env: + JOB_DEPLOYMENT_KIND: staging + JOB_STATUS: ${{ job.status }} + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_CLOUD_DEPLOYMENTS_WEBHOOK_URL }} + JOB_TITLE: "Dashboard deployment to {{ env.ENVIRONMENT }}" + run: | + python3 ./.github/workflows/notify/notify-slack.py \ No newline at end of file diff --git a/.github/workflows/deploy-stable-staging.yaml b/.github/workflows/deploy-stable-staging.yaml index 971c0834d..9ced2dee4 100644 --- a/.github/workflows/deploy-stable-staging.yaml +++ b/.github/workflows/deploy-stable-staging.yaml @@ -49,3 +49,13 @@ jobs: aws s3 sync build/dashboard s3://${{ secrets.AWS_STAGING_DEPLOYMENT_BUCKET }}/${ENVIRONMENT}/static/ aws s3 cp build/dashboard/index.html s3://${{ secrets.AWS_STAGING_DEPLOYMENT_BUCKET }}/${ENVIRONMENT}/ aws cloudfront create-invalidation --distribution-id ${{ secrets.AWS_STAGING_CF_DIST_ID }} --paths "/dashboard*" + + - name: Notify Slack + if: ${{ always() }} + env: + JOB_DEPLOYMENT_KIND: staging + JOB_STATUS: ${{ job.status }} + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_CLOUD_DEPLOYMENTS_WEBHOOK_URL }} + JOB_TITLE: "Dashboard deployment to {{ env.ENVIRONMENT }}" + run: | + python3 ./.github/workflows/notify/notify-slack.py \ No newline at end of file diff --git a/.github/workflows/notify/notify-slack.py b/.github/workflows/notify/notify-slack.py new file mode 100755 index 000000000..5c99eee75 --- /dev/null +++ b/.github/workflows/notify/notify-slack.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python +""" +Notifies about deployment status to a given slack channel. + +This file currently needs to be duplicated between repositories until +https://github.com/github/roadmap/issues/98 is closed. + +Dependencies (already shipped by ubuntu-20.04): +- Python 3.6+ +- requests package (any version) + +Manual Environment Variables (explicit): +- JOB_DEPLOYMENT_KIND: deployment target kind, staging, dev, etc. +- SLACK_WEBHOOK_URL: incoming webhook URL to send payload/message to +- JOB_STATUS: status from GitHub's ``job.status`` +- JOB_TITLE: the title of the job + +Global GitHub Environment Variables (implicit): +- GITHUB_RUN_ID +- GITHUB_REPOSITORY +- GITHUB_ACTOR +""" + +import os +import sys + +import requests + + +class JobNotifier: + JOB_STATUS_COLOR_MAP = { + "success": "#5DC292", + "failure": "#FE6E76", + "cancelled": "#868B8E", + } + + def __init__(self): + # The title of the pull request + self.title: str = os.environ["JOB_TITLE"] + + # The kind of deployment (dev, staging, ...) + self.deployment_kind: str = os.environ["JOB_DEPLOYMENT_KIND"] + + # Incoming Webhook Endpoint, it is set a the organization level + # Development notifier configuration is available at: https://api.slack.com/apps/A0210C30YLD/ + self.slack_endpoint = os.environ["SLACK_WEBHOOK_URL"] + + # Workflow Run ID to retrieve the logs permalink of the actual run (failed/succeeded) + self.run_id: str = os.environ["GITHUB_RUN_ID"] + + # / + self.repository: str = os.environ["GITHUB_REPOSITORY"] + + # The user that triggered the action + self.author: str = os.environ["GITHUB_ACTOR"] + + # Job Status (success|failure|cancelled) + self.job_status: str = os.environ["JOB_STATUS"] + + @property + def run_permalink(self) -> str: + """Permalink to the current run logs""" + return f"https://github.com/{self.repository}/actions/runs/{self.run_id}" + + @property + def job_status_color(self) -> str: + """Color from Saleor Cloud palette for job status""" + return self.JOB_STATUS_COLOR_MAP[self.job_status] + + def make_slack_message(self) -> dict: + status = self.job_status.capitalize() + # Dev deployment triggered by JohnDoe: Success + text = ( + f"{self.author} deployment finished for '{self.deployment_kind.capitalize()}', result: " + f"{status}" + ) + message_data = { + "attachments": [ + { + "fallback": text, + "pretext": "", + "title": f"{self.repository}: {self.title}", + "title_link": self.run_permalink, + "text": text, + "color": self.job_status_color, + } + ] + } + return message_data + + def send_notification(self) -> None: + post_data = self.make_slack_message() + print(f"Notifying slack with payload: {post_data!r}", file=sys.stderr) + response = requests.post(self.slack_endpoint, json=post_data) + response.raise_for_status() + + +def main(): + JobNotifier().send_notification() + + +if __name__ == "__main__": + main()