Skip to content

Check for Tool Updates #148

Check for Tool Updates

Check for Tool Updates #148

Workflow file for this run

name: Check for Tool Updates
on:
schedule:
- cron: "0 0 * * *" # Daily at midnight UTC
workflow_dispatch: # Manual trigger
jobs:
check-updates:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
actions: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
- name: Check for updates
id: check
env:
SUPABASE_URL: ${{ secrets.SUPABASE_URL }}
SUPABASE_SERVICE_ROLE_KEY: ${{ secrets.SUPABASE_SERVICE_ROLE_KEY }}
run: |
UPDATES_FOUND=false
rm -f updates.txt
# Fetch tools from Supabase
HTTP_CODE=$(curl -s -w "%{http_code}" -o /tmp/tools_response.json "$SUPABASE_URL/rest/v1/tools?select=packagename,version" \
-H "apikey: $SUPABASE_SERVICE_ROLE_KEY" \
-H "Authorization: Bearer $SUPABASE_SERVICE_ROLE_KEY")
TOOLS_RESPONSE=$(cat /tmp/tools_response.json)
# Check HTTP status code
if [ "$HTTP_CODE" -lt 200 ] || [ "$HTTP_CODE" -ge 300 ]; then
echo "❌ Failed to fetch tools from Supabase (HTTP $HTTP_CODE)"
echo "Response: $TOOLS_RESPONSE"
exit 1
fi
# Check if response is valid JSON array
if ! echo "$TOOLS_RESPONSE" | jq -e 'type == "array"' > /dev/null 2>&1; then
echo "❌ Invalid response from Supabase - expected JSON array"
echo "Response: $TOOLS_RESPONSE"
exit 1
fi
# For each tool in Supabase
for row in $(echo "$TOOLS_RESPONSE" | jq -r '.[] | @base64'); do
_jq() {
echo ${row} | base64 --decode | jq -r "${1}"
}
NPM_PACKAGE=$(_jq '.packagename')
CURRENT_VERSION=$(_jq '.version')
# Skip if packagename is null or empty
if [ -z "$NPM_PACKAGE" ] || [ "$NPM_PACKAGE" = "null" ]; then
continue
fi
# Get latest npm version
LATEST_VERSION=$(npm view $NPM_PACKAGE version 2>/dev/null || echo "")
if [ -z "$LATEST_VERSION" ]; then
echo "⚠️ Could not fetch version for $NPM_PACKAGE"
continue
fi
# Compare versions
if [ "$LATEST_VERSION" != "$CURRENT_VERSION" ]; then
NPM_INFO=$(npm view "$NPM_PACKAGE@$LATEST_VERSION" --json 2>/dev/null || echo '{}')
# Retrieve update-time metadata from the new package version.
CSP_JSON=$(echo "$NPM_INFO" | jq -c '.csp_exceptions // .cspexception // .configurations.csp_exceptions // null')
FEATURES_JSON=$(echo "$NPM_INFO" | jq -c '.features // .configurations.features // null')
# Preserve structured JSON safely in line-oriented temp files.
CSP_B64=$(printf '%s' "$CSP_JSON" | base64 | tr -d '\n')
FEATURES_B64=$(printf '%s' "$FEATURES_JSON" | base64 | tr -d '\n')
echo "🔄 Update available for $NPM_PACKAGE: $CURRENT_VERSION → $LATEST_VERSION"
echo "$NPM_PACKAGE,$CURRENT_VERSION,$LATEST_VERSION,$CSP_B64,$FEATURES_B64" >> updates.txt
UPDATES_FOUND=true
fi
done
echo "found=$UPDATES_FOUND" >> $GITHUB_OUTPUT
- name: Create tool_updates record in Supabase
if: steps.check.outputs.found == 'true'
env:
SUPABASE_URL: ${{ secrets.SUPABASE_URL }}
SUPABASE_SERVICE_ROLE_KEY: ${{ secrets.SUPABASE_SERVICE_ROLE_KEY }}
run: |
# Remove any stale mapping file so this run controls what gets processed
rm -f tool_update_ids.txt
while IFS=',' read -r PACKAGE CURRENT_VERSION LATEST_VERSION CSP_B64 FEATURES_B64; do
if [ -z "$PACKAGE" ]; then
continue
fi
if [ -z "$LATEST_VERSION" ]; then
continue
fi
PACKAGE_FILTER=$(printf '%s' "$PACKAGE" | jq -sRr @uri)
VERSION_FILTER=$(printf '%s' "$LATEST_VERSION" | jq -sRr @uri)
EXISTING_RESPONSE=$(curl -sS "$SUPABASE_URL/rest/v1/tool_updates?select=id&package_name=eq.$PACKAGE_FILTER&version=eq.$VERSION_FILTER&limit=1" \
-H "apikey: $SUPABASE_SERVICE_ROLE_KEY" \
-H "Authorization: Bearer $SUPABASE_SERVICE_ROLE_KEY")
if echo "$EXISTING_RESPONSE" | jq -e 'type == "array" and length > 0' > /dev/null 2>&1; then
EXISTING_ID=$(echo "$EXISTING_RESPONSE" | jq -r '.[0].id // empty')
if [ -n "$EXISTING_ID" ]; then
echo "tool_updates already exists for $PACKAGE@$LATEST_VERSION (id=$EXISTING_ID); enqueueing for processing."
echo "$PACKAGE,$EXISTING_ID,$CSP_B64,$FEATURES_B64" >> tool_update_ids.txt
else
echo "Warning: tool_updates exists for $PACKAGE@$LATEST_VERSION but no id was returned; skipping enqueue."
fi
continue
fi
RESPONSE=$(curl -sS -X POST "${{ secrets.SUPABASE_URL }}/rest/v1/tool_updates" \
-H "apikey: ${{ secrets.SUPABASE_SERVICE_ROLE_KEY }}" \
-H "Authorization: Bearer ${{ secrets.SUPABASE_SERVICE_ROLE_KEY }}" \
-H "Content-Type: application/json" \
-H "Prefer: resolution=merge-duplicates,return=representation" \
-d "{\"package_name\":\"$PACKAGE\",\"version\":\"$LATEST_VERSION\"}")
echo "Supabase tools response for $PACKAGE: $RESPONSE"
# Check if response is an error object
if echo "$RESPONSE" | jq -e '.code' > /dev/null 2>&1; then
echo "❌ Error creating tool_updates record for $PACKAGE: $RESPONSE"
continue
fi
# Extract the inserted id (Supabase returns an array when using return=representation)
TOOL_UPDATES_ID=$(echo "$RESPONSE" | jq -r 'if type == "array" then .[0].id else .id end // empty')
# Persist mapping for the next step
if [ -n "$TOOL_UPDATES_ID" ]; then
echo "$PACKAGE,$TOOL_UPDATES_ID,$CSP_B64,$FEATURES_B64" >> tool_update_ids.txt
else
echo "Warning: No tool_updates id returned for $PACKAGE"
fi
done < updates.txt
- name: Invoke NextJS Update Tool API
if: steps.check.outputs.found == 'true'
env:
UPDATE_TOOL_API_URL: ${{ secrets.UPDATE_TOOL_API_URL }}
run: |
set -euo pipefail
if [ -z "$UPDATE_TOOL_API_URL" ]; then
echo "❌ Secret UPDATE_TOOL_API_URL is not set."
exit 1
fi
# Iterate over created tool_update ids and notify API per package
if [ ! -f tool_update_ids.txt ]; then
echo "No tool_update_ids.txt found; skipping API notifications."
exit 0
fi
while IFS=',' read -r PACKAGE TOOL_UPDATES_ID CSP_B64 FEATURES_B64; do
if [ -z "$PACKAGE" ] || [ -z "$TOOL_UPDATES_ID" ]; then
continue
fi
CSP_JSON=$(printf '%s' "$CSP_B64" | base64 --decode)
FEATURES_JSON=$(printf '%s' "$FEATURES_B64" | base64 --decode)
PAYLOAD=$(jq -c -n \
--arg packageName "$PACKAGE" \
--arg toolUpdateId "$TOOL_UPDATES_ID" \
--argjson csp_exceptions "$CSP_JSON" \
--argjson features "$FEATURES_JSON" \
'{
packageName: $packageName,
toolUpdateId: $toolUpdateId,
cspExceptions: $csp_exceptions,
features: $features
}')
echo "Queuing update API notification for $PACKAGE (tool_update_id=$TOOL_UPDATES_ID)"
# Fire-and-forget: do not wait for the HTTP response.
# Notes:
# - We still set small timeouts to avoid hangs if DNS/TLS stalls.
# - Output is discarded to keep logs clean.
nohup curl -sS -L --post301 --post302 --post303 \
--connect-timeout 5 \
--max-time 15 \
-X POST "$UPDATE_TOOL_API_URL" \
-H "Content-Type: application/json" \
-d "$PAYLOAD" \
>/dev/null 2>&1 &
done < tool_update_ids.txt
# Give background curls a moment to start before the step exits.
sleep 5