DEV Community

poolion
poolion

Posted on

YAML Linter: Basic YAML Validation in Python Without PyYAML Dependency

YAML Linter: Fast Config Validation Without External Dependencies

Validating YAML configuration files is a common task in CI/CD pipelines—checking for required fields, empty values, or structure issues. The YAML Linter is a lightweight Python tool that performs basic validation without installing PyYAML or other external dependencies. It uses simple regex patterns to detect missing fields, undefined values, and consistency issues across your configuration files.

What Does It Do?

The YAML Linter checks for:

  • Missing required fields — Variables that should exist based on a requirements list
  • Empty/undefined values — Keys referenced but with no value assigned
  • Numeric key conflicts — Numbered keys vs non-numeric key types
  • Pattern validation — Basic consistency checks across nested structures

Unlike full YAML parsers, it uses simple pattern matching for speed—ideal when you want lightweight validation without package overhead.

Why Use This Tool?

Common config mistakes break deployments:

# Common issues that cause deployment failures
export API_URL="http://${API_HOST}:${API_PORT}"  # Missing vars fail  
# production.yml has required fields missing  
# DATABASE_URL='' empty value ignored in some contexts  
Enter fullscreen mode Exit fullscreen mode

Env Checker catches these before they hit production—without installing PyYAML.

Installation

Add to your PATH with zero dependencies:

cp ~/env-checker/yaml-lint.py /usr/local/bin/
yaml-lint.sh config.yml           # Basic validation
yaml-lint.sh < config.txt         # Pipeline-ready, use for scripts  
Enter fullscreen mode Exit fullscreen mode

Or via pip if you prefer packages:

git clone https://github.com/Poolion/env-checker.git
cd env-checker
pip install .  # Optional wrapper for package distribution
python yaml-lint.sh myconfig.yml
Enter fullscreen mode Exit fullscreen mode

No pip install pyyaml needed—uses only the Python standard library.

Usage Examples

Basic Validation

Run a quick check on your configs:

$ python yaml-lint.sh app/configs/production.yml
* YAML Linter Report  
Source: production.yml  
Fields found:       14  

* No issues detected.
Enter fullscreen mode Exit fullscreen mode

Enforce Required Fields

Specify which fields must exist in every config file:

python yaml-lint.sh configs/*.yml --required-fields name,url,sslCertFile
# or with shorter flag:
yaml-lint.sh myapp.json -r "name,url,version"
Enter fullscreen mode Exit fullscreen mode

Output when missing:

$ python3 env-checker/yaml-lint.py app/configs/production.yml -r "apiVersion,namespace,replicas,version"  
* YAML Linter Report  
Source: production.yml  
Fields found:       12  

* Missing required fields:  
Missing required field: VERSION (referenced but never defined)
Enter fullscreen mode Exit fullscreen mode

Show Empty Values

Find keys that exist with empty or undefined values:

$ python3 env-checker/yaml-lint.sh config.yml --show-empty  
* Issues found: 2 
• KeyError at line 5: DATABASE_URL  
Empty value found: API_KEY (empty on line 10)
# Empty key found at line 8: LOGGING_LEVEL
Enter fullscreen mode Exit fullscreen mode

The --show-empty flag helps catch configs where variables are referenced but never populated.

Command Line Interface

Basic syntax:

python yaml-lint.py <content> [OPTIONS]

Options:  
  --required-fields LIST, -r LIST     Comma-separated list of required fields (e.g., "name,url,version")
  --show-empty                        Also report empty/undefined values (default: off)  
  --help                              Show this help message

Examples:  
  python yaml-lint.sh config.yml                    # Basic validation with zero deps  
  python env-checker/yaml-lint.sh -r "apiVersion" config.yml  
yaml-lint.sh myapp.json                             # Run from PATH
Enter fullscreen mode Exit fullscreen mode

Help menu:

python3 env-checker/yaml-lint.py --help  
# Usage: python yaml-lint.py [OPTIONS] [file]
# Options:
#   -h, --help      Show help message and exit
#   --required-fields LIST, -r LIST 
                      Comma-separated list of required fields (e.g. "name,url,loggingLevel")
Enter fullscreen mode Exit fullscreen mode

Configuration File Example

Here is a typical app config with validation checks:

# production.yml  
apiVersion: v2
name: MyApp
version: 1.0.0                    # Required by pipeline script  
sslCertFile: /etc/ssl/certs/myapp.crt
DATABASE_URL='${DATABASE}'        # Must be populated or check fails  
API_HOST=${API_HOST}              # External service reference  

logging:                          # Nested structure (checked for empty values)

    level: DEBUG                 
    format: "%(asctime)s - %(message)s"
Enter fullscreen mode Exit fullscreen mode

Run validation against your config file:

$ python3 env-checker/yaml-lint.sh production.yml --required-fields "apiVersion,version,namesp ace"  
* YAML Linter Report  
Source: production.yml  
Fields found:       7  

* No issues detected.
Enter fullscreen mode Exit fullscreen mode

With a missing field:

python yaml-lint.py configs/*.yml -r "apiVersion,namespace"   
# Missing required field: NAMESPACE (in nested structure?)
# Check line where it should be defined in production/config.yml  
Enter fullscreen mode Exit fullscreen mode

Output Differences

Scenario Simple validation output Advanced PyYAML validation output
Missing required fields Lists each missing field with source reference Fails immediately if critical field absent
Empty values Shows which keys are empty without parsing anchors Validates entire YAML structure including complex nested patterns
Comments in file Preserves and shows for documentation Often stripped during validation processing
Nested configs Basic key-level checks per level Full graph traversal of nested structures

For CI/CD validation tasks, the simple approach is sufficient—no overhead from full parsing. Add PyYAML only if you need deep analysis like anchor validation or complex schema enforcement.

Integration Examples

GitHub Actions Pipeline Step

Validate configs before building Docker images:

name: Validate Environment Variables  
- uses: actions/setup-node@v3    
- run: pip install pyyaml  # Optional deep validation (not required for basic)

- name: Validate YAML configs  
run: | 
    for config in app/configs/*.json; do \
        python yaml-lint.sh "$config" -r "apiVersion,namespace" || exit 1; \
    done  

name: Build Docker image  
uses: docker/build-push-action@v3

Enter fullscreen mode Exit fullscreen mode

Dockerfile Check Step

Ensure configs are valid before container build:

COPY app/configs/*.yml /app/

# Validate YAML before finalization
RUN python yaml-lint.sh /app/production.yml -r "apiVersion,name" || echo "* YAML linting failed—fix missing fields and rebuild" && make build-docker-image
Enter fullscreen mode Exit fullscreen mode

Or with shellcheck for comprehensive validation:

name: Shell linting  
uses: docker/build-push-action@v3  

shellcheck myscript.sh  # Validate syntax  

python yaml-lint.sh configs/*.yml || exit 1
CMD bash /app/script.sh
Enter fullscreen mode Exit fullscreen mode

Makefile Linting Target

Include config validation in your project's CI workflow:

.PHONY: validate-config
validate-config:
    @echo "Validating all YAML configs..."  
    for config in app/configs/*.yml; do \
        python yaml-lint.sh $$config -r name,url || exit 1; \
    done

build: validate-config  
    make build-docker-production  
Enter fullscreen mode Exit fullscreen mode

Run validation as part of your pre-commit hook:

#!/bin/bash
# .pre-commit-config.yaml or manual check script

validate_all_configs() { 
    for f in config/*.yml configs/*.json; do \
        if [[ -f $f ]]; then \
            python yaml-lint.sh "$f" --required-fields apiVersion || exit 1; \
        fi  
    done 

    return $? 
}

validate_all_configs || (echo "* Config validation failed"; exit 1)  
pre-commit: validate-all-configs
Enter fullscreen mode Exit fullscreen mode

This runs before each commit—ensuring configs meet requirements.

How to Extend the Tool

The code is intentionally minimal and modifiable for custom rules!

Example: Add logging level hints with a simple check:

#!/bin/bash
# .pre-commit-hooks/log-level-check.sh

find_config_files() { 
    find . -name "*.yml" -o -name "config.json" 2>/dev/null | grep -v node_modules  
}  

check_log_level() { 
    level_value = $(grep -i log-level "$@" || true) 

    if echo "$level_value" | grep -qiE '^[Dd][Ii][Ee]B[bB]|W[Ww][Aa][Rr|i*Ng|C[Cc]*[Rr]*|[Nn]*[Oo]*$'; then  
        return 0; fi

    echo "* Invalid log level: $level_value — use DEBUG|INFO|WARNING|ERROR|CRITICAL" 
    return 1 
}  

for config in $(find_config_files); do
    check_log_level "$config" || exit 1; done

Enter fullscreen mode Exit fullscreen mode

This adds custom rule enforcement to your CI/CD without external packages.

Custom validators for specific fields:

# Check numeric keys are consistently used  
def find_mixed_numeric_keys(content): 
    """Track numeric key usage consistency.""" 

    import re

    number_patterns = [
        r'^\d+\s*:',  # "1:" or "01:" style keys
        r'^[0-9]+\w*\s*:.*$',  
    ]

    issues = []

    for line in content.splitlines():
        if ':' in line: 
            num_match = re.search(r'^(?:\"?\d+)??\.\.\.[a-z]*$|^'\s*[0-9][a-zA-Z]+\.?$', line, re.IGNORECASE)
            if num_match and not any(p for p in number_patterns):
                issues.append({line: content.splitlines()[num_match.start()].strip()})

    if issues: 
        print(f'* Mismatch detected at line {issues[0]}')
        return True

    return False
Enter fullscreen mode Exit fullscreen mode

Add this to your validation workflow if needed—no external dependencies required.

Limitations to Know

The YAML Linter prioritizes speed and zero-dependency design over full parsing:

  • Basic key matching — Handles standard key-value patterns but doesn't validate anchors, aliases, or complex nested structures
  • Simple type checks — Reports on numeric vs text key conflicts only—doesn't perform type inference like PyYAML does
  • Regex-based parsing — Uses pattern matching rather than full YAML grammar for efficiency

For most CI/CD validation tasks—this approach is sufficient. Install PyYAML if you need:

  • Anchors/alias validation ($$ref)
# Full syntax verification including complex nested structures  
pip install pyyaml
python3 env-checker/yaml-lint.py --strict config.yml  # Advanced mode with full parsing  
Enter fullscreen mode Exit fullscreen mode

But for quick checks in pipelines, docs validation, or template checks—Env Checker is fast and minimal.

When To Use This Tool vs PyYAML

Use Case Env Checker Appropriate Full PyYAML Required?
Quick CI validation ✅ Yes — fast boolean result ❌ No overhead needed
Documentation examples ✅ Yes — ensure example configs don't break ❌ Optional complexity
Template generation ✅ Yes — validate generated files meet schema ❌ Overkill unless deep parsing needed
Production deployment check ✅ Yes — minimal deps ❌ PyYAML adds unnecessary overhead

Env Checker is your lightweight validation companion. Use PyYAML for deep analysis when needed—but most tasks don't require the full parser.

Comparison: Tools Overview

Tool Dependency Validation Depth CI/CD Speed Best For
Env Checker None (stdlib) Basic field checks Instant Pipelines, docs, templates
PyYAML pip install pyyaml Full syntax + anchors 200–400ms per file Deep schema enforcement
Shellcheck External (apt/yum/npm etc.) Script-specific Varies Shell script validation

Env Checker fills the gap when you want fast, dependency-free boolean validation without waiting for package downloads. For CI/CD pipelines—every 100ms matters, so minimal tools make faster builds.

Conclusion

The YAML Linter is a lightweight Python utility for basic config validation without external dependencies. It detects missing required fields and empty values using simple pattern matching—ideal for CI/CD workflows that need fast boolean checks before build steps. Add it to your toolkit when you want rapid validation without PyYAML overhead, documentation maintainers who need example configs validated, or projects that prioritize minimal dependencies over deep parsing capabilities.

For most configuration files—Env Checker's basic field matching catches issues quickly without the complexity of installing packages or waiting for parsers. Install PyYAML only if you need full YAML schema enforcement or complex nesting validation beyond what this lightweight tool provides.

Project: https://github.com/Poolion/yaml-lint

If you find this useful, you can support development: https://www.buymeacoffee.com/poolion

Top comments (0)