name: Validate skills on: push: branches: [main] pull_request: permissions: contents: read jobs: validate: runs-on: ubuntu-latest steps: - name: Check out repository uses: actions/checkout@v7 with: fetch-depth: 0 - name: Set up Ruby uses: ruby/setup-ruby@v1 with: ruby-version: '3.3' - name: Set up Python uses: actions/setup-python@v7 with: python-version: '3.12' - name: Install Python validator dependencies run: python3 -m pip install -r requirements-dev.txt - name: Lint Python (ruff) run: python3 -m ruff check scripts/ eval_runner/ - name: Check Python formatting (ruff) run: python3 -m ruff format --check scripts/ eval_runner/ - name: Type check (mypy) run: python3 -m mypy scripts/ eval_runner/ - name: Complexity check (radon) run: python3 -m radon cc scripts/ eval_runner/ --min B --total-average - name: Unused dependency check (deptry) run: python3 -m deptry . - name: Check for large files run: | large_files=$(git ls-files -- ':(glob)**' | while read -r f; do if [ -f "$f" ]; then size=$(stat -c%s "$f" 2>/dev/null || echo 0) if [ "$size" -gt 5242880 ]; then echo "$f ($((size / 1024)) KB)" fi fi done) if [ -n "$large_files" ]; then echo "Files exceeding 5 MB:" echo "$large_files" exit 1 fi echo "No large files detected." - name: Scan technical debt markers run: python3 scripts/scan-tech-debt.py - name: Duplicate code detection (jscpd) run: npx jscpd --config .jscpd.json . - name: Security review (bandit) run: python3 -m bandit -r scripts/ -f txt --severity-level high - name: Check dependency release age (supply chain) run: python3 scripts/check-dependency-age.py - name: Validate skill format and links run: ruby scripts/validate-skills.rb - name: Run reference-scan validator tests run: ruby scripts/test-validate-skills.rb - name: Test eval manifest validation run: python3 scripts/test-eval-validation.py - name: Validate eval manifests run: python3 scripts/validate-evals.py - name: Test life-coach capability validation run: python3 -m unittest discover -s life-coach/tests -p 'test_*.py' - name: Run skill-local script tests run: | set -euo pipefail while IFS= read -r dir; do echo "=== $dir ===" python3 -m pytest "$dir" -o "addopts=-ra --strict-markers --tb=short" -o "python_files=test_*.py" -v --durations=10 done < <(git ls-files | grep -E '/scripts/test_[^/]*\.py$' | sed 's#/[^/]*$##' | sort -u) - name: Install epub skill test deps (EbookLib is AGPL; test-only) run: python3 -m pip install -r requirements-epub-test.txt - name: Run skill-local shell test scripts run: python3 scripts/check-skill-tests.py --run - name: Check skill test coverage run: python3 scripts/check-skill-tests.py --check - name: Run core test suite with coverage run: python3 -m pytest scripts/test-eval-validation.py scripts/test-eval-coverage.py scripts/test_check_skill_tests.py eval_runner/tests/ -v --durations=10 --cov=scripts --cov=eval_runner --cov-fail-under=60 --cov-report=term-missing - name: Run tests in parallel (isolation check) run: python3 -m pytest scripts/test-eval-validation.py scripts/test-eval-coverage.py scripts/test_check_skill_tests.py eval_runner/tests/ -n auto -v --durations=10 - name: Validate AGENTS.md consistency run: python3 scripts/validate-agents-md.py - name: Run integration tests run: python3 -m pytest tests/integration/ -v --durations=10 -o "addopts=-ra --strict-markers --tb=short --durations=10" - name: Test changed-skill quality validation run: ruby scripts/test-validate-skill-quality.rb - name: Validate changed skill quality env: SKILL_QUALITY_BASE: ${{ github.event.pull_request.base.sha || github.event.before }} run: ruby scripts/validate-skill-quality.rb --base "$SKILL_QUALITY_BASE" - name: Check tracked repository artifacts run: python3 scripts/check-artifacts.py - name: Validate Claude Code marketplace run: ruby scripts/gen-claude-marketplace.rb - name: Validate Codex plugin packaging run: ruby scripts/gen-codex-plugin.rb - name: Test eval coverage ratchet run: python3 scripts/test-eval-coverage.py - name: Report eval coverage run: python3 scripts/eval-coverage.py - name: Enforce eval coverage ratchet env: EVAL_RATCHET_BASE: ${{ github.event.pull_request.base.sha || github.event.before }} run: python3 scripts/eval-coverage.py --modified-from "$EVAL_RATCHET_BASE" - name: Test llms.txt generator run: ruby scripts/test-gen-llms-txt.rb - name: Validate llms.txt catalog run: ruby scripts/gen-llms-txt.rb - name: Validate bundle manifests run: ruby scripts/validate-bundles.rb - name: Test bundle manifest validation run: ruby scripts/test-validate-bundles.rb - name: Validate lifecycle capability matrix catalog run: ruby scripts/gen-lifecycle-matrix.rb - name: Validate lifecycle capability matrix run: ruby scripts/validate-lifecycle-matrix.rb - name: Build tracked Python packages run: | wheel_dir=$(mktemp -d) while IFS= read -r pyproject; do # Skip root pyproject.toml (not a distributable package) [ "$pyproject" = "pyproject.toml" ] && continue python3 -m pip wheel --no-deps "./$(dirname "$pyproject")" --wheel-dir "$wheel_dir" done < <(git ls-files -- ':(glob)**/pyproject.toml')