From 0b30c62ff92e0dcc070652fb2320a00bd4f3f5d0 Mon Sep 17 00:00:00 2001 From: Ajax Davis Date: Wed, 3 Dec 2025 08:57:30 +1000 Subject: [PATCH] feat: add skipped packages list to Discord notifications MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit API Changes: - Track skipped package names in keyword sync endpoint - Include skippedPackages array in API response Workflow Changes: - Extract skipped package names from sync response - Display skipped packages in Discord notification as comma-separated list - Only show "📋 Skipped Packages" field when packages are skipped - Dynamic field construction using jq Example Discord output: 📋 Skipped Packages package-name-1, package-name-2, package-name-3 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .github/workflows/sync-keyword.yml | 107 ++++++++++----------- apps/web/src/app/api/sync/keyword/route.ts | 5 + 2 files changed, 54 insertions(+), 58 deletions(-) diff --git a/.github/workflows/sync-keyword.yml b/.github/workflows/sync-keyword.yml index 5c93293..6e41602 100644 --- a/.github/workflows/sync-keyword.yml +++ b/.github/workflows/sync-keyword.yml @@ -57,6 +57,14 @@ jobs: fi fi + # Store skipped packages for Discord + if [ "$skipped" -gt 0 ]; then + skippedList=$(echo "$response" | jq -r '.data.skippedPackages[]?' 2>/dev/null | paste -sd ", " - || echo "") + if [ -n "$skippedList" ]; then + echo "$skippedList" > /tmp/skipped_packages.txt + fi + fi + # Determine status emoji if [ "$errors" -gt 0 ]; then echo "status_emoji=⚠️" >> $GITHUB_OUTPUT @@ -73,67 +81,50 @@ jobs: duration_sec=$(echo "scale=2; ${{ steps.sync.outputs.durationMs }} / 1000" | bc) # Build Discord payload using jq for proper JSON escaping - if [ -f /tmp/error_summary.txt ] && [ ${{ steps.sync.outputs.errors }} -gt 0 ]; then - # Read and truncate error text - error_text=$(cat /tmp/error_summary.txt | head -c 800) + # Read optional data + error_text="" + skipped_text="" - # 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 - }] - }') + if [ -f /tmp/error_summary.txt ] && [ ${{ steps.sync.outputs.errors }} -gt 0 ]; then + error_text=$(cat /tmp/error_summary.txt | head -c 800) fi + if [ -f /tmp/skipped_packages.txt ] && [ ${{ steps.sync.outputs.skipped }} -gt 0 ]; then + skipped_text=$(cat /tmp/skipped_packages.txt) + fi + + # Build fields array dynamically + base_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 } + ]' + + # Create payload with dynamic fields + payload=$(jq -n \ + --arg title "${{ steps.sync.outputs.status_emoji }} NPM Keyword Search Sync" \ + --argjson color ${{ steps.sync.outputs.status_color }} \ + --argjson baseFields "$base_fields" \ + --arg error_text "$error_text" \ + --arg skipped_text "$skipped_text" \ + --arg timestamp "$(date -u +%Y-%m-%dT%H:%M:%S.000Z)" \ + ' + { + embeds: [{ + title: $title, + color: $color, + fields: ( + $baseFields + + (if $skipped_text != "" then [{ name: "📋 Skipped Packages", value: $skipped_text, inline: false }] else [] end) + + (if $error_text != "" then [{ name: "🔍 Error Details", value: ("```\n" + $error_text + "\n```"), inline: false }] else [] end) + ), + timestamp: $timestamp + }] + }') + # Send to Discord curl -X POST "${{ secrets.DISCORD_WEBHOOK }}" \ -H "Content-Type: application/json" \ diff --git a/apps/web/src/app/api/sync/keyword/route.ts b/apps/web/src/app/api/sync/keyword/route.ts index 1989256..e3fd8f2 100644 --- a/apps/web/src/app/api/sync/keyword/route.ts +++ b/apps/web/src/app/api/sync/keyword/route.ts @@ -30,6 +30,7 @@ export async function POST(request: NextRequest) { let skipped = 0; let errors = 0; const errorMessages: string[] = []; + const skippedPackages: string[] = []; try { // Search for packages with 'tpmjs-tool' keyword @@ -47,12 +48,14 @@ export async function POST(request: NextRequest) { // Skip if package not found if (!pkg) { skipped++; + skippedPackages.push(result.package.name); continue; } // Check if package has tpmjs field if (!pkg.tpmjs) { skipped++; + skippedPackages.push(pkg.name); continue; } @@ -60,6 +63,7 @@ export async function POST(request: NextRequest) { const validation = validateTpmjsField(pkg.tpmjs); if (!validation.valid || !validation.data) { skipped++; + skippedPackages.push(pkg.name); continue; } @@ -179,6 +183,7 @@ export async function POST(request: NextRequest) { packagesFound: searchResults.length, durationMs: Date.now() - startTime, errorMessages: errorMessages.slice(0, 5), // Include first 5 error messages + skippedPackages: skippedPackages, // Include all skipped package names }, }); } catch (error) {