|
| 1 | +#!/usr/bin/env bash |
| 2 | + |
| 3 | +# Exit immediately if a command exits with a non-zero status |
| 4 | +set -e |
| 5 | + |
| 6 | +# ============================================================================== |
| 7 | +# GLOBAL VARIABLES & SETUP |
| 8 | +# ============================================================================== |
| 9 | +SRC_DIR="mailjet_rest" |
| 10 | +TEST_DIR="tests" |
| 11 | +CONDA_ENV_NAME="mailjet-dev" |
| 12 | + |
| 13 | +# Color formatting for terminal output |
| 14 | +CYAN='\033[1;36m' |
| 15 | +GREEN='\033[1;32m' |
| 16 | +YELLOW='\033[1;33m' |
| 17 | +RED='\033[1;31m' |
| 18 | +NC='\033[0m' # No Color |
| 19 | + |
| 20 | +info() { echo -e "${CYAN}=> $1${NC}"; } |
| 21 | +success() { echo -e "${GREEN}=> $1${NC}"; } |
| 22 | +warn() { echo -e "${YELLOW}=> WARNING: $1${NC}"; } |
| 23 | +error() { echo -e "${RED}=> ERROR: $1${NC}"; } |
| 24 | + |
| 25 | +# ============================================================================== |
| 26 | +# ENVIRONMENT & SETUP |
| 27 | +# ============================================================================== |
| 28 | +env_setup() { |
| 29 | + # Example: ./manage.sh env_setup |
| 30 | + info "Creating and updating conda environment '${CONDA_ENV_NAME}'..." |
| 31 | + conda env create -n "${CONDA_ENV_NAME}" -y --file environment-dev.yaml || conda env update -n "${CONDA_ENV_NAME}" --file environment-dev.yaml |
| 32 | + info "Installing package in editable mode..." |
| 33 | + conda run --name "${CONDA_ENV_NAME}" pip install -e . |
| 34 | + info "Installing pre-commit hooks..." |
| 35 | + conda run --name "${CONDA_ENV_NAME}" pre-commit install |
| 36 | + success "Environment ready! Don't forget to run: conda activate ${CONDA_ENV_NAME}" |
| 37 | +} |
| 38 | + |
| 39 | +# ============================================================================== |
| 40 | +# FORMATTING & LINTING (Modernized 2026 Stack) |
| 41 | +# ============================================================================== |
| 42 | +format() { |
| 43 | + # Example: ./manage.sh format |
| 44 | + info "Formatting code with Ruff (replaces Black/Isort)..." |
| 45 | + ruff format "${SRC_DIR}" "${TEST_DIR}" scripts/ |
| 46 | + info "Applying safe auto-fixes..." |
| 47 | + ruff check --fix "${SRC_DIR}" "${TEST_DIR}" scripts/ |
| 48 | + success "Code formatted successfully." |
| 49 | +} |
| 50 | + |
| 51 | +lint() { |
| 52 | + # Example: ./manage.sh lint |
| 53 | + info "Running Ruff linter (replaces Flake8/Pylint)..." |
| 54 | + ruff check "${SRC_DIR}" "${TEST_DIR}" |
| 55 | + info "Running MyPy strict type checking..." |
| 56 | + mypy "${SRC_DIR}" "${TEST_DIR}" |
| 57 | + success "Linting passed!" |
| 58 | +} |
| 59 | + |
| 60 | +# ============================================================================== |
| 61 | +# TESTING SCENARIOS |
| 62 | +# ============================================================================== |
| 63 | +# Note: "$@" allows you to pass ANY extra pytest flags (like -s, -vvv, or -k "test_name") |
| 64 | + |
| 65 | +test_all() { |
| 66 | + # Example: ./manage.sh test_all |
| 67 | + # Example with flags: ./manage.sh test_all -vvv -s |
| 68 | + info "Running ALL tests (Unit + Integration)..." |
| 69 | + pytest -n auto "${TEST_DIR}" "$@" |
| 70 | +} |
| 71 | + |
| 72 | +test_unit() { |
| 73 | + # Example: ./manage.sh test_unit |
| 74 | + # Example specific test: ./manage.sh test_unit tests/unit/test_client.py::test_get_version |
| 75 | + # Example specific class: ./manage.sh test_unit -k "TestClientAuth" |
| 76 | + info "Running UNIT tests..." |
| 77 | + pytest "${TEST_DIR}/unit" "$@" |
| 78 | +} |
| 79 | + |
| 80 | +test_integration() { |
| 81 | + # Example: ./manage.sh test_integration |
| 82 | + info "Running INTEGRATION tests..." |
| 83 | + pytest "${TEST_DIR}/integration" "$@" |
| 84 | +} |
| 85 | + |
| 86 | +test_cov() { |
| 87 | + # Example: ./manage.sh test_cov |
| 88 | + info "Running tests with Coverage requirements (Fail under 80%)..." |
| 89 | + pytest -n auto --cov="${SRC_DIR}" "${TEST_DIR}" --cov-fail-under=80 --cov-report=term-missing --cov-report=html |
| 90 | + success "Coverage report generated in htmlcov/index.html" |
| 91 | +} |
| 92 | + |
| 93 | +test_no_warnings() { |
| 94 | + # Example: ./manage.sh test_no_warnings |
| 95 | + # Example for specific group: ./manage.sh test_no_warnings tests/unit/ |
| 96 | + info "Running tests and SUPPRESSING all DeprecationWarnings..." |
| 97 | + pytest -W "ignore::DeprecationWarning" "$@" |
| 98 | +} |
| 99 | + |
| 100 | +test_strict_warnings() { |
| 101 | + # Example: ./manage.sh test_strict_warnings |
| 102 | + info "Running tests and treating DeprecationWarnings as ERRORS..." |
| 103 | + pytest -W "error::DeprecationWarning" "$@" |
| 104 | +} |
| 105 | + |
| 106 | +# ============================================================================== |
| 107 | +# PERFORMANCE & BENCHMARKING |
| 108 | +# ============================================================================== |
| 109 | +perf_bench() { |
| 110 | + # Example: ./manage.sh perf_bench |
| 111 | + # Example compare: ./manage.sh perf_bench --benchmark-compare |
| 112 | + info "Running pytest-benchmark performance tests..." |
| 113 | + pytest "${TEST_DIR}/test_perf.py" "$@" |
| 114 | +} |
| 115 | + |
| 116 | +perf_profile() { |
| 117 | + # Example: ./manage.sh perf_profile |
| 118 | + info "Running cold-boot profiler (cProfile)..." |
| 119 | + python "${TEST_DIR}/test_boot.py" |
| 120 | +} |
| 121 | + |
| 122 | +# ============================================================================== |
| 123 | +# SECURITY AUDITS & PRE-COMMIT |
| 124 | +# ============================================================================== |
| 125 | +audit_deps() { |
| 126 | + # Example: ./manage.sh audit_deps |
| 127 | + info "Running pip-audit for known vulnerabilities..." |
| 128 | + pip-audit || warn "pip-audit found issues." |
| 129 | + |
| 130 | + if command -v osv-scanner &> /dev/null; then |
| 131 | + info "Running Google OSV-Scanner..." |
| 132 | + osv-scanner -r . |
| 133 | + else |
| 134 | + warn "osv-scanner not found. Skipping." |
| 135 | + fi |
| 136 | +} |
| 137 | + |
| 138 | +run_hooks() { |
| 139 | + # Example: ./manage.sh run_hooks |
| 140 | + info "Running all pre-commit hooks (including slotscheck, gitleaks, etc.)..." |
| 141 | + pre-commit run --all-files |
| 142 | +} |
| 143 | + |
| 144 | +# ============================================================================== |
| 145 | +# BUILD & RELEASE |
| 146 | +# ============================================================================== |
| 147 | +build_pkg() { |
| 148 | + # Example: ./manage.sh build_pkg |
| 149 | + clean |
| 150 | + info "Building source and wheel distribution..." |
| 151 | + python -m build |
| 152 | + ls -l dist |
| 153 | + success "Build complete." |
| 154 | +} |
| 155 | + |
| 156 | +release() { |
| 157 | + # Example: ./manage.sh release |
| 158 | + build_pkg |
| 159 | + info "Uploading to PyPI via Twine..." |
| 160 | + twine upload dist/* |
| 161 | +} |
| 162 | + |
| 163 | +# ============================================================================== |
| 164 | +# CLEANUP |
| 165 | +# ============================================================================== |
| 166 | +clean() { |
| 167 | + # Example: ./manage.sh clean |
| 168 | + info "Cleaning up workspace (caches, builds, coverage)..." |
| 169 | + |
| 170 | + # Python caches |
| 171 | + find . -type d -name '__pycache__' -exec rm -rf {} + |
| 172 | + find . -type f -name '*.py[co]' -exec rm -f {} + |
| 173 | + find . -type f -name '*~' -exec rm -f {} + |
| 174 | + |
| 175 | + # Test & Coverage artifacts |
| 176 | + rm -rf .pytest_cache/ .mypy_cache/ .ruff_cache/ .tox/ |
| 177 | + rm -rf .coverage htmlcov/ coverage.xml reports/ |
| 178 | + |
| 179 | + # Build artifacts |
| 180 | + rm -rf build/ dist/ .eggs/ |
| 181 | + find . -type d -name '*.egg-info' -exec rm -rf {} + |
| 182 | + find . -type f -name '*.egg' -exec rm -f {} + |
| 183 | + |
| 184 | + # Temp logs and profilers |
| 185 | + rm -f *.prof profile.html profile.json tmp.txt wget-log |
| 186 | + |
| 187 | + success "Workspace cleaned!" |
| 188 | +} |
| 189 | + |
| 190 | +# ============================================================================== |
| 191 | +# MAIN ROUTER & HELP |
| 192 | +# ============================================================================== |
| 193 | +help() { |
| 194 | + echo -e "${CYAN}Mailjet SDK Management Script${NC}" |
| 195 | + echo "Usage: ./manage.sh <command> [extra_arguments...]" |
| 196 | + echo "" |
| 197 | + echo -e "${YELLOW}Development & Code Quality:${NC}" |
| 198 | + echo " env_setup - Create/update conda dev env and install pre-commit" |
| 199 | + echo " format - Format code (Ruff)" |
| 200 | + echo " lint - Run linters and type checkers (Ruff, MyPy)" |
| 201 | + echo " run_hooks - Run all pre-commit hooks manually (slotscheck, etc.)" |
| 202 | + echo "" |
| 203 | + echo -e "${YELLOW}Testing (Any pytest flags like '-s', '-vvv', '-k' can be added at the end):${NC}" |
| 204 | + echo " test_all - Run all tests" |
| 205 | + echo " test_unit - Run only unit tests" |
| 206 | + echo " test_integration - Run only integration tests" |
| 207 | + echo " test_cov - Run tests with HTML coverage report" |
| 208 | + echo " test_no_warnings - Run tests and hide all DeprecationWarnings" |
| 209 | + echo " test_strict_warnings - Run tests and fail on any DeprecationWarning" |
| 210 | + echo "" |
| 211 | + echo -e "${YELLOW}Performance & Security:${NC}" |
| 212 | + echo " perf_bench - Run pytest-benchmark suite" |
| 213 | + echo " perf_profile - Run cProfile on cold boot" |
| 214 | + echo " audit_deps - Run pip-audit and osv-scanner" |
| 215 | + echo "" |
| 216 | + echo -e "${YELLOW}Build & Maintenance:${NC}" |
| 217 | + echo " clean - Remove all build, test, and cache artifacts" |
| 218 | + echo " build_pkg - Build source and wheel package" |
| 219 | + echo " release - Build and upload release to PyPI" |
| 220 | + echo " help - Show this menu" |
| 221 | + echo "" |
| 222 | + echo -e "${GREEN}Examples:${NC}" |
| 223 | + echo " ./manage.sh test_unit -vvv -s" |
| 224 | + echo " ./manage.sh test_unit -k \"test_pep578_audit_hooks\"" |
| 225 | + echo " ./manage.sh test_no_warnings tests/unit/test_client.py" |
| 226 | +} |
| 227 | + |
| 228 | +# Check if at least one argument is provided |
| 229 | +if [ $# -eq 0 ]; then |
| 230 | + help |
| 231 | + exit 1 |
| 232 | +fi |
| 233 | + |
| 234 | +COMMAND=$1 |
| 235 | +shift # Remove the command from the arguments list, leaving only extra flags |
| 236 | + |
| 237 | +case "$COMMAND" in |
| 238 | + env_setup|format|lint|test_all|test_unit|test_integration|test_cov|test_no_warnings|test_strict_warnings|perf_bench|perf_profile|audit_deps|run_hooks|build_pkg|release|clean|help) |
| 239 | + "$COMMAND" "$@" # Execute the function with any remaining arguments |
| 240 | + ;; |
| 241 | + *) |
| 242 | + error "Unknown command: $COMMAND" |
| 243 | + help |
| 244 | + exit 1 |
| 245 | + ;; |
| 246 | +esac |
0 commit comments