diff --git a/.github/workflows/badges.yml b/.github/workflows/badges.yml deleted file mode 100644 index 453946e..0000000 --- a/.github/workflows/badges.yml +++ /dev/null @@ -1,82 +0,0 @@ -name: Generate Badges - -on: - workflow_run: - workflows: ["Enhanced CI/CD Pipeline"] - types: - - completed - push: - branches: [main] - workflow_dispatch: - -jobs: - badges: - name: Generate Status Badges - runs-on: ubuntu-latest - if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name == 'workflow_dispatch' }} - - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - ref: main - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: '3.12' - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install coverage pytest pytest-cov - pip install -e . - - - name: Run coverage - run: | - pytest tests/ --cov=varidex --cov-report=json --cov-report=term - - - name: Extract coverage percentage - id: coverage - run: | - COVERAGE=$(python -c "import json; print(json.load(open('coverage.json'))['totals']['percent_covered'])") - echo "coverage=$COVERAGE" >> $GITHUB_OUTPUT - echo "Coverage: $COVERAGE%" - - - name: Create coverage badge - uses: schneegans/dynamic-badges-action@v1.7.0 - with: - auth: ${{ secrets.GIST_SECRET }} - gistID: YOUR_GIST_ID_HERE - filename: varidex-coverage.json - label: Coverage - message: ${{ steps.coverage.outputs.coverage }}% - color: ${{ steps.coverage.outputs.coverage > 90 && 'brightgreen' || steps.coverage.outputs.coverage > 80 && 'green' || steps.coverage.outputs.coverage > 70 && 'yellow' || 'orange' }} - continue-on-error: true - - - name: Count tests - id: tests - run: | - TEST_COUNT=$(pytest tests/ --collect-only -q | tail -1 | awk '{print $1}') - echo "count=$TEST_COUNT" >> $GITHUB_OUTPUT - echo "Total tests: $TEST_COUNT" - - - name: Generate badge data - run: | - cat > badge-data.json << EOF - { - "schemaVersion": 1, - "label": "tests", - "message": "${{ steps.tests.outputs.count }} passing", - "color": "brightgreen" - } - EOF - - - name: Create badge summary - run: | - echo "## ๐Ÿ† Badge Status" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "- **Coverage**: ${{ steps.coverage.outputs.coverage }}%" >> $GITHUB_STEP_SUMMARY - echo "- **Tests**: ${{ steps.tests.outputs.count }} passing" >> $GITHUB_STEP_SUMMARY - echo "- **Python**: 3.10, 3.11, 3.12" >> $GITHUB_STEP_SUMMARY - echo "- **Status**: Development" >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml deleted file mode 100644 index 86eebc2..0000000 --- a/.github/workflows/cd.yml +++ /dev/null @@ -1,34 +0,0 @@ -name: CD # Deploy VariDex on main - -on: - push: - branches: [main] - tags: ['v*'] # Auto-release on tags - -jobs: - deploy: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 - with: python-version: '3.12' - - run: pip install build twine pytest-cov - - run: python -m build - - uses: pypa/gh-action-pypi-publish@release/v1 - with: - password: ${{ secrets.PYPI_TOKEN }} # Add in Settings > Secrets - # Optional Docker for genome tool - - uses: docker/login-action@v3 - with: - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - run: docker build -t ghcr.io/${{ github.repository }}:latest . - uses: docker/build-push-action@v6 - with: - push: true - tags: ghcr.io/${{ github.repository }}:latest - deploy-staging: - needs: deploy - if: github.ref == 'refs/heads/main' - environment: staging # Settings > Environments > Add - # Deploy steps diff --git a/.github/workflows/ci-enhanced.yml b/.github/workflows/ci-enhanced.yml deleted file mode 100644 index 8b7d6a6..0000000 --- a/.github/workflows/ci-enhanced.yml +++ /dev/null @@ -1,334 +0,0 @@ -name: Enhanced CI/CD Pipeline - -on: - push: - branches: [main, develop, feature/*] - pull_request: - branches: [main, develop] - workflow_dispatch: - -env: - PYTHON_VERSION_DEFAULT: "3.12" - MIN_COVERAGE: 90 - -jobs: - # Code Quality Checks - Fast feedback - code-quality: - name: Code Quality - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: ${{ env.PYTHON_VERSION_DEFAULT }} - cache: 'pip' - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install black mypy flake8 pylint isort - pip install -r requirements.txt - pip install -r requirements-dev.txt - - - name: Check Black formatting - run: | - black --check --diff varidex/ tests/ - echo "โœ“ All code is Black-formatted (PEP 8 compliant)" - - - name: Check import sorting - run: | - isort --check-only --diff varidex/ tests/ - - - name: Run Flake8 - run: | - flake8 varidex/ tests/ --max-line-length=88 --extend-ignore=E203,W503 - - - name: Run mypy type checking - continue-on-error: true - run: | - mypy varidex/ --config-file=mypy.ini | tee mypy-report.txt - - - name: Upload type checking report - if: always() - uses: actions/upload-artifact@v4 - with: - name: mypy-report - path: mypy-report.txt - retention-days: 30 - - # Security Scanning - security: - name: Security Scan - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: ${{ env.PYTHON_VERSION_DEFAULT }} - cache: 'pip' - - - name: Install security tools - run: | - python -m pip install --upgrade pip - pip install bandit safety detect-secrets - - - name: Run Bandit security linter - continue-on-error: true - run: | - bandit -r varidex/ -f json -o bandit-report.json - bandit -r varidex/ -f txt - - - name: Check dependencies for vulnerabilities - continue-on-error: true - run: | - safety check --json > safety-report.json || true - safety check || true - - - name: Detect secrets - run: | - detect-secrets scan --baseline .secrets.baseline - - - name: Upload security reports - if: always() - uses: actions/upload-artifact@v4 - with: - name: security-reports - path: | - bandit-report.json - safety-report.json - retention-days: 30 - - # Matrix Testing - Multiple Python Versions - test: - name: Tests (Python ${{ matrix.python-version }}) - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - matrix: - os: [ubuntu-latest] - python-version: ["3.10", "3.11", "3.12"] - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - cache: 'pip' - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -e . - pip install -r requirements-test.txt - - - name: Run unit tests - run: | - pytest tests/ \ - --cov=varidex \ - --cov-report=xml \ - --cov-report=term \ - --cov-report=html \ - --junitxml=junit-${{ matrix.python-version }}.xml \ - -v \ - --tb=short - - - name: Check coverage threshold - run: | - coverage report --fail-under=${{ env.MIN_COVERAGE }} - - - name: Upload coverage to Codecov - uses: codecov/codecov-action@v4 - with: - file: ./coverage.xml - flags: python-${{ matrix.python-version }} - name: Python-${{ matrix.python-version }} - fail_ci_if_error: false - - - name: Upload test results - if: always() - uses: actions/upload-artifact@v4 - with: - name: test-results-${{ matrix.python-version }} - path: | - junit-${{ matrix.python-version }}.xml - htmlcov/ - retention-days: 30 - - - name: Generate coverage badge - if: matrix.python-version == env.PYTHON_VERSION_DEFAULT - run: | - coverage report | grep TOTAL | awk '{print "Coverage: " $NF}' - - # Integration & E2E Tests - integration-tests: - name: Integration Tests - runs-on: ubuntu-latest - needs: [code-quality] - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: ${{ env.PYTHON_VERSION_DEFAULT }} - cache: 'pip' - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -e . - pip install -r requirements-test.txt - - - name: Run integration tests - run: | - pytest tests/test_integration_e2e.py -v --tb=short - - - name: Run pipeline validation tests - run: | - pytest tests/test_pipeline_validators.py -v --tb=short - - # Build & Package Validation - build: - name: Build Package - runs-on: ubuntu-latest - needs: [test] - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: ${{ env.PYTHON_VERSION_DEFAULT }} - cache: 'pip' - - - name: Install build tools - run: | - python -m pip install --upgrade pip - pip install build twine check-wheel-contents - - - name: Build distribution packages - run: | - python -m build - ls -lh dist/ - - - name: Check package metadata - run: | - twine check dist/* - - - name: Validate wheel contents - run: | - check-wheel-contents dist/*.whl - - - name: Upload build artifacts - uses: actions/upload-artifact@v4 - with: - name: dist-packages - path: dist/ - retention-days: 30 - - # Documentation Build Test - docs: - name: Documentation - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: ${{ env.PYTHON_VERSION_DEFAULT }} - cache: 'pip' - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -r requirements-docs.txt - - - name: Check docs for broken links - continue-on-error: true - run: | - if [ -d "docs/" ]; then - echo "Documentation directory found" - # Add sphinx-build or mkdocs commands here when ready - else - echo "No docs directory yet - skipping" - fi - - - name: Validate README - run: | - python -c "import re; content=open('README.md').read(); assert len(content)>1000, 'README too short'" - echo "โœ“ README.md is comprehensive" - - # Performance Benchmarking (Optional) - performance: - name: Performance Benchmarks - runs-on: ubuntu-latest - if: github.event_name == 'pull_request' - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: ${{ env.PYTHON_VERSION_DEFAULT }} - cache: 'pip' - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -e . - pip install pytest-benchmark - - - name: Run performance benchmarks - continue-on-error: true - run: | - echo "Performance benchmarking placeholder" - echo "Add pytest-benchmark tests when ready" - - # Final Status Check - ci-success: - name: CI Pipeline Success - runs-on: ubuntu-latest - needs: [code-quality, security, test, integration-tests, build, docs] - if: always() - - steps: - - name: Check all jobs status - run: | - echo "Code Quality: ${{ needs.code-quality.result }}" - echo "Security: ${{ needs.security.result }}" - echo "Tests: ${{ needs.test.result }}" - echo "Integration: ${{ needs.integration-tests.result }}" - echo "Build: ${{ needs.build.result }}" - echo "Docs: ${{ needs.docs.result }}" - - if [ "${{ needs.code-quality.result }}" != "success" ] || \ - [ "${{ needs.test.result }}" != "success" ] || \ - [ "${{ needs.integration-tests.result }}" != "success" ] || \ - [ "${{ needs.build.result }}" != "success" ]; then - echo "โŒ CI Pipeline Failed" - exit 1 - fi - - echo "โœ… CI Pipeline Passed - All checks successful" - echo "๐Ÿ“ฆ Build artifacts ready for deployment" - echo "โš ๏ธ Status: DEVELOPMENT (Not for production use)" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e69de29..0feead2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -0,0 +1,59 @@ +# VariDex CI โ€” the gate. This workflow is `./check.sh`, and nothing else. +# +# WHAT THIS REPLACES. Nine workflows that enforced nothing: `ci.yml` was 0 bytes, every pytest and +# coverage step carried `continue-on-error: true` ("non-blocking during development"), and Codecov +# ran with `fail_ci_if_error: false`. Worse, none of it ever ran: the install step did +# `pip install -e .` against a pyproject.toml with NO [project] table, then +# `pip install -r requirements-test.txt` against a file that was never committed. Both fail, the +# install step had no continue-on-error, and so every test step after it was UNREACHABLE. Every +# `test.yml` run in the repository's history is a failure; the most recent was 2026-02-09. +# +# THE RULE THIS ENCODES: a check must distinguish EXAMINED-AND-CLEAN from NEVER-EXAMINED. Each gate +# in check.sh returns 2 when it could not examine anything, and check.sh reports that as REFUSED โ€” +# never as a pass. An install failure now reads INSTALL FAILED, not as a test result. +name: CI + +on: + push: + branches: [main, develop] + pull_request: + branches: [main, develop] + workflow_dispatch: + +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: true + +jobs: + check: + name: check.sh (Python ${{ matrix.python-version }}) + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ['3.10', '3.11', '3.12'] + + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + cache: pip + + # NO continue-on-error anywhere in this file, deliberately. If install fails the job fails + # here, loudly, and the log says INSTALL FAILED โ€” it does not silently skip the gates and + # report a green tick, which is precisely how this repo went six months without a test run. + - name: Install + run: | + set -euo pipefail + python -m pip install --upgrade pip setuptools wheel + pip install -e . || { echo "::error::INSTALL FAILED โ€” package metadata is broken. No tests were run."; exit 1; } + pip install -r requirements-test.txt || { echo "::error::INSTALL FAILED โ€” test requirements unresolvable. No tests were run."; exit 1; } + pip install ruff mypy + + # check.sh IS the CI. Run it locally and you have run this job. + - name: check.sh + env: + PY: python + run: ./check.sh diff --git a/.github/workflows/dependabot.yml b/.github/workflows/dependabot.yml deleted file mode 100644 index e35ec9d..0000000 --- a/.github/workflows/dependabot.yml +++ /dev/null @@ -1,5 +0,0 @@ -version: 2 -updates: - - package-ecosystem: "pip" - directory: "/" - schedule: {interval: "weekly"} diff --git a/.github/workflows/dependency-updates.yml b/.github/workflows/dependency-updates.yml deleted file mode 100644 index 55b3bdf..0000000 --- a/.github/workflows/dependency-updates.yml +++ /dev/null @@ -1,140 +0,0 @@ -name: Dependency Updates - -on: - schedule: - # Run every Monday at 09:00 UTC - - cron: '0 9 * * 1' - workflow_dispatch: - -permissions: - contents: write - pull-requests: write - -jobs: - # ==================== CHECK OUTDATED PACKAGES ==================== - check-outdated: - name: Check Outdated Packages - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: '3.11' - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install pip-check pip-audit - pip install -r requirements.txt - pip install -r requirements-test.txt - - - name: Check for outdated packages - run: | - echo "๐Ÿ“Š Checking for outdated packages..." - pip list --outdated > outdated-packages.txt - cat outdated-packages.txt - - - name: Upload outdated packages report - uses: actions/upload-artifact@v4 - with: - name: outdated-packages-report - path: outdated-packages.txt - retention-days: 30 - - # ==================== SECURITY UPDATES ==================== - security-updates: - name: Check Security Updates - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: '3.11' - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install safety pip-audit - pip install -r requirements.txt - pip install -r requirements-test.txt - - - name: Run Safety check - run: | - echo "๐Ÿ”’ Checking for known vulnerabilities..." - pip freeze | safety check --stdin --json > safety-report.json || true - pip freeze | safety check --stdin || true - - - name: Run pip-audit - run: | - echo "๐Ÿ” Auditing packages for vulnerabilities..." - pip-audit --desc --format json > pip-audit-report.json || true - pip-audit --desc || true - - - name: Upload security reports - uses: actions/upload-artifact@v4 - with: - name: security-update-reports - path: | - safety-report.json - pip-audit-report.json - retention-days: 90 - - # ==================== PYTHON VERSION COMPATIBILITY ==================== - python-compat: - name: Test Python ${{ matrix.python-version }} Compatibility - runs-on: ubuntu-latest - strategy: - matrix: - python-version: ['3.9', '3.10', '3.11', '3.12'] - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -e . - - - name: Test import - run: | - python -c "import varidex; print(f'VariDex v{varidex.__version__} works on Python ${{ matrix.python-version }}')" - - - name: Run basic tests - run: | - pip install pytest - pytest tests/ -v --maxfail=3 || echo "โš ๏ธ Some tests failed on Python ${{ matrix.python-version }}" - - # ==================== DEPENDENCY SUMMARY ==================== - summary: - name: Dependency Update Summary - runs-on: ubuntu-latest - needs: [check-outdated, security-updates, python-compat] - if: always() - - steps: - - name: Generate summary - run: | - echo "๐Ÿ“Š ====================================" - echo "๐Ÿ“Š DEPENDENCY UPDATE CHECK COMPLETE" - echo "๐Ÿ“Š ====================================" - echo "๐Ÿ“Š Results:" - echo " - Outdated Check: ${{ needs.check-outdated.result }}" - echo " - Security Updates: ${{ needs.security-updates.result }}" - echo " - Python Compatibility: ${{ needs.python-compat.result }}" - echo "๐Ÿ“Š ====================================" - echo "โ„น๏ธ Check artifacts for detailed reports" - echo "๐Ÿ“Š ====================================" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml deleted file mode 100644 index 01cd5b8..0000000 --- a/.github/workflows/test.yml +++ /dev/null @@ -1,122 +0,0 @@ -name: CI/CD Tests - -on: - push: - branches: [ main, develop ] - pull_request: - branches: [ main ] - workflow_dispatch: - -jobs: - test: - name: Test Python ${{ matrix.python-version }} on ${{ matrix.os }} - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - matrix: - os: [ubuntu-latest, windows-latest, macos-latest] - python-version: ['3.10', '3.11', '3.12'] - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - cache: 'pip' - - - name: Install dependencies - run: | - python -m pip install --upgrade pip setuptools wheel - pip install -e . - pip install -r requirements-test.txt - - - name: Run tests with pytest (non-blocking during development) - run: | - pytest tests/ -v --tb=short --strict-markers - continue-on-error: true - - - name: Run tests with coverage - if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.11' - run: | - pytest tests/ --cov=varidex --cov-report=xml --cov-report=term - continue-on-error: true - - - name: Upload coverage to Codecov - if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.11' - uses: codecov/codecov-action@v4 - with: - file: ./coverage.xml - flags: unittests - name: codecov-umbrella - fail_ci_if_error: false - continue-on-error: true - - lint: - name: Code Quality Checks - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: '3.11' - cache: 'pip' - - - name: Install linting tools - run: | - python -m pip install --upgrade pip - pip install flake8 black mypy - - - name: Check code formatting with Black - run: | - black --check --diff varidex/ tests/ - - - name: Lint with flake8 (non-blocking during development) - run: | - flake8 varidex/ tests/ --count --select=E9,F63,F7,F82 --show-source --statistics - flake8 varidex/ tests/ --count --max-line-length=100 --statistics - continue-on-error: true - - - name: Type check with mypy (non-blocking) - run: | - mypy varidex/ --ignore-missing-imports - continue-on-error: true - - build: - name: Build Package - runs-on: ubuntu-latest - needs: [test, lint] - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: '3.11' - - - name: Install build tools - run: | - python -m pip install --upgrade pip build twine - - - name: Build package - run: | - python -m build - - - name: Check package with twine - run: | - twine check dist/* - - - name: Upload build artifacts - uses: actions/upload-artifact@v4 - with: - name: dist-packages - path: dist/ - retention-days: 7 diff --git a/.gitignore b/.gitignore index 51b2f28..f04d420 100644 --- a/.gitignore +++ b/.gitignore @@ -55,3 +55,10 @@ temp_phase1.py cleanup_*.py downloader.py test_*.py +.venv/ + +# test run artifacts +/output +/report.html +.coverage +coverage.xml diff --git a/check.sh b/check.sh new file mode 100755 index 0000000..e636fb7 --- /dev/null +++ b/check.sh @@ -0,0 +1,93 @@ +#!/usr/bin/env bash +# check.sh โ€” this IS the CI, runnable locally. If this passes, CI passes. +# +# VariDex had nine workflows and enforced nothing: ci.yml was 0 bytes, every pytest step carried +# continue-on-error, and the install step referenced a requirements-test.txt that was never +# committed โ€” so the job died at install and no test ever ran. Every test.yml run in the history is +# a failure; the last was 2026-02-09. A green badge meant nothing had been examined. +# +# So every gate below distinguishes EXAMINED-AND-CLEAN from NEVER-EXAMINED, and each returns a +# distinct exit code for "refused" so a dead tool can never read as a pass. +# +# Usage: ./check.sh all gates +# ./check.sh --quick skip coverage (the slow one) +set -uo pipefail +cd "$(dirname "$0")" +PY="${PY:-python}" +[ -x .venv/bin/python ] && PY=.venv/bin/python +QUICK=0; [ "${1:-}" = "--quick" ] && QUICK=1 +FAILED=(); REFUSED=() +run() { # run