- Fix HEAD~1...HEAD error for shallow clones by using github.event.before/after - Add proper error suppression with 2>/dev/null - Add fallback logic for edge cases (initial commits, force pushes) - Archive resolved runner setup issue to docs/issues/archive/resolved/ - Add archive documentation and next steps guide Fixes workflow execution on test/runner-validation branch. Resolves issue with 'fatal: bad revision' error in logs.
177 lines
7.3 KiB
YAML
177 lines
7.3 KiB
YAML
name: KB Lint
|
|
|
|
on:
|
|
push:
|
|
paths:
|
|
- 'kb/**/*.md'
|
|
pull_request:
|
|
paths:
|
|
- 'kb/**/*.md'
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
jobs:
|
|
validate:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Validate KB Files
|
|
run: |
|
|
set -e
|
|
|
|
# Colors for output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
NC='\033[0m' # No Color
|
|
|
|
echo "Validating KB files..."
|
|
|
|
# Find all KB files that changed
|
|
if [ "${{ github.event_name }}" = "pull_request" ]; then
|
|
# For pull requests, check changed files
|
|
changed_files=$(git diff --name-only --diff-filter=ACMR origin/${{ github.base_ref }}...HEAD -- 'kb/**/*.md' 2>/dev/null || true)
|
|
else
|
|
# For push events, use commit SHAs from event (works with shallow clones)
|
|
# Fallback to finding all KB files if commit comparison fails
|
|
if [ -n "${{ github.event.before }}" ] && [ "${{ github.event.before }}" != "0000000000000000000000000000000000000000" ]; then
|
|
changed_files=$(git diff --name-only --diff-filter=ACMR ${{ github.event.before }}...${{ github.event.after }} -- 'kb/**/*.md' 2>/dev/null || true)
|
|
else
|
|
# If no before commit (e.g., initial commit or force push), check all KB files
|
|
changed_files=$(find kb -type f -name "*.md" 2>/dev/null || true)
|
|
fi
|
|
|
|
# If git diff failed or returned empty, fallback to finding all KB files
|
|
if [ -z "$changed_files" ]; then
|
|
changed_files=$(find kb -type f -name "*.md" 2>/dev/null || true)
|
|
fi
|
|
fi
|
|
|
|
if [ -z "$changed_files" ]; then
|
|
echo "No KB files changed, skipping validation"
|
|
exit 0
|
|
fi
|
|
|
|
errors=0
|
|
|
|
# Process each changed file
|
|
while IFS= read -r file; do
|
|
# Skip empty lines
|
|
[ -z "$file" ] && continue
|
|
|
|
# Skip if file doesn't exist (deleted files)
|
|
[ ! -f "$file" ] && continue
|
|
|
|
# Skip special directories and files
|
|
if [[ "$file" == *"/_guides/"* ]] || \
|
|
[[ "$file" == *"/_templates/"* ]] || \
|
|
[[ "$file" == "kb/README.md" ]] || \
|
|
[[ "$file" == "kb/_index.md" ]] || \
|
|
[[ "$file" == "kb/CHANGELOG.md" ]]; then
|
|
echo -e "${YELLOW}⏭️ Skipping: $file (excluded from validation)${NC}"
|
|
continue
|
|
fi
|
|
|
|
filename=$(basename "$file")
|
|
relative_path="${file#kb/}"
|
|
file_errors=0
|
|
|
|
echo -e "\n${GREEN}Validating: $file${NC}"
|
|
|
|
# Validate filename pattern
|
|
if ! [[ "$filename" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}--[a-z0-9-]+--(idea|note|spec|decision|howto|retro|meeting)(--p[0-9]+)?\.md$ ]]; then
|
|
echo -e "${RED}❌ ERROR: Invalid filename pattern${NC}"
|
|
echo " Expected: YYYY-MM-DD--slug--type.md"
|
|
echo " Got: $filename"
|
|
file_errors=$((file_errors + 1))
|
|
errors=$((errors + 1))
|
|
fi
|
|
|
|
# Extract date and type from filename
|
|
filename_date=$(echo "$filename" | sed -E 's/^([0-9]{4}-[0-9]{2}-[0-9]{2})--.*/\1/')
|
|
filename_type=$(echo "$filename" | sed -E 's/.*--([a-z]+)(--p[0-9]+)?\.md$/\1/')
|
|
|
|
# Check frontmatter exists
|
|
if ! grep -q "^---$" "$file"; then
|
|
echo -e "${RED}❌ ERROR: Missing frontmatter delimiter${NC}"
|
|
echo " File must start with '---'"
|
|
file_errors=$((file_errors + 1))
|
|
errors=$((errors + 1))
|
|
fi
|
|
|
|
# Extract frontmatter
|
|
frontmatter=$(sed -n '/^---$/,/^---$/p' "$file" | sed '1d;$d')
|
|
|
|
if [ -z "$frontmatter" ]; then
|
|
echo -e "${RED}❌ ERROR: Empty frontmatter${NC}"
|
|
file_errors=$((file_errors + 1))
|
|
errors=$((errors + 1))
|
|
fi
|
|
|
|
# Check required fields (14 base fields)
|
|
REQUIRED_FIELDS=("title" "date" "author" "source" "project" "topics" "tags" "type" "status" "routing_hint" "proposed_path" "routing_confidence" "related" "summary")
|
|
for field in "${REQUIRED_FIELDS[@]}"; do
|
|
if ! echo "$frontmatter" | grep -q "^${field}:"; then
|
|
echo -e "${RED}❌ ERROR: Missing required field: $field${NC}"
|
|
file_errors=$((file_errors + 1))
|
|
errors=$((errors + 1))
|
|
fi
|
|
done
|
|
|
|
# Validate date matches
|
|
frontmatter_date=$(echo "$frontmatter" | grep "^date:" | sed -E 's/^date:[[:space:]]*["'\'']?([^"'\'']+)["'\'']?.*/\1/' | tr -d ' ' | head -1)
|
|
if [ "$frontmatter_date" != "$filename_date" ]; then
|
|
echo -e "${RED}❌ ERROR: Date mismatch${NC}"
|
|
echo " Filename date: $filename_date"
|
|
echo " Frontmatter date: $frontmatter_date"
|
|
file_errors=$((file_errors + 1))
|
|
errors=$((errors + 1))
|
|
fi
|
|
|
|
# Validate type matches
|
|
frontmatter_type=$(echo "$frontmatter" | grep "^type:" | sed -E 's/^type:[[:space:]]*["'\'']?([^"'\'']+)["'\'']?.*/\1/' | tr -d ' ' | head -1)
|
|
if [ "$frontmatter_type" != "$filename_type" ]; then
|
|
echo -e "${RED}❌ ERROR: Type mismatch${NC}"
|
|
echo " Filename type: $filename_type"
|
|
echo " Frontmatter type: $frontmatter_type"
|
|
file_errors=$((file_errors + 1))
|
|
errors=$((errors + 1))
|
|
fi
|
|
|
|
# Validate routing_confidence
|
|
routing_confidence=$(echo "$frontmatter" | grep "^routing_confidence:" | sed -E 's/^routing_confidence:[[:space:]]*([0-9.]+).*/\1/' | tr -d ' ' | head -1)
|
|
if ! awk -v conf="$routing_confidence" 'BEGIN {if (conf < 0.0 || conf > 1.0 || conf == "") exit 1}' 2>/dev/null; then
|
|
echo -e "${RED}❌ ERROR: Invalid routing_confidence value${NC}"
|
|
echo " Value: $routing_confidence"
|
|
echo " Must be numeric between 0.00 and 1.00"
|
|
file_errors=$((file_errors + 1))
|
|
errors=$((errors + 1))
|
|
fi
|
|
|
|
# Enforce review queue policy
|
|
if awk -v conf="$routing_confidence" 'BEGIN {exit !(conf < 0.60)}' 2>/dev/null; then
|
|
if [[ "$file" != *"/_review_queue/"* ]]; then
|
|
echo -e "${RED}❌ ERROR: File has routing_confidence < 0.60 but is not in kb/_review_queue/${NC}"
|
|
echo " routing_confidence: $routing_confidence"
|
|
echo " File path: $file"
|
|
file_errors=$((file_errors + 1))
|
|
errors=$((errors + 1))
|
|
fi
|
|
fi
|
|
|
|
if [ $file_errors -eq 0 ]; then
|
|
echo -e "${GREEN}✅ Valid: $file${NC}"
|
|
fi
|
|
done <<< "$changed_files"
|
|
|
|
if [ $errors -gt 0 ]; then
|
|
echo -e "\n${RED}Validation failed with $errors error(s)${NC}"
|
|
exit 1
|
|
else
|
|
echo -e "\n${GREEN}All KB files validated successfully${NC}"
|
|
exit 0
|
|
fi
|
|
|