From c0732d12c385c9c3a0800387539a5358de77e343 Mon Sep 17 00:00:00 2001 From: Plamen Valentinov Kolev Date: Thu, 13 Mar 2025 17:20:42 +0100 Subject: [PATCH 1/2] Add slack alert --- .github/workflows/daily.yaml | 24 ++++++++ .github/workflows/dryrun.yaml | 29 ++++++++- download_analytics/slack_utils.py | 98 +++++++++++++++++++++++++++++++ pyproject.toml | 3 +- 4 files changed, 151 insertions(+), 3 deletions(-) create mode 100644 download_analytics/slack_utils.py diff --git a/.github/workflows/daily.yaml b/.github/workflows/daily.yaml index 35b4f08..6fac62d 100644 --- a/.github/workflows/daily.yaml +++ b/.github/workflows/daily.yaml @@ -2,6 +2,11 @@ name: Daily Collection on: workflow_dispatch: + inputs: + slack_channel: + description: Slack channel to post the error message to if the builds fail. + required: false + default: "sdv-alerts-debug" schedule: - cron: '0 0 * * *' @@ -28,3 +33,22 @@ jobs: env: PYDRIVE_CREDENTIALS: ${{ secrets.PYDRIVE_CREDENTIALS }} BIGQUERY_CREDENTIALS: ${{ secrets.BIGQUERY_CREDENTIALS }} + + alert: + needs: [collect] + runs-on: ubuntu-latest + if: failure() + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.13' + - name: Install slack dependencies + run: | + python -m pip install --upgrade pip + python -m pip install invoke + python -m pip install .[dev] + - name: Slack alert if failure + run: python -m github_analytics.slack_utils -r ${{ github.run_id }} -c ${{ github.event.inputs.slack_channel || 'sdv-alerts' }} + env: + SLACK_TOKEN: ${{ secrets.SLACK_TOKEN }} diff --git a/.github/workflows/dryrun.yaml b/.github/workflows/dryrun.yaml index 6953831..48ee293 100644 --- a/.github/workflows/dryrun.yaml +++ b/.github/workflows/dryrun.yaml @@ -1,12 +1,18 @@ name: Health-check Dry Run on: - - workflow_dispatch + - workflow_dispatch: + inputs: + slack_channel: + description: Slack channel to post the error message to if the builds fail. + required: false + default: "sdv-alerts-debug" + - push - pull_request jobs: - collect: + dry_run: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -29,3 +35,22 @@ jobs: env: PYDRIVE_CREDENTIALS: ${{ secrets.PYDRIVE_CREDENTIALS }} BIGQUERY_CREDENTIALS: ${{ secrets.BIGQUERY_CREDENTIALS }} + + alert: + needs: [dry_run] + runs-on: ubuntu-latest + if: failure() + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.13' + - name: Install slack dependencies + run: | + python -m pip install --upgrade pip + python -m pip install invoke + python -m pip install .[dev] + - name: Slack alert if failure + run: python -m github_analytics.slack_utils -r ${{ github.run_id }} -c ${{ github.event.inputs.slack_channel || 'sdv-alerts' }} + env: + SLACK_TOKEN: ${{ secrets.SLACK_TOKEN }} diff --git a/download_analytics/slack_utils.py b/download_analytics/slack_utils.py new file mode 100644 index 0000000..be90928 --- /dev/null +++ b/download_analytics/slack_utils.py @@ -0,0 +1,98 @@ +"""Utility functions for Slack integration.""" + +import argparse +import os + +from slack_sdk import WebClient + +GITHUB_URL_PREFIX = 'https://github.com/datacebo/download-analytics/actions/runs/' +DEFAULT_SLACK_CHANNEL = 'sdv-alerts-debug' + + +def _get_slack_client(): + """Create an authenticated Slack client. + + Returns: + WebClient: + An authenticated Slack WebClient instance. + """ + token = os.getenv('SLACK_TOKEN') + client = WebClient(token=token) + return client + + +def post_slack_message(channel, text): + """Post a message to a Slack channel. + + Args: + channel (str): + The name of the channel to post to. + text (str): + The message to send to the channel. + + Returns: + SlackResponse: + Response from Slack API call + """ + client = _get_slack_client() + response = client.chat_postMessage(channel=channel, text=text) + if not response['ok']: + error = response.get('error', 'unknown_error') + msg = f'{error} occured trying to post message to {channel}' + raise RuntimeError(msg) + + return response + + +def post_slack_message_in_thread(channel, text, thread_ts): + """Post a message as a threaded reply in a Slack channel. + + Args: + channel (str): + The name of the channel to post to. + text (str): + The message to send as a reply in the thread. + thread_ts (str): + The timestamp of the message that starts the thread. + + Returns: + SlackResponse: + Response from Slack API call. + """ + client = _get_slack_client() + response = client.chat_postMessage(channel=channel, text=text, thread_ts=thread_ts) + if not response['ok']: + error = response.get('error', 'unknown_error') + msg = f'{error} occurred trying to post threaded message to {channel}' + raise RuntimeError(msg) + + return response + + +def send_alert(args): + """Send an alert message to a slack channel.""" + url = GITHUB_URL_PREFIX + args.run_id + message = f'Download Analytics build failed :fire: :dumpster-fire: :fire: See errors <{url}|here>' + post_slack_message(args.channel, message) + + +def get_parser(): + """Get the parser.""" + parser = argparse.ArgumentParser(description='Function to alert when a Github workflow fails.') + parser.add_argument('-r', '--run-id', type=str, help='The id of the github run.') + parser.add_argument( + '-c', + '--channel', + type=str, + help='The slack channel to post to.', + default=DEFAULT_SLACK_CHANNEL, + ) + parser.set_defaults(action=send_alert) + + return parser + + +if __name__ == '__main__': + parser = get_parser() + args = parser.parse_args() + args.action(args) diff --git a/pyproject.toml b/pyproject.toml index e28e030..ae5c259 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,7 +37,8 @@ include = ['download_analytics', 'download_analytics.*'] [project.optional-dependencies] dev = [ "ruff>=0.9.8", - "invoke" + "invoke", + "slack-sdk>=3.34,<4.0", ] [tool.ruff] From ec4372f28055a0f661a390f70890682a73aa3961 Mon Sep 17 00:00:00 2001 From: Plamen Valentinov Kolev Date: Thu, 13 Mar 2025 17:32:49 +0100 Subject: [PATCH 2/2] Fix lint and workflow --- .github/workflows/dryrun.yaml | 6 +++--- download_analytics/slack_utils.py | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/dryrun.yaml b/.github/workflows/dryrun.yaml index 48ee293..bdf0f72 100644 --- a/.github/workflows/dryrun.yaml +++ b/.github/workflows/dryrun.yaml @@ -1,15 +1,15 @@ name: Health-check Dry Run on: - - workflow_dispatch: + workflow_dispatch: inputs: slack_channel: description: Slack channel to post the error message to if the builds fail. required: false default: "sdv-alerts-debug" - - push - - pull_request + push: + pull_request: jobs: dry_run: diff --git a/download_analytics/slack_utils.py b/download_analytics/slack_utils.py index be90928..2cdb4c9 100644 --- a/download_analytics/slack_utils.py +++ b/download_analytics/slack_utils.py @@ -72,7 +72,9 @@ def post_slack_message_in_thread(channel, text, thread_ts): def send_alert(args): """Send an alert message to a slack channel.""" url = GITHUB_URL_PREFIX + args.run_id - message = f'Download Analytics build failed :fire: :dumpster-fire: :fire: See errors <{url}|here>' + message = ( + f'Download Analytics build failed :fire: :dumpster-fire: :fire: See errors <{url}|here>' + ) post_slack_message(args.channel, message)