mirror of
https://github.com/ivuorinen/actions.git
synced 2026-03-02 16:53:57 +00:00
* fix(deps): replace step-security/retry with nick-fields/retry * chore(deps): update github action sha pins via pinact * refactor: remove common-retry references from tests and validators * chore: simplify description fallback and update action count * docs: remove hardcoded test counts from memory and docs Replace exact "769 tests" references with qualitative language so these files don't go stale as test count grows.
9.5 KiB
9.5 KiB
Validate Inputs - Modular Validation System
A comprehensive, modular validation system for GitHub Actions inputs with automatic convention-based detection, custom validator support, and extensive testing capabilities.
Features
- 🔍 Automatic Validation - Convention-based input detection
- 🧩 Modular Architecture - 9 specialized validators
- 🛡️ Security First - Injection and traversal protection
- 🎯 Custom Validators - Action-specific validation logic
- 🧪 Test Generation - Automatic test scaffolding
- 📊 Performance Tools - Benchmarking and profiling
- 🐛 Debug Utilities - Troubleshooting helpers
Quick Start
Using in Your Action
# In your action.yml
runs:
using: composite
steps:
- name: Validate inputs
uses: ./validate-inputs
with:
action-type: ${{ github.action }}
Automatic Validation
Name your inputs following conventions for automatic validation:
inputs:
github-token: # Automatically validates token format
description: GitHub token
default: ${{ github.token }}
node-version: # Automatically validates version format
description: Node.js version
default: '18'
dry-run: # Automatically validates boolean
description: Run without making changes
default: 'false'
Architecture
validate-inputs/
├── validators/ # Core validator modules
│ ├── base.py # Abstract base class
│ ├── registry.py # Dynamic validator discovery
│ ├── conventions.py # Pattern-based matching
│ ├── boolean.py # Boolean validation
│ ├── version.py # Version validation (SemVer/CalVer)
│ ├── token.py # Token validation
│ ├── numeric.py # Numeric range validation
│ ├── file.py # File path validation
│ ├── network.py # URL/email validation
│ ├── docker.py # Docker-specific validation
│ ├── security.py # Security pattern detection
│ └── codeql.py # CodeQL validation
├── scripts/
│ ├── update-validators.py # Generate validation rules
│ ├── generate-tests.py # Generate test files
│ ├── debug-validator.py # Debug validation issues
│ └── benchmark-validator.py # Performance testing
├── docs/
│ ├── API.md # Complete API reference
│ ├── DEVELOPER_GUIDE.md # Adding new validators
│ └── ACTION_MAINTAINER.md # Using validation
├── rules/ # Auto-generated validation rules
├── tests/ # Comprehensive test suite
└── validator.py # Main entry point
Core Validators
Version Validator
- SemVer:
1.2.3,2.0.0-beta.1 - CalVer:
2024.3.15,24.03 - Flexible: Accepts both formats
Token Validator
- GitHub:
ghp_*,github_pat_*,${{ secrets.GITHUB_TOKEN }} - NPM: UUID format
- Docker: Any non-empty value
Boolean Validator
- Accepted:
true,false(case-insensitive) - Rejected:
yes,no,1,0
Numeric Validator
- Ranges:
0-100,1-10,1-128 - Types: Integers only by default
File Validator
- Security: No path traversal (
../) - Paths: Relative paths only
- Extensions: Validates common file types
Network Validator
- URLs: HTTP/HTTPS validation
- Emails: RFC-compliant
- Hostnames: Valid DNS names
- IPs: IPv4 and IPv6
Docker Validator
- Images: Lowercase, valid characters
- Tags: Alphanumeric with
-,_,. - Platforms:
linux/amd64,linux/arm64, etc. - Registries: Known registries validation
Security Validator
- Injection: Command, SQL, script detection
- Traversal: Path traversal prevention
- Secrets: API key and password detection
Convention Patterns
The system automatically detects validation types based on input names:
| Pattern | Validator | Examples |
|---|---|---|
*-token |
TokenValidator | github-token, api-token |
*-version |
VersionValidator | node-version, go-version |
dry-run, debug |
BooleanValidator | dry-run, verbose |
max-*, *-limit |
NumericValidator | max-retries, rate-limit |
*-file, *-path |
FileValidator | config-file, output-path |
*-url, webhook-* |
NetworkValidator | api-url, webhook-endpoint |
dockerfile |
FileValidator | dockerfile |
image-*, tag |
DockerValidator | image-name, tag |
Custom Validators
Create action-specific validation logic:
# my-action/CustomValidator.py
from pathlib import Path
import sys
validate_inputs_path = Path(__file__).parent.parent / "validate-inputs"
sys.path.insert(0, str(validate_inputs_path))
from validators.base import BaseValidator
class CustomValidator(BaseValidator):
def validate_inputs(self, inputs: dict[str, str]) -> bool:
# Custom validation logic
return True
def get_required_inputs(self) -> list[str]:
return ["required-field"]
Development Tools
Generate Validation Rules
# Update all action rules
make update-validators
# Update specific action
python3 validate-inputs/scripts/update-validators.py --action my-action
Generate Tests
# Generate missing tests
make generate-tests
# Preview changes
make generate-tests-dry
Debug Validation
# Test specific inputs
./validate-inputs/scripts/debug-validator.py \
--action docker-build \
--input "image-name=myapp" \
--input "tag=v1.0.0"
# Test input matching
./validate-inputs/scripts/debug-validator.py \
--test-matching github-token node-version dry-run
# List available validators
./validate-inputs/scripts/debug-validator.py --list-validators
Performance Testing
# Benchmark specific action
./validate-inputs/scripts/benchmark-validator.py \
--action docker-build \
--inputs 20 \
--iterations 1000
# Compare validators
./validate-inputs/scripts/benchmark-validator.py --compare
# Profile for bottlenecks
./validate-inputs/scripts/benchmark-validator.py \
--profile docker-build
Testing
# Run all tests
make test
# Run Python tests only
make test-python
# Run specific test
uv run pytest validate-inputs/tests/test_version_validator.py
# Run with coverage
make test-python-coverage
Documentation
- API Reference - Complete validator API documentation
- Developer Guide - Adding new validators
- Action Maintainer Guide - Using validation in actions
Best Practices
- Use Conventions - Name inputs to trigger automatic validation
- Allow Expressions - Always support
${{ }}GitHub expressions - Clear Errors - Provide actionable error messages
- Test Thoroughly - Test valid, invalid, and edge cases
- Document Rules - Document validation in action README
- Performance - Keep validation fast (< 10ms typical)
Examples
Complete Action with Validation
name: Deploy Application
description: Deploy application with validation
inputs:
environment:
description: Deployment environment
required: true
github-token:
description: GitHub token for API access
default: ${{ github.token }}
node-version:
description: Node.js version
default: '18'
dry-run:
description: Preview changes without deploying
default: 'false'
runs:
using: composite
steps:
# Validate all inputs
- uses: ./validate-inputs
with:
action-type: deploy-application
# Setup Node.js
- uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
# Deploy application
- run: |
if [[ "${{ inputs.dry-run }}" == "true" ]]; then
echo "DRY RUN: Would deploy to ${{ inputs.environment }}"
else
./deploy.sh --env "${{ inputs.environment }}"
fi
shell: bash
Custom Validator Example
# deploy-application/CustomValidator.py
class CustomValidator(BaseValidator):
def validate_inputs(self, inputs: dict[str, str]) -> bool:
valid = True
# Validate environment
if inputs.get("environment"):
valid_envs = ["dev", "staging", "prod"]
if inputs["environment"] not in valid_envs:
self.add_error(
f"Invalid environment: {inputs['environment']}. "
f"Must be one of: {', '.join(valid_envs)}"
)
valid = False
# Production requires explicit token
if inputs.get("environment") == "prod":
if not inputs.get("github-token"):
self.add_error("Production deployments require github-token")
valid = False
return valid
def get_required_inputs(self) -> list[str]:
return ["environment"]
Quality Metrics
- Test Coverage: 100%
- Validators: 9 specialized + unlimited custom
- Performance: < 10ms typical validation time
- Zero Dependencies: Uses only Python stdlib + PyYAML
- Production Ready: Zero defects policy
Contributing
- Create new validator in
validators/directory - Add convention patterns to
conventions.py - Write comprehensive tests
- Update documentation
- Run
make allto verify
License
Part of ivuorinen/actions - see repository license.