From b426d43de0a308821bf1d00b6a712eb767485558 Mon Sep 17 00:00:00 2001 From: Brian Bjarke Jensen Date: Fri, 19 Sep 2025 19:38:10 +0200 Subject: [PATCH] added tests --- .gitea/workflows/test-templates.yml | 460 ++++++++++++ TESTING.md | 389 ++++++++++ scripts/validate-templates.sh | 709 ++++++++++++++++++ .../.gitea/workflows/test-templates.yml | 22 + test-projects/python-basic/README.md | 30 + test-projects/python-basic/pyproject.toml | 82 ++ .../python-basic/src/test_package/__init__.py | 8 + .../src/test_package/calculator.py | 31 + .../python-basic/src/test_package/utils.py | 24 + .../python-basic/tests/test_calculator.py | 47 ++ .../python-basic/tests/test_utils.py | 41 + .../.gitea/workflows/test-formatting.yml | 16 + test-projects/web-basic/.prettierrc.json | 7 + test-projects/web-basic/config.yml | 20 + test-projects/web-basic/index.js | 15 + test-projects/web-basic/package.json | 18 + 16 files changed, 1919 insertions(+) create mode 100644 .gitea/workflows/test-templates.yml create mode 100644 TESTING.md create mode 100755 scripts/validate-templates.sh create mode 100644 test-projects/python-basic/.gitea/workflows/test-templates.yml create mode 100644 test-projects/python-basic/README.md create mode 100644 test-projects/python-basic/pyproject.toml create mode 100644 test-projects/python-basic/src/test_package/__init__.py create mode 100644 test-projects/python-basic/src/test_package/calculator.py create mode 100644 test-projects/python-basic/src/test_package/utils.py create mode 100644 test-projects/python-basic/tests/test_calculator.py create mode 100644 test-projects/python-basic/tests/test_utils.py create mode 100644 test-projects/web-basic/.gitea/workflows/test-formatting.yml create mode 100644 test-projects/web-basic/.prettierrc.json create mode 100644 test-projects/web-basic/config.yml create mode 100644 test-projects/web-basic/index.js create mode 100644 test-projects/web-basic/package.json diff --git a/.gitea/workflows/test-templates.yml b/.gitea/workflows/test-templates.yml new file mode 100644 index 0000000..e35d2ec --- /dev/null +++ b/.gitea/workflows/test-templates.yml @@ -0,0 +1,460 @@ +name: Test CI Templates + +on: + push: + branches: [main] + pull_request: + schedule: + - cron: '0 2 * * *' # Run daily at 2 AM + workflow_dispatch: # Manual trigger + +jobs: + validate-syntax: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install yamllint + run: pip install yamllint + + - name: Validate YAML syntax + run: | + echo "Validating workflow YAML syntax..." + find .gitea/workflows -name "*.yml" -exec yamllint {} \; + + - name: Check workflow structure + run: | + echo "Checking workflow structure..." + for file in .gitea/workflows/**/*.yml; do + echo "Checking $file..." + if ! grep -q "workflow_call" "$file"; then + echo "Error: $file is missing workflow_call trigger" + exit 1 + fi + if ! grep -q "inputs:" "$file"; then + echo "Warning: $file has no inputs defined" + fi + done + + test-python-code-quality: + runs-on: ubuntu-latest + needs: validate-syntax + strategy: + matrix: + python-version: ["3.10", "3.11", "3.12"] + include: + - test-type: "basic" + skip-mypy: false + skip-pyupgrade: false + - test-type: "skip-mypy" + skip-mypy: true + skip-pyupgrade: false + - test-type: "skip-pyupgrade" + skip-mypy: false + skip-pyupgrade: true + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Create test Python project + run: | + mkdir -p test-project/src/test_package + cd test-project + + # Create pyproject.toml + cat > pyproject.toml << 'EOF' + [build-system] + requires = ["hatchling"] + build-backend = "hatchling.build" + + [project] + name = "test-package" + version = "0.1.0" + description = "Test package for CI templates" + dependencies = [] + + [project.optional-dependencies] + dev = [ + "mypy>=1.0.0", + "ruff>=0.1.0", + "pyupgrade>=3.0.0", + ] + + [tool.ruff] + line-length = 88 + target-version = "py310" + + [tool.mypy] + python_version = "3.10" + warn_return_any = true + warn_unused_configs = true + EOF + + # Create source code + cat > src/test_package/__init__.py << 'EOF' + """Test package for CI template validation.""" + __version__ = "0.1.0" + + def hello_world() -> str: + """Return a greeting message.""" + return "Hello, World!" + EOF + + cat > src/test_package/main.py << 'EOF' + """Main module for test package.""" + from typing import List + + def process_items(items: List[str]) -> List[str]: + """Process a list of items.""" + return [item.upper() for item in items] + + if __name__ == "__main__": + test_items = ["hello", "world"] + result = process_items(test_items) + print(result) + EOF + + - name: Test Python code quality workflow + uses: ./ + with: + python-version: ${{ matrix.python-version }} + working-directory: "./test-project" + skip-mypy: ${{ matrix.skip-mypy }} + skip-pyupgrade: ${{ matrix.skip-pyupgrade }} + uv-extras: "dev" + + test-python-pytest: + runs-on: ubuntu-latest + needs: validate-syntax + strategy: + matrix: + python-version: ["3.11", "3.12"] + coverage: [true, false] + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Create test Python project with tests + run: | + mkdir -p test-pytest-project/src/test_package tests + cd test-pytest-project + + # Create pyproject.toml + cat > pyproject.toml << 'EOF' + [build-system] + requires = ["hatchling"] + build-backend = "hatchling.build" + + [project] + name = "test-package" + version = "0.1.0" + description = "Test package for pytest CI template" + dependencies = [] + + [project.optional-dependencies] + test = [ + "pytest>=7.0.0", + "pytest-cov>=4.0.0", + ] + + [tool.pytest.ini_options] + testpaths = ["tests"] + python_files = ["test_*.py"] + addopts = ["--strict-markers", "--strict-config"] + EOF + + # Create source code + cat > src/test_package/__init__.py << 'EOF' + """Test package for pytest template validation.""" + __version__ = "0.1.0" + EOF + + cat > src/test_package/calculator.py << 'EOF' + """Simple calculator for testing.""" + + def add(a: float, b: float) -> float: + """Add two numbers.""" + return a + b + + def multiply(a: float, b: float) -> float: + """Multiply two numbers.""" + return a * b + + def divide(a: float, b: float) -> float: + """Divide two numbers.""" + if b == 0: + raise ValueError("Cannot divide by zero") + return a / b + EOF + + # Create tests + cat > tests/test_calculator.py << 'EOF' + """Tests for calculator module.""" + import pytest + from test_package.calculator import add, multiply, divide + + def test_add(): + assert add(2, 3) == 5 + assert add(-1, 1) == 0 + assert add(0.1, 0.2) == pytest.approx(0.3) + + def test_multiply(): + assert multiply(2, 3) == 6 + assert multiply(-2, 3) == -6 + assert multiply(0, 5) == 0 + + def test_divide(): + assert divide(6, 2) == 3 + assert divide(5, 2) == 2.5 + + def test_divide_by_zero(): + with pytest.raises(ValueError, match="Cannot divide by zero"): + divide(5, 0) + EOF + + - name: Test pytest workflow + uses: ./ + with: + python-version: ${{ matrix.python-version }} + working-directory: "./test-pytest-project" + coverage-package: ${{ matrix.coverage && 'test_package' || '' }} + pytest-args: "--verbose" + uv-extras: "test" + + test-web-formatting: + runs-on: ubuntu-latest + needs: validate-syntax + strategy: + matrix: + node-version: ["18", "20"] + check-yaml: [true, false] + check-json: [true, false] + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Create test web project + run: | + mkdir -p test-web-project/src/components + cd test-web-project + + # Create package.json + cat > package.json << 'EOF' + { + "name": "test-web-project", + "version": "1.0.0", + "description": "Test project for web formatting template" + } + EOF + + # Create JavaScript files + cat > src/index.js << 'EOF' + // Test JavaScript file for formatting + const greeting = "Hello, World!"; + + function greet(name) { + return `Hello, ${name}!`; + } + + export { greet }; + EOF + + cat > src/components/Button.js << 'EOF' + // Button component + export default function Button({ children, onClick }) { + return ( + + ); + } + EOF + + # Create JSON file + cat > config.json << 'EOF' + { + "name": "test-config", + "version": "1.0.0", + "settings": { + "debug": true, + "timeout": 5000 + } + } + EOF + + # Create YAML file + cat > config.yml << 'EOF' + name: test-config + version: 1.0.0 + settings: + debug: true + timeout: 5000 + features: + - feature1 + - feature2 + EOF + + # Create Prettier config + cat > .prettierrc.json << 'EOF' + { + "semi": true, + "trailingComma": "es5", + "singleQuote": true, + "printWidth": 80, + "tabWidth": 2 + } + EOF + + - name: Test web formatting workflow + uses: ./ + with: + node-version: ${{ matrix.node-version }} + working-directory: "./test-web-project" + check-yaml: ${{ matrix.check-yaml }} + check-json: ${{ matrix.check-json }} + + test-monorepo: + runs-on: ubuntu-latest + needs: validate-syntax + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Create monorepo structure + run: | + mkdir -p monorepo-test/{backend,frontend} + + # Backend Python project + cd monorepo-test/backend + cat > pyproject.toml << 'EOF' + [build-system] + requires = ["hatchling"] + build-backend = "hatchling.build" + + [project] + name = "backend-service" + version = "0.1.0" + dependencies = [] + + [project.optional-dependencies] + dev = ["ruff>=0.1.0", "mypy>=1.0.0"] + EOF + + mkdir -p src/backend_service + cat > src/backend_service/__init__.py << 'EOF' + """Backend service package.""" + __version__ = "0.1.0" + EOF + + # Frontend project + cd ../frontend + cat > package.json << 'EOF' + { + "name": "frontend-app", + "version": "1.0.0" + } + EOF + + mkdir -p src + cat > src/App.js << 'EOF' + export default function App() { + return
Hello from frontend!
; + } + EOF + + - name: Test backend code quality + uses: ./ + with: + working-directory: "./monorepo-test/backend" + uv-extras: "dev" + + - name: Test frontend formatting + uses: ./ + with: + working-directory: "./monorepo-test/frontend" + + test-edge-cases: + runs-on: ubuntu-latest + needs: validate-syntax + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Test minimal Python project + run: | + mkdir minimal-python + cd minimal-python + cat > pyproject.toml << 'EOF' + [build-system] + requires = ["hatchling"] + build-backend = "hatchling.build" + + [project] + name = "minimal" + version = "0.1.0" + dependencies = [] + EOF + + mkdir -p src/minimal + cat > src/minimal/__init__.py << 'EOF' + pass + EOF + + - name: Test with minimal configuration + uses: ./ + with: + working-directory: "./minimal-python" + skip-mypy: true + skip-pyupgrade: true + + test-results: + runs-on: ubuntu-latest + needs: [test-python-code-quality, test-python-pytest, test-web-formatting, test-monorepo, test-edge-cases] + if: always() + + steps: + - name: Report test results + run: | + echo "## CI Template Test Results" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + if [ "${{ needs.test-python-code-quality.result }}" == "success" ]; then + echo "✅ Python Code Quality: PASSED" >> $GITHUB_STEP_SUMMARY + else + echo "❌ Python Code Quality: FAILED" >> $GITHUB_STEP_SUMMARY + fi + + if [ "${{ needs.test-python-pytest.result }}" == "success" ]; then + echo "✅ Python Pytest: PASSED" >> $GITHUB_STEP_SUMMARY + else + echo "❌ Python Pytest: FAILED" >> $GITHUB_STEP_SUMMARY + fi + + if [ "${{ needs.test-web-formatting.result }}" == "success" ]; then + echo "✅ Web Formatting: PASSED" >> $GITHUB_STEP_SUMMARY + else + echo "❌ Web Formatting: FAILED" >> $GITHUB_STEP_SUMMARY + fi + + if [ "${{ needs.test-monorepo.result }}" == "success" ]; then + echo "✅ Monorepo Support: PASSED" >> $GITHUB_STEP_SUMMARY + else + echo "❌ Monorepo Support: FAILED" >> $GITHUB_STEP_SUMMARY + fi + + if [ "${{ needs.test-edge-cases.result }}" == "success" ]; then + echo "✅ Edge Cases: PASSED" >> $GITHUB_STEP_SUMMARY + else + echo "❌ Edge Cases: FAILED" >> $GITHUB_STEP_SUMMARY + fi \ No newline at end of file diff --git a/TESTING.md b/TESTING.md new file mode 100644 index 0000000..a8428ad --- /dev/null +++ b/TESTING.md @@ -0,0 +1,389 @@ +# CI Template Testing Guide + +This document explains how to test and validate the CI workflow templates in this repository. + +## Overview + +The testing infrastructure includes: + +- **Automated validation workflow** (`.gitea/workflows/validate-templates.yml`) +- **Manual validation script** (`scripts/validate-templates.sh`) +- **Test projects** (`test-projects/`) +- **Matrix testing** across multiple configurations + +## Quick Start + +### 1. Run Local Validation + +```bash +# Make the script executable +chmod +x scripts/validate-templates.sh + +# Validate all workflows +./scripts/validate-templates.sh + +# List available templates +./scripts/validate-templates.sh list + +# Show help +./scripts/validate-templates.sh help +``` + +### 2. Test with Sample Projects + +```bash +# Navigate to a test project +cd test-projects/python-basic + +# Install dependencies (if using UV) +uv sync --all-extras + +# Run local tests +uv run pytest +uv run mypy . +uv run ruff check . +``` + +## Testing Infrastructure + +### 1. Validation Workflow (`.gitea/workflows/validate-templates.yml`) + +Automatically runs on every push and pull request: + +- **Matrix testing** across Python versions (3.10, 3.11, 3.12) +- **Template validation** for all workflow files +- **Integration testing** with sample projects +- **Documentation checks** + +**Triggered by:** + +- Push to `main` branch +- Pull requests +- Manual dispatch +- Daily schedule (2 AM UTC) + +**Test matrix:** + +```yaml +strategy: + matrix: + python-version: ["3.10", "3.11", "3.12"] + template: ["code-quality", "pytest"] + include: + - template: "formatting" + python-version: "3.12" # Only test once for web template +``` + +### 2. Validation Script (`scripts/validate-templates.sh`) + +**Features:** + +- YAML syntax validation with `yamllint` +- Workflow structure validation with `yq` +- Reusable workflow detection +- Input parameter validation +- Documentation checks +- Common issue detection + +**Usage:** + +```bash +# Basic validation +./scripts/validate-templates.sh + +# List all templates +./scripts/validate-templates.sh list + +# Help +./scripts/validate-templates.sh help +``` + +**Dependencies:** + +- `yamllint` (optional): `pip install yamllint` +- `yq` (optional): `brew install yq` (macOS) or `apt install yq` (Ubuntu) + +**Note:** The script works without dependencies but provides more detailed validation when they're available. + +### 3. Test Projects + +#### Python Basic (`test-projects/python-basic/`) + +**Purpose:** Test Python-specific templates + +**Structure:** + +```text +python-basic/ +├── .gitea/workflows/ +│ └── test-templates.yml # Tests both code-quality and pytest templates +├── src/test_package/ +│ ├── __init__.py +│ ├── calculator.py # Sample module with type hints +│ └── utils.py # Utility functions +├── tests/ +│ ├── test_calculator.py # Pytest tests +│ └── test_utils.py # More tests +├── pyproject.toml # UV-compatible project config +└── README.md +``` + +**Features:** + +- Type hints for mypy testing +- Pytest with coverage +- Ruff-compatible code style +- UV package management + +#### Web Basic (`test-projects/web-basic/`) + +**Purpose:** Test web formatting template + +**Structure:** + +```text +web-basic/ +├── .gitea/workflows/ +│ └── test-formatting.yml # Tests formatting template +├── package.json # Node.js project +├── index.js # JavaScript file +├── config.yml # YAML file for validation +├── .prettierrc.json # Prettier configuration +└── README.md +``` + +**Features:** + +- Prettier configuration +- YAML validation +- JSON validation +- Node.js setup + +## Testing Workflow Templates + +### 1. Local Testing with Act + +Install and use Act to test workflows locally: + +```bash +# Install Act +brew install act # macOS +# or +curl https://raw.githubusercontent.com/nektos/act/master/install.sh | sudo bash + +# Test a specific workflow +act -W .gitea/workflows/python/code-quality.yml + +# Test with inputs +act workflow_call -W .gitea/workflows/python/code-quality.yml \ + --input python-version=3.11 \ + --input working-directory=./test-projects/python-basic +``` + +### 2. Branch Testing + +Test changes on feature branches: + +```bash +# Create feature branch +git checkout -b feature/improve-python-template + +# Make changes to templates +# ... + +# Update test project to use feature branch +# In test-projects/python-basic/.gitea/workflows/test-templates.yml: +# uses: gitea.gt-proj.com/brian/CI-templates/.gitea/workflows/python/code-quality.yml@feature/improve-python-template + +# Push and test +git push origin feature/improve-python-template +``` + +### 3. Matrix Testing + +Test multiple configurations: + +```yaml +# Example matrix test +strategy: + matrix: + python-version: ["3.10", "3.11", "3.12"] + os: ["ubuntu-latest", "ubuntu-20.04"] + working-directory: [".", "./src"] +``` + +## Validation Checklist + +### Workflow File Validation + +- [ ] Valid YAML syntax +- [ ] Contains required fields: `name`, `on`, `jobs` +- [ ] Uses `workflow_call` trigger for reusable workflows +- [ ] All inputs have descriptions and types +- [ ] No hardcoded values that should be inputs +- [ ] Proper defaults for optional inputs + +### Template Testing + +- [ ] All input combinations work correctly +- [ ] Error handling for invalid inputs +- [ ] Backwards compatibility maintained +- [ ] Documentation updated +- [ ] Test projects updated + +### Integration Testing + +- [ ] Works with real projects +- [ ] Performance is acceptable +- [ ] No conflicts between templates +- [ ] Secrets handling works correctly + +## Common Issues and Solutions + +### 1. YAML Syntax Errors + +**Problem:** Invalid YAML syntax + +```bash +Error: YAML syntax validation failed +``` + +**Solution:** + +- Use `yamllint` to check syntax +- Validate indentation (use spaces, not tabs) +- Check for missing quotes around strings with special characters + +### 2. Missing Required Fields + +**Problem:** Workflow missing required fields + +```bash +Error: Missing required field 'on' in workflow.yml +``` + +**Solution:** + +- Ensure all workflows have `name`, `on`, and `jobs` fields +- For reusable workflows, use `workflow_call` trigger + +### 3. Input Validation Issues + +**Problem:** Missing input descriptions or types + +```bash +Warning: Input 'python-version' missing description +``` + +**Solution:** + +```yaml +inputs: + python-version: + description: 'Python version to use (e.g., "3.12")' + required: false + type: string + default: "3.12" +``` + +### 4. Test Project Issues + +**Problem:** Tests fail in sample projects + +```bash +Error: Module not found +``` + +**Solutions:** + +- Check `pyproject.toml` configuration +- Verify package structure +- Ensure dependencies are correctly specified +- Check import paths + +## CI/CD Best Practices + +### 1. Version Control + +- Use semantic versioning for releases +- Tag stable versions: `v1.0.0`, `v1.1.0`, etc. +- Reference specific versions in production: `@v1.0.0` +- Use `@main` for development/testing + +### 2. Backwards Compatibility + +- Test with previous versions +- Provide migration guides for breaking changes +- Deprecate features gracefully +- Maintain changelog + +### 3. Security + +- Validate all inputs +- Use secrets for sensitive data +- Limit permissions to minimum required +- Regular security updates + +### 4. Documentation + +- Keep README files updated +- Document all input parameters +- Provide usage examples +- Include troubleshooting guides + +## Troubleshooting + +### Validation Script Issues + +```bash +# Check dependencies +./scripts/validate-templates.sh + +# Manual YAML validation +yamllint .gitea/workflows/python/code-quality.yml + +# Manual structure check +yq eval '.on | has("workflow_call")' .gitea/workflows/python/code-quality.yml +``` + +### Test Project Issues + +```bash +# Python projects +cd test-projects/python-basic +uv sync --all-extras +uv run pytest --verbose + +# Web projects +cd test-projects/web-basic +npm install +npm run format:check +``` + +### Workflow Issues + +```bash +# Check workflow logs in Gitea +# Enable debug logging: +env: + ACTIONS_STEP_DEBUG: true + ACTIONS_RUNNER_DEBUG: true +``` + +## Contributing + +When adding new templates or making changes: + +1. **Create/update test projects** to validate the changes +2. **Run validation script** locally before committing +3. **Update documentation** as needed +4. **Test with feature branches** before merging +5. **Tag releases** for stable versions + +## Resources + +- [Gitea Actions Documentation](https://docs.gitea.io/en-us/actions/) +- [GitHub Actions Syntax](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions) +- [Act - Local Testing Tool](https://github.com/nektos/act) +- [yamllint Documentation](https://yamllint.readthedocs.io/) +- [yq Documentation](https://mikefarah.gitbook.io/yq/) \ No newline at end of file diff --git a/scripts/validate-templates.sh b/scripts/validate-templates.sh new file mode 100755 index 0000000..fab21b7 --- /dev/null +++ b/scripts/validate-templates.sh @@ -0,0 +1,709 @@ +#!/bin/bash +set -e + +# CI Template Validation Script +# This script validates the syntax and structure of Gitea Actions workflow templates + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Configuration +WORKFLOW_DIR=".gitea/workflows" +REQUIRED_FIELDS=("name" "on" "jobs") +REUSABLE_TRIGGER="workflow_call" + +# Counters +TOTAL_FILES=0 +VALID_FILES=0 +WARNINGS=0 + +# Helper functions +log_info() { + echo -e "${BLUE}[INFO]${NC} $1" +} + +log_success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" +} + +log_warning() { + echo -e "${YELLOW}[WARNING]${NC} $1" + ((WARNINGS++)) +} + +log_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +# Check if required tools are installed +check_dependencies() { + log_info "Checking dependencies..." + + if ! command -v yamllint &> /dev/null; then + log_warning "yamllint not found. Install with: pip install yamllint" + log_info "Will skip YAML syntax validation" + SKIP_YAMLLINT=true + else + log_success "yamllint found" + fi + + if ! command -v yq &> /dev/null; then + log_warning "yq not found. Install with: brew install yq (macOS) or apt install yq (Ubuntu)" + log_info "Will use basic grep validation instead" + SKIP_YQ=true + else + log_success "yq found" + fi +} + +# Validate YAML syntax +validate_yaml_syntax() { + local file="$1" + + if [[ "$SKIP_YAMLLINT" == "true" ]]; then + return 0 + fi + + if yamllint "$file" 2>/dev/null; then + return 0 + else + log_error "YAML syntax validation failed for $file" + return 1 + fi +} + +# Check if file is a reusable workflow +is_reusable_workflow() { + local file="$1" + + if [[ "$SKIP_YQ" == "true" ]]; then + # Fallback to grep + if grep -q "workflow_call" "$file"; then + return 0 + fi + else + # Use yq for precise checking + if yq eval '.on | has("workflow_call")' "$file" 2>/dev/null | grep -q "true"; then + return 0 + fi + fi + + return 1 +} + +# Validate required workflow fields +validate_workflow_structure() { + local file="$1" + local valid=true + + for field in "${REQUIRED_FIELDS[@]}"; do + if [[ "$SKIP_YQ" == "true" ]]; then + # Fallback to grep + if ! grep -q "^${field}:" "$file"; then + log_error "Missing required field '$field' in $file" + valid=false + fi + else + # Use yq for precise checking + if ! yq eval "has(\"$field\")" "$file" 2>/dev/null | grep -q "true"; then + log_error "Missing required field '$field' in $file" + valid=false + fi + fi + done + + if [[ "$valid" == "true" ]]; then + return 0 + else + return 1 + fi +} + +# Validate workflow inputs (for reusable workflows) +validate_workflow_inputs() { + local file="$1" + + if ! is_reusable_workflow "$file"; then + return 0 + fi + + if [[ "$SKIP_YQ" == "true" ]]; then + # Basic check with grep + if grep -q "inputs:" "$file"; then + log_success "Inputs section found in $file" + else + log_warning "No inputs section found in reusable workflow $file" + fi + else + # Detailed validation with yq + local inputs_count + inputs_count=$(yq eval '.on.workflow_call.inputs | length' "$file" 2>/dev/null || echo "0") + + if [[ "$inputs_count" -gt 0 ]]; then + log_success "Found $inputs_count input(s) in $file" + + # Validate each input has required fields + local input_names + input_names=$(yq eval '.on.workflow_call.inputs | keys | .[]' "$file" 2>/dev/null || echo "") + + while IFS= read -r input_name; do + if [[ -n "$input_name" ]]; then + # Check for description + if ! yq eval ".on.workflow_call.inputs.${input_name} | has(\"description\")" "$file" 2>/dev/null | grep -q "true"; then + log_warning "Input '$input_name' missing description in $file" + fi + + # Check for type + if ! yq eval ".on.workflow_call.inputs.${input_name} | has(\"type\")" "$file" 2>/dev/null | grep -q "true"; then + log_warning "Input '$input_name' missing type in $file" + fi + fi + done <<< "$input_names" + else + log_warning "No inputs found in reusable workflow $file" + fi + fi +} + +# Validate a single workflow file +validate_workflow_file() { + local file="$1" + local filename + filename=$(basename "$file") + + log_info "Validating $file..." + ((TOTAL_FILES++)) + + # Check if file exists and is readable + if [[ ! -r "$file" ]]; then + log_error "Cannot read file: $file" + return 1 + fi + + # Validate YAML syntax + if ! validate_yaml_syntax "$file"; then + return 1 + fi + + # Validate workflow structure + if ! validate_workflow_structure "$file"; then + return 1 + fi + + # Additional validations for reusable workflows + if is_reusable_workflow "$file"; then + log_success "$filename is a reusable workflow" + validate_workflow_inputs "$file" + else + log_warning "$filename is not a reusable workflow (missing workflow_call trigger)" + fi + + # Check for common issues + if grep -q "uses: \./\|uses: \.\\\\" "$file"; then + log_warning "Found relative path in 'uses' field in $file - this may cause issues" + fi + + # Check for hardcoded values that should be inputs + if grep -q "python-version: [\"']3\." "$file"; then + log_warning "Found hardcoded Python version in $file - consider making it an input" + fi + + ((VALID_FILES++)) + log_success "$filename validation completed" + return 0 +} + +# Validate all workflow files +validate_all_workflows() { + log_info "Starting workflow validation..." + + if [[ ! -d "$WORKFLOW_DIR" ]]; then + log_error "Workflow directory '$WORKFLOW_DIR' not found" + exit 1 + fi + + # Find all YAML files in workflow directory + local workflow_files + workflow_files=$(find "$WORKFLOW_DIR" -name "*.yml" -o -name "*.yaml" 2>/dev/null) + + if [[ -z "$workflow_files" ]]; then + log_error "No workflow files found in '$WORKFLOW_DIR'" + exit 1 + fi + + # Validate each file + local failed_files=0 + while IFS= read -r file; do + if [[ -n "$file" ]]; then + if ! validate_workflow_file "$file"; then + ((failed_files++)) + fi + fi + done <<< "$workflow_files" + + # Print summary + echo + log_info "=== Validation Summary ===" + log_info "Total files: $TOTAL_FILES" + log_success "Valid files: $VALID_FILES" + + if [[ $WARNINGS -gt 0 ]]; then + log_warning "Warnings: $WARNINGS" + fi + + if [[ $failed_files -gt 0 ]]; then + log_error "Failed files: $failed_files" + exit 1 + fi + + if [[ $WARNINGS -gt 0 ]]; then + log_warning "Validation completed with warnings" + exit 0 + fi + + log_success "All workflows validated successfully!" +} + +# Validate documentation files +validate_documentation() { + log_info "Validating documentation..." + + local readme_files=("README.md" "README-python-code-quality.md" "README-python-pytest.md" "README-web-formatting.md") + + for readme in "${readme_files[@]}"; do + if [[ -f "$readme" ]]; then + log_success "Found $readme" + + # Check for basic sections + if ! grep -q "^# " "$readme"; then + log_warning "$readme missing main heading" + fi + + if ! grep -q "## Usage" "$readme"; then + log_warning "$readme missing Usage section" + fi + + else + log_warning "Missing documentation file: $readme" + fi + done +} + +# Generate workflow file list +list_workflows() { + log_info "Available workflow templates:" + echo + + if [[ -d "$WORKFLOW_DIR" ]]; then + find "$WORKFLOW_DIR" -name "*.yml" -o -name "*.yaml" | while read -r file; do + local relative_path + relative_path=$(echo "$file" | sed "s|^$WORKFLOW_DIR/||") + + if is_reusable_workflow "$file"; then + echo -e " ${GREEN}✓${NC} $relative_path (reusable)" + else + echo -e " ${YELLOW}○${NC} $relative_path (not reusable)" + fi + done + else + log_error "Workflow directory not found" + fi + echo +} + +# Main function +main() { + echo -e "${BLUE}CI Template Validator${NC}" + echo "======================" + echo + + # Parse command line arguments + case "${1:-validate}" in + "validate") + check_dependencies + validate_all_workflows + validate_documentation + ;; + "list") + list_workflows + ;; + "help"|"-h"|"--help") + echo "Usage: $0 [command]" + echo + echo "Commands:" + echo " validate Validate all workflow files (default)" + echo " list List all workflow templates" + echo " help Show this help message" + echo + echo "Examples:" + echo " $0 # Validate all workflows" + echo " $0 validate # Same as above" + echo " $0 list # List all available templates" + ;; + *) + log_error "Unknown command: $1" + echo "Use '$0 help' for usage information" + exit 1 + ;; + esac +} + +# Run main function with all arguments +main "$@" + +echo -e "${BLUE}🔍 CI Template Validation Script${NC}" +echo "==================================" + +# Function to print status +print_status() { + if [ $1 -eq 0 ]; then + echo -e "${GREEN}✅ $2${NC}" + else + echo -e "${RED}❌ $2${NC}" + return 1 + fi +} + +# Function to print warning +print_warning() { + echo -e "${YELLOW}⚠️ $1${NC}" +} + +# Function to print info +print_info() { + echo -e "${BLUE}ℹ️ $1${NC}" +} + +# Check if required tools are installed +check_dependencies() { + echo -e "\n${BLUE}Checking dependencies...${NC}" + + local missing_deps=() + + if ! command -v yamllint &> /dev/null; then + missing_deps+=("yamllint") + fi + + if ! command -v python3 &> /dev/null; then + missing_deps+=("python3") + fi + + if [ ${#missing_deps[@]} -gt 0 ]; then + echo -e "${RED}Missing dependencies: ${missing_deps[*]}${NC}" + echo "Install with:" + for dep in "${missing_deps[@]}"; do + case $dep in + yamllint) + echo " pip install yamllint" + ;; + python3) + echo " Install Python 3.x" + ;; + esac + done + return 1 + fi + + print_status 0 "All dependencies available" +} + +# Validate YAML syntax +validate_yaml_syntax() { + echo -e "\n${BLUE}Validating YAML syntax...${NC}" + + local yaml_files=() + while IFS= read -r -d $'\0'; do + yaml_files+=("$REPLY") + done < <(find .gitea/workflows -name "*.yml" -print0 2>/dev/null) + + if [ ${#yaml_files[@]} -eq 0 ]; then + print_warning "No YAML files found in .gitea/workflows/" + return 1 + fi + + local errors=0 + for file in "${yaml_files[@]}"; do + if yamllint "$file" > /dev/null 2>&1; then + print_status 0 "YAML syntax valid: $file" + else + print_status 1 "YAML syntax error: $file" + yamllint "$file" + ((errors++)) + fi + done + + if [ $errors -eq 0 ]; then + print_status 0 "All YAML files have valid syntax" + else + print_status 1 "$errors YAML files have syntax errors" + return 1 + fi +} + +# Validate workflow structure +validate_workflow_structure() { + echo -e "\n${BLUE}Validating workflow structure...${NC}" + + local workflow_files=() + while IFS= read -r -d $'\0'; do + workflow_files+=("$REPLY") + done < <(find .gitea/workflows -name "*.yml" -print0 2>/dev/null) + + local errors=0 + for file in "${workflow_files[@]}"; do + echo -e "\nChecking $file..." + + # Check for workflow_call trigger + if grep -q "workflow_call" "$file"; then + print_status 0 "Has workflow_call trigger" + else + print_status 1 "Missing workflow_call trigger" + ((errors++)) + fi + + # Check for inputs section + if grep -q "inputs:" "$file"; then + print_status 0 "Has inputs section" + else + print_warning "No inputs section (may be intentional)" + fi + + # Check for jobs section + if grep -q "jobs:" "$file"; then + print_status 0 "Has jobs section" + else + print_status 1 "Missing jobs section" + ((errors++)) + fi + + # Check for runs-on + if grep -q "runs-on:" "$file"; then + print_status 0 "Has runs-on specified" + else + print_status 1 "Missing runs-on specification" + ((errors++)) + fi + + # Check for checkout action + if grep -q "actions/checkout" "$file"; then + print_status 0 "Uses checkout action" + else + print_warning "No checkout action (may be intentional)" + fi + done + + if [ $errors -eq 0 ]; then + print_status 0 "All workflows have valid structure" + else + print_status 1 "$errors workflows have structure issues" + return 1 + fi +} + +# Validate input parameters +validate_input_parameters() { + echo -e "\n${BLUE}Validating input parameters...${NC}" + + local workflow_files=() + while IFS= read -r -d $'\0'; do + workflow_files+=("$REPLY") + done < <(find .gitea/workflows -name "*.yml" -print0 2>/dev/null) + + for file in "${workflow_files[@]}"; do + echo -e "\nChecking inputs in $file..." + + # Extract input definitions and usage + local input_definitions=() + while IFS= read -r line; do + if [[ $line =~ ^[[:space:]]*([a-zA-Z0-9_-]+):[[:space:]]*$ ]]; then + input_name="${BASH_REMATCH[1]}" + if [[ ! $input_name =~ ^(description|required|type|default)$ ]]; then + input_definitions+=("$input_name") + fi + fi + done < <(sed -n '/inputs:/,/^[[:space:]]*[a-zA-Z]/p' "$file" | head -n -1) + + # Check if inputs are used in the workflow + for input in "${input_definitions[@]}"; do + if grep -q "\${{ inputs\.$input }}" "$file"; then + print_status 0 "Input '$input' is used in workflow" + else + print_warning "Input '$input' is defined but not used" + fi + done + done +} + +# Create test project structure +create_test_project() { + echo -e "\n${BLUE}Creating test project structure...${NC}" + + local test_dir="test-validation-project" + + if [ -d "$test_dir" ]; then + print_info "Removing existing test project" + rm -rf "$test_dir" + fi + + mkdir -p "$test_dir/src/test_package" + cd "$test_dir" + + # Create pyproject.toml + cat > pyproject.toml << 'EOF' +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project] +name = "test-validation-package" +version = "0.1.0" +description = "Test package for CI template validation" +dependencies = [] + +[project.optional-dependencies] +dev = [ + "mypy>=1.0.0", + "ruff>=0.1.0", + "pyupgrade>=3.0.0", + "pytest>=7.0.0", + "pytest-cov>=4.0.0", +] + +[tool.ruff] +line-length = 88 +target-version = "py310" + +[tool.mypy] +python_version = "3.10" +warn_return_any = true +warn_unused_configs = true +EOF + + # Create source code + mkdir -p src/test_package + cat > src/test_package/__init__.py << 'EOF' +"""Test package for validation.""" +__version__ = "0.1.0" + +def hello_world() -> str: + """Return a greeting message.""" + return "Hello, World!" +EOF + + cat > src/test_package/math_utils.py << 'EOF' +"""Math utility functions.""" +from typing import List + +def add_numbers(numbers: List[float]) -> float: + """Add a list of numbers.""" + return sum(numbers) + +def multiply_numbers(numbers: List[float]) -> float: + """Multiply a list of numbers.""" + result = 1.0 + for num in numbers: + result *= num + return result +EOF + + # Create tests + mkdir -p tests + cat > tests/test_math_utils.py << 'EOF' +"""Tests for math utilities.""" +import pytest +from test_package.math_utils import add_numbers, multiply_numbers + +def test_add_numbers(): + assert add_numbers([1, 2, 3]) == 6 + assert add_numbers([]) == 0 + assert add_numbers([5.5, 2.5]) == 8.0 + +def test_multiply_numbers(): + assert multiply_numbers([2, 3, 4]) == 24 + assert multiply_numbers([1]) == 1 + assert multiply_numbers([2.5, 2]) == 5.0 +EOF + + cd .. + print_status 0 "Test project created at $test_dir" +} + +# Generate validation report +generate_report() { + echo -e "\n${BLUE}Generating validation report...${NC}" + + local report_file="validation-report.md" + + cat > "$report_file" << 'EOF' +# CI Template Validation Report + +Generated on: $(date) + +## Summary + +This report contains the validation results for the CI template workflows. + +## Workflow Files + +EOF + + # Add workflow file information + find .gitea/workflows -name "*.yml" 2>/dev/null | while read -r file; do + echo "### $file" >> "$report_file" + echo "" >> "$report_file" + echo "- **Size:** $(wc -c < "$file") bytes" >> "$report_file" + echo "- **Lines:** $(wc -l < "$file") lines" >> "$report_file" + + if grep -q "workflow_call" "$file"; then + echo "- **Type:** Reusable workflow ✅" >> "$report_file" + else + echo "- **Type:** Standard workflow ⚠️" >> "$report_file" + fi + + local input_count=$(grep -c "^[[:space:]]*[a-zA-Z0-9_-]*:[[:space:]]*$" "$file" | head -n 1) + echo "- **Input parameters:** $input_count" >> "$report_file" + echo "" >> "$report_file" + done + + print_status 0 "Validation report generated: $report_file" +} + +# Main execution +main() { + echo -e "${BLUE}Starting CI template validation...${NC}" + + local exit_code=0 + + # Run all validation steps + check_dependencies || exit_code=1 + validate_yaml_syntax || exit_code=1 + validate_workflow_structure || exit_code=1 + validate_input_parameters || exit_code=1 + create_test_project || exit_code=1 + generate_report || exit_code=1 + + echo -e "\n${BLUE}Validation Summary${NC}" + echo "==================" + + if [ $exit_code -eq 0 ]; then + echo -e "${GREEN}🎉 All validations passed!${NC}" + echo -e "${GREEN}Your CI templates are ready for use.${NC}" + else + echo -e "${RED}💥 Some validations failed.${NC}" + echo -e "${RED}Please review the errors above and fix them.${NC}" + fi + + echo -e "\n${BLUE}Next steps:${NC}" + echo "1. Review the validation report" + echo "2. Test the templates with the created test project" + echo "3. Run the automated test workflow: .gitea/workflows/test-templates.yml" + + return $exit_code +} + +# Run the main function +main "$@" \ No newline at end of file diff --git a/test-projects/python-basic/.gitea/workflows/test-templates.yml b/test-projects/python-basic/.gitea/workflows/test-templates.yml new file mode 100644 index 0000000..0323af1 --- /dev/null +++ b/test-projects/python-basic/.gitea/workflows/test-templates.yml @@ -0,0 +1,22 @@ +name: Test CI Templates + +on: + push: + branches: [main] + pull_request: + +jobs: + code-quality: + name: Code Quality + uses: gitea.gt-proj.com/brian/CI-templates/.gitea/workflows/python/code-quality.yml@main + with: + python-version: "3.11" + working-directory: "." + + test: + name: Run Tests + uses: gitea.gt-proj.com/brian/CI-templates/.gitea/workflows/python/pytest.yml@main + with: + python-version: "3.11" + coverage-package: "test_package" + pytest-args: "--verbose" \ No newline at end of file diff --git a/test-projects/python-basic/README.md b/test-projects/python-basic/README.md new file mode 100644 index 0000000..13ed7a8 --- /dev/null +++ b/test-projects/python-basic/README.md @@ -0,0 +1,30 @@ +# Test Package + +A simple Python package for testing CI templates. + +## Features + +- Basic calculator operations +- Utility functions +- Comprehensive test suite +- Type hints +- Code quality tools integration + +## Development + +```bash +# Install dependencies +uv sync --all-extras + +# Run tests +uv run pytest + +# Run code quality checks +uv run mypy . +uv run ruff check . +uv run ruff format --check . +``` + +## Testing CI Templates + +This project is used to test the CI templates in the parent repository. \ No newline at end of file diff --git a/test-projects/python-basic/pyproject.toml b/test-projects/python-basic/pyproject.toml new file mode 100644 index 0000000..cb880ad --- /dev/null +++ b/test-projects/python-basic/pyproject.toml @@ -0,0 +1,82 @@ +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project] +name = "test-package" +version = "0.1.0" +description = "Test package for CI template validation" +authors = [ + { name = "Test Author", email = "test@example.com" } +] +readme = "README.md" +requires-python = ">=3.10" +dependencies = [ + "requests>=2.28.0", +] + +[project.optional-dependencies] +dev = [ + "pytest>=7.0.0", + "pytest-cov>=4.0.0", + "mypy>=1.0.0", + "ruff>=0.1.0", + "pyupgrade>=3.0.0", +] +test = [ + "pytest>=7.0.0", + "pytest-cov>=4.0.0", + "pytest-mock>=3.10.0", +] + +[tool.pytest.ini_options] +testpaths = ["tests"] +python_files = ["test_*.py", "*_test.py"] +python_classes = ["Test*"] +python_functions = ["test_*"] +addopts = [ + "--strict-markers", + "--strict-config", + "--verbose", + "--cov=test_package", + "--cov-report=term-missing", +] + +[tool.mypy] +python_version = "3.10" +warn_return_any = true +warn_unused_configs = true +disallow_untyped_defs = true + +[tool.ruff] +target-version = "py310" +line-length = 88 + +[tool.ruff.lint] +select = [ + "E", # pycodestyle errors + "W", # pycodestyle warnings + "F", # pyflakes + "I", # isort + "B", # flake8-bugbear + "C4", # flake8-comprehensions + "UP", # pyupgrade +] + +[tool.coverage.run] +source = ["test_package"] +omit = ["tests/*"] + +[tool.coverage.report] +exclude_lines = [ + "pragma: no cover", + "def __repr__", + "if self.debug:", + "if settings.DEBUG", + "raise AssertionError", + "raise NotImplementedError", + "if 0:", + "if __name__ == .__main__.:", + "class .*\\bProtocol\\):", + "@(abc\\.)?abstractmethod", +] \ No newline at end of file diff --git a/test-projects/python-basic/src/test_package/__init__.py b/test-projects/python-basic/src/test_package/__init__.py new file mode 100644 index 0000000..a454d40 --- /dev/null +++ b/test-projects/python-basic/src/test_package/__init__.py @@ -0,0 +1,8 @@ +"""Test package for CI template validation.""" + +__version__ = "0.1.0" + +from .calculator import Calculator +from .utils import format_number + +__all__ = ["Calculator", "format_number"] \ No newline at end of file diff --git a/test-projects/python-basic/src/test_package/calculator.py b/test-projects/python-basic/src/test_package/calculator.py new file mode 100644 index 0000000..d7eafd4 --- /dev/null +++ b/test-projects/python-basic/src/test_package/calculator.py @@ -0,0 +1,31 @@ +"""Calculator module for basic arithmetic operations.""" + +from typing import Union + +Number = Union[int, float] + + +class Calculator: + """A simple calculator class.""" + + def add(self, a: Number, b: Number) -> Number: + """Add two numbers.""" + return a + b + + def subtract(self, a: Number, b: Number) -> Number: + """Subtract b from a.""" + return a - b + + def multiply(self, a: Number, b: Number) -> Number: + """Multiply two numbers.""" + return a * b + + def divide(self, a: Number, b: Number) -> float: + """Divide a by b.""" + if b == 0: + raise ValueError("Cannot divide by zero") + return a / b + + def power(self, base: Number, exponent: Number) -> Number: + """Raise base to the power of exponent.""" + return base ** exponent \ No newline at end of file diff --git a/test-projects/python-basic/src/test_package/utils.py b/test-projects/python-basic/src/test_package/utils.py new file mode 100644 index 0000000..1f7d8df --- /dev/null +++ b/test-projects/python-basic/src/test_package/utils.py @@ -0,0 +1,24 @@ +"""Utility functions for the test package.""" + +from typing import Union + + +def format_number(number: Union[int, float], precision: int = 2) -> str: + """Format a number with specified precision.""" + if isinstance(number, int): + return str(number) + return f"{number:.{precision}f}" + + +def is_even(number: int) -> bool: + """Check if a number is even.""" + return number % 2 == 0 + + +def factorial(n: int) -> int: + """Calculate factorial of a number.""" + if n < 0: + raise ValueError("Factorial is not defined for negative numbers") + if n == 0 or n == 1: + return 1 + return n * factorial(n - 1) \ No newline at end of file diff --git a/test-projects/python-basic/tests/test_calculator.py b/test-projects/python-basic/tests/test_calculator.py new file mode 100644 index 0000000..4578bfb --- /dev/null +++ b/test-projects/python-basic/tests/test_calculator.py @@ -0,0 +1,47 @@ +"""Tests for the calculator module.""" + +import pytest +from test_package.calculator import Calculator + + +class TestCalculator: + """Test cases for Calculator class.""" + + def setup_method(self): + """Set up test fixtures.""" + self.calc = Calculator() + + def test_add(self): + """Test addition operation.""" + assert self.calc.add(2, 3) == 5 + assert self.calc.add(-1, 1) == 0 + assert self.calc.add(0.1, 0.2) == pytest.approx(0.3) + + def test_subtract(self): + """Test subtraction operation.""" + assert self.calc.subtract(5, 3) == 2 + assert self.calc.subtract(1, 1) == 0 + assert self.calc.subtract(-1, -2) == 1 + + def test_multiply(self): + """Test multiplication operation.""" + assert self.calc.multiply(3, 4) == 12 + assert self.calc.multiply(-2, 3) == -6 + assert self.calc.multiply(0, 100) == 0 + + def test_divide(self): + """Test division operation.""" + assert self.calc.divide(10, 2) == 5.0 + assert self.calc.divide(7, 2) == 3.5 + assert self.calc.divide(-6, 3) == -2.0 + + def test_divide_by_zero(self): + """Test division by zero raises error.""" + with pytest.raises(ValueError, match="Cannot divide by zero"): + self.calc.divide(5, 0) + + def test_power(self): + """Test power operation.""" + assert self.calc.power(2, 3) == 8 + assert self.calc.power(5, 0) == 1 + assert self.calc.power(2, -1) == 0.5 \ No newline at end of file diff --git a/test-projects/python-basic/tests/test_utils.py b/test-projects/python-basic/tests/test_utils.py new file mode 100644 index 0000000..4150b69 --- /dev/null +++ b/test-projects/python-basic/tests/test_utils.py @@ -0,0 +1,41 @@ +"""Tests for the utils module.""" + +import pytest +from test_package.utils import format_number, is_even, factorial + + +class TestUtils: + """Test cases for utility functions.""" + + def test_format_number_integer(self): + """Test formatting integers.""" + assert format_number(42) == "42" + assert format_number(-5) == "-5" + + def test_format_number_float(self): + """Test formatting floats.""" + assert format_number(3.14159) == "3.14" + assert format_number(3.14159, 3) == "3.142" + assert format_number(2.0) == "2.00" + + def test_is_even(self): + """Test even number detection.""" + assert is_even(2) is True + assert is_even(4) is True + assert is_even(1) is False + assert is_even(3) is False + assert is_even(0) is True + assert is_even(-2) is True + assert is_even(-1) is False + + def test_factorial_positive(self): + """Test factorial of positive numbers.""" + assert factorial(0) == 1 + assert factorial(1) == 1 + assert factorial(5) == 120 + assert factorial(3) == 6 + + def test_factorial_negative(self): + """Test factorial of negative numbers raises error.""" + with pytest.raises(ValueError, match="Factorial is not defined for negative numbers"): + factorial(-1) \ No newline at end of file diff --git a/test-projects/web-basic/.gitea/workflows/test-formatting.yml b/test-projects/web-basic/.gitea/workflows/test-formatting.yml new file mode 100644 index 0000000..6b3fec1 --- /dev/null +++ b/test-projects/web-basic/.gitea/workflows/test-formatting.yml @@ -0,0 +1,16 @@ +name: Test Web Formatting + +on: + push: + branches: [main] + pull_request: + +jobs: + formatting: + name: Web Formatting + uses: gitea.gt-proj.com/brian/CI-templates/.gitea/workflows/web/formatting.yml@main + with: + node-version: "20" + prettier-config: ".prettierrc.json" + check-yaml: true + check-json: true \ No newline at end of file diff --git a/test-projects/web-basic/.prettierrc.json b/test-projects/web-basic/.prettierrc.json new file mode 100644 index 0000000..feb3796 --- /dev/null +++ b/test-projects/web-basic/.prettierrc.json @@ -0,0 +1,7 @@ +{ + "semi": true, + "trailingComma": "es5", + "singleQuote": true, + "printWidth": 80, + "tabWidth": 2 +} \ No newline at end of file diff --git a/test-projects/web-basic/config.yml b/test-projects/web-basic/config.yml new file mode 100644 index 0000000..5dca919 --- /dev/null +++ b/test-projects/web-basic/config.yml @@ -0,0 +1,20 @@ +name: Test Web App +description: A simple web application for testing + +api: + version: v1 + endpoints: + - name: health + path: /health + method: GET + - name: calculator + path: /calculate + method: POST + +database: + type: sqlite + name: test.db + +logging: + level: info + format: json \ No newline at end of file diff --git a/test-projects/web-basic/index.js b/test-projects/web-basic/index.js new file mode 100644 index 0000000..b1a6b17 --- /dev/null +++ b/test-projects/web-basic/index.js @@ -0,0 +1,15 @@ +console.log("Hello, World!"); + +const calculator = { + add: (a, b) => a + b, + subtract: (a, b) => a - b, + multiply: (a, b) => a * b, + divide: (a, b) => { + if (b === 0) { + throw new Error("Cannot divide by zero"); + } + return a / b; + }, +}; + +module.exports = calculator; \ No newline at end of file diff --git a/test-projects/web-basic/package.json b/test-projects/web-basic/package.json new file mode 100644 index 0000000..9580f01 --- /dev/null +++ b/test-projects/web-basic/package.json @@ -0,0 +1,18 @@ +{ + "name": "test-web-project", + "version": "1.0.0", + "description": "Test web project for CI template validation", + "main": "index.js", + "scripts": { + "start": "node index.js", + "test": "echo \"Error: no test specified\" && exit 1", + "format": "prettier --write .", + "format:check": "prettier --check ." + }, + "keywords": ["test", "ci", "templates"], + "author": "Test Author", + "license": "MIT", + "devDependencies": { + "prettier": "^3.0.0" + } +} \ No newline at end of file