fix: make example field optional and fix Discord webhook JSON escaping
Database Changes: - Made `example` field optional in Prisma schema (String?) - Added default empty arrays for `frameworks` and `tags` - Allows tools to be synced without example field Workflow Changes: - Use jq to properly construct Discord webhook JSON - Fixes "invalid JSON" error caused by unescaped special characters - Properly escapes error messages with newlines and quotes This fixes sync errors for packages missing the example field and ensures Discord notifications are sent successfully. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
da12d1fc5b
commit
484190720a
2 changed files with 73 additions and 64 deletions
113
.github/workflows/sync-keyword.yml
vendored
113
.github/workflows/sync-keyword.yml
vendored
|
|
@ -72,60 +72,69 @@ jobs:
|
|||
# Format duration
|
||||
duration_sec=$(echo "scale=2; ${{ steps.sync.outputs.durationMs }} / 1000" | bc)
|
||||
|
||||
# Build base fields
|
||||
fields='[
|
||||
{
|
||||
"name": "📦 Packages Found",
|
||||
"value": "${{ steps.sync.outputs.packagesFound }}",
|
||||
"inline": true
|
||||
},
|
||||
{
|
||||
"name": "✨ Processed",
|
||||
"value": "${{ steps.sync.outputs.processed }}",
|
||||
"inline": true
|
||||
},
|
||||
{
|
||||
"name": "⏭️ Skipped",
|
||||
"value": "${{ steps.sync.outputs.skipped }}",
|
||||
"inline": true
|
||||
},
|
||||
{
|
||||
"name": "❌ Errors",
|
||||
"value": "${{ steps.sync.outputs.errors }}",
|
||||
"inline": true
|
||||
},
|
||||
{
|
||||
"name": "⏱️ Duration",
|
||||
"value": "'"$duration_sec"'s",
|
||||
"inline": true
|
||||
},
|
||||
{
|
||||
"name": "🔗 Run",
|
||||
"value": "[View Logs](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})",
|
||||
"inline": true
|
||||
}'
|
||||
|
||||
# Add error details if errors exist
|
||||
# Build Discord payload using jq for proper JSON escaping
|
||||
if [ -f /tmp/error_summary.txt ] && [ ${{ steps.sync.outputs.errors }} -gt 0 ]; then
|
||||
error_text=$(cat /tmp/error_summary.txt | head -c 900)
|
||||
fields="$fields"',
|
||||
{
|
||||
"name": "🔍 Error Details",
|
||||
"value": "```\n'"$error_text"'\n```",
|
||||
"inline": false
|
||||
}'
|
||||
# Read and truncate error text
|
||||
error_text=$(cat /tmp/error_summary.txt | head -c 800)
|
||||
|
||||
# Create payload with error details using jq
|
||||
payload=$(jq -n \
|
||||
--arg title "${{ steps.sync.outputs.status_emoji }} NPM Keyword Search Sync" \
|
||||
--argjson color ${{ steps.sync.outputs.status_color }} \
|
||||
--arg packages "${{ steps.sync.outputs.packagesFound }}" \
|
||||
--arg processed "${{ steps.sync.outputs.processed }}" \
|
||||
--arg skipped "${{ steps.sync.outputs.skipped }}" \
|
||||
--arg errors "${{ steps.sync.outputs.errors }}" \
|
||||
--arg duration "${duration_sec}s" \
|
||||
--arg url "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" \
|
||||
--arg error_text "$error_text" \
|
||||
--arg timestamp "$(date -u +%Y-%m-%dT%H:%M:%S.000Z)" \
|
||||
'{
|
||||
embeds: [{
|
||||
title: $title,
|
||||
color: $color,
|
||||
fields: [
|
||||
{ name: "📦 Packages Found", value: $packages, inline: true },
|
||||
{ name: "✨ Processed", value: $processed, inline: true },
|
||||
{ name: "⏭️ Skipped", value: $skipped, inline: true },
|
||||
{ name: "❌ Errors", value: $errors, inline: true },
|
||||
{ name: "⏱️ Duration", value: $duration, inline: true },
|
||||
{ name: "🔗 Run", value: ("[View Logs](" + $url + ")"), inline: true },
|
||||
{ name: "🔍 Error Details", value: ("```\n" + $error_text + "\n```"), inline: false }
|
||||
],
|
||||
timestamp: $timestamp
|
||||
}]
|
||||
}')
|
||||
else
|
||||
# Create payload without error details
|
||||
payload=$(jq -n \
|
||||
--arg title "${{ steps.sync.outputs.status_emoji }} NPM Keyword Search Sync" \
|
||||
--argjson color ${{ steps.sync.outputs.status_color }} \
|
||||
--arg packages "${{ steps.sync.outputs.packagesFound }}" \
|
||||
--arg processed "${{ steps.sync.outputs.processed }}" \
|
||||
--arg skipped "${{ steps.sync.outputs.skipped }}" \
|
||||
--arg errors "${{ steps.sync.outputs.errors }}" \
|
||||
--arg duration "${duration_sec}s" \
|
||||
--arg url "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" \
|
||||
--arg timestamp "$(date -u +%Y-%m-%dT%H:%M:%S.000Z)" \
|
||||
'{
|
||||
embeds: [{
|
||||
title: $title,
|
||||
color: $color,
|
||||
fields: [
|
||||
{ name: "📦 Packages Found", value: $packages, inline: true },
|
||||
{ name: "✨ Processed", value: $processed, inline: true },
|
||||
{ name: "⏭️ Skipped", value: $skipped, inline: true },
|
||||
{ name: "❌ Errors", value: $errors, inline: true },
|
||||
{ name: "⏱️ Duration", value: $duration, inline: true },
|
||||
{ name: "🔗 Run", value: ("[View Logs](" + $url + ")"), inline: true }
|
||||
],
|
||||
timestamp: $timestamp
|
||||
}]
|
||||
}')
|
||||
fi
|
||||
|
||||
fields="$fields"']'
|
||||
|
||||
# Create Discord embed
|
||||
# Send to Discord
|
||||
curl -X POST "${{ secrets.DISCORD_WEBHOOK }}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"embeds": [{
|
||||
"title": "${{ steps.sync.outputs.status_emoji }} NPM Keyword Search Sync",
|
||||
"color": ${{ steps.sync.outputs.status_color }},
|
||||
"fields": '"$fields"',
|
||||
"timestamp": "'"$(date -u +%Y-%m-%dT%H:%M:%S.000Z)"'"
|
||||
}]
|
||||
}'
|
||||
-d "$payload"
|
||||
|
|
|
|||
|
|
@ -28,18 +28,18 @@ model Tool {
|
|||
npmMaintainers Json? @map("npm_maintainers") @db.JsonB
|
||||
|
||||
// TPMJS Metadata (from package.json tpmjs field)
|
||||
category String @db.VarChar(50)
|
||||
description String @db.Text
|
||||
example String @db.Text
|
||||
parameters Json? @db.JsonB
|
||||
returns Json? @db.JsonB
|
||||
authentication Json? @db.JsonB
|
||||
pricing Json? @db.JsonB
|
||||
frameworks String[] @db.Text
|
||||
links Json? @db.JsonB
|
||||
tags String[] @db.Text
|
||||
status String? @db.VarChar(20)
|
||||
aiAgent Json? @map("ai_agent") @db.JsonB
|
||||
category String @db.VarChar(50)
|
||||
description String @db.Text
|
||||
example String? @db.Text
|
||||
parameters Json? @db.JsonB
|
||||
returns Json? @db.JsonB
|
||||
authentication Json? @db.JsonB
|
||||
pricing Json? @db.JsonB
|
||||
frameworks String[] @default([]) @db.Text
|
||||
links Json? @db.JsonB
|
||||
tags String[] @default([]) @db.Text
|
||||
status String? @db.VarChar(20)
|
||||
aiAgent Json? @map("ai_agent") @db.JsonB
|
||||
|
||||
// Discovery Metadata
|
||||
discoveryMethod String @map("discovery_method") @db.VarChar(20) // 'keyword' | 'changes-feed'
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue