Skip to content
238 changes: 238 additions & 0 deletions .github/workflows/design-system-notify.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
# Design System ๋ณ€๊ฒฝ ์‹œ Slack ์•Œ๋ฆผ์„ ๋ณด๋‚ด๋Š” ์›Œํฌํ”Œ๋กœ์šฐ
#
# ํ๋ฆ„:
# 1. develop push ์‹œ packages/design-system/ ํ•˜์œ„ ํŒŒ์ผ ๋ณ€๊ฒฝ ๊ฐ์ง€
# 2. ๋ณ€๊ฒฝ๋œ ์ปดํฌ๋„ŒํŠธ/foundation ์ด๋ฆ„ ์ถ”์ถœ (kebab-case โ†’ DSPascalCase)
# 3. Claude API๋กœ diff ์š”์•ฝ ์ƒ์„ฑ
# 4. Slack Block Kit ํ˜•์‹์œผ๋กœ ์•Œ๋ฆผ ์ „์†ก
#
# Slack ๋ฉ”์‹œ์ง€ ์˜ˆ์‹œ:
# [Extension DS] DSColor, DSTypography ์—…๋ฐ์ดํŠธ
# ๐Ÿ”ง *DSColor*: dark/light ํ…Œ๋งˆ ํ† ํฐ ๊ฐ’ ๋ณ€๊ฒฝ
# ๐Ÿ“– Storybook | #1901
name: Design System Change Notification

on:
push:
branches:
- develop
paths:
- "packages/design-system/src/**"
workflow_dispatch:

jobs:
notify:
name: Notify Slack on Design System changes
runs-on: ubuntu-24.04
timeout-minutes: 5
permissions:
contents: read
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

# Step 1: ๋ณ€๊ฒฝ๋œ ์ปดํฌ๋„ŒํŠธ ์ด๋ฆ„ ์ถ”์ถœ
- name: Extract component info
id: components
run: |
BASE_SHA=${{ github.event.before }}
HEAD_SHA=${{ github.sha }}
STORYBOOK_BASE="https://chainapsis.github.io/keplr-wallet/storybook/?path="

# ๋ณ€๊ฒฝ๋œ ๋””๋ ‰ํ† ๋ฆฌ ๋ชฉ๋ก (foundation/color, foundation/typography ๋“ฑ)
# sed ๊ตฌ๋ถ„์ž์™€ alternation | ์ถฉ๋Œ ๋ฐฉ์ง€๋ฅผ ์œ„ํ•ด ์นดํ…Œ๊ณ ๋ฆฌ๋ณ„ ๋ณ„๋„ ํŒจํ„ด ์‚ฌ์šฉ
CHANGED_DIRS=$(git diff --name-only $BASE_SHA...$HEAD_SHA -- 'packages/design-system/src/' \
| grep -E '(components|foundation|theme)/' \
| sed -E 's|.*/components/([^/]+)/.*|components/\1|; s|.*/foundation/([^/]+)/.*|foundation/\1|; s|.*/theme/([^/]+)/.*|theme/\1|; s|.*/theme/[^/]+$|theme|' \
| sort -u)

# DSPascalCase ์ด๋ฆ„ ๋ชฉ๋ก (kebab-case โ†’ DSPascalCase)
COMPONENTS=$(echo "$CHANGED_DIRS" \
| while read dir_path; do
echo "$dir_path" | sed -E 's|.*/||' | sed -E 's/-(.)/\U\1/g; s/^(.)/DS\U\1/'
done \
| paste -sd ',' - | sed 's/,/, /g')
echo "names=${COMPONENTS:-Unknown}" >> $GITHUB_OUTPUT

# DSName|storybook_url ๋งคํ•‘ ํŒŒ์ผ ์ƒ์„ฑ
if [ -z "$CHANGED_DIRS" ]; then
> /tmp/component_urls.txt
else
echo "$CHANGED_DIRS" | while read dir_path; do
category=$(echo "$dir_path" | cut -d/ -f1)
dir_name=$(echo "$dir_path" | cut -d/ -f2)
ds_name=$(echo "$dir_name" | sed -E 's/-(.)/\U\1/g; s/^(.)/DS\U\1/')
# Storybook URL: /story/foundations-color--color
# category ๋งคํ•‘: foundation โ†’ foundations, components โ†’ components, theme โ†’ foundations
case "$category" in
foundation) sb_category="foundations" ;;
theme) sb_category="foundations" ;;
*) sb_category="$category" ;;
esac
sb_name="${dir_name:-theme}"
echo "${ds_name}|${STORYBOOK_BASE}/story/${sb_category}-${sb_name}--${sb_name}"
done > /tmp/component_urls.txt
fi

# ์‹ ๊ทœ ์ปดํฌ๋„ŒํŠธ: ๋””๋ ‰ํ† ๋ฆฌ ์ž์ฒด๊ฐ€ BASE_SHA์— ์—†์—ˆ๋˜ ๊ฒฝ์šฐ๋งŒ
NEW_COMPONENTS=$(git diff --name-status $BASE_SHA...$HEAD_SHA -- 'packages/design-system/src/' \
| grep '^A' \
| grep -E '(components|foundation|theme)/[^/]+/' \
| sed -E 's|.*/components/([^/]+)/.*|components/\1|; s|.*/foundation/([^/]+)/.*|foundation/\1|; s|.*/theme/([^/]+)/.*|theme/\1|' \
| sort -u \
| while read dir_path; do
if ! git ls-tree -r "$BASE_SHA" -- "packages/design-system/src/${dir_path}/" 2>/dev/null | grep -q .; then
echo "$dir_path" | sed -E 's|.*/||' | sed -E 's/-(.)/\U\1/g; s/^(.)/DS\U\1/'
fi
done \
| paste -sd ',' - | sed 's/,/, /g')
echo "new_components=${NEW_COMPONENTS}" >> $GITHUB_OUTPUT

echo "storybook_link=https://chainapsis.github.io/keplr-wallet/storybook/" >> $GITHUB_OUTPUT

# Step 2: Claude API๋กœ diff ์š”์•ฝ ์ƒ์„ฑ
- name: Summarize changes with Claude
id: summary
run: |
BASE_SHA=${{ github.event.before }}
HEAD_SHA=${{ github.sha }}

git diff $BASE_SHA...$HEAD_SHA -- 'packages/design-system/src/' | head -500 > /tmp/diff.txt

DIFF_CONTENT=$(cat /tmp/diff.txt)

PROMPT="์•„๋ž˜ diff๋ฅผ ๋ถ„์„ํ•ด์„œ ์ปดํฌ๋„ŒํŠธ๋ณ„ ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ์š”์•ฝํ•ด์ค˜.

๋ณ€๊ฒฝ๋œ ์ปดํฌ๋„ŒํŠธ: ${{ steps.components.outputs.names }}
์ด ์ค‘ ์ƒˆ๋กœ ์ถ”๊ฐ€๋œ ์ปดํฌ๋„ŒํŠธ: ${{ steps.components.outputs.new_components }}

๊ทœ์น™:
- ์ƒˆ ์ปดํฌ๋„ŒํŠธ๋Š” ๐Ÿ†•, ์ˆ˜์ •๋œ ์ปดํฌ๋„ŒํŠธ๋Š” ๐Ÿ”ง๋กœ ์‹œ์ž‘
- ์ปดํฌ๋„ŒํŠธ๋ช…์€ *bold*, ์ฝœ๋ก  ๋’ค์— ํ•œ์ค„ ์„ค๋ช…
- ์ฃผ์š” props๋Š” ๋“ค์—ฌ์“ฐ๊ธฐ ํ›„ โ€ข ๋กœ ๋‚˜์—ด (prop๋ช…: ์„ค๋ช…)
- ๋ฐ˜๋“œ์‹œ diff์˜ ์‹ค์ œ ๋ณ€๊ฒฝ ๋‚ด์šฉ๋งŒ ์‚ฌ์šฉํ•  ๊ฒƒ
- ์„œ๋ก /๊ฒฐ๋ก  ์—†์ด ์ปดํฌ๋„ŒํŠธ๋ณ„ ์š”์•ฝ๋งŒ ์ถœ๋ ฅ

ํ˜•์‹ ์ฐธ๊ณ ์šฉ ์˜ˆ์‹œ (์ด ๋‚ด์šฉ์„ ๊ทธ๋Œ€๋กœ ์“ฐ์ง€ ๋ง ๊ฒƒ):
๐Ÿ†• *DSBadge*: ๋ฑƒ์ง€ ์ปดํฌ๋„ŒํŠธ ์ถ”๊ฐ€
โ€ข variant: solid, outline ๋“ฑ ์Šคํƒ€์ผ ์„ ํƒ
๐Ÿ”ง *DSColor*: dark ํ…Œ๋งˆ ํ† ํฐ ๊ฐ’ ๋ณ€๊ฒฝ
โ€ข fill.neutral.high: gray10 โ†’ gray200์œผ๋กœ ๋ณ€๊ฒฝ"

jq -n --arg prompt "$PROMPT" --arg diff "$DIFF_CONTENT" '{
"model": "claude-haiku-4-5",
"max_tokens": 600,
"messages": [{
"role": "user",
"content": ($prompt + "\n\ndiff:\n" + $diff)
}]
}' > /tmp/payload.json

RESPONSE=$(curl -s "https://api.anthropic.com/v1/messages" \
-H "Content-Type: application/json" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-d @/tmp/payload.json)

echo "Claude API Response: $RESPONSE"

ERROR_TYPE=$(echo "$RESPONSE" | jq -r '.error.type // empty')
if [ -n "$ERROR_TYPE" ]; then
ERROR_MSG=$(echo "$RESPONSE" | jq -r '.error.message // empty')
echo "API Error: $ERROR_TYPE - $ERROR_MSG"
SUMMARY="๋””์ž์ธ ์‹œ์Šคํ…œ์ด ์—…๋ฐ์ดํŠธ๋˜์—ˆ์Šต๋‹ˆ๋‹ค."
else
SUMMARY=$(echo "$RESPONSE" | jq -r '.content[0].text // "๋””์ž์ธ ์‹œ์Šคํ…œ์ด ์—…๋ฐ์ดํŠธ๋˜์—ˆ์Šต๋‹ˆ๋‹ค."' \
| tr -d '`' \
| sed 's/\*\*/*/g' \
| grep -E '^(๐Ÿ†•|๐Ÿ”ง| +โ€ข)' \
)
fi
echo "text<<EOF" >> $GITHUB_OUTPUT
echo "$SUMMARY" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY_FOR_DS }}

# Step 3: Slack Block Kit ํ˜•์‹์œผ๋กœ ์•Œ๋ฆผ ์ „์†ก
- name: Send Slack notification
run: |
NEW_COMPONENTS="${{ steps.components.outputs.new_components }}"
ALL_COMPONENTS="${{ steps.components.outputs.names }}"
SUMMARY="${{ steps.summary.outputs.text }}"
STORYBOOK_LINK="${{ steps.components.outputs.storybook_link }}"

# ํ—ค๋”: ์ถ”๊ฐ€/์—…๋ฐ์ดํŠธ ๊ตฌ๋ถ„
if [ -n "$NEW_COMPONENTS" ]; then
UPDATED_COMPONENTS=$(comm -23 \
<(echo "$ALL_COMPONENTS" | tr ',' '\n' | sed 's/^ //' | sort) \
<(echo "$NEW_COMPONENTS" | tr ',' '\n' | sed 's/^ //' | sort) \
| paste -sd ',' - | sed 's/,/, /g')

if [ -n "$UPDATED_COMPONENTS" ]; then
HEADER="[Extension DS] ${NEW_COMPONENTS} ์ถ”๊ฐ€ ยท ${UPDATED_COMPONENTS} ์—…๋ฐ์ดํŠธ"
else
HEADER="[Extension DS] ${NEW_COMPONENTS} ์ถ”๊ฐ€"
fi
else
HEADER="[Extension DS] ${ALL_COMPONENTS} ์—…๋ฐ์ดํŠธ"
fi

# ์ปดํฌ๋„ŒํŠธ๋ช…์— Storybook ๋งํฌ ์‚ฝ์ž…: *DSXxx* โ†’ *<url|DSXxx>*
while IFS='|' read -r ds_name url; do
SUMMARY=$(echo "$SUMMARY" | sed "s@\\*${ds_name}\\*@*<${url}|${ds_name}>*@g")
done < /tmp/component_urls.txt

MSG="$SUMMARY"

PR_NUMBER=$(echo "$COMMIT_MSG" | grep -oP '#\K\d+' | head -1)

if [ -n "$PR_NUMBER" ]; then
PR_LINK="<https://github.com/${GITHUB_REPOSITORY}/pull/${PR_NUMBER}|#${PR_NUMBER}>"
else
COMMIT_SHORT_SHA=$(echo "$GITHUB_SHA" | cut -c1-7)
PR_LINK="<${COMMIT_URL}|${COMMIT_SHORT_SHA}>"
fi

jq -n \
--arg header "$HEADER" \
--arg content "$MSG" \
--arg storybook_link "<${STORYBOOK_LINK}|๐Ÿ“– Storybook>" \
--arg pr_link "$PR_LINK" \
'{
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": $header
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": $content
}
},
{
"type": "context",
"elements": [
{
"type": "mrkdwn",
"text": ($storybook_link + " | " + $pr_link)
}
]
}
]
}' > /tmp/slack_payload.json

curl -X POST \
-H "Content-Type: application/json" \
-d @/tmp/slack_payload.json \
"$SLACK_WEBHOOK_URL"
env:
SLACK_WEBHOOK_URL: ${{ secrets.DS_NOTIFICATION_SLACK_WEBHOOK_URL }}
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
COMMIT_MSG: ${{ github.event.head_commit.message }}
COMMIT_URL: ${{ github.event.head_commit.url }}
Loading