Files
ara/orchestra-skills/.github/workflows/sync-skills.yml
T

200 lines
7.4 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
name: Sync Skills to Orchestra
on:
push:
branches:
- main
workflow_dispatch: # Allow manual trigger
jobs:
sync-skills:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 2 # Fetch last 2 commits to detect changes
- name: Detect changed skill folders
id: changes
run: |
# Get list of changed files in last commit
CHANGED_FILES=$(git diff --name-only HEAD^..HEAD)
echo "Changed files:"
echo "$CHANGED_FILES"
# Find skill directories - supports two patterns:
# Pattern 1: XX-category/skill-name/SKILL.md (nested skills)
# Pattern 2: XX-category/SKILL.md (standalone skills like 20-ml-paper-writing)
SKILL_DIRS=""
# Pattern 1: Nested skills (XX-category/skill-name/)
NESTED=$(echo "$CHANGED_FILES" | grep -E '^[0-9]{2}-[^/]+/[^/]+/' | sed -E 's|^([0-9]{2}-[^/]+/[^/]+)/.*|\1|' | sort -u)
if [ -n "$NESTED" ]; then
SKILL_DIRS="$NESTED"
fi
# Pattern 2: Standalone skills (XX-category/ with SKILL.md directly inside)
STANDALONE=$(echo "$CHANGED_FILES" | grep -E '^[0-9]{2}-[^/]+/SKILL\.md$' | sed -E 's|^([0-9]{2}-[^/]+)/SKILL\.md$|\1|' | sort -u)
if [ -n "$STANDALONE" ]; then
if [ -n "$SKILL_DIRS" ]; then
SKILL_DIRS=$(printf "%s\n%s" "$SKILL_DIRS" "$STANDALONE" | sort -u)
else
SKILL_DIRS="$STANDALONE"
fi
fi
echo "Changed skill directories:"
echo "$SKILL_DIRS"
# Convert to JSON array for matrix
if [ -z "$SKILL_DIRS" ]; then
SKILLS_JSON="[]"
SKILL_COUNT=0
else
SKILLS_JSON=$(echo "$SKILL_DIRS" | jq -R -s -c 'split("\n") | map(select(length > 0))')
SKILL_COUNT=$(echo "$SKILL_DIRS" | grep -c . || echo "0")
fi
echo "skills=$SKILLS_JSON" >> $GITHUB_OUTPUT
echo "count=$SKILL_COUNT" >> $GITHUB_OUTPUT
- name: Process and sync skills
if: steps.changes.outputs.count > 0
env:
ORCHESTRA_API_URL: ${{ secrets.ORCHESTRA_API_URL }}
ORCHESTRA_SYNC_API_KEY: ${{ secrets.ORCHESTRA_SYNC_API_KEY }}
run: |
SKILLS='${{ steps.changes.outputs.skills }}'
echo "Processing $(echo $SKILLS | jq 'length') skill(s)..."
# Install jq for JSON processing
sudo apt-get update && sudo apt-get install -y jq zip
# Loop through each skill directory
echo "$SKILLS" | jq -r '.[]' | while read SKILL_PATH; do
echo "==================================================="
echo "Processing: $SKILL_PATH"
echo "==================================================="
# Check if SKILL.md exists
if [ ! -f "$SKILL_PATH/SKILL.md" ]; then
echo "⚠️ WARNING: No SKILL.md found in $SKILL_PATH, skipping"
continue
fi
# Extract skill name from SKILL.md frontmatter
SKILL_NAME=$(grep -A 20 "^---$" "$SKILL_PATH/SKILL.md" | grep "^name:" | head -1 | sed 's/name: *//;s/"//g;s/'\''//g' | tr -d '\r')
# Extract author from SKILL.md frontmatter
AUTHOR=$(grep -A 20 "^---$" "$SKILL_PATH/SKILL.md" | grep "^author:" | head -1 | sed 's/author: *//;s/"//g;s/'\''//g' | tr -d '\r')
# Default values
if [ -z "$SKILL_NAME" ]; then
# Extract from directory name as fallback
SKILL_NAME=$(basename "$SKILL_PATH")
echo "⚠️ No 'name' in frontmatter, using directory name: $SKILL_NAME"
fi
if [ -z "$AUTHOR" ]; then
AUTHOR="Orchestra Research"
echo "⚠️ No 'author' in frontmatter, defaulting to: $AUTHOR"
fi
echo "Skill Name: $SKILL_NAME"
echo "Author: $AUTHOR"
echo "Path: $SKILL_PATH"
# Create temporary directory for zipping
TEMP_DIR=$(mktemp -d)
SKILL_DIR="$TEMP_DIR/$SKILL_NAME"
mkdir -p "$SKILL_DIR"
# Copy all contents of skill directory (SKILL.md, references/, scripts/, assets/, etc.)
cp -r "$SKILL_PATH"/* "$SKILL_DIR/" 2>/dev/null || true
# Create zip file (exclude hidden files and .gitkeep)
ZIP_FILE="$TEMP_DIR/${SKILL_NAME}.zip"
cd "$TEMP_DIR"
zip -r "$ZIP_FILE" "$SKILL_NAME" -x "*/.*" "*/.gitkeep" "*.DS_Store"
cd -
# Verify zip was created
if [ ! -f "$ZIP_FILE" ]; then
echo "❌ ERROR: Failed to create zip file for $SKILL_NAME"
continue
fi
echo "✓ Created zip: $(ls -lh "$ZIP_FILE" | awk '{print $5}')"
# Write SKILL.md content to temp file (avoid argument length limits)
SKILL_MD_FILE="$TEMP_DIR/skill.md"
cat "$SKILL_PATH/SKILL.md" > "$SKILL_MD_FILE"
# Encode zip to base64 and write to temp file (avoid argument length limits)
ZIP_BASE64_FILE="$TEMP_DIR/base64.txt"
base64 -w 0 "$ZIP_FILE" > "$ZIP_BASE64_FILE" 2>/dev/null || base64 "$ZIP_FILE" > "$ZIP_BASE64_FILE"
# Prepare JSON payload (use --rawfile for large content)
JSON_PAYLOAD=$(jq -n \
--arg skillName "$SKILL_NAME" \
--arg skillPath "$SKILL_PATH" \
--arg author "$AUTHOR" \
--rawfile skillMdContent "$SKILL_MD_FILE" \
--rawfile zipBase64 "$ZIP_BASE64_FILE" \
'{
skillName: $skillName,
skillPath: $skillPath,
author: $author,
skillMdContent: $skillMdContent,
zipBase64: $zipBase64
}')
# Send to Orchestra API (write JSON to file to avoid argument length limits)
echo "📤 Uploading to Orchestra..."
JSON_FILE="$TEMP_DIR/payload.json"
echo "$JSON_PAYLOAD" > "$JSON_FILE"
RESPONSE=$(curl -s -w "\n%{http_code}" -L \
-X POST \
-H "Content-Type: application/json" \
-H "X-Admin-API-Key: $ORCHESTRA_SYNC_API_KEY" \
-d @"$JSON_FILE" \
"$ORCHESTRA_API_URL/api/admin/sync-github-skill")
HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
BODY=$(echo "$RESPONSE" | sed '$d')
echo "HTTP Status: $HTTP_CODE"
echo "Response: $BODY"
if [ "$HTTP_CODE" = "200" ]; then
ACTION=$(echo "$BODY" | jq -r '.action // "synced"')
SOURCE=$(echo "$BODY" | jq -r '.source // "unknown"')
echo "✅ SUCCESS: Skill $SKILL_NAME $ACTION (source: $SOURCE)"
else
ERROR_MSG=$(echo "$BODY" | jq -r '.error // "Unknown error"')
echo "❌ FAILED: $ERROR_MSG"
exit 1
fi
# Cleanup
rm -rf "$TEMP_DIR"
echo ""
done
echo "==================================================="
echo "✅ Sync completed successfully!"
echo "==================================================="
- name: No changes detected
if: steps.changes.outputs.count == 0
run: |
echo "️ No skill changes detected in this commit"
echo "Only commits that modify skill directories will trigger sync"