diff --git a/.github/workflows/validate_skill.yml b/.github/workflows/validate_skill.yml index b1dd8a50..d4598f12 100644 --- a/.github/workflows/validate_skill.yml +++ b/.github/workflows/validate_skill.yml @@ -14,16 +14,37 @@ jobs: steps: - uses: actions/checkout@v6 - uses: actions/setup-python@v6 + with: + python-version: "3.x" + cache: "pip" + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install PyYAML - name: Validate SKILL.md files env: - BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.before }} HEAD_SHA: ${{ github.sha }} + EVENT_NAME: ${{ github.event_name }} + DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} + PR_BASE_REF: ${{ github.event.pull_request.base.ref }} run: | set -euo pipefail - echo "Base: $BASE_SHA" + + HEAD_SHA="${HEAD_SHA}" + + if [ "${EVENT_NAME}" = "pull_request" ]; then + BASE_BRANCH="${PR_BASE_REF}" + else + BASE_BRANCH="${DEFAULT_BRANCH}" + fi + + echo "Base branch: $BASE_BRANCH" echo "Head: $HEAD_SHA" - DIFF=$(git diff --name-status "$BASE_SHA" "$HEAD_SHA" -- 'skills/*/SKILL.md' || true) + git fetch --no-tags origin "${BASE_BRANCH}:refs/remotes/origin/${BASE_BRANCH}" + BASE=$(git merge-base "refs/remotes/origin/${BASE_BRANCH}" "$HEAD_SHA") + + DIFF=$(git diff --name-status "$BASE" "$HEAD_SHA" -- 'skills/*/SKILL.md' || true) if [ -z "$DIFF" ]; then exit 0 @@ -68,36 +89,38 @@ jobs: fi # Validate skills that were added or modified - CHANGED_FILES=$(echo "$DIFF" | awk ' + CHANGED_FOLDERS=$(echo "$DIFF" | awk ' $1 ~ /^A/ {print $2} $1 ~ /^M/ {print $2} $1 ~ /^R/ {print $3} - ') + ' | awk -F/ '{print $1"/"$2}' | sort -u) - if [ -z "$CHANGED_FILES" ]; then + if [ -z "$CHANGED_FOLDERS" ]; then exit 0 fi echo "Validating:" - echo "$CHANGED_FILES" + echo "$CHANGED_FOLDERS" INVALID_COUNT=0 - while IFS= read -r f; do - [ -z "$f" ] && continue - if [ ! -f "$f" ]; then - echo "[SKIP] missing file: $f" + while IFS= read -r d; do + [ -z "$d" ] && continue + if [ ! -d "$d" ]; then + echo "[SKIP] missing folder: $d" continue fi - if ! python scripts/check_skill_md.py "$f"; then + if ! python scripts/validate_skill.py "$d"; then + echo "[ERROR] Validation failed for $d" INVALID_COUNT=$((INVALID_COUNT + 1)) fi - done <<< "$CHANGED_FILES" + done <<< "$CHANGED_FOLDERS" echo "" - echo "$INVALID_COUNT skills failed validation." - if [ "$INVALID_COUNT" -ne 0 ]; then + echo "[ERROR] $INVALID_COUNT skills failed validation." exit 1 + else + echo "All skills passed validation." fi exit 0 \ No newline at end of file diff --git a/scripts/validate_skill.py b/scripts/validate_skill.py index 94617a0b..b3cc0997 100644 --- a/scripts/validate_skill.py +++ b/scripts/validate_skill.py @@ -4,9 +4,10 @@ """ import re -import yaml from pathlib import Path +import yaml + def validate_skill(skill_path): """Basic validation of a skill""" skill_path = Path(skill_path) @@ -17,7 +18,7 @@ def validate_skill(skill_path): return False, "SKILL.md not found" # Read and validate frontmatter - content = skill_md.read_text() + content = skill_md.read_text(encoding='utf-8', errors='replace') # Allow frontmatter block to appear anywhere in the file match = re.search(r'(?m)^---\n(.*?)\n---', content, re.DOTALL) if not match: @@ -67,4 +68,19 @@ def validate_skill(skill_path): # if len(description) > 1024: # return False, f"Description is too long ({len(description)} characters). Maximum is 1024 characters." - return True, "Skill is valid!" \ No newline at end of file + return True, "Skill is valid!" + +if __name__ == "__main__": + import sys + + if len(sys.argv) != 2: + print("Usage: validate_skill.py path/to/SKILL.md", file=sys.stderr) + raise SystemExit(2) + + ok, msg = validate_skill(sys.argv[1]) + if ok: + print(msg) + raise SystemExit(0) + else: + print(msg, file=sys.stderr) + raise SystemExit(1) \ No newline at end of file