diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7fca116..559596c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,38 +1,265 @@ -name: EU-Utility CI/CD +name: CI/CD on: push: - branches: [ main, develop ] + branches: [main, develop] + tags: ["v*"] pull_request: - branches: [ main ] + branches: [main, develop] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + PYTHON_LATEST: "3.12" + PYTHON_MINIMUM: "3.11" jobs: + # =========================================================================== + # Code Quality Checks + # =========================================================================== + lint: + name: Lint and Format Check + 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_LATEST }} + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install black flake8 isort mypy + + - name: Check code formatting with black + run: black --check --diff . + + - name: Check import sorting with isort + run: isort --check-only --diff . + + - name: Lint with flake8 + run: | + # Stop on syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # Treat warnings as errors for CI + flake8 . --count --max-complexity=10 --max-line-length=100 --statistics + + - name: Type check with mypy + run: mypy core plugins --ignore-missing-imports + + # =========================================================================== + # Security Analysis + # =========================================================================== + security: + name: Security Analysis + 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_LATEST }} + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install bandit[toml] safety + + - name: Run Bandit security linter + run: bandit -r core plugins -f json -o bandit-report.json || true + + - name: Upload Bandit report + uses: actions/upload-artifact@v4 + if: always() + with: + name: bandit-report + path: bandit-report.json + + - name: Run Safety check + run: safety check || true + + # =========================================================================== + # Tests + # =========================================================================== test: + name: Test (Python ${{ matrix.python-version }}, ${{ matrix.os }}) runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest] + python-version: ["3.11", "3.12"] + include: + - os: ubuntu-latest + python-version: "3.11" + coverage: true + + 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 }} + + - name: Install system dependencies (Ubuntu) + if: runner.os == 'Linux' + run: | + sudo apt-get update + sudo apt-get install -y libegl1 libxkbcommon-x11-0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-xinerama0 libxcb-xfixes0 x11-utils + + - name: Install Python dependencies + run: | + python -m pip install --upgrade pip + pip install -e ".[dev]" + + - name: Run tests with coverage + if: matrix.coverage + run: | + pytest tests/ -v --cov=core --cov=plugins --cov-report=xml --cov-report=html --cov-fail-under=80 + env: + QT_QPA_PLATFORM: offscreen + + - name: Run tests without coverage + if: '!matrix.coverage' + run: | + pytest tests/ -v -m "not slow and not ui" + env: + QT_QPA_PLATFORM: offscreen + + - name: Upload coverage to Codecov + if: matrix.coverage + uses: codecov/codecov-action@v3 + with: + files: ./coverage.xml + fail_ci_if_error: true + verbose: true + + - name: Upload coverage report + if: matrix.coverage + uses: actions/upload-artifact@v4 + with: + name: coverage-report + path: htmlcov/ + + # =========================================================================== + # Build and Package + # =========================================================================== + build: + name: Build Package + runs-on: ubuntu-latest + needs: [lint, test] + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: ${{ env.PYTHON_LATEST }} + + - name: Install build dependencies + run: | + python -m pip install --upgrade pip + pip install build twine + + - name: Build package + run: python -m build + + - name: Check package + run: twine check dist/* + + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: dist + path: dist/ + + # =========================================================================== + # Test Installation + # =========================================================================== + test-install: + name: Test Installation + runs-on: ${{ matrix.os }} + needs: build strategy: matrix: os: [ubuntu-latest, windows-latest] - python-version: ['3.11', '3.12'] - steps: - - uses: actions/checkout@v3 - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install flake8 pytest - pip install -r requirements.txt - - - name: Lint with flake8 - run: | - flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics - flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics - - - name: Test with pytest - run: | - pytest tests/ -v + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: ${{ env.PYTHON_LATEST }} + + - name: Download build artifacts + uses: actions/download-artifact@v4 + with: + name: dist + path: dist/ + + - name: Install from wheel + run: pip install dist/*.whl + + - name: Test CLI entry point + run: | + eu-utility --help || true + python -c "from core import __version__; print(f'Version: {__version__}')" + + # =========================================================================== + # Release + # =========================================================================== + release: + name: Create Release + runs-on: ubuntu-latest + needs: [build, test-install] + if: startsWith(github.ref, 'refs/tags/v') + permissions: + contents: write + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Download build artifacts + uses: actions/download-artifact@v4 + with: + name: dist + path: dist/ + + - name: Create GitHub Release + uses: softprops/action-gh-release@v1 + with: + files: dist/* + generate_release_notes: true + draft: false + prerelease: ${{ contains(github.ref, 'alpha') || contains(github.ref, 'beta') || contains(github.ref, 'rc') }} + + # =========================================================================== + # Publish to PyPI (on tagged releases) + # =========================================================================== + publish: + name: Publish to PyPI + runs-on: ubuntu-latest + needs: release + if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, 'dev') + environment: + name: pypi + url: https://pypi.org/p/eu-utility + permissions: + id-token: write + steps: + - name: Download build artifacts + uses: actions/download-artifact@v4 + with: + name: dist + path: dist/ + + - name: Publish to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..461b643 --- /dev/null +++ b/Makefile @@ -0,0 +1,263 @@ +.PHONY: help install install-dev install-all update update-dev clean clean-all +.PHONY: test test-unit test-integration test-ui test-coverage test-fast +.PHONY: lint lint-flake8 lint-mypy lint-bandit lint-all +.PHONY: format format-check format-diff +.PHONY: build build-check build-dist upload-test upload-prod +.PHONY: docs docs-serve docs-build docs-clean +.PHONY: check security coverage-report + +# Default target +.DEFAULT_GOAL := help + +# Python executable +PYTHON := python3 +PIP := pip3 +PYTEST := pytest +BLACK := black +FLAKE8 := flake8 +MYPY := mypy +ISORT := isort +BANDIT := bandit +SAFETY := safety +TWINE := twine + +# Directories +SRC_DIRS := core plugins +TEST_DIR := tests +DOCS_DIR := docs +BUILD_DIR := build +DIST_DIR := dist + +# ============================================================================= +# HELP +# ============================================================================= + +help: ## Show this help message + @echo "EU-Utility Development Commands" + @echo "================================" + @echo "" + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-20s\033[0m %s\n", $$1, $$2}' + +# ============================================================================= +# INSTALLATION +# ============================================================================= + +install: ## Install production dependencies + $(PIP) install -e . + +install-dev: ## Install development dependencies + $(PIP) install -e ".[dev]" + +install-all: ## Install all dependencies including optional features + $(PIP) install -e ".[all,dev]" + +install-spotify: ## Install with Spotify support + $(PIP) install -e ".[spotify]" + +install-discord: ## Install with Discord Rich Presence support + $(PIP) install -e ".[discord]" + +update: ## Update production dependencies + $(PIP) install --upgrade -e . + +update-dev: ## Update development dependencies + $(PIP) install --upgrade -e ".[dev]" + +update-all: ## Update all dependencies + $(PIP) install --upgrade -e ".[all,dev]" + +# ============================================================================= +# TESTING +# ============================================================================= + +test: ## Run all tests + $(PYTEST) $(TEST_DIR) -v + +test-unit: ## Run unit tests only + $(PYTEST) $(TEST_DIR) -v -m unit + +test-integration: ## Run integration tests only + $(PYTEST) $(TEST_DIR) -v -m integration + +test-ui: ## Run UI tests only + $(PYTEST) $(TEST_DIR) -v -m ui + +test-performance: ## Run performance benchmarks + $(PYTEST) $(TEST_DIR) -v -m performance --benchmark-only + +test-coverage: ## Run tests with coverage report + $(PYTEST) $(TEST_DIR) -v --cov=$(SRC_DIRS) --cov-report=term-missing --cov-report=html + +test-coverage-xml: ## Run tests with XML coverage report + $(PYTEST) $(TEST_DIR) -v --cov=$(SRC_DIRS) --cov-report=xml + +test-fast: ## Run fast tests only (excludes slow and UI tests) + $(PYTEST) $(TEST_DIR) -v -m "not slow and not ui" + +test-parallel: ## Run tests in parallel (requires pytest-xdist) + $(PYTEST) $(TEST_DIR) -v -n auto + +# ============================================================================= +# CODE QUALITY +# ============================================================================= + +lint: lint-flake8 lint-mypy ## Run all linters + +lint-flake8: ## Run flake8 linter + $(FLAKE8) $(SRC_DIRS) $(TEST_DIR) + +lint-mypy: ## Run mypy type checker + $(MYPY) $(SRC_DIRS) + +lint-bandit: ## Run bandit security linter + $(BANDIT) -r $(SRC_DIRS) -f txt + +lint-pydocstyle: ## Run pydocstyle docstring checker + pydocstyle $(SRC_DIRS) + +lint-all: lint-flake8 lint-mypy lint-bandit lint-pydocstyle ## Run all linters + +format: ## Format code with black and isort + $(BLACK) $(SRC_DIRS) $(TEST_DIR) + $(ISORT) $(SRC_DIRS) $(TEST_DIR) + +format-check: ## Check code formatting without modifying files + $(BLACK) --check $(SRC_DIRS) $(TEST_DIR) + $(ISORT) --check-only $(SRC_DIRS) $(TEST_DIR) + +format-diff: ## Show formatting changes without applying them + $(BLACK) --diff $(SRC_DIRS) $(TEST_DIR) + +# ============================================================================= +# SECURITY +# ============================================================================= + +security: security-bandit security-safety ## Run all security checks + +security-bandit: ## Run bandit security analysis + $(BANDIT) -r $(SRC_DIRS) -f json -o bandit-report.json || true + $(BANDIT) -r $(SRC_DIRS) -f txt + +security-safety: ## Run safety check for known vulnerabilities + $(SAFETY) check + +security-pip-audit: ## Run pip-audit for dependency vulnerabilities + pip-audit --desc + +# ============================================================================= +# BUILD & DISTRIBUTION +# ============================================================================= + +build: clean-build ## Build source and wheel distributions + $(PYTHON) -m build + +build-check: ## Check the built distributions + $(TWINE) check $(DIST_DIR)/* + +build-dist: build build-check ## Build and check distributions + +upload-test: build-dist ## Upload to TestPyPI + $(TWINE) upload --repository testpypi $(DIST_DIR)/* + +upload-prod: build-dist ## Upload to PyPI (requires credentials) + $(TWINE) upload $(DIST_DIR)/* + +# ============================================================================= +# DOCUMENTATION +# ============================================================================= + +docs: docs-build ## Build documentation + +docs-build: ## Build Sphinx documentation + cd $(DOCS_DIR) && $(MAKE) html + +docs-serve: docs-build ## Serve documentation locally + cd $(DOCS_DIR)/_build/html && $(PYTHON) -m http.server 8000 + +docs-clean: ## Clean documentation build files + cd $(DOCS_DIR) && $(MAKE) clean + +docs-live: ## Serve documentation with auto-reload (requires sphinx-autobuild) + sphinx-autobuild $(DOCS_DIR) $(DOCS_DIR)/_build/html --watch $(SRC_DIRS) + +# ============================================================================= +# CLEANUP +# ============================================================================= + +clean: ## Remove build artifacts and cache files + rm -rf $(BUILD_DIR) + rm -rf $(DIST_DIR) + rm -rf *.egg-info + rm -rf .eggs + rm -rf .pytest_cache + rm -rf .mypy_cache + rm -rf .coverage + rm -rf htmlcov + rm -rf coverage.xml + find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true + find . -type f -name "*.pyc" -delete + find . -type f -name "*.pyo" -delete + find . -type f -name "*.so" -delete + +clean-all: clean ## Remove all generated files including virtual environments + rm -rf .venv + rm -rf venv + rm -rf env + rm -rf .tox + rm -rf bandit-report.json + +# ============================================================================= +# UTILITY +# ============================================================================= + +check: format-check lint test-fast ## Run all checks (format, lint, fast tests) + +ci: clean install-dev format-check lint test-coverage security ## Full CI pipeline + +coverage-report: ## Open coverage report in browser + $(PYTHON) -m webbrowser htmlcov/index.html + +requirements: ## Generate requirements.txt from pyproject.toml + $(PIP) freeze > requirements.txt + +dependency-tree: ## Show dependency tree + pipdeptree + +pre-commit: ## Install and run pre-commit hooks + pre-commit install + pre-commit run --all-files + +# ============================================================================= +# DEVELOPMENT +# ============================================================================= + +run: ## Run EU-Utility + $(PYTHON) -m core.main + +run-secure: ## Run EU-Utility (secure/optimized version) + $(PYTHON) -m core.main_optimized + +run-tests: ## Run the test suite runner + $(PYTHON) run_tests.py + +demo: ## Run the core functionality demo + $(PYTHON) core_functionality_demo.py + +profile: ## Run with profiling + $(PYTHON) -m cProfile -o profile.stats -m core.main + +# ============================================================================= +# VERSION MANAGEMENT +# ============================================================================= + +version: ## Show current version + $(PYTHON) -c "from core import __version__; print(__version__)" + +bump-patch: ## Bump patch version + bump2version patch + +bump-minor: ## Bump minor version + bump2version minor + +bump-major: ## Bump major version + bump2version major diff --git a/core/__init__.py b/core/__init__.py index cac9250..da08d61 100644 --- a/core/__init__.py +++ b/core/__init__.py @@ -114,7 +114,7 @@ def _get_dashboard(): def _get_activity_bar(): """Lazy load activity bar components.""" - from core.activity_bar_enhanced import ( + from core.activity_bar import ( AppDrawer, EnhancedActivityBar, PinnedPluginsArea,