diff --git a/.gitea/workflows/validate-config.yaml b/.gitea/workflows/validate-config.yaml new file mode 100644 index 00000000..3025807d --- /dev/null +++ b/.gitea/workflows/validate-config.yaml @@ -0,0 +1,134 @@ +# validate-config.yaml +# Validates all config files, scripts, and playbooks on every PR. +# Addresses #289: repo-native validation for timmy-config changes. +# +# Runs: YAML lint, Python syntax check, shell lint, JSON validation, +# deploy script dry-run, and cron syntax verification. + +name: Validate Config + +on: + pull_request: + branches: [main] + push: + branches: [main] + +jobs: + yaml-lint: + name: YAML Lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install yamllint + run: pip install yamllint + - name: Lint YAML files + run: | + find . -name '*.yaml' -o -name '*.yml' | \ + grep -v '.gitea/workflows' | \ + xargs -r yamllint -d '{extends: relaxed, rules: {line-length: {max: 200}}}' + + json-validate: + name: JSON Validate + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Validate JSON files + run: | + find . -name '*.json' -print0 | while IFS= read -r -d '' f; do + echo "Validating: $f" + python3 -m json.tool "$f" > /dev/null || exit 1 + done + + python-check: + name: Python Syntax & Import Check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.11' + - name: Install dependencies + run: | + pip install py_compile flake8 + - name: Compile-check all Python files + run: | + find . -name '*.py' -print0 | while IFS= read -r -d '' f; do + echo "Checking: $f" + python3 -m py_compile "$f" || exit 1 + done + - name: Flake8 critical errors only + run: | + flake8 --select=E9,F63,F7,F82 --show-source --statistics \ + scripts/ allegro/ cron/ || true + + shell-lint: + name: Shell Script Lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install shellcheck + run: sudo apt-get install -y shellcheck + - name: Lint shell scripts + run: | + find . -name '*.sh' -print0 | xargs -0 -r shellcheck --severity=error || true + + cron-validate: + name: Cron Syntax Check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Validate cron entries + run: | + if [ -d cron ]; then + find cron -name '*.cron' -o -name '*.crontab' | while read f; do + echo "Checking cron: $f" + # Basic syntax validation + while IFS= read -r line; do + [[ "$line" =~ ^#.*$ ]] && continue + [[ -z "$line" ]] && continue + fields=$(echo "$line" | awk '{print NF}') + if [ "$fields" -lt 6 ]; then + echo "ERROR: Too few fields in $f: $line" + exit 1 + fi + done < "$f" + done + fi + + deploy-dry-run: + name: Deploy Script Dry Run + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Syntax-check deploy.sh + run: | + if [ -f deploy.sh ]; then + bash -n deploy.sh + echo "deploy.sh syntax OK" + fi + + playbook-schema: + name: Playbook Schema Validation + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Validate playbook structure + run: | + python3 -c " +import yaml, sys, glob +required_keys = {'name', 'description'} +for f in glob.glob('playbooks/*.yaml'): + with open(f) as fh: + try: + data = yaml.safe_load(fh) + if not isinstance(data, dict): + print(f'ERROR: {f} is not a YAML mapping') + sys.exit(1) + missing = required_keys - set(data.keys()) + if missing: + print(f'WARNING: {f} missing keys: {missing}') + print(f'OK: {f}') + except yaml.YAMLError as e: + print(f'ERROR: {f}: {e}') + sys.exit(1) +" \ No newline at end of file