chore: add repo-wide AGENTS.md with agent guidelines #42
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Validate Recipe Manifests | |
| on: | |
| pull_request: | |
| paths: | |
| - 'core/**' | |
| - 'contrib/**' | |
| - '.github/schemas/manifest-schema.json' | |
| - '.github/workflows/validate-manifest.yml' | |
| - 'tools/validate.py' | |
| - 'tools/validate_manifest.py' | |
| - 'pyproject.toml' | |
| push: | |
| branches: | |
| - main | |
| paths: | |
| - 'core/**' | |
| - 'contrib/**' | |
| - '.github/schemas/manifest-schema.json' | |
| - '.github/workflows/validate-manifest.yml' | |
| - 'tools/validate.py' | |
| - 'tools/validate_manifest.py' | |
| - 'pyproject.toml' | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| jobs: | |
| validate-manifests: | |
| name: Check recipe manifest.yaml presence and schema | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@v6 | |
| - name: Install validation dependencies | |
| run: uv sync | |
| - name: Detect affected recipe directories | |
| id: affected | |
| run: | | |
| # For pull_request events, diff against the base branch merge-base (three-dot diff). | |
| # For push events, diff against the previous commit if available. | |
| # For workflow_dispatch or unknown base, validate all recipes. | |
| if [ "${{ github.event_name }}" = "pull_request" ]; then | |
| BASE_REF="${{ github.event.pull_request.base.ref }}" | |
| git fetch origin "$BASE_REF" | |
| CHANGED_FILES=$(git diff --name-only "origin/$BASE_REF"...HEAD) | |
| elif [ "${{ github.event_name }}" = "push" ] && [ "${{ github.event.before }}" != "0000000000000000000000000000000000000000" ]; then | |
| CHANGED_FILES=$(git diff --name-only "${{ github.event.before }}" HEAD) | |
| else | |
| # workflow_dispatch or new branch with no previous commit — validate all | |
| echo "No base to diff against — will validate all recipes." | |
| echo "validate_all=true" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| echo "Changed files:" | |
| echo "$CHANGED_FILES" | |
| # If the schema, workflow, or validation tools changed, validate all recipes | |
| INFRA_CHANGED=$(echo "$CHANGED_FILES" | grep -E "^(\.github/(schemas/manifest-schema\.json|workflows/validate-manifest\.yml)|tools/validate(_manifest)?\.py|pyproject\.toml)$" || true) | |
| if [ -n "$INFRA_CHANGED" ]; then | |
| echo "Schema, workflow, or validation tools changed — will validate all recipes." | |
| echo "validate_all=true" >> "$GITHUB_OUTPUT" | |
| else | |
| # Derive the recipe directory from the path structure alone — no | |
| # filesystem lookups — so new recipes without a manifest.yaml are | |
| # still caught rather than silently skipped. | |
| # | |
| # Layout rules (mirrors validate_manifest.py): | |
| # core/<namespace>/<recipe>/... when <namespace> is a language dir | |
| # core/<recipe>/... otherwise | |
| # | |
| # Language namespace directories (must stay in sync with | |
| # LANGUAGE_NAMESPACE_DIRS in tools/validate_manifest.py). | |
| LANG_NAMESPACES="python java go typescript kotlin" | |
| recipe_dir_for() { | |
| local file="$1" | |
| # Strip to: <root>/<part1>/<part2>/... | |
| local root part1 part2 | |
| root="${file%%/*}" # core or contrib | |
| rest="${file#*/}" # everything after root/ | |
| part1="${rest%%/*}" # first component under root | |
| rest2="${rest#*/}" # everything after part1/ | |
| part2="${rest2%%/*}" # second component (may equal rest2 if no slash) | |
| # Check if part1 is a language namespace and part2 is a real component | |
| for ns in $LANG_NAMESPACES; do | |
| if [ "$part1" = "$ns" ] && [ -n "$part2" ] && [ "$part2" != "$rest2" -o "$rest2" != "$part1" ]; then | |
| # Namespaced: root/namespace/recipe | |
| echo "${root}/${part1}/${part2}" | |
| return | |
| fi | |
| done | |
| # Flat: root/recipe | |
| echo "${root}/${part1}" | |
| } | |
| CHANGED_RECIPES="" | |
| while IFS= read -r file; do | |
| [ -z "$file" ] && continue | |
| case "$file" in | |
| core/*|contrib/*) ;; | |
| *) continue ;; | |
| esac | |
| recipe_dir="$(recipe_dir_for "$file")" | |
| [ -z "$recipe_dir" ] && continue | |
| CHANGED_RECIPES="${CHANGED_RECIPES}${recipe_dir}"$'\n' | |
| done <<< "$CHANGED_FILES" | |
| # Deduplicate and filter to only directories. Files like | |
| # core/README.md must be excluded; new recipe dirs are safe to | |
| # filter this way because the branch is already checked out by | |
| # the time this step runs, so new dirs exist on disk. | |
| CHANGED_RECIPES=$(echo "$CHANGED_RECIPES" | sort -u | grep -v '^$' | while read -r path; do [ -d "$path" ] && echo "$path"; done || true) | |
| echo "Changed recipes: $CHANGED_RECIPES" | |
| echo "changed_recipes=$(echo "$CHANGED_RECIPES" | tr '\n' ' ')" >> "$GITHUB_OUTPUT" | |
| echo "validate_all=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Validate affected recipe manifests | |
| run: | | |
| if [ "${{ steps.affected.outputs.validate_all }}" = "true" ]; then | |
| uv run validate manifest | |
| else | |
| RECIPES="${{ steps.affected.outputs.changed_recipes }}" | |
| if [ -z "$RECIPES" ]; then | |
| echo "[INFO] No recipe directories to validate." | |
| exit 0 | |
| fi | |
| for recipe in $RECIPES; do | |
| uv run validate manifest "$recipe" | |
| done | |
| fi |