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
|
# Format duration
|
||||||
duration_sec=$(echo "scale=2; ${{ steps.sync.outputs.durationMs }} / 1000" | bc)
|
duration_sec=$(echo "scale=2; ${{ steps.sync.outputs.durationMs }} / 1000" | bc)
|
||||||
|
|
||||||
# Build base fields
|
# Build Discord payload using jq for proper JSON escaping
|
||||||
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
|
|
||||||
if [ -f /tmp/error_summary.txt ] && [ ${{ steps.sync.outputs.errors }} -gt 0 ]; then
|
if [ -f /tmp/error_summary.txt ] && [ ${{ steps.sync.outputs.errors }} -gt 0 ]; then
|
||||||
error_text=$(cat /tmp/error_summary.txt | head -c 900)
|
# Read and truncate error text
|
||||||
fields="$fields"',
|
error_text=$(cat /tmp/error_summary.txt | head -c 800)
|
||||||
{
|
|
||||||
"name": "🔍 Error Details",
|
# Create payload with error details using jq
|
||||||
"value": "```\n'"$error_text"'\n```",
|
payload=$(jq -n \
|
||||||
"inline": false
|
--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
|
fi
|
||||||
|
|
||||||
fields="$fields"']'
|
# Send to Discord
|
||||||
|
|
||||||
# Create Discord embed
|
|
||||||
curl -X POST "${{ secrets.DISCORD_WEBHOOK }}" \
|
curl -X POST "${{ secrets.DISCORD_WEBHOOK }}" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d '{
|
-d "$payload"
|
||||||
"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)"'"
|
|
||||||
}]
|
|
||||||
}'
|
|
||||||
|
|
|
||||||
|
|
@ -28,18 +28,18 @@ model Tool {
|
||||||
npmMaintainers Json? @map("npm_maintainers") @db.JsonB
|
npmMaintainers Json? @map("npm_maintainers") @db.JsonB
|
||||||
|
|
||||||
// TPMJS Metadata (from package.json tpmjs field)
|
// TPMJS Metadata (from package.json tpmjs field)
|
||||||
category String @db.VarChar(50)
|
category String @db.VarChar(50)
|
||||||
description String @db.Text
|
description String @db.Text
|
||||||
example String @db.Text
|
example String? @db.Text
|
||||||
parameters Json? @db.JsonB
|
parameters Json? @db.JsonB
|
||||||
returns Json? @db.JsonB
|
returns Json? @db.JsonB
|
||||||
authentication Json? @db.JsonB
|
authentication Json? @db.JsonB
|
||||||
pricing Json? @db.JsonB
|
pricing Json? @db.JsonB
|
||||||
frameworks String[] @db.Text
|
frameworks String[] @default([]) @db.Text
|
||||||
links Json? @db.JsonB
|
links Json? @db.JsonB
|
||||||
tags String[] @db.Text
|
tags String[] @default([]) @db.Text
|
||||||
status String? @db.VarChar(20)
|
status String? @db.VarChar(20)
|
||||||
aiAgent Json? @map("ai_agent") @db.JsonB
|
aiAgent Json? @map("ai_agent") @db.JsonB
|
||||||
|
|
||||||
// Discovery Metadata
|
// Discovery Metadata
|
||||||
discoveryMethod String @map("discovery_method") @db.VarChar(20) // 'keyword' | 'changes-feed'
|
discoveryMethod String @map("discovery_method") @db.VarChar(20) // 'keyword' | 'changes-feed'
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue