Skip to content

fix(ci): Use jq to safely build Discord JSON payload #18

fix(ci): Use jq to safely build Discord JSON payload

fix(ci): Use jq to safely build Discord JSON payload #18

name: Discord Commit Notification
on:
push:
branches:
- main
- develop
- master
jobs:
notify:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Get commit info
id: commit_info
run: |
COMMIT_SHA=${{ github.sha }}
COMMIT_MESSAGE=$(git log -1 --format=%B)
COMMIT_AUTHOR=$(git log -1 --format=%an)
COMMIT_AUTHOR_EMAIL=$(git log -1 --format=%ae)
COMMIT_URL=${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }}
BRANCH_NAME=${GITHUB_REF#refs/heads/}
# Get file stats
FILES_CHANGED=$(git diff --name-only HEAD~1 HEAD 2>/dev/null | wc -l)
INSERTIONS=$(git diff --numstat HEAD~1 HEAD 2>/dev/null | awk '{sum+=$1} END {print sum}')
DELETIONS=$(git diff --numstat HEAD~1 HEAD 2>/dev/null | awk '{sum+=$2} END {print sum}')
# Get changed files list (max 5)
CHANGED_FILES=$(git diff --name-only HEAD~1 HEAD 2>/dev/null | head -5 | sed 's/^/• /')
echo "commit_sha=${COMMIT_SHA:0:7}" >> $GITHUB_OUTPUT
echo "commit_message<<EOF" >> $GITHUB_OUTPUT
echo "$COMMIT_MESSAGE" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
echo "commit_author=$COMMIT_AUTHOR" >> $GITHUB_OUTPUT
echo "commit_url=$COMMIT_URL" >> $GITHUB_OUTPUT
echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT
echo "files_changed=$FILES_CHANGED" >> $GITHUB_OUTPUT
echo "insertions=${INSERTIONS:-0}" >> $GITHUB_OUTPUT
echo "deletions=${DELETIONS:-0}" >> $GITHUB_OUTPUT
echo "changed_files<<EOF" >> $GITHUB_OUTPUT
echo "$CHANGED_FILES" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Send Discord notification
env:
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
MENTION_USER_ID: ${{ secrets.DISCORD_MENTION_USER_ID }}
COMMIT_MESSAGE: ${{ steps.commit_info.outputs.commit_message }}
COMMIT_SHA_SHORT: ${{ steps.commit_info.outputs.commit_sha }}
COMMIT_URL: ${{ steps.commit_info.outputs.commit_url }}
COMMIT_AUTHOR: ${{ steps.commit_info.outputs.commit_author }}
BRANCH_NAME: ${{ steps.commit_info.outputs.branch_name }}
FILES_CHANGED: ${{ steps.commit_info.outputs.files_changed }}
INSERTIONS: ${{ steps.commit_info.outputs.insertions }}
DELETIONS: ${{ steps.commit_info.outputs.deletions }}
CHANGED_FILES: ${{ steps.commit_info.outputs.changed_files }}
run: |
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
# Template for the embed
EMBED_TEMPLATE=$(cat <<EOF
{
"title": "📝 New Commit Pushed (Notify #${{ github.run_number }})",
"description": "%s",
"url": "$COMMIT_URL",
"color": 3447003,
"fields": [
{"name": "🔔 Notify", "value": "#${{ github.run_number }} (attempt ${{ github.run_attempt }})", "inline": true},
{"name": "🔗 Commit", "value": "[$COMMIT_SHA_SHORT]($COMMIT_URL)", "inline": true},
{"name": "👤 Author", "value": "$COMMIT_AUTHOR", "inline": true},
{"name": "🌿 Branch", "value": "\`$BRANCH_NAME\`", "inline": true},
{"name": "📊 Stats", "value": "**Files:** $FILES_CHANGED | **+$INSERTIONS** | **-$DELETIONS**", "inline": false},
{"name": "📄 Changed Files", "value": "%s", "inline": false}
],
"timestamp": "$TIMESTAMP"
}
EOF
)
# Use jq to safely build the final JSON payload
if [ -n "$MENTION_USER_ID" ]; then
JSON_PAYLOAD=$(jq -n \
--arg content "<@$MENTION_USER_ID>" \
--argjson allowed_mentions "{\"users\":[\"$MENTION_USER_ID\"]}" \
--arg username "GitHub Bot" \
--arg avatar_url "https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png" \
--arg commit_msg "$COMMIT_MESSAGE" \
--arg changed_files "$CHANGED_FILES" \
--argjson embed_template "$(printf "$EMBED_TEMPLATE" "" "")" \
'$embed_template.description = $commit_msg | $embed_template.fields[5].value = $changed_files | {username: $username, avatar_url: $avatar_url, content: $content, allowed_mentions: $allowed_mentions, embeds: [$embed_template]}')
else
JSON_PAYLOAD=$(jq -n \
--arg username "GitHub Bot" \
--arg avatar_url "https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png" \
--arg commit_msg "$COMMIT_MESSAGE" \
--arg changed_files "$CHANGED_FILES" \
--argjson embed_template "$(printf "$EMBED_TEMPLATE" "" "")" \
'$embed_template.description = $commit_msg | $embed_template.fields[5].value = $changed_files | {username: $username, avatar_url: $avatar_url, embeds: [$embed_template]}')
fi
# send and capture response
RESPONSE=$(curl -s -w "\n%{http_code}" -X POST -H 'Content-type: application/json' --data-raw "$JSON_PAYLOAD" "${{ secrets.DISCORD_WEBHOOK_URL }}")
# split body and status
HTTP_STATUS=$(echo "$RESPONSE" | tail -n1)
BODY=$(echo "$RESPONSE" | sed '$d')
echo "Discord response status: $HTTP_STATUS"
echo "Discord response body: $BODY"
if [ "$HTTP_STATUS" -ne 204 ] && [ "$HTTP_STATUS" -ne 200 ]; then
echo "Webhook failed with status $HTTP_STATUS" >&2
exit 1
fi