From 8738f65dd5a2595ad4168865d299c22dd8a0a0b4 Mon Sep 17 00:00:00 2001 From: abs2023 Date: Tue, 16 Dec 2025 14:12:12 -0500 Subject: [PATCH] add slack_notify --- .github/actions/slack-notify/action.yml | 211 ++++++++++++++++++++++++ .github/workflows/build.yml | 65 +++++++- 2 files changed, 269 insertions(+), 7 deletions(-) create mode 100644 .github/actions/slack-notify/action.yml diff --git a/.github/actions/slack-notify/action.yml b/.github/actions/slack-notify/action.yml new file mode 100644 index 0000000..6dc8993 --- /dev/null +++ b/.github/actions/slack-notify/action.yml @@ -0,0 +1,211 @@ +name: 'Slack Deployment Notification' +description: 'Send beautifully formatted deployment notifications to Slack' + +inputs: + status: + description: 'Deployment status (success, failure, cancelled)' + required: true + environment: + description: 'Target environment (dev, stg, main)' + required: true + service_name: + description: 'Name of the service being deployed' + required: true + version: + description: 'Version tag being deployed' + required: true + slack_webhook_url: + description: 'Slack webhook URL' + required: true + github_token: + description: 'GitHub token for API calls to fetch PR info' + required: false + default: '' + additional_info: + description: 'Additional info to include in markdown format (optional)' + required: false + default: '' + image_tag: + description: 'Docker image tag if applicable (optional)' + required: false + default: '' + +runs: + using: 'composite' + steps: + - name: Get PR info + id: pr_info + shell: bash + env: + GH_TOKEN: ${{ inputs.github_token }} + GITHUB_REPOSITORY: ${{ github.repository }} + GITHUB_SHA: ${{ github.sha }} + run: | + # Try to find PR number from merge commit message first + COMMIT_MSG=$(git log -1 --pretty=%s 2>/dev/null || echo "") + + # Pattern: "Merge pull request #123 from ..." + if [[ "$COMMIT_MSG" =~ Merge\ pull\ request\ \#([0-9]+) ]]; then + PR_NUMBER="${BASH_REMATCH[1]}" + echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT + echo "๐Ÿ“Ž Found PR #$PR_NUMBER from merge commit" + # Pattern: "... (#123)" - squash merge pattern + elif [[ "$COMMIT_MSG" =~ \(\#([0-9]+)\) ]]; then + PR_NUMBER="${BASH_REMATCH[1]}" + echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT + echo "๐Ÿ“Ž Found PR #$PR_NUMBER from squash commit" + # Fallback: Use GitHub API to find associated PR + elif [ -n "$GH_TOKEN" ]; then + PR_NUMBER=$(curl -s -H "Authorization: token $GH_TOKEN" \ + "https://api.github.com/repos/${GITHUB_REPOSITORY}/commits/${GITHUB_SHA}/pulls" \ + | jq -r '.[0].number // empty' 2>/dev/null || echo "") + if [ -n "$PR_NUMBER" ]; then + echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT + echo "๐Ÿ“Ž Found PR #$PR_NUMBER from GitHub API" + else + echo "pr_number=" >> $GITHUB_OUTPUT + echo "๐Ÿ“Ž No PR found for this commit" + fi + else + echo "pr_number=" >> $GITHUB_OUTPUT + echo "๐Ÿ“Ž No PR info available (no token provided)" + fi + + - name: Send Slack notification + shell: bash + env: + SLACK_WEBHOOK: ${{ inputs.slack_webhook_url }} + STATUS: ${{ inputs.status }} + ENVIRONMENT: ${{ inputs.environment }} + SERVICE_NAME: ${{ inputs.service_name }} + VERSION: ${{ inputs.version }} + ADDITIONAL_INFO: ${{ inputs.additional_info }} + IMAGE_TAG: ${{ inputs.image_tag }} + PR_NUMBER: ${{ steps.pr_info.outputs.pr_number }} + GITHUB_ACTOR: ${{ github.actor }} + GITHUB_REPOSITORY: ${{ github.repository }} + GITHUB_REF_NAME: ${{ github.ref_name }} + GITHUB_SHA: ${{ github.sha }} + GITHUB_RUN_ID: ${{ github.run_id }} + GITHUB_SERVER_URL: ${{ github.server_url }} + run: | + # Set environment emoji and label + case "$ENVIRONMENT" in + dev) ENV_EMOJI="๐Ÿ”ง"; ENV_LABEL="DEV" ;; + stg) ENV_EMOJI="๐Ÿงช"; ENV_LABEL="STG" ;; + main) ENV_EMOJI="๐Ÿš€"; ENV_LABEL="PROD" ;; + *) ENV_EMOJI="๐Ÿ“ฆ"; ENV_LABEL="${ENVIRONMENT^^}" ;; + esac + + # Set status emoji and color + case "$STATUS" in + success) STATUS_EMOJI="โœ…"; COLOR="good"; STATUS_TEXT="Deployed Successfully" ;; + failure) STATUS_EMOJI="โŒ"; COLOR="danger"; STATUS_TEXT="Deployment Failed" ;; + cancelled) STATUS_EMOJI="โš ๏ธ"; COLOR="warning"; STATUS_TEXT="Deployment Cancelled" ;; + skipped) STATUS_EMOJI="โญ๏ธ"; COLOR="#808080"; STATUS_TEXT="Skipped" ;; + *) STATUS_EMOJI="โ„น๏ธ"; COLOR="#808080"; STATUS_TEXT="$STATUS" ;; + esac + + # Get commit info + COMMIT_SHORT="${GITHUB_SHA:0:7}" + COMMIT_MSG=$(git log -1 --pretty=%s 2>/dev/null | head -n 1 | cut -c1-80 || echo "No commit message") + + # Clean up merge commit messages + if [[ "$COMMIT_MSG" =~ ^Merge\ pull\ request\ \#[0-9]+\ from\ .*/(.+)$ ]]; then + COMMIT_MSG="Merged: ${BASH_REMATCH[1]}" + fi + + # Build URLs + REPO_SHORT="${GITHUB_REPOSITORY#*/}" + RUN_URL="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" + COMMIT_URL="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/commit/${GITHUB_SHA}" + + # Build header: ENV | repo | service | status + HEADER_TEXT="$ENV_EMOJI $ENV_LABEL | $REPO_SHORT | $SERVICE_NAME | $STATUS_EMOJI $STATUS_TEXT" + + # Build fields array + FIELDS=$(jq -n \ + --arg version "$VERSION" \ + --arg actor "$GITHUB_ACTOR" \ + '[ + {"type": "mrkdwn", "text": ("*Version:*\n`" + $version + "`")}, + {"type": "mrkdwn", "text": ("*Triggered by:*\n" + $actor)} + ]') + + # Add PR field if available + if [ -n "$PR_NUMBER" ]; then + PR_URL="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/pull/${PR_NUMBER}" + FIELDS=$(echo "$FIELDS" | jq --arg pr_url "$PR_URL" --arg pr_num "$PR_NUMBER" \ + '. + [{"type": "mrkdwn", "text": ("*Pull Request:*\n<" + $pr_url + "|#" + $pr_num + ">")}]') + fi + + # Add image tag field if available + if [ -n "$IMAGE_TAG" ]; then + FIELDS=$(echo "$FIELDS" | jq --arg img "$IMAGE_TAG" \ + '. + [{"type": "mrkdwn", "text": ("*Image:*\n`" + $img + "`")}]') + fi + + # Build commit section + COMMIT_TEXT="*Commit:* ${COMMIT_MSG}" + + # Build blocks array + BLOCKS=$(jq -n \ + --arg header "$HEADER_TEXT" \ + --argjson fields "$FIELDS" \ + --arg commit_text "$COMMIT_TEXT" \ + '[ + {"type": "header", "text": {"type": "plain_text", "text": $header, "emoji": true}}, + {"type": "section", "fields": $fields}, + {"type": "section", "text": {"type": "mrkdwn", "text": $commit_text}} + ]') + + # Add additional info block if provided + if [ -n "$ADDITIONAL_INFO" ]; then + BLOCKS=$(echo "$BLOCKS" | jq --arg info "$ADDITIONAL_INFO" \ + '. + [{"type": "section", "text": {"type": "mrkdwn", "text": $info}}]') + fi + + # Add action buttons + if [ -n "$PR_NUMBER" ]; then + PR_URL="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/pull/${PR_NUMBER}" + BLOCKS=$(echo "$BLOCKS" | jq \ + --arg pr_url "$PR_URL" \ + --arg pr_num "$PR_NUMBER" \ + --arg run_url "$RUN_URL" \ + --arg commit_url "$COMMIT_URL" \ + '. + [ + {"type": "actions", "elements": [ + {"type": "button", "text": {"type": "plain_text", "text": "๐Ÿ”€ View PR", "emoji": true}, "url": $pr_url, "style": "primary"}, + {"type": "button", "text": {"type": "plain_text", "text": "๐Ÿ“‹ Actions", "emoji": true}, "url": $run_url}, + {"type": "button", "text": {"type": "plain_text", "text": "๐Ÿ” Commit", "emoji": true}, "url": $commit_url} + ]} + ]') + else + BLOCKS=$(echo "$BLOCKS" | jq \ + --arg run_url "$RUN_URL" \ + --arg commit_url "$COMMIT_URL" \ + '. + [ + {"type": "actions", "elements": [ + {"type": "button", "text": {"type": "plain_text", "text": "๐Ÿ“‹ Actions", "emoji": true}, "url": $run_url, "style": "primary"}, + {"type": "button", "text": {"type": "plain_text", "text": "๐Ÿ” Commit", "emoji": true}, "url": $commit_url} + ]} + ]') + fi + + # Build final payload + PAYLOAD=$(jq -n \ + --arg color "$COLOR" \ + --argjson blocks "$BLOCKS" \ + '{"attachments": [{"color": $color, "blocks": $blocks}]}') + + # Send to Slack + RESPONSE=$(curl -s -X POST -H 'Content-type: application/json' -d "$PAYLOAD" "$SLACK_WEBHOOK") + + if [ "$RESPONSE" = "ok" ]; then + echo "๐Ÿ“ข Slack notification sent successfully" + else + echo "โš ๏ธ Slack response: $RESPONSE" + echo "Payload was:" + echo "$PAYLOAD" | jq . + fi + diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 68714b5..b940116 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -23,6 +23,7 @@ on: - main - stg - dev + - 'cicd/**' paths: [".github/**", "src/**", "Dockerfile", "package.json", "package-lock.json", "LICENSE", "README.md"] # Build test only on PRs targeting dev @@ -60,6 +61,7 @@ jobs: vfull: ${{ steps.gen_tag_name.outputs.vfull }} environment: ${{ steps.determine_env.outputs.environment }} should_deploy: ${{ steps.determine_env.outputs.should_deploy }} + is_cicd_branch: ${{ steps.determine_env.outputs.is_cicd_branch }} ecs_cluster: ${{ steps.determine_env.outputs.ecs_cluster }} ecs_service: ${{ steps.determine_env.outputs.ecs_service }} aws_region: ${{ steps.determine_env.outputs.aws_region }} @@ -73,24 +75,34 @@ jobs: - name: Determine environment id: determine_env run: | + IS_CICD="false" # Determine environment based on branch or manual input if [ "${{ github.event_name }}" == "workflow_dispatch" ] && [ -n "${{ github.event.inputs.environment }}" ]; then ENV="${{ github.event.inputs.environment }}" else BRANCH="${{ github.ref_name }}" - case "$BRANCH" in - main) ENV="main" ;; - stg) ENV="stg" ;; - *) ENV="dev" ;; - esac + if [ "$BRANCH" == "main" ]; then + ENV="main" + elif [ "$BRANCH" == "stg" ]; then + ENV="stg" + elif [[ "$BRANCH" == cicd/* ]]; then + ENV="dev" + IS_CICD="true" + echo "๐Ÿ”ง CI/CD test branch detected - will skip build/deploy" + else + ENV="dev" + fi fi echo "environment=${ENV}" >> $GITHUB_OUTPUT + echo "is_cicd_branch=${IS_CICD}" >> $GITHUB_OUTPUT - # Determine if we should deploy (not a PR, not build_only) + # Determine if we should deploy (not a PR, not build_only, not cicd branch) if [ "${{ github.event_name }}" == "pull_request" ]; then echo "should_deploy=false" >> $GITHUB_OUTPUT elif [ "${{ github.event.inputs.build_only }}" == "true" ]; then echo "should_deploy=false" >> $GITHUB_OUTPUT + elif [ "$IS_CICD" == "true" ]; then + echo "should_deploy=false" >> $GITHUB_OUTPUT else echo "should_deploy=true" >> $GITHUB_OUTPUT fi @@ -184,7 +196,7 @@ jobs: build-and-push: name: ๐Ÿ”จ Build & Push - if: github.event_name != 'pull_request' + if: github.event_name != 'pull_request' && needs.generate-tag.outputs.is_cicd_branch != 'true' runs-on: ubuntu-latest needs: generate-tag outputs: @@ -379,3 +391,42 @@ jobs: echo "" >> $GITHUB_STEP_SUMMARY echo "โš ๏ธ Deployment or verification failed. Check logs above for details." >> $GITHUB_STEP_SUMMARY echo "๐Ÿ“ฆ Note: Container was pushed and git tag was created before deployment failed." >> $GITHUB_STEP_SUMMARY + + notify: + name: ๐Ÿ“ข Notify + runs-on: ubuntu-latest + needs: [generate-tag, build-and-push, deploy, verify, summary] + if: always() && github.event_name != 'pull_request' + + steps: + - name: Checkout (for composite action) + uses: actions/checkout@v4 + with: + fetch-depth: 2 + + - name: Determine status + id: status + run: | + if [ "${{ needs.generate-tag.outputs.is_cicd_branch }}" == "true" ]; then + echo "status=success" >> $GITHUB_OUTPUT + elif [ "${{ needs.verify.result }}" == "success" ]; then + echo "status=success" >> $GITHUB_OUTPUT + elif [ "${{ needs.verify.result }}" == "failure" ] || [ "${{ needs.deploy.result }}" == "failure" ] || [ "${{ needs.build-and-push.result }}" == "failure" ]; then + echo "status=failure" >> $GITHUB_OUTPUT + elif [ "${{ needs.verify.result }}" == "cancelled" ] || [ "${{ needs.deploy.result }}" == "cancelled" ]; then + echo "status=cancelled" >> $GITHUB_OUTPUT + else + echo "status=skipped" >> $GITHUB_OUTPUT + fi + + - name: Send Slack notification + uses: ./.github/actions/slack-notify + with: + status: ${{ steps.status.outputs.status }} + environment: ${{ needs.generate-tag.outputs.environment }} + service_name: 'Spot Indexer' + version: ${{ needs.generate-tag.outputs.tag_name }} + slack_webhook_url: ${{ secrets.SLACK_WEBHOOK_URL }} + github_token: ${{ secrets.GITHUB_TOKEN }} + image_tag: ${{ needs.generate-tag.outputs.is_cicd_branch != 'true' && format('{0}:{1}', env.GHCR_IMAGE, needs.generate-tag.outputs.tag_name) || '' }} + additional_info: '${{ needs.generate-tag.outputs.is_cicd_branch == ''true'' && ''*Mode:* CI/CD Test (no build/deploy)'' || format(''*ECS:* `{0}` / `{1}`'', needs.generate-tag.outputs.ecs_cluster, needs.generate-tag.outputs.ecs_service) }}'