# Automated Skill Updates - Runs weekly to refresh documentation # Security Note: Schedule triggers with hardcoded constants. Workflow_dispatch input # accessed via FRAMEWORKS_INPUT env variable (safe pattern). name: Scheduled Skill Updates on: schedule: # Run every Sunday at 3 AM UTC - cron: '0 3 * * 0' workflow_dispatch: inputs: frameworks: description: 'Frameworks to update (comma-separated or "all")' required: false default: 'all' type: string jobs: update-skills: name: Update ${{ matrix.framework }} runs-on: ubuntu-latest strategy: fail-fast: false matrix: # Popular frameworks to keep updated framework: - react - django - fastapi - godot - vue - flask env: FRAMEWORK: ${{ matrix.framework }} FRAMEWORKS_INPUT: ${{ github.event.inputs.frameworks }} steps: - uses: actions/checkout@v3 with: submodules: recursive - name: Set up Python 3.12 uses: actions/setup-python@v4 with: python-version: '3.12' - name: Install dependencies run: | python -m pip install --upgrade pip pip install -e . - name: Check if framework should be updated id: should_update run: | FRAMEWORKS_INPUT="${FRAMEWORKS_INPUT:-all}" if [ "$FRAMEWORKS_INPUT" = "all" ] || [ -z "$FRAMEWORKS_INPUT" ]; then echo "update=true" >> $GITHUB_OUTPUT elif echo "$FRAMEWORKS_INPUT" | grep -q "$FRAMEWORK"; then echo "update=true" >> $GITHUB_OUTPUT else echo "update=false" >> $GITHUB_OUTPUT echo "ā­ļø Skipping $FRAMEWORK (not in update list)" fi - name: Check for existing skill if: steps.should_update.outputs.update == 'true' id: check_existing run: | SKILL_DIR="output/$FRAMEWORK" if [ -d "$SKILL_DIR" ]; then echo "exists=true" >> $GITHUB_OUTPUT echo "šŸ“¦ Found existing skill at $SKILL_DIR" else echo "exists=false" >> $GITHUB_OUTPUT echo "šŸ†• No existing skill found" fi - name: Incremental update (if exists) if: steps.should_update.outputs.update == 'true' && steps.check_existing.outputs.exists == 'true' run: | echo "⚔ Performing incremental update for $FRAMEWORK..." SKILL_DIR="output/$FRAMEWORK" # Detect changes using incremental updater python3 << 'EOF' import sys from pathlib import Path sys.path.insert(0, 'src') from skill_seekers.cli.incremental_updater import IncrementalUpdater import os framework = os.environ['FRAMEWORK'] skill_dir = Path(f'output/{framework}') updater = IncrementalUpdater(skill_dir) changes = updater.detect_changes() if changes.has_changes: print(f"šŸ”„ Changes detected:") print(f" Added: {len(changes.added)}") print(f" Modified: {len(changes.modified)}") print(f" Deleted: {len(changes.deleted)}") # Save current versions for next run updater.current_versions = updater._scan_documents() updater.save_current_versions() else: print("āœ“ No changes detected, skill is up to date") EOF - name: Full scrape (if new or manual) if: steps.should_update.outputs.update == 'true' && steps.check_existing.outputs.exists == 'false' run: | echo "šŸ“„ Performing full scrape for $FRAMEWORK..." CONFIG_FILE="configs/${FRAMEWORK}.json" if [ ! -f "$CONFIG_FILE" ]; then echo "āš ļø Config not found: $CONFIG_FILE" exit 0 fi # Use streaming ingestion for large docs skill-seekers scrape --config "$CONFIG_FILE" --streaming --max-pages 200 - name: Generate quality report if: steps.should_update.outputs.update == 'true' run: | SKILL_DIR="output/$FRAMEWORK" if [ ! -d "$SKILL_DIR" ]; then echo "āš ļø Skill directory not found" exit 0 fi echo "šŸ“Š Generating quality metrics..." python3 << 'EOF' import sys import os from pathlib import Path sys.path.insert(0, 'src') from skill_seekers.cli.quality_metrics import QualityAnalyzer framework = os.environ['FRAMEWORK'] skill_dir = Path(f'output/{framework}') analyzer = QualityAnalyzer(skill_dir) report = analyzer.generate_report() print(f"\nšŸ“Š Quality Score: {report.overall_score.grade} ({report.overall_score.total_score:.1f}/100)") print(f" Completeness: {report.overall_score.completeness:.1f}%") print(f" Accuracy: {report.overall_score.accuracy:.1f}%") print(f" Coverage: {report.overall_score.coverage:.1f}%") print(f" Health: {report.overall_score.health:.1f}%") EOF - name: Package for Claude if: steps.should_update.outputs.update == 'true' run: | SKILL_DIR="output/$FRAMEWORK" if [ -d "$SKILL_DIR" ]; then echo "šŸ“¦ Packaging $FRAMEWORK for Claude AI..." skill-seekers package "$SKILL_DIR" --target claude fi - name: Upload updated skill if: steps.should_update.outputs.update == 'true' uses: actions/upload-artifact@v3 with: name: ${{ env.FRAMEWORK }}-skill-updated path: output/${{ env.FRAMEWORK }}.zip retention-days: 90 summary: name: Update Summary needs: update-skills runs-on: ubuntu-latest if: always() steps: - name: Create summary run: | echo "## šŸ”„ Scheduled Skills Update" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "**Date:** $(date -u '+%Y-%m-%d %H:%M UTC')" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "### Updated Frameworks" >> $GITHUB_STEP_SUMMARY echo "- React" >> $GITHUB_STEP_SUMMARY echo "- Django" >> $GITHUB_STEP_SUMMARY echo "- FastAPI" >> $GITHUB_STEP_SUMMARY echo "- Godot" >> $GITHUB_STEP_SUMMARY echo "- Vue" >> $GITHUB_STEP_SUMMARY echo "- Flask" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "Updated skills available in workflow artifacts." >> $GITHUB_STEP_SUMMARY