chore: import upstream snapshot with attribution
Test Vector Database Adaptors / Test MCP Vector DB Tools (push) Has been cancelled
Tests / Code Quality (Ruff & Mypy) (push) Has been cancelled
Tests / Fast Unit Tests (parallel) (macos-latest, 3.11) (push) Has been cancelled
Tests / Fast Unit Tests (parallel) (macos-latest, 3.12) (push) Has been cancelled
Tests / Tests (push) Has been cancelled
Docker Publish / Build and Push Docker Images (map[description:Skill Seekers CLI - Convert documentation to AI skills dockerfile:Dockerfile name:skill-seekers]) (push) Has been cancelled
Docker Publish / Build and Push Docker Images (map[description:Skill Seekers MCP Server - 25 tools for AI assistants dockerfile:Dockerfile.mcp name:skill-seekers-mcp]) (push) Has been cancelled
Docker Publish / Test Docker Images (push) Has been cancelled
Tests / Fast Unit Tests (parallel) (ubuntu-latest, 3.10) (push) Has been cancelled
Tests / Fast Unit Tests (parallel) (ubuntu-latest, 3.11) (push) Has been cancelled
Tests / Fast Unit Tests (parallel) (ubuntu-latest, 3.12) (push) Has been cancelled
Tests / Serial / Integration / E2E Tests (push) Has been cancelled
Tests / MCP Server Tests (push) Has been cancelled
Test Vector Database Adaptors / Test chroma Adaptor (push) Has been cancelled
Test Vector Database Adaptors / Test faiss Adaptor (push) Has been cancelled
Test Vector Database Adaptors / Test qdrant Adaptor (push) Has been cancelled
Test Vector Database Adaptors / Test weaviate Adaptor (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 12:46:28 +08:00
commit 2cab53bc94
2985 changed files with 1288070 additions and 0 deletions
+139
View File
@@ -0,0 +1,139 @@
# Docker Image Publishing - Automated builds and pushes to Docker Hub
# Security Note: Uses secrets for Docker Hub credentials. Matrix values are hardcoded.
# Triggers: push/pull_request/workflow_dispatch only. No untrusted input.
name: Docker Publish
on:
push:
branches: [ main ]
tags:
- 'v*'
pull_request:
branches: [ main ]
paths:
- 'Dockerfile*'
- 'docker-compose.yml'
- 'src/**'
- 'pyproject.toml'
workflow_dispatch:
env:
DOCKER_REGISTRY: docker.io
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
jobs:
build-and-push:
name: Build and Push Docker Images
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
image:
- name: skill-seekers
dockerfile: Dockerfile
description: "Skill Seekers CLI - Convert documentation to AI skills"
- name: skill-seekers-mcp
dockerfile: Dockerfile.mcp
description: "Skill Seekers MCP Server - 25 tools for AI assistants"
env:
IMAGE_NAME: ${{ matrix.image.name }}
IMAGE_DOCKERFILE: ${{ matrix.image.dockerfile }}
IMAGE_DESCRIPTION: ${{ matrix.image.description }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Log in to Docker Hub
if: github.event_name != 'pull_request'
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v4
with:
images: ${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_USERNAME }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push Docker image
uses: docker/build-push-action@v4
with:
context: .
file: ${{ env.IMAGE_DOCKERFILE }}
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
platforms: linux/amd64,linux/arm64
- name: Create image summary
run: |
echo "## 🐳 Docker Image: $IMAGE_NAME" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Description:** $IMAGE_DESCRIPTION" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Tags:**" >> $GITHUB_STEP_SUMMARY
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
echo "${{ steps.meta.outputs.tags }}" >> $GITHUB_STEP_SUMMARY
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
test-images:
name: Test Docker Images
needs: build-and-push
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Build CLI image
run: |
docker build -t skill-seekers:test -f Dockerfile .
- name: Test CLI image
run: |
echo "🧪 Testing CLI image..."
docker run --rm skill-seekers:test skill-seekers --version
docker run --rm skill-seekers:test skill-seekers --help
- name: Build MCP image
run: |
docker build -t skill-seekers-mcp:test -f Dockerfile.mcp .
- name: Test MCP image
run: |
echo "🧪 Testing MCP server image..."
# Start MCP server in background
docker run -d --name mcp-test -p 8765:8765 skill-seekers-mcp:test
# Wait for server to start
sleep 10
# Check health
curl -f http://localhost:8765/health || exit 1
# Stop container
docker stop mcp-test
docker rm mcp-test
- name: Test Docker Compose
run: |
echo "🧪 Testing Docker Compose..."
docker compose config
echo "✅ Docker Compose configuration valid"
+171
View File
@@ -0,0 +1,171 @@
# Security Note: This workflow uses workflow_dispatch inputs and pull_request events.
# All untrusted inputs are accessed via environment variables (env:) as recommended.
# No direct usage of github.event.issue/comment/review content in run: commands.
name: Quality Metrics Dashboard
on:
workflow_dispatch:
inputs:
skill_dir:
description: 'Path to skill directory to analyze (e.g., output/react)'
required: true
type: string
fail_threshold:
description: 'Minimum quality score to pass (default: 70)'
required: false
default: '70'
type: string
pull_request:
paths:
- 'output/**'
- 'configs/**'
jobs:
analyze:
name: Quality Metrics Analysis
runs-on: ubuntu-latest
env:
SKILL_DIR_INPUT: ${{ github.event.inputs.skill_dir }}
FAIL_THRESHOLD_INPUT: ${{ github.event.inputs.fail_threshold }}
steps:
- uses: actions/checkout@v4
- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -e .
- name: Find skill directories
id: find_skills
run: |
if [ -n "$SKILL_DIR_INPUT" ]; then
# Manual trigger with specific directory
echo "dirs=$SKILL_DIR_INPUT" >> $GITHUB_OUTPUT
else
# PR trigger - find all skill directories
DIRS=$(find output -maxdepth 1 -type d -name "*" ! -name "output" | tr '\n' ' ' || echo "")
if [ -z "$DIRS" ]; then
echo "No skill directories found"
echo "dirs=" >> $GITHUB_OUTPUT
else
echo "dirs=$DIRS" >> $GITHUB_OUTPUT
fi
fi
- name: Analyze quality metrics
id: quality
run: |
DIRS="${{ steps.find_skills.outputs.dirs }}"
THRESHOLD="${FAIL_THRESHOLD_INPUT:-70}"
if [ -z "$DIRS" ]; then
echo "No directories to analyze"
exit 0
fi
ALL_PASSED=true
SUMMARY_FILE="quality_summary.md"
echo "# 📊 Quality Metrics Dashboard" > $SUMMARY_FILE
echo "" >> $SUMMARY_FILE
echo "**Threshold:** $THRESHOLD/100" >> $SUMMARY_FILE
echo "" >> $SUMMARY_FILE
for skill_dir in $DIRS; do
if [ ! -d "$skill_dir" ]; then
continue
fi
SKILL_NAME=$(basename "$skill_dir")
echo "🔍 Analyzing $SKILL_NAME..."
# Run quality analysis
python3 -c "
import sys
from pathlib import Path
from skill_seekers.cli.quality_metrics import QualityAnalyzer
skill_dir = Path('$skill_dir')
threshold = float('$THRESHOLD')
skill_name = '$SKILL_NAME'
analyzer = QualityAnalyzer(skill_dir)
report = analyzer.generate_report()
formatted = analyzer.format_report(report)
print(formatted)
with open(f'quality_{skill_name}.txt', 'w') as f:
f.write(formatted)
score = report.overall_score.total_score
grade = report.overall_score.grade
status = 'PASS' if score >= threshold else 'FAIL'
summary_line = f'{status} **{skill_name}**: {grade} ({score:.1f}/100)'
print(f'\n{summary_line}')
with open('quality_summary.md', 'a') as f:
f.write(f'{summary_line}\n')
if score < threshold:
print(f'::error file={skill_dir}/SKILL.md::Quality score {score:.1f} is below threshold {threshold}')
sys.exit(1)
elif score < 80:
print(f'::warning file={skill_dir}/SKILL.md::Quality score {score:.1f} could be improved')
else:
print(f'::notice file={skill_dir}/SKILL.md::Quality score {score:.1f} - Excellent!')
"
if [ $? -ne 0 ]; then
ALL_PASSED=false
fi
echo "" >> $SUMMARY_FILE
done
if [ "$ALL_PASSED" = false ]; then
echo "❌ Some skills failed quality thresholds"
exit 1
else
echo "✅ All skills passed quality thresholds"
fi
- name: Upload quality reports
uses: actions/upload-artifact@v4
with:
name: quality-metrics-reports
path: quality_*.txt
retention-days: 30
continue-on-error: true
- name: Post summary to PR
if: github.event_name == 'pull_request'
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const summary = fs.readFileSync('quality_summary.md', 'utf8');
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: summary
});
continue-on-error: true
- name: Create dashboard summary
run: |
if [ -f "quality_summary.md" ]; then
cat quality_summary.md >> $GITHUB_STEP_SUMMARY
fi
+125
View File
@@ -0,0 +1,125 @@
name: Release
on:
push:
tags:
- 'v*'
permissions:
contents: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: 'recursive'
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.10'
cache: 'pip'
- name: Install uv
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
if [ -f skill_seeker_mcp/requirements.txt ]; then pip install -r skill_seeker_mcp/requirements.txt; fi
# Install package in editable mode for tests (required for src/ layout)
pip install -e .
- name: Run tests
timeout-minutes: 30
run: |
pip install pytest-timeout
python -m pytest tests/ -v \
-m "not slow and not integration and not e2e and not network and not serial and not mcp_only" \
--timeout=120
- name: Extract version from tag
id: get_version
run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
- name: Verify Python version
run: |
python --version
python -c "import sys; assert sys.version_info >= (3, 10), f'Python {sys.version} is not >= 3.10'"
- name: Verify version consistency
run: |
TAG_VERSION="${{ steps.get_version.outputs.VERSION }}"
PKG_VERSION=$(python -c "import skill_seekers; print(skill_seekers.__version__)")
TOML_VERSION=$(grep -m1 '^version' pyproject.toml | sed 's/version *= *"\(.*\)"/\1/')
echo "Tag version: $TAG_VERSION"
echo "Package version: $PKG_VERSION"
echo "TOML version: $TOML_VERSION"
if [ "$TAG_VERSION" != "$PKG_VERSION" ]; then
echo "::error::Version mismatch! Tag=$TAG_VERSION but package reports=$PKG_VERSION"
exit 1
fi
if [ "$TAG_VERSION" != "$TOML_VERSION" ]; then
echo "::error::Version mismatch! Tag=$TAG_VERSION but pyproject.toml has=$TOML_VERSION"
exit 1
fi
echo "✅ All versions match: $TAG_VERSION"
- name: Create Release Notes
id: release_notes
run: |
if [ -f CHANGELOG.md ]; then
# Extract changelog for this version (escape dots for exact match)
VERSION="${{ steps.get_version.outputs.VERSION }}"
ESCAPED_VERSION=$(echo "$VERSION" | sed 's/\./\\./g')
sed -n "/## \[${ESCAPED_VERSION}\]/,/## \[/p" CHANGELOG.md | sed '$d' > release_notes.md
fi
# Fallback if extraction produced empty file or CHANGELOG.md missing
if [ ! -s release_notes.md ]; then
echo "Release v${{ steps.get_version.outputs.VERSION }}" > release_notes.md
fi
- name: Check if release exists
id: check_release
run: |
if gh release view ${{ github.ref_name }} > /dev/null 2>&1; then
echo "exists=true" >> $GITHUB_OUTPUT
else
echo "exists=false" >> $GITHUB_OUTPUT
fi
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Create GitHub Release
if: steps.check_release.outputs.exists == 'false'
uses: softprops/action-gh-release@v1
with:
name: v${{ steps.get_version.outputs.VERSION }}
tag_name: ${{ github.ref_name }}
body_path: release_notes.md
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Skip Release Creation
if: steps.check_release.outputs.exists == 'true'
run: |
echo "️ Release ${{ github.ref_name }} already exists, skipping creation"
echo "View at: https://github.com/${{ github.repository }}/releases/tag/${{ github.ref_name }}"
- name: Build package
run: |
uv build
- name: Publish to PyPI
env:
UV_PUBLISH_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
run: |
uv publish --token $UV_PUBLISH_TOKEN
+197
View File
@@ -0,0 +1,197 @@
# 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@v4
with:
submodules: recursive
- name: Set up Python 3.12
uses: actions/setup-python@v5
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 -c "
import sys, os
from pathlib import Path
from skill_seekers.cli.incremental_updater import IncrementalUpdater
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)}')
updater.current_versions = updater._scan_documents()
updater.save_current_versions()
else:
print('No changes detected, skill is up to date')
"
- 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 create "$CONFIG_FILE" --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 -c "
import sys, os
from pathlib import Path
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'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}%')
"
- 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@v4
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
+112
View File
@@ -0,0 +1,112 @@
# Security Note: This workflow uses only push/pull_request/workflow_dispatch triggers.
# Matrix values are hardcoded constants. No untrusted input is used in run: commands.
name: Test Vector Database Adaptors
on:
push:
branches: [ main, development ]
paths:
- 'src/skill_seekers/cli/adaptors/**'
- 'src/skill_seekers/mcp/tools/vector_db_tools.py'
- 'tests/test_*adaptor*.py'
- 'tests/test_mcp_vector_dbs.py'
- 'tests/test_docker_smoke.py'
- 'tests/test_upload_integration.py'
pull_request:
branches: [ main, development ]
paths:
- 'src/skill_seekers/cli/adaptors/**'
- 'src/skill_seekers/mcp/tools/vector_db_tools.py'
- 'tests/test_*adaptor*.py'
- 'tests/test_mcp_vector_dbs.py'
- 'tests/test_docker_smoke.py'
- 'tests/test_upload_integration.py'
workflow_dispatch:
jobs:
test-adaptors:
name: Test ${{ matrix.adaptor }} Adaptor
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
adaptor: [weaviate, chroma, faiss, qdrant]
python-version: ['3.10', '3.12']
env:
ADAPTOR_NAME: ${{ matrix.adaptor }}
PYTHON_VERSION: ${{ matrix.python-version }}
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pytest
pip install -e .
- name: Run adaptor tests
run: |
echo "🧪 Testing $ADAPTOR_NAME adaptor..."
python -m pytest "tests/test_adaptors/test_${ADAPTOR_NAME}_adaptor.py" -v --tb=short
- name: Test adaptor integration
run: |
echo "🔗 Testing $ADAPTOR_NAME integration..."
# Create test skill
mkdir -p test_skill/references
echo "# Test Skill" > test_skill/SKILL.md
echo "Test content" >> test_skill/SKILL.md
echo "# Reference" > test_skill/references/ref.md
# Test adaptor packaging
python3 -c "
import os
from pathlib import Path
from skill_seekers.cli.adaptors import get_adaptor
adaptor_name = os.environ['ADAPTOR_NAME']
adaptor = get_adaptor(adaptor_name)
package_path = adaptor.package(Path('test_skill'), Path('.'))
print(f'Package created: {package_path}')
assert package_path.exists(), 'Package file not created'
print(f'Package size: {package_path.stat().st_size} bytes')
"
- name: Upload test package
uses: actions/upload-artifact@v4
with:
name: test-package-${{ env.ADAPTOR_NAME }}-py${{ env.PYTHON_VERSION }}
path: test_skill-${{ env.ADAPTOR_NAME }}.json
retention-days: 7
test-mcp-tools:
name: Test MCP Vector DB Tools
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pytest
pip install -e .
- name: Run MCP vector DB tests
run: |
echo "🧪 Testing MCP vector database tools..."
python -m pytest tests/test_mcp_vector_dbs.py -v --tb=short
+155
View File
@@ -0,0 +1,155 @@
name: Tests
on:
push:
branches: [ main, development ]
pull_request:
branches: [ main, development ]
jobs:
lint:
name: Code Quality (Ruff & Mypy)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install "ruff==0.15.8" mypy
pip install -e .
- name: Run ruff linter
run: ruff check src/ tests/ --output-format=github
- name: Run ruff formatter check
run: ruff format --check src/ tests/
- name: Run mypy type checker
run: mypy src/skill_seekers --show-error-codes --pretty
continue-on-error: true
test-fast:
name: Fast Unit Tests (parallel)
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
python-version: ['3.10', '3.11', '3.12']
exclude:
- os: macos-latest
python-version: '3.10'
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Cache pip packages
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pytest-xdist pytest-timeout
pip install -e .
- name: Run fast tests (xdist parallel)
run: |
python -m pytest tests/ -n auto --dist=loadfile \
-m "not slow and not integration and not e2e and not network and not serial and not mcp_only" \
-q --timeout=120 --cov=src/skill_seekers --cov-report=xml --cov-report=term
timeout-minutes: 15
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
with:
file: ./coverage.xml
flags: unittests
name: codecov-umbrella
fail_ci_if_error: false
test-serial:
name: Serial / Integration / E2E Tests
runs-on: ubuntu-latest
needs: [test-fast]
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pytest-timeout
pip install -e .
- name: Run serial/integration/E2E tests
run: |
python -m pytest tests/ \
--ignore=tests/test_bootstrap_skill_e2e.py \
--ignore=tests/test_bootstrap_skill.py \
-m "(integration or e2e or slow or network or serial) and not mcp_only" \
-v --timeout=300
timeout-minutes: 25
test-mcp:
name: MCP Server Tests
runs-on: ubuntu-latest
needs: [test-fast]
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pytest-timeout
pip install -e ".[mcp]"
pip install -e .
- name: Run MCP tests
run: |
python -m pytest tests/ \
-m "mcp_only and not network" \
-v --timeout=180
timeout-minutes: 10
tests-complete:
name: Tests
needs: [lint, test-fast, test-serial, test-mcp]
runs-on: ubuntu-latest
if: always()
steps:
- name: Check all results
run: |
if [ "${{ needs.lint.result }}" != "success" ]; then
echo "❌ Code quality checks failed!"
exit 1
fi
if [ "${{ needs.test-fast.result }}" != "success" ]; then
echo "❌ Fast tests failed!"
exit 1
fi
if [ "${{ needs.test-serial.result }}" == "failure" ]; then
echo "❌ Serial/integration tests failed!"
exit 1
fi
if [ "${{ needs.test-mcp.result }}" == "failure" ]; then
echo "❌ MCP tests failed!"
exit 1
fi
echo "✅ All checks passed!"
+188
View File
@@ -0,0 +1,188 @@
name: Vector Database Export
on:
workflow_dispatch:
inputs:
skill_name:
description: 'Skill name to export (e.g., react, django, godot)'
required: true
type: string
targets:
description: 'Vector databases to export (comma-separated: weaviate,chroma,faiss,qdrant or "all")'
required: true
default: 'all'
type: string
config_path:
description: 'Path to config file (optional, auto-detected from skill_name if not provided)'
required: false
type: string
schedule:
# Run weekly on Sunday at 2 AM UTC for popular frameworks
- cron: '0 2 * * 0'
jobs:
export:
name: Export to Vector Databases
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
# For scheduled runs, export popular frameworks
skill: ${{ github.event_name == 'schedule' && fromJson('["react", "django", "godot", "fastapi"]') || fromJson(format('["{0}"]', github.event.inputs.skill_name)) }}
env:
SKILL_NAME: ${{ matrix.skill }}
TARGETS_INPUT: ${{ github.event.inputs.targets }}
CONFIG_PATH_INPUT: ${{ github.event.inputs.config_path }}
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -e .
- name: Determine config path
id: config
run: |
if [ -n "$CONFIG_PATH_INPUT" ]; then
echo "path=$CONFIG_PATH_INPUT" >> $GITHUB_OUTPUT
else
echo "path=configs/$SKILL_NAME.json" >> $GITHUB_OUTPUT
fi
- name: Check if config exists
id: check_config
run: |
CONFIG_FILE="${{ steps.config.outputs.path }}"
if [ -f "$CONFIG_FILE" ]; then
echo "exists=true" >> $GITHUB_OUTPUT
else
echo "exists=false" >> $GITHUB_OUTPUT
echo "⚠️ Config not found: $CONFIG_FILE"
fi
- name: Scrape documentation
if: steps.check_config.outputs.exists == 'true'
run: |
echo "📥 Scraping documentation for $SKILL_NAME..."
skill-seekers create "${{ steps.config.outputs.path }}" --max-pages 100
continue-on-error: true
- name: Determine export targets
id: targets
run: |
TARGETS="${TARGETS_INPUT:-all}"
if [ "$TARGETS" = "all" ]; then
echo "list=weaviate chroma faiss qdrant" >> $GITHUB_OUTPUT
else
echo "list=$(echo "$TARGETS" | tr ',' ' ')" >> $GITHUB_OUTPUT
fi
- name: Export to vector databases
if: steps.check_config.outputs.exists == 'true'
env:
EXPORT_TARGETS: ${{ steps.targets.outputs.list }}
run: |
SKILL_DIR="output/$SKILL_NAME"
if [ ! -d "$SKILL_DIR" ]; then
echo "❌ Skill directory not found: $SKILL_DIR"
exit 1
fi
echo "📦 Exporting $SKILL_NAME to vector databases..."
for target in $EXPORT_TARGETS; do
echo ""
echo "🔹 Exporting to $target..."
# Use adaptor directly via CLI
python3 -c "
from pathlib import Path
from skill_seekers.cli.adaptors import get_adaptor
adaptor = get_adaptor('$target')
package_path = adaptor.package(Path('$SKILL_DIR'), Path('output'))
print(f'Exported to {package_path}')
"
if [ $? -eq 0 ]; then
echo "✅ $target export complete"
else
echo "❌ $target export failed"
fi
done
- name: Generate quality report
if: steps.check_config.outputs.exists == 'true'
run: |
SKILL_DIR="output/$SKILL_NAME"
if [ -d "$SKILL_DIR" ]; then
echo "📊 Generating quality metrics..."
python3 -c "
from pathlib import Path
from skill_seekers.cli.quality_metrics import QualityAnalyzer
analyzer = QualityAnalyzer(Path('$SKILL_DIR'))
report = analyzer.generate_report()
formatted = analyzer.format_report(report)
print(formatted)
with open('quality_report_${SKILL_NAME}.txt', 'w') as f:
f.write(formatted)
"
fi
continue-on-error: true
- name: Upload vector database exports
if: steps.check_config.outputs.exists == 'true'
uses: actions/upload-artifact@v4
with:
name: ${{ env.SKILL_NAME }}-vector-exports
path: |
output/${{ env.SKILL_NAME }}-*.json
retention-days: 30
- name: Upload quality report
if: steps.check_config.outputs.exists == 'true'
uses: actions/upload-artifact@v4
with:
name: ${{ env.SKILL_NAME }}-quality-report
path: quality_report_${{ env.SKILL_NAME }}.txt
retention-days: 30
continue-on-error: true
- name: Create export summary
if: steps.check_config.outputs.exists == 'true'
env:
EXPORT_TARGETS: ${{ steps.targets.outputs.list }}
run: |
echo "## 📦 Vector Database Export Summary: $SKILL_NAME" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
for target in $EXPORT_TARGETS; do
FILE="output/${SKILL_NAME}-${target}.json"
if [ -f "$FILE" ]; then
SIZE=$(du -h "$FILE" | cut -f1)
echo "✅ **$target**: $SIZE" >> $GITHUB_STEP_SUMMARY
else
echo "❌ **$target**: Export failed" >> $GITHUB_STEP_SUMMARY
fi
done
echo "" >> $GITHUB_STEP_SUMMARY
if [ -f "quality_report_${SKILL_NAME}.txt" ]; then
echo "### 📊 Quality Metrics" >> $GITHUB_STEP_SUMMARY
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
head -30 "quality_report_${SKILL_NAME}.txt" >> $GITHUB_STEP_SUMMARY
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
fi