Config Linter: JSON/YAML Config Validation in Pure Python
Configuration files are deceptively fragile. A trailing comma, an undefined variable, or an empty nested object can break your deployment before you even deploy the app. The Config Linter is a lightweight CLI tool written in pure Python that checks for these common issues without requiring any external dependencies.
What Problem Does It Solve?
When managing configuration files for applications, CI/CD pipelines, or infrastructure as code, developers frequently encounter:
- Trailing commas — syntax errors that break YAML parsers but slip through simple JSON validators
-
Undefined variables like
${DATABASE_HOST}referenced in configs but never set in environment - Empty sections in nested config structures that cause silent failures
- Tedious manual inspection of large configuration files before deployment
Config Linter provides automated validation for these problems.
Installation
# Clone and use directly
git clone https://github.com/Poolion/config-linter.git
cd config-linter
chmod +x config-lint.py
python config-lint.py your-config.yaml
# Add to PATH (Linux/macOS)
cp config-lint.py /usr/local/bin/
Now you can run it from anywhere:
config-lint.py app.yaml --list-keys
Usage Examples
Basic Validation
python config-lint.py database.json
Output:
[OK] Loaded as JSON
Keys found (8):
- database.host
- database.port
- database.username
- database.password
[...]
===============================================================
Config validates successfully - no issues found!
With Undefined Variable Warnings
python config-lint.py myapp.yaml
Output includes warnings:
[OK] Loaded as JSON
- WARN: Possible undefined variable: ${SOME_VAR}
[*] Trailing comma before }} at position 145
List All Configuration Keys
python config-lint.py config.yaml --list-keys
# Shows every key path in the configuration
Keys found (24):
- server.host
- server.port
- server.debug
- database.primary
[...]
Reading from Stdin for Pipelines
echo "name: myapp
version: 1.0" | python config-lint.py -
[OK] Loaded as INI/Text/Key-Value
- INFO: Empty configuration object
How It Works
The tool handles multiple configuration formats with a single parser approach:
Detection Order
-
JSON/YAML detection — Files ending in
.json,.yml, or.yamlcontaining valid JSON syntax are parsed as JSON. Most YAML configs without special anchors/aliases are actually valid JSON anyway. -
Key=Value parsing — Treated as INI-style with sections
[section]andkey = valuepairs - Type conversion — Attempts to convert string values to proper types (integers, floats, booleans)
Validation Categories
| Check Type | What It Detects | Example |
|---|---|---|
| Undefined variables |
${VAR} or %KEY% patterns |
${MISSING_VAR} with no definition |
| Trailing commas | Commas before ] or }
|
"a": 1, followed by } breaks YAML |
| Empty sections | Nested objects with zero keys | { "empty_section": {} } |
Command Line Interface
python config-lint.py [options] <config_file>
Options:
-c CONFIG Path to configuration file (or "-" for stdin)
-l, --list-keys List all keys found in the configuration
-h, --help Show help message and examples
Why Pure Python?
This tool requires no external dependencies—only what comes bundled with Python:
-
argparse— Command-line argument parsing -
json— JSON file loading -
re— Regular expression pattern matching
No pyyaml, no ruamel.yaml, no schema validators. Just standard library, making it:
- Lightweight — No virtual environment needed
- Portable — Works on any Python installation
- Script-friendly — Easy to import into other Python tools
Configuration Formats Supported
JSON Example
{
"server": {
"host": "${HOST}",
"port": 8080, // Valid - port is a number
"debug": true
}
}
YAML Example (YAML-like syntax)
[server]
host: ${HOME_DIR}
port: 3000
[database]
name: myapp_db
# This triggers undefined variable warning for %DB%
connection_url: postgres://%DB%/myapp
INI-style Example
[general]
version = 1.2.0
debug_mode = true
[paths]
logs_dir = /var/log/app
temp_dir = ${TEMP_DIR}
When to Use This Tool
Ideal scenarios for config-lint:
- CI/CD pipelines — Validate configs before deployment
- Configuration templates — Ensure example configs have no undefined variables
- DevOps workflows — Lint Kubernetes manifests alongside other files
- Documentation — Validate example configuration files in docs
- Automated testing — Include config validation in test suites
Integration Examples
GitHub Actions Workflow
# .github/workflows/lint-config.yml
name: Validate Configuration Files
on:
pull_request:
paths:
- 'config/**'
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: python config-lint.py app.yaml --list-keys
Dockerfile Integration
# Validate configs at build time
RUN python config-lint.py /etc/app/config.yaml || exit 1
COPY config/ /etc/app/conf.d/
CMD ["python-config", "config-lint.py" "/etc/app/conf.d/*.yaml"]
Limitations to Know
As a lightweight tool, Config Linter doesn't attempt to be comprehensive:
- No schema validation — Cannot check that values match expected types or ranges (use JSON Schema or similar for this)
- Limited YAML parsing — True YAML with anchors, aliases, or complex multi-line strings requires PyYAML. This parses key=value style content.
- Shallow nested checks — Recursively checks only 3 levels deep (enough for most configs)
- No semantic analysis — Cannot detect if a value makes sense in context
These are intentional trade-offs for simplicity and zero-dependency requirements.
Extending the Tool
The code is intentionally simple to extend:
# Add a new check after find_empty()
def check_duplicates(config):
"""Find duplicate keys in nested dicts."""
issues = []
def traverse(d, path=''):
seen = {}
for k, v in d.items():
key_id = f"{path}/{k}" if path else str(k)
if isinstance(v, dict):
traverse(v, key_id)
elif v in seen:
issues.append(f"Duplicate value at {key_id}: {seen[v]} != {key}")
else:
seen[v] = key_id
traverse(config)
return issues
# Add to main() validation loop
if not isinstance(config, dict):
... # existing checks
else:
duplicates = check_duplicates(config)
if duplicates:
for d in duplicates:
print(f'- WARN: Duplicate configuration: {d}')
Conclusion
Config Linter fills a practical gap in developer tooling. It's not trying to replace comprehensive validation suites, but provides targeted checks for the most common configuration file mistakes that break deployments and cause confusion.
For minimal overhead—zero dependencies plus about 100 lines of code—it catches issues that manual inspection often misses. Add it to your toolkit when working with configuration-heavy projects.
Project: https://github.com/Poolion/config-linter
If you find this useful, you can support development: https://www.buymeacoffee.com/poolion
Top comments (0)