Skip to content

chore: add repo-wide AGENTS.md with agent guidelines #42

chore: add repo-wide AGENTS.md with agent guidelines

chore: add repo-wide AGENTS.md with agent guidelines #42

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