diff --git a/.gitea/workflows/python/code-quality.yml b/.gitea/workflows/python/code-quality.yml index 07aa324..302de3a 100644 --- a/.gitea/workflows/python/code-quality.yml +++ b/.gitea/workflows/python/code-quality.yml @@ -1,6 +1,7 @@ +--- name: Python Code Quality Pipeline -on: +"on": workflow_call: inputs: python-version: @@ -36,7 +37,7 @@ jobs: defaults: run: working-directory: ${{ inputs.working-directory }} - + steps: - name: Checkout code uses: actions/checkout@v4 @@ -71,4 +72,8 @@ jobs: - name: Pyupgrade check if: ${{ !inputs.skip-pyupgrade }} - run: uv run pyupgrade --py312-plus $(git ls-files '*.py') && git diff --exit-code \ No newline at end of file + run: | + echo "Running pyupgrade..." + uv run pyupgrade --py312-plus $(git ls-files '*.py') + echo "Checking for changes..." + git diff --exit-code diff --git a/.gitea/workflows/python/pytest.yml b/.gitea/workflows/python/pytest.yml index b1174bf..347c98a 100644 --- a/.gitea/workflows/python/pytest.yml +++ b/.gitea/workflows/python/pytest.yml @@ -1,6 +1,7 @@ +--- name: Python Test Pipeline -on: +"on": workflow_call: inputs: python-version: @@ -49,7 +50,7 @@ jobs: defaults: run: working-directory: ${{ inputs.working-directory }} - + steps: - name: Checkout code uses: actions/checkout@v4 @@ -77,13 +78,20 @@ jobs: PYTHONPATH: . run: | if [ -n "${{ inputs.coverage-package }}" ]; then - uv run pytest --cov=${{ inputs.coverage-package }} --cov-report=term-missing ${{ inputs.pytest-args }} > coverage.txt + uv run pytest --cov=${{ inputs.coverage-package }} \ + --cov-report=term-missing ${{ inputs.pytest-args }} > coverage.txt else uv run pytest ${{ inputs.pytest-args }} fi - name: Post coverage summary to PR - if: ${{ !inputs.skip-coverage-comment && inputs.coverage-package != '' && github.event_name == 'pull_request' && inputs.api-url != '' }} + if: >- + ${{ + !inputs.skip-coverage-comment && + inputs.coverage-package != '' && + github.event_name == 'pull_request' && + inputs.api-url != '' + }} env: API_URL: ${{ inputs.api-url }} REPO_OWNER: ${{ github.repository_owner }} @@ -94,7 +102,9 @@ jobs: if [ -f coverage.txt ]; then COVERAGE=$(cat coverage.txt) COMMENT_BODY="**Test Coverage Report:**\n\`\`\`\n$COVERAGE\n\`\`\`" - curl -s -X POST "$API_URL/repos/$REPO_OWNER/$REPO_NAME/issues/$PR_NUMBER/comments" \ + API_ENDPOINT="$API_URL/repos/$REPO_OWNER/$REPO_NAME/issues/$PR_NUMBER/comments" + curl -s -X POST \ + "$API_ENDPOINT" \ -H "Authorization: token $CI_RUNNER_TOKEN" \ -H "Content-Type: application/json" \ -d "{\"body\": \"$COMMENT_BODY\"}" diff --git a/.gitea/workflows/test-templates.yml b/.gitea/workflows/test-templates.yml index e35d2ec..16dc234 100644 --- a/.gitea/workflows/test-templates.yml +++ b/.gitea/workflows/test-templates.yml @@ -1,6 +1,7 @@ +--- name: Test CI Templates -on: +"on": push: branches: [main] pull_request: @@ -14,24 +15,29 @@ jobs: 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 + # Skip the test file itself + if [[ "$file" == *"test-templates.yml" ]]; then + echo "Skipping test file: $file" + continue + fi echo "Checking $file..." if ! grep -q "workflow_call" "$file"; then echo "Error: $file is missing workflow_call trigger" @@ -58,69 +64,69 @@ jobs: - 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: @@ -137,89 +143,89 @@ jobs: 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: @@ -237,16 +243,16 @@ jobs: 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' { @@ -255,19 +261,19 @@ jobs: "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 }) { @@ -278,7 +284,7 @@ jobs: ); } EOF - + # Create JSON file cat > config.json << 'EOF' { @@ -290,7 +296,7 @@ jobs: } } EOF - + # Create YAML file cat > config.yml << 'EOF' name: test-config @@ -302,7 +308,7 @@ jobs: - feature1 - feature2 EOF - + # Create Prettier config cat > .prettierrc.json << 'EOF' { @@ -313,7 +319,7 @@ jobs: "tabWidth": 2 } EOF - + - name: Test web formatting workflow uses: ./ with: @@ -325,37 +331,37 @@ jobs: 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' @@ -364,20 +370,20 @@ jobs: "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: @@ -386,11 +392,11 @@ jobs: 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 @@ -399,18 +405,18 @@ jobs: [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: @@ -420,41 +426,47 @@ jobs: test-results: runs-on: ubuntu-latest - needs: [test-python-code-quality, test-python-pytest, test-web-formatting, test-monorepo, test-edge-cases] + 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 + + 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 + fi diff --git a/.gitea/workflows/web/formatting.yml b/.gitea/workflows/web/formatting.yml index 9ae6e81..0933200 100644 --- a/.gitea/workflows/web/formatting.yml +++ b/.gitea/workflows/web/formatting.yml @@ -1,6 +1,7 @@ +--- name: Web/General Formatting Pipeline -on: +"on": workflow_call: inputs: working-directory: @@ -33,6 +34,11 @@ on: required: false type: boolean default: false + python-version: + description: 'Python version to use for YAML linting (e.g., "3.12")' + required: false + type: string + default: "3.12" jobs: formatting: @@ -40,7 +46,7 @@ jobs: defaults: run: working-directory: ${{ inputs.working-directory }} - + steps: - name: Checkout code uses: actions/checkout@v4 @@ -68,7 +74,7 @@ jobs: if: ${{ inputs.check-yaml }} uses: actions/setup-python@v5 with: - python-version: "3.12" + python-version: ${{ inputs.python-version }} - name: Install yamllint if: ${{ inputs.check-yaml }} @@ -84,4 +90,4 @@ jobs: find . -name "*.json" -type f | while read -r file; do echo "Checking $file" python -m json.tool "$file" > /dev/null - done \ No newline at end of file + done diff --git a/README.md b/README.md index ec9260e..1e8739f 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ A formatting pipeline for web technologies and general file formats using Pretti 📖 [Read the full documentation](README-web-formatting.md) -## Quick Start +## Usage ### For Python Projects diff --git a/scripts/validate-templates.sh b/scripts/validate-templates.sh index fab21b7..792ef32 100755 --- a/scripts/validate-templates.sh +++ b/scripts/validate-templates.sh @@ -199,17 +199,26 @@ validate_workflow_file() { log_success "$filename is a reusable workflow" validate_workflow_inputs "$file" else - log_warning "$filename is not a reusable workflow (missing workflow_call trigger)" + # Skip warning for test files + if [[ "$filename" != *"test-"* ]]; then + log_warning "$filename is not a reusable workflow (missing workflow_call trigger)" + fi 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" + # Skip warning for test files - they intentionally use relative paths to test local workflows + if [[ "$filename" != *"test-"* ]]; then + log_warning "Found relative path in 'uses' field in $file - this may cause issues" + fi 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" + # Skip warning for test files + if [[ "$filename" != *"test-"* ]]; then + log_warning "Found hardcoded Python version in $file - consider making it an input" + fi fi ((VALID_FILES++)) diff --git a/test-validation-project/pyproject.toml b/test-validation-project/pyproject.toml new file mode 100644 index 0000000..3e4b0f4 --- /dev/null +++ b/test-validation-project/pyproject.toml @@ -0,0 +1,27 @@ +[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 diff --git a/test-validation-project/src/test_package/__init__.py b/test-validation-project/src/test_package/__init__.py new file mode 100644 index 0000000..2be14c7 --- /dev/null +++ b/test-validation-project/src/test_package/__init__.py @@ -0,0 +1,6 @@ +"""Test package for validation.""" +__version__ = "0.1.0" + +def hello_world() -> str: + """Return a greeting message.""" + return "Hello, World!" diff --git a/test-validation-project/src/test_package/math_utils.py b/test-validation-project/src/test_package/math_utils.py new file mode 100644 index 0000000..0d1a98a --- /dev/null +++ b/test-validation-project/src/test_package/math_utils.py @@ -0,0 +1,13 @@ +"""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 diff --git a/test-validation-project/tests/test_math_utils.py b/test-validation-project/tests/test_math_utils.py new file mode 100644 index 0000000..c927c07 --- /dev/null +++ b/test-validation-project/tests/test_math_utils.py @@ -0,0 +1,13 @@ +"""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 diff --git a/validation-report.md b/validation-report.md new file mode 100644 index 0000000..de58dd1 --- /dev/null +++ b/validation-report.md @@ -0,0 +1,38 @@ +# CI Template Validation Report + +Generated on: $(date) + +## Summary + +This report contains the validation results for the CI template workflows. + +## Workflow Files + +### .gitea/workflows/test-templates.yml + +- **Size:** 13104 bytes +- **Lines:** 472 lines +- **Type:** Reusable workflow ✅ +- **Input parameters:** 35 + +### .gitea/workflows/python/pytest.yml + +- **Size:** 3311 bytes +- **Lines:** 111 lines +- **Type:** Reusable workflow ✅ +- **Input parameters:** 20 + +### .gitea/workflows/python/code-quality.yml + +- **Size:** 1954 bytes +- **Lines:** 79 lines +- **Type:** Reusable workflow ✅ +- **Input parameters:** 14 + +### .gitea/workflows/web/formatting.yml + +- **Size:** 2559 bytes +- **Lines:** 93 lines +- **Type:** Reusable workflow ✅ +- **Input parameters:** 16 + diff --git a/web/formatting.yml b/web/formatting.yml deleted file mode 100644 index 9ae6e81..0000000 --- a/web/formatting.yml +++ /dev/null @@ -1,87 +0,0 @@ -name: Web/General Formatting Pipeline - -on: - workflow_call: - inputs: - working-directory: - description: 'Working directory for the project' - required: false - type: string - default: "." - node-version: - description: 'Node.js version to use for prettier (e.g., "20")' - required: false - type: string - default: "20" - skip-prettier: - description: 'Skip prettier formatting check' - required: false - type: boolean - default: false - prettier-config: - description: 'Path to prettier config file (optional)' - required: false - type: string - default: "" - check-yaml: - description: 'Check YAML files with yamllint' - required: false - type: boolean - default: false - check-json: - description: 'Check JSON files for validity' - required: false - type: boolean - default: false - -jobs: - formatting: - runs-on: ubuntu-latest - defaults: - run: - working-directory: ${{ inputs.working-directory }} - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up Node.js - if: ${{ !inputs.skip-prettier }} - uses: actions/setup-node@v4 - with: - node-version: ${{ inputs.node-version }} - - - name: Install prettier - if: ${{ !inputs.skip-prettier }} - run: npm install --save-dev --save-exact prettier - - - name: Run prettier check - if: ${{ !inputs.skip-prettier }} - run: | - if [ -n "${{ inputs.prettier-config }}" ]; then - npx prettier --check . --config ${{ inputs.prettier-config }} - else - npx prettier --check . - fi - - - name: Set up Python for YAML linting - if: ${{ inputs.check-yaml }} - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install yamllint - if: ${{ inputs.check-yaml }} - run: pip install yamllint - - - name: Check YAML files - if: ${{ inputs.check-yaml }} - run: yamllint . - - - name: Check JSON files - if: ${{ inputs.check-json }} - run: | - find . -name "*.json" -type f | while read -r file; do - echo "Checking $file" - python -m json.tool "$file" > /dev/null - done \ No newline at end of file